🧠 8.3 Working with Streams
🟥 8.3.1 The FileInputStream and FileOutputStream Classes
The FileInputStream and FileOutputStream are used to read/write bytes to a file
The classes have constructors which accept either a File or String which points to location of file
E.g.:
String location = System .getProperty ("user.dir" )+"\\ src" + "\\ chapter_8\\ c_8_3_workinWithStreams\\ javaCode\\ data.txt" ;
try (InputStream in = new FileInputStream (location )) {
int b ;
while ((b =in .read ())!=-1 ) {
System .out .print (b );
}
}
// prints TIGERS
The FileOutStream
object is accessed via writing successive bytes using write(int)
, here is an example of writing TIGER
to a new file:
String destination = System .getProperty ("user.dir" )+"\\ src" +
"\\ chapter_8\\ c_8_3_workinWithStreams\\ javaCode\\ output.txt" ;
try (FileOutputStream out = new FileOutputStream ()) {
char [] chars = new char [] {'t' , 'i' , 'g' , 'e' , 'r' , 's' };
for (char c : chars ) {
out .write (c );
}
}
Running the above code will create a new file with text of tigers
inside!
🟡 The BufferedInputStream and BufferedOutputStream Classes
We can wrap the FileInputStream
and FileOutputStream
classes with BufferedInputStream
and BufferedOutputStream
The BufferedInputStream
class has a read(byte[])
method which reutnrs the number of bytes read into the argument array.
If the return is 0, then we are at the end of file.
public class WorkingWithBufferedInputStream {
static void copy (File source , File destination ) throws IOException {
try (InputStream in = new BufferedInputStream (new FileInputStream (source ));
OutputStream out = new BufferedOutputStream (new FileOutputStream (destination ))) {
byte [] buffer = new byte [1024 ];
int lengthRead ;
while ((lengthRead = in .read (buffer )) > 0 ) {
System .out .println (lengthRead );
out .write (buffer ,0 ,lengthRead );
out .flush ();
}
}
}
public static void main (String [] args ) throws IOException {
String source = System .getProperty ("user.dir" )+"\\ src" +
"\\ chapter_8\\ c_8_3_workinWithStreams\\ javaCode\\ data.txt" ;
String destination = System .getProperty ("user.dir" )+"\\ src" +
"\\ chapter_8\\ c_8_3_workinWithStreams\\ javaCode\\ Returned-data.txt" ;
File sourceFile = new File (source ); // this file contains the alphabet!
Fille destinationFile = new File (destination );
copy (sourceFile , destinationFile );
// prints 27 - the length of the source file
// the data is copied to the destination file
}
}
🟥 8.3.2 The FileReader and FilerWriter Classes
The FileReader and FileWriter classes are the most convenient classes for reading and writing text data
These classes have a read()
and write()
which read ad write char values, unlike the byte values from the FileInput/Output classes.
The Writer class offers a write(String)
which allows a String object to be directly written to a stream
🟡 The BufferedReader and BufferedWriter Classes
static List <String > readFile (Fille source ) throws IOException {
List <String > data = new ArrayList <>();
try (BufferedReader reader = new BufferedReader (ney FileReader (source ))) {
String s ;
while ((s =reader .readLine ()) != null ) {
data .add (s );
}
}
return data ;
}
// MAIN METHOD:
String source = System .getProperty ("user.dir" )+"\\ src" +
"\\ chapter_8\\ c_8_3_workinWithStreams\\ javaCode\\ text.txt"
// this file has 3 lines
List <String > data = readFile (new File (source ));
System .out .println (data );
// [Line 1, Line 2, Line 3]
🟥 8.3.3 The ObjectInputStream and ObjectOutputStream Classes
Serialisation is the process of writing an in memory Java object to the disk, deserialisation is converted the stored data into an object.
🟡 The Serializable Interface
You can serialise a java object provided it implements java.io.Serializable
- an interface which contains no methods and serves to act as a marker
You implement Serializable
to indicate you have taken premeasures to ensure an object can be serialized
Trying to Serialize a class which does not properly implement Serializable, will throw a NotSerializablleException
.
You can apply the transient
keyword to instance variables to skip deserialization.
Static fields are also ignored even without the transient keyword
Here is an example of a class which implements the interface:
public class Animal implements Serializable {
private static final long serialVersionUID = 1L ;
private String name ;
private int age ;
private char type ;
public Animal (String name , int age , char type ) {
this .name = name ; this .age = age ; this .type = type ;
}
public String toString () {
return "Animal [name=" + name + ", age=" + age + ", type=" + type + "]" ;
}
}
The serialVersionUID
is a good practice to avoid conflicts with deserialisation
🟡 Serializing and Deserializing Objects
The java.io
API has two classes to achieve this: ObjectInputStream
and ObjectOutputStream
The ObjectOutputStream
class has a void writeObject(Object)
method which if is not serializable or contains an embedded reference to a class which is not Serializable then a NotSerializableException
at runtime.
Here is a method which serializes the Animal objects:
static void createAnimalsFile (List <Animal > animals , File dataFile ) {
try (ObjectOutputStream out = new ObjectOutputStream (new FileOutputStream (dataFile ))) {
for (Animal animal : animals ) {
out .writeObject (animal );
}
}
}
The ObjectInputStream
class has a Object readObject()
method.
Here is a method which deserializes data:
static List <Animal > getAnimals (File dataFile ) {
try (ObjectInputStream in = new ObjectInputStream (
new FileInputStream (new File (dataFile )))) {
List <Animal > data = new ArrayList <>();
while (true ) {
Object object = in .readObject ();
if (object instanceof Animal )
data .add ((Animal ) object );
}
} catch (EOFException e ) {
}
return data ;
}
Here is a program which serialises some Animal data:
List <Animal > animals = new ArrayList <>();
animals .add (new Animal ("Monkey" , 5 , 'M' ));
animals .add (new Animal ("Parrot" , 2 , 'B' ));
String animalFile = System .getProperty ("user.dir" )+"\\ src" +
"\\ chapter_8\\ c_8_3_workinWithStreams\\ javaCode\\ animal.data" ;
File dataFile = new File (animalFile );
createAnimalsFile (animals , dataFile );
This created the animal.data
file with the following inside:
The folllowing code deserializes the above and prints the results:
List <Animal > deserializedData = getAnimals (dataFile );
System .out .println (deserializedData );
// [Animal [name=Monkey, age=5, type=M], Animal [name=Parrot, age=2, type=B]]
🟡 Understanding Object Creation
When you deserialize an object, it is not NECESSARILLY created through the constructor but calls the first no-argument constructor nonseriallizable parent class.
public class Animal implements Serializable {
private static final long serialVersionUID = 2L ;
private transient String name ;
private transient int age = 10 ;
private static char type = 'C' ;
{ this .age = 14 ; }
public Animal () {
this .name = "Unknown" ;
this .age = 12 ;
this .type = type ;
}
public Animal (String name , int age , char type ) {
this .name = name ;
this .age = age ;
this .type = type ;
}
public String getName () { return name ; }
public int getAge () { return age ; }
public char getType () { return type ; }
public String toString () {
return "Animal [name=" + name + ", age=" + age + ", type=" + type + "]" ;
}
}
Rerunning the above program generates the following
[Animal [name=null, age=0, type=B], Animal [name=null, age=0, type=B]]
Since name and age are transient, they get the default value the class would recieve.
The type is a static field, so it maintains the value of which it was last set to.
🟥 8.3.4 The PrintStream and PrintWriter Classes
These are high oevel stream classes which write formatted representation of Java objects to TEXT-BASED output stream.
System.out
and System.err
are actually PrintStream objects!
Both classes support the underlying write()
method as well as methods like print()
, println()
, format()
, and printf()
These methods do NOT throw a checked IOException
The print()
method is overloaded with Java primitives, String and Object. Typically this method calls String.valueOf()
on the argument and call the underlying stream's write() method
E.g. the following print and write methods are equivalent
PrintWriter out = new PrintWriter ("zoo.log" );
out .print (5 ); // PrintWriter method
out .write (String .valueOf (5 )); // Writer method
out .print (2.0 ); // PrintWriter method
out .write (String .valueOf (2.0 )); // Writer method
Animal animal = new Animal ();
out .print (animal ); // PrintWriter method
out .write (animal ==null ?"null" :animal .toString ()); // Writer method
This method is same as print method except it prints to a new line
This method is useful when you are unsure of the new line symbol on a system. You can always obtain it using:
System .getProperty ("line.seperator" ); // \n on my system
These methods are akin to the methods in C
.
These two methods have identical ehaviour and only differ in name:
public PrintWriter format(String format, Object args...)
public PrintWriter printf(String format, Object args...)
Consider the following application:
String destination = System .getProperty ("user.dir" )+"\\ src" +
"\\ chapter_8\\ c_8_3_workinWithStreams\\ javaCode\\ zoo.log" ;
File source = new File (destination );
try (PrintWriter out = new PrintWriter (new FileWriter (source ))){
out .print ("Today's weather is: " );
out .println ("Sunny" );
out .println ("Today's temperature is: " +1 /3.0 );
out .print ('C' );
out .format ("It has rained %f inches this year" , 1.2F );
}
Today's weather is: Sunny
Today's temperature is: 0.3333333333333333
CIt has rained 1.200000 inches this year
Notice how format()
did not print a new line!
🟥 8.3.5 Review of Stream Classes
Here is a diagram of the classes in the java.io
package: