Input and Output - Source and Destination

Java IO解决的问题是从一个source读取数据,和写数据到一个destination。在Java中常见的source和destination有下面几种:

  • Files
  • Pipes
  • Network Connections
  • System.in, System.out, System.error
  • In-memory Buffers (e.g. arrays)

这里面除了In-memory Buffers比较特殊,其它的(文件、管道、socket、设备等)其实在操作系统层面已经统一抽象为文件。

从source读取数据,写数据到destination是非常常见的需求:

java-io-source-and-destination-with-stream

对于上面的应用场景,Java提供引入流(Stream)的抽象,让从source中读写数据变得简单和统一:

Reading

open a stream
while more information
    read information
close the stream

Writing

open a stream
while more information
    write information
close the stream

其中根据读写数据的特点,又区分为二进制流(字节流)和文本流(字符流):

  • 二进制流:字节流, 8-bit bytes; InputStream, OutputStream.
  • 文本流:字符流, 16-bit characters; Reader, Writer.

java-io-source-and-destination-with-stream

Overview of the stream hierarchy

  • Reader, root in unicode input hierarchy
  • Writer, root in unicode output hierarchy
  • InputStream, root in binary input hierarchy
  • OutputStream, root in binary output hierarchy

所以上面的这两个Reading和Writing的步骤,使用Java IO就是如下的历程:

public class CopyBytes {
    public static void main(String[] args) throws IOException {

        FileInputStream in = null;
        FileOutputStream out = null;

        try {
            in = new FileInputStream("xanadu.txt");
            out = new FileOutputStream("outagain.txt");
            int c;

            while ((c = in.read()) != -1) {
                out.write(c);
            }
        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}


public class CopyCharacters {
    public static void main(String[] args) throws IOException {

        FileReader inputStream = null;
        FileWriter outputStream = null;

        try {
            inputStream = new FileReader("xanadu.txt");
            outputStream = new FileWriter("characteroutput.txt");

            int c;
            while ((c = inputStream.read()) != -1) {
                outputStream.write(c);
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
        }
    }
}

java-io-reader-writer-api

The class hierarchies for readers and writers in java.io. Subclasses of Reader and Writer implement specialized streams and are divided into two categories: those that read from or write to data sinks (shaded) and those that perform some sort of processing (unshaded).

java-io-inputstream-and-outputstream-api

The class hierarchies for inputstreams and outputstreams in java.io. Subclasses of Inputstream and OutputStream implement specialized streams and are divided into two categories: those that read from or write to data sinks (shaded) and those that perform some sort of processing (unshaded). LineNumberInputStream is deprecated; use LineNumberReader instead.

TIPS InputStream或者Reader 是连接source的桥梁。OutputStream或者Writer是连接destination的桥梁。

Character Streams that Use Byte Streams

因为其实chars就是bytes的编码,所以Character Streams其实底层就是使用了Byte Streams来读取数据,然后按照指定的编码(如果应用没有指定那么就是file.encoding值)进行byte-to-character转换。所以Reader或者Writer的构造函数往往就是接收一个InputStream或者OutputStream,以及对应的编码(character)。如:

Reader reader = new InputStreamReader(inputStream, "UTF-8");
Writer writer = new OutputStreamWriter(outputStream, "UTF-8");

Character streams are often “wrappers” for byte streams. The character stream uses the byte stream to perform the physical I/O, while the character stream handles translation between characters and bytes. FileReader, for example, uses FileInputStream, while FileWriter uses FileOutputStream. There are two general-purpose byte-to-character “bridge” streams: InputStreamReader and OutputStreamWriter. Use them to create character streams when there are no prepackaged character stream classes that meet your needs.

Decorator Pattern and Java IO

Two issues with I/O

  • What are you talking to (n).
  • The way you are talking to it (m).

Solution no. 1

  • Make a class for every combination
  • n * m classes, not flexible, hard to extend

Solutions no. 2

  • Java filter streams (decorators) are added dynamically to create the functionality needed.
  • n + m classes
  • Input decorator: FilterInputStream
    • DataInputStream: Full interface for reading built-in types
    • BufferedInputStream: Adds buffering to the stream (do this by default)
    • LineNumberInputStream: Only adds line numbers
    • PushbackInputStream: One-character push pack for scanners (lexers)
  • Output decorator: FilterOutputStream
    • DataOutputStream: Full interface for writing built-in types
    • PrintStream: Allows primitive formatting of data for display (not printf!)
    • BufferedOutputStream: Adds buffering to output (do this by default!)

应用的比较多的是BufferedInputStream/BufferedReader和BufferdOutputStream/BufferedWriter:

Reader reader = new BufferedReader(new FileReader(...));
Writer writer = new BufferedWriter(new FileWriter(...));


import java.io.*; // [Source: java.sun.com]

public class DataIODemo {
	public static void main(String[] args) throws IOException {
		// where to write to
		DataOutputStream out = new DataOutputStream(
			new FileOutputStream("invoice1.txt"));

		// alternative also using a buffer decorator
		DataOutputStream out = new DataOutputStream(
			new BufferedOutputStream(
				new FileOutputStream("invoice1.txt")));
	}
}

Java IO的设计目标和类体系结构

Java IO的设计目标如下:

  • File Access
  • Network Access
  • Internal Memory Buffer Access
  • Inter-Thread Communication (Pipes)
  • Buffering
  • Filtering
  • Parsing
  • Reading and Writing Text (Readers / Writers)
  • Reading and Writing Primitive Data (long, int etc.)
  • Reading and Writing Objects

这些目标自然而然的决定了Java现有的IO类体系结构:

java-io-class-overview

参考文章

  1. Basic I/O
  2. Java IO Tutorial
  3. The Java I/O System