装饰流使用

除了按照流的方向可以把流划分为输入流和输出流两类,按照流读写数据的基本单位把流划分为字节流和字符流两类以外,还可以按照流是否直接连接实际数据源,例如文件、网络、字节数组等,将流又可以划分为实体流和装饰流两大类。

其中实体流指直接连接数据源的流类,如前面介绍的FileInputStream/FileOutputStream和FileReader和FileWriter,该类流直接实现将数据源转换为流对象,在实体流类中实现了流和数据源之间的转换,实体流类均可单独进行使用。

而装饰流指不直接连接数据源,而是以其它流对象(实体流对象或装饰流对象)为基础建立的流类,该类流实现了将实体流中的数据进行转换,增强流对象的读写能力,比较常用的有DataInputStream/DataOutputStream和BufferedReader/BufferedWriter等,装饰流类不可以单独使用,必须配合实体流或装饰流进行使用。

由于装饰流都是在已有的流对象基础上进行创建的,所以这种创建流的方式被称作“流的嵌套”,通过流的嵌套,可以修饰流的功能,例如使读写的速度增加或者提供更多的读写方式,方便数据格式的处理。

装饰流不改变原来实体流对象中的数据内容,只是从实体流对象基础上创建出的装饰流对象相对于实体流对象进行了一些功能的增强。

流的嵌套是学习IO编程时必须掌握的知识,使用它才可以让你真正体会到IO类设计时的设计思路,也可以方便的使用IO类。

下面分别以DataInputStream/DataOutputStream和BufferedReader/BufferedWriter为例子,详细介绍装饰类的使用。

11.3.3.1 DataInputStream/DataOutputStream

在前面的示例中,在向流中写入的数据必须首先转换为byte数组或char数 组,当写入的数据比较少、比较简单时,则向流中写入数据时还是不是很麻烦的,但是如果向流中写入数据比较多时,手动转换数据格式则会比较麻烦。当然,很多 文件都是根据文件存储的需要设计了专门的存储格式,但是这些格式一般都比较复杂,需要阅读专门的格式文档才可以读写这些特定格式的文件。

为了简化程序员对于流的操作,使得程序员可以从繁杂的数据格式中解脱出来,在IO类中专门设计了两个类——DataInputStream/DataOutputStream类简化流数据的读写,使用这两个类,可以实现以增强型的读写方法读写数据,使得读写流的数据变得比较简单。

在实际使用这两个类时,必须匹配起来进行使用。也就是说,只有使用DataOutputStream流格式写入的数据,在实际读取时才可以使用DataInputStream进行读取。因为在使用DataOutputStream向流中写入数据时,除了写入实际的数据内容以外,还写入了特定的数据格式,该格式对于程序员来说是透明的,这种特定的格式不需要程序员熟悉,而只需要使用DataInputStream读取即可,读取时的顺序和写入时的顺序和类型保持一致即可。

在DataInputStream类中,增加了一系列readXXX的方法,例如readInt、readUTF、readBoolean等等,而在DataOutputStream类中,也增加了一系列writeXXX的方法,例如writeInt、writeUTF、writeBoolean等等,使得对于数据的读写更加方便很容易。

下面以读写文件为例子,演示DataInputStream/DataOutputStream类的基本使用。

/**

*模拟需要存储到文件中的数据

*该类中保存4种类型的数据

*/

public class MyData {

boolean b;

int n;

String s;

short sh[];

public MyData(){}

public MyData(boolean b,int n,String s,short sh[]){

this.b = b;

this.n = n;

this.s = s;

this.sh = sh;

}

}

在该示例中,需要将MyData类型的对象内部保存的数据按照一定的格式存储到文件中,这里列举了2种基本数据类型boolean和int,以及两种引用数据类型String和数组,在下面的示例代码中将会以一定的格式写入到文件中。

import java.io.*;

/**

*使用DataOutputStream书写具有一定格式的文件

*/

public class WriteFileUseDataStream {

public static void main(String[] args) {

short sh[] = {1,3,134,12};

MyData data =new MyData(true,100,"Java语言",sh);

//写入文件

writeFile(data);

}

/**

*将MyData对象按照一定格式写入文件中

* @param data数据对象

*/

public static void writeFile(MyData data){

FileOutputStream fos = null;

DataOutputStream dos = null;

try{

//建立文件流

fos = new FileOutputStream("test.my");

//建立数据输出流,流的嵌套

dos = new DataOutputStream(fos);

//依次写入数据

dos.writeBoolean(data.b);

dos.writeInt(data.n);

dos.writeUTF(data.s);

//写入数组

int len = data.sh.length;

dos.writeInt(len); //数组长度

//依次写入每个数组元素

for(int i = 0;i < len;i++){

dos.writeShort(data.sh[i]);

}

}catch(Exception e){

e.printStackTrace();

}finally{

try {

dos.close();

fos.close();

} catch (Exception e2){

e2.printStackTrace();

}

}

}

}

在该示例代码中,首先建立一个实体流fos,该实体流连接到数据源——文件,然后以该实体流对象为基础,使用流的嵌套,建立装饰流对象dos,由于需要写入流中的对象data中包含的数据比较多,所以需要以一定的格式写入流,这里使用DataOutputStream避免自定义数据格式,而写入流中的顺序就是该流的格式,也就是文件test.my的格式,这种格式对于程序员来说是透明的。

使用对象dos中对应的writeXXX方法依次将需要存储的数据写入流中,在写入字符串时,为了使字符编码保持一致,一般使用writeUTF写入字符串,也就是先将字符串转换为utf-8格式的byte数组,然后再将该数组以一定的格式写入到流中。而在写入数组时,则首先写入数组的长度,然后再将数组的内容依次写入到流中,使用这种方式就可以很方便的将数组写入到流中。

这样文件test.my文件就具有了自己特定的文件格式,程序员需要记忆的就是该文件在写入时的写入顺序,可以很方便的使用DataInputStream读取出来。

下面的代码是使用DataInputStream读取test.my文件的代码,注意文件格式的处理。

import java.io.*;

/**

*使用DataInputStream读取自定义格式的文件

*/

public class ReadFileUseDataStream {

public static void main(String[] args) {

MyData data = readFile();

System.out.println(data.b);

System.out.println(data.n);

System.out.println(data.s);

int len = data.sh.length;

for(int i = 0;i < len;i++){

System.out.println(data.sh[i]);

}

}

/**

*从文件test.my中读取数据,并使用读取到的数据初始化data对象

* @return读取到的对象内容

*/

public static MyData readFile(){

MyData data = new MyData();

FileInputStream fis = null;

DataInputStream dis = null;

try {

//建立文件流

fis = new FileInputStream("test.my");

//建立数据输入流,流的嵌套

dis = new DataInputStream(fis);

//依次读取数据,并赋值给data对象

data.b = dis.readBoolean();

data.n = dis.readInt();

data.s = dis.readUTF();

int len = dis.readInt();

data.sh = new short[len];

for(int i = 0;i < len;i++){

data.sh[i] = dis.readShort();

}

} catch (Exception e) {

e.printStackTrace();

}finally{

try {

dis.close();

fis.close();

} catch (Exception e) {

e.printStackTrace();

}

}

return data;

}

}

在该示例代码中,首先建立实体流fis,然后以该流对象为基础建立dos装饰流,然后按照写入文件的顺序,依次将流中的数据读取出来,并将读取到的数值赋值给data对象中对应的属性,从而实现将数据从文件中恢复到实际的对象。

最后再次强调,DataInputStream和DataOutputStream必须匹配起来进行使用,也就是使用DataInputStream读取的流数据必须是使用DataOutputStream流写入的数据,这样才能保持格式上的统一。

当然,使用DataInputStream和DataOutputStream和其它的实体流也可以匹配起来进行使用,例如和ByteArrayInputStream和ByteArrayOutputStream匹配使用将可以实现方便的把数据转换为特定格式的byte数组以及将byte数组恢复回来,使用的格式和上面的示例类似,这里就不再重复了。

