`
leonzhx
  • 浏览: 786133 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论
阅读更多

1.  The abstract classes InputStream and OutputStream form the basis for a hierarchy of byte-oriented input/output (I/O) stream classes. A separate hierarchy provides classes for processing Unicode characters that inherit from the abstract Reader and Writer classes. These classes have read and write operations that are based on two-byte Unicode code units rather than on single-byte characters.

 

2.  The InputStream class has an abstract method: 

  abstract int read()

 

This method reads one byte and returns the byte that was read, or -1 if it encounters the end of the input source. It also has non-abstract methods to read an array of bytes or to skip a number of bytes. These methods call the abstract read method, so subclasses need to override only one method. Similarly, the OutputStream class defines the abstract method

    abstract void write(int b)

 

which writes one byte to an output location.

 

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

 

4.  The available method lets you check the number of bytes that are currently available for reading.

 

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

 

6.  Java gives you many stream classes derived from the basic InputStream and OutputStream classes that let you work with data in the forms that you usually use, not with bytes.

 

7.  InputStream.mark(int readlimit) puts a marker at the current position in the input stream (not all streams support this feature). If more than readlimit bytes have been read from the input stream before the reset method is called, the stream is allowed to forget the marker. InputStream.reset returns to the last marker. Subsequent calls to read reread the bytes. If there is no current marker, the stream is not reset. InputStream.markSupported returns true if the stream supports marking.

 

8.  The InputStream and OutputStream classes let you read and write individual bytes and arrays of bytes. These classes form the basis of the hierarchy below:



 The DataInputStream and DataOutputStream let you read and write all the primitive Java types in binary format. The ZipInputStream and ZipOutputStream let you read and write files in the familiar ZIP compression format. FileInputStream and FileOutputStream give you input and output streams attached to a disk file. You need to pass the file name or full path name of the file to the constructor.

 

9.  For Unicode text, on the other hand, you can use subclasses of the abstract classes Reader and Writer. The basic methods of the Reader and Writer classes are:

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

 

The read method returns either a Unicode code unit (as an integer between 0 and 65535) or -1 when you have reached the end of the file. The write method is called with a Unicode code unit.

 

10.  There are four additional interfaces: Closeable, Flushable, Readable, and Appendable. The first two interfaces are very simple, with methods: void close() throws IOException and void flush() respectively. The classes InputStream, OutputStream, Reader, and Writer all implement the Closeable interface. OutputStream and Writer implement the Flushable interface. The Readable interface has a single method int read(CharBuffer cb). It attempts to read as many char values into cb as it can hold. Returns the number of values read, or -1 if no further values are available. The CharBuffer class has methods for sequential and random read/write access. It represents an in-memory buffer or a memory-mapped file. The Appendable interface has two methods for appending single characters and character sequences: Appendable append(char c) and Appendable append(CharSequence s). The CharSequence interface describes basic properties of a sequence of char values. It is implemented by String, CharBuffer, StringBuilder, and StringBuffer. Of the stream zoo classes, only Writer implements Appendable.

 

11.  The java.io.Closeable interface extends the java.lang.AutoCloseable interface. Therefore, you can use the try-with-resources statement with any Closeable. Why have two interfaces? The close method of the Closeable interface only throws an IOException, whereas the AutoCloseable.close method may throw any exception.

 

12.  All the classes in java.io interpret relative path names as starting from the user’s working directory. You can get this directory by a call to System.getProperty("user.dir"). The working directory was set when the VM started. For portable programs, use the file separator character for the platform on which your program runs. It is available as the constant string java.io.File.separator.

 

13.  Java uses a clever mechanism to separate two kinds of responsibilities. Some streams (such as the FileInputStream and the input stream returned by the openStream method of the URL class) can retrieve bytes from files and other more exotic locations. Other streams (such as the DataInputStream and the PrintWriter) can assemble bytes into more useful data types. The Java programmer has to combine the two by nesting them (passing the instance of one to the constructor of the other).

 

14.  The subclasses of FilterInputStream and FilterOutputStream are used to add capabilities to raw byte streams. You can add multiple capabilities by nesting the filters.

 

15.  By default streams are not buffered. That is, every call to read asks the operating system to dole out yet another byte. It is more efficient to request blocks of data instead and store them in a buffer. If you want buffering and the data input methods for a file, you need:

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

 

16.  When reading input, you often need to peek at the next byte to see if it is the value that you expect. Java provides the PushbackInputStream for this purpose. You can speculatively read the next byte through read method and throw it back through unread method if it isn’t what you wanted.

 

17.  When saving text strings, you need to consider the character encoding. ISO 8859-1 is the encoding most commonly used in the United States and Western Europe.

 

18.  The OutputStreamWriter class turns a stream of Unicode code units into a stream of bytes, using a chosen character encoding. Conversely, the InputStreamReader class turns an input stream that contains bytes (specifying characters in some character encoding) into a reader that emits Unicode code units.

 

19.  For text output, use a PrintWriter. That class has methods to print strings and numbers in text format. There is even a convenience constructor to link a PrintWriter to a FileWriter (also buffered):

  PrintWriter out = new PrintWriter("employee.txt");

 

20.  To write to a print writer, use the same print, println, and printf methods that you used with System.out. The println method adds the correct end-of-line character for the target system ("\r\n" on Windows, "\n" on UNIX) to the line. This is the string obtained by the call System.getProperty("line.separator").

 

21.  If the writer is set to autoflush mode, all characters in the buffer are sent to their destination whenever println is called. (Print writers are always buffered.) By default, autoflushing is not enabled. You can enable or disable autoflushing by using the PrintWriter(Writer out, boolean autoFlush) constructor.

 

22.  The print methods don’t throw exceptions. You can call the checkError method to see if something went wrong with the stream.

 

23.  The PrintStream class internally converts Unicode characters to the default host encoding in the same way as the PrintWriter does. Objects of type PrintStream act exactly like print writers when you use the print and println methods, but unlike print writers they allow you to output raw bytes with the write(int) and write(byte[]) methods.

 

24.  The BufferedReader.readLine method returns null when no more input is available. A BufferedReader has no methods for reading numbers. It’s suggested that you use a Scanner for reading text input.

 

25.  A character set maps between sequences of Unicode characters and byte sequences used in a local character encoding. A popular character encoding is ISO 8859-1, a single-byte encoding of the first 256 Unicode characters. The Charset class uses the character set names standardized in the IANA Character Set Registry (www.iana.org/assignments/character-sets). For compatibility with other naming conventions, each character set can have a number of aliases. The Charset.aliases method returns a Set of String indicating the aliases. Character set names are case-insensitive. To obtain a Charset, call the static forName method with either the official name or one of its aliases.

 

26.  To find out which character sets are available in a particular implementation, call the static availableCharsets method. 

 

27.  Local encoding schemes cannot represent all Unicode characters. If a character cannot be represented, it is transformed to a ?.

 

28.  You can use a Charset to convert between Java strings (which contain Unicode code units) and encoded byte sequences:

String str = . . .;
ByteBuffer buffer = cset.encode(str);
byte[] bytes = buffer.array();

 

To decode a byte sequence, you need a byte buffer. Use the static wrap method of the ByteBuffer class to turn a byte array into a byte buffer. The result of the decode method is a CharBuffer. Call its toString method to get a string:

byte[] bytes = . . .;
ByteBuffer bbuf = ByteBuffer.wrap(bytes, offset, length);
CharBuffer cbuf = cset.decode(bbuf);
String str = cbuf.toString()

 

29.  The DataOutput interface defines the following methods for writing a number, a character, a boolean value, or a string in binary format: writeByte, writeInt, writeShort, writeLong, writeFloat, writeDouble, writeChar, writeBoolean, writeUTF. The resulting output is not human-readable, but the space needed will be the same for each value of a given type and reading it back in will be faster than parsing text.

 

30.  In Java, all values are written in the big-endian fashion, regardless of the processor.

 

31.  The writeUTF method writes string data using a modified version of 8-bit Unicode Transformation Format.This modified encoding is different for characters with codes higher than 0xFFFF. It is used for backward compatibility with virtual machines that were built when Unicode had not yet grown beyond 16 bits:

 

All characters in the range '\u0001' to '\u007F' are represented by a single byte:

        Bit Values
Byte 1
0                bits 6-0

 

The null character '\u0000' and characters in the range '\u0080' to '\u07FF' are represented by a pair of bytes:

  Bit Values
Byte 1
1 1 0 bits 10-6
Byte 2
1 0 bits 5-0

 

char values in the range '\u0800' to '\uFFFF' are represented by three bytes:

  Bit Values
Byte 1
1 1 1 0 bits 15-12
Byte 2
1 0 bits 11-6
Byte 3
1 0 bits 5-0

 

UTF-8 Encoding:

 

 

UTF-16 Encoding:

 

 

The differences between this format and the standard UTF-8 format are the following:

  • The null byte '\u0000' is encoded in 2-byte format rather than 1-byte, so that the encoded strings never have embedded nulls.
  • Only the 1-byte, 2-byte, and 3-byte formats are used.
  • Supplementary characters are represented in the form of surrogate pairs.

32.  To read the data back in, use the following methods defined in the DataInput interface: readInt, readShort, readLong, readFloat, readDouble, readChar, readBoolean, readUTF.

 

33.  The RandomAccessFile class lets you read or write data anywhere in a file. You can open a random-access file either for reading only or for both reading and writing; specify the option by using the string "r" (for read access) or "rw" (for read/write access) as the second argument in the constructor. A random-access file has a file pointer that indicates the position of the next byte to be read or written. The seek method sets the file pointer to an arbitrary byte position within the file. The argument to seek is a long integer between zero and the length of the file in bytes. The getFilePointer method returns the current position of the file pointer. To determine the total number of bytes in a file, use the length method.

 

34.  The RandomAccessFile class implements both the DataInput and DataOutput interfaces. 

 

35.  Each ZIP archive has a header with information such as the name of each file and the compression method that was used. You can use a ZipInputStream to read a ZIP archive. You need to look at the individual entries in the archive. The getNextEntry method returns an object of type ZipEntry that describes the entry. The read method of the ZipInputStream is modified to return -1 at the end of the current entry (instead of just at the end of the ZIP file). You must then call closeEntry to read the next entry:

 

ZipInputStream zin = new ZipInputStream(new FileInputStream(zipname));
ZipEntry entry;
while ((entry = zin.getNextEntry()) != null)
{
   analyze entry
   read the contents of zin
   zin.closeEntry();
}
zin.close();
 

 

36.  To write a ZIP file, use a ZipOutputStream. For each entry that you want to place into the ZIP file, create a ZipEntry object. Pass the file name to the ZipEntry constructor; it sets the other parameters such as file date and decompression method. Then, call the putNextEntry method of the ZipOutputStream to begin writing a new file. Send the file data to the ZIP stream. When you are done, call closeEntry:

 

FileOutputStream fout = new FileOutputStream("test.zip");
ZipOutputStream zout = new ZipOutputStream(fout);
for all files
{
   ZipEntry ze = new ZipEntry(filename);
   zout.putNextEntry(ze);
   send data to zout
   zout.closeEntry();
}
zout.close();
 

 

37.  JAR files are simply ZIP files with a special entry, the so-called manifest. Use the JarInputStream and JarOutputStream classes to read and write the manifest entry.

 

38.  In fact, whenever the class loader of an applet reads a JAR file, it reads and decompresses data from the network.

 

39.  To save object data, simply use the writeObject method of the ObjectOutputStream class as following:

 

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.dat"));
Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
out.writeObject(harry);
out.writeObject(boss);
 

To read the objects back in, retrieve the objects in the same order in which they were written, using the readObject method of ObjectInputStream class:

 

ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.dat"));
Employee e1 = (Employee) in.readObject();
Employee e2 = (Employee) in.readObject();
 

 

40.  To be able to save to and restore from an object stream, the class must implement the Serializable interface. The Serializable interface has no methods, you don’t need to change your classes in any way.

 

41.  An ObjectOutputStream looks at all the fields of the objects and saves their contents. 

 

42.  Each object is saved with a serial number, hence the name object serialization for this mechanism. Here is the algorithm:

  1)  Associate a serial number with each object reference that you encounter.

  2)  When encountering an object reference for the first time, save the object data to the stream.

  3)  If it has been saved previously, just write “same as the previously saved object with serial number x.”

When reading back the objects, the procedure is reversed.

  1)  When an object is specified in the stream for the first time, construct it, initialize it with the stream data, and remember the association between the serial number and the object reference.

  2)  When the tag “same as the previously saved object with serial number x” is encountered, retrieve the object reference for the sequence number.

Commented by Sean : so if you serialize an object twice, even if the state of the object was changed between two serialization, only the old state is saved.

 

43.  Every object serialization stream(the whole stream written by one ObjectOutputStream instance which may contain several serialized objects) begins with the two-byte “magic number”: AC ED followed by the version number of the object serialization format: 00 05. Then, it contains a sequence of objects, in the order in which they were saved.

 

44.  String objects are saved as 74 two-byte-length characters. For example, the string “Harry” is saved as 74 00 05 Harry. The Unicode characters of the string are saved in the “modified UTF-8” format.

 

45.  When an object is saved, the class of that object must be saved as well. The class description contains

    1)  The name of the class

    2)  The serial version unique ID, which is a fingerprint of the data field types and method signatures

    3)  A set of flags describing the serialization method

    4)  A description of the data fields

    The fingerprint is obtained by ordering the descriptions of the class, superclass, interfaces, field types, and method signatures in a canonical way, and then applying the so-called Secure Hash Algorithm (SHA) to that data. SHA is a fast algorithm that gives a “fingerprint” to a larger block of information. This fingerprint is always a 20-byte data packet, regardless of the size of the original data. However, the serialization mechanism uses only the first eight bytes of the SHA code as a class fingerprint. When reading an object, its fingerprint is compared against the current fingerprint of the class. If they don’t match, it means the class definition has changed after the object was written, and an exception is generated. Here is how a class identifier is stored:

    1)  72

    2)  2-byte length of class name

    3)  Class name

    4)  8-byte fingerprint

    5)  1-byte flag

    6)  2-byte count of data field descriptors

    7)  Data field descriptors

    8)  78 (end marker)

    9)  Superclass type (70 if none)

The flag byte is composed of three bit masks, defined in java.io.ObjectStreamConstants:

    1)  static final byte SC_WRITE_METHOD = 1;// class has a writeObject method that writes additional data

    2)  static final byte SC_SERIALIZABLE = 2; // class implements the Serializable interface

    3)  static final byte SC_EXTERNALIZABLE = 4; // class implements the Externalizable interface

The serializable java.util.Date class defines its own readObject/writeObject methods and has a flag of 03.

 

46.  Each data field descriptor has the format:

    1)  1-byte type code

    2)  2-byte length of field name

    3)  Field name

    4)  Class name (if the field is an object)

The type code is one of the following:

    B      byte

    C      char

    D      double

    F       float

    I        int

    J       long

    L      object

    S      short

    Z      boolean

    [      array

When the type code is L, the field name is followed by the field type. Class and field name strings do not start with code 74, but field types do. Field types use a slightly different encoding of their names—namely, the format used by native methods.(use / instead of . to separate package name)

 

47.  Here is the complete class descriptor of the Employeeclass:



 

 

48.  Arrays are saved in the following format:

    75 class-descriptor 4-byte-number-of-entries entries

 

49.  All objects (including arrays and strings) and all class descriptors are given serial numbers (The numbers starts from 00 7E 00 00.) as they are saved in the output file. If the same class descriptor is needed again in the file, an abbreviated form is used:71 4-byte-serial-number. It is always clear from the context whether the particular serial reference denotes a class descriptor or an object.

 

50.  A null reference is stored as 70.

 

51.  The object stream output contains the types and data fields of all objects. Each object is assigned a serial number. Repeated occurrences of the same object are stored as references to that serial number.

 

52.  Here is the commented output of the ObjectRefTest program:

 

Employee harry = new Employee("Harry Hacker", 50000, 1989, 10, 1);
Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15);
carl.setSecretary(harry);
Manager tony = new Manager("Tony Tester", 40000, 1990, 3, 15);
tony.setSecretary(harry);
Employee[] staff = new Employee[3];
staff[0] = carl;
staff[1] = harry;
staff[2] = tony;
// save all employee records to the file employee.dat
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.dat")))
{
    out.writeObject(staff);
}


 

 

 

53.  You need to tag fields as transient if they belong to non-serializable classes. Transient fields are always skipped when objects are serialized.

 

54.  A serializable class can define methods with the signature:

 

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
private void writeObject(ObjectOutputStream out) throws IOException;
 

 Then, the data fields are no longer automatically serialized, and these methods are called instead.

 

55.  The defaultWriteObject method is a special method of the ObjectOutputStream class that can only be called from within a writeObject method of a serializable class which write the object descriptor and the non-transient fields.

 

56.  The java.util.Date class supplies its own readObject and writeObject methods. These methods write the date as a number of milliseconds from the epoch (January 1, 1970, midnight UTC). The Date class has a complex internal representation that stores both a Calendar object and a millisecond count to optimize lookups. The state of the Calendar is redundant and does not have to be saved.

 

57.  The readObject and writeObject methods only need to save and load their data fields. They should not concern themselves with superclass data or any other class information.

 

58.  Instead of letting the serialization mechanism save and restore object data, a class can define its own mechanism by implementing the Externalizable interface. This, in turn, requires it to define two methods:

 

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
public void writeExternal(ObjectOutput out) throws IOException;
 

 

59.  Unlike the readObject and writeObject methods, these methods are fully responsible for saving and restoring the entire object, including the superclass data. The serialization mechanism merely records the class of the object in the stream. When reading an externalizable object, the object stream creates an object with the no-argument constructor (the no-argument constructor should be accessible) and then calls the readExternal method.

 

60.  If the readResolve method is defined (ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;), it is called after the object is deserialized. It must return an object which then becomes the return value of the readObject method. Remember to add a readResolve method to all typesafe enumerations in your legacy code and to all classes that follow the singleton design pattern. It applied to externalizable objects too (as result of readExternal). (Enum will handle it automatically)

 

61.  To obtain the fingerprint of a class, use the stand-alone serialver program that is part of the JDK:

serialver Employee

Employee: static final long serialVersionUID = -1814239825517340645L;

 

When a class has a static data member named serialVersionUID, it will not compute the fingerprint manually but will use that value instead. Once that static data member has been placed inside a class with same value, the serialization system is now willing to read in different versions of objects of that class.

 

62.  The object stream compares the data fields of the current version of the class with those of the version in the stream. Of course, the object stream considers only the non-transient and non-static data fields. If two fields have matching names but different types, the object stream makes no effort to convert one type to the other—the objects are incompatible(InvalidClassException will be thrown). If the object in the stream has data fields that are not present in the current version, the object stream ignores the additional data. If the current version has data fields that are not present in the streamed object, the added fields are set to their default (null for objects, zero for numbers, and false for boolean values).

 

63.  ObjectInputStream has a method:

 

public ObjectInputStream.GetField readFields() throws IOException, ClassNotFoundException
 

This mehod reads the persistent fields from the stream and makes them available by name. GetField has many get methods for primitive types and Object to get field value by name, such as get(String name, char val), and the val parameter is the default value if the field specified by the name parameter is not there. Note, the type of val parameter should be same as the field (specified by the name parameter) definition in the serializable class (otherwise exception will be thrown).  And if the filed type defined in the class is not same as the one defined in the serilization stream, an InvalidClassException will be thrown.

 

64.  Simply serializing an object to an output stream and then reading it back in will create a new object that is a deep copy of the existing object. This method, although clever, will usually be much slower than a clone method that explicitly constructs a new object and copies or clones the data fields.

 

65.  The Path and Files classes were added in Java SE 7. They are much more convenient to use than the File class which dates back all the way to JDK 1.0.

 

66.  A path that starts with a root component is absolute. Otherwise, it is relative. The static Paths.get method receives one or more strings, which it joins with the path separator of the default file system. It then parses the result, throwing an InvalidPathException if the result is not a valid path(valid doesn’t mean existing) in the given file system. The result is a Path object. 

 

67.  The call p.resolve(q) returns a path according to these rules:

    1)  If q is absolute, then the result is q.

    2)  Otherwise, the result is “p then q”, according to the rules of the file system.

There is an overloaded method for the resolve that takes a string instead of a path. There is a convenience method resolveSibling that resolves against a path’s parent, yielding a sibling path.

 

68.  The opposite of resolve is relativize. The call p.relativize(r) yields the path q which, when resolved with p, yields r

 

69.  The normalize method removes any redundant . and .. components.

 

70.  The toAbsolutePath method yields the absolute path of a given path, starting at a root component.

 

71.  The Path class has a toFile method, and the File class has a toPath method.

 

72.  The Files class makes quick work of common file operations. You can easily read the entire contents of a file:

 

byte[] bytes = Files.readAllBytes(path);
 

if you want the file as a sequence of lines, call

 

List<String> lines = Files.readAllLines(path, charset);

if you want to write a string, call

 

Files.write(path, content.getBytes(charset));
 

To append to a given file, use

 

Files.write(path, content.getBytes(charset), StandardOpenOption.APPEND);

You can also write a collection of lines with

 

Files.write(path, lines);
 

These simple methods are intended for dealing with text files of moderate length. If your files are large or binary, you can still use the streams or readers/writers:

 

InputStream in = Files.newInputStream(path);
OutputStream out = Files.newOutputStream(path);
Reader in = Files.newBufferedReader(path, charset);
Writer out = Files.newBufferedWriter(path, charset);
 

73.  To copy a file from one location to another, simply call

 

Files.copy(fromPath, toPath);

To move the file, call

 

Files.move(fromPath, toPath);

 

The copy or move will fail if the target exists. If you want to overwrite an existing target, use the REPLACE_EXISTING option. If you want to copy all file attributes, use the COPY_ATTRIBUTES option:

 

Files.copy(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
 

You can specify that a move should be atomic. Then you are assured that either the move completed successfully, or the source continues to be present. Use the ATOMIC_MOVE option:

 

Files.move(fromPath, toPath, StandardCopyOption.ATOMIC_MOVE);
 

to delete a file, simply call

 

Files.delete(path);
 

This method throws an exception if the file doesn’t exist, so instead you may want to use

 

boolean deleted = Files.deleteIfExists(path);
 

 74.  To create a new directory, call

 

Files.createDirectory(path);
 

All but the last component in the path must already exist. To create intermediate directories as well, use

 

Files.createDirectories(path);
 

You can create an empty file with

 

Files.createFile(path);
 

The call throws an exception if the file already exists. The check for existence and creation are atomic.

 

75.  The following static methods return a boolean value to check a property of a path:

  1)  exists

  2)  isHidden

  3)  isReadable, isWritable, isExecutable

  4)  isRegularFile, isDirectory, isSymbolicLink

 

76.  The size method returns the number of bytes in a file.

 

long fileSize = Files.size(path);
 

The getOwner method returns the owner of the file, as an instance of java.nio.file.attribute.UserPrincipal.

 

77.  All file systems report a set of basic attributes, encapsulated by the BasicFileAttributes interface. The basic file attributes are

  1)  The times at which the file was created, last accessed, and last modified, as instances of the class java.nio.file.attribute.FileTime

  2)  Whether the file is a regular file, a directory, a symbolic link, or none of these

  3)  The file size

  4)  The file key—an object of some class, specific to the file system, that may or may not uniquely identify a file

To get these attributes, call

 

BasicFileAttributes attributes = Files.readAttributes(path, BasicFileAttributes.class);
 

If you know that the user’s file system is POSIX-compliant, you can instead get an instance of PosixFileAttributes:

 

PosixFileAttributes attributes = Files.readAttributes(path, PosixFileAttributes.class);
 

78.  The old File class had a method for getting an array of all files in a directory, but that led to poor performance with directories holding huge numbers of files. For that reason, the Files class has a method that yields an Iterable object instead:

 

try (DirectoryStream<Path> entries = Files.newDirectoryStream(dir))
{
   for (Path entry : entries)
      Process entries
}
 

 You can filter the files with a glob pattern:

 

DirectoryStream<Path> entries = Files.newDirectoryStream(dir, "*.java");
 

The following chart shows all glob patterns:

 

 

79.  If you want to visit all descendants of a directory, call the walkFileTree method instead and supply an object of type FileVisitor. That object gets notified

    1)  When a file or directory is encountered: FileVisitResult visitFile(T path, BasicFileAttributes attrs)

    2)  Before a directory is processed: FileVisitResult preVisitDirectory(T dir, IOException ex)

    3)  After a directory is processed: FileVisitResult postVisitDirectory(T dir, IOException ex)

    4)  When an error occurred trying to visit a file or directory, such as trying to open a directory without the necessary permissions: FileVisitResult visitFileFailed(T path, IOException ex)

 

In each case, you can specify whether you want to

    1)  Continue visiting the next file: FileVisitResult.CONTINUE

    2)  Continue the walk, but without visiting the entries in this directory:FileVisitResult.SKIP_SUBTREE

    3)  Continue the walk, but without visiting the siblings of this file: FileVisitResult.SKIP_SIBLINGS

    4)  Terminate the walk: FileVisitResult.TERMINATE

If any of the methods throws an exception, the walk is also terminated, and that exception is thrown from the walkFileTree method.

 

80.  A convenience class SimpleFileVisitor implements the FileVisitor interface. All but the visitFileFailed method do nothing and continue. The visitFileFailed method throws the exception that caused the failure, thereby terminating the visit.

 

81.  If zipname is the name of a ZIP file, then the call

 

FileSystem fs = FileSystems.newFileSystem(Paths.get(zipname), null);
establishes a file system that contains all files in the ZIP archive. It’s an easy matter to copy a file out of that archive if you know its name:

 

Files.copy(fs.getPath(sourceName), targetPath);
 

Here, fs.getPath is the analog of Paths.get for an arbitrary file system.

To list all files in a ZIP archive, walk the file tree:

 

FileSystem fs = FileSystems.newFileSystem(Paths.get(zipname), null);
Files.walkFileTree(fs.getPath("/"), new SimpleFileVisitor<Path>()
   {
      public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
      {
         System.out.println(file);
         return FileVisitResult.CONTINUE;
      }
   });
 

 

82.  Most operating systems can take advantage of the virtual memory implementation to “map” a file, or a region of a file, into memory. Then the file can be accessed as if it were an in-memory array, which is much faster than the traditional file operations. The java.nio package makes memory mapping quite simple.

    1)  First, get a channel for the file. A channel is an abstraction for a disk file that lets you access operating system features such as memory mapping, file locking, and fast data transfers between files.

 

FileChannel channel = FileChannel.open(path, options);
 

    2)  Then, get a ByteBuffer from the channel by calling the map method of the FileChannel class. Specify the area of the file that you want to map and a mapping mode. Three modes are supported:

        a)  FileChannel.MapMode.READ_ONLY: The resulting buffer is read-only. Any attempt to write to the buffer results in a ReadOnlyBufferException.

         b)  FileChannel.MapMode.READ_WRITE: The resulting buffer is writable, and the changes will be written back to the file at some time. Note that other programs that have mapped the same file might not see those changes immediately. The exact behavior of simultaneous file mapping by multiple programs depends on the operating system.

        c)  FileChannel.MapMode.PRIVATE: The resulting buffer is writable, but any changes are private to this buffer and not propagated to the file.

    3)  Once you have the buffer, you can read and write data using the methods of the ByteBuffer class and the Buffer superclass. 

 

82.  Buffers support both sequential and random data access. A buffer has a position that is advanced by get and put operations. For example, you can sequentially traverse all bytes in the buffer as

 

while (buffer.hasRemaining())
{
   byte b = buffer.get();
   . . .
}
 

Alternatively, you can use random access:

 

for (int i = 0; i < buffer.limit(); i++)
{
   byte b = buffer.get(i);
   . . .
}
 

You can also read and write arrays of bytes with the methods

 

get(byte[] bytes)
get(byte[], int offset, int length)
 

Finally, there are methods

getInt

getLong

getShort

getChar

getFloat

getDouble

to read primitive type values that are stored as binary values in the file. Java uses big-endian ordering for binary data. However, if you need to process a file containing binary numbers in little-endian order, simply call

 

buffer.order(ByteOrder.LITTLE_ENDIAN);
 

To find out the current byte order of a buffer, call

 

ByteOrder b = buffer.order()
 

To write numbers to a buffer, use one of the methods

putInt

putLong

putShort

putChar

putFloat

putDouble

At some point, and certainly when the channel is closed, these changes are written back to the file. 

 

84.  A buffer is an array of values of the same type. The Buffer class is an abstract class with concrete subclasses ByteBuffer, CharBuffer,  DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, and ShortBuffer.

 

85.  A buffer has

    1)  A capacity that never changes

    2)  A position at which the next value is read or written

    3)  A limit beyond which reading and writing is meaningless

    4)  Optionally, a mark for repeating a read or write operation



 

These values fulfill the condition: 0 ≤ markpositionlimitcapacity

 

86.  The principal purpose of a buffer is a “write, then read” cycle. At the outset, the buffer’s position is 0 and the limit is the capacity. Keep calling put to add values to the buffer. When you run out of data or reach the capacity, it is time to switch to reading. Call flip to set the limit to the current position and the position to 0. Now keep calling get while the remaining method (which returns limitposition) is positive. When you have read all values in the buffer, call clear to prepare the buffer for the next writing cycle. The clear method resets the position to 0 and the limit to the capacity. If you want to reread the buffer, use rewind (setting the position to 0 and leaving the limit unchanged) or mark (setting the mark to the position)/reset (setting the position to the mark). 

 

87.  To get a buffer, call a static method such as ByteBuffer.allocate or ByteBuffer.wrap. Then, you can fill a buffer from a channel, or write its contents to a channel:

ByteBuffer buffer = ByteBuffer.allocate(RECORD_SIZE);
channel.read(buffer);
channel.position(newpos);
buffer.flip();
channel.write(buffer);

 

88.  A file lock controls access to a file or a range of bytes within a file. To lock a file, call either the lock or tryLock methods of the FileChannel class.

FileChannel = FileChannel.open(path);
FileLock lock = channel.lock(); 
// or FileLock lock = channel.tryLock();

 

The first call blocks until the lock becomes available. The second call returns immediately, either with the lock or with null if the lock is not available. The file remains locked until the channel is closed or the release method is invoked on the lock. You can also lock a portion of the file with the call

FileLock lock(long start, long size, boolean shared)
FileLock tryLock(long start, long size, boolean shared)

 

The shared flag is false to lock the file for both reading and writing. It is true for a shared lock, which allows multiple processes to read from the file, while preventing any process from acquiring an exclusive lock. Not all operating systems support shared locks. You may get an exclusive lock even if you just asked for a shared one. Call the isShared method of the FileLock class to find out which kind you have.

 

89.  If you lock the tail portion of a file and the file subsequently grows beyond the locked portion, the

additional area is not locked. To lock all bytes, use a size of Long.MAX_VALUE.

 

90.  Be sure to unlock the lock when you are done. As always, this is best done with a try-with-resources statement:

try (FileLock lock = channel.lock())
{
   access the locked file or segment
}

 

90.  File locking is system-dependent. Here are some points to watch for:

    1)  On some systems, file locking is merely advisory. If an application fails to get a lock, it may still write to a file that another application has currently locked.

    2)  On some systems, you cannot simultaneously lock a file and map it into memory.

    3)  File locks are held by the entire Java virtual machine. If two programs are launched by the same virtual machine (such as an applet or application launcher), they can’t each acquire a lock on the same file. The lock and tryLock methods will throw an OverlappingFileLockException if the virtual machine already holds another overlapping lock on the same file.

    4)  On some systems, closing a channel releases all locks on the underlying file held by the Java virtual machine. You should therefore avoid multiple channels on the same locked file.

    5)  Locking files on a networked file system is highly system-dependent and should probably be avoided.

 

  • 大小: 185.6 KB
  • 大小: 62.8 KB
  • 大小: 211 KB
  • 大小: 494.2 KB
  • 大小: 50.3 KB
  • 大小: 43.7 KB
  • 大小: 160 KB
  • 大小: 99.7 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics