Saving Objects in Java
When you are building java applications you might need to save the state of objects.
For example, If you are creating a game with multiple levels you might need to save the level the player is at.
There are many ways that you can save the state of an object. But in this article we will be considering two main ways you can save the state of an object without using a database.
- Serialization — Only if the data will be used by the Java program that generated it
- Writing to a Text file — If the Data will be used by other programs as well.
You can either serialize the objects write to a file, or write a plain text file. A serialized file is not in a human friendly, when we write to an plain text file it would be comma separated values.
When an object is serialized, It saves all the values of the instance variables of that object. So that a similar object can be created in the heap.
If a object has a instance variable which is a reference, then that object has to be serialized as well.
For example if the dog object has an instance variable named collar which is a reference to the collar object, when saving the collar object has to be serialized as well.
If you want to make a class Serializable, you have to implement the Serializable interface, the serializable interface does not have any methods that needs to be implemented. The purpose of the interface is to say that the class is serializable.
If a superclass implements the serializable interface the subclasses automatically becomes serializable.
If there is a situation where you shouldn't save an instance variable you can mark that variable as a transient.
String name;
transient int age; // age will not be saved
String breed;If you make an reference variable transient, The object will be null when you deserialize the object. You can initialize the object again, If the object doesn’t depend on a transient variable or if the value of that transient variable doesn’t matter.
you do not have to make an object serializable if the object relies on information that are specific to the run time, and cant be saved. You cant save things such as network connections, file objects and threads.
A reason for why a class shouldn’t be serializable would be for security, if you have information that should not be stored, or information where saving them is not useful.
If an object has references to multiple objects, that object wont be stored multiple times, during deserialization the references to that object will be restored.
For Example, if an owner has 2 dogs, Only a single object of the owner will be saved.
Writing a serialized object to a file.
Dog dog = new Dog();try{
FileOutputStream fileOutputStream = new FileOutputStream("Dog.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);//Writing the Dog object
objectOutputStream.writeObject(dog);//Close the ObjectOutputStream
objectOutputStream.close();}catch(IOException e){
e.printStackTrace
}
We provide the destination file to the File output stream. If the file doesnt exist the file output stream will create a new file in the given name. The file output stream can throw a FileNotFound Exception.
The Object output stream takes an parameter of type FileOutputStream. We can use the Object Output stream to write the Object to the Output Stream. The Object Output stream can throw a IOException.
After writing the object we close the stream. We only have to close the output stream at the top, since when we close the output stream at the top the ones undernaeth gets closed automatically.
In order to make the Dog object serializable, we have to make the Dog class implement the Serializable interface.
public class Dog extends Animal implements Serializable {The Dog.ser file would look as shown below.
There are 2 types of streams,
- Connection Streams
- Chain Streams
Connection streams connect the source to the destination, The FileOutputStream is a connection stream. It represents the connection.
Chain streams have to be connected to a connection stream. The ObjectOutputStream is a Chain Stream. The ObjectOutputStream converts the object so that it can be written into a stream.
When we use the WriteObject method and pass an object, That object goes into the ObjectOutputStream and then into the FileOutputStream and then gets written into the file.
Deserialization
During Deserialization the serialized object which was stored written to the file at serialization will be bought back to life. The object with all the instance variables will be bought back to life and an object will be created in the heap.
The JVM reads the file, and get the type of the class. Then the JVM tries to find and load that class, If the JVM cannot find the class or Load the class an Exception will be thrown, and then the object is recreated in the heap.
When the JVM creates the object it does not run the constructor of the serialized object, because if the constructor is run a new object of that class will be created. and that would mean the object that is created in the object will be in ‘new’ state, but we use serialization to save the object in order to get the object of the state that it was saved.
Get Shashane Ranasinghe’s stories in your inbox
Join Medium for free to get updates from this writer.
If the object that is being created has a class in its inheritance tree which does not implement the serializable interface then that classes constructor will be called and all the constructors of that non serializable class will be called despite of being serializable or not.
For Example. If the object we are trying to recreate is of class Dog and that class implements from the Class Canine, and the Canine class does not implement the serializable interface, then the constructor of the Canine class will be called, and the constructors of all the classes that the Canine class extends will be called. If the Canine class extends the Animal class, the constructor of the animal class will be called no matter if the animal class implements the serializable interface or not.
All the instance variables of the object will be initialized to the values that were saved
For Example if the dog object had an instance variable called age and its value was 5 before serialization, when recreating the dog object the value of the age variable will be 5.
All the transient variables will have default values.
The static variables are not serialized, since static variables are in class level.
The implementation to deserialize an object would look like this
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("Dog.ser"));
Dog restoredDog = (Dog) objectInputStream.readObject();
System.out.println(restoredDog.name);
}catch (Exception e){
e.printStackTrace();
}we used the FileOutputStream and the ObjectOutputStream to serialize the object. And as you can see we have used the FileInputStream, and ObjectInputStream to Deserialize the object.
When we are using Serialization and Deserialization there are some things that we have to consider.
Imagine after Serialize an object, A change occurs in the Class. This could be bad when we try to deserialize the object.
There are some changes that are usually okay, such as
- Adding new instance variables to the Class
- Adding or Removing Classes from the inheritance tree
- Changing the access level of an instance variable
- Changing an instance variable from non transient to transient
The variable will take the Default value
But there are some changes that shouldn’t be done to the class after serialization such as
- Deleting an instance variable
- Changing the type of the instance variable
- Moving a class up or down in the inheritance tree
- Making a Serializable class non-serializable
Removing the implementation of the Serializable interface - Changing an instance variable to static
If we feel like the class of the object is going to change after serialization, we can use a Serial Version Id. This id is stamped to the serialized object each time an object gets serialized. At Deserialization Java compares the Serial Version ID of the object with the class that the JVM is using to deserialize the object. If the IDs do not match an exception will be thrown.
Writing to a plain text file
If you need to save an object so that it can be used by another application we can write to an plain text file.
We can write to a text file as shown as below.
try{
FileWriter fileWriter = new FileWriter("Dog.txt");
fileWriter.write("Welcome to file writing");
fileWriter.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}We have to create a file writer object and give the name of the file in to which we are going to write, if the file does not exist the file writer will create a new file with the given name, and we can use the write method to write to the file. The write method takes an String argument, And finally close the fileWriter. And the code in the try block can throw an IOException.
and the output will be shown as this.
A filewriter writes everything you pass to the file writer each and every time. but by using a bufferedWriter we can make this process more efficient. A buffer is like a temporary storage where we can store the strings that we read, when the buffer is full, The strings are written to the file. The buffer temporarily stores the data and only writes when the buffer is full. Therefore the number of writes will reduce, and hence save time.
If you want to forcefully write what is in the buffer before the buffer gets filled you can use the flush() method. It will write what is currently in the buffer on to the file.
bufferedWriter.flush();Similarly you can read from a file as well. The following code shows how to read from a file.
try{
FileReader fileReader = new FileReader("Dog.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine())!= null){
System.out.println(line);
}
}catch (IOException ioException){
ioException.printStackTrace();
}We create a new file reader object and pass the file path. The file Reader is a connection stream. Then we create a buffered reader object which is a chained stream and chain it to the connection stream.
The we use loop to read the file until there are lines to read. The buffered reader throws an IOException. The filenotFound exception which is thrown by the File reader is caught when we catch the IOException.
The Output of the above code is shown below
Welcome to file writingIf we want to represent a file without simply providing the filename we can use an object of the File class. This class does not have any read or write methods. Therefore it is more secure to have a File object rather than just passing the filename.
The File object represents a file at a specific path, but it does not represent the file’s real contents. A File object can be used in most streams that can use a String filename.
Splitting Strings
If you want to split a string by a separator(Eg. separate a string by a comma) you can split a string in to multiple parts by using the split method.
String text = "Cat,Dog,Hippo,Bunny";//Using the Split method
String[] animals = text.split(",");System.out.println(Arrays.toString(animals));
//Output : [Cat, Dog, Hippo, Bunny]
We have to pass the separator to the split method, the split method will split the String and give us an Array of strings.
You can get to know more about Java by checking out my list on introduction to Java, by clicking here
References
- Kathy Sierra and Bert Bates. 2005. Head First Java, 2nd Edition. O’Reilly & Associates, Inc., USA.










