Customized serialization in Java - java

I have the following class
class UserAccount implements Serializable
{
public String username;
public String password;
public UserAccount()
{
username = "defaultUsername";
password = "defaultPassword";
}
public UserAccount(String u, String p)
{
username = u;
password = p;
}
private void readObject(ObjectInputStream o)
throws IOException, ClassNotFoundException
{
//username = (String)o.readObject();
o.defaultReadObject();
}
private void writeobject(ObjectOutputStream o)
throws IOException, ClassNotFoundException
{
//o.defaultWriteObject();
o.writeObject(username);
}
public String toString()
{
return username + ", " + password;
}
}
And I wrote the following snippet to serialize and de-serialize an instance of it.
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File("out.dat")));
out.writeObject(new UserAccount("test", "test2"));
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("out.dat")));
UserAccount u = (UserAccount)in.readObject();
in.close();
System.out.println(u);
I am customizing the serialization using the writeObject() hook, such that, I am only persisting the username. But when I read back the object, I do the default de-serialization.
I am expecting the output to be test, null while the out put is test, test2
Basically I am expecting the member password to be null since I did not persist it. Can anyone help me understand how password is initialized to test2.
I also verified that the call to the constructor was not made[I knew it wouldn't be made, but I checked nevertheless] during deserialization.
Thanks in advance.

Use the transient keyword to variables to make them not serialized. This might be another solution to skaffman's answer.
Reference: Why does Java have transient fields?

writeObject() doesn't replace the default serialization mechanism, it adds to it (allowing you to add additional data to the stream).
If you want a completely custom mechanism, consider implementing java.io.Externalizable instead of java.io.Serializable.
From the javadoc:
Only the identity of the class of an Externalizable instance is written in the serialization stream and it is the responsibility of the class to save and restore the contents of its instances. The writeExternal and readExternal methods of the Externalizable interface are implemented by a class to give the class complete control over the format and contents of the stream for an object and its supertypes. These methods must explicitly coordinate with the supertype to save its state. These methods supersede customized implementations of writeObject and readObject methods.

Related

Confused about Serializable vs Externalizable [duplicate]

What is the difference between Serializable and Externalizable in Java?
To add to the other answers, by implementating java.io.Serializable, you get "automatic" serialization capability for objects of your class. No need to implement any other logic, it'll just work. The Java runtime will use reflection to figure out how to marshal and unmarshal your objects.
In earlier version of Java, reflection was very slow, and so serializaing large object graphs (e.g. in client-server RMI applications) was a bit of a performance problem. To handle this situation, the java.io.Externalizable interface was provided, which is like java.io.Serializable but with custom-written mechanisms to perform the marshalling and unmarshalling functions (you need to implement readExternal and writeExternal methods on your class). This gives you the means to get around the reflection performance bottleneck.
In recent versions of Java (1.3 onwards, certainly) the performance of reflection is vastly better than it used to be, and so this is much less of a problem. I suspect you'd be hard-pressed to get a meaningful benefit from Externalizable with a modern JVM.
Also, the built-in Java serialization mechanism isn't the only one, you can get third-party replacements, such as JBoss Serialization, which is considerably quicker, and is a drop-in replacement for the default.
A big downside of Externalizable is that you have to maintain this logic yourself - if you add, remove or change a field in your class, you have to change your writeExternal/readExternal methods to account for it.
In summary, Externalizable is a relic of the Java 1.1 days. There's really no need for it any more.
Serialization provides default functionality to store and later recreate the object. It uses verbose format to define the whole graph of objects to be stored e.g. suppose you have a linkedList and you code like below, then the default serialization will discover all the objects which are linked and will serialize. In default serialization the object is constructed entirely from its stored bits, with no constructor calls.
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("/Users/Desktop/files/temp.txt"));
oos.writeObject(linkedListHead); //writing head of linked list
oos.close();
But if you want restricted serialization or don't want some portion of your object to be serialized then use Externalizable. The Externalizable interface extends the Serializable interface and adds two methods, writeExternal() and readExternal(). These are automatically called while serialization or deserialization. While working with Externalizable we should remember that the default constructer should be public else the code will throw exception. Please follow the below code:
public class MyExternalizable implements Externalizable
{
private String userName;
private String passWord;
private Integer roll;
public MyExternalizable()
{
}
public MyExternalizable(String userName, String passWord, Integer roll)
{
this.userName = userName;
this.passWord = passWord;
this.roll = roll;
}
#Override
public void writeExternal(ObjectOutput oo) throws IOException
{
oo.writeObject(userName);
oo.writeObject(roll);
}
#Override
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException
{
userName = (String)oi.readObject();
roll = (Integer)oi.readObject();
}
public String toString()
{
StringBuilder b = new StringBuilder();
b.append("userName: ");
b.append(userName);
b.append(" passWord: ");
b.append(passWord);
b.append(" roll: ");
b.append(roll);
return b.toString();
}
public static void main(String[] args)
{
try
{
MyExternalizable m = new MyExternalizable("nikki", "student001", 20);
System.out.println(m.toString());
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/Desktop/files/temp1.txt"));
oos.writeObject(m);
oos.close();
System.out.println("***********************************************************************");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/Desktop/files/temp1.txt"));
MyExternalizable mm = (MyExternalizable)ois.readObject();
mm.toString();
System.out.println(mm.toString());
}
catch (ClassNotFoundException ex)
{
Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
}
catch(IOException ex)
{
Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Here if you comment the default constructer then the code will throw below exception:
java.io.InvalidClassException: javaserialization.MyExternalizable;
javaserialization.MyExternalizable; no valid constructor.
We can observe that as password is sensitive information, so i am not serializing it in writeExternal(ObjectOutput oo) method and not setting the value of same in readExternal(ObjectInput oi). That's the flexibility that is provided by Externalizable.
The output of the above code is as per below:
userName: nikki passWord: student001 roll: 20
***********************************************************************
userName: nikki passWord: null roll: 20
We can observe as we are not setting the value of passWord so it's null.
The same can also be achieved by declaring the password field as transient.
private transient String passWord;
Hope it helps. I apologize if i made any mistakes. Thanks.
Key differences between Serializable and Externalizable
Marker interface: Serializable is marker interface without any methods. Externalizable interface contains two methods: writeExternal() and readExternal().
Serialization process: Default Serialization process will be kicked-in for classes implementing Serializable interface. Programmer defined Serialization process will be kicked-in for classes implementing Externalizable interface.
Maintenance: Incompatible changes may break serialisation.
Backward Compatibility and Control: If you have to support multiple versions, you can have full control with Externalizable interface. You can support different versions of your object. If you implement Externalizable, it's your responsibility to serialize super class
public No-arg constructor: Serializable uses reflection to construct object and does not require no arg constructor. But Externalizable demands public no-arg constructor.
Refer to blog by Hitesh Garg for more details.
Serialization uses certain default behaviors to store and later recreate the object. You may specify in what order or how to handle references and complex data structures, but eventually it comes down to using the default behavior for each primitive data field.
Externalization is used in the rare cases that you really want to store and rebuild your object in a completely different way and without using the default serialization mechanisms for data fields. For example, imagine that you had your own unique encoding and compression scheme.
Object Serialization uses the Serializable and Externalizable interfaces.
A Java object is only serializable. if a class or any of its superclasses implements either the java.io.Serializable interface or its subinterface, java.io.Externalizable. Most of the java class are serializable.
NotSerializableException: packageName.ClassName « To participate a Class Object in serialization process, The class must implement either Serializable or Externalizable interface.
Serializable Interface
Object Serialization produces a stream with information about the Java classes for the objects which are being saved. For serializable objects, sufficient information is kept to restore those objects even if a different (but compatible) version of the implementation of the class is present. The Serializable interface is defined to identify classes which implement the serializable protocol:
package java.io;
public interface Serializable {};
The serialization interface has no methods or fields and serves only to identify the semantics of being serializable. For serializing/deserializing a class, either we can use default writeObject and readObject methods (or) we can overriding writeObject and readObject methods from a class.
JVM will have complete control in serializing the object. use transient keyword to prevent the data member from being serialized.
Here serializable objects is reconstructed directly from the stream without executing
InvalidClassException « In deserialization process, if local class serialVersionUID value is different from the corresponding sender's class. then result's in conflict as
java.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
The values of the non-transient and non-static fields of the class get serialized.
Externalizable Interface
For Externalizable objects, only the identity of the class of the object is saved by the container; the class must save and restore the contents. The Externalizable interface is defined as follows:
package java.io;
public interface Externalizable extends Serializable
{
public void writeExternal(ObjectOutput out)
throws IOException;
public void readExternal(ObjectInput in)
throws IOException, java.lang.ClassNotFoundException;
}
The Externalizable interface has two methods, an externalizable object must implement a writeExternal and readExternal methods to save/restore the state of an object.
Programmer has to take care of which objects to be serialized. As a programmer take care of Serialization So, here transient keyword will not restrict any object in Serialization process.
When an Externalizable object is reconstructed, an instance is created using the public no-arg constructor, then the readExternal method called. Serializable objects are restored by reading them from an ObjectInputStream.
OptionalDataException « The fields MUST BE IN THE SAME ORDER AND TYPE as we wrote them out. If there is any mismatch of type from the stream it throws OptionalDataException.
#Override public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt( id );
out.writeUTF( role );
out.writeObject(address);
}
#Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.address = (Address) in.readObject();
this.role = in.readUTF();
}
The instance fields of the class which written (exposed) to ObjectOutput get serialized.
Example « implements Serializable
class Role {
String role;
}
class User extends Role implements Serializable {
private static final long serialVersionUID = 5081877L;
Integer id;
Address address;
public User() {
System.out.println("Default Constructor get executed.");
}
public User( String role ) {
this.role = role;
System.out.println("Parametarised Constructor.");
}
}
class Address implements Serializable {
private static final long serialVersionUID = 5081877L;
String country;
}
Example « implements Externalizable
class User extends Role implements Externalizable {
Integer id;
Address address;
// mandatory public no-arg constructor
public User() {
System.out.println("Default Constructor get executed.");
}
public User( String role ) {
this.role = role;
System.out.println("Parametarised Constructor.");
}
#Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt( id );
out.writeUTF( role );
out.writeObject(address);
}
#Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.address = (Address) in.readObject();
this.role = in.readUTF();
}
}
Example
public class CustomClass_Serialization {
static String serFilename = "D:/serializable_CustomClass.ser";
public static void main(String[] args) throws IOException {
Address add = new Address();
add.country = "IND";
User obj = new User("SE");
obj.id = 7;
obj.address = add;
// Serialization
objects_serialize(obj, serFilename);
objects_deserialize(obj, serFilename);
// Externalization
objects_WriteRead_External(obj, serFilename);
}
public static void objects_serialize( User obj, String serFilename ) throws IOException{
FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
ObjectOutputStream objectOut = new ObjectOutputStream( fos );
// java.io.NotSerializableException: com.github.objects.Address
objectOut.writeObject( obj );
objectOut.flush();
objectOut.close();
fos.close();
System.out.println("Data Stored in to a file");
}
public static void objects_deserialize( User obj, String serFilename ) throws IOException{
try {
FileInputStream fis = new FileInputStream( new File( serFilename ) );
ObjectInputStream ois = new ObjectInputStream( fis );
Object readObject;
readObject = ois.readObject();
String calssName = readObject.getClass().getName();
System.out.println("Restoring Class Name : "+ calssName); // InvalidClassException
User user = (User) readObject;
System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);
Address add = (Address) user.address;
System.out.println("Inner Obj : "+ add.country );
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void objects_WriteRead_External( User obj, String serFilename ) throws IOException {
FileOutputStream fos = new FileOutputStream(new File( serFilename ));
ObjectOutputStream objectOut = new ObjectOutputStream( fos );
obj.writeExternal( objectOut );
objectOut.flush();
fos.close();
System.out.println("Data Stored in to a file");
try {
// create a new instance and read the assign the contents from stream.
User user = new User();
FileInputStream fis = new FileInputStream(new File( serFilename ));
ObjectInputStream ois = new ObjectInputStream( fis );
user.readExternal(ois);
System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);
Address add = (Address) user.address;
System.out.println("Inner Obj : "+ add.country );
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
#see
What is Object Serialization
Object Serialization: Frequently Asked Questions
The Externalizable interface was not actually provided to optimize the serialization process performance! but to provide means of implementing your own custom processing and offer complete control over the format and contents of the stream for an object and its super types!
Examples of this is the implementation of AMF (ActionScript Message Format) remoting to transfer native action script objects over the network.
https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html
Default serialization is somewhat verbose, and assumes the widest possible usage scenario of the serialized object, and accordingly the default format (Serializable) annotates the resultant stream with information about the class of the serialized object.
Externalization give the producer of the object stream complete control over the precise class meta-data (if any) beyond the minimal required identification of the class (e.g. its name). This is clearly desirable in certain situations, such as closed environments, where producer of the object stream and its consumer (which reifies the object from the stream) are matched, and additional metadata about the class serves no purpose and degrades performance.
Additionally (as Uri point out) externalization also provides for complete control over the encoding of the data in the stream corresponding to Java types. For (a contrived) example, you may wish to record boolean true as 'Y' and false as 'N'. Externalization allows you to do that.
When considering options for improving performance, don't forget custom serialization. You can let Java do what it does well, or at least good enough, for free, and provide custom support for what it does badly. This is usually a lot less code than full Externalizable support.
There are so many difference exist between Serializable and Externalizable but when we compare difference between custom Serializable(overrided writeObject() & readObject()) and Externalizable then we find that custom implementation is tightly bind with ObjectOutputStream class where as in Externalizable case , we ourself provide an implementation of ObjectOutput which may be ObjectOutputStream class or it could be some other like org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream
In case of Externalizable interface
#Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(key);
out.writeUTF(value);
out.writeObject(emp);
}
#Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.key = in.readUTF();
this.value = in.readUTF();
this.emp = (Employee) in.readObject();
}
**In case of Serializable interface**
/*
We can comment below two method and use default serialization process as well
Sequence of class attributes in read and write methods MUST BE same.
// below will not work it will not work .
// Exception = java.io.StreamCorruptedException: invalid type code: 00\
private void writeObject(java.io.ObjectOutput stream)
*/
private void writeObject(java.io.ObjectOutputStream Outstream)
throws IOException {
System.out.println("from writeObject()");
/* We can define custom validation or business rules inside read/write methods.
This way our validation methods will be automatically
called by JVM, immediately after default serialization
and deserialization process
happens.
checkTestInfo();
*/
stream.writeUTF(name);
stream.writeInt(age);
stream.writeObject(salary);
stream.writeObject(address);
}
private void readObject(java.io.ObjectInputStream Instream)
throws IOException, ClassNotFoundException {
System.out.println("from readObject()");
name = (String) stream.readUTF();
age = stream.readInt();
salary = (BigDecimal) stream.readObject();
address = (Address) stream.readObject();
// validateTestInfo();
}
I have added sample code to explain better. please check in/out object case of Externalizable. These are not bound to any implementation directly.
Where as Outstream/Instream are tightly bind to classes. We can extends ObjectOutputStream/ObjectInputStream but it will a bit difficult to use.
Basically, Serializable is a marker interface that implies that a class is safe for serialization and the JVM determines how it is serialized. Externalizable contains 2 methods, readExternal and writeExternal. Externalizable allows the implementer to decide how an object is serialized, where as Serializable serializes objects the default way.
Some differences:
For Serialization there is no need of default constructor of that class because Object because JVM construct the same with help of Reflection API. In case of Externalization contructor with no arg is required, because the control is in hand of programmar and later on assign the deserialized data to object via setters.
In serialization if user want to skip certain properties to be serialized then has to mark that properties as transient, vice versa is not required for Externalization.
When backward compatiblity support is expected for any class then it is recommended to go with Externalizable. Serialization supports defaultObject persisting and if object structure is broken then it will cause issue while deserializing.

Is selective Serialization possible with the Serializable Interface?

Lets us say my class MyClass has 10 variables. By marking the class with Serializable we serialize all the 10 variables.
My question is is there any way to serialize only some of these variables, let us say 5 only?
I know it can be done by marking the variables as transient. But I want to know if there is any other way to do that than using transient keyword.
If your class implements the Externalizable interface, then you will have better control of how the object will be serialized.
Note that, unlike Serializable, the Externalizable interface is not a marker one and you will need to implement the readExternal() and writeExternal() methods, where you can actually pick programmatically which class members to be serialized and how de-serialization will be done.
More info:
Difference between Serializable and Externalizable
Java supports Custom Serialization. Read the section Customize the Default Protocol.
There is, however, a strange yet crafty solution. By using a built-in
feature of the serialization mechanism, developers can enhance the
normal process by providing two methods inside their class files.
Those methods are:
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException;
An option when you want to customize serialization is to use serialization proxies: instead of your "real" object you create a substitute that is serialized instead. The serialization framework uses the writeReplace()/readResolve() methods, which allow you to do exactly this.
This is roughly what it looks like:
public class Foo implements Serializable {
private final String bar;
private final String baz;
private static class FooProxy implements Serializable {
private final String barBaz;
private FooProxy(Foo foo) {
this.barBaz = foo.bar + "|" + foo.baz; //don't do this for real
}
private Object readResolve() {
String [] arr = this.barBaz.split( "|" );
return new Foo(arr[0], arr[1]);
}
}
private Object writeReplace() {
return new FooProxy(this);
}
// this method is required to stop a maliciously constructed serialized form to be deserialized
private void readObject(ObjectInputStream ois) throws InvalidObjectException {
throw new InvalidObjectException( "Use a proxy." );
}
}
So every time Foo is to be serialized, it is replaced with a FooProxy object that has completely different fields, and every time FooProxy is deserialized, it's replaced with a corresponding Foo.
The advantage of this technique is that you can separate the serialized form from the internal representation completely, allowing you to change the internal representation arbitrarily, so long as it can be rebuilt from the serialized form.

Custom serialization with Ehcache

Is it possible to tell Ehcache to use a custom serialization when moving an object from the memory to the disc or off-heap cache and the other way? I want to skip some fields from serialization which are not declared transient (third-party-library) but which I do not need to store since I can easily recalculate them. I want to do this to save quite some memory. At best, I want to use a library like Kryo.
I found a solution by using wrappers and by adding another abstraction layer, a SerializationWrapper. The wrapper presented here uses Kryo:
final class SerializationWrapper implements Serializable {
private final static Kryo KRYO = new Kryo();
private Object object;
public SerializationWrapper(Object object) {
this.object = object;
}
private void readObject(ObjectInputStream objectInputStream)
throws IOException, ClassNotFoundException {
Input input = new Input(objectInputStream);
object = KRYO.readClassAndObject(input);
input.close();
}
private void writeObject(ObjectOutputStream objectOutputStream)
throws IOException {
Output output = new Output(objectOutputStream);
KRYO.writeClassAndObject(output, object);
output.close();
}
public Object getWrappedObject() {
return object;
}
}
The serialization methods readObject and writeObjectwill be called by the Java contract and allow me to implement a custom serialization. Additionally, This solution does not create a size overhead since I only write the wrapped object to the output stream and skip the writing of the wrapper entirely. You can also read this documentation for more info what helped me a lot.

java serialization

I am trying to write message to a file using serialization.Message consists of two fields-
date and TibrvMsg(TibrvMsg is a propriotory message by Tibco and this class is not serializable as per their documentation).So my custom message is:
Message msg = new Message(TibrvMsg msg)
Problem is though i am declaring Message Serializable, i am not able to serialize it as TibrvMsg is not serializable.
So i get java.io.NotSerializableException: com.tibco.tibrv.TibrvMsg exception.
Another approach is to use a serialization proxy. Serialization proxy is a different class altogether than the object being serialized with the logical state of the object. The Object readResolve() method to write a proxy instead of this object and create an object by reading proxy.
Some semi-pseudo code:
class Message implements Serializable {
private Date dt;
private TibrvMsg msg;
private Object writeReplace() {
return new Proxy(this);
}
private static class Proxy implements Serializable {
private Date dt;
private Map msgData;
Proxy(Message msg) {
this.dt = msg.dt;
this.msgData = doTransform(msg.msg, "UTF-16");
}
private Object readResolve() {
Message msg = new Message();
msg.dtd = dt;
msg.msg = asTibrvMsg(msgData);
return msg;
}
}
}
Additionally override readObject(ObjectInputStream) to throw an InvalidObjectException. The serialization proxy pattern also has certain security advantages over normal serialization. It also has a few disadvantages
You need to find a way to represent your TibrvMsg as a serializable object (maybe something like this, which transforms it into a Map).
You can then override the following two methods to write this data to the output stream (or read it):
private void writeObject(java.io.ObjectOutputStream out)
throws IOException{
out.writeObject(date);
out.writeObject(doSomethingWithTibrv(tibrv);
}
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException{
date = (Date) in.readObject();
tibrv = readTibrv(in.readObject());
}
What do you mean by "this class is not serializable as per their documentation"?
Couldent you just extend their class and implement serializable? Its just a marker interface, so...

In java how do I serialize a class that is not marked Serializable?

There is a specific class in a third party library that I want to serialize. How would I go about doing this?
I'm assuming I will have to write a method that takes in an object of the class and uses reflection to get the private member values. Then for deserialization I would use reflection to put the values back.
Would this work? Is there an easier way?
You could just use a transfer object that implements Serializable, and has the same fields as the third party object. Let the transfer object implement a method that returns an object of the original third party class and you're done:
Pseudocode:
class ThirdParty{
int field1;
int field2;
}
class Transfer implements Serializable{
int field1;
int field2;
/* Constructor takes the third party object as
an argument for copying the field values.
For private fields without getters
use reflection to get the values */
Transfer (ThirdParty orig){
this.field1=orig.field1;
this.field2=orig.field2;
}
ThirdParty getAsThirdParty(){
ThirdParty copy=new ThirdParty();
copy.field1=this.field1;
copy.field2=this.field2;
return copy;
}
/* override these methods for custom serialization */
void writeObject(OutputStream sink);
void readObject(InputStream src);
}
You just have to make sure that the members are serialized correctly if you got any special member objects.
Alternatively if the third party class isn't final you could just extend it, have that implement Serializable and write your own writeObject and readObject methods.
Check here for some serialization infos:
Serialization Secrets - WayBack
Serialization API - Oracle
Serialization Secrets - Old
You need to wrap it into something that does the serialization.
Ideally, the third-party class supports some other form of serialization, for example XML serialization (which is based on bean properties). If not, you have to roll your own. Whether that involves reflection or just getters, setters and constructors depends on the class.
In any case, the wrapper would convert the object into a byte[] or a String or something else and write that into the serialization output. On deserialization it reconstructs the object from that data.
The two methods your wrapper has to implement are
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
A lot depends on the nature of the third party class. Is it final, does it have a no argument constructor, can you construct it given known values or is it constructed by another class, does it itself contain non-Serializable members?
Easiest way is to decompile the class, add an implements Serializable, and recompile it, but if it contains non-Serializable members, things get more complicated.
Another possible solution may be to define a set of private methods inside your Serializable class that uses the instances of the third party class.These special methods are part of a special callback contract the serialization system offers.These methods will be called during the serialization/deserialization process.
Their signatures must be like:
private void writeObject(ObjectOutputStream os) {
// your code for saving the third party variables
}
private void readObject(ObjectInputStream is) {
// your code to read the third party state, create a new ThirdParty instance,
// and assign it to your class.
}
This example clarifies this idea further:
public class MyClass implements Serializable
{
transient private ThirdParty thirdPartyInstance ;
private int myClassVariable ;
private void writeObject(ObjectOutputStream oos)
{
try
{
oos.defaultWriteObject();
oos.writeInt(thirdPartyInstance.getThirdPartyVariable());
oos.writeInt(thirdPartyInstance.getFourthPartyInstance().getFourthPartyVariable());
}
catch(Exception e)
{
e.printStackTrace();
}
}
private void readObject(ObjectInputStream ois)
{
try
{
ois.defaultReadObject(); //the call to defaultReadObject method must always be before any other code in the try block
//Reconstructing thirdPartyInstance
thirdPartyInstance =new ThirdParty(ois.readInt(),new FourthParty(ois.readInt()));
}
catch(Exception e)
{
e.printStackTrace();
}
}
MyClass(int myClassVariable, ThirdParty thirdPartyInstance)
{
this.myClassVariable=myClassVariable;
this.thirdPartyInstance=thirdPartyInstance;
}
ThirdParty getThirdPartyInstance()
{
return thirdPartyInstance;
}
int getMyClassVariable()
{
return myClassVariable;
}
public static void main(String args[])
{
FourthParty fourthPartyInstance=new FourthParty(45);
ThirdParty thirdPartyInstance=new ThirdParty(13,fourthPartyInstance);
MyClass myClassInstance=new MyClass(7,thirdPartyInstance);
System.out.println("Before: ThirdParty variable value is "+myClassInstance.getThirdPartyInstance().getThirdPartyVariable());
System.out.println("Before: FourthParty variable value is "+myClassInstance.getThirdPartyInstance().getFourthPartyInstance().getFourthPartyVariable());
System.out.println("Before: MyClass variable value is "+myClassInstance.getMyClassVariable());
try
{
FileOutputStream fios=new FileOutputStream("D://TestFileq.ser");
ObjectOutputStream oos=new ObjectOutputStream(fios);
oos.writeObject(myClassInstance);
oos.close();
FileInputStream fi = new FileInputStream("D://TestFileq.ser");
ObjectInputStream objectIn = new ObjectInputStream(fi);
MyClass myClassInst = (MyClass)objectIn.readObject();
System.out.println("After: ThirdParty variable value is "+myClassInst.getThirdPartyInstance().getThirdPartyVariable());
System.out.println("After: FourthParty variable value is "+myClassInst.getThirdPartyInstance().getFourthPartyInstance().getFourthPartyVariable());
System.out.println("After:MyClass variable value is "+myClassInst.getMyClassVariable());
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class ThirdParty
{
private int thirdPartyVariable;
private FourthParty fourthPartyInstance;
ThirdParty(int thirdPartyVariable,FourthParty fourthPartyInstance)
{
this.thirdPartyVariable=thirdPartyVariable;
this.fourthPartyInstance=fourthPartyInstance;
}
int getThirdPartyVariable()
{
return thirdPartyVariable;
}
FourthParty getFourthPartyInstance()
{
return fourthPartyInstance;
}
}
class FourthParty
{
private int fourthPartyVariable;
FourthParty(int fourthPartyVariable)
{
this.fourthPartyVariable=fourthPartyVariable;
}
int getFourthPartyVariable()
{
return fourthPartyVariable;
}
}
Note that the thirdPartyInstance in MyClass must be declared transient otherwise an exception of type 'java.io.NotSerializableException' occurs.
For more explanation see:
SCJP Sun Certified Programmer for Java 6 by 'Cathy Sierra',Page Number 497

Categories

Resources