1.InputStream和OutputStream简介
在java中InputStream和OutputStream分别代表字节输入流和字节输出流,表示以字节的方式来实现进程或者程序的通信,InputStream是输入流,表示以字节的方式从文件(FileInputStream)或者字节数组(ByteArrayInputStream)等读取数据,与之相对应的OutputStream是输出流,表示以字节的方式向文件(FileOutputStream)或者字节数组(ByteArrayOutputStream)等写入数据,InputStream和OutputStream分别是所有的字节流的超类,定义了字节输入流和字节输出流的抽象方法和公用实现,这里使用设计模式中的模版模式,超类定义一些公用的实现和相关的方法的约束,子类实现。下面让我们来看一些InputStream和OutputStream的源码
2.InputStream源码解析
1 package java.io; 2 public abstract class InputStream implements Closeable { 3 4 //定义可以跳过的最大字节数 5 private static final int MAX_SKIP_BUFFER_SIZE = 2048; 6 7 //抽象读取方法,定义读取一个字节的方法约束,让子类去实现, 8 public abstract int read() throws IOException; 9 10 //把读取的字节放到byte数组中,返回的是读取字节的数量 11 public int read(byte b[]) throws IOException { 12 return read(b, 0, b.length); 13 } 14 //具体的读取字节到字节数组的实现,并提供读取到字节数组b的起始位置off和读入的长度len 15 public int read(byte b[], int off, int len) throws IOException { 16 if (b == null) { 17 throw new NullPointerException(); 18 } else if (off < 0 || len < 0 || len > b.length - off) { 19 throw new IndexOutOfBoundsException(); 20 } else if (len == 0) { 21 return 0; 22 } 23 //先读取一个字节,如果返回-1,说明当前流中没有数据,直接返回-1 24 int c = read(); 25 if (c == -1) { 26 return -1; 27 } 28 //如果有数据,则把数据存到b中 29 b[off] = (byte)c; 30 31 int i = 1; 32 try { 33 //通过循环读取,然后把读取的内容存到传入的buffer中 34 for (; i < len ; i++) { 35 c = read(); 36 //如果返回-1,则表示当前读取到文件的结尾 37 if (c == -1) { 38 break; 39 } 40 b[off + i] = (byte)c; 41 } 42 } catch (IOException ee) { 43 } 44 return i;//返回读取的字节的数量 45 } 46 47 //返回真实跳过的字节的数量 48 public long skip(long n) throws IOException { 49 50 long remaining = n; 51 int nr; 52 //如果当前要求跳过0个,则直接返回0 53 if (n <= 0) { 54 return 0; 55 } 56 //字符缓冲区的大小不能大于2048.跳过的数量n可能大于2048. 57 //这里有个原因,为什么不直接声明一个和跳过字节n一样大小的数组。因为内存是个很昂贵的资源,如果一个文件很大的话,一次在内存中声明一个很大的内存, 58 //一方面会占用很大的内存,另一方面也影响垃圾回收 59 60 61 int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining); 62 byte[] skipBuffer = new byte[size];//定义一个缓冲记录跳过的字节的数量 63 while (remaining > 0) { 64 //如果跳过的数量大于2048,则通过缓冲区多次执行read方法计算跳过的数量 65 nr = read(skipBuffer, 0, (int)Math.min(size, remaining)); 66 if (nr < 0) { 67 break; 68 } 69 remaining -= nr; 70 } 71 //返回实际跳过的字节的数量 72 return n - remaining; 73 } 74 //返回有多少数据能够读取 75 public int available() throws IOException { 76 return 0; 77 } 78 79 //关闭流,留给子类去实现 80 public void close() throws IOException {} 81 82 //标记一个位置,然后执行reset方法的时候可以重新回到标记的位置 83 public synchronized void mark(int readlimit) {} 84 85 //重新从标记的位置读取 86 public synchronized void reset() throws IOException { 87 throw new IOException("mark/reset not supported"); 88 } 89 90 //表明是否支持标记功能 91 public boolean markSupported() { 92 return false; 93 } 94 95 }
3.OutputSteam源码解析
1 public abstract class OutputStream implements Closeable, Flushable { 2 //写入一个字节b到输入流 3 public abstract void write(int b) throws IOException; 4 5 //把字节数组b中的字节写入到输入流中 6 public void write(byte b[]) throws IOException { 7 write(b, 0, b.length); 8 } 9 10 //把字节数组b中的字节写入到输入流中,并提供控制参数,从字节数组的那个位置开始读取,以及读取多长的长度 11 public void write(byte b[], int off, int len) throws IOException { 12 if (b == null) { 13 throw new NullPointerException(); 14 } else if ((off < 0) || (off > b.length) || (len < 0) || 15 ((off + len) > b.length) || ((off + len) < 0)) { 16 throw new IndexOutOfBoundsException(); 17 } else if (len == 0) { 18 return; 19 } 20 for (int i = 0 ; i < len ; i++) { 21 write(b[off + i]); 22 } 23 } 24 25 //刷新输出流,强制把字符缓冲中的字节从内存中写入到输出流中,如果输出流是内存比如ByteArrayOutputStream等,则该实现为空 26 27 public void flush() throws IOException { 28 } 29 30 //关闭输出流,并释放资源,如果子类不需要,则该实现为空 31 public void close() throws IOException { 32 } 33 34 }
4.结语
虽然InputStream和OutputStream的两个抽象类的实现比较简单,代码量比较少,但是我们还是可以从中学到一些东西,比如模版设计模式的使用方法,代码中对方法参数的校验逻辑等。多多体会别人写的代码,总会学到一些东西,或许现在看不出来有什么用,但是对你的影响会慢慢看出来的。
===============================================================
努力工作,用心生活
===============================================================
时间: 2024-10-18 22:03:38