文章目录
- 前置内容:字符集
- 一、IO 流概述
- 二、字节流
- 2.1 文件字节输入流 FileInputStream
- 2.1.1 案例:每次读取一个字节
- 2.1.2 案例:每次读取一个字节数组
- 2.1.3 案例:读取文件的全部字节
- 2.2 文件字节输出流 FileOutputStream
- 2.3 文件拷贝
- 2.4 资源释放的方式
- 2.4.1 try-catch-finally
- 2.4.2 try-with-resource
- 三、字符流
- 3.1 文件字符输入流 FileReader
- 3.2 文件字符输出流 FileWriter
- 四、缓冲流
- 4.1 字节缓冲流
- 4.1.1 字节缓冲输入流 BufferedInputStream
- 4.1.2 字节缓冲输出流 BufferedOutputStream
- 4.2 字符缓冲流
- 4.2.1 字符缓冲输入流 BufferedReader
- 4.2.2 字符缓冲输出流 BufferedWritter
- 五、转换流
- 5.1 字符输入转换流 InputStreamReader
- 5.2 字符输出转换流 OutputStreamWrite
- 六、序列化流
- 6.1 对象序列化与反序列化
- 6.2 对象字节输出流 ObjectOutputStream
- 6.3 对象字节输入流 ObjectInputStream
- 七、打印流
- 7.1 PrintStream
- 7.2 PrintWriter
- 7.3 PrintStream 和 PrintWriter 的区别
- 补充:Commons-IO 框架
前置内容:字符集
字符集(Character Set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同。
常见的字符集有:ASCII、GBK、Unicode(UTF-8)字符集等。
-
ASCII 字符集:包括了数字、英文、符号。
ASCII 字符集中一个字符存储 1 个字节,一个字节是 8 位,总共可以表示 128 个字符信息,对于表示英文、数字来说是够用的。 -
GBK 字符集:是中国的码表,包含了几万个汉字等字符
GBK 字符中一个中文字符一般以 2 个字节的形式存储,同时兼容 ASCII 编码表。 -
Unicode 字符集 ★:又叫统一码、万国码,其容纳了世界上大多数国家的常见文字和字符,是计算机科学领域里的一项业界标准。
UTF-8 是 Unicode 字符集的一种常见编码方式,用于将数字转换成二进制。
UTF-8 编码后一个中文一般以 3 个字节的形式存储,同时兼容 ASCII 编码表。
中文存储和展示流程解析:
注意:
① 英文和数字不会乱码;
② 英文和数字等在任何国家的字符集中都占 1 个字节;
③ 字符解码时使用的字符集和编码时使用的字符集必须一致,否则会出现中文乱码;
字符集的编码、解码操作:
String 编码:
String 解码:
举例:
public class Test {
public static void main(String[] args) throws Exception {
// 1、编码:把文字转换成字节(使用指定的编码)
String name = "abc我爱你中国";
byte[] bytes = name.getBytes(); // 以当前代码默认字符集进行编码 (UTF-8)
//byte[] bytes = name.getBytes("GBK"); // 指定编码
System.out.println(bytes.length); // 18 abc占三个字节,5个汉字各占三个字节,一共 18
System.out.println(Arrays.toString(bytes));
// [97, 98, 99, -26, -120, -111, -25, -120, -79, -28, -67, -96, -28, -72, -83, -27, -101, -67]
// 2、解码:把字节转换成对应的中文形式(编码前 和 编码后的字符集必须一致,否则乱码 )
String rs = new String(bytes); // 默认的UTF-8
// String rs = new String(bytes, "GBK"); // 指定GBK解码
System.out.println(rs); // abc我爱你中国
}
}
一、IO 流概述
I 表示 intput,负责读,把硬盘文件中的数据读入到内存的过程,称之输入。
O 表示 output,负责写,把内存中的数据写出到硬盘文件的过程,称之输出。
IO流的分类:按照流的方向分为输入流与输出流,若按照流中数据的最小单位可分为字节流与字符流。
IO 流的四大类:
① 字节输入流: 以内存为基准,来自磁盘文件/网络中的数据 以字节的形式读入到内存中去。
② 字节输出流: 以内存为基准,把内存中的数据 以字节写出到磁盘文件或者网络中去 。
③ 字符输入流: 以内存为基准,来自磁盘文件/网络中的数据 以字符的形式读入到内存中去 。
④ 字符输出流: 以内存为基准,把内存中的数据 以字符写出到磁盘文件或者网络介质中去。
IO 流的体系如下:
二、字节流
2.1 文件字节输入流 FileInputStream
以内存为基准,来自磁盘文件/网络中的数据 以字节的形式读入到内存 中去。
构造方法:
常用方法:
2.1.1 案例:每次读取一个字节
示例:
public class FileInputStreamDemo01 {
public static void main(String[] args) throws Exception {
// 1、创建一个文件字节输入流管道与源文件接通。
// InputStream is = new FileInputStream(new File("day09-oop/src/data.txt"));// ab3
// 简化写法
InputStream is = new FileInputStream("day09-oop/src/data.txt");
// 2、读取一个字节返回 (每次读取一滴水)
//int b1 = is.read();
System.out.println((char)is.read()); // a
int b2 = is.read();
System.out.println((char)b2); // b
int b3 = is.read();
System.out.println((char)b3); // 3
int b4 = is.read(); // 读取完毕返回-1
System.out.println(b4); // -1
}
}
上文案例的循环改进版本:
public class FileInputStreamDemo01 {
public static void main(String[] args) throws Exception {
// 1、创建一个文件字节输入流管道与源文件接通。
InputStream is = new FileInputStream("day09-oop/src/data.txt");// ab3
// 2、使用循环改进
int b;
while (( b = is.read() ) != -1){
System.out.print((char) b);
}
}
}
问题分析:由于中文字符占3个字节,每次读取一个字符会出现乱码问题。
2.1.2 案例:每次读取一个字节数组
示例2: 每次读取一个字节数组(可以理解为一桶水)
public class FileInputStreamDemo03 {
public static void main(String[] args) throws Exception {
// 1、创建一个文件字节输入流管道与源文件接通
InputStream is = new FileInputStream("day09-oop/src/data02.txt"); // abcdefgh
// 2、定义一个字节数组,用于读取字节数组
byte[] buffer = new byte[3]; // 3B
int len1 = is.read(buffer);
System.out.println("读取了几个字节:" + len1); // 3
String rs1 = new String(buffer);
System.out.println(rs1); // abc
int len2 = is.read(buffer);
System.out.println("读取了几个字节:" + len2); // 3
String rs2 = new String(buffer);
System.out.println(rs2); // def
int len3 = is.read(buffer);
System.out.println("读取了几个字节:" + len3); // 2
// 读取多少倒出多少
String rs3 = new String(buffer,0 ,len2);// gh
String rs4 = new String(buffer); // ghf
System.out.println(rs3);
int len4 = is.read(buffer);
System.out.println(len4); // 读取完毕返回-1
}
}
问题分析1:为什么上文案例当读到 “第三桶水” 的时候,桶里面的水不是 gh,而是 ghf ?
int len3 = is.read(buffer);
System.out.println("读取了几个字节:" + len3); // 2
// 读取多少倒出多少
String rs3 = new String(buffer,0 ,len2);// gh
String rs4 = new String(buffer); // ghf
原因在于:当读完 “第二桶水” 的时候,字节数组 buffer 中装有的水是 def
,当继续从读取的时候,由于只有前两滴水可被读到桶中,因此最终桶里的水只有 ghf
。
上文案例的循环改进版本:
public class FileInputStreamDemo04 {
public static void main(String[] args) throws Exception {
// 1、创建一个文件字节输入流管道与源文件接通
InputStream is = new FileInputStream("day09-oop/src/data02.txt"); // abcdefgh
// 2、循环改进
byte[] buffer = new byte[3];
int len; // 记录每次读取的字节数。
while ((len = is.read(buffer)) != -1) {
// 读取多少倒出多少
System.out.print(new String(buffer, 0 , len));
}
}
}
问题分析2:读取中文字符输出无法避免乱码问题。
例:读取内容为 ab巴s啦啦c
时,每次读取三个字节,会出现乱码问题。
2.1.3 案例:读取文件的全部字节
方式一: 定义一个字节数组与文件的大小一样大,然后使用读取字节数组的方法,一次性读取完成。
方式二: 字节输入流 InputStream 提供了如下 API 可以直接把文件的全部数据读取到一个字节数组中。
public class FileInputStreamDemo05 {
public static void main(String[] args) throws Exception {
// 1、创建一个文件字节输入流管道与源文件接通
File f = new File("day09-oop/src/data3.txt");
InputStream is = new FileInputStream("day09-oop/src/data3.txt");
// 2、定义一个字节数组与文件的大小刚刚一样大。
// byte[] buffer = new byte[(int) f.length()];
// int len = is.read(buffer);
// System.out.println("读取了多少个字节:" + len);
// System.out.println("文件大小:" + f.length());
// System.out.println(new String(buffer));
// 读取全部字节数组
byte[] buffer = is.readAllBytes();
System.out.println(new String(buffer));
}
}
问题分析:不会有中文乱码,但如果文件过大,字节数组可能引起内存溢出。
2.2 文件字节输出流 FileOutputStream
以内存为基准,把内存中的数据 以字节写出到磁盘文件或者网络中去 。
构造方法:
常用方法:
流的刷新与关闭:
示例:
public class OutputStreamDemo01 {
public static void main(String[] args) throws Exception {
// 1、创建一个文件字节输出流管道与目标文件接通
// OutputStream os = new FileOutputStream("day09-oop/src/out04.txt" , true); // 追加数据管道
OutputStream os = new FileOutputStream("day09-oop/src/out04.txt"); // 先清空之前的数据,写新数据进入
// 2、写数据出去
// a.public void write(int a):写一个字节出去
os.write('a'); // 自动转换成整型
os.write(98);
os.write("\r\n".getBytes()); // 添加换行,win和其他系统都支持换行
// os.write('徐'); // [ooo]
// b.public void write(byte[] buffer):写一个字节数组出去。
byte[] buffer = {'a' , 97, 98, 99};
os.write(buffer);
os.write("\r\n".getBytes()); // 添加换行
byte[] buffer2 = "我是中国人".getBytes();
// byte[] buffer2 = "我是中国人".getBytes("GBK");
os.write(buffer2);
os.write("\r\n".getBytes());
// c. public void write(byte[] buffer , int pos , int len):写一个字节数组的一部分出去。
byte[] buffer3 = {'a',97, 98, 99};
os.write(buffer3, 0 , 3);// 左闭右开,写前三个字符
os.write("\r\n".getBytes());
// os.flush(); // 写数据必须,刷新数据 可以继续使用流
os.close(); // 释放资源,包含了刷新的!关闭后流不可以使用流对象
}
}
2.3 文件拷贝
思路:
① 根据数据源创建字节输入流对象。
② 根据目的地创建字节输出流对象。
③ 定义一个字节数组进行数据的转移。
④ 释放资源。
实现:
public class CopyDemo {
public static void main(String[] args) {
try {
// 1、创建一个字节输入流管道与原视频接通
InputStream is = new FileInputStream("day09-oop/src/data.txt");
// 2、创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("day09-oop/src/copy.txt");
// 3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];
int len; // 记录每次读取的字节数。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0 , len);
}
// 4、关闭流。
os.close();
is.close();
} catch (Exception e){
e.printStackTrace();
}
}
}
任何文件的底层都是字节,只要前后文件格式、编码一致没有任何问题,字节流可以完成一切文件数据的拷贝。
总结:字节流适合做文件数据的拷贝,但不适合做中文数据的输出。
2.4 资源释放的方式
2.4.1 try-catch-finally
finally: 放在 try-catch 后面的,无论是正常执行还是异常执行代码,最后一定要执行,除非 JVM 退出。
作用:一般用于进行最后的资源释放操作(专业做法)。
2.4.2 try-with-resource
上文的资源虽然可行,但是释放资源的代码过于繁琐,如下文:
finally{...}
中判断是否为空,是因为有可能在得到流对象之前出现了异常的情况;
finally{...}
中再次 try catch
是因为系统认为可能在上文 try
未结束时已经关闭了流。
JDK 7 和 JDK 9 中都简化了资源释放操作:
JDK7 以及 JDK9 的改进方案中只能放置资源对象,否则报错。(注:资源都是实现了Closeable/AutoCloseable
接口的类对象)
如:
public abstract class InputStream implements Closeable {...}
public abstract class OutputStream implements Closeable, Flushable{...}
示例1: JDK7 做法
public class TryCatchResourceDemo2 {
public static void main(String[] args) {
try (
// 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源
// 即使出现异常也会做关闭操作
// 1、创建一个字节输入流管道与原视频接通
InputStream is = new FileInputStream("day09-oop/src/data.txt");
// 2、创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("day09-oop/src/copy02.txt");
// int age = 23; //报错,这里只能放资源
MyConnection connection = new MyConnection(); // 模拟资源对象,最终会自动调用资源的close方法
) {
// 3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];
int len; // 记录每次读取的字节数。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0 , len);
}
System.out.println("复制完成了!");
} catch (Exception e){
e.printStackTrace();
}
}
}
class MyConnection implements AutoCloseable{
@Override
public void close() throws IOException {
System.out.println("连接资源被成功释放了!");
}
}
示例2: JDK9 做法
public class TryCatchResourceDemo3 {
public static void main(String[] args) throws Exception {
// 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
// 1、创建一个字节输入流管道与原视频接通
InputStream is = new FileInputStream("day09-oop/src/data.txt");
// 2、创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("day09-oop/src/copy03.txt");
MyConn conn = new MyConn(); // 最终会自动调用资源的close方法
try ( is; os; conn ) {
// 3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];
int len; // 记录每次读取的字节数。
while ((len = is.read(buffer)) != -1){
os.write(buffer, 0 , len);
}
System.out.println("复制完成了!");
} catch (Exception e){
e.printStackTrace();
}
}
}
class MyConn implements AutoCloseable{
@Override
public void close() throws IOException {
System.out.println("连接资源被成功释放了~~~");
}
}
三、字符流
字节流适合做文件拷贝,但是读取中文会出现乱码或者内存溢出。
字符流则可以完全解决这个问题,其把中文和英文数字都当成一个字符来处理。
3.1 文件字符输入流 FileReader
以内存为基准,来自磁盘文件/网络中的数据 以字符的形式读入到内存中去 。
构造器:
常用方法:
示例1: 每次读取一个字符
public class FileReaderDemo01 {
public static void main(String[] args) throws Exception {
// 1. 创建一个字符输入流与源文件接通
Reader fr = new FileReader("day09-oop/src/data3.txt");
// 2. 读取一个字符,若没有可读字符返回-1
/*int code = fr.read();
System.out.println((char)code); // 单个中文一个字符
int code1 = fr.read();
System.out.println((char)code1);*/
// 循环读取
int code;
while((code = fr.read()) != -1){
System.out.print((char)code);
}
}
}
注:如果代码文件编码一致,读取中文字符不会出现乱码;读取性能较慢。
示例2: 每次读取一个字符数组
public class FileReaderDemo02 {
public static void main(String[] args) throws Exception {
// 1. 创建一个字符输入流与源文件接通
Reader fr = new FileReader("day09-oop/src/data3.txt");
// 2. 用循环,每次读取一个字符数组的数据
char[] buffer = new char[1024]; // 1K 字符
int len;
while((len = fr.read(buffer)) != -1){
String rs = new String(buffer, 0, len); // 解码并输出
System.out.print(rs);
}
}
}
注:读取的性能得到了提升;读取中文字符输出不会乱码。
3.2 文件字符输出流 FileWriter
以内存为基准,把内存中的数据 以字符写出到磁盘文件或者网络介质中去。
构造器:
常用方法:
流的关闭与刷新:
示例:
public class FileWriterDemo03 {
public static void main(String[] args) throws Exception {
// 1、创建一个字符输出流管道与目标文件接通
Writer fw = new FileWriter("day09-oop/src/out04.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
// Writer fw = new FileWriter("day09-oop/src/out05.txt", true); // 追加数据
// a.public void write(int c):写一个字符出去
fw.write('c');
fw.write(98);
fw.write('爱');
// b.public void write(String c)写一个字符串出去
fw.write("我是中国人");
fw.write("\r\n");
// c.public void write(char[] buffer):写一个字符数组出去
char[] chars = "我是中国人".toCharArray();
fw.write(chars);
// d.public void write(String c ,int pos ,int len):写字符串的一部分出去
fw.write("我是中国人", 0, 3);
// e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
fw.write("我是中国人".toCharArray(), 0, 3);
// fw.flush(); // 刷新后流可以继续使用
fw.close(); // 关闭包含刷线,关闭后流不能使用
}
}
四、缓冲流
缓冲流也称为高效流、或者高级流。之前学习的字节流、字符流可以称为原始流。
作用:缓冲流自带 8KB 缓冲区、可以提高原始字节流、字符流读写数据的性能。
4.1 字节缓冲流
字节缓冲流性能优化原理:
字节缓冲输入流自带了 8KB 缓冲池,以后我们直接从缓冲池读取数据,所以性能较好。
字节缓冲输出流自带了 8KB 缓冲池,数据就直接写入到缓冲池中去,写数据性能极高了。
4.1.1 字节缓冲输入流 BufferedInputStream
字节缓冲输入流:BufferedInputStream,提高字节输入流读取数据的性能。
4.1.2 字节缓冲输出流 BufferedOutputStream
字节缓冲输出流:BufferedOutputStream,提高字节输出流读取数据的性能。
构造方法:
示例:
public class ByteBufferDemo {
public static void main(String[] args) {
try (
// 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
// 1、创建一个字节输入流管道与原视频接通
InputStream is = new FileInputStream("day10-oop/src/csb.txt");
// a.把原始的字节输入流包装成高级的缓冲字节输入流
InputStream bis = new BufferedInputStream(is); // 多态
// 2、创建一个字节输出流管道与目标文件接通
OutputStream os = new FileOutputStream("day10-oop/src/copy01.txt");
// b.把字节输出流管道包装成高级的缓冲字节输出流管道
OutputStream bos = new BufferedOutputStream(os);
) {
// 3、定义一个字节数组转移数据
byte[] buffer = new byte[1024];
int len; // 记录每次读取的字节数。
while ((len = bis.read(buffer)) != -1){
bos.write(buffer, 0 , len);
}
System.out.println("复制完成了!");
} catch (Exception e){
e.printStackTrace();
}
}
}
4.2 字符缓冲流
4.2.1 字符缓冲输入流 BufferedReader
字符缓冲输入流:BufferedReader,可提高字符输入流读取数据的性能,除此之外多了按照行读取数据的功能。
构造器:
独有方法:
示例1:
public class BufferedReaderDemo1 {
public static void main(String[] args) {
try(
// 1、创建一个文件字符输入流与源文件接通。
Reader fr = new FileReader("day10-oop\\src\\csb.txt");
// a、把低级的字符输入流包装成高级的缓冲字符输入流。
BufferedReader br = new BufferedReader(fr); // 多态下调用子类方法,只能强转或不用多态
) {
// 2、用循环,每次读取一个字符数组的数据。 1024 + 1024 + 8
char[] buffer = new char[1024]; // 1K字符
int len;
while ((len = br.read(buffer)) != -1) {
String rs = new String(buffer, 0, len);
System.out.print(rs);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
示例2:
public class BufferedReaderDemo2 {
public static void main(String[] args) {
try(
// 1、创建一个文件字符输入流与源文件接通。
Reader fr = new FileReader("day10-oop\\src\\csb.txt");
// a、把低级的字符输入流包装成高级的缓冲字符输入流。
BufferedReader br = new BufferedReader(fr); // 多态下调用子类方法,只能强转或不用多态
) {
// 2、用循环,每次读取一行数据。
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.2.2 字符缓冲输出流 BufferedWritter
字符缓冲输出流:BufferedWritter,可提高字符输出流写取数据的性能,除此之外多了换行功能。
构造器:
独有方法:
注:特性是由原始管道决定。(如:若想追加数据,在原始管道中使用追加管道数据的构造方法)
示例:
public class BufferedWriterDemo3 {
public static void main(String[] args) throws Exception {
// 1、创建一个字符输出流管道与目标文件接通
// Writer fw = new FileWriter("day10-oop\\src\\data02.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
Writer fw = new FileWriter("day10-oop\\src\\data02.txt", true); // 追加数据
BufferedWriter bw = new BufferedWriter(fw); // 特性是由原始管道决定,因此若想追加数据,也是设置上一行代码
// a.public void write(int c):写一个字符出去
bw.write(98);
bw.write('a');
bw.write('徐');
bw.newLine(); // bw.write("\r\n"); // 换行
// b.public void write(String c)写一个字符串出去
bw.write("abc我是中国人");
bw.newLine(); // bw.write("\r\n"); // 换行
// c.public void write(char[] buffer):写一个字符数组出去
char[] chars = "abc我是中国人".toCharArray();
bw.write(chars);
bw.newLine(); // bw.write("\r\n"); // 换行
// d.public void write(String c ,int pos ,int len):写字符串的一部分出去
bw.write("abc我是中国人", 0, 5);
bw.newLine(); // bw.write("\r\n"); // 换行
// e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
bw.write(chars, 3, 5);
bw.newLine(); // bw.write("\r\n"); // 换行
// fw.flush();// 刷新后流可以继续使用
bw.close(); // 关闭包含刷新,关闭后流不能使用
}
}
五、转换流
上文在使用字符流读取中文的时候,只有在文件和代码的编码一致才不会乱码,但编码不一致会出现乱码,而转换流可解决上述问题。
5.1 字符输入转换流 InputStreamReader
字符输入转换流:InputStreamReader,可以把原始的字节流按照指定编码转换成字符输入流。
构造器:
示例:
public class InputStreamReaderDemo01 {
public static void main(String[] args) throws Exception {
// 1、提取GBK文件的原始字节流。
InputStream is = new FileInputStream("day10-oop\\src\\dataGBK.txt");
// 2、把原始字节流转换成字符输入流
// Reader isr = new InputStreamReader(is); // 默认以UTF-8的方式转换成字符流。 还是会乱码的 跟直接使用FileReader是一样的
Reader isr = new InputStreamReader(is , "GBK"); // 以指定的GBK编码转换成字符输入流 完美的解决了乱码问题
// 3、把低级的字符输入流包装成高级的缓冲字符输入流。
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null){
System.out.println(line); // 正常打印
}
}
}
5.2 字符输出转换流 OutputStreamWrite
字符输出转换流:OutputStreamWriter,可以把字节输出流按照指定编码转换成字符输出流。
构造器:
示例:
public class OutputStreamWriterDemo02 {
public static void main(String[] args) throws Exception {
// 1、定义一个字节输出流
OutputStream os = new FileOutputStream("day10-oop\\src\\demoGBK.txt");
// 2、把原始的字节输出流 转换成 字符输出流
// Writer osw = new OutputStreamWriter(os); // 以默认的UTF-8写字符出去 跟直接写FileWriter一样
Writer osw = new OutputStreamWriter(os , "GBK"); // 指定GBK的方式写字符出去
// 3、把低级的字符输出流包装成高级的缓冲字符输出流。
BufferedWriter bw = new BufferedWriter(osw);
bw.write("我爱中国1~~");
bw.write("我爱中国2~~");
bw.write("我爱中国3~~");
bw.close();
}
}
六、序列化流
6.1 对象序列化与反序列化
对象序列化: 以内存为基准,把内存中的对象存储到磁盘文件中去。使用到的流是对象字节输出流:ObjectOutputStream。
对象反序列化: 以内存为基准,把存储到磁盘文件中去的对象数据恢复成内存中的对象。使用到的流是对象字节输入流:ObjectInputStream
6.2 对象字节输出流 ObjectOutputStream
构造器:
序列化方法:
注意:
① 序列化对象必须实现序列化接口。
② transient 修饰的成员变量不参与序列化。
示例:
public class Student implements Serializable {
// 申明序列化的版本号码
// 序列化的版本号与反序列化的版本号必须一致才不会出错!
private static final long serialVersionUID = 1;
private String name;
private String loginName;
// transient 修饰的成员变量不参与序列化了
private transient String passWord;
private int age ;
...
}
public class ObjectOutputStreamDemo1 {
public static void main(String[] args) throws Exception {
// 1、创建学生对象
Student s = new Student("张三", "zhangsan", "1314520", 21);
// 2、对象序列化:使用对象字节输出流包装字节输出流管道
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day10-oop/src/obj.txt"));
// 3、直接调用序列化方法
oos.writeObject(s);
// 4、释放资源
oos.close();
System.out.println("序列化完成了~~");
}
}
6.3 对象字节输入流 ObjectInputStream
构造器:
序列化方法:
示例:
public class ObjectInputStreamDemo2 {
public static void main(String[] args) throws Exception {
// 1、创建对象字节输入流管道包装低级的字节输入流管道
ObjectInputStream is = new ObjectInputStream(new FileInputStream("day10-oop/src/obj.txt"));
// 2、调用对象字节输入流的反序列化方法
Student stu = (Student) is.readObject();
System.out.println(stu );
}
}
七、打印流
打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream、PrintWriter两个类。
可以实现打印什么数据就是什么数据,如:打印整数 97 写出去就是 97,打印 boolean 的 true,写出去就是 true。
7.1 PrintStream
构造器:
常用方法:
7.2 PrintWriter
构造器:
常用方法:
示例1:
public class PrintDemo1 {
public static void main(String[] args) throws Exception {
// 1、创建一个打印流对象
// PrintStream ps = new PrintStream(new FileOutputStream("day10-oop/src/print.txt"));
PrintStream ps = new PrintStream(new FileOutputStream("day10-oop/src/print.txt" , true)); // 追加数据,在低级管道后面加True
// PrintStream ps = new PrintStream("day10-oop/src/print.txt" );
// PrintWriter pw = new PrintWriter("day10-oop/src/print.txt"); // 打印功能上与PrintStream的使用没有区别
ps.println(97);
ps.println('a');
ps.println(23.3);
ps.println(true);
ps.println("我是打印流输出的,我是啥就打印啥");
ps.close();
}
}
示例2: 改变输出语句的位置
public class PrintDemo2 {
public static void main(String[] args) throws Exception {
System.out.println("锦瑟无端五十弦");
System.out.println("一弦一柱思华年"); // out 是 PrintStream 对象
// 改变输出语句的位置(重定向)
PrintStream ps = new PrintStream("day10-oop/src/log.txt");
System.setOut(ps); // 把系统打印流改成我们自己的打印流
System.out.println("庄生晓梦迷蝴蝶");
System.out.println("望帝春心托杜鹃");
}
}
7.3 PrintStream 和 PrintWriter 的区别
- 打印数据功能上是一模一样的,都是使用方便,性能高效(核心优势)。
- PrintStream 继承自字节输出流 OutputStream,支持写字节数据的方法。
- PrintWriter 继承自字符输出流 Writer,支持写字符数据出去。
补充:Commons-IO 框架
Commons-IO 是 apache 开源基金组织提供的一组有关 IO 操作的类库,可以提高 IO 功能开发的效率。
Commons-IO 工具包提供了很多有关 IO 操作的类。有两个主要的类 FileUtils, IOUtils。
FileUtils 主要有如下方法:
示例:
/**
包的功能描述见下表:
| 包 | 功能描述 |
| ----------------------------------- | :------------------------------------------- |
| org.apache.commons.io | 有关Streams、Readers、Writers、Files的工具类 |
| org.apache.commons.io.input | 输入流相关的实现类,包含Reader和InputStream |
| org.apache.commons.io.output | 输出流相关的实现类,包含Writer和OutputStream |
| org.apache.commons.io.serialization | 序列化相关的类
步骤:
1. 下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/
2. 把commons-io-2.6.jar包复制到指定的Module的lib目录中
3. 将commons-io-2.6.jar加入到classpath中
小结:
IOUtils和FileUtils可以方便的复制文件和文件夹!!
*/
public class CommonsIODemo {
public static void main(String[] args) throws Exception {
// 1.完成文件复制!
IOUtils.copy(new FileInputStream("D:\\resources\\hushui.jpeg"),
new FileOutputStream("D:\\resources\\hushui2.jpeg"));
// 2.完成文件复制到某个文件夹下!
FileUtils.copyFileToDirectory(new File("D:\\resources\\hushui.jpeg"), new File("D:/"));
// 3.完成文件夹复制到某个文件夹下!
FileUtils.copyDirectoryToDirectory(new File("D:\\resources") , new File("D:\\new"));
FileUtils.deleteDirectory(new File("D:\\new"));
// JDK1.7 也做了一些一行代码完成复制的操作:New IO的技术
// Files.copy(Path.of("D:\\resources\\hushui.jpeg"), Path.of("D:\\resources\\hushui3.jpeg"));
FileUtils.deleteDirectory(new File("D:\\new")); // 只能删除空文件夹
}
}
文章参考:Java入门基础视频教程,java零基础自学就选黑马程序员Java入门教程(含Java项目和Java真题)