Java中IO流中的装饰设计模式(BufferReader的原理)

简介:

本文粗略的介绍下JavaIO的整体框架,重在解释BufferReader/BufferWriter的演变过程和原理(对应的设计模式)

一.JavaIO的简介

流按操作数据分为两种:字节流与字符流.

流按流向分为:输入流(),输出流()

字符流由来就是:早期的字节流+编码表,为了更便于操作文字数据。 

记住:只要是操作字符数据,应该优先使用字符流。

字节流的抽象基类:InputStream OutputStream

字符流的抽象基类:Reader , Writer

二.JavaIO中的流对象的继承和字节流,字符流的对应关系.

InputStream字节输入流:

OutputStream:字节输出流:

 

InputStreamOutputStream之间的对应关系:

 

Reader :字符输入流

Writer :字符输出流

 

ReaderWriter之间的对应关系

 

输入字节流、输入字符流之间对应关系

 

输出字节流、输出字符流之间对应关系

 

转换流 :InputStreamReader,OutputStreamWriter

转换流的由来:字符流与字节流之间的桥梁 ,方便了字符流与字节流之间的操作

转换流的应用:字节流中的数据都是字符时,转成字符流操作更高效。

标准输入输出流 :

System类中的字段:inout它们各代表了系统标准的输入和输出设备,默认输入设备是键盘,输出设备是显示器。

System.in的类型是InputStream. 

System.out的类型是PrintStreamOutputStream的子类FilterOutputStream 的子类.

举例引入:从原始IO----->用字符数组作为缓冲区---->用IO中的BufferReader/BufferWriter----->JavaIO中的设计模式(装饰设计模式)

①使用最原始的方式拷贝方式代码:

复制代码
 1 /*
 2  * 需求:作业:将c盘的一个文本文件复制到d盘。
 3  * 
 4  * 思路:
 5  * 1,需要读取源,
 6  * 2,将读到的源数据写入到目的地。
 7  * 3,既然是操作文本数据,使用字符流。 
 8  * 
 9  */
10 public class CopyTextTest {
11     public static void main(String[] args) throws IOException {
12         //1,读取一个已有的文本文件,使用字符读取流和文件相关联。
13         FileReader fr = new FileReader("IO流_2.txt");
14         //2,创建一个目的,用于存储读到数据。
15         FileWriter fw = new FileWriter("copytext_1.txt");
16         //3,频繁的读写操作。
17         int ch = 0;
18         while((ch=fr.read())!=-1){
19             fw.write(ch);
20         }
21         //4,关闭流资源。 
22         fw.close();
23         fr.close();
24     }
25 }
复制代码

 

②引入字符数组作为缓冲区:(循环次数小,效率高)

复制代码
 1 public class CopyTextTest_2 {
 2     private static final int BUFFER_SIZE = 1024;
 3     public static void main(String[] args) {
 4         FileReader fr = null;
 5         FileWriter fw = null;
 6         try {
 7             fr = new FileReader("IO流_2.txt");
 8             fw = new FileWriter("copytest_2.txt");
 9             //创建一个临时容器,用于缓存读取到的字符。
10             char[] buf = new char[BUFFER_SIZE];//这就是缓冲区。 
11             //定义一个变量记录读取到的字符数,(其实就是往数组里装的字符个数)
12             int len = 0;
13             while((len=fr.read(buf))!=-1){
14                 fw.write(buf, 0, len);
15             }
16         } catch (Exception e) {
17 //            System.out.println("读写失败");
18             throw new RuntimeException("读写失败");
19         }finally{
20             if(fw!=null)
21                 try {
22                     fw.close();
23                 } catch (IOException e) {
24                     
25                     e.printStackTrace();
26                 }
27             if(fr!=null)
28                 try {
29                     fr.close();
30                 } catch (IOException e) {
31                     
32                     e.printStackTrace();
33                 }
34         }
35     }
36 }
复制代码

 

原理图:

缓冲区的出现提高了文件的读写效率,有缓冲区可以提高效率,在Java中把缓冲区进行了封装,关闭缓冲区就是关闭的被缓冲的流对象!所以只需要关闭缓冲区就可以,不必要再关闭流了。

③引入BufferWriter(缓冲区的出现提高了对数据的读写效率,缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强)

