"For language designers, create a good input / output system is a particularly difficult task."
Due to a large number of different design, the difficulty of this task is very proven. The biggest challenge seems to be how to overwrite all possible factors. Not only do three different types of IO need to consider (files, consisters, network connections), but also communicate with them through a large number of different ways (order, random access, binary, characters, line, feature, etc.).
The Java library designer overcomes this problem by creating a large number of classes. In fact, Java's IO system uses so many classes, so that you will have a feeling that you don't know where to start (ironically, the Java's IO design is actually avoiding too much class). After upgrading from Java 1.0 to Java 1.1, the design of the IO library has also changed significantly. At this time, it is not simply replaced with new libraries. Sun's designers have made large-handed extensions to the original library, adding a lot of new content. Therefore, we have to mix the use of new libraries and old libraries to create a helpless complex 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 "old" Java 1.0 IO library, because there is a large number of code still use that library. The remaining parts of this chapter will introduce some new features of the Java 1.1 IO library. Note If you use the Java 1.1 compiler to compile part of the code introduced in this chapter, you may get a "DEPRED FEATURE" warning message. The code is still available; the compiler is only suggesting that we have some new features to be described later in this chapter. But we do this is valuable, because you can understand some differences between old methods and new methods, deepen our understanding (and read code written as Java 1.0).
10.1 input and output
You can split the IO class of the Java library into two parts of the input and output, you can know when you read the online Java class document with a web browser. By inheriting, all classes derived from inputStream have a basic approach called read () to read a single byte or byte array. Similarly, all classes derived from OutputStream have basic methods Write () for writing a single byte or byte array. However, we usually do not use these methods; they exist because more complex classes can take advantage of them to provide a more useful interface. Therefore, we rarely create their own system objects with a single class. Under normal circumstances, we all overlap multiple objects together and provide their own expectations. We feel that the stream library of Java is extremely complex, which is because in order to create a single result stream, you need to create multiple objects.
It is necessary to classify the class according to the function. The instrument of the library first decides all classes related to the input from inputStream, while all classes related to the output are inherited from OutputStream.
10.1.1 INPUTSTREAM Type
The role of InputStream is to mark classes that generate input from different origins. These origins include (each having a related inputStream subclass):
(1) byte arrays
(2) String object
(3) file
(4) "Pipeline", its working principle is similar to the pipeline in real life: put some things into one end, they come out on the other end. (5) A series of other streams so that we will collect them in a single stream.
(6) Other origins, such as Internet connections, etc. (will be described later in the book).
In addition, FilterInputStream also belongs to a type of InputStream that provides a basic class with it to "Destroyer" classes to connect attributes or useful interfaces together with input streams. This will be discussed later. Table 10.1 INPUTSTREAM type
Class Function Builder Parameters / How to Use
ByteArrayInputStream allows a buffer in memory as an InputStream to use buffers / as a data source to extract bytes as a data source. Provide a useful interface by connecting its same FilterInputStream object
StringBufferInputStream converts a String into an InputStream a string (string). The foundation implementation actually uses a StringBuffer (string buffer) / as a data source. Provide a useful interface by connecting its same FilterInputStream object
FILEINPUTSTREAM is used to read a String from the file name representative file name, or a File or FileDescriptor object / as a data source. Provide a useful interface by connecting its same FilterInputStream object
PiPedInputString generates data written by the associated PiPedoutputStream. The concept of "Piping" is implemented PipedputStream / as a data source. Provide a useful interface by connecting its same FilterInputStream object
SequenceInputStream converts two or more InputStream objects into a single InputStream using two inputStream objects or an enumeration, using a container / as a data source for the InputStream object. Provide a useful interface by connecting its same FilterInputStream object
FilterInputStream abstracts the class that is used as a destroyer interface; the destroy provides useful features for other InputStream classes. See Table 10.3 See Table 10.3 / See Table 10.3
10.1.2 Type of OutputStream
This category includes the class that determines where our input is going: a byte array (but there is no string; assuming that we can create one byte array); a file; or a "pipe".
In addition, FILTEROUTPUTSTREAM provides a fundamental class for the "destroyer" class, which connects the attribute or useful interface to the output stream. This will be discussed later.
Table 10.2 Type of OutputStream
Class Function Builder Parameters / How to Use
ByteArrayoutputStream creates a buffer in memory. All data we send to the stream will place this buffer. The initial size of the optional buffer / is used to point out the destination of the data. If you connect it to the FilterOutputStream object, you can provide a useful interface
FileOutputStream sends information to a file represents a file name, or uses a File or FileDescriptor object / to point out the destination of the data. If you connect it to the FilterOutputStream object, you can provide a useful interface
PipedputStream We write any information that will automatically become the output of the relevant PiPedInputStream. The concept of "pipeline" pipedinputstream / to the multi-threaded process points out the destination of your data / connect it with the filteroutPutStream object, you can provide a useful interface.
FilterOutputStream abstracts the class as a class that is used as a destroyer interface; the destroyer provides useful features for other OutputStream classes. See Table 10.4 See Table 10.4 / See Table 10.410.2 Adding Properties and Useful Interfaces
The practice of using a hierarchical object dynamics and transparently adding a single object is called "Decorator" scheme - "Solution" belongs to the subject of Chapter 16 of this book (Note 1). The decorator solution specifies that all objects encapsulated in the initialization object have the same interface to use the "transparent" nature of the decorator - we will send the same news to an object, whether it has been "decorated". This is why the "Filter" class is existing in Java IO Class: Abstract "Filter" class is the basic class of all decorator (the decorator must have the same interface as the object decorated, but The decorator can also extend the interface, which is seen in several special "filter" classes).
Subclass processing requires a large number of subclasses to support each possible combination, often uses decorator - due to too much combination, resulting in sub-treatment becomes unrealistic. The Java IO library is in many different feature portfolios, which is why the decorator program is particularly useful. However, the decorator program also has a shortcomings. When we write a program, the decorator provides us with much flexibility (because it can make it easy to mix and match attributes), but they also make their code more complicated. The reason is that the Java IO library is inconvenient, we must create many classes - "core" IO type plus all decorators - can get a single IO object you want.
FilterInputStream and FilterOutputStream (these two names are not very intuitive) provide the corresponding decorator interface for controlling a specific input stream (InputStream) or OutputStream. They are derived from InputStream and OutputStream. In addition, they all belong to an abstract class, which provides a general interface for our different communication methods with a stream. In fact, FilterInputStream and FilterOutputStream simply simply imitated its own basic class, which is the basic requirement of a decorator.
10.2.1 Read the data from InputStream via FILTERINPUTSTREAM
The FilterInputStream class has to complete two things. Among them, DataInputStream allows us to read different basic types of data and String objects (all methods begin with "read", such as readbyte (), readfloat (), etc.). With the corresponding DataOutputStream, we can move the basic type of data from one place to another via data "stream". These "places" are determined by those class summarized in Table 10.1. If you read the data within the block, you don't need to use DataInputStream. However, in many other cases, we generally want to automatically format the data read in yourself.
The remaining class is used to modify the internal behavior of InputStream: whether it is buffered, whether it is tracking yourself to read the data line, and whether it can be pushed back to a character. The latter two classes seem to provide support for building a compiler (in other words, add them to support the construction of the Java compiler), so they are generally used in regular programming.
Perhaps almost every time you have to buffer your own input, regardless of which IO device is connected. Therefore, the most sensible approach of the IO library is to handle unobburizing input as a special case while receiving buffer input as a standard practice.
Table 10.3 FilterInputStream Type Function Builder Parameters / How to Use
DataInputStream is combined with DataOutputStream to allow yourself to read basic data types (int, char, long, etc.) in a motor mode (int, char, long, etc.) InputStream / contain a complete interface to read basic data types.
BufferedInputStream avoids physical reading every time you want more data, tell it "Please find" InputStream in the buffer, no optional buffer size / it does not provide an interface, just sending a buffer District requirements. Ask the same interface object to join together
LINENUMBERINPUTSTREAM Tracks the line number in the input stream; can call getLineNumber () and setLineNumber (int) just add the ability to add data line numbers, so you may need to connect the same true interface object connection.
PushbackInputStream has a later bump buffer so that you will be read into the previous character inputStream / usually used by the compiler in the scanner because the Java compiler requires it. Generally not used in their own code
10.2.2 Write data to OutputStream through FilterOutputStream
It corresponds to DataNputStream that formats each of the basic data types and String objects, and places it into a data "stream" so that DataInputStream on any machine can read them normally. All methods are started with "wirte", such as WritebyTe (), Writefloat (), and more.
To make some real formatting output, such as output to the console, use PrintStream. Using it can print out all basic data types and String objects, and can use an easy viewing format. This is the opposite of DataOutputStream, the latter's goal is to place those data into a data stream so that DataInputStream can easily reconstruct them. System.out static object is a printStream.
Two important methods in PrintStream are print () and println (). They have been covered, and all data types can be printed. The difference between print () and println () is that the latter will automatically add a new row after the operation is completed.
BufferedOutputStream belongs to a "modifier" for indicating that data streams use buffer techniques to make themselves to physically write data in the stream every time. It is usually applied to file processing and controller IO.
Table 10.4 Type of FilterOutputStream
Class Function Builder Parameters / How to Use
DataOutputStream is used with DataInputStream to write basic data types (int, CHAR, LONG, etc.) in a convenient form to include full interface so that we write basic data types.
PrintStream is used to generate a formatted output. DataOutputStream controls the "storage" of the data, and PrintStream controls "display" OutputStream, optional a Boolean parameter, indicating whether the buffer is refreshed with each new line / for its own OutputStream object, should use "Final" It is closed. May often use it
BufferedOutputStream uses it to avoid physical writing, ask it, "Please find it in the buffer" first. Flush () can be called, and the buffer is refreshed outstream. The optional buffer size / itself does not provide an interface, just issues the requirements of using the buffer. Need to connect to the same interface object 10.3 itself defect: RandomaccessFile
RandomaccessFile is used to include a file that is known for a record, so that we can use Seek () from a record to another; then read or modify those records. The length of each record is not necessarily the same; just know how much they are, where they are placed.
First, we are a bit difficult to believe that randomaccessfile is not part of the INPUTSTREAM or OutputStream hierarchical structure. In addition to happens that DataInput and DataOutput (both are also implemented by DataInputStream and DataOutputStream), they have nothing to do with those hierarchical structures. It doesn't even use the functionality of existing InputStream or OutputStream classes - uses a completely unconfigure class. This class belongs to a new design that contains all its own (most of the inherent) method. The reason why RandomaccessFile has a completely different behavior with other IO types, because we can move forward or backward in a file. No matter which case, it is operated independently, as a "direct heir" of Object.
Fundamentally, RandomaccessFile is similar to DataInputStream and DataOutputStream. Among them, getFilePointer () is used to understand where the current file is currently filed, and seek () is used to move to a new location within the file, and longens () is used to determine the maximum length of the file. In addition, the builder requires another independent variable (exactly the same as FOpen () ()), pointing that it is just random read ("r") or reading and writing ("rw"). There is no support for "write only files" here. That is, if it is inherited from DataInputStream, then RandomaccessFile may also work well.
It is more difficult to deal with. It is easy to imagine that we are sometimes searching in other types of data streams, such as a ByteArrayInputStream, but the search method is only RandomaccessFile. The latter can only be operated for files, and cannot operate on data streams. At this point, BufferedInputStream does allow us to mark a location (using Mark (), which is accommodated in a single internal variable, and reset the location with RESET (). But these practices are limited and is not particularly useful.
10.4 File class
The FILE class has a spoofing name - usually thinks it is a file, but the truth is not the case. It represents both a particular file name and represents a series of files in the directory. If you represent a file set, you can use the list () method to query this set, returning a string array. The reason why you want to return an array, not a flexible collection class, because the number of elements is fixed. And if you want to get a list of different directory, just create a different File object. In fact, "FilePath" seems to be a better name. This section will completely exempt how to use this class, including the associated FileNameFilter interface. 10.4.1 directory list
Now suppose we want to watch a list of directorys. File objects can be listed in two ways. If list () is called without the argument (parameter), a full list of File objects is obtained. However, if you want to make some restrictions on this list, you need to use a "directory filter" that is to indicate how the File object should be selected to complete the display.
Below is a code for this example (or difficulty in executing the program, please refer to Chapter 3 3.1.2 "Assignment"):
449-450 page program
The Dirfilter class "implements" Interface FileNameFilter (the issue of the interface has been detailed in Chapter 7). Let's take a look at how simple FileNameFilter interface:
Public interface filenamefilter {
Boolean Accept (file directory, string name);
}
It indicates that all objects of this type provide a method called Accept (). The reason why the reason is to create such a class, which is to provide the Accept () method to the list () method, so that List () can "call" accept (), which determines which file names should be included in the list. Therefore, this technique is usually referred to as "callback", sometimes referred to as "operator" (that is, Dirfilter is an operator because it is the only way to accommodate a method). Since List () uses a FileNameFilter object as its own own argument, we can pass an object that implements any class of FileNameFilter, which is used to determine (or even running period) List () method behavior. The purpose of the callback is to provide greater flexibility on the behavior of the code.
With Dirfilter, we see that although a "interface" contains only a series of methods, it is not limited to only written those methods (however, at least one must provide definition of all methods in an interface. In this case, Dirfilter build The device is also created).
The Accept () method must accept a File object that indicates the directory for finding a specific file; and accepts a string, which contains the name of the file to find. Determine one of these two parameters can be used or ignored, but sometimes at least use the file name. Remember that the list () method is prepared to call accept () for each file name in the directory object, and verify which should be included - the "Boolean" result decision is determined by accept ().
To determine that our operation is just a file name, there is no path information, and the String object must be used and a File object is created outside it. Then call GetName (), which is to remove all path information (mode-independent way). Subsequently, accept () uses the String class's indexoF () method to check if there is a search string "AFN" during the filename. If you find AFN in the string, the return value is the starting point index of the AFN; however, if not found, the return value is -1. Note that this is just a simple string search example, not using a common expression "wildcard" scheme, such as "fo? .B? R *"; this program is more difficult to implement. The List () method returns an array. You can query the length of this array and then traverse, select the array element. The behavior of this approach to the array within and outside the method is undoubtedly a significant progress than the similar behavior of C and C .
Anonymous internal class
The following example uses an anonymous internal class (which has been described in Chapter 7) is very ideal. First, a filter () method is created, it returns a handle pointing to FileNameFilter:
451-452 page program
Note that the self-variable of Filter () must be Final. This is an anonymous internal requirement to use an object from its own scope.
The reason why think so is better because the FileNameFilter class is now closely combined with DirList2. However, we can take a further operation to define an anonymous internal class to a parameter of list () so that it looks more streamlined. As follows:
452 page program
Main () The current variable is Final because the anonymous internal class uses Args [0].
This shows how to quickly create a streamlined class with anonymous internal classes to solve some complex problems. Since everything in Java is related to the class, it is undoubtedly a quite useful encoding technology. One of its benefits is to isolate specific issues in a place uniform. But on the other hand, the code generated is not very easy to read, so it must be cautious when used.
2. Sequence directory list
File names often need to be provided in a row. Since Java 1.0 and Java 1.1 do not provide support for sorting (start from Java 1.2), you must use the SortVector created in Chapter 8 to add this capabilities directly to your own program. Just like this:
453-454 page program
Here, there is another little improvement. It is no longer a local variable that creates Path (path) and LIST (list) as main (), which becomes a member of the class, so that their values can be easily accessed during the object "survival". In fact, Main () is now only a way to test the class. As you can see that once the list is created, the class builder will automatically sort the list.
This sort is not required to be case sensitive, so it will eventually get a list of all words that begin with uppercase letters, followed by all lists of lowercase letters. However, we noticed that in a set of files starting with the same letters, our uppercase letters are row in front - this is still a unqualified behavior for sorting of standards. Java 1.2 has successfully solved this problem.
10.4.2 Check and create a directory
The File class is not just a representation of existing directory paths, files, or file groups. You can also create a full directory with a File object, or even create a complete directory path - if it doesn't exist. It can also be used to understand the properties of the file (length, the last modification date, read / write property, etc.), checking a file object represents a file or a directory, and deleting a file, etc. The following programs fully demonstrate how to use the File Class: 454-456 Page Program
In FileData (), you can see the various file survey methods to display information related to the file or directory path.
The first method of the main () application is RenameTo (), using it to rename (or move) a file to a new path (this path is determined by the parameter), which belongs to another File object. This also applies to the directory of any length.
If you test the above procedure, you can find a directory path that you can make any complexity, because MKDIRS () will help us complete all your work. In Java 1.0, the -d flag report directory has been deleted, but it still exists; but in Java 1.1, the directory will be deleted.
Typical application of 10.5 IO stream
Although there are a large number of IO flows in the library, it can be combined in a variety of different ways, but in fact, only a few ways will be used frequently. However, you must be careful to get the correct combination. The following quite a long example shows the creation and use of a typical IO configuration, which can be used as a reference when writing your own code. Note that each configuration starts with a note form, and provides appropriate interpretation information.
457-459 page program
10.5.1 input flow
Of course, one of the things we often want to do is to print the formatted output to the console, but it has been simplified in com.bruceeckel.tools created in Chapter 5.
1 to 4 demonstrate the creation and use of input streams (although Part 4 shows the simple application of the output stream as a test tool).
1. Buffered input file
To open a file to enter, you need to use a FileInputStream while uses a String or File object as a file name. In order to improve the speed, it is best to buffer the file first, thereby obtaining the result handle for builders for a bufferedinputstream. In order to read the input data in a formatted form, we assume that result handle to builders for a DataInputStream. DataInputStream is our final (FINAL) object and is an interface we have read operations.
In this example, only the readline () method is used, but it is possible to use any DataInputStream method. Once the end of the file is arrived, readline () will return a null (empty) to abort and exit the While loop.
"String S2" is used to aggregate complete file content (including new rows that must be added, because readline () removes those rows). Subsequently, S2 is used in the rear portion of this program. Finally, we call close (), with it to close the file. As a result, Close () will be called when running Finalize (). And we hope that once the program exits, this happens (regardless of whether garbage collection is carried out). However, Java 1.0 has a very prominent error (bug) that does not happen this. In Java 1.1, you must call System.RunfinalizersOnexit (TRUE), which is guaranteed to call Finalize () for each object in the system. However, the safest way is to express close () for documents. 2. Enter from memory
This part uses String S2 that has already included full file content, and uses it creates a StringBufferInputStream-as a parameter of the builder, requiring a StringBuffer, rather than a stringbuffer. Subsequently, we use read () to read each character and send it to the console. Note that read () returns the next byte to INT, so you must make it as a char to print it correctly.
3. Format Memory input
The interface of StringBufferInputStream is limited, so it usually needs to encapsulate it into a DataInputStream, thereby enhancing its ability. However, if you select ReadByte (), all values are valid, so that the return value cannot be used to end the input. Instead, how many characters available can be used to determine the available available () method. The following example shows how to read a character from the file:
461 page
Note that it is also different from what media read from what media is currently read. It means that "the number of bytes that can be read without blocking". For a document, it means the entire file. But for a different kind of data stream, it may have different meanings. Therefore, it should be considered when used.
In order to detect the end of the input in this case, it can also be implemented by capturing a violation. However, if you really use a violation to control the data stream, it seems to be a small amount of large materials.
4. The number of lines and file output
This example shows how LINENUMBERINPUTSTREAM to track the number of the input row. Here, it is not simply combining all builders because it must be maintained in a handle of LineNumberinputStream (note that this is not a inheritance environment, so IN4 cannot be simply treated to a LINENUMBERINPUTSTREAM). Therefore, LI accommodates the handle to the LINENUMBERINPUTSTREAM, and then creates a DataInputStream on it to read the data.
This example also shows how to write formatted data into a file. First create a FileoutPutStream and connect it with its same file. Considering efficiency reasons, it generates a bufferedoutputstream. This is certainly definitely our general approach, but it must be done in this way. It is then formatted, it is converted into a printStream. The data file created in this way can be read as an original text file.
One method of the logo DataInputStream ends is Readline (). Once there is no more strings, it can be read, it will return NULL. Each row will be printed into the file with your line number. This line number can be inquired by Li.
It can be seen that a clearly specified close () used for OUT1. If the program is ready to turn down, then read the same file again, this approach is quite useful. However, this program did not check the file odemo.txt. As before, it is not possible to call Close () if you do not call all the output files, you may find that the buffer will not be refreshed, resulting in incomplete. . 10.5.2 output flow
The two primary output streams are divided by their writing data: a human habitual writing, and another for later written by a DataInputStream. RandomaccessFile is independent, although its data format is compatible with DataInputStream and DataOutputStream.
5. Save and recover data
PrintStream can format data so that it can be read according to our habits. However, in order to output data so as to recover from another data stream, you need to write data with a DataOutputStream and use a DataInputStream to recover (obtain) data. Of course, these data streams can be anything, but here is a file and buffer processing to speed read and write speed.
Word strings are written with WriteBytes () instead of Writechars (). If the latter is used, the write is 16-bit Unicode characters. Since there is no "Readchars" method in DataInputStream, you have to take READCHAR () each time a character. So for ASCII, more convenient practice is to write characters as byte, followed by a new row; then use readline () to read characters as a normal ASCII.
WriteDouble () Saves the Double Digital to the data stream and recovered it with a supplementary readDouble (). However, in order to ensure that any reading method can work normally, you must know the accurate position in the stream because it is possible to read saved Double data as a simple byte sequence, and it is possible to read in a char or other format. So you must either use a fixed format for the data in the file, or save additional information to the file to correctly determine the storage location of the data.
6. Read and write random access files
As early as possible, the remainder of the RandomaccessFile and the IO hierarchy is almost completely isolated, although it also implements DataInput and DataOutput interfaces. So it cannot be associated with any part of the INPUTSTREAM and OUTPUTSTREAM subclasses. Although it may be treated with a ByTearRayInputStream as a random access element, you can only open a file with RandomaccessFile. It must be assumed that RandomaccessFile has got the correct buffer because we can't choose it.
You can choose the second builder parameter: you can decide to open a randomaccessfile file in a "read-only" (R) mode or "read and write" (RW).
When using randomaccessfile, similar to a combination using DataInputStream and DataOutstream (because it implements equivalent interface). In addition to this, you can also see that Seek () in the program is used to move around the file and modify a value.
10.5.3 shortcut file processing
Since some typical forms previously involved in file processing, you may suspect that there is so much code input - this is a shortcoming of the decorator program. This section will show you how to create and use typical files to read and write a shortcut version. These shortcuts are placed in packagecom.bruceeckel.tools (created from Chapter 5). In order to add each class to the library, simply put it into the appropriate directory and add the corresponding package statement. 7. Quick file input
To create an object, use it to read a file from a buffered DataInputStream to encapsulate this process into a class named Infile. As follows:
463-464 page program
Regardless of the String version of the builder or the File version, it is used to create a FileInputStream.
As this example is displayed, it is now possible to effectively reduce the problem caused by repeated emphasis on the creation of files.
8. Fast output formatted files
You can also create a printStream with the same type of method to write it to a buffer file. Here is an extension to com.bruceeckel.tools:
464-465 page program
Note that the builder is not possible to capture a violation of the "throw" by the underlying builder.
9. Quick output data file
Finally, use similar shortcuts to create a buffer output file, with it to save data (opposite to the data format viewed by people):
465 page
Very strange is (also very unfortunate), the Java library designers did not expect to provide these convenience measures directly as part of their standards.
10.5.4 Read data from standard input
Based on the "Standard Input", "Standard Output", "Standard Error Output", "Standard Error Output", and "Standard Error Output", and "Standard Error Output", and "Standard Error Output". Through this whole book, everyone will come into contact with the standard output with System.out, which is preparably installed into a printStream object. System.err is also a PrintStream, but System.in is an original InputStream without any packaging processing. This means that despite System.Out and System.err, you must package System.in in advance, otherwise you cannot read data.
Typically, we want to use readline () to read information each time, so you need to encapsulate System.in into a DataInputStream. This is the "old" approach taken when Java 1.0 is input. In this chapter, everyone will see the Java 1.1 solution. The following is a simple example, the role is to respond to each of our rows:
466 page
The reason why the TRY block is used is because readline () may "throw" out an oException. Be careful to cushion with Most of the other flows.
Since the System.in is packaged into a DataInputStream in each program, it is a bit inconvenient. However, this design can be used to achieve maximum flexibility.
10.5.5 Pipe data stream
This chapter has a brief introduction to PiPedInputStream (pipe input stream) and PiPedoutputStream (pipe output stream). Although the description is not very detailed, it is not to say that they don't have a lot. However, only after the concept of multi-threaded processing is mastered, they can truly understand their value. The reason is very simple, because the pipelined data flow is communication between threads. Problems in this regard will be described in Chain 14.
10.6 StreamTokenizer
Although streamTaikenizer is not derived from InputStream or OutputStream, it only works with InputStream, so it is very appropriate to include the IO portion of the library. StreamTokenizer class is used to split any InputStream into a series of "token". These marks are actually some intermittent text blocks, which are separated by anything we choose. For example, our marker can be a word, with a blank (space) and punctuation symbol.
Below is a simple program for calculating the number of times each word repeated in a text file:
467-469 page program
It is best to output the results according to the sort format, but because Java 1.0 and Java 1.1 have no sorting methods, you must do it yourself. This goal can be easily reached with a STRSORTVector (create in Chapter 8, which belongs to the package created by the package. Remember that the start directory of the subdirectory of this book must be in the classpath, otherwise the program will not correctly Compilation).
In order to open the file, use a FileInputStream. Also, in order to convert files into words, a streamTokenizer created from FileInputStream. In StreamTokenizer, there is a default separator list, we can use a series of methods to add more separators. Here, we pointed out that "This character is not particularly important", so the parser does not treat it as part of any word created by himself. For example, St.ordinaryChar ('.') Indicates that the decimal point does not become part of the word parsed. More related information can be found in the online documentation provided with Java.
In Countwords (), each time you take a mark from the data stream, and the role of TTYPE information is to determine what action taken to each mark - because the mark may represent a row, a number, a string or a character.
After finding a marker, you will query HashTable Count, verify that it has been included in the form of "key" (key). If the answer is affirmative, the corresponding Counter object will value, indicating another example of the word already found. If the answer is not, a new counter is created.
SortedWordCount is not a type of HashTable, so it will not inherit. It performs a specific type of operation, so although the keys () and values () methods must be re-disclosed, but still do not indicate that inheritance, because a large number of HashTable methods are inappropriate here. In addition, for other methods (such as getCounter () - for obtaining a specific string counter; as sortedKeys () - is used to generate an enumeration), they eventually changed the sortedWordCount interface. form.
In Main (), we use sortedWordCount to open and calculate the number of words in the file - only two lines of code. Subsequently, we extract a enumeration for a list of row. And use it to get each key and the associated count (count). Note that cleanup () must be called, otherwise the file cannot be turned off.
The second example of STREAMTOKENIZER will be provided on Chapter 17.
10.6.1 StringTokenizer
Although there is no need to part of the IO library, StringTokenizer provides a very similar functionality with StreamTokenizer, so it will be described here. The role of StringTokenizer is a mark in the word string each time. These marks are some consecutive characters separated by tab, spaces, and new row. Therefore, the marks "where is my cat?" Is "where", "IS", "my", and "cat?". Similar to StreamTokenizer, we can indicate that StringTokenizer is split in accordance with our desire. But for StringTokenizer, you need to pass another parameter to the builder, namely the partition string we want to use. Typically, if you want more complex operations, StreamTokenizer should be used.
Use nexttoken () to request the next mark within the string to the StringTokenizer object. This method either returns a marker or returns an empty string (indicating that there is no mark).
As an example, the following procedure will perform a limited syntactic analysis, query the short-language sequence, understanding the sentence suggests that happiness or sadness.
471-472 page program
For each string preparing to analyze, we entered a while loop and remove the marker from that string. Please note the first IF statement, if the marker is neither "i", nor "are", will execute continuous (return loop starting point, once again). This means that the mark will be truly award unless a "I" or "" "is found. Everyone may want to use == instead of the equals () method, but doing abnormal performance, because == compare the handle value, and equals () compares content.
The logic of the remainder is search for "I am Sad" (I am very sad, "I am NOTHAPPY" or "Are you sad?" (Are you sad?) This syntax format. BREAK statement, this code may even be more misappropriated. You should pay attention to a typical parser, usually have a table of these marks, and can use a small code in the form when reading new marks. .
In any case, StringTokenizer should only be regarded as a simple and special simplified form of StreamTokenizer. However, if there is a string that needs to be processed, and the function of StringTokenizer is limited, then all things that should be done is to convert it to a stream with StringBufferInputStream, and use it to create a more powerful streamTokenizer.
10.7 Java 1.1 IO
At this time, everyone may fall into a dilemma, doubt whether there is another design of IO streams and may require a larger code quantity. Anyone else can make a more weird design? In fact, Java 1.1 has made some major improvements to the IO loop. When you see Reader and Writer classes, most people's first impression (just like me) is to replace the original InputStream and OutputStream classes. But the truth is not the case. Although some functions of the original data stream library are not recommended (such as using them, you will receive a warning message from the compiler), but the original data stream is still reserved to maintain backward compatibility, and:
(1) Add new classes in the old-fashioned hierarchy, so Sun will not abandon the vintage data stream.
(2) In many cases, we need to use classes in the new structure to use classes in the old structure. To achieve this, you need to use some "bridge" class: InputStreamReader converts an InputStream into Reader, OutputStreamWritrel converts an OutputStream to Writer. So, compared with the original IO loop, you often have a new IO stream to more packages. Similarly, this is also a disadvantage of the decorator solution - it is necessary to pay for additional flexibility.
The reason why the Reader and Writer hierarchicals have been added in Java 1.1, and the most important reason is the need for internationalization. The old-fashioned Io-level structure only supports 8-digit current flow, and 16-bit Unicode characters cannot be controlled well. Since Unicode is mainly to international support (Java's CHAR is a 16-bit Unicode), the Reader and Writer hierarchical are added to provide support for Unicode in all IO operations. In addition, the new library is also optimized for speeds, which can be run faster than the old library.
Like other places in this book, I will try to provide an overview of the class, but assume that you will use the online document to get all the details, such as the method's detailed list, etc.
10.7.1 Initiation and reception of data
Almost all IO streams of Java 1.0 have corresponding Java 1.1 classes, used to provide built-in Unicode management. It seems that the easiest thing is "All use of new classes, no longer use old", but the actual situation is not as simple. Sometimes, we have to use the IO stream of Java 1.0 due to some restrictions on the library design. In particular, the Java.util.zip library is added based on the old log library, which relies on the old stream components. So the most sensible approach is "trying to use Reader and Writer classes). If the code cannot be compiled, it must be replaced back to the vintage library.
The following table summarizes the correspondence between information initiation and reception, respectively.
Initiate & Receive: Java 1.1 class corresponding to Java 1.0 class
474 page in the table
We have found that even if it is incomplete, the interfaces in the old library components are usually similar to the new interface.
10.7.2 Modifying data stream behavior
In Java 1.0, the data stream adapts to specific needs by FilterInputStream and FilterOutputStream's "decorator" subclass. Java 1.1's IO streaming has this idea, but does not continue to use all decorators from derived from the same "Filter" base class. If it is understood by observing the hierarchy of the observation, this may have a little confusion.
In the table below, the corresponding relationship is rough than the previous table. This difference is caused by the organization of the class: Although bufferedoutputstream is a subclass of FilterOutputStream, BufferedWriter is not a subclass of FilterWriter (for the latter, although it is an abstract class, but no own Subclasses or approximate subclasses, there is no "placeholder" available, so don't worry about it. However, the interfaces of the two classes are very similar, and no matter what the new version should be used as possible, the old version should be considered (that is, unless a stream must be generated in some classes. Generate Reader or Writer.
Filter: Java 1.0 Class 1.0 class
FilterInputStream FilterReader
FilterOutputStream FilterWriter (abstract class with no subclavles) BUFFEREDInputStream BufferedReader (also readline ())
Bufferedoutputstream BufferedWriter
DataInputStream uses DataInputStream (unless you want to use readline (), you need to use a bufferedReader)
PrintStream PrintWriter
LinenumberInputStream LineNumberReader
StreamTokenizer StreamTokenizer (replacing Reader with builder)
PushbackInputStream PushbackReader
There is an obvious manner: If you want to use readline (), don't use a DataInputStream to be implemented (otherwise you will get an error message in the compile period), but a bufferedReader should be used. But in addition to this, DataInputStream is still a "preferred" member of the Java 1.1 IO library.
In order to become a transition to PrintWriter, it provides a builder that can adopt any OutputStream object. The format support provided by PrintWriter has no PrintStream, but the interface is almost the same.
10.7.3 Unlike class
Obviously, the Java library designers feel that there is no problem in some of the previous classes, so there is no modification of them, and they continue to use them as before:
No Java 1.0 class for Java 1.1 class
DataOutputstream
File
RandomaccessFile
SequenceInputStream
Special unreachable is DataOutputStream, so in order to save and acquire data in a transferable format, it is necessary to use the INPUTSTREAM and OUTPUTSTREAM hierarchies.
10.7.4 An example
To experience the effect of the new class, let's take a look at how to modify the corresponding area of the IostreamDemo.java example to use Reader and Writer classes:
Page 476-478
Everyone usually see is that the conversion process is very intuitive, and the code looks quite similar. But these are not an important difference. Most importantly, since the random access file has changed, the 6th section is not repeated.
Section 1 is harder, because all things to do is to read the row input, then only need to encapsulate a FileReader to the bufferedReader. The section 1 is shown in the package system.in to read a new method input by the console. The amount of code here increases, because system.in is a DataInputStream, and BufferedReader requires a Reader parameter, so use InputStreamReader to convert.
In Section 2, you can see if there is a string, and you want to read data from it, just replace StringBufferInputStream with a StringReader, the remaining code is exactly the same.
Section 3 reveals an error in the design of the new IO log. If there is a string and wants to read data from it, then StringBufferInputStream can no longer be used in any form. If you compile a code involving StringBufferInputStream, you will get a "opposition" message and tell us not to use it. It is best to switch to a StringReader at this time. However, if you want to format the memory input in Section 3, you must use DataInputStream - Nothing "DataReader" can replace it - and DataInputStream unfortunately requires an inputStream parameter. So we have no rooms, you have to use the StringBufferInputStream class that the compiler is not approved. The compiler also issues against information, but we have no hand to this hand (notes 2). StringReader replaces StringBufferInputStream, and the remaining code is exactly the same.
2: When you are officially used, this error may have been corrected.
Section 4 is obviously a direct conversion from old-fashioned data to a new data stream, and it is not necessary to specifically point out. In Section 5, we were forced to use all of the old-fashioned data streams because DataOutputStream and DataInputStream require them to use them, and there is no replacement. However, no "opposition" information is generated during compilation. If a data stream is not approved, it is usually due to its builder generated an objection, and we use the entire class. But in the case of DataInputStream, only readline () is not approved because we'd better use a bufferedReader for readline (), but use a DataInputStream for all other formatting inputs.
If comparing the section in Section 5 and IostreamDemo.java, it will be noted that data is written before the text is written. It is because Java 1.1 itself exists, as shown in the following code:
479-480 page program
It seems that anything we wrote after the call to a Writebytes () is not capable of recovering. This is a very limited mistake, I hope that it has been corrected when you read this book. To detect if it is correct, run the above program. If you don't get a violation, and the value can be printed correctly, it indicates that it has been corrected.
10.7.5 Heavy Guide Standard IO
Java 1.1 adds special methods in the System class, allowing us to reallite standard input, output, and error IO streams. At this point, you should use the simple static method call:
Setin (InputStream)
Setout (PrintStream)
SETERR (PrintStream)
If you suddenly generate a large amount of output on the screen, and the speed of scrolling is faster than people's reading speed, the redirection of the output is particularly useful. In a command line program, if you want to repeat a specific user input sequence, the redirected input is also particularly valuable. The following simple example shows the use of these methods:
Page 481
The role of this program is to connect the standard to the same file and redirect the standard output and errors to another file.
This is not to avoid another example of "oppose" messages. The message obtained when compiled with the -Deprecation flag is as follows:
Note: The constructor java.io.printStream (java.io.outputstream) HAS been DepRecated.
Note: The builder java.io.printStream (Java.io.outputStream) is not recommended.
However, regardless of system.setout () or system.seterr () requires use of a printStream as a parameter, the PrintStream builder must be called. So everyone may feel strange, since Java 1.1 opposed the entire PrintStream by opposing the builder, why the library designer added a new method while adding this opposition, and indicated the requirements of PrintStream instead of printwriter ? After all, the latter is a new and preferred replacement measures? This is really puzzling. 10.8 compression
Java 1.1 also adds a class to support reading and writing of data streams in compressed format. They encapsulate into the ready-to-range IO class to provide compression.
One problem with Java 1.1 appears very high: they are not derived from new Reader and Writer class, but are part of the INPUTSTREAM and OUTPUTSTREAM hierarchies. So sometimes it has to be mixed using two types of data streams (note that INPUTSTREADER and OUTPUTSTREAMWRITER are easily converted in different types).
Java 1.1 compression class function
CheckedInputStream getChecksum () generates checksum for any InputStream (not only unzipped)
CheckedoutputStream getChecksum () generates checksum for any OutputStream (not only unzipped)
DEFLATEROUTPUTSTREAM is used for basic classes of compression classes
ZipOutputStream A deflateroutputstream, compress data into a zip file format
GzipOutputStream A DeflaterOutputStream, compress data into a Gzip file format
INFLATERINPUTSTREAM is used to decompress the basic class
ZipinputStream A DeflaterInputStream, unzipped data saved with ZIP file format
GzipinputStream A DeflaterInputStream, unzipped data saved with Gzip file format
Although there are many compression algorithms, ZIP and Gzip may most commonly used. Therefore, it is easy to read and write compressed data in these formats with a variety of ready-made tools.
10.8.1 Simple compression with GZIP
The GZIP interface is very simple, so if only a single data stream needs to be compressed (not a series of different data), it may be the most appropriate choice. Here is an example of compressing a single file:
Page 483-484
The usage of the compressed class is very intuitive - simply simply capturing the output stream into a GzipOutputStream or zipoutputStream, and the input streaming can be installed into GzipinputStream or the ZipinputStream. All remaining operations is the standard IO read and write. However, this is a typical example, we have to mix using the new Old IO stream: The input of the data uses the Reader class, and the GzipOutputStream builder can only receive an OutputStream object and cannot receive the Writer object.
10.8.2 Multi-file preservation with ZIP
The Java 1.1 library supported by ZIP is also more comprehensive. Use it easy to save multiple files. There is even a separate class to simplify the read operation of the ZIP file. This library uses standard ZIP format, so it can work well with a large amount of compression and decompression tools that can be used on the current Internet. The following example takes the same form as the previous example, but can control any number of command line parameters based on the needs of any quantity. In addition, it shows how to calculate and verify the "Checksum) with the Checksum class. Two types of Checksum: ADler32 (fast speed) and CRC32 (slow, but more accurate). Page 484-486
PutneXTENTRY () must be called to a ZIPENTRY object for each file to be added to the compressed file. The ZIpenTry object contains a full-featured interface that uses it to get all the data that can be accepted on the specific Entry (inlet) in the zip file: name, compression, and compressed length, date, CRC checksum, Additional fields of data, annotations, compression methods, and whether it is in portions, and more. However, although the ZIP format provides a method of setting a password, the Java's ZIP library does not provide this support. Although CHECKEDINPUTSTREAM and CHECKEDOTPUTSTREAM provide support for Adler32 and CRC32 checksum, Zipe only supports the CRC interface. This is limited to the restriction of the grassroots ZIP format, but limits the ADler32 that we use speed.
To unzip files, ZipinputStream provides a getNextentry () method, which can return to the next Zipentry under existing. As a simpler method, you can read files with Zipfile objects. This object has an entries () method that returns an Enumeration for ZIPENTRY.
To read the checksum, you must have access to the associated Checksum object. Here, a handle pointing to CheckedOutputStream and CheckedInputStream objects is retained here. However, it is also possible to account for only one handle to the Checksum object.
A confusing method in ZIP stream is setcomment (). As mentioned earlier, we can set annotations when writing a file, but there is no way to remove the comments within ZipinputStream. It seems that it can only provide full support to the annotation through the entrance to ZiPENTRY.
Of course, it is not limited to files when using Gzip or ZIP library - anything can be compressed, including data to be sent over the network.
10.8.3 Java Archive (JAR) Utility
The ZIP format is also adopted in the JAR (Java Archive) file format of Java 1.1. The role of this file format is to merge a series of files into a single compressed file, just like ZIP. However, like anything in Java, the JAR file is cross-platform, so it is not necessary to care about the problem involving the specific platform. In addition to the sound and image files, it can include class files therein.
JAR files are particularly useful when involving Internet applications. Before the JAR file, the web browser must repeat the Web server multiple times to download all the files that make up a "program" (applet). In addition, each file is uncompressed. However, after all of these files are merged into a JAR file, just send a request to the remote server. At the same time, due to compression techniques, all data can be obtained in a shorter time. In addition, each entrance in the JAR file can be added to the digital signature (see the Java User Documentation). A JAR file consists of a series of files with ZIP compression format, and there is a "details" that describes all of these files (you can create your own details single file; otherwise, the JAR program will work for us). In the online user documentation, you can find more information with JAR details (Details English is "manifest").
The JAR utility has provided with Sun's JDK support, you can automatically compress files according to our choice. Please call it on the command line:
JAR [option] Description [Details] Enter file
Where "Options" is represented by a series of letters (there is no need to enter a font size or any other indicator). As follows:
487 pages
c Create new or empty compressed files
T list the catalog table
X decompressed all files
X file extracts the specified file
f pointing out "I am going to provide you with file name". If this parameter is omitted, JAR will assume that its input is from standard input; or when it creates a file, the output will enter the standard output.
m pointed out that the first parameter will be the name of the details table file.
V generates a detailed output, and the work made by JAR is a description.
O Save the file; do not compress file (used to create a JAR file so that we will put it in your own class)
M does not automatically generate details table files
In the file that is ready to enter the JAR file, if a subdirectory is included, the subdirectory will be added automatically, including its own subdirectory, and so on. Path information will also be reserved.
Below is some typical methods of calling JAR:
Jar cf myjarfile.jar * .class
Used to create a JAR file called myjarfile.jar, which contains all kinds of files in the current directory, as well as automatically generated details table files.
Jar cmf myjarfile.jar mymanifestfile.mf * .class
Similar to the previous example, but add a user-built-in-reference table file called mymanifestfile.mf.
Jar tf myjarfile.jar
Generate a directory table for all files in myjarfile.jar.
Jar tvf myjarfile.jar
Add the "verbose" flag to provide more detailed information related to the files in Myjarfile.jar.
Jar CVF myapp.jar audio classes image
Assume that AUDIO, CLASSES, and Image are subdirectory, which combines all subdirectories into file myapp.jar. It also includes "Verbose" logo, which can feed back more detailed information when the JAR program is working.
If you create a JAR file with an O option, that file can be placed in your own classpath:
ClassPath = "lib1.jar; lib2.jar;"
Java can search for target class files in lib1.jar and lib2.jar.
The function of the JAR tool is not as rich as ZIP tools. For example, you cannot add or update a file in an outgoing JAR file, you can only start new JAR files from the beginning. Also, you cannot move file into a JAR file and delete them after moving. However, JAR files created on a platform can be read from JAR tools in any other platform (this problem sometimes plagues the zip tool). As everyone sees in Chapter 13, we also packaged with Jar as Java Beans.
10.9 Object Series
Java 1.1 adds an interesting feature called "Object Serialization). It is facing objects that implement the serializable interface, which can be converted into a series of bytes, and can completely recover from the original look. This process can also be carried out through the network. This means that the serialization mechanism can automatically compensate the difference between the operating system. In other words, you can create an object on the Windows machine, serialize it, and then send it to a UNIX machine through the network, then "assembly" accurately. Don't care about how data is in different machines, nor does it need to care about the order of the byte or any other details.
As far as it is, the sequence of object is very interesting because it can achieve "limited persistence". Keep in mind that "persistence" means that the "living time" of the object does not depend on whether the program is being executed - it exists or "survive" every call between the program. By serializing an object, write it into the disk, and then recover the object when the program is reused, the "lasting" effect can be successfully realized. The reason why it is "limited" because it is not possible to simply define an object with some "persistent" keyword, and let the system automatically look at all other detail issues (although it may become a reality in the future). Instead, sequence and assemble objects must be clarified in their own procedures.
After the language of the language is added, it provides support for both main features. The "Remote Method Call" (RMI) of Java 1.1 enables objects that exist in other machines, can show behaviors on the local machine. When sending a message to a remote object, you need to transmit parameters and return values through object serialization. RMI will be discussed in Chapter 15 as a specific discussion.
The sequence of objects is also required for Java Beans, which is introduced by Java 1.1. When using a bean, its status information is usually configured during the design. After the program starts, this status information must be saved so that the program is restored after the program is started; the specific work is completed by the object serialization.
The serialization of the object is very simple, and only the object implements the serializable interface (this interface is just a tag, there is no way). In Java 1.1, many standard library classes have changed in order to be able to serialize-including all packages for basic data types, all set classes, and many other things. Even Class objects can also be serialized (Chapter 11 describes the specific implementation process).
To serialize an object, first create some OutputStream objects, then package it into the ObjectOutputStream object. At this point, just call WriteObject () to complete the sequence of objects, and send it to OutputStream. The opposite process is to encapsulate an InputStream into the ObjectInputStream and then call ReadObject (). As usual, we finally got to point to a handle of the shape Object, so it must be tracered so that it can be directly set.
One place for object serialization special "smart" is that it not only saves the "panorama" of the object, but also tracks all the handles contained in the object and saves those objects; then tracks the handle contained in each object; Push it in this class. We sometimes refer to this situation as "object network", and a single object can be connected to it. Moreover, it also contains an array of objects and a member object. If you have to manipulate a set of object serialization mechanisms, you may be very troublesome when you track all of these links in your code. On the other hand, because the sequence of Java objects seems to have no shortcomings, please try not to do it yourself, let it use the optimized algorithm to automatically maintain the entire object network. The following example tested the serialization mechanism. It has established a "Worm" (worm) of many link objects, each object is a next link in Worm, while also associated with an object handle of a different class (DATA): 490-492 Page Program
More interesting is that the array of DATA objects in the Worm is initialized with random numbers (so you don't have to doubt a compiler to keep some original information). Each WORM segment is tagged with a CHAR. This char is automatically generated when the WORM list of repeatedly generated a link. When you create a WORM, you need to tell the builder to hope how long it is. To produce the next handle (Next), it always calls the WORM builder with the length of minus 1. The last NEXT handle remains null (empty) indicating that the end of the WORM has arrived.
All of the above operations are in order to deepen the complexity of things, increase the difficulty of sequentialization of objects. However, the real serialization process is very simple. Once ObjectOutputStream is created from another stream, WriteObject () will serialize the object. Note that WriteObject () can also be called for a String. All basic data types can also be written in the same way as DataOutputStream (they have the same interface).
There are two separate TRY blocks look similar. The first reading is the file, and the other is written is a ByteArray (byte array). You can read and write a specific object using serialization of any DataInputStream or DataOutputStream; these objects even include the network as chapter on the network. The output after the one cycle is as follows:
492-493 page program
It can be seen that the objects of assembly back the original state have indeed contain all links included in that object.
Note Any builder (or even default builder) will not be called during the process of reassignment of a serializable (serialized) object. The entire object is recovered by obtaining data from the InputStream.
As a Java 1.1 feature, we noticed that the sequence of objects is not part of the new Reader and Writer hierarchies, but is along vintage InputStream and OutputStream structure. Therefore, in some special occasions, two types of hierarchies have to be mixed.
10.9.1 Searching
The reader may be strange why it takes an object to recover from its serialization. For example, we assume that we serialize an object and transfer it as a file to another by the network. At this point, the program at another machine can re-construct this object with the file directory?
The best way to answer this question is to do an experiment. The following file is located in subdirectory in this chapter:
Page 493
Files used to create and serialize an Alien object are in the same directory:
493-494 page program
This program is not capturing and controlling violations, but is simple and directly transferred to main (), so you can report them on the command line.
After the program is compiled and run, copy the result. File.x to the subdirectories named XFiles, the code is as follows: 494 page
The program can open the file and successfully read the contents in the mystery object. However, once attempts to find any information related to the object - this requires Alien's Class object - Java virtual machine (JVM) can not find Alien.class (unless it is within the classpath, the opposite of this example). This will get a violation called ClassNotFoundException (in the same manner, if it is unable to verify the evidence of Alien, it is equal to disappearance).
After recovering a serialized object, if you want to do more things, you must ensure that the JVM can find the relevant .class files in the local path or the Internet.
10.9.2 Serialization Control
As everyone saw, the default serialization mechanism is not difficult to manipulate. However, what should I do if there is a special requirement? We may have special security issues, do not want some part of the object; or a child object does not have to serialize, because the object is restored, that part needs to be recreated.
At this point, by implementing the externalizable interface, it can control the specific process of serialization with it instead of the Serializable interface. This Externalizable interface extends serializable and adds two ways: WriteExternal () and readExternal (). These two methods are called automatically during serialization and re-assembly so that we can do some special operations.
The following example shows the simple application of the Externalizable interface method. Note that blip1 and blip2 are almost entirely, except for the very small difference (you study the code, see if you can find):
495-496 page program
The program is output as follows:
496-497 page program
The reason why the Blip2 object is not recovered is that it will lead to a violation. Do you find the difference between Blip1 and Blip2? The BLIP1 builder is "public" (public), the BLIP2 builder, otherwise, this will cause violations when recovery. Try to turn the BLIP2 builder properties into "public", then delete /! Annotation tag to see if it can get the correct result.
After restoring B1, the BLIP1 default builder is called. This is different from the restore a serializable object. In the case of the latter, the object is completely restored based on the binary position it saved, and there is no builder call. For an externalizable object, all ordinary default build behaviors occur (including initialization during field definition), and readexternal (). Must pay attention to this fact - pay special attention to all default build behaviors - otherwise it is difficult to generate the correct behavior in your own Externalizable object.
The following example reveals all things that saved and restore an Externalizable object:
497-498 page program
The fields S and I are initialized in the second builder, and it is not relevant to the default builder. This means that if S and I are initialized in Readexternal, they will become NULL (because the storage space of the object has been cleared in the first step of the object). If you comment, follow the two lines of code behind "You Must Do this", and run the program, it will find that after the object is restored, S is NULL, and i is zero.
If you inherit from an Externalizable object, you usually need to invoke the basic class version of WriteExternal () and readExternal () to properly save and restore the base class component. So in order to let everything works, it is necessary to write important data for the object during WriteExternal () method (without default behavior can be used to write all member objects for a Externalizable object), but must be in Readexternal. The data is also restored. There may be some inconsistency during the initial operation, because the default build behavior of the Externalizable object makes it seems to be ongoing some kind of storage and recovery operation. But the truth is not the case.
TRANSIENT keyword
When control the serialization process, there may be a specific child object to automatically save and recover Java serialization mechanism. Generally, if the sub-object contains sensitive information (such as passwords) that does not want to serialize, it will face this. Even if the information has a "private" attribute in the object, once serialized, people can get a file or block the network transmission to get it.
In order to prevent the sensitive part of the object being serialized, one means to implement its own class as Externalizable, as in the previous display. In this way, there is no such thing as automatic serialization, and only the parties needed in WriteExternal ().
However, if the operation is a serializable object, all serialization operations are automatically performed. To solve this problem, you can use Transient (temporary) to close the serialization one by one, it means "Don't trouble you (refer to the automatic mechanism) save or restore it - I will handle it."
For example, suppose a login object contains information related to a specific login session. When checking the legitimacy of the login, it usually wants to save the data, but does not include the password. To do this, the easiest way is to implement Serializable and set the Password field to Transient. Here is the specific code:
499-500 page program
It can be seen that the DATE and Username fields maintain the original state (not TRANSIENT), so automatically serialize. However, Password is set to Transient, so it will not be saved to disk; in addition, the automatic serialization mechanism does not recover its attempt. The output is as follows:
501 page
Once the object is restored to the original look, the Password field will become NULL. Note You must use toString () to check if Password is null because the overloaded " " operator is assembled with a String object, and that operator encounters a NULL handle, it will cause a violation called NullPointerexception (new Java) You may provide code to avoid this problem).
We also found that the DATE field is saved to disk and recovered from disk without regenerating.
Because the Externalizable object does not save any fields when default, the Transient keyword can only be used with Serializable.
2. Alternative method for Externalizable
If it is not particularly intended to implement an Externalizable interface, there is another method to be available. We can implement the serializable interface and add (note that "add", rather than "override" or "implementation") named WriteObject () and readObject (). Once the object is serialized or re-assemble, the two methods are called. That is, as long as these two methods are provided, they will be prioritized without considering the default serialization mechanism. These methods must contain the following accurate signatures:
501 page program
From the perspective of the design, the situation has become a bit confusing. First, everyone may think that these methods are not part of the basic class or the serializable interface, which should be defined in their own interface. But please note that they are defined as "private", which means they can only call other members of this class. However, we don't actually call them from other members of this class, but to call our object's WriteObject () and readObject () methods by ObjectOutputStream and ObjectInputStream's WriteObject () and readObject () methods to call our object's WriteObject () and readObject () method (note me here Large inhibitory is to avoid using the same method name - because it is afraid of confusion). Everyone may be strange ObjectOutputStream and ObjectInputStream how to access our Private method for our class - can only think this is a trick playing with serialization mechanisms.
In any case, anything in the interface will automatically have a public property, so if WriteObject () and readObject () must be private, they cannot be part of the interface. But because we accurately add the signature, the final effect is the same as that of the implementation of an interface.
It seems that when we call ObjectOutputStream.WriteObject (), the Serializable object we passed to it seems to be checked whether to implement its own WriteObject (). If the answer is affirmed, it will skip the routine serialization process and call WriteObject (). ReadObject () will also encounter the same situation.
There is still another problem. In our WriteObject (), DefaultWriteObject () can be called to determine the default WriteObject () action. Similarly, DEFAULTREADOBJECT () can be called inside ReadObject (). The following simple example demonstrates how to control the storage and recovery of a serializable object:
502-503 page program
In this example, a String keeps the original state, which is set to Transient (temporary) to prove that the non-temporary field will be saved automatically by the defaultWriteObject () method, and the Transient field must be clearly saved and restored in the program. The field is initialized inside the builder instead of being defined, which proves that they will not be initialized by certain automation mechanisms when they are reassigned.
If you are ready to write to the non-Transient section of the object through the default mechanism, you must call the defaultWriteObject (), which makes it a first one in WriteObject (); and calls defaultReadObject () to make it the first operation as readObject (). . These are uncommon to call methods. For example, when we call DEFAULTWRITEOBJECT () for an ObjectOutputStream, and does not pass parameters for it, you need to take this action to know the handle of the object and how to write all non-Transient. This practice is very inconvenient. The storage and recovery of the Transient object uses our more familiar code. Now consider what happens. A SerialCTL object will be created in Main (), followed by sequence into an ObjectOutputStream (note that in this case is a buffer, not file - completely consistent with ObjectOutputStream). The formal serialization operation occurred in the following line code:
O.WriteObject (SC);
The WriteObject () method must check the SC to determine whether it has its own WriteObject () method (not checking its interface - it is not, nor the type of check class, but using the reflex method actual search method). If the answer is affirmative, use that method. Similar situations will occur on ReadObject (). Perhaps this is the only way to solve the problem, but it really looks a little quirky.
3. Version problem
Sometimes you might want to change a sequentially-sequential class (such as the original class of objects) that may be saved in the database). Although this approach has been supported, it is generally only used in very special cases. In addition, it requires an operator to have a relatively deep understanding of the principles behind, and we still don't want to achieve this depth. The HTML document of JDK 1.1 has been a very comprehensive discussion on this topic (can be downloaded from Sun Company, but may also become part of the Java development package).
10.9.3 Using "Persistence"
A more attractive idea is to use sequence chemical to save some status information of the program, so that the program is conveniently restored to the previous state. But before the specific implementation, some issues must be solved. If both objects have a handle to the third object, how do you serialize these two objects? If you restore them from two objects, the handle of the third object will only appear on an object? If you sequence these two objects into a separate file, then re-assemble them in different parts of the code, what results get?
The following example is a good description of the above questions:
504-506 page program
One interesting thing here is perhaps the serialization of an object to be applied to one byte array, thereby implementing a "comprehensive replication" for any serializable (serialized) object (comprehensive replication means copying the entire object network. Not only is the basic object and its handle). The replication problem will be fully described in Chapter 12.
Animal objects contain fields with type HOUSE. In Main (), one vector of these Animal is created, and serialized twice, which are sent to two different data streams. These data are re-assembled and printed, and the results below can be seen (the object is in different memory locations at each run, so the results of each run):
506-507 page program
Of course, we hope that the objects are equipped with different addresses. Note that the same address appears in Animals1 and Animals2, including shared, reference to the HOUSE object. On the other hand, when Animals3 is restored, the system has no way to know that the object within the other stream is the avatar of the first stream object, so a completely different object network is generated. As long as you sequence all things into a single data stream, you can restore the identical object network that is exactly the same as previously written, and it will not cause repetition of the object. Of course, between the time of writing the first and last object, can change the status of the object, but the object must be explicitly taken - serialization, the object uses any of the status (including them with other objects) The connection relationship is written.
To save the system status, the safest practice is to be serialized as a "micro" operation. If you have sequenced some things, then do some other work, then serialize more things, so that you will fail safely, you will fail safely. Instead, all objects constituting the system status should be placed in a single collection, and the writing is completed in one operation. In this way, only one method is called, it can be successfully restored.
Below this example is a set of imaginary computer-aided design (CAD) systems that have a good demonstration of this method. In addition, it also introduces the problem of Static fields. save. This is a wise practice anyway.
507-510 page program
The Shape (Geometric "class" Implement SERIALIZABLE), so anything inherited from Shape will automatically "serialize". Each Shape contains data, and each derived Shape class contains a special static field that determines all of those types of Shape (if you place a Static field into the base class, the result will only generate one Fields because the Static field is not copied in the derived class). The method in the basic class can be overwritten to set the color to different types (Static method does not dynamically bind, so these are common methods). Each time you call the RandomFactory () method, it creates a different shape (Shape value uses a random value).
CIRCLE (Circle) and Square belong to the direct extension of Shape; the only difference is that the Circle will initialize the color when defined, and Square is initialized in the builder. The problem of Line will be discussed later.
In Main (), a vector is used to accommodate the Class object and the other is used to accommodate the shape. If you do not provide the corresponding command line parameters, you will create Shapertypes Vector and add a Class object. Then create Shapes Vector and add a Shape object. Next, all static color color values will be set to Green, and all things will be serialized to file cadstate.out.
If a command line argument is provided (assuming cadstate.out), the file will be opened and the status of the program is recovered. No matter which case, the result produced Shape's Vector will print out. The following lists the results of its run:
511 page
It can be seen from it, the values of XPOS, YPOS, and DIM have been successfully saved and recovered. However, there is a problem when getting Static information. All "3" has been entered, but it is not normal. Circle has a 1 value (defined as Red), and Square has a 0 value (remember, they are initialized in the builder). It seems that STATIC has not been initialized at all! This is the case - although class Class is "sequentially", but cannot work according to our hopes. Therefore, if you want to sequence the static value, you must do your hand.
This is the purpose of serializestaticState () and deserializestaticState () in line. It can be seen that both methods are clearly called as part of the storage and recovery process (note that the write sequence file and the order of the readback cannot be changed). So in order to make CADSTATE.java correctly, one of the following three methods must be adopted:
(1) Add a serializestaticState () and deserializestaticState () to geometric shapes.
(2) Delete all code related to it
(3) Adding a call to new serialization and undo serialization static methods in a geometric shape
Another problem to note is safe because serialization processing will save the private data. If you need a confidential field, it should be marked into Transient. But after this, a safe information save method must be designed. In this way, once it needs to be recovered, you can reset those private variables.
10.10 Summary
The Java IO Logu can meet many of our basic requirements: you can read and write through console, file, memory block or even Internet (see Chapter 15). You can create a new input and output object type (inherited from InputStream and OutputStream). When an object is transmitted to a method that is expected to receive a string, since Java has restricted "automatic type conversion", the toString () method is automatically called. And we can redefine this TSTRING () to extend the type of objects accepted by a data stream.
Some problems have not been resolved during the online documentation and design process of the IO data stream library. For example, when we open a file to output, you can specify that once someone tries to overwrite the file, "throw" out a violation - some programming system allows us to specify the output file, but the only premise is not yet exist. However, in Java, it seems that a file object must be used to determine if a file exists because it will be overwritten if it is opened as FileoutputStream or FileWriter. If you specify a file and directory path, a shortcomings on the File class will be exposed, because it will say "Don't try to do too much in a single class"!
The IO library is easy to confuse some concepts. It does make many things, and it can also be transplanted. However, if there is no concept of decorator programs in advance, then all designs have a little blind property. So don't care about it, you have to spend some effort. And it is incomplete: no support for output formatting, and other IO packages in other languages provide this support (this is not corrected in Java 1.1, it is completely missed to change the library design) Opportunity, but add more specific situations, making complex degree further). Java 1.1 goes to those IO libraries that have not been replaced, rather than adding new libraries. Moreover, the design personnel do not seem to pointed out which features are not approved, which are preferred, and some annoying objections often occur in the library design.
However, once the decorator scheme is mastered, the benefits of this design will be recognized in some more flexible environments. At that time, the code to pay more for this, should not make you feel too angry. 10.11 practice
(1) Open a text file and read a line of content each time. Read each line as a String and place the String object into a vector. Print all rows in the VECTOR in the opposite order.
(2) Modifying Exercise 1, enabling the name of the file as a command line parameter.
(3) Modify the exercise 2 and open a text file to write the text into it. Write the line in the VECTOR to write the file with the same line.
(4) Modifying Exercise 2, forcing all rows in the VECTOR become a capital form, send the result to System.out.
(5) Modify the exercise 2, find the specified word in the file. Print all the texts that contain all the text to find words.
(6) Copy the file in Blips.java, rename it to BlipCheck.java. Then the class Blip2 is renamed BlipCheck (labeled it in the process). Delete //! Marks in the file and execute the program. Next, turn the BLIPCHECK's default builder into an comment message. Run it and explain why still work.
(7) In Blip3.java, the two lines after "You Must Do this:" will become a comment, and then run the program. Why is the result that the result is different from the two lines of code executed.
(8) Convert SortedWordCount.java programs to use Java 1.1 IO streams.
(9) CADSTATE.JAVA in accordance with the text of this chapter.
(10) Find the GreenhouseControls.java example in Chapter 7 (middle part), which should be constructed by three files. In GreenhouseControls.java, the Restart () The internal class has a hardcoded event set. Please modify this program so that it can dynamically read events from a text file and their related time.