`

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流讲解代码: File 类、RandomAccessFile 类、字节流(文件字节流、缓冲字节流、基本数据类型

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

    java中的语法理解

    java中的基础,每个概念对应一个实例。。易理解绝对对你有好处。

    (超赞)JAVA精华之--深入JAVA API

    1.2 深入理解嵌套类和内部类 1.2.1 什么是嵌套类及内部类? 1.2.2 静态嵌套类 1.2.3 在外部类中定义内部类 1.2.4 在方法中定义内部类 1.2.5 匿名内部类 1.2.6 内部类使用的其它的问题 1.3 文件和流 1.3.1 什么是数据...

    JAVA高级程序设计测试题含答案.docx

    示例 第2题 【单选题】【2.00分】【概念理解】 java.io包的File类是 [单选题] * A. 字符流类 B. 字节流类 C. 对象流类 D. 非流类(正确答案) E. 示例 JAVA高级程序设计测试题含答案全文共43页,当前为第1页。第3题 ...

    JAVA 流 的ppt课件

    关于流的PPT课件理解流的概念及分类 掌握常用流的体系结构 运用字节流读写文本文件 运用字符流读写文本文件 使用文件类操作文件及目录

    Java面试题合集最新版2024.zip

    面向对象编程:深入理解Java中的类、对象、继承、封装和多态等概念。 异常处理:了解try-catch-finally块的使用,以及自定义异常的处理。 二、Java进阶知识 集合框架:熟悉Java集合框架中的List、Set、Map等接口...

    java应用与开发 Java实验,综合应用 运用Java语言的各方面知识解决实际问题,提高综合编程能力

    用途: 学习Java中的类的概念和使用,深入理解面向对象编程。 实验三: 关键词: 异常处理机制与数据流 内容关键词: 异常处理,数据流,Java实验 用途: 掌握Java中的异常处理机制和数据流操作,提高代码的稳定性和可靠...

    《Java基础入门(第3版)》(Java):课后答案-docx文档

    (3)学习Java的核心概念:理解Java的核心概念,如面向对象编程、类、对象、继承、多态等,是学好Java的基础。 (4)学习常用的Java库和框架:了解Java的常用库和框架,如集合、I/O流、多线程等,可以帮助你更好地...

    Java开发详解.zip

    010201_【第2章:简单Java程序】_简单Java程序笔记.pdf 010301_【第3章:Java基础程序设计】_Java数据类型笔记.pdf 010302_【第3章:Java基础程序设计】_运算符、表达式与语句笔记.pdf 010303_【第3章:Java基础程序...

    JAVA SE学习精华集锦

    1.2 深入理解嵌套类和内部类 47 1.2.1 什么是嵌套类及内部类? 47 1.2.2 静态嵌套类 48 1.2.3 在外部类中定义内部类 48 1.2.4 在方法中定义内部类 49 1.2.5 匿名内部类 49 1.2.6 内部类使用的其它的问题 50 1.3 文件...

    AIC的Java课程1-6章

    第12章 IO与串行化 2课时  了解Java IO 中类的层次结构,介绍Java IO采用的装饰器模式。  学会使用File,FileReader,BufferedReader,FileWriter,BufferedWriter,PrintWriter等类输入...

    JAVA基础课程讲义

    Java中IO流类的体系 149 四个IO基本抽象类 150 InputStream 150 OutputStream 150 常用InputStream和OutputStream子类用法 150 FileInputStream和FileOutputStream 150 ByteArrayInutStream和ByteArrayOutputStream ...

    java基础案例与开发详解案例源码全

    5.4.1 对象封装的概念理解124 5.4.2 类的理解125 5.4.3 Java类模板创建125 5.4.4 Java中对象的创建和使用127 5.5 属性130 5.5.1 属性的定义130 5.5.2 变量131 5.6 方法132 5.6.1 方法的定义132 5.6.2 构造方法135 ...

    Java入门1·2·3:一个老鸟的Java学习心得.PART3(共3个)

    1.4.1 类(Class):Java世界中一类物体 14 1.4.2 方法(Method):物体的功能 15 1.4.3 main()方法:所有Java程序执行的起点 15 .1.5 名词解释 16 1.5.1 JDK和Java平台 16 1.5.2 Java编译器(Java Compiler)...

    JAVA入门1.2.3:一个老鸟的JAVA学习心得 PART1(共3个)

    1.4.1 类(Class):Java世界中一类物体 14 1.4.2 方法(Method):物体的功能 15 1.4.3 main()方法:所有Java程序执行的起点 15 .1.5 名词解释 16 1.5.1 JDK和Java平台 16 1.5.2 Java编译器(Java Compiler)...

    疯狂JAVA讲义

    1.1 Java语言的发展简史 2 1.2 Java的竞争对手及各自优势 4 1.2.1 C#简介和优势 4 1.2.2 Ruby简介和优势 4 1.2.3 Python的简介和优势 5 1.3 Java程序运行机制 5 1.3.1 高级语言的运行机制 6 1.3.2 Java程序的...

    java io包课件

    理解流,理解输入/输出流的概念 运用FileInputStream类和FileOutputStream类读/写字节文件 运用FileReader类和FileWriter类配合BufferedReader类和BufferedWriter类读/写字符文件 使用BufferedReader类从控制台接受...

    JAVA程序设计教程第2版.pdf

    相关概念及知识点都辅以相应的实例,通俗易懂,便于理解掌握面向对象的编程思想。  实用与流行。涵盖了Java开发过程中重要的及流行的方法和技巧,讲解细致,环环相扣。  教学与互动。文字叙述注重可读性,知识组织...

    Java精华学习资料

    深入理解嵌套类和内部类 文件和流 java中的一些常用词汇 J2SE学习中的30个基本概念 Java线程 Java 5.0多线程编程 Java Socket编程 Java的内存泄漏 抽象类与接口的区别 Java变量类型间的相互转换 ……

Global site tag (gtag.js) - Google Analytics