java(jdk1.7) IO系列01之InputStream和OutputStream解析

时间:2021-03-08 00:30:36

1.InputStream和OutputStream简介

在java中InputStream和OutputStream分别代表字节输入流和字节输出流,表示以字节的方式来实现进程或者程序的通信,InputStream是输入流,表示以字节的方式从文件(FileInputStream)或者字节数组(ByteArrayInputStream)等读取数据,与之相对应的OutputStream是输出流,表示以字节的方式向文件(FileOutputStream)或者字节数组(ByteArrayOutputStream)等写入数据,InputStream和OutputStream分别是所有的字节流的超类,定义了字节输入流和字节输出流的抽象方法和公用实现,这里使用设计模式中的模版模式,超类定义一些公用的实现和相关的方法的约束,子类实现。下面让我们来看一些InputStream和OutputStream的源码

2.InputStream源码解析

 package java.io;
public abstract class InputStream implements Closeable { //定义可以跳过的最大字节数
private static final int MAX_SKIP_BUFFER_SIZE = 2048; //抽象读取方法,定义读取一个字节的方法约束,让子类去实现,
public abstract int read() throws IOException; //把读取的字节放到byte数组中,返回的是读取字节的数量
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
  //具体的读取字节到字节数组的实现,并提供读取到字节数组b的起始位置off和读入的长度len
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
//先读取一个字节,如果返回-1,说明当前流中没有数据,直接返回-1
int c = read();
if (c == -1) {
return -1;
}
     //如果有数据,则把数据存到b中
b[off] = (byte)c; int i = 1;
try {
//通过循环读取,然后把读取的内容存到传入的buffer中
for (; i < len ; i++) {
c = read();
          //如果返回-1,则表示当前读取到文件的结尾
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;//返回读取的字节的数量
} //返回真实跳过的字节的数量
public long skip(long n) throws IOException { long remaining = n;
int nr;
//如果当前要求跳过0个,则直接返回0
if (n <= 0) {
return 0;
}
//字符缓冲区的大小不能大于2048.跳过的数量n可能大于2048.
    //这里有个原因,为什么不直接声明一个和跳过字节n一样大小的数组。因为内存是个很昂贵的资源,如果一个文件很大的话,一次在内存中声明一个很大的内存,
    //一方面会占用很大的内存,另一方面也影响垃圾回收 int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];//定义一个缓冲记录跳过的字节的数量
while (remaining > 0) {
//如果跳过的数量大于2048,则通过缓冲区多次执行read方法计算跳过的数量
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
     //返回实际跳过的字节的数量
return n - remaining;
}
//返回有多少数据能够读取
public int available() throws IOException {
return 0;
} //关闭流,留给子类去实现
public void close() throws IOException {} //标记一个位置,然后执行reset方法的时候可以重新回到标记的位置
public synchronized void mark(int readlimit) {} //重新从标记的位置读取
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
} //表明是否支持标记功能
public boolean markSupported() {
return false;
} }

3.OutputSteam源码解析

 public abstract class OutputStream implements Closeable, Flushable {
//写入一个字节b到输入流
public abstract void write(int b) throws IOException; //把字节数组b中的字节写入到输入流中
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
} //把字节数组b中的字节写入到输入流中,并提供控制参数,从字节数组的那个位置开始读取,以及读取多长的长度
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}   //刷新输出流,强制把字符缓冲中的字节从内存中写入到输出流中,如果输出流是内存比如ByteArrayOutputStream等,则该实现为空 public void flush() throws IOException {
} //关闭输出流,并释放资源,如果子类不需要,则该实现为空
public void close() throws IOException {
} }

4.结语

虽然InputStream和OutputStream的两个抽象类的实现比较简单,代码量比较少,但是我们还是可以从中学到一些东西,比如模版设计模式的使用方法,代码中对方法参数的校验逻辑等。多多体会别人写的代码,总会学到一些东西,或许现在看不出来有什么用,但是对你的影响会慢慢看出来的。

===============================================================

努力工作,用心生活

===============================================================