Flow Overview
In Java, a stream is an ordered sequence of bytes that can have any length. The flow from the application to the destination is called the output stream, from the destination to the application called the input stream.
Stream Genealogy of Java
Java's java.io package includes the entire stream family, and the lineage of the output stream and input stream is as follows:
InputStream and OutputStream
InputStream and OutputStream are the top-level abstract parent classes of the input and output streams, and only some abstract methods are defined for subclasses to implement.
In the output stream OutputStream, if you need to write data to an output stream, you can call the void write (int b) method, which will write the low eight bits of B to the stream, and the high 24 bits will be automatically ignored. If you want to write data in bulk, you can call the void write (byte[] b) method to write the contents of a byte array to the stream, and void write (byte[] b, int off, int len) lets you specify how much data to write from.
If you want the data that you write to the stream to be delivered to your destination as quickly as possible, such as a file, you can call the flush () method to brush the current output stream into a buffer at the operating system level after writing the data. However, it is important to note that this method does not guarantee that the data will be immediately brushed to the actual physical destination (for example, storage).
After the stream is used, its close () method should be called to close the stream, and when the stream is closed, it is first flush and then closed.
In the input stream InputStream, a byte can be read from the stream through the int read () method, and the bulk read byte can be achieved via int read (byte[] b) or int read (byte[] b, int off, int len), both The return value of a method is the number of bytes actually read. If you need to read a piece of data in the stream repeatedly, you can use the void mark (int readlimit) method to make a tick at the current position before reading, and then return to the previously marked position by using the Void Reset () method, but before you do these tag operations, you need to pass the The Boolean marksupported () method to determine whether the stream supports markup. If you are not interested in some predictable data, you can use long (long N) to tune some of the data in some streams.
When you are finished using a stream, either the input or the output stream, the close () method is called to turn it off.
Adorner mode
Adorner mode (Decorator pattern) allows you to add new functionality to an existing object without changing its structure. This type of design pattern belongs to the structural pattern, which is a wrapper as an existing class.
This design pattern creates an adornment class that wraps the original class and provides additional functionality, while preserving the integrity of the class method signature.
Take InputStream as an example, it is an abstract class:
Public abstract class InputStream implements Closeable {... ...}
and define an abstract method
public abstract int Read () throws IOException;
The abstract method is implemented by a specific subclass, which can be seen through InputStream's genealogy map, which directly inherits the InputStream, and the subclasses that provide a particular function are:
- Bytearrayinputstream
- FileInputStream
- ObjectInputStream
- PipedInputStream
- Sequenceinputstream
- StringBufferInputStream
These subclasses have specific functions, for example, FileInputStream represents a file input stream and provides the ability to read the contents of a file, ObjectInputStream provides the ability to deserialize objects.
InputStream this abstract class has a subclass that is very different from the other subclasses above, and this subclass is FilterInputStream , see the InputStream genealogy in.
Turning on the FilterInputStream code, we can see that it maintains a InputStream member object internally, and all of its methods are called methods that call this member object with the same name. In other words, filterinputstream it does nothing. is to delegate the invocation to an internal InputStream member object. As shown below:
public class FilterInputStream extends InputStream { protected volatile inputstream in; Protected FilterInputStream (InputStream in) { this.in = in; } public int read () throws IOException { return in.read (); } public int read (byte b[]) throws IOException { return read (b, 0, b.length); } public int read (byte b[], int off, int len) throws IOException { return in.read (b, off, Len); } Public long Skip (long N) throws IOException { return In.skip (n); } public int available () throws IOException { return in.available (); } public void Close () throws IOException { in.close (); } Public synchronized void mark (int readlimit) { in.mark (readlimit); } Public synchronized void Reset () throws IOException { in.reset (); } public boolean marksupported () { return in.marksupported (); }
FilterInputStream also has its sub-categories, namely:
- Bufferedinputstream
- DataInputStream
- Linenumberinputstream
- Pushbackinputstream
Although the above code to see FilterInputStream did not do anything with the eggs, but its subclasses can be different, take bufferedinputstream as an example, this class provides the ability to read data in advance, which is the function of buffering. You can take a look at its Read method:
public synchronized int read () throws IOException { if (pos >= count) { fill (); if (POS >= count) return-1; } return Getbufifopen () [pos++] & 0xFF; }
As you can see, when Pos>=count, it is time to buffer some data beforehand, then call Fill () to fill the buffer so that subsequent reads. Because this article only discusses the adorner pattern for IO streams, there is no discussion about the specifics of the implementation, such as this article does not discuss how the fill () method is implemented, which can be used as a black box first.
From here you can begin to feel that Bufferedinputstream is a decorator, which can add buffering functionality to a inputstream that does not have a buffering function.
For example, we use FileInputStream, it does not have buffering function, each time we call read, we will call the operating system to request data. If we decorate it through bufferedinputstream, then each call to read will advance the operating system to get some more data, thus unknowingly improve the performance of the program. As shown in the following code:
Bufferedinputstream bis = new Bufferedinputstream (new FileInputStream (New File ("/home/user/abc.txt"));
Similarly, for other FilterInputStream subclasses, its role is the same, that is to decorate a inputstream, for it to add the functionality it does not have. OutputStream and the family's embodiment of the adorner pattern, and so on.
The design of the IO stream in the JDK is a classic demonstration of the adorner pattern in design mode, and if you find it carefully, there are many other design patterns in the JDK, such as listener patterns and so on.
Application of Java IO Stream and adorner mode on it