jdk源码解析八之BIO

怼烎@ 2023-02-21 14:21 97阅读 0赞

文章目录

  • 字节流
    • InputStream
      • FilterInputStream
      • ByteArrayInputStream
      • //todo FileInputStream
      • BufferInputStream
      • PipedInputStream
      • //todo ObjectInputStream
    • OutputStream
      • //todo FileOutputStream
      • BufferedOutputStream
      • PipedOutputStream
      • //todo ObjectOutputStream
  • 字符流
    • Reader
      • //todo FileReader
      • BufferedReader
    • Writer
      • //todo FileWriter
      • //todo BufferWriter
  • 转换流
    • //todo InputStreamReader
    • //todo OutputStreamWriter

在这里插入图片描述

字节流

InputStream

  1. public abstract class InputStream implements Closeable {
  2. //最大可跳过字节数
  3. private static final int MAX_SKIP_BUFFER_SIZE = 2048;
  4. public int read(byte b[], int off, int len) throws IOException {
  5. if (b == null) {
  6. throw new NullPointerException();
  7. } else if (off < 0 || len < 0 || len > b.length - off) {
  8. throw new IndexOutOfBoundsException();
  9. } else if (len == 0) {
  10. return 0;
  11. }
  12. //读取一字节数据
  13. int c = read();
  14. //到达文件的末端返回-1
  15. if (c == -1) {
  16. return -1;
  17. }
  18. //赋值
  19. b[off] = (byte)c;
  20. //一个字节一个字节读取,填充到b数组
  21. int i = 1;
  22. try {
  23. for (; i < len ; i++) {
  24. c = read();
  25. if (c == -1) {
  26. break;
  27. }
  28. b[off + i] = (byte)c;
  29. }
  30. } catch (IOException ee) {
  31. }
  32. return i;
  33. }
  34. public long skip(long n) throws IOException {
  35. long remaining = n;
  36. int nr;
  37. if (n <= 0) {
  38. return 0;
  39. }
  40. //最大创建MAX_SKIP_BUFFER_SIZE大小数组
  41. int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
  42. byte[] skipBuffer = new byte[size];
  43. //使用循环,尽量读取remaining大小数据
  44. while (remaining > 0) {
  45. nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
  46. //读到流的末端,则返回
  47. if (nr < 0) {
  48. break;
  49. }
  50. remaining -= nr;
  51. }
  52. return n - remaining;
  53. }
  54. //返回默认值
  55. public int available() throws IOException {
  56. return 0;
  57. }
  58. //标记一个位置,用于reset,当超过readlimit,则标记位置失效
  59. public synchronized void mark(int readlimit) {
  60. }
  61. public synchronized void reset() throws IOException {
  62. throw new IOException("mark/reset not supported");
  63. }
  64. //是否支持标记,默认不支持
  65. public boolean markSupported() {
  66. return false;
  67. }

FilterInputStream

  1. public class FilterInputStream extends InputStream {
  2. //装饰器的代码特征:被装饰的对象一般是装饰器的成员变量
  3. protected volatile InputStream in; //将要被装饰的字节输入流
  4. protected FilterInputStream(InputStream in) {
  5. //通过构造方法传入此被装饰的流
  6. this.in = in;
  7. }
  8. //下面这些方法,完成最小的装饰――0装饰,只是调用被装饰流的方法而已
  9. public int read() throws IOException {
  10. return in.read();
  11. }
  12. public int read(byte b[]) throws IOException {
  13. return read(b, 0, b.length);
  14. }
  15. public int read(byte b[], int off, int len) throws IOException {
  16. return in.read(b, off, len);
  17. }
  18. public long skip(long n) throws IOException {
  19. return in.skip(n);
  20. }
  21. public int available() throws IOException {
  22. return in.available();
  23. }
  24. public void close() throws IOException {
  25. in.close();
  26. }
  27. public synchronized void mark(int readlimit) {
  28. in.mark(readlimit);
  29. }
  30. public synchronized void reset() throws IOException {
  31. in.reset();
  32. }
  33. public boolean markSupported() {
  34. return in.markSupported();
  35. }
  36. }

ByteArrayInputStream

将内存中的数组装饰成InputStrean

  1. public
  2. class ByteArrayInputStream extends InputStream {
  3. //包装的字节数组
  4. protected byte buf[];
  5. //读取位置
  6. protected int pos;
  7. //标记位置
  8. protected int mark = 0;
  9. //数组长度
  10. protected int count;
  11. public ByteArrayInputStream(byte buf[]) {
  12. //装饰的数组
  13. this.buf = buf;
  14. //设置位置和长度
  15. this.pos = 0;
  16. this.count = buf.length;
  17. }
  18. public ByteArrayInputStream(byte buf[], int offset, int length) {
  19. this.buf = buf;
  20. this.pos = offset;
  21. this.count = Math.min(offset + length, buf.length);
  22. this.mark = offset;
  23. }
  24. public synchronized int read() {
  25. return (pos < count) ? (buf[pos++] & 0xff) : -1;
  26. }
  27. public synchronized int read(byte b[], int off, int len) {
  28. if (b == null) {
  29. throw new NullPointerException();
  30. } else if (off < 0 || len < 0 || len > b.length - off) {
  31. throw new IndexOutOfBoundsException();
  32. }
  33. //超过范围,返回-1
  34. if (pos >= count) {
  35. return -1;
  36. }
  37. //查看剩下可读字节大小
  38. int avail = count - pos;
  39. //超过限制,则默认查询剩下可读字节数
  40. if (len > avail) {
  41. len = avail;
  42. }
  43. if (len <= 0) {
  44. return 0;
  45. }
  46. System.arraycopy(buf, pos, b, off, len);
  47. pos += len;
  48. return len;
  49. }
  50. public synchronized long skip(long n) {
  51. long k = count - pos;
  52. if (n < k) {
  53. k = n < 0 ? 0 : n;
  54. }
  55. pos += k;
  56. return k;
  57. }
  58. public synchronized int available() {
  59. return count - pos;
  60. }
  61. public boolean markSupported() {
  62. return true;
  63. }
  64. public void mark(int readAheadLimit) {
  65. mark = pos;
  66. }
  67. /**
  68. * Resets the buffer to the marked position. The marked position
  69. * is 0 unless another position was marked or an offset was specified
  70. * in the constructor.
  71. */
  72. public synchronized void reset() {
  73. pos = mark;
  74. }
  75. public void close() throws IOException {
  76. //什么操作都没有
  77. }
  78. }

//todo FileInputStream

BufferInputStream

  1. public
  2. class BufferedInputStream extends FilterInputStream {
  3. //默认缓冲区大小
  4. private static int DEFAULT_BUFFER_SIZE = 8192;
  5. //缓冲区最大扩展容量
  6. private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
  7. //缓冲数组
  8. protected volatile byte buf[];
  9. //用于CAS 修改数组
  10. private static final
  11. AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
  12. AtomicReferenceFieldUpdater.newUpdater
  13. (BufferedInputStream.class, byte[].class, "buf");
  14. //缓冲区读取的个数
  15. protected int count;
  16. //当前读取位置
  17. protected int pos;
  18. //标记当前pos,用于reset,则重新开始从markpos读取
  19. protected int markpos = -1;
  20. //读取内容超过limit,markpos失效
  21. protected int marklimit;
  22. private InputStream getInIfOpen() throws IOException {
  23. //获取封装输入流
  24. InputStream input = in;
  25. if (input == null)
  26. throw new IOException("Stream closed");
  27. return input;
  28. }
  29. private byte[] getBufIfOpen() throws IOException {
  30. ///获取缓冲区数组
  31. byte[] buffer = buf;
  32. if (buffer == null)
  33. throw new IOException("Stream closed");
  34. return buffer;
  35. }
  36. public BufferedInputStream(InputStream in) {
  37. //默认缓冲数组大小8192
  38. this(in, DEFAULT_BUFFER_SIZE);
  39. }
  40. public BufferedInputStream(InputStream in, int size) {
  41. super(in);
  42. if (size <= 0) {
  43. throw new IllegalArgumentException("Buffer size <= 0");
  44. }
  45. buf = new byte[size];
  46. }
  47. private void fill() throws IOException {
  48. //获取缓冲数组
  49. byte[] buffer = getBufIfOpen();
  50. if (markpos < 0)
  51. pos = 0; /* no mark: throw away the buffer */
  52. //设置了标记位置,以及当前读取的位置超过缓存数据最大长度
  53. else if (pos >= buffer.length) /* no room left in buffer */
  54. //这一步相当于把markpos右边数据全部挪移到左边,因为标记的位置指不定需要reset,所以相当于保存一个进度点
  55. if (markpos > 0) {
  56. /* can throw away early part of the buffer */
  57. int sz = pos - markpos;
  58. //将buffer数组从标记位置开始,复制sz个数到buffer
  59. System.arraycopy(buffer, markpos, buffer, 0, sz);
  60. //记录新的位置
  61. pos = sz;
  62. //标记位置回滚0
  63. markpos = 0;
  64. } else if (buffer.length >= marklimit) {
  65. //当buffer长度超过marklimit时,mark失效
  66. markpos = -1; /* buffer got too big, invalidate mark */
  67. pos = 0; /* drop buffer contents */
  68. } else if (buffer.length >= MAX_BUFFER_SIZE) {
  69. //说明无法继续扩容缓冲数组容量了
  70. throw new OutOfMemoryError("Required array size too large");
  71. } else {
  72. /* grow buffer */
  73. //扩容一倍容量
  74. int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
  75. pos * 2 : MAX_BUFFER_SIZE;
  76. //太大则为marklimit大小
  77. if (nsz > marklimit)
  78. nsz = marklimit;
  79. byte nbuf[] = new byte[nsz];
  80. //将buffer数据copy扩容后的nbuf
  81. System.arraycopy(buffer, 0, nbuf, 0, pos);
  82. if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
  83. // Can't replace buf if there was an async close.
  84. // Note: This would need to be changed if fill()
  85. // is ever made accessible to multiple threads.
  86. // But for now, the only way CAS can fail is via close.
  87. // assert buf == null;
  88. throw new IOException("Stream closed");
  89. }
  90. buffer = nbuf;
  91. }
  92. //设置下步没读取到数据,设置默认值
  93. count = pos;
  94. //从流中读取数据到缓冲数组
  95. int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
  96. //读取到数据,则记录最新的缓冲数组大小
  97. if (n > 0)
  98. count = n + pos;
  99. }
  100. public synchronized int read() throws IOException {
  101. //当前读取位置>=缓冲区最大容量,则重新从输入流获取数据
  102. if (pos >= count) {
  103. fill();
  104. if (pos >= count)
  105. return -1;
  106. }
  107. //缓冲读取一个字节
  108. return getBufIfOpen()[pos++] & 0xff;
  109. }
  110. private int read1(byte[] b, int off, int len) throws IOException {
  111. //余下缓冲数组容量=buf数组长度-当前读取位置
  112. int avail = count - pos;
  113. //初次读取,又或者读取完缓冲数组
  114. if (avail <= 0) {
  115. //len超过缓冲数组的长度,则直接返回,不缓存
  116. if (len >= getBufIfOpen().length && markpos < 0) {
  117. return getInIfOpen().read(b, off, len);
  118. }
  119. //填充到缓冲数组
  120. fill();
  121. //返回读取个数
  122. avail = count - pos;
  123. if (avail <= 0) return -1;
  124. }
  125. //边界检查,最多只能获取缓冲数组最大容量的数据
  126. int cnt = (avail < len) ? avail : len;
  127. //缓冲数组获取数据
  128. System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
  129. //记录当前读取位置
  130. pos += cnt;
  131. return cnt;
  132. }
  133. public synchronized int read(byte b[], int off, int len)
  134. throws IOException
  135. {
  136. //判断buffer是否打开
  137. getBufIfOpen(); // Check for closed stream
  138. //off=0 len=1024.则值为1024
  139. //判断off+len < b.length 则越界异常
  140. //也就是说从off开始读取len长度.
  141. if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
  142. throw new IndexOutOfBoundsException();
  143. } else if (len == 0) {
  144. return 0;
  145. }
  146. //记录读取的总字节数
  147. int n = 0;
  148. for (;;) {
  149. //读取到b数组,返回读取个数
  150. int nread = read1(b, off + n, len - n);
  151. //说明没读取到,则直接返回
  152. if (nread <= 0)
  153. return (n == 0) ? nread : n;
  154. //累加读取字节数
  155. n += nread;
  156. //读取的总字节数>=读取长度,则直接返回
  157. if (n >= len)
  158. return n;
  159. // if not closed but no bytes available, return
  160. InputStream input = in;
  161. //虽然读取的字节数<=读取长度,但是如果可以读取的字节数预估读完了,则直接返回
  162. if (input != null && input.available() <= 0)
  163. return n;
  164. }
  165. }
  166. public synchronized long skip(long n) throws IOException {
  167. getBufIfOpen(); // Check for closed stream
  168. if (n <= 0) {
  169. return 0;
  170. }
  171. //可以跳过的最大范围
  172. long avail = count - pos;
  173. //说明尚未读取,又或者已经读取填充完整个缓冲区
  174. if (avail <= 0) {
  175. // If no mark position set then don't keep in buffer
  176. //尚未标记位置,则直接操作输入流跳过n
  177. if (markpos <0)
  178. return getInIfOpen().skip(n);
  179. // Fill in buffer to save bytes for reset
  180. //填充缓存数组
  181. fill();
  182. //获取填充之后的可跳过范围
  183. avail = count - pos;
  184. //依旧没,则返回
  185. if (avail <= 0)
  186. return 0;
  187. }
  188. //跳过的最大不能超过最大可以跳过的范围
  189. long skipped = (avail < n) ? avail : n;
  190. //记录新的位置
  191. pos += skipped;
  192. return skipped;
  193. }
  194. //该方法的返回值为缓存中的可读字节数目加流中可读字节数目的和
  195. public synchronized int available() throws IOException {
  196. int n = count - pos;
  197. int avail = getInIfOpen().available();
  198. return n > (Integer.MAX_VALUE - avail)
  199. ? Integer.MAX_VALUE
  200. : n + avail;
  201. }
  202. public synchronized void mark(int readlimit) {
  203. //标记位置无效之前可以读取的最大字节数
  204. marklimit = readlimit;
  205. //标记此输入流中的当前位置
  206. markpos = pos;
  207. }
  208. public synchronized void reset() throws IOException {
  209. getBufIfOpen(); // Cause exception if closed
  210. if (markpos < 0)
  211. throw new IOException("Resetting to invalid mark");
  212. pos = markpos;
  213. }
  214. //该流和ByteArrayInputStream一样都支持mark
  215. public boolean markSupported() {
  216. return true;
  217. }
  218. public void close() throws IOException {
  219. byte[] buffer;
  220. while ( (buffer = buf) != null) {
  221. //CAS,清空缓冲流
  222. if (bufUpdater.compareAndSet(this, buffer, null)) {
  223. InputStream input = in;
  224. in = null;
  225. if (input != null)
  226. input.close();
  227. return;
  228. }
  229. }
  230. }
  231. }

