java序列化 反序列化_Java序列化– Java序列化

java序列化 反序列化

Serialization in Java was introduced in JDK 1.1 and it is one of the important feature of Core Java.

Java的序列化是JDK 1.1中引入的,它是Core Java的重要功能之一。

Java序列化 (Serialization in Java)

Serialization in Java allows us to convert an Object to stream that we can send over the network or save it as file or store in DB for later usage. Deserialization is the process of converting Object stream to actual Java Object to be used in our program. Serialization in Java seems very easy to use at first but it comes with some trivial security and integrity issues that we will look in the later part of this article. We will look into following topics in this tutorial.

Java中的序列化允许我们将对象转换为流,可以通过网络发送该流,也可以将其保存为文件或存储在DB中以备后用。 反序列化是将对象流转换为要在我们的程序中使用的实际Java对象的过程。 一开始,使用Java进行序列化似乎非常容易,但是它附带了一些琐碎的安全性和完整性问题,我们将在本文的后面部分进行介绍。 我们将在本教程中研究以下主题。

  1. Serializable in Java

    Java可序列化
  2. Class Refactoring with Serialization and serialVersionUID

    带有序列化和serialVersionUID的类重构
  3. Java Externalizable Interface

    Java外部化接口
  4. Java Serialization Methods

    Java序列化方法
  5. Serialization with Inheritance

    继承序列化
  6. Serialization Proxy Pattern

    序列化代理模式

Java可序列化 (Serializable in Java)

If you want a class object to be serializable, all you need to do it implement the java.io.Serializable interface. Serializable in java is a marker interface and has no fields or methods to implement. It’s like an Opt-In process through which we make our classes serializable.

如果您希望类对象可序列化,则只需执行一下即可实现java.io.Serializable接口。 Java中的Serializable是标记接口,没有可实现的字段或方法。 这就像一个选择加入过程,通过该过程我们可以使类可序列化。

Serialization in java is implemented by ObjectInputStream and ObjectOutputStream, so all we need is a wrapper over them to either save it to file or send it over the network. Let’s see a simple Serialization in java program example.

Java中的序列化是通过ObjectInputStreamObjectOutputStream实现的,因此我们所需要的只是对其的包装,以将其保存到文件或通过网络发送。 让我们看一下Java程序示例中的简单序列化。

package com.journaldev.serialization;

import java.io.Serializable;

public class Employee implements Serializable {

//	private static final long serialVersionUID = -6470090944414208496L;
	
	private String name;
	private int id;
	transient private int salary;
//	private String password;
	
	@Override
	public String toString(){
		return "Employee{name="+name+",id="+id+",salary="+salary+"}";
	}
	
	//getter and setter methods
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getSalary() {
		return salary;
	}

	public void setSalary(int salary) {
		this.salary = salary;
	}

//	public String getPassword() {
//		return password;
//	}
//
//	public void setPassword(String password) {
//		this.password = password;
//	}
	
}

Notice that it’s a simple java bean with some properties and getter-setter methods. If you want an object property to be not serialized to stream, you can use transient keyword like I have done with salary variable.

注意,它是一个简单的Java bean,具有一些属性和getter-setter方法。 如果您不希望将对象属性序列化为流,则可以像我对薪水变量所做的那样使用瞬态关键字。

Now suppose we want to write our objects to file and then deserialize it from the same file. So we need utility methods that will use ObjectInputStream and ObjectOutputStream for serialization purposes.

现在假设我们要将对象写入文件,然后从同一文件反序列化。 因此,我们需要实用程序方法,这些方法将使用ObjectInputStreamObjectOutputStream进行序列化。

package com.journaldev.serialization;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * A simple class with generic serialize and deserialize method implementations
 * 
 * @author pankaj
 * 
 */
public class SerializationUtil {

	// deserialize to Object from given file
	public static Object deserialize(String fileName) throws IOException,
			ClassNotFoundException {
		FileInputStream fis = new FileInputStream(fileName);
		ObjectInputStream ois = new ObjectInputStream(fis);
		Object obj = ois.readObject();
		ois.close();
		return obj;
	}

	// serialize the given object and save it to file
	public static void serialize(Object obj, String fileName)
			throws IOException {
		FileOutputStream fos = new FileOutputStream(fileName);
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(obj);

		fos.close();
	}

}

