日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Effective Java之对于实例控制,枚举类型优于readResolve(七十七)

發(fā)布時間:2024/2/28 java 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Effective Java之对于实例控制,枚举类型优于readResolve(七十七) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
Object readResolve()

這個方法會緊挨著readObject()之后被調用,該方法的返回值將會代替原來反序列化的對象,而原來readObject()反序列化的對象將會立即丟棄。

readObject()方法在序列化單例類時尤其有用。當然,如果使用java5提供的enum來定義枚舉類,則完全不用擔心,程序沒有任何問題。

我們從一個單例模式開始:

public final class MySingleton { private MySingleton() { } private static final MySingleton INSTANCE = new MySingleton(); public static MySingleton getInstance() { return INSTANCE; } }

如果實現(xiàn)了序列化,那么會執(zhí)行readObject方法或默認的序列化。他們都會返回一個新建的實例,也就違反了單例。

readResolve()方法正好滿足需求:

public final class MySingleton { private MySingleton() { } private static final MySingleton INSTANCE = new MySingleton(); public static MySingleton getInstance() { return INSTANCE; } private Object readResolve() throws ObjectStreamException { // instead of the object we're on, // return the class variable INSTANCE return INSTANCE; } }

書中提到了如果單例實例中存在非transient對象引用,就會有被攻擊的危險,例子有點難懂,我在這里簡單地解釋一下;

因為單例包含一個非transient對象引用域,這個域內容在Singleton的readResolve方法運行之前被反序列化,于是,攻擊者截胡,把這段readResolve方法運行之前的字節(jié)流截下來(不讓他運行readResolve),來個偷梁換柱,把對象引用域指向自己寫的“盜用者”。

于是,把readResolve方法運行之前的字節(jié)流 換成 偷梁換柱的字節(jié)流,再讓他執(zhí)行一次,系統(tǒng)看到單例對象有個非transient對象引用域,指向“盜用者”(因為偷梁換柱了),所以系統(tǒng)也會序列化“盜用者”。

盜用者是這樣的:

class ElvisStealer implement Serializable{static Elvis impersonator;private Elvis payload;private Object readResolve(){impersonator = payload;return new String[]{"foolish"};} }

序列化盜用者“ElvisStealer”的時候,執(zhí)行它的readResolve()方法,impersonator = payload;偷偷 把第二次序列化的payload 記錄下來(作為攻擊者,其實沒有必要,只是為了展示),然后返回一個錯誤的對象引用。

但是枚舉類型就完全不同了:

// 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)); } }

這樣做完全沒有后顧之憂,因為枚舉enum生來就是支持序列化的,下面的官方對枚舉的序列化的聲明的翻譯:

在序列化的時候Java僅僅是將枚舉對象的name屬性輸出到結果中,反序列化的時候則是通過java.lang.Enum的valueOf方法來根據(jù)名字查找枚舉對象。同時,編譯器是不允許任何對這種序列化機制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。 我們看一下這個valueOf方法:

public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) { T result = enumType.enumConstantDirectory().get(name); if (result != null) return result; if (name == null) throw new NullPointerException("Name is null"); throw new IllegalArgumentException( "No enum const " + enumType +"." + name); }

總結:盡可能用枚舉類型來控制實例,否則,就要必須提供readResolve方法,并確保類的所有實例域都是基本類型或transient的。

總結

以上是生活随笔為你收集整理的Effective Java之对于实例控制,枚举类型优于readResolve(七十七)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。