编码表

基础知识:
计算机中储存的信息都是用二进制数据表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果按照编码表规则,将字符存储到计算机中,称为编码。 (字符转换为二进制的过程)按照同样的编码表规则,将存储在计算机中的二进制数据解析显示出来,称为解码 。编码和解码使用的码表必须一致,否则会导致乱码。
>简单理解:
存储一个字符a,首先需在码表中查到对应的数字是97,然后按照转换成二进制的规则进行存储。称为编码 读取的时候,先把二进制解析出来,再转成97,通过97查找早码表中对应的字符是a。称为解码

ASCII码表:
ASCII(American Standard Code for Information Interchange,美国信息交换标准码表):
包括了数字字符,英文大小写字符和一些常见的标点符号字符。
注意:ASCII码表中是没有中文的。
GBK:window系统默认的码表。兼容ASCII码表,也包含了21003个汉字,并支持繁体汉字以及部分日韩文字。
注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。
Day11  字符流 转换流  序列化流  装饰者模式 - 图1
Unicode码表:
由国际组织ISO 制定,是统一的万国码表,计算机科学领域里的一项业界标准,容纳世界上大多数国家的所有常见文字和符号。

但是因为表示的字符太多,所以Unicode码表中的数字不是直接以二进制的形式存储到计算机的,会先通过UTF-7,UTF-7.5,UTF-8,UTF-16,以及 UTF-32的编码方式再存储到计算机,其中最为常见的就是UTF-8。
注意: Unicode是万国码表,以UTF-8编码后一个中文以三个字节的形式存储

image.png

汉字存储和展示过程解析

image.png
重点:
windows默认使用码表为:GBK,一个中文占两个字节。
idea工具和以后工作, 默认使用Unicode的UTF-8编解码格式,一个中文个字节。
所有的编码表都兼容ASCII码表 , 所以数字 , 英文不会出现乱码问题

为什么字节流读取纯文本文件,可能会出现乱码?

因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以就会出现乱码问题。

字节流拷贝不会出现乱码的问题?

  1. 如果用字节流将文件中的数据读到内存中,打印在控制台会出现乱码。 <br />但是字节流进行拷贝文件的时候是不会出现乱码。

字符流读取中文的过程

字符流 = 字节流 + 编码表
基础知识:
不管是在哪张码表中,中文的第一个字节一定是负数。image.png

字符输出流

字符输出流介绍:
Writer类 : 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化需要使用其子类FileWriter类
FileWriter类 : 用来写入字符文件的便捷类】、
构造方法 :
public FileWriter(File file) : 往指定的File路径中写入数据
public FileWriter(String fileName) : 往指定的String路径中写入数据

