我们将会介绍如何在JAVA中进行输入和输出操作。
part 1 大体介绍输入输出流
在JavaAPI中,可以从其中读入一个字节序列的对象称作输入流,而可以向其中写入一个字节序列的对象称为输出流。这些字节序列的来源和目的地可以是文件,也可以是网络连接,甚至内存块。**抽象类!**InputStream 和 OutputStream 构成了输入和输出各类层次的基础。
part 2.1 读写字节虚类的介绍
在InputStream中有许多很有用的抽象方法,其中有一些可能会在子类中重写,例如:
read()方法。这个方法将读入一个字节,并返回读入的字节,或者在遇到输入源结尾时返回-1。在我们具体的输入流类中,他往往被重写以提供适用的功能。
与之类似OutputStream类中也定义了类似的方法——abstract void write(int b)
read和write方法在执行时都将阻塞,直至字节确实被读入或写出。
available方法可以让我们去检查当前可读入的字节数量,那我们就可以通过这个方法来避免,read和write没有可读信息的阻塞:
int by = in.available();
if(by > 0)
{
byte[] data = new byte[byteAvailable];
in.read(data);
}
当我们完成了对输入/输出流的读写时,应该通过他们的close方法来关闭它,这可以及时释放我们有限的系统资源。
我们可以使用众多继承InputStream和OutputStream的类,而不是直接使用字节
Tip:我们的读写都是按照一个字节进行的,但是Unicode编码有两个字节,所以我们对Unicode文本建立了Reader和Writer的抽象类,基本方法与InputStream和OutputStream类似。
part 2.2 输入/输出流过滤器的组合
FileInputStream和FileOutputStream可以提供磁盘上文件的输入流和输出流,而你只需要提供文件民或者文件的完整路径即可。但是注意,他们是抽象类InputStream和OutputStream类一样,是基于一个字节操作的。
FileInputStream fin = new FileInputStream("emplotee.dat");
上面那行代码可以查看用户目录下的名为employee.dat的文件。(我们当然可以绝对路径)
我们以后会看到,DataInputStram,只能读入数值类型。
而FileInpuStream没有任何读入数值类型的方法,DataInputStream也没有任何从文件中获取数据的方法。
在Java中,我们使用一种机制来融合分离这两种职责,简单说,这过程感觉像是过滤一样,我们可以这样:
FileInputStram fin = new FileInputStream("emplotee.dat");
DataInputStream din = new DataInputStream(fin);
double x = din.readDouble();
我们可以通过嵌套过滤器来添加多重功能过滤来获得数据。例如,输入流在默认情况下是不被缓冲区缓存的,相比之下,请求一个数据块并将其置于缓冲区会显得更加高效。如果我们想使用缓冲机制,以及用于文件的数据输入方法,那么就需要使用下面这种构造序列:
DataInputStream din = new DataInputStream(
new BufferedInputStream(
new FileInputStream("employee.dat")));
part 2.3 如何写出文本输出
对于文本输出,我们用PrintWriter。这个类拥有以文本格式打印字符串和数字的方法,它还有一个将PrinterWriter链接到FileWriter的便捷方法:
PrintWriter out = new PrintWriter("employee.txt","UTF-8");
等同于:
PrintWriter out = new PrintWriter(new FileOutputStream("employee.txt"),"UTF-8");
为了打印写出器,需要使用System.out的print, println, printf方法,例如:
//上面有out了
out.print();
part 2.4 如何读入文本输入
最简单的处理任意文本的方式就是我们常见的Scanner类。
我们可以将短小的文本文件,像这样读入到这样的一个字符串:
String content = new String(Files.readALLbYTES(path),charset);
如果我们想将这个文件一行行地读入,那我们可以这样:
List<String> lines = Files.readAllLines(path, charset);
如果文件太大,那么可以将行惰性处理为一个Stream< String >对象:
try(Stream< String >lines = Files.lines(path, charset))
{
...
}
part 2.3 以文本格式存储对象
package TextFileTest;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Scanner;
public class TextTest {
public static void main(String[] args) throws IOException {
Employee[] staff = new Employee[3];
// 创建数组内容
staff[0] = new Employee("黄一东",10000);
staff[1] = new Employee("黄玮东",20000);
staff[2] = new Employee("黄玮琛",30000);
// 写出通过写出类来 写出staff的内容 。 这里的PrintWriter自己内部会实现FileOutputStream
try(PrintWriter out = new PrintWriter("Employee.dat","UTF-8"))
{
// 调用WriteData 先向文本写入数组的大小,再根据数组大小分别调用WriteEmpolyee
// 按照格式写入各个数据
writeData(staff, out);
}
// 通过读入类 Scanner来读入employee.dat(UTF-8)的内容 这里显式的调用了
// FileInputStream类 组合了流类
try(Scanner in = new Scanner(new FileInputStream("employee.dat"),"UTF-8"))
{
// 这里调用readData(文件读入流对象) 先获取文件第一行保存的对象行数(n),然后再跳到下一行
// 调用readEmployee每次读取一行内容 循环n次 每次保存内容到数组newStaff中
Employee[] newStaff = readData(in);
for(Employee e:newStaff)
System.out.println(e);
}
}
private static void writeData(Employee[] employees, PrintWriter out)throws IOException{
out.println(employees.length);
for(Employee e:employees)
writeEmployee(out, e);
}
public static void writeEmployee(PrintWriter out, Employee e){
out.println(e.getName() + "|" + e.getSalary());
}
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 Employee readEmployee(Scanner in){
String line = in.nextLine();
String[] tokens = line.split("\\|");
String name = tokens[0];
int salary = Integer.parseInt(tokens[1]);
return new Employee(name, salary);
}
}