Java中的stream流的概念解析及实际运用总结

 更新时间:2016年06月26日 15:44:42   作者:pizzq  
流是指传输时的数据,Java为流准备了很多内置类,尤其是IO输入输出流非常常用,这里我们来看一下Java中的stream流的概念解析及实际运用总结

流是字节序列的抽象概念。
文件是数据的静态存储形式,而流是指数据传输时的形态。
流类分为两个大类:节点流类和过滤流类(也叫处理流类)。
程序用于直接操作目标设备所对应的类叫节点流类,程序也可以通过一个间接流类去调用节点流类,以达到更加灵活方便地读取各种类型的数据,这个间接流类就是过滤流类(也叫处理流类),或者称为包装类。
包装类的调用过程如下图:

2016626153726182.gif (524×112)

流分类的关系
不管流的分类是多么的丰富和复杂,其根源来自于四个基本的类。这个四个类的关系如下:

字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

Java内用 Unicode 编码存储字符,字符流处理类负责将外部的其他编码的字符流和java内 Unicode 字符流之间的转换。而类InputStreamReader 和OutputStreamWriter处理字符流和字节流的转换。字符流(一次可以处理一个缓冲区)一次操作比字节流(一次一个字节)效率高。

InputStream

2016626153854884.gif (462×199)

由于InputStream和OutputStream是abstact类,所以它们还不能表明具体对应哪种IO设备。它们下面有许多子类,包括网络、管道、内存、文件等具体的IO设备,实际程序中使用的它们的各种子类对象。
注:我们将节点流类所对应的IO源和目标称为流节点(Node)。
注意:将A文件的内容写入B文件,程序对A文件的操作所用的是输出类还是输入类这个问题。输入输出类是相对程序而言的,而不是代表文件的,所以我们应该创建一个输入类来完成对A文件的操作,创建一个输出类来完成对B文件的操作。

OutputStream

2016626153922575.gif (458×142)

以字符为导向的 stream Reader/Writer
以 Unicode 字符为导向的 stream ,表示以 Unicode 字符为单位从 stream 中读取或往 stream 中写入信息。同样,Reader/Writer也为abstact类。

Reader
2016626153945333.gif (462×169)

Writer

2016626154006667.gif (465×222)

IO程序代码的复用:
平时写代码用-1来作为键盘输入的结束,在写的函数中不直接使用System.in,只是在调用该函数时,将System.in作为参数传递进去,这样,我们以后要从某个文件中读取数据,来代替手工键盘输入时,我们可以直接使用这个函数,程序就不用做太多的修改了,达到以不变应万变的效果。

字节流和字符流的相互转换
InputStreamReader和OutputStreamReader:把一个以字节为导向的stream转换成一个以字符为导向的stream。
InputStreamReader类是从字节流到字符流的桥梁:它读入字节,并根据指定的编码方式,将之转换为字符流。
使用的编码方式可能由名称指定,或平台可接受的缺省编码方式。
InputStreamReader的read()方法之一的每次调用,可能促使从基本字节输入流中读取一个或多个字节。
为了达到更高效率,考虑用BufferedReader封装InputStreamReader,

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

Java流使用的一点总结
最经工作中碰到不少Java流的使用,总结如下:
1. 生成Zip格式,遇到的是要在一个Servlet中生成Zip文件,输出到web 客户端,直接下载。

response.setContentType("application/zip"); 
 response.addHeader("Content-Disposition", "attachment;filename=/"xxx.zip/""); 
 ZipOutputStream out = new ZipOutputStream(response.getOutputStream()) 
  
 for() 
 {  
  ZipEntry entry = new ZipEntry("aa" + i ".dat"); 
  out.putNewEntry(entry); 
  bytes[] bt = s.getBytes(); 
  out.writeBytes(bt, 0, bt.length()); 
  out.closeEntry(); 
 } 
 out.flush(); 
 out.close(); 

ZipOutputStream 继承自 java.io.FilterOutputStream. 因此真正的写操作是通过参数OutputStream out去写的。
 其 void write(byte[] b, int off, int len) 最终调用了 out.write(b, off, len);

如果要生成一个zip文件,构造时就这样写 new ZipOutputStream(new FileOutputStream(path));

2. 类似的写XML.
 

 XMLWriter writer = new XMLWriter(new FileOutputStream(path), formater)

writer.write(doc).道理和上面类似
3. 写文本文件,追加。   

PrintStream ps = new PrintStream(new FileOutputStream(path, true), "utf-8")

ps.println(s); // 能写boolean、int等各种类型。

PrintSteam同样继承自FilterOutputStream
其内部使用一个OutputStreamWriter的对象textOut来写。例如write(String s)最终调用到textOut.write(s);
    
这里涉及到编码的问题。其参数里的"utf-8"最终传递到OutputStream。
    
OutputStream是一个字符流和字节流之间的桥梁。
 因此其提供了write(char[] cbuf, int off, int len) 和 write(String str, int off, int len) 用来写字符和字符串。
OutputStream内部又通过一个StreamEncoder对象来序列化字符和字符串。
4. 写出到socket。
 DataOutputStream out = new DataOutputStream(socket.getOutputStream());
 out.writeBytes(bt);
 out.writeBoolean(boolean v) ; 

DataOutputStream同样是一个自FilterOutputStream.

5. 从文本中读取

 BufferedReader reader = new BufferedReader(new FileReader(path));
 reader.readLine();

    BufferedReader的模式和上面的Filter模式一样,其内部存储一个Reader对象为参数传进来并用来实际读取的对象。
    BufferedReader对应java 1.0的类就是BufferedInputStream,是一个FilterInputStream。

6. 从Socket中读取

 BufferedInputStream is = new BufferedInputStream(socket.getInputStream());
 is.read(bt, 0, bt.length());


总结:
基类Stream系列是InputStream和OutputStream,他们是抽象类,要求的方法只有(以Output为例)

void write(int b) throws IOException;
void write(byte b[]) throws IOException
void write(byte b[], int off, int len)

其最基本的只是字节操作。第1个方法看似写一个整数,其实只写一个字节(最低八个bit)。其子类分两个系列,一个是直接操作输出设备的,我们上面碰到的有文件(FileOutputStream)和Servlet输出(ServletOutputStream)。其他常用的还有一个 ByteArrayOutputStream,是直接在内存里操作的。
再就是FilterOutputStream系列了,都是接收一个OutputStream对象座位参数,真正的写操作通过该对象去完成。例如ZipOutputStream,其本身只负责生成压缩格式的数据,至于这些数据是写到文件、内存、还是servletResponse,由输入的参数确定。这就是装饰器模式。

Filter系列常用的有PrintStream(提供了print,println,write(boolean[int, char, string])各种操作,最终利用out.write方法以写字节的方式写进去。
还有DataOutputStream,其提供了writeByte/writeBoolean/writeDouble/writeLong/wiretUTF等方法。
还有就是socket/zip等不常用的。

Java的流很方便也很复杂。复杂就复杂在实现一个功能往往需要多个类,而且有多种组合的办法。尚需继续在实践中总结。  

相关文章

  • Spring运行时手动注入bean的方法实例

    Spring运行时手动注入bean的方法实例

    spring给我们提供了IOC服务,让我们可以用注解的方式,方便的使用bean的相互引用,下面这篇文章主要给大家介绍了关于Spring运行时手动注入bean的相关资料,需要的朋友可以参考下
    2022-05-05
  • Java中Arraylist动态扩容方法详解

    Java中Arraylist动态扩容方法详解

    ArrayList的列表对象实质上是存储在一个引用型数组里的,下面这篇文章主要给大家介绍了关于Java中Arraylist动态扩容方法的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面跟着小编来一起学习学习吧。
    2017-08-08
  • SpringBoot集成Quartz实现持久化定时接口调用任务

    SpringBoot集成Quartz实现持久化定时接口调用任务

    Quartz是功能强大的开源作业调度库,几乎可以集成到任何 Java 应用程序中,从最小的独立应用程序到最大的电子商务系统,本文将通过代码示例给大家介绍SpringBoot集成Quartz实现持久化定时接口调用任务,需要的朋友可以参考下
    2023-07-07
  • Java spring的三种注入方式详解流程

    Java spring的三种注入方式详解流程

    Spring框架由Rod Johnson开发,2004年发布了Spring框架的第一版。Spring是一个从实际开发中抽取出来的框架,因此它完成了大量开发中的通用步骤,留给开发者的仅仅是与特定应用相关的部分,从而大大提高了企业应用的开发效率
    2021-10-10
  • Java RabbitMQ的三种Exchange模式

    Java RabbitMQ的三种Exchange模式

    这篇文章主要介绍了Java RabbitMQ的三种Exchange模式,分别为Direct模式、Fanout模式、Topic模式,Rabbit的Direct Exchange模式是指消息发送导RouteKey中指定的Queue,Direct模式可以使用Rabbit自带的Exchange
    2022-08-08
  • java发送邮件示例讲解

    java发送邮件示例讲解

    这篇文章主要为大家详细介绍了java发送邮件示例的全过程,温习邮件协议,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-03-03
  • jmeter基础逻辑控制器之if控制器的使用

    jmeter基础逻辑控制器之if控制器的使用

    在实际工作中,当使用JMeter做性能脚本或者接口脚本时,当遇到需要对不同的条件做不同的操作时,我们可以使用JMeter中if控制器来实现,本文就详细的介绍一下如何使用
    2021-11-11
  • Java中文件写入内容的几种常见方法

    Java中文件写入内容的几种常见方法

    本文主要介绍了Java中文件写入内容的几种常见方法,主要包括使用NIO的Files工具类、通过commons-io的FileUtils工具类、RandomAccessFile、PrintWriter和BufferedWriter这几种,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • 举例讲解Java设计模式中的对象池模式编程

    举例讲解Java设计模式中的对象池模式编程

    这篇文章主要介绍了Java设计模式中的对象池模式编程示例分享,对象池模式经常在多线程开发时被用到,需要的朋友可以参考下
    2016-02-02
  • 只用400行Java代码就能实现的飞翔的小鸟游戏

    只用400行Java代码就能实现的飞翔的小鸟游戏

    今天给大家带来的是关于Java实战的相关知识,文章围绕着只用400行Java代码就能实现的飞翔的小鸟游戏展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06

最新评论