Java IO 對象序列化

一、對象序列化是什麼?

一個對象產生以後其實是在內存中爲其開闢了一個存儲空間,方便存儲信息。java

定義可序列化的類:

import java.io.Serializable ;
public class Person implements Serializable{
	private String name ;	// 聲明name屬性
	private int age ;		// 聲明age屬性
	public Person(String name,int age){	// 經過構造設置內容
		this.name = name ;
		this.age = age ;
	}
	public String toString(){	// 覆寫toString()方法
		return "姓名:" + this.name + ";年齡:" + this.age ;
	}
}

之後此類的對象,就能夠被序列化了。變爲二進制byte流。數組

對象的序列化和反序列化:

serialVersionUID

import java.io.Serializable ;
public class Person implements Serializable{
	private static final long serialVersionUID = 1L;/*驗證版本的一致性*/
	private String name ;	// 聲明name屬性
	private int age ;		// 聲明age屬性
	public Person(String name,int age){	// 經過構造設置內容
		this.name = name ;
		this.age = age ;
	}
	public String toString(){	// 覆寫toString()方法
		return "姓名:" + this.name + ";年齡:" + this.age ;
	}
}

若是使用開發工具開發,沒有編寫此代碼,則會出現一些安全警告信息。安全

二、對象的序列化:ObjectOutputStream

import java.io.File ;
import java.io.FileOutputStream ;
import java.io.OutputStream ;
import java.io.ObjectOutputStream ;
public class SerDemo01{
	public static void main(String args[]) throws Exception {
		File f = new File("D:" + File.separator + "test.txt") ;	// 定義保存路徑
		ObjectOutputStream oos = null ;	// 聲明對象輸出流
		OutputStream out = new FileOutputStream(f) ;	// 文件輸出流
		oos = new ObjectOutputStream(out) ;
		oos.writeObject(new Person("張三",30)) ;	// 保存對象
		oos.close() ;	// 關閉
	}
}

全部的對象擁有各自的屬性值,可是全部的方法都是公共的,因此序列化對象的時候實際上序列化的就是屬性。ide

三、對象的反序列化:ObjectInputStream

import java.io.File ;
import java.io.FileInputStream ;
import java.io.InputStream ;
import java.io.ObjectInputStream ;
public class SerDemo02{
	public static void main(String args[]) throws Exception {
		File f = new File("D:" + File.separator + "test.txt") ;	// 定義保存路徑
		ObjectInputStream ois = null ;	// 聲明對象輸入流
		InputStream input = new FileInputStream(f) ;	// 文件輸入流
		ois = new ObjectInputStream(input) ;	// 實例化對象輸入流
		Object obj = ois.readObject() ;	// 讀取對象
		ois.close() ;	// 關閉
		System.out.println(obj) ;
	}
}

四、transient關鍵字

import java.io.Serializable ;工具

public class Person implements Serializable{
	private static final long serialVersionUID = 1L;
	private transient String name ;	// 聲明name屬性,可是此屬性不被序列化
	private int age ;		// 聲明age屬性
	public Person(String name,int age){	// 經過構造設置內容
		this.name = name ;
		this.age = age ;
	}
	public String toString(){	// 覆寫toString()方法
		return "姓名:" + this.name + ";年齡:" + this.age ;
	}
}
操做代碼:
import java.io.File ;
import java.io.IOException ;
import java.io.FileOutputStream ;
import java.io.OutputStream ;
import java.io.ObjectOutputStream ;
import java.io.FileInputStream ;
import java.io.InputStream ;
import java.io.ObjectInputStream ;
public class SerDemo04{
    public static void main(String args[]) throws Exception{
        ser() ;
        dser() ;
    }
    public static void ser() throws Exception {
        File f = new File("D:" + File.separator + "test.txt") ;    // 定義保存路徑
        ObjectOutputStream oos = null ;    // 聲明對象輸出流
        OutputStream out = new FileOutputStream(f) ;   // 文件輸出流
        oos = new ObjectOutputStream(out) ;
        oos.writeObject(new Person("張三",30)) ; // 保存對象
        oos.close() ;  // 關閉
    }
    public static void dser() throws Exception {
        File f = new File("D:" + File.separator + "test.txt") ;    // 定義保存路徑
        ObjectInputStream ois = null ; // 聲明對象輸入流
        InputStream input = new FileInputStream(f) ;   // 文件輸入流
        ois = new ObjectInputStream(input) ;   // 實例化對象輸入流
        Object obj = ois.readObject() ;    // 讀取對象
        ois.close() ;  // 關閉
        System.out.println(obj) ;
    }
}

1. transient使用小結

1)一旦變量被transient修飾,變量將再也不是對象持久化的一部分,該變量內容在序列化後沒法得到訪問。開發工具

2)transient關鍵字只能修飾變量,而不能修飾方法和類。注意,本地變量是不能被transient關鍵字修飾的。變量若是是用戶自定義類變量,則該類須要實現Serializable接口。this

3)被transient關鍵字修飾的變量再也不能被序列化,一個靜態變量無論是否被transient修飾,均不能被序列化。spa

