Java I/O OverviewS2C Home « Java I/O Overview
Up until now we have been writing our output to the console, it's now time to look at the Java input and output mechanisms (I/O) in much more detail and we start three lessons on the subject with an overview of the
various streams available in Java. Before we do this we need to look back to most of the programs we have written, including the very first program we wrote and interpret the following code System.out.println
used in most of these programs.
System
is a predefined class within java.lang
that encapsulates different aspects of the JVM
and provides access to the underlying system and out
is the output stream
connected to the console. The println()
method displays the message passed to it on a new line. So when joined together a message is passed to the console and appears on a new line.
But what do we mean by output stream? To investigate this we need to take a closer look at the System
class which contains three predefined fields named err
, in
and the
field we are most familiar with out
. Further inspection of these fields shows that they are public static
and so can be used without an instance and are of the following types:
Name | Type | Description | Default I/O Device |
---|---|---|---|
err | PrintStream | The "standard" error output stream. | Console |
in | InputStream | The "standard" input stream. | Keyboard |
out | PrintStream | The "standard" output stream. | Console |
So we can now see that these fields are of types InputStream
and PrintStream
and this is what Java I/O constitutes, class hierarchies of streams. Streams are named as an analogy to a stream of water
that continues to flow; so you can think of Java streams as an abstraction, that produces or consumes flows of data. In this way we can conceptualise streams as data that flows into our programs as input streams, or flows out of
our programs as output streams.
Java is made up of two types of streams, these being byte streams used for handling byte input and and output and character streams used for handling character input and and output. The primary reason Java uses streams for I/O is to make Java code independent of the input and output devices involved, thus making the peripheral device used and thus the coding to use it, transparent.
These streams are represented as classes of the java.io
package and we will spend the rest of this lesson taking a high level look at this package and its classes to get a feel for how Java I/O hangs together.
We will be looking at Byte Stream Classes and Character Stream Classes in much more detail in the next two lessons:
Byte StreamsTop
Byte streams are defined within two class hierarchies, one for input and one for output and represent byte stream classes which provide the tools to read and write binary data as a sequence of bytes.
- The
OutputStream
class is the abstract superclass of all byte output streams - The
InputStream
class is the abstract superclass of all byte input streams
These classes define the characteristics that are common to byte input and byte output streams, which are implemented in the concrete subclasses of each hierarchy.
Byte Output Stream HierarchyTop
The diagram below shows most of the classes in the byte output stream hierarchy of which OutputStream
class is the abstract superclass. Some subclasses of the FilterOutputStream
class are not shown.
Class | Description |
---|---|
OutputStream | Abstract byte stream superclass which describes this type of output stream. |
ByteArrayOutputStream | Output byte stream that writes data to a byte array. |
FileOutputStream | Output byte stream that writes bytes to a file in a file system. |
FilterOutputStream | Output byte stream that implements OutputStream . |
BufferedOutputStream | Output byte stream that writes bytes to a buffered output stream. |
DataOutputStream | Output stream to write Java primitive data types. |
PrintStream | Convenience output byte stream to add functionality to another stream, an example being to print to the console using print() and println() . |
ObjectOutputStream | Output stream to write and serialize objects for reading using ObjectInputStream . |
PipedOutputStream | Piped Output stream that is connected to a piped input stream to create a communication pipe. |
Byte Input Stream HierarchyTop
The diagram below shows most of the classes in the byte input stream hierarchy of which InputStream
class is the abstract superclass. Some subclasses of the FilterInputStream
class are not shown.
Class | Description |
---|---|
InputStream | Abstract byte stream superclass which describes this type of input stream. |
ByteArrayInputStream | Input byte stream that reads bytes from an internal byte array. |
FileInputStream | Input byte stream that reads bytes from a file in a file system. |
FilterInputStream | Input byte stream that implements InputStream . |
BufferedInputStream | Input byte stream that reads bytes into an internal buffer before use. |
DataInputStream | Input stream to reads Java primitive data types. |
PushbackInputStream | Input byte stream containing functionality to return bytes to the input stream. |
ObjectInputStream | Input stream to read and deserialize objects output and serialized using ObjectOutputStream . |
PipedInputStream | Piped input stream that is connected to a piped output stream to create a communication pipe. |
SequenceInputStream | Concatenation of two or more input streams read sequentially. |
Character StreamsTop
Character streams are defined within two class hierarchies, one for input and one for output:
- The
Writer
class is the abstract superclass of all character output streams - The
Reader
class is the abstract superclass of all character input streams
These classes define the characteristics that are common to character input and character output streams, which are implemented in the concrete subclasses of each hierarchy.
Character Output Stream HierarchyTop
The diagram below shows the classes in the character output stream hierarchy of which the Writer
class is the abstract superclass.:
Class | Description |
---|---|
Writer | Abstract character stream superclass which describes this type of output stream. |
BufferedWriter | Buffered output character stream. |
CharArrayWriter | Character buffer output stream. |
FilterWriter | Abstract character stream for writing filtered streams. |
OuputStreamWriter | Output Stream that acts as a bridge for encoding byte streams from character streams. |
FileWriter | Output stream for writing characters to a file. |
PipedWriter | Piped character output stream. |
PrintWriter | Convenience output character stream to add functionality to another stream, an example being to print to the console using print() and println() . |
StringWriter | Output stream for writing characters to a string. |
Character Input Stream HierarchyTop
The diagram below shows the classes in the character input stream hierarchy of which the Reader
class is the abstract superclass.:
Class | Description |
---|---|
Reader | Abstract character stream superclass which describes this type of input stream. |
BufferedReader | Buffered input character stream. |
LineNumberReader | Input character stream that keeps a count of line numbers. |
CharArrayReader | Character buffer input stream. |
FilterReader | Abstract character stream for reading filtered streams. |
PushbackReader | Character stream reader containing functionality to return characters to the input stream. |
InputStreamReader | Input Stream that acts as a bridge for decoding byte streams into character streams. |
FileReader | Input stream for reading characters from a file. |
PipedReader | Piped character input stream. |
StringReader | Input stream for reading characters from a string. |
Other Java I/O ClassesTop
The diagram below shows some other pertinent classes in the java.io
package not covered in the byte and character streams above:
Class | Description |
---|---|
File | Abstract representation of file and directory pathnames. |
FielDescriptor | Opaque handle to the underlying machine-specific structure. |
RandomAccessFile | Allows reading and writing of bytes to a random access file. |
StreamTokenizer | Input stream to be parsed into 'tokens'. |
The java.io.File
ClassTop
We will finish this lesson with a talk about the File
class that exists within the Java.io
package and how we use objects of this class to represent an actual file and directory pathname that
may or may not exist already on a hard drive. A File
object doesn't contain the file in question or any data associated with the file, it just acts like a pointer to said file and can be a relative or absolute pathname:
- Relative URL - The common use of a relative URL is by omitting the protocol and server name, as documents generally reside on the same server. So this would be directoryName/fileName.extension.
For example the relative url images/runwhile.webp - Absolute URL - An absolute url is the complete address of the resource.
For example the absolute url https://server2client.com/images/runwhile.webp
Before we get look at some of the methods of the File
class, lets clarify what we mean by a File
object pointing to a file:
/*
Create a file object that points to a file
*/
class TestFileCreation {
public static void main(String[] args) {
File newFile = new File("MyFile.txt");
}
}
Save, compile and run the TestFileCreation
test class in directory c:\_APIContents2 in the usual way.
The above screenshot shows the output of compiling the TestFileCreation
class. The compiler cannot find the File
object type. This is because the File
class exists within the
java.io
package and so we need to either qualify usage as in:
/*
Create a file object that points to a file
*/
class TestFileCreation {
public static void main(String[] args) {
java.io.File newFile = new java.io.File("MyFile.txt"); // Qualifying File gives clean compile
}
}
Or we can import the java.io
package as in the code below. We will use the import
keyword over qualifying the names in our examples as we think it is easier and less error-prone:
/*
Create a file object that points to a file
*/
import java.io.File; // Just import the file class from java.io package
class TestFileCreation {
public static void main(String[] args) {
File newFile = new File("MyFile.txt");
}
}
Save, compile and run the TestFileCreation
test class in directory c:\_APIContents2 in the usual way.
The above screenshot shows the output of running the TestFileCreation
class and then doing a dir command in the c:\_APIContents2 directory. As you can
see we haven't created a physical file called MyFile.txt, we just made a File
object to point to a file that may exist called MyFile.txt in the relative path we are in.
This time we will create a phsical file and directory and use some File
methods:
/*
Create a file object that points to a directory
*/
import java.io.File; // Just import the file class from java.io package
import java.io.IOException; // Import the IOException class from java.io package
class TestFileCreation2 {
public static void main(String[] args) {
File newFile = new File("MyFile.txt");
if (newFile.exists()) { // Check to see if Myfile.txt already exists
System.out.println("Myfile.txt already exists.");
} else {
System.out.println("Try to create Myfile.txt");
try {
newFile.createNewFile(); // Create a file called Myfile.txt
System.out.println("We created Myfile.txt");
}
catch (IOException ex) {
System.out.println("We caught exception: " + ex);
}
}
File newDir = new File("JavaIO");
newDir.mkdir(); // Create a directory called JavaIO
if (newDir.isDirectory()) { // true as we created a directory
System.out.println("We created a directory.");
System.out.println(newDir.getAbsolutePath());
System.out.println(newDir.getParent());
}
}
}
Save, compile and run the TestFileCreation2
test class in directory c:\_APIContents2 in the usual way.
The above screenshot shows the output of running the TestFileCreation2
class and then doing a dir command in the c:\_APIContents2 directory.
Firstly we created a new File
object and then check to see if this file already phsically exists using the exist()
method. If the file doesn't actually exist we physically create the file on
disk using the createNewFile()
method which can throw an IOException
which we deal with. We then print a message showing the file was created.
Secondly we create a new File
object but this time we make a directory. We then physically create a directory on disk called JavaIO
using the mkdir()
method and test to see if it
exists using the isDirectory()
method. We then print a message showing the directory was created and print out the absolute path using the getAbsolutePath()
method. The program then prints
null
from the getParent()
method as we didn't use a parent directory when creating the JavaIO
directory.
As we can see from doing the dir command in the c:\_APIContents2 directory we have indeed created a file called Myfile.txt
and a directory called JavaIO
.
There are plenty of other methods in the java.io.File
Class to use for creation and deletion of files and directories so make sure to look at the Oracle docs for this.
Lesson 4 Complete
In this lesson we started three lessons on the subject of Java I/O with an overview of the various streams available in Java.
What's Next?
In the next lesson we take a closer look at some of the byte stream classes that are available in Java and how we use them.