黑马程序员——关于java字节流的read()方法返回值为int的思考(转载)

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

 之前一直不明白java字节流的read()方法返回值为什么为int,今天在网上找到了答案。以下为转载,原文地址:http://blog.sina.com.cn/s/blog_9e351f9b01015kgp.html



我们都知道java中io操作分为字节流和字符流,对于字节流,顾名思义是按字节的方式读取数据,所以我们常用字节流来读取二进制流(如图片,音乐 等文件)。问题是为什么字节流中定义的read()方法返回值为int类型呢?既然它一次读出一个字节数据为什么不返回byte类型呢?(不知道有没有人 和我有同样的困惑,不过既然有了问题咱就得解决。)

于是我翻阅了java的源码,下面先把源码贴出来(以BufferedInputStream/BufferedOutputStream为例):

[java] view plaincopy

//BufferedInputStream中的read()方法的实现

[java] view plaincopy

public synchronized int read() throws IOException {

if (pos >= count) {

fill();

if (pos >= count)

return -1;

}

return getBufIfOpen()[pos++] & 0xff;//这边getBufIfOpen()返回的是byte[],而& 0xff是为了保证由char类型向上拓展成int的时候,不进行符号拓展,而是0拓展。

[java] view plaincopy

public synchronized int read() throws IOException {

if (pos >= count) {

fill();

if (pos >= count)

return -1;

}

return getBufIfOpen()[pos++] & 0xff;//这边getBufIfOpen()返回的是byte[],而& 0xff是为了保证由char类型向上拓展成int的时候,不进行符号拓展,而是0拓展。

从源码中我们得到的信息是直到getBufIfOpen()方法返回,我们得到的都是byte类型,可是为什么方法的最终返回值是int?

首先我先简单解释下符号扩展,这是指由byte向上转化成更宽的类型时,是扩展的符号位。这对于正数补0,负数补1,例如,定义byte b = -1;在计算机内部它是用八位1111 1111表示的,当扩展成32位整型的时候,一般情况下是1111 1111 1111 1111 1111 1111 1111 1111,即符号扩展,而对于无符号扩展,也称为0扩展,其结果是0000 0000 0000 0000 0000 0000 1111 1111(实际上这样一来值已经变成255了)。过于向上转型和强制向下转型的进一步讨论,我在以后再说。这里要说的是我们能从java源码中得到的第二 个信息,即上面的注释部分,read()方法的最后一行把读到的字节0扩展成了int,也就是说如果我们直接读出来这个值可能就是不对了。这里我又疑惑 了,为什么BufferedOutputStream中的writer()方法能正确读出字节呢?所以我再去查下对应的源码:

[java] view plaincopy

public synchronized void write(int b) throws IOException {

if (count >= buf.length) {

flushBuffer();

}

buf[count++] = (byte)b;

}

[java] view plaincopy

public synchronized void write(int b) throws IOException {

if (count >= buf.length) {

flushBuffer();

}

buf[count++] = (byte)b;

}

可以看到,这里它果断的又将int强制转成了byte(截取后八位)。于是总结下现在得到的信息是,java字节流把byte转成int读出来再转回byte存起来。何必呢?

经过一番思考,我初步有了答案:在用输入流读取一个byte数据时,有时会出现连续8个1的情况,这个值在计算机内部表示-1,正 好符合了流结束标记。所以为了避免流操作数据提前结束,将读到的字节进行int类型的扩展。保留该字节数据的同时,前面都补0,避免出现-1的情况。而真 正读到文件最后结束是通过这句实现的:

[java] view plaincopy

if (pos >= count)  return -1;

[java] view plaincopy

if (pos >= count)  return -1;

所以我们使用的-1这个结束标志是通过这句返回的,而不是输入流读到了一个-1。

这样一来就解决了我们前面的疑惑,也证实了确实有这样实现的必要。对于字符流的读写也可以用类似的方法分析,这里不再赘述。

时间: 2024-07-29 15:00:50

黑马程序员——关于java字节流的read()方法返回值为int的思考(转载)的相关文章

黑马程序员 内部类 Java

内部类:将一个类定义在另一个类里面,对里面那个类就叫做内部类(内置类,嵌套类). 访问特点:内部类可以直接访问外部类中的成员,包括私有成员.而外部类要访问内部类中的成员必须要建立内部类的对象. class Outer{ private int  x=3; class Inner{ void function(){ function1(); System.out.println("内部类"+x); } } void function1(){ System.out.println(&quo

黑马程序员之Java多线程学习

android培训  java培训 期待与您交流! 这一篇文章主要关于java多线程,主要还是以例子来驱动的.因为讲解多线程的书籍和文章已经很多了,所以我也不好意思多说,呵呵.大家可以去参考一些那些书籍.我这个文章主要关于实际的一些问题.同时也算是我以后复习的资料吧,.呵呵大家多多指教. 同时希望多结交一些技术上的朋友.谢谢. -------------------------------------------------------------------------------------

黑马程序员——【Java高新技术】——代理

一.“代理概述”及“AOP概念” (一)代理概述 1.问题:要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理.日志.计算方法的运行时间.事务管理等等,如何去做? 解答:编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码. 2.代理原理图,如下: 3.代理的优点 如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类.还是代理类,这样以后很容易切换.例如,想要日志功能时就

黑马程序员之——Java基础 IO流——第四部分

-----------android培训.java培训.java学习型技术博客.期待与您交流!------------ 第一讲:黑马程序员_毕向东_Java基础视频教程第21天-01-IO流(对象的序列化) 一,对象序列化的概念:将堆内存中的对象存入硬盘,保留对象中的数据,称之为对象的持久化(或序列化). 二,ObjectInputStream和ObjectOutputStream类的了解: ObjectInputStream  特有方法:public final Object  readObj

黑马程序员之——Java基础 IO流——第三部分

-----------android培训.java培训.java学习型技术博客.期待与您交流!------------ 第一讲:黑马程序员_毕向东_Java基础视频教程第20天-01-IO流(File概述) 一,File 类的了解: 用于将文件或者文件夹封装成对象. 方便对文件与文件夹进行操作. File对象可作为参数传递给流对象的构造方法. File 类的声明:public class File extends Object implements Serializable, Comparabl

黑马程序员——【Java基础】——集合框架

一.集合框架概述 (一)集合框架中集合类关系简化图 (二)为什么出现集合类? 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是用于存储对象的. (三)数组和集合类同是容器,有何不同? .数组长度是固定的:集合长度是可变的. 2.数组中可以存储基本数据类型,集合只能存储对象. (四)集合类的特点 集合可以存储不同类型的对象.集合只用于存储对象,集合长度是可变的. (五)为什么会出现这么多容器? 因为每一个容器对数据的存储方式都有不同,这个存储方式称

黑马程序员——【Java基础】——File类、Properties集合、IO包中的其他类

---------- android培训.java培训.期待与您交流! ---------- 一.File类 (一)概述 1.File类:文件和目录路径名的抽象表现形式 2.作用: (1)用来将文件或文件夹封装成对象 (2)方便于对“文件”与“文件夹属性信息”进行操作 (3)File对象,可以作为参数传递给流的构造函数 (二)构造方法 * 通过File的构造函数创建File对象 方式1:File f = new File("c:\\a.txt"); 方式2:File f2 = newF

黑马程序员——【Java基础】——泛型、Utilities工具类、其他对象API

一.泛型 (一)泛型概述 1.泛型:JDK1.5版本以后出现的新特性,用于解决安全问题,是一个类型安全机制. 2.泛型技术是给编译器使用的技术,用于编译时期,确保类型的安全. 3.泛型的擦除:运行时,会将泛型去掉,生成class文件中的是不带泛型的,这个称为“泛型的擦除”.擦除泛型的原因是为了兼容运行时的类加载器. 4.泛型的好处:(1)将运行时期出现的问题ClassCastException,转移到了编译时期.方便于程序员解决问题,让运行时期问题减少.安全.(2)避免了强制转换的麻烦. 5.泛

黑马程序员_ JAVA中的多线程

尽管线程对象的常用方法可以通过API文档来了解,但是有很多方法仅仅从API说明是无法详细了解的. 本来打算用一节的篇幅来把线程方法中一些重要的知识说完,但这样下来估计要很常的篇幅,可能要用好几节才能说把和线程方法相关的一些重要的知识说完. 首先我们接基础篇(二)来说明start()方法. 一个线程对象生成后,如果要产生一个执行的线程,就一定要调用它的start()方法.在介绍这个方法时不得不同时说明run方法.其实线程对 象的run方法完全是一个接口回调方法,它是你这个线程对象要完成的具体逻辑.