Notice that the method arguments work with Object that is the base class of any java object. It’s written in this way to be generic in nature.

注意,方法参数与Object一起使用,Object是任何java对象的基类。 这种方式本质上是通用的。

Now let’s write a test program to see Java Serialization in action.

现在,让我们编写一个测试程序来查看实际的Java序列化。

package com.journaldev.serialization;

import java.io.IOException;

public class SerializationTest {
	
	public static void main(String[] args) {
		String fileName="employee.ser";
		Employee emp = new Employee();
		emp.setId(100);
		emp.setName("Pankaj");
		emp.setSalary(5000);
		
		//serialize to file
		try {
			SerializationUtil.serialize(emp, fileName);
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}
		
		Employee empNew = null;
		try {
			empNew = (Employee) SerializationUtil.deserialize(fileName);
		} catch (ClassNotFoundException | IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("emp Object::"+emp);
		System.out.println("empNew Object::"+empNew);
	}
}

When we run above test program for serialization in java, we get following output.

当我们在Java上运行测试程序以进行序列化时,将得到以下输出。

emp Object::Employee{name=Pankaj,id=100,salary=5000}
empNew Object::Employee{name=Pankaj,id=100,salary=0}

Since salary is a transient variable, it’s value was not saved to file and hence not retrieved in the new object. Similarly static variable values are also not serialized since they belongs to class and not object.

由于薪金是一个临时变量,因此其值未保存到文件中,因此也没有在新对象中检索。 同样, 静态变量值也不会序列化,因为它们属于类而不是对象。

带有序列化和serialVersionUID的类重构 (Class Refactoring with Serialization and serialVersionUID)

Serialization in java permits some changes in the java class if they can be ignored. Some of the changes in class that will not affect the deserialization process are:

如果可以忽略Java中的序列化,则允许对Java类进行一些更改。 类中的一些更改不会影响反序列化过程,这些更改包括:

  • Adding new variables to the class

    向类添加新变量
  • Changing the variables from transient to non-transient, for serialization it’s like having a new field.

    将变量从瞬态更改为非瞬态,对于序列化来说,这就像拥有一个新字段。
  • Changing the variable from static to non-static, for serialization it’s like having a new field.

    将变量从静态更改为非静态,以进行序列化就像有一个新字段。

But for all these changes to work, the java class should have serialVersionUID defined for the class. Let’s write a test class just for deserialization of the already serialized file from previous test class.

但是,为了使所有这些更改生效 ,java类应该为该类定义了serialVersionUID 。 让我们编写一个仅用于反序列化来自先前测试类的已序列化文件的测试类。

package com.journaldev.serialization;

import java.io.IOException;

public class DeserializationTest {

	public static void main(String[] args) {

		String fileName="employee.ser";
		Employee empNew = null;
		
		try {
			empNew = (Employee) SerializationUtil.deserialize(fileName);
		} catch (ClassNotFoundException | IOException e) {
			e.printStackTrace();
		}
		
		System.out.println("empNew Object::"+empNew);
		
	}
}

Now uncomment the “password” variable and it’s getter-setter methods from Employee class and run it. You will get below exception;

现在取消注释“ password”变量,它是Employee类中的getter-setter方法并运行它。 您将获得以下例外;

java.io.InvalidClassException: com.journaldev.serialization.Employee; local class incompatible: stream classdesc serialVersionUID = -6470090944414208496, local class serialVersionUID = -6234198221249432383
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:604)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1601)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1514)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1750)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1347)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:369)
	at com.journaldev.serialization.SerializationUtil.deserialize(SerializationUtil.java:22)
	at com.journaldev.serialization.DeserializationTest.main(DeserializationTest.java:13)
empNew Object::null

The reason is clear that serialVersionUID of the previous class and new class are different. Actually if the class doesn’t define serialVersionUID, it’s getting calculated automatically and assigned to the class. Java uses class variables, methods, class name, package etc to generate this unique long number. If you are working with any IDE, you will automatically get a warning that “The serializable class Employee does not declare a static final serialVersionUID field of type long”.

原因很明显,上一类和新类的serialVersionUID不同。 实际上,如果该类未定义serialVersionUID,则会自动进行计算并分配给该类。 Java使用类变量,方法,类名,包等来生成此唯一的长号。 如果使用任何IDE,您将自动收到一条警告:“可序列化的类Employee没有声明类型为long的静态最终serialVersionUID字段”。

We can use java utility “serialver” to generate the class serialVersionUID, for Employee class we can run it with below command.

我们可以使用Java实用程序“ serialver”来生成类serialVersionUID,对于Employee类,可以使用以下命令运行它。

SerializationExample/bin$serialver -classpath . com.journaldev.serialization.Employee

Note that it’s not required that the serial version is generated from this program itself, we can assign this value as we want. It just need to be there to let deserialization process know that the new class is the new version of the same class and should be deserialized of possible.

请注意,不需要从程序本身生成串行版本,我们可以根据需要分配该值。 只需要在那里使反序列化过程知道新类是同一类的新版本,并应尽可能进行反序列化。

For example, uncomment only the serialVersionUID field from the Employee class and run SerializationTest program. Now uncomment the password field from Employee class and run the DeserializationTest program and you will see that the object stream is deserialized successfully because the change in Employee class is compatible with serialization process.

例如,仅取消注释Employee类中的serialVersionUID字段,然后运行SerializationTest程序。 现在,取消注释Employee类中的password字段并运行DeserializationTest程序,您将看到对象流已成功反序列化,因为Employee类中的更改与序列化过程兼容。

Java外部化接口 (Java Externalizable Interface)

If you notice the java serialization process, it’s done automatically. Sometimes we want to obscure the object data to maintain it’s integrity. We can do this by implementing java.io.Externalizable interface and provide implementation of writeExternal() and readExternal() methods to be used in serialization process.

如果您注意到Java序列化过程,它会自动完成。 有时我们想遮掩对象数据以保持其完整性。 我们可以通过实现java.io.Externalizable接口并提供在序列化过程中使用的writeExternal()readExternal()方法的实现来实现。

package com.journaldev.externalization;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class Person implements Externalizable{

	private int id;
	private String name;
	private String gender;
	
	@Override
	public void writeExternal(ObjectOutput out) throws IOException {
		out.writeInt(id);
		out.writeObject(name+"xyz");
		out.writeObject("abc"+gender);
	}

	@Override
	public void readExternal(ObjectInput in) throws IOException,
			ClassNotFoundException {
		id=in.readInt();
		//read in the same order as written
		name=(String) in.readObject();
		if(!name.endsWith("xyz")) throw new IOException("corrupted data");
		name=name.substring(0, name.length()-3);
		gender=(String) in.readObject();
		if(!gender.startsWith("abc")) throw new IOException("corrupted data");
		gender=gender.substring(3);
	}

	@Override
	public String toString(){
		return "Person{id="+id+",name="+name+",gender="+gender+"}";
	}
	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 String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}

}

Notice that I have changed the field values before converting it to Stream and then while reading reversed the changes. In this way, we can maintain data integrity of some sorts. We can throw exception if after reading the stream data, the integrity checks fail. Let’s write a test program to see it in action.

请注意,在将其转换为Stream之前,然后在读取时撤消了更改,我已经更改了字段值。 这样,我们可以维护某种数据完整性。 如果在读取流数据后完整性检查失败,则可以引发异常。 让我们编写一个测试程序以查看其运行情况。

package com.journaldev.externalization;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ExternalizationTest {

	public static void main(String[] args) {
		
		String fileName = "person.ser";
		Person person = new Person();
		person.setId(1);
		person.setName("Pankaj");
		person.setGender("Male");
		
		try {
			FileOutputStream fos = new FileOutputStream(fileName);
			ObjectOutputStream oos = new ObjectOutputStream(fos);
		    oos.writeObject(person);
		    oos.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		FileInputStream fis;
		try {
			fis = new FileInputStream(fileName);
			ObjectInputStream ois = new ObjectInputStream(fis);
		    Person p = (Person)ois.readObject();
		    ois.close();
		    System.out.println("Person Object Read="+p);
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
		}
	    
	}
}

When we run above program, we get following output.

当我们运行上面的程序时,我们得到以下输出。

Person Object Read=Person{id=1,name=Pankaj,gender=Male}

So which one is better to be used for serialization in java. Actually it’s better to use Serializable interface and by the time we reach at the end of article, you will know why.

因此,哪个更好用于Java的序列化。 实际上,最好使用Serializable接口,等到本文末尾,您将知道原因。

Java序列化方法 (Java Serialization Methods)

We have seen that serialization in java is automatic and all we need is implementing Serializable interface. The implementation is present in the ObjectInputStream and ObjectOutputStream classes. But what if we want to change the way we are saving data, for example we have some sensitive information in the object and before saving/retrieving we want to encrypt/decrypt it. That’s why there are four methods that we can provide in the class to change the serialization behavior.

我们已经看到Java中的序列化是自动的,我们所需要的只是实现Serializable接口。 该实现存在于ObjectInputStream和ObjectOutputStream类中。 但是,如果我们想更改保存数据的方式,例如,对象中有一些敏感信息,而在保存/检索之前,我们想要对其进行加密/解密,该怎么办? 这就是为什么我们可以在类中提供四种方法来更改序列化行为的原因。

If these methods are present in the class, they are used for serialization purposes.

如果这些方法存在于类中,则将它们用于序列化目的。

  1. readObject(ObjectInputStream ois): If this method is present in the class, ObjectInputStream readObject() method will use this method for reading the object from stream.

    readObject(ObjectInputStream ois) :如果类中存在此方法,则ObjectInputStream readObject()方法将使用此方法从流中读取对象。
  2. writeObject(ObjectOutputStream oos): If this method is present in the class, ObjectOutputStream writeObject() method will use this method for writing the object to stream. One of the common usage is to obscure the object variables to maintain data integrity.

    writeObject(ObjectOutputStream oos) :如果类中存在此方法,则ObjectOutputStream writeObject()方法将使用此方法将对象写入流。 常见用法之一是遮盖对象变量以保持数据完整性。
  3. Object writeReplace(): If this method is present, then after serialization process this method is called and the object returned is serialized to the stream.

    Object writeReplace() :如果存在此方法,则在序列化过程之后,将调用此方法,并将返回的对象序列化到流中。
  4. Object readResolve(): If this method is present, then after deserialization process, this method is called to return the final object to the caller program. One of the usage of this method is to implement Singleton pattern with Serialized classes. Read more at Serialization and Singleton.

    Object readResolve() :如果存在此方法,则在反序列化过程之后,将调用此方法以将最终对象返回给调用程序。 此方法的用途之一是使用序列化类实现Singleton模式。 在序列化和单例中了解更多信息。

Usually while implementing above methods, it’s kept as private so that subclasses can’t override them. They are meant for serialization purpose only and keeping them private avoids any security issue.

通常,在实现上述方法时,它会保持私有状态,以使子类无法覆盖它们。 它们仅用于序列化目的,并且将它们设为私有可以避免任何安全问题。

继承序列化 (Serialization with Inheritance)

Sometimes we need to extend a class that doesn’t implement Serializable interface. If we rely on the automatic serialization behavior and the superclass has some state, then they will not be converted to stream and hence not retrieved later on.

有时我们需要扩展一个不实现Serializable接口的类。 如果我们依靠自动序列化行为,并且超类具有某种状态,则它们将不会转换为流,因此以后也不会检索。

This is one place, where readObject() and writeObject() methods really help. By providing their implementation, we can save the super class state to the stream and then retrieve it later on. Let’s see this in action.

这是readObject()和writeObject()方法真正有用的地方。 通过提供它们的实现,我们可以将超类状态保存到流中,然后在以后检索它。 让我们看看实际情况。

package com.journaldev.serialization.inheritance;

public class SuperClass {

	private int id;
	private String value;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}	
}

SuperClass is a simple java bean but it’s not implementing Serializable interface.

SuperClass是一个简单的Java bean,但未实现Serializable接口。

package com.journaldev.serialization.inheritance;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectInputValidation;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SubClass extends SuperClass implements Serializable, ObjectInputValidation{

	private static final long serialVersionUID = -1322322139926390329L;

	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
	@Override
	public String toString(){
		return "SubClass{id="+getId()+",value="+getValue()+",name="+getName()+"}";
	}
	
	//adding helper method for serialization to save/initialize super class state
	private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException{
		ois.defaultReadObject();
		
		//notice the order of read and write should be same
		setId(ois.readInt());
		setValue((String) ois.readObject());	
	}
	
	private void writeObject(ObjectOutputStream oos) throws IOException{
		oos.defaultWriteObject();
		
		oos.writeInt(getId());
		oos.writeObject(getValue());
	}

	@Override
	public void validateObject() throws InvalidObjectException {
		//validate the object here
		if(name == null || "".equals(name)) throw new InvalidObjectException("name can't be null or empty");
		if(getId() <=0) throw new InvalidObjectException("ID can't be negative or zero");
	}	
}

Notice that order of writing and reading the extra data to the stream should be same. We can put some logic in reading and writing data to make it secure.

请注意,向流中写入和读取额外数据的顺序应相同。 我们可以在读写数据时加入一些逻辑以确保其安全性。

Also notice that the class is implementing ObjectInputValidation interface. By implementing validateObject() method, we can put some business validations to make sure that the data integrity is not harmed.

还要注意,该类正在实现ObjectInputValidation接口。 通过实现validateObject()方法,我们可以进行一些业务验证,以确保数据完整性不会受到损害。

Let’s write a test class and see if we can retrieve the super class state from serialized data or not.

让我们编写一个测试类,看看是否可以从序列化数据中检索超类状态。

package com.journaldev.serialization.inheritance;

import java.io.IOException;

import com.journaldev.serialization.SerializationUtil;

public class InheritanceSerializationTest {

	public static void main(String[] args) {
		String fileName = "subclass.ser";
		
		SubClass subClass = new SubClass();
		subClass.setId(10);
		subClass.setValue("Data");
		subClass.setName("Pankaj");
		
		try {
			SerializationUtil.serialize(subClass, fileName);
		} catch (IOException e) {
			e.printStackTrace();
			return;
		}
		
		try {
			SubClass subNew = (SubClass) SerializationUtil.deserialize(fileName);
			System.out.println("SubClass read = "+subNew);
		} catch (ClassNotFoundException | IOException e) {
			e.printStackTrace();
		}
	}
}

When we run above class, we get following output.

当我们在类上运行时,将得到以下输出。

SubClass read = SubClass{id=10,value=Data,name=Pankaj}

So in this way, we can serialize super class state even though it’s not implementing Serializable interface. This strategy comes handy when the super class is a third-party class that we can’t change.

因此,通过这种方式,即使超类状态未实现Serializable接口,我们也可以对其进行序列化。 当超级类是我们无法更改的第三方类时,此策略非常有用。

序列化代理模式 (Serialization Proxy Pattern)

Serialization in java comes with some serious pitfalls such as;

Java中的序列化带有一些严重的陷阱,例如;

  • The class structure can’t be changed a lot without breaking the java serialization process. So even though we don’t need some variables later on, we need to keep them just for backward compatibility.

    在不中断Java序列化过程的情况下,无法对类结构进行很多更改。 因此,即使以后不再需要某些变量,我们也需要保留它们只是为了向后兼容。
  • Serialization causes huge security risks, an attacker can change the stream sequence and cause harm to the system. For example, user role is serialized and an attacker change the stream value to make it admin and run malicious code.

    序列化会带来巨大的安全风险,攻击者可能会更改流序列并损害系统。 例如,用户角色被序列化,攻击者更改流值以使其成为admin并运行恶意代码。

Java Serialization Proxy pattern is a way to achieve greater security with Serialization. In this pattern, an inner private static class is used as a proxy class for serialization purpose. This class is designed in the way to maintain the state of the main class. This pattern is implemented by properly implementing readResolve() and writeReplace() methods.

Java序列化代理模式是一种通过序列化实现更高安全性的方法。 在这种模式下,内部私有静态类用作代理类以进行序列化。 以维护主类状态的方式设计此类。 通过正确实现readResolve()writeReplace()方法来实现此模式。

Let us first write a class which implements serialization proxy pattern and then we will analyze it for better understanding.

让我们首先编写一个实现序列化代理模式的类,然后我们将对其进行分析以更好地理解。

package com.journaldev.serialization.proxy;

import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class Data implements Serializable{

	private static final long serialVersionUID = 2087368867376448459L;

	private String data;
	
	public Data(String d){
		this.data=d;
	}

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}
	
	@Override
	public String toString(){
		return "Data{data="+data+"}";
	}
	
	//serialization proxy class
	private static class DataProxy implements Serializable{
	
		private static final long serialVersionUID = 8333905273185436744L;
		
		private String dataProxy;
		private static final String PREFIX = "ABC";
		private static final String SUFFIX = "DEFG";
		
		public DataProxy(Data d){
			//obscuring data for security
			this.dataProxy = PREFIX + d.data + SUFFIX;
		}
		
		private Object readResolve() throws InvalidObjectException {
			if(dataProxy.startsWith(PREFIX) && dataProxy.endsWith(SUFFIX)){
			return new Data(dataProxy.substring(3, dataProxy.length() -4));
			}else throw new InvalidObjectException("data corrupted");
		}
		
	}
	
	//replacing serialized object to DataProxy object
	private Object writeReplace(){
		return new DataProxy(this);
	}
	
	private void readObject(ObjectInputStream ois) throws InvalidObjectException{
		throw new InvalidObjectException("Proxy is not used, something fishy");
	}
}
  • Both Data and DataProxy class should implement Serializable interface.

    DataDataProxy类都应实现Serializable接口。
  • DataProxy should be able to maintain the state of Data object.

    DataProxy应该能够维护Data对象的状态。
  • DataProxy is inner private static class, so that other classes can’t access it.

    DataProxy是内部私有静态类,因此其他类无法访问它。
  • DataProxy should have a single constructor that takes Data as argument.

    DataProxy应该具有一个以Data作为参数的构造函数。
  • Data class should provide writeReplace() method returning DataProxy instance. So when Data object is serialized, the returned stream is of DataProxy class. However DataProxy class is not visible outside, so it can’t be used directly.

    Data类应提供返回DataProxy实例的writeReplace()方法。 因此,当对Data对象进行序列化时,返回的流属于DataProxy类。 但是,DataProxy类在外部不可见,因此无法直接使用。
  • DataProxy class should implement readResolve() method returning Data object. So when Data class is deserialized, internally DataProxy is deserialized and when it’s readResolve() method is called, we get Data object.

    DataProxy类应实现readResolve()方法,以返回Data对象。 因此,当对Data类进行反序列化时,在内部对DataProxy进行反序列化,并在调用readResolve()方法时,我们得到了Data对象。
  • Finally implement readObject() method in Data class and throw InvalidObjectException to avoid hackers attack trying to fabricate Data object stream and parse it.

    最后,在Data类中实现readObject()方法并抛出InvalidObjectException以避免黑客攻击来伪造Data对象流并对其进行解析。

Let’s write a small test to check whether implementation works or not.

让我们写一个小测试来检查实现是否有效。

package com.journaldev.serialization.proxy;

import java.io.IOException;

import com.journaldev.serialization.SerializationUtil;

public class SerializationProxyTest {

	public static void main(String[] args) {
		String fileName = "data.ser";
		
		Data data = new Data("Pankaj");
		
		try {
			SerializationUtil.serialize(data, fileName);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		try {
			Data newData = (Data) SerializationUtil.deserialize(fileName);
			System.out.println(newData);
		} catch (ClassNotFoundException | IOException e) {
			e.printStackTrace();
		}
	}

}

When we run above class, we get below output in console.

当我们在类上运行时,会在控制台中得到以下输出。

Data{data=Pankaj}

If you will open the data.ser file, you can see that DataProxy object is saved as stream in the file.

如果您将打开data.ser文件,则可以看到DataProxy对象已作为流保存在文件中。

That’s all for Serialization in Java, it looks simple but we should use it judiciously and it’s always better not to rely on default implementation. Download the project from above link and play around with it to learn more.

这就是Java序列化的全部内容,它看起来很简单,但是我们应该明智地使用它,并且最好不要依赖默认实现。 从上面的链接下载该项目,并试用它以了解更多信息。

翻译自: https://www.journaldev.com/2452/serialization-in-java

java序列化 反序列化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值