对象的序列化

       何为序列化和反序列化?

             序列化机制允许将实现序列化的java对象转换成字节序列,这些字节序列可以保存在磁盘上,或者通过网络的传输,以备以后重新恢复成原来的对象。序列化机制使得对象可以脱离程序的运行独立存在。

             对象的序列化指将一个java对象写入IO流中,与此对应的是,对象的反序列化则指从IO流中恢复该java对象。

       如何将对象设置为可序列化的?

             将类实现Serializable接口。这只是一个标记接口,没有任何的方法。

       对象的序列化的用途?

             所有可能在网络上传输的对象都应该是可序列化的,否则程序将会出现异常。比如RMI(远程方法调用,是java ee的基础)过程的参数和返回值;所有因为保存到磁盘里的对象的类都必须可序列化,

            比如Web应用中需要保存到HttpSession或者ServletContext属性的java对象。

            因为序列化是RMI过程的参数和返回值都必须实现的机制,而RMI又是java EE 技术的基础---------所有的分布式应用常常需要跨平台,跨网络,所以要求所有传递的参数,返回值必须实现序列化,

            因此:序列化机制是java EE平台的基础。通常建议:程序创建的每一个JavaBean类都实现Seralizable.


            对象序列化与反序列化的应用实例:

           1.将对象写入磁盘,然后读出。

                1.将对象所属的类实现Serialize接口:

                         public class Student implements Serializable{
      private String name;
      private int age;
      public Student(String name, int age) {
         this.name = name;
         this.age = age;
    }
      
      
      
      public String getName() {
return name;
}



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



public int getAge() {
return age;
}



public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
     
     
}

