java对象的序列化

来源:互联网 发布:java毕业设计题目大全 编辑:程序博客网 时间:2024/05/16 15:27

序列化概念
对象序列化:将Java实体对象转化成二进制字节流的过程。
对象反序列化:将二进制字节流恢复为Java实体对象的过程。
主要用途
1. 持久化对象到磁盘
2. 网络传输
3. 对象的拷贝

序列化的实现

  1. 通过实现Serializable接口,java有默认的实现算法,我们也可以自定义序列化算法(用于实现一些特殊的需求),在反序列化时,不会调用该序列化的任何构造方法。
  2. 通过实现Externalizable接口,需要我们自己写序列化规则。

实现Serializable接口

package com.zd.test.java.ds;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class SerializableTest {    //首先我们用这个类来检验,探究一下序列化的算法。    static class Person implements Serializable{        /**         * 版本号         */        private static final long serialVersionUID = -1285437413613384110L;        String name;        Integer age;        transient String address;        Dog dog;        public Person(String name, Integer age,String address,Dog dog) {            super();            this.name = name;            this.age = age;            this.address = address;            this.dog = dog;        }        @Override        public String toString() {            return "Person [name=" + name + ", age=" + age + ", address="                    + address + ", dog=" + dog + "]";        }    }    static class Dog implements Serializable{        private static final long serialVersionUID = 5581802748286331206L;        String name;        public Dog(String name) {            super();            this.name = name;        }    }    //测试自定义的序列化方案    static class Man implements Serializable{        /**         * 版本号         */        private static final long serialVersionUID = -1285437413613384110L;        String name;        Integer age;        transient String address;        Dog dog;        transient String password;        public Man(String name, Integer age,String address,String password,Dog dog) {            super();            this.name = name;            this.age = age;            this.address = address;            this.dog = dog;            this.password = password;        }        @Override        public String toString() {            return "Man [name=" + name + ", age=" + age + ", address="                    + address + ", dog=" + dog+", password=" + password + "]";        }        //重写持久化规则        private void writeObject(ObjectOutputStream out) throws IOException{            out.defaultWriteObject(); //执行默认的写对象方法,会把这当前中非静态成员变量和非transient成员变量写到流中            //在这里,我们把address这个属性先转换成大写,再持久化            if (address != null) {                out.writeUTF(address.toUpperCase());            }            if (password != null) {                out.writeUTF(new StringBuffer(password).reverse().toString());            }            //注意这里无需关闭流,否则会失败,这只是一个普通的回调方法,java底层调用完此方法之后还会处理其他的逻辑。            //out.close();        }        private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException{            in.defaultReadObject();            Object obj = in.readUTF();            if (obj != null) {                address = obj.toString();            }            obj = in.readUTF();            if (obj != null) {                password = new StringBuffer(obj.toString()).reverse().toString();            }        }    }    public static void main(String[] args) throws ClassNotFoundException, IOException {        //1.测试一般用法,顺便测试一下transient关键字        Person person = new Person("张三", 23, "北京市",new Dog("旺旺"));        //如果person里面的Dog类没有实现Serializable接口,会报异常:java.io.NotSerializableException: com.zd.test.java.ds.SerializableTest$Dog        Person p = deepCopy(person);        System.out.println(p);        //结果:Person [name=张三, age=23, address=null]         //结论:被transient关键字修饰的对象默认是不能够持久化的,但是我们可以重写持久化规则。        //测试重写测试化规则,持久化被transient修饰的成员变量,加密重要隐私信息        //需要按自定义方式序列化的成员变量,是否定义为transient类型无关紧要。        Man man = new Man("张三", 23, "北京市", "admin",new Dog("旺旺"));        Man m = deepCopy(man);        System.out.println(m);    }    public static <T> T deepCopy(T t) throws IOException, ClassNotFoundException{        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);        objectOutputStream.writeObject(t);        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);        @SuppressWarnings("unchecked")        T clone = (T) objectInputStream.readObject();        return clone;    }}

实现Externalizable接口

package com.zd.test.java.ds;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.Externalizable;import java.io.IOException;import java.io.ObjectInput;import java.io.ObjectInputStream;import java.io.ObjectOutput;import java.io.ObjectOutputStream;public class ExternalizableTest {    static class Person implements Externalizable{        String name;        transient String password;        Integer age;        @Override        public void writeExternal(ObjectOutput out) throws IOException {            if (name != null) {                out.writeUTF(name);            }            if (password != null) {                out.writeUTF(new StringBuffer(password).reverse().toString());            }            if (age != null) {                out.writeInt(age);            }        }        @Override        public void readExternal(ObjectInput in) throws IOException,                ClassNotFoundException {            Object obj = null;            if ((obj = in.readUTF()) != null) {                name = obj.toString();            }            if ((obj = in.readUTF()) != null) {                password = new StringBuffer(obj.toString()).reverse().toString();            }            if ((obj = in.readInt()) != null) {                age = (Integer) obj;            }        }        public Person(String name, String password, Integer age) {            super();            this.name = name;            this.password = password;            this.age = age;        }        @Override        public String toString() {            return "Person [name=" + name + ", password=" + password + ", age="                    + age + "]";        }        //java.io.InvalidClassException: com.zd.test.java.ds.ExternalizableTest$Person; no valid constructor        public Person() { //给一个无参的构造器        }    }    public static void main(String[] args) throws Exception{        Person person = new Person("张三", "admin", 23);        Person person2 = deepCopy(person);        System.out.println(person2);    }    public static <T> T deepCopy(T t) throws IOException, ClassNotFoundException{        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);        objectOutputStream.writeObject(t);        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);        @SuppressWarnings("unchecked")        T clone = (T) objectInputStream.readObject();        return clone;    }}

补充:
默认序列化方式的不足

  1. 不宜对对象中对安全要求比较高的敏感数据进行序列化,安全性极低。
  2. 不会检查对象的成员变量是否合乎正确的约束条件。
  3. 默认的序列化方式需要对对象图(继承等关系)进行递归遍历,如果对象图很复杂,会消耗很多空间和时间,甚至引起Java虚拟机堆栈溢出。

总结
一般我们需要掌握的自定义序列化方式:
1. 实现Serializable接口,并且提供private的writeObject()和readObject()。
2. 实现Externalizable接口,实现writeExternal()和readExternal()方法,必须提供public无参的构造方法,否则会报异常,上面案例有涉及到。
3. 关于默认的序列化方案和自定义序列化方案,根据需求而定。

另外关于serialVersionUID,主要是为了兼容序列化类的变化,便于项目后期对序列化类的扩充和修改。

当然,还有很多的序列化方案,比如我们在java业务中前台和后台使用的json形式就是序列化的一种,它有着可以跨语言的优势,做过JavaEE的都知道怎么使用。

更多的序列化方案可以可以下https://github.com/eishay/jvm-serializers/wiki里面的,里面有详细比较。
这里写图片描述

0 0