# 目标

  1. 能够使用字节缓冲流读取数据到程序
  2. 能够使用字节缓冲流写出数据到文件
  3. 能够明确字符缓冲流的作用和基本用法
  4. 能够使用缓冲流的特殊功能
  5. 能够阐述编码表的意义
  6. 能够使用转换流读取指定编码的文本文件
  7. 能够使用转换流写入指定编码的文本文件
  8. 能够使用序列化流写出对象到文件
  9. 能够使用反序列化流读取文件到程序中
  10. 能够使用Properties的load方法加载文件中配置信息

# IO异常的处理

# JDK1.7前处理异常

之前的入门练习,我们一直把异常抛出,而实际开发中并不能这样处理,建议使用 try...catch...finally 代码块,处理异常部分。

try {
	可能有问题的代码;
} catch (异常类名 变量名) {
	处理异常的代码
} finally {
	释放资源
}

示例代码:

public static void test01() {
    FileWriter fw = null;
    try {
        fw = new FileWriter("1.txt");
        fw.write("abc");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fw != null) {
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

# JDK1.7及以后处理异常

JDK7增加 try-with-resource 语句来处理资源,该语句确保了每个资源在语句结束时关闭。

try (创建流的代码) {
	其他代码;
} catch (IOException e) {
	处理异常的代码
}

注意: try() 中的代码不是什么都可以的,主要用来使用的资源的资源管理。
示例代码如下:

public static void test02() {
    // 类实现了AutoCloseable接口就能放到try的()中,就会自动关流
    try (
        FileWriter fw = new FileWriter("study_day12\\abc\\1.txt");
        FileReader fr = new FileReader("study_day12\\abc\\2.txt");
    ) {
        fw.write("aaa");
    } catch (IOException e) {
        System.out.println("处理了IO流异常!");
    }
}

# 练习

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

/*
目标:学习IO异常的处理
    JDK7以前的处理IO流异常
        try {
            可能有问题的代码
        } catch (异常类名 变量名) {
            处理异常的代码
        } finally {
            关闭流
        }

        代码繁琐

    JDK7处理IO流异常
        会自动关闭流

        try (创建流的代码) {
            可能有问题的代码
        } catch (异常类名 变量名) {
            处理异常的代码
        }

 */
public class Demo {
    public static void main(String[] args) {
//        JDK1.7 以前的 IO 处理
        FileWriter fw = null;
        try {
            fw = new FileWriter("D:\\learn\\java\\code\\javaUp\\day11\\doc\\1.txt");
            fw.write("好好玩呀,真的好开心");
        } catch(IOException err) {
            System.out.println("处理 IO 异常");
        } finally {
            if (fw != null) {
                try {
                    fw.close();
                } catch(IOException err) {
                    err.printStackTrace();
                }
            }
        }

        // JDK1.7处理IO流异常, 只有实现了AutoCloseable 接口的类才能放入,会自动关闭流
        try(
                FileWriter fileWriter = new FileWriter("D:\\learn\\java\\code\\javaUp\\day11\\doc\\1.txt");
                FileReader fileReader = new FileReader("D:\\learn\\java\\code\\javaUp\\day11\\doc\\1.txt");
        ) {
            fileWriter.write("哈哈哈");
        } catch(IOException err) {
            err.printStackTrace();
        }
    }
}

# 缓冲流

# 缓冲流作用

之前我们学到的基本流有:

  • 字节流: FileInputStream , FileOutputStream
  • 字符流: FileReader , FileWriter

后面我们要学的缓冲流,转换流,对象流等是对基本流的增强。

缓冲流是对4个基本的 FileXxx 流的增强,所以也是4个流,按照数据类型分类:

  • 字节缓冲流: BufferedInputStream , BufferedOutputStream
  • 字符缓冲流: BufferedReader , BufferedWriter

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认 8KB 大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

# 字节缓冲流

image.png
今天我们要学的字节缓冲流 API 有:

构造器 说明
public BufferedInputStream(InputStream in) 把基本的字节输入流包装成一个字节输入缓冲流,从而提高字节输入流读数据的性能
public BufferedOutputStream(OutputStream out) 把基本的字节输出流包装成一个字节输出缓冲流,从而提高写数据的性能

# 字节流复制文件效率对比

使用字节流四种方式复制文件测试不同方式下的性能情况

需求:使用四种方式复制 "D:\\learn\\java\\code\\javaUp\\day11\\doc\\1.txt" 到 "D:\\learn\\java\\code\\javaUp\\day11\\doc\\2.txt"

文件1.txt的大小为 4285kb

分析:
字节流四种方式复制文件
基本流读写一个字节
基本流读写一个字节数组
缓冲流读写一个字节
缓冲流读写一个字节数组

代码如下:

package demo03字节流四种复制文件_扩展;

import java.io.*;

/*
目标:使用四种方式复制 "D:\\learn\\java\\code\\javaUp\\day11\\doc\\1.txt" 到 "D:\\learn\\java\\code\\javaUp\\day11\\doc\\2.txt"

四种方式复制:
    基本流读写一个字节
    基本流读写一个字节数组
    缓冲流读写一个字节
    缓冲流读写一个字节数组

小结:
    缓冲流读写一个字节比基本流读取一个字节快非常多
    缓冲流读写一个字节数组和基本流读取一个字节数组差不多

    如果使用读写一个字节时,建议使用缓冲流
    如果使用读写一个字节数组,建议使用基本流
 */
public class Demo {
    public static void main(String[] args) throws IOException {
        long startTime1 = System.currentTimeMillis();
        test1();
        long endTime1 = System.currentTimeMillis();
        System.out.println("基本流读写一个字节:" + (endTime1 - startTime1));

        long startTime2 = System.currentTimeMillis();
        test2();
        long endTime2 = System.currentTimeMillis();
        System.out.println("基本流读写一个字节数组:" + (endTime2 - startTime2));

        long startTime3 = System.currentTimeMillis();
        test3();
        long endTime3 = System.currentTimeMillis();
        System.out.println("缓冲流读写一个字节:" + (endTime3 - startTime3));

        long startTime4 = System.currentTimeMillis();
        test4();
        long endTime4 = System.currentTimeMillis();
        System.out.println("缓冲流读写一个字节数组:" + (endTime4 - startTime4));
    }
    // 基本流读写一个字节
    public static void test1() throws IOException {
        // 创建输入流
        FileInputStream fileInputStream = new FileInputStream("D:\\learn\\java\\code\\javaUp\\day11\\doc\\1.txt");
        // 创建输出流
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\learn\\java\\code\\javaUp\\day11\\doc\\2.txt");

        // 循环读写
        int b; // 保存读取的数据
        while ((b = fileInputStream.read()) != -1) {
            fileOutputStream.write(b);
        }

        // 关闭流
        fileOutputStream.close();
        fileInputStream.close();
    }
    // 基本流读写一个字节数组
    public static void test2() throws IOException {
        // 创建输入流
        FileInputStream fileInputStream = new FileInputStream("D:\\learn\\java\\code\\javaUp\\day11\\doc\\1.txt");
        // 创建输出流
        FileOutputStream fileOutputStream = new FileOutputStream("D:\\learn\\java\\code\\javaUp\\day11\\doc\\2.txt");

        // 循环读写
        byte[] buf = new byte[1024 * 8]; // 保存读取的数据
        int len; // 保存读取的数量
        while ((len = fileInputStream.read(buf)) != -1) {
            fileOutputStream.write(buf,0, len);
        }

        // 关闭流
        fileInputStream.close();
        fileOutputStream.close();
    }
    // 缓冲流读写一个字节
    public static void test3() throws IOException {
        // 创建输入流
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("D:\\learn\\java\\code\\javaUp\\day11\\doc\\1.txt"));
        // 创建输出流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("D:\\learn\\java\\code\\javaUp\\day11\\doc\\2.txt"));

        // 循环读写
        int b; // 保存读取的数据
        while ((b = bufferedInputStream.read()) != -1) {
            bufferedOutputStream.write(b);
        }

        // 关闭流
        bufferedOutputStream.close();
        bufferedInputStream.close();
    }
    // 缓冲流读写一个字节数组
    public static void test4() throws IOException {
        // 创建输入流
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("D:\\learn\\java\\code\\javaUp\\day11\\doc\\1.txt"));
        // 创建输出流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("D:\\learn\\java\\code\\javaUp\\day11\\doc\\2.txt"));

        // 循环读写
        byte[] buf = new byte[1024 * 8];
        int len;
        while ((len = bufferedInputStream.read(buf)) != -1) {
            bufferedOutputStream.write(buf, 0, len);
        }

        // 关闭流
        bufferedInputStream.close();
        bufferedOutputStream.close();
    }
}

