2KB项目,专业的源码交易网站 帮助 收藏 每日签到

Java 的序列化 (Serialization) 教程

  • 时间:2019-01-23 18:49 编辑:2KB 来源:2KB.COM 阅读:423
  • 扫一扫,手机访问
  • 分享
摘要:
Java 英文原文:Serialization in java

Java提供一种机制叫做序列化,通过有序的格式或者字节序列持久化java对象,其中包含对象的数据,还有对象的类型,和保存在对象中的数据类型。

所以,如果我们已经序列化了一个对象,那么它可以被读取并通过对象的类型和其他信息进行反序列化,并最终获取对象的原型。

ObjectInputStream 和 ObjectOutputStream对象是高级别的流对象,包含序列化和反序列化的方法。

ObjectOutputStream 拥有很多序列化对象的方法,最常用的是:

 private void writeObject(ObjectOutputStream os) throws IOException
    { 
        
    }
类似的 ObjectInputStream 提供如下方法:
    private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException
    {
        
    }


其它翻译版本 (1) 加载中

那么哪里会需要序列化呢?序列化通常在需要通过网络传输数据,或者保存对象到文件的场合使用。这里说的数据是对象而不是文本。

现在的问题是,我们的网络架构和硬盘都只能识别二进制和字节,而不能识别Java对象。

序列化就是把Java对象中的value/states翻译为字节,以便通过网络传输或者保存。另外,反序列化就是通过读取字节码,并把它翻译回java对象。

serialVersionUID概念

serialVersionUID 是用于保证同一个对象(在序列化中会被用到)可以在Deserialization过程中被载入。serialVersionUID 是用于对象的版本控制。你可以参考serialVersionUID in java serialization获取更多信息。

对于序列化:

步骤如下:

让我们看一个列子:

在 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 接口 ,这个接口是标记接口(marker interface)。


Java中的标记接口(marker interface)就是一个没有任何字段或者方法的接口,简单的来说,java中把空接口叫做标记接口(marker interface)

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
就这样,我们序列化了一个employee对象,并对它进行反序列化。这看起来和简单,但是如果其中包含对象引用,继承,那么情况就会变得复杂。接下来让我们一个接一个的看一下例子,看看如何在各种场合中实现序列化。



案例1 - 如果对象引用了其他对象,那该如何

我们已经看过最简单的序列化例子,现在看看,如何处理对象中引用了其他对象的场合。我们该如何序列化?引用对象也会被序列化吗?对的,你不需要显式的序列化引用对象。当你序列化任何对象,如果它包含引用对象,那么Java序列化会自动序列化该对象的整个对象图。例如,Employee现在引用了一个address对象,并且Address也引用了其他对象(例如,Home),那么当你序列化Employee对象的时候,所有其他引用对象,例如address和home将会被自动地被序列化。让我们来创建Address类,并它Address的对象作为引用,添加到employee类中。

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 
Address.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.javapostsforlearning中,创建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 类也必须是serializable。那么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类的源码)

如果我们不能访问到address类,那么我们该如何在Address类中实现serializable 接口?是否有另外的途径来实现呢?对的,你可以创建另外一个类,并继承Address,然后让它继承serializable 接口,但是对于下面的情况,这个方案会失败:

  • 如果引用类被定义为final
  • 如果引用类引用了另外一个非可序列化的对象

那么,我们该如何序列化Employee对象?解决的办法是,标记transient。如果你不需要序列化任何字段,只需把它标记为transient。

transient Address address 
在Employee类中,标记了address为transient之后,运行程序。你会得到nullPointerException,因为在反序列化过程中,Address引用将会是null。



案例3 - 如果我仍然需要保存引用对象的状态呢?(例如address对象)

如果你在反序列化过程中,标记了address为transient,它将会返回null结果。但是如果你仍然需要保存它的状态,你就需要序列化address对象。 Java序列化提供一个机制,如果你有特定签名的private方法,那么它们就会在序列化和反序列化过程中被调用,所以我们将重写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
Address.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.javapostsforlearning中创建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

现在我们就得到了address对象的状态,就如它被序列化前的一样。 其它翻译版本 (1) 加载中

序列化中的继承:

现在我们看看继承是如何影响序列化的。不管父类是不是可序列化,这将引出很多个例子。如果父类是非可序列化的,我们将如何处理,并且它是如何工作的。让我们看看例子。

我们将创建一个Person.java,作为 Employee的父类。

案例4: 如果父类是可序列化的

如果父类可序列化,那么所有的继承类将是可序列化的。

案例5: 如果父类为非可序列化呢?

如果父类为非可序列化的 ,那么我们的处理办法会很不一样。

  • 如果父类为非可序列化的,那么它必然不会有参数构造函数。

Person.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.javapostsforlearning包中创建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后,你会得到如下的输出,如果父类是非可序列化的,那么在反序列化过程中,所有继承于父类的实例变量值,将会通过调用非序列化构造函数来初始化。 这里 name继承于person,所以在反序列化过程中,name将会被初始化为默认值。

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。


2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务

  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【计算机/互联网|】Nginx出现502错误(2020-01-20 21:02)
【计算机/互联网|】网站运营全智能软手V0.1版发布(2020-01-20 12:16)
【计算机/互联网|】淘宝这是怎么了?(2020-01-19 19:15)
【行业动态|】谷歌关闭小米智能摄像头,因为窃听器显示了陌生人家中的照片(2020-01-15 09:42)
【行业动态|】据报道谷歌新闻终止了数字杂志,退还主动订阅(2020-01-15 09:39)
【行业动态|】康佳将OLED电视带到美国与LG和索尼竞争(2020-01-15 09:38)
【行业动态|】2020年最佳AV接收机(2020-01-15 09:35)
【行业动态|】2020年最佳流媒体设备:Roku,Apple TV,Firebar,Chromecast等(2020-01-15 09:31)
【行业动态|】CES 2020预览:更多的流媒体服务和订阅即将到来(2020-01-08 21:41)
【行业动态|】从埃隆·马斯克到杰夫·贝佐斯,这30位人物定义了2010年代(2020-01-01 15:14)
联系我们

Q Q: 7090832

电话:400-0011-990

邮箱:7090832@qq.com

时间:9:00-23:00

联系客服
商家入住 服务咨询 投拆建议 联系客服
0577-67068160
手机版

扫一扫进手机版
返回顶部