一、输入输出流
读入一个字节序列的对象叫做输入流,写入一个字节序列的对象叫做输出流
抽象类InputStream和OutputStream是Java输入输出流的基础,字节相关操作的对象都继承自这两个。
InputStream和OutputStream有两个抽象的方法,由此衍生出来的类都实现了这两个方法,分别为:
- abstarct int read();
- abstarct int write();
很多时候,我们需要处理Unicode形式的存储信息,因为各国文字的差异,Unicode为了进行兼容,因此Java还有两个基于字符读写的抽象类,Reader和Writer,它们两个换门用来处理Unicode字符,很多处理字符的类多是继承自他们两。
二、Java的IO类图
下边是一个简单的InputStream和OutputStream类图,包含了我们常用的输入输出类
再来看一下Reader和Writer的层次结构
Java中IO相关的类特别多,每次写Java文件相关的代码时都会去百度查,没有去深入理解JavaIO相关的操作。JavaIO相关的类如此多的原因,也是为了IO操作的灵活性而设计的,IO操作一般都会阻塞住线程,并且比较耗时,因此有了BUfferIntputStream,带有缓冲区的类的出现,又为了兼容Unicode码和字节,因此又有了Reader/Writer,这大大的增加了IO操作的灵活性。
三、简单的demo
Employee.class
import java.util.Date;
import java.util.GregorianCalendar;
/**
* @description: Employee
* @date: 2020/8/24
* @author: whiltes
*/
public class Employee {
private String name;
private double salary;
private Date hireDay;
public Employee(String name, double salary, int year, int month, int day) {
this.name = name;
this.salary = salary;
GregorianCalendar calendar = new GregorianCalendar();
calendar.set(year, month, day);
this.hireDay = calendar.getTime();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Date getHireDay() {
return hireDay;
}
public void setHireDay(Date hireDay) {
this.hireDay = hireDay;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", salary=" + salary +
", hireDay=" + hireDay +
'}';
}
}
TextFileTest
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Scanner;
/**
* @description: TextFileTest
* @date: 2020/8/24
* @author: whiltes
*/
public class TextFileTest {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
Employee[] staff = new Employee[3];
staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 5);
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 5);
staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 5);
try(PrintWriter out = new PrintWriter("employee.dat", String.valueOf(StandardCharsets.UTF_8))) {
writeData(staff, out);
}
try(Scanner in = new Scanner(new FileInputStream("employee.dat"), "UTF-8")){
Employee[] newStaff = readData(in);
for(Employee e: newStaff){
System.out.println(e);
}
}
}
private static void writeData(Employee[] employees, PrintWriter out){
out.println(employees.length);
for(Employee e: employees){
writeEmployee(out, e);
}
}
private static Employee[] readData(Scanner in){
int n = in.nextInt();
in.nextLine();
Employee[] employees = new Employee[n];
for(int i = 0; i < n; i++){
employees[i] = readEmployee(in);
}
return employees;
}
public static void writeEmployee(PrintWriter out, Employee e){
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(e.getHireDay());
out.println(e.getName() + "|" + e.getSalary() + "|" + calendar.get(Calendar.YEAR) +
"|" + (calendar.get(Calendar.MONTH) + 1) + "|" + calendar.get(Calendar.DAY_OF_MONTH) );
}
public static Employee readEmployee(Scanner in){
String line = in.nextLine();
String[] tokens = line.split("\\|");
String name = tokens[0];
double salary = Double.parseDouble(tokens[1]);
int year = Integer.parseInt(tokens[2]);
int month = Integer.parseInt(tokens[3]);
int day = Integer.parseInt(tokens[4]);
return new Employee(name, salary,year,month,day);
}
}
四、总结
Java的输入输出流功能非常强大,但是常用的就那么几个,对Java的输入输出流要有整体的认识,发现其实也不是很难。
- Java用于读写字节的流操作,都是基于InputStream和OutputStream
- 为了对Unicode进行兼容,又有了Reader和Writer
- 大多数都是通过磁盘读写,因此我们使用FileInputStream的时候非常多