Java IO流read()方法为什么返回int而不是byte

2 篇文章 0 订阅

一 官方文档

按照我个人处理问题的习惯,先拿出官方文档的定义,然后再解决问题,这样更准去,也更具有说服力。参考自Java SE 8:抽象类java.io.InputStream

public abstract int read() throws IOException

Reads the next byte of data from the input stream. The value byte is returned as an int in the range 0 to 255. If no byte is available because the end of the stream has been reached, the value -1 is returned. This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.
读取字节流数据的next字节。其值是范围从0到255之间的Int类型的数据。如果因为已经达到了流的末尾而没有可以读取的字节,则返回值-1。此方法会阻塞,知道输入数据可用、检测到流结束或抛出异常。

A subclass must provide an implementation of this method.
子类必须实现这个方法。

Returns:
the next byte of data, or -1 if the end of the stream is reached.
Throws:
IOException - if an I/O error occurs.

在计算机中,字节是数据存储的最小单位。而字节(Byte)由8个位(bit)最成。从0000 0000到1111 1111共有256种组合。

程序要处理数据,需要把数据“拿进来”。首先,我们想到java中的byte是8位的数据类型,正好可以“盛载”对应的字节数据。即对应关系如下(注意,byte类型的数据存储时使用补码

字节数据byte值二进制形式byte值十进制形式
0000 00000000 00000
0000 00010000 00011
0111 11110111 1111127
1000 00001000 0000- 128
1000 00011000 0001-127
1111 11111111 1111-1

但是,会存在一个问题,文件(或其他数据源)的结束无法表示。文件的结束跟定不能使用byte类型的某个数字进行表示,因为byte的全部数字都和字节数据进行对应,如果使用了某个数字作为文件的结束,就和数据产生了冲突,程序无法判断该数字代表字节数据还是文件的结束。

于是,我们选择使用int类型的后8(Int类型有32位,使用最后8位)“盛载”对应的字节数据。即对应关系如下:

字节数据Int值二进制形式Int值十进制形式
0000 00000000 0000 0000 0000 0000 0000 0000 00000
0000 00010000 0000 0000 0000 0000 0000 0000 00011
1111 11110000 0000 0000 0000 0000 0000 1111 1111255
EOF(非字节数据,文件尾标志)1111 1111 1111 1111 1111 1111 1111 1111-1

文件尾EOF(end of file)我们用-1来表示,它没有和任何字节数据冲突,因此,读取一个字节数据,我们使用Int数据类型来“盛载”,是为了文件的结束EOF可以得到表示,并且不和字节数据冲突。

想要了解更多相关知识,请继续阅读扩展知识。

二 扩展知识

文件结尾(英语:End of File,缩写为EOF),是操作系统无法从数据源读取更多数据的情形。数据源通常为文件或流。【百度百科


简而言之,文件尾标志EOF,就是文件结束的标志,读取文件字节数据时,如果返回EOF,则表示文件已经读取完毕或没有字节数据可供读取。

疑问一: EOF是一个可被插入到文件中的字符(类似换行符被插入到文件中)吗?

测试,我们使用notepad++编辑一个文件,在其中输入“abc”,然后使用工具查看其底层存储对应的十六进制数。
在这里插入图片描述
可以看到“abc”三个字符对应的ASCII编码的码点正好是“61 62 63”,并不存在其他字符。
因此,EOF不是一个类似回车换行的字符。

疑问二 EOF不是一个可被插入到文件中字符,它是什么,又如何判断是否读取到了文件尾?

  没有EOF这样的字符。操作系统确切地知道文件包含多少字节(这与其他元数据一起存储在内存里,如读写权限,创建日期和名称)。因此操作系统可以告诉尝试读取十字节文件的第十一个字节的程序:你已经到达文件末尾,没有更多的字节可以读取。

告知的方式就是通过返回EOF标志,不同的语言(或平台)EOF标志对应的数值可能不同,通常EOF对应的值是-1。
.

疑问三 数据源除了文件之外,还可能是终端,它如何表示文件尾(即输入结束)?

  数据源,即数据的源头,例如文件、磁盘、终端、网络连接等等都是数据源。这里我们重点说明以下我们常用的终端(如Windows的cmd命令行程序)。

  从一个终端的输入从来不会真的“结束”(除非设备被断开),但把从终端输入的数据分区成多个“文件”却很有用,因此一个关键的序列被保留下来来指明输入结束。在UNIX和AmigaDOS中,将击键翻译为EOF的过程是由终端的驱动程序完成的,因此应用程序无需将终端和其它输入文件区分开来。Unix平台的驱动程序在行首传送一个传输结束字符(Control-D,ASCII编码为为04)来指明文件结束

在微软的DOS和Windows(以及CP/M和许多DEC操作系统)中,读取数据时终端不会产生EOF。此时,应用程序知道数据源是一个终端(或者其它“字符设备”),并将一个已知的保留的字符或序列解释为文件结束的指明;最普遍地说,它是ASCII码中的替换字符(Control-Z,代码26)

  简而言之,对于Windows和DOS,终端(如cmd)不会产生EOF标志,程序知道数据眼是终端并预留一个其他的ASCII控制字符(0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符)),作为EOF的等价/替换字符(或理解为“别名”),这个字符如下图所示:终端输入方式Ctrl + Z
  对于UNIX系统而言,终端的驱动程序可以产生EOF标志,因此程序无法判断数据源是文件还是终端。终端输入方式Ctrl + D

总结如下

系统终端输入EOF标志或等价标志ASC码十进制十六进制控制字符说明
Windows/DOSCtrl + Z261ASUBSubstitute(替补/替换)
UNIX系列(Linux等)Ctrl + Z404EOTEnd of Transmission(传输结束)

注释:0~31及127(共33个)是控制字符或通信专用字符(其余为可显示字符),想了解更过ASCII编码知识,点击【ASCII中的控制字符含义

  • 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
发出的红包

打赏作者

明月几时有666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值