我在java中声明了一个ISerializable接口。
我基本上有两种方法:serialize()
和deserialize(byte[] buffer)
。
public interface ISerializable{
byte[] serialize();
deserialize(byte[] buffer);
}
以下是实现此接口的类的示例:
public class MySerializableClass implements ISerializable{
byte[] serialize(){bla bla}
deserialize(byte[] buffer){bla bla};
}
理想情况下,我希望deserailize
的调用是隐含的。即,在调用构造函数MySerializableClass(byte[] buffer)
时,它将调用正确的deserialize
并传递缓冲区。那样:
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在构造函数中调用虚方法是有问题的,这可能最终导致未定义的行为。 所以目前,我正在做以下事情:
MySerializableClass myClass = new MySerializableClass();
myClass.deserialize(buffer);
或者使用为扩展我的界面的每个类定义的专用静态方法(基本上只做上面两行代码):
MySerializableClass myClass = MySerializableClass.CreateMySerializableClass(buffer);
我的问题是:有没有优雅的方法可以做到这一点,而无需为每个类实现ISerializable定义专用的静态方法?是否有任何设计模式可以解决这个问题?
注意:我的序列化是独一无二的,所以我需要自己编写,而且由于技术原因,我只能使用Java的非常基本的功能。 (没有注释,模板化元数据等)所以我需要一个非常基本的OOP解决方案。
答案 0 :(得分:2)
我发现你的解决方案足够优雅,你正在做的是Factory,这是解决问题的一种优雅方式。您可以将构造函数保持为私有,并始终通过工厂检索对象
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};
}
答案 1 :(得分:0)
另一个解决方案是删除no-argument constructor
,因此具体类必须必须使用参数构造函数进行初始化。
答案 2 :(得分:0)
在进行序列化时我不会传递字节数组 - 而是使用java.io.DataOutput和java.io.DataInput。然后,您可以声明接口ISerializable
,例如这样:
public interface ISerializable{
void serialize(DataOutput out) throws IOException;
void deserialize(DataInput in) throws IOException;
}
然后,您可以提供静态实用程序方法,这些方法能够在提供某些DataOutput / DataInput时序列化和反序列化ISerializable
的实例。然后,静态deserialize方法也可以调用一个可能的构造函数,该构造函数接受DataInput作为其唯一参数。
以下是此方法的完整示例代码,其中还包括用于测试序列化的主要方法:
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) {}
}
}
}
}
当使用静态方法时,您将拥有将类名与数据一起存储的内存开销。但是,如果这是一个问题,您仍然可以手动调用serialize
和deserialize
方法。
答案 3 :(得分:0)
在我看来,您不需要使用方法实现ISerializable接口:serialize和deserialize。在任何可序列化的类中。
我认为最好interface ISerializer
{
byte[] serialize(ISerializable serializable);
ISerializable deserialize(byte[] buffer);
}
并具有实现此ISerializer接口的Serializer类
ISerializable _序列化或反序列化的内容。
ISerializable将具有方法,序列化和反序列化需要什么。 如果序列化和反序列化不需要任何东西,它可以是Object而不是ISerializable。
如果您在使用deserialize方法后不想进行强制转换,则此反序列化方法可以是通用的:
反序列化&lt; T&gt;(byte []缓冲区);