• 《Java基础知识》Java序列化与反序列化详解


    序列化的作用:为了不同jvm之间共享实例对象的一种解决方案.由java提供此机制。

    序列化应用场景:

    1. 分布式传递对象。

    2. 网络传递对象。

    3. tomcat关闭以后会把session对象序列化到SESSIONS.ser文件中,等下次启动的时候就把这些session再加载到内存中。

    完整案例:

    import java.io.Serializable;
    
    public class Box implements Serializable {
    
        public Box(){
            System.out.println("调用构造Box方法");
        }
    
        private String width;
        private String height;
    
        public String getWidth() {
            return width;
        }
    
        public void setWidth(String width) {
            this.width = width;
        }
    
        public String getHeight() {
            return height;
        }
    
        public void setHeight(String height) {
            this.height = height;
        }
    
        @Override
        public String toString() {
            return "Box{" +
                    "width=" + width +
                    ", height=" + height +
                    '}';
        }
    }
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    public class SerializableDemo {
        public static void main(String[] args){
            Box myBox = new Box();
            myBox.setWidth("50");
            myBox.setHeight("30");
    
            try{
                File file = new File("src\\demo\\knowledgepoints\\file\\foo.ser");
    
                //把对象信息写入文件中。
                ObjectOutputStream oout = new ObjectOutputStream (new FileOutputStream(file));
                oout.writeObject(myBox);
                oout.close();
    
                //把对象信息从文件中获取出来。
                ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
                Box newMyBox = (Box)oin.readObject(); // 没有强制转换到Person类型
                oin.close();
                System.out.println(newMyBox);
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }

    运行结果:

    序列化相关注意事项:

        a)序列化时,只对对象的状态进行保存,而不管对象的方法;

        b)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;

        c)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;

        d)  被序列化的对象需要 实现(Serializable)接口;

        e) 案例:构造方法没有执行,说明生成新对象,不是通过New出来的;

    Java 对象被序列化需要实现(Serializable)接口,原因:

    在 我们将对象序列化的类 ObjectOutputStream 的方法 writeObject0 中可以找到答案。

    以下是JDK8的部分源码,红色字体部分就是原因。

    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() + "\n" + debugInfoStack.toString());
                    } else {
                        throw new NotSerializableException(cl.getName());
                    }
                }
            } finally {
                depth--;
                bout.setBlockDataMode(oldMode);
            }
        }

    如何将序列化控制在自己手中?

    1. 通过关键字【transient】实现,字段值不被序列化。

    改一下Box类:

    import java.io.Serializable;
    
    public class Box implements Serializable {
    
        public Box(){
            System.out.println("调用构造Box方法");
        }
    
        // 关键字:transient 控制width不被序列化,保护数据。
        private transient String width;
        private String height;
    
        public String getWidth() {
            return width;
        }
    
        public void setWidth(String width) {
            this.width = width;
        }
    
        public String getHeight() {
            return height;
        }
    
        public void setHeight(String height) {
            this.height = height;
        }
    
        @Override
        public String toString() {
            return "Box{" +
                    "width=" + width +
                    ", height=" + height +
                    '}';
        }
    }

    运行结果:

    这个box的width值被擦除了。

    2. writeObject()方法与readObject()方法。

    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    public class Box implements Serializable {
    
        public Box(){
            System.out.println("调用构造Box方法");
        }
    
        // 关键字:transient 控制width不被序列化,保护数据。
        private transient String width;
        private String height;
    
        public String getWidth() {
            return width;
        }
    
        public void setWidth(String width) {
            this.width = width;
        }
    
        public String getHeight() {
            return height;
        }
    
        public void setHeight(String height) {
            this.height = height;
        }
    
        private void writeObject(ObjectOutputStream out) throws IOException {
            out.defaultWriteObject();
            out.writeChars(width);
        }
    
        private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
            in.defaultReadObject();
            width = in.readLine();
        }
    
        @Override
        public String toString() {
            return "Box{" +
                    "width=" + width +
                    ", height=" + height +
                    '}';
        }
    }

    运行结果:

    加入writeObject()方法与readObject()方法后,width值又回来了,这两个方法都是私有的,已经可以基本猜测是通过反射调用方法,赋值的。

    3.Externalizable 接口,自定义实现写入和读取序列化对象

    import java.io.Externalizable;
    import java.io.IOException;
    import java.io.ObjectInput;
    import java.io.ObjectOutput;
    
    public class BoxTmp implements Externalizable {
        public BoxTmp(){
            System.out.println("调用构造Box方法");
        }
    
        private String width;
        private String height;
    
        public String getWidth() {
            return width;
        }
    
        public void setWidth(String width) {
            this.width = width;
        }
    
        public String getHeight() {
            return height;
        }
    
        public void setHeight(String height) {
            this.height = height;
        }
    
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeChars(width+","+width);
        }
    
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            String str = in.readLine();
            this.width = str.split(",")[0];
            this.height = str.split(",")[1];
        }
    
        @Override
        public String toString() {
            return "BoxTmp{" +
                    "width='" + width + '\'' +
                    ", height='" + height + '\'' +
                    '}';
        }
    }
    public class ExternalizableDemo {
        public static void main(String[] args){
            BoxTmp boxTmp = new BoxTmp();
            boxTmp.setWidth("50");
            boxTmp.setHeight("30");
    
            try{
                File file = new File("src\\demo\\knowledgepoints\\file\\foo.txt");
    
                //把对象信息写入文件中。
                ObjectOutputStream oout = new ObjectOutputStream (new FileOutputStream(file));
                oout.writeObject(boxTmp);
                oout.close();
    
                //把对象信息从文件中获取出来。
                ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
                BoxTmp newBoxTmp = (BoxTmp)oin.readObject(); // 没有强制转换到Person类型
                oin.close();
                System.out.println(newBoxTmp);
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }

    运行结果:

    Externalizable接口中的writeExternal和readExternal 方法可以用来自定义实现值的传递,覆盖等其他操作。

    同时构造方法被执行了,说明这个类是被new出来后,由你支配。

    4. readResolve()方法

    import java.io.ObjectStreamException;
    import java.io.Serializable;
    
    public class Box implements Serializable {
    
        public static Box box = new Box();
    
        public Box(){}
    
        public Box(String height,String width){
            this.height = height;
            this.width = width;
        }
    
        public static Box getInstance() {
            if(box == null){
                box = new Box("20","10");
            }
            return box;
        }
    
        private String width;
        private String height;
    
        public String getWidth() {
            return width;
        }
    
        public void setWidth(String width) {
            this.width = width;
        }
    
        public String getHeight() {
            return height;
        }
    
        public void setHeight(String height) {
            this.height = height;
        }
    
        private Object readResolve() throws ObjectStreamException {
            return Box.box;
        }
    }
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    public class SerializableDemo {
        public static void main(String[] args){
            Box myBox = Box.getInstance();
            try{
                File file = new File("src\\demo\\knowledgepoints\\file\\foo.ser");
    
                //把对象信息写入文件中。
                ObjectOutputStream oout = new ObjectOutputStream (new FileOutputStream(file));
                oout.writeObject(myBox);
                oout.close();
    
                //把对象信息从文件中获取出来。
                ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
                Box newMyBox = (Box)oin.readObject(); // 没有强制转换到Person类型
                oin.close();
                System.out.println("newMyBox == myBox : "+(newMyBox == myBox));
            }catch(Exception ex){
                ex.printStackTrace();
            }
        }
    }

    运行结果:

    Box类中实现readResolve() 方法可以实现单例对象还是同一个。


    尚学堂给同学们带来全新的Java300集课程啦!java零基础小白自学Java必备优质教程_手把手图解学习Java,让学习成为一种享受_哔哩哔哩_bilibili

  • 相关阅读:
    翻译专业论文选题有哪些值得写的方向?
    Linux安装oracle 19C
    企业财务数字化转型怎么才能落地?_光点科技
    深入Android系统基础知识及基本概念
    纯CSS打造酷炫霓虹灯loading bar特效
    用74HC573进行LED数码管驱动的编程
    zk的watch机制使用及原理分析
    为什么有了 HTTP 还要 RPC
    数据库系统原理与实践 笔记 #7
    Minecraft 1.19.2 Forge模组开发 03.动画生物实体
  • 原文地址:https://blog.csdn.net/Limingmingaa/article/details/125338364