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连接池原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。频繁创建和关闭连接会消耗大量资源,导致性能瓶颈。为此,Java连接池技术通过复用连接,实现高效、稳定的数据库连接管理。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接池的基本操作、配置和使用方法,以及在电商应用中的具体应用示例。
32 5
|
22天前
|
存储 算法 Java
Java HashSet:底层工作原理与实现机制
本文介绍了Java中HashSet的工作原理,包括其基于HashMap实现的底层机制。通过示例代码展示了HashSet如何添加元素,并解析了add方法的具体过程,包括计算hash值、处理碰撞及扩容机制。
|
2天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
2天前
|
Java
Java之CountDownLatch原理浅析
本文介绍了Java并发工具类`CountDownLatch`的使用方法、原理及其与`Thread.join()`的区别。`CountDownLatch`通过构造函数接收一个整数参数作为计数器,调用`countDown`方法减少计数,`await`方法会阻塞当前线程,直到计数为零。文章还详细解析了其内部机制,包括初始化、`countDown`和`await`方法的工作原理,并给出了一个游戏加载场景的示例代码。
Java之CountDownLatch原理浅析
|
4天前
|
Java 索引 容器
Java ArrayList扩容的原理
Java 的 `ArrayList` 是基于数组实现的动态集合。初始时,`ArrayList` 底层创建一个空数组 `elementData`,并设置 `size` 为 0。当首次添加元素时,会调用 `grow` 方法将数组扩容至默认容量 10。之后每次添加元素时,如果当前数组已满,则会再次调用 `grow` 方法进行扩容。扩容规则为:首次扩容至 10,后续扩容至原数组长度的 1.5 倍或根据实际需求扩容。例如,当需要一次性添加 100 个元素时,会直接扩容至 110 而不是 15。
Java ArrayList扩容的原理
|
29天前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
63 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
10天前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
25 2
|
13天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
10天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
24 1
|
16天前
|
存储 安全 Java
深入理解Java中的FutureTask:用法和原理
【10月更文挑战第28天】`FutureTask` 是 Java 中 `java.util.concurrent` 包下的一个类,实现了 `RunnableFuture` 接口,支持异步计算和结果获取。它可以作为 `Runnable` 被线程执行,同时通过 `Future` 接口获取计算结果。`FutureTask` 可以基于 `Callable` 或 `Runnable` 创建,常用于多线程环境中执行耗时任务,避免阻塞主线程。任务结果可通过 `get` 方法获取,支持阻塞和非阻塞方式。内部使用 AQS 实现同步机制,确保线程安全。
下一篇
无影云桌面