Java中的序列化
Java提供一种称为序列化的机制,以按字节顺序或字节顺序持久化Java对象,其中包括对象的数据以及有关对象的类型和存储在对象中的数据类型的信息。 因此,如果我们已序列化了任何对象,则可以使用对象的类型和其他信息对其进行读取和反序列化,以便检索原始对象。
类ObjectInputStream和ObjectOutputStream是高级流,其中包含用于序列化和反序列化对象的方法。 ObjectOutputStream有许多用于序列化对象的方法,但是常用的方法是:
private void writeObject(ObjectOutputStream os) throws IOException { }
同样,ObjectInputStream具有
private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { }
需要序列化吗?
通常在需要通过网络发送数据或存储在文件中时使用序列化。 数据是对象,而不是文本。 现在的问题是您的网络基础结构和硬盘是可以理解位和字节但不能理解Java对象的硬件组件。 序列化是将Java对象的值/状态转换为字节,以便通过网络发送或保存它。反序列化是将字节码转换为相应的Java对象。
serialVersionUID的概念:
SerialVersionUID用于确保在反序列化期间加载相同的对象(在序列化期间使用的对象)。serialVersionUID用于对象的版本控制。您可以在Java序列化中的serialVersionUID上阅读更多内容
对于序列化:
步骤如下:
让我们举个例子:在src-> org.arpit.javapostsforlearning中创建Employee.java:
1,Employee.java
package org.arpit.javapostsforlearning; import java.io.Serializable; public class Employee implements Serializable{ int employeeId; String employeeName; String department; public int getEmployeeId() { return employeeId; } public void setEmployeeId(int employeeId) { this.employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } }
从上面可以看到,如果要序列化任何类,则它必须实现Serializable接口(即标记接口)。
Java中的标记接口是没有字段或方法的接口,或者简单的单词空接口在Java中称为标记接口。 在src-> org.arpit.javapostsforlearning中创建SerializeMain.java
2. SerializeMain.java
package org.arpit.javapostsforlearning; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class SerializeMain { /** * @author Arpit Mandliya */ public static void main(String[] args) { Employee emp = new Employee(); emp.setEmployeeId(101); emp.setEmployeeName('Arpit'); emp.setDepartment('CS'); try { FileOutputStream fileOut = new FileOutputStream('employee.ser'); ObjectOutputStream outStream = new ObjectOutputStream(fileOut); outStream.writeObject(emp); outStream.close(); fileOut.close(); }catch(IOException i) { i.printStackTrace(); } } }
对于反序列化:
步骤如下:
在src-> org.arpit.javapostsforlearning中创建DeserializeMain.java
3.DeserializeMain.java
package org.arpit.javapostsforlearning; import java.io.IOException; import java.io.ObjectInputStream; public class DeserializeMain { /** * @author Arpit Mandliya */ public static void main(String[] args) { Employee emp = null; try { FileInputStream fileIn =new FileInputStream('employee.ser'); ObjectInputStream in = new ObjectInputStream(fileIn); emp = (Employee) in.readObject(); in.close(); fileIn.close(); }catch(IOException i) { i.printStackTrace(); return; }catch(ClassNotFoundException c) { System.out.println('Employee class not found'); c.printStackTrace(); return; } System.out.println('Deserialized Employee...'); System.out.println('Emp id: ' + emp.getEmployeeId()); System.out.println('Name: ' + emp.getEmployeeName()); System.out.println('Department: ' + emp.getDepartment()); } }
4,运行它
首先运行SerializeMain.java,然后运行DeserializeMain.java,您将获得以下输出:
Deserialized Employee... Emp id: 101 Name: Arpit Department: CS
因此,我们先序列化了一个雇员对象,然后对其进行反序列化。这看起来很简单,但是当引用对象,继承关系出现时,它可能会非常复杂。因此,我们将逐一介绍不同的情况以及如何在不同的情况下应用序列化。
情况1-如果一个对象引用了其他对象怎么办
我们已经看到了非常简单的序列化案例,现在如果它也引用其他对象怎么办?它将如何序列化呢? 是的,您不必显式地序列化引用对象。当序列化任何对象并且如果它包含任何其他对象引用时,Java序列化将序列化该对象的整个对象图。
例如:让我们说,Employee现在引用了地址对象,而Address可以引用了其他某个对象(例如Home),那么当您序列化Employee对象时,所有其他引用对象(例如address和home)将被自动序列化。 让我们创建Address类并添加Address对象作为对上述员工类的引用。
Employee.java:
package org.arpit.javapostsforlearning; import java.io.Serializable; public class Employee implements Serializable{ int employeeId; String employeeName; String department; Address address; public int getEmployeeId() { return employeeId; } public void setEmployeeId(int employeeId) { this.employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } }
在org.arpit.javapostsforlearning中创建Address.java:
地址.java:
package org.arpit.javapostsforlearning; public class Address { int homeNo; String street; String city; public Address(int homeNo, String street, String city) { super(); this.homeNo = homeNo; this.street = street; this.city = city; } public int getHomeNo() { return homeNo; } public void setHomeNo(int homeNo) { this.homeNo = homeNo; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } }
在org.arpit.javaposts学习中创建SerializeDeserializeMain.java:
SerializeDeserializeMain.java: package org.arpit.javapostsforlearning;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializeDeserializeMain {
/**
* @author Arpit Mandliya
*/
public static void main(String[] args) {
Employee emp = new Employee();
emp.setEmployeeId(101);
emp.setEmployeeName('Arpit');
emp.setDepartment('CS');
Address address=new Address(88,'MG road','Pune');
emp.setAddress(address);
//Serialize
try
{
FileOutputStream fileOut = new FileOutputStream('employee.ser');
ObjectOutputStream outStream = new ObjectOutputStream(fileOut);
outStream.writeObject(emp);
outStream.close();
fileOut.close();
}catch(IOException i)
{
i.printStackTrace();
}
//Deserialize
emp = null;
try
{
FileInputStream fileIn =new FileInputStream('employee.ser');
ObjectInputStream in = new ObjectInputStream(fileIn);
emp = (Employee) in.readObject();
in.close();
fileIn.close();
}catch(IOException i)
{
i.printStackTrace();
return;
}catch(ClassNotFoundException c)
{
System.out.println('Employee class not found');
c.printStackTrace();
return;
}
System.out.println('Deserialized Employee...');
System.out.println('Emp id: ' + emp.getEmployeeId());
System.out.println('Name: ' + emp.getEmployeeName());
System.out.println('Department: ' + emp.getDepartment());
address=emp.getAddress();
System.out.println('City :'+address.getCity());
}
}
运行 :
运行SerializeDeserializeMain.java时,将获得以下输出:
java.io.NotSerializableException: org.arpit.javapostsforlearning.Address at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source) at java.io.ObjectOutputStream.writeSerialData(Unknown Source) at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source) at java.io.ObjectOutputStream.writeObject0(Unknown Source) at java.io.ObjectOutputStream.writeObject(Unknown Source)
我们错了,出了什么问题。我忘了提,Address类也必须是可序列化的。因此,必须通过实现serialzable接口使Address序列化。
地址.java:
import java.io.Serializable; public class Address implements Serializable{ int homeNo; String street; String city; public Address(int homeNo, String street, String city) { super(); this.homeNo = homeNo; this.street = street; this.city = city; } public int getHomeNo() { return homeNo; } public void setHomeNo(int homeNo) { this.homeNo = homeNo; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } }
再次运行:
当再次运行SerializeDeserializeMain.java时,将获得以下输出:
Deserialized Employee... Emp id: 101 Name: Arpit Department: CS City: Pune
情况2:如果您无权访问引用对象的源代码(例如,您无权访问上述Address类)该怎么办?
如果您没有访问地址类的权限,那么如何在地址类中实现可序列化的接口呢? 是的,您可以创建另一个扩展地址并使其可序列化的类,但是在许多情况下它可能会失败:
- 如果将类声明为final怎么办
- 如果类引用了其他不可序列化的对象,该怎么办?
那么,您将如何序列化Employee对象? 所以解决方案是您可以使其瞬变。如果您不想序列化任何字段,请使其瞬变。
transient Address address
因此,在运行程序时在Employee类中使地址瞬态之后,您将获得nullPointerException,因为在反序列化期间地址引用将为null
情况3:如果您仍要保存引用对象的状态(例如,地址对象上方),该怎么办:
如果您使地址处于瞬态状态,那么在反序列化期间它将返回null。但是如果您仍然希望与序列化地址对象时的状态相同,Java序列化会提供一种机制,如果您拥有带有特定签名的私有方法,则它们将获得在序列化和反序列化期间调用,因此我们将覆盖employee类的writeObject和readObject方法,并且将在Employee对象的序列化和反序列化期间调用它们。
Employee.java:
package org.arpit.javapostsforlearning; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class Employee implements Serializable{ int employeeId; String employeeName; String department; transient Address address; public int getEmployeeId() { return employeeId; } public void setEmployeeId(int employeeId) { this.employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } private void writeObject(ObjectOutputStream os) throws IOException, ClassNotFoundException { try { os.defaultWriteObject(); os.writeInt(address.getHomeNo()); os.writeObject(address.getStreet()); os.writeObject(address.getCity()); } catch (Exception e) { e.printStackTrace(); } } private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException { try { is.defaultReadObject(); int homeNo=is.readInt(); String street=(String) is.readObject(); String city=(String) is.readObject(); address=new Address(homeNo,street,city); } catch (Exception e) { e.printStackTrace(); } } }
应该记住一件事,ObjectInputStream应该以我们将数据写入ObjectOutputStream的相同顺序读取数据。 在org.arpit.javapostsforlearning中创建Address.java:
地址.java:
package org.arpit.javapostsforlearning; import java.io.Serializable; public class Address { int homeNo; String street; String city; public Address(int homeNo, String street, String city) { super(); this.homeNo = homeNo; this.street = street; this.city = city; } public int getHomeNo() { return homeNo; } public void setHomeNo(int homeNo) { this.homeNo = homeNo; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } }
在org.arpit.javaposts学习中创建SerializeDeserializeMain.java:
SerializeDeserializeMain.java:
package org.arpit.javapostsforlearning; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeDeserializeMain { /** * @author Arpit Mandliya */ public static void main(String[] args) { Employee emp = new Employee(); emp.setEmployeeId(101); emp.setEmployeeName('Arpit'); emp.setDepartment('CS'); Address address=new Address(88,'MG road','Pune'); emp.setAddress(address); //Serialize try { FileOutputStream fileOut = new FileOutputStream('employee.ser'); ObjectOutputStream outStream = new ObjectOutputStream(fileOut); outStream.writeObject(emp); outStream.close(); fileOut.close(); }catch(IOException i) { i.printStackTrace(); } //Deserialize emp = null; try { FileInputStream fileIn =new FileInputStream('employee.ser'); ObjectInputStream in = new ObjectInputStream(fileIn); emp = (Employee) in.readObject(); in.close(); fileIn.close(); }catch(IOException i) { i.printStackTrace(); return; }catch(ClassNotFoundException c) { System.out.println('Employee class not found'); c.printStackTrace(); return; } System.out.println('Deserialized Employee...'); System.out.println('Emp id: ' + emp.getEmployeeId()); System.out.println('Name: ' + emp.getEmployeeName()); System.out.println('Department: ' + emp.getDepartment()); address=emp.getAddress(); System.out.println('City :'+address.getCity()); } }
运行 :
运行SerializeDeserializeMain.java时,将获得以下输出:
Deserialized Employee... Emp id: 101 Name: Arpit Department: CS City :Pune
所以现在我们得到的地址对象的状态与序列化之前的状态相同。
序列化中的继承:
现在我们将了解继承如何影响序列化,因此在某些情况下,超类是否可序列化是可能的,如果不是,那么您将如何处理它以及它是如何工作的,让我们举个例子来看。 我们将创建Person.java,它将是Employee的超类:
情况4:如果超类是可序列化的怎么办?
如果超类是可序列化的,则其所有子类都可以自动序列化。
情况5:如果超类不可序列化怎么办?
如果超类不能序列化,那么我们必须以完全不同的方式处理它。
- 如果超类不可序列化,则它必须没有参数构造函数。
人.java
package org.arpit.javapostsforlearning; public class Person { String name='default'; String nationality; public Person() { System.out.println('Person:Constructor'); } public Person(String name, String nationality) { super(); this.name = name; this.nationality = nationality; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNationality() { return nationality; } public void setNationality(String nationality) { this.nationality = nationality; } }
在org.arpit.javapostsforlearning中创建Employee.java:
Employee.java:
package org.arpit.javapostsforlearning; import java.io.Serializable; public class Employee extends Person implements Serializable{ int employeeId; String department; public Employee(int employeeId,String name,String department,String nationality) { super(name,nationality); this.employeeId=employeeId; this.department=department; System.out.println('Employee:Constructor'); } public int getEmployeeId() { return employeeId; } public void setEmployeeId(int employeeId) { this.employeeId = employeeId; } public String getDepartment() { return department; } public void setDepartment(String department) { this.department = department; } }
在org.arpit.javaposts学习中创建SerializeDeserializeMain.java:
SerializeDeserializeMain.java:
package org.arpit.javapostsforlearning; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeDeserializeMain { /** * @author Arpit Mandliya */ public static void main(String[] args) { //Serialize Employee emp = new Employee(101,'Arpit','CS','Indian'); System.out.println('Before serializing'); System.out.println('Emp id: ' + emp.getEmployeeId()); System.out.println('Name: ' + emp.getName()); System.out.println('Department: ' + emp.getDepartment()); System.out.println('Nationality: ' + emp.getNationality()); System.out.println('************'); System.out.println('Serializing'); try { FileOutputStream fileOut = new FileOutputStream('employee.ser'); ObjectOutputStream outStream = new ObjectOutputStream(fileOut); outStream.writeObject(emp); outStream.close(); fileOut.close(); }catch(IOException i) { i.printStackTrace(); } //Deserialize System.out.println('************'); System.out.println('Deserializing'); emp = null; try { FileInputStream fileIn =new FileInputStream('employee.ser'); ObjectInputStream in = new ObjectInputStream(fileIn); emp = (Employee) in.readObject(); in.close(); fileIn.close(); }catch(IOException i) { i.printStackTrace(); return; }catch(ClassNotFoundException c) { System.out.println('Employee class not found'); c.printStackTrace(); return; } System.out.println('After serializing'); System.out.println('Emp id: ' + emp.getEmployeeId()); System.out.println('Name: ' + emp.getName()); System.out.println('Department: ' + emp.getDepartment()); System.out.println('Nationality: ' + emp.getNationality()); } }
运行 :
运行SerializeDeserializeMain.java时,将获得以下输出:
如果超类不可序列化,则在反序列化过程中,将通过调用Non-Serializable超类的构造函数来初始化从超类继承的实例变量的所有值。 因此,这里name是从person继承的,因此在反序列化期间,name会初始化为默认值。
情况6-如果超类可序列化但您不希望子类可序列化怎么办
如果您不希望子类可序列化,则需要实现writeObject()和readObject()方法,并且需要从此方法中抛出NotSerializableException。
情况7-您可以序列化静态变量吗?
不,您不能。众所周知,静态变量是在类级别而不是对象级别,并且您序列化了一个对象,因此您无法序列化静态变量。
摘要:
- 序列化是将Java对象的值/状态转换为字节,以便通过网络发送或保存它。反序列化是将字节码转换为相应的Java对象。
- 关于序列化的好处是,整个过程是与JVM无关的,这意味着可以在一个平台上序列化对象,并在完全不同的平台上反序列化对象。\
- 如果要序列化任何类,则它必须实现Serializable接口(即标记接口)。
- Java中的标记接口是没有字段或方法的接口,或者简单的单词空接口在Java中称为标记接口
- serialVersionUID用于确保在反序列化期间加载相同的对象(在序列化期间使用的对象)。serialVersionUID用于对象的版本控制。
- 当序列化任何对象并且如果它包含任何其他对象引用时,Java序列化将序列化该对象的整个对象图。
- 如果您不想序列化任何字段,请使其成为临时字段。
- 如果超类是可序列化的,则其子类将自动可序列化。
- 如果超类不可序列化,则在反序列化过程中,将通过调用Non-Serializable超类的构造函数来初始化从超类继承的实例变量的所有值。
- 如果您不希望子类可序列化,则需要实现writeObject()和readObject()方法,并且需要从此方法中抛出NotSerializableException。
- 您不能序列化静态变量。
参考:来自JCG合作伙伴 Arpit Mandliya的Java 序列化, 适用于初学者博客的Java框架和设计模式 。
翻译自: https://www.javacodegeeks.com/2013/03/serialization-in-java.html