复制代码
 1 public class CopyTextByBufTest {
 2     public static void main(String[] args) throws IOException {
 3         FileReader fr = new FileReader("buf.txt");        
 4         BufferedReader bufr = new BufferedReader(fr);
 5         
 6         FileWriter fw = new FileWriter("buf_copy.txt");
 7         BufferedWriter bufw = new BufferedWriter(fw);
 8         
 9         String line = null;
10         while((line=bufr.readLine())!=null){
11             bufw.write(line);
12             bufw.newLine();
13             bufw.flush();
14         }
15         /*
16         int ch = 0;
17         while((ch=bufr.read())!=-1){
18             bufw.write(ch);
19         }
20         */
21         bufw.close();
22         bufr.close();
23     }
24 }
复制代码

字符流缓冲区:

BufferedWriternewLine();

BufferedReader: readLine();

Buffer***的原理图

 

④装饰设计模式

装饰设计模式的简易代码:

复制代码
 1 public class PersonDemo {
 2     public static void main(String[] args){
 3         Person p = new Person();
 4         p.chifan();
 5         NewPerson p1 = new NewPerson(p);
 6         p1.chifan();
 7         NewPerson2 p2 = new NewPerson2();
 8         p2.chifan();
 9     }
10 }
11 class Person{
12     void chifan(){
13         System.out.println("吃饭");
14     }
15 }
16 //这个类的出现是为了增强Person而出现的。
17 class NewPerson{
18     private Person p ;
19     NewPerson(Person p){
20         this.p = p;
21     }
22     public void chifan(){
23         System.out.println("开胃酒");
24         p.chifan();
25         System.out.println("甜点");
26     }
27 }
28 class NewPerson2 extends Person{
29     public void chifan(){
30         System.out.println("开胃酒");
31         super.chifan();
32         System.out.println("甜点");
33     }
34 }
复制代码

NewPerson是对Person采用了装饰设计模式对Person对象的功能,NewPerson2是继承了Person,对对象的功能进行增强。

装饰和继承都能实现一样的特点:进行功能的扩展增强,但是他们之前是有区别的,装饰更加灵活。

程序输出:

复制代码
吃饭
开胃酒
吃饭
甜点
开胃酒
吃饭
甜点
复制代码

 对以上的代码的具体分析贴图:

装饰和继承都能实现一样的特点:对类对象的功能的扩展增强,区别有哪些?

假设首先有一个继承体系如下:(TextWriter,MediaWriter并不存在)

Writer

|--TextWriter:用于操作文本

|--MediaWriter:用于操作媒体。

 

想要对操作的动作进行效率的提高。按照面向对象,可以通过继承对具体的进行功能的扩展,效率提高需要加入缓冲技术,上面的体系结构变成如下:

复制代码
Writer

|--TextWriter:用于操作文本

  |--BufferTextWriter:加入了缓冲技术的操作文本的对象。

|--MediaWriter:用于操作媒体。

  |--BufferMediaWriter:
复制代码

 

到这里就可以了,能达到对功能增强的目标,但是这样做好像并不理想。

如果这个体系进行功能扩展,又多了一些其他的流对象(****Writer,****Reader。。。。)

那么这个流要提高效率,是不是也要产生子类呢?

答案是:是。这时就会发现只为提高功能,进行的继承,导致继承体系越来越臃肿,不够灵活。 

 

重新思考这个问题?

既然加入的都是同一种技术--缓冲。

前一种是让缓冲和具体的对象相结合。 

可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联

通过上面的代码的分析,可以使用装饰设计模式的思想:

复制代码
class Buffer{
    Buffer(TextWriter w)
    {
}
    
    Buffer(MediaWirter w)
    {
    }
}
复制代码

这样Buffer仅仅对传入的TextWriterMediaWriter进行操作,下面传入一个Writer,就对Writer中的所有子类进行了操作。

复制代码
//缓冲对象进行的也是写的操作,所以要继承Writer
class BufferWriter extends Writer{
    BufferWriter(Writer w)
    {
    }
}
复制代码

  

这样Writer的体系结构变成如下:

Writer
    |--TextWriter:用于操作文本
    |--MediaWriter:用于操作媒体。
    |--BufferWriter:用于提高效率。

 

和上面最开始的通过继承增强功能的方式相比:

这两个体系相比,装饰比继承灵活,如果想对已有体系进行功能的扩展,首先要想到的就是装饰模式

装饰模式的特点:装饰类和被装饰类都必须所属同一个接口或者父类。 

 

字节流和字符流的区别:

字节流能处理的数据单元不一样,数据的格式不一样,MP3,文本等。字符流只能操作文字。

字符流用的是缓冲区是字符数组。字节流用的缓冲区是字节数组。

字节流一次就不能读取出一个中文文字。字符流可以。

 

能用字符流进行媒体文件的操作吗?

