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 ;
	}
}
相关文章
相关标签/搜索