BufferedReader/BufferedWriter

在进行IO操作时,除了功能以外,程序的执行效率也是必须要考虑的问题。基本的IO类只是注重功能的实现,例如将特定的数据源转换为流对象,而没有过多的关注读写的效率问题,而实际在进行项目开发时,读写效率也是必须要考虑的问题。

为了提高IO类的读写效率,在装饰流中专门制作了一类缓冲流,该类流的作用就是提高流的读写效率,这组缓冲流包含:BufferedInputStream/BufferedOutputStream、BufferedReader/BufferedWriter。

该部分以BufferedReader/BufferedWriter为基础进行介绍。

由于前面介绍DataInputStream/DataOutputStream时,是以文件流作为实体流进行介绍,这里就不再重复了,这里以前面介绍的接收控制台输入为基础介绍缓冲输入流的使用。

由于装饰流在进行嵌套时,只能嵌套相同类型的流,例如InputStream类型的流之间可以嵌套,但是InputStream和Reader两个体系之间的流就无法直接嵌套,为了使用新的IO类带来的特性,在IO类中提供了两个专门的类,实现体系之间的转换,这两个流类被形象的称为“桥接流”。

桥接流主要包含2个,依次是:

1、InputStreamReader

该类实现将InputStream及其子类的对象转换为Reader体系类的对象,实现将字节输入流转换为字符输入流。

2、OutputStreamWriter

该类实现将OutputStream及其子类的对象转换为Writer体系类的对象,实现将字节输入流转换为字符输入流。

这两个桥接流使得字节流可以被转换为字符流,但是需要注意的是,字符流无法转换为字节流。

在读取控制台输入时,直接使用System.in进行读取,虽然在功能上可以实现,但是这种方式读写流的效率比较差,所以在实际使用时一般需要提高读写的效率,这就需要使用装饰流中的缓冲流,这是一个典型的流的嵌套的示例。该代码实现的功能是如果回显用户输入,当用户输入quit时程序退出。该示例的代码如下:

import java.io.*;

/**

*使用BufferedReader读取控制台输入

*/

public class ReadConsoleWithBuffer {

public static void main(String[] args) {

BufferedReader br = null;

String s = null;

try{

//使用流的嵌套构造缓冲流

br = new BufferedReader(

new InputStreamReader(System.in));

do{

//输出提示信息

System.out.println("请输入:");

//按行读取输入

s = br.readLine();

//输出用户输入

System.out.println(s);

}while(!s.equals("quit"));

}catch(Exception e){

e.printStackTrace();

}finally{

try{

br.close();

}catch(Exception e){

e.printStackTrace();

}

}

}

}

在该示例代码中,首先使用流的嵌套构建了BufferedReader类型的对象br,然后使用BufferedReader中的readLine方法,每次读取用户输入的一行信息,使用readLine方法读取内容时,系统以”\r\n”作为每次的结束符号,而且读取的内容不包含”\r\n”,当读取到流的末尾时readLine方法的返回值是null。然后使用do-while循环判断用户输入的是否是quit,如果输入的是quit,则程序结束,否则继续下一次循环。

关于BufferedWriter的使用,没有什么特别的地方,这里就不单独举例说明了。

11.3.3.3装饰流小结

前面介绍了两类比较常见的装饰流,在实际的开发中,根据逻辑的需要还可能会用到其它的装饰流,这些装饰流的使用和前面介绍的类类似,在实际使用时通过查阅JDK API文档找到根据功能找到合适的装饰流,然后进行使用即可。

java 装饰流_java装饰流的使用【转】相关推荐

  1. java io流过滤流_JAVA io流 文件流 字节流 字符流 过滤流 缓冲流

    一.概念 1.1.按流向分类: 输入流: 程序可以从中读取数据的流. 输出流: 程序能向其中写入数据的流. 1.2.按数据传输单位分类: 字节流:以字节(8位二进制)为单位进行处理.主要用于读写诸如图 ...

  2. JAVA mac系统io文件流_Java IO流基础1--IO的分类体系与文件流

    什么是IO流 Java中的IO 了解什么是IO流之前,要先知道什么是IO.IO,就是in和out(即输入和输出),指应用程序和外部设备之间的数据传递,常见的外部设备包括文件.管道.网络连接等. 流的概 ...

  3. java每秒限流_java限流工具类

    代码 import com.google.common.util.concurrent.RateLimiter; import java.util.concurrent.ConcurrentHashM ...

  4. java关闭io流_Java IO流关闭问题的深入研究

    前言 前几天看了一篇文章(见参考文章),自己动手试了下,发现有些不一样结论,作博客记录下,本文主要研究两个问题: 1.包装流的close方法是否会自动关闭被包装的流? 答:会. 2.关闭流方法是否有顺 ...

  5. java序列化流_java 序列化流与反序列化流

    一 对象序列化流ObjectOutputStream ObjectOutputStream 将Java对象的基本数据类型和图形写入OutputStream.可以使用ObjectInputStream ...

  6. java 装饰器_JAVA装饰器模式

    Java程序员们应该对java.io对不会陌生,因为java.io包采用了装饰器模式. 一.定义: Decorator装饰器,顾名思义,就是动态地给一个对象添加一些额外的职责,就好比为房子进行装修一样 ...

  7. java 8流在另一个流_Java 8流– Java流

    java 8流在另一个流 Welcome to Java 8 Stream API tutorial. In the last few java 8 posts, we looked into Jav ...

  8. java字节流分为_Java的流操作分为字节流和字符流两种。

    Java的流操作分为字节流和字符流两种.           1. 字节流  所有的读操作都继承自一个公共超类java.io.InputStream类.  所有的写操作都继承自一个公共超类java.i ...

  9. java 文件转换成流_java -IO流_转换流

    转换流 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStre ...

最新文章

  1. 机器学习、深度学习、自然语言处理、计算机视觉顶级期刊的论文资料分享(附顶会论文下载链接)...
  2. LXC linux容器简介——在操作系统层次上为进程提供的虚拟的执行环境,限制其使用的CPU和mem等资源,底层是linux内核资源管理的cgroups子系统...
  3. Tomcat 配置文件
  4. linux桌面使用网卡设置,Linux的KDE桌面下怎样设置网络连接?
  5. 力扣-1925 统计平方和三元组的数目
  6. 利用CMD命令关闭进程
  7. 高红梅 第一章 海明威自我身份意识的形成 第一节 文化氛围与自我身份意识的生成
  8. Panabit存在命令执行漏洞
  9. 旭辉完成2020年销售目标:还要加码广西,却在北京违规被通报
  10. 【矩阵论笔记】满秩分解
  11. wireshark 找不到wifi网卡
  12. 信息安全与管理2002_李付贵
  13. PYNQ-Z2零基础学习详解
  14. 兔子繁殖问题(C语言)
  15. MySQL_Windows 下重启MySQL服务
  16. python 老照片修复软件_这款开源的 Python 老照片修复工具火了
  17. 西门子系列全套学习视频,免费领取!
  18. dllexport和dllimport
  19. [附源码]Python计算机毕业设计JAVA高校田径运动会管理
  20. 新版 PodBot MM 安装方法

热门文章

  1. bpe编码_缓冲池扩展(BPE)–实施另一级缓存
  2. Tmux 配置:打造最适合自己的终端复用工具
  3. 7-1 查找整数 (10 分)
  4. 如何量化考核技术人的KPI?
  5. 1602: [Usaco2008 Oct]牧场行走
  6. 帝国列表页分开调取年份和月份单独调用的方法?
  7. android笔记--与服务器交互更改简历状态
  8. Viod Class 启动
  9. vs.net 2005中引用webservice的简单方法
  10. mysql having关键字可以对group by后的结果再进行筛选