This question already has answers here:
What is the difference between Serializable and Externalizable in Java?
(11 answers)
Closed 4 years ago.
I'm learning about Serializable and Externalizable interface and I see that, when an Externalizable object is reconstructed, an instance is created first using the public no-arg constructor, then the readExternal method is called. If the object does not support Externalizable, then Serializable objects are restored by reading them from an ObjectInputStream.
I don't understand why we use ObjectInputStream for Externalization if the object isn't readed from there? What exactly is readed from the ObjectInputStream? I think we read something from there if we use it.
Also I found this chart about Deserialization Externalizable or Serializable interface
What is the difference between Serializable and Externalizable at the deserialization process?
I don't understand why the Externalizable objects aren't restored by reading them from an ObjectInputStream in the same way like Serializable objects?
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("employee.ser"))
I know that the FileInputStream opens a file, creates a sequence of bytes based on the data in the file. The ObjectInputStream takes a sequence of bytes, recreats the object based on the sequence of bytes.
And here is a code.
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class Employee implements Externalizable {
private int id;
private String name;
private int age;
public void Employee() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age + "]";
}
public void writeExternal(ObjectOutput oo) throws IOException {
System.out.println("Inside writeExternal method");
oo.writeInt(id);
oo.writeObject(name);
}
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException {
System.out.println("Inside readExternal method");
id = oi.readInt();
name = (String) oi.readObject();
}
}
ExternalizableWrite
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ExternalizableWrite {
public static void main(String args[]) {
ExternalizableWrite ew = new ExternalizableWrite();
ew.writeEmployeeObject();
}
private void writeEmployeeObject() {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("employee.ser"))) {
Employee employee = new Employee();
employee.setId(101);
employee.setName("Peter");
employee.setAge(25);
System.out.println(employee);
oos.writeObject(employee); // write the specified object to the ObjectOutputStream
System.out.println("Successfully written employee object to the file.\n");
} catch (FileNotFoundException ex) {
System.out.printf("ERROR: %s", ex);
} catch (IOException ex) {
System.out.printf("ERROR: %s", ex);
}
}
}
ExternalizableRead
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ExternalizableRead {
public static void main(String args[]) {
ExternalizableRead er = new ExternalizableRead();
er.readEmployeeObject();
}
private void readEmployeeObject() {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("employee.ser"))) {
Employee employee = (Employee) ois.readObject();
System.out.println(employee);
System.out.println("Successfully read employee objecct to the file.\n");
} catch (FileNotFoundException ex) {
System.out.printf("ERROR: %s", ex);
} catch (IOException | ClassNotFoundException ex) {
System.out.printf("ERROR: %s", ex);
}
}
}
What is the difference between Serializable and Externalizable at the deserialization process?
According to the implementation of ObjectInputStream, Externalizable objects are handled differently than Serializable objects, as expected:
if (desc.isExternalizable()) {
readExternalData((Externalizable) obj, desc);
} else {
readSerialData(obj, desc);
}
As you might expect, the readExternalData method calls Externalizable#readExternal for the object that is being deserialized, while the readSerialData method simply deserializes the serialized fields.
I don't understand why we use ObjectInputStream for Externalization if the object isn't readed from there?
I'm not sure what you're asking, but ObjectInputStream does handle Externalizable objects, as seen above.
I don't understand why the Externalizable objects aren't restored by reading them from an ObjectInputStream in the same way like Serializable objects?
Because Externalizable objects force you to manually serialize and deserialize them, whereas Serializable attempts to serialize all non-static and non-transient fields.
I don't understand why we use ObjectInputStream for Externalization if the object isn't readed from there?
Externalizable also uses ObjectInputStream.
ObjectInputStream is the parameter that is passed to the readExternal method. One may use methods such as readInt,readFloat etc of the ObjectInputStream to read the values from the serialized object.
What is the difference between Serializable and Externalizable at the deserialization process?
A class (implementing Serializable interface) can customize the data written the serialized object by using the following methods:
private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
Suppose that there are classes A and B like this
class A implement Serializable {
writeObject(...){....}
readObject(...){....}
}
class B extends A implements Serializable{
writeObject(...){....}
readObject(...){....}
}
When an object of B is serialized/de-serialized then writeObject/readObject methods of parent/super class A is called before that of B, thus allowing for parent class' to decide which fields to serialize/de-serialize.
But, when it comes to Externalizable, this does not happen. Only sub-class's readExternal and writeExternal methods are called, overriding the parent's serialization/de-serialization behaviour.
Related
I'm learning about custom serialization and I don't understand how is it possible to overide the 2 methods writeObject() and readObject() because I know these 2 methods are final, and I know that final methods cannot be overriden.
writeObject() method from ObjectOutputStream:
public final void writeObject(Object obj) throws IOException
And writeObject() needs to be overriden like here:
private void writeObject(ObjectOutputStream output) throws IOException
I understand that the new writeObject() method is private, but it is called using reflection by Java serialization mechanism. But I don't understand how is it possible to override a final method.
Account Class:
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Account implements Serializable {
private static final long serialVersionUID = 154754873L;
String userName = "durga";
transient String psw = "anushka";
private void writeObject(ObjectOutputStream output) throws IOException {
output.defaultWriteObject();
output.writeObject(psw);
}
private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException {
input.defaultReadObject();
psw = (String) input.readObject();
}
}
SerializationDemo Class:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializationDemo {
public void serialize(Account a1, String fileName) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fileName))) {
oos.writeObject(a1);
} catch (FileNotFoundException ex) {
System.out.printf("ERROR: %s", ex);
} catch (IOException ex) {
System.out.printf("ERROR: %s", ex);
}
}
public Account deserialize(String fileName) {
Account a2 = null;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("account.ser"))) {
a2 = (Account) ois.readObject();
} catch (FileNotFoundException ex) {
System.out.printf("ERROR: %s", ex);
} catch (IOException | ClassNotFoundException ex) {
System.out.printf("ERROR: %s", ex);
}
return a2;
}
}
SerializationApp Class:
public class SerializationApp {
public static void main(String args[]) {
Account a1 = new Account();
System.out.println(a1.userName + " " + a1.psw);
SerializationDemo demo = new SerializationDemo();
demo.serialize(a1, "account.ser");
Account a2 = demo.deserialize("account.ser");
System.out.println(a2.userName + " " + a2.psw);
}
}
It is not possible to override a final method.
But you don't need to.
One way to customize serialization is to provide writeObject and readObject methods (like you did):
private void writeObject(ObjectOutputStream out) throws IOException;
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
Note that we don't talk about implementing or overriding methods here.
This should be actually enough - I wonder why you need to override writeObject in ObjectOutputStream.
If you want to customize serialization by subclassing the ObjectOutputStream, you still can "override" how objects are written. For this, subclass ObjectOutputStream, in the subclass call super() and override writeObjectOverride. If you call the protected ObjectOutputStream() constructor, the enableOverride flag is set to true and the final method writeObject (which you can't override) delegates to writeObjectOverride (which can be overridden).
You are confused.
ObjectOutputStream.writeObject() is final and you cannot override it.
The private void writeObject(ObjectOutput) method you can use for custom serialization goes into your own Serializable class, not into a class that extends ObjectOutputStream. The question of overriding does not arise.
I would like to save the data of an injected stateful bean at various intervals: change - save - change- save... I'm using core serialization and the problem is that all the byte arrays are the same. i believe the proxy is serialized because if I deserialize one of the arrays later I get the current state of the bean.
Example of serialization not capturing changes in the bean:
#Stateful
#RequestScoped
public class State implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
StatelessBean bean; // assume it's needed
private List<String> list = new ArrayList<>();
public void add() {
list.add("S");
}
}
And this is a JAX-RS class:
#Stateless
#Path("t1")
public class ChickensResource {
#Inject
State state;
#GET
#Path("/test")
public String test() {
state.add();
byte[] b0 = serialize(state);
System.out.println(b0.length + " " + Arrays.toString(b0));
state.add();
byte[] b1 = serialize(state);
System.out.println(b1.length + " " + Arrays.toString(b1)); // prints same as b0
System.out.println(b0.length + " " + Arrays.toString(b0)); // prints same thing
}
public static <T extends Serializable> byte[] serialize(T s) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos))
{
oos.writeObject(s);
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
What I want to do is only save the list in State as that's the relevant data. I also tried JSON serialization and it gave an IOException, but I'm trying core serialization.
Using JavaEE7 and Wildfly 10.1.
For various reasons, serializing a CDI bean directly is dangerous:
You may have a proxy, not the actual object; same holds true for the dependencies of that object
Serialization implies that the data will be deserialized at a time. But CDI beans are managed by CDI and CDI has no way to "attach" a deserialized object into its set of managed objects.
But the purpose of this question is to somehow save the state of a CDI bean in a way that it can be restored later. This can be accomplished by using another object that holds the state of the CDI bean. This other object is not managed by CDI, i.e. created with new, and is serializable. Each CDI bean that needs to persist its state has the pair of setState(state)/getState() methods - they could even be part of an interface. You probably want each object to propagate setState(state)/getState() to its collaborators too.
See the Memento design pattern. This is also implemented in the JSF state saving/restoring mechanism, if you are familiar with it.
Some example code (there are other valid ways to do it), starting with the state interface:
interface HasState<S extends Serializable> {
S getState();
void setState(S state);
}
Then the service itself, that has a collaborator, and the relevant state object:
class SomeServiceState implements Serializable {
private String someData;
private Long someId;
private List<String> list;
private CollaboratorState collaboratorState;
// accessors
}
#RequestScoped
public class SomeService implements HasState<SomeServiceState> {
// COLLABORATORS
#Inject
Collaborator collaborator; // assume it's needed
// INTERNAL STATE
private String someData;
private Long someId;
private List<String> list = new ArrayList<>();
public void add() {
list.add("S");
}
// ...
public SomeServiceState getState() {
SomeServiceState state = new SomeServiceState();
state.setSomeData(someData);
state.setSomeId(someId);
state.setList(new ArrayList<>(list)); // IT IS PROBABLY SAFER TO COPY STATE!
// SEE HOW STATE GETS EXTRACTED RECURSIVELY:
state.setCollaboratorState(collaborator.getState());
return state;
}
public void setState(SomeServiceState state) {
someData = state.getSomeData();
someId = state.getSomeId();
list = new ArrayList<>(state.getList());
// SEE HOW STATE GETS APPLIED RECURSIVELY:
collaborator.setState(state.getCollaboratorState());
}
}
The collaborator and its state follow the same pattern:
class CollaboratorState implements Serializable {
private String anyName;
// accessors
}
#RequestScoped
class Collaborator implements HasState<CollaboratorState> {
// you get the point...
}
And an example usage, following the code from the question:
#Stateless
#Path("t1")
public class ChickensResource {
#Inject
SomeService someService;
#GET
#Path("/test")
public String test() {
someService.add();
byte[] b0 = serialize(someService.getState());
// ...
}
public static <T extends Serializable> byte[] serialize(T s) {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos))
{
oos.writeObject(s);
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
EDIT: If the client of a service needs to know that a service has state, then the client and service might be more coupled than it would be desired. A way out is to modify HasState to deal with opaque objects:
interface HasState {
Object getState();
void setState(Object state);
}
The state of the client contains a list for the state of each collaborator:
class SomeServiceState implements Serializable {
private String someData;
private Long someId;
private List<String> list;
private List<Object> collaboratorsState;
// accessors
}
The client adds a collaborator to the state only if it extends HasState:
public Object getState() {
SomeServiceState state = new SomeServiceState();
state.setSomeData(someData);
state.setSomeId(someId);
state.setList(new ArrayList<>(list));
if( collaborator instanceof HasState ) {
state.getCollaboratorsState().add(collaborator.getState());
}
return state;
}
I ran into this issue when testing a Spring controller using MockMvc, Mockito and Jackson, so I made a simple class to test out how Jackson behaves. I'm using jackson-databind:2.3.1 and mockito-core:1.9.5.
Given this class:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.Serializable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class Person implements Serializable {
private String name;
private int age;
// Public getters and setters...
public static void main(String[] args) {
String name = "Bob";
int age = 21;
ObjectMapper objectMapper = new ObjectMapper();
// attempt serialization with real object
Person person = new Person();
person.setName(name);
person.setAge(age);
try {
System.out.println(objectMapper.writeValueAsString(person));
} catch (JsonProcessingException e) {
e.printStackTrace();
System.err.println("Failed to serialize real object");
}
// attempt serialization with mock object
Person mockPerson = mock(Person.class);
when(mockPerson.getName()).thenReturn(name);
when(mockPerson.getAge()).thenReturn(age);
try {
System.out.println(objectMapper.writeValueAsString(mockPerson));
} catch (JsonProcessingException e) {
e.printStackTrace();
System.err.println("Failed to serialize mock object.");
}
}
Jackson has no problem serializing the real object, however it will throw a JsonMappingException when it tries to serialize the mocked object. Debugging through the code, it's calling serializeFields(bean, jgen, provider) repeatedly, getting stuck on the internal Mockito properties.
So, my question is: Is there anyway to force Jackson to use the getter methods? I tried #JsonIgnoreProperties on the class, #JsonIgnore on the fields, and #JsonProperty on the methods (in different combinations, to no success). Or, do I have to write my own custom serializer?
Thanks!
Here is a solution that will work for you particular case:
First of all you need to create a PersonMixin since you cannot add the required annotations to the mock.
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public interface PersonMixin {
#JsonProperty
String getName();
#JsonProperty
Integer getAge();
}
Now, use the object mapper like the in following code and you will get the same result as when you serialize the real object:
Person mockPerson = mock(Person.class);
when(mockPerson.getName()).thenReturn(name);
when(mockPerson.getAge()).thenReturn(age);
objectMapper.addMixInAnnotations(Person.class, PersonMixin.class);
try {
System.out.println(objectMapper.writeValueAsString(mockPerson));
} catch (JsonProcessingException e) {
e.printStackTrace();
System.err.println("Failed to serialize mock object.");
}
Here's my ObjectMapper that sorted it out without the need for mixins.
The mapper ignores all members of that has "Mockito" in somewhere in their names.
This solution avoids having a mix-in for each serialized object, or annotating code that may not be accessible.
Running the following test succeeds with the output {"name":"Jonh"}.
package test;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import org.mockito.Mockito;
public class AppTest extends Mockito {
public void testApp() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
#Override
public boolean hasIgnoreMarker(final AnnotatedMember m) {
return super.hasIgnoreMarker(m) || m.getName().contains("Mockito");
}
});
final String name = "Jonh";
Person mockPerson = mock(Person.class);
when(mockPerson.getName()).thenReturn(name);
System.out.println(mapper.writeValueAsString(mockPerson));
}
public static class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
I declared an ISerializable interface in java.
I basically have 2 methods: serialize(), and deserialize(byte[] buffer).
public interface ISerializable{
byte[] serialize();
deserialize(byte[] buffer);
}
and here is an example of a class implementing this interface:
public class MySerializableClass implements ISerializable{
byte[] serialize(){bla bla}
deserialize(byte[] buffer){bla bla};
}
Ideally, I would like the call to deserailize to be implicit. i.e. when calling the constructor MySerializableClass(byte[] buffer), it would call the correct deserialize with the buffer passed. like that:
public abstract class AbstractSerializable {
public abstract byte[] serialize();
public abstract void deserialize(byte[] buffer);
public AbstractSerializable (){}
public AbstractSerializable (byte[] buffer){
deserialize();
}
}
public class MySerializableClass extends AbstractSerializable {
byte[] serialize(){bla bla}
deserialize(byte[] buffer){bla bla};
}
AFAIK it is problematic to call virtual methods within the constructor and this might end up with an undefined behavior.
so currently, I am doing the following:
MySerializableClass myClass = new MySerializableClass();
myClass.deserialize(buffer);
or by using a dedicated static method that is defined for each class that extends my interface (and basically just do the above 2 lines of code):
MySerializableClass myClass = MySerializableClass.CreateMySerializableClass(buffer);
My questions is: is there any elegant way to do that without the need to define a dedicated static method for each class implements ISerializable? Is there any design pattern that solves this issue?
Note: My serialization is unique so I need to write it on my own, and also for technical reasons I can only use very basic features of Java. ( no annotations,templaates metadata, etc.) so I need a very basic OOP solution.
I find your solution elegant enough, what you're doing is a Factory, which is an elegant way to solve your problem. You can keep your constructor private, and always retrieve the objects through the factories
public class MySerializableClass extends AbstractSerializable {
private MySerializableClass(){
}
public static MySerializableClass CreateMySerializableClass(final byte[] buffer){
MySerializableClass result = new MySerializableClass();
result.deserialize(buffer)
return result;
}
byte[] serialize(){bla bla}
deserialize(byte[] buffer){bla bla};
}
One another solution is you remove no-argument constructor so your concrete class must have to initialize with argument constructor .
I wouldn't pass byte arrays around when doing the serialization - instead I would use java.io.DataOutput and java.io.DataInput. You could then declare the interface ISerializable like e.g. so:
public interface ISerializable{
void serialize(DataOutput out) throws IOException;
void deserialize(DataInput in) throws IOException;
}
Then you could provide static utility methods, that are able to serialize and deserialize instances of ISerializable when provided with some DataOutput/DataInput. The static deserialize method could then also call a possible constructor, that accepts a DataInput as its only argument.
Here is a complete example code for this approach, that also includes a main method for testing the serialization:
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class SerializableTest {
public interface ISerializable{
void serialize(DataOutput out) throws IOException;
void deserialize(DataInput in) throws IOException;
}
/**
* Writes the given ISerializable to the given DataOutput.
*/
public static void writeSerializable(ISerializable s, DataOutput out) throws IOException{
writeClass(out, s.getClass());
s.serialize(out);
}
/**
* Reads an ISerializable from the given DataInput.
*/
public static ISerializable readSerializable(DataInput in, ClassLoader cl) throws IOException{
ISerializable element = null;
Class<?> c;
try {
c = readClass(in, cl);
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
try {
try {
// see if the class has a constructor that accepts a DataInput
Constructor<?> constructor= c.getDeclaredConstructor(DataInput.class);
constructor.setAccessible(true);
return (ISerializable)constructor.newInstance(in);
} catch (NoSuchMethodException e) {
//ignore
}
element = (ISerializable) newInstance(c);
element.deserialize(in);
} catch (Exception e) {
throw new IOException("Could not deserialize the class" + c.getName());
}
return element;
}
private static <T> T newInstance(Class<T> c) throws IOException {
T element = null;
Constructor<T> constructor;
try {
constructor = c.getDeclaredConstructor();
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
element = constructor.newInstance();
} catch (NoSuchMethodException | InstantiationException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException e) {
throw new IOException(e);
}
return element;
}
private static void writeClass(DataOutput out, Class<?> c) throws IOException {
out.writeUTF(c.getName());
}
private static Class<?> readClass(DataInput in, ClassLoader cl) throws IOException, ClassNotFoundException {
String name = in.readUTF();
return cl.loadClass(name);
}
// some test classes for testing serialization in the main method
public static class TestClass implements ISerializable{
private String data;
protected TestClass() {
// ISerializable no argument constructor
super();
}
public TestClass(String data) {
super();
this.data = data;
}
#Override
public void serialize(DataOutput out) throws IOException {
out.writeUTF(data);
}
#Override
public void deserialize(DataInput in) throws IOException {
this.data = in.readUTF();
}
}
public static class TestClass2 implements ISerializable{
private final String data;
protected TestClass2(DataInput in) throws IOException {
// ISerializable DataInput constructor
super();
this.data = in.readUTF();
}
public TestClass2(String data) {
super();
this.data = data;
}
#Override
public void serialize(DataOutput out) throws IOException {
out.writeUTF(data);
}
#Override
public void deserialize(DataInput in) throws IOException {
throw new UnsupportedOperationException();
}
}
// tests serialization and deserialization of two test classes
public static void main(String[] args) {
TestClass t1 = new TestClass("TestClass 1");
TestClass2 t2 = new TestClass2("TestClass 2");
File file = new File("testfile");
if (file.exists()) {
file.delete();
}
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
return;
}
DataOutputStream out = null;
try {
out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
writeSerializable(t1, out);
writeSerializable(t2, out);
} catch (IOException e) {
e.printStackTrace();
return;
}finally{
if (out != null) {
try {
out.close();
} catch (IOException e) {}
}
}
DataInputStream in = null;
try {
in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
ClassLoader cl = SerializableTest.class.getClassLoader();
TestClass loadedClass1 = (TestClass) readSerializable(in, cl);
TestClass2 loadedClass2 = (TestClass2) readSerializable(in, cl);
System.out.println("loadedClass1.data: " + loadedClass1.data);
System.out.println("loadedClass2.data: " + loadedClass2.data);
} catch (IOException e) {
e.printStackTrace();
return;
} finally{
if (in != null) {
try {
in.close();
} catch (IOException e) {}
}
}
}
}
Of course you will have the memory overhead of storing the class name together with the data, when using the static methods. But you can still call the serialize and deserialize methods manually, if this is a problem.
in my opinion you don't need to implement ISerializable interface with methods: serialize and deserialize. in any serializable class.
I think it would be better to have
interface ISerializer
{
byte[] serialize(ISerializable serializable);
ISerializable deserialize(byte[] buffer);
}
and have Serializer class which implements this ISerializer interface
ISerializable _ what to serialize or deserialize.
ISerializable will have methods, what serialization and deserialization need.
if serialization and deserialization don't need anything, it can be Object instead of ISerializable.
if you don't want casts after using deserialize method, this deserialize method can be generic:
T deserialize<T>( byte[] buffer );
When i run this demo it's call TestBean's writeObject method which is private
How is it possible ?
Here is the Code:
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) {
try {
TestBean testBean = test.new TestBean();
testBean.setSize(23);
testBean.setWidth(167);
FileOutputStream fos =
new FileOutputStream(new File("d:\\serial.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(testBean);
oos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
class TestBean implements Serializable {
private static final long serialVersionUID = 1L;
private int size;
private int width;
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
private void writeObject(ObjectOutputStream out) throws IOException {
System.out.println("TestBean writeObject");
out.defaultWriteObject();
}
private void readObject(ObjectInputStream input) throws IOException,
ClassNotFoundException {
System.out.println("TestBean readObject ===================> ");
input.defaultReadObject();
}
}
}
If your serializable object has any writeObject method, it will be called otherwise the defaultWriteObject method will be called.
The private method calling is possible using the reflection. If you see the source code of ObjectOutputStream Class in that method writeSerialData, the code below answers your question.
if (slotDesc.hasWriteObjectMethod()) {
// through reflection it will call the Serializable objects writeObject method
} else {
// the below is the same method called by defaultWriteObject method also.
writeSerialData(obj, desc);
}
The virtual machine will automatically check to see if either method
is declared during the corresponding method call. The virtual machine
can call private methods of your class whenever it wants but no other
objects can. Thus, the integrity of the class is maintained and the
serialization protocol can continue to work as normal. The
serialization protocol is always used the same way, by calling either
ObjectOutputStream.writeObject() or ObjectInputStream.readObject().
So, even though those specialized private methods are provided, the
object serialization works the same way as far as any calling object
is concerned.
You will get more about from this article:
Discover the secrets of the Java Serialization API
It uses reflection. private and public are not security measures. That is only a contract for class users.