15.3 字节流和字符流 15.3.1 InputStream和Reader
15.3 字节流和字符流
字节流和字符流的操作方式几乎完全一样,区别只是操作的数据单元不同而已:
- 字节流操作的数据单元是字节,
- 字符流操作的数据单元是字符。
15.3.1 InputStream和Reader
InputStream
和Reader
是所有输入流的**抽象基类**,本身并不能创建实例来执行输入,但它们将成为所有输入流的模板,所以它们的方法是所有输入流都可使用的方法。
InputStream常用方法
在InputStream
里包含如下三个方法。
方法 | 描述 |
---|---|
abstract int read() |
从输入流中读取单个字节,返回所读取的字节数据(字节数据可直接转换为int 类型)。 |
int read(byte[] b) |
从输入流中最多读取b.length 个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数。 |
int read(byte[] b, int off, int len) |
从输入流中最多读取len 个字节的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off 位置开始,返回实际读取的字节数。 |
Reader常用方法
在Reader
里包含如下三个常用方法。
方法 | 描述 |
---|---|
int read() |
从输入流中读取单个字符,返回所读取的字符数据(字符数据可直接转换为int 类型) |
int read(char[] cbuf) |
从输入流中最多读取cbuf.length 个字符的数据,并将其存储在字符数组cbuf 中,返回实际读取的字符数。 |
abstract int read(char[] cbuf, int off, int len) |
从输入流中最多读取len 个字符的数据,并将其存储在字符数组cbuf 中,放入数组cbuf 中时,并不是从数组起点开始,而是从of ‘位置开始,返回实际读取的字符数。 |
对比InputStream
和Reader
所提供的方法,就不难发现这两个基类的功能基本是一样的InputStream
和Reader
都是将输入数据抽象成如图15.5所示的水管,所以程序既可以通过read()
方法每次读取一个“水滴”,也可以通过read(char[] cbuf)
或read(byte[] b)
方法来读取多个“水滴”。
当使用数组作为read()
方法的参数时,可以理解为使用一个“竹筒”到如图15.5所示的水管中取水,如图15.8所示。read(char[] cbuf)
方法中的数组可理解成一个“竹筒”,程序每次调用输入流的read(cha[] cbuf)
或read(byte[] b)
方法,就相当于用“竹筒”从输入流中取岀一筒“水滴”,程序得到“竹筒”里的“水滴”后,转换成相应的数据即可;
程序多次重复这个“取水”过程,直到最后。
如何判断已经读取完毕
程序如何判断取水取到了最后呢?直到read(char[] cbuf)
或read(byte[] b)
方法返回-1,即表明到了输入流的结束点。
FileInputStream
和FileReader
正如前面提到的,InputStream
和Reader
都是抽象类,本身不能创建实例,但它们分别有一个用于读取文件的输入流:FileInputStream
和FileReader
,它们都是节点流——会直接和指定文件关联。
程序示例 读取java文件自身
下面程序示范了使用FileInputStream
来读取自身的效果。
1 | import java.io.*; |
上面程序中的while循环就是使用FileInputStream
循环“取水”的过程,运行上面程序,将会输出上面程序的源代码。
上面程序创建了一个长度为1024的字节数组来读取该文件,实际上该Java
源文件的长度还不到1024字节,也就是说,程序只需要执行一次read()
方法即可读取全部内容。
但如果创建较小长度的字节数组,程序运行时在输岀中文注释时就可能出现乱码,这是因为本文件保存时采用的是GBK
编码方式,在这种方式下,每个中文字符占2字节,如果read()
方法读取时只读到了半个中文字符,这将导致乱码
文件IO资源要显示关闭
上面程序最后使用了fis.close();
来关闭该文件输入流,与JDBC
编程一样,程序里打开的文件IO
资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。
Java7后可使用自动关闭资源的try来关闭IO流
Java7
改写了所有的IO
资源类,它们都实现了AutoCloseable
接口,因此都可通过自动关闭资源的try
语句来关闭这些IO流。
程序示例:FileReader读取java文件本身
下面程序使用FileReader
来读取文件本身。
1 | import java.io.*; |
上面的FileReaderTest.java
程序与前面的FileInputStreamTest.java
并没有太大的不同,程序只是将字符数组的长度改为32,这意味着程序需要多次调用read()
方法才可以完全读取输入流的全部数据。
程序最后使用了自动关闭资源的try
语句来关闭文件输入流,这样可以保证输入流一定会被关闭。
移动记录指针的方法
除此之外,InputStream
和Reader
还支持如下几个方法来移动记录指针。
方法 | 描述 |
---|---|
void mark(int readAheadLimit) |
在记录指针当前位置记录一个标记(mark )。 |
boolean markSupported() |
判断此输入流是否支持mark() 操作,即是否支持记录标记。 |
void reset() |
将此流的记录指针重新定位到上一次记录标记(mark )的位置。 |
long skip(long n) |
记录指针向前移动n个字节/字符。 |
InputStream其他方法
方法 | 描述 |
---|---|
int available() |
Returns an estimate of the number of bytes that can be read(or skipped over) from this input stream without blocking by the next invocation of a method for this input stream. |
void close() |
Closes this input stream and releases any system resources associated with the stream. |
byte[] readAllBytes() |
Reads all remaining bytes from the input stream. |
int readNBytes(byte[] b, int off, int len) |
Reads the requested number of bytes from the input stream into the given byte array. |
long transferTo(OutputStream out) |
Reads all bytes from this input stream and writes the bytes to the given output stream in the order that they are read. |
Reader其他方法
方法 | 描述 |
---|---|
abstract void close() |
Closes the stream and releases any system resources associated with it. |
int read(CharBuffer target) |
Attempts to read characters into the specified character buffer. |
boolean ready() |
Tells whether this stream is ready to be read. |