为什么在单例中声明所有实例字段都是瞬态的?

Why declare all instance fields transient in singletons?

To make a singleton class that is implemented using either of the previous approaches serializable (Chapter 11), it is not sufficient merely to add imple- ments Serializable to its declaration. To maintain the singleton guarantee, you have to declare all instance fields transient and provide a readResolve method (Item 77). Otherwise, each time a serialized instance is deserialized, a new instance will be created

这引出了我的问题:为什么要声明所有实例字段为 transient ?我认为 readResolve 就足够了! 我的问题是:为什么作者说我们应该在单例中声明所有实例字段为瞬态

package com.effective.test;

import java.io.Serializable;

public class NormalBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private  transient  int id;

    /**
     * no matter declare transient or not...nothing is different!why author say that??
     */
    private/* transient */String name;

    private NormalBean(int id, String name) {
        this.id = id;
        this.name = name;
    }

    private static final NormalBean INSTANCE = new NormalBean(12345,"jack");

    public static NormalBean getInstance() {
        return INSTANCE;
    }

    /*
     * The readResolve method is called when ObjectInputStream has read an
     * object from the stream and is preparing to return it to the caller.
     * ObjectInputStream checks whether the class of the object defines the
     * readResolve method. If the method is defined, the readResolve method is
     * called to allow the object in the stream to designate the object to be
     * returned.
     * 
     * And in Singleton case we are returning the same instance that was created
     * while classloading and no new instances are returned. So singletonness is
     * maintained.
     */
    private Object readResolve() {
        return INSTANCE;
    }

    @Override
    public String toString() {
        return "NormalBean [id=" + id + ", name=" + name + ", getClass()=" + getClass() + ", hashCode()=" + hashCode()
                + "]";
    }
}

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

public class TestTransientPlusReadResolve {
      NormalBean bean=NormalBean.getInstance() ;
    public   void writeAndRead() throws IOException {
        ObjectOutputStream outStream = new ObjectOutputStream(new FileOutputStream("writeAndRead.txt"));
        System.out.println("this is the one "+bean );

        outStream.writeObject(bean);
        outStream.close();
        try {
            Thread.sleep(500l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("writeAndRead.txt"));

        try {
            NormalBean backBean = (NormalBean) inputStream.readObject();
            System.out.println("this is the one "+backBean );

            System.out.println("still the One?"+(backBean==bean));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally{
            inputStream.close();
        }
    }

    public static void main(String[] args) throws IOException {
      new TestTransientPlusReadResolve().writeAndRead();
    }

}

输出为:

    this is the one NormalBean [id=12345, name=jack, getClass()=class com.effective.test.NormalBean, hashCode()=366712642]

    this is the one NormalBean [id=12345, name=jack, getClass()=class com.effective.test.NormalBean, hashCode()=366712642]

    still the One?true

我认为如果您在单个 JVM 中使用它不会突出任何问题(class 将保存静态单例实例的引用,无论您如何序列化它)。也许在序列化后重新启动并读取文件会有所帮助,但我认为 class 加载应该在反序列化之前发生。

它还有性能方面的考虑。如果您不需要序列化字段的值,因为它们对于单身人士来说是相同的,那么为什么要序列化它,例如通过网络发送它,然后执行 readResolve 只是为了忽略您收到的值?

(Chapter 11)

什么第 11 章?

To maintain the singleton guarantee, you have to declare all instance fields transient

不,你不知道。不需要使实例变量 transient 来提供单例保证。提供一个readResolve()方法就足够了。

my question is :why the author said we shuold declare all instance fields transient in singletons

什么作者?什么的作者?他在哪里说的?他的原话是什么?你的问题是什么?

可能他建议作为性能改进,因为序列化不会使用的数据只是浪费时间,space。

如果您将一个实例变量标记为瞬态,您是在告诉 JVM 跳过 当您尝试序列化包含它的对象时(忽略)这个变量。 序列化是 Java 最酷的功能之一;它可以让你保存(有时称为 "flatten") 一个对象,通过写入它的状态(换句话说,它的实例的值) 变量)到特殊类型的 I/O 流。通过序列化,您可以保存一个对象 到一个文件,甚至通过电线将其运送到另一端重新膨胀(反序列化),在 另一个 JVM。