Java I/O之InputStream和OutputStream(一)

版权声明:此文章为作者原创,转载需要经过作者同意! https://blog.csdn.net/JavaDad/article/details/81663408

InputStream:字节输入流的所有类的父类

OutputStream:字节输出流的所有类的父类

这里的输入和输出是针对内存说的,一般来说,向内存读入数据为输入,从内存读出为输出。

下边是对两个类的源码分析:

InputStream.java

package java.io;

/**
 * 字节输入流的所有类的超类
 * 1. Closeable:实现close方法,可以在try-with-resource中自动关闭资源
 */
public abstract class InputStream implements Closeable {

  // 在skip方法中使用
  private static final int MAX_SKIP_BUFFER_SIZE = 2048;

  /**
   * 1. 读取输入流数据的下一个字节,返回这个字节的int值,一个字节能表示的大小为0-255,
   *    所以这个方法的(正确)返回值范围是0-255。
   * 2. 如果该输入流到达末尾处,没有可用的字节来读取,那么返回-1。
   * 3. 该方法会阻塞,直到 1)有输入数据可用 2)到达流的末尾 3)有异常抛出。
   * 4. 子类必须实现该方法
   */
  public abstract int read() throws IOException;

  /**
   * 1. 读取输入流中若干(不是一个了,效率提升)字节并且将它们存储在缓冲数组中,返回实际读到的字节数量。
   * 2. 该方法同样会阻塞,直到 1)有输入数据可用 2)到达流的末尾 3)有异常抛出。
   * 3. 如果缓冲数组的长度为0,那么返回0;否则至少读取一个字节。
   *    如果读到流的末尾,返回读取的字节数。
   * 4. 第一个读取的字节存储在b[0],下一个存储在b[1],依次类推。读取到的字节数量最多等于b数组的长度。假设
   *    k是实际读取到的字节数量,这些字节将被存储到b[0]-b[k-1]中,b[k]-b[b.length-1]的元素不受影响。这里
   *    的不受影响是什么意思呢??
   */
  public int read(byte b[]) throws IOException {
      return read(b, 0, b.length);
  }

  /**
   * 1. 从输入流中读取len个字节到字节数组中,尽可能的读取len个字节,但是可能少于len个字节。返回实际读取到的字节数。
   *    如果len=0,那么没有数据可读,返回0。
   * 2. 第一个读取的字节存储在b[off]中,依次类推。最多读len个字节。
   */
  public int read(byte b[], int off, int len) throws IOException {
      // 如果缓冲数组为null,则抛出 NullPointerException
      if (b == null) {
          throw new NullPointerException();
      } else if (off < 0 || len < 0 || len > b.length - off) { 
          // 1. 如果数组存储数据的开始位置小于0,IndexOutOfBoundsException
          // 2. 如果设置的读取字节长度小于0,IndexOutOfBoundsException
          // 3. 如果数组偏移后的剩余长度不足len,IndexOutOfBoundsException
          throw new IndexOutOfBoundsException();
      } else if (len == 0) {
          // 如果len=0,返回0,什么也不做
          return 0;
      }

      int c = read();
      // 如果读到的字节为-1,说明到达流的末尾,返回-1
      if (c == -1) {
          return -1;
      }
      // 将读取的第一个字节存入b[off]
      b[off] = (byte)c;

      // i表示读取到的字节数
      int i = 1;
      try {
          for (; i < len ; i++) {
              c = read();
              // 读到流末尾处,终止循环
              if (c == -1) {
                  break;
              }
              b[off + i] = (byte)c;
          }
      } catch (IOException ee) {
      }
      return i;
  }

  // n 表示传入需要跳过的字节数
  // 返回实际跳过的字节数
  // 1. 当输入流的数据大于n时,且 n<MAX_SKIP_BUFFER_SIZE时,则一次就可跳过n
  // 2. 当输入流的数据大于n时,且 n>MAX_SKIP_BUFFER_SIZE时,则一次只能读取MAX_SKIP_BUFFER_SIZE个,最后一次读取
  //    n%MAX_SKIP_BUFFER_SIZE个
  // 3. 当输入流的数据小于n时,且 n<MAX_SKIP_BUFFER_SIZE时,则一次就可跳过整个输入流,不是n
  // 还有其它场景,原理大致一样
  public long skip(long n) throws IOException {

      // 记录每循环一次读取,剩余需要跳过的字节数
      long remaining = n;
      // 记录每次循环跳过的字节数
      int nr;

      // n 需要大于0,否则没有意义
      if (n <= 0) {
          return 0;
      }

      int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
      byte[] skipBuffer = new byte[size];
      while (remaining > 0) {
          nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
          if (nr < 0) {
              break;
          }
          remaining -= nr;
      }
      // 返回实际跳过的字节数,其实nr才是统计每次跳过的字节数的变量,原理是 remianing(最终的) = remaining(其实就是n)-(nr1+nr2+nr3+...),
      // 返回 n-remianing = nr1+nr2+nr3+...
      return n - remaining;
  }

  public int available() throws IOException {
      return 0;
  }

  public void close() throws IOException {}

  /**
   * mark和reset方法一起使用,mark表示标记某个reset恢复的位置
   */
  public synchronized void mark(int readlimit) {}

  public synchronized void reset() throws IOException {
      throw new IOException("mark/reset not supported");
  }
  
  /*
   * 是否支持mark和reset方法
   */
  public boolean markSupported() {
      return false;
  }
}

 OutputStream.java

package java.io;

/**
 * 字节输出流的超类,接收字节并将其发送到某个接收器
 * 实现两个重要的接口:
 * 1. Closeable:实现close方法,可以在try-with-resource中自动关闭资源
 * 2. Flushable:实现flush方法,可以操作底层刷新流
 */
public abstract class OutputStream implements Closeable, Flushable {
	
	/*
	 * 子类必须实现的方法
	 */
    public abstract void write(int b) throws IOException;

    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

    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]);
        }
    }

    public void flush() throws IOException {
    }

    public void close() throws IOException {
    }

}

猜你喜欢

转载自blog.csdn.net/JavaDad/article/details/81663408
今日推荐