坚持的第16篇。
鸡汤一下:真正能让你走远的,都是自律、积极和勤奋
序言
我们在使用电脑时经常会有这样的操作:打开某一磁盘下的text文件,word文档会显示文件的内容到屏幕上。大家有无这样的疑惑:word文档是如何读取text文件的数据并且显示出来的?究其原因,答:IO
,即in和out,也就是输入和输出,指应用程序和外部设备之间的数据传递。
因为有了输入与输出,数据才能被永久保存,等需要时再取出。另外通过输入与输出才可以达到数据传递的目的。
Java采用面向对象的文件读/写方式来操作文件,将要读/写的文件数据转化为相应流类的对象,通过流对象进行操作。实现输入/输出操作的类和接口都在java.io
包中。总结来说就是Java 中是通过流处理IO 的,那么什么是流?
一、什么是流
生活中,自来水公司用管道将自来水厂与用户连接,用户需要水,就打开水龙头。计算机处理数据时我们可以把数据想成是自来水,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。
流(Stream)
,是指一连串的数据(字符或字节集合),有序的(以先进先出
的方式)从数据源流向目的地。
计算机处理数据的操作步骤大体相同:先创建、打开流,再进行读写操作,最后关闭流。
数据流是一组有顺序的、有起点和终点的字节组合,是对输入和输出的总称和抽象。掌握流内容就能按照相同方式进行输入和输出操作。
一般来说关于流的特性有下面几点:
- 先进先出:最先写入输出流的数据最先被输入流读取到。
- 顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。(RandomAccessFile除外)
- 只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要取数据,则要分别提供两个流。
二、流的种类划分
IO流有4种
基本的输入输出流:
- InputStream
- OutputStream
- Reader
- Writer
InputStream
与OutputStream
是两个抽象类,是字节流的基类,所有具体的字节流实现类都是分别继承了这两个类。Reader
与Writer
也是两个抽象类,是字符流的基类。这4种IO流主要的分类方式有以下3种:
- 数据流的方向
- 处理数据单位
- 功能分(衍生出来的子类)
1.按数据流的方向分:输入流、输出流
1.1 定义
输入流表示从外部设备流入计算机内存的数据序列,输出流表示从计算机内存向外部设备流出的数据序列,输入与输出是相对于应用程序而言的,比如文件读写,读取文件是输入流,写文件是输出流。
1.2 层次结构
2.按处理数据单位分:字节流、字符流
2.1 定义
抽象类InputStream
和OutputStream
处于字节流的顶层,派生出多个具体的子类标识不同情况下产生的输入和输出操作。
抽象类Reader
和Writer
处于字符流的顶层,是Java为字符文本的输入输出提供的一套单独的类。
流中的数据可以是二进制数据,也可以是按某种特定格式处理过的数据。数据流中的数据因为数据类型不同可以分为字节和字符流。Java使用的是Unicode编码,所有字符占用2个字节(16bits),所以每16位都能唯一标识一个字符,可以是数字、字母、汉字和特殊字符。
2.2 层次结构
2.3 字节流和字符流的区别
- 字节流和字符流的用法几乎一样,区别在于字节流和字符流所操作的数据单元不同:字节流以字节(1 byte)(8bit)为单位,字符流以字符(2 byte)(16bit)为单位
字节(Byte)是计量单位,表示数据量多少,是计算机信息技术用于计量存储容量的一种计量单位,通常情况下一字节等于八位。
> 1byte = 8 bit
1 char = 2 byte = 16bit
一般在英文状态下一个字母或字符占用一个字节,一个汉字用两个字节表示。
字符(Character)计算机中使用的字母、数字、字和符号,比如’A’、‘B’、’$’、’&'等。
字节与字符:
ASCII 码中,一个英文字母(不分大小写)为一个字节,一个中文汉字为两个字节。
UTF-8 编码中,一个英文字为一个字节,一个中文为三个字节。
Unicode 编码中,一个英文为一个字节,一个中文为两个字节。
符号:英文标点为一个字节,中文标点为两个字节。例如:英文句号 . 占1个字节的大小,中文句号 。占2个字节的大小。
UTF-16 编码中,一个英文字母字符或一个汉字字符存储都需要 2 个字节(Unicode 扩展区的一些汉字存储需要 4 个字节)。
UTF-32 编码中,世界上任何字符的存储都需要 4 个字节。
- 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理处理纯文本的数据
- 设备上的数据无论是图片或者视频,文字,都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据
三、流的类别详细划分
下面分别对上面谈到的4种基本的输入输出流进行详细分析。
1 .字节流类
InputStream
与OutputStream
是两个抽象类,是字节流的基类,所有具体的字节流实现类都是分别继承了这两个类。
1.1 字节流InputStream类
InputStream
类的子类定义了各种数据源产生的输入流,包括字节数据、字符串对象、文件、管道和一些其他流组成的序列等,如下层次结构图:
InputStream
类有很多的实现子类,下面列举了一些比较常用的:
InputStream
是所有的输入字节流的父类,它是一个抽象类。ByteArrayInputStream
、StringBufferInputStream
、FileInputStream
是三种基本的介质流,它们分别从Byte 数组(将资源文件都以字节的形式存入到该类中的字节数组中)、StringBuffer、和本地文件中读取数据。FilterInputStream
:装饰者类,具体的装饰者继承该类,这些类都是处理类,作用是对节点类进行封装,实现一些特殊功能。DataInputStream
:数据输入流,它是用来装饰其它输入流,作用是“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。BufferedInputStream
:缓冲流,对节点流进行装饰,内部会有一个缓存区,用来存放字节,每次都是将缓存区存满然后发送,而不是一个字节或两个字节这样发送,效率更高。ObjectInputStream
:对象输入流,用来提供对基本数据或对象的持久存储。通俗点说,也就是能直接传输对象,通常应用在反序列化中。它也是一种处理流,构造器的入参是一个InputStream的实例对象。PipedInputStream
是从与其它线程共用的管道中读取数据。
InputStream
类提供的read方法以字节为单位顺序的读取源中的数据,只要不关闭流,每次调用read方法就顺序的读取源中的其余内容,直到源的末尾或输入流被关闭。InputStream
类的常用方法如下:
int read()
: 输入流调用该方法从源中读取单个字节的数据,该方法返回字节值(0~255之间的一个整数),如果未读出字节则返回-1。int read(byte[ ] b)
:输入流调用该方法从源文件中读取b.length个字节到b中,返回实际读取的字节数目,如果到达文件的末尾则返回-1。in read(byte[ ] b,int off,int len )
:输入流调用该方法从源文件中读取len个字节到b中,返回实际读取的字节数目,如果到达文件的末尾则返回-1,参数off指定从b的某个位置开始存放读取的数据。void close()
:输入流调用该方法关闭输入流,关闭之后的读取会产生IOException异常。long skip(long numBytes)
:输入流调用该方法跳过numBytes个字节,返回实际跳过的字节数
1.2 字节流OutputStream类
OutputStream
类定义了数据输出的目的地,其具体层次结构为:
OutputStream
类有很多的实现子类,下面列举了一些比较常用的:
OutputStream
是所有的输出字节流的父类,它是一个抽象类。ByteArrayOutputStream
、FileOutputStream
是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。PipedOutputStream
是向与其它线程共用的管道中写入数据。ObjectOutputStream
和所有FilterOutputStream
的子类都是装饰流。
OutputStream
流以字节为单位顺序的写文件,只要不关闭流,每次调用write方法就顺序的向目的地写入内容,直到流被关闭。OutputStream
类的常用方法如下:
void write(int n)
:输出流调用该方法向输入流写入单个字节void write(byte[ ] b)
:输出流调用该方法向输入流写入一个字节数组void write(byte [] b,int off,int len)
:输出流调用该方法向输入流写入一个从b的off处开始的 len个字节长度的字节,遇到文件末尾返回-1。void close()
:关闭输出流
2 .字符流
字符流也有两个抽象基类,分别是Reader
和Writer
。其他的字符流实现类都是继承了这两个类。
2.1 字符流Reader类
Reader
类不是InputStream类的替换者,Reader类在处理字符串时简化了编程。Reader类是字符输入流的抽象类,所有的字符输入流都是其子类,其具体层次结构为:
Reader
类有很多的实现子类,下面列举了一些比较常用的:
BufferedReader
:从字符输入流中读取文本,设置一个缓冲区来提高效率。BufferedReader
是对InputStreamReader
的封装,前者构造器的入参就是后者的一个实例对象。CharArrayReader
:从Char数组中读取数据的介质流。InputStreamReader
:从字节流到字符流的桥梁(InputStreamReader构造器入参是FileInputStream的实例对象),它读取字节并使用指定的字符集将其解码为字符。它使用的字符集可以通过名称指定,也可以显式给定,或者可以接受平台的默认字符集。FileReader
:用于读取字符文件的处理类,FileReader不能指定字符编码和默认字节缓冲区大小。理论上来说new FileReader(File file)
等于new InputStreamReader(new FileInputStream(file, true),"UTF-8")
,PipedReader
:管道字符输入流。实现多线程间的管道通信。StringReader
:从String中读取数据的介质流。
Reader
类提供的read方法以字符为单位顺序的读取源中的数据,只要不关闭流,每次调用read方法就顺序的读取源中的其余内容,直到源的末尾或输入流被关闭,Reader
类的常用方法如下:
int read()
: 输入流调用该方法从源中读取一个字符的数据,该方法返回字节值(0~65535之间的一个整数,Unicode字符),如果未读出字节则返回-1。int read(char[ ] b)
:输入流调用该方法从源文件中读取b.length个字符到b中,返回实际读取的字符数目,如果到达文件的末尾则返回-1。in read(char[ ] b,int off,int len )
:输入流调用该方法从源文件中读取len个字节到b中,返回实际读取的字节数目,如果到达文件的末尾则返回-1,参数off指定从b的某个位置开始存放读取的数据。void close()
:输入流调用该方法关闭输入流,关闭之后的读取会产生IOException异常。long skip(long numBytes)
:输入流调用该方法跳过numBytes个字符,返回实际跳过的字符数
2.2 字节流Writer类
Writer
类是字符输出流的抽象类,所有字符输出类的实现都是其子类,其具体层次结构为:
Writer
类有很多的实现子类,下面列举了一些比较常用的:
BufferedWriter
:从字符输入流中写入文本,设置一个缓冲区来提高效率。BufferedWriter
是对OutputStreamWriter
的封装,前者构造器的入参就是后者的一个实例对象。CharArrayWriter
:从Char数组中写入数据的介质流。InputStreamWriter
:从字节流到字符流的桥梁(InputStreamReader构造器入参是FileInputStream的实例对象),它写入字节并使用指定的字符集将其解码为字符。它使用的字符集可以通过名称指定,也可以显式给定,或者可以接受平台的默认字符集。FileWriter
:用于写入字符文件的处理类,FileWriter不能指定字符编码和默认字节缓冲区大小。理论上来说new FileWriterr(File file)
等于new OutputStreamReader(new FileOutputStream(file, true),"UTF-8")
,PipedWriter
:管道字符输出流。实现多线程间的管道通信。StringWriter
:从String中写入数据的介质流。
Writer
类的常用方法如下:
void write(int n)
:向输入流写入单个字符void write(byte[ ] b)
:向输入流写入一个字符数组void write(byte [] b,int off,int len)
:从给定字符数组b中 off处取len个字符写到输出流void close()
:关闭输出流
3.按功能分:节点流、处理流、缓冲流、关闭流
上面的许多子类可以按照各自的功能划分为:节点流、处理流、缓冲流、关闭流
3.1 节点流
节点流:直接操作数据读写的流类;
常用的节点流
父 类
:InputStream 、OutputStream、 Reader、 Writer文 件
:FileInputStream 、 FileOutputStrean 、FileReader 、FileWriter 文件进行处理的节点流数 组
:ByteArrayInputStream、 ByteArrayOutputStream、 CharArrayReader 、CharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)字符串
:StringReader、 StringWriter 对字符串进行处理的节点流管 道
:PipedInputStream 、PipedOutputStream 、PipedReader 、PipedWriter 对管道进行处理的节点流
3.2 处理流
处理流:处理流是对节点流的封装,最终的数据处理还是由节点流完成的;
常用的处理流
缓冲流
:BufferedInputStrean 、BufferedOutputStream、 BufferedReader、 BufferedWriter 增加缓冲功能,避免频繁读写硬盘。转换流
:InputStreamReader 、OutputStreamReader实现字节流和字符流之间的转换。数据流
: DataInputStream 、DataOutputStream 将基础数据类型写入到文件中,或者读取出来。
3.3 缓冲流
缓冲流:普通流每次读写一个字节,而缓冲流在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。
常用的缓冲流:BufferedInputStrean
、BufferedOutputStream
、 DataInputStream
、DataOutputStream
、PushbackInputStrean
、PrintStream
3.4 关闭流
流都提供了关闭方法close()
,尽管程序结束时会自动关闭所有打开的流,但是当程序使用完流后,显示的关闭任何打开的流更好。如果没有关闭那些被打开的流,那么就可能不被允许另一个程序操作这些流所使用的资源。在操作系统把程序所写到输出流上的那些字节保存到磁盘上之前,有时是被存放在内存缓冲区中的,通过调用close()方法可以保证操作系统把流缓冲区的内容写到他的目的地,也就是关闭输出流可以把该用到的缓冲区的内容冲洗掉。
四、Java的标准输入/输出
Java程序使用字符界面与系统标准输入输出界面进行数据通信,即从键盘读入数据或向屏幕输出数据,为此而频繁的创建输入输出流类对象很不方便,因此java系统还定义了3个流对象,分别与系统的标准输入(stdin)、标准输出(stdout)和标准错误(stderr)相联系。
标准输入文件是键盘,标准输出文件是终端屏幕,标准错误输出文件也指向屏幕。
标准输入(stdin)
、标准输出(stdout)
和标准错误(stderr)
在System类中,System类在java.lang包中,System类的所有属性和方法都是静态的,调用时需要以类名System为前缀。
System.out
:把输出送到默认的显示设备上(显示屏)System.in
:从标准输入设备(一般是键盘)获取输入System.err
:把错误方法显示在设备上
1 .标准输入
System.in
是InputStream类
对象,当程序需要从键盘读数据时,只需要调用System.in
的read()方法。
如从键盘中读一个字节的数据:
Char ch = System.in.read();
查看源代码看看到:System.in.read()
会抛出IOException
public static final InputStream in;
....
/**
* Reads the next byte of data from the input stream. The value byte is
* returned as an <code>int</code> in the range <code>0</code> to
* <code>255</code>. If no byte is available because the end of the stream
* has been reached, the value <code>-1</code> is returned. This method
* blocks until input data is available, the end of the stream is detected,
* or an exception is thrown.
*
* <p> A subclass must provide an implementation of this method.
*
* @return the next byte of data, or <code>-1</code> if the end of the
* stream is reached.
* @exception IOException if an I/O error occurs.
*/
public abstract int read() throws IOException;
因此在使用System.in.read()
方法时需要注意以下的几点:
System.in.read()
语句必须包含在try
语句中,且try
语句后面要有catch
捕获IOException
System.in
对象只能从键盘读取二进制数据,不能把信息转化为整型、字符、浮点数、字符串等数据类型。System.in.read()
方法将从键盘缓存区读取一个字节的数据,然而返回的是16位的整型(2byte),其低字节是输入的数据,高位字节全部为0。- 当键盘缓冲区没有未被读取的数据时,执行
System.in.read()
将导致系统进入阻塞状态,在阻塞状态下,当前流程将停留在上个语句的位置,整个程序被挂起,等用户从键盘输入一个数据后才能继续运行下去,程序有时可以利用System.in.read()
语句来暂时保留屏幕的目的
2 .标准输出
System.out
是PrintStream
类的对象,PrintStream
类是FilterOutputStream
的一个子类,PrintStream
类定义了向屏幕输出不同类型数据的方法print()和println()
。
查看源码可以看到:
public static final PrintStream out;
public static final PrintStream err;
public class PrintStream extends FilterOutputStream implements Appendable, Closeable {
public void print(char var1) {
this.write(String.valueOf(var1));
}
public void print(int var1) {
this.write(String.valueOf(var1));
}
public void print(long var1) {
this.write(String.valueOf(var1));
}
public void print(float var1) {
this.write(String.valueOf(var1));
}
public void print(double var1) {
this.write(String.valueOf(var1));
}
}
print()和println()的不同就是前者输出对象后不换行,后者需要换行。
举个例子:从键盘输入字符:
import java.io.IOException;
/**
* 从键盘输入一行字符,存储在缓冲区buffer中,count保存实际读入的字节个数,以整数和字符两种方式输出buffer中的值
* read方法在java.io包中,抛出IOException异常
*/
class Scratch {
public static void main(String[] args) throws IOException {
System.out.println("Input:");
byte[] buffer = new byte[512];//输入缓冲区
int count = System.in.read(buffer);//读取标准输入流
System.out.println("Output");
for (int i = 0; i < count; i++) {
System.out.println("" + buffer[i]);//输出buffer元素的值
}
System.out.println();
for (int i = 0; i < count; i++) {
System.out.print((char) buffer[i]);//按字符方式输出buffer
}
System.out.println("count=" + count);
System.in.close();
}
}
输出结果为:
Input:
123
Output
49
50
51
10
123
count=4
五、文件的操作
文件是指封装在一起的数据,大多数操作系统把和输入输出有关的操作统一到文件的概念中,程序与外部的数据交换都通过文件概念来实现。
1. File类
1.1 文件类定义
上面谈到了4个基本输入/输出,此外还有一个重要的非流类:File类
,File类是java.io包中唯一代表磁盘文件本身的对象。File类中定义了一些与平台无关的方法来操作文件,可以通过调用File类中的方法得到文件或者目录的描述信息,包括名称、所在路径、读写性、长度等,还可以创建目录、创建文件改变文件名、删除文件、列出目录中的文件等。File类的对象主要用来获取文件本身的一些信息,如文件所在的目录、文件的长度、文件的读写权限等等,数据流可以将数据写入到文件中,文件也是数据流最常用的数据媒体。
File类
的声明如下:
public class File implements Serializable, Comparable<File>
File类
是用来管理目录和文件的,并没有指定信息怎样的从文件中读取或向文件输出,而需要FileInputStream和FileOutputStream来实现。
1.2 文件类构造方法
File类
创建一个文件对象,通常使用以下4种构造方法:
(1) public File(String pathname)
:创建一个pathname路径下的File对象
File f1 = new File("D:/java");
(2) public File(String parent, String child)
:创建一个parent路径下的名为child的File对象
File f2 = new File("D:/java","text.txt");
(3) public File(File parent, String child)
:创建一个parent路径下的名为child的File对象
File f3 = new File(f1,"text.txt");
(4) public File(URI uri)
:通过给定的file:URI转换为抽象路径名来创建新的File对象
File f4 = new File(file:/D:/java/test.txt");
上面有4个File对象,f1只有一个路径,f2有路径和文件名称,f2和f3
指向相同的文件,f4用uri一个参数构造一个文件。
1.3 文件类常用方法
创建一个文件对象后,可以用File类提供的很多方法获取文件的相关信息。
1.3.1 对文件名进行操作的方法
String getName()
:获取一个文件的名称String getPath()
:获取一个文件的路径String getAbsolutePath()
:获取一个文件的绝对路径名String getParent()
:获取一个文件的上一级目录名boolean renameTo(File dest)
:将当前的文件更名为给定文件的完整路径,成功返回true,失败返回false
1.3.2 测试文件属性的方法
boolean exists()
:测试当前的文件是否存在boolean canWrite()
: 测试当前文件是否可写boolean canRead()
:测试当权文件是否可读boolean isFile()
:测试是否当前文件是文件,不是目录boolean isDirectory()
:测试当前文件是否为目录
1.3.3 获取普通文件信息的工具
long lastModified()
:获取文件最近一次修改时间long length()
:获取文件的长度,以字节为单位boolean delete()
:删除当前文件boolean createNewFile()
:创建一个新的空白文件
1.3.4 对目录进行操作的方法
boolean mkdir()
:根据当前对象生成一个该对象指向的路径String[] list()
:列出当前目录下的文件
1.4 举例
例1:判断D盘中的chap07文件夹中是否存在abd.txt文件,如果存在该文件则将其删除,不存在则创建该文件
class Scratch {
public static void main(String[] args) {
File file = new File("d:\\chapter7", "abc.txt");//创建文件对象
if (file.exists()) {
file.delete();
System.out.println("文件已删除");
} else {
try {
file.createNewFile();//createNewFile方法会抛出IOException
System.out.println("文件已创建");
}catch (IOException e){
e.printStackTrace();
}
}
}
}
程序编译运行后,输出结果为:
文件已创建
再次运行后,输出结果为:
文件已删除
如果d:\chapter7路径不存在,创建file对象会失败,运行结果为:
java.io.IOException: No such file or directory
at java.io.UnixFileSystem.createFileExclusively(Native Method)
at java.io.File.createNewFile(File.java:1012)
at Scratch.main(scratch_1.java:12)
例2:获取当前文件夹下word.txt文件的文件名、文件长度并判断该文件是否为隐藏文件。
class Scratch {
public static void main(String[] args) {
File file1 = new File("/home/qp/桌面/abc.txt");//创建文件对象
if (file1.exists()) {
System.out.println(file1.getName());
System.out.println(file1.length());
System.out.println(file1.isHidden());//判断是否为隐藏文件
System.out.println(file1.canRead());//判断文件是否可读
} else {
System.out.println("文件不存在");
}
}
}
输出结果为:由于事先创建了该目录下的文件,所以可以执行存在的代码
abc.txt
0
false
true
如果没有创建该文件,输出的结果为:
文件不存在
2. 目录
2.1 目录定义
目录是一个包含其他文件和路径列表的File类。当创建一个File对象并且是目录时,isDirectory()
方法返回true。在这种情况下,可以调用该对象的list()
方法来提取该目录内部其他文件和目录列表。
2.2 目录常用方法
2.2.1 创建目录
boolean mkdir()
:创建目录,创建成功返回true,失败返回false(若该目录已经存在则返回false),此方法不能在不存在的目录下创建新的目录boolean mkdirs()
:创建包括所有必需但不存在的父目录。然后父目录不存在并且最后一级子目录也不存在,也会自动新建所有路径里写的目录;如果父目录存在,则会在父目录下新建子目录。
2.2.2 列出目录中的文件
String[] list()
:用字符串形式返回目录下的全部文件File[] listFiles()
:用File对象形式返回目录下全部文件String[] list(FilenameFilter filter)
:用字符串形式返回目录下指定类型的所有文件listFiles(FilenameFilter filter)
:用File形式返回目录下指定类型的所有文件
FilenameFilter是一个接口,该接口有一个方法
boolean accept(File dir,String name)
查看源代码可知:使用list方法时,需要向其传递一个实现FilenameFilter
接口的对象,list方法执行时,参数filter
不断回调方法boolean accept(File pathname,String name)
,该方法中的参数dir
为调用list
的当前目录,参数name
为被实例化目录中的一个文件名,当接口方法返回true时list
方法就将名字为name
的文件保存到返回的数组中
boolean accept(File dir, String name);
public String[] list(FilenameFilter filter) {
String names[] = list();
if ((names == null) || (filter == null)) {
return names;
}
List<String> v = new ArrayList<>();
for (int i = 0 ; i < names.length ; i++) {
if (filter.accept(this, names[i])) {
v.add(names[i]);
}
}
return v.toArray(new String[v.size()]);
}
2.3 举例
列出指定目录下全部java文件的名字
class FileAccept implements FilenameFilter{ //调用list方法时需要指定目录,所以需要FilenameFilter对象
@Override
public boolean accept(File dir, String name) {
return name.endsWith(".txt");
}
}
public class Scratch_1 {
public static void main(String[] args) {
File dir = new File("/home/qp/桌面/");//根据实际目录创建file对象
FileAccept fileAccept = new FileAccept();
String[] list = dir.list(fileAccept);
for(String name : list){
System.out.println(name);
}
}
}
输出结果为:/home/qp/桌面/目录下以.txt结尾的文件的名字
1.txt
2.txt
如果去调 return name.endsWith(".txt");直接返回true时,相当于调用String[] list()
方法。
六、文件输入/输出流
上面谈到的File类
是用来管理目录和文件的,并没有指定信息怎样的从文件中读取或向文件输出,而需要FileInputStream
和FileOutputStream
来实现。
程序运行期间,大部分数据都在内存志中进行操作,当程序结束或关闭时,这些数据将消失。如果需要将数据永久保存,可使用文件输入/输出流与指定的文件建立连接,从而将需要的数据永久保存到文件中。
1. 文件字节流
InputStream
和OutputStream
都是抽象类,不能实例化,因此在实际应用中使用的都是他们的子类,这些子类在实现其超类方法的同时也各自实现了特有的功能便于用于不同的场合。
文件数据流FileInputStream
和FileOutputStream
用于进行文件的输入输出处理,其数据源都是文件。
1.1 FileInputStream
FileInputStream
用于顺序的访问本地文件,继承read()
、close()
等方法,对文件进行操作。其常用构造方法有:
FileInputStream(String name)
:通过指定文件名构造文件输入流FileInputStream(File file)
:通过文件构造文件输入流
都会抛出FileNotFoundException
。name是文件的全称路径,file是描述该文件的File对象。举个例子:
try {
FileInputStream f1 = new FileInputStream("Test.java");
File file = new File("Test.java");//根据实际目录创建file对象
FileInputStream f2 = new FileInputStream(file); //f2与f1指向同一个文件
}catch (FileNotFoundException e){
e.printStackTrace();
}
FileInputStream
也重写了抽象类InputStream
的read
方法:这些方法读取数据时,输入流结束返回-1
int read() throws IOException
int read(byte b[]) throws IOException
int read(byte b[], int off, int len) throws IOException
1.2 FileOutputStream
FileOutputStream
用于向一个文本文件写数据,继承write()
、close()
等方法,对文件进行操作。其常用构造方法有:
FileOutputStream(String name)
:通过指定文件名构造文件输出流FileOutputStream(File file)
:通过文件构造文件输出流FileOutputStream(String name, boolean append)
:通过指定文件名构造文件输出流,如果append
为true
,则为追加方式写入FileOutputStream(File file, boolean append)
:当append
为true
时构造一个追加方式的文件输出流
都会抛出IOException
异常。name是文件的全称路径,file是描述该文件的File对象。当append
为true
时,文件会以追加的方式打开,不覆盖已有文件的内容,如果为false
,则覆盖原文的内容。
FileOutputStream的创建不依赖于文件是否存在。如果file表示的文件不存在,则FileOutputStream
在打开之前创建它;如果文件已经存在,则打开它,准备写。若试图打开一个只读文件,会引发IOException
异常
FileOutputStream
也重写了抽象类OutputStream
的write
方法:
void write(int b) throws IOException
void write(byte b[]) throws IOException
write(byte b[], int off, int len) throws IOException
其中,int b
类型占4个字节,只有最低的一个字节被写入字节输入流,其它字节会被忽略。
1.3 举例
使用FileOutputStream类向文件word.txt写入信息,然后通过FileInputStream类将文件读取到控制台上。
public class Example7_1 {
public static void main(String[] args) throws FileNotFoundException {
File file = new File("word.txt");//创建文件对象
try {
FileOutputStream fileOutputStream = new FileOutputStream(file);//通过指定文件构造文件输出流
byte[] b = "hello word".getBytes();
fileOutputStream.write(b);
} catch (IOException e) {//catch处理write方法抛出的异常
e.printStackTrace();//输出异常消息
}
try {
FileInputStream fileInputStream = new FileInputStream(file);
byte[] b = new byte[1024];
int len = fileInputStream.read(b);//从文件中读取消息到b中并且返回读取的实际的字节数目给到len
for (int i = 0; i < len; i++) {
System.out.print((char)b[i]);//要将字节中读取到的数据转化为字符才能输出字符,否则会输出字节内容
}
//System.out.println(new String(b,0,len));//或者将字节数组b直接转化为字符串,与for循环是一样的效果
fileInputStream.close();//关闭流
} catch (IOException e) {//catch处理write方法抛出的异常
e.printStackTrace();//输出异常消息
}
}
}
2. 文件字符流
使用FileInputStream
和FileOutputStream
类向文件中写入数据和读取内容存在一个问题就是这两个类都只提供了字节或字节数组的读取方法,由于汉字占两个字节,使用字节流可能会出现乱码,FileReader
和FileWriter
就是针对汉字数据的读写的。
2.1 FileReader
FileReader
类是Reader
子类,对应于FileInputStream
,FileReader
流顺序读取文件,只要不关闭流,每次调用read()
方法就顺序的读取源内容直到源的末尾或流被关闭掉。其常用构造方法有:
FileReader(String fileName) throws FileNotFoundException
:fileName是一个文件的完整路径FileReader(File file) throws FileNotFoundException
:file是描述该文件的File对象,如果文件不存在,会引发FileNotFoundException异常
2.2 FileWriter
FileWriter
是Writer子类,以字符方式写文件内容,其常用构造方法有:
FileWriter(String fileName) throws IOException
:fileName是文件的完整路径FileWriter(String fileName, boolean append) throws IOException
:如果append为true,则输出内容附加到文件末尾否则覆盖文件内容FileWriter(File file) throws IOException
:file是描述该文件的File对象
FileWriter
类的创建不依赖于文件存在与否,如果文件不存在,则创建文件,然后打开文件将其输出,如果文件只读,则会抛出IOException
异常。
2.3 举例
实现文件的复制,使用字符方式读取文件和写入文件。
class Scratch {
public static void main(String[] args) throws Exception {
File file = new File("/home/qp/桌面/1.txt");
FileWriter fileWriter = new FileWriter(file);
fileWriter.write("花落知多少");//查看文件发现文件已写入该字符串
fileWriter.close();//如果没有关闭文件输出流,会导致文件被锁定,无法读取文件内容
FileReader fileReader = new FileReader(file);
char[] chars = new char[1024];
int len = fileReader.read(chars);
// String str = new String(chars,0,len);//可将有效字符转化为字符串输出
// System.out.println(str);
for (int c = 0; c < len; c++) {
System.out.print((chars[c]));//也可直接输出字符,无需转化
}
fileReader.close();//关闭流是比较好的习惯
}
}
七、缓冲流
常用的缓冲流有:BufferedInputStream
、BufferedOutputStream
、 DataInputStream
、DataOutputStream
、PushbackInputStrean
、PrintStream
可以发现缓冲流是过滤流(FilterInputStream、FilterOutputStream)
的子类(前面第3节层次结构图)。过滤流的构造方法为:
FilterInputStream(InputStream in)
FilterOutputStream(OutputStream out)
看出过滤流扩展了输入输出的功能,典型的扩展是缓冲、字符字节转换和数据转换。为了提高数据的传输效率。为每一个流配备缓冲区(buffer),称为缓冲流。
为了使用一个过滤流,必须首先把过滤流连接到某个输入输出流上,通过在构造方法的参数中指定所要连接的输入输出流来实现。
向缓冲流写入数据时,系统将数据先发送到缓冲区,缓冲区自动记录数据,当缓冲区满时,系统将数据全部发送给设备;从缓冲流读取数据时,系统先从缓冲区读取,当缓冲区为空时,系统会自动从设备中读取数据,并读取尽可能多的数据填充到缓冲区。
缓冲流支持跳过(skip
)、标记(mark
)和重新设置流(reset
)
1. BufferedInputStream和BufferedOutputStream类
1.1 BufferedInputStream
BufferedInputStream
类可以对所有InputStream
类进行带缓冲区的包装。其常用构造方法有:
BufferedInputStream(InputStream in)
:构造一个32字节的缓冲区BufferedInputStream(InputStream in, int size)
:构造size大小缓冲区
字节数据读取文件的过程为:
1.2 BufferedOutputStream
BufferedOutputStream
类可以对所有OutputStream
类进行带缓冲区的包装。其常用构造方法有:
BufferedOutputStream(OutputStream out)
:构造一个32字节的缓冲区BufferedOutputStream(OutputStream out, int size)
:构造size大小缓冲区
BufferedOutputStream
的flush()
方法在缓冲区没有写满时,也能从缓冲流将数据写入到外部设备,称之为刷新。调用close()
方法前,系统在关闭流之前也会将缓冲区信息更新到磁盘文件中。
1.3 举例
复制文件:
class Scratch {
//在保证有源文件的情况下,运行结果为,可见缓冲可以大大提高IO执行的效率。执行结果和文件的大小以及机器的运行速度有关
static long sTime;
static long eTime;
public static void main(String[] args) throws Exception {
String sFile =new String( "/home/qp/桌面/1.txt");
String oFile =new String( "/home/qp/桌面/2.txt");
File inputFile = new File(sFile);//定义读源文件
File outputFile = new File(oFile);//定义读取目标文件
//不使用缓冲流复制文件
copyFile(inputFile,outputFile);
//使用缓冲流复制文件
useBufferCopyFile(inputFile,outputFile);
}
private static void copyFile(File inputFile,File outputFile) throws IOException {
sTime = System.currentTimeMillis();//定义开始时间
FileInputStream in = new FileInputStream(inputFile);//定义输入文件流
FileOutputStream out = new FileOutputStream(outputFile);//定义输出文件流
int c;
//循环读取文件和写入文件(复制)
while ((c=in.read())!=-1){
out.write(c);//使用write方法向文件写入信息
}
in.close();//关闭文件输入流
out.close();//关闭文件输出流
eTime = System.currentTimeMillis();//定义结束时间
System.out.println("无缓冲流用时:"+(eTime-sTime)+"ms");
}
private static void useBufferCopyFile(File inputFile,File outputFile)throws IOException {
sTime = System.currentTimeMillis();//定义开始时间
FileInputStream in = new FileInputStream(inputFile);//定义输入文件流
BufferedInputStream bufferedInputStream = new BufferedInputStream(in);//将文件输入流构造到缓冲
FileOutputStream out = new FileOutputStream(outputFile);//定义输出文件流
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(out);//将文件输出流构造到缓冲
int c;
while ((c=bufferedInputStream.read())!=-1){
bufferedOutputStream.write(c);//使用write方法向文件写入信息
}
bufferedInputStream.close();
bufferedOutputStream.close();
eTime = System.currentTimeMillis();//定义结束时间
System.out.println("有缓冲流用时:"+(eTime-sTime)+"ms");
}
}
如果没有sFile
目录, 系统会报错:
Exception in thread "main" java.io.FileNotFoundException: /home/qp/桌面/1.txt (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at Scratch.copyFile(scratch_1.java:26)
at Scratch.main(scratch_1.java:19)
在保证有源文件的情况下,运行结果为下图。可见缓冲可以大大提高IO执行的效率。执行结果和文件的大小以及机器的运行速度有关
无缓冲流用时:8ms
有缓冲流用时:2ms
2. BufferedReader和BufferedWriter类
提高字符流读写的效率,引入了缓冲机制,进行字符批量的读写,提高了单个字符读写的效率,BufferedReader
用于加快读取字符的速度,BufferedWriter
用于加快写入的速度。
2.1 BufferedReader
BufferedReader
类继承Writer
类,可以包装字符流。可以从字符输入流中(FileReader
)读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。。其常用构造方法有:
BufferedReader(Reader in)
:构造一个32字节的缓冲区BufferedReader(Reader in, int sz)
:构造size大小字符缓冲区
成员方法有:
int read()
:读取单个字符。int read(char[] cbuf, int off, int len)
:将字符读入数组的某一部分。String readLine()
:读取一个文本行。long skip(long n)
:跳过字符。boolean ready()
:判断此流是否已准备好被读取。void close()
:关闭该流并释放与之关联的所有资源。void mark(int readAheadLimit)
:标记流中的当前位置。boolean markSupported()
:判断此流是否支持 mark() 操作(它一定支持)。void reset()
:将流重置到最新的标记。
2.2 BufferedWriter
BufferedWriter
类继承Writer
类,也具有内部缓存机制,其常用构造方法有:
BufferedWriter(Writer out)
:构造默认大小的输出缓冲区BufferedWriter(Writer out, int size)
:构造size大小字符缓冲区的输出缓冲区
成员方法有:
void write(int c)
:写入单个字符。void write(char[] cbuf, int off, int len)
:写入字符数组的某一部分。void write(String s, int off, int len)
:写入字符串的某一部分。void newLine()
:写入一个行分隔符。void close()
:关闭此流,但要先刷新它。void flush()
:刷新该流的缓冲。如果想立刻将缓存区中的数据写入输出流,需要调用此方法
2.3 举例
1. 向指定磁盘文件中写入数据并通过BufferedWriter
类将文件中的信息分行显示。
public class Example7_3 {
public static void main(String[] args) throws Exception {
//向指定磁盘文件写数据
String[] content = {"竹杖芒鞋轻胜马", "谁怕", "一蓑烟雨任平生"};
File file = new File("1.txt");//创建文件对象
FileWriter fileWriter = new FileWriter(file);//创建FileWriter类对象
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);//创建BufferedWriter类别对象
for (int i = 0; i < content.length; i++) {
bufferedWriter.write(content[i]);//将字符串数组中的元素写入到磁盘文件中
bufferedWriter.newLine();//写入一个分行符,数组的元素以单行的形式存在
}
bufferedWriter.close();//关闭BufferedWriter流
fileWriter.close();//关闭FileWriter流
//读取磁盘文件的信息
FileReader fileReader = new FileReader(file);//创建FileReader对象
BufferedReader bufferedReader = new BufferedReader(fileReader);//创建BufferedReader对象
String s = null;//用于文本内容判断
int i = 0;
while ((s = bufferedReader.readLine()) != null) {
i++;
System.out.printf("第%d行:%s",i,s);
System.out.println();//换行
}
fileReader.close();
bufferedReader.close();//关闭流是有必要的好习惯
}
}
编译后的结果如下:
第1行:竹杖芒鞋轻胜马
第2行:谁怕
第3行:一蓑烟雨任平生
BufferedReader
将文件中的信息分行显示,并且再工程的根目录下生成一个“1.txt”的文档。
八、 数据流
前面提到的常用的缓冲流中的 DataInputStream
、DataOutputStream
就是本节的主角了。他们两即使缓冲流,也称为数据输入流和数据输出流。 DataInputStream
、DataOutputStream
允许程序读取一个数值时,不必再关心数值的应当是多少字节。
1. DataInputStream
构造方法有:
public DataInputStream(InputStream in)
: 创建的数据输入流指向一个由参数in指定的输入流
成员方法有:
close()
:关闭流boolean readBoolean()
:读取一个布尔值byte readByte()
:读取一个字节char readChar()
:读取一个字符double readDouble()
:读取一个双精度浮点值float readFloat()
:读取一个单精度浮点值int readInt()
:读取一个整型值short readShort()
:读取一个短整型值long readLong()
:读取一个长整型值int readUnsignedByte()
:读取一个无符号短整型值int readUnsignedShort()
:读取一个UTF字符串String readUTF()
:读取一个UTF字符串int skipBytes(int n)
:跳过给定数量的字节
2. DataOutputStream
构造方法有:
public DataInputStream(InputStream in)
: 创建的数据输入流指向一个由参数in指定的输入流
成员方法有:
close()
:关闭流void writeBoolean(boolean v)
:写入一个布尔值void byte writeBytes(String s)
:写入一个字符串void char writeChars(String s)
:写入字符串void double writeFloat(float v)
:写入一个单精度浮点值void writeDouble(double v)
:写入一个双精度浮点值void writeInt(int v)
:写入一个整型值void writeLong(long v))
:写入一个长整型值void writeShort(int v)
:写入一个短整型值void writeUTF(String str
:写入一个UTF字符串
3. 举例
例1:写入几个Java类型数据到一个文件中,然后再读出来。
public class Example7_4 {
public static void main(String[] args) throws Exception {
File file = new File("2.txt");//创建文件对象
//写数据,需要用到输出流
FileOutputStream fileOutputStream = new FileOutputStream(file);//创建FileOutputStream对象
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);//创建DataOutputStream对象
dataOutputStream.writeInt(123);//写入一个整数值
dataOutputStream.writeLong(123456);//写入一个长整数型
dataOutputStream.writeFloat(3.14234556f);//写入一个单精度浮点值,需要加f后缀
dataOutputStream.writeDouble(3.1415926);//写入一个单精度浮点值
dataOutputStream.writeUTF("Hello Word");//写入UTF字符串
dataOutputStream.close();//及时关闭数据输出流
//读取数据,需要用到数据输入流
FileInputStream fileInputStream = new FileInputStream(file);
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
System.out.println(dataInputStream.readInt());//读取int数据
System.out.println(dataInputStream.readLong());//读取int数据
System.out.println(dataInputStream.readFloat());//读取int数据
System.out.println(dataInputStream.readDouble());//读取int数据
System.out.println(dataInputStream.readUTF());//读取int数据
}
}
程序编译后输出结果为:
123
123456
3.1423457
3.1415926
Hello Word
例2:将字符串加密后写入文件,然后读取该文件并解密内容
public class Example7_5 {
public static void main(String[] args) throws Exception{
//首先要写入一串加密的字符串,考虑定义一个字符串,然后加密后返回加密后的字串
String command = new String("真相永远只有一个");//定义明文
String password = "Hello Word";//定义加解密的密码
//加密算法
char[] c =command.toCharArray();//将明文字符串转化为字符型数组
char[] p = password.toCharArray();//将密码字符串转化为字符型数组
int j = p.length;
for(int i = 0;i<c.length;i++){
c[i]=(char)(c[i]+p[i%j]);;
}
String secret = new String(c);
File file = new File("3.txt");
//写数据,需要用到输出流
FileOutputStream fileOutputStream = new FileOutputStream(file);//创建FileOutputStream对象
DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream);//创建DataOutputStream对象
dataOutputStream.writeUTF(secret);//写入一个加密后的字符串
dataOutputStream.close();
//读取数据,需要用到数据输入流
FileInputStream fileInputStream = new FileInputStream(file);
DataInputStream dataInputStream = new DataInputStream(fileInputStream);
String str = dataInputStream.readUTF();
System.out.println("加密后的内容为:"+str);//此时读取的是加密后的内容
//解密算法
for(int i = 0;i<c.length;i++){
c[i]=(char)(c[i]-p[i%j]);
}
System.out.println("实际写入的内容为:"+new String(c));//返回明文
fileInputStream.close();
}
}
程序运行后,输出结果为:
加密后的内容为:睧睝沤遈呙朩乗亙
实际写入的内容为:真相永远只有一个
九、 对象的串行化(序列化)
1. 什么是串行化?
前面的各种流在创建时,都会先生成流对象,通过流对象对文件内容的进行一番操作。首先我们要知道对象的寿命通常随着生成该对象的程序的终止而终止:我们创建出来的这些对象都存在于JVM中的堆内存中(参考字符串这一文),只有当JVM处于运行状态的时候,这些对象才可能存在,一旦JVM停止,这些对象也就随之消失。
但是在实际应用场景中,有些对象需要一直存在,需要随用随取,这个时候我们就需要“持久化”的对象了,“持久化”的对象就是对象的状态会保存下来,在需要时再将对象恢复,即对象的持续性(
persistence):对象的这种能记录自己的状态以便将来再生的能力。
对象通过写出描述自己状态的数值来记录自己 ,这个过程叫对象的串行化(Serialization)
。串行化的主要任务是写出对象实例变量的数值。如果变量是另一对象的引用,则引用的对象也要串行化。
2. 串行化的方法
Java提供了对象串行化的机制,在java.io包中定义了一些接口和类作为对象串行化的工具。
2.2.1 Serializable接口
只有实现 Serializable
接口的对象才能被串行化工具存储和恢复。 Serializable
接口没有定义任何成员,它只用来表示一个类可以被串行化。如果一个类可以被串行化,他的所有子类都可以被串行化。
2.2.2 Externalizable接口
Java的串行化和反串行化的工具都被涉及成自动存储和恢复对象的状态。在某些情况下,程序员必须控制这些过程,例如在需要压缩或加密技术时Externalizable
接口的则是为了这些情况涉及。
Externalizable
接口定义了两个方法:
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
:in是对象被读取的字节流void writeExternal(ObjectOutput out) throws IOException
:out是对象被写入的字节流
2.2.3 ObjectOutput接口
ObjectOutput
接口 继承DataOutput接口并支持对象串行化。ObjectOutput
接口 的writeObject()
可以输出一个对象,如:
- void writeObject(Object obj):向流中写入对象obj,出错会引发
IOException
异常
2.2.4 ObjectOutputStream类
ObjectOutputStream
类继承OutputStream
类,实现 ObjectOutput
,ObjectStreamConstants
接口,它负责向流写入对象,该类声明格式为:
public class ObjectOutputStream
extends OutputStream implements ObjectOutput, ObjectStreamConstants
ObjectStreamConstants
中定义了一些常量,在串行化时可以把这些常量写入输出流。
ObjectOutputStream
类的构造函数如下:
ObjectOutputStream(OutputStream out) throws IOException
: 参数out表示串行化的对象将要写入的输出流。
ObjectOutputStream
类中最常用的方法为:
close():
关闭流flush():
刷新输出缓存区write(byte b[]):
向流写入一个字节数组write(byte b[], int off, int len):
向流写入数组b中从off位置开始的len个字节长度区域的数据write(int b):
向流写入单个字节,写入的时b的低位字节writeBoolean(boolean val):
向流写入一个布尔值writeByte(int b):
向流写入字节,写入的时b的低位字节writeBytes(String s):
把str转化为字节输出writeChar():
向流写入字符型值writeChars(String str):
把str转化为字符数组输出writeDouble(double val):
向流写入双精度值writeFloat(float val):
向流写入浮点数writeInt(int i):
向流写入整型数writeLong(long val):
向流写入长整型数writeObject(Object obj):
向流写入对象writeShort(int i):
向流写入short型
2.2.5 ObjectInput接口
ObjectInput
接口 继承DataInput接口并支持对象反串行化。ObjectOutput
接口 的readObject()
的作用是从流中读取一个对象,如:
- void readObject(Object obj):从流中读取对象obj,出错会引发
IOException
异常
2.2.6 ObjectInputStream类
ObjectInputStream
类继承InputStream
类,实现 ObjectInput
,ObjectStreamConstants
接口,它负责向流读取对象,该类声明格式为:
public class ObjectInputStream
extends InputStream implements ObjectInput, ObjectStreamConstants
ObjectInputStream
类的构造函数如下:
public ObjectInputStream(InputStream in) throws IOException
: 参数in表示串行化的对象将被读取的输入流
ObjectInputStream
类中最常用的方法为:
close():
关闭流int available() :
返回输入流可访问的字节数int read():
返回表示下一个输入字节的整数,遇到文件末尾返回-1int read(byte b[], int off, int len):
试图读取len个字节长度区域的数据到数组b中从off位置开始,返回实际成功读取的字节数,遇到文件末尾返回-1boolean readBoolean():
从流读取并返回一个boolean值byte readByte() :
从流读取并返回一个byte型值char readChar() :
从流读取并返回一个char 型值double readDouble() :
从流读取并返回一个double 型值float readFloat():
从流读取并返回一个float 型值void readFully(byte b[]):
读取b.length个字节到b中,当所有字节都能被读取后返回void readFully(byte b[], int off, int len):
读取len个字节到b中以off为起点的位置,当len个字节都能被读取后返回int readInt():
从流中读取并返回int型值long readLong():
从流中读取并返回long型值short readShort() :
从流中读取并返回short 型值Object readObject():
从流中读取并返回一个对象int readUnsignedByte() :
从流中读取并返回一个无符号byte对象int readUnsignedShort() :
从流中读取并返回一个无符号short对象
3. 串行化的注意点
- 串行化只能保存对象的非静态成员变量,不能保存变量的修饰符
transient
关键字:某些类型的变量,其状态是瞬时的,无法或无须保存其状态,对于这些变量,可以用transient
关键字标明- 对于一些需要保密的变量,为了保证其安全性,不应该串行化,也可以在其前面加上
transient
关键字
4. 串行化举例
(1)定义一个可串行化对象:被串行化对象必须实现Serializable
接口
class Example7_6 implements Serializable {
private static final long serialVersionUID = 1L;
int id;
String name;
int age;
String department;
public Example7_6(int id,String name,int age,String department){
this.id = id;
this.name = name;
this.age = age;
this.department = department;
}
}
(2)构造对象输入输出流:要串行化对象,必须与对象的输入输出联系起来,通过write方法串行化对象,通过readObject方法反串行化对象
/**
* @Author: qp
* @Time: 2021/9/28 22:50
* @Description
*/
public class Example7_6 implements Serializable {
public static void main(String[] args) throws Exception{
Student student = new Student(20160926,"qp",18,"计算机学院");
FileOutputStream fileOutputStream= new FileOutputStream("stu.txt");//构造文件输出流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(student);//向文件输出流写入student对象
objectOutputStream.close();
student = null; //让对象释放内存空间
FileInputStream fileInputStream = new FileInputStream("stu.txt");//构造文件输入流
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);//读入对象
student =(Student) objectInputStream.readObject();//从输入流读取返回的的输出流里面的学生对象
objectInputStream.close();
System.out.printf("学生信息:ID:%s,姓名:%s,年龄:%d,学院:%s",student.id,student.name,student.age,student.department);;
}
}
class Student implements Serializable{
private static final long serialVersionUID = 1L;
int id;
String name;
int age;
String department;
public Student(int id,String name,int age,String department){
this.id = id;
this.name = name;
this.age = age;
this.department = department;
}
}
输出结果为:
学生信息:ID:20160926,姓名:qp,年龄:18,学院:计算机学院
可以看出,通过串行化机制保存和恢复了对象的状态,串行化过程是由writeObject()
方法自动进行的,如果向明确控制对象实例变量的写入顺序、写入种类姐二写入方式,必须自定义writeObject()
方法和readObject()
方法,定义自己的数据流读写方式。