Java 9中如何對IntegerCache進(jìn)行修改詳解
在開始本文的正文之前,我們下面來看看下面這段代碼:
Java中Integer類的IntegerCache的作用
包名:java.lang
文件名:Integer.java
方法名:IntegerCache
方法的代碼如下:
private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
// high value may be configured by property
int h = 127;
if (integerCacheHighPropValue != null) {
// Use Long.decode here to avoid invoking methods that
// require Integer's autoboxing cache to be initialized
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - -low);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
我們在代碼中看到,low為-128,high為127,這樣的話,在Java編程中,如果要使用-128——127這個區(qū)間的對象的話,是直接使用這個Cache中的對象的。
上面是段簡單的介紹,幫助大家理解下IntegerCache,下面開始本文的正文:
引言
5 年前,我在 Hungarian 上發(fā)表了一篇關(guān)于 JDK 中如何改變 IntegerCache 的文章。這種做法其實(shí)是深入進(jìn) Java 運(yùn)行時,在實(shí)際并沒有使用的場景。當(dāng)你開發(fā)這些研究代碼時,你才能更好的理解反射是如何工作的,以及 Integer 類是如何實(shí)現(xiàn)的。
Integer 類有一個私有的嵌套內(nèi),名為 IntegerCache ,包含了值從 -127 到 128 的 Integer 對象。
當(dāng)代碼需要從 int 類型封箱成 Integer 對象,而且值在這個范圍內(nèi)時,那么 Java 運(yùn)行時會使用這個緩存,而不是創(chuàng)建一個新的 Integer 對象。這主要是處于性能優(yōu)化的考慮,我們必須牢記在心的是很多 int 值在程序中很多時候都處于這個范圍內(nèi)(例如數(shù)組的下標(biāo)索引)。
這樣做的副作用是,很多時候,使用等號操作符來比較兩個 Integer 對象時,只要值在范圍內(nèi)都是有效的。這在單元測試中很典型。而在運(yùn)行模式下,當(dāng)數(shù)值大于 128 時,代碼執(zhí)行會失敗。
使用反射來訪問 IntegerCache 類時會導(dǎo)致一些奇怪的副作用,注意這會影響到整個的 JVM。如果一個 Servlet 重新定義了小的 Integer 緩存值,那么所有運(yùn)行在同一個 Tomcat 下的其他 Servlet 也遭遇同樣問題。
在 Lukas Eder 和 Sitepoint 上面還有其他一些文章描述此問題。
現(xiàn)在我已經(jīng)開始在玩弄 Java 9 的早期發(fā)布版本,在我腦海里我一直要做的就是對新的 Java 版本進(jìn)行各種實(shí)驗(yàn)。在開始之前,讓我們先看看在 Java 8 中的做法。
在 Lukas 的文章中,我將他的示例代碼貼在此處:
import java.lang.reflect.Field;
import java.util.Random;
public class Entropy {
public static void main(String[] args)
throws Exception {
// Extract the IntegerCache through reflection
Class << ? > clazz = Class.forName(
"java.lang.Integer$IntegerCache");
Field field = clazz.getDeclaredField("cache");
field.setAccessible(true);
Integer[] cache = (Integer[]) field.get(clazz);
// Rewrite the Integer cache
for (int i = 0; i < cache.length; i++) {
cache[i] = new Integer(
new Random().nextInt(cache.length));
}
// Prove randomness
for (int i = 0; i < 10; i++) {
System.out.println((Integer) i);
}
}
}
此代碼通過反射方式訪問 IntegerCache,然后使用隨機(jī)值對緩存進(jìn)行填充(淘氣?。?。
我們嘗試在 Java 9 中執(zhí)行相同的代碼,別指望有什么樂趣。當(dāng)有人嘗試違反它時會發(fā)現(xiàn) Java 9 的限制更加嚴(yán)格。
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field static final java.lang.Integer[] java.lang.Integer$IntegerCache.cache accessible: module java.base does not "opens java.lang" to unnamed module @1bc6a36e
程序拋出了異常,這個異常在 Java 8 中是不會發(fā)生的。相當(dāng)于說對象是不可范文的,因?yàn)?java.base 模塊的原因,這是 JDK 的組成部分,在每個 Java 程序啟動時被自動的導(dǎo)入,不允許打開未命名的模塊。這個異常是在當(dāng)我們嘗試設(shè)置字段可訪問屬性時拋出的。
我們在 Java 8 可輕松訪問的對象,現(xiàn)在在 Java 9 中不能訪問了,因?yàn)樾碌哪K系統(tǒng)對此進(jìn)行了保護(hù)。代碼只能訪問字段、方法和其他用反射能訪問的信息,只有當(dāng)類在相同的模塊中,或者模塊打開了包用于反射方式訪問。這個可以通過 module-info.java 模塊定義文件來實(shí)現(xiàn):
module myModule {
exports com.javax0.module.demo;
opens com.javax0.module.demo;
}
這個模塊 java.base 不用不用自行打開用于反射訪問,特別是未命名模塊更不需要。如果我們創(chuàng)建了一個模塊并進(jìn)行命名,那么錯誤信息將包含模塊的名稱。
我們能否在程序里打開模塊呢? java.lang.reflect.Module 模塊有一個 addOpens 的方法可以做到。
可行嗎?
對開發(fā)者來說壞消息是:不可行。它只能在另外一個模塊中打開一個模塊中的包,并且包已經(jīng)在該模塊中通過調(diào)用這個方法打開過。這種方法只能讓模塊傳遞給另外的模塊權(quán)利,前提是另外的模塊已經(jīng)以某種方式打開過相同的包,而不能打開沒有打開過的包(譯者注:很難理解,不是嗎?)。
但與此同時好消息是:Java 9 不像 Java 8 那么容易被破解。最少這個漏洞被關(guān)閉了。看起來 Java 開始往專業(yè)級發(fā)展,而不僅僅是個玩具(譯者注:誰說 Java 是個玩具了?)。不久的將來你可以非常嚴(yán)肅的將 RPG 和 COBOL 語言的項(xiàng)目遷移到 Java 上了。(很抱歉,我開玩笑的)
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
本文翻譯自:https://dzone.com/articles/hacking-the-integercache-in-java-9
相關(guān)文章
SpringBoot+阿里云OSS實(shí)現(xiàn)在線視頻播放的示例
這篇文章主要介紹了SpringBoot+阿里云OSS實(shí)現(xiàn)在線視頻播放的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
通過使用Byte?Buddy便捷創(chuàng)建Java?Agent
這篇文章主要為大家介紹了如何通過使用Byte?Buddy便捷創(chuàng)建Java?Agent的使用說明,有需要的朋友可以借鑒參考下希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03
使用spring stream發(fā)送消息代碼實(shí)例
這篇文章主要介紹了使用spring stream發(fā)送消息代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-05-05

