Stream

Previous Page Next Page

Chapter 1. Streams and Files

In this chapter, we cover the Java application programming interfaces (APIs) for input and output.

 在这一章里,我们想要为了进行I/O,覆盖了Java应用程序接口(APIs)。

 

You will learn how to access files and directories and how to read and write data in binary and text format.

你将会学到访问文件和目录以及如何读写二进制格式和文本格式的数据。

 

 This chapter also shows you the object serialization mechanism that lets you store objects as easily as you can store text or numeric data.

本章还介绍了对象序列化机制,可以让你觉得就像存储文本或者是数字一样存储对象。

 

 Next, we turn to several improvements that were made in the "new I/O" package java.nio, introduced in Java SE 1.4.

接下来,我们来看看那些在“new I / O”的包java.nio中介绍了在Java SE的1.4作了一些改进。

 

 We finish the chapter with a discussion of regular expressions, even though they are not actually related to streams and files.

即使他们实际上并不涉及流和文件,我们完成了一个正则表达式的讨论。

 

We couldn't find a better place to handle that topic, and apparently neither could the Java team—the regular expression API specification was attached to the specification request for the "new I/O" features of Java SE 1.4.

我们无法找到一个更好的地方来处理这一议题,显然也不能在Java team---正则表达式API规范是附设在Java SE 1.4“new I / O”上的。

Streams

In the Java API, an object from which we can read a sequence of bytes is called an input stream.

在Java的API里面,一个能够读一个字符序列的对象,称作输入流。

 

An object to which we can write a sequence of bytes is called an output stream.

一个能够写一个字符序列的对象,称作输出流。

These sources and destinations of byte sequences can be—and often are—files, but they can also be network connections and even blocks of memory. The abstract classes InputStream and OutputStream form the basis for a hierarchy of input/output (I/O) classes.

这些字节序列的来源和目的地可以而且往往是文件,但它们也可以是网络连接,甚至内存块。抽象类InputStream和OutputStream的形成一个输入/输出(I / O)的类的层次结构的基础。

 

Because byte-oriented streams are inconvenient for processing information stored in Unicode (recall that Unicode uses multiple bytes per character), there is a separate hierarchy of classes for processing Unicode characters that inherit from the abstract Reader and Writer classes. These classes have read and write operations that are based on two-byte Unicode code units rather than on single-byte characters.

Reading and Writing Bytes

The InputStream class has an abstract method:

abstract int read()

This method reads one byte and returns the byte that was read, or -1 if it encounters the end of the input source. The designer of a concrete input stream class overrides this method to provide useful functionality. For example, in the FileInputStream class, this method reads one byte from a file. System.in is a predefined object of a subclass of InputStream that allows you to read information from the keyboard.

The InputStream class also has nonabstract methods to read an array of bytes or to skip a number of bytes. These methods call the abstract read method, so subclasses need to override only one method.

Similarly, the OutputStream class defines the abstract method

abstract void write(int b)

which writes one byte to an output location.

Both the read and write methods block until the bytes are actually read or written. This means that if the stream cannot immediately be accessed (usually because of a busy network connection), the current thread blocks. This gives other threads the chance to do useful work while the method is waiting for the stream to again become available.

The available method lets you check the number of bytes that are currently available for reading. This means a fragment like the following is unlikely to block:

int bytesAvailable = in.available();
if (bytesAvailable > 0)
{
   byte[] data = new byte[bytesAvailable];
   in.read(data);
}

When you have finished reading or writing to a stream, close it by calling the close method. This call frees up operating system resources that are in limited supply. If an application opens too many streams without closing them, system resources can become depleted. Closing an output stream also flushes the buffer used for the output stream: any characters that were temporarily placed in a buffer so that they could be delivered as a larger packet are sent off. In particular, if you do not close a file, the last packet of bytes might never be delivered. You can also manually flush the output with the flush method.

Even if a stream class provides concrete methods to work with the raw read and write functions, application programmers rarely use them. The data that you are interested in probably contain numbers, strings, and objects, not raw bytes.

Java gives you many stream classes derived from the basic InputStream and OutputStream classes that let you work with data in the forms that you usually use rather than at the byte level.

java.io.InputStream 1.0

  • abstract int read()

    reads a byte of data and returns the byte read. The read method returns a -1 at the end of the stream.

  • int read(byte[] b)

    reads into an array of bytes and returns the actual number of bytes read, or -1 at the end of the stream. The read method reads at most b.length bytes.

  • int read(byte[] b, int off, int len)

    reads into an array of bytes. The read method returns the actual number of bytes read, or -1 at the end of the stream.

    Parameters:bThe array into which the data is read
     offThe offset into b where the first bytes should be placed
     lenThe maximum number of bytes to read

  • long skip(long n)

    skips n bytes in the input stream. Returns the actual number of bytes skipped (which may be less than n if the end of the stream was encountered).

  • int available()

    returns the number of bytes available without blocking. (Recall that blocking means that the current thread loses its turn.)

  • void close()

    closes the input stream.

  • void mark(int readlimit)

    puts a marker at the current position in the input stream. (Not all streams support this feature.) If more than readlimit bytes have been read from the input stream, then the stream is allowed to forget the marker.

  • void reset()

    returns to the last marker. Subsequent calls to read reread the bytes. If there is no current marker, then the stream is not reset.

  • boolean markSupported()

    returns true if the stream supports marking.

 

java.io.OutputStream 1.0

  • abstract void write(int n)

    writes a byte of data.

  • void write(byte[] b)

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

    writes all bytes or a range of bytes in the array b.

    Parameters:bThe array from which to write the data
     offThe offset into b to the first byte that will be written
     lenThe number of bytes to write

  • void close()

    flushes and closes the output stream.

  • void flush()

    flushes the output stream; that is, sends any buffered data to its destination.


The Complete Stream Zoo

Unlike C, which gets by just fine with a single type FILE*, Java has a whole zoo of more than 60 (!) different stream types (see Figures 1-1 and 1-2).

 


 

 

Let us divide the animals in the stream class zoo by how they are used. There are separate hierarchies for classes that process bytes and characters. As you saw, the InputStream and OutputStream classes let you read and write individual bytes and arrays of bytes. These classes form the basis of the hiearchy shown in Figure 1-1. To read and write strings and numbers, you need more capable subclasses. For example, DataInputStream and DataOutputStream let you read and write all the primitive Java types in binary format. Finally, there are streams that do useful stuff; for example, the ZipInputStream and ZipOutputStream that let you read and write files in the familiar ZIP compression format.

For Unicode text, on the other hand, you use subclasses of the abstract classes Reader and Writer (see Figure 1-2). The basic methods of the Reader and Writer classes are similar to the ones for InputStream and OutputStream.

abstract int read()
abstract void write(int c)

The read method returns either a Unicode code unit (as an integer between 0 and 65535) or -1 when you have reached the end of the file. The write method is called with a Unicode code unit. (See Volume I, Chapter 3 for a discussion of Unicode code units.)

Java SE 5.0 introduced four additional interfaces: Closeable, Flushable, Readable, and Appendable (see Figure 1-3). The first two interfaces are very simple, with methods

void close() throws IOException

and

void flush()

respectively. The classes InputStream, OutputStream, Reader, and Writer all implement the Closeable interface. OutputStream and Writer implement the Flushable interface.

 

 

 

The Readable interface has a single method

int read(CharBuffer cb)

The CharBuffer class has methods for sequential and random read/write access. It represents an in-memory buffer or a memory-mapped file. (See "The Buffer Data Structure" on page 72 for details.)

The Appendable interface has two methods for appending single characters and character sequences:

Appendable append(char c)
Appendable append(CharSequence s)

The CharSequence interface describes basic properties of a sequence of char values. It is implemented by String, CharBuffer, StringBuilder, and StringBuffer.

Of the stream zoo classes, only Writer implements Appendable.

 

java.io.Closeable 5.0

  • void close()

    closes this Closeable. This method may throw an IOException.


java.io.Flushable 5.0

  • void flush()

    flushes this Flushable.


java.lang.Readable 5.0

  • int read(CharBuffer cb)

    attempts to read as many char values into cb as it can hold. Returns the number of values read, or -1 if no further values are available from this Readable.


java.lang.Appendable 5.0

  • Appendable append(char c)

  • Appendable append(CharSequence cs)

    appends the given code unit, or all code units in the given sequence, to this Appendable; returns this.

 

java.lang.CharSequence 1.4

  • char charAt(int index)

    returns the code unit at the given index.

  • int length()

    returns the number of code units in this sequence.

  • CharSequence subSequence(int startIndex, int endIndex)

    returns a CharSequence consisting of the code units stored at index startIndex to endIndex - 1.

  • String toString()

    returns a string consisting of the code units of this sequence.


Combining Stream Filters

FileInputStream and FileOutputStream give you input and output streams attached to a disk file. You give the file name or full path name of the file in the constructor. For example,

FileInputStream fin = new FileInputStream("employee.dat");

looks in the user directory for a file named "employee.dat".

Tip

Because all the classes in java.io interpret relative path names as starting with the user's working directory, you may want to know this directory. You can get at this information by a call to System.getProperty("user.dir").

Like the abstract InputStream and OutputStream classes, these classes support only reading and writing on the byte level. That is, we can only read bytes and byte arrays from the object fin.

byte b = (byte) fin.read();

As you will see in the next section, if we just had a DataInputStream, then we could read numeric types:

DataInputStream din = . . .;
double s = din.readDouble();

But just as the FileInputStream has no methods to read numeric types, the DataInputStream has no method to get data from a file.

Java uses a clever mechanism to separate two kinds of responsibilities. Some streams (such as the FileInputStream and the input stream returned by the openStream method of the URL class) can retrieve bytes from files and other more exotic locations. Other streams (such as the DataInputStream and the PrintWriter) can assemble bytes into more useful data types. The Java programmer has to combine the two. For example, to be able to read numbers from a file, first create a FileInputStream and then pass it to the constructor of a DataInputStream.

FileInputStream fin = new FileInputStream("employee.dat");
DataInputStream din = new DataInputStream(fin);
double s = din.readDouble();

If you look at Figure 1-1 again, you can see the classes FilterInputStream and FilterOutputStream. The subclasses of these files are used to add capabilities to raw byte streams.

You can add multiple capabilities by nesting the filters. For example, by default, streams are not buffered. That is, every call to read asks the operating system to dole out yet another byte. It is more efficient to request blocks of data instead and put them in a buffer. If you want buffering and the data input methods for a file, you need to use the following rather monstrous sequence of constructors:

DataInputStream din = new DataInputStream(
   new BufferedInputStream(
      new FileInputStream("employee.dat")));

Notice that we put the DataInputStream last in the chain of constructors because we want to use the DataInputStream methods, and we want them to use the buffered read method.

Sometimes you'll need to keep track of the intermediate streams when chaining them together. For example, when reading input, you often need to peek at the next byte to see if it is the value that you expect. Java provides the PushbackInputStream for this purpose.

PushbackInputStream pbin = new PushbackInputStream(
   new BufferedInputStream(
      new FileInputStream("employee.dat")));

Now you can speculatively read the next byte

int b = pbin.read();

and throw it back if it isn't what you wanted.

if (b != '<') pbin.unread(b);

But reading and unreading are the only methods that apply to the pushback input stream. If you want to look ahead and also read numbers, then you need both a pushback input stream and a data input stream reference.

DataInputStream din = new DataInputStream(
   pbin = new PushbackInputStream(
      new BufferedInputStream(
         new FileInputStream("employee.dat"))));

Of course, in the stream libraries of other programming languages, niceties such as buffering and lookahead are automatically taken care of, so it is a bit of a hassle in Java that one has to resort to combining stream filters in these cases. But the ability to mix and match filter classes to construct truly useful sequences of streams does give you an immense amount of flexibility. For example, you can read numbers from a compressed ZIP file by using the following sequence of streams (see Figure 1-4):

ZipInputStream zin = new ZipInputStream(new FileInputStream("employee.zip"));
DataInputStream din = new DataInputStream(zin);

					  

 

 

 

(See "ZIP Archives" on page 32 for more on Java's ability to handle ZIP files.)

 

java.io.FileInputStream 1.0

  • FileInputStream(String name)

  • FileInputStream(File file)

    creates a new file input stream, using the file whose path name is specified by the name string or the file object. (The File class is described at the end of this chapter.) Path names that are not absolute are resolved relative to the working directory that was set when the VM started.


java.io.FileOutputStream 1.0

  • FileOutputStream(String name)

  • FileOutputStream(String name, boolean append)

  • FileOutputStream(File file)

  • FileOutputStream(File file, boolean append)

    creates a new file output stream specified by the name string or the file object. (The File class is described at the end of this chapter.) If the append parameter is true, then data are added at the end of the file. An existing file with the same name will not be deleted. Otherwise, this method deletes any existing file with the same name.


java.io.BufferedInputStream 1.0

  • BufferedInputStream(InputStream in)

    creates a buffered stream. A buffered input stream reads characters from a stream without causing a device access every time. When the buffer is empty, a new block of data is read into the buffer.

 

java.io.BufferedOutputStream 1.0

  • BufferedOutputStream(OutputStream out)

    creates a buffered stream. A buffered output stream collects characters to be written without causing a device access every time. When the buffer fills up or when the stream is flushed, the data are written.


java.io.PushbackInputStream 1.0

  • PushbackInputStream(InputStream in)

  • PushbackInputStream(InputStream in, int size)

    constructs a stream with one-byte lookahead or a pushback buffer of specified size.

  • void unread(int b)

    pushes back a byte, which is retrieved again by the next call to read.

    Parameters:bThe byte to be read again

Previous Page Next Page

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值