• Effective Java 77 For instance control, prefer enum types to readResolve


    The readResolve feature allows you to substitute another instance for the one created by readObject [Serialization, 3.7].

       

    If the class of an object being deserialized defines a readResolve method with the proper declaration, this method is invoked on the newly created object after it is deserialized. The object reference returned by this method is then returned in place of the newly created object. In most uses of this feature, no reference to the newly created object is retained, so it immediately becomes eligible for garbage collection.

       

    public class Elvis implements Serializable {  

    /**

    * The version UID for the serial version object.

    */

    private static final long serialVersionUID = 4285474589312744336L;

       

    public static final Elvis INSTANCE = new Elvis();

       

    private Elvis() {

    }

       

    // readResolve for instance control - you can do better!

    private Object readResolve() {

    // Return the one true Elvis and let the garbage collector take care of the Elvis impersonator.

    return INSTANCE;

    }

       

    public static void main(String[] args) {

    ByteArrayOutputStream os = new ByteArrayOutputStream();

    ObjectOutputStream oos;

    try {

    oos = new ObjectOutputStream(os);

    oos.writeObject(Elvis.INSTANCE);

    oos.close();

       

    ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());

    ObjectInputStream ois = new ObjectInputStream(is);

    Elvis e = (Elvis) ois.readObject();

    System.out.printf("Elvis is %s the one used before? ",

    e == Elvis.INSTANCE? "": "not");

    ois.close();

    } catch (IOException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    } catch (ClassNotFoundException e) {

    // TODO Auto-generated catch block

    e.printStackTrace();

    }

       

    }

    }

       

    Note

    If you depend on readResolve for instance control, all instance fields with object reference types must be declared transient. If a singleton contains a nontransient object reference field, the contents of this field will be deserialized before the singleton's readResolve method is run.

       

    A demo for attack to secure a reference to the deserialized object before its readResolve method is run.

       

    First, write a "stealer" class that has both a readResolve method and an instance field that refers to the serialized singleton in which the stealer "hides." In the serialization stream, replace the singleton's nontransient field with an instance of the stealer. You now have a circularity: the singleton contains the stealer and the stealer refers to the singleton.

    Because the singleton contains the stealer, the stealer's readResolve method runs first when the singleton is deserialized. As a result, when the stealer's readResolve method runs, its instance field still refers to the partially deserialized (and as yet unresolved) singleton.

    The stealer's readResolve method copies the reference from its instance field into a static field, so that the reference can be accessed after the readResolve method runs. The method then returns a value of the correct type for the field in which it's hiding. If it didn't do this, the VM would throw a ClassCastException when the serialization system tried to store the stealer reference into this field.

    // Broken singleton - has nontransient object reference field!

    public class Elvis implements Serializable {

    private static final long serialVersionUID = 1L;

    public static final Elvis INSTANCE = new Elvis();

       

    private Elvis() {

    }

       

    private String[] favoriteSongs = { "Hound Dog", "Heartbreak Hotel" }; // favorite field

       

    public void printFavorites() {

    System.out.println(Arrays.toString(favoriteSongs));

    }

       

    private Object readResolve() throws ObjectStreamException {

    return INSTANCE;

    }

    }  

    public class ElvisStealer implements Serializable {

    static Elvis impersonator;

    private Elvis payload;

       

    private Object readResolve() {

    // Save a reference to the "unresolved" Elvis instance

    impersonator = payload;

    // Return an object of correct type for favorites field

    return new String[] { "A Fool Such as I" };

    }

       

    private static final long serialVersionUID = 0;

    }

       

    You could fix the problem by declaring the favorites field transient, but you're better off fixing it by making Elvis a single-element enum type (Item 3).

       

    // Enum singleton - the preferred approach

    public enum Elvis {

    INSTANCE;

    private String[] favoriteSongs = { "Hound Dog", "Heartbreak Hotel" };

       

    public void printFavorites() {

    System.out.println(Arrays.toString(favoriteSongs));

    }

    }

       

    The accessibility of readResolve is significant.

    If you place a readResolve method on a final class, it should be private.

    If you place a readResolve method on a nonfinal class, you must carefully consider its accessibility.

    If it is private, it will not apply to any subclasses.

    If it is package-private, it will apply only to subclasses in the same package.

    If it is protected or public, it will apply to all subclasses that do not override it.

    If a readResolve method is protected or public and a subclass does not overriden it, deserializing a serialized subclass instance will produce a superclass instance, which is likely to cause a ClassCastException.

    Summary

    You should use enum types to enforce instance control invariants wherever possible. If this is not possible and you need a class to be both serializable and instance-controlled, you must provide a readResolve method and ensure that all of the class's instance fields are either primitive or transient.

       

  • 相关阅读:
    科普 | 验证码有什么用?
    验证码是什么?有什么作用?(风控必看)
    银保监会提示信用卡逾期风险 简析“以卡养卡”背后的三个原因
    微信、电商、机票、抖音上的黑灰产“生意”
    假“信用卡”坑用户害商家 如何帮助银行构建反欺诈模型
    “羊毛党”们最喜欢用的手机号码分析
    是贫穷限制了想象力?双11黑产薅一天可吃一年,史上最强反羊毛党科普
    一场直播成交过亿?揭开数字背后的猫腻
    关联网络的特点有哪些?
    “双十一”购物节 电商平台面临的五大业务风险
  • 原文地址:https://www.cnblogs.com/haokaibo/p/for-instance-control-prefer-enum-types-to-readResolve.html
Copyright © 2020-2023  润新知