public class Test {
   public static void main(String[] args) {
Student st=new Student("徐才厚", 17);
//创建File对象
File file=new File("D:\\3.txt");
try {
//创建一个ObjectOutPutStream输出流
ObjectOutputStream oos=new ObjectOutputStream
(new FileOutputStream(file));

//序列化teacher对象
oos.writeObject(st);
//创建一个ObjectInputStream输入流
ObjectInputStream ois=new ObjectInputStream
(new FileInputStream(file));

Student stu=(Student) ois.readObject();
System.out.println(stu);

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
   

2.将对象在网络上传输,在网络上传输时,在服务的另一端,反序列化读取的仅仅是java对象的数据,而不是Java类,因此反序列化恢复java对象时,必须提供Java对象所属类的class文件,否则将会引发ClassNotFoundException异常。

但是在客户端和服务器端都必须有Person类的class文件。


客户端:

public class Person implements Serializable{
    private String name;
    private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
    
}

public class Client {
  public static void main(String[] args) {
       try {
  Socket socket=new Socket("localhost", 9999);
      OutputStream os=socket.getOutputStream();
      ObjectOutputStream oos=new ObjectOutputStream(os); 
      Person per=new Person("徐才厚", 19);
      oos.writeObject(per);
      //加flush与不加flush影响不大,但是一般都加上,确保数据全部都能在通道里传输。
      oos.flush();

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
       
}
  
}





服务器端:

public class Person implements Serializable{
    private String name;
    private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

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

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
    
}

public class server {
    public static void main(String[] args) {
try {

ServerSocket ss=new ServerSocket(9999);
Socket socket=ss.accept();
InputStream is=socket.getInputStream();
ObjectInputStream ois=new ObjectInputStream(is);
Object obj=ois.readObject();
System.out.println(obj);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


2.自定义序列化

实现一:

如果我们不想序列化对象中的某个字段,我们一般在对象所属的类中的字段前加上transient关键字。这样在序列化对象时,此字段的值不会被序列化。就像上一个Person类,如果age这个字段

被transient修饰,则,打印对象的时候,这时候age为默认值为0.


实现二:

我们可以重写Person对象的

       private  void writeObject(java.io.ObjectOutputStream out) throws IOException {

      } 和

      private void  readObject(java.io.ObjectInputStream io) throws IOException,ClassNotFoundexception{

}

writeObject()方法负责写入特定类的实例状态,以便相应的readObject()方法可以恢复它,通过重写此方法,我们可以完全获得对序列化

机制的控制,可以自主决定哪些Fileld需要序列化,需要怎样的序列化。这样同时保证了网络传输的安全性,因为在传输时,进行了加密。

同时readObject()方法负责从流中读取并恢复对象Field,通过重写该方法,我们可以完全获得对反序列化机制的控制,可以自主决定需要反序列化

哪些字段,以及如何进行反序列化。如果writeObject()方法中对java对象进行了一些处理,则应该在readObject()方中,对其Field字段

进行相应的处理,以便正确的恢复该对象。


代码示例:其他的代码,与上面的代码一样。

public class Student implements Serializable{
      private String name;
      private int age;
      public Student(String name, int age) {
this.name = name;
this.age = age;
}
      
      
      
      public String getName() {
return name;
}



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



public int getAge() {
return age;
}



public void setAge(int age) {
this.age = age;
}



/***
       *   功能:对于自定义的序列化,序列化和反序列化Person实例没有任何的
       *   区别,区别在于序列化的对象流,即使有Cracker截获到Person对象流
       *   ,看到的name也是加密后的name,这样就提高了序列化的安全性,这样
       *   在网络流中是安全的。同时在序列化时,你可以自行的定义哪些字段可以序列化
       *   哪些字段不可序列化。这个这样的话可以进行加密。网络传输更安全。重写
       *   writeObject()方法和readObject()方法。
       * *******/
     private void writeObject(java.io.ObjectOutputStream out){
    try {
      //将name Field值反转后写入二进制流
out.writeObject(new StringBuffer(name).reverse());
//out.writeInt(age);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
     }
     
     
     private void readObject(java.io.ObjectInputStream in){
      try {
      //将读取的字符串反转后赋给name field
this.name=((StringBuffer)in.readObject()).reverse().toString();
//this.age=in.readInt();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
      
     }



@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
     
     
}



实现三:

Java的序列化机制保证在序列化某个对象之前,先调用该对象的writeReplace()方法,如果该方法返回另一个对象

,则系统转化为序列化另一个Java对象。


/**
 *功能的描述:
 *java的序列化机制保证在序列化某个对象之前,先调用该该对象的
 *writeReplace()方法,如果该方法返回另一个java对象,则系统
 *转化为序列化另一个对象,此程序表面上是序列化Person对象,实际
 *上是序列化ArrayList对象。
 * 
 * ***/
public class Teacher implements java.io.Serializable {
     private String name;
     private int age;
     private char sex;
public Teacher(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
     

private Object writeReplace() throws ObjectStreamException
{
ArrayList<Object>list=new ArrayList<Object>();
list.add(name);
list.add(age);
return list;
}


}


测试代码:

public class testWriteReplace {
    
public static void main(String[] args) {
//创建File对象
File file=new File("D:\\3.txt");
try {
//创建一个ObjectOutPutStream输出流
ObjectOutputStream oos=new ObjectOutputStream
(new FileOutputStream(file));
Teacher t1=new Teacher("徐才厚", 19);
//序列化teacher对象
oos.writeObject(t1);

ObjectInputStream ois=new ObjectInputStream
(new FileInputStream(file));
ArrayList<Object>array=(ArrayList<Object>) ois.readObject();
System.out.println(array);

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


与writeReplace()方法相对的是readResolve()方法

这个方法紧接着readObject()之后被调用,该方法的返回值会代替原来反序列化的对象,而原来readobject()反序列化的对象将会被立即丢弃。readResolve()在序列化单例类,枚举类时,有用。

这个的意思是我们在序列化和反序列单例时,应该得到得是同一份对象。但是如果不重写readResolve()方法,readObject()方法会返回一个不同的对象,此时在执行readResolve()时,此时重写readresolve()


会返回重写后的一个对象,原有对象丢弃。

public class Orientation implements Serializable{ 
      public static final Orientation HORIZONTAL=new Orientation(1); 
      public static final Orientation VERTICAL=new Orientation(2); 
      private int value; 
 public Orientation(int value) { 
this.value = value; 
 } 
  
 /***** 
         功能测试:readResolve()方法会在readObject()之后被调用 
         该方法的返回值将会代替原来的反序列化的对象,而原来readObject() 

        反序列化的对象将会立即丢弃。它在序列化单例类和枚举类时尤其有用。

        这只在把数据往磁盘中写时,起作用。

  * *******/ 
 private Object readResolve()throws ObjectStreamException{ 
 if(value==1){ 
 return HORIZONTAL; 
 } 
 if(value==2){ 
 return VERTICAL; 
 } 
 return null; 
 } 

  




public class testOrientation { 
public static void main(String[] args) throws Exception{
     
File file=new File("D:\\4.txt");
ObjectOutputStream oos=new 
ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(Orientation.HORIZONTAL);
ObjectInputStream ois=new 
ObjectInputStream(new FileInputStream(file));
Object obj=ois.readObject();
System.out.println(obj==Orientation.HORIZONTAL);        //打印true.

}

}