IO流概述
一、概述
Java对数据的处理都是通过流的方式,称为IO(Input-Outpu)
流。IO流用于处理设备上的数据传输,如硬盘上储存的数据,内存中驻留的数据。
二、IO流的区分
1.按流向分
按流向分为:输入流和输出流。
2.按操作数据分
流按操作数据分为两种:字节流与字符流。
小扩展:
字符流的出现是为了方便处理文本字符。英语有英文字符集ASCII码;中文字符集由原来的GB2312,扩展为现在的GBK。之后,国际标准化组织计划将世界上所有国家的文字都进行编排,形成了国际标准码表,UNICODE;优化之后,形成了UTF-8字符编码表。那么,如果一个电脑用GBK编码存储文本,另一个电脑用UTF-8编码读取,就会出现乱码。Java就这个情况,在字节流的基础上,增加了字符流。字符流内部融合了编码表,并可以指定查询的编码表,处理文本字符更加方便,避免乱码。
三、IO流常用基类
1.字符流的抽象基类
- Reader
- Writer
2.字节流的抽象基类
- InputStream
- OutputStream
IO流用于操作数据,数据的最常见体现形式是文件。那么以操作文件为主,从字符流开始,来演示IO流程序。
IO流(一) 字符流
一、Writer类
1.概述Writer
类可以在硬盘上创建一个文件,并写入或添加数据。该类的子类还能实现写入过程中的不同功能。
2.FileWriter
类FileWriter
类专门用于操作文件。该对象中只有构造方法,并且没有空参数的构造方法,因为初始化的时候必须要有文件对象,才能进行写入操作。在示例代码中,将对每个环节做详细注释。
1)文件创建并写入
第一步:创建FileWriter
对象,传入要操作的文件名
class FileWriterDemo {
public static void main(String[] args) throws IOException {
/* 在当前目录创建一个名为demo.txt的文件作为文本写入的目标 */
FileWriter fw = new FileWriter("demo.txt");
}
}
注意:
-创建文件会报出异常,此处先抛出,后期会有专门处理异常的代码;
-如果指定文件夹下有同名文件,该文件将被覆盖,所以要小心创建操作
第二步:写入自定义数据
class FileWriterDemo {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("demo.txt");
/* 将字符串写入缓冲区 */
fw.write("abcde");
/* 将缓冲区中的内容写入文件 */
fw.flush();
}
}
注意:
-writer()方法不是将文本内容直接写入文件,而是先写入流的缓冲区;
-flush()方法将缓冲区中的内容写入到文件;每次调用完writer()方法,都要调用flush()
第三步:关闭流资源
class FileWriterDemo {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("demo.txt");
/* 将字符串写入缓冲区 */
fw.write("abcde");
/* 将缓冲区中的内容写入文件 */
fw.flush();
/* 关闭流资源,关闭之前会刷新缓冲区一次 */
fw.close();
}
}
注意:
-由于每个系统创建文件写入内容的方式不同,Java需要调用系统底层的功能来实现文件的创建和写入;使用完系统资源的之后,一定要调用close()方法,关闭该资源;
-close()方法与flush()方法的区别在于,close()方法刷新缓冲区之后,关闭了该输出流资源,之后不能再写入数据;而flush()之后,可以继续写入数据,流资源依然存在;
第四步:针对处理IO异常
class FileWriterDemo {
public static void main(String[] args) throws IOException {
FileWriter fw = null;
try {
fw = new FileWriter("demo.txt");
fw.write("abcde");
fw.flush();
} catch(IOException e) {
System.out.println(e.toString());
} finally {
try {
if(fw != null)
fw.close();
} catch(IOException e) {
System.out.println(e.toString());
}
}
}
}
注意:
-用try catch捕捉并处理异常;
-关闭资源的代码一定要放在finally中;
-close()方法同样会抛出IO异常,同样需要用try catch捕捉并处理;
-如果创建文件的路径有误(例如电脑上没有k盘,创建时却写fw = new FileWriter("k:\"demo.txt");),那么会报出FileNotFoundException文件无法找到异常,同时会报出NullPointerException空指针异常,因为路径找不到,fw对象没有建立,而finally中还要调用close()方法,会发生空指针异常;所以需要在finally中建立判断;
-无论读、写数据还是关闭资源操作,都有可能发生IO异常
2)文件的续写
在原有数据的基础上,续写自定义数据。
第一步:修改构造方法
class FileWriterDemo {
public static void main(String[] args) throws IOException {
/* 传递一个布尔型参数,true表示不覆盖已有文件
* 从文件内容末尾开始添加新内容;如果文件不存在
* 则创建一个新文件
*/
FileWriter fw = new FileWriter("demo.txt", true);
}
}
第二步:写入自定义数据
class FileWriterDemo {
public static void main(String[] args) throws IOException {
FileWriter fw = null;
try {
fw = new FileWriter("demo.txt", true);
/* windows中判断换行是用\r\n来判断,\n只适用于linux系统 */
fw.write("nihao\r\nxiexie");
fw.flush();
} catch(IOException e) {
System.out.println(e.toString());
} finally {
try {
if(fw != null)
fw.close();
} catch(IOException e) {
System.out.println(e.toString());
}
}
}
}
二、Reader类
1.概述Reader
类用于高效读取字符流。该类的子类可以实现读取过程中的不同功能。
2.FileReader类FileReader
类是用于读取字符文件的便捷类。该类包含默认的字符编码和默认的字节缓冲区大小。
1)read()方法读取文件
第一步:创建FileReader对象
class FileReaderDemo {
public static void main(String[] args) {
try {
/* 创建一个文件读取流对象,并与指定名称的文件相关联 */
FileReader fr = new FileReader("demo.txt");
} catch(IOException e) {
System.out.println(e.toString());
}
}
}
注意:
创建FileReader对象时关联的读取文件必须已经存在,不然会发生FileNotFoundException
第二步:读入单个字符并关闭资源
class FileReaderDemo {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("demo.txt");
/* read()方法一次读一个字符,第二次调用会自动读取下一个字符 */
int ch1 = fr.read();
System.out.print("ch1 = " + (char)ch1);
} catch(IOException e) {
System.out.println(e.toString());
} finally {
try {
if(fr != null)
fr.close();
} catch(IOException e) {
System.out.println(e.toString());
}
}
}
}
注意:
read()方法读到文件末尾没有字符的时候,会返回-1;可以利用这个机制,循环读取所有数据
第三步:一次性读取全部数据
class FileReaderDemo {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("demo.txt");
int ch = 0;
/* 当ch不等于-1的时候,继续读文件,直到文件末尾 */
while((ch = fr.read()) != -1) {
System.out.print("ch = " + (char)ch);
}
} catch(IOException e) {
System.out.println(e.toString());
} finally {
try {
if(fr != null)
fr.close();
} catch(IOException e) {
System.out.println(e.toString());
}
}
}
}
2)read(ch[] ch)方法读取文件
第一步:定义一个字符数组用于储存读到的字符
class FileReaderDemo {
public static void main(String[] args) {
try {
FileReader fr = new FileReader("demo.txt");
/* 创建长度为10的字符数组 */
char[] buff = new char[10];
} catch(IOException e) {
System.out.println(e.toString());
}
}
}
第二步:读取字符并存入字符数组
class FileReaderDemo {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("demo.txt");
char[] buff = new char[10];
/* 读取字符并存入字符数组 */
int num = fr.read(buff);
System.out.print(new String(buff));
} catch(IOException e) {
System.out.println(e.toString());
} finally {
try {
if(fr != null)
fr.close();
} catch(IOException e) {
System.out.println(e.toString());
}
}
}
}
注意:
read(ch[] ch)方法返回的是读取了多少个字符;独到文件末尾,返回-1;可以利用这一机制,循环读取全部字符
第三步:一次性读取全部数据
class FileReaderDemo {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("demo.txt");
char[] buff = new char[1024];
int num = 0;
/* 当返回值不等于-1时,继续读文件 */
while((num = fr.read(buff)) != -1) {
/* 读取了多少个字符,就打印多少个字符 */
System.out.print(new String(buff, 0, num));
}
} catch(IOException e) {
System.out.println(e.toString());
} finally {
try {
if(fr != null)
fr.close();
} catch(IOException e) {
System.out.println(e.toString());
}
}
}
}
注意:
-缓冲区字符数组通常定义1024的整数倍长度;
-read(ch[] ch)方法的效率优于read()方法;由于read()方法读一个字符,写一个字符,效率低下;而raead(ch[] ch)方法读取一段字符到缓冲区,然后一起写入,效率较高
三、字符流练习
1.读取文件并打印
读取一个.java
文件,打印在控制台上。
示例代码:
package com.heisejiuhuche.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderDemo {
public static void main(String[] args) {
// print_1();
print_2();
}
/* 用read(ch[] ch)方法读取所有数据 */
private static void print_1() {
/* 创建FileReader引用 */
FileReader fr = null;
try {
/* 创建FileReader对象,关联文件 */
fr = new FileReader(
"C:\\RuntimeDemo.java");
/* 创建字符串缓冲区 */
char[] ch = new char[1024];
int len = 0;
/* 当read方法返回值不等于-1,继续读文件 */
while ((len = fr.read(ch)) != -1) {
System.out.print(new String(ch, 0, len));
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch (IOException e) {
throw new RuntimeException("操作失败");
} finally {
try {
if (fr != null) {
/* 关闭资源 */
fr.close();
}
} catch (IOException e) {
throw new RuntimeException("输入流关闭失败");
}
}
}
/* 用read()方法读取所有数据 */
private static void print_2() {
/* 创建FileWriter引用,关联文件 */
FileReader fr = null;
try {
/* 创建FileWriter对象,关联文件 */
fr = new FileReader(
"C:\\RuntimeDemo.java");
int ch = 0;
/* 当read方法返回值不等于-1,继续读文件 */
while ((ch = fr.read()) != -1) {
System.out.print((char) ch);
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch (IOException e) {
throw new RuntimeException("操作失败");
} finally {
try {
if (fr != null) {
fr.close();
}
} catch (IOException e) {
throw new RuntimeException("输入流关闭失败");
}
}
}
}
注意:
打印在控制台上不要用println方法,否则会出现不必要的换行
2.复制文件
将指定文件从一个目录复制到另一个目录。
原理:
将一个文件的内容写入到另一个目录下的另一个文件中
示例代码:
package com.heisejiuhuche.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyFileTest {
private static FileReader fr;
private static FileWriter fw;
public static void main(String[] args) {
copy_1();
// copy_2();
}
/* 用read(ch[] ch)方法复制文件 */
private static void copy_1() {
try {
/* 创建目的文件 */
fw = new FileWriter(
"C:/Users/jeremy/Documents/javaTmp/Runtime.java");
/* 关联需要复制的文件 */
fr = new FileReader(
"C:\\RuntimeDemo.java");
/* 创建字符数组缓冲区 */
char[] buff = new char[1024];
/* 循环控制 */
int len = 0;
/* 将需要复制的文件写入缓冲区,并将缓冲区文件写入目的文件 */
while ((len = fr.read(buff)) != -1) {
fw.write(buff, 0, len);
}
fw.flush();
} catch (FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch (IOException e) {
throw new RuntimeException("操作失败");
} finally {
try {
if (fw != null) {
fw.close();
}
if (fr != null) {
fr.close();
}
} catch (IOException e) {
throw new RuntimeException("流资源关闭失败");
}
}
}
/* 用read()方法复制文件 */
private static void copy_2() {
try {
/* 步骤与上述方法相近 */
fw = new FileWriter(
"C:/Users/jeremy/Documents/javaTmp/Runtime.java");
fr = new FileReader(
"C:\\RuntimeDemo.java");
int ch = 0;
while((ch = fr.read()) != -1) {
fw.write(ch);
}
fw.flush();
} catch(FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch(IOException e) {
throw new RuntimeException("操作失败");
} finally {
try {
if(fw != null) {
fw.close();
}
if(fr != null) {
fr.close();
}
} catch(IOException e) {
throw new RuntimeException("流资源关闭失败");
}
}
}
}
四、字符流缓冲区
1.概述
缓冲区的出现,提高了字符流对数据的读写效率。缓冲区可以实现先读出一部分数据,再一起写入,避免了硬盘在读与写之间频繁操作,提高读写速度。缓冲区必须结合流才能使用,在流的基础上对流的功能进行了增强。开发时通常都有缓冲区。
2.缓冲区对应类
缓冲区对应两个类,分别是BufferedWriter
和BufferedReader
。它们提供的方法,可以实现对文本字符数据的高效读写。
3.BufferedWriter类
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。在创建BufferedWriter
类时,必须先有流对象。
1)文件创建并写入
第一步:创建字符流输出流对象
class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
/* 创建一个字符输出流对象 */
FileWriter fw = new FileWriter("Demo.txt");
}
}
第二步:创建缓冲字符输出流对象
class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
/* 创建一个字符输出流对象 */
FileWriter fw = new FileWriter("Demo.txt");
/* 创建缓冲字符输出流对象,并将字符输出流对象作为参数传递给其构造方法 */
BufferedWriter buffWriter = new BufferedWriter(fw);
}
}
第三步:调用方法进行写入
class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
/* 创建一个字符输出流对象 */
FileWriter fw = new FileWriter("Demo.txt");
/* 创建缓冲字符输出流对象,并将字符输出流对象作为参数传递给其构造方法 */
BufferedWriter buffWriter = new BufferedWriter(fw);
for(int x = 1; x < 5; x++) {
buffWriter.write("abcde" + x);
/* 缓冲区提供的新方法,能跨平台换行 */
buffWriter.newLine();
buffWriter.flush();
}
}
}
注意:
只要用到缓冲区,就要调用flush()方法进行刷新
第四步:写入完毕,关闭资源
class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
/* 创建一个字符输出流对象 */
FileWriter fw = new FileWriter("Demo.txt");
/* 创建缓冲字符输出流对象,并将字符输出流对象作为参数传递给其构造方法 */
BufferedWriter buffWriter = new BufferedWriter(fw);
for(int x = 1; x < 5; x++) {
buffWriter.write("abcde" + x);
/* 缓冲区提供的新方法,能跨平台换行 */
buffWriter.newLine();
buffWriter.flush();
}
/* 关闭缓冲区 */
buffWriter.close();
}
}
注意:
正真进行写入操作的是字符输出流FileWriter;所以关闭缓冲区其实就是关闭FileWriter对象
4.BufferedReader类
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。在创建BufferedReader
类时,必须先有流对象。该缓冲区提供了readLine()
方法,使获取文本数据更高效。
1)文件读取
第一步:创建字符输入流对象
class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
/* 创建一个字符输入流对象并关联目标文件 */
FileReader fr = new FileReader("Demo.txt");
}
}
第二步:创建缓冲字符输入流对象
class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
/* 创建一个字符输入流对象并关联目标文件 */
FileReader fr = new FileReader("Demo.txt");
/* 创建缓冲字符输入流对象,并将字符输入流对象作为参数传递给其构造方法 */
BufferedReader buffReader = new BufferedReader(fr);
}
}
第三步:调用方法读取数据
class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
/* 创建一个字符输入流对象并关联目标文件 */
FileReader fr = new FileReader("Demo.txt");
/* 创建缓冲字符输入流对象,并将字符输入流对象作为参数传递给其构造方法 */
BufferedReader buffReader = new BufferedReader(fr);
/* 调用readLine()方法读取数据,如果独到最后一行,该方法返回null */
String line = null;
while((line = buffReader.readLine()) != null) {
System.out.println(line);
}
}
}
第四步:关闭缓冲区
class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
/* 创建一个字符输入流对象并关联目标文件 */
FileReader fr = new FileReader("Demo.txt");
/* 创建缓冲字符输入流对象,并将字符输入流对象作为参数传递给其构造方法 */
BufferedReader buffReader = new BufferedReader(fr);
/* 调用readLine()方法读取数据,如果独到最后一行,该方法返回null */
String line = null;
while((line = buffReader.readLine()) != null) {
System.out.println(line);
}
/* 关闭缓冲区 */
buffReader.close();
}
}
5.缓冲区练习
1)通过缓冲区复制一个.java
文件
示例代码:
package com.heisejiuhuche.io;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CopyByBuffTest {
private static BufferedWriter buffw;
private static BufferedReader buffr;
public static void main(String[] args) {
copy();
}
private static void copy() {
try {
/* 分别创建缓冲区对象,并关联文件 */
buffw = new BufferedWriter(new FileWriter(
"C:/Users/jeremy/Documents/javaTmp/SystemDemo.java"));
buffr = new BufferedReader(new FileReader(
"C:\\Users\\jeremy\\java\\workspaces\\test\\src\\com\\heisejiuhuche\\api\\SystemDemo.java"));
/* 循环控制,记录读取的每一行 */
String line = null;
/* 读取数据,直到最后一行 */
while((line = buffr.readLine()) != null) {
buffw.write(line);
buffw.newLine();
buffw.flush();
}
} catch(FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch(IOException e) {
throw new RuntimeException("操作失败");
} finally {
/* 关闭缓冲区 */
try {
if(buffw != null) {
buffw.close();
}
if(buffr != null) {
buffr.close();
}
} catch(IOException e) {
throw new RuntimeException("流资源关闭失败");
}
}
}
}
注意:
使用readLine()方法读取时,不会读取每一行的回车符;所以在写入时,每写一行要调用newLine()方法换行
6.readLine()方法
1)原理
readLine()
方法的实现是基于read()
方法的。如图,readLine()
方法在内存中创建了一个字符数组作为缓冲,每次读取操作都调用read()
方法,读取一个字符。步骤1
,2
,3
,4
,5
读取了五次,并分别将这五个字符存入字符数组。当读到换行附的时候,readLine()
方法不会将换行字符存入数组,而将字符数组中的字符组成字符串返回,再由输出流调用写入方法,在第6
步写入目标文件。
2)自定义readLine()方法
模拟一个BufferedReader
类,要求有readLine()
功能的方法和close()
功能的方法。
示例代码:
package com.heisejiuhuche.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class MyBufferedReaderTest {
/* 在main方法中读取一个文件,并在控制台打印,测试MyBufferedReader类 */
public static void main(String[] args) {
MyBufferedReader buffr = null;
try {
FileReader fr = new FileReader("Demo.txt");
buffr = new MyBufferedReader(fr);
String line = null;
while ((line = buffr.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch (IOException e) {
throw new RuntimeException("操作失败");
} finally {
try {
if(buffr != null)
buffr.close();
} catch(IOException e) {
throw new RuntimeException("资源关闭失败");
}
}
}
}
class MyBufferedReader {
/* 持有FileReader的引用,因为readLine()方法基于read()方法 */
private FileReader fr;
/* 初始化时需要传入字符流对象 */
MyBufferedReader(FileReader fr) {
this.fr = fr;
}
/* 创建readLine()方法,标识异常 */
public String readLine() throws IOException {
/* 用StringBuilder代替数组,完成字符串拼接 */
StringBuilder sb = new StringBuilder();
int ch = 0;
/* 调用read()方法,读取数据 */
while ((ch = fr.read()) != -1) {
/* 如果读到'\r',跳过,继续读下一个字符 */
if (ch == '\r')
continue;
/*如果读到'\n',标识一行结束,返回这行的字符串 */
if (ch == '\n')
return sb.toString();
else
/* 如果是字符,就加入StringBuilder */
sb.append((char)ch);
}
/* 这行代码排除了行末没有'\n'而整行字符串不会被打印的情况 */
if (sb.length() != 0) {
return sb.toString();
}
/* 如果读到文件最后,返回null */
return null;
}
/* 创建关闭缓冲区方法,标识异常 */
public void close() throws IOException {
fr.close();
}
}
程序输出结果:
abcd1
abcd2
abcd3
7.LineNumberReader类LineNumberReader
类是可以跟踪行号的缓冲字符输入流。LineNumberReader
是BufferedReader
的子类,其使用也必须结合流。该类的用法和BufferedReader
基本一致。
1)输出带行号文本
示例代码:
package com.heisejiuhuche.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
public class LineNumberReaderDemo {
public static void main(String[] args) {
LineNumberReader lnr = null;
try {
lnr = new LineNumberReader(new FileReader("SystemDemo.java"));
String line = null;
while((line = lnr.readLine()) != null) {
/* 调用getLineNumber()方法现实行号 */
System.out.println(lnr.getLineNumber() + ":" + line);
}
} catch(FileNotFoundException e) {
throw new RuntimeException("文件不存在");
} catch(IOException e) {
throw new RuntimeException("操作失败");
} finally {
try {
if(lnr != null)
lnr.close();
} catch(IOException e) {
throw new RuntimeException("资源关闭失败");
}
}
}
}
程序输出部分结果:
6:public class SystemDemo {
7: public static void main(String[] args) {
8: Properties prop = System.getProperties();
五、装饰设计模式
1.定义
装饰设计模式是当要对已有对象进行功能增强时,可以定义一个新类,将已有对象传入;基于已有对象的功能提供加强功能;那么自定义的这个类就叫做装饰类。如第四部分自定义readLine()
方法练习中,自定义的MyBufferedReader
就是一个装饰类。
2.装饰和继承的区别
装饰类增强了原有类的功能,问题是为什么不直接继承原有类,并复写方法,在方法里增强该方法的功能?以一个例子说明装饰和继承的区别。
现在有一个继承体系,MyReader
类是基类,用于读取数据;该基类有数个子类,MyTextReader
,MyMediaReader
,MyDataReader
,分别用于读取文本、多媒体和数据库数据,已有继承体系结构如下:
- MyReader
- MyTextReader
- MyMediaReader
- MyDataReader
现发现三个子类的功能不够强大,要要加缓冲技术;按照继承的思想,那么就在三个子类下面再各自创建三个具备缓冲功能的子类,继承结构变为:
- MyReader
- MyTextReader
- MyBufferedTextReader
- MyMediaReader
- MyBufferedMediaReader
- MyDataReader
- MyBufferedDataReader
- MyTextReader
这样做看似没有问题,但是缺点在于拥有缓冲功能的代码没有复用性,并导致继承体系结构庞大臃肿。因此,做好的做法不是创建子类,而是保持原有继承体系不变,将具备缓冲功能的代码提取出来,将需要增强的对象传递进来即可:
class MyBufferedReader {
MyBufferedReader(MyTextReader text) {}
MyBufferedReader(MyMediaReader media) {}
MyBufferedReader(MyDataReader data) {}
}
这样的做法已经简化了继承体系,同时增强了功能;但是后期MyReader
基类下继续添加新的子类时,这个MyBufferedReader
类还需修改;因此,应该使用多态的特性,在MyBufferedReader
中只需接收MyReader
类的对象即可;由于MyBufferedReader
增强的是MyReader
子类的功能,这些功能都是趋同的,因此最好让MyBufferedReader
作为MyReader
的子类,加入继承体系:
class MyBufferedReader extends MyReader{
MyBufferedReader(MyReader r) {}
}
新的继承结构如下:
- MyReader
- MyTextReader
- MyMediaReader
- MyDataReader
- MyBufferedReader
装饰设计模式比继承灵活,避免了继承体系的复杂臃肿。
3.装饰设计模式特点
装饰设计模式灵活性强,在扩展原有类功能的前提下,不失原有类的方法,即后期开发中,扩展功能和原有功能都可以视情况使用。