五十七、IO流
1、IO流概述
IO流,用于读写文件中的数据,IO流是以程序(内存)为参照,在读取数据,或者写入数据
Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作,输入也叫做读取数据,输出也叫做作写出数据
2、IO的分类
根据流的方向分为:输入流和输出流
- 输入流 :把数据从其他设备上读取到内存中的流
- 输出流 :把数据从内存 中写出到其他设备上的流
根据操作的文件类型分为:字节流和字符流
- 字节流 :以字节为单位,读写数据的流,可以操作所有类型的文件
- 字符流 :以字符为单位,读写数据的流,只能操作纯文本文件
3、IO流体系
蓝色框中的为抽象类
4、顶级父类
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流 InputStream | 字节输出流 OutputStream |
字符流 | 字符输入流 Reader | 字符输出流 Writer |
五十八、字节流
1、一切皆为字节
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据
2、字节输出流 OutputStream 抽象类
java.io.OutputStream 抽象类是表示字节输出流的所有类的超(父)类,将指定的字节信息写出到目的地,它定义了字节输出流的基本共性功能方法
- public void close() 关闭此输出流并释放与此流相关联的任何系统资源
- public void flush() 刷新此输出流并强制任何缓冲的输出字节被写出
- public void write(byte[] b) 将 b.length 字节从指定的字节数组写入此输出流
- public void write(byte[] b, int off, int len) 从指定的字节数组写入 len 字节,从偏移量 off 开始输出到此输出流
- public abstract void write(int b) 将指定的字节输出流
close方法,当完成流的操作时,必须调用此方法,释放系统资源
3、FileOutputStream 类
OutputStream有很多子类,我们从最简单的一个子类开始,java.io.FileOutputStream类是文件输出流,用于将数据写出到文件
3.1、构造方法
- public FileOutputStream(File file) 以指定的 File 对象表示路径,默认清空原有数据
- public FileOutputStream(String name) 以指定的字符串表示路径,默认清空原有数据
- public FileOutputStream(File file, boolean append) 以指定的 File 对象表示路径,第二个参数,true 表示追加数据,false 表示清空原有数据
- public FileOutputStream(String name, boolean append) 以指定的字符串表示路径,第二个参数,true 表示追加数据,false 表示清空原有数据
当创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件,但要保证父级路径存在
示例
import java.io.File;import java.io.FileOutputStream;import java.io.IOException;// FileOutputStream把程序中的数据写到本地文件中// 步骤:创建对象、写出数据、释放资源// 创建对象时可以给出第二个参数值,不写默认为false,会清空文件中原有的数据// 设置为true,表示在原有数据后追加写入数据public class ByteStreamOut1 { public static void main(String[] args) throws IOException { // 创建对象fos File f = new File("bb.txt"); // 路径在当前项目下 FileOutputStream fos = new FileOutputStream(f, true); // 不会清空文件,而是以追加的方式写入 // 用write方法写入数据 // write方法的参数是整数,但实际写到文件中的是对应的ASCII码字符 fos.write(97); // 写入的是a // 释放资源 fos.close(); }}
3.2、写出字节数据的三种方式
- void write(int b) 一个写一个字节数据
- void write(byte[] b) 一次写一个字节数组数据
- void write(byte[] b, int off, int len) 一次写入一个字节数组的部分数据
写出字节 write(int b) 方法,每次可以写出一个字节数据
public class FOSWrite { public static void main(String[] args) throws IOException { // 使用文件名称创建流对象 FileOutputStream fos = new FileOutputStream("fos.txt"); // 写出数据 fos.write(97); // 写出第1个字节 fos.write(98); // 写出第2个字节 fos.write(99); // 写出第3个字节 // 关闭资源 fos.close(); }}
- 虽然参数为int类型四个字节,但是只会保留一个字节的信息写出
- 流操作完毕后,必须释放系统资源,调用close方法,千万记得
写出字节数组 write(byte[] b),每次可以写出数组中的数据
public class FOSWrite { public static void main(String[] args) throws IOException { // 使用文件名称创建流对象 FileOutputStream fos = new FileOutputStream("fos.txt"); // 字符串转换为字节数组 byte[] b = "泰酷辣".getBytes(); // 写出字节数组数据 fos.write(b); // 关闭资源 fos.close(); }}
写出指定长度字节数组 write(byte[] b, int off, int len) ,每次写出从off索引开始,len个字节
public class FOSWrite { public static void main(String[] args) throws IOException { // 使用文件名称创建流对象 FileOutputStream fos = new FileOutputStream("fos.txt"); // 字符串转换为字节数组 byte[] b = "abcde".getBytes(); // 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd fos.write(b, 2, 2); // 关闭资源 fos.close(); }}
3.3、写出换行
写出换行
写出一个换行符就可以实现换行
Windows系统,换行符号是rn
Linux系统,换行符号是n
Mac系统,换行符号是r
回车符r和换行符n:
- 回车符:回到一行的开头(return)
- 换行符:下一行(newline)
系统中的换行:
- Windows系统里,每行结尾是 回车+换行 ,即rn;
- Linux系统里,每行结尾只有 换行 ,即n;
- Mac系统里,每行结尾是 回车 ,即r。从 Mac OS X开始与Linux统一
4、字节输入流 InputStream 抽象类
java.io.InputStream 抽象类是表示字节输入流的所有类的超(父)类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法
- public void close() 关闭此输入流并释放与此流相关联的任何系统资源
- public int read() 从输入流读取数据的下一个字节
- public int read(byte[] b) 从输入流中读取一些字节数,并将它们存储到字节数组 b 中
5、FileInputStream 类
java.io.FileInputStream 类是文件输入流,从文件中读取字节
5.1、构造方法
- FileInputStream(File file) 通过打开与实际文件的连接来创建一个 FileInputStream,以指定的 File 对象表示路径
- FileInputStream(String name) 通过打开与实际文件的连接来创建一个 FileInputStream,以指定的字符串表示路径
public class FileInputStreamConstructor throws IOException{ public static void main(String[] args) { // 使用File对象创建流对象 File file = new File("a.txt"); FileInputStream fos = new FileInputStream(file); // 使用文件名称创建流对象 FileInputStream fos = new FileInputStream("b.txt"); }}
5.2、读取字节数据
读取字节:read方法,每次可以读取一个字节的数据,返回 int 类型,读取到文件末尾,返回-1
public class FISRead { public static void main(String[] args) throws IOException{ // 使用文件名称创建流对象 FileInputStream fis = new FileInputStream("read.txt"); // 读取数据,返回一个字节 int read = fis.read(); System.out.println((char)read); read = fis.read(); System.out.println((char)read); read = fis.read(); System.out.println((char)read); read = fis.read(); // …… // 读取到末尾,返回-1 // 关闭资源 fis.close(); }}
循环改进读取方式
public class FISRead { public static void main(String[] args) throws IOException{ // 使用文件名称创建流对象 FileInputStream fis = new FileInputStream("read.txt"); // 定义变量,保存数据 int b; // 循环读取 while ((b = fis.read()) != -1) { System.out.println((char)b); } // 关闭资源 fis.close(); }}
使用字节数组读取:read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1
public class FISRead { public static void main(String[] args) throws IOException{ // 使用文件名称创建流对象 FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde // 定义变量,作为有效个数 int len; // 定义字节数组,作为装字节数据的容器 byte[] b = new byte[2]; // 循环读取 while (( len = fis.read(b)) != -1) { // 每次读取后,把数组变成字符串打印 System.out.println(new String(b)); } // 关闭资源 fis.close(); }} 运行结果:abcded
错误数据d,是由于最后一次读取时,只读取一个字节e,数组中,上次读取的数据没有被完全替换,所以要通过len ,获取有效的字节
public class FISRead { public static void main(String[] args) throws IOException{ // 使用文件名称创建流对象 FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde // 定义变量,作为有效个数 int len; // 定义字节数组,作为装字节数据的容器 byte[] b = new byte[2]; // 循环读取 while (( len = fis.read(b)) != -1) { // 每次读取后,把数组的有效字节部分,变成字符串打印 System.out.println(new String(b, 0, len)); // len为每次读取的有效字节个数 } // 关闭资源 fis.close(); }}运行结果:abcde
使用数组读取,每次读取多个字节,减少了系统间的IO操作次数,从而提高了读写的效率,建议开发中使用
五十九、字符流
1、介绍
当使用字节流读取文本文件时,可能会有一个小问题,就是遇到中文字符时,可能不会显示完整的字符,那是因为一个中文字符可能占用多个字节存储,所以Java提供一些字符流类,以字符为单位读写数据,专门用于处理纯文本文件
为什么会有乱码
- 读取数据时未读完整个汉字
- 编码和解码的方式不统一
字符流
字符流的底层其实就是字节流
字符流 = 字节路 + 字符集
特点
- 输入流:一次读一个字节,遇到中文时,一次读多个字节
- 输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中
2、字符输入流 Reader 抽象类
java.io.Reader抽象类是表示用于读取字符流的所有类的超(父)类,可以读取字符信息到内存中,它定义了字符输入流的基本共性功能方法
读取数据
public int read() 从输入流读取一个字符,读到末尾返回-1
public int read(char[] cbuf) 从输入流中读取一些字符,并将它们存储到字符数组 cbuf 中,读到末尾返回-1
释放资源
public void close() 关闭此流并释放与此流相关联的任何系统资源
3、FileReader 类
java.io.FileReader 类是读取字符文件的便利类,构造时使用系统默认的字符编码和默认字节缓冲区
- 字符编码:字节与字符的对应规则,Windows系统的中文编码默认是GBK编码表,idea中为UTF-8
- 字节缓冲区:一个字节数组,用来临时存储字节数据
3.1、构造方法
- FileReader(File file) 创建字符输入流关联本地文件
- FileReader(String fileName) 创建字符输入流关联本地文件
示例
public class FileReaderConstructor throws IOException{ public static void main(String[] args) { // 使用File对象创建流对象 File file = new File("a.txt"); FileReader fr = new FileReader(file); // 使用文件名称(路径)创建流对象 FileReader fr = new FileReader("b.txt"); }}
3.2、读取字符数据
读取字符:int read()方法,按字节一个一个的读取,遇到中文,一次读多个字节,读取后解码,并返回一个十进制整数,这个十进制的整数也表示在字符集上的数字,如果想看到中文汉字,需要把这些十进制数据强制转换,当读取到文件末尾,返回-1
import java.io.FileReader;import java.io.IOException;public class CharStreamDemo1 { public static void main(String[] args) throws IOException { // 创建对象、读取数据、释放资源 // 创建对象 FileReader fr = new FileReader("aaa.txt"); // 读取数据 System.out.print(fr.read()); // 返回十进制数 int ch; while ((ch = fr.read()) != -1) { System.out.print((char)ch); // 强制转换读取的数据才能看到汉字 } // 释放资源 fr.close(); }}运行结果:20320好,阿里嘎多!
使用字符数组读取:int read(char[] cbuf),每次读取 cbuf.length 个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1
import java.io.FileReader;import java.io.IOException;public class CharStreamDemo2 { public static void main(String[] args) throws IOException { // 创建对象 FileReader fr = new FileReader("aaa.txt"); // 读取数据 char[] chars = new char[2]; int len; while ((len = fr.read(chars)) != -1) { //把数组中的数据变成字符串再打印 System.out.print(new String(chars, 0, len)); } // 释放资源 fr.close(); }}运行结果:你好,阿里嘎多!
两个read方法对比
int read():空参的read方法是将读取到的数据解码为十进制就返回了,想把十进制变为对应的字符,需要一个强制转换
int read(char[] cbuf):有参的read方法是将读取数据、解码、和强转三步进行合并了,然后把强换后的字符放进数组中
4、字符输出流 Writer 抽象类
java.io.Writer 抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地,它定义了字节输出流的基本共性功能方法
- void write(int c) 写入单个字符
- void write(char[] cbuf) 写入字符数组
- void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off表示数组的开始索引,len表示写的字符个数
- void write(String str) 写入字符串
- void write(String str, int off, int len) 写入字符串的某一部分,off表示字符串的开始索引,len表示写的字符个数
- void flush() 刷新该流的缓冲
- void close() 先刷新缓冲区,然后关闭此流
5、FileWriter 类
java.io.FileWriter 类是写出字符到文件的便利类,构造时使用系统默认的字符编码和默认字节缓冲区
5.1、构造方法
- FileWriter(File file) 创建一个新的 FileWriter 关联本地文件
- FileWriter(String fileName) 创建一个新的 FileWriter 关联本地文件
- FileWriter(File file, boolean append) 创建一个新的 FileWriter 关联本地文件,续写
- FileWriter(String fileName, boolean append) 创建一个新的 FileWriter 关联本地文件,续写
示例
public class FileWriterConstructor { public static void main(String[] args) throws IOException { // 使用File对象创建流对象 File file = new File("a.txt"); FileWriter fw = new FileWriter(file); // 使用文件名称(路径)创建流对象 FileWriter fw = new FileWriter("b.txt"); }}
5.2、写出字符数据
写出字符:void write(int b) 方法,每次可以写出一个字符数据
public class FWWrite { public static void main(String[] args) throws IOException { // 使用文件名称创建流对象 FileWriter fw = new FileWriter("fw.txt"); // 写出数据 fw.write(97); // 写出第1个字符 fw.write('b'); // 写出第2个字符 fw.write('C'); // 写出第3个字符 fw.write(30000); // 写出第4个字符,中文编码表中30000对应一个汉字 fw.close(); }}写到文件中的数据:abC田
- 虽然参数为int类型四个字节,但是只会保留一个字符的信息写出
- 若不调用close方法,数据只是保存到了缓冲区,不会写出到文件中
写出字符数组 :void write(char[] cbuf) 和 write(char[] cbuf, int off, int len) ,每次可以写出字符数组中的数据,用法类似FileOutputStream
import java.io.FileWriter;import java.io.IOException;public class CharStreamDemo5 { public static void main(String[] args) throws IOException { // 使用文件名称创建流对象 FileWriter fw = new FileWriter("fw2.txt"); // 字符串转换为字节数组 char[] chars = "天气真好".toCharArray(); // 写出完整的字符数组 fw.write(chars); // char占两个字节,写出从索引2开始,索引2是'真',两个长度,也就是'真好' fw.write(chars, 2, 2); // 关闭资源 fw.close(); }}运行后文件中的内容:天气真好真好
写出字符串:write(String str) 和 write(String str, int off, int len) ,每次可以写出字符串中的数据,更为方便,用得最多
import java.io.FileWriter;import java.io.IOException;public class CharStreamDemo6 { public static void main(String[] args) throws IOException { // 使用文件名(路径)创建流对象 FileWriter fw = new FileWriter("fw3.txt"); // 写出字符串 String str = "这件事情真的"; fw.write(str); // 从索引4开始,写5个字符 fw.write("这件事情泰酷la!", 4, 5); // 刷新缓冲区并关闭资源 fw.close(); }}运行后文件中的内容:这件事情真的泰酷la!
续写和换行:操作类似于FileOutputStream
public class FWWrite { public static void main(String[] args) throws IOException { // 使用文件名称创建流对象,可以续写数据 FileWriter fw = new FileWriter("fw.txt",true); // 写出字符串 fw.write("明天"); // 写出换行 fw.write("rn"); // 写出字符串 fw.write("你好"); // 关闭资源 fw.close(); }}输出结果:明天你好
字符流,只能操作文本文件,不能操作图片,视频等非文本文件
当我们单纯读或者写文本文件时 使用字符流 其他情况使用字节流
5.3、关闭和刷新
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中,但是关闭的流对象,是无法继续写出数据的,如果我们既想写出数据,又想继续使用流,就需要 flush 方法了
- flush 刷新缓冲区,流对象可以继续使用
- close 先刷新缓冲区,然后通知系统释放资源,流对象不可以再被使用了
示例
public class FWWrite { public static void main(String[] args) throws IOException { // 使用文件名称创建流对象 FileWriter fw = new FileWriter("fw.txt"); // 写出数据,通过flush fw.write('刷'); // 写出第1个字符 fw.flush(); fw.write('新'); // 继续写出第2个字符,写出成功 fw.flush(); // 写出数据,通过close fw.write('关'); // 写出第1个字符 fw.close(); fw.write('闭'); // 继续写出第2个字符,【报错】java.io.IOException: Stream closed fw.close(); }}
即便是flush方法写出了数据,操作的最后还是要调用close方法,释放系统资源
六十、缓冲流
1、缓冲流介绍
缓冲流是高级流,可以提高读写的效率,字符流的基本流中已经有了缓冲区,所以 BufferedReader 和 BufferedWriter 两个缓冲流提高的效率不是很明显,但它们里面有两个特有的方法,很好用
2、字节缓冲流
字节缓冲流底层自带了长度为 8192 的缓冲区(字节流为byte类型,8k字节),可以显著提高字节流的读写性能
2.1、构造方法
- public BufferedInputStream(InputStream in) 字节缓冲输入流,把基本流包装成高级流,提高读取数据的性能
- public BufferedOutputStream(OutputStream out) 字节缓冲输出流,把基本流包装成高级流,提高读取数据的性能
示例
// 创建字节缓冲输入流BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));// 创建字节缓冲输出流BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
3、字符缓冲流
字符缓冲流底层自带了长度为 8192 的缓冲区(字符流为char型,16k字节),因为字符流的基本流中已经有了缓冲区,所以字符缓冲流提高的效率不是很明显,但它们里面有两个非常好用的方法
3.1、构造方法
- public BufferedReader(Reader in) 创建一个缓冲输入流,把基本流变成高级流
- public BufferedWriter(Writer out) 创建一个缓冲输出流,把基本流变成高级流
示例
// 创建字符缓冲输入流BufferedReader br = new BufferedReader(new FileReader("br.txt"));// 创建字符缓冲输出流BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
3.1、特有方法
- BufferedReader:public String readLine() 一次读一行数据,遇到回车换行结束,不会把回车换行读到内存中,如果没有数据可读,返回null
- BufferedWriter:public void newLine() 跨平台的换行,会先判断你的操作系统,再写出相应的换行符
public String readLine()方法示例
import java.io.BufferedReader;import java.io.FileReader;import java.io.IOException;public class BufferedStreamDemo1 { public static void main(String[] args) throws IOException { // 创建流对象 BufferedReader br = new BufferedReader(new FileReader("in.txt")); // 定义字符串,保存读取的一行文字 String line = null; // 循环读取,读取到最后返回null while ((line = br.readLine()) != null) { System.out.println(line); } // 释放资源 br.close(); }}
public void newLine()方法示例
import java.io.BufferedWriter;import java.io.FileWriter;import java.io.IOException;public class BufferedStreamDemo2 { public static void main(String[] args) throws IOException { // 创建字符缓冲输出流对象 BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt")); // 写出数据 bw.write("123"); bw.newLine(); // 换行 bw.write("456"); // 刷新缓冲区并关闭文件 bw.close(); }}运行后文件中的内容:123456
六十一、转换流
1、概述
转换流属于字符流,也是高级流,用来包装基本流,是字节流和字符流之间的桥梁
在IO流体系中的位置
转换输入流与转换输出流图示
转换输入流:InputStreamReader把字节流转换成字符流,包装了字节输入流InputStream,包装后就变成字符输入流
转换输出流:OutputStreamWriter把字符流转换成字节流,包装了字节输出流OutputStream,包装后就变成字节输出流
作用
- 以指定字符集读写数据(JDK11可以被替代)
- 字节流想使用字符流中的方法,就可以使用转换流转换
2、转换输入流 InputStreamReader 类
转换流 java.io.InputStreamReader,是 Reader 的子类,是从字节流到字符流的桥梁,它读取字节,并使用指定的字符集将其解码为字符,它的字符集可以由名称指定,也可以接受平台的默认字符集
2.1、构造方法
- InputStreamReader(InputStream in) 创建一个使用默认字符集的字符流
- InputStreamReader(InputStream in, String charsetName) 创建一个指定字符集的字符流
示例
InputStreamReader isr1 = new InputStreamReader(new FileInputStream("in.txt"));InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt"), "GBK");
2.1、指定编码读取
使用InputStreamReader(InputStream in, String charsetName)方法读取
import java.io.FileInputStream;import java.io.IOException;import java.io.InputStreamReader;public class ConvertStreamDemo1 { public static void main(String[] args) throws IOException { // 创建输入转换流对象,指定以GBK编码格式读取 InputStreamReader isr = new InputStreamReader(new FileInputStream("GBK.txt"), "GBK"); // 读取数据 int ch; while ((ch = isr.read()) != -1) { System.out.print((char)ch); } // 关闭资源 isr.close(); }}运行结果:你厉害,我给你大拇哥!
上面这种方式在JDK11以后被淘汰,替代方案是使用 FileReader 类,它里面添加了一个新的构造,这个构造可以指定字符编码
import java.io.FileReader;import java.io.IOException;import java.nio.charset.Charset;public class ConvertStreamDemo2 { public static void main(String[] args) throws IOException { // 创建FileReader对象,指定以GBK编码格式读取,这个构造方法在JDK11后才有 FileReader fr = new FileReader("GBK.txt", Charset.forName("GBK")); // 读取数据 int ch; while ((ch = fr.read()) != -1) { System.out.print((char)ch); } // 关闭资源 fr.close(); }}运行结果:你厉害,我给你大拇哥!
3、转换输出流 OutputStreamWriter 类
转换流 java.io.OutputStreamWriter ,是 Writer 的子类,是从字符流到字节流的桥梁,使用指定的字符集将字符编码为字节,它的字符集可以由名称指定,也可以接受平台的默认字符集
3.1、构造方法
- OutputStreamWriter(OutputStream in) 创建一个使用默认字符集的字符流
- OutputStreamWriter(OutputStream in, String charsetName) 创建一个指定字符集的字符流
示例
OutputStreamWriter osw1 = new OutputStreamWriter(new FileOutputStream("out.txt"));OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("out.txt"), "GBK");
3.2、指定编码写出
import java.io.*;public class ConvertStreamDemo3 { public static void main(String[] args) throws IOException { // 创建OutputStreamWriter对象,指定以GBK编码格式写出 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"), "GBK"); // 写出数据 osw.write("泰酷辣!"); // 释放资源 osw.close(); }}
同样地,上面这种方式在JDK11以后被淘汰,替代方案是使用 FileWriterr 类,它里面添加了一个新的构造,这个构造可以指定字符编码
import java.io.FileWriter;import java.io.IOException;import java.nio.charset.Charset;public class ConvertStreamDemo4 { public static void main(String[] args) throws IOException { // 创建FiledWriter对象,指定以GBK编码格式写出 FileWriter fw = new FileWriter("a.txt", Charset.forName("GBK")); // 写出数据 fw.write("泰酷辣!"); // 释放资源 fw.close(); }}
六十二、序列化流和反序列化流
1、概述
-
Java 提供了一种对象序列化的机制,用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息,字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息;反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化,对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象
-
序列化流属于字节流,也是高级流,可以把Java中的对象写到本地文件中,写到文件中的数据我们是看不懂的,但可以用反序列化流将数据读取出来
在IO流体系中的位置
序列化流和反序列化流图示
2、序列化流 ObjectOutputStream 类
序列化流也叫对象操作输出流,java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储
2.1、构造方法
public ObjectOutputStream(OutputStream out) 创建 ObjectOutputStream 对象,把基本流包装成高级流
示例
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
2.2、序列化操作
一个对象要想序列化,必须满足两个条件
- 该类必须实现 java.io.Serializable 接口,Serializable 是一个标记接口(里面没有抽象方法),只有实现了这个接口,该类才可以被序列化
- 该类的所有属性必须是可序列化的,如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用 transient 关键字修饰
写出对象的方法
public final void writeObject (Object obj) 将指定的对象序列化(写出)到文件中
import java.io.*;public class ObjectStreamDemo1 { public static void main(String[] args) throws IOException { // 创建Student类对象 Student stu = new Student(18, "西施"); // 创建ObjectOutputStream类对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("bb.txt")); // 写出数据 oos.writeObject(stu); // 释放资源 oos.close(); }}// Student类实现Serializable接口class Student implements Serializable { private int age; private String name; public Student() { } public Student(int age, String name) { this.age = age; this.name = name; } @Override public String toString() { return "Student{" + "age=" + age + ", name='" + name + ''' + '}'; }}
3、反序列化流 ObjectInputStream 类
ObjectInputStream 反序列化流,将之前使用 ObjectOutputStream 序列化的原始数据恢复为对象
3.1、构造方法
public ObjectInputStream(InputStream in) 创建一个 ObjectInputStream 对象
3.2、反序列化操作
读取对象的方法
public final Object readObject ()
import java.io.FileInputStream;import java.io.IOException;import java.io.ObjectInputStream;public class ObjectStreamDemo2 { public static void main(String[] args) throws IOException, ClassNotFoundException { // 创建ObjectInputStream对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("bb.txt")); // 读取数据 Object obj = ois.readObject(); // 打印对象 System.out.println(obj); // 释放资源 ois.close(); }}运行结果:Student{age=18, name='西施'}
3.3、注意事项
**另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。**发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
- 该类没有可访问的无参数构造方法
Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
六十三、打印流
1、概述
打印流是高级流,也是用来包装基本流的,打印流只能写,不能读,所以只有输出流。平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式
2、字节打印流 PrintStream 类
构造方法
构造方法 | 说明 |
---|---|
public PrintStream(OutputStream/File/String name) | 关联字节输出流/文件/文件路径 |
public PrintStream(String fileName, Charset charset) | 指定字符编码 |
public PrintStream(OutputStream out, boolean autoFlush) | 自动刷新(字节流底层没有缓冲区,开不开和自动刷新都一样,下面这个方法同理) |
public PrintStream(OutputStream out, boolean autoFlush, String encoding) | 指定字符编码且自动刷新 |
成员方法
成员方法 | 说明 |
---|---|
public void write(int b) | 常规方法,将指定的字节写出 |
public void println(Xxx x) | 特有方法,打印任意数据,自动刷新,自动换行 |
public void print(Xxx x) | 特有方法,打印任意数据,不换行 |
public void printf(String format, Object… args) | 特有方法,带有占位符的打印语句,不换行 |
3、字符打印流 PrintWriter 类
构造方法
构造方法 | 说明 |
---|---|
public PrintWriter(Write/File/String) | 关联输出流/文件/文件路径 |
public PrintWriter(String fileName, Charset charset) | 指定字符集编码 |
public PrintWriter(Writer w, boolean atuoFlush) | 自动刷新 |
public PrintWriter(OutputStream out, boolean autoFlush, Charset charset) | 指定字符编码且自动刷新 |
成员方法
成员方法 | 说明 |
---|---|
public void write(…) | 常规方法,写出字节或字符串 |
public void println(Xxx x) | 特有方法,打印任意数据,自动刷新,自动换行 |
public void print(Xxx x) | 特有方法,打印任意数据,不换行 |
public void printf(String format, Object… args) | 特有方法,带有占位符的打印语句,不换行 |
4、打印流的应用场景
System.out.println()
import java.io.PrintStream;public class Demo1 { public static void main(String[] args) { // 获取打印流的对象,此打印流在虚拟机启动的时候,由虚拟机创建,默认指向控制台 // 该打印流也叫系统中的标准输出流,这个流不能关闭,在系统中是唯一的 PrintStream ps = System.out; // 调用方法 ps.println("123"); // 以上代码相当于下面这行代码 System.out.println("123"); // System.out就是在获取一个打印流对象 // 在虚拟机启动时由虚拟机自动创建,默认指向控制台 // .println就是调用方法打印 }}
六十四、解压缩流和压缩流
1、概述
- 解压缩流
主要是读取压缩包中的文件,属于输入流 - 压缩流
把文件中的数据写到压缩包中,属于输出流
2、解压缩流 ZipInputStream
压缩包里的每一个文件,都是一个ZipyEntry类的对象
Java中,只能识别zip格式的压缩文件
解压本质
把压缩包里每一个文件或文件夹读取出来,按照层级拷贝到另一个文件夹中
3、压缩流 ZipOutputStream
压缩就是把多个文件或多个文件夹变成一个压缩包,压缩包里的每一个文件或文件夹都是一个ZipEntry类的对象
压缩本质
把每一个文件或文件夹看成ZipEntry类的对象并放入到压缩包中
The End