结合源码介绍IO体系框架

一、IO框架的概括

    初学Java的IO框架,感觉这个体系很庞大,总是理不清头绪。随着学习的不断深入,加之阅读一些IO源代码,对IO框架的整体有了新的认识。首先,理解两个概念输入流和输出流。所谓流,就是数据的有序排列,而流是可以是从某个源(Source of Stream)出来,流向个目的地(Sink of Stream)。根据流的方向,可以分成输入流和输出流,一个程序从输入流读取数据向输出流写数据。根据数据类型输入流有可分为字节输入流和字符输入流,输出流分为字节输出流和字符输出流,如下图。

IO体系中四大基本类(抽象类):InputStream,OutputStream,Reader,Writer。InputStream和OutputStream处理8为字节的输入和输出,Reader和Writer处理16位字符(char型)的输入和输出。

二、IO框架的详细说明

1、字节流体系结构

    InputStream和OutputStream是两个抽象类,从源码中可以看出,类中只是简单的定义了基本的读写方法和关闭流方法。最重要的两个读写方法read()和Writer方法分别是类中唯一的抽象方法,是其子类必须实现的方法。其中FileInputStream中的read()方法和FileOutputStream中的Writer()方法是native本地方法,真正从磁盘读取数据和往磁盘写数据的正是这两个native方法。无论是字符流还是字节流,无论中间经历的多么复杂的过程,数据源如果是磁盘,最终调用的是native read(),数据目的地是磁盘最终调用native Write()方法。因为Java 是运行在JVM之上的,不能直接与底层硬件和OS交互,只能通过C/C++等语言编写的native方法实现交互。native方法就像是JVM和磁盘之间一个接口,要想对磁盘进行IO操作,程序必须链接到此接口。
    ByteArrayStream(ByteArrayInputStreamByteArrayOutputStream简写)的数据源和数据宿都是内存,其中就没有native方法,所以调用close()方法是没作用的。此类继承自InputStream和OutputStream,但只是简单重写父类的所有方法,用流的思想去操作数组。但是作为字节流的子类,是可以链接在其他字节流上的,比如BufferedInputStream。因为此类操作内存数据的,ByteArrayInputStream部分源码如下:
public class ByteArrayInputStream extends InputStream {

    //数据源是数组
    public ByteArrayInputStream(byte buf[]) {
        this.buf = buf;
        this.pos = 0;
        this.count = buf.length;
    }
    //利用数组指针  逐一读取数组数据
    public synchronized int read() {
        return (pos < count) ? (buf[pos++] & 0xff) : -1;
    }
    //没有任何语句 空函数
    public void close() throws IOException {
    }

}
可以把ByteArrayStream简单的理解成操作字节数组的工具类
IO架构整体设计利用了decorator模式,以FilterInputStream为例,部分源码如下:
public class FilterInputStream extends InputStream {
    
    //The input stream to be filtered.
     
    protected volatile InputStream in;

    protected FilterInputStream(InputStream in) {
        this.in = in;
    }
	//	//调用父类的方法
    public int read() throws IOException {
        return in.read();
    }
    public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }
    public int read(byte b[], int off, int len) throws IOException {
        return in.read(b, off, len);
    }
    public long skip(long n) throws IOException {
        return in.skip(n);
    }
FilterInputStream继承自InputStream,然后又引用的InputStream,这是典型的decorator模式。
从源码中可以看出,此类简单重现父类所有方法,调用的都是父类方法。
而真正负责装饰InputStream的是 FilterInputStream的子类BufferedInputStream、DataInputStream、LineNumberInputStream等。
装饰者类必须接受另外一个流对象(被装饰者)作为源。源经过装饰者后会具备一些额外的增强功能。
BufferedInputStream为输入流提供一个内存缓冲区,解决了每次要用数据的时候都要进行物理读取的问题。
DataInputStream:和DataOutputStream配合使用,实现了直接对java基本数据类型IO操作。
LineNumberInputStream(已弃用):跟踪输入流的行号;有getLineNumber( )和setLineNumber(int)方法。
看一下DataStream怎么进行装饰的:
 //DataOutputStream
  //把Boolean型转换成数字1、0保存到文件
    public final void writeBoolean(boolean v) throws IOException {
        out.write(v ? 1 : 0);
        incCount(1);
    }
