目录
前言
在上两篇文章中,我们学习了Java I/O流——面向字节的输入/输出流和面向字符的输入/输出流,今天,我们学习Java I/O流——标准I/O、重新包装标准I/O、标准I/O流重定向、对象序列化。
标准I/O
输入流和输出流对象的生命周期是短暂的,不会存在于程序运行的整个生命周期,通常在I/O操作完毕时就应该适时地关闭I/O流。但有些应用程序需要在程序运行的整个生命周期中,例如日志文件。若每次都要重新打开I/O流,再关闭,会很不方便,因此,java.lang.System类提供以下3个静态常量:
-
static final InputStream in :标准输入流,已打开并准备提供输入数据,从键盘输入或主机或用户指定的另一个输入源;
-
static final PrintStream out :标准输出流,已打开并准备接收输出数据,从显示器输出或由主机或用户指定的另一个输出目标;;
-
static final PrintStream err :标准错误输出流,已打开并准备接收输出错误信息,从显示器输出或者由主机或用户指定的另一个输出目标。
以上3个流由JVM在启动程序时自动创建,它们存在程序运行的整个生命周期中。
程序在任何时候都可通过它们来输入/输出数据,所有输入都可来自标准输入流,所有输出都可发送标准输出流,而所有的错误信息则发送到标准错误输出流。
重新包装标准I/O
因为System.out已事先被包装成PrintStream对象,所有我们常常直接使用System.out将数据写到标准输出,同样的,System.err是PrintStream对象,但System.in是一个未被包装的InputStream对象,为了读取到格式化的数据,以及提高读数据的效率,需要对其进行包装。
要把System.in重新包装,先使用InputStreamReader把System.in转换成Reader类,再用BufferedReader来装饰它。
例如:
import java.io.*;
public class first {
public static void main(String[] args)throws IOException {
BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); //包装标准输入
PrintWriter out=new PrintWriter(System.out,true); //包装标准输出
String s; //创建一个字符变量
while((s=br.readLine())!=null&&s.length()!=0) //读取字符信息
out.println(s.toUpperCase()); //把字符串转换为大写
}
}
运行后,在控制台输入:asdfg
运行结果为:
标准I/O流重定向
在默认情况下,标准输入流是从键盘读取数据,标准输出流和标准错误输出流是向控制台输出数据,Java的System类提供了一些简单的静态方法调用,允许对标准输入/输出流和错误I/O流进行重定向。
-
static void setIn(PrintStream in):对标准输入流重定向;
-
static void setOut(PrintStream out):对标准输出流重定向;
-
static void setErr(PrintStream err):对标准错误输出流重定向。
程序向控制台输出大量数据,由于输出数据滚动太快,刷新比较慢,会影响阅读,此时可以把标准输出流定向到一个文件中。
PrintStream中的println方法具有原样输出和自动刷新的功能,这样就可以解决输出数据块,刷新比较慢的问题。
例如:测试标准I/O流的重定向,将标准输入附接到文件上,而将标准输出和标准错误输出定向到另一个文件。
import java.io.*;
public class first {
public static void main(String[] args)throws IOException {
PrintStream console=System.out; //PrintStream,标准输出流
BufferedInputStream in=new BufferedInputStream(new FileInputStream("G:/table.txt")); //创建一个BufreredInputStream类实例对象in
PrintStream out =new PrintStream(new BufferedOutputStream(new FileOutputStream("G:/a.txt"))); //创建一个PrintStream类实例对象out
System.setIn(in); //对标准输入流重定向
System.setOut(out); //对标准输出流重定向
System.setErr(out); //对标准错误输出流重定向
BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); //创建一个BufferedReader类实例对象br,从在键盘中输入或者用户指定的另一个输入源输入
String s;
while((s=br.readLine())!=null) //从BufferedReader类中读取一行数据
System.out.println(s);
out.close(); //关闭文本文件
System.setOut(console); //对标准输出流重定向
}
}
运行该程序,a.txt文本文件将被复制到table.txt文本文件中。
运行结果为:
对象的序列化和反序列化
学习序列化之前,先简单了解数据的状态。数据的状态分两种:
-
持久态:硬盘上的数据;
-
游离态:运行在内存中的数据,程序运行结束后,数据就消失了。
序列化与反序列化:
-
序列化:把游离态的对象写到文件中保存持久态,把Java对象转换为字节序列的过程称为对象的序列化;
-
反序列化:把文件中的持久态对象读到程序中游离态,把字节序列恢复为Java对象的过程称为对象的反序列化。
对象的序列化主要有两种用途:
-
把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
-
在网络上传送对象的字节序列。
默认的序列化机制只将对象的非静态成员变量进行序列化,而任何成员方法和静态成员变量都不参与序列化。序列化时保存的只是变量的值,而变量的任何修饰符都不能保存。
注意:用transient关键字修饰的成员变量,不参与序列化过程。
JDK类库中的序列化API
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中;
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化成一个对象,并将其返回。
基本数据类型的包装类、所有容器类甚至Class对象都可以被序列化,但是用户自定义的对象默认是不能序列化的,若要使用一个用户自定义的对象也具备序列化的能力,则必须明确实现java.io.Serializable接口,该接口的定义为:
public interface Serializable{}
该接口未定义任何方法,只是个标记型接口,但只要一个类声明实现了该接口,Java系统就认为该接口可被序列化,该类的对象就可以存盘或通过网络传输了。
例如:创建10个点对象后将它们写入aa.txt文本文件中,然后从该文本文件中读取这10个对象并在控制台显示出来。
import java.io.*;
import java.util.*;
public class first {
static class Point implements Serializable{ //创建一个静态的Point类并连接接口Serializable
private int x,y; //创建私有变量x,y
private transient int z; //使用关键字transient创建私有变量z
public Point(int x,int y,int z) { //构造方法,传递值
this.x=x;
this.y=y;
this.z=z;
}
public String toString() { //返回输出格式
return "("+x+","+y+","+z+")";
}
}
public static void main(String[] args)throws Exception {
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("G:/aa.txt")); //创建ObjectOutputStream对象,使用FileOutputStream类将输出存放在aa.txt文本文件中
for(int i=0;i<10;i++) { //利用for循环写入10个点对象到文本文件中
oos.writeObject(new Point(i,i*2,i*3));
}
oos.flush(); //强制刷新流
oos.close(); //关闭文件
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("G:/aa.txt")); //创建ObjectInputStream对象,使用FileInputStream类读取aa.txt文件
for(int k=0;k<10;k++) {
Point pt=(Point)ois.readObject(); //利用for循环读取10个点对象
System.out.print(pt+""); //输出点对象
}
ois.close(); //关闭文件
}
}
运行结果为:
最后
好了,有关Java I/O流——标准I/O、重新包装标准I/O、标准I/O流重定向、对象序列化的知识学到这里了,谢谢观看!!!
我们下篇文章再见!!!
成功不是将来才有的,而是从决定去做的那一刻起,持续累积而成。