IO流学习总结
什么是IO流
IO流之前也接触了好多次,因为这一块内容很多,所以老是有不太清楚的地方,所以现在来总结一下。
流分为字节流,字符流。
简单的说,字节流就是由二进制数据组成的流,一个字节八个bit,这个计算机很容易理解,但是不方便人阅读,所以就有了字符流。字符流,有不同的编码方式。
IO流中类很多,但是层次关系还是很明显的,画图比较能说明问题
InputStream用于字节序列的读取
OutputStream用于字节序列的写入
Reader和Writer是面向字符的流,他们处理的对象是字符串
简单说一下,因为这些后面都会总结,这些其实只是继承关系的一部分,其实主要用到的就是FileXXXStream,BufferedXXXStream,DataXXXStream。
这里的FiliterInputStream使用了装饰者模式,用于对InputStream子类的扩展。
什么是装饰者模式?
简单的来说,装饰者模式由抽象对象,抽象构件,装饰抽象对象,装饰构件组成
他们的关系像是这样
装饰对象是对抽象对象的扩展,我们知道一个良好的类意味着减少自己内部实例和方法被修改的可能,同时也要增加扩展的机会。在此基础上,如果要增加扩展,
有继承和组合两种办法,但是继承会增加类之间的耦合性,不利于进一步扩展。因此使用组合的装饰者模式,是不错的选择。
上下的两幅图多像啊,IO流中的Filiter就是使用了装饰者模式对抽象流进行了扩展
这里,推荐一个博客,虽然他里面没有更具体的例子,但是这种直接构建装饰器的方式更容易迁移到IO流的理解上
https://blog.csdn.net/jialinqiang/article/details/8881855
接来下,先从简单的字符流总结
字符流
文本编码
只需知道UTF-8和UTF-16属于Unicode标准的编码就好了,而现在一般都用unicode编码
字节流转换为字符流
使用InputStreamReader或OutputStreamWriter可以将字节流转换为字符流
例如从标准输入中获得字符流,但是标准输入本身是一个字节流
Reader in = new InputStreamReader(System.in);
字符输入
字符输入分文标准输入和文本输入
标准输入
就用上面那个就可以了,或者用Scanner。
Scanner s = new Scanner(System.in);
Scanner用法:http://www.runoob.com/java/java-scanner-class.html
文本输入
文本输入分为传统Reader和Files.readXXX获取
Files.readXXX
- 对于短小的文本文件可以使用下面这个方法:
String content=new String(Files.readAllBytes(path),charset);
- 如果要将文本一行行的读入:
List<String> lines=Files.readAllLines(path,charset);
- 如果文件很大,那么可以使用流Stream来处理,Stream的特点之一就是惰性处理,读到哪算哪那种
try(Stream<String> lines = Files.lines(path,charset)){
}
(这种叫做try-with-resource方式,对于实现了Closeable的类都可以使用这种方式来自动关闭对资源的占用)
传统Reader
通常使用BufferReader+FileReader的组合来读取文本
或者使用BufferReader+InputStreamReader+InputStream(通常是FileInputStream)
字符输出
标准输出
System.out,System.err这个应该没什么好说的
文本输出
通常使用PrintWriter,这个类拥有以文本格式打印字符串和数字的方法(BufferReader可没有处理数字的能力,但是Scanner有处理数字的能力)
它还有一个将PrintWriter链接到FileWriter的便捷方法
例如:
PrintWriter out = new PrintWriter("path","UTF-8");//或者你也可以不指定编码
等同于
PrintWriter out = new PrintWriter(new FileOutputStream("path"),"UTF-8");
和
PrintWriter out = new PrintWriter(new FileWriter("path")));
接下来有个例子,可以更具体体会字符输入输出:
package core.java.IO;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class ReadAndWriteChar {
public static class Employee{
public String name;
public int age;
public String date;
public Employee(String name, int age, String date) {
super();
this.name = name;
this.age = age;
this.date = date;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name+" "+age+" "+date;
}
}
private static void writeData(Employee[] employees,PrintWriter out) {
int len=employees.length;
out.println(len);
for (Employee employee : employees) {
writeEmployee(employee, out);
}
}
private static void writeEmployee(Employee employee,PrintWriter out) {
out.println(employee.name+"|"+employee.age+"|"+employee.date);
}
private static Employee[] readData(BufferedReader in) throws NumberFormatException, IOException {
int len=Integer.parseInt(in.readLine());
Employee[] employees=new Employee[len];
for(int i=0;i<len;i++) {
employees[i]=readEmployee(in);
}
return employees;
}
private static Employee readEmployee(BufferedReader in) {
String[] employeeInfo = null;
try {
employeeInfo = in.readLine().split("\\|");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return new Employee(employeeInfo[0], Integer.parseInt(employeeInfo[1]), employeeInfo[2]);
}
public static void main(String[] args) throws IOException {
Employee[] employees= {new Employee("小钟", 20, "1998-2-3"),new Employee("小齐", 22, "1996-3-32")};
try(PrintWriter pw=new PrintWriter(new FileWriter("E:\\Desktop\\data1\\target.txt"))){
writeData(employees, pw);
}
Employee[] receive;
try(BufferedReader in=new BufferedReader(new FileReader("E:\\Desktop\\data1\\target.txt"))){
receive=readData(in);
}
for (Employee employee : receive) {
System.out.println(employee);
}
}
}
输出:
小钟 20 1998-2-3
小齐 22 1996-3-32
二进制数据的读取
再看下这幅图,实际上用的多的是BufferedXXXStream,DataXXXStream,FileXXXStream.
先从DataXXXStream 总结
这是可以将二进制流转换为Chars,byte,int,short,long,float,double,char,boolean,utf这些基本数据类型,其中字符串以UTF进行编码
例如你要读取某个文件
DataInputStream in = new DataInputStream(new FileInputSream(path));
in.readXXX;
写入文件
DataOutputStream out = new DataOutputStream(new FileOutputStream(path));
BufferedInputStream 是装饰流,用于打开一个缓冲区进行读写,这样比频繁地打开文件来读写要进行的快些,就好像我们的文件像一口大缸里的水,如果
你每次都是打开文件来读写那就像每次舀水都要打开大缸沉重的盖子,而如果你用缓冲区来处理,就像给这口缸加了一个水龙头,你每一次读取就像打开水龙头取水一样轻松。
BufferedInputStream in=new BufferedInputStream(new FileInputStream("fllename"));