_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) 写入字符数组的一部分
void write(String str) 写一个字符串
void write(String str, int off, int len) 写一个字符串的一部分_

  1. public class WriterDemo1 {
  2. public static void main(String[] args) {
  3. //1.创
  4. //续写FileWriter fw = new FileWriter("day11_demo/file01.txt ",true)
  5. try(FileWriter fw = new FileWriter("day11_demo/file01.txt ")) {
  6. //2.写
  7. fw.write('A');//写单个字符
  8. char[] chars = "HelloWole".toCharArray();//字符串变成字符数组
  9. fw.write(chars);//写完整的而字符数组
  10. //写出数组部分数据
  11. //void write(char[] cbuf, int off, int len) 写入字符数组的一部分
  12. fw.write(chars,0,5);
  13. //写字符串
  14. String str = "HelloJava";
  15. fw.write(str);
  16. //写字符部分的数据
  17. //void write(String str, int off, int len) 写一个字符串的一部分
  18. fw.write(str , 5 , 4 );
  19. //3.关
  20. //fw.close(); 自动关闭
  21. } catch (IOException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }

运行效果如下:
image.png

刷新和关流的区别:

flush() 刷新流,还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

  1. public class WriterDemo2 {
  2. public static void main(String[] args) throws IOException {
  3. // flush() 刷新流,还可以继续写数据
  4. // close() 关闭流,释放资源,但是在关闭之前会先刷新流。[一旦关闭,就不能再写数据]
  5. //FileWriter写数据时,现将字符数据进行编码得到字节数据
  6. //字节数据不会直接写到文件中,而是先缓存在数组中。字节长度是8192
  7. //如果数组没有存满二没有执行flush,或者没有执行close,数组中的内容无法写到文件中
  8. FileWriter fw = new FileWriter("day11_demo/file02.txt");
  9. //缓冲满8192,再有数据过来自动刷新
  10. for (int i = 0; i < 8192; i++) {
  11. fw.write("A");
  12. }
  13. fw.write("B");
  14. fw.flush();//刷新,后续可用
  15. fw.write("C");
  16. fw.close();//关流:刷新 + 资源的释放 后面不能用了
  17. //fw.write("D");//报错
  18. }
  19. }

案例:
需求 : 将用户键盘录入的用户名和密码保存到本地实现永久化存储。
要求 : 用户名和密码在文件中各占一行

  1. public class WriterTest {
  2. public static void main(String[] args) {
  3. Scanner sc = new Scanner(System.in);
  4. System.out.println("请输入用户名:");
  5. String name = sc.next();
  6. System.out.println("请输入用户密码:");
  7. String password = sc.next();
  8. //Ctrl + Alt + T : 选中代码块快速包裹
  9. try (FileWriter fw = new FileWriter("day11_demo/file03.txt")) {
  10. fw.write(name);//名称
  11. fw.write("\r\n");//换行
  12. fw.write(password);//密码
  13. //自动关流,关流前会自动刷新
  14. } catch (IOException e) {
  15. e.printStackTrace();
  16. }
  17. }
  18. }

字符输入,输出流!

字符缓冲流/高效字符流

字符缓冲流:
BufferedWriter:可以将数据高效的写出
BufferedReader:可以将数据高效的读入到内存
注意 : 字符缓冲流不具备读写功能 , 只提供缓冲区 , 真正读写还是需要依赖于构造接收的基本的字符流
构造方法:
BufferedWriter(Writer out)
BufferedReader(Reader in)

  1. 数据源 : D:\java学习资料\斗罗大陆.txt
  2. 目的地 : 直接输出打印在控制台
  3. */
  4. public class ReaderDemo1 {
  5. public static void main(String[] args) throws IOException {
  6. //创建
  7. FileReader fr = new FileReader("D:\\java学习资料\\斗罗大陆.txt");
  8. //读入
  9. // int read() : 一次读一个字符数据 , 如果读到文件的末尾 , 则返回-1
  10. 方法一:
  11. int c;
  12. while ((c=fr.read())!=-1){
  13. System.out.print( (char) c);
  14. }
  15. // int read(char[] cbuf) :一次读一个字符数组数据
  16. // 一次读多个字符数组放到字符数组cBuff中,
  17. // 返回的是有效读取字符的个数,读取完成返回-1
  18. 方法二:
  19. int len;
  20. char[] cBuff = new char[1024];
  21. while ((len=fr.read(cBuff))!=-1){
  22. //把字符数组转换为字符串!
  23. System.out.println(new String(cBuff,0,len));
  24. }
  25. //关闭资源
  26. fr.close();
  27. }
  28. }

**

  1. 字符缓冲高效流! 复制读写速度快 代码简洁!
  2. public class BuffRaed {
  3. public static void main(String[] args) {
  4. try (
  5. FileReader fr = new FileReader("D:\\java学习资料\\斗罗大陆01.txt");
  6. BufferedReader br = new BufferedReader(fr);
  7. //将基本字符输入流 换成字符高效输入流!
  8. FileWriter fw = new FileWriter("day11_demo\\AAA\\斗罗大陆02.txt");
  9. BufferedWriter bw = new BufferedWriter(fw);
  10. //将基本字符输出流 换成字符高效输出流!
  11. ) {
  12. int len;
  13. char[] cbuff = new char[1024];
  14. while ((len = br.read(cbuff)) != -1) {
  15. bw.write(cbuff, 0, len);
  16. }
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }}}
  1. 字符高效流特殊俩种方法!读写文本文档
  2. void newLine();换行
  3. String redline();读取
  4. public class BuffWirte {
  5. public static void main(String[] args) {
  6. test01();
  7. tesr02();
  8. } }
  1. //高效率写入
  2. public static void test01() {
  3. try (
  4. FileWriter fw = new FileWriter("day11_demo\\fali01.txt");
  5. BufferedWriter bw = new BufferedWriter(fw);
  6. ) {
  7. bw.write("Hello");
  8. bw.newLine();
  9. bw.write("java");
  10. bw.newLine();
  11. } catch (IOException e) {
  12. e.printStackTrace();
  13. }
  1. //高效流读取
  2. public static void tesr02() {
  3. try (
  4. FileReader fr = new FileReader("day11_demo\\fali01.txt");
  5. BufferedReader br = new BufferedReader(fr);
  6. ) {
  7. String str; //临时保存每次读取的行!
  8. while ((str=br.readLine())!=null){
  9. System.out.println(str);
  10. //虽然等于null即使中间有空行 也不会停止读取
  11. }
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }
  1. 对文本文档的数字字符串进行读取,排序后写到新的文件!
  2. public class Test {
  3. public static void main(String[] args) {
  4. List<String> list;
  5. try (
  6. FileReader fr = new FileReader("day11_demo\\fali01.txt");
  7. BufferedReader br = new BufferedReader(fr);
  8. FileWriter fw=new FileWriter("day11_demo\\lali002.txt");
  9. ) {
  10. String str = br.readLine();
  11. String[] arr = str.split(" "); //按照空格切割!
  12. //Arrays.asList(数组) 将数组直接拼进集合!
  13. list = Arrays.asList(arr);
  14. Collections.sort(list);
  15. for (String str2 : list) {
  16. fw.write(str2+" "); //写入也是原来的格式 后面加空格!
  17. }
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }

字符缓冲流

BufferedWriter:
void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
BufferedReader:
public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null

/
BufferedWriter
void newLine():换行功能
BufferedReader
String readLine() : 一次读取一行数据,读完返回null
/
public class Demo01 {
public static void main(String[] args) {
//test1();
test2();
}
// String readLine() : 一次读取一行数据,读完返回null
private static void test2() {
try (
FileReader fr = new FileReader(“day11_demo/file04.txt”);
BufferedReader br = new BufferedReader(fr);
) {
//一次读取一行readline
String str;//临时保存每次读取的行
while ((str = br.readLine()) != null) {
System.out.println(str);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//void newLine():换行功能

  1. private static void test1() {<br /> try(<br /> FileWriter fw = new FileWriter("day11_demo/file04.txt");<br /> BufferedWriter bw = new BufferedWriter(fw);<br /> ) {<br /> //写数据<br /> bw.write("Hello");<br /> //换行<br /> bw.newLine();//最佳换行操作<br /> bw.write("Java");<br /> bw.newLine();
  2. } catch (IOException e) {<br /> e.printStackTrace();<br /> }<br /> }<br />}<br />案例:读取文件中的数据排序后再次写到本地<br />需求:读取文件中的数据 : 33 22 11 55 44<br />排序后 : 11 22 33 44 55 再次写到本地文件<br />运行前<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/26698229/1647421256608-d2202321-ad2e-4c80-b906-0f264f95eb1f.png#clientId=u29393910-a8c9-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=39&id=u249304b5&margin=%5Bobject%20Object%5D&name=image.png&originHeight=77&originWidth=331&originalType=binary&ratio=1&rotation=0&showTitle=false&size=5239&status=done&style=none&taskId=ud0a5715e-98d1-411b-a0c7-e91076c1089&title=&width=165.5)
  1. public class Test {
  2. public static void main(String[] args) {
  3. //将file01.txt中的数字字符串读取排序后写到新的文件
  4. List<String> list;
  5. //1.先读取这行字符串
  6. try (
  7. FileReader fr = new FileReader("day11_demo/file01.txt");
  8. BufferedReader br = new BufferedReader(fr);
  9. //定义输出流
  10. FileWriter fw = new FileWriter("day11_demo/file01_01.txt");
  11. ) {
  12. String str = br.readLine();
  13. //2.切割字符串放到集合
  14. String[] arr = str.split(" ");
  15. list = Arrays.asList(arr);//Arrays
  16. //3.排序
  17. Collections.sort(list);
  18. //4.重新写出
  19. for (String str2 : list) {
  20. fw.write(str2 + " ");
  21. }
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }

运行效果如下:
image.png

转换流

转换流分类


image.png

字符流读写数据工作原理

image.png
字符流:字节流 + 编码表
小结
转换流就是来进行字节流和字符流之间转换的
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。

_

练习:

需求1 : 使用转换流 , 把以下数据按照GBK的编码写入文件 , 在使用GBK的编码读取数据
数据如下 :
远桥之下泛莲舟
岱岩石上松溪流
万仞翠山梨亭在
莫闻空谷声悠悠

  1. public class ConversionDemo1 {
  2. public static void main(String[] args) {
  3. write();
  4. read();
  5. }
  6. private static void write() {
  7. //数据按照GBK的编码写入文件
  8. //1.创建
  9. try(
  10. //public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter。
  11. FileOutputStream fos = new FileOutputStream("day11_demo/file05.txt");
  12. OutputStreamWriter osw = new OutputStreamWriter(fos , "GBK");
  13. ) {
  14. //写
  15. osw.write("远桥之下泛莲舟\r\n");
  16. osw.write("岱岩石上松溪流\r\n");
  17. osw.write("万仞翠山梨亭在\r\n");
  18. osw.write("莫闻空谷声悠悠\r\n");
  19. //自动关
  20. } catch (IOException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. private static void read() {
  25. //用GBK的编码读取数据
  26. //1.创
  27. try(
  28. //public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader。
  29. FileInputStream fis = new FileInputStream("day11_demo/file05.txt");
  30. InputStreamReader isr = new InputStreamReader(fis , "GBK");
  31. ) {
  32. //2.读
  33. int c;
  34. while ((c = isr.read()) != -1) {
  35. System.out.print((char) c);
  36. }
  37. //自动释放
  38. } catch (IOException e) {
  39. e.printStackTrace();
  40. }
  41. }
  42. }

对象操作流

image.png
对象操作流分为两类 :对象操作输入流和对象操作输出流
对象操作输出流(对象序列化流) :就是将对象序列化为字节信息写到本地文件中,或者在网络中传输对象
对象操作输入流(对象反序列化流):把写到本地文件中的字节信息转换为对象读到内存中,或者接收网络中传输的对象
对象操作流特点 : 可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中。

练习 : 使用对象操作输出流 , 把一个User对象写入文件中 , 在使用对象操作输入流读取对象数据
注意 : 如果一个类对象想要被序列化 , 那么此类需要实现Serializable接口
Serializable接口的含义 :
1 是一个标记性接口 , 里面没有任何抽象方法
2 只要一个类实现了此接口 , 表示此类的对象可以被序列化

  1. public class ObjectStreamDemo1 {
  2. public static void main(String[] args) {
  3. //test1();
  4. test2();
  5. }
  6. //序列化:将对象转换为字节信息
  7. private static void test1() {
  8. //将User对象序列化但文件中
  9. User user = new User("张懿", 18);
  10. //ObjectOutputStream 可以实现将对象序列化保存到文件中
  11. //ObjectOutputStream(OutputStream out)借助字节流输出
  12. try(
  13. FileOutputStream fos = new FileOutputStream("day11_demo/file06user.obj");
  14. ObjectOutputStream oos = new ObjectOutputStream(fos);
  15. ) {
  16. oos.writeObject(user);//写出user对象中的内容到文件中
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. //反序列化:将字节信息转换为对象
  22. private static void test2() {
  23. //将文件中的user对象 重新读取到程序中
  24. //ObjectInputStream :
  25. //ObjectInputStream (InputStream in)创建从指定的InputStream读取的ObjectInputStream
  26. //1.创
  27. try(
  28. FileInputStream fis = new FileInputStream("day11_demo/file06user.obj");
  29. ObjectInputStream ois = new ObjectInputStream(fis);
  30. ) {
  31. //读
  32. User o = (User) ois.readObject();
  33. System.out.println("o = " + o);
  34. } catch (IOException | ClassNotFoundException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. }
  39. public class User implements Serializable { }

特殊操作流

public class User implements Serializable { } 对象类要实现 Serializable接口!

用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会不会出问题呢?
会出问题,会抛出InvalidClassException异常

我们自己手动给出序列号, 不让虚拟机自动生成 , 并且这个值恒久不变
private static final long seriaLVersionUID=1L;

如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

作业示例:
作业1;
package com.itheima.Day11Work;
//请将D:盘下的一个文件复制到E:盘下,例如:d:\视频.itcast,复制到 E:\视频.itcast。
_import java.io.*;
//请使用“字节缓冲流”:BufferedInputStream和BufferedOutputStream实现
public class Demo01 {
public static void main(String[] args) {
String src = “D:\java学习资料\写读.png”;
String dest = “D:\java\写读01.png”;
try
(
FileInputStream fis = new FileInputStream(src);
BufferedInputStream bis = new BufferedInputStream
(fis);
FileOutputStream fos = new FileOutputStream
(dest);
BufferedOutputStream bos = new BufferedOutputStream
(fos)_;

  1. _) {<br /> _byte_[]_buf=new byte_[_8*1024_]_;<br /> int b;<br /> while _((_b = fis.read_(_buf_)) _!= -1_) {<br /> _fos.write_(_buf,0,b_)_;<br /> _}<br /> } _catch _(_IOException e_) {<br /> _e.printStackTrace_()_;<br /> _}<br /> }<br />}_

作业2;
package com.itheima.Day11Work;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/请编写main()方法,定义一个存储String的集合:List,并初始化以下数据:
List list = new ArrayList<>();
list.add(“迪丽热巴”);
list.add(“古力娜扎”);
list.add(“周杰伦”);
list.add(“蔡徐坤”);
请定义“字符缓冲输出流”BufferedWriter将集合中的数据写入到文件:Task1_2.txt中,每个名字一行。
/
_public class Demo02
{
public static void main(String[] args) {
List<_String_> list = new ArrayList<>();
list.add
(“迪丽热巴”);
list.add
(“古力娜扎”);
list.add
(“周杰伦”);
list.add
(“蔡徐坤”);
try
(
FileWriter fw = new FileWriter(“day11_demo\fali01.txt”);
BufferedWriter bw = new BufferedWriter
(fw);
) {
String str;
for
(String s : list) {
str = s;
bw.write
(str+”\r\n”);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}_

作业3;
package com.itheima.Day11Work;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/请编写main()方法,定义一个字符缓冲输入流BufferedReader,
读取Task1_2.txt文件,一次读取一行,
将读取的内容存储到一个List\集合中,遍历、并打印集合中的每个元素:
//1.定义一个字符缓冲输入流
BufferedReader in = new BufferedReader(……);
//2.定义一个集合
List list = new ArrayList<>();
/
//3.一次读取一行
_public class Demo03
{
public static void main(String[] args) {
List<_String_> list = new ArrayList<>();
new ArrayList
<>();
try
(
FileReader fr = new FileReader(“day11_demo\fali01.txt”);
BufferedReader br = new BufferedReader
(fr);
) {
String text;
while
((text=br.readLine())!=null){
String[] split = text.split(“\r\n”);
System._out
.println(_split[0]);
list.add
(split[0]);
}
System._out.println(“list = “ + list);
} _catch (IOException e) {
e.printStackTrace();
}
}
}_

作业4;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class Demo04 {
_public static void main
(String[] args) {
List<_Student_> stuList = new ArrayList<>();
stuList.add
(new Student(“迪丽热巴”, “女”, 18, 99));
stuList.add
(new Student(“古力娜扎”, “女”, 19, 98));
stuList.add
(new Student(“周杰伦”, “男”, 20, 88));
stuList.add
(new Student(“蔡徐坤”, “男”, 19, 78));
try
(
FileWriter fw = new FileWriter(“day11_demo\fali01.txt”);
BufferedWriter bw = new BufferedWriter
(fw);
) {
for (Student student : stuList) {
StringBuffer sb = new StringBuffer();
sb.append
(student.getName()).append(“,”).append(student.getSex()).append(“,”).append(student.getAge()).append(“,”).append(student.fenshu);
bw.write
(sb.toString());
bw.newLine
()_;

  1. _}<br /> } _catch _(_IOException e_) {<br /> _e.printStackTrace_()_;<br /> _}<br /> }<br />}


作业5;_
package com.itheima.Day11Work;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/## 题目5
请编写main()方法,定义一个字符缓冲输入流BufferedReader对象,读取”test1_4.txt”文件,一次读取一行,将每行数据封装为一个Student对象,并将Student对象存储到一个集合。遍历并打印集合的所有Student信息。
//定义一个BufferedReader对象
BufferedReader in = new BufferedReader(……);
//定义一个集合
List stuList = new ArrayList<>();
//一次读取一行
……
```
/
_public class Demo05
{
public static void main(String[] args) {
List<_Student_> list = new ArrayList<>();
try
(
FileReader fr = new FileReader(“day11_demo\test1_4.txt”);
BufferedReader br = new BufferedReader
(fr);
) {
String str;
while
((str=br.readLine())!=null){
System._out.println(“str = “ + str);
}
} _catch
(IOException e) {
e.printStackTrace();
}
}
}_

作业6;
//# 【转换流】
// 现有一字符串:”我爱Java”。将该字符串保存到当前项目根目录下的a.txt文件中。
// 要求:使用gbk编码保存。
// 注意:idea的默认编码是utf-8,
// 所以可以通过file -> settings -> file encodings设置为gbk格式,
// 否则打开a.txt文件看到的将会是乱码。
_public class Demo06
{
public static void main(String[] args) {
try (
FileOutputStream fos=new FileOutputStream(“day11_demo\fali01.txt”);
OutputStreamWriter osw=new OutputStreamWriter
(fos,”GBK”);
) {
String str=”我爱java”;
osw.write
(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}_

作业7;
package com.itheima.Day11Work;
//需求:
// 利用转换输入流将当前项目根目录下使用gbk编码的a.txt文件的内容读取出来,并打印在控制台上。
_import java.io.*;
// 要求:不能出现乱码的情况。
public class Demo07 {
public static void main(String[] args) {
try (
FileInputStream fis = new FileInputStream(“day11_demo\fali01.txt”);
InputStreamReader isr = new InputStreamReader
(fis, “GBK”);
) {
int c;
while
((c = isr.read()) != -1) {
System._out.print((_char) c);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}_

作业8;
package com.itheima.Day11Work;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/## 题目8
需求:
1. 定义一个学生类,包含姓名,年龄,性别等成员变量,提供setters和getters方法以及构造方法。
2. 在测试类中创建一个学生对象,给学生对象的三个成员变量赋值。然后将该对象保存到当前项目根目录下的stu.txt文件中。
/
_public class Demo08
{
public static void main(String[] args) {
Student01 student01=new Student01(“张三”,10,”男”);
try
(
FileOutputStream fos=new FileOutputStream(“day11_demo\stu.txt”);
ObjectOutputStream obs=new ObjectOutputStream
(fos);
){
obs.writeObject(student01)_;

  1. _} _catch _(_IOException e_) {<br /> _e.printStackTrace_()_;<br /> _}<br /> }<br />}<br />作业9_<br />package com.itheima.Day11Work;<br />import java.io.FileInputStream;<br />import java.io.IOException;<br />import java.io.ObjectInputStream;<br />_//需求:将上一题(序列化流题目1)保存到stu.txt文件中的学生对象读取出来。<br />_public class Demo09 _{<br /> _public static void main_(_String_[] _args_) {<br /> _try_(<br /> _FileInputStream fis=new FileInputStream_(_"day11_demo\\stu.txt"_)_;<br /> ObjectInputStream ois=new ObjectInputStream_(_fis_)_;<br /> _){<br /> _Student01 S1=_(_Student01_)_ois.readObject_()_;<br /> System._out_.println_(_"S1 = " + S1_)_;<br /> _} _catch _(_IOException | ClassNotFoundException e_) {<br /> _e.printStackTrace_()_;<br /> _}<br /> }<br />}_

作业10;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Scanner;
//需求:从键盘录入一行字符串,利用字节打印流将该行字符串保存到当前项目根目录下的d.txt文件中。
_public class Demo10
{
public static void main(String[] args) {
Scanner sc = new Scanner(System._in);
System.out.println(“请输入你要写的数据!”);
String str=sc.nextLine();
try (
_FileOutputStream fos=new FileOutputStream
(“day11_demo\b.txt”);
OutputStreamWriter osw=new OutputStreamWriter
(fos);
){
osw.write(str);
} catch (IOException e) {
e.printStackTrace();
}
}
}_

俩种复制的高效方法!

public class Test1 {
public static void main(String[] args) throws IOException {

// public static int copy(InputStream in, OutputStream out):
// 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)

  1. FileInputStream in = new FileInputStream("day11_demo\\fali.txt");<br /> FileOutputStream out = new FileOutputStream("day10_demo\\fa1.txt");<br /> IOUtils.copyLarge(in, out);

// public static long copyLarge(InputStream in, OutputStream out):
// 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)
FileInputStream IN =new FileInputStream
(“D:\IDEA_Paper\Java_Employment\day11_demo\GBK编码文件.txt”);
FileOutputStream FO=new FileOutputStream(“day10_demo\gbk.txt”);
IOUtils.copy(IN, FO);
}}

public class Test2 {
public static void main(String[] args) throws IOException {
//复制文件到另外一个目录下。
File src=new File(“day11_demo\fali.txt”);//要拷贝的文件
File destDir=new File(“day11_demo\fali02\fali001.txt”);//目标文件
FileUtils.copyFileToDirectory(src, destDir);
// 复制src目录到dest目录中。
File src1=new File(“day11_demo\fali.txt”);
File desrDir1=new File(“D:\java学习资料”);
FileUtils.copyDirectoryToDirectory(src1, desrDir1);
}
}

装饰者模式

public class Test {
_public static void main
(String[] args) {
liuDeHua liuDeHua=new liuDeHua(); //被装饰对象!
//FileWriter fw=new FileWriter();<br /> _liuDeHuaWrapper liuDeHuaWrapper=new liuDeHuaWrapper_(_liuDeHua_)_;<br /> _//BufferedWriter bw=new BufferedWriter();
liuDeHuaWrapper.sing();//刘德华 唱歌扩展改动实现
liuDeHuaWrapper.dance();// 华仔正在辛苦跳舞

}
}

// 明星接口 , 装饰类和被装饰类的父类型
interface Star {
public abstract void sing(); // 唱歌
public abstract void dance();// 跳舞

}
//被装饰的类
class liuDeHua implements Star{

  1. _@Override<br /> public void sing_() {<br /> _System._out_.println_(_"刘德华唱歌"_)_;
  2. _}
  3. _@Override<br /> public void dance_() {<br /> _System._out_.println_(_"华仔正在辛苦跳舞"_)_;<br /> _}<br />}

//装饰类
class liuDeHuaWrapper implements Star{
_private liuDeHua liuDeHua;

  1. public liuDeHuaWrapper_(_liuDeHua liuDeHua_){<br /> _this.liuDeHua=liuDeHua;<br /> _}
  2. _@Override<br /> public void sing_() {<br /> _System._out_.println_(_"刘德华 唱歌扩展改动实现"_)_;<br /> _}
  3. _@Override<br /> public void dance_() {<br /> _liuDeHua.dance_()_;<br /> _}<br />_}

day12-编码表,字符流,对象流,其他流

今日目标

  • 编码表
  • 字符输出流
  • 字符输入流
  • 字符缓冲流
  • 转换流
  • 对象操作流
  • 装饰模式
  • commons-iojar包

1 编码表

1.1 思考:

  • 既然字节流可以操作所有文件,那么为什么还要学习字符流 ?
    • 如果使用字节流 , 把文本文件中的内容读取到内存时, 可能会出现乱码
    • 如果使用字节流 , 把中文写入文本文件中 , 也有可能会出现乱码

1.2 编码表介绍

  • 计算机中储存的信息都是用二进制数据表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
  • 按照编码表规则,将字符存储到计算机中,称为编码。
  • 按照同样的编码表规则,将存储在计算机中的二进制数据解析显示出来,称为解码 。
  • 编码和解码使用的码表必须一致,否则会导致乱码。
  • 简单理解:
    • 存储一个字符a,首先需在码表中查到对应的数字是97,然后按照转换成二进制的规则进行存储。称为编码
    • 读取的时候,先把二进制解析出来,再转成97,通过97查找早码表中对应的字符是a。称为解码
  • ASCII码表:
    • ASCII(American Standard Code for Information Interchange,美国信息交换标准码表):
    • 包括了数字字符,英文大小写字符和一些常见的标点符号字符。
    • 注意:ASCII码表中是没有中文的。
  • image.png

    GBK码表:
    • window系统默认的码表。兼容ASCII码表,也包含了21003个汉字,并支持繁体汉字以及部分日韩文字
    • 注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。
  • Unicode码表:
    • 由国际组织ISO 制定,是统一的万国码表,计算机科学领域里的一项业界标准,容纳世界上大多数国家的所有常见文字和符号。
    • 但是因为表示的字符太多,所以Unicode码表中的数字不是直接以二进制的形式存储到计算机的,会先通过UTF-7,UTF-7.5,UTF-8,UTF-16,以及 UTF-32的编码方式再存储到计算机,其中最为常见的就是UTF-8。
    • 注意: Unicode是万国码表,以UTF-8编码后一个中文以三个字节的形式存储

1.3 编码表小结

image.png

image.png

1.4 字节流读中文出现码的原码

  • 因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以就会出现乱码问题

2 字符输出流

2.1 字符流输出介绍

  • Writer类
    • 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化 , 需要使用其子类FileWriter类
  • 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 释放资源
    • 注意事项:
      每次使用完流必须要释放资源。
  • ```java package com.itheima.writer_demo;

import java.io.FileWriter; import java.io.IOException;

/* Writer类 : 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化 需要使用其子类FileWriter类

  1. FileWriter : 用来写入字符文件的便捷类
  2. 构造方法 :
  3. public FileWriter(File file) : 往指定的File路径中写入数据
  4. public FileWriter(String fileName) : 往指定的String路径中写入数据
  5. 成员方法
  6. void write(int c) 写一个字符
  7. void write(char[] cbuf) 写入一个字符数组
  8. void write(char[] cbuf, int off, int len) 写入字符数组的一部分
  9. void write(String str) 写一个字符串
  10. void write(String str, int off, int len) 写一个字符串的一部分
  11. flush() 刷新流,还可以继续写数据
  12. close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

*/ public class WriterDemo1 { public static void main(String[] args) throws IOException { // 创建字符输出流对象 // 如果文件不存在会创建一个空的文件 // 如果文件存在 , 会把文件中的内容清空 FileWriter fw = new FileWriter(“day12_demo\charstream2.txt”);

  1. // 写数据
  2. fw.write('a');
  3. fw.write('b');
  4. // 刷新流 , 把流中的数据刷到硬盘中 , 刷新之后可以继续写数据
  5. // fw.flush();
  6. // 释放资源
  7. // 关闭流 , 但是会先刷新流
  8. fw.close();
  9. // 一旦关闭流无法写数据
  10. // fw.write('c');
  11. }

}

  1. ```java
  2. package com.itheima.writer_demo;
  3. import java.io.FileWriter;
  4. import java.io.IOException;
  5. /*
  6. Writer类 : 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化
  7. 需要使用其子类FileWriter类
  8. FileWriter类 : 用来写入字符文件的便捷类
  9. 构造方法 :
  10. public FileWriter(File file) : 往指定的File路径中写入数据
  11. public FileWriter(String fileName) : 往指定的String路径中写入数据
  12. 成员方法
  13. void write(int c) 写一个字符
  14. void write(char[] cbuf) 写入一个字符数组
  15. void write(char[] cbuf, int off, int len) 写入字符数组的一部分
  16. void write(String str) 写一个字符串
  17. void write(String str, int off, int len) 写一个字符串的一部分
  18. flush() 刷新流,还可以继续写数据
  19. close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
  20. */
  21. public class WriterDemo2 {
  22. public static void main(String[] args) throws IOException {
  23. // 创建字符输出流对象
  24. FileWriter fw = new FileWriter("day12_demo\\charstream2.txt");
  25. // 写数据
  26. // void write(int c) 写一个字符
  27. // fw.write('a');
  28. // fw.write('b');
  29. // fw.write('c');
  30. // void write(char[] cbuf) 写入一个字符数组
  31. char[] chs = {'a', 'b', 'c', 'd', 'e'};
  32. // fw.write(chs);
  33. // void write(char[] cbuf, int off, int len) 写入字符数组的一部分
  34. // fw.write(chs , 2 , 3);
  35. // void write(String str) 写一个字符串
  36. // fw.write("abcadaasda");
  37. // void write(String str, int off, int len) 写一个字符串的一部分
  38. // fw.write("abnacna", 3, 2);
  39. // 释放资源
  40. fw.close();
  41. }
  42. }

2.4 字符输出流练习

  • ```java package com.itheima.writer_demo;

import java.io.FileWriter; import java.io.IOException; import java.util.Scanner;

/* 需求 : 将用户键盘录入的用户名和密码保存到本地实现永久化存储。 要求 : 用户名和密码在文件中各占一行

  1. 步骤:
  2. 1 用户键盘录入用户名
  3. 2 创建字符输出流对象
  4. 3 将用户名和密码写到本地文件中

*/ public class WriterTest { public static void main(String[] args) throws IOException { Scanner sc = new Scanner(System.in); System.out.println(“请输入用户名:”); String username = sc.nextLine();

  1. System.out.println("请输入密码:");
  2. String password = sc.nextLine();
  3. // 创建字符输出流对象
  4. FileWriter fw = new FileWriter("day12_demo\\user.txt");
  5. // 往文件中写入用户名和密码
  6. fw.write(username);
  7. // 换行
  8. fw.write("\r\n");
  9. fw.write(password);
  10. // 刷新
  11. fw.flush();
  12. // 释放资源
  13. fw.close();
  14. }

}

  1. <a name="845473ef"></a>
  2. ## 3 字符输入流
  3. <a name="777a0052"></a>
  4. ### 3.1 字节输入流介绍
  5. - Reader类 :
  6. - 读取字符流的最顶层的类 , 是一个抽象类 ,不能实例化
  7. - 需要使用其子类FileReader类
  8. - FileReader类 :
  9. - 用来读取字符文件的便捷类
  10. <a name="cd909b99"></a>
  11. ### 3.2 FileReader的成员
  12. - 构造方法 :
  13. - public FileReader(File file) : 从指定的File路径中读取数据
  14. - public FileReader(String fileName) : 从指定的String路径中读取数据
  15. - 成员方法 :
  16. -
  17. | int read() | 一次读一个字符数据 |
  18. | --- | --- |
  19. | int read(char[] cbuf) | 一次读一个字符数组数据 |
  20. -
  21. ```java
  22. package com.itheima.reader_demo;
  23. import java.io.FileNotFoundException;
  24. import java.io.FileReader;
  25. import java.io.IOException;
  26. /*
  27. Reader类 : 读取字符流的最顶层的类 , 是一个抽象类 ,不能实例化
  28. 需要使用其子类FileReader类
  29. FileReader类 : 用来读取字符文件的便捷类
  30. 构造方法 :
  31. public FileReader(File file) : 从指定的File路径中读取数据
  32. public FileReader(String fileName) : 从指定的String路径中读取数据
  33. 成员方法 :
  34. int read() : 一次读一个字符数据
  35. int read(char[] cbuf) : 一次读一个字符数组数据
  36. */
  37. public class ReaderDemo1 {
  38. public static void main(String[] args) throws IOException {
  39. // 创建字符输入流对象
  40. FileReader fr = new FileReader("day12_demo\\charstream.txt");
  41. // 一次读一个字符数据
  42. int ch;
  43. while ((ch = fr.read()) != -1) {
  44. System.out.print((char) ch);
  45. }
  46. // 释放资源
  47. fr.close();
  48. }
  49. }
  • ```java package com.itheima.reader_demo;

import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException;

/* Reader类 : 读取字符流的最顶层的类 , 是一个抽象类 ,不能实例化 需要使用其子类FileReader类

  1. FileReader : 用来读取字符文件的便捷类
  2. 构造方法 :
  3. public FileReader(File file) : 从指定的File路径中读取数据
  4. public FileReader(String fileName) : 从指定的String路径中读取数据
  5. 成员方法 :
  6. int read() 一次读一个字符数据
  7. int read(char[] cbuf) 一次读一个字符数组数据

*/ public class ReaderDemo2 { public static void main(String[] args) throws IOException { // 创建字符输入流对象 FileReader fr = new FileReader(“day12_demo\charstream.txt”);

  1. // 一次读一个字符数组数据
  2. char[] chs = new char[1024];
  3. int len;
  4. while ((len = fr.read(chs)) != -1) {
  5. System.out.println(new String(chs, 0, len));
  6. }
  7. // 释放资源
  8. fr.close();
  9. }

}

  1. <a name="d88d05e9"></a>
  2. ## 4 字符缓冲流
  3. <a name="7297efbd"></a>
  4. ### 4.1 字符缓冲流
  5. - BufferedWriter:可以将数据高效的写出
  6. - BufferedReader:可以将数据高效的读入到内存
  7. - 注意 : 字符缓冲流不具备读写功能 , 只提供缓冲区 , 真正读写还是需要依赖于构造接收的基本的字符流
  8. - 构造方法:
  9. - public BufferedWriter(Writer out) : 构造方法中需要接收一个基本的字符输出流
  10. - public BufferedReader(Reader in) : 构造方法中需要接收一个基本的字符输入流

package com.itheima.bufferedstream_demo;

import java.io.*;

/ 需求 : 使用字符缓冲流复制纯文本文件 将当日课程资料中的 ‘斗罗大陆.txt’ 复制到当前模块下 ‘copy.txt’ / public class BufferedStreamDemo1 { public static void main(String[] args) throws IOException { // 创建高效的字符输入流对象 BufferedReader br = new BufferedReader(new FileReader(“D:\传智播客\上海-JavaSE进阶面授\day12【缓冲流、转换流、序列化流、装饰者模式、commons-io工具包】\资料\斗罗大陆.txt”)); // 创建高效的字符输出流对象 BufferedWriter bw = new BufferedWriter(new FileWriter(“day12_demo\copy.txt”));

  1. // 一次读写一个字符

// int ch; // while ((ch = br.read()) != -1) { // bw.write(ch); // }

  1. // 一次读写一个字符数组
  2. char[] chs = new char[1024];
  3. int len;
  4. while ((len = br.read(chs)) != -1) {
  5. bw.write(chs, 0, len);
  6. }
  7. // 释放资源
  8. br.close();
  9. bw.close();
  10. }

}

  1. <a name="6f64ace0"></a>
  2. ### 4.2 字符缓冲流特有的功能
  3. -
  4. <a name="481a4e83"></a>
  5. ##### BufferedWriter类
  6. - void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
  7. -
  8. <a name="556c4003"></a>
  9. ##### BufferedReader类
  10. - public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
  11. -
  12. ```java
  13. package com.itheima.bufferedstream_demo;
  14. import java.io.*;
  15. /*
  16. 1 字符缓冲流:
  17. BufferedWriter:可以将数据高效的写出
  18. BufferedReader:可以将数据高效的读入到内存
  19. 2 字符缓冲流特有功能
  20. BufferedWriter类
  21. void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
  22. BufferedReader类
  23. public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
  24. 远桥之下泛莲舟
  25. 岱岩石上松溪流
  26. 万仞翠山梨亭在
  27. 莫闻空谷声悠悠
  28. */
  29. public class BufferedStreamDemo2 {
  30. public static void main(String[] args) throws IOException {
  31. // 创建高效的字符输出流对象
  32. BufferedWriter bw = new BufferedWriter(new FileWriter("day12_demo\\abc.txt"));
  33. // void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
  34. bw.write("远桥之下泛莲舟");
  35. bw.newLine();
  36. bw.write("岱岩石上松溪流");
  37. bw.newLine();
  38. bw.write("万仞翠山梨亭在");
  39. bw.newLine();
  40. bw.write("莫闻空谷声悠悠");
  41. bw.flush();
  42. // 创建高效的字符输入流对象
  43. BufferedReader br = new BufferedReader(new FileReader("day12_demo\\abc.txt"));
  44. // public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
  45. // String s = br.readLine();
  46. // System.out.println(s);
  47. // s = br.readLine();
  48. // System.out.println(s);
  49. // s = br.readLine();
  50. // System.out.println(s);
  51. // s = br.readLine();
  52. // System.out.println(s);
  53. // System.out.println("============");
  54. // s = br.readLine();
  55. // System.out.println(s);
  56. // s = br.readLine();
  57. // System.out.println(s);
  58. // 循环改进
  59. String line;
  60. while((line = br.readLine()) != null){
  61. System.out.println(line);
  62. }
  63. // 释放资源
  64. br.close();
  65. bw.close();
  66. }
  67. }

4.3 字符缓冲流练习

  1. package com.itheima.bufferedstream_demo;
  2. import java.io.*;
  3. import java.util.Arrays;
  4. /*
  5. 需求:读取文件中的数据 : 33 22 11 55 44
  6. 排序后 : 11 22 33 44 55 再次写到本地文件
  7. 步骤 :
  8. 1 创建高效的字符输入流对象
  9. 2 读取文件中的一行数据
  10. 3 将数据按照空格切割
  11. 4 把字符串数组转成int类型数组
  12. 5 对int类型的数组进行排序
  13. 6 创建高效的字符输出流对象
  14. 7 遍历数组,把数组中的数据写入到文件中
  15. 8 释放资源
  16. */
  17. public class BufferedStreamDemo3 {
  18. public static void main(String[] args) throws IOException {
  19. // 1 创建高效的字符输入流对象
  20. BufferedReader br = new BufferedReader(new FileReader("day12_demo\\sort.txt"));
  21. // 2 读取文件中的一行数据
  22. String line = br.readLine();
  23. // 3 将数据按照空格切割
  24. String[] strs = line.split(" ");
  25. // 4 把字符串数组转成int类型数组
  26. int[] arr = new int[strs.length];
  27. for (int i = 0; i < strs.length; i++) {
  28. arr[i] = Integer.parseInt(strs[i]);
  29. }
  30. // 5 对int类型的数组进行排序
  31. Arrays.sort(arr);
  32. // 6 创建高效的字符输出流对象
  33. BufferedWriter bw = new BufferedWriter(new FileWriter("day12_demo\\sort.txt"));
  34. // 7 遍历数组,把数组写入到文件中
  35. for (int i = 0; i < arr.length; i++) {
  36. bw.write(arr[i] + " ");
  37. bw.flush();
  38. }
  39. // 8 释放资源
  40. br.close();
  41. bw.close();
  42. }
  43. }

5 转换流

5.1 转换流介绍

  • 转换流就是来进行字节流和字符流之间转换的桥梁

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。
  • 练习
    ```java package com.itheima.conversion_demo;

import java.io.*;

/* 转换流就是来进行字节流和字符流之间转换的桥梁

  1. InputStreamReader是从字节流到字符流的桥梁
  2. public InputStreamReader(InputStream in) : 创建一个使用默认编码的 InputStreamReader
  3. public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader
  4. OutputStreamWriter是从字符流到字节流的桥梁
  5. public OutputStreamWriter(OutputStream out) : 创建使用默认字符编码的 OutputStreamWriter
  6. public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter
  7. 需求1 : 使用转换流 , 把以下数据按照GBK的编码写入文件 , 在使用GBK的编码读取数据
  8. 数据如下 :
  9. 远桥之下泛莲舟
  10. 岱岩石上松溪流
  11. 万仞翠山梨亭在
  12. 莫闻空谷声悠悠

*/ public class ConversionDemo2 { public static void main(String[] args) throws IOException { // 创建转换输出流 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(“day12_demo\conversion.txt”), “GBK”); osw.write(“远桥之下泛莲舟”); osw.write(“\r\n”); osw.write(“岱岩石上松溪流”); osw.write(“\r\n”); osw.write(“万仞翠山梨亭在”); osw.write(“\r\n”); osw.write(“莫闻空谷声悠悠”); osw.write(“\r\n”); osw.close();

  1. // 创建转换输入流
  2. InputStreamReader isr = new InputStreamReader(new FileInputStream("day12_demo\\conversion.txt"), "GBK");
  3. int ch;
  4. while ((ch = isr.read()) != -1) {
  5. System.out.print((char) ch);
  6. }
  7. isr.close();
  8. }

}

  1. ```java
  2. package com.itheima.conversion_demo;
  3. import java.io.*;
  4. /*
  5. 转换流就是来进行字节流和字符流之间转换的桥梁
  6. InputStreamReader是从字节流到字符流的桥梁
  7. public InputStreamReader(InputStream in) : 创建一个使用默认编码的 InputStreamReader。
  8. public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader。
  9. OutputStreamWriter是从字符流到字节流的桥梁
  10. public OutputStreamWriter(OutputStream out) : 创建使用默认字符编码的 OutputStreamWriter
  11. public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter。
  12. 需求2 : 将模块根目录中GBK编码的文本文件 , 转换为UTF-8编码的文本文件
  13. */
  14. public class ConversionDemo2 {
  15. public static void main(String[] args) throws IOException {
  16. // 创建转换输入流
  17. InputStreamReader isr = new InputStreamReader(new FileInputStream("day12_demo\\GBK编码的文件.txt"), "GBK");
  18. // 创建转换输出流
  19. OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day12_demo\\UTF编码的文件.txt"), "UTF-8");
  20. int ch;
  21. while ((ch = isr.read()) != -1) {// 以GBK编码进去读取
  22. osw.write(ch);// 以UTF-8编码进行写入
  23. }
  24. // 释放资源
  25. isr.close();
  26. osw.close();
  27. }
  28. }

6 对象操作流

6.1 对象操作流介绍

  • 可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中

6.2 对象操作流的分类

  • ObjectOutputStream :
    • 对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
  • ObjectInputStream :
    • 对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象

6.3 对象操作流的注意事项

  • 注意 : 如果一个类对象想要被序列化 , 那么此类需要实现Serializable接口
    • Serializable接口的含义 :
      • 1 是一个标记性接口 , 里面没有任何抽象方法
      • 2 只要一个类实现了此接口 , 表示此类的对象可以被序列化
  • 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会不会出问题呢?
    • 会出问题,会抛出InvalidClassException异常
  • 如果出问题了,如何解决呢?
    • 给对象所属的类加一个serialVersionUID
    • private static final long serialVersionUID = 42L;
  • 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
  • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
  • ``` package com.itheima.objectstream_demo;

import java.io.Serializable;

/ 如果此类对象想要被序列化 , 那么此类需要实现Serializable接口 Serializable接口的含义 : 是一个标记性接口 , 里面没有任何抽象方法 只要一个类实现了此接口 , 表示此类的对象可以被序列化 / public class User implements Serializable { /* 问题分析 : serialVersionUID : 序列号 序列号是根据类的信息进行生成的 如果没有自己给出序列号 , JVM会根据类的信息自动计算一个序列号 如果改动了类的信息 , 那么JVM会重新计算一个序列号

  1. 第一步 : 把对象序列化到本地中 , 序列号为 -4446663370728791812 也会存储到本地中
  2. 第二步 : 我们自己修改了类 , 会重新计算一个新的序列号 2908680347500030933
  3. 第三步 : 当把对象读到内存中时 , 本地中的序列号和类中的序列号不一致就会发生 InvalidClassException异常
  4. 解决方案 :
  5. 我们自己手动给出序列号, 不让虚拟机自动生成 , 并且这个值恒久不变
  6. private static final long serialVersionUID = L;
  7. */
  8. private static final long serialVersionUID = 111L;
  9. private String username;
  10. private transient String password;
  11. public User() {
  12. }
  13. public User(String username, String password) {
  14. this.username = username;
  15. this.password = password;
  16. }
  17. public String getUsername() {
  18. return username;
  19. }
  20. public void setUsername(String username) {
  21. this.username = username;
  22. }
  23. public String getPassword() {
  24. return password;
  25. }
  26. public void setPassword(String password) {
  27. this.password = password;
  28. }
  29. @Override
  30. public String toString() {
  31. return "User{" +
  32. "username='" + username + '\'' +
  33. ", password='" + password + '\'' +
  34. '}';
  35. }

}

  1. <a name="672acd77"></a>
  2. ### 6.4 对象操作流的练习
  3. <a name="db8d687e"></a>
  4. ## 7 装饰设计模式
  5. - 设计模式 : 一套良好的编码风格 , 经过众多的开发人员不断的测试总结而来
  6. <a name="9455ddb6"></a>
  7. #### 学习目标
  8. - 熟悉装饰设计模式的使用
  9. <a name="c2079de7"></a>
  10. #### 内容讲解
  11. <a name="978f42fd"></a>
  12. #### 【1】概述
  13. - 装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能。
  14. - 不使用继承技术扩展功能, 可以降低耦合
  15. -
  16. <a name="41537f81"></a>
  17. ##### 使用原则:
  18. - 装饰类和被装饰类需要有共同的父类型。
  19. - 在之前学习过的 BufferedWriter 和 FileWriter 就是装饰设计模式
  20. - BufferedWriter的父类为Writer
  21. - FileWriter的父类也是Writer
  22. - 我们把FileWriter的对象传递到BufferedWriter的构造中 , 那么可以理解为BufferedWriter是装饰类 , FileWriter是被装饰类
  23. - BufferedWriter对FileWriter的功能做了增强
  24. - 装饰类的构造要接收被装饰类的对象
  25. - FileWriter fw = new FileWriter("路径");
  26. - BufferedWriter bw = new BufferedWriter(fw);
  27. - 在装饰类中把要增强扩展的功能进行扩展
  28. - BufferedWriter和FileWriter的功能一样, 都具备Writer中写数据的功能
  29. - 但是BufferedWriter提供了缓冲区 , 相当于对FileWriter功能做了扩展
  30. - 对于不要增强的功能直接调用
  31. - 不需要增强的功能直接继承父类的即可
  32. <a name="1d001642"></a>
  33. #### 【2】代码实践
  34. 已知有接口Star和其子类型LiuDeHua。
  35. ```java
  36. public interface Star {
  37. public abstract void sing();
  38. public abstract void dance();
  39. }

需求 :在不改变LiuDeHua类,及不使用继承的技术前提下,动态的扩展LiuDeHua的sing功能。

LiuDeHua就是一个被装饰类 , 需要对唱歌的功能进行扩展

思路 :

定义一个装饰类,去装饰增强 LiuDehua类。

步骤:

  • 创建LiuDeHua类并实现接口Star【被装饰类】
  • 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】
  • 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。
  • 在装饰类中对sing方法进行功能扩展
  • 对dance不做改动
  • 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象
  1. package com.itheima.design_demo;
  2. import java.io.BufferedWriter;
  3. import java.io.FileWriter;
  4. import java.io.IOException;
  5. import java.util.Scanner;
  6. /*
  7. 装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能
  8. 使用原则 :
  9. 1. 装饰类和被装饰类需要有共同的父类型。
  10. 2. 装饰类要传入被装饰类的对象
  11. 3. 在装饰类中把要增强扩展的功能进行扩展
  12. 4. 对于不要增强的功能直接调用
  13. 需求 : 在不改变LiuDeHua类,及不使用继承的技术前提下,动态的扩展LiuDeHua的sing功能。
  14. LiuDeHua就是一个被装饰类 , 需要对唱歌的功能进行扩展
  15. 步骤:
  16. 1. 创建LiuDeHua类并实现接口Star【被装饰类】
  17. 2. 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】
  18. 3. 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。
  19. 4. 在装饰类中对sing方法进行功能扩展
  20. 5. 对dance不做改动
  21. 6. 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象
  22. */
  23. public class Test {
  24. public static void main(String[] args) throws IOException {
  25. // 被装饰类对象
  26. LiuDeHua huaZai = new LiuDeHua();// 0x001
  27. // 装饰类对象
  28. LiuDeHuaWrapper liuDeHuaWrapper = new LiuDeHuaWrapper(huaZai);
  29. liuDeHuaWrapper.sing();
  30. liuDeHuaWrapper.dance();
  31. // // 被装饰类对象
  32. // FileWriter fw = new FileWriter("路径");
  33. // // 装饰类对象
  34. // BufferedWriter bw = new BufferedWriter(fw);
  35. }
  36. }
  37. // 1. 创建LiuDeHua类并实现接口Star【被装饰类】
  38. class LiuDeHua implements Star {
  39. @Override
  40. public void sing() {
  41. System.out.println("唱忘情水...");
  42. }
  43. @Override
  44. public void dance() {
  45. System.out.println("华仔在跳老年迪斯高..");
  46. }
  47. }
  48. // 2. 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】
  49. class LiuDeHuaWrapper implements Star {
  50. // 3. 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。
  51. private LiuDeHua huaZai;// 0x001
  52. public LiuDeHuaWrapper(LiuDeHua huaZai) {// 0x001
  53. this.huaZai = huaZai;
  54. }
  55. @Override
  56. public void sing() {
  57. // 4. 在装饰类中对sing方法进行功能扩展
  58. System.out.print("华仔深情");
  59. huaZai.sing();
  60. }
  61. @Override
  62. public void dance() {
  63. // 5. 对dance不做改动
  64. huaZai.dance();
  65. }
  66. }
  67. // 明星接口 , 装饰类和被装饰类的父类型
  68. interface Star {
  69. public abstract void sing(); // 唱歌
  70. public abstract void dance();// 跳舞
  71. }

内容小结

  1. 装饰类和被装饰类需要有共同的父类型。
  2. 装饰类要传入被装饰类的对象
  3. 在装饰类中把要增强扩展的功能进行扩展
  4. 对于不要增强的功能直接调用

9 commons-io工具包(对文件的拷贝做优化)

学习目标

  • 能够熟悉导入commons-io工具包,并使用

内容讲解

【1】三方库的导入

  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加入到项目中
    右键jar包:

image.png
image.png

【2】API

1)org.apache.commons.io.IOUtils类

  1. public static int copy(InputStream in, OutputStream out):
  2. input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)
  3. public static long copyLarge(InputStream in, OutputStream out):
  4. input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)
  1. package com.itheima.commons_io;
  2. import org.apache.commons.io.IOUtils;
  3. import java.io.FileInputStream;
  4. import java.io.FileNotFoundException;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. /*
  8. org.apache.commons.io.IOUtils类
  9. public static int copy(InputStream in, OutputStream out):
  10. 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)
  11. public static long copyLarge(InputStream in, OutputStream out):
  12. 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)
  13. */
  14. public class Test1 {
  15. public static void main(String[] args) throws IOException {
  16. IOUtils.copy(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg") , new FileOutputStream("day12_demo\\copy.jpg"));
  17. }
  18. }

2)org.apache.commons.io.FileUtils

  1. public static void copyFileToDirectory(final File srcFile, final File destFile):
  2. 复制文件到另外一个目录下。
  3. public static void copyDirectoryToDirectory(File src , File dest ):
  4. 复制src目录到dest位置。

代码实践:

  1. package com.itheima.commons_io;
  2. import org.apache.commons.io.FileUtils;
  3. import java.io.File;
  4. import java.io.IOException;
  5. /*
  6. org.apache.commons.io.FileUtils
  7. public static void copyFileToDirectory(final File srcFile, final File destFile):
  8. 复制文件到另外一个目录下。
  9. public static void copyDirectoryToDirectory(File src , File dest ):
  10. 复制src目录到dest目录中。
  11. */
  12. public class Test2 {
  13. public static void main(String[] args) throws IOException {
  14. FileUtils.copyDirectoryToDirectory(new File("D:\\传智播客\\安装包\\好看的图片") , new File("D:\\"));
  15. }
  16. }

内容小结

commons-io可以简化IO复制文件的操作。