PipedInputStream

  1. public class PipedInputStream extends InputStream {
  2. //分别标记当前读管道,写管道的状态
  3. boolean closedByWriter = false;
  4. volatile boolean closedByReader = false;
  5. //标记是否连接到写管道
  6. boolean connected = false;
  7. //读写2个线程
  8. Thread readSide;
  9. Thread writeSide;
  10. //默认管道缓冲区大小
  11. private static final int DEFAULT_PIPE_SIZE = 1024;
  12. protected static final int PIPE_SIZE = DEFAULT_PIPE_SIZE;
  13. protected byte buffer[];
  14. //下一个写入字节位置 in=out则说明满了
  15. protected int in = -1;
  16. //下一个读取字节位置
  17. protected int out = 0;
  18. public PipedInputStream(PipedOutputStream src) throws IOException {
  19. this(src, DEFAULT_PIPE_SIZE);
  20. }
  21. public PipedInputStream(PipedOutputStream src, int pipeSize)
  22. throws IOException {
  23. //初始化缓冲区大小,默认大小1024
  24. initPipe(pipeSize);
  25. //将当前对象传入PipedOutputStream
  26. connect(src);
  27. }
  28. public PipedInputStream() {
  29. //初始化缓冲区大小,默认大小1024
  30. initPipe(DEFAULT_PIPE_SIZE);
  31. }
  32. public PipedInputStream(int pipeSize) {
  33. //初始化指定大小的缓冲区
  34. initPipe(pipeSize);
  35. }
  36. private void initPipe(int pipeSize) {
  37. //初始化指定大小管道缓冲区
  38. if (pipeSize <= 0) {
  39. throw new IllegalArgumentException("Pipe Size <= 0");
  40. }
  41. buffer = new byte[pipeSize];
  42. }
  43. public void connect(PipedOutputStream src) throws IOException {
  44. //管道连接
  45. src.connect(this);
  46. }
  47. //接受output发送过来的数据
  48. protected synchronized void receive(int b) throws IOException {
  49. checkStateForReceive();
  50. //获取当前线程
  51. writeSide = Thread.currentThread();
  52. //读满,则等待,通知其他读线程,我要开始写了
  53. if (in == out)
  54. awaitSpace();
  55. //读满或初次读取,则重置下标
  56. if (in < 0) {
  57. in = 0;
  58. out = 0;
  59. }
  60. //写入数据
  61. buffer[in++] = (byte)(b & 0xFF);
  62. //超过边界则从0开始
  63. if (in >= buffer.length) {
  64. in = 0;
  65. }
  66. }
  67. synchronized void receive(byte b[], int off, int len) throws IOException {
  68. //检查读写管道状态是否正常开放
  69. checkStateForReceive();
  70. writeSide = Thread.currentThread();
  71. int bytesToTransfer = len;
  72. while (bytesToTransfer > 0) {
  73. //读满,则等待,通知其他读线程,我要开始写了
  74. if (in == out)
  75. awaitSpace();
  76. int nextTransferAmount = 0;
  77. //如果还有空间可以读取,则获取最大可读取大小
  78. if (out < in) {
  79. nextTransferAmount = buffer.length - in;
  80. } else if (in < out) {
  81. //读满或者初次读取则重置
  82. if (in == -1) {
  83. in = out = 0;
  84. nextTransferAmount = buffer.length - in;
  85. } else {
  86. //读到这里说明in走了一圈,重置为0了
  87. nextTransferAmount = out - in;
  88. }
  89. }
  90. //读取空间足够
  91. if (nextTransferAmount > bytesToTransfer)
  92. nextTransferAmount = bytesToTransfer;
  93. assert(nextTransferAmount > 0);
  94. //写入范围数据到buffer
  95. System.arraycopy(b, off, buffer, in, nextTransferAmount);
  96. //记录剩下还需要写入的空间
  97. bytesToTransfer -= nextTransferAmount;
  98. off += nextTransferAmount;
  99. in += nextTransferAmount;
  100. //写入超限,重置
  101. if (in >= buffer.length) {
  102. in = 0;
  103. }
  104. }
  105. }
  106. private void checkStateForReceive() throws IOException {
  107. //当前是否连接
  108. if (!connected) {
  109. throw new IOException("Pipe not connected");
  110. } else if (closedByWriter || closedByReader) {
  111. //当前读写管道是否关闭了
  112. throw new IOException("Pipe closed");
  113. } else if (readSide != null && !readSide.isAlive()) {
  114. //当前线程死了
  115. throw new IOException("Read end dead");
  116. }
  117. }
  118. private void awaitSpace() throws IOException {
  119. while (in == out) {
  120. //检查读写管道状态是否正常开放
  121. checkStateForReceive();
  122. /* full: kick any waiting readers */
  123. //通知
  124. notifyAll();
  125. try {
  126. //等待
  127. wait(1000);
  128. } catch (InterruptedException ex) {
  129. throw new java.io.InterruptedIOException();
  130. }
  131. }
  132. }
  133. synchronized void receivedLast() {
  134. //标记写管道流关闭
  135. closedByWriter = true;
  136. //通知所有等待的线程
  137. notifyAll();
  138. }
  139. public synchronized int read() throws IOException {
  140. //校验当前线程是否正常连接
  141. if (!connected) {
  142. throw new IOException("Pipe not connected");
  143. } else if (closedByReader) {
  144. //当前读管道是否关闭
  145. throw new IOException("Pipe closed");
  146. } else if (writeSide != null && !writeSide.isAlive()
  147. && !closedByWriter && (in < 0)) {
  148. //写线程是否存活,以及是否关闭了写流
  149. throw new IOException("Write end dead");
  150. }
  151. readSide = Thread.currentThread();
  152. int trials = 2;
  153. //等待写入
  154. while (in < 0) {
  155. //写入流关闭,则返回-1
  156. if (closedByWriter) {
  157. /* closed by writer, return EOF */
  158. return -1;
  159. }
  160. //写线程已经不存活了,则抛异常
  161. if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
  162. throw new IOException("Pipe broken");
  163. }
  164. /* might be a writer waiting */
  165. //通知线程,开始读了
  166. notifyAll();
  167. try {
  168. //等待
  169. wait(1000);
  170. } catch (InterruptedException ex) {
  171. throw new java.io.InterruptedIOException();
  172. }
  173. }
  174. //读入数据
  175. int ret = buffer[out++] & 0xFF;
  176. //读到缓冲区上限则重置
  177. if (out >= buffer.length) {
  178. out = 0;
  179. }
  180. //读满则重置
  181. if (in == out) {
  182. /* now empty */
  183. in = -1;
  184. }
  185. return ret;
  186. }
  187. public synchronized int read(byte b[], int off, int len) throws IOException {
  188. if (b == null) {
  189. throw new NullPointerException();
  190. } else if (off < 0 || len < 0 || len > b.length - off) {
  191. throw new IndexOutOfBoundsException();
  192. } else if (len == 0) {
  193. return 0;
  194. }
  195. /* possibly wait on the first character */
  196. //尝试读取一个字节
  197. int c = read();
  198. //没数据则直接返回
  199. if (c < 0) {
  200. return -1;
  201. }
  202. b[off] = (byte) c;
  203. int rlen = 1;
  204. while ((in >= 0) && (len > 1)) {
  205. int available;
  206. //获取可读取的范围
  207. if (in > out) {
  208. available = Math.min((buffer.length - out), (in - out));
  209. } else {
  210. //执行到这里,说明in写了一圈了.
  211. available = buffer.length - out;
  212. }
  213. // A byte is read beforehand outside the loop
  214. //可读取的空间足够,则直接读取len-1长度
  215. if (available > (len - 1)) {
  216. available = len - 1;
  217. }
  218. System.arraycopy(buffer, out, b, off + rlen, available);
  219. out += available;
  220. rlen += available;
  221. len -= available;
  222. //读到缓冲区上限则重置
  223. if (out >= buffer.length) {
  224. out = 0;
  225. }
  226. //读满则重置
  227. if (in == out) {
  228. /* now empty */
  229. in = -1;
  230. }
  231. }
  232. return rlen;
  233. }
  234. public synchronized int available() throws IOException {
  235. //读满,或者第一次读取,则暂无字节可读取
  236. if(in < 0)
  237. return 0;
  238. else if(in == out)
  239. //读满,则重置范围
  240. return buffer.length;
  241. else if (in > out)
  242. //还在一圈范围内
  243. return in - out;
  244. else
  245. //读取一圈
  246. return in + buffer.length - out;
  247. }
  248. public void close() throws IOException {
  249. //标记当前读流关闭
  250. closedByReader = true;
  251. synchronized (this) {
  252. in = -1;
  253. }
  254. }
  255. }

//todo ObjectInputStream

OutputStream

  1. public abstract class OutputStream implements Closeable, Flushable {
  2. public abstract void write(int b) throws IOException;
  3. public void write(byte b[]) throws IOException {
  4. write(b, 0, b.length);
  5. }
  6. public void write(byte b[], int off, int len) throws IOException {
  7. //非空,上下界限校验
  8. if (b == null) {
  9. throw new NullPointerException();
  10. } else if ((off < 0) || (off > b.length) || (len < 0) ||
  11. ((off + len) > b.length) || ((off + len) < 0)) {
  12. throw new IndexOutOfBoundsException();
  13. } else if (len == 0) {
  14. return;
  15. }
  16. //一个字节一个字节写入
  17. for (int i = 0 ; i < len ; i++) {
  18. write(b[off + i]);
  19. }
  20. }
  21. //刷新此输出流并强制任何缓冲的输出字节被写出。
  22. public void flush() throws IOException {
  23. }
  24. //关闭
  25. public void close() throws IOException {
  26. }
  27. }

//todo FileOutputStream

BufferedOutputStream

Java IO中的管道为运行在同一个JVM中的两个线程提供了通信的能力。所以管道也可以作为数据源以及目标媒介

  1. public
  2. class BufferedOutputStream extends FilterOutputStream {
  3. //记录缓冲大小,默认8192
  4. protected byte buf[];
  5. //记录缓冲数据个数
  6. protected int count;
  7. public BufferedOutputStream(OutputStream out) {
  8. this(out, 8192);
  9. }
  10. public BufferedOutputStream(OutputStream out, int size) {
  11. //使用父类装饰outputStream
  12. super(out);
  13. if (size <= 0) {
  14. throw new IllegalArgumentException("Buffer size <= 0");
  15. }
  16. //创建缓冲数组,默认大小8192
  17. buf = new byte[size];
  18. }
  19. private void flushBuffer() throws IOException {
  20. //当缓冲数组有数据,则刷新到输出流中,重置数组长度
  21. if (count > 0) {
  22. out.write(buf, 0, count);
  23. count = 0;
  24. }
  25. }
  26. public synchronized void write(int b) throws IOException {
  27. //写入长度超过缓冲数组长度,则刷新一遍
  28. if (count >= buf.length) {
  29. flushBuffer();
  30. }
  31. //记录
  32. buf[count++] = (byte)b;
  33. }
  34. public synchronized void write(byte b[], int off, int len) throws IOException {
  35. //写入长度,超过缓冲数组长度,则刷新一遍,写入输出流
  36. if (len >= buf.length) {
  37. flushBuffer();
  38. out.write(b, off, len);
  39. return;
  40. }
  41. //写入长度,超过余下缓冲数组长度,刷新输出流一次
  42. if (len > buf.length - count) {
  43. flushBuffer();
  44. }
  45. //写入缓冲数组
  46. System.arraycopy(b, off, buf, count, len);
  47. ///累加数组记录字节数
  48. count += len;
  49. }
  50. public synchronized void flush() throws IOException {
  51. //缓冲数据写入输出流
  52. flushBuffer();
  53. //输出流刷新
  54. out.flush();
  55. }
  56. }

PipedOutputStream

  1. public
  2. class PipedOutputStream extends OutputStream {
  3. private PipedInputStream sink;
  4. public PipedOutputStream(PipedInputStream snk) throws IOException {
  5. //管道连接
  6. connect(snk);
  7. }
  8. public PipedOutputStream() {
  9. }
  10. public synchronized void connect(PipedInputStream snk) throws IOException {
  11. if (snk == null) {
  12. throw new NullPointerException();
  13. } else if (sink != null || snk.connected) {
  14. throw new IOException("Already connected");
  15. }
  16. sink = snk;
  17. //标记初次写入
  18. snk.in = -1;
  19. //读取起始位置
  20. snk.out = 0;
  21. //标记状态为连接
  22. snk.connected = true;
  23. }
  24. public void write(int b) throws IOException {
  25. if (sink == null) {
  26. throw new IOException("Pipe not connected");
  27. }
  28. //往input写入数据
  29. sink.receive(b);
  30. }
  31. public void write(byte b[], int off, int len) throws IOException {
  32. if (sink == null) {
  33. throw new IOException("Pipe not connected");
  34. } else if (b == null) {
  35. throw new NullPointerException();
  36. } else if ((off < 0) || (off > b.length) || (len < 0) ||
  37. ((off + len) > b.length) || ((off + len) < 0)) {
  38. throw new IndexOutOfBoundsException();
  39. } else if (len == 0) {
  40. return;
  41. }
  42. //往input写入指定范围数据
  43. sink.receive(b, off, len);
  44. }
  45. //强制唤醒所有阻塞状态
  46. public synchronized void flush() throws IOException {
  47. if (sink != null) {
  48. synchronized (sink) {
  49. //通知其他
  50. sink.notifyAll();
  51. }
  52. }
  53. }
  54. public void close() throws IOException {
  55. //标记当前管道已经关闭
  56. if (sink != null) {
  57. sink.receivedLast();
  58. }
  59. }
  60. }

//todo ObjectOutputStream

字符流

Reader

  1. public abstract class Reader implements Readable, Closeable {
  2. /**
  3. * The object used to synchronize operations on this stream. For
  4. * efficiency, a character-stream object may use an object other than
  5. * itself to protect critical sections. A subclass should therefore use
  6. * the object in this field rather than <tt>this</tt> or a synchronized
  7. * method.
  8. */
  9. //锁,默认当前对象
  10. protected Object lock;
  11. /**
  12. * Creates a new character-stream reader whose critical sections will
  13. * synchronize on the reader itself.
  14. */
  15. protected Reader() {
  16. this.lock = this;
  17. }
  18. /**
  19. * Creates a new character-stream reader whose critical sections will
  20. * synchronize on the given object.
  21. *
  22. * @param lock The Object to synchronize on.
  23. */
  24. protected Reader(Object lock) {
  25. if (lock == null) {
  26. throw new NullPointerException();
  27. }
  28. this.lock = lock;
  29. }
  30. /**
  31. * Attempts to read characters into the specified character buffer.
  32. * The buffer is used as a repository of characters as-is: the only
  33. * changes made are the results of a put operation. No flipping or
  34. * rewinding of the buffer is performed.
  35. *
  36. * @param target the buffer to read characters into
  37. * @return The number of characters added to the buffer, or
  38. * -1 if this source of characters is at its end
  39. * @throws IOException if an I/O error occurs
  40. * @throws NullPointerException if target is null
  41. * @throws java.nio.ReadOnlyBufferException if target is a read only buffer
  42. * @since 1.5
  43. */
  44. public int read(java.nio.CharBuffer target) throws IOException {
  45. int len = target.remaining();
  46. char[] cbuf = new char[len];
  47. int n = read(cbuf, 0, len);
  48. if (n > 0)
  49. target.put(cbuf, 0, n);
  50. return n;
  51. }
  52. /**
  53. * Reads a single character. This method will block until a character is
  54. * available, an I/O error occurs, or the end of the stream is reached.
  55. *
  56. * <p> Subclasses that intend to support efficient single-character input
  57. * should override this method.
  58. *
  59. * @return The character read, as an integer in the range 0 to 65535
  60. * (<tt>0x00-0xffff</tt>), or -1 if the end of the stream has
  61. * been reached
  62. *
  63. * @exception IOException If an I/O error occurs
  64. */
  65. public int read() throws IOException {
  66. //创建容量为一字符的数组
  67. char cb[] = new char[1];
  68. //读取一个字符
  69. if (read(cb, 0, 1) == -1)
  70. return -1;
  71. else
  72. return cb[0];
  73. }
  74. /**
  75. * Reads characters into an array. This method will block until some input
  76. * is available, an I/O error occurs, or the end of the stream is reached.
  77. *
  78. * @param cbuf Destination buffer
  79. *
  80. * @return The number of characters read, or -1
  81. * if the end of the stream
  82. * has been reached
  83. *
  84. * @exception IOException If an I/O error occurs
  85. */
  86. public int read(char cbuf[]) throws IOException {
  87. return read(cbuf, 0, cbuf.length);
  88. }
  89. /**
  90. * Reads characters into a portion of an array. This method will block
  91. * until some input is available, an I/O error occurs, or the end of the
  92. * stream is reached.
  93. *
  94. * @param cbuf Destination buffer
  95. * @param off Offset at which to start storing characters
  96. * @param len Maximum number of characters to read
  97. *
  98. * @return The number of characters read, or -1 if the end of the
  99. * stream has been reached
  100. *
  101. * @exception IOException If an I/O error occurs
  102. */
  103. abstract public int read(char cbuf[], int off, int len) throws IOException;
  104. /** Maximum skip-buffer size */
  105. private static final int maxSkipBufferSize = 8192;
  106. /** Skip buffer, null until allocated */
  107. private char skipBuffer[] = null;
  108. public long skip(long n) throws IOException {
  109. if (n < 0L)
  110. throw new IllegalArgumentException("skip value is negative");
  111. //默认最大只能跳过8192字节
  112. int nn = (int) Math.min(n, maxSkipBufferSize);
  113. synchronized (lock) {
  114. //新建记录跳过的字符
  115. if ((skipBuffer == null) || (skipBuffer.length < nn))
  116. skipBuffer = new char[nn];
  117. long r = n;
  118. while (r > 0) {
  119. int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
  120. if (nc == -1)
  121. break;
  122. r -= nc;
  123. }
  124. return n - r;
  125. }
  126. }
  127. /**
  128. * Tells whether this stream is ready to be read.
  129. *
  130. * @return True if the next read() is guaranteed not to block for input,
  131. * false otherwise. Note that returning false does not guarantee that the
  132. * next read will block.
  133. *
  134. * @exception IOException If an I/O error occurs
  135. */
  136. //告诉这个流是否准备好被读取。
  137. public boolean ready() throws IOException {
  138. return false;
  139. }
  140. /**
  141. * Tells whether this stream supports the mark() operation. The default
  142. * implementation always returns false. Subclasses should override this
  143. * method.
  144. *
  145. * @return true if and only if this stream supports the mark operation.
  146. */
  147. //不支持标记
  148. public boolean markSupported() {
  149. return false;
  150. }
  151. /**
  152. * Marks the present position in the stream. Subsequent calls to reset()
  153. * will attempt to reposition the stream to this point. Not all
  154. * character-input streams support the mark() operation.
  155. *
  156. * @param readAheadLimit Limit on the number of characters that may be
  157. * read while still preserving the mark. After
  158. * reading this many characters, attempting to
  159. * reset the stream may fail.
  160. *
  161. * @exception IOException If the stream does not support mark(),
  162. * or if some other I/O error occurs
  163. */
  164. public void mark(int readAheadLimit) throws IOException {
  165. throw new IOException("mark() not supported");
  166. }
  167. /**
  168. * Resets the stream. If the stream has been marked, then attempt to
  169. * reposition it at the mark. If the stream has not been marked, then
  170. * attempt to reset it in some way appropriate to the particular stream,
  171. * for example by repositioning it to its starting point. Not all
  172. * character-input streams support the reset() operation, and some support
  173. * reset() without supporting mark().
  174. *
  175. * @exception IOException If the stream has not been marked,
  176. * or if the mark has been invalidated,
  177. * or if the stream does not support reset(),
  178. * or if some other I/O error occurs
  179. */
  180. public void reset() throws IOException {
  181. throw new IOException("reset() not supported");
  182. }
  183. /**
  184. * Closes the stream and releases any system resources associated with
  185. * it. Once the stream has been closed, further read(), ready(),
  186. * mark(), reset(), or skip() invocations will throw an IOException.
  187. * Closing a previously closed stream has no effect.
  188. *
  189. * @exception IOException If an I/O error occurs
  190. */
  191. abstract public void close() throws IOException;
  192. }

