"Creating a good input/output system is a very difficult task for Language designers ."
Because there are a lot of different design schemes, the difficulty of this task is easily proved. The biggest challenge seems to be how to cover all possible factors. There are not only three different types of Io to consider (file, console, network connection ), in addition, you need to communicate with them in a large number of different ways (sequence, random access, binary, character, by line, by word, etc ).
Java library designers solve this problem by creating a large number of classes. In fact, Java's Io system uses so many classes that at the beginning it will feel like starting from nowhere (ironically, java Io is designed to avoid too many classes ). After the upgrade from Java 1.0 to Java 1.1, the design of the IO Library has also undergone significant changes. At this time, it is not simple to replace the old library with the new library. Sun's designers have extended the original library and added a lot of new content. Therefore, we sometimes have to mix the New and Old databases, resulting in helpless complexity.Code.
This chapter will help you understand the various Io classes in the standard Java library and learn how to use them. The first part of this chapter will introduce the "old" Java 1.0 Io stream library, because there is a lot of code still using that library. The rest of this chapter will introduce some new features of the Java 1.1 I/O library. Note that if you use the Java 1.1 compiler to compile part of the code described in the first part of this chapter, you may receive a "deprecated feature not recommended" warning message. The code can still be used. The Compiler only recommends that you replace the new features described later in this chapter. However, this is valuable because we can better understand the differences between the old and new methods, so as to deepen our understanding (and can smoothly read the code written for Java 1.0 ).
10.1 Input and Output
The I/O class of the Java library can be divided into two parts: input and output, which can be known when you use a web browser to read the online Java class documentation. Through inheritance, all classes derived from inputstream (input stream) have a basic method named read () to read a single byte or byte array. Similarly, all classes derived from outputstream have the basic write () method, which is used to write a single byte or byte array. However, we usually do not use these methods; they exist because more complex classes can use them to provide a more useful interface. Therefore, we seldom use a single class to create our own system objects. Generally, multiple objects are overlapped to provide the desired functions. We feel that the Java stream library is abnormal and complicated because multiple objects need to be created to create a single result stream.
It is necessary to classify classes by function. The Database Designer first decides that all input-related classes are inherited from inputstream, and all output-related classes are inherited from outputstream.
10.1.1 inputstream type
Inputstream is used to mark the classes that generate input from different sources. These origins include (each has a related inputstream subclass ):
(1) byte array
(2) String object
(3) File
(4) "Pipeline", which works in a similar way as the pipeline in real life: put some things at one end and they come out at the other end. (5) A series of other streams so that we can collect them in a single stream.
(6) other origins, such as Internet connections, will be described later in this book ).
In addition, filterinputstream is also a type of inputstream. It provides a basic class for the "destructor" class to connect attributes or useful interfaces with the input stream. This will be discussed later.
Bytearrayinputstream allows a buffer in the memory to be used as an inputstream to extract bytes from the buffer/as a data source. A useful interface can be provided by connecting the same filterinputstream object.
stringbufferinputstream converts a string to a string (string) of inputstream ). The basic implementation scheme actually uses a stringbuffer (string buffer)/as a data source. By connecting the same filterinputstream object, you can provide a useful interface
fileinputstream is used to read information from a file to represent a string of the file name, or a file or filedescriptor object/used as a data source. By connecting the same filterinputstream object, you can provide a useful interface
pipedinputstring to generate data written for the relevant pipedoutputstream. Pipedoutputstream/is used as a data source. By connecting the same filterinputstream object, you can provide a useful interface
sequenceinputstream converts two or more inputstream objects into a single inputstream using two inputstream objects or one enumeration, A container/used for inputstream objects as a data source. By connecting the same filterinputstream object, you can provide a useful interface
filterinputstream to abstract the classes used as the Destructor interface; the breaker provides useful functions for other inputstream classes. See Table 10.3. See table 10.3./See Table 10.3.
10.1.2 type of outputstream
the classes in this category determine where our input goes: A byte array (but no string; suppose we can create a byte array), a file, or a "Pipeline ".
In addition, filteroutputstream provides a basic class for the "Breaker" class, which connects attributes or useful interfaces with the output stream. This will be discussed later
bytearrayoutputstream creates a buffer in the memory. All the data we send to the stream is placed in this buffer zone. The initial size of the optional buffer/used to indicate the destination of the data. If you connect the object with filteroutputstream, you can provide a useful interface
fileoutputstream to send information to a file and use a string to represent the file name, or select a file or filedescriptor object/to indicate the data destination. If you connect it with the filteroutputstream object, you can provide a useful interface
any information we write to pipedoutputstream will automatically become the relevant pipedinputstream output. Pipedinputstream/specifies the destination of the data for multi-threaded processing/connects it with the filteroutputstream object, you can provide a useful interface
filteroutputstream to abstract the classes used as the Destructor interface. The Destructor provides useful functions for other outputstream classes. See Table 10.4. See table 10.4./See Table 10.4.
10.2 adding attributes and useful interfaces
the ability to dynamically and transparently add a single object using hierarchical objects is called a decorator) solution-the "solution" belongs to the topic in chapter 1 of this book (note ① ). The decorator scheme specifies that all objects encapsulated in the initialization object have the same interface to use the "Transparent" nature of the decorator-we send the same message to an object, whether it is "decorated" or not ". This is why the "filter" class exists in the Java Io Library: the abstract "filter" class is the basic class of all decorator (the decorator must have the same interface as the object it decorated, but the decorator can also expand the interface, this situation can be found in several special "filter" classes ).
when subclass processing requires a large number of subclasses to provide support for each possible combination, it is often used as a decoration device-because there are too many combinations, subclass processing becomes impractical. The Java Io Library requires many different feature combinations, which is the reason why the decorator solution is particularly useful. However, the decorator solution also has its own disadvantages. When we write a Program , the decorators provide us with much greater flexibility (because attributes can be easily mixed and matched), but they also make our code more complex. The reason is that the Java Io library is inconvenient to operate. We must create many classes-"core" I/O type plus all the decorators-to get a single Io object we want.
filterinputstream and filteroutputstream (these two names are not very intuitive) provide the corresponding modifier interface for controlling a specific input stream or output stream ). They are derived from inputstream and outputstream respectively. In addition, they all belong to abstract classes. Theoretically, they provide a common interface for different communication methods between a stream and a stream. In fact, filterinputstream and filteroutputstream simply imitate their own basic classes. They are the basic requirements of a decorator.
10.2.1 read data from inputstream through filterinputstream
The filterinputstream class has two completely different tasks. Datainputstream allows us to read different basic data types and string objects (all methods start with "read", such as readbyte () and readfloat ). With the corresponding dataoutputstream, we can use the data "stream" to move basic data types from one place to another. These "Places" are determined by the classes summarized in table 10.1. If you read data in a block and parse it yourself, you do not need to use datainputstream. However, in many other cases, we generally want to use it to automatically format the data we read.
The remaining classes are used to modify the inputstream's internal behavior mode: whether to buffer, whether to track the data rows you read, and whether to push back a character. The last two types seem to provide support for building a compiler (in other words, adding them to support building a Java compiler), so they are generally not needed in general programming.
You may buffer your input almost every time, no matter which Io device is connected. Therefore, the best practice of the IO library is to treat unbuffered input as a special case and accept buffered input as a standard practice.
The combination of datainputstream and dataoutputstream enables you to read the basic data types (INT, Char, long, and so on) of a stream in a mobile way. inputstream/contains a complete interface, to read Basic Data Types
Bufferedinputstream does not allow you to read more data physically every time. It is told that "Please first find in the buffer zone" inputstream. There is no available buffer size/it cannot provide an interface, only the request for using the buffer zone is sent. The same interface object must be connected together.
Linenumberinputstream traces the row numbers in the input stream. You can call getlinenumber () and setlinenumber (INT) to add only the ability to number data rows. Therefore, you may need to connect to the same real interface object.
Pushbackinputstream has a byte push buffer so that the last character inputstream/to be read by the push is usually used by the compiler in the scanner because it is required by the Java compiler. It is generally not used in your own code.
10.2.2 write data to outputstream through filteroutputstream
Dataoutputstream corresponds to datainputstream, which is used to format each basic data type and string object and put it into a data "stream, so that datainputstream on any machine can read them normally. All methods start with "wirte", such as writebyte () and writefloat.
If you want to perform some real formatted output, such as output to the console, use printstream. It can be used to print all basic data types and string objects, and a format that is easy to view. This is the opposite of dataoutputstream. The latter aims to put those data into a data stream so that datainputstream can easily reconstruct them. The system. Out static object is a printstream.
Two important methods in printstream are print () and println (). They have been overwritten to print out all data types. The difference between print () and println () is that the latter automatically adds a new row after the operation is completed.
Bufferedoutputstream is a "modifier" that instructs data streams to use buffering technology so that they do not have to write data physically to the stream each time. Generally, it should be applied to file processing and controller Io.