Oct 13, 2015

Textual description of firstImageUrl

Java I/O - Internal details of Java Input and Output class (java.io.*)

Java support Input and Output(I/O) operations with characters/text and binary stream. Java I/O model is highly flexible so that it can accommodate data sources like File, Arrays, piped streams, etc. Since, ASCII (8 bit) character encoding was not sufficient to cover all possible character sets, Java uses 16 bit Unicode to represent characters.

Unicode and UTF-8 encoding in Java:- 

In Java, text(collection of characters) is represented as two-byte UNICODE characters. UNICODE use two bytes to represent characters from various character sets throughout the world(Japanese, Chinese, etc ). Since Java is platform independent language and each platform has its own native character set(which usually has some mapping to the UNICODE standard). Java needs some way to map the native character set to UNICODE. It is Java's I/O classes that translate the native characters to and from UNICODE. In each platform dependent JDK, there is a "default mapping" that is used for translations.
Java internally uses UTF-8 encoding scheme to store Strings in class files. UTF-8 is a simple encoding of UNICODE characters and strings that is optimized for the ASCII characters.

Along with Character data, Java provides various classes(java.io.*) and API's to deal with binary stream. Java I/O model can be broadly classified in two categories:-
1. Character/Text Input and Output - native encoding to two byte Unicode mapping
2. Binary Input and Output - No mapping required, binary data I/O in form of stream.
Following diagram depicts a holistic view of Java I/O and classes involved to support both text I/O and binary I/O.
Block diagram of Java I/O Classes interdependency
Java I/O classification, class hierarchy and its interdependency 
  • For handling character I/O Java provides Reader and Writer abstract class and for byte stream InputStream and OutputStream - which provides flexible read() and write() methods and gives an abstraction capability to subclasses.
  • For File related operation (character and byte stream read/write)- Java provides concrete classes like FileReader, FileWriter, FileInputStream and FileOutputStream.
  • For handling byte array/character array- ByteArrayInputStream, ByteArrayOutputStream / CharArrayReader, CharArrayWriter
  • For String object -  read and write operation is carried out with StringReader and StringWriter.
  • String representation of primitive and object values are performed using PrintStream and PrintWriter.(Not shown in above diagram)
Note:-
  1. Interconversion of character and byte stream is achieved using bridge classes- InputStreamReader - Binary to Character and OutputStreamWriter - Character to Binary. 
  2. Java I/O provides Buffered classes (BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter) which minimizes read and write operation time. Instead of reading/wrirting one byte at a time buffered classes allows buffering large chunks of bytes in its buffer and allows read/write large chunks at a time. Refer why buffering is recommended for all character/byte stream I/O - notice the differences in write operation execution time with and without buffer. 

Inner details of each of the above classes, methods supported and sample code how to use it : 