<span style="white-space:pre">	</span>//DataInputStream
	//读取时,再转换回来 1返回true,0返回false
	public final boolean readBoolean() throws IOException {
	int ch = in.read();
	if (ch < 0)
		throw new EOFException();
	return (ch != 0);
	}
很简单,就是把Boolean的true/false和1/0相互转换。
最后还有一个略显特殊的类RandomAccessFile,该类是Object的直接子类。从结构图上看,该类不属于IO体系,可能是因为这个类能同时进行文件的读写吧,既属于Input也属于Output不好划分啊。结果自己就傲娇的独立出来了。我们先来看一下这个类和InputStream、OutputStream有什么区别,还是看源码,源码能直接说明问题:
    /**
     * Although RandomAccessFile is not a subclass of
     * InputStream, this method behaves in exactly the same
     * way as the {@link InputStream#read()} method of InputStream.
	 */
    public int read() throws IOException {
        Object traceContext = IoTrace.fileReadBegin(path);
        int b = 0;
        try {
            b = read0();
        } finally {
            IoTrace.fileReadEnd(traceContext, b == -1 ? 0 : 1);
        }
        return b;
    }
	//native方法 读
    private native int read0() throws IOException;
	//native方法 写
	private native void write0(int b) throws IOException;
    虽然RandomAccessFile不是InputStream的子类,但是read()方法和InputStream要完成的功能是一样的,而且read()和write都是native方法。该类和IO体系中的类的最大区别就是可以读也可以写,而且有一个非常强大的seek()方法,能对文件的任意位置进行读写,也就是所谓的随机访问文件。既然不属于IO体系当然也不能使用装饰类,所以只能自己实现,实现了DataInput和DataOutput接口,具备了对基本数据类型进行读写的功能
2、字符流体系结构



理解了字节流框架,字符流也就非常简单了,无非就是把字节转换成了字符进行操作。先来看一下Reader的部分源码:

public abstract class Reader implements Readable, Closeable {
    public int read() throws IOException {
        char cb[] = new char[1];
        if (read(cb, 0, 1) == -1)
            return -1;
        else
            return cb[0];
    }
    public int read(char cbuf[]) throws IOException {
        return read(cbuf, 0, cbuf.length);
    }
	//最重要的方法,确实抽象方法
    abstract public int read(char cbuf[], int off, int len) throws IOException;

    Reader和Writer真的可以直接以字符为单位对磁盘进行读写吗?当然不可以,所有的文件在计算机中都是1001的二进制,哪有什么字符。构造InputStreamReader时要传入InputStream,说明读取文件的还是InputStream中的native read(),InputStreamReader的作用是把字节解码成字符,就是所谓的字节流通向字符流的桥梁。

import sun.nio.cs.StreamDecoder;

/**
 * An InputStreamReader is a bridge from byte streams to character streams: It
 * reads bytes and decodes them into characters using a specified charset. 
 */

public class InputStreamReader extends Reader {
   //负责把字节转换成字符
    private final StreamDecoder sd;
   //InputStream读取数据
    public InputStreamReader(InputStream in) {
        super(in);
        try {
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
        } catch (UnsupportedEncodingException e) {
            // The default encoding should always be available
            throw new Error(e);
        }
    }

    public int read() throws IOException {
        return sd.read();
    }

    public int read(char cbuf[], int offset, int length) throws IOException {
        return sd.read(cbuf, offset, length);
    }

    public boolean ready() throws IOException {
        return sd.ready();
    }

    public void close() throws IOException {
        sd.close();
    }
}
    这是InputStreamReader的全部代码,很简单,都是在调用StreamDecode类中方法,StreamDecode是负责把读取的字节数据解码成字符数据的关键类。这个类好像是java官方的内部类,源码包中没有这个类,不过在这里可以查看。总之一句话,Reader、Writer就是把InputStream、OutputStream又装饰了一下,可以让程序方便的直接对字符读写。
字符流整体框架结构和字节流框架差不多,有一点值得注意:为什么BufferedReader不是FilterReader的子类,怎么直接继承了Reader?很简单,Reader这个类本身就是装饰类(这句话对么??)。

我用了一个星期才把IO框架理解到了这个程度,智商捉急,如有不对的地方,还望有大神能指正。

最后说明一下,这个框架图我是参考的别人的,稍作完善,尊重版权。参考博客


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值