In this part we will learn about input/output (I/O) in Java, focusing specifically on file I/O. We will also learn how to read and write from the console.
Java provides two major frameworks in packages java.io
and java.nio
. The main differences between these frameworks are:
IO | NIO |
---|---|
Stream-oriented | Buffer-oriented |
Blocking | Non-blocking |
Here we will focus mainly on the IO framework.
java.io
provides several types of abstractions for
A basic mechnism for I/O in the java.io
framework are the InputStream
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/InputStream.html and OutputStream
abstract classes https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/io/OutputStream.html.
These allow reading (InputStream
) and writing (OutputStream
) binary data in a blocking fashion. That is once you call a method to read or write from a stream, the execution of your program is paused while waiting for a result (unless an Exception is thrown to be thrown). We already discussed some I/O related concepts when talking about exception handling. Let us discuss some methods of InputStream
and OutputStream
:
close()
- closes the stream and releases all system resources associated with it. Always close streams when you are done with them!int available()
- returns an estimate of the number of bytes that are available for readingint read()
- reads a single byte from the stream. This method blocks until a byte is available.int read (byte[] b)
- reads a number of bytes into buffer b
and returns the number of bytes that have been readclose()
- closes the stream and releases all system resources associated with it. Always close streams when you are done with them!write (int b)
- write a single byte to the output streamwrite(byte[] b)
- write the content of byte array b
to the output streamflush()
- flushes this output stream which causes any buffered content (if the outputstream does buffering) to be written outSome noteworthy subclasses are:
FileInputStream
- which reads data from a fileFileOutputStream
- writes data to a fileBufferedInputStream
- wraps another input stream and adds buffers readsBufferedOutputStream
- wraps an output stream and adds buffering of writesObjectInputStream
- read primitive java values and objects from an input stream in a machine-architecture independent way (data is expected to have been written by ObjectOutputStream
. Only classes that implement the interface java.io.Serializable
or java.io.Externalizable
can be read or written using this meachnism.ObjectOutputStream
- write primitive java values and objects as explained above for ObjectInputStream
The abstract Reader
and Writer
classes are superclasses for all classes that read or write character streams. The main difference input and output streams is that data is interpreted as characters instead of raw bytes. Relevant methods are:
close()
- close the stream and release resourcesboolean ready()
- is the stream ready for reading?int read()
- reads a single characterint read(char[] cbuf)
- reads characters into buffer cbuf
, returns the number of characters that were readskip(long n)
- skips n
charactersclose()
and flush()
behave like for streamswrite(String str)
- write the content of string to the writerSome subclasses of interest:
StringReader
- reads from a String.StringWriter
- writes to a StringBufferBufferedReader/Writer
- buffers a reader or writerInputStreamReader
- wraps an input stream to read characters from itFileReader/Writer
- reads/writes from/to a fileimport java.io.StringReader;
// Reading from String
StringReader r = new StringReader("aabbaa");
int read;
while((read = r.read()) != -1)
System.out.println((char) read);
import java.io.FileInputStream;
import java.io.File;
import java.io.IOException;
FileInputStream in = null;
File f;
byte[] b = new byte[256]; // 256 byte buffer
System.out.println("try to open test.txt");
f = new File("test.txt");
try {
if (f.exists()) {
System.out.println("File exists, try to read");
in = new FileInputStream(f);
for(int available = in.available(); available > 0; available = in.available()) { // read data using buffer b
int numread = in.read(b);
System.out.printf("\nread %d bytes: ", numread);
for(int i = 0; i < numread; i++) // print all bytes read and the a newline
System.out.print((char) b[i]);
System.out.println();
}
}
else {
System.out.println("File does not exist!");
}
}
catch (IOException e) {
System.out.println("error reading text.txt: " + e.toString());
}
finally {
if (in != null) {
try {
in.close();
System.out.println("closed file!"); // look out for this in the output. This statement is excuted even though no exception has been thrown.
}
catch (IOException e) {
System.out.println("failed closing file input stream:\n" + e.toString());
}
}
}
Java allows objects to be written and read from streams using ObjectOuputStream
and ObjectInputStream
which wrap an OutputStream
and InputStream
, e.g., a FileInputStream
to read from a file. This interface does not define any methods. Per default, all non-static fields are serialized and deserialized. A class implementing Serializable
should be define a default constructor (no arguments). More details of serialization and deserialization in Java can be found in the documentation.
package lecture;
import java.io.Serializable;
public class SerPerson implements Serializable {
private String name;
public SerPerson() {
this.name = null;
}
public SerPerson(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import lecture.SerPerson;
import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
File f = new File("person_test.txt");
SerPerson p = new SerPerson("Peter");
// write p to a file
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
out.writeObject(p);
out.close();
// read p from the file
ObjectInputStream in = new ObjectInputStream(new FileInputStream(f));
p = (SerPerson) in.readObject();
in.close();
if(f.exists())
f.delete();
return p.getName();