IO简介
数据流是一组有序,有起点和终点的字节的数据序列。包括输入流和输出流。
流序列中的数据既可以是未经加工的原始二进制数据,也可以是经一定编码处理后符合某种格式规定的特定数据。因
此Java中的流分为两种: 1) 字节流:数据流中最小的数据单元是字节 2) 字符流:数据流中最小的数据单元是字符,
Java中的字符是Unicode编码,一个字符占用两个字节。
Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;
一个接口指的是Serializable。掌握了这些就掌握了Java I/O的精髓了。
Java I/O主要包括如下3层次:
- 流式部分——最主要的部分。如:OutputStream、InputStream、Writer、Reader等 2. 非流式部分——如:File类、RandomAccessFile类和FileDescriptor等类
- 其他——文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操作系统相关的文件系
统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。
IO详细介绍
在Android 平台,从应用的角度出发,我们最需要关注和研究的就是 字节流(Stream)字符流(Reader/Writer) 和 File/ RandomAccessFile。当我们需要的时候再深入研究也未尝不是一件好事。
关于字符和字节,例如文本文件,XML这些都是用字符流来读取和写入。
而如RAR,EXE文件,图片等非文本,则用字节流来读取和写入。面对如此复杂的类关系,有一个点是我们必须要首先掌握的,那就是设计模式中的装饰模式,学会并理解装饰模式是搞懂流必备的前提条件。
以上都是废话,暂时没有那么大的精力从最基础开始讲起,本文主要是记录一下要点,方便回忆。
这是一张字节流的总结图,可以看出有out就有int。
write和read是从内存的角度来讲,out:从内存里写到文件,read:从文件里写入内存。
将一个文件读取到内存我们一般都是:
使用FilterInputString,然后再用BufferedInPutString。
FilterInputString是对InputString+传入一个文件的包装,FileInputString实现了IntputString。
内部都是装饰模式,其实说到底还是使用IntputString去工作。
BufferedInputString是为了提升速度,一次读取一个字节和一次读取100个字节,当然后者更快,至于它一次读取多少字节其实没有必要深究,除非特殊需求,你设置太大你的CPU寄存器也受不了。
当初开始学IO流的时候记得老师从最基础的自己实现一个类似Buffered缓存,然后具体演示比较了跟JDK提供的buffered的区别,当初好像是一次1m,差不多是这个缓存,总之一句话,没特殊需求直接用JDK的就行。
反之,有int肯定有out,看看上面那张图都是流的读取输出都是成对出现的,这是最常用的,其他几个有一定了解就行,反之用法也是一样。
DataOutputStream这个也是一样,可以根据数据的类型输出读取,这个类型指的是java的基本类型。
字符流
首先从上图我们也可以看到字符流的读写也是成对地出现,字符流我们用得比较少,一般都是使用字节流,因为我们一般读取png exe json jar xml等等都只需要用到字节流,那么为什么还要有字符流呢?
我们知道英文是一个byte8位就可以完全表达了,但是中文不行,它需要用到一个字符来表达,同时使用字符还会涉及到编码问题,字符流的读取往往还需要传入编码格式。
字符它也是使用装饰模式,例如read,底层一个read,FilterRead实现了,同时自身声明为抽象流,作为基础组件被使用。
整个一个装饰模式的使用过程大概是这样的:
Writer- >FilterWriter->BufferedWriter->OutputStreamWriter->FileWriter->其他
完整的创建过程:
BufferedWriter bufferedWriter = BufferedWriter(new OutputStringWrier(new FileOutputString(new File(“xxxx.txt”),“GBK”))
实际上new OutputStringWrier(new FileOutputString(new File(“xxxx.txt”) = FileWriter(“xxxx.txt”);
因为FileWriter继承使用的是FileOutputString,直接代替这个OutputStringWrier给Buffered
还是装饰模式的运用。
flush方法:强制IO,当我们用buffered缓存区写入的时候,如果装满缓冲去会自动写入到目的文件当中,当最后一次不满缓存区的时候,调用flush会进行强制不用等直接写入。
当然这是字节流需要,字符流的bufferedWrite是不用的,因为close方法会自动帮你调用flush
buffered加快速速度的原理:读的时候一次读1000字节,然后回来写入内存,和一次读一次回来写一次的区别:IO操作都是堵塞操作,当你进行读写的时候其实让出了CPU的执行权,等待读写完毕重新进入就绪状态。
让出CPU执行权涉及CPU轮转概念,如果有对并发不了解的朋友可以看:
读写一次1kb和1000kb,减少磁盘缓冲头的调用,这也是优化。
RandomAccessFile的使用
//rw表示我可以写也可以读
RandomAccessFile rsfWriter = new RandomAccessFile(“xxx.txt”,“rw”);
//不改变文件大小,意思是前面是空的,跟空格不一样,空格可是有实际意义存在。
//可以理解将光标移动1000,下一个将从1001开始读写。文件的长度依然是0
rsfWriter.seek(1000); //真正意义是从某个位置开始读写
//设置了文件的长度,文件的长度就变成1000了
rsfWriter.setLength(1000); //给前面的位置预留控件
rsfWriter.wroterXXX();//使用对应的数据类型方法写数据进去
应用场景:网络数据断点续传
NIO:FileChannel
使用了ByteBuffered去工作,更底层的源码看不到了,android里面使用的非常少,有兴趣的朋友可以自行去了解,作用:速度更快。