12-JavaSE【缓冲流、转换流、序列化流、装饰者模式、commons-io工具包】

回顾上次课内容

  • IO流分类:

    • 字节输入流:InputStream
    • 字节输出流:OutputStream
    • 字符输入流:Reader
    • 字符输出流:Writer
  • 字节流

    • 特点:以字节为单位,对任意类型的文件进行读写操作

    • 字节输出流:OutputStream

      • 因为OutputStream是一个抽象类,所以使用其子类:FileOutputStream类

      • 构造方法:

        • //覆盖写入
          FileOutpuStream fos = new FileOutputStream("文件路径");
          FileOutpuStream fos = new FileOutputStream(File对象);
          
          //追加写入
          FileOutpuStream fos = new FileOutputStream("文件路径" , true);
          FileOutpuStream fos = new FileOutputStream(File对象 , true);
          
      • 常用方法:

        • write(int b)
          write(byte[] buf)
          write(byte[] buf, int offset , int len)
              
          close()    
          
    • 字节输入流:InputStream

      • InputStream是一个抽象类,使用其子类:FileInputStream类

      • 构造方法:

        • FileInputStream fis = new FileInputStream("文件路径")
          FileInputStream fis = new FileInputStream(文件对象)
          
      • 常用方法:

        • int read()
          int read(byte[] buf)
          
          close()    
          
  • 字符流

    • 特点:以字符为单位,对文本类型的文件进行读写操作

      • 文本类型的文件: 只要使用记事本软件打开的文档,可以看懂就是文本文件(没有乱码)
      • 字节流的弊端:当文本文件中有中文数据时,一个中文数据占2个或3个字节(读取中文有乱码)
        • 解决方案:字符流 (一个中文就是一个字符)
    • 字符输出流:Writer

      • Writer是一个抽象类,使用其子类:FileWriter

      • 构造方法:

        • //覆盖写入
          FileWriter fw = new FileWriter("文件路径");
          FileWriter fw = new FileWriter(File对象);
          
          //追加写入
          FileWriter fw = new FileWriter("文件路径" , true);
          FileWriter fw = new FileWriter(File对象 , true);
          
      • 常用方法:

        • write(int c)
          write(char[] cbuf)
          write(char[] cbuf, int offset , int len)
          write(String s)
          write(String s, int  offset , int len)  
          //以上所有的write()方法,是把字符数据写入到缓冲区中   
              
          close()
          flush() //刷新流  把缓冲区中的数据写入到指定文件中
          
    • 字符输入流:Reader

      • Reader是一个抽象类,使用其子类:FileReader类

      • 构造方法:

        • FileReader fr = new FileReader("文件路径")
          FileReader fr = new FileReader(文件对象)
          
      • 常用方法:

        • int read() //读一个字符
          int read(char[] cbuf) //最多读取cbuf.length个字符数据,读取到的字符数据存储到char[]数组中,并返回实际读取到的字符数据个数。读取到文件末尾时返回:-1   
              
          close()    
          
  • Properties类

    • 作用:用来加载xxx.properties属性集文件

      • 借用IO流可以实现对属性集文件的读写操作
    • 特点:

      • Properties类实现Map接口
      • 数据以K-V形式存储到集合中
      • Properties中的k-v都是String类型
      • 可以借助IO流
    • 使用:

      • 构造方法:

        • Properties pro = new Properties();
          
      • 常用方法:

        • //向属性集文件中写入数据
          pro.setProperty("key值","value值");
          pro.stroe( new FileWriter(".../xxx.properties", true) );
          
          //从属性集文件中读数据
          pro.load( new FileReader(".../xx.properties") );
          //获取所有的key
          Set<String> keys = pro.stringPropertyNames();
          for(String key : keys){
              //通过key,获取value
              String value = pro.getProperty(key);
          }
          

学习了基本的一些流,作为IO流的入门,我们要见识一些更强大的流。

比如能够高效读写的缓冲流,

能够转换编码的转换流,

能够持久化存储对象的序列化流等。

这些功能更为强大的流,都是在基本的流对象基础之上创建而来的

相当于是对基本流对象的一种增强。

一、缓冲流

缓冲流,也叫高效流(作用:可以提高对文件进行读写的效率)

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

缓冲流,自身是不具备直接对文件进行读写操作的功能,需要依赖于字节流或字符流。

  • 字节缓冲流BufferedInputStreamBufferedOutputStream
  • 字符缓冲流BufferedReaderBufferedWriter

在这里插入图片描述

缓冲流的分类:

  • 字节缓冲流
    • 字节输入缓冲流
    • 字节输出缓冲流
  • 字符缓冲流
    • 字符输入缓冲流
    • 字符输出缓冲流

1_缓冲流:字节缓冲流

01、字节输入缓冲流

java.io.BufferedInputStream

构造方法:

//字节输入缓冲流自己不具备读操作,需要依赖字节输入流
public BufferedInputStream(InputStream input) //使用默认的缓冲区大小:8*1024

//创建一个字节输入缓冲流,并指定缓冲区大小
public BufferedInputStream(InputStream input , int size)    

字节输入缓冲流中的方法,都可以使用InputStream中的

02、字节输出缓冲流

java.io.BufferedOutputStream

构造方法:

//缓冲流自身不具备写操作。需要依赖字节输出流
public BufferedOutputStream(OutputStream out) //使用默认缓冲区大小

public BufferedOutputStream(OutputStream out, int size) //使用指定的缓冲区大小
       
//示例:以追加写入方式使用字节输出缓冲流
OutputStream out = new FileOutputStream("文件路径",true);//追加写入    
BufferedOutputStream bos = new BufferedOutputStream( out );    

字节输出缓冲流中的方法,都可以使用OutputStream中的

案例:使用字节缓冲流复制视频文件

 public static void copy(File src, File dest) throws IOException {
        //创建字节输入流,关联源文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
        //创建字节输出流,关联目标文件
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest));

        int len=0;
        byte[] buf = new byte[1024];

        while((len=bis.read(buf)) !=-1){
            bos.write(buf,0,len);
        }

        //释放流
        bis.close();
        bos.close();
    }
}

小结

字节缓冲流:

  • BufferedInputStream
    • 缓冲流,自身不能直接读操作,需要依赖字节输入流
  • BufferedOutputStream
    • 缓冲流,自身不能直接写操作,需要依赖字节输出流

可以通过构造方法中修改缓冲流中缓冲区大小

2_缓冲流:字符缓冲流

1、字符输入缓冲流

java.io.BufferedReader

  • 构造方法

    • BufferedReader(Reader in)//依赖字符输入流实现读操作,使用默认的缓冲区大小
      BufferedReader(Reader in, int size)//可设置缓冲区大小    
      
  • 常用方法

    • 可以使用之前学习Reader类方法
2、字符输出缓冲流

java.io.BufferedWriter

  • 构造方法:

    • BufferedWriter(Writer out)//依赖于字符输出流实现写操作。使用默认缓冲区大小
      BufferedWriter(Writer out, int sz)    
      
  • 常用方法

    • 可以使用之前学习的Writer类中的方法
3、字符缓冲流特有方法
  • BufferedReader:public String readLine(): 读一行文字。
  • BufferedWriter:public void newLine(): 写一行行分隔符,由系统属性定义符号。

案例:使用字符缓冲流复制文本文件

    public static void copy(File src, File dest) throws IOException {
        //创
        FileReader fr = new FileReader(src);
        BufferedReader br =new BufferedReader(fr);

        BufferedWriter bw = new BufferedWriter(new FileWriter(dest));

        //读写
//        int len=0;
//        char[] cbuf = new char[1024];
//        while((len=br.read(cbuf)) !=-1){
//            bw.write(cbuf,0,len);
//        }
        //按行读写
        String line =null;
        while(  (line = br.readLine()  ) != null){
            bw.write(line);
            bw.newLine();//写入换行符
        }

        //关
        fr.close();
        br.close();
        bw.close();
    }
}

小结

字符缓冲流:

  • 字符输入缓冲流:BufferedReader
  • 字符输出缓冲流:BufferedWriter

可以使用字符缓冲流中特有的方式来读写文件:按行读写

BufferedReader br = new BufferedReader( FileReader对象 )
BufferedWriter bw = new BufferedWriter( FileWriter对象 )
    
String line;
while((line = br.readLine()) != null){
    bw.write(line);
    bw.newLine();
}
    

二、转换流

字符编码和字符集

编码:将字符按照某种规则转换为二进制数据存储到硬盘。

解码:将硬盘中的二进制数据读取按照某种规则转换为字符。

在计算机的硬盘上,保存的数据其实都是2进制数据