第三點可能有些人很迷惑,由於發如今User類中的username字段前加上static關鍵字後,程序運行結果依然不變,即static類型的username也讀出來爲「Alexia」了,這不與第三點說的矛盾嗎?其實是這樣的:第三點確實沒錯(一個靜態變量無論是否被transient修飾,均不能被序列化),反序列化後類中static型變量username的值爲當前JVM中對應static變量的值,這個值是JVM中的不是反序列化得出的,不相信?好吧,下面我來證實:對象

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
publicclass TransientTest {
    
    public static void main(String[] args) {
        
        User user = new User();
        user.setUsername("Alexia");
        user.setPasswd("123456");
        
        System.out.println("read before Serializable: ");
        System.out.println("username: " + user.getUsername());
        System.err.println("password: " + user.getPasswd());
        
        try {
            ObjectOutputStream os = new ObjectOutputStream(
                    new FileOutputStream("C:/user.txt"));
            os.writeObject(user); // 將User對象寫進文件            
            os.flush();
            os.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            // 在反序列化以前改變username的值
           User.username = "jmwang";
            
            ObjectInputStream is = new ObjectInputStream(new FileInputStream(
                    "C:/user.txt"));
            user = (User) is.readObject(); // 從流中讀取User的數據            
            is.close();            
            System.out.println("\nread after Serializable: ");
            System.out.println("username: " + user.getUsername());
            System.err.println("password: " + user.getPasswd());            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

class User implements Serializable {
    private static final long serialVersionUID = 8294180014912103005L;  
    
    public static String username;
    private transient String passwd;
    
    public String getUsername() {
        return username;
    }
    
    publicvoid setUsername(String username) {
        this.username = username;
    }
    
    public String getPasswd() {
        return passwd;
    }
    
    publicvoid setPasswd(String passwd) {
        this.passwd = passwd;
    }
}

運行結果爲:接口

read before Serializable: 
username: Alexia
password: 123456

read after Serializable: 
username: jmwang
password: null

這說明反序列化後類中static型變量username的值爲當前JVM中對應static變量的值,爲修改後jmwang,而不是序列化時的值Alexia。

2. transient使用細節——被transient關鍵字修飾的變量真的不能被序列化嗎?

思考下面的例子:

import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
publicclass ExternalizableTest implements Externalizable {

    private transient String content = "是的,我將會被序列化,無論我是否被transient關鍵字修飾";

    @Override
    publicvoid writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(content);
    }

    @Override
    publicvoid readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
        content = (String) in.readObject();
    }

    publicstaticvoid main(String[] args) throws Exception {
        
        ExternalizableTest et = new ExternalizableTest();
        ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
                new File("test")));
        out.writeObject(et);

        ObjectInput in = new ObjectInputStream(new FileInputStream(new File("test")));
        et = (ExternalizableTest) in.readObject();
        System.out.println(et.content);

        out.close();
        in.close();
    }
}

content變量會被序列化嗎?好吧,我把答案都輸出來了,是的,運行結果就是:

是的,我將會被序列化,無論我是否被transient關鍵字修飾

這是爲何呢,不是說類的變量被transient關鍵字修飾之後將不能序列化了嗎?

咱們知道在Java中,對象的序列化能夠經過實現兩種接口來實現,若實現的是Serializable接口,則全部的序列化將會自動進行,若實現的是Externalizable接口,則沒有任何東西能夠自動序列化,須要在writeExternal方法中進行手工指定所要序列化的變量,這與是否被transient修飾無關。所以第二個例子輸出的是變量content初始化的內容,而不是null。

五、序列化一組對象

若是要保存多個對象,則最好使用對象數組的形式完成。

import java.io.File ;
import java.io.IOException ;
import java.io.FileOutputStream ;
import java.io.OutputStream ;
import java.io.ObjectOutputStream ;
import java.io.FileInputStream ;
import java.io.InputStream ;
import java.io.ObjectInputStream ;
public class SerDemo05{
	public static void main(String args[]) throws Exception{
		Person per[] = {new Person("張三",30),new Person("李四",31),
			new Person("王五",32)} ;
		ser(per) ;
		Object o[] = (Object[])dser() ;
		for(int i=0;i<o.length;i++){
			Person p = (Person)o[i] ;
			System.out.println(p) ;
		}
	}
	public static void ser(Object obj[]) throws Exception {
		File f = new File("D:" + File.separator + "test.txt") ;	// 定義保存路徑
		ObjectOutputStream oos = null ;	// 聲明對象輸出流
		OutputStream out = new FileOutputStream(f) ;	// 文件輸出流
		oos = new ObjectOutputStream(out) ;
		oos.writeObject(obj) ;	// 保存對象
		oos.close() ;	// 關閉
	}
	public static Object[] dser() throws Exception {
		File f = new File("D:" + File.separator + "test.txt") ;	// 定義保存路徑
		ObjectInputStream ois = null ;	// 聲明對象輸入流
		InputStream input = new FileInputStream(f) ;	// 文件輸入流
		ois = new ObjectInputStream(input) ;	// 實例化對象輸入流
		Object obj[] = (Object[])ois.readObject() ;	// 讀取對象
		ois.close() ;	// 關閉
		return obj ;
	}
}
相關文章
相關標籤/搜索