Thursday 23 March 2017

The Byte Streams - Java Tutorials ( Page 2 of 2 )

ByteArrayOutputStream

ByteArrayOutputStream is an implementation of an output stream that uses a byte array as the destination. ByteArrayOutputStream has two constructors, shown here:

      ByteArrayOutputStream( )
      ByteArrayOutputStream(int numBytes)

In the first form, a buffer of 32 bytes is created. In the second, a buffer is created with a size equal to that specified by numBytes. The buffer is held in the protected buf field of ByteArrayOutputStream. The buffer size will be increased automatically, if needed. The number of bytes held by the buffer is contained in the protected count field of ByteArrayOutputStream.

The following example demonstrates ByteArrayOutputStream:

  // Demonstrate ByteArrayOutputStream.
  import java.io.*;

  class ByteArrayOutputStreamDemo {
    public static void main(String args[]) throws IOException {
      ByteArrayOutputStream f = new ByteArrayOutputStream();
      String s = "This should end up in the array";
      byte buf[] = s.getBytes();

      f.write(buf);
      System.out.println("Buffer as a string");
      System.out.println(f.toString());
      System.out.println("Into array");
      byte b[] = f.toByteArray();
      for (int i=0; i<b.length; i++) {
        System.out.print((char) b[i]);
      }
      System.out.println("\nTo an OutputStream()");
      OutputStream f2 = new FileOutputStream("test.txt");

      f.writeTo(f2);
      f2.close();
      System.out.println("Doing a reset");
      f.reset();
      for (int i=0; i<3; i++)
        f.write('X');
      System.out.println(f.toString());
    }
  }

When you run the program, you will create the following output. Notice how after the
call to reset( ), the three X’s end up at the beginning.

  Buffer as a string
  This should end up in the array
  Into array
  This should end up in the array
  To an OutputStream()
  Doing a reset
  XXX

This example uses the writeTo( ) convenience method to write the contents of f to test.txt. Examining the contents of the test.txt file created in the preceding example shows the result we expected:

  This should end up in the array


Filtered Byte Streams

Filtered streams are simply wrappers around underlying input or output streams that transparently provide some extended level of functionality. These streams are typically accessed by methods that are expecting a generic stream, which is a superclass of the filtered streams. Typical extensions are buffering, character translation, and raw data translation. The filtered byte streams are FilterInputStream and FilterOutputStream. Their constructors are shown here:

      FilterOutputStream(OutputStream os)
      FilterInputStream(InputStream is)

The methods provided in these classes are identical to those in InputStream and OutputStream.


Buffered Byte Streams

For the byte-oriented streams, a buffered stream extends a filtered stream class by attaching a memory buffer to the I/O streams. This buffer allows Java to do I/O operations on more than a byte at a time, hence increasing performance. Because the buffer is available, skipping, marking, and resetting of the stream becomes possible. The buffered byte stream classes are BufferedInputStream and BufferedOutputStream. PushbackInputStream also implements a buffered stream.


BufferedInputStream

Buffering I/O is a very common performance optimization. Java’s BufferedInputStream class allows you to “wrap” any InputStream into a buffered stream and achieve this performance improvement.

BufferedInputStream has two constructors:

      BufferedInputStream(InputStream inputStream)
      BufferedInputStream(InputStream inputStream, int bufSize)

The first form creates a buffered stream using a default buffer size. In the second, the size of the buffer is passed in bufSize. Use of sizes that are multiples of memory page, disk block, and so on can have a significant positive impact on performance. This is, however, implementation-dependent. An optimal buffer size is generally dependent on the host operating system, the amount of memory available, and how the machine is configured. To make good use of buffering doesn’t necessarily require quite this degree of sophistication. A good guess for a size is around 8,192 bytes, and attaching even a rather small buffer to an I/O stream is always a good idea. That way, the low-level system can read blocks of data from the disk or network and store the results in your buffer. Thus, even if you are reading the data a byte at a time out of the InputStream, you will be manipulating fast memory over 99.9 percent of the time.

Buffering an input stream also provides the foundation required to support moving backward in the stream of the available buffer. Beyond the read( ) and skip( ) methods implemented in any InputStream, BufferedInputStream also supports the mark( ) and reset( ) methods. This support is reflected by BufferedInputStream.markSupported( ) returning true.

The following example contrives a situation where we can use mark( ) to remember where we are in an input stream and later use reset( ) to get back there. This example is parsing a stream for the HTML entity reference for the copyright symbol. Such a reference begins with an ampersand (&) and ends with a semicolon (;) without any intervening whitespace. The sample input has two ampersands to show the case where the reset( ) happens and where it does not.

  // Use buffered input.
  import java.io.*;

  class BufferedInputStreamDemo {
    public static void main(String args[]) throws IOException {
      String s = "This is a &copy; copyright symbol " +
        "but this is &copy not.\n";
      byte buf[] = s.getBytes();
      ByteArrayInputStream in = new ByteArrayInputStream(buf);
      BufferedInputStream f = new BufferedInputStream(in);
      int c;
      boolean marked = false;

      while ((c = f.read()) != -1) {
        switch(c) {
        case '&':
          if (!marked) {
            f.mark(32);
            marked = true;
          } else {
            marked = false;
          }
          break;
        case ';':
          if (marked) {
            marked = false;
            System.out.print("(c)");
          } else
            System.out.print((char) c);
          break;
        case ' ':
          if (marked) {
            marked = false;
            f.reset();
            System.out.print("&");
          } else
            System.out.print((char) c);
          break;
        default:
          if (!marked)
              System.out.print((char) c);
          break;
        }
      }
    }
  }

Notice that this example uses mark(32), which preserves the mark for the next 32 bytes read (which is enough for all entity references). Here is the output produced by this program:

  This is a (c) copyright symbol but this is &copy not.

Use of mark( ) is restricted to access within the buffer. This means that you can only specify a parameter to mark( ) that is smaller than the buffer size of the stream.


BufferedOutputStream

A BufferedOutputStream is similar to any OutputStream with the exception of an added flush( ) method that is used to ensure that data buffers are physically written to the actual output device. Since the point of a BufferedOutputStream is to improve performance by reducing the number of times the system actually writes data, you may need to call flush( ) to cause any data that is in the buffer to get written.

Unlike buffered input, buffering output does not provide additional functionality. Buffers for output in Java are there to increase performance. Here are the two available constructors:

      BufferedOutputStream(OutputStream outputStream)
      BufferedOutputStream(OutputStream outputStream, int bufSize)

The first form creates a buffered stream using a buffer of 512 bytes. In the second form, the size of the buffer is passed in bufSize.


PushbackInputStream

One of the novel uses of buffering is the implementation of pushback. Pushback is used on an input stream to allow a byte to be read and then returned (that is, “pushed back”) to the stream. The PushbackInputStream class implements this idea. It provides a mechanism to “peek” at what is coming from an input stream without disrupting it.

PushbackInputStream has the following constructors:

      PushbackInputStream(InputStream inputStream)
      PushbackInputStream(InputStream inputStream, int numBytes)

The first form creates a stream object that allows one byte to be returned to the input tream. The second form creates a stream that has a pushback buffer that is numBytes long. This allows multiple bytes to be returned to the input stream.

Beyond the familiar methods of InputStream, PushbackInputStream provides unread( ), shown here:

      void unread(int ch)
      void unread(byte buffer[ ])
      void unread(byte buffer, int offset, int numChars)

The first form pushes back the low-order byte of ch. This will be the next byte returned by a subsequent call to read( ). The second form returns the bytes in buffer. The third form pushes back numChars bytes beginning at offset from buffer. An IOException will be thrown if there is an attempt to return a byte when the pushback buffer is full.

Java 2 made a small change to PushbackInputStream: it added the skip( ) method. Here is an example that shows how a programming language parser might use a PushbackInputStream and unread( ) to deal with the difference between the = = operator for comparison and the = operator for assignment:

  // Demonstrate unread().
  import java.io.*;

  class PushbackInputStreamDemo {
    public static void main(String args[]) throws IOException {
      String s = "if (a == 4) a = 0;\n";
      byte buf[] = s.getBytes();
      ByteArrayInputStream in = new ByteArrayInputStream(buf);
      PushbackInputStream f = new PushbackInputStream(in);
      int c;

      while ((c = f.read()) != -1) {
        switch(c) {
        case '=':
        if ((c = f.read()) == '=')
          System.out.print(".eq.");
        else {
          System.out.print("<-");
          f.unread(c);
        }
        break;
      default:
        System.out.print((char) c);
        break;
        }
      }
    }
  }