我们在实际操作时,使用的数据大多都是文本数据(字符、数字)

// 文本数据  ==转换==>  2进制数据  ==写入==>  硬盘  (编码)
// 硬盘  ==读取==>  2进制数据  ==转换==>  文本数据  (解码)

编码和解码都需要依赖于编码表(字符集)实现字符和2进制的转换

图解:

在这里插入图片描述

字符集 (Charset):也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。

什么是编码表?

  • 把生活中的数据和计算机中的数据存储在一张表中(对应关系)

  • 编码和解码的转换是借用了编码值实现的

  • 生活中的数据编码值计算机中的数据
    A6500000000 00000000 00000000 01000001
    20000310101011 00101010 … (模拟的)

计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码,常见字符集有ASCII字符集、GBK字符集、Unicode字符集等

在这里插入图片描述

指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的

了解

  • ASCII字符集
  • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
  • 基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256字符,方便支持欧洲常用字符。
  • ISO-8859-1字符集
  • 拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
  • ISO-5559-1使用单字节编码,兼容ASCII编码。
  • GBxxx字符集
  • GB就是国标的意思,是为了显示中文而设计的一套字符集。
  • GB2312:简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。
  • GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
  • GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
  • Unicode字符集
  • Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。
  • 它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF-32。最为常用的UTF-8编码。
  • UTF-8编码,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以,我们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每个字符编码,编码规则:
    1. 128个US-ASCII字符,只需一个字节编码。
    2. 拉丁文等字符,需要二个字节编码。
    3. 大部分常用字(含中文),使用三个字节编码。
    4. 其他极少使用的Unicode辅助字符,使用四字节编码
1、字符编码问题

概述

Windows OS 默认的编码 GBK。如果使用GBK编码边写保存的一个文本文件。

再使用FileReader去读取,这个时候就有可能会乱码了。如果存在中文就会乱码了,FileReader默认采用的编码是UTF-8

结论:编码和解码所采用的编码表要一样,否则就会出现乱码

案例:字符乱码

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

        //FileReader类默认使用:UTF-8字符集
        //gbk-file.txt文件,使用:GB2312字符集
        FileReader fr = new FileReader("f:/files/gbk-file.txt");

        int len;
        char[] cbuf = new char[2];
        while ((len = fr.read(cbuf)) != -1) {
            System.out.println(new String(cbuf,0,len));
            //因为使用的编码不同,会出现乱码
        }
    }
}

2、转换流:InputStreamReader
  • java.io.InputStreamReader类,是FileReader的父类
  • InputStreamReader是从字节流到字符流的桥梁,可以实现把字节流转换为字符流
  • 可以使用InputStream流指定字符集,按照指定的字符集读取文件

构造方法:

//转换流自身不具备对文件读操作。需要依赖于字节输入流实现读文件。
InputStreamReader(InputStream in)//使用默认的字符集(以当前环境为主)//UTF-8
        
//创建转换流对象,指定字符集    
InputStreamReader(InputStream in, String charsetName)
    
    
//示例:  创建一个可以读取GBK编码的流对象
     InputStreamReader isr = new InputStreamReader(new FileInputStream("文件"), "GBK");

常用方法:

可以使用Reader类中的方法

**案例:**InputStreamReader读取GBK编码文件

public class InputStreamReaderDemo1 {
    public static void main(String[] args) throws IOException {
        //创建转换流(读操作)
        InputStreamReader isr = new InputStreamReader(new FileInputStream("F:/files/gbk-File.txt"),"gbk");
        
        int len;
        char[] cbuf = new char[2];
        while ((len = isr.read(cbuf)) != -1) {

            System.out.println(new String(cbuf,0,len));
        }
    }
}

3、转换流:OutputStreamWriter
  • java.io.OutputStreamWriter类,继承自Writer类
  • OutputStreamWriter是从字符流到字节流的桥梁,可以把字符流转换为字节流
  • 可以使用OutputStreamWriter向特定字符集文件中写入字符数据

构造方法

//自身不具备写操作,需要依赖OutputStream流
OutputStreamWriter(OutputStream out, String charsetName)//可以指定字符集

OutputStreamWriter osw  = new OutputStreamWriter(
    new FileOutputStream("文件"), "编码名称");

常用方法

  • 可以使用Writer类中的方法

**案例:**OutputStreamWriter向GBK编码文件中写入

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

        //创建转换流对象
        OutputStreamWriter osw  = new OutputStreamWriter(
                new FileOutputStream("f:/files/GBK.txt"),"GBk");

        osw.write("程序员\r\n");
        osw.write("哈哈哈\r\n");

        osw.close();//关闭流


        InputStreamReader isr = new InputStreamReader(
                new FileInputStream("f:/files/GBK.txt"),"GBK");

        char[] cbuf= new char[1024];
        int len =isr.read(cbuf);
        isr.close();

        String str = new String(cbuf,0,len);
        System.out.println(str);
    }
}
4、转换文件编码(案例)

案例需求

需求:将GBK编码的文本文件,转换为UTF-8编码的文本文件

案例分析

  • 指定GBK编码的转换流,读取文本文件(读字符集为GBK的文件)

  • 使用UTF-8编码的转换流,写出文本文件(以UTF-8字符集为主,向文件中写入)
    在这里插入图片描述

案例代码

public class Example1 {
    public static void main(String[] args) {
        try {
            method1();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void method1() throws IOException {
        //转换流:读GBK文件
        InputStreamReader isr = new InputStreamReader(
                new FileInputStream("f:/files/GBK.txt"), "GBK");
        //转换流:写UTF-8数据  默认字符集:UTF-8
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("f:/files/utf-8.txt"));

        //读、写
        char[] cbuf = new char[1024];
        int len = 0;
        while ((len = isr.read(cbuf)) != -1) {

            osw.write(cbuf, 0, len);
        }

        //关闭
        isr.close();
        osw.close();
    }
}

三、序列化

序列化图解

在这里插入图片描述

要使用序列化功能,需要借助类:ObjectOutputStream流

要实现反序列化功能,需要借助类:ObjectInputStream流

1、序列化ObjectOutputStream

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息(持久化存储)

序列化,可以将内存中的对象信息,转换为字节序列,将字节序列写到文件中(发送到网络)

java.io.ObjectOutputStream类就是用来进行对象的序列化操作

对象序列化的条件

  1. 对象的类型必须要实现接口:Serializable,否则会报NotSerializableException
  2. 该类型的所有属性都要支持序列化。如果某个属性是不需要序列化的,可以使用transient关键字进行修饰

构造方法

public ObjectOutputStream(OutputStream out)//创建一个指定OutputStream的序列化对象

//注意:序列化流ObjectOutputStream类本身不具备直接向文件写操作,需要借用一个字节输出流来实现写操作   

常用方法

public void writeXxx(Xxx  v)  //可以将指定的Xxx基本数据类型序列化   
                              //示例:writeDouble(double val)

public void writeObject(Object obj)  //将对象进行序列化

案例代码

public class Test01 {
    public static void main(String[] args) throws IOException {
        //实例化Student
        Student stu = new Student("景甜",18,"北京");

        //创建一个序列化对象流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.txt"));
        //进行序列化操作
        oos.writeObject(stu);//把学生对象写入到student.txt文件中
        //关闭流
        oos.close();
    }
}


//对象要进行序列化操作时,必须保证对象实现:序列化接口
public class Student implements Serializable {
    //属性要保证可以实现序列化
    private String name;
    private int age;
    //如果某个属性不需要进行序列化操作时,可以使用关键字transient修饰
    transient String address;	
    ..........
    ..........
    ..........
        
}

2、反序列化ObjectInputStream

从保存对象信息的文件中读取字节序列、重构对象,就是反序列化。对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。

反序列化,可以将字节序列重构为内存中的对象

java.io.ObjectInputStream是反序列化流,可以将之前使用ObjectOutputStream序列化的原始数据恢复为对象

构造方法

public ObjectInputStream(InputStream in)  //创建一个指定InputStream的ObjectInputStream

//注意:反序列化流ObjectInputStream类本身不具备直接向文件进行读操作,需要借用一个字节输入流来实现对文件的读操作    

常用方法

public Xxx readXxx() //读取基本数据类型的方法

public Object readObject() //从ObjectInputStream读取对象 把序列化对象读取到内存中 

案例代码

将上一个案例中序列化的对象,通过反序列化重构为对象放到到内存中

public static void main(String[] args) throws IOException, ClassNotFoundException {

        //创建反序列流对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Student.txt"));
        
        //把存储在文件中的对象信息,读取到内存中
        Object obj = ois.readObject();

        //关闭
        ois.close();

        //把Object转换为Student类型
        Student student = (Student)obj;

        System.out.println(student.getName());
        System.out.println(student.getAge());
        System.out.println(student.getAddress());
    }
3、【序列化】【反序列化】注意事项

在进行序列化操作时,需要注意的事项:

  • 对象所属类型必须实现Serializable接口

在进行反序列化操作时,需要注意的事项:

  • 保证对象的.class文件存在
  • 保证序列化中的serialVersionUID和本地类中的serialVersionUID保持一致(避免异常)

解决方案:

  1. 保证类是存在的

  2. 在序列化的类型中,定义一个静态常量表示序列版本号

如果某个对象需要序列化,在定义类的时候就应该加上一个 long类型的serialVersionUID静态常量

Serializable 接口给需要序列化的类,提供了一个序列版本号serialVersionUID, 该版本号的目的在于验证序列化的对象和对应类是否版本匹配

Serializable接口,称为:空接口(标记接口),标记接口就是为了给某个对象添加一个标记

public static final long serialVersionUID = 1L;
//当添加了固定的版本号后:
//在进行序列化操作时,会把版本号存储到文件中
//在进行反序列化操作时,从文件中读取版本号:拿读取到的版本号(流中的版本号),和当前类已知的版本号(本地的版本号)进行匹配(是否相同): 不相同就会报错

//每次对本地类中的代码进行修改后,本地的版本号,都会更新(新的版本号)

Exception

  1. 如果反序列化中找不到对应的类型:ClassNotFoundException
    • 对于JVM可以反序列化对象,它必须能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常
  2. 如果序列化对象后,类型发生改变,再次反序列化:InvalidClassException
    • 当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常
      • 发生这个异常的原因如下:
        • 该类的序列版本号与从流中读取的类描述的版本号不匹配
        • 该类包含未知数据类型

小结

序列化操作的作用:

  • 把内存中的对象,存储到硬盘上的文件中(实现了对象的持久化存储)
  • 可以实现网络之间对象的数据传输

序列化的注意事项:

  • 保证要进行序列化的对象有实现Serializable接口

反序列化操作的作用:

  • 把已序列化的对象,读取后重构为一个新的对象(从文件中读取,在内存中创建新对象)

反序列化的注意事项:

  • 保证序列化对象的.class文件存在
  • 在对象中添加一个序列化版本号,避免修改代码后流中的版本号和本地类的版本不一致,而导致报错

四、打印流PrintStream

平时在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。

构造方法

public PrintStream(String filePath)  //构建一个打印流对象,传入接收数据的文件路径

常用方法

public void println(数据)   //打印后换行
public void print(数据)     //打印不换行

**案例:**用打印流向文件写入内容

public class PrintStreamDemo1 {
    public static void main(String[] args) throws FileNotFoundException {
        //创建打印流
        PrintStream ps = new PrintStream("print.txt");

        //写入
        ps.print(100);//不写入换行符
        ps.println(200);//写入换行符
        ps.println("你好");
        ps.println(false);

        //关闭
        ps.close();
    }
}

案例:改变System.out.print()输出的流向

System.out就是PrintStream类型的,只不过它的流向是系统规定的,打印在控制台上。不过,既然是流对象,我们就可以玩一个"小把戏",将数据输出到指定文本文件中。

public class PrintDemo {
    public static void main(String[] args) throws IOException {
		// 调用系统的打印流,控制台直接输出97
        System.out.println(97);
      
		// 创建打印流,指定文件的名称
        PrintStream ps = new PrintStream("ps.txt");
      	
      	// 设置系统的打印流流向,输出到ps.txt
        System.setOut(ps);
      	// 调用系统的打印流,ps.txt中输出97
        System.out.println(97);
    }
}

五、设计模式:装饰者模式

**设计模式:**是一种解决问题的方案,就是前人们通过不断积累,总结出来的一套用来解决某种问题的方案

**装饰模式:**是在不改变原类,不使用继承的基础上,动态地扩展一个对象的功能,用来增强某个类中的功能(不破坏原有类中的代码,没有继承)

原则:

  1. 装饰类和被装饰类需要有共同的父类型
  2. 装饰类要传入被装饰类的对象
  • 在装饰类构造方法中,把被装饰的类对象作为参数传入
  • 被装饰的类作为装饰类中的一个成员变量存在
  1. 在装饰类中把要增强扩展的功能进行扩展
  2. 对于不要增强的功能直接调用

案例代码

已知有接口Star和其子类型LiuDeHua

public interface Star {
    public void sing();
    public void dance();
}
public class LiuDeHua implements Star {
    @Override
    public void sing() {
        System.out.println("刘德华在唱忘情水...");
    }
    @Override
    public void dance() {
        System.out.println("刘德华在跳街舞...");
    }
}

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

思路:

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

步骤:

  1. 定义一个装饰类实现Star
  2. 在装饰类中定义一个成员变量,类型是LiuDeHua
  3. 对sing方法进行功能扩展
  4. 对dance不做改动
  5. 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象。
/*
	装饰模式遵循原则:
		装饰类和被装饰类必须实现相同的接口
		在装饰类中必须传入被装饰类的引用
		在装饰类中对需要扩展的方法进行扩展
		在装饰类中对不需要扩展的方法调用被装饰类中的同名方法
*/
//LiuDeHua类: 被装饰类
//LiuDeHuaWarpper类:称之为装饰类
public class LiuDeHuaWarpper implements Star {
    // 存放被装饰类的引用
    private LiuDeHua liuDeHua;
    // 通过构造器传入被装饰类对象
    public LiuDeHuaWarpper(LiuDeHua liuDeHua){
        this.liuDeHua = liuDeHua;
    }
    @Override
    public void sing() {
        // 对需要扩展的方法进行扩展增强
        System.out.println("刘德华在鸟巢的舞台上演唱忘情水.");
    }
    @Override
    public void dance() {
        // 不需要增强的方法调用被装饰类中的同名方法
        liuDeHua.dance();
    }
}

//测试类
public static void main(String[] args) {
    // 创建被装饰类对象
    LiuDeHua liuDeHua = new LiuDeHua();
    // 创建装饰类对象,被传入被装饰类
    LiuDeHuaWarpper liuDeHuaWarpper = new LiuDeHuaWarpper(liuDeHua);
    // 调用装饰类的相关方法,完成方法扩展
    liuDeHuaWarpper.sing();
    liuDeHuaWarpper.dance();
}

小结

装饰者设计模式:

  • 增加原有类中的功能

装饰者设计模式的好处:

  • 没有破坏原有类中的代码
  • 不需要继承

装饰者模式的实现:

  • 被装饰者类和装饰者类有共同的父类型(通常父类型是一个接口)
  • 被装饰者类 ,作为装饰者类中的成员变量存在
  • 在实现化装饰者类时,把被装饰者类对象作为参数传入

六、commons-io工具包

commons-io介绍

commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以挺提高IO功能开发的效率。commons-io工具包提供了很多有关io操作的类,见下表:

功能描述
org.apache.commons.io有关Streams、Readers、Writers、Files的工具类
org.apache.commons.io.input输入流相关的实现类,包含Reader和InputStream
org.apache.commons.io.output输出流相关的实现类,包含Writer和OutputStream
org.apache.commons.io.serialization序列化相关的类

commons-io使用步骤

  1. 下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/
  2. 把commons-io-2.6.jar包复制到指定的Module的lib目录中
  3. 将commons-io-2.6.jar加入到classpath中

commons-io常用API

org.apache.commons.io.IOUtils 工具类,封装了大量IO读写操作的代码

其中有两个常用方法:

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

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位置

结论:IOUtils工具类,通常用来复制文件

FileUtils工具类,通常用来复制文件夹

案例代码

public class Demo01 {
    public static void main(String[] args) throws IOException {
        //拷贝文件 IOUtils
        IOUtils.copy(new FileInputStream("day13/video1/io1.mp4"), new FileOutputStream("day13/video2/io1_copy.mp4"));

        //文件夹拷贝 FileUtils
        FileUtils.copyDirectoryToDirectory(new File("day13/video1"),new File("day13/video2"));

    }
}

小结

commons-io包提供了大量针对IO资源操作的功能:

  • 复制文件(2G以下)
  • 复制大文件(2G以上)
  • 复制整个目录

使用commons-io的步骤:

  1. 从官网下载commons-io的jar文件

  2. 在idea的项目工程下创建一个名为lib的文件夹

  3. 把commons-io的jar文件,复制到lib文件夹下

  4. 把jar文件添加到classpath下

  5. 使用commons中提供的工具类

  • IOUtils:用来复制文件
  • FileUtils:用来复制文件夹
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程小栈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值