输出结果:

基本流读写一个字节:66497
缓冲流读写一个字节:174

基本流读写一个字节数组:30
缓冲流读写一个字节数组:20

从上面的输出结果可以看出:
基本流读写一个字节,从文件中读取一个字节到内存,再写一个字节到文件,非常慢
image.png
缓冲流读写一个字节,从文件中读取8192个字节到缓冲输入流的内存,再从内存读取一个字节到变量再写8192个字节到文件,比较快
image.png
基本流读写一个字节数组,从文件中读取8192个字节到我们自己的数组,再写8192个字节到文件,非常快
image.png
缓冲流读写一个字节数组,从文件中读取8192个字节到缓冲输入流的内存,再复制到我们自己的数组再把我们自己的数组8192复制到缓冲字节输出流的内存,再写8192个字节到文件,快
image.png
总结:

  • 缓冲流读取一个字节比基本流读取一个字节快非常多;
  • 缓冲流读取一个字节数组和基本流读取一个字节数组差不多;

如果使用读一个字节时,建议使用缓冲流;如果使用读一个字节数组,建议使用基本流。

# 字符缓冲流及特有方法

字符输出缓冲流:BufferedWriter,继承了Writer, 可以使用父类Writer中的write方法

构造器 说明
public BufferedWriter(Writer r) 把基本的字符输出流包装成一个字符输出缓冲流,从而提高字符输出流写数据的性能

字符输出缓冲流新增功能

方法 说明
public String newLine() 换行

字符缓冲输入流:BufferedReader 继承了Reader,可以使用父类Reader中的read方法

构造器 说明
public BufferedReader(Reader r) 把基本的字符输入流包装成一个字符输入缓冲流,从而提高字符输入流读数据的性能

字符输入缓冲流新增功能

方法 说明
public String readLine() 读取一行数据返回,如果读取没有完毕,无行可读返回null

练习代码如下:

package com.itheima.demo04字符缓冲流;

import java.io.*;

/*
目标:字符缓冲流及特有方法(重点)

讲解:
    字符输出缓冲流
    字符输入缓冲流

小结:
    1.BufferReader特有方法?
        readLine() // 读一行, 读取到\r\n结束

    2.BufferedWriter特有方法?
        newLine() 换行, 相当于write("\r\n");
 */
public class Demo04 {
    public static void main(String[] args) throws IOException {
        // test01();
        test02();
    }

    /*
        BufferedWriter: 缓冲字符输出流, 继承了Writer, 还是使用之前的write写数据
            构造方法:
                BufferedWriter(Writer out) 创建缓冲字符输出流, 对参数传入的流进行包装加强.
            特有方法:
                newLine() 换行
     */
    public static void test01() throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("study_day11\\abc\\3.txt", true));
        bw.write("今天很冷!");
        bw.newLine(); // 相当于write(\r\n");
        bw.write("明天升温!");
        bw.close();
    }


    /*
        BufferedReader: 缓冲字符输入流, 继承了Reader,还是使用read读数据
            构造方法:
                BufferedReader(Reader in) 创建缓冲字符输入流, 对参数传入的流进行包装加强
            特有方法:
                readLine() // 读一行, 读取到\r\n结束
     */
    public static void test02() throws IOException {
        /*BufferedReader br = new BufferedReader(new FileReader("study_day11\\abc\\3.txt"));
        String line = br.readLine();
        System.out.println(line);

        line = br.readLine();
        System.out.println(line);

        // 读取不到数据返回null
        line = br.readLine();
        System.out.println(line);*/

        BufferedReader br = new BufferedReader(new FileReader("study_day11\\abc\\3.txt"));
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
        br.close();
    }
}

