java序列化

java序列化本身存在的問題

1.  序列化數據結果比較大、傳輸效率比較低
2.  不能跨語言對接


序列化和反序列化的概念
把對象轉化爲字節序列的過程稱之爲對象的序列化
反之,稱之爲反序列化

怎麼去實現一個序列化操作
1. 實現Serializable接口
2. ObjectInputStream  : 表示讀取指定的字節數據轉換成對象
3. ObjectOutputStream 

上代碼:

環境   idea   jdk1.8  maven

生成一個類的serialVersionUID,idea提供快捷鍵


這樣alt鍵+單擊 就可以創建serialVersionUID了

也可以自己手動指定UID


Person對象,之後序列化的例子都會使用

public class Person implements Serializable {

    private static final long serialVersionUID = -7290089721196747652L;

    private String name;

    private int 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;
    }

   

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age 
        
                '}';
    }
}

public class SerializeDemo {
    public static void main(String [] args){

        //序列化操作
        SerializePerson();

        //反序列化操作
        DeSerializePerson();

    }


    public static void SerializePerson(){
        try {
            ObjectOutputStream oo=new ObjectOutputStream(new FileOutputStream(new File("person")));
            Person person=new Person();
            person.setName("c羅");
            person.setAge(32);
            oo.writeObject(person);
            oo.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void DeSerializePerson(){
        try {
            ObjectInputStream oi=new ObjectInputStream(new FileInputStream(new File("person")));
            Person person = (Person)oi.readObject();
            System.out.println(person);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
序列化生成的二進制數


序列化時類指定UID,反序列化時類沒有指定UID(系統會給你自動指定一個UID),兩個UID不一致,會報錯

簡單來說serialVersionUID可以保證序列化和反序列化對象一致

靜態變量的序列化
序列化並不保存靜態變量的狀態


在剛纔的Person對象中增加一個靜態變量字段height

public class Person implements Serializable {

    private static final long serialVersionUID = -7290089721196747652L;

    public static int height = 178;

public static void main(String [] args){

    //序列化操作
    SerializePerson();

    Person.height=180;

    //反序列化操作
    Person person=DeSerializePerson();
    System.out.println(person.height);

}

結果打印的不是序列化對象的height=178

也就是說,靜態變量不能被序列化


Transient關鍵字
transient關鍵字表示指定屬性不參與序列化

在Person類中加一個變量transient修飾的sex
private transient String sex;
public class SerializeDemo3 {
    public static void main(String [] args){

        //序列化操作
        SerializePerson();

        //反序列化操作
        Person person=DeSerializePerson();
        System.out.println(person);

    }


    public static void SerializePerson(){
        try {
            ObjectOutputStream oo=new ObjectOutputStream(new FileOutputStream(new File("person")));  //默認在根目錄
            Person person=new Person();
            person.setName("c羅");
            person.setAge(32);
            person.setSex("man");
            oo.writeObject(person);
            oo.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Person DeSerializePerson(){
        try {
            ObjectInputStream oi=new ObjectInputStream(new FileInputStream(new File("person")));
            Person person = (Person)oi.readObject();
            return person;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

transient修飾的sex不參與序列化


父子類問題
如果父類沒有實現序列化,而子類實現列序列化。那麼父類中的成員沒辦法做序列化操作

public class SuperUser {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "SuperUser{" +
                "name='" + name + '\'' +
                '}';
    }
}

public class User extends SuperUser implements Serializable {
    private static final long serialVersionUID = -1290089721196747652L;

    @Override
    public String toString() {
        return "User{} " + super.toString();
    }
}

public class MyTest {
    public static void main(String [] args){

        //序列化操作
        SerializeUser();

        //反序列化操作
        User user=DeSerializeUser();
        System.out.println(user);

    }


    public static void SerializeUser(){
        try {
            ObjectOutputStream oo=new ObjectOutputStream(new FileOutputStream(new File("user")));  //默認在根目錄
            User user=new User();
            user.setName("c羅");

            oo.writeObject(user);
            oo.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static User DeSerializeUser(){
        try {
            ObjectInputStream oi=new ObjectInputStream(new FileInputStream(new File("user")));
            User user = (User)oi.readObject();
            return user;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

}

除非把父類也實現Serializable

public class SuperUser implements Serializable{


序列化的存儲規則

public class SerializeDemo4 {
    public static void main(String [] args){

        //序列化操作
        SerializePerson();

        //反序列化操作
        Person person=DeSerializePerson();
        System.out.println(person);

    }


    public static void SerializePerson(){
        try {
            ObjectOutputStream oo=new ObjectOutputStream(new FileOutputStream(new File("person")));  //默認在根目錄
            ObjectInputStream oi=new ObjectInputStream(new FileInputStream(new File("person")));
            Person person=new Person();
            person.setName("c羅");
            person.setAge(32);
            person.setSex("man");
            oo.writeObject(person);
            oo.flush();
            System.out.println("第一次序列化成功,長度爲"+new File("person").length());
            Person person1=(Person)oi.readObject();
            oo.writeObject(person);
            oo.flush();
            System.out.println("第二次序列化成功,長度爲"+new File("person").length());
            Person person2=(Person)oi.readObject();
            oo.close();




            System.out.println(person1==person2);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Person DeSerializePerson(){
        try {
            ObjectInputStream oi=new ObjectInputStream(new FileInputStream(new File("person")));
            Person person = (Person)oi.readObject();
            return person;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

對同一個對象進行多次寫入,打印出的第一次存儲結果和第二次存儲結果,只多了5個字節的引用關係
並不會導致文件累加


序列化實現深度克隆
簡單來說, 淺克隆複製對象,不復制對象的引用;深克隆複製對象,也複製對象的引用;

public class Student implements Serializable {

    private static final long serialVersionUID = -3330089721196747652L;

    private String name;
    private int age;

    private Teacher teacher;

    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 Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    //序列化的方式實現深度克隆
    public Object deepClone() throws  Exception{

        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bos);
        oos.writeObject(this);

        ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois=new ObjectInputStream(bis);

        return ois.readObject();

    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", teacher=" + teacher +
                '}';
    }
}
public class Teacher implements Serializable {

    private static final long serialVersionUID = -4330089021196747652L;

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                '}';
    }
}
public class cloneDemo {
    public static void main(String []args)throws  Exception{
        Teacher teacher = new Teacher();
        teacher.setName("a");

        Student student=new Student();
        student.setName("zhangsan");
        student.setAge(18);
        student.setTeacher(teacher);

        System.out.println(student);

        Student s = (Student)student.deepClone();
        System.out.println(s);

        System.out.println("student==s : "+(student==s));   //返回false表示不是指向同一個對象,即深度克隆
        System.out.println("student.equals(s) : "+(student.equals(s)));

        //修改s的老師的引用對象的屬性值
        s.getTeacher().setName("b");
        System.out.println(student);
        System.out.println(s);

    }


}

小結
1. java中,只要一個類實現了java.io.Serializable接口,那麼它就可以被序列化
2. 通過ObjectOutputStream和ObjectInputStream對對象進行序列化合反序列化操作
3.  對象是否允許被反序列化,不僅僅是取決於對象的代碼是否一致,同時還有一個重要的因素(UID)
4.  序列化不保存靜態變量
5.  要想父類對象也參與序列化操作,那麼必須要讓父類也實現Serializable接口
6.  Transient關鍵字,主要是控制變量是否能夠被序列化。如果沒有被序列化的成員變量反序列化後,會被設置成初始值,比如String -> null
7.  通過序列化操作實現深度克隆

主流的序列化技術

序列化技術所需依賴
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>pers.wmx.demo</groupId>
    <artifactId>Serialize</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>

        <!-- spring默認的-->
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.31</version>
        </dependency>

        <!--  百度封裝的 -->
        <dependency>
            <groupId>com.baidu</groupId>
            <artifactId>jprotobuf</artifactId>
            <version>2.1.2</version>
        </dependency>

        <dependency>
            <groupId>com.caucho</groupId>
            <artifactId>hessian</artifactId>
            <version>4.0.38</version>
        </dependency>

    </dependencies>

</project>

上代碼

public class JsonDemo {

    private static Person init(){
        Person person = new Person();
        person.setName("梅西");
        person.setAge(30);
        return person;

    }

    public static void main(String []args)throws Exception{

        System.out.println("JackSon序列化...");
        excuteWithJack();

        System.out.println("FastJson序列化...");
        excuteWithFastJson();

        System.out.println("Protobuf序列化...");
        excuteWithProtobuf();

        System.out.println("Hessian序列化...");
        excuteWithHessian();

    }

    //使用jackson序列化
    private static void excuteWithJack() throws Exception{
        Person person=init();

        ObjectMapper mapper = new ObjectMapper();

        byte [] writeBytes=null;

        Long start=System.currentTimeMillis();
        for(int i=0;i<10000;i++){
            writeBytes=mapper.writeValueAsBytes(person);
        }

        System.out.println("序列化用時"+(System.currentTimeMillis()-start)+"ms");
        System.out.println(writeBytes.length);

        Person person1 = mapper.readValue(writeBytes,Person.class);
        System.out.println(person1);
    }


    //使用fastjson序列化
    private static void excuteWithFastJson() throws Exception{
        Person person=init();
        String text=null;


        Long start=System.currentTimeMillis();
        for(int i=0;i<10000;i++){
            text=JSON.toJSONString(person);
        }

        System.out.println("序列化用時"+(System.currentTimeMillis()-start)+"ms");
        System.out.println("text "+text);
        System.out.println(text.getBytes().length);

        Person person1 = JSON.parseObject(text,Person.class);
        System.out.println("person1 "+person1);

    }

    //使用Protobuf序列化     性能高    字節數小,適合網絡傳輸
    private static void excuteWithProtobuf() throws Exception{
        Person person=init();

        Codec<Person> personCodec =ProtobufProxy.create(Person.class,false);

        Long start=System.currentTimeMillis();
        byte[] bytes=null;
        for (int i = 0; i <10000 ; i++) {
            bytes=personCodec.encode(person);
        }

        System.out.println("序列化用時"+(System.currentTimeMillis()-start)+"ms");
        System.out.println(bytes.length);

        Person person1 = personCodec.decode(bytes);
        System.out.println(person1);
    }


    //使用Hessian序列化
    private static void excuteWithHessian() throws Exception{
        Person person=init();

        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        HessianOutput ho=new HessianOutput(baos);

        Long start=System.currentTimeMillis();

        for (int i = 0; i <10000 ; i++) {
            ho.writeObject(person);
        }

        System.out.println("序列化用時"+(System.currentTimeMillis()-start)+"ms");
        System.out.println(baos.toByteArray().length);

        ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
        HessianInput hi=new HessianInput(bais);
        Person person1 = (Person)hi.readObject();
        System.out.println(person1);
    }
}

發佈了39 篇原創文章 · 獲贊 8 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章