zoukankan      html  css  js  c++  java
  • 对象序列化(二)

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

    自定义序列化

    在一些情况下,如果某个类的一些属性不希望被序列化,或者没有实现Serializable接口又不希望在序列化时报错,可以在属性前面加上transient关键字,Java程序在序列化时会忽略该属性

    代码1-1

    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 Book implements Serializable {
    	private String name;
    	private transient int num;
    
    	public Book(String name, int num) {
    		super();
    		this.name = name;
    		this.num = num;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public int getNum() {
    		return num;
    	}
    
    	@Override
    	public String toString() {
    		return "Book [name=" + name + ", num=" + num + "]";
    	}
    
    }
    
    public class TransientTest {
    
    	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]));
    			Book book = new Book("红楼梦", 50);
    			System.out.println(book);
    			oos.writeObject(book);
    			book = (Book) ois.readObject();
    			System.out.println(book);
    		} 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-1运行结果:

    root@lejian:/home/software/.io# touch object
    root@lejian:/home/software/.io# java TransientTest object 
    Book [name=红楼梦, num=50]
    Book [name=红楼梦, num=0]
    

     如代码1-1运行结果所示,将age标记为transient,在序列化时则可忽略该变量的值,所以在反序列化时,age的值为0

    Java还提供了另一种自定义序列化机制,通过这种自定义序列化机制可以让程序控制如何序列化各属性,甚至完全不序列化某些属性。在序列化和反序列化过程中,需要特殊处理的类应该用如下特殊签名的方法,这些特殊的方法用以自定义序列化:

    • private void writeObject(java.io.ObjectOutputStream out) throws IOException
    • private void readObject(java.io.ObjectInputStream in) throws ClassNotFoundException, IOException

    以上方法的访问权限必须为private,否则序列化和反序列化时不会调用如上方法

    writeObject()方法负责写入特定类的实例状态,以便相应的readObject()方法可以恢复它。通过重写该方法,程序员可以完全获得对序列化机制的控制,程序员可以自主决定哪些属性需要序列化,或者在序列化前对一些字段做操作后再进行序列化

    readObject()方法负责从流中读取并恢复对象属性,通过重写该方法,程序员可以完全获得对反序列化机制的控制,可以自主决定哪些属性可以反序列化,以及反序列化后可以再进行其他操作

    代码1-2

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    public class Student implements Serializable {
    
    	private String name;
    	private transient int age;
    
    	public Student(String name, int age) {
    		super();
    		System.out.println("构造Student对象");
    		this.name = name;
    		this.age = age;
    	}
    
    	private void writeObject(ObjectOutputStream out) throws IOException {
    		out.writeObject(new StringBuffer(name).reverse());
    		out.writeInt(age);
    	}
    
    	private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException {
    		this.name = in.readObject().toString();
    		this.age = in.readInt() + 5;
    	}
    
    	@Override
    	public String toString() {
    		return "Student [name=" + name + ", age=" + age + "]";
    	}
    
    	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("Amy", 18);
    			System.out.println(student);
    			oos.writeObject(student);
    			student = (Student) ois.readObject();
    			System.out.println(student);
    		} 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-2运行结果:

    root@lejian:/home/software/.io# java Student student 
    构造Student对象
    Student [name=Amy, age=18]
    Student [name=ymA, age=23]
    

     如代码1-2,在序列化student对象时,对name的值进行逆序再序列化,虽然代码中对age变量做了transient的标记,但还是在序列化该对象时把age序列化到流中,而在反序列化时又对age加5,所以student对象在序列化前后,字段的属性不再一致

    writeReplace()方法可以在序列化对象时将该对象替换成其他对象,该方法可以设为private、protected或者包私有等访问权限,代码1-3展示了在序列化teacher对象时替换成List对象

    代码1-3

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.ObjectStreamException;
    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.List;
    
    public class Teacher implements Serializable {
    
    	private String name;
    	private int age;
    
    	public Teacher(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    
    	@Override
    	public String toString() {
    		return "Teacher [name=" + name + ", age=" + age + "]";
    	}
    
    	private Object writeReplace() throws ObjectStreamException {
    		List<Object> list = new ArrayList<Object>();
    		list.add("name:" + name);
    		list.add("age:" + age);
    		return list;
    	}
    
    	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]));
    			Teacher teacher = new Teacher("王老师", 35);
    			System.out.println(teacher);
    			oos.writeObject(teacher);
    			Object obj = ois.readObject();
    			System.out.println(obj.getClass());
    			System.out.println(obj);
    		} 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-3运行结果:

    root@lejian:/home/software/.io# java Teacher teacher 
    Teacher [name=王老师, age=35]
    class java.util.ArrayList
    [name:王老师, age:35]
    

    程序调用被序列化对象的writeReplace()方法时,如果该方法返回另一个对象,系统将再次调用另一个对象的writeReplace()方法,直到该方法不再返回另一个对象为止

    readResolve()方法在序列化单例类、枚举类尤其有用,如果使用Java5提供的enum来定义枚举类则不用担心,如代码1-4就是一个枚举类

    代码1-4

    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    public class Orientation implements Serializable {
    
    	public static final Orientation HORIZONTAL = new Orientation(1);
    	public static final Orientation VERTICAL = new Orientation(2);
    
    	private int value;
    
    	private Orientation(int value) {
    		super();
    		this.value = value;
    	}
    
    	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]));
    			oos.writeObject(HORIZONTAL);
    			Orientation obj = (Orientation) ois.readObject();
    			System.out.println(obj == HORIZONTAL);
    		} 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-4运行结果:

    root@lejian:/home/software/.io# java Orientation object 
    false
    

     运行代码1-4可以发现,反序列化后的Orientation对象与HORIZONTAL并不是同一个对象,虽然Orientation的构造器是private的,但反序列化依旧可以创建Orientation对象,在这种情况下,可以添加一个readResolve()方法来解决问题,readResolve()方法的返回值会替代原来反序列化的对象,如代码1-5

    代码1-5

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

     与writeReplace()方法类似,readResolve()方法可以使用任意的访问控制符,因此父类的readResolve()可能被子类重写,利用readResolve()方法会有一个缺点,就是如果父类已经实现了一个public或者protected的readResolve()方法,而子类没有重写,将会在反序列化时得到父类对象,这显然不是程序想要的结果,也很难排查这样的问题,总是让子类重写readResolve()方法也是一种负担,通常建议是对于final类重写readResolve()方法不会有任何问题,或者重写readResolve()方法应该尽量使用private修饰

  • 相关阅读:
    Python 面向对象
    Python OS 文件
    Python File(文件) 方法
    Python 输入和输出
    Python 的__name__属性
    Python 数据结构
    Docker用途 & 和tomcat的区别
    sql 聚合函数和group by 联合使用
    SQL UNIQUE 约束
    MySQL中如何实现select top n ----Limit
  • 原文地址:https://www.cnblogs.com/baoliyan/p/6236180.html
Copyright © 2011-2022 走看看