# 字符流扩展练习

目标:将文件每一行数据读取放到Map中,= 前面是键,= 后面是值

讲解:
    分析:
        1.创建缓冲字符输入流
        2.创建Map集合
        3.循环读取每一行数据
        4.使用=分割,存储到Map中,=前面的数据是键,=后面的是值

代码如下:

package demo05字符流扩展练习;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;

/*
目标:将文件每一行数据读取放到Map中,= 前面是键,= 后面是值

讲解:
    分析:
        1.创建缓冲字符输入流
        2.创建Map集合
        3.循环读取每一行数据
        4.使用=分割,存储到Map中,=前面的数据是键,=后面的是值
 */
public class Demo {
	public static void main(String[] args) throws IOException {
		// 1. 创建缓冲字符输入流
		BufferedReader bufferedReader = new BufferedReader(new FileReader("D:\\learn\\java\\code\\javaUp\\day11\\doc\\1.txt"));

		// 2. 创建 Map 集合

		// 报错,不能直接创建 Map 因为 'Map' is abstract; cannot be instantiated
		// Map<String, InternalError> stringInternalErrorMap = new Map<>();

		//        HashMap<String, String> stringStringHashMap = new HashMap<>();
		HashMap<String, Integer> stringIntegerHashMap = new HashMap<>();

		// 3.循环读取每一行数据
		String line; // 保存读取到的一行数据
		while ((line = bufferedReader.readLine()) != null) {
			// 4.使用=分割,存储到Map中,=前面的数据是键,=后面的是值
			// line是     name=fang
			String[] split = line.split("=");
			//            stringStringHashMap.put(split[0], split[1]);

			// stringIntegerHashMap 接收的第二个参数是 Integer 类型,需要转换
			stringIntegerHashMap.put(split[0], Integer.parseInt(split[1]));
		}

		// 遍历Map
		// 1.键找值, 2.键值对
		//        stringStringHashMap.forEach(new BiConsumer<String, String>() {
		//            @Override
		//            public void accept(String key, String value) {
		//                System.out.println(key + ":::" + value);
		//            }
		//        });

		//        stringStringHashMap.forEach((key, value) -> {
		//            System.out.println(key + ":::" + value);
		//        });

		stringIntegerHashMap.forEach((key, value) -> {
			System.out.println(key + ":::" + value);
		});
	}
}

# 小结

  • 字符缓冲流自带8K缓冲区可以提高基本字符流读写数据的性能
  • 说出字符缓冲流的新增功能
    • BufferedReader(Reader r) 多了 readLine() 读取一行的功能
    • BufferedWriter(Writer w)多了 newLine() 换行的功能

# 转换流

# 字符集和字符编码的概念

字符集是一些字符的集合,如:ASCII字符集、ISO-8859-1字符集、GBxxx字符集、Unicode字符集等。

字符编码是字符与数字之间的对应规则。

将字符转成二进制,称为编码。如:使用ASCII编码,将 'b' -> 01100010
将二进制数解析成文字,称为解码。如:使用ASCII编码,将 01100010 -> 'b'

# 字符流读不同编码的文件乱码

