Java provides several classes for basic input and output, a few of which are discussed in this chapter. The basic classes can be used to read and write to files, sockets, and the console. They also provide for working with files and directories and for serializing data. Java I/O classes throw exceptions, including the IOException
, which needs to be handled.
Java I/O classes also support formatting data, compressing and decompressing streams, encrypting and decrypting, and communicating between threads using piped streams.
The new I/O (NIO) APIs that were introduced in Java 1.4 provide additional I/O capabilities, including buffering, file locking, regular expression matching, scalable networking, and buffer management.
NIO.2 was introduced with Java SE 7 and is covered in the next chapter. NIO.2 extends NIO and provides a new filesystem API.
Java uses three standard streams: in
, out
, and err
.
System.in
is the standard input stream that is used to get data from the user to a program:
byte
teamName
[]
=
new
byte
[
200
];
int
size
=
System
.
in
.
read
(
teamName
);
System
.
out
.
write
(
teamName
,
0
,
size
);
System.out
is the standard output stream that is used to output data from a program to the user:
System
.
out
.
(
"Team complete"
);
System.err
is the standard error stream that is used to output error data from a program to the user:
System
.
err
.
println
(
"Not enough players"
);
Figure 12-1 shows a class hierarchy for commonly used readers, writers, and input and output streams. Note that I/O classes can be chained together to get multiple effects.
The Reader
and Writer
classes are used for reading and writing character data (text). The InputStream
and OutputStream
classes are typically used for reading and writing binary data.
Java provides facilities to easily read and write to system files.
To read character data from a file, use a BufferedReader
. A FileReader
can also be used, but it will not be as efficient if there is a large amount of data. The call to readLine()
reads a line of text from the file. When reading is complete, call close()
on the BufferedReader
:
BufferedReader
bReader
=
new
BufferedReader
(
new
FileReader
(
"Master.txt"
));
String
lineContents
;
while
((
lineContents
=
bReader
.
readLine
())
!=
null
)
{...}
bReader
.
close
();
Use NIO 2.0’s Files.newBufferedReader(<path>,<charset>);
to avoid the implicit assumption about the file encoding.
To read binary data, use a DataInputStream
. The call to read()
reads the data from the input stream. Note that if only an array of bytes will be read, you should just use InputStream
:
DataInputStream
inStream
=
new
DataInputStream
(
new
FileInputStream
(
"Team.bin"
));
inStream
.
read
();
If a large amount of data is going to be read, you should also use a BufferedInputStream
to make reading the data more efficient:
DataInputStream
inStream
=
new
DataInputStream
(
new
BufferedInputStream
(
new
FileInputStream
(
team
)));
Binary data that has been read can be put back on the stream using methods in the PushbackInputStream
class:
unread
(
int
i
);
// pushback a single byte
unread
(
byte
[]
b
);
// pushback array of bytes
To write character data to a file, use a PrintWriter
. Call the close()
method of class PrintWriter
when writing to the output stream is complete:
String
in
=
"A huge line of text"
;
PrintWriter
pWriter
=
new
PrintWriter
(
new
FileWriter
(
"CoachList.txt"
));
pWriter
.
println
(
in
);
pWriter
.
close
();
Text can also be written to a file using a FileWriter
if there is only a small amount of text to be written. Note that if the file passed into the FileWriter
does not exist, it will automatically be created:
FileWriter
fWriter
=
new
FileWriter
(
"CoachList.txt"
);
fWriter
.
write
(
"This is the coach list."
);
fWriter
.
close
();
To write binary data, use a DataOutputStream
. The call to writeInt()
writes an array of integers to the output stream:
File
positions
=
new
File
(
"Positions.bin"
);
int
[]
pos
=
{
0
,
1
,
2
,
3
,
4
};
DataOutputStream
outStream
=
new
DataOutputStream
(
new
FileOutputStream
(
positions
));
for
(
int
i
=
0
;
i
<
pos
.
length
;
i
++)
outStream
.
writeInt
(
pos
[
i
]);
If a large amount of data is going to be written, then also use a BufferedOutputStream
:
DataOutputStream
outStream
=
new
DataOutputStream
(
new
BufferedOutputStream
(
positions
));
Java provides facilities to read and write to system sockets with ease.
To read character data from a socket, connect to the socket and then use a BufferedReader
to read the data:
Socket
socket
=
new
Socket
(
"127.0.0.1"
,
64783
);
InputStreamReader
reader
=
new
InputStreamReader
(
socket
.
getInputStream
());
BufferedReader
bReader
=
new
BufferedReader
(
reader
);
String
msg
=
bReader
.
readLine
();
BufferedReader
introduced the lines()
method in Java SE 8, relative to the new Stream API. This method returns a Stream
, the elements of which are lines lazily read from the contexted BufferedReader
.
To read binary data, use a DataInputStream
. The call to read()
reads the data from the input stream. Note that the Socket
class is located in java.net
:
Socket
socket
=
new
Socket
(
"127.0.0.1"
,
64783
);
DataInputStream
inStream
=
new
DataInputStream
(
socket
.
getInputStream
());
inStream
.
read
();
If a large amount of data is going to be read, then also use a BufferedInputStream
to make reading the data more efficient:
DataInputStream
inStream
=
new
DataInputStream
(
new
BufferedInputStream
(
socket
.
getInputStream
()));
To write character data to a socket, make a connection to a socket and then create and use a PrintWriter
to write the character data to the socket:
Socket
socket
=
new
Socket
(
"127.0.0.1"
,
64783
);
PrintWriter
pWriter
=
new
PrintWriter
(
socket
.
getOutputStream
());
pWriter
.
println
(
"Dad, we won the game."
);
To write binary data, use a DataOutputStream
. The call to write()
writes the data to an output stream:
byte
positions
[]
=
new
byte
[
10
];
Socket
socket
=
new
Socket
(
"127.0.0.1"
,
64783
);
DataOutputStream
outStream
=
new
DataOutputStream
(
socket
.
getOutputStream
());
outStream
.
write
(
positions
,
0
,
10
);
If a large amount of data is going to be written, then also use a BufferedOutputStream
:
DataOutputStream
outStream
=
new
DataOutputStream
(
new
BufferedOutputStream
(
socket
.
getOutputStream
()));
To save a version of an object (and all related data that would need to be restored) as an array of bytes, the class of this object must implement the interface Serializable
. Note that data members declared transient
will not be included in the serialized object. Use caution when using serialization and deserialization, as changes to a class—including moving the class in the class hierarchy, deleting a field, changing a field to nontransient or static, and using different JVMs—can all impact restoring an object.
The ObjectOutputStream
and ObjectInputStream
classes can be used to serialize and deserialize objects.
To serialize an object, use an ObjectOutputStream
:
ObjectOutputStream
s
=
new
ObjectOutputStream
(
new
FileOutputStream
(
"p.ser"
));
An example of serializing follows:
ObjectOutputStream
oStream
=
new
ObjectOutputStream
(
new
FileOutputStream
(
"PlayerDat.ser"
));
oStream
.
writeObject
(
player
);
oStream
.
close
();
To deserialize an object (i.e., turn it from a flattened version of an object to an object), use an ObjectInputStream
; then read in the file and cast the data into the appropriate object:
ObjectInputStream
d
=
new
ObjectInputStream
(
new
FileInputStream
(
"p.ser"
));
An example of deserializing follows:
ObjectInputStream
iStream
=
new
ObjectInputStream
(
new
FileInputStream
(
"PlayerDat.ser"
));
Player
p
=
(
Player
)
iStream
.
readObject
();
Java 9 allows incoming streams of object-serialization data to be filtered for security and robustness.
Java provides classes for creating compressed ZIP and GZIP files. ZIP archives multiple files, whereas GZIP archives a single file.
Use ZipOutputStream
to zip files and ZipInputSteam
to unzip them:
ZipOutputStream
zipOut
=
new
ZipOutputStream
(
new
FileOutputStream
(
"out.zip"
));
String
[]
fNames
=
new
String
[]
{
"f1"
,
"f2"
};
for
(
int
i
=
0
;
i
<
fNames
.
length
;
i
++)
{
ZipEntry
entry
=
new
ZipEntry
(
fNames
[
i
]);
FileInputStream
fin
=
new
FileInputStream
(
fNames
[
i
]);
try
{
zipOut
.
putNextEntry
(
entry
);
for
(
int
a
=
fin
.
read
();
a
!=
-
1
;
a
=
fin
.
read
())
{
zipOut
.
write
(
a
);
}
fin
.
close
();
zipOut
.
close
();
}
catch
(
IOException
ioe
)
{...}
}
To unzip a file, create a ZipInputStream
, call its getNextEntry()
method, and read the file into an OutputStream
.
To compress a GZIP file, create a new GZIPOutputStream
, pass it the name of a file with the .gzip extension, and then transfer the data from the GZIPOutputStream
to the FileInputStream
.
To uncompress a GZIP file, create a GZipInputStream
, create a new FileOutputStream
, and read the data into it.