Java InputStream 浅析

1 InputStream 子类结构

  • 下图是 InputStream 及其子类的结构图。可以看出 InputStream 是一个典型的装饰者模式的示例
  • InputStream:抽象的组件类,具体的组件类继承它,来实现不同的输入流功能
  • FilterInputStream:抽象装饰者类,具体装饰者类继承它,来对它包装的组件类,进行扩展
  • Java IO 的扩展:
    • 添加具体组件类,实现特定的输入流功能
    • 添加具体装饰类,达到保证组件类的目的
      在这里插入图片描述

1.1 InputStream 源码分析

  • inputStream抽象类功能
    • 定义读取一个字节 或 多个字节的 方法
    • 定义了 mark、reset 的方法,由具体子类实现 重复读取一段数据流的功能
    • 定义了关闭输入流的方法
    • 定义了返回剩余可读取字节的方法
    • 注: 只有读取一个字节的 read() 方法是抽象的,也就是说,具体类只要实现这一个方法即可,其它的可不实现
public abstract class InputStream implements Closeable {
   

    //返回下一个字节的 int 表示,如果输入流已经结束了,则返回 -1。此方法会一直阻塞到有值返回,或抛异常
	public abstract int read() throws IOException;
    //从输入流中读取一些字节放入 字节数组 b 中,返回值即是读取的真实字节数。此方法会一直阻塞到有值返回,或抛异常
    //如果 字节数组 b 的长度为 0,则不会读取任何字节,并且返回 0。如果输入流已经结束则返回 -1
    public int read(byte b[]) throws IOException {
   
        return read(b, 0, b.length);
    }
    //和 read(byte b[]) 方法的区别仅仅是,读出来的数据放入 字节数组的位置不一样。此方法是从 字节数组的 off 索引处开始存放字节。返回值即是读取的真实字节数
    //如果 字节数组 b 的长度为 0,则不会读取任何字节,并且返回 0。如果输入流已经结束则返回 -1
    public int read(byte b[], int off, int len) throws IOException {
   ...}

    //丢弃输入流中的一些字节,丢弃的个数为返回值。如果 n<=0,则不会操作流。
	public long skip(long n) throws IOException {
   ...}
    //返回输入流可以读取的字节数(不准确),由子类实现
    public int available() throws IOException {
   
        return 0;
    }
    //关闭输入流,并且释放和流相关的系统资源
    public void close() throws IOException {
   }
    //标记当前输入流的位置,这样之后调用 reset() 方法时,将会重新将流设置到该标记的位置。实现重复读取
    //如果 调用 mark() 方法后 又读取了超过 readlimit 字节,mark 将会失效
    public synchronized void mark(int readlimit) {
   }

    //重新将流设置到 mark() 方法标记的位置,如果 mark() 已经失效则报错
    public synchronized void reset() throws IOException {
   
        throw new IOException("mark/reset not supported");
    }
    //该输入流是否可以被 mark
    public boolean markSupported() {
   
        return false;
    }
    //skip 方法最多跳过的字节数,如果 skip 的参数值大于此值,则实际使用的是此值
    private static final int MAX_SKIP_BUFFER_SIZE = 2048;
}

2 InputStream 具体实现类源码分析

2.1 ByteArrayInputStream —— 字节输入流

  • 核心: buf[] 数组,这个数组即是数据流的 源
    • buf[] 数组,通过构造函数传入!
  • 字节流操作即是,操作数组的位置 pos。思想很简单
  • 基本所有操作都被 synchronized 修饰
public class ByteArrayInputStream extends InputStream {
   
    protected byte buf[];//输入流的源
    protected int pos;//输入流当前位置
    protected int mark = 0;//标记的位置
    protected int count;//流的最大位置
    //返回当前位置的字节,并 pos 加 1
    public synchronized int read() {
   
        return (pos < count) ? (buf[pos++] & 0xff) : -1;
    }
    //将流中的自己赋值到 字节数组 b 中,并更新流的位置
    public synchronized int read(byte b[], int off, int len) {
   ...}
    //跳过 n 个字节
    public synchronized long skip(long n) {
   }
    //返回还有多少字节(准确)
    public synchronized int available() {
   }
    public void mark(int readAheadLimit) {
   
        mark = pos;//标记当前流位置,以便后面的 reset
    }
    public synchronized void reset() {
   
        pos = mark;//重置流到 mark 位置
    }
    //因为 ByteArrayInputStream 流对其它资源无任何影响,所以不用任何操作
    public void close() throws IOException {
   }
    
    //用 buf[] 数组初始化流
    public ByteArrayInputStream(byte buf[]) {
   
        this.buf = buf;
        this.pos = 0;
        this.count = buf.length;
    }
    //用 buf[] 数组的 offset-length 阶段初始化流
    public ByteArrayInputStream(byte buf[], int offset, int length) {
   
        this.buf = buf;
        this.pos = offset;
        this.count = Math.min(offset + length, buf.length);
        this.mark = offset;
    }
}

2.2 FileInputStream —— 文件输入流

  • 核心: file、fileDescriptor
    • 通过构造函数 传入 fileName 或 File 类,内部创建一个 文件链接
  • 核心功能实现方法(本地方法):
    • read0():获取下一个字节
    • readBytes(byte b[], int off, int len):从偏移量 off 开始,读取 len 个字节,放入 b[] 数组中
    • skip0(long n):跳过 n 个字节
    • available0():还剩多少字节
    • close0():关闭文件流
  • 方法没有被 synchronized 修饰,因为最终调用的是本地方法,本地方法应该加了手段
public class FileInputStream extends InputStream {
   
    
    /* File Descriptor - handle to the open file */
    private final FileDescriptor fd;
    // 文件的路径
    private final String path;
    //适配 java nio
    private FileChannel channel = null;

    private final Object closeLock = new Object();//保证只有一个线程调用 close 方法
    private volatile boolean closed = false;// double check 用途
    
    //通过打开一个与实际文件的连接,来创建一个 文件流。同时会创建一个 fileDescriptor 来标识这个连接。如果 file 不存在或是一个文件夹,抛异常。
    //如果存在 security manager 则会调用 security.checkRead(name) 方法验证
    public 
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值