//todo FileReader

BufferedReader

  1. public class BufferedReader extends Reader {
  2. private Reader in;
  3. //缓冲区
  4. private char cb[];
  5. //缓冲区缓存数据个数
  6. private int nChars,
  7. //下一个字符的位置
  8. nextChar;
  9. private static final int INVALIDATED = -2;
  10. private static final int UNMARKED = -1;
  11. private int markedChar = UNMARKED;
  12. private int readAheadLimit = 0; /* Valid only when markedChar > 0 */
  13. /** If the next character is a line feed, skip it */
  14. private boolean skipLF = false;
  15. /** The skipLF flag when the mark was set */
  16. private boolean markedSkipLF = false;
  17. private static int defaultCharBufferSize = 8192;
  18. private static int defaultExpectedLineLength = 80;
  19. /**
  20. * Creates a buffering character-input stream that uses an input buffer of
  21. * the specified size.
  22. *
  23. * @param in A Reader
  24. * @param sz Input-buffer size
  25. *
  26. * @exception IllegalArgumentException If {@code sz <= 0}
  27. */
  28. public BufferedReader(Reader in, int sz) {
  29. //当前锁对象为in流
  30. super(in);
  31. if (sz <= 0)
  32. throw new IllegalArgumentException("Buffer size <= 0");
  33. this.in = in;
  34. //默认缓冲区大小8192
  35. cb = new char[sz];
  36. //数组总个数和下一个字符索引默认0
  37. nextChar = nChars = 0;
  38. }
  39. /**
  40. * Creates a buffering character-input stream that uses a default-sized
  41. * input buffer.
  42. *
  43. * @param in A Reader
  44. */
  45. public BufferedReader(Reader in) {
  46. this(in, defaultCharBufferSize);
  47. }
  48. /** Checks to make sure that the stream has not been closed */
  49. private void ensureOpen() throws IOException {
  50. if (in == null)
  51. throw new IOException("Stream closed");
  52. }
  53. private void fill() throws IOException {
  54. int dst;
  55. //没有设置标记点,则默认0开始读取
  56. if (markedChar <= UNMARKED) {
  57. /* No mark */
  58. dst = 0;
  59. } else {
  60. /* Marked */
  61. int delta = nextChar - markedChar;
  62. //超过标记限制,则清除标记状态
  63. //markedChar~readAheadLimit范围还在余下缓冲区空间中
  64. if (delta >= readAheadLimit) {
  65. /* Gone past read-ahead limit: Invalidate mark */
  66. markedChar = INVALIDATED;
  67. readAheadLimit = 0;
  68. dst = 0;
  69. } else {
  70. //markedChar右边数据移动到最左边,然后重置markedChar=0
  71. //markedChar~readAheadLimit余下缓冲区大小无法满足,但是数组最大容量可以满足
  72. if (readAheadLimit <= cb.length) {
  73. /* Shuffle in the current buffer */
  74. System.arraycopy(cb, markedChar, cb, 0, delta);
  75. markedChar = 0;
  76. dst = delta;
  77. } else {
  78. //数组最大容量满足不了,只好扩容到readAheadLimit大小了
  79. /* Reallocate buffer to accommodate read-ahead limit */
  80. //扩容,容量最大范围为readAheadLimit
  81. //markedChar右边数据移动到扩容数组的最左边,然后重置markedChar=0
  82. char ncb[] = new char[readAheadLimit];
  83. System.arraycopy(cb, markedChar, ncb, 0, delta);
  84. cb = ncb;
  85. markedChar = 0;
  86. dst = delta;
  87. }
  88. nextChar = nChars = delta;
  89. }
  90. }
  91. int n;
  92. //一口气读取到cb数组
  93. do {
  94. n = in.read(cb, dst, cb.length - dst);
  95. } while (n == 0);
  96. //读取到值
  97. if (n > 0) {
  98. //记录当前读取总个数
  99. nChars = dst + n;
  100. //记录读取的起始位置
  101. nextChar = dst;
  102. }
  103. }
  104. /**
  105. * Reads a single character.
  106. *
  107. * @return The character read, as an integer in the range
  108. * 0 to 65535 (<tt>0x00-0xffff</tt>), or -1 if the
  109. * end of the stream has been reached
  110. * @exception IOException If an I/O error occurs
  111. */
  112. public int read() throws IOException {
  113. synchronized (lock) {
  114. //判断是否开启
  115. ensureOpen();
  116. for (;;) {
  117. //当下一个字节超过当前缓冲区数据个数大小,则重新从0覆盖缓冲区数据
  118. if (nextChar >= nChars) {
  119. fill();
  120. //说明没读取到数据,则返回-1
  121. if (nextChar >= nChars)
  122. return -1;
  123. }
  124. //遇到换行
  125. if (skipLF) {
  126. skipLF = false;
  127. //下标移动到下一行
  128. if (cb[nextChar] == '\n') {
  129. nextChar++;
  130. continue;
  131. }
  132. }
  133. return cb[nextChar++];
  134. }
  135. }
  136. }
  137. /**
  138. * Reads characters into a portion of an array, reading from the underlying
  139. * stream if necessary.
  140. */
  141. private int read1(char[] cbuf, int off, int len) throws IOException {
  142. //老规矩,读到最后,则重新从0覆盖读取到缓冲区
  143. if (nextChar >= nChars) {
  144. /* If the requested length is at least as large as the buffer, and
  145. if there is no mark/reset activity, and if line feeds are not
  146. being skipped, do not bother to copy the characters into the
  147. local buffer. In this way buffered streams will cascade
  148. harmlessly. */
  149. //一次性读取的数据范围超过缓冲区大小,且尚未标记点,且没遇到换行,则直接从流中获取数据
  150. if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
  151. return in.read(cbuf, off, len);
  152. }
  153. fill();
  154. }
  155. //执行到这里,说明要么没越界,要么越界了但是没读到数据,则直接返回-1
  156. if (nextChar >= nChars) return -1;
  157. //执行到这里说明没越界
  158. if (skipLF) {
  159. //重置没换行
  160. skipLF = false;
  161. //换行,则下标+1,移动到下一行
  162. if (cb[nextChar] == '\n') {
  163. nextChar++;
  164. //如果移动下一行后,超过最大容量,则再次加载缓冲区
  165. if (nextChar >= nChars)
  166. fill();
  167. //如果加载缓冲区,没读到数据,则直接返回-1
  168. if (nextChar >= nChars)
  169. return -1;
  170. }
  171. }
  172. //一次获取的最大容量不超过缓冲区剩余大小
  173. int n = Math.min(len, nChars - nextChar);
  174. System.arraycopy(cb, nextChar, cbuf, off, n);
  175. nextChar += n;
  176. return n;
  177. }
  178. public int read(char cbuf[], int off, int len) throws IOException {
  179. synchronized (lock) {
  180. //检验流是否开启
  181. ensureOpen();
  182. //范围校验
  183. if ((off < 0) || (off > cbuf.length) || (len < 0) ||
  184. ((off + len) > cbuf.length) || ((off + len) < 0)) {
  185. throw new IndexOutOfBoundsException();
  186. } else if (len == 0) {
  187. return 0;
  188. }
  189. //获取数据
  190. int n = read1(cbuf, off, len);
  191. //没数据直接返回
  192. if (n <= 0) return n;
  193. //保证获取数据容量偏向于len
  194. while ((n < len) && in.ready()) {
  195. //再次从off+n开始获取数据
  196. int n1 = read1(cbuf, off + n, len - n);
  197. //没读取则跳出循环
  198. if (n1 <= 0) break;
  199. n += n1;
  200. }
  201. return n;
  202. }
  203. }
  204. /**
  205. * Reads a line of text. A line is considered to be terminated by any one
  206. * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
  207. * followed immediately by a linefeed.
  208. *
  209. * @param ignoreLF If true, the next '\n' will be skipped
  210. *
  211. * @return A String containing the contents of the line, not including
  212. * any line-termination characters, or null if the end of the
  213. * stream has been reached
  214. *
  215. * @see java.io.LineNumberReader#readLine()
  216. *
  217. * @exception IOException If an I/O error occurs
  218. */
  219. //ignoreLF=true 则跳过下一个\n
  220. String readLine(boolean ignoreLF) throws IOException {
  221. StringBuffer s = null;
  222. int startChar;
  223. synchronized (lock) {
  224. //校验流是否打开
  225. ensureOpen();
  226. //是否跳过\n或\r\n
  227. boolean omitLF = ignoreLF || skipLF;
  228. bufferLoop:
  229. for (;;) {
  230. if (nextChar >= nChars)
  231. //数据填充缓冲区
  232. fill();
  233. //越界,则直接返回
  234. if (nextChar >= nChars) {
  235. /* EOF */
  236. if (s != null && s.length() > 0)
  237. return s.toString();
  238. else
  239. return null;
  240. }
  241. boolean eol = false;
  242. char c = 0;
  243. //记录下一个\r or \n 下标
  244. int i;
  245. /* Skip a leftover '\n', if necessary */
  246. //跳过换行,并移动到下一行
  247. if (omitLF && (cb[nextChar] == '\n'))
  248. nextChar++;
  249. //执行到这一步,说明没换行,或者已经跳过换行,则重置状态
  250. skipLF = false;
  251. omitLF = false;
  252. //遍历查找到\r or \n下标赋值给i
  253. charLoop:
  254. for (i = nextChar; i < nChars; i++) {
  255. c = cb[i];
  256. if ((c == '\n') || (c == '\r')) {
  257. eol = true;
  258. break charLoop;
  259. }
  260. }
  261. //分别标记读取的数据的开始结束位置
  262. startChar = nextChar;
  263. nextChar = i;
  264. //遇到换行或者空行情况
  265. if (eol) {
  266. String str;
  267. if (s == null) {
  268. //s为空,则新建对象
  269. str = new String(cb, startChar, i - startChar);
  270. } else {
  271. //s有数据则累加
  272. s.append(cb, startChar, i - startChar);
  273. str = s.toString();
  274. }
  275. //因为换行了,所以自增,移动到下一行
  276. nextChar++;
  277. if (c == '\r') {
  278. //标记遇到换行
  279. skipLF = true;
  280. }
  281. return str;
  282. }
  283. //没遇到换行情况
  284. if (s == null)
  285. s = new StringBuffer(defaultExpectedLineLength);
  286. s.append(cb, startChar, i - startChar);
  287. }
  288. }
  289. }
  290. public String readLine() throws IOException {
  291. //不跳过换行
  292. return readLine(false);
  293. }
  294. public long skip(long n) throws IOException {
  295. if (n < 0L) {
  296. throw new IllegalArgumentException("skip value is negative");
  297. }
  298. synchronized (lock) {
  299. ensureOpen();
  300. long r = n;
  301. while (r > 0) {
  302. if (nextChar >= nChars)
  303. fill();
  304. if (nextChar >= nChars) /* EOF */
  305. break;
  306. if (skipLF) {
  307. skipLF = false;
  308. if (cb[nextChar] == '\n') {
  309. nextChar++;
  310. }
  311. }
  312. long d = nChars - nextChar;
  313. if (r <= d) {
  314. nextChar += r;
  315. r = 0;
  316. break;
  317. }
  318. else {
  319. r -= d;
  320. nextChar = nChars;
  321. }
  322. }
  323. return n - r;
  324. }
  325. }
  326. public boolean ready() throws IOException {
  327. synchronized (lock) {
  328. ensureOpen();
  329. if (skipLF) {
  330. /* Note that in.ready() will return true if and only if the next
  331. * read on the stream will not block.
  332. */
  333. //越界且同时流准备好,再次加载
  334. if (nextChar >= nChars && in.ready()) {
  335. fill();
  336. }
  337. //针对下一个下标换行的处理
  338. if (nextChar < nChars) {
  339. if (cb[nextChar] == '\n')
  340. nextChar++;
  341. skipLF = false;
  342. }
  343. }
  344. return (nextChar < nChars) || in.ready();
  345. }
  346. }
  347. /**
  348. * Tells whether this stream supports the mark() operation, which it does.
  349. */
  350. public boolean markSupported() {
  351. return true;
  352. }
  353. public void mark(int readAheadLimit) throws IOException {
  354. if (readAheadLimit < 0) {
  355. throw new IllegalArgumentException("Read-ahead limit < 0");
  356. }
  357. synchronized (lock) {
  358. //检查流是否开启
  359. ensureOpen();
  360. this.readAheadLimit = readAheadLimit;
  361. markedChar = nextChar;
  362. markedSkipLF = skipLF;
  363. }
  364. }
  365. public void reset() throws IOException {
  366. synchronized (lock) {
  367. ensureOpen();
  368. if (markedChar < 0)
  369. throw new IOException((markedChar == INVALIDATED)
  370. ? "Mark invalid"
  371. : "Stream not marked");
  372. nextChar = markedChar;
  373. skipLF = markedSkipLF;
  374. }
  375. }
  376. public void close() throws IOException {
  377. synchronized (lock) {
  378. if (in == null)
  379. return;
  380. try {
  381. //关闭流
  382. in.close();
  383. } finally {
  384. //释放流和缓冲区GC
  385. in = null;
  386. cb = null;
  387. }
  388. }
  389. }
  390. public Stream<String> lines() {
  391. Iterator<String> iter = new Iterator<String>() {
  392. String nextLine = null;
  393. @Override
  394. public boolean hasNext() {
  395. if (nextLine != null) {
  396. return true;
  397. } else {
  398. try {
  399. nextLine = readLine();
  400. return (nextLine != null);
  401. } catch (IOException e) {
  402. throw new UncheckedIOException(e);
  403. }
  404. }
  405. }
  406. @Override
  407. public String next() {
  408. if (nextLine != null || hasNext()) {
  409. String line = nextLine;
  410. nextLine = null;
  411. return line;
  412. } else {
  413. throw new NoSuchElementException();
  414. }
  415. }
  416. };
  417. return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
  418. iter, Spliterator.ORDERED | Spliterator.NONNULL), false);
  419. }
  420. }

Writer

//todo FileWriter

//todo BufferWriter

转换流

//todo InputStreamReader

//todo OutputStreamWriter

发表评论

表情:
评论列表 (有 0 条评论,97人围观)

还没有评论,来说两句吧...

相关阅读