InputStream and OutputStream:- 
  1. InputStream(IS) is an abstract class (superclass of all classes which represents input stream of bytes). It gives flexibility to concrete classes(class which extends IS) by providing overload read() methods (abstract and concrete implementation), so that concrete classes can define their own version of read() method and can also use read() of IS. The signature of abstract and concrete read method of InputStream is as follows:-
    //Reads the next byte of data from the input stream and each concrete class must implement a method returning next byte of stream
    public abstract int read() throws IOException;
    
    public int read(byte b[]) throws IOException {
     // b is byte buffer into which maximum b.length bytes can be read.
    }
    
    public int read(byte b[], int off, int len) throws IOException {
     // reads bytes from stream and start storing each byte from starting index = off
     // and maximum no of bytes read = len
    }
    
  2. Note:- Internally all read methods(concrete implementation provided in IS ) execute abstract read() method, so it is responsibility of concrete class to implement abstract read() method.
  3. OutputStream(OS) is an abstract class (superclass of all classes which represents output stream of bytes. It provides an abstract method write() and overloaded write() method. Signature of write() method is as follows :-  
    // Writes the specified byte(b bytes) to this output stream.
    public abstract void write(int b) throws IOException;
    
    public void write(byte b[]) throws IOException {
     //Writes b.length bytes from the specified byte buffer(array b) to the output stream.
    }
    
    public void write(byte b[], int off, int len) throws IOException {
    // Writes len bytes from the specified byte array starting from offset off to the output stream.
    //Internally it executes write(len) method and write len of byte at a time. 
    }
    
  4. Note:- Internally all write methods (concrete implementation provided in OS ) execute abstract write() method, so it is responsibility of concrete class to implement abstract read() method.
FileInputStream, FileReader , FileOutputStream and FileWriter:-
  1. FileInputStream(FIS) read input bytes from a file and it is commonly  used for reading streams of raw bytes such as image data. The constructor of FIS is responsible for opening an connection with the abstract file name(binding stream with file). The signature of FileInputStream constructors which creates a file descriptor and connection with abstract file.
    public FileInputStream(String name) throws FileNotFoundException {
     // Creates an Inputstream and open connection with Abstract file name 
    }
    public FileInputStream(File file) throws FileNotFoundException {
     // Creates an Inputstream and open connection with File Object
    FileNotFoundException is thrown if file does not exist in file system
    
    public FileInputStream(FileDescriptor fdObj) {
     // Creates an Inputstream and uses existing connection(file descriptor object fobj and 
     //SecurityException is thrown if read access is not allowed for the given file descriptor
    }
     
    
    In order to manage this connection Operating system kernel maintains an list of integer corresponding to each opened file termed as "File descriptor". File descriptor is very critical system resources, so every time we open a file and we should close it using close() method. Once close() method is called on the given FIS, it releases lock and associated system resources to OS kernel.
    FIS extends abstract class InputStream class and inherits read() methods from it. Since InputSteam class has an abstract method, FIS provides definition of that read() method in form of native method implementation.FIS also provides native implementation for open(), close(), readBytes() methods. The signature of native methods are as follows:-
    //Open a connection with specified abstract file name, else throw exception if file does not exist
    private native void open(String name) throws FileNotFoundException;
    
    //It is being called by close() method and releases all system resources to OS kernel.
    private native void close0() throws IOException;
    
    //native modifier indicates that this method has been implemented in other languages(preferably c) not in Java. reda() methods return value of one byte read in range of 0 to 255, -1 if end of file is reached.
    public native int read() throws IOException;
    private native int readBytes(byte b[], int off, int len) throws IOException;
    Note:- In Java, performance critical and hardware interaction code lines are implemented as native methods in c language and and JNI (Java Native Interface) provides communication channel between them.
  2. FileOutputStream(FOS) write bytes to a file. FOS is commonly used for writing streams of raw bytes such as image data. Similar to FileInputStream, FOS creates an output stream connection with abstract file and O/S kernel manage file descriptor for it. FOS also has close() method for releasing system resources. The signature of FileOutputStream constructors are as follows:-
    public FileOutputStream(String name) throws FileNotFoundException {
     //Creates an output file stream to write to the file and bytes written to staring of file
     //a new file descriptor object is created to represent this connection 
    }
    
    public FileOutputStream(String name, boolean append){
     //Creates an output file stream to write to the file 
     //and if append boolean is true, bytes will be written to end of file not at begining
    }
    
    public FileOutputStream(FileDescriptor fdObj) {
        //Creates an output file stream to write to the specified file 
        //descriptor, which represents an existing connection to an actual 
        //file in the file system.
    }
    

    FOS extends Outputstream and inherits write method. Outputstream provides an abstract write() method and FOS provides definition of write method in native form as we saw for read() method in FIS. FOS also provides native implementation for open(), close()  The signature of native write() method is as follows:- 
    // Open file with specified abstract name 
    private native void open(String name) throws FileNotFoundException;
    
    //Open file with specified abstract name in append mode, write bytes at end of file  
    private native void openAppend(String name) throws FileNotFoundException;
    
    //Close FileOutputStream and releases system resources
    private native void close0() throws IOException;
    
    //Writes the specified byte to this file output stream
    public native void write(int b) throws IOException;
    
    //Write bytes from buffer b from index off and up to length len.
    private native void writeBytes(byte b[], int off, int len) throws IOException;
    
    public void write(byte b[]) throws IOException {
    //Write bytes from buffer b start form index 0 to length of b
    }
    
    
  3. FileReader:- FileReader is used for reading character/text from file. FileReader extends InputStreamReader class(bridge between character and byte stream - reads bytes and decodes them into characters) and provides read() methods. FileReader constructor assumes that default encoding and byte buffer size is appropriate for read operation. FileReader uses read method of InputStreamReader class. The constructor of FileReader internally calls InputStreamReader constructor and creates an Instance of FileInputStream. Constructor signature are as follows:- 
    //Calls InputStreamReader constructor and create connection with specified 
    //fileName and File Object
     public FileReader(String fileName) throws FileNotFoundException {
     super(new FileInputStream(fileName));
     }
     public FileReader(File file) throws FileNotFoundException {
     super(new FileInputStream(file));
     }
    
    On each read() operation invocation, InputStreamReader read() method is executed and one byte/byte buffer is read. InputStreamReader uses character encoder (java.nio.charset.Charset ) to convert bytes read into character. In order to increase efficiency of read operation buffered stream should be used. Syntax of read method is as follows:-
     public int read() throws IOException {
            //Return The character read - int value of char between 0 to 255
        }
    public int read(char cbuf[], int offset, int length) throws IOException {
     // returnns number of character read into  buffer cbuf stasrting at index 
     //offset cbuf[offset] and up to cbuf[lenght- offset+1]
        }
    
  4.  FileWriter:-FileWriter is used for writing characters in file. It extends another bridge class OutputStreamWriter - bridge from character streams to byte streams. FileWriter constructor assumes that default character encoding and the default byte-buffer size are acceptable. Find below signature of constructor:-
    //Calls OutputStreamWriter constructor and create connection with specified 
    //fileName and File Object
    public FileWriter(String fileName) throws IOException {
     super(new FileOutputStream(fileName));
        }
    public FileWriter(String fileName, boolean append) throws IOException {
     //Connection created in append mode so that byte written at end of file
     super(new FileOutputStream(fileName, append));
        }
    public FileWriter(File file) throws IOException {
     super(new FileOutputStream(file));
        }
    

    FileWriter uses write method of OutputStreamWriter and Characters written to it are encoded into bytes using java.nio.charset.Charset. Below is the signature of the write() method. 
    public void write(int c) throws IOException {
     // Writes a single character.
    }
    public void write(char cbuf[], int off, int len) throws IOException {
     // Writes characters into cbuf starting from cbuf[off] and of length len.
    }
    public void write(String str, int off, int len) throws IOException {
     // Writes a portion of a string, starting from off and of length len
    }
    
    Refer following example for read and write operation in Java
In next post we will discuss about other classes for Array of bytes (ByteArrayInputStream and ByteArrayOutputStream), Array of characters (CharArrayReader and CharArrayWriter) and Strings(StringReader and StringWriter).


Location: Hyderabad, Telangana, India