Java IO—缓冲字符流以及IO中的装饰者模式
Posted 刘Java
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java IO—缓冲字符流以及IO中的装饰者模式相关的知识,希望对你有一定的参考价值。
详细介绍了Java IO中的缓冲字符流BufferedWriter、BufferedReader、LineNumberReader的使用方式,以及Java IO中的装饰者模式。
文章目录
1 BufferedWriter缓冲区字符输出流
public class BufferedWriter
extends Writer
特点:
- 自带缓冲区。缓冲区可以实现自动扩容,提高了写的效率。
- 特有的newLine(); 方法可以写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行 (’\\n’) 符,可以实现跨平台。
1.1 构造器
public BufferedWriter(Writer out);
创建一个使用默认大小输出缓冲区的缓冲字符输出流。Writer类,是一个抽象类,应该传递的是该抽象类的实现类对象。
public BufferedWriter(Writer out,int sz)
创建一个使用给定大小输出缓冲区的新缓冲字符输出流。在大多数情况下,默认值就足够大了,因此该构造器一般不用。sz:输出缓冲区的大小,是一个正整数
为什么要传递一个流对象而不传递file或者路径呢?
因为缓冲区流仅仅多提供一个缓冲区的功能,是为了高效而设计的。真正的读写还是靠基本的流对象的方法实现!(即装饰设计模式)。并且关闭外层流就相当于关闭了内层的流!
1.2 API方法
大多数方法均继承或重写自直接父类Writer。
特有的方法:
public void newLine();
写入一个行分隔符,即提供了一个换行的方法。
2 BufferedReader缓冲区字符输入流
public class BufferedReader
extends Reader
特点:
- 自带缓冲区。可以实现自动扩容,提高了读的效率。
- 提供了readLine方法,一次可以读取一行数据。
2.1 构造器
public BufferedReader(Reader in)
创建一个使用默认大小输入缓冲区的缓冲字符输入流。传递一个实现Reade实现类。
2.2 API方法
大部分方法都继承和重写自父类Reader。
特有的方法:
public String readLine()
能够一个读取一行数据,提高了读的效率。返回包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回null。
2.2.1 readLine方法实现原理
BufferedReader构造的时候,包装了一个类:FileReader。
其底层是调用了FileReader#read()方法,一次读取一个字符,将读取的字符放在缓冲区,当读取到换行符号的时候,将一行数据返回到内存当中,“\\r’的ASCII码为13,“\\n”的ASCII码为10。
3 案例
3.1 写入文件
/**
* @author lx
*/
public class BufferedWriterDemo01 {
public static void main(String[] args) {
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter("C:\\\\Users\\\\lx\\\\Desktop\\\\test.txt"));
bw.write(97);
bw.newLine();
bw.write("nishuo");
bw.newLine();
bw.write("你说啥你", 1, 2);
bw.newLine();
char[] ch = new char[]{'1', '-', '\\t', ' '};
bw.write(ch);
bw.write(ch, 1, 2);
bw.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.2 读取文件
/**
* @author lx
*/
public class BufferedReaderDemo01 {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("C:\\\\Users\\\\lx\\\\Desktop\\\\test.txt"));
String str;
//一次读取一行
while ((str = br.readLine()) != null) {
System.out.println(str);
}
br.close();
}
}
3.3 拷贝文件
使用带有缓冲区的流,实现文件的copy。
/**
* @author lx
*/
public class CopyFile {
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader("C:\\\\Users\\\\lx\\\\Desktop\\\\test.txt"));
bw = new BufferedWriter(new FileWriter("C:\\\\Users\\\\lx\\\\Desktop\\\\test2.txt"));
String str;
//读取 写入
while ((str = br.readLine()) != null) {
bw.write(str);
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4 LinenumberReader跟踪行号缓冲字符输入流
public class LineNumberReader
extends BufferedReader
这是Reader的又一个包装类,其特点是:
- 是BufferedReader子类,具备缓冲区,具有有一次读取一行的方法。
- 具有获得行号和设置行号的方法
- setLineNumber(int num); 设置行号
- getLineNumber(); 获得当前行的行号,返回的是int 类型的值
注意:循环之前设置行号后,循环后获得第一行字符串行号是设置的行号+1。
构造器:
LineNumberReader(Reader in);
LineNumberReader(Reader in, int sz);
5 装饰设计模式
作用:对某个类的某个方法进行功能性的增强。
装饰模式的实现步骤:
- 定义一个类:装饰类。实现被装饰类的最上层的接口或类,让装饰类和被装饰类有共同的行为,即同一体系。
- 引入一个被装饰类,作为全局变量。这里选择reader抽象类,此后reader的子类都能使用。
- 将被装饰的类作为参数传递给装饰类的构造器,用于初始化变量。
- 对该被装饰类的某个方法进行功能性的增强。
5.1 Java IO中的装饰模式
BufferedReader类对Reader类的read()方法进行包装,它是基于read方法,并对read方法进行了功能的增强,形成readLine()方法。实际上整个IO流系统用的最多的就是装饰设计模式。
LineNumberReader类对BufferedReader类的readLine()方法进行包装,让其在调用时能够对行号计数,添加了获得行号和设置行号的方法。
5.2 装饰设计模式和继承的异同点
相同点:装饰设计模式和继承都是为了对某个类的行为或者属性的扩展(增强)。
不同点:
当我有2个功能差不多 但是操作数据对象不一样的功能的子类 想要扩展一样的功能的时候,继承需要给每个子类进行扩展,并且需要继承每一个方法,而装饰设计模式,只要写一份,把父类传进来就扩展了制定的方法。
这样如果为了某个功能的扩展而使用继承那么可能产生很多子类,那系统体系是非常臃肿的,并且继承的类之间耦合度高,不利于扩展。
装饰模式使用对象的依赖关系代替继承关系,允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。因此装饰设计模式更加灵活,同时避免类型体系的快速膨胀降低了类与类之间的关系(即所谓的耦合性,继承的一个缺点就是耦合性太强)。
装饰模式符合程序设计开闭原则就是说对扩展开放,对修改关闭。在对某个类需要进行拓展的时候,最好不要修改原有的代码,而是要扩展原有代码,使用装饰设计模式就可以做到,使程序的扩展性好,易于维护和升级。
建议:通常使用装饰设计模式,少用继承。
5.3 案例
5.3.1 自定义读取一行的方法
自定义MyBufferedReader类:对FileReader类进行了包装并对FileReader 的read()方法进行功能性的增强,完成readLine方法的效果。
/**
* @author lx
*/
public class MyBufferedReader extends Reader {
private Reader reader;
public MyBufferedReader(Reader reader) {
this.reader = reader;
}
public String MyReaderLine() throws IOException {
int read;
StringBuilder sb = new StringBuilder();
while ((read = reader.read()) != -1) {
//判断是否等于13,13是\\r的Unicode码,即回车符,回车符不加
if (read == 13) {
continue;
}
//判断是否是换行符,是换行就返回,不加,否则就添加
if (read == 10) {
return sb.toString();
} else {
sb.append((char) read);
}
}
//到这说明读到了末尾,但是末尾可能没有\\n,因此这里需要额外判断
if (sb.length() > 0) {
return sb.toString();
}
//走到这里说明,读到了末尾.并且缓冲数组的内容已经全部返回,读取完毕
return null;
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return reader.read(cbuf, off, len);
}
@Override
public void close() throws IOException {
reader.close();
}
}
5.3.2 自定义设置和获得行号的方法
自定义MyLineNumberReader类,使用装饰设计模式,设计设置和获得行号并且读取一行的方法。
/**
* @author lx
*/
public class MyLineNumberReader extends Reader {
private Reader reader;
private int num;
public MyLineNumberReader(Reader reader) {
this.reader = reader;
}
public void setMyLineNumber(int num) {
this.num = num;
}
public int getLineNumber() {
return num;
}
public String myReadLine() throws IOException {
StringBuilder sb = new StringBuilder();
int read;
while ((read = reader.read()) != -1) {
if (read == '\\r') {
continue;
}
if (read == '\\n') {
num++;
return sb.toString();
} else {
sb.append((char) read);
}
}
if (sb.length() > 0) {
num++;
return sb.toString();
}
return null;
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return reader.read(cbuf, off, len);
}
@Override
public void close() throws IOException {
reader.close();
}
}
如有需要交流,或者文章有误,请直接留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!
以上是关于Java IO—缓冲字符流以及IO中的装饰者模式的主要内容,如果未能解决你的问题,请参考以下文章
Java-IO流之BufferedReader 和BufferedWriter的使用和原理