文章目录
回顾上次课内容
-
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次数,从而提高读写的效率
缓冲流,自身是不具备直接对文件进行读写操作的功能,需要依赖于字节流或字符流。
- 字节缓冲流:
BufferedInputStream
,BufferedOutputStream
- 字符缓冲流:
BufferedReader
,BufferedWriter
缓冲流的分类:
- 字节缓冲流
- 字节输入缓冲流
- 字节输出缓冲流
- 字符缓冲流
- 字符输入缓冲流
- 字符输出缓冲流
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)
:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
什么是编码表?
-
把生活中的数据和计算机中的数据存储在一张表中(对应关系)
-
编码和解码的转换是借用了编码值实现的
-
生活中的数据 编码值 计算机中的数据 A 65 00000000 00000000 00000000 01000001 中 200003 10101011 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编码。它使用一至四个字节为每个字符编码,编码规则:
- 128个US-ASCII字符,只需一个字节编码。
- 拉丁文等字符,需要二个字节编码。
- 大部分常用字(含中文),使用三个字节编码。
- 其他极少使用的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
类就是用来进行对象的序列化操作
对象序列化的条件
- 对象的类型必须要实现接口:Serializable,否则会报NotSerializableException
- 该类型的所有属性都要支持序列化。如果某个属性是不需要序列化的,可以使用
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保持一致(避免异常)
解决方案:
保证类是存在的
在序列化的类型中,定义一个静态常量表示序列版本号
如果某个对象需要序列化,在定义类的时候就应该加上一个 long类型的serialVersionUID静态常量
Serializable
接口给需要序列化的类,提供了一个序列版本号serialVersionUID
, 该版本号的目的在于验证序列化的对象和对应类是否版本匹配Serializable接口,称为:空接口(标记接口),标记接口就是为了给某个对象添加一个标记
public static final long serialVersionUID = 1L;
//当添加了固定的版本号后:
//在进行序列化操作时,会把版本号存储到文件中
//在进行反序列化操作时,从文件中读取版本号:拿读取到的版本号(流中的版本号),和当前类已知的版本号(本地的版本号)进行匹配(是否相同): 不相同就会报错//每次对本地类中的代码进行修改后,本地的版本号,都会更新(新的版本号)
Exception
- 如果反序列化中找不到对应的类型:ClassNotFoundException
- 对于JVM可以反序列化对象,它必须能够找到class文件的类。如果找不到该类的class文件,则抛出一个
ClassNotFoundException
异常
- 对于JVM可以反序列化对象,它必须能够找到class文件的类。如果找不到该类的class文件,则抛出一个
- 如果序列化对象后,类型发生改变,再次反序列化:InvalidClassException
- 当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个
InvalidClassException
异常- 发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述的版本号不匹配
- 该类包含未知数据类型
- 码
- 发生这个异常的原因如下:
- 当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个
小结
序列化操作的作用:
- 把内存中的对象,存储到硬盘上的文件中(实现了对象的持久化存储)
- 可以实现网络之间对象的数据传输
序列化的注意事项:
- 保证要进行序列化的对象有实现Serializable接口
反序列化操作的作用:
- 把已序列化的对象,读取后重构为一个新的对象(从文件中读取,在内存中创建新对象)
反序列化的注意事项:
- 保证序列化对象的.class文件存在
- 在对象中添加一个序列化版本号,避免修改代码后流中的版本号和本地类的版本不一致,而导致报错
四、打印流PrintStream
平时在控制台打印输出,是调用
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);
}
}
五、设计模式:装饰者模式
**设计模式:**是一种解决问题的方案,就是前人们通过不断积累,总结出来的一套用来解决某种问题的方案
**装饰模式:**是在不改变原类,不使用继承的基础上,动态地扩展一个对象的功能,用来增强某个类中的功能(不破坏原有类中的代码,没有继承)
原则:
- 装饰类和被装饰类需要有共同的父类型
- 装饰类要传入被装饰类的对象
- 在装饰类构造方法中,把被装饰的类对象作为参数传入
- 被装饰的类作为装饰类中的一个成员变量存在
- 在装饰类中把要增强扩展的功能进行扩展
- 对于不要增强的功能直接调用
案例代码
已知有接口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类
步骤:
- 定义一个装饰类实现Star
- 在装饰类中定义一个成员变量,类型是LiuDeHua
- 对sing方法进行功能扩展
- 对dance不做改动
- 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象。
/*
装饰模式遵循原则:
装饰类和被装饰类必须实现相同的接口
在装饰类中必须传入被装饰类的引用
在装饰类中对需要扩展的方法进行扩展
在装饰类中对不需要扩展的方法调用被装饰类中的同名方法
*/
//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使用步骤
- 下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/
- 把commons-io-2.6.jar包复制到指定的Module的lib目录中
- 将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的步骤:
-
从官网下载commons-io的jar文件
-
在idea的项目工程下创建一个名为lib的文件夹
-
把commons-io的jar文件,复制到lib文件夹下
-
把jar文件添加到classpath下
-
使用commons中提供的工具类
- IOUtils:用来复制文件
- FileUtils:用来复制文件夹