整理了一些关于Java IO的知识点

Java的IO

流部分

流序列中的数据既可以是未经加工的原始二进制数据,也可以是经一定编码处理后符合某种格式规定的特定数据。因此Java中的流分为两种: 1) 字节流:数据流中最小的数据单元是字节 2) 字符流:数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

java I/O 的设计使用到了 Decorator(装饰器)模式,按功能划分Stream,您可以动态装配这些 Stream,以便获得您需要的功能。例如,您需要一个具有缓冲的文件输入流,则应当组合使用FileInputStream和BufferedInputStream。

关于缓冲区

应用程序每次IO都要和设备进行通信,效率很低,因此缓冲区为了提高效率,当写入设备时,先写入缓冲区,每次等到缓冲区满了时,就将数据一次性整体写入设备,避免了每一个数据都和IO进行一次交互,IO交互消耗太大。缓冲区默认是8192字节,在使用结束时,需要使用flush()和close()方法将还没有输出的缓冲区内容输出。

先来看看最基本的两个类

InputStream

public int read(byte[] b,int off,int len)throws IOException
//将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。

OutputStream

public int public void write(byte[] b,int off,int len) throws IOException
//将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。write(b, off, len) 的常规协定是:将数组 b 中的某些字节按顺序写入输出流;元素 b[off] 是此操作写入的第一个字节,b[off+len-1] 是此操作写入的最后一个字节

关于字节流各个子类的使用(参考Java API)

看几个比较常用的

BufferedInputStream

继承了FileInputStream类

构造函数

//构造函数
public BufferedInputStream(InputStream in) //以输入流in创建一个具有缓冲区功能的输入流
read(byte[] b, int off, int len)  //从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中。返回信息:下一个数据字节,如果到达流末尾,则返回 -1

FileInputStream

继承了InputStream类

构造函数

public FileInputStream(File file) throws FileNotFoundException
//通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。创建一个新 FileDescriptor 对象来表示此文件连接。
public FileInputStream(String name) //使用name字符串创建新的文件输入流

FileOutputStream

public FileOutputStream(File file,boolean append) //创建一个新的文件输出流,如果append参数为true,数据添加到文件尾,而且具有相同名字的已有文件不会被删除;否则会删除所有具有相同名字的已有文件

再来看看字符流

Write

写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

Reader

用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。

文本输入与输出

在存储文本字符串时,需要考虑字符编码(character encoding),OutputStreamWriter类将使用选定的字符编码方式,把Unicode码元的输出流转换为字节流,而InputStreamReader类将包含字节(用某种字符编码方式表示的字符)的输入流转换为可以产生Unicode码元的读入器。

Reader in=new InputStreamReader(new FileInputStream("data.txt"),StandardCharsets.UTF_8)

写出文本输出

对于文本输出,可以使用PrintWriter。这个类拥有以文本格式打印字符串和数字的方法,还有一个将PrintWriter链接到FileWriter的便捷方法:

PrintWriter out=new PrintWriter("emloyee.txt","UTF-8");
//等同于
PrintWriter out=new PrintWriter(new FileOutputStram("emloyee.txt"),"UTF-8");

如何读入文本输入

将短小的文本文件读入到一个字符串中:String content=new String(Files.readAllBytes(Path),charset);

如果文件太大,可以将行惰性处理为一个Stream<String>,Stream<String> lines=Files.lines(path,charset)

操作文件

Path

Path表示的是一个目录名序列,其后还可以跟着一个文件名。以根部件/或者C:`开始的路径为绝对路径,否则就是相对路径。

Path提供了很多与路径有关的操作

这边说一下关于绝对路劲和相对路径的问题
/home/jakobjenkov/myfile.txt Windows中会被认为是当前盘下的路径
这个路径会被理解其C盘上的文件,所以路径又变成了
C:/home/jakobjenkov/myfile.txt
合并两个路径的技术允许你先定义一个固定的根目录然后再附上局部的路径,在NIO.2 中,使用resolve() 方法来实现这一功能。

File

File类主要用于命名文件,查询文件属性和处理文件目录。

Files

重点来看看这个nio中的类,其中提供了很多非常便利操作的方法,其中的操作基本上都基于Path

这里是一小部分的方法api,具体的可以见java9的详细api文档

Stream

以前一直不知道这个东西,在学习io的时候发现了,感觉是真的好用。

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

简单说,对 Stream 的使用就是实现一个 filter-map-reduce 过程,产生一个最终结果

把一个数据结构包装成一个Stream后,对Stream中的元素进行操作,下面是几个例子

//留下偶数
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens =
Stream.of(sixNums).filter(n -> n%2 == 0).toArray(Integer[]::new);
//统计单词
List<String> output = reader.lines().
 flatMap(line -> Stream.of(line.split(REGEXP))).
 filter(word -> word.length() > 0).
 collect(Collectors.toList());
//打印姓名
roster.stream()
 .filter(p -> p.getGender() == Person.Sex.MALE)
 .forEach(p -> System.out.println(p.getName()));