Serializable

ObjectOutputStream(序列化)ObjectInputStream(反序列化)

serialVersionUID

字段不参与序列化

Externalizable

序列化的应用

ObjectOutputStream(序列化)ObjectInputStream(反序列化)

package com.datang.netty.serializabletest;

import java.io.Serializable;

public class Amimal{
    private String hobby;
    private String eat;
    private int age;
    public String getHobby() {
        return hobby;
    }
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
    public String getEat() {
        return eat;
    }
    public void setEat(String eat) {
        this.eat = eat;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
    }
    
    
}
View Code
package com.datang.netty.serializabletest;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Run {
    public static void main(String[] args)throws Exception {
        
        Amimal amimal = new Amimal();
        amimal.setAge(11);
        amimal.setEat("鸡蛋");
        amimal.setHobby("打游戏");
        
        
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\Users\bykj\Desktop\obj.txt"));
        oos.writeObject(amimal);
        
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\Users\bykj\Desktop\obj.txt"));
        Amimal a = (Amimal)ois.readObject();
        System.out.println(a);
    }
}
View Code

ObjectOutputStream将对象写入到本地文件,ObjectInputStream从本地文件中获取一个对象。这一对方法是需要实现Serializable接口的。以上代码中Animal并未实现Serializable所以会抛出一个java.io.NotSerializableException: com.datang.netty.serializabletest.Amimal异常。

Animal实现Aerializable后执行结果如下

Amimal [hobby=打游戏, eat=鸡蛋, age=11]

查看源码可以发现如果被写入本地的对象类型不是String,不是数组,不是Enum,也不是Serializable便会抛出异常。

 public final void writeObject(Object obj) throws IOException {
        if (enableOverride) {
            writeObjectOverride(obj);
            return;
        }
        try {
            writeObject0(obj, false);
        } catch (IOException ex) {
            if (depth == 0) {
                writeFatalException(ex);
            }
            throw ex;
        }
    }
