Java 序列化机制

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

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

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

 

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

private void writeObject(ObjectOutputStream os) throws IOException
    {
         
    }

类似的 ObjectInputStream 提供如下方法: 

    private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException
    {
         
    }

那么哪里会需要序列化呢?序列化通常在需要通过网络传输数据,或者保存对象到文件的场合使用。这里说的数据是对象而不是文本。简单总结起来,进行对象序列化的话的主要原因就是实现对象持久化进行网络传输.

现在的问题是,我们的网络架构和硬盘都只能识别二进制和字节,而不能识别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();
  }
 }
}

这其中主要涉及到JavaI/O方面的内容,主要用到两个类FileOutputStreamObjectOutputStream

对于反序列化:

步骤是  

在包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对象的时候,所有其他引用对象,例如addresshome将会被自动地被序列化。让我们来创建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类中,标记了addresstransient之后,运行程序。你会得到nullPointerException,因为在反序列化过程中,Address引用将会是null

参考:

http://www.oschina.net/translate/serialization-in-java


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值