关于java字节流的read()方法返回int型而非byte型的思考

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

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

//BufferedInputStream中的read()方法的实现
 /**
     * See
     * the general contract of the <code>read</code>
     * method of <code>InputStream</code>.
     *
     * @return     the next byte of data, or <code>-1</code> if the end of the
     *             stream is reached.
     * @exception  IOException  if this input stream has been closed by
     *                          invoking its {@link #close()} method,
     *                          or an I/O error occurs.
     * @see        java.io.FilterInputStream#in
     */
    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()方法能正确读出字节呢?所以我再去查下对应的源码:

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的情况。而真正读到文件最后结束是通过这句实现的:

if (pos >= count)  return -1;

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

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

当然了java的设计者有可能还有许多其他各方面的考虑,如果大家有不同见解请提出来让我学习下。此博文仅供参考。

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,可以了解到Java IO流分为字节流和字符流两种类型。其中字节流以字节为单位进行读写,而字符流以字符为单位进行读写。下面是Java IO字节流的构造方法和使用方法: 1. InputStream类是所有字节输入流的父类,常用方法有: - read():从输入流中读取一个字节的数据。 - read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。 - skip(long n):跳过并丢弃输入流中的n个字节数据。 - available():返回输入流中可以被读取的字节数。 2. FileInputStream类是InputStream类的子类,常用方法有: - FileInputStream(String name):创建一个文件输入流,以读取具有指定名称的文件。 - read():从输入流中读取一个字节的数据。 - read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中。 - skip(long n):跳过并丢弃输入流中的n个字节数据。 - available():返回输入流中可以被读取的字节数。 3. FileOutputStream类是OutputStream类的子类,常用方法有: - FileOutputStream(String name):创建一个文件输出流,以写入具有指定名称的文件。 - write(int b):将指定的字节写入此文件输出流。 - write(byte[] b):将b.length个字节从指定的字节数组写入此文件输出流中。 - flush():刷新此输出流并强制任何缓冲的输出字节被写出。 下面是一个Java IO字节流复制文件的示例代码: ```java public static void main(String[] args) { try { // 创建要复制文件的字节输入流 InputStream inp = new FileInputStream("/Users/chenyq/Documents/learn_Java/code/file-io-app/src/test.pdf"); // 创建目标路径的字节输出流 OutputStream oup = new FileOutputStream("/Users/chenyq/Documents/newtest.pdf"); // 使用文件输入流获取要复制文件的全部数据的字节数组 byte[] arr = inp.readAllBytes(); // 使用文件输出流将字节数组写入目标文件 oup.write(arr); System.out.println("复制成功!"); // 释放资源 inp.close(); oup.close(); } catch (IOException e) { e.printStackTrace(); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值