本文内容来自《On Java进阶卷》相关章节学习总结
1 IO流
Java8函数式流和IO流之间并无关联
- 字节流用于处理原始的二进制数据
- 字符流用于处理字符数据,它会自动处理和本地字符集间的相互转换
- 缓存流提升性能,通过减少调用本地API的次数,优化输入和输出
1.1 各种InputStream类型
InputStream用于表示从不同源生成输入的类,可能得源有以下几种:
- 字节数组
- 字符串对象
- 文件
- 管道
- 其他流组成的序列
- 网络连接等其他源
1.2 各种OutputStream类型
决定输出去向的类,可以是字节数组、文件或管道。
1.3 添加属性和有用的接口
FilterInputStream和FilterOutputStream提供了可控制某个特定InputStream和OutputStream的装饰器接口。
- 用FilterInputStream从InputStream中读取
| 类 | 功能 | 构造器参数 |
|---|---|---|
| DataInputStream | 与DataOutputStream配合使用,从流中读取基本类型(int、char、long等) | InputStream |
| BufferedInputStream | 增加缓冲操作 | InputStream和可选的缓存区大小 |
- 用FilterOutputStream向OutputStream中写入
| 类 | 功能 | 构造器参数 |
|---|---|---|
| DataOutputStream | 向流中写入基本类型 | OutputStream |
| PrintStream | 用于生成格式化的输出 | OutputStream和可选参数:boolean,表示是否在每次换行时都清空缓存 |
| BufferedOutputStream | 增加缓冲操作 | OutputStream和可选的缓存区大小 |
1.4 各种Reader和Writer
Reader和Writer提供了兼容Unicode并且基于字符的IO能力。并非用来代替InputStream和OutputStream。
有时需要将“字节”类和“字符”类结合使用,要使用适配器类:InputStreamReader将InputStream转换为Reader,OutputStreamWriter将OutputStream转换为Writer。
-
数据的来源和去处 对应字节流,字符流主要有FileWriter、FileReader、StringWriter、StringReader、CharArrayWriter、CharArrayReader等;
-
改变流的行为 主要有BufferedReader、BufferedWriter、PrintWriter等
1.5 RandomAccessFile
RandomAccessFile适合用来处理大小已知的记录组成的文件,可以通过seek()在记录上来回移动,读取或修改记录。
1.6 IO流的典型用法
1、缓冲输入文件
为提高速度,需要对文件进行缓冲
public class BufferedInputFile {
public static String read(String filename) {
try(BufferedReader in = new BufferedReader(
new FileReader(filename))) {
return in.lines()
.collect(Collectors.joining("\n"));
} catch(IOException e) {
throw new RuntimeException(e);
}
}
}
2、从内存输入
public static void
main(String[] args) throws IOException {
StringReader in = new StringReader(
BufferedInputFile.read("iostreams/MemoryInput.java"));//String字符串
int c;
while((c = in.read()) != -1)
System.out.print((char)c);
}
3、格式化的内存输入 要读取格式化后的数据,可以使用面向字节的DataInputStream。
public static void main(String[] args) {
try(
DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("iostreams/TestEOF.java")))
) {
while(in.available() != 0)
System.out.write(in.readByte());
} catch(IOException e) {
throw new RuntimeException(e);
}
}
4、基本的文件输出
FileWriter对象用于向文件中写入数据。一般通过将输出包装在BufferedWriter中来进行缓冲。此外,FileWriter被装饰为PrintWriter以提供格式化的能力。
static String file = "iostreams/BasicFileOutput.dat";
public static void main(String[] args) {
try(
BufferedReader in = new BufferedReader(
new StringReader(
BufferedInputFile.read(
"iostreams/BasicFileOutput.java")));
PrintWriter out = new PrintWriter(
new BufferedWriter(new FileWriter(file)))
) {
in.lines().forEach(out::println);
} catch(IOException e) {
throw new RuntimeException(e);
}
// Show the stored file:
System.out.println(BufferedInputFile.read(file));
}
文本文件输出的快捷方式
PrintWriter提供了一个辅助构造器,不用自行去实现缓冲。
public PrintWriter(String fileName) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),
false);
}
5、存储和恢复数据
要输出可供另一个流恢复的数据,可以使用DataOutputStream来写入数据,并用DataInputStream来恢复数据。
public static void main(String[] args) {
try(
DataOutputStream out = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("iostreams/Data.txt")))
) {
out.writeDouble(3.14159);
out.writeUTF("That was pi");
out.writeDouble(1.41413);
out.writeUTF("Square root of 2");
} catch(IOException e) {
throw new RuntimeException(e);
}
try(
DataInputStream in = new DataInputStream(
new BufferedInputStream(
new FileInputStream("iostreams/Data.txt")))
) {
System.out.println(in.readDouble());
// Only readUTF() will recover the
// Java-UTF String properly:
System.out.println(in.readUTF());
System.out.println(in.readDouble());
System.out.println(in.readUTF());
} catch(IOException e) {
throw new RuntimeException(e);
}
}
注意:写入不同类型的数据,在读取时,必须使用相应类型的读取方法才能正确获取。因此,必须对文件中的数据使用固定的格式,或者在要解析的文件中加入额外的信息来确定数据所在的位置。
推荐使用对象序列化或者XML来进行存取复杂数据结构。
读写随机访问文件
使用RandomAccessFile类似于组合使用DataInputStream和DataOutputStream(都实现了相同的接口DataInput和DataOutput)。此外可以使用seek()来回移动并修改值。
static String file = "iostreams/rtest.dat";
public static void display() {
try(
RandomAccessFile rf =
new RandomAccessFile(file, "r")
) {
for(int i = 0; i < 7; i++)
System.out.println(
"Value " + i + ": " + rf.readDouble());
System.out.println(rf.readUTF());
} catch(IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
try(
RandomAccessFile rf =
new RandomAccessFile(file, "rw")
) {
for(int i = 0; i < 7; i++)
rf.writeDouble(i*1.414);
rf.writeUTF("The end of the file");
rf.close();
display();
} catch(IOException e) {
throw new RuntimeException(e);
}
try(
RandomAccessFile rf =
new RandomAccessFile(file, "rw")
) {
rf.seek(5*8);//double类型占8个字节,则第5个double的位置为5*8
rf.writeDouble(47.0001);
rf.close();
display();
} catch(IOException e) {
throw new RuntimeException(e);
}
}
2 标准IO
程序的所有输入都可以来自标准输入,所有输出都可以发送到标准输出,所有错误都可以发送到标准错误。
2.1 从标准输入中读取
Java实现了System.out、System.err和System.in,其中System.out和System.err已经被预先封装为PrintStream对象,而System.in是未经包装的原生InputStream,必须在读取前先进行包装。
public static void main(String[] args) {
TimedAbort abort = new TimedAbort(2);
new BufferedReader(
new InputStreamReader(System.in))
.lines()
.peek(ln -> abort.restart())
.forEach(System.out::println);
// Ctrl-Z or two seconds inactivity
// terminates the program
}
2.2 将System.out转换为PrintWriter
PrintWriter提供了一个以OutputStream为参数的构造器,内部使用OutputStreamWriter和BufferedWriter进行了包装。
public static void main(String[] args) {
PrintWriter out =
new PrintWriter(System.out, true);
out.println("Hello, world");
}
2.3 标准IO重定向
System类通过静态方法可以将IO标准流重定向:
- setIn(InputStream in)
- setOut(PrintStream out)
- setErr(PrintStream err)
public static void main(String[] args) {
PrintStream console = System.out;
try(
BufferedInputStream in = new BufferedInputStream(
new FileInputStream("Redirecting.java"));
PrintStream out = new PrintStream(
new BufferedOutputStream(
new FileOutputStream("Redirecting.txt")))
) {
System.setIn(in);//标准输入重定向到文件
System.setOut(out);//标准输出重定向到文件
System.setErr(out);//标准错误重定向到文件
new BufferedReader(
new InputStreamReader(System.in))
.lines()
.forEach(System.out::println);//读取文件内容,再写入另一个文件
} catch(IOException e) {
throw new RuntimeException(e);
} finally {
System.setOut(console);//恢复指向原始的System.out对象
}
}
3 NIO系统
NIO内容较多,且比较重要,所以另外进行总结。