• 对象序列化(一)


    本文是基于Linux环境运行,读者阅读前需要具备一定Linux知识

    对象序列化

    对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,通过网络将这种二进制流传输到另一个网络节点,其他程序一旦获取到这种二进制流,都可以将这种二进制流恢复成原来的Java对象

    对象序列化是将一个Java对象写入IO流,与此对应的是,对象反序列化是从IO流中恢复Java对象,如果需要让某个对象支持序列化机制,必须让其实现如下两个接口:

    • Serializable
    • Externalizable

    代码1-1中,声明一个Person对象,Person对象实现了Serializable接口,将对象序列化到磁盘文件

    代码1-1

    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    class Person implements Serializable {
    
    	private String name;
    	private int age;
    
    	public Person() {
    		super();
    		System.out.println("Person()构造方法");
    	}
    
    	public Person(String name, int age) {
    		super();
    		System.out.println("Person(String name, int age)构造方法");
    		this.name = name;
    		this.age = age;
    	}
    
    	@Override
    	public String toString() {
    		return "Person [name=" + name + ", age=" + age + "]";
    	}
    
    }
    
    public class WriteObject {
    
    	public static void main(String[] args) {
    		if (args == null || args.length == 0) {
    			throw new RuntimeException("请输入对象序列化路径");
    		}
    		Person p = new Person("小红", 10);
    		ObjectOutputStream oos = null;
    		try {
    			oos = new ObjectOutputStream(new FileOutputStream(args[0]));
    			oos.writeObject(p);
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} finally {
    			try {
    				if (oos != null) {
    					oos.close();
    				}
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    
    }
    

    代码1-2位从磁盘文件读取Person对象

    代码1-2

    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    
    public class ReadObject {
    
    	public static void main(String[] args) {
    		if (args == null || args.length == 0) {
    			throw new RuntimeException("请输入对象序列化路径");
    		}
    		ObjectInputStream ois = null;
    		try {
    			ois = new ObjectInputStream(new FileInputStream(args[0]));
    			Person p = (Person) ois.readObject();
    			System.out.println(p);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} finally {
    			try {
    				if (ois != null) {
    					ois.close();
    				}
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    
    	}
    
    }
    

    代码1-1、代码1-2运行结果:

    root@lejian:/home/software/.io# touch object
    root@lejian:/home/software/.io# java WriteObject object 
    Person(String name, int age)构造方法
    root@lejian:/home/software/.io# java ReadObject object 
    Person [name=小红, age=10]
    

    需要指出一点的是:Person类虽然声明两个构造器,一个有参数,一个没有参数,但在反序列读取Java对象时,并没有看到程序调用Person任何一个构造器,这表明反序列化机制无须通过构造器来初始化Java对象

    对象引用的序列化

    如果某各类的属性类型不是基本类型或者String类型,且没有实现可序列化接口,则该类型属性类是不可序列化

    代码1-3

    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    class Teacher implements Serializable {
    	private String name;
    	private Student student;
    
    	public Teacher(String name, Student student) {
    		super();
    		this.name = name;
    		this.student = student;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public Student getStudent() {
    		return student;
    	}
    
    	@Override
    	public String toString() {
    		return "Teacher [name=" + name + ", student=" + student + "]";
    	}
    
    }
    
    class Student implements Serializable {
    	private String name;
    	private int age;
    
    	public Student(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    
    	@Override
    	public String toString() {
    		return "Student [name=" + name + ", age=" + age + "]";
    	}
    
    }
    
    public class WriteTeacher {
    
    	public static void main(String[] args) {
    		if (args == null || args.length == 0) {
    			throw new RuntimeException("请输入对象序列化路径");
    		}
    		ObjectOutputStream oos = null;
    		try {
    			oos = new ObjectOutputStream(new FileOutputStream(args[0]));
    			Student student = new Student("小明", 15);
    			Teacher teacher1 = new Teacher("王老师", student);
    			Teacher teacher2 = new Teacher("张老师", student);
    			System.out.println("student:" + student);
    			System.out.println("teacher1:" + teacher1);
    			System.out.println("teacher2:" + teacher2);
    			oos.writeObject(teacher1);
    			oos.writeObject(teacher2);
    			oos.writeObject(student);
    			oos.writeObject(teacher2);
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} finally {
    			try {
    				if (oos != null) {
    					oos.close();
    				}
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    
    }
    

    代码1-4

    import java.io.FileInputStream;
    import java.io.ObjectInputStream;
    
    public class ReadTeacher {
    
    	public static void main(String[] args) {
    		if (args == null || args.length == 0) {
    			throw new RuntimeException("请输入对象序列化路径");
    		}
    		ObjectInputStream ois = null;
    		try {
    			ois = new ObjectInputStream(new FileInputStream(args[0]));
    			Teacher teacher1 = (Teacher) ois.readObject();
    			System.out.println("teacher1:" + teacher1);
    			Teacher teacher2 = (Teacher) ois.readObject();
    			System.out.println("teacher2:" + teacher2);
    			Student student = (Student) ois.readObject();
    			System.out.println("student:" + student);
    			Teacher teacher3 = (Teacher) ois.readObject();
    			System.out.println("teacher3:" + teacher3);
    			System.out.println("teacher2 == teacher3:" + (teacher2 == teacher3));
    			System.out.println("teacher1.student == teacher2.student:" + (teacher1.getStudent() == teacher2.getStudent()));
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    
    }
    

    代码1-3、1-4运行结果:

    root@lejian:/home/software/.io# touch object
    root@lejian:/home/software/.io# java WriteTeacher object 
    student:Student [name=小明, age=15]
    teacher1:Teacher [name=王老师, student=Student [name=小明, age=15]]
    teacher2:Teacher [name=张老师, student=Student [name=小明, age=15]]
    root@lejian:/home/software/.io# java ReadTeacher object 
    teacher1:Teacher [name=王老师, student=Student [name=小明, age=15]]
    teacher2:Teacher [name=张老师, student=Student [name=小明, age=15]]
    student:Student [name=小明, age=15]
    teacher3:Teacher [name=张老师, student=Student [name=小明, age=15]]
    teacher2 == teacher3:true
    teacher1.student == teacher2.student:true
    

     当程序序列化一个Teacher对象时,如果该Teacher对象持有一个Student对象的引用,为了在反序列化时可以正常恢复该Teacher对象,程序会顺带序列化Teacher对象所引用的Student对象,如果Student对象非空且没有实现Serializable接口,则在序列化时会抛出NotSerializableException异常

    代码1-3中,程序先序列化teacher1,则会将teacher1所引用的student对象一起序列化,再序列化teacher2,同样会序列化teacher2所引用的student对象,最后再序列化student对象,这个过程中,程序似乎针对student对象序列化了三次,如果程序对student对象序列化了三次,那么在反序列化时,将会得到三个student对象,teacher1和teacher2所引用的student对象不是同一个,与最初的效果不一致,这将违背Java序列化机制的初衷

    为了解决这一问题,Java序列化机制采用了一种特殊的序列化算法,大致如下:

    1. 所有保存到磁盘中的对象都有一个序列化编号
    2. 当程序试图序列化一个对象时,程序会先检查该对象是否被序列化过,只有该对象从未在本次虚拟机中被序列化过,系统才会将该对象转换成字节序列并输出
    3. 如果某个对象被序列化过,程序将直接输出一个序列化编号,而不是重新序列化该对象

    由于Java序列化机制使然,如果多次序列化同一个Java对象时,只有第一次序列化才会把该Java对象转换成字节序列并输出,可能会引起一个潜在的问题,当对象的某些属性或状态修改后,再次序列化该对象,程序只是输出前面的序列化编号,而该对象被改变的值却不会同步到输出流中,如代码1-5

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    class Student implements Serializable {
    
    	private String name;
    	private int age;
    
    	public Student(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;
    	}
    
    }
    
    public class SerializeMutable {
    
    	public static void main(String[] args) {
    		if (args == null || args.length == 0) {
    			throw new RuntimeException("请输入对象序列化路径");
    		}
    		ObjectOutputStream oos = null;
    		ObjectInputStream ois = null;
    		try {
    			oos = new ObjectOutputStream(new FileOutputStream(args[0]));
    			ois = new ObjectInputStream(new FileInputStream(args[0]));
    			Student student = new Student("小明", 15);
    			oos.writeObject(student);
    			student.setName("小王");
    			oos.writeObject(student);
    			Student student1 = (Student) ois.readObject();
    			Student student2 = (Student) ois.readObject();
    			System.out.println("student1 == student2 : " + (student1 == student2));
    			System.out.println("student1.name = " + student1.getName());
    		} catch (Exception e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} finally {
    			try {
    				if (oos != null) {
    					oos.close();
    				}
    				if (ois != null) {
    					ois.close();
    				}
    			} catch (IOException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	}
    
    }
    

    代码1-5运行结果:

    root@lejian:/home/software/.io# touch object
    root@lejian:/home/software/.io# java SerializeMutable object 
    student1 == student2 : true
    student1.name = 小明
    
  • 相关阅读:
    python基础:映射和集合类型
    python基础:列表生成式和生成器
    python基础:名称空间与作用域
    http://www.cnblogs.com/fczjuever/archive/2013/04/05/3000680.html
    2016新年总结
    send()和recv()函数详解
    python基础:测量python代码的运行时间
    python函数与方法装饰器
    Python基础:11.2_函数调用
    MySQL学习笔记-数据库后台线程
  • 原文地址:https://www.cnblogs.com/baoliyan/p/6230990.html
Copyright © 2020-2023  润新知