View Code
 private void writeObject0(Object obj, boolean unshared)
        throws IOException
    {
        boolean oldMode = bout.setBlockDataMode(false);
        depth++;
        try {
            // handle previously written and non-replaceable objects
            int h;
            if ((obj = subs.lookup(obj)) == null) {
                writeNull();
                return;
            } else if (!unshared && (h = handles.lookup(obj)) != -1) {
                writeHandle(h);
                return;
            } else if (obj instanceof Class) {
                writeClass((Class) obj, unshared);
                return;
            } else if (obj instanceof ObjectStreamClass) {
                writeClassDesc((ObjectStreamClass) obj, unshared);
                return;
            }

            // check for replacement object
            Object orig = obj;
            Class<?> cl = obj.getClass();
            ObjectStreamClass desc;
            for (;;) {
                // REMIND: skip this check for strings/arrays?
                Class<?> repCl;
                desc = ObjectStreamClass.lookup(cl, true);
                if (!desc.hasWriteReplaceMethod() ||
                    (obj = desc.invokeWriteReplace(obj)) == null ||
                    (repCl = obj.getClass()) == cl)
                {
                    break;
                }
                cl = repCl;
            }
            if (enableReplace) {
                Object rep = replaceObject(obj);
                if (rep != obj && rep != null) {
                    cl = rep.getClass();
                    desc = ObjectStreamClass.lookup(cl, true);
                }
                obj = rep;
            }

            // if object replaced, run through original checks a second time
            if (obj != orig) {
                subs.assign(orig, obj);
                if (obj == null) {
                    writeNull();
                    return;
                } else if (!unshared && (h = handles.lookup(obj)) != -1) {
                    writeHandle(h);
                    return;
                } else if (obj instanceof Class) {
                    writeClass((Class) obj, unshared);
                    return;
                } else if (obj instanceof ObjectStreamClass) {
                    writeClassDesc((ObjectStreamClass) obj, unshared);
                    return;
                }
            }

            // remaining cases
            if (obj instanceof String) {
                writeString((String) obj, unshared);
            } else if (cl.isArray()) {
                writeArray(obj, desc, unshared);
            } else if (obj instanceof Enum) {
                writeEnum((Enum<?>) obj, desc, unshared);
            } else if (obj instanceof Serializable) {
                writeOrdinaryObject(obj, desc, unshared);
            } else {
                if (extendedDebugInfo) {
                    throw new NotSerializableException(
                        cl.getName() + "
" + debugInfoStack.toString());
                } else {
                    throw new NotSerializableException(cl.getName());
                }
            }
        } finally {
            depth--;
            bout.setBlockDataMode(oldMode);
        }
    }
View Code

serialVersionUID

在Eclipse编辑器或IDEA编辑器时实现Serializable接口编辑器会提示我们做额外的操作。

先看下有几种处理方案。

1 private static final long serialVersionUID = 1L;

2 private static final long serialVersionUID = 7643364732831133867L;

3 @SuppressWarnings("serial")

4 忽略它的提示,什么都不做。

第四种方案忽略提示什么都不做。执行Run后执行Run2此时结果是正确的。

package com.datang.netty.serializabletest;

import java.io.Serializable;

public class Amimal implements Serializable{
    private String hobby;
    private String eat;
    private int age;
    
    
    
    public String getHobby() {
        return hobby;
    }
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
    public String getEat() {
        return eat;
    }
    public void setEat(String eat) {
        this.eat = eat;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
    }
    
    
}
View Code
package com.datang.netty.serializabletest;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Run {
    public static void main(String[] args)throws Exception {
        
        Amimal amimal = new Amimal();
        amimal.setAge(11);
        amimal.setEat("鸡蛋");
        amimal.setHobby("打游戏");
        
        
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\Users\bykj\Desktop\obj.txt"));
        oos.writeObject(amimal);
        

    }
}
View Code
package com.datang.netty.serializabletest;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Run2 {
    public static void main(String[] args)throws Exception {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\Users\bykj\Desktop\obj.txt"));
        Amimal a = (Amimal)ois.readObject();
        System.out.println(a);
    }
}
View Code

若此时我们修改了Animal类再次执行Run2就会抛出异常。异常原因为Class中的serialVersionUID 和和本地文件中的serialVersionUID不相同导致的。得出结论,就算我们不使用1,2,3编译器也会自动帮我们生成serialVersionUID。

package com.datang.netty.serializabletest;

import java.io.Serializable;

public class Amimal implements Serializable{
    private String hobby;
    private String eat;
    private int age;
    
    public void a() {}
    
    public String getHobby() {
        return hobby;
    }
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
    public String getEat() {
        return eat;
    }
    public void setEat(String eat) {
        this.eat = eat;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
    }
    
    
}
View Code
Exception in thread "main" java.io.InvalidClassException: com.datang.netty.serializabletest.Amimal; local class incompatible: stream classdesc serialVersionUID = 7643364732831133867, local class serialVersionUID = 6225763375310236200

第三种方案其实只是将警告给抑制了,只是让有强迫症的程序员看起来舒服点,并没有做其他操作。重复第四种方案的测试步骤得出相同的结果。

package com.datang.netty.serializabletest;

import java.io.Serializable;

@SuppressWarnings("serial")
public class Amimal implements Serializable{
    private String hobby;
    private String eat;
    private int age;
    
    public int a;
    
    
    public String getHobby() {
        return hobby;
    }
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
    public String getEat() {
        return eat;
    }
    public void setEat(String eat) {
        this.eat = eat;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
    }
    
    
}
View Code
Exception in thread "main" java.io.InvalidClassException: com.datang.netty.serializabletest.Amimal; local class incompatible: stream classdesc serialVersionUID = 7643364732831133867, local class serialVersionUID = 5410859156642948805

第二种方案显式的将生成的serialVersionUID,在这种情况下可以在修改Animal类后再次执行Run2并不会抛出异常,新增加的属性为null。

package com.datang.netty.serializabletest;

import java.io.Serializable;

public class Amimal implements Serializable{
    /**
     * 
     */
    private static final long serialVersionUID = 7643364732831133867L;
    private String hobby;
    private String eat;
    private int age;
    
    private String name;
    
    
    
    public String getHobby() {
        return hobby;
    }
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
    public String getEat() {
        return eat;
    }
    public void setEat(String eat) {
        this.eat = eat;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + ", name=" + name + "]";
    }
    
    
}
View Code
Amimal [hobby=打游戏, eat=鸡蛋, age=11, name=null]

第一种方案和第二种没有太大区别只是将根据类特征生成的serialVersionUID写成了固定值1L,但是需要注意3,4两个方案虽然可以修改源类,但并不能修改serialVersionUID值,不然还是会报错。

package com.datang.netty.serializabletest;

import java.io.Serializable;

public class Amimal implements Serializable{
    
    /**
     * 
     */
    private static final long serialVersionUID = 2L;
    private String hobby;
    private String eat;
    private int age;
    
    
    
    
    public String getHobby() {
        return hobby;
    }
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
    public String getEat() {
        return eat;
    }
    public void setEat(String eat) {
        this.eat = eat;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
    }
    
    
}
View Code
Exception in thread "main" java.io.InvalidClassException: com.datang.netty.serializabletest.Amimal; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

字段不参与序列化

若类中有些字段不需要参与序列号则可以使用以下三种方式排除

1 transient关键字

package com.datang.netty.serializabletest;

import java.io.Serializable;

public class Amimal implements Serializable{
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String hobby;
    private transient String eat;
    private  int age;
    
    
    
    
    public  String  getHobby() {
        return hobby;
    }
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
    public String getEat() {
        return eat;
    }
    public void setEat(String eat) {
        this.eat = eat;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
    }
    
    
}
View Code
Amimal [hobby=打游戏, eat=null, age=11]

2 static关键字

package com.datang.netty.serializabletest;

import java.io.Serializable;

public class Amimal implements Serializable{
    
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private static String hobby;
    private transient String eat;
    private  int age;
    
    
    
    
    public  String  getHobby() {
        return hobby;
    }
    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
    public String getEat() {
        return eat;
    }
    public void setEat(String eat) {
        this.eat = eat;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
    }
    
    
}
View Code
Amimal [hobby=null, eat=null, age=11]

serialPersistentFields数组,这种方式指定哪些需要参与序列化。

package com.datang.netty.serializabletest;

import java.io.ObjectStreamField;
import java.io.Serializable;

public class Amimal implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String hobby;
    private String eat;
    private int age;
    
    private static final ObjectStreamField[] serialPersistentFields = {
            new ObjectStreamField("hobby", String.class),
            new ObjectStreamField("eat", String.class)
    };

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public String getEat() {
        return eat;
    }

    public void setEat(String eat) {
        this.eat = eat;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
    }

}
View Code
Amimal [hobby=打游戏, eat=鸡蛋, age=0]

Externalizable

externalizable实现了Serializable接口,所以我们需要被序列化的类实现该类也可以。

但是有两个必要条件

1 需要被序列号的类必须有空的构造函数

2 必须实现writeExternal和readExternal两个方法。在这两个方法内写需要被序列化的字段,需要注意的是write和read字段必须是有顺序的。

package com.datang.netty.serializabletest;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectStreamField;
import java.io.Serializable;

public class Amimal implements Externalizable {

    private String hobby;
    private String eat;
    private int age;
    
    
    

    public Amimal() {
        
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public String getEat() {
        return eat;
    }

    public void setEat(String eat) {
        this.eat = eat;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Amimal [hobby=" + hobby + ", eat=" + eat + ", age=" + age + "]";
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        
        out.writeObject(hobby);
        out.writeObject(eat);
        out.writeInt(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        hobby = (String) in.readObject();
        eat = (String) in.readObject();
        age = in.readInt();
    }

}
View Code

序列化的应用

序列化和反序列化在Java中的一个应用场景就是深拷贝,详情可以查看我的另一篇博客。

https://www.cnblogs.com/zumengjie/p/12198362.html

 

 

原文地址:https://www.cnblogs.com/zumengjie/p/14859168.html