What happens during serialization in java, if two object refrences are pointing to the same serializable Object? Does the Serializable Objects get saved twice ?
for example :
class King implements java.io.Serializable {
private String name="Akbar";
}
class Kingdom implements java.io.Serializable {
King goodKing=new King();
King badKing=goodKing;
}
public class TestSerialization {
public static void serializeObject(String outputFileName,
Object serializableObject) throws IOException {
FileOutputStream fileStream=new FileOutputStream(outputFileName);
ObjectOutputStream outStream=new ObjectOutputStream(fileStream);
outStream.writeObject(serializableObject);
outStream.close();
}
public static void main(String[] args) {
Kingdom kingdom=new Kingdom();
try {
TestSerialization.serializeObject("Kingdom1.out", kingdom);
}catch(IOException ex) {
ex.getMessage();
}
}
}
Now, whether only one object state is saved for both goodKing and badKing refrences or the King object get saved twice ?
The documentation for ObjectOutputStream says what happens:
The default serialization mechanism for an object writes the class of the object, the class signature, and the values of all non-transient and non-static fields. References to other objects (except in transient or static fields) cause those objects to be written also. Multiple references to a single object are encoded using a reference sharing mechanism so that graphs of objects can be restored to the same shape as when the original was written.
(My emphasis)
E.g., if you have multiple references to a single object, when the graph is reconstituted, you end up with multiple references to a single reconstituted version of that object, not references to multiple equivalent instances of it.
Of course, if the container being serialized implements a different mechanism, the behavior is dictated by that mechanism, not the default one.
So for instance, if we have Thing and Test:
Thing.java:
import java.io.*;
import java.util.*;
public class Thing implements Serializable {
private Map<String,String> map1;
private Map<String,String> map2;
public Thing() {
this.map1 = new HashMap();
this.map2 = this.map1; // Referring to same object
}
public void put(String key, String value) {
this.map1.put(key, value);
}
public boolean mapsAreSameObject() {
return this.map1 == this.map2;
}
}
Test.java:
import java.io.*;
public class Test implements Serializable {
public static final void main(String[] args) {
try
{
// Create a Thing
Thing t = new Thing();
t.put("foo", "bar");
// Write it out
ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("foo"));
os.writeObject(t);
os.close();
os = null;
// Read it in
Thing t2;
ObjectInputStream is = new ObjectInputStream(new FileInputStream("foo"));
t2 = (Thing)is.readObject();
is.close();
is = null;
// Same underlying map for both properties?
System.out.println("t2.mapsAreSameObject? " + t2.mapsAreSameObject());
}
catch (Exception e)
{
System.out.println("Exception: " + e.getMessage());
}
}
}
And run java Test, we get:
t2.mapsAreSameObject? true
...because both of Thing's members, map1 and map2 end up pointing to a single HashMap instance.
Related
I had a class Inventory that contains a list of Treasure class. I designed the Reader to read the Inventory as a list of Treasure, but when I want to call the reader from my main function, it says that Inventory is not the same as a list of Treasure. And now I don't know what should I do, should I change the read method to only read Inventory, but then again Inventory contains all the list of Treasure that I need. I am very lost as how to do it.
public class Inventory implements SavedGames {
private ArrayList<Treasure> inventory;
private static Inventory instance;
// EFFECTS: constructs an empty player inventory
private Inventory() {
this.inventory = new ArrayList<Treasure>();
inventory.add(new Treasure("Potion", 4));
}
private void loadGames() {
try {
MainCharacter character = Reader.readCharacter(new File(CHARACTER_FILE));
Monster monster = Reader.readMonster(new File(MONSTER_FILE));
List<Treasure> inventory = Reader.readInventory(new File(INVENTORY_FILE));
this.character = character;
this.monster = monster;
this.inventory = inventory;
} catch (IOException e) {
System.out.println("Unable to read files, resetting...");
startApp();
}
}
private static List<Treasure> parseInventory(List<String> fileContent) {
List<Treasure> inventory = new ArrayList<>();
for (String line : fileContent) {
ArrayList<String> lineComponents = splitString(line);
inventory.add(parseTreasure(lineComponents));
}
return inventory;
}
private static Treasure parseTreasure(List<String> components) {
String description = components.get(0);
int amount = Integer.parseInt(components.get(1));
return new Treasure(description, amount);
}
}
Serialization is a mechanism of converting the state of an object into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This mechanism is used to persist the object.
The byte stream created is platform independent. So, the object serialized on one platform can be deserialized on a different platform.
To make a Java object serializable we implement the java.io.Serializable interface.
The ObjectOutputStream class contains writeObject() method for serializing an Object.
public final void writeObject(Object obj)
throws IOException
The ObjectInputStream class contains readObject() method for deserializing an object.
public final Object readObject()
throws IOException,
ClassNotFoundException
I'm kind of stuck with a problem. I do understand the concept of serialization. Nevertheless I'm getting errors when I try to serialize/deserialize (deepCopy) an object:
I have a basic domain objects that hold information (two files):
public class DomainObject implements java.io.Serializable {
private String defaultDescription = "";
private List<Translation> translations;
public DomainObject() {
;
}
public void setTranslations(final List<Translation> newTranslations) {
this.translations = newTranslations;
}
public List<Translation> getTranslations() {
return this.translations;
}
public void setDefaultDescription(final String newDescription) {
this.defaultDescription = newDescription;
}
public String getDefaultDescription() {
return this.defaultDescription;
}
}
public class Translations implements java.io.Serializable {
private String description = "";
public Translation() {
;
}
public void setDescription(final String newDescription) {
this.description = newDescription;
}
public String getDescription() {
return this.description;
}
}
I also have a frame so the user can fill in all the necessary information for this domain object. Since I have multiple domain objects (this example only shows one) with different fields I have different frames for each domain object. Each of these frames includes a "MultiLanguageFrame" which gives the user the ability to add optional translations for this domain object's description.
public class MultiLanguageFrame extends org.eclipse.swt.widgets.Composite {
private List<Translation> translations = new ArrayList<Translation>();
public MultiLanguageFrame(final Composite parent, final int style) {
super(parent, style);
...
}
public List<Translation> getTranslations() {
return translations;
}
}
I deepCopy objects via this method:
...
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
oos.flush();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception t) {
logger.error(deepCopy() error: " + t.getMessage()); //$NON-NLS-1$
throw new RuntimeException("deepCopy() error", t); //$NON-NLS-1$
}
So now to the error:
When i try to do something like this:
MultiLanguageFrame frame = new MultiLanguageFrame(parent, SWT.NONE);
DomainObject dom = new DomainObject();
dom.setDefaultDescription("Testobject");
dom.setTranslations(frame.getTranslations())
deepCopy(dom);
I receive an error telling me that MultiLanguageFrame is not Serializable. Why would Java try to serialize the frame when I only want that DomainObject?
I thought maybe it is because of the reference in frame. So when I add the Serializable-Interface to MultiLanguageFrame and markt the SWT-Components as transient it tells me that no valid constructor was found. I can't add a parameterless constructor because it would logically make no sense and also SWT-Components need a parent to exist.
I'm really stuck with this problem because I do not know how to work around this. Thanks for answers in advance!
I found the solution myself. I'll just post this so others can see it, it might help.
Thanks to #greg-449 who lead the way. I do have an inner class TranslationHelper which extends Translation in MultiLanguageFrame. The purpose of this is so I can save some flags (deleted, changed, new) for Translations without changing Translation itself. When I call frame.getTranslations() I cast the elements from TranslationsHelper to Translation. The instance of the object remains a TranslationHelper though.
Now it all makes sense that MultiLanguageFrame was involved in all of this.
Following on from this question;
Please give a short example of how one would de-serialize an object with two references.
I've included the below code so all answers refer to the same object names.
public class Person implements Serializable{
private String name;
private int age;
private boolean single;
private int numKids;
private final static long serialVersionUID = 1L;
public Person(String name, int age, boolean single, int numKids) {
this.name = name;
this.age = age;
this.single = single;
this.numKids = numKids;
this.surname = surname;
}
}
Assuming the following:
A text file has already been created
We created a Person object jim
There is another Person bob that references the jimobject
The question already referenced describes how only jim is written to the file. The bob reference is not. How then do we read the values to jim and bob if the file might contain another few objects of the Person class? How can we ensure that bob has the correct value?
You seem to be confused a bit. Your Person class does not allow for referencing another Person object. You would need a Person field within the Person class to get there! From that perspective, your question doesn't make sense: jim and bob would be two completely independent objects, and serializing one of them will not at all serialize the other one!
But beyond that: typically, when you think in plurals of things (like multiple persons) then you would have an enclosing "container" object (like a List<Person>) that you think about. You don't deal with single Person objects then - but with such containers when thinking about serialization!
You are trying to build an artificial example that simply doesn't work out. Rather think of a situation where the Person class has maybe a field Person spouse. Then alice could be referenced by bob. And when you now have those Person objects, and some more in a list, and you serialize that list - then the system will make sure that alice and bob are serialized just once.
I am little confused your question - maybe because I do not see any issue with serialization and deserialization of the same object and/or multiple objects with or without references to any objects.
The point is that serialization is like creating copy of object (in file system or somewhere). This copy can be recreated in memory (deserialization).
You may create object in memory (deserialization) once or more times.
It something like:
object A --serialization--> file A
file A --deserialization--> object A'
file A --deserialization--> object A"
object A, A' and A" are different objects - but all fields will be have the same values.
If object A contains an sophisticated structure (which can be serialized/deserialized) it can be also another objects then the same mechanism are working also for these objects.
All fields will have the same values but object will be different.
see sample code:
package test;
import java.io.Serializable;
public class Person implements Serializable {
int id;
String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
}
and kind of test
package test;
import java.io.*;
public class Main {
public static void main(String... args) {
Person p1 = new Person(1, "aaa");
Person p1a = null;
Person p1b = null;
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.data"))) {
oos.writeObject(p1);
} catch (IOException e) {
e.printStackTrace();
}
try (ObjectInputStream oos = new ObjectInputStream(new FileInputStream("test.data"))) {
p1a = (Person) oos.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try (ObjectInputStream oos = new ObjectInputStream(new FileInputStream("test.data"))) {
p1b = (Person) oos.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
assert p1a != null && p1b != null;
assert p1a.id == p1b.id;
assert p1.id == p1b.id;
assert p1.name.equals(p1a.name);
assert p1a.name.equals(p1b.name);
System.out.println(String.format("Three different object: %s, %s, %s", p1, p1a, p1b));
}
}
PS. How java.lang.String objects are copied/stored/managed in memory it is different story.
How can I deserialize a class which was modified after serialization?
More specifically, I know this can be done when a class had serialVersionUID in its initial version. Is there any way to do it for classes without serialVersionUID?
I have an object
package com.test.serialize;
import java.io.Serializable;
public class MyObject implements Serializable{
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and I serialize classes like this
package com.test.serialize;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializeTest {
public static void main(String[] args) {
try {
MyObject myObject = new MyObject();
myObject.setName("Ajit");
ObjectOutputStream objectOStr = null;
ByteArrayOutputStream byteOStr = null;
try {
byteOStr = new ByteArrayOutputStream();
objectOStr = new ObjectOutputStream(byteOStr);
objectOStr.writeObject(myObject);
} catch (IOException e) {
System.out.println(e);
} finally {
try {
if (objectOStr != null)
objectOStr.close();
} catch (IOException e2) {
}
}
FileOutputStream fo = new FileOutputStream(new File("serialize"));
fo.write(byteOStr.toByteArray());
fo.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
and deserialize like this
package com.test.serialize;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.RandomAccessFile;
public class DeserializeTest {
public static void main(String[] args) {
try {
// File f = new File("serialize");
// FileInputStream fs = new FileInputStream(f);
RandomAccessFile raF = new RandomAccessFile("serialize", "r");
byte[] b = new byte[(int)raF.length()];
raF.read(b);
ObjectInputStream oIstream = null;
ByteArrayInputStream bIstream = null;
bIstream = new ByteArrayInputStream(b);
oIstream = new ObjectInputStream(bIstream);
Object finalResult = oIstream.readObject();
System.out.println(finalResult.toString());
} catch (IOException | ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
After some time, I added
#Override
public String toString() {
return "MyObject [name=" + name + ", names=" + names + "]";
}
to MyObject. After adding that I got exceptions like
java.io.InvalidClassException: com.test.serialize.MyObject; local class in
compatible: stream classdesc serialVersionUID = 5512234731442983181, local class
serialVersionUID = -6186454222601982895
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1622)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at com.test.serialize.DeserializeTest.main(DeserializeTest.java:25)
Please help me with this.
Thanks #Gábor Bakos.
This can be solved by creating serialVersionUID for older class (Which signatures should be same as the one during serialization )and adding that serialVersionUID in current class.
serialver -classpath /***PATH***/bin com.test.serialize.MyObject
That returns
com.test.serialize.MyObject: static final long serialVersionUID = 5512234731442983181L;
After that I have added it to my MyObject as below
package com.test.serialize;
import java.io.Serializable;
public class MyObject implements Serializable{
/**
* Added serial version Id of old class, created before adding new fields
*/
private static final long serialVersionUID = 5512234731442983181L;
public MyObject() {
System.out.println("Constructor");
}
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
String names ="Altered after change!";
public String getNames() {
return names;
}
public void setNames(String names) {
System.out.println("Setting names");
this.names = names;
}
#Override
public String toString() {
return "MyObject [name=" + name + ", names=" + names + "]";
}
}
It works fine.
More info refer: serialver
First advice: use serialization, because everything is almost done.
Second advice: use a serialVersionUID and keep it fix with one version: it is here to warn you and prevent confusion between different serialized versions.
So : if you change fields or meaning of fields, change the serialVersionUID.
Then you have your backward compatibility problem.
See this for many ideas: Managing several versions of serialized Java objects
IMHO:
whatever solution you take, keep in mind that your program will be managing objects with partial datas: then you have to manage all cases with or without datas.
if you dont change often your version: use several different classes. Perhaps subclasses, or implementations of an interface: then you can get your program, and you manage several versions of object: MyClass_V1, MyClass_V2, etc. When you deserialize, you can test/retry and get the good Object. After that, you perhaps have to convert datas between your classes
if you change your version, by adding new fields (not changing old fields), it is a little more easy (subclasses, converting is direct to parents)
or you could consider use a XML structure to serialize and deserialize: you can have backward and forward compatibility because it is extensible: fields are there, or are null. You have to manage mapping yourself or use some libraries.
Hope it helps !
I would remember following points,
Every Serializable class contains a serialVersionUID ( it doesn't matter if you have specified the one explicitly or not ).
There are compatible changes and there are incompatible changes
e.g. adding a new field is a compatible change, removing a field is not a compatible change. Adding / removing / editing a method are generally compatible changes but in your case surely that is not the way it is ( serialVersionUID got changed after you added toString() method)
3.Prior to modify the class, you can use serialver utility to find serialVersionUID of old class and use that in new class
Don't think there are any other magic tricks :)
I'm stumbled upon understanding java serialization. I have read in many documents and books that static and transient variables cannot be serialized in Java.
We declare a serialVersionUid as follows.
private static final long serialVersionUID = 1L;
If a static variable was not serialized then, we often face an exception during the de-serialization process.
java.io.InvalidClassException
in which the serialVersionUID from the deserialized object is extracted and compared with the serialVersionUID of the loaded class.
To my knowledge i think that if static variables cannot be serialized. There is no point of that exception. I may be wrong because I'm still learning.
Is it a myth that "Static and transient variables in java cannot be serialized". Please correct me, I'm in a mess about this concept.
Instance Variables: These variables are serialized, so during deserialization we will get back the serialized state.
Static Variables: These variables are not serialized, So during deserialization static variable value will loaded from the class.(Current value will be loaded.)
transient Variables: transient variables are not serialized, so during deserialization those variables will be initialized with corresponding default values (ex: for objects null, int 0).
Super class variables: If super class also implemented Serializable interface then those variables will be serialized, otherwise it won't serialize the super class variables. and while deserializing, JVM will run default constructor in super class and populates the default values. Same thing will happen for all superclasses.
serialVersionUID is a special static variable used by the serialization and deserialization process, to verify that a local class is compatible with the class used to serialize an object. It's not just a static variable as others, which are definitely not serialized.
When an object of a class is first serialized, a class descriptor containing among other things the class name and serial version UID is written to the stream. When this is deserialized, the JVM checks if the serial version UID read from the stream is the same as the one of the local class. If they're not, it doesn't even try to deserialize the object, because it knows the classes are incompatible.
serialVersionUID is special and is not subject to these rules. There is code within the serialization machinery that specifically handles this field to perform the automatic version checks.
The serialVersionUID is also serialized in this case.
Any static variable that is provided a value during class initialization is serialized.
However in normal cases, where you would provide the value to a static variable at the main class / run-time would not be serialized.
You can try to access the serialVersionUID by making it public and try to access it after deserialization.
You can refer "http://javabeginnerstutorial.com/core-java-tutorial/transient-vs-static-variable-java/" for more information.
Hope that helps. Cheers !!
You can test this for yourself - here's some example code that should answer your question:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
class TestJava implements Serializable{
public static int k = 10;
public int j = 5;
public static void main(String[] args) {
TestJava tj1= new TestJava();
TestJava tj2;
try{ //serialization
FileOutputStream fos = new FileOutputStream("myclass.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(tj1);
oos.close();
fos.close();
System.out.println("object serielized 1..."+tj1.j);
System.out.println("object serielized 2..."+tj1.k);
System.out.println("object serielized 3..."+k);
k=++k; // 'k' value incrementd after serialization
} catch(FileNotFoundException fnfe){
fnfe.printStackTrace();
} catch(IOException ioex){
ioex.printStackTrace();
}
try{ //deserialization
FileInputStream fis = new FileInputStream("myclass.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
tj2 = (TestJava) ois.readObject();
ois.close();
fis.close();
System.out.println("object DEEEEserielized 1..."+tj2.j);
System.out.println("object DEEEEserielized 2..."+tj2.k);
System.out.println("object DEEEEserielized 3..."+k);
// in deserialization 'k' value is shown as incremented.
// That means Static varialbe 'K' is not serialized.
// if 'K' value is serialized then, it has to show old value before incrementd the 'K' value.
} catch(FileNotFoundException fnfe){
fnfe.printStackTrace();
} catch(IOException ioex){
ioex.printStackTrace();
} catch(ClassNotFoundException CNFE){
CNFE.printStackTrace();
}
}
}
This will output the following:
object serielized 1...5
object serielized 2...10
object serielized 3...10
object DEEEEserielized 1...5
object DEEEEserielized 2...11
object DEEEEserielized 3...11
So we create an object of class TestJava with one static integer field and one non-static field. We serialize the object, then - after serialization - increment the static integer.
When we later deserialize the object, we see that it has the incremented value, implying that it was not serialized.
Below example explains about static ,instance,transient and super class varialbes serialization and their outputs.
Serializing class:
public class SerializeEx extends SuperSerializeEx implements Serializable {
private static final long serialVersionUID = 1L;
public static int staticNumber = 1234;
public int instanceNumber = 1234;
public SerializeEx() {
staticNumber = 0;
instanceNumber = 0;
System.out.println("---sub class constructor---");
}
public SerializeEx(int staticNumber, int instanceNumber, int superNumber) {
super(superNumber);
this.staticNumber = staticNumber;
this.instanceNumber = instanceNumber;
}
}
Super Class:
public class SuperSerializeEx {
public int superNumber;
public SuperSerializeEx() {
System.out.println("---super class constructor---");
this.superNumber = 1000;
}
public SuperSerializeEx(int superNumber) {
this.superNumber = superNumber;
}
}
Serialization & Deserialization:
public class MainSerialization {
public static void main(String[] args) {
String fileName = "testing.txt";
serialize(fileName);
deSerialize(fileName);
}
public static void serialize(String fileName) {
System.err.println("Serialize.....");
SerializeEx serializeMe = new SerializeEx(10, 10, 10);
FileOutputStream fos = null;
ObjectOutputStream out = null;
try {
fos = new FileOutputStream(fileName);
out = new ObjectOutputStream(fos);
out.writeObject(serializeMe);
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static void deSerialize(String fileName) {
System.err.println("DeSerialize.....");
SerializeEx time = null;
FileInputStream fis = null;
ObjectInputStream in = null;
try {
fis = new FileInputStream(fileName);
in = new ObjectInputStream(fis);
time = (SerializeEx) in.readObject();
in.close();
} catch (IOException ex) {
ex.printStackTrace();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
System.err.println("Instance Numer = " + time.instanceNumber + " \tStatic Number= " + time.staticNumber + " \t Super Number= " + time.superNumber);
SerializeEx serializeMe = new SerializeEx(1001, 1001, 1001); //Modifying the static and instnce variables
System.err.println("Instance Numer = " + time.instanceNumber + " \tStatic Number= " + time.staticNumber + " \t Super Number= " + time.superNumber);
}
}
Output:
---super class constructor---
Serialize.....
DeSerialize.....
Instance Numer = 10 Static Number= 10 Super Number= 1000
Instance Numer = 10 Static Number= 1001 Super Number= 1000
No, if a class have static variable then at the time of serialization that variable will be skipped . because static variable is unique for all object and serialization is used for only save the object properties ( state of object ).
static variable is a property of class
Yes, static variable will be serialized if it is initialized at the time of declaration.
For example,
case 1 : without initialization at the time of declaration
class Person implements Serializable{
public String firstName;
static String lastName;
}
public class Employee {
public static void main(String[] args) {
Person p = new Person();
p.firstName="abc";
p.lastName="xyz";
//to do serialization
}
}
output :
//after deserialization
firstName= abc
lastName= null
case 2 : with initialization at the time of declaration
class Person implements Serializable{
public String firstName=="abc";
static String lastName="pqr";
}
public class Employee {
public static void main(String[] args) {
Person p = new Person();
p.firstName="abc";
p.lastName="xyz";
//to do serialization
}
}
output :
//after deserialization
firstName= abc
lastName= pqr
Any static variable which has been initialised at the time of declaration will be serialized.