`

java流类的概念理解 二

阅读更多
下面让我们来看一看由JDK提供的所有输入流与输出流的超类InputStream和OutputStream.

1.1 InputStream
    InputStream既是一个抽象类也是所有输入流类的超类.它可以被看作是数据源.一旦输入流被打开,客户程序就可以从流中读取数据了.InputStream按照用途不同可以分为三类,分别是:
    1.数据读取
    2.流导航
    3.资源管理
数据读取
数据读取方法是InputStream定义的所有方法中最重要的方法.属于这一类的方法有三个,分别是:
public int read() throws IOException
public int read(byte[] buffer) throws IOException
public int read(byte[] buffer, int startingOffset, int numberOfBytes) throws IOException

    其中,第一个方法,read(),返回位于流中当前位置紧后一个字节的字节值.此字节返回值是以0至255之间整数来表示的.如果返回值为-1,则表示从流中不能再获得任何的数据了.通常,当客户程序到达文件尾时,read()会返回-1.另一方面,倘若在read()方法执行过程中出现数据临时无法得到的情况,read()方法会被阻塞.
    当一段程序为了等待获得某个资源的控制权而不能正常地结束时,它就处于阻塞状态了.通常,从文件中读取数据时,read()方法就有可能为了等待获得对目标硬盘驱动器的控制权而被迫停止执行进入阻塞状态.阻塞常常会给我们带来许多的麻烦.比如说,当客户程序一直在等待一个永远都不会到来的字节时,这段程序就已经崩溃了.
    另外两个方法是无参数read()方法的高级版本(更有效率),例如,一段程序要从计算机设备中读取65000个字节.如果客户程序逐个字节地执行读操作,那么这段程序的运行效率会是非常地低下.但是,如果你已经事先知道要读取数据的数据量大小,那最好的做法就是一次性地把所有的数据都读出来放到内存里,而不是一个字节接一个字节地提取,具体方法如下:
byte buffer = new byte[1000];  
read(buffer);

read(byte[] buffer)方法的作用是从流中一次性地读取缓冲区大小的字节序列(在本例中就是buffer.length个字节),read(byte[] buffer)方法的返回值是一个整数表示被实际读取的字节个数.如果方法的返回值是-1,就表示没有数据被读入.
    最后,read(byte[] buffer, int startingOffset, int numberOfBytes)方法的作用是从流中读取numberOfBytes个字节,然后将它们放置在缓冲区内以buffer[startingOffset]开始(包括buffer[startingOffset])的后续空间里.例如
read(buffer, 2, 7);

这条语句将从流中读取7个字节,然后将它们分别放置在buffer[2], buffer[3]… buffer[8]中.像前面的方法一样,它也会返回一个整数返回值,以代表实际从流中读取的字节个数.如果返回值为-1,则表示没有数据被读入.

流导航
    流导航方法用来前后移动漂浮于流中的游标.它们分别为:
public int available( ) throws IOException

用来统计客户程序能够立即从流中检索出的字节个数,在调用read()方法进行实际数据读取操作之前,客户程序都会先使用available()方法来判断此时流中是否有数据可以获得,以避免出现阻塞现象.代码片段如下:
while (stream.available( ) >0 ) {    
processNextByte(stream.read( ));
}
    关于available()方法的用法有两个注意事项:
    1.你要确定在流定义中已经正确地实现了available()方法.在InputStream中,available()方法的默认实现是仅只返回0值的.如果在它的子类中没有正确地重载此方法的话,其它相关模块内的程序就会被误导(例如,在上面的代码片段中,如果available()方法仅只返回0值的话,那么循环体内的程序是永远也不会被执行的).
    2.你一定要使用缓存.至于如何通过缓存来提高读操作的效率,可以查找其他相关资料
public long skip(long numberOfBytes) throws IOException

向前推进游标numberOfBytes个字节.对于大多数InputStream子类的实现来说,skip()方法都是以不断地读入字节的方式向前推动游标.事实上,大多数skip()方法的实现都是以连续地从流中读入字节的方式向前推动游标.因此,如果skip()方法正要读入的字节由于某些原因不能被立即获得,skip()方法和read()方法一样会出现阻塞的现象.而这是值得所有程序开发人员注意的.
public void mark(int numberOfBytes) throws IOException
public boolean markSupported( ) throws IOException
public void reset( ) throws IOException

    许多输入流都只支持"仅进游标",即游标只能向前推进不能后退.为支持"双向游标",在流定义中就必须实现"标记(marking)"功能.而"标记"功能的原理其实十分简单,就是在客户程序从流中读入字节数据的同时标记当前位置点,以备今后将游标再重新置回此位置.客户程序使用的输入流对象是否支持"标记"功能可以通过markSupported()方法的返回值来判断.如果返回值为true,表示支持;否则表示不支持.
    假设客户程序使用的输入流对象支持"标记"功能,为了实现游标的"后退",你就需要使用mark()方法来标记游标在流中当前位置.然后,在以后的某个时间点,客户程序就能够再调用reset()方法将游标重置到被标记的位置.mark()方法的唯一的参数numberOfBytes是用来指定标记点的过期条件的.具体地说,就是当游标从标记点开始再往前推进多少个字节之后系统就会自动将标记点从记忆中抹除.InputStream和其子类最多只可以记忆一个标记点.因此,如果你在输入流中记录第二个标记点的话,系统会自动地抹除第一个标记点.

资源管理
    因为流通常要关联计算机设备(文件或网络连接),所以使用流就要求操作系统分配非内存的资源.但是,出于性能方面的考量,绝大多数操作系统都会限制一个程序(或者说进程)能够同时打开的文件与网络连接的个数.在InputStream中定义的资源管理方法是通过调用本地API来实现对系统资源的管理.
    InputStream抽象类中定义的唯一的资源管理方法是close().无论何时客户程序结束了对流操作之后,它都必须以显示地调用close()方法来关闭流,释放系统资源(比如说,文件句柄).
    乍看来,这种作法有些让人费解.毕竟,JAVA语言的一个突出的卖点就是它的内置于语言规范中的垃圾回收机制.为什么不能由垃圾收集器来自动地释放系统资源呢?
    原因是:垃圾收集器不可靠.JAVA语言规范的确清晰地要求JVM必须拥有自动的垃圾回收机制,但是它并没有保证当某个对象开始不被任何引用指向时,它就会被立即回收.JAVA语言规范甚至也没有明确地要求当某个对象开始不被任何引用指向时,垃圾收集器会立刻启动运行.而事实上,我们唯一能够确定的事实是:如果某个客户程序快要耗尽它的配额内存时,垃圾收集器才会被激活来回收那些已经没有任何引用指向的对象并且释放相应的内存空间.这种懒惰的垃圾回收机制是完全不足以管理稀缺的系统资源的.
综上所述可以归纳为三点:
    1.你不能控制一个对象从它应该被回收和它实际被回收之前的时间间隔.
    2.你不能控制对象被回收的次序.
    3.你能支配的文件句柄的个数与你未占用的内存余量之间没有必然的因果联系.通常,在耗尽内存之前,你就已经早早地用光了所有可用的文件句柄了.而这时垃圾收集器还在后台待命呢.
    客户程序可以使用软引用(SoftReference)来最低程度地干预垃圾收集器回收对象的次序.SoftReference是在java.lang.ref包中被定义.简而言之,垃圾收集器对于系统资源的管理是不可靠的.无论何时使用稀缺的系统资源,客户程序都有义务显示地释放资源.请记住,关闭被你打开的流是对你最低的要求.

在上述几种操作中,我们看到都会抛出输入输出异常(IOException)
    在InputStream抽象类中定义的所有方法都会抛出IOException异常.IOException异常是一个必须被客户程序捕获的异常.从代码层面来说,所有的流操作程序都必须出现在try/catch块内,请看下面的代码片段:
 try{        
    while( -1 != (nextByte = bufferedStream.read())) {
       char nextChar = (char)nextByte;          
        ...        
      }    
    }catch (IOException e) {
         ...      
     }
    由于流总是被用来与计算机设备进行数据交换,所以IOException被引入以便当设备出现了故障时,可以将故障信息立即通报给用户.
    例如,当打印机由于缺纸而暂停打印任务时,它就会向提交打印请求的客户程序抛出一段异常信息.但是,由于客户程序是不可能在没有人为干预的情况下自动向打印机的托盘中塞满打印纸,所以这个打印异常信息应该被立即地发送给终端用户.
    大部分流异常发生的场景都与上面的那个例子类似.它们都或多或少地需要一定程度的用户干预(至少,要求用户知情).因此,这些异常都要求被实时地处理.基于这些考虑,流程序库的设计师们把IOException异常设计成为必须被客户程序捕获的异常类型,以强制客户程序的开发程序员显示地,明确地处理可能会出现失败.

上面概述了输入流的知识,下面我们来看下输出流(OutputStream)
    OutputStream也是一个抽象类.它可以被看作是数据宿.输出流一旦被打开,客户程序就可以向输出流中发送数据了.
OutputStream中包括的方法有:
public void close() throws IOException
public void flush() throws IOException
public void write(byte[] buffer) throws IOException
public void write(byte[] buffer, int startingOffset, int numberOfBytes)  throws IOException
public void write(int value) throws IOException

    OutputStream抽象类有一点类似于InputStream抽象类,但是它并不支持流导航方法.因为数据一旦已经被发送,你就不可能再把它给追回来了.从功能的角度分析,OutputStream抽象类定义的方法可以被分成两类,分别是:
    1.写数据
    2.资源管理

写数据
    OutputStream抽象类定义了三个写数据方法:
public void write(byte[] buffer) throws IOException
public void write(byte[] buffer, int startingOffset, int numberOfBytes) throws IOException
public void write(int value) throws IOException

    这些写数据方法类似于InputStream抽象类中的read()方法.write(int value)方法一次只能向流中写入一个字节.它唯一的输入参数是一个取值范围在0至255之间的整数.如果参数值大于255,系统将会自动地将其对256的模作为方法的输入参数.
    write(int value)方法也有两个基于数组的变体.write(byte[] buffer)方法将把缓冲区内的所有字节都一次性地写入流中.write(byte[] buffer, int startingOffset, int numberOfBytes)方法将缓冲区内从buffer[startingOffset]字节开始向后numberOfBytes个字节写入流中
    大家可能会很费解为什么write()方法的输入参数也是一个整数呢?read()方法返回整数而不是直接返回一个字节是为了方便向外界通告状态信息.而把write()方法的输入参数也设计成一个整数,则是考虑到了write()方法与read()方法的设计对称性.这样一来,客户程序从输入流出读取的整数只要不等于-1,就可以在不做任何类型转换的条件下直接地写入到输出流中了.

资源管理
    OutputStream抽象类定义了两个资源管理方法,分别是:
public void close( )
public void flush( )

    无论何时结束了对输出流的操作之后,客户程序都有义务显示地调用close()方法关闭流和释放系统资源.因为缓存技术在文件操作和网络通信中广泛使用,所以flush()方法专门用来将缓冲区内保存的所有待发送数据一次性地发送到底层流或计算机设备中,然后再清空缓冲区.之所以要在Java虚拟机中缓存待发送的数据是因为在客户程序与操作系统之间逐个字节的交换数据是昂贵的也是低效的.

好,以上俺学习的内容一并放在这里跟大家共享,希望兄弟姐妹们能多点耐心学习下,吃的苦中苦,坚持就是王道,哈哈.....
分享到:
评论

相关推荐

    Java-Io流,练习

    Java的IO流是Java编程语言中的重要组成部分,它主要用于数据的输入和输出操作。...对于初学者而言,理解和掌握IO流的基本概念、分类以及常用类的用法是至关重要的。通过实践练习,可以加深对IO流的理解,提高编程能力。

    Java标准类库(java基础类)

    2. **向文件写入数据**:通过`FileOutputStream`向文件写入字节流,使用`DataOutputStream`将高级Java类型转换为字节流进行存储。 #### 序列化 序列化是指将对象的状态转换为字节流的过程,以便于存储或在网络上...

    java 视频流读写

    在实际的JMF_demo项目中,这些概念会通过具体的代码实现,包括类的定义、方法的调用以及异常处理等。这个实例可以帮助开发者更好地理解和应用JMF进行多媒体处理,特别是对于需要在Java应用中处理视频流的场景,具有...

    实验9 Java输入输出流.doc

    Java输入输出流是Java编程中一个非常重要的概念,主要用于数据的读取和写入操作,包括文本文件和二进制文件。在实验9中,主要目的是理解和掌握I/O流的分类,以及如何进行文本和二进制文件的读写。 I/O流在Java中被...

    ACCP7.0使用Java理解程序逻辑教学PPT

    4. **面向对象编程**:Java是一种面向对象的语言,因此需要理解类、对象、封装、继承和多态的概念。了解如何通过类来组织代码,创建对象实例,并利用继承和多态性实现代码的扩展和重用。 5. **异常处理**:Java中的...

    Java图书管理系统(IO流版)(csdn)————程序.pdf

    * 理解Java中的集合和IO流的概念。 * 熟悉使用Java中的集合,例如ArrayList、LinkedList等。 * 了解Java中的IO流的使用,例如使用FileInputStream和FileOutputStream等。 七、图书管理系统的实现 * 实现图书管理...

    java中的IO流操作大全

    Java流还可以分为节点流和过滤流。节点流是直接与文件或内存区域进行交互的流,而过滤流则是在节点流的基础上提供了额外的功能,如缓冲、字符编码转换等。 三、Java中IO流的操作示例 案例1:创建新文件 ```java ...

    java输入输出流与文件处理

    通过理解和掌握流的概念、基础流类以及流的操作流程,开发者可以更加高效地编写数据处理代码,无论是简单的文件读写,还是复杂的网络通信,都能够得心应手。此外,缓冲流的引入进一步优化了数据处理的性能,使得Java...

    Java语言基础教程-Java NIO流篇2

    本教程将深入讲解Java NIO中的流和通道概念,以帮助开发者更好地理解和利用这一强大的功能。 首先,我们要理解Java NIO的核心组件之一——流。在Java的IO体系中,流是数据传输的抽象,它代表了数据的流向,可以是...

    Java的IO流讲解代码: File 类、RandomAccessFile 类、字节流(文件字节流、缓冲字节流、基本数据类型

    此代码资源的目标是提供简单易懂的示例代码,帮助读者深入理解Java IO流的概念和使用方法。通过研究这些代码示例,读者将能够了解如何使用不同类型的IO类来进行文件读写、字符流、字节流、网络传输等各种常见的IO...

    Java输入输出(IO)和流的基本概念-Java教程共2页

    总结,Java输入输出(IO)和流的概念是Java程序员必须掌握的基础知识,理解和熟练使用各种流类和接口,能够有效地处理数据的读取和写入,实现程序与外部世界的高效交互。通过学习和实践,开发者可以构建出强大的文件...

    使用Java理解程序逻辑第2章.zip

    7. **输入/输出流**:虽然这不是第二章通常会深入的内容,但了解Java I/O流的概念可以帮助你理解数据的读取和写入。流提供了一种标准化的方式来处理不同类型的输入输出,包括文件、网络和内存。 8. **实践与案例...

    java IO流精讲 JAVA IO流实例开发

    Java IO流的精讲涵盖了许多概念和技术,包括流的分类、流的方向、缓冲区的使用、字符编码以及一些实用的IO类和方法。在实例开发中,我们将深入理解这些概念并应用到实际项目中。 首先,Java IO流按照数据传输的方向...

    Java实现字节流与图片的转化

    首先,我们需要理解字节流的概念。在Java中,字节流分为两种类型:输入字节流(InputStream)和输出字节流(OutputStream)。它们分别用于读取和写入数据。字节流通常用于处理非文本文件,因为这些文件由一系列字节...

    java核心技术(pdf)

    文件I/O流在Java中扮演着重要角色,不论是读取文本文件、处理二进制数据还是网络通信,都会涉及到流的概念。《Java核心技术》会涵盖输入输出流、缓冲流、对象流以及文件操作相关的API。 Java集合框架是另一个重要的...

    java IO流+socket源码 实现简单文本传输

    Java IO流和Socket是Java编程语言中用于处理输入输出和网络通信的重要概念。在这个示例中,我们将深入探讨如何利用Java IO流与Socket实现简单的文本传输。对于初学者来说,理解这两个概念及其交互是非常关键的。 ...

    java教程(易理解全面)

    13. 多维数组:理解二维数组和多维数组的表示与操作。 五、面向对象 14. 类与对象:理解类的定义、对象的创建和使用,以及封装的概念。 15. 继承:讲解单一继承和抽象类,以及super关键字的使用。 16. 多态:探讨...

    Java IO处理类的汇总

    本篇文章将全面解析Java IO处理类,包括基本概念、常用类库、流的概念及分类,以及实际编程中的应用。 首先,Java IO体系基于流(Stream)的概念,流是数据的序列,可以是从输入源读取或向输出目标写入。Java IO提供...

    java文件流学习实验

    2. **基本流类**:Java的基础文件流类有FileInputStream和FileOutputStream,它们分别用于读取和写入文件。例如,`new FileInputStream("file.txt")` 创建一个从指定文件读取数据的输入流,而`new FileOutputStream...

    使用Java理解程序逻辑

    2. **类与对象**:Java是面向对象的语言,"类"是对象的蓝图,"对象"则是类的实例。理解类的定义、对象的创建和使用,以及封装、继承和多态这三大面向对象特性,对于理解Java程序逻辑至关重要。 3. **数组与集合框架...

Global site tag (gtag.js) - Google Analytics