Here is the output for this example. Notice that = = was replaced by “.eq.” and = was replaced by “<–”.

  if (a .eq. 4) a <- 0;
  
PushbackInputStream has the side effect of invalidating the mark( ) or reset( ) methods of the InputStream used to create it. Use markSupported( ) to check any stream on which you are going to use mark( )/reset( ).


SequenceInputStream

The SequenceInputStream class allows you to concatenate multiple InputStreams. The construction of a SequenceInputStream is different from any other InputStream. A SequenceInputStream constructor uses either a pair of InputStreams or an Enumeration of InputStreams as its argument:

      SequenceInputStream(InputStream first, InputStream second)
      SequenceInputStream(Enumeration streamEnum)

Operationally, the class fulfills read requests from the first InputStream until it runs out and then switches over to the second one. In the case of an Enumeration, it will continue through all of the InputStreams until the end of the last one is reached.

Here is a simple example that uses a SequenceInputStream to output the contents of two files:

  // Demonstrate sequenced input.
  import java.io.*;
  import java.util.*;

  class InputStreamEnumerator implements Enumeration {
    private Enumeration files;
    public InputStreamEnumerator(Vector files) {
      this.files = files.elements();
    }

    public boolean hasMoreElements() {
      return files.hasMoreElements();
    }

    public Object nextElement() {
      try {
        return new FileInputStream(files.nextElement().toString());
      } catch (Exception e) {
        return null;
      }
    }
  }

  class SequenceInputStreamDemo {
    public static void main(String args[]) throws Exception {
      int c;
      Vector files = new Vector();

      files.addElement("/autoexec.bat");
      files.addElement("/config.sys");
      InputStreamEnumerator e = new InputStreamEnumerator(files);
      InputStream input = new SequenceInputStream(e);

      while ((c = input.read()) != -1) {
        System.out.print((char) c);
      }
      input.close();
    }
  }

This example creates a Vector and then adds two filenames to it. It passes that vector of names to the InputStreamEnumerator class, which is designed to provide a wrapper on the vector where the elements returned are not the filenames but rather open FileInputStreams on those names. The SequenceInputStream opens each file in turn, and this example prints the contents of the two files.


PrintStream

The PrintStream class provides all of the formatting capabilities we have been using from the System file handle, System.out, since the beginning of the book. Here are two of its constructors:

      PrintStream(OutputStream outputStream)
      PrintStream(OutputStream outputStream, boolean flushOnNewline)

where flushOnNewline controls whether Java flushes the output stream every time a newline (\n) character is output. If flushOnNewline is true, flushing automatically takes place. If it is false, flushing is not automatic. The first constructor does not automatically flush.

Java’s PrintStream objects support the print( ) and println( ) methods for all types, including Object. If an argument is not a simple type, the PrintStream methods will call the object’s toString( ) method and then print the result.


RandomAccessFile

RandomAccessFile encapsulates a random-access file. It is not derived from InputStream or OutputStream. Instead, it implements the interfaces DataInput and DataOutput, which define the basic I/O methods. It also supports positioning requests—that is, you can position the file pointer within the file. It has these two constructors:

      RandomAccessFile(File fileObj, String access)
          throws FileNotFoundException

      RandomAccessFile(String filename, String access)
          throws FileNotFoundException

In the first form, fileObj specifies the name of the file to open as a File object. In the second form, the name of the file is passed in filename. In both cases, access determines what type of file access is permitted. If it is “r”, then the file can be read, but not written. If it is “rw”, then the file is opened in read-write mode. If it is “rws”, the file is opened for read-write operations and every change to the file’s data or metadata will be immediately written to the physical device. If it is “rwd”, the file is opened for read-write operations and every change to the file’s data will be immediately written to the physical device.

The method seek( ), shown here, is used to set the current position of the file pointer within the file:

      void seek(long newPos) throws IOException

Here, newPos specifies the new position, in bytes, of the file pointer from the beginning of the file. After a call to seek( ), the next read or write operation will occur at the new file position.

RandomAccessFile implements the standard input and output methods, which you can use to read and write to random access files. It also includes some additional methods. One is setLength( ). It has this signature:

      void setLength(long len) throws IOException