需求:分别使用如下两种方式读取文件内容

  • 代码编码是UTF-8,文件编码也是UTF-8,使用字符流读取观察输出的中文字符结果。
  • 代码编码是UTF-8,文件编码使用GBK,使用字符流读取观察输出的中文字符结果。

中文在GBK编码中占2个字节,中文在UTF-8编码中占3个字节,IDEA默认是UTF-8编码在IDEA中,使用 FileReader 读取项目中的文本文件。由于IDEA的设置,都是默认的 UTF-8 编码,所以没有任何问题。但是当读取Windows系统中创建的文本文件时,由于Windows系统的默认是GBK编码,就会出现乱码。

为什么会乱码?保存和读取使用了不同的编码。
如何解决乱码?保证读取时的编码和保存的编码是相同的就可以。

# 转换流使用

InputStreamReader 指定编码读取数据

方法名 说明
InputStreamReader(InputStream in) 使用默认编码读取数据
InputStreamReader(InputStream in, String charsetName) 使用指定编码读取数据

OutputStreamWriter 指定编码写数据

方法名 说明
OutputStreamWriter(OutputStream out) 使用默认编码写数据
OutputStreamWriter(OutputStream out, String charsetName) 使用指定编码写数据

# 转换流案例-转换文件编码

需求:将GBK编码的文本文件,转换为UTF-8编码的文本文件。
分析:
1. 创建输入流InputStreamReader指定GBK读取文件
2. 创建输出流OutputStreamWriter指定UTF-8写文件
3. 循环读写数据
4. 关闭流

操作字符时如何选择流?

  • 基本流: FileReader / FileWriter
  • 转换流: InputStreamReader / OutputStreamWriter

如果不需要修改编码就使用基本流,使用更简单;如果需要修改编码就使用转换流。

# 对象流

# 对象流的使用

对象流分为两类:

  • ObjectOutputStream: 对象输出流
  • ObjectInputStream: 对象输入流

image.png
对象操作流API

构造器 说明
ObjectOutputStream(OutputStream out) 对象输出流,把对象以字节的形式写到文件
public void writeObject(Object obj) 写一个对象到文件中
ObjectInputStream(InputStream in) 对象输入流,把写到文件中的对象读到程序中
public Object readObject() 读取文件中的对象

注意:对象流操作的类需要实现 Serializable 接口,否则会出现 NotSerializableException 异常。

# 对象流使用细节

  • InvalidClassException: 无效的类异常,原因是保存数据后,修改了类,再读取解决方案:给类添加一个版本号,模仿String类的版本号(serialVersionUID) private static final long serialVersionUID = 42L;
  • 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?给该成员变量加 transient 关键字修饰,该关键字标记的成员变量不参与序列化过程

# 打印流

  • 作用:打印流可以实现方便、高效的打印数据到文件中去。打印流有:PrintStream,PrintWriter两个类。
  • 可以实现原样打印,例如打印整数97写出去就是97,打印boolean的true,写出去就是true。 | 构造器 | 说明 | | --- | --- | | PrintStream(File file) | 创建PrintStream, 指定打印的目的地 | | PrintStream(String fileName) | 创建PrintStream, 指定打印的目的地 | | PrintStream(OutputStream out) | 创建PrintStream, 指定打印的目的地 |
方法名 说明
print(Xxx x) 打印数据, 不换行
println(Xxx x) 打印数据, 换行

# 属性集

# Properties作为集合使用

image.png
Properties类表示属性集。他是一个特殊的集合,可以结合 IO 流操作。

  • Properties实现了Map接口,就是一个双列集合可以存储键值对数据,键和值都是字符串类型。
  • Properties可以结合流进行操作,可以把集合中的数据保存到流中,也可以从流中来加载数据。

Properties作为集合的特有方法:

方法名 说明
Object setProperty(String key, String value) 添加键和值,键和值都是String类型
String getProperty(String key) 通过键获取值
Set stringPropertyNames() 获取所有的键

# 代码示例

Properties作为集合使用:

package com.itheima.demo14Properties_重点;

import java.util.Properties;
import java.util.Set;

/*
目标:学习Properties作为集合的使用

讲解:
    继承体系
       Map
        ↑   实现
    Hashtable
        ↑   继承
    Properties


    Properties类介绍:
        1.Properties实现了Map接口,就是一个双列集合可以存储键值对数据, 键和值都是字符串类型
        2.Properties可以结合流进行操作, 可以把集合中的数据保存到流中, 也可以从流中来加载数据

    Properties作Map集合的特有方法:
        Object setProperty(String key, String value) 添加键值对,修改键值对
        String getProperty(String key) 通过键获取值

小结:
    Properties是一个Map集合,可以存储键值对数据,键和值都是String类型

 */
public class Demo141 {
    public static void main(String[] args) {
        Properties pp = new Properties();
        System.out.println("添加前pp = " + pp);

        // 添加键值对
        pp.setProperty("name", "唐僧");
        pp.setProperty("age", "23");
        pp.setProperty("address", "东土大唐");

        System.out.println("添加后pp = " + pp);

        // 通过键获取值
        String name = pp.getProperty("name");
        String age = pp.getProperty("age");
        String address = pp.getProperty("address");
        String sex = pp.getProperty("sex"); // 键不存在返回null
        System.out.println(name + ": " + age + ": " + address + ": " + sex);

        // 遍历Properties,使用forEach
        pp.forEach((key, value) -> {
            System.out.println(key + ": " + value);
        });

        System.out.println("--------------------------------------");

        // 了解: stringPropertyNames() 相当于 keySet() 获取所有的键
        Set<String> names = pp.stringPropertyNames();
        for (String s : names) {
            System.out.println(s);
        }
    }
}

# Properties和IO流结合使用

Properties 和 IO 流结合的方法:

方法名 说明
void load(Reader reader) 从输入字符流读取属性列表(键和值)
void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)
void store(Writer writer, String comments) 将Properties中的键和值写入输出字符流中
void store(OutputStream out, String comments) 将Properties中的键和值写入输出字节流中

属性文件介绍:后缀是.properties结尾,里面的内容一行一个 key=value,后续可以做为软件的配置文件。

# 代码示例

Properties 保存和加载文件数据:

package com.itheima.demo14Properties_重点;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

/*
目标:学习Properties保存和加载文件数据

讲解:
    Properties和流操作的特有方法:
        void store(Writer writer, String comments) 将Properties集合中的键值对保存到流中
            Writer writer: 指定保存的目的地
            String comments: 注释

        void load(Reader reader) 从流中加载数据到Properties对象中
            Reader reader: 指定读取的路径
 */
public class Demo142 {
    public static void main(String[] args) throws IOException {
        // testStore();
        testLoad();
    }

    // Properties属性集保存数据到文件: void store(Writer writer, String comments)
    public static void testStore() throws IOException {
        Properties pp = new Properties();
        pp.setProperty("name", "admin");
        pp.setProperty("password", "123456");

        pp.store(new FileWriter("study_day11\\abc\\pp.properties"), "数据库信息");
    }

    // 加载文件中数据到Properties属性集中: void load(Reader reader)
    public static void testLoad() throws IOException {
        Properties pp = new Properties();
        System.out.println("加载前: " + pp);

        pp.load(new FileReader("study_day11\\abc\\pp.properties"));
        // pp.load(new FileReader("study_day11\\abc\\student.txt"));
        System.out.println("加载后: ");
        pp.forEach((key, value) -> {
            System.out.println(key + ": " + value);
        });
    }
}

# 装饰模式

# 装饰模式介绍

装饰设计模式的作用:可以原本的类增强和扩展类的功能

# 了解装饰模式3个角色

装饰设计模式有3个角色:

  • 原对象
  • 装饰对象
  • 共同的接口

# 代码演示装饰模式

package com.itheima.demo15装饰设计模式;

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

public class Demo15 {
    public static void main(String[] args) throws IOException {
        // 创建装饰类, 对蔡徐坤进行包装加强
        CaiXuKunWarpper warpper = new CaiXuKunWarpper(new CaiXuKun());

        warpper.sing(); // 不变
        warpper.dance(); // 加强
        warpper.rap(); // 扩展新功能

        // 其实我们今天学的这些类基本上都是使用了装饰模式
        // BufferedReader br = new BufferedReader(new FileReader("study_day11\\abc\\3.txt"));
        // br.read(); // 加强
        // br.readLine(); // 扩展新功能
        // br.close(); // 不变
    }
}
package com.itheima.demo15装饰设计模式;

// 明星接口
public interface Star {
    public abstract void sing();
    public abstract void dance();
}
package com.itheima.demo15装饰设计模式;

// 原对象
public class CaiXuKun implements Star {
    @Override
    public void sing() {
        System.out.println("蔡徐坤一本正经的唱歌!");
    }

    @Override
    public void dance() {
        System.out.println("蔡徐坤一本正经的跳舞!");
    }
}
package com.itheima.demo15装饰设计模式;

// 装饰类
public class CaiXuKunWarpper implements Star {
    // 拥有蔡徐坤,进行加强或扩展
    public Star star;

    // 构造器给成员变量赋值

    public CaiXuKunWarpper(Star star) {
        this.star = star;
    }

    // 不改变,原样调用
    @Override
    public void sing() {
        star.sing();
    }

    // 增强功能
    @Override
    public void dance() {
        System.out.println("一边跳舞,一边打篮球");
    }

    // 扩展新功能
    public void rap() {
        System.out.println("鸡你太美!");
    }
}

# 小结

这章我们主要介绍了一些常用的 IO 流方法,方法比较多,可能有点迷糊,下面这张图列出它们之间的关系:
image.png
总结:

package com.itheima.demo16总结;

import java.io.IOException;

/*
能够使用字节缓冲流读取数据到程序
    BufferedInputStream类:
        int read(): 读取一个字节数据
        int read(byte[] buf): 读取一个字节数组的数据

能够使用字节缓冲流写出数据到文件
    BufferedOutputStream类
        void write(int b): 写一个字节
        void write(byte[] bs): 写一个字节数组

能够明确字符缓冲流的作用和基本用法
    字符缓冲流的作用: 提高效率,有新功能

    字符缓冲流的和基本用法:
        字符缓输入缓冲流: new BufferedReader(new FileReader(路径));

        字符缓输出缓冲流: new BufferedWriter(new FileWriter(路径));

能够使用缓冲流的特殊功能
    BufferedReader:
        String readLine(): 读取一行

    BufferedWriter:
        void newLine(): 换行

能够阐述编码表的意义
    让文字对应数字,保存到计算中

能够使用转换流读取指定编码的文本文件
    InputStreamReader isr = new InputStreamReader(new FileInputStream("路径"), "编码");
    isr.read();

能够使用转换流写入指定编码的文本文件
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("路径"), "编码");
    osw.write(文字);

能够使用序列化流写出对象到文件
    Person p = new Person("凤姐", 18);
    // 1.创建对象输出流
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("study_day11\\abc\\persons.txt"));

    // 2.写对象到文件
    oos.writeObject(p);

    // 3.关闭
    oos.close();

能够使用反序列化流读取文件对象数据到程序中
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("study_day11\\abc\\persons.txt"));

    Object obj = ois.readObject(); // Person obj = (Person) ois.readObject();
    System.out.println("obj = " + obj);

    ois.close();

能够使用Properties的load方法加载文件中配置信息
    Properties pp = new Properties();

    pp.load(new FileReader("study_day11\\abc\\pp.properties"));

    System.out.println("加载后: ");
    pp.forEach((key, value) -> {
        System.out.println(key + ": " + value);
    });

 */
public class Demo {
    public static void main(String[] args) throws IOException {
    }
}