出于很多原因我们想使用自定义的序列化方法取代Java默认的机制。一个最常见的原因是提高性能,而另一个原因是有时候我们无法使用默认的序列化方法。在这篇文章中,我们具体来讨论怎样通过定制的序列化方法,对一个较大的、带有不可序列化属性的对象进行序列化。
下面这段代码定义了一个简单的类。它可以把一个给定的对象序列化到一个指定的文件,或者从相同的文件中把对象反序列化出来。在这片文章中,我将使用这个类进行演示。
SerializationDemonstrator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
package dustin.examples.serialization;
import static java.lang.System.out;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/** * Simple serialization/deserialization demonstrator.
*
* @author Dustin
*/ public class SerializationDemonstrator
{ /**
* Serialize the provided object to the file of the provided name.
* @param objectToSerialize Object that is to be serialized to file; it is
* best that this object have an individually overridden toString()
* implementation as that is used by this method for writing our status.
* @param fileName Name of file to which object is to be serialized.
* @throws IllegalArgumentException Thrown if either provided parameter is null.
*/ public static <T> void serialize( final T objectToSerialize, final String fileName)
{
if (fileName == null )
{
throw new IllegalArgumentException(
"Name of file to which to serialize object to cannot be null." );
}
if (objectToSerialize == null )
{
throw new IllegalArgumentException( "Object to be serialized cannot be null." );
}
try (FileOutputStream fos = new FileOutputStream(fileName);
ObjectOutputStream oos = new ObjectOutputStream(fos))
{
oos.writeObject(objectToSerialize);
out.println( "Serialization of Object " + objectToSerialize + " completed." );
}
catch (IOException ioException)
{
ioException.printStackTrace();
}
}
/**
* Provides an object deserialized from the file indicated by the provided
* file name.
*
* @param <T> Type of object to be deserialized.
* @param fileToDeserialize Name of file from which object is to be deserialized.
* @param classBeingDeserialized Class definition of object to be deserialized
* from the file of the provided name/path; it is recommended that this
* class define its own toString() implementation as that will be used in
* this method's status output.
* @return Object deserialized from provided filename as an instance of the
* provided class; may be null if something goes wrong with deserialization.
* @throws IllegalArgumentException Thrown if either provided parameter is null.
*/ public static <T> T deserialize( final String fileToDeserialize, final Class<T> classBeingDeserialized)
{
if (fileToDeserialize == null )
{
throw new IllegalArgumentException( "Cannot deserialize from a null filename." );
}
if (classBeingDeserialized == null )
{
throw new IllegalArgumentException( "Type of class to be deserialized cannot be null." );
}
T objectOut = null ;
try (FileInputStream fis = new FileInputStream(fileToDeserialize);
ObjectInputStream ois = new ObjectInputStream(fis))
{
objectOut = (T) ois.readObject();
out.println( "Deserialization of Object " + objectOut + " is completed." );
}
catch (IOException | ClassNotFoundException exception)
{
exception.printStackTrace();
}
return objectOut;
}
} |
下面这段代码给出了一个使用SerializationDemonstrator类序列化和反序列化标准的Java字符串的例子。字符串是支持序列化的。代码之后的截图显示了在Netbeans中运行该类的serialize和deserialize方法后的输出。
Running SerializationDemonstrator Methods on String
1
2
|
SerializationDemonstrator.serialize( "Inspired by Actual Events" , "string.dat" );
final String stringOut = SerializationDemonstrator.deserialize( "string.dat" , String. class );
|
下面这两段代码定义了Person和CityState两个类。CityState是Person的一个属性。可以看到尽管Person实现了Serializable接口,CityState却没有。
Person.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package dustin.examples.serialization;
import java.io.Serializable;
/** * Person class.
*
* @author Dustin
*/ public class Person implements Serializable
{ private String lastName;
private String firstName;
private CityState cityAndState;
public Person(
final String newLastName, final String newFirstName,
final CityState newCityAndState)
{
this .lastName = newLastName;
this .firstName = newFirstName;
this .cityAndState = newCityAndState;
}
public String getFirstName()
{
return this .firstName;
}
public String getLastName()
{
return this .lastName;
}
@Override public String toString()
{
return this .firstName + " " + this .lastName + " of " + this .cityAndState;
}
} |
CityState.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package dustin.examples.serialization;
/** * Simple class storing city and state names that is NOT Serializable.
*
* @author Dustin
*/ public class CityState
{ private final String cityName;
private final String stateName;
public CityState( final String newCityName, final String newStateName)
{
this .cityName = newCityName;
this .stateName = newStateName;
}
public String getCityName()
{
return this .cityName;
}
public String getStateName()
{
return this .stateName;
}
@Override public String toString()
{
return this .cityName + ", " + this .stateName;
}
} |
下面这段代码演示了使用SerializationDemonstrator序列化Person类。由于包含了一个不可序列化的属性CityState,在之后截图里,我们可以看到Netbean抛出了异常。
Running SerializationDemonstrator Methods on Serializable Person with Non-Serializable CityState
1
2
3
4
|
final Person personIn = new Person( "Flintstone" , "Fred" , new CityState( "Bedrock" , "Cobblestone" ));
SerializationDemonstrator.serialize(personIn, "person.dat" );
final Person personOut = SerializationDemonstrator.deserialize( "person.dat" , Person. class );
|
在这个例子里,由于CityState类是我们自己写的,我们可以使它支持序列化。但是如果这个类属于一个第三方的框架或者库,我们就很难去修改这个类。但是我们可以修改Person类,通过使用自定义的序列化和反序列化方法,使它和CityState类一起正常工作。下面这段代码定义了一个从Person类改过来的SerializablePerson类。
SerializablePerson.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
package dustin.examples.serialization;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
/** * Person class.
*
* @author Dustin
*/ public class SerializablePerson implements Serializable
{ private String lastName;
private String firstName;
private CityState cityAndState;
public SerializablePerson(
final String newLastName, final String newFirstName,
final CityState newCityAndState)
{
this .lastName = newLastName;
this .firstName = newFirstName;
this .cityAndState = newCityAndState;
}
public String getFirstName()
{
return this .firstName;
}
public String getLastName()
{
return this .lastName;
}
@Override public String toString()
{
return this .firstName + " " + this .lastName + " of " + this .cityAndState;
}
/**
* Serialize this instance.
*
* @param out Target to which this instance is written.
* @throws IOException Thrown if exception occurs during serialization.
*/ private void writeObject( final ObjectOutputStream out) throws IOException
{
out.writeUTF( this .lastName);
out.writeUTF( this .firstName);
out.writeUTF( this .cityAndState.getCityName());
out.writeUTF( this .cityAndState.getStateName());
}
/**
* Deserialize this instance from input stream.
*
* @param in Input Stream from which this instance is to be deserialized.
* @throws IOException Thrown if error occurs in deserialization.
* @throws ClassNotFoundException Thrown if expected class is not found.
*/ private void readObject( final ObjectInputStream in) throws IOException, ClassNotFoundException
{
this .lastName = in.readUTF();
this .firstName = in.readUTF();
this .cityAndState = new CityState(in.readUTF(), in.readUTF());
}
private void readObjectNoData() throws ObjectStreamException
{
throw new InvalidObjectException( "Stream data required" );
}
} |
在上面这段代码中,SerializablePerson有自定义的writeobject和readObject方法。它们以适当的方式处理CityState的序列化和反序列化。下面这段代码使用SerializationDemonstrator运行了这个类,我们可以看到这次的运行是成功的。
Running SerializationDemonstrator on SerializablePerson
1
2
3
4
|
final SerializablePerson personIn = new SerializablePerson( "Flintstone" , "Fred" , new CityState( "Bedrock" , "Cobblestone" ));
SerializationDemonstrator.serialize(personIn, "person1.dat" );
final SerializablePerson personOut = SerializationDemonstrator.deserialize( "person1.dat" , SerializablePerson. class );
|
上面描述的这个方法可以允许我们在一个可序列化的类中使用不可序列化的属性,而且不需要transient。现在看上去已经挺不错了,但是如果前面这个CityState要在多个需要序列化的类中使用,更好的方式是用一个支持序列化的Decorator去修饰CityState。然后在那些需要做序列化的类中使用这个Decorator。下面这段代码定义了SerializableCityState。它是CityState的一个支持序列化的Decorator版本。
SerializableCityState.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
package dustin.examples.serialization;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
/** * Simple class storing city and state names that IS Serializable. This class
* decorates the non-Serializable CityState class and adds Serializability.
*
* @author Dustin
*/ public class SerializableCityState implements Serializable
{ private CityState cityState;
public SerializableCityState( final String newCityName, final String newStateName)
{
this .cityState = new CityState(newCityName, newStateName);
}
public String getCityName()
{
return this .cityState.getCityName();
}
public String getStateName()
{
return this .cityState.getStateName();
}
@Override public String toString()
{
return this .cityState.toString();
}
/**
* Serialize this instance.
*
* @param out Target to which this instance is written.
* @throws IOException Thrown if exception occurs during serialization.
*/ private void writeObject( final ObjectOutputStream out) throws IOException
{
out.writeUTF( this .cityState.getCityName());
out.writeUTF( this .cityState.getStateName());
}
/**
* Deserialize this instance from input stream.
*
* @param in Input Stream from which this instance is to be deserialized.
* @throws IOException Thrown if error occurs in deserialization.
* @throws ClassNotFoundException Thrown if expected class is not found.
*/ private void readObject( final ObjectInputStream in) throws IOException, ClassNotFoundException
{
this .cityState = new CityState(in.readUTF(), in.readUTF());
}
private void readObjectNoData() throws ObjectStreamException
{
throw new InvalidObjectException( "Stream data required" );
}
} |
这个可序列化的Decorator可以在Person类中直接使用。由于所有的属性都支持序列化,Person类可以使用默认的序列化方法。下面这段代码定义了一个从Person类改过来的Person2类。
Person2.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
package dustin.examples.serialization;
import java.io.Serializable;
/** * Person class.
*
* @author Dustin
*/ public class Person2 implements Serializable
{ private final String lastName;
private final String firstName;
private final SerializableCityState cityAndState;
public Person2(
final String newLastName, final String newFirstName,
final SerializableCityState newCityAndState)
{
this .lastName = newLastName;
this .firstName = newFirstName;
this .cityAndState = newCityAndState;
}
public String getFirstName()
{
return this .firstName;
}
public String getLastName()
{
return this .lastName;
}
@Override public String toString()
{
return this .firstName + " " + this .lastName + " of " + this .cityAndState;
}
} |
下面这段代码运行了这个类。之后是NetBeans输出的截图。
Running SerializationDemonstrator Against Person2/SerializableCityState
1
2
3
4
|
final Person2 personIn = new Person2( "Flintstone" , "Fred" , new SerializableCityState( "Bedrock" , "Cobblestone" ));
SerializationDemonstrator.serialize(personIn, "person2.dat" );
final Person2 personOut = SerializationDemonstrator.deserialize( "person2.dat" , Person2. class );
|
通过使用定制的序列化方法,可以在不使用transient的情况下,对一个带有不可序列化属性的类进行序列化。当你要在一个需要序列化的类中使用不可序列化的类型,并且这些类型不能被修改时,这是一个有用的技术。
相关推荐
每个可序列化的类都有一个`serialVersionUID`,它是类的一个版本标识。当反序列化时,JVM会检查这个ID以确保序列化和反序列化的类版本一致。如果版本ID不匹配,会抛出`InvalidClassException`。通常,当你修改了类...
总之,Java序列化不仅仅是一种简单的对象持久化手段,它还涉及到了一系列高级技术和注意事项。对于开发者来说,深入理解序列化的工作原理及其背后的设计哲学,能够帮助他们在实际项目中更加高效、安全地使用这一技术...
每个可序列化的类型(如IntWritable, Text等)都实现了`write()`和`readFields()`方法,用于写入和读取数据到DataOutput和DataInput流。然而,Writable接口有一些局限性: - 需要手动编写序列化和反序列化的代码。 ...
Kryo的优点在于它的效率,但缺点是不完全兼容Java序列化,可能导致与其他使用Java序列化的系统不兼容。 **Fastjson** Fastjson是阿里巴巴开源的一个高性能的JSON库,不仅支持JSON与Java对象之间的转换,还提供了...
在Java编程中,对象序列化是将一个对象的状态转换为可存储或可传输的数据格式的过程,而反序列化则是将这些数据恢复为原来的对象状态。这个过程在数据持久化、网络传输或者跨进程通信等场景中十分常见。Java提供了一...
Java 序列化是将Java对象...在设计可序列化的类时,应考虑对象的生命周期、安全性需求和版本控制等因素。通过合理使用`transient`关键字,控制`serialVersionUID`,以及对敏感数据的处理,可以有效地管理序列化过程。
但是,Hessian序列化的可靠性和可扩展性不如Java序列化。Hessian序列化需要对不同的类型进行不同的处理,遇到特殊对象还要做特殊的处理,这增加了实现的复杂度。同时,Hessian序列化也存在一定的不一致性,例如遇到...
Java反序列化漏洞是一种安全问题,主要出现在Java应用程序中,当程序不安全地处理来自不可信源的反序列化数据时,攻击者可以构造恶意序列化对象,导致非预期的对象创建或代码执行。本文将深入探讨Java反序列化的基本...
- **Java序列化**:Java自带的标准序列化机制,简单易用,但相对较慢且序列化后的数据较大。 4. **Memcached与Tomcat整合**:文件名中提到了Apache Tomcat,这是一款流行的Java Servlet容器。在Tomcat中配置...
序列化是将内存中的对象转化为可存储或可传输的数据格式的过程,而反序列化则是相反的操作,将这些数据恢复为原来的对象。在Hadoop中,序列化主要用于持久化数据和在网络中高效地传输数据。Java自带的序列化框架虽然...
如果父类不可序列化,但子类需要序列化,那么需要在父类中添加`transient`修饰符,或者显式声明`serialVersionUID`。 7. **安全性考虑**:序列化可能导致安全问题,因为恶意用户可以构造字节流来创建对象。因此,不...
1. **Java序列化**:Java自带的序列化机制简单易用,适用于Java对象的本地持久化或跨JVM通信。但其缺点在于序列化后的数据量大,且不适用于网络传输,因为其包含了大量元数据,效率较低。 2. **protobuf(Protocol ...
Kryo序列化是一种高效、快速的Java对象序列化框架,被广泛用于数据持久化、网络传输和内存缓存等场景。Kryo库以其优秀的性能在众多序列化库中脱颖而出,尤其是在处理大量对象时,它的速度比Java内置的序列化机制快得...
B.2 运用反射机制来持久化Java对象 附录C 用XDoclet工具生成映射文件 C.1 创建带有@hibernate标记的Java源文件 C.2 建立项目的目录结构 C.3 运行XDoclet工具 附录D 发布和运行netstore应用 D.1 运行...
Java序列化和反序列化是Java开发中常用的技术,它允许我们将Java对象转换成字节流,以便于存储、在网络中传输或在不同时间点重建对象。这一过程对于实现某些关键功能至关重要,如持久化数据、分布式计算和跨进程通信...
例如,代码片段展示了如何构建一个带有`__wakeup()`魔术方法的类,该方法在对象被反序列化时执行。如果`$this->inject`字段包含恶意命令,`eval()`函数就会执行这些命令。 为了检测和利用这种漏洞,可以使用工具如...
Jsonex JSONCoder是一个轻量级的通用对象序列化/反序列化库,类似于Jackson,GSON或FastJson。 多年来,该库已在各种eBay项目中广泛使用。 它不能替代其他流行的库。 但是,它解决了一些其他替代产品无法提供或无法...
(3) **实例化Java对象** 创建Java对象并填充数据。 (4) **XML序列化** 使用`JAXBContext`实例化一个marshaller对象,然后调用其`marshal()`方法将Java对象转换为XML字符串或文件。 (5) **XML反序列化** 同样,...
在Java编程中,有时我们需要对一个对象的属性进行深度赋值,即将源对象的所有属性值复制到目标对象中,而不是简单地让两个对象共享同一内存引用。标题中的“通过构造及反射实现类属性赋值”涉及到的技术是Java的构造...
在Java编程领域,创建一个基于Java内置API的聊天室是一个典型的网络编程实例,它涉及到多线程、套接字通信以及数据序列化等核心技术。在这个项目中,我们不依赖任何Web服务器,如Tomcat,而是直接利用Java的核心功能...