java进阶篇09、JavaIO及字节流字符流

163 阅读5分钟

一、前言:

1、在io操作中经常会看到如下所示这样的嵌套结构,如果需要理解这种嵌套结构,就需要知道各个类之间的层级与调用关系;io系统这些类之间的层级关系使用了装饰模式进行设计,与android中context家族成员使用了相同的设计模式;

DataOutputStream out = new DataOutputStream(
				            new BufferedOutputStream(
				                    new FileOutputStream(
				                            new File("src/testtxt/tataStreamTest.txt"))));

2、还有一个重要的点就是我们在理解output、input、write、read这些名词时,需要站在内存的角度上进行思考,output是指文件从内存中写出到硬盘或打印系统中,也就会对应写出操作write;而input操作指文件从硬盘读入到内存中,也就是对应read;

3、使用ObjectOutputStream进行操作的对象一定要实现Serializable接口;每个文件的文件头用来表示这是一个什么类型的文件以及文件重要信息,比如把一个ppt文件名称的后缀改成txt,那么我们用文本工具并打不开,因为文件头标识中显示这并不是一个txt文件,所以文本工具打不开;

4、io操作完之后一定要close,否则一定会造成内存泄漏;close的关闭顺序要注意,外层嵌套调用close时会将内层嵌套的也close;

5、flush解析:BufferedOutputStream提高性能的原理就是减少磁盘磁头的操作,比如使用byte[1024]进行操作,那么一次就操作1024个字节,如果某此操作不足1024字节怎么办?那么这时候flush方法就派上用场了,flush不管byte数组有多少个字节了,强制进行一次io操作;所以flush仅在BufferedoutputStream有效,在FileOutputStream中不需要使用flush,因为在FileOutputStream调用close时会主动调用flush;BufferedWrite进行字符流操作时,调用close时会自动调用flush,所以最后close的时候也不用手动再flush了,这是字符流和字节流在buffered相关使用的一个区别;

二、装饰模式

component:抽象构建接口
    concreteComponent:具体的构建接口,实现自component接口
    componentDecorator:所有装饰器的抽象父类,内部持有一个component的引用,需要传入一个实现了component的具体的类;
        concreteDecoratorA:具体的装饰器
        concreteDecoratorB:具体的装饰器
        concreteDecoratorC:具体的装饰器

android中context的家族成员设计就使用了典型的装饰模式,所以我们可以使用context家族来理解装饰模式,将Context比作component;将ContextImpl比作concreteComponent;而将ContextWrapper比作componentDecorator,将application比作concreteDecoratorA,将service比作concreteDecoratorB,将ContextThemeWrapper比作concreteDecoratorC;

三、IO中的装饰器模式

InputStream:抽象构建接口
    ByteArrayInputStream:具体的构建接口
    FileInputStream:具体的构建接口
    ObjectInputStream:具体的构建接口
    FilterInputStream:装饰器的抽象父类,构造函数中需要传入一个具体的构建接口作为参数,内部持有此具体构建接口;
        DataInputStream:具体的装饰
        BufferedInputStream:具体的装饰器
OutputStream:抽象构建接口
    ByteArrayOutputStream:具体的构建接口
    FileOutputStream:具体的构建接口
    ObjectOutputStream:具体的构建接口
    FilterOutputStream:装饰器的抽象父类,构造函数中需要传入一个具体的构建接口作为参数,内部持有此具体构建接口;
        DataOutputStream:具体的装饰
        BufferedOutputStream:具体的装饰器

理解了IO的装饰器模式,再看前言1中的嵌套结构就很清晰了,因为DataOutputStream和BufferedOutputStream是装饰器,需要传入一个具体的构建接口,那么这个FileOutputStream就充当了这个具体的构建接口;

那么为什么不直接使用FileOutputStream进行io读写呢,而是要用这种嵌套结构呢?因为DataOutputStream可以对特定类型的信息进行读入写出的操作,比如int string bool long等等;而BufferedOutputStream是为了提高读写的效率的;

四、IO体系

IO体系
    流式部分
        字节流
            InputStream
            OutputStream
        字符流
            Reader
            Writer
    非流式部分
        File
        RandomAccessFile
    其它

其中在第二部分介绍IO的装饰模式时已经将字节流的体系结构介绍了一遍,下面介绍一下字符流的体系;

Writer
    CharArrayWriter
    PipedWriter
    FilterWriter
    BufferedWriter
    OutputStreamWriter
        FileWriter
Reader
    CharArrayReader
    PipedReader
    FilterReader
    BufferedReader
    InputStreamReader
        FileReader

五、字节流字符流辨析

BufferedReader bufferedReader = new BufferedReader(
        new InputStreamReader(
                new FileInputStream(
                        new File("src/testtxt/BufferedReader.txt")
                )
        )
);
bufferedReader.read();

这是一段字节流和字符流混用的嵌套,为什么我们不直接用字节流FileInputStream去读,而是要嵌套两层字符流;因为我们很多时候读取的是字符,并不是字节,英文字符还好说,占一个字节,但是读中文还按照字节去读,就会乱套了; 最上面一层的BUfferedReader是为了提升效率,使用一个缓冲区;减少硬盘磁头的移动;

六、非流式部分

1、RandomAccessFile

RandomAccessFile raf = new RandomAccessFile(File file, String mode); 参数 mode 的值可选 "r":可读,"w" :可写,"rw":可读写; RandomAccessFile不属于是一个完全独立的类,它既可以读也可以写;还可以从指定位置读写

2、FileChannel

FileChannel配合着ByteBuffer,将读写的数据缓存到内存中,然后以批量/缓存的方式read/write,省去了非批量操作时的重复中间操作,操纵大文件时可以显著提高效率;其实这种方式读写和Stream以byte数组的形式效率上没有太大的区别;