Objects and Java Seminar by Bill Venners
Input and Output
Lecture Handout
Agenda
- Introduce i/o streams
- Discuss filters
- Talk about object serialization
- Look at readers and writers
- Touch on a few additional classes
Input and Output
- The standard Java I/O package,
java.io
, is defined in
terms of streams.
- Streams are ordered sequences of data that have a:
- source (an input stream), or
- destination (an output stream).
- Abstract
InputStream
and OutputStream
classes
serve as base classes for various specialized stream subclasses that deal
with byte
s.
- Abstract
Reader
and Writer
classes
serve as base classes for various specialized stream subclasses that deal
with UNICODE char
s.
Output Streams
- Basic subclasses of class
OutputStream
are:
FileOutputStream |
- writes bytes to a file |
ByteArrayOutputStream |
- writes bytes sequentially to a byte array |
PipedOutputStream |
- writes bytes to another thread |
FilterOutputStream |
- superclass of "filter" output streams |
- Subclasses of
FilterOutputStream
are:
BufferedOutputStream |
- buffers output to an output stream |
DataOutputStream |
- writes primitive types and String s to an output stream |
PrintStream |
- writes values and objects to an output stream |
- Filter for serialization:
ObjectOutputStream |
- serializes an object to an output stream |
Input Streams
- Basic subclasses of class
InputStream
are:
FileInputStream |
- reads bytes from a file |
ByteArrayInputStream |
- reads bytes sequentially from a byte array |
PipedInputStream |
- reads bytes from another thread |
SequenceInputStream |
- combines multiple InputStreams in a series |
FilterInputStream |
- superclass of "filter" input streams |
- Subclasses of
FilterInputStream
are:
BufferedInputStream |
- buffers input from an input stream |
DataInputStream |
- reads primitive types and String s from an input stream |
PushbackInputStream |
- reads and unreads bytes from an input stream |
- Filter for deserialization:
ObjectInputStream |
- deserializes an object from an input stream |
Using Filters
- Filters are an example of the decorator pattern:
To add responsibilities to an object, you enclose that object in a
"decorator" (or "filter") object that has an interface in common with
the enclosed object.
- Must start with an underlying object, such as
FileOutputStream
, ByteArrayOutputStream
, or
PipedOutputStream
.
- Can enclose an underlying object in a filter, such as
BufferedOutputStream
or DataOutputStream
.
Example: Buffered Byte Output
-
For example, if want to write buffered bytes to a file:
-
Both the underlying object and the filter share a common interface, which
is
OutputStream
's interface.
-
This application write bytes to a file with buffered output:
1 // In file io/ex1/Example1.java
2 import java.io.*;
3
4 public class Example1 {
5
6 // Must enter a filename and a buffer
7 // size on the command line, as in:
8 //
9 // $ java Example1 outfile.dat 32
10 //
11 // The above command line will cause this
12 // program to write out byte values 0 to
13 // 99 to the file named filename. Output
14 // will be buffered in a buffer of size 32.
15 //
16 public static void main(String[] args)
17 throws IOException {
18
19 if (args.length < 2) {
20
21 System.out.println(
22 "Must enter filename and buffsize as arguments.");
23 System.exit(0);
24 }
25
26 int buffSize = Integer.parseInt(args[1]);
27
28 FileOutputStream fos =
29 new FileOutputStream(args[0]);
30
31 BufferedOutputStream bos =
32 new BufferedOutputStream(fos, buffSize);
33
34 for (int i = 0; i < 100; ++i) {
35
36 bos.write(i);
37 }
38
39 bos.close();
40 }
41 }
-
Here are the first sixteen bytes of
outfile.dat
in hex:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
Example: Buffered int
Output
-
For example, if want to write buffered
int
s to a file:
-
The underlying object and all the filters share a common interface, which
is
OutputStream
's interface.
-
This application writes
int
s to a file with buffered output:
1 // In file io/ex2/Example2.java
2 import java.io.*;
3
4 public class Example2 {
5
6 // Must enter a filename and a buffer
7 // size on the command line, as in:
8 //
9 // $ java Example2 outfile.dat 32
10 //
11 // The above command line will cause this
12 // program to write out int values 0 to 99
13 // to the file named filename. Output will
14 // be buffered in a buffer of size 32.
15 //
16 public static void main(String[] args)
17 throws IOException {
18
19 if (args.length < 2) {
20
21 System.out.println(
22 "Must enter filename and buffsize as arguments.");
23 System.exit(0);
24 }
25
26 int buffSize = Integer.parseInt(args[1]);
27
28 FileOutputStream fos =
29 new FileOutputStream(args[0]);
30
31 BufferedOutputStream bos =
32 new BufferedOutputStream(fos, buffSize);
33
34 DataOutputStream dos =
35 new DataOutputStream(bos);
36
37 for (int i = 0; i < 100; ++i) {
38
39 dos.writeInt(i);
40 }
41
42 dos.close();
43 }
44 }
-
Here's the first sixteen bytes of
outfile.dat
in hex:
00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 03
Example: Buffered int
Input
-
For example, if want to read buffered
int
s from a file:
-
The underlying object and all the filters share a common interface, which
is
InputStream
's interface.
-
This application reads
int
s from a file with buffered input:
1 // In file io/ex3/Example3.java
2 import java.io.*;
3
4 public class Example3 {
5
6 // Must enter a filename and a buffer
7 // size on the command line, as in:
8 //
9 // $ java Example2 infile.dat 32
10 //
11 // The above command line will cause this
12 // program to read in int values from the
13 // file named filename and print those ints
14 // to the standard output. Input will be
15 // buffered in a buffer of size 32.
16 //
17 public static void main(String[] args)
18 throws IOException {
19
20 if (args.length < 2) {
21
22 System.out.println(
23 "Must enter filename and buffsize as arguments.");
24 System.exit(0);
25 }
26
27 int buffSize = Integer.parseInt(args[1]);
28
29 FileInputStream fis =
30 new FileInputStream(args[0]);
31
32 BufferedInputStream bis =
33 new BufferedInputStream(fis, buffSize);
34
35 DataInputStream dis =
36 new DataInputStream(bis);
37
38 try {
39 for (;;) {
40
41 int i = dis.readInt();
42 String intAsString =
43 Integer.toString(i);
44 System.out.println(intAsString);
45 }
46 }
47 catch (EOFException e) {
48 }
49 finally {
50 dis.close();
51 }
52 }
53 }
-
Here's the first few lines of this program's output when reading
in a file written by the previous example:
0
1
2
3
4
Object Serialization
-
Allows you to read and write objects to and from a stream.
-
An object has:
- state (instance variables: on the heap)
- behavior (interface, method bodies: class file)
- identity (reference)
-
Serialization just writes out an object's state as a sequence of bytes.
-
Deserialization, therefore, needs the class files to reconstitute the objects.
-
All of state, including objects contained via composition, are serialized.
Why Serialize?
-
Allows you to store objects in files for later retrieval, or to send objects
across a network, etc.
-
Storing objects in a file is lightweight persistence:
- JavaBeans
- Applications:
UserPreferences
-
Sending an object across the network:
- RMI (send parameters, get back return value)
- Mobile Agents (class files + object state)
How to Serialize
-
To serialize an object, pass a reference to that object as the parameter of
ObjectOutputStream
's writeObject()
method, as in:
CoffeeCup cup = new CoffeeCup(50);
oos.writeObject(cup);
-
Only objects that implement the
java.io.Serializable
interface
will get serialized.
-
Class variables and transient instance variables won't get serialized:
private static int cupCount;
private final int size;
private transient int innerCoffee;
private Table myTable;
-
To deserialize an object, invoke
readObject()
on an
ObjectInputStream
, as in:
CoffeeCup cup = (CoffeeCup) ois.readObject();
-
Classes that require special handling can implement these two methods:
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException;
private void writeObject(ObjectOutputStream oos);
throws IOException;
-
To control everything, implement
java.io.Externalizable
public void readExternal(ObjectInput oi)
throws IOException, ClassNotFoundException;
public void writeExternal(ObjectOutput oo);
throws IOException;
Serialization Example
1 // In file io/ex4/Chair.java
2 public class Chair
3 implements java.io.Serializable {
4
5 public String toString() {
6 return "Chair";
7 }
8 }
1 // In file io/ex4/Table.java
2 public class Table
3 implements java.io.Serializable {
4
5 private Chair[] chairs;
6
7 public Table(Chair[] chairs) {
8
9 this.chairs = chairs;
10 }
11
12 public String toString() {
13
14 StringBuffer sb = new StringBuffer();
15
16 for (int i = 0; i < chairs.length; ++i) {
17
18 if (i !=0) {
19 sb.append(", ");
20 }
21 sb.append(chairs[i].toString());
22 }
23
24 return "Table{" + sb.toString() + "}";
25 }
26 }
1 // In file io/ex4/CoffeeCup.java
2 public class CoffeeCup
3 implements java.io.Serializable {
4
5 private int innerCoffee;
6 private Table myTable;
7
8 public CoffeeCup(int innerCoffee,
9 Table myTable) {
10
11 this.innerCoffee = innerCoffee;
12 this.myTable = myTable;
13 }
14
15 public String toString() {
16 return "CoffeeCup{" + innerCoffee
17 + ", " + myTable.toString() + "}";
18 }
19 }
1 // In file io/ex4/Example4.java
2 import java.io.*;
3
4 public class Example4 {
5
6 // Must enter a filename, an amount
7 // of coffee (in a cup), and a number
8 // of chairs (at a table) on the command
9 // line, as in:
10 //
11 // $ java Example4 outfile.dat 50 4
12 //
13 // The above command line will cause this
14 // program to create a CoffeeCup object with
15 // 50 ml of Coffee in it and a Table object
16 // with 4 chairs. The application will then
17 // serialize the CoffeeCup object to the file
18 // named as filename.
19 //
20 public static void main(String[] args)
21 throws IOException {
22
23 if (args.length < 3) {
24
25 System.out.println(
26 "Must enter filename, coffee, and chairs.");
27 System.exit(0);
28 }
29
30 int amtCoffee = Integer.parseInt(args[1]);
31 int chairCount = Integer.parseInt(args[2]);
32
33 Chair[] chairs = new Chair[chairCount];
34 for (int i = 0; i < chairCount; ++i) {
35 chairs[i] = new Chair();
36 }
37
38 Table table = new Table(chairs);
39 CoffeeCup cup =
40 new CoffeeCup(amtCoffee, table);
41
42 FileOutputStream fos =
43 new FileOutputStream(args[0]);
44
45 BufferedOutputStream bos =
46 new BufferedOutputStream(fos);
47
48 ObjectOutputStream oos =
49 new ObjectOutputStream(bos);
50
51 oos.writeObject(cup);
52 oos.close();
53 }
54 }
$ java Example4 cup.ser 43 3
1 // In file io/ex5/Example5.java
2 import java.io.*;
3
4 public class Example5 {
5
6 // Must enter a filename on the
7 // command line, as in:
8 //
9 // $ java Example5 outfile.dat
10 //
11 // The above command line will cause
12 // this program to attempt to read a
13 // serialized CoffeeCup object from
14 // the file named as filename.
15 //
16 public static void main(String[] args)
17 throws IOException, ClassNotFoundException {
18
19 if (args.length < 1) {
20
21 System.out.println(
22 "Must enter filename.");
23 System.exit(0);
24 }
25
26 FileInputStream fis =
27 new FileInputStream(args[0]);
28
29 BufferedInputStream bis =
30 new BufferedInputStream(fis);
31
32 ObjectInputStream ois =
33 new ObjectInputStream(bis);
34
35 CoffeeCup cup = (CoffeeCup) ois.readObject();
36 ois.close();
37
38 System.out.println(cup.toString());
39 }
40 }
$ java Example5 cup.ser
CoffeeCup{43, Table{Chair, Chair, Chair}}
Writers
-
Basic subclasses of class
Writer
are:
OutputStreamWriter |
- converts char s to bytes and writes to an
OutputStream |
FileWriter |
- convenience OutputStreamWriter for writing
to files with default character conversion |
CharArrayWriter |
- writes char s to a char array |
StringWriter |
- writes char s to StringBuffer |
PipedWriter |
- writes char s to another thread |
FilterWriter |
- abstract superclass of "filter" writers |
-
Filter writers (not subclasses of
FilterWriter
):
BufferedWriter |
- buffers character output to an writer |
PrintWriter |
- writes values and objects to a character output stream |
Readers
-
Basic subclasses of class
Reader
are:
InputStreamReader |
- reads from an InputStream and converts bytes
to char s |
FileReader |
- convenience InputStreamReader for reading
from files with default character conversion |
CharArrayReader |
- reads char s sequentially from a char array |
StringReader |
- reads char s sequentially from a String |
PipedReader |
- reads char s from another thread |
FilterReader |
- abstract superclass of "filter" readers |
-
Filter readers (most not subclasses of
FilterReader
):
BufferedReader |
- buffers char s from a reader |
LineNumberReader |
- a BufferedReader that keeps track of line numbers
while reading char s from a reader |
PushbackReader |
- FilterReader that reads and unreads char s from reader |
Character I/O Example
1 // In file io/ex6/Example6.java
2 import java.io.*;
3
4 public class Example6 {
5
6 // Must enter a filename on the
7 // command line, as in:
8 //
9 // $ java Example6 outfile.dat
10 //
11 // The above command line will cause
12 // this program to attempt to read
13 // characters from the named file and
14 // print them to the standard output.
15 //
16 public static void main(String[] args)
17 throws IOException {
18
19 if (args.length < 1) {
20
21 System.out.println(
22 "Must enter filename.");
23 System.exit(0);
24 }
25
26 // File reader is already buffered.
27 FileReader fr =
28 new FileReader(args[0]);
29
30 OutputStreamWriter osw =
31 new OutputStreamWriter(System.out);
32
33 BufferedWriter bw =
34 new BufferedWriter(osw);
35
36 int c = fr.read();
37 while (c != -1) {
38
39 bw.write(c);
40 c = fr.read();
41 }
42 fr.close();
43 bw.close();
44 }
45 }
-
The input side:
-
The output side:
Class RandomAccessFile
-
Allows you to both read from and write to a file.
-
Allows you to modify (with a
seek(long)
invocation) the position
in the file where the next read or
write occurs.
-
Not part of the
InputStream
/OutputStream
families.
-
Does operate on bytes (not
chars
).
-
Does implement the
DataOutput
and DataInput
interfaces (can read and write primitive types).
Class File
-
Represents the name of a file or directory on the host file system.
-
Intended to deal with machine-dependent complexities of file and pathnames in
a machine-independent way.
-
Allows you to do things like:
boolean canRead()
- see if a file is readable
boolean canWrite()
- see if a file is writable
boolean exists()
- test if a file exists
long length()
- get length of file
boolean mkdir()
- make a directory
boolean renameTo()
- rename a file
String[] list()
- list files in a directory
String getAbsolutePath()
- return full pathname
String getCanonicalPath()
- return short name
-
Can pass a
File
object to constructors for things like
FileReader
, FileWriter
, FileInputStream
,
FileOutputStream
, etc.
Class StreamTokenizer
-
Powerful way to parse characters from a
Reader
into tokens:
-
Tokenizer can recognize:
- identifiers
- numbers
- quoted strings
- comments
-
Can use it to parse Java code.
-
Extends object, but takes a
Reader
in its constructor.
Exercises
Problem 1.
Write an application named Prettifier.java
that reads
lines of text from the standard input. For each line read in, prepend
the line with the line number followed by a single space, and print
this to the standard output.
Problem 2.
In the
PackagesAccess/innerclasses/ex3
directory of the
sample code, edit
Example3.java
. At the end of the
main()
method, write a serialized version of the
CoffeeCup
object to a file named
"freezedried.coffee"
. To get this to work, you'll have
to make changes to several other source files besides
Example3.java
.
Problem 3.
While still in the PackagesAccess/innerclasses/ex3
directory of
the sample code, create a new class named Example3a.java
with the usual main()
method. Inside this
main()
method, deserialize the CoffeeCup
object which you stored in file freezedried.coffee
in
Problem 2. Get an iterator from
the resurrected CoffeeCup
and iterate through the objects contained in the cup. For each
object returned by the iterator, print out the String
returned
by invoking toString()
on the object.
Problem 4.
Create an application called Cat
that takes zero to many
command line arguments. Unlike all other Cat
classes used
in examples in this course, this Cat
actually does something
moderately useful. It mimics the functionality of the Unix cat
command -- it "concatenates and prints."
If Cat
is invoked with no
arguments, it reads bytes from the standard input and
writes them to the standard output. It continues this process until
it reaches end of file on the standard input. If Cat
is
invoked with one or more arguments, it interprets these command line
arguments as filenames. It attempts to open each named file in the order
the filenames appear on the command line. It reads each file in turn
(byte by byte) and prints it's contents to the standard output.
One last request: please buffer your input and output.
Problem 5.
Take the
Cat
application from Problem 4 and change its name
to
UniCat
. Make
UniCat
concatenate and print
Unicode character files, in the same manner that plain old
Cat
concatenated and printed bytes. Use
Reader
s and
Writer
s to perform the any necessary character transformations,
such as from ASCII in files to Unicode in the program and back to ASCII
on the standard output.
As usual, buffer your input and output.