`
0428loveyu
  • 浏览: 30914 次
  • 性别: Icon_minigender_2
  • 来自: 西安
文章分类
社区版块
存档分类
最新评论

Java I/O (2): OutputStream分析

 
阅读更多

在Java I/O中,抽象类OutputStream是其他输出流类(如FileOutputStream)的基础类,分析一下这个类的源码很有必要。

概要

这个抽象类实现了两个接口:Closeable和Flushable。需要注意的是,在这个类的API中写到这个抽象了实现了三个接口,还包括AutoCloseable,这是因为Closeable接口继承了AutoCloseable接口的缘故。类定义如下:
public abstract class OutputStream implements Closeable, Flushable

该类使用默认的构造函数,没有参数。类中定义了1个抽象方法和4个具体方法,其中close()和flush()是对接口中方法的实现(其实是空实现,什么都没有)。方法声明分别如下:
    public abstract void write(int b) throws IOException;

    public void write(byte b[]) throws IOException

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

以上三个方法都是用于写出字节,具体请看下文。还有两个空实现的方法:
    public void flush() throws IOException {
    }
    public void close() throws IOException {
    }


方法分析

下面是对各个方法的分析,先来看write(int b)

write(int b)

这是唯一一个抽象方法,子类中必须提供具体的实现。这个方法写出一个字节到输出流中。仔细看一下参数,发现是int类型,也就是有4个字节,那是如何处理的呢?很容易想到,只取低8位,也就意味这int的取值范围为0-255.如果提供一个超出这个范围的参数,将自动把高的24位去掉,具体地说,如果给定一个b超出范围,则将b除以256取模。例如,传递一个256,实际上写入的是1。将这个值写入输出流之后,输出流的另一端如何解析,取决于另一端。例如,对于控制台,是将其转换成ASCII码输出。子类必须实现这一方法,例如,FileOutputStream就调用本地方法来实现这个方法。如果写出过程遇到错误,抛出IOException,例如说,这个流已经被关闭,则抛出这个异常。

write(byte b[], int off, int len)

上一个方法一次只写入一个字节,许多时候是不方便的,这个方法则写入data字节数组中的len字节到输出流。参数中data就是待写入的字节(不一定全部写入),从第offset位开始写入,off+len-1是最后一位被写入的。在OutputStream这个抽象类中,直接循环调用write(int b)方法,如下:
if (b == null) {
            throw new NullPointerException();
        } else if ((off < 0) || (off > b.length) || (len < 0) ||
                   ((off + len) > b.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]);
        }

API鼓励子类提供更高性能的实现方法。在这个方法中,由于字节本身就是8位,没有超出范围的情况,所以直接写入无需转换。如果传递的data为空,则抛出NullPointerException异常。如果被写入的部分超出字节数组范围,抛出IndexOutofBoundsException异常。具体地说,有以下5中情况,也就是if语句当中的5个表达式。除此之外,如果写入过程出现错误,一样抛出IOException。

write(byte b[])

这个方法其实是上一个方法的特殊情况,当off=0, len=data.length的时候,就得到了这个方法。具体实现也是通过调用上个方法来实现的,如下:
    public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

有一点需要说明,如果一次写入的字节数组太大,可能就会是性能下降,至于多少合适,得根据具体情况而定。比如写入网络的时候就需要小一点了,如128字节。写入文件的话,稍大一点1024.

flush()

这个方法是对Flushable接口的实现。如果在写出方法的具体实现中,用到了缓冲机制。则这个方法用于将缓冲区的数据“冲刷”到目的地去。这里需要特别理解,如果写出的方法用到了操作系统层的抽象,比如说FileOutputStream,那么该方法只能保证将缓冲区的数据提交给操作系统,但是不能保证数据被写入到磁盘文件中去。如果冲刷过程出现I/O 错误,抛出IOException。OutputStream类中的方法什么都不做。当输出流被关闭或者程序退出的时候,缓冲区的数据互自动被冲刷。对于System.out、System.err这样的输出流,当调用println()方法或者遇到换行符‘\n’的时候,缓冲数据自动被冲刷。当然,你可以在PrintStream的构造函数中传递参数来设置是否自动冲刷。

close()

这是对Closeable接口中close方法的实现。用于关闭输出流,释放相关的系统资源(如文件句柄(file handle)或者网络端口)。关闭之后,该输出流不能再被操作或者重新打开,否则抛出异常。当然,你可以不关闭,但是这不是一个好习惯,时常会出现严重问题。比如你打开文件,正在操作,那你不关闭,其他线程可能就会一直阻塞下去了。可以如下关闭输出流:
try {
  OutputStream out = new FileOutputStream("numbers.dat");
  // Write to the stream...
  out.close( );
}
catch (IOException ex) {
  System.err.println(ex);
}

当然,这样有个问题,如果在写入的时候出现异常,那么输出流的close方法就不会被执行,也就是不会被关闭。换一种方法;
OutputStream out = null;
try {
  out = new FileOutputStream("numbers.dat");
  // Write to the stream...
}
catch (IOException ex) {
  System.err.println(ex);
}
finally {
  if (out != null) {
    try {
      out.close( );
    }
    catch (IOException ex) {
      System.err.println(ex);
    }
  }
}

注意到,这里的OutputStream声明放到了try语句外面,必须这样才能被finally中的语句访问。这样写虽然安全,但是有点丑陋就是了。如果不处理异常而是抛出,那么可以这样:
OutputStream out == null;
try {
  out = new FileOutputStream("numbers.dat");
  // Write to the stream...
}
finally {
  if (out != null) out.close( );
}

一个自定义子类

OutputStream的子类至少要重写write(int b)方法,当然,有些子类把所有方法都重写或者重写一部分。下面这个例子写入的时候什么都不敢,只是提供一个实现参考:
完整代码如下:
import java.io.*;
public class NullOutputStream extends OutputStream {
  private boolean closed = false;
  public void write(int b) throws IOException {
    if (closed) throw new IOException("Write to closed stream");
  }
  public void write(byte[] data, int offset, int length)
   throws IOException {
    if (data == null) throw new NullPointerException("data is null");
    if (closed) throw new IOException("Write to closed stream");
  }
  public void close( ) {
    closed = true;
  }
}

这个类重写了出了flush以外的四个方法,因为写入方法什么都不干,当然没必须flush啦。实现很容易懂。不多说明。




分享到:
评论

相关推荐

    Java I/O, 2nd Edition

    2. **标准I/O库**:详细讲解了java.io包中的类和接口,如File类用于文件的创建、读写和删除,InputStream和OutputStream是所有字节流的基类,Reader和Writer则是字符流的基类。此外,还有ObjectInputStream和...

    java.lang.IllegalStateException: OutputStream already obtain

    标题 "java.lang.IllegalStateException: OutputStream already obtain" 涉及到的是Java编程中的一个常见错误,特别是当处理I/O流时。这个异常通常在尝试获取已经存在的OutputStream实例时抛出,表明该输出流已经被...

    Java I/O 过滤流-带格式的读写操作

    在Java编程语言中,输入/输出(I/O)是处理数据传输的核心部分。过滤流(Filter Stream)是Java I/O框架中的一个重要概念,它提供了一种优雅的方式来进行数据的读写操作,同时允许我们添加额外的功能,如字符编码...

    Java I/O, NIO and NIO.2

    Java I/O, NIO, 和 NIO.2 是Java平台中处理输入/输出操作的核心组件,对于任何Java开发者来说,理解和掌握这些概念至关重要。本文将深入探讨这些技术,旨在提供一个全面而详尽的概述。 Java I/O(Input/Output)是...

    深入分析 Java I/O 的工作机制(转载)

    以下是对Java I/O机制的详细分析: 1. **I/O 流的概念** Java中的I/O操作基于流的概念,流是数据的有序传输通道。Java将所有的I/O操作抽象为流对象,分为字节流和字符流两大类。字节流处理单个字节的数据,如...

    Java I/O编程 java

    Java I/O 编程是Java开发中的重要组成部分,主要用于处理数据的输入与输出。下面将详细阐述其中的关键概念和方法。 1. 数据流的概念及输入输出方法: 数据流是计算机中进行数据传输的通道,包括从外部设备到程序的...

    java I/O类的使用

    Java 1.0 和 1.1 中的I/O类主要是基于两个基础类:`InputStream`和`OutputStream`,它们处理字节流。`InputStream`家族包括了如`ByteArrayInputStream`、`FileInputStream`等,它们分别从字节数组或文件中读取数据。...

    java阻塞i/o与非阻塞i/o控制

    阻塞I/O模型是最常见的I/O模式,在Java中主要体现在`InputStream`和`OutputStream`等基本I/O类上。当一个线程调用read或write方法时,如果数据尚未准备好,那么这个线程会被挂起,即进入阻塞状态,直到数据准备就绪...

    java i/o 实例 编程 学习 教程 复习

    Java 的 I/O 主要分为两大类:`InputStream` 和 `OutputStream` 用于处理字节流,而 `Reader` 和 `Writer` 则用于处理字符流。 #### 二、Java I/O 示例代码详解 下面我们将对提供的代码片段进行逐段分析。 ##### ...

    Java I/O系统

    Java I/O系统是Java编程语言中的一个重要组成部分,它允许程序进行输入输出操作,与外部世界进行数据交互。在Java中,I/O系统基于流的概念,流可以被视为数据的流动渠道,既可以用来读取数据(输入流),也可以写入...

    java i/0习题

    Java I/O(输入/输出)是Java编程语言中不可或缺的一部分,它允许程序与外部资源进行数据交换,如文件、网络连接、系统硬件等。在Java I/O中,我们使用流(Stream)的概念来处理数据,流是数据传输的通道。本套习题...

    java对I/O流的处理

    Java中的I/O流处理是程序与外部设备交互数据的关键机制,包括从文件、网络、内存等数据源读取数据和向这些目标写入数据。I/O流系统在Java的`java.io`包中被实现,提供了丰富的类和接口来支持各种类型的流操作。 **I...

    Java I/O层次结构详解

    Java I/O层次结构详解 Java I/O系统是Java平台中不可或缺的一部分,它为开发者提供了处理输入和输出的强大工具。在Java中,I/O操作主要基于流(Stream)的概念,流可以被视为数据的有序序列,既可以代表从源读取...

    Java I/O总结

    ### Java I/O总结 #### 一、从`new BufferedReader(new InputStreamReader(conn.getInputStream()))`想到的 在Java编程中,处理输入输出(I/O)是一项常见的任务。这段代码`new BufferedReader(new ...

    Java I/O详细笔记

    ### Java I/O详细笔记 #### 一、流的分类 Java I/O系统主要涉及四种基本的流分类方式:根据数据流动的方向、数据处理的类型、流处理的源以及是否使用了缓冲机制。 **1. 按数据流动方向** - **输入流(Input ...

    java基础之I/O流

    Java中的I/O流是程序与外部数据交互的重要机制,它允许数据在程序、文件、网络等之间流动。I/O流分为两大类:字符流(Character Stream)和字节流(Byte Stream),每类又分为输入流(Input Stream)和输出流...

    探索Java I/O 模型的演进

    1. **传统Java I/O(基于流的I/O)**:Java早期的I/O库基于InputStream和OutputStream,这些类采用阻塞I/O模型,如FileInputStream和FileOutputStream。 2. **NIO(Non-blocking I/O)**:Java 1.4引入了NIO(New I/...

    怎么使用I/O编程???

    在Java编程中,I/O(Input/Output)处理是与外部世界交互的关键技术,涉及文件读写、网络通信等场景。I/O的核心思想是通过流(Stream)来传输数据,使得程序能从数据源读取数据或将数据写入目标。 **1.1 I/O简介** I...

    Java I/O 使用和最佳实践

    Java I/O系统是Java编程语言中的一个核心组成部分,它提供了处理输入输出操作的类和接口。这个系统的设计目的是为了使得应用程序能够与外部世界交互,包括读取和写入文件、网络数据、标准输入输出流等。在Java中,I/...

    java数据流 I/O系统

    Java的I/O系统是Java平台的核心特性之一,用于处理数据的输入输出操作。这个系统包括一系列的类和接口,主要用于字节和字符的传输,涵盖了从简单文件操作到复杂的网络通信。在Java中,I/O操作是通过数据流的概念来...

Global site tag (gtag.js) - Google Analytics