Java的流其实总的可以划分为两种:字符流和字节流,如下图
简介:本文基于Java核心技术卷二,进行整理和总结对Java的部分流库
- 输入 / 输出流(I/O流) ——主要针对一些特别大的文件或二进制文件
- 操作文件 ——主要针对一些长度中小的文件
1. 输入 / 输出流——面向字节流
输入流:可以从其中读入一个字节序列的对象称为输入流
输出流:可以向其中写入一个字节序列的对象称为输出流
而注意这些字节序列的来源地和目的地可以是文件(一般都是文件),网络连接,内存块等;
抽象类 InputStream 和 OutputStream 构成了输入/输出(I/O)类层次结构的基础
-
为什么首先强调InputStream 和 OutputStream?
就在于InputStream 和 OutputStream 是之后介绍的比如FileInputStream、FilterInputStream 等其他API的 父类,一张图来解释,OutputStream与下面几乎一致
1.1— InputStream具体介绍(OutputStream拥有前四个和一个void flush()(用于冲刷输出流,即将所有缓冲的数据发送到目的地))
java.io.InputStream
函数
介绍
abstract void read()
从数据中读入一个字节,并返回该字节,但碰到文件结尾的时候返回-1,
注意此方法是一个抽象方法,即他的子类继承InputStream时都会重写这个方法
int read (byte[] b)
将数据写入b这个byte数组,并返回实际读入的字节数,遇到文件结尾返回-1
这个方法最多读入b.length个字节
int read (byte[] b,int off , int len)
同上,参数off : 将读入的第一个数据放入b[off] 中,即off是一个偏移量
参数len : 读入字节的最大数量
void close ()
关闭输入流
long skip (long n )
在输入流中跳过n个字节,并返回实际跳过的字节数,遇到文件结尾,
返回结果可能小于n
void mark (int readlimit)
在输入流当前位置打一个标记
void reset ( )
返回到最后一个标记,在调用read会重新读入标记之后的字节
boolean markSupported()
如果这个流支持打标记,则返回true
-
可以看出,InputStream 和 OutputStream类只能支持对字节或者字节数组的读写,但我们在编程中一般用字符串和数字,它的子类DataInputStream 和 DateOutputStream就可以实现以二进制格式读写几乎所有基本Java类型!
注意:这里InputStream 和 OutputStream包括其子类主要面向字节流,不支持Unicode形式存储的信息,所以从抽象类reader 和 writer继承出一些子类来处理。何为Unicode可以自行百度
1.2—组合输入/输出流过滤器
FileInputStream 和 FileOutputStream类(见上图中的部分流家族)可以提供一个附着在磁盘文件上的输入流和输出流,如下代码
FileInputStream fin = new FileInputStream("data.txt");
与他们的父类—抽象类InputStream 和 OutputStream类一样,这些类只支持在字节级别上的读写,也就是说,我们只能从上述代码中 fin对象中读入字节或字节数组,前面强调了,输入输出流是面向字节的嘛
-
问题来了,既然这些类是面向字节流的,而且之前说过DataInputStream可以实现以二进制格式读写几乎所有基本Java类型,但他们都没有任何直接的方法可以实现直接读写啊,怎么办?正如本节题目:组合输入/输出过滤器——Java就提供了这样一种灵巧的机制分离这两种职责 ——读写字节 和 读写数据,如下代码
FileInputStream fin = new FileInputStream("data.txt");
DataInputStream din = new DataInputStream(fin);
din.read_double();
//read你想要的基本数据类型!
-
具体什么意思呢?就是我得先将文件中的数据以字节流的形式存放到一个输入流对象fin中,然后我再利用他的子类DataInputStream中的一些方法 将该输入流转换组装成我们常用的数据类型!nice!
注意:
- 这里我们可以自由嵌套过滤器来实现多重功能,比如输入流在默认情况下是不支持缓冲区缓存的,也就是每次read调用都会向操作系统进行请求调用,看起来比较繁琐,因此我们找来了部分流家族中的BufferedInputStream这个类,可以实现请求一个数据块放在缓冲区,等读满了,我在一次性的进行写入或读出,效率比较高,代码如下
DataInputStream din = new DataInputStream( new BufferedInputStream( new FileInputStream("data.txt"))); din.readDouble(); //read你想要的基本数据类型!
- 还有就是当有多个输入流嵌套组合的时候,有时候你想预览下一个字节是否是你想要的值,Java就提供了这样一个子类PushbackInputStream,可以预读下一个字节,并且如果不想要可以直接推回流中,以下代码便实现了既可以回推输入流,又可以读入数字的组合过滤器
2.读写文件
Files类可以使得普通文件操作变得十分快捷。例下面的方式可以很容易的读取一个文件的所有内容
byte[] bytes = Files.readAllBytes(path);
如果想将文件以字符串的形式读入,可以在执行完上述语句之后调用下列语句
String s = new String(bytes, charset);
如果想将文件以行序列读入,可以调用
List<String> l = Files.readAllLines(path,charset);
如果想将字符串写入文件中,可以调用
Files.write(path, l.getBytes(charset));
//charset一般为utf-8
Files.write(path, l.getBytes(charset),StandardOpenOption.APPEND);
//最后一个参数是以追加的形式
但是注意,这些方法在操作比较短小的文件的时候效率比较高,一旦操作大的文件或者二进制文件,就需要使用之前介绍的输入输出流或者读入写出器来进行:
InputStream is = Files.newInputStream(path);
OutputStream os = Files.newOutputStream(path);
Reader in = Files.newBufferedReader(path, charset);
Writer in = Files.newBufferedWriter(path, charset);