day12-编码表,字符流,对象流,其他流
今日目标
- 编码表
- 字符输出流
- 字符输入流
- 字符缓冲流
- 转换流
- 对象操作流
- 装饰模式
-
1 编码表
1.1 思考:
既然字节流可以操作所有文件,那么为什么还要学习字符流 ?
计算机中储存的信息都是用二进制数据表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
- 按照编码表规则,将字符存储到计算机中,称为编码。
- 按照同样的编码表规则,将存储在计算机中的二进制数据解析显示出来,称为解码 。
- 编码和解码使用的码表必须一致,否则会导致乱码。
- 简单理解:
- 存储一个字符a,首先需在码表中查到对应的数字是97,然后按照转换成二进制的规则进行存储。称为编码
- 读取的时候,先把二进制解析出来,再转成97,通过97查找早码表中对应的字符是a。称为解码
- ASCII码表:
- ASCII(American Standard Code for Information Interchange,美国信息交换标准码表):
- 包括了数字字符,英文大小写字符和一些常见的标点符号字符。
- 注意:ASCII码表中是没有中文的。
- GBK码表:
- window系统默认的码表。兼容ASCII码表,也包含了21003个汉字,并支持繁体汉字以及部分日韩文字
- 注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。
Unicode码表:
因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以就会出现乱码问题
2 字符输出流
2.1 字符流输出介绍
Writer类
- 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化 , 需要使用其子类FileWriter类
-
2.2 FileWriter的成员
构造方法 :
- public FileWriter(File file) : 往指定的File路径中写入数据
- public FileWriter(String fileName) : 往指定的String路径中写入数据
- 成员方法 : | void write(int c) | 写一个字符 | | —- | —- | | void write(char[] cbuf) | 写入一个字符数组 | | void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 | | void write(String str) | 写一个字符串 | | void write(String str, int off, int len) | 写一个字符串的一部分 |
flush() | 刷新流,还可以继续写数据 |
---|---|
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
2.3 FileWriter写数据的步骤
- 1 创建字符输出流对象
- 注意事项:
如果文件不存在,就创建。但是要保证父级路径存在。
如果文件存在就清空
- 注意事项:
- 2 写数据
- 注意事项:
写出int类型的整数,实际写出的是整数在码表上对应的字母。
写出字符串数据,是把字符串本身原样写出。
- 注意事项:
3 释放资源
- 注意事项:
每次使用完流必须要释放资源。package com.itheima.writer_demo;
import java.io.FileWriter;
import java.io.IOException;
/*
Writer类 : 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化
需要使用其子类FileWriter类
FileWriter类 : 用来写入字符文件的便捷类
构造方法 :
public FileWriter(File file) : 往指定的File路径中写入数据
public FileWriter(String fileName) : 往指定的String路径中写入数据
成员方法
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个字符
flush() 刷新流,还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
*/
public class WriterDemo1 {
public static void main(String[] args) throws IOException {
// 1. 创
FileWriter fw = new FileWriter("day11_demo/file02.txt");
// 2. 写
fw.write('A');
char[] chars = "HelloWorld".toCharArray();
fw.write(chars);
//写出Hello
fw.write(chars, 0, 5);
//写字符串
String str = "HelloJava";
fw.write(str);
//写出字符串中的Java
fw.write(str, 5, 4);
// 3. 关
fw.close();
// fw.flush();
}
}
2.4 字符输出流练习
package com.itheima.writer_demo;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
/*
需求 : 将用户键盘录入的用户名和密码保存到本地实现永久化存储。
要求 : 用户名和密码在文件中各占一行
步骤:
1 用户键盘录入用户名,密码
2 创建字符输出流对象
3 将用户名和密码写到本地文件中
注意:如果要求文件存在不能覆盖,需要在原有内容中续写
public FileWriter(String path, boolean append): 把append设置为true,表示有续写
public FileWriter(File path, boolean append): 把append设置为true,表示有续写
*/
public class WriterTest {
public static void main(String[] args) throws IOException {
System.out.println("注册开始!");
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入用密码:");
String password = sc.nextLine();
//要求 : 用户名和密码在文件中各占一行
FileWriter fw = new FileWriter("day11_demo/file04.txt",true);//续写
fw.write(name+"\r\n");
fw.write(password+"\r\n");
fw.close();
}
}
2.5 刷新和关流
package com.itheima.writer_demo;
import java.io.FileWriter;
import java.io.IOException;
/*
Writer类 : 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化
需要使用其子类FileWriter类
//重点记住
flush() 刷新流,还可以继续写数据【涉及到缓冲区的流,用法都是一样】
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
*/
public class WriterDemo2 {
public static void main(String[] args) throws IOException {
//刷新和close的区别
FileWriter fw = new FileWriter("day11_demo/file03.txt");
//字符流底层会有一个字节数组缓冲区,大小8192
//当字符输出流写出字符时,先将字符编码成为字节,放到缓冲区。
//如果缓冲区没满,不会自动刷新到文件。如果满了放不下了,就会自动刷新。
//或者调用flush方法也可以主动刷新
for (int i = 0; i < 8192; i++) {
fw.write('A');
}
fw.write('B'); // 缓冲区存满,再放一个导致自动刷新
fw.flush(); //flush调用,主动刷新
fw.write("C");//flush之后可以继续写数据
fw.close();//刷新+关流 ,后续不能再用
//fw.write("D"); //续不能再用
}
}
3 字符输入流
3.1 字节输入流介绍
- 注意事项:
Reader类 :
- 读取字符流的最顶层的类 , 是一个抽象类 ,不能实例化
- 需要使用其子类FileReader类
FileReader类 :
构造方法 :
- public FileReader(File file) : 从指定的File路径中读取数据
- public FileReader(String fileName) : 从指定的String路径中读取数据
- 成员方法 : | int read() | 一次读一个字符数据 | | —- | —- | | int read(char[] cbuf) | 一次读一个字符数组数据 |
package com.itheima.reader_demo;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
Reader类 : 读取字符流的最顶层的类 , 是一个抽象类 ,不能实例化
需要使用其子类FileReader类
FileReader类 : 用来读取字符文件的便捷类
构造方法 :
public FileReader(File file) : 从指定的File路径中读取数据
public FileReader(String fileName) : 从指定的String路径中读取数据
成员方法 :
int read() : 一次读一个字符数据 , 如果读到文件的末尾 , 则返回-1
int read(char[] cbuf) : 一次读一个字符数组数据
数据源 : 斗罗大陆.txt
目的地 : 直接输出打印在控制台
*/
public class ReaderDemo1 {
public static void main(String[] args) throws IOException {
//1. 创
FileReader fr = new FileReader("day11_demo/斗罗大陆.txt");
//2. 读
//int read() : 一次读一个字符数据 , 如果读到文件的末尾 , 则返回-1
/*
int c;//用来保存每次读取的字符
while ((c = fr.read()) != -1) {//每次循环读取一个字符,如果不是-1就是有效的字符
System.out.print((char)c);
}
*/
//int read(char[] cbuf) :
// 一次读一个字符数组数据,读取的字符保存到字符数组中,返回有效读取字符数据的个数,如果读取完成返回-1
char[] cbuf = new char[1024];//循环中每次保存读取到的字符
int len; //每次读取字符的有效个数
while ((len = fr.read(cbuf)) != -1) {
//将有效的字符数据转换为字符串
//public String(char[] cbuf, int off,int len) 将字符数组中,从off位置开始的len个字符变成字符串
System.out.print(new String(cbuf, 0, len));
}
//3. 关
fr.close();
}
}
4 字符缓冲流
4.1 字符缓冲流
- BufferedWriter:可以将数据高效的写出
- BufferedReader:可以将数据高效的读入到内存
- 注意 : 字符缓冲流不具备读写功能 , 只提供缓冲区 , 真正读写还是需要依赖于构造接收的基本的字符流
构造方法:
- public BufferedWriter(Writer out) : 构造方法中需要接收一个基本的字符输出流
- public BufferedReader(Reader in) : 构造方法中需要接收一个基本的字符输入流
package com.itheima.bufferedstream_demo;
import java.io.*;
/*
1 字符缓冲流:
BufferedWriter:可以将数据高效的写出
BufferedReader:可以将数据高效的读入到内存
2 注意 : 字符缓冲流不具备读写功能 , 只提供缓冲区 , 真正读写还是需要依赖于构造接收的基本的字符流
3 构造方法:
public BufferedWriter(Writer out) : 构造方法中需要接收一个基本的字符输出流
public BufferedReader(Reader in) : 构造方法中需要接收一个基本的字符输入流
需求 : 使用字符缓冲流复制纯文本文件
将当日课程资料中的 ‘斗罗大陆.txt’ 复制到当前模块下 'copy.txt'
数据源 : 斗罗大陆.txt
目的地 : day11_demo\copy.txt
*/
public class BufferedStreamDemo1 {
public static void main(String[] args) {
String src = "day11_demo/斗罗大陆.txt";
String dest = "day11_demo/斗罗大陆copy2.txt";
copyTextFile(src, dest);
}
public static void copyTextFile(String src, String dest) {
try (
FileReader fr = new FileReader(src);
//包装为高效流
BufferedReader br = new BufferedReader(fr);
FileWriter fw = new FileWriter(dest);
//包装为高效流
BufferedWriter bw = new BufferedWriter(fw)
) {
//缓冲流的基本使用和基本流是一样的
//BufferedReader ---> FileReader
//BufferedWriter ---> FileWriter
//边读边写
int len;
char[] cbuf = new char[1024];
while ((len = br.read(cbuf)) != -1) {//读取文本中的数据到内存
//将内存的数据写到文件中
bw.write(cbuf, 0, len);
}
//自动关流
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.2 字符缓冲流特有的功能
BufferedWriter类
- void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
BufferedReader类
- public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
package com.itheima.bufferedstream_demo;
import java.io.*;
/*
1 字符缓冲流:
BufferedWriter:可以将数据高效的写出
BufferedReader:可以将数据高效的读入到内存
2 字符缓冲流特有功能
BufferedWriter类
void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
BufferedReader类
public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
远桥之下泛莲舟
岱岩石上松溪流
万仞翠山梨亭在
莫闻空谷声悠悠
*/
public class BufferedStreamDemo2 {
public static void main(String[] args) {
//newLineTest();
readLineTest();
}
public static void newLineTest() {
//BufferedWriter类
//void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
try (FileWriter fw = new FileWriter("day11_demo/诗歌.txt", true);
BufferedWriter bw = new BufferedWriter(fw)
) {
//用下newLine
bw.write("静夜思");
bw.newLine();//换行
bw.write("李白");
bw.newLine();//换行
bw.write("床前明月光");
bw.newLine();//换行
bw.write("疑是地上霜");
bw.newLine();//换行
bw.write("举头望明月");
bw.newLine();//换行
bw.write("地上鞋两双");
bw.newLine();//换行
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readLineTest() {
try (FileReader fr = new FileReader("day11_demo/诗歌.txt");
BufferedReader br = new BufferedReader(fr);
) {
//BufferedReader类
//public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
String line;//保存每次读取的行
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.3 字符缓冲流练习
package com.itheima.bufferedstream_demo;
import java.io.*;
import java.util.Arrays;
/*
需求:读取文件中的数据 : 33 22 11 55 44
排序后 : 11 22 33 44 55 再次写到本地文件
分析 :
步骤 :
1 创建高效的字符输入流对象
2 读取文件中的一行数据
3 将数据按照空格切割
4 把字符串数组转成int类型数组
5 对int类型的数组进行排序
6 创建高效的字符输出流对象
7 遍历数组,把数组中的数据写入到文件中
8 释放资源
*/
public class BufferedStreamDemo3 {
public static void main(String[] args) {
//1 创建高效的字符输入流对象
String lineNumber = null;
try (FileReader fr = new FileReader("day11_demo/file01.txt");
BufferedReader br = new BufferedReader(fr)) {
//2 读取文件中的一行数据
lineNumber = br.readLine();
} catch (IOException e) {
e.printStackTrace();
}
//3 将数据按照空格切割
String[] strArr = lineNumber.split(" +");//正则表达式 用一个或者多个空格进行切割
//System.out.println(Arrays.toString(strArr));
//4 把字符串数组转成int类型数组
int[] arr = new int[strArr.length];
//把字符串的数字转为int数据
for (int i = 0; i < strArr.length; i++) {
arr[i] = Integer.parseInt(strArr[i]);
}
//5 对int类型的数组进行排序
Arrays.sort(arr);
//System.out.println("Arrays.toString(arr) = " + Arrays.toString(arr));
//6 创建高效的字符输出流对象
try (FileWriter fw = new FileWriter("day11_demo/file01_1.txt");
BufferedWriter bw = new BufferedWriter(fw)) {
//7 遍历数组,把数组中的数据写入到文件中
for (int i : arr) {
bw.write(i +" ");//把数字转为字符串,写出
}
//8 释放资源【自动完成】
} catch (IOException e) {
e.printStackTrace();
}
}
}
5 转换流
5.1 转换流介绍
- public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
-
5.2 转换流分类
InputStreamReader是从字节流到字符流的桥梁
- public InputStreamReader(InputStream in) : 创建一个使用默认编码的 InputStreamReader。
- public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader。
- OutputStreamWriter是从字符流到字节流的桥梁
- public OutputStreamWriter(OutputStream out) : 创建使用默认字符编码的 OutputStreamWriter
- public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter。
练习
package com.itheima.conversion_demo;
import java.io.*;
/*
转换流就是来进行字节流和字符流之间转换的桥梁
InputStreamReader : 是从字节流到字符流的桥梁
public InputStreamReader(InputStream in) : 创建一个使用默认编码的 InputStreamReader。 idea默认编码是UTF-8
public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader。
charsetName: GBK UTF-8
OutputStreamWriter : 是从字符流到字节流的桥梁
public OutputStreamWriter(OutputStream out) : 创建使用默认字符编码的 OutputStreamWriter
public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter。
charsetName: GBK UTF-8
需求1 : 使用转换流 , 把以下数据按照GBK的编码写入文件 , 在使用GBK的编码读取数据
思考:转换流属于字符流还是属于字节流? 转换流属于字符流
FileReader 的父类就是InputStreamReader
FileWriter 的父类就是OutputStreamWriter
用法上和普通的字符流是一样的,只是构建对象的时候不一样
数据如下 :
远桥之下泛莲舟
岱岩石上松溪流
万仞翠山梨亭在
莫闻空谷声悠悠
*/
public class ConversionDemo1 {
// 需求1 : 使用转换流 , 把以下数据按照GBK的编码写入文件 , 在使用GBK的编码读取数据
public static void main(String[] args) {
//writeGBKText();
readGBKText();
}
//指定GBK编码写文本
public static void writeGBKText() {
//OutputStreamWriter : 是从字符流到字节流的桥梁
// public OutputStreamWriter(OutputStream out) : 创建使用默认字符编码的 OutputStreamWriter
// public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter。
// charsetName: GBK UTF-8
//1. 创
try (FileOutputStream fos = new FileOutputStream("day11_demo/file05GBK.txt");
//OutputStreamWriter osw = new OutputStreamWriter(fos);//默认编码转换字符 IDEA:utf-8
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");//指定GBK编码
) {
//2. 写
osw.write("远桥之下泛莲舟\r\n");
osw.write("岱岩石上松溪流\r\n");
osw.write("万仞翠山梨亭在\r\n");
osw.write("莫闻空谷声悠悠\r\n");
//3. 关【自动关】
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readGBKText() {
//InputStreamReader : 是从字节流到字符流的桥梁
// public InputStreamReader(InputStream in) : 创建一个使用默认编码的 InputStreamReader。 idea默认编码是UTF-8
// public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader。
// charsetName: GBK UTF-8
//1. 创
try (FileInputStream fis = new FileInputStream("day11_demo/file05GBK.txt");
//InputStreamReader isr = new InputStreamReader(fis);//默认采用IDEA的UTF-8
InputStreamReader isr = new InputStreamReader(fis, "gbk");//默认采用IDEA的UTF-8
) {
//2. 读
int c;
while ((c = isr.read()) != -1) {
System.out.print((char) c);
}
//3. 关
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.itheima.conversion_demo;
import java.io.*;
/*
转换流也可以进一步包装为高效流使用
字节流 ---> 字符流 ---->高效流
需求2 : 将模块根目录中GBK编码的文本文件 , 转换为UTF-8编码的文本文件
//1.使用GBK编码方式读取到内存中,然后在使用UTF-8的方式写到文件中
*/
public class ConversionDemo2 {
public static void main(String[] args) {
String gbkFile = "day11_demo/GBK编码文件.txt";
String utf8File = "day11_demo/UTF8编码文件.txt";
gbkConvertUtf8(gbkFile,utf8File);
}
/**
* 将GBK文件转换为UTF8的文件
*
* @param gbkFile
* @param utf8File
*/
public static void gbkConvertUtf8(String gbkFile, String utf8File) {
//1.创
try (FileInputStream fis = new FileInputStream(gbkFile);
InputStreamReader isr = new InputStreamReader(fis, "GBK");//以GBK方式读取文本
FileOutputStream fos = new FileOutputStream(utf8File);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8")//以UTF-8的方式写文本
) {
//2.读,写 (和字符流的读写是一样的)
int c;
while ((c = isr.read()) != -1) {//以GBK编码读
osw.write(c);//以UTF-8编码写
}
//3.关 [自动]
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 转换字符流能不能转化为高效字符流? 可以
* @param gbkFile
* @param utf8File
*/
public static void gbkConvertUtf82(String gbkFile, String utf8File) {
//1.创
try (FileInputStream fis = new FileInputStream(gbkFile);
InputStreamReader isr = new InputStreamReader(fis, "GBK");//以GBK方式读取文本
BufferedReader br = new BufferedReader(isr);//将转换流变成高效流
FileOutputStream fos = new FileOutputStream(utf8File);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");//以UTF-8的方式写文本
BufferedWriter bw = new BufferedWriter(osw);//将转换流变成高效流
) {
//2.读,写 (和字符流的读写是一样的)
int c;
while ((c = br.read()) != -1) {//以GBK编码读
bw.write(c);//以UTF-8编码写
}
//3.关 [自动]
} catch (IOException e) {
e.printStackTrace();
}
}
}
6 对象操作流
6.1 对象操作流介绍
可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中
6.2 对象操作流的分类
ObjectOutputStream :
对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
写对象方法:public void writeObject(Object obj)
- ObjectInputStream :
对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
读对象方法:public Object readObject()
练习
package com.itheima.objectstream_demo;
import java.io.*;
/*
对象操作流 :
ObjectOutputStream :
对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
ObjectOutputStream(OutputStream out)创建一个写入指定的OutputStream的ObjectOutputStream。
写对象方法: public void writeObject(Object obj) 将对象写出
ObjectInputStream :
对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
ObjectInputStream(InputStream in)
读对象方法: public Object readObject() 读对象
练习 : 使用对象操作输出流 , 把一个User对象写入文件中 , 在使用对象操作输入流读取对象数据
注意 : 如果一个类对象想要被序列化 , 那么此类需要实现Serializable接口
Serializable接口的含义 :
1 是一个标记性接口 , 里面没有任何抽象方法
2 只要一个类实现了此接口 , 表示此类的对象可以被序列化
*/
public class ObjectStreamDemo1 {
public static void main(String[] args) {
writeObject();
readObject();
}
//将对象写到文件中
public static void writeObject() {
User user = new User("张三", 18);
//把对象user使用ObjectOutputStream写到文件
//1.创
try (FileOutputStream fos = new FileOutputStream("day11_demo/userObj.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
//2.写
//public void writeObject(Object obj) 写对象的方法
oos.writeObject(user);
//3.关[自动完成]
} catch (IOException e) {
e.printStackTrace();
}
}
//将文件中的对象数据读取到内存
public static void readObject() {
//ObjectInputStream 对象读取流
//1.创
try (FileInputStream fis = new FileInputStream("day11_demo/userObj.txt");
ObjectInputStream ois = new ObjectInputStream(fis)) {
//2.读
Object obj = ois.readObject();//读取对象
System.out.println("obj = " + obj);
//System.out.println(ois.readObject());
//对象读取完毕,继续读不是返回null,-1 之类的,报异常:EOFException
//3.关[自动]
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
6.3 对象操作流的注意事项
1 要写出对象(序列化)到文件,该对象的类型一定要去实现接口:Serializable
否则会有异常:NotSerializableException
2 写一个对象,读一个对象,如果对象读取完毕继续读,
Object readObject()
对象读取完毕,继续读不是返回null,-1 之类的,报异常:EOFException (End Of File Exception)
3 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
给此成员变量加上一个修饰符(关键字) : transient
4 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会不会出问题呢?
会出现问题 : 发生异常InvalidClassException
每个类写好后,都存在一个版本号。
写对象前类的版本号要和读取对象时类的版本号要一致,否则就会报错。
如果出问题了,如何解决呢?
给当前类加上一个写死的序列号 :
private static final long serialVersionUID = 数值L;
private static final long serialVersionUID = -9081456910220446530L;
package com.itheima.objectstream_demo;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/*
对象操作流 :
对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
ObjectOutputStream :
对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
ObjectInputStream :
对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
练习 : 使用对象操作输出流 , 把一个User对象写入文件中 , 在使用对象操作输入流读取对象数据
1 要写出对象(序列化)到文件,该对象的类型一定要去实现接口:Serializable
否则会有异常:NotSerializableException
2 写一个对象,读一个对象,如果对象读取完毕继续读,
Object readObject()
对象读取完毕,继续读不是返回null,-1 之类的,报异常:EOFException (End Of File Exception)
3 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
给此成员变量加上一个修饰符(关键字) : transient
4 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会不会出问题呢?
会出现问题 : 发生异常InvalidClassException
每个类写好后,都存在一个版本号。
写对象前类的版本号要和读取对象时类的版本号要一致,否则就会报错。
如果出问题了,如何解决呢?
给当前类加上一个写死的序列号 :
private static final long serialVersionUID = 数值L;
private static final long serialVersionUID = -9081456910220446530L;
*/
public class ObjectStreamDemo2 {
public static void main(String[] args) {
readObject();
}
//将文件中的对象数据读取到内存
public static void readObject() {
//ObjectInputStream 对象读取流
//1.创
try (FileInputStream fis = new FileInputStream("day11_demo/userObj.txt");
ObjectInputStream ois = new ObjectInputStream(fis)) {
//2.读
Object obj = ois.readObject();//读取对象
System.out.println("obj = " + obj);
//System.out.println(ois.readObject());
//对象读取完毕,继续读不是返回null,-1 之类的,报异常:EOFException
//3.关[自动]
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
package com.itheima.objectstream_demo;
import java.io.Serializable;
/*
以后如果类要序列化,干脆就先自己定义一个版本号
private static final long serialVersionUID = 1000L;
*/
public class User implements Serializable{
//告诉JVM我的版本号:
private static final long serialVersionUID = -9081456910220446530L;
private String name;
private transient int age;
// 如果age前使用transient 修饰,意味着age只能短暂存储与内存,不能随对象写到文件中
private int weight;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", weight=" + weight +
'}';
}
}
6.4 对象操作流的练习
package com.itheima.objectstream_demo;
import java.io.*;
import java.util.ArrayList;
/*
需求:创建多个Javabean类对象写到文件中,再次读取到内存。
思路:
1 创建学生对象
2 利用对象操作输出流写到本地
3 利用对象操作输入流读到内存
*/
public class ObjectStreamTest1 {
public static void main(String[] args) {
//1.先定义学生类,并让学生类实现Serializable接口
//writeStudent();
readStudent();
}
public static void writeStudent() {
//使用集合存储多个学生对象,将集合对象写出
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("张三", 18));
list.add(new Student("李四", 18));
list.add(new Student("王五", 18));
list.add(new Student("赵六", 18));
try (FileOutputStream fos = new FileOutputStream("day11_demo/students.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
//写集合对象到文件中
oos.writeObject(list);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readStudent() {
try (FileInputStream fis = new FileInputStream("day11_demo/students.txt");
ObjectInputStream ois = new ObjectInputStream(fis)) {
//读取对象
ArrayList<Student> list = (ArrayList<Student>) ois.readObject();
for (Student student : list) {
System.out.println("student = " + student);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
package com.itheima.objectstream_demo;
import java.io.Serializable;
public class Student implements Serializable {
//写死一个版本号
private static final long serialVersionUID = 1000000L;
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
7 打印流
l打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream,PrintWriter两个类。
l可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true。
PrintStream和PrintWriter的异同
相同:
PrintStream 和 PrintWriter的特有使用功能都是一样的,提供了打印数据较为方便的print/println方法,而且他们在使用的时候都不会抛出异常
区别:
- PrintStream是字节流,PrintWriter是字符流
- PrintStream在打印输出数据后默认会自动刷新,而PrintWriter默认没有自动刷新的(关流或者刷新)。
package com.itheima.printstream_demo; import java.io.FileNotFoundException; import java.io.PrintStream; /* PrintStream类 : 打印流 作用 : 就是为了方便记录数据(日志) 构造方法 : public PrintStream(String filePath) : 构建一个打印流对象,传入接收数据的文件路径 成员方法 : public void println(数据) 打印后换行 public void print(数据) 打印不换行 需求 : 创建打印流对象,记录一些数据到指定文件 */ public class PrintStreamDemo { public static void main(String[] args) { System.out.print("Hello"); System.out.println("World"); //System.out 该对象类型就是PrintStream类型 try (PrintStream ps = new PrintStream("day11_demo/file01.txt")) { ps.println(97); ps.println(98); ps.println('A'); ps.println("Hello"); ps.println(true); ps.println(false); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
package com.itheima.printstream_demo; import java.io.FileNotFoundException; import java.io.PrintStream; /* PrintStream类 : 打印流 构造方法 : public PrintStream(String filePath) : 构建一个打印流对象,传入接收数据的文件路径 成员方法 : public void println(数据) 打印后换行 public void print(数据) 打印不换行 需求 : System.out 对象类型,其实就是PrintStream类型。 可以改变系统输出的流向 : System.setOut(打印流) */ public class PrintStreamDemo2 { public static void main(String[] args) throws FileNotFoundException { System.out.println("Hello");//控制台 //想要让 System.out.println("Hello") 输出的数据到文件中去 PrintStream ps = new PrintStream("day11_demo/log.txt"); //可以改变系统输出的流向 : System.setOut(打印流) System.setOut(ps); //System.out.println() 数据输出都不会到控台,到了log.txt //System.out.print() 数据输出都不会到控台,到了log.txt System.out.println("HelloWorld!!"); System.out.print("HelloJava!!"); } }
8 装饰设计模式
设计模式 : 一套良好的编码风格 , 经过众多的开发人员不断的测试总结而来
学习目标
-
内容讲解
【1】概述
作用:在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能。
- 不使用继承技术扩展功能, 可以降低耦合(一个类A明显用到另外一个类B,A对于B是偶尔)
使用原则:
- 装饰类和被装饰类需要有共同的父类型。
- 在之前学习过的 BufferedWriter 和 FileWriter 就是装饰设计模式
- BufferedWriter的父类为Writer
- FileWriter的父类也是Writer
- 我们把FileWriter的对象传递到BufferedWriter的构造中 , 那么可以理解为BufferedWriter是装饰类 , FileWriter是被装饰类
- BufferedWriter对FileWriter的功能做了增强
- 装饰类的构造要接收被装饰类的对象
- FileWriter fw = new FileWriter(“路径”);
- BufferedWriter bw = new BufferedWriter(fw);
- FileWriter fw = new FileWriter(“路径”);
- 在装饰类中把要增强扩展的功能进行扩展
- BufferedWriter和FileWriter的功能一样, 都具备Writer中写数据的功能
- 但是BufferedWriter提供了缓冲区 , 相当于对FileWriter功能做了扩展
- 对于不要增强的功能直接调用
- 装饰类和被装饰类需要有共同的父类型。
创建LiuDeHua类并实现接口Star【被装饰类】
- 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】
- 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。
- 在装饰类中对sing方法进行功能扩展
- 对dance不做改动
- 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象
package com.itheima.design_demo; /* 装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能 使用原则 : 1. 装饰类和被装饰类需要有共同的父类型。 2. 装饰类要传入被装饰类的对象 3. 在装饰类中把要增强扩展的功能进行扩展 4. 对于不要增强的功能直接调用 需求 : 在不改变LiuDeHua类,及不使用继承的技术前提下,动态的扩展LiuDeHua的sing功能。 LiuDeHua就是一个被装饰类 , 需要对唱歌的功能进行扩展 步骤: 1. 创建LiuDeHua类并实现接口Star【被装饰类】 2. 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】 3. 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。 4. 在装饰类中对sing方法进行功能扩展 5. 对dance不做改动 6. 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象 */ public class Test { public static void main(String[] args) { //被装饰对象 LiuDeHua deHua = new LiuDeHua(); //FileReader fr = new FileReader() //装饰对象 LiuDeHuaWrapper liuDeHuaWrapper = new LiuDeHuaWrapper(deHua); //BufferedReader br = new BufferedReader(fr); liuDeHuaWrapper.sing(); liuDeHuaWrapper.dance(); } } // 明星接口 , 装饰类和被装饰类的父类型 interface Star { void sing(); // 唱歌 void dance();// 跳舞 } //被装饰类 class LiuDeHua implements Star{ @Override public void sing() { System.out.println("刘德华在唱忘情水!"); } @Override public void dance() { System.out.println("刘德华在跳舞!"); } } //装饰类 class LiuDeHuaWrapper implements Star{ private LiuDeHua liuDeHua; //使用构造方法设置被装饰类对象 public LiuDeHuaWrapper(LiuDeHua liuDeHua) { this.liuDeHua = liuDeHua; } @Override public void sing() { //功能改造,增强 System.out.println("刘德华在深情的演唱《忘情水》"); } @Override public void dance() { //不用增强的功能 liuDeHua.dance(); } }
内容小结
- 下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/
- 把commons-io-2.6.jar包复制到指定的Module的lib目录中
- 将commons-io-2.6.jar加入到项目中
右键jar包:
【2】API
1)org.apache.commons.io.IOUtils类 ```java public static int copy(InputStream in, OutputStream out): 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)
public static long copyLarge(InputStream in, OutputStream out): 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)
```java
package com.itheima.commons_io;
import org.apache.commons.io.IOUtils;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
org.apache.commons.io.IOUtils类
public static int copy(InputStream in, OutputStream out):
把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)
public static long copyLarge(InputStream in, OutputStream out):
把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)
*/
public class Test1 {
public static void main(String[] args) throws IOException {
IOUtils.copy(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg") , new FileOutputStream("day12_demo\\copy.jpg"));
}
}
2)org.apache.commons.io.FileUtils
public static void copyFileToDirectory(final File srcFile, final File destFile):
复制文件到另外一个目录下。
public static void copyDirectoryToDirectory(File src , File dest ):
复制src目录到dest位置。
代码实践:
package com.itheima.commons_io;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
/*
org.apache.commons.io.FileUtils
public static void copyFileToDirectory(final File srcFile, final File destFile):
复制文件到另外一个目录下。
public static void copyDirectoryToDirectory(File src , File dest ):
复制src目录到dest目录中。
*/
public class Test2 {
public static void main(String[] args) throws IOException {
FileUtils.copyDirectoryToDirectory(new File("D:\\传智播客\\安装包\\好看的图片") , new File("D:\\"));
}
}
内容小结
commons-io可以简化IO复制文件的操作。