前面已经学习了FilterInputStream与FilterOutputStream。文章中到了FilterInputStream与FilterOutputStream的子类可进一步重写父类方法中的一些方法,来提供装饰功能。今天就来介绍下它们子类中的DataInputStream与DataOutputStream。
DataInputStream为数据输入流,它允许应用程序以与机器无关方式从底层输入流中读取基本Java数据类型。
DataOutputStream为数据输出流,它允许应用程序以适当方式将基本 Java数据类型写入输出流中。
阅读源码需要复习一些基础知识,比如基本数据类型、位移运算、&xFF操作。
先来复习下基本数据类型各有多少位。
基本数据类型 | byte | short | int | long | float | double | boolean | char |
---|---|---|---|---|---|---|---|---|
位 | 8 | 16 | 32 | 64 | 32 | 64 | 1 | 16 |
再来复习下位移运算符中的<<和>>>。
<<表示左移运算符,是将运算符左边的对象,向左移动运算符右边指定的位数,并且在低位补零。其实,向左移n 位,就相当于乘上2 的n 次方。
如,20 < 2;20的二进制为 0001 0100,右移2位后为 0010 1000,则结果就为 40;
>>>表示无符号右移,也叫逻辑右移。即若该数为正,则高位补0,而若该数为负数,则右移后高位同样补0。
如,20 >>> 2;20的二进制为 0001 0100,右移2位后为 0000 0101,则结果就为 5;
而-20 >> 2;-20的二进制为 1110 1011,右移2位,此时高位补0,即 0011 1010,结果为58;
最后复习下&xFF操作代表什么含义。
取低8位。0xFF是二进制的 1111 1111,那么0000 1010 0101 0101&1111 1111就能取到0000 1010 0101 0101的低8位。
DataInputStream
下面来看下DataInputStream的源码。
/**
* DataInput接口用于从二进制流中读取字节,并根据所有Java基本类型数据进行重构。
* 同时还提供根据UTF-8修改版格式的数据重构String的工具。
*/
public class DataInputStream extends FilterInputStream implements DataInput {
/**
* 构造方法之一
* 使用特定的输出流创建DataInputStream
*/
public DataInputStream(InputStream in) {
super(in);
}
/**
* readUTF()使用的数组
*/
private byte bytearr[] = new byte[80];
private char chararr[] = new char[80];
/**
* 从输入流中读取b.length个数据保存到byte数组b中。
* 实际读取的字节数以整数的形式返回。
* 在输入数据可用、检测到文件末尾、或抛出异常之前,该方法一直阻塞。
* 如果b为null,会抛出NullPointerException。
* 如果b长度为0,不会读取数据,方法返回值为0;否则会尝试读取至少一个字节。
* 如果因为流在文件末尾导致没有字节可用,将返回-1;否则至少会读取一个字节并保存到b中。
*
* 第一个字节保存到b[0],下一个字节保存到b[1],以此类推。
* 读取的字节数最大等于b的长度。
*
* 此方法等价于read(b, 0, b.length)
*
* @param b 存储读取数据的byte数组
* @return 实际读取的字节数, 如果因为到达流末尾导致没有数据,返回-1。
* @exception IOException 如果不是因为流位于文件末尾而无法读取第一个字节;该流已关闭并且底层输入流在关闭后不支持读取操作;发生其他 I/O错误。
*/
public final int read(byte b[]) throws IOException {
return in.read(b, 0, b.length);
}
/**
* 从包含的输入流中将最多len个字节读入一个byte数组中。
* 尽量读取len个字节,但读取的字节数可能少于len个,也可能为零。
* 以整数形式返回实际读取的字节数。
* 在输入数据可用、检测到文件末尾或抛出异常之前,此方法将阻塞。
*
* 如果len为零,则不读取任何字节并返回0;
* 否则,尝试读取至少一个字节。如果因为流位于文件未尾而没有字节可用,则返回值-1;
* 否则,至少读取一个字节并将其存储到b中。
*
* @param b 存储读取数据的缓冲区。
* @param off 目标数组 b 中的起始偏移量
* @param len 读取的最大字节数。
* @return 读入缓冲区的字节总数;如果因为已经到达流末尾而没有更多的数据,则返回-1。
* @exception NullPointerException 如果b为null。
* @exception IndexOutOfBoundsException 如果off为负, len为负,或者len大于b.length-off
* @exception IOException 如果不是因为流位于文件末尾而无法读取第一个字节;该流已关闭并且底层输入流在关闭后不支持读取操作;发生其他I/O
*/
public final int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
/**
* 参考DataInput.readFully()的介绍。
*
* 以下是DataInput.readFully()的介绍。
* 从输入流中读取一些字节,并将它们存储在缓冲区数组b中。读取的字节数等于b的长度。
*
* 该方法的执行结果有三种:
* 1.输入数据的 len 个字节是可用的,在这种情况下,正常返回。
* 2.检测到文件末尾,在这种情况下,抛出 EOFException。
* 3.如果发生 I/O 错误,在这种情况下,将抛出 IOException,而不是 EOFException。
*
* 如果b为null,则抛出NullPointerException。
* 如果off为负,或len为负,或者off+len大于数组b的长度,则抛出IndexOutOfBoundsException。
* 如果len为零,则不读取字节。
* 否则,将读取的第一个字节存储到元素b[off]中,下一个字节存储到 b[off+1]中,
* 依此类推。读取的字节数至多等于b[0]。
*
* @param b 存储读取数据的缓冲区。
* @exception EOFException 如果此输入流在读取所有字节之前到达末尾。
* @exception IOException 该流已关闭并且包含的输入流在关闭后不支持读取操作,或者发生其他 I/O 错误。
*/
public final void readFully(byte b[]) throws IOException {
readFully(b, 0, b.length);
}
/**
* 参考readFully(byte b[])
*
* @param b 存储读取数据的缓冲区。
* @param off 指定数据中的偏移量
* @param len 指定读取的字节数
* @exception EOFException 如果此流在读取所有字节之前到达末尾。
* @exception IOException 该流已关闭并且包含的输入流在关闭后不支持读取操作,或者发生其他I/O错误。
*/
public final void readFully(byte b[], int off, int len) throws IOException {
//如果参数不合法,抛出异常
if (len < 0)
throw new IndexOutOfBoundsException();
int n = 0;
//读取字节,直到读取了len个字节
while (n < len) {
int count = in.read(b, off + n, len - n);