This method sets the length of the invoking file to that specified by len. This method can be used to lengthen or shorten a file. If the file is lengthened, the added portion is undefined. Java 2, version 1.4 added the getChannel( ) method to RandomAccessFile. This method returns a channel connected to the RandomAccessFile object. Channels are used by the new I/O classes contained in java.nio.

The Byte Streams - Java Tutorials ( Page 1 of 2 )

The Stream Classes

Java’s stream-based I/O is built upon four abstract classes: InputStream, OutputStream, Reader, and Writer. These classes were briefly discussed in Chapter 12. They are used to create several concrete stream subclasses. Although your programs perform their I/O operations through concrete subclasses, the top-level classes define the basic functionality common to all stream classes.

InputStream and OutputStream are designed for byte streams. Reader and Writer are designed for character streams. The byte stream classes and the character stream classes form separate hierarchies. In general, you should use the character stream classes when working with characters or strings, and use the byte stream classes when working with bytes or other binary objects.

In the remainder of this chapter, both the byte- and character-oriented streams are examined.



The Byte Streams

The byte stream classes provide a rich environment for handling byte-oriented I/O. A byte stream can be used with any type of object, including binary data. This versatility makes byte streams important to many types of programs. Since the byte stream classes are topped by InputStream and OutputStream, our discussion will begin with them.


InputStream

InputStream is an abstract class that defines Java’s model of streaming byte input. All of the methods in this class will throw an IOException on error conditions. 


The Methods Defined by InputStream

int available( ):  Returns the number of bytes of input currently available for reading.

void close( ):  Closes the input source. Further read attempts will generate an IOException.

void mark(int numBytes):  Places a mark at the current point in the input stream that will remain valid until numBytes bytes are read.

boolean markSupported( ):  Returns true if mark( )/reset( ) are supported by the invoking stream.

int read( ):  Returns an integer representation of the next available byte of input. –1 is returned when the end of the file is encountered.

int read(byte buffer[ ]):  Attempts to read up to buffer.length bytes into buffer and returns the actual number of bytes that were successfully read. –1 is returned when the end of the file is encountered.

int read(byte buffer[ ], int offset, int numBytes):  Attempts to read up to numBytes bytes into buffer starting at buffer[offset], returning the number of bytes successfully read. –1 is returned when the end of the file is encountered.

void reset( ):  Resets the input pointer to the previously set mark.

long skip(long numBytes):  Ignores (that is, skips) numBytes bytes of input, returning the number of bytes actually ignored.


OutputStream

OutputStream is an abstract class that defines streaming byte output. All of the methods in this class return a void value and throw an IOException in the case of errors.


The Methods Defined by OutputStream

void close( ):  Closes the output stream. Further write attempts will generate an IOException.

void flush( ):  Finalizes the output state so that any buffers are cleared. That is, it flushes the output buffers.

void write(int b):  Writes a single byte to an output stream. Note that the parameter is an int, which allows you to call write( ) with expressions without having to cast them back to byte.

void write(byte buffer[ ]):  Writes a complete array of bytes to an output stream.

void write(byte buffer[ ], int offset, int numBytes):  Writes a subrange of numBytes bytes from the array buffer, beginning at buffer[offset].


FileInputStream

The FileInputStream class creates an InputStream that you can use to read bytes from a file. Its two most common constructors are shown here:

      FileInputStream(String filepath)
      FileInputStream(File fileObj)

Either can throw a FileNotFoundException. Here, filepath is the full path name of a file, and fileObj is a File object that describes the file.

The following example creates two FileInputStreams that use the same disk file and each of the two constructors:

  FileInputStream f0 = new FileInputStream("/autoexec.bat")
  File f = new File("/autoexec.bat");
  FileInputStream f1 = new FileInputStream(f);

Although the first constructor is probably more commonly used, the second allows us to closely examine the file using the File methods, before we attach it to an input stream. When a FileInputStream is created, it is also opened for reading.

FileInputStream overrides six of the methods in the abstract class InputStream. The mark( ) and reset( ) methods are not overridden, and any attempt to use reset( ) on a FileInputStream will generate an IOException.

