Streams
Navigate User Interface topic: ) |
The most basic input and output in Java (System.in
and System.out
fields that have been used in the Basic I/O) is done using streams. Streams are objects that represent sources and destinations of data. Streams that are sources of data can be read from, and streams that are destinations of data can be written to. A stream in Java is an ordered sequence of bytes of undetermined length. Streams are ordered and in sequence so that the java virtual machine can understand and work upon the stream. Streams are analogous to water streams. They exist as a communication medium, just like electromagnetic waves in communication. The order or sequence of bytes in a Java stream allow the virtual machine to classify it among other streams.
Java has various inbuilt streams implemented as classes in the package java.io
like the classes of System.in
and System.out
. Streams can be classed as both input and output streams. All Java streams are derived from Input Stream (java.io.InputStream
) and Output Stream (java.io.OutputStream
) classes. They are abstract base classes meant for other stream classes. The System.in
is the input stream class derivative and analogically System.out
is the output counterpart. Both are basic classes used to directly interact with input and output through console, similarly follows System.err
. Also Java has streams to communicate across different parts of a program or even among threads. There are also classes that "filter" streams, changing one format to another (e.g. class DataOutputStream
, which translates various primitive types to byte streams).
It is a characteristic of streams that they deal only in one discrete unit of data at a time, and different streams deal with different types of data. If one had a stream that represented a destination for bytes, for example, one would send data to the destination one byte at a time. If a stream was a source of byte data, one would read the data a byte at a time. Because this is the only way to access data from a stream, in this latter case, we wouldn't know when we had read all the data from the stream until we actually got there. When reading a stream, one generally has to check each unit of data each read operation to see if the end of the stream has been reached (with byte streams, the special value is the integer -1, or FFFF hex).
Input streams
[edit | edit source]Input streams acquire bytes for our programmed java application/program (e.g. a file, an array, a keyboard or monitor, etc.). InputStream
is an abstract class that represents a source of byte data. It has a read()
method, which returns the next byte in the stream and a close()
method, which should be called by a program when that program is done with the stream. The read()
method is overloaded, and can take a byte array to read to. It has a skip()
method that can skip a number of bytes, and an available()
method that a program can use to determine the number of bytes immediately available to be read, as not all the data is necessarily ready immediately. As an abstract class, it cannot be instantiated, but describes the general behavior of an input stream. A few examples of concrete subclasses would be ByteArrayInputStream
, which reads from a byte array, and FileInputStream
, which reads byte data from a file.
In the following example, we print "Hello world!" on the screen several times. The number of times the message is printed is stored in a file named source.txt
. This file should only contain a integer and should be placed in the same folder of the ConfiguredApplication
class.
Code listing 9.1: Example of input stream.
import java.io.File;
import java.io.FileInputStream;
public class ConfiguredApplication {
public static void main(String[] args) throws Exception {
// Data reading
File file = new File("source.txt");
FileInputStream stream = new FileInputStream(file);
StringBuffer buffer = new StringBuffer();
int character = 0;
while ((character = stream.read()) != -1) {
buffer.append((char) character);
}
stream.close();
// Data use
Integer readInteger = Integer.parseInt(buffer.toString());
for (int i = 0; i < readInteger ; i++) {
System.out.println("Hello world!");
}
}
}
|
The close()
method is not always mandatory but can avoid some inter-process concurrency conflicts. However if it occurs before a read()
or write()
(in the same process) they return the warning Stream closed
.
The class start to identify the filename with a File
object. The File
object is used by an input stream as the source of the stream. We create a buffer and a character to prepare the data loading. The buffer will contain all the file content and the character will temporary contain each character present in the file, one after one. This is done while{}
in the loop. Each iteration of the loop will copy a character from the stream to the buffer. The loop ends when no more character is present in the stream. Then we close the stream. The last part of the code use the data we have loaded in from the file. It is transformed into string and then into an integer (so the data must be an integer). If it works, the integer is used to determine the number of time we print "Hello world!" on the screen. No try/catch block has been defined for readability but the thrown exceptions should be caught.
Let's try with the following source file:
Code listing 9.2: source.txt
4 |
We should obtain this:
Output for ConfiguredApplication
$ java ConfiguredApplication Hello world! Hello world! Hello world! Hello world! |
There is also Reader
which is an abstract class that represents a source of character data. It is analogous to InputStream
, except that it deals with characters instead of bytes (remember that Java uses Unicode, so that a character is 2 bytes, not one). Its methods are generally similar to those of InputStream
. Concrete subclasses include classes like FileReader
, which reads characters from files, and StringReader
, which reads characters from strings. You can also convert an InputStream
object to a Reader object with the InputStreamReader
class, which can be "wrapped around" an InputStream
object (by passing it as an argument in its constructor). It uses a character encoding scheme (which can be changed by the programmer) to translate a byte into a 16-bit Unicode character.
Output streams
[edit | edit source]Output Streams direct streams of bytes outwards to the environment from our program or application. OutputStream
is an abstract class which is the destination counterpart of InputStream
. OutputStream
has a write()
method which can be used to write a byte to the stream. The method is overloaded, and can take an array as well. A close()
method closes the stream when the application is finished with it, and it has a flush()
method. The stream may wait until it has a certain amount before it writes it all at once for efficiency. If the stream object is buffering any data before writing it, the flush()
method will force it to write all of this data. Like InputStream
, this class cannot be instantiated, but has concrete subclasses that parallel those of InputStream
, eg ByteArrayOutputStream
, FileOutputStream
, etc.
In the following example, we store the current time in an already existing file called log.txt
located in the same folder than the class.
Code listing 9.2: Example of output stream.
import java.io.File;
import java.io.FileOutputStream;
import java.util.Date;
public class LogTime {
public static void main(String[] args) throws Exception {
// Generate data
String timeInString = new Date().toString();
// Store data
File file = new File("log.txt");
FileOutputStream stream = new FileOutputStream(file);
byte[] timeInBytes = timeInString.getBytes();
stream.write(timeInBytes);
stream.flush();
stream.close();
}
}
|
This case is more simple as we can put all the data in the stream at the same time. The first part of the code generate a string containing the current time. Then we create a File
object identifying the output file and an output stream for this file. We write the data in the stream, flush it and close it. That's all. No try/catch block has been defined for readability but the thrown exceptions should be caught.
In order to read a text file several times from the beginning, a FileChannel variable should be introduced, only to reposition the reader.
|
Now let's execute it:
LogTime execution
$ java LogTime |
We should obtain this content:
Code listing 9.4: log.txt
Tue Oct 8 10:50:44 CEUTC 2024 |
If it shows a FileNotFoundException or an IOException , the file should not have been created or it is not placed in the right folder.
|
There is also Writer
which is a character counterpart of OutputStream
, and a destination counterpart to Reader, this is also an abstract superclass. Particular implementations parallel those of Reader, eg FileWriter
, StringWriter
, and OutputStreamWriter
, for converting a regular OutputStream
into a reader so that it can take character data.
System.out
and System.err
[edit | edit source]System
is a class in the package java.lang
with a number of static members that are available to Java programs. Two members that are useful for console output are System.out
and System.err
. Both System.out and System.err are PrintStream
objects. PrintStream
is a subclass of FilterOutputStream
, itself a subclass of OutputStream
(discussed above), and its main purpose is to translate a wide variety of data types into streams of bytes that represent that data in characters according to some encoding scheme.
System.out
and System.err
both display text to a console where the user can read it, however what this means exactly depends on the platform used and the environment in which the program is running. In BlueJay and Eclipse IDE, for example, there is a special "terminal" window that will display this output. If the program is launched in Windows, the output will be sent to the DOS prompt (usually this means that you have to launch the program from the command line to see the output).
System.out
and System.err
differ in what they're supposed to be used for. System.out
should be used for normal program output, System.err
should be used to inform the user that some kind of error has occurred in the program. In some situations, this may be important. In DOS, for instance, a user can redirect standard output to some other destination (a file, for example), but error output will not be redirected, but rather displayed on the screen. If this weren't the case, the user might never be able to tell that an error had occurred.
New I/O
[edit | edit source]Versions of Java prior to J2SE 1.4 only supported stream-based blocking I/O. This required a thread per stream being handled, as no other processing could take place while the active thread blocked waiting for input or output. This was a major scalability and performance issue for anyone needing to implement any Java network service. Since the introduction of NIO (New I/O) in J2SE 1.4, this scalability problem has been rectified by the introduction of a non-blocking I/O framework (though there are a number of open issues in the NIO API as implemented by Oracle).
The non-blocking IO framework, though considerably more complex than the original blocking IO framework, allows any number of "channels" to be handled by a single thread. The framework is based on the Reactor Pattern.
More Info
[edit | edit source]More information on the contents of the java.io
package can be viewed on the Oracle website by clicking this link (http://docs.oracle.com/javase/7/docs/api/index.html).