字符流的特点在于,读取完字节之后并没有直接去往目的地里面去写而是去查表(查表有对应的数据,然后我们接着写不一样吗,是的,真的是这样的,但就是这个地方出了问题),万一读到的这个字节数据在表里查不到内容呢?文字有特定的编码格式,而这些媒体文件没有,他们都有其自身的编码方式,而且这些编码方式都是千变万化的,他拿到码表去查没有找到对应的,怎么办?码表会拿一些未知字符区的数据来表示这个没有对应的情况,就直接写到目的数据里面去了,这样元数据和目的数据就不一致了,这样就不能被图片编辑器所解析,解析不了。

不要尝试用字符流去操作媒体文件,你操作完之后有可能发现操作之后的数据大小和源数据的数据大小不一致。


本文转自SummerChill博客园博客,原文链接:http://www.cnblogs.com/DreamDrive/p/7508752.html,如需转载请自行联系原作者

相关文章
|
12天前
|
设计模式 Java 开发者
设计模式揭秘:Java世界的七大奇迹
【4月更文挑战第7天】探索Java设计模式:单例、工厂方法、抽象工厂、建造者、原型、适配器和观察者,助你构建健壮、灵活的软件系统。了解这些模式如何提升代码复用、可维护性,以及在特定场景下的应用,如资源管理、接口兼容和事件监听。掌握设计模式,但也需根据实际情况权衡,打造高效、优雅的软件解决方案。
|
14天前
|
设计模式 存储 Java
23种设计模式,享元模式的概念优缺点以及JAVA代码举例
【4月更文挑战第6天】享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享技术有效地支持大量细粒度对象的重用。这个模式在处理大量对象时非常有用,特别是当这些对象中的许多实例实际上可以共享相同的状态时,从而可以减少内存占用,提高程序效率
31 4
|
12天前
|
设计模式 监控 Java
设计模式 - 观察者模式(Observer):Java中的战术与策略
【4月更文挑战第7天】观察者模式是构建可维护、可扩展系统的关键,它在Java中通过`Observable`和`Observer`实现对象间一对多的依赖关系,常用于事件处理、数据绑定和同步。该模式支持事件驱动架构、数据同步和实时系统,但需注意避免循环依赖、控制通知粒度,并关注性能和内存泄漏问题。通过明确角色、使用抽象和管理观察者注册,可最大化其效果。
|
2天前
|
设计模式 算法 Java
Java中的设计模式及其应用
【4月更文挑战第18天】本文介绍了Java设计模式的重要性及分类,包括创建型、结构型和行为型模式。创建型模式如单例、工厂方法用于对象创建;结构型模式如适配器、组合关注对象组合;行为型模式如策略、观察者关注对象交互。文中还举例说明了单例模式在配置管理器中的应用,工厂方法在图形编辑器中的使用,以及策略模式在电商折扣计算中的实践。设计模式能提升代码可读性、可维护性和可扩展性,是Java开发者的必备知识。
|
4天前
|
设计模式 算法 Java
小谈设计模式(30)—Java设计模式总结
小谈设计模式(30)—Java设计模式总结
|
5天前
|
设计模式 存储 Java
Java设计模式:解释一下单例模式(Singleton Pattern)。
`Singleton Pattern`是Java中的创建型设计模式,确保类只有一个实例并提供全局访问点。它通过私有化构造函数,用静态方法返回唯一的实例。类内静态变量存储此实例,对外仅通过静态方法访问。
12 1
|
9天前
|
运维 NoSQL 算法
Java开发-深入理解Redis Cluster的工作原理
综上所述,Redis Cluster通过数据分片、节点发现、主从复制、数据迁移、故障检测和客户端路由等机制,实现了一个分布式的、高可用的Redis解决方案。它允许数据分布在多个节点上,提供了自动故障转移和读写分离的功能,适用于需要大规模、高性能、高可用性的应用场景。
16 0
|
9天前
|
设计模式 算法 Java
23种设计模式,模板方法模式的概念优缺点以及JAVA代码举例
【4月更文挑战第10天】模板方法模式是一种行为设计模式,它定义了一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。
13 0
|
10天前
|
设计模式 Java
23种设计模式,状态模式的概念优缺点以及JAVA代码举例
【4月更文挑战第9天】状态模式是一种行为设计模式,允许一个对象在其内部状态改变时改变它的行为,这个对象看起来似乎修改了它的类。
25 4
|
12天前
|
设计模式 Java
23种设计模式,命令模式的概念优缺点以及JAVA代码举例
【4月更文挑战第7天】命令模式是一种行为设计模式,它将请求或简单操作封装为一个对象。这种模式允许用户通过调用对象来参数化其他对象的方法,并能保存、排队和执行方法调用。
18 1