The next example shows how to read a single byte, an array of bytes, and a subrange array of bytes. It also illustrates how to use available( ) to determine the number of bytes remaining, and how to use the skip( ) method to skip over unwanted bytes. The program reads its own source file, which must be in the current directory.

  // Demonstrate FileInputStream.
  import java.io.*;

  class FileInputStreamDemo {
    public static void main(String args[]) throws Exception {
      int size;
      InputStream f =
        new FileInputStream("FileInputStreamDemo.java");

      System.out.println("Total Available Bytes: " +
                         (size = f.available()));
      int n = size/40;
      System.out.println("First " + n +
                         " bytes of the file one read() at a time");
      for (int i=0; i < n; i++) {
        System.out.print((char) f.read());
      }
      System.out.println("\nStill Available: " + f.available());
      System.out.println("Reading the next " + n +
                         " with one read(b[])");
      byte b[] = new byte[n];
      if (f.read(b) != n) {
        System.err.println("couldn't read " + n + " bytes.");
      }
      System.out.println(new String(b, 0, n));
      System.out.println("\nStill Available: " + 
                         (size = f.available()));
      System.out.println("Skipping half of remaining bytes 
                         with skip()");
      f.skip(size/2);
      System.out.println("Still Available: " + f.available());
      System.out.println("Reading " + n/2 + 
                         " into the end of array");
      if (f.read(b, n/2, n/2) != n/2) {
        System.err.println("couldn't read " + n/2 + " bytes.");
      }
      System.out.println(new String(b, 0, b.length));
      System.out.println("\nStill Available: " + f.available());
      f.close();
    }
  }

Here is the output produced by this program:

  Total Available Bytes: 1433
  First 35 bytes of the file one read() at a time
  // Demonstrate FileInputStream.
  im
  Still Available: 1398

  Reading the next 35 with one read(b[])
  port java.io.*;

  class FileInputS

  Still Available: 1363
  Skipping half of remaining bytes with skip()
  Still Available: 682
  Reading 17 into the end of array
  port java.io.*;
  read(b) != n) {
  S

  Still Available: 665

This somewhat contrived example demonstrates how to read three ways, to skip input, and to inspect the amount of data available on a stream.

Java 2, version 1.4 added the getChannel( ) method to FileInputStream. This method returns a channel connected to the FileInputStream object. Channels are used by the new I/O classes contained in java.nio.


FileOutputStream

FileOutputStream creates an OutputStream that you can use to write bytes to a file. Its most commonly used constructors are shown here:

      FileOutputStream(String filePath)
      FileOutputStream(File fileObj)
      FileOutputStream(String filePath, boolean append)
      FileOutputStream(File fileObj, boolean append)

They can throw a FileNotFoundException or a SecurityException. Here, filePath is the full path name of a file, and fileObj is a File object that describes the file. If append is true, the file is opened in append mode. The fourth constructor was added by Java 2, version 1.4.

Creation of a FileOutputStream is not dependent on the file already existing. FileOutputStream will create the file before opening it for output when you create the object. In the case where you attempt to open a read-only file, an IOException will be thrown.

The following example creates a sample buffer of bytes by first making a String and then using the getBytes( ) method to extract the byte array equivalent. It then creates three files. The first, file1.txt, will contain every other byte from the sample. The second, file2.txt, will contain the entire set of bytes. The third and last, file3.txt, will contain only the last quarter. Unlike the FileInputStream methods, all of the FileOutputStream methods have a return type of void. In the case of an error, these
methods will throw an IOException.

  // Demonstrate FileOutputStream.
  import java.io.*;

  class FileOutputStreamDemo {
    public static void main(String args[]) throws Exception {
      String source = "Now is the time for all good men\n"
        + " to come to the aid of their country\n"
        + " and pay their due taxes.";
      byte buf[] = source.getBytes();
      OutputStream f0 = new FileOutputStream("file1.txt");
      for (int i=0; i < buf.length; i += 2) {
        f0.write(buf[i]);
      }
      f0.close();

      OutputStream f1 = new FileOutputStream("file2.txt");
      f1.write(buf);
      f1.close();

      OutputStream f2 = new FileOutputStream("file3.txt");
      f2.write(buf,buf.length-buf.length/4,buf.length/4);
      f2.close();
    }
  }

Here are the contents of each file after running this program. First, file1.txt:

  Nwi h iefralgo e
  t oet h i ftercuty n a hi u ae.

Next, file2.txt:

  Now is the time for all good men
  to come to the aid of their country
  and pay their due taxes.

Finally, file3.txt:

  nd pay their due taxes.

Java 2, version 1.4 added the getChannel( ) method to FileOutputStream. This method returns a channel connected to the FileOutputStream object. Channels are used by the new I/O classes contained in java.nio.


ByteArrayInputStream

ByteArrayInputStream is an implementation of an input stream that uses a byte array as the source. This class has two constructors, each of which requires a byte array to provide the data source:

      ByteArrayInputStream(byte array[ ])
      ByteArrayInputStream(byte array[ ], int start, int numBytes)

Here, array is the input source. The second constructor creates an InputStream from a subset of your byte array that begins with the character at the index specified by start and is numBytes long.

The following example creates a pair of ByteArrayInputStreams, initializing them with the byte representation of the alphabet:

  // Demonstrate ByteArrayInputStream.
  import java.io.*;

  class ByteArrayInputStreamDemo {
    public static void main(String args[]) throws IOException {
      String tmp = "abcdefghijklmnopqrstuvwxyz";
      byte b[] = tmp.getBytes();
      ByteArrayInputStream input1 = new ByteArrayInputStream(b);
      ByteArrayInputStream input2 = new ByteArrayInputStream(b, 
                                                             0,3);
    }
  }

The input1 object contains the entire lowercase alphabet, while input2 contains only the first three letters.

A ByteArrayInputStream implements both mark( ) and reset( ). However, if mark( ) has not been called, then reset( ) sets the stream pointer to the start of the stream—which in this case is the start of the byte array passed to the constructor. The next example shows how to use the reset( ) method to read the same input twice. In this case, we read and print the letters “abc” once in lowercase and then again in uppercase.

  import java.io.*;

  class ByteArrayInputStreamReset {
    public static void main(String args[]) throws IOException {
      String tmp = "abc";
      byte b[] = tmp.getBytes();
      ByteArrayInputStream in = new ByteArrayInputStream(b);

      for (int i=0; i<2; i++) {
        int c;
        while ((c = in.read()) != -1) {
          if (i == 0) {
            System.out.print((char) c);
          } else {
            System.out.print(Character.toUpperCase((char) c));
          }
        }
        System.out.println();
        in.reset();
      }
    }
  }

This example first reads each character from the stream and prints it as is, in lowercase. It then resets the stream and begins reading again, this time converting each character to uppercase before printing. Here’s the output:

  abc
  ABC

The Java I/O Classes and Interfaces & File - Java Tutorials

The Java I/O Classes and Interfaces

The I/O classes defined by java.io are listed here:

BufferedInputStream 
FileWriter 
PipedInputStream
BufferedOutputStream 
FilterInputStream 
PipedOutputStream
BufferedReader 
FilterOutputStream 
PipedReader
BufferedWriter 
FilterReader 
PipedWriter
ByteArrayInputStream 
FilterWriter 
PrintStream
ByteArrayOutputStream 
InputStream 
PrintWriter
CharArrayReader 
InputStreamReader 
PushbackInputStream
CharArrayWriter 
LineNumberReader 
PushbackReader
DataInputStream 
ObjectInputStream 
RandomAccessFile
DataOutputStream 
ObjectInputStream.
GetField Reader
File 
ObjectOutputStream 
SequenceInputStream
FileDescriptor 
ObjectOutputStream.PutField 
SerializablePermission
FileInputStream 
ObjectStreamClass 
StreamTokenizer
FileOutputStream 
ObjectStreamField 
StringReader
FilePermission 
OutputStream 
StringWriter
FileReader 
OutputStreamWriter 
Writer

The ObjectInputStream.GetField and ObjectOutputStream.PutField inner classes were added by Java 2. The java.io package also contains two classes that were deprecated by Java 2 and are not shown in the preceding table: LineNumberInputStream and StringBufferInputStream. These classes should not be used for new code.

The following interfaces are defined by java.io:

DataInput 
FilenameFilter 
ObjectOutput
DataOutput 
ObjectInput 
ObjectStreamConstants
Externalizable 
ObjectInputValidation 
Serializable
FileFilter

The FileFilter interface was added by Java 2.

As you can see, there are many classes and interfaces in the java.io package. These include byte and character streams, and object serialization (the storage and retrieval of objects). This chapter examines several of the most commonly used I/O components, beginning with one of the most unique: File.




File

Although most of the classes defined by java.io operate on streams, the File class does not. It deals directly with files and the file system. That is, the File class does not specify how information is retrieved from or stored in files; it describes the properties of a file itself. A File object is used to obtain or manipulate the information associated with a disk file, such as the permissions, time, date, and directory path, and to navigate subdirectory hierarchies.

Files are a primary source and destination for data within many programs. Although there are severe restrictions on their use within applets for security reasons, files are still a central resource for storing persistent and shared information. A directory in Java is treated simply as a File with one additional property—a list of filenames that can be examined by the list( ) method.

The following constructors can be used to create File objects:

      File(String directoryPath)
      File(String directoryPath, String filename)
      File(File dirObj, String filename)
      File(URI uriObj)

Here, directoryPath is the path name of the file, filename is the name of the file, dirObj is a File object that specifies a directory, and uriObj is a URI object that describes a file. The fourth constructor was added by Java 2, version 1.4.

The following example creates three files: f1, f2, and f3. The first File object is constructed with a directory path as the only argument. The second includes two arguments—the path and the filename. The third includes the file path assigned to f1 and a filename; f3 refers to the same file as f2.

  File f1 = new File("/");
  File f2 = new File("/","autoexec.bat");
  File f3 = new File(f1,"autoexec.bat");

Java does the right thing with path separators between UNIX and Windows conventions. If you use a forward slash (/) on a Windows version of Java, the path will still resolve correctly. Remember, if you are using the Windows convention of a backslash character (\), you will need to use its escape sequence (\\) within a string. The Java convention is to use the UNIX- and URL-style forward slash for path separators.

File defines many methods that obtain the standard properties of a File objet. For example, getName( ) returns the name of the file, getParent( ) returns the name of the parent directory, and exists( ) returns true if the file exists, false if it does not. The File class, however, is not symmetrical. By this, we mean that there are many methods that allow you to examine the properties of a simple file object, but no corresponding function exists to change those attributes. The following example demonstrates several of the File methods:

  // Demonstrate File.
  import java.io.File;

  class FileDemo {
    static void p(String s) {
      System.out.println(s);
    }

    public static void main(String args[]) {
      File f1 = new File("/java/COPYRIGHT");
      p("File Name: " + f1.getName());
      p("Path: " + f1.getPath());
      p("Abs Path: " + f1.getAbsolutePath());
      p("Parent: " + f1.getParent());
      p(f1.exists() ? "exists" : "does not exist");
      p(f1.canWrite() ? "is writeable" : "is not writeable");
      p(f1.canRead() ? "is readable" : "is not readable");
      p("is " + (f1.isDirectory() ? "" : "not" + " a directory"));
      p(f1.isFile() ? "is normal file" : "might be a named pipe");
      p(f1.isAbsolute() ? "is absolute" : "is not absolute");
      p("File last modified: " + f1.lastModified());
      p("File size: " + f1.length() + " Bytes");
    }
  }

When you run this program, you will see something similar to the following:

  File Name: COPYRIGHT
  Path: /java/COPYRIGHT
  Abs Path: /java/COPYRIGHT
  Parent: /java
  exists
  is writeable
  is readable
  is not a directory
  is normal file
  is absolute
  File last modified: 812465204000
  File size: 695 Bytes

Most of the File methods are self-explanatory. isFile( ) and isAbsolute( ) are not. isFile( ) returns true if called on a file and false if called on a directory. Also, isFile( ) returns false for some special files, such as device drivers and named pipes, so this method can be used to make sure the file will behave as a file. The isAbsolute( ) method returns true if the file has an absolute path and false if its path is relative. File also includes two useful utility methods. The first is renameTo( ), shown here:

      boolean renameTo(File newName)

Here, the filename specified by newName becomes the new name of the invoking File object. It will return true upon success and false if the file cannot be renamed (if you either attempt to rename a file so that it moves from one directory to another or use an existing filename, for example).

The second utility method is delete( ), which deletes the disk file represented by the path of the invoking File object. It is shown here:

      boolean delete( )

You can also use delete( ) to delete a directory if the directory is empty. delete( ) returns true if it deletes the file and false if the file cannot be removed. Here are some other File methods that you will find helpful. (They were added by Java 2.)


void deleteOnExit( ):  Removes the file associated with the invoking object when the Java Virtual Machine terminates.

boolean isHidden( ):  Returns true if the invoking file is hidden. Returns false otherwise.

boolean setLastModified(long millisec):  Sets the time stamp on the invoking file to that specified by millisec, which is the number of milliseconds from January 1, 1970, Coordinated Universal Time (UTC).

boolean setReadOnly( ):  Sets the invoking file to read-only.


Also, because File supports the Comparable interface, the method compareTo( ) is also supported.


Directories

A directory is a File that contains a list of other files and directories. When you create a File object and it is a directory, the isDirectory( ) method will return true. In this case, you can call list( ) on that object to extract the list of other files and directories inside. It has two forms. The first is shown here:

      String[ ] list( )

The list of files is returned in an array of String objects.

The program shown here illustrates how to use list( ) to examine the contents of a directory:

  // Using directories.
  import java.io.File;

  class DirList {
    public static void main(String args[]) {
      String dirname = "/java";
      File f1 = new File(dirname);

      if (f1.isDirectory()) {
        System.out.println("Directory of " + dirname);
        String s[] = f1.list();

        for (int i=0; i < s.length; i++) {
          File f = new File(dirname + "/" + s[i]);
          if (f.isDirectory()) {
            System.out.println(s[i] + " is a directory");
          } else {
            System.out.println(s[i] + " is a file");
          }
        }
      } else {
        System.out.println(dirname + " is not a directory");
      }
    }
  } 

Here is sample output from the program. (Of course, the output you see will be different, based on what is in the directory.)

  Directory of /java
  bin is a directory
  lib is a directory
  demo is a directory
  COPYRIGHT is a file
  README is a file
  index.html is a file
  include is a directory
  src.zip is a file
  .hotjava is a directory
  src is a directory


Using FilenameFilter

You will often want to limit the number of files returned by the list( ) method to include only those files that match a certain filename pattern, or filter. To do this, you must use a second form of list( ), shown here:

      String[ ] list(FilenameFilter FFObj)

In this form, FFObj is an object of a class that implements the FilenameFilter interface. FilenameFilter defines only a single method, accept( ), which is called once for each file in a list. Its general form is given here:

      boolean accept(File directory, String filename)

The accept( ) method returns true for files in the directory specified by directory that should be included in the list (that is, those that matchthe filename argument), and returns false for those files that should be excluded.

The OnlyExt class, shown next, implements FilenameFilter. It will be used to modify the preceding program so that it restricts the visibility of the filenames returned by list( ) to files with names that end in the file extension specified when the object is constructed.

  import java.io.*;

  public class OnlyExt implements FilenameFilter {
    String ext;

    public OnlyExt(String ext) {
      this.ext = "." + ext;
    }

    public boolean accept(File dir, String name) {
      return name.endsWith(ext);
    }
  }

The modified directory listing program is shown here. Now it will only display files that use the .html extension.

  // Directory of .HTML files.
  import java.io.*;

  class DirListOnly {
    public static void main(String args[]) {
      String dirname = "/java";
      File f1 = new File(dirname);
      FilenameFilter only = new OnlyExt("html");
      String s[] = f1.list(only);

      for (int i=0; i < s.length; i++) {
        System.out.println(s[i]);
      }
    }
  }


The listFiles( ) Alternative

Java 2 added a variation to the list( ) method, called listFiles( ), which you might find useful. The signatures for listFiles( ) are shown here:

      File[ ] listFiles( )
      File[ ] listFiles(FilenameFilter FFObj)
      File[ ] listFiles(FileFilter FObj)
  
These methods return the file list as an array of File objects instead of strings. The first method returns all files, and the second returns those files that satisfy the specified FilenameFilter. Aside from returning an array of File objects, these two versions of listFiles( ) work like their equivalent list( ) methods.

The third version of listFiles( ) returns those files with path names that satisfy the specified FileFilter. FileFilter defines only a single method, accept( ), which is called once for each file in a list. Its general form is given here:

      boolean accept(File path)

The accept( ) method returns true for files that should be included in the list (that is, those that match the path argument), and false for those that should be excluded.


Creating Directories

Another two useful File utility methods are mkdir( ) and mkdirs( ). The mkdir( ) method creates a directory, returning true on success and false on failure. Failure indicates that the path specified in the File object already exists, or that the directory cannot be created because the entire path does not exist yet. To create a directory for which no path exists, use the mkdirs( ) method. It creates both a directory and all the parents of the directory.