Previously we had explored reading and writing data in plain text files to disk. This works, but requires the developer to handle the processing of each line or token of input, which is rather tedious. It becomes much more challenging if we wish to handle, for example, lists of instances of objects, which could themselves contain other objects.
Fortunately Java makes it very easy to write instances of classes to disk, we simply implement the Serializable interface. The serializable interface does not require the implementation of any methods (it simply acts as a flag), to all that is required is to add implements Serializable
after the class declaration and add the appropriate import statement An example is shown here:
package com.tinyappco;
import java.io.Serializable;
class MyClass implements Serializable {
//class implementation goes here
}
Provided any classes that comprise the class also implement Serializable
, then it can be written to file. Note that if you try to write an object to disk and the class or a member class does not implement Serializable, then a NotSerializableException will be thrown
ArrayList
, LinkedList
, HashSet
and HashMap
as well as some other implementations of Collection implement Serializable, so provided the objects which any of those collection types contains also implement Serializable, then an Collection of objects can be easily written to file (which avoids saving objects one at a time)
Once we've implemented Serializable in the class (and any compositional classes), the process to write it to disk is relatively simple. We import the java.io.*
packages and
FileOutputStream
object pointing to the location of the file we wish to write to on disk (this will be created if it does not exist)ObjectOutputStream
which we pass the FileOutputStream object towriteObject
on the ObjectOutputStream object and pass in the object we wish to write to file//code assumes odo is an instance of Odometer class which implements Serializable
try {
FileOutputStream fileOutputStream = new FileOutputStream("save.dat");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(odo);
}
catch (Exception ex){
System.out.println("Can't write data to file");
}
Note that the file will not be readable in text format should you try and open it
The process for reading from file is farily similar, however instead of Output stream objects, we create input stream objects. Example code to do this is a follows:
try {
FileInputStream fileInputStream = new FileInputStream("save.dat");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Odometer odoFromFile = (Odometer)objectInputStream.readObject();
}
catch (Exception ex){
System.out.println("error reading from file or restoring data");
}
One thing that is important to be aware of is that we have to explicitly declare the type of object being read by the ObjectInputStream instance. This is because the method can read any type of object. To do this, we specify the type, in brackets, immediatly beore the object's method class e.g. (Odometer)objectInputStream.readObject();
. This is known as 'casting' and tells the computer what type of object something is, when it is unable to determine this itself.
The Poketutor game restarts everytime the application is launched - all the captured PokeTutors are lost and the player starts again. Implement a feature so that they players game state is automatically saved if they choose to exit the game, and ensure that their previous game state (if it exists) is loaded. Remember that the first time the player starts the game there will be no file, so you should test for this be deleting the file before launching the app once you have implemented the save and load functionality.