4 字符缓冲流
4.1 字符缓冲流
- BufferedWriter:可以将数据高效的写出
- BufferedReader:可以将数据高效的读入到内存
- 注意 : 字符缓冲流不具备读写功能 , 只提供缓冲区 , 真正读写还是需要依赖于构造接收的基本的字符流
构造方法:
public BufferedWriter(Writer out) : 构造方法中需要接收一个基本的字符输出流
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")); // 一次读写一个字符 // int ch; // while ((ch = br.read()) != -1) { // bw.write(ch); // } // 一次读写一个字符数组 char[] chs = new char[1024]; int len; while ((len = br.read(chs)) != -1) { bw.write(chs, 0, len); } // 释放资源 br.close(); bw.close(); } }
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) throws IOException { // 创建高效的字符输出流对象 BufferedWriter bw = new BufferedWriter(new FileWriter("day12_demo\\abc.txt")); // void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符 bw.write("远桥之下泛莲舟"); bw.newLine(); bw.write("岱岩石上松溪流"); bw.newLine(); bw.write("万仞翠山梨亭在"); bw.newLine(); bw.write("莫闻空谷声悠悠"); bw.flush(); // 创建高效的字符输入流对象 BufferedReader br = new BufferedReader(new FileReader("day12_demo\\abc.txt")); // public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null // String s = br.readLine(); // System.out.println(s); // s = br.readLine(); // System.out.println(s); // s = br.readLine(); // System.out.println(s); // s = br.readLine(); // System.out.println(s); // System.out.println("============"); // s = br.readLine(); // System.out.println(s); // s = br.readLine(); // System.out.println(s); // 循环改进 String line; while((line = br.readLine()) != null){ System.out.println(line); } // 释放资源 br.close(); bw.close(); } }
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) throws IOException { // 1 创建高效的字符输入流对象 BufferedReader br = new BufferedReader(new FileReader("day12_demo\\sort.txt")); // 2 读取文件中的一行数据 String line = br.readLine(); // 3 将数据按照空格切割 String[] strs = line.split(" "); // 4 把字符串数组转成int类型数组 int[] arr = new int[strs.length]; for (int i = 0; i < strs.length; i++) { arr[i] = Integer.parseInt(strs[i]); } // 5 对int类型的数组进行排序 Arrays.sort(arr); // 6 创建高效的字符输出流对象 BufferedWriter bw = new BufferedWriter(new FileWriter("day12_demo\\sort.txt")); // 7 遍历数组,把数组写入到文件中 for (int i = 0; i < arr.length; i++) { bw.write(arr[i] + " "); bw.flush(); } // 8 释放资源 br.close(); bw.close(); } }
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。
练习
package com.itheima.conversion_demo; import java.io.*; /* 转换流就是来进行字节流和字符流之间转换的桥梁 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的编码读取数据 数据如下 : 远桥之下泛莲舟 岱岩石上松溪流 万仞翠山梨亭在 莫闻空谷声悠悠 */ 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(); // 创建转换输入流 InputStreamReader isr = new InputStreamReader(new FileInputStream("day12_demo\\conversion.txt"), "GBK"); int ch; while ((ch = isr.read()) != -1) { System.out.print((char) ch); } isr.close(); } }
package com.itheima.conversion_demo; import java.io.*; /* 转换流就是来进行字节流和字符流之间转换的桥梁 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。 需求2 : 将模块根目录中GBK编码的文本文件 , 转换为UTF-8编码的文本文件 */ public class ConversionDemo2 { public static void main(String[] args) throws IOException { // 创建转换输入流 InputStreamReader isr = new InputStreamReader(new FileInputStream("day12_demo\\GBK编码的文件.txt"), "GBK"); // 创建转换输出流 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day12_demo\\UTF编码的文件.txt"), "UTF-8"); int ch; while ((ch = isr.read()) != -1) {// 以GBK编码进去读取 osw.write(ch);// 以UTF-8编码进行写入 } // 释放资源 isr.close(); osw.close(); } }
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会重新计算一个序列号 第一步 : 把对象序列化到本地中 , 序列号为 -4446663370728791812 也会存储到本地中 第二步 : 我们自己修改了类 , 会重新计算一个新的序列号 2908680347500030933 第三步 : 当把对象读到内存中时 , 本地中的序列号和类中的序列号不一致就会发生 InvalidClassException异常 解决方案 : 我们自己手动给出序列号, 不让虚拟机自动生成 , 并且这个值恒久不变 private static final long serialVersionUID = 值L; */ private static final long serialVersionUID = 111L; private String username; private transient String password; public User() { } public User(String username, String password) { this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
6.4 对象操作流的练习
7 装饰设计模式
- 设计模式 : 一套良好的编码风格 , 经过众多的开发人员不断的测试总结而来
目标
- 熟悉装饰设计模式的使用
内容讲解
【1】概述
- 装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能。
- 不使用继承技术扩展功能, 可以降低耦合
- 使用原则:
- 装饰类和被装饰类需要有共同的父类型。
- 在之前学习过的 BufferedWriter 和 FileWriter 就是装饰设计模式
- BufferedWriter的父类为Writer
- FileWriter的父类也是Writer
- 我们把FileWriter的对象传递到BufferedWriter的构造中 , 那么可以理解为BufferedWriter是装饰类 , FileWriter是被装饰类
- BufferedWriter对FileWriter的功能做了增强
- 装饰类的构造要接收被装饰类的对象
- FileWriter fw = new FileWriter(“路径”);
- BufferedWriter bw = new BufferedWriter(fw);
- 在装饰类中把要增强扩展的功能进行扩展
- BufferedWriter和FileWriter的功能一样, 都具备Writer中写数据的功能
- 但是BufferedWriter提供了缓冲区 , 相当于对FileWriter功能做了扩展
- 对于不要增强的功能直接调用
- 不需要增强的功能直接继承父类的即可
【2】代码实践
已知有接口Star和其子类型LiuDeHua。
public interface Star { public abstract void sing(); public abstract void dance(); }
需求 :在不改变LiuDeHua类,及不使用继承的技术前提下,动态的扩展LiuDeHua的sing功能。
LiuDeHua就是一个被装饰类 , 需要对唱歌的功能进行扩展
思路 :
定义一个装饰类,去装饰增强 LiuDehua类。
步骤:
- 创建LiuDeHua类并实现接口Star【被装饰类】
- 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】
- 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。
- 在装饰类中对sing方法进行功能扩展
- 对dance不做改动
- 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象
package com.itheima.design_demo; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.util.Scanner; /* 装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能 使用原则 : 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) throws IOException { // 被装饰类对象 LiuDeHua huaZai = new LiuDeHua();// 0x001 // 装饰类对象 LiuDeHuaWrapper liuDeHuaWrapper = new LiuDeHuaWrapper(huaZai); liuDeHuaWrapper.sing(); liuDeHuaWrapper.dance(); // // 被装饰类对象 // FileWriter fw = new FileWriter("路径"); // // 装饰类对象 // BufferedWriter bw = new BufferedWriter(fw); } } // 1. 创建LiuDeHua类并实现接口Star【被装饰类】 class LiuDeHua implements Star { @Override public void sing() { System.out.println("唱忘情水..."); } @Override public void dance() { System.out.println("华仔在跳老年迪斯高.."); } } // 2. 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】 class LiuDeHuaWrapper implements Star { // 3. 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。 private LiuDeHua huaZai;// 0x001 public LiuDeHuaWrapper(LiuDeHua huaZai) {// 0x001 this.huaZai = huaZai; } @Override public void sing() { // 4. 在装饰类中对sing方法进行功能扩展 System.out.print("华仔深情"); huaZai.sing(); } @Override public void dance() { // 5. 对dance不做改动 huaZai.dance(); } } // 明星接口 , 装饰类和被装饰类的父类型 interface Star { public abstract void sing(); // 唱歌 public abstract void dance();// 跳舞 }
内容小结
- 装饰类和被装饰类需要有共同的父类型。
- 装饰类要传入被装饰类的对象
- 在装饰类中把要增强扩展的功能进行扩展
- 对于不要增强的功能直接调用
8 commons-io工具包(对文件的拷贝做优化)
学习目标
- 能够熟悉导入commons-io工具包,并使用
内容讲解
【1】三方库的导入
下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/
把commons-io-2.6.jar包复制到指定的Module的lib目录中
将commons-io-2.6.jar加入到项目中
在这里插入图片描述
【2】API
1)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以上)
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复制文件的操作。