jdk源碼讀到如今這裏,重要的集合類也讀了一部分了。
集合類再往下讀的話,就要涉及到兩個方向。
第一,是比較典型的可是不經常使用的數據結構,這部分我準備將數據結構複習、回顧後再繼續閱讀。
第二,是併發相關的集合,這部分我準備留到和併發相關的類一塊兒閱讀。java
因此,今天就讀些輕鬆的。緩存
<!--more -->數據結構
java的對象系統設計是採用單根繼承,全部的對象往上追溯,Object都是它們共同的祖先。併發
有了這個假設,我突然想起java中一個有趣的事實:jvm
List<Integer> list = new ArrayList<Integer>(); list.toString();
這段代碼能正常編譯、運行嗎?經驗告訴我,固然能夠。
但是從類型系統的角度仔細思考,list引用的類型爲List<Integer>
,其爲List
接口。
然而,List
接口中並無toString
方法,爲何能調用?函數
這是因爲,在java中,會讓接口類型也擁有Object的全部方法。一個接口對象,也是一個Object對象。由於單根繼承這一整體設計,因此這樣設計接口是合理的。
這裏有關於該問題的有趣討論,因此這裏就不詳細展開了。工具
在java中,除了最基本的單根繼承的祖先類以外,Object還內置了不少機制。如:性能
Object o = new Object(); synchronized(o) { /* ... */ }
在其它語言中,鎖這一機制都是標準庫中提供的函數,成對使用。一個lock函數
用於獲取鎖,一個release函數
函數用於釋放鎖。優化
然而,java直接將鎖機制做爲語法的一部分,還給它一個專屬關鍵字synchronized
。每一個Object對象,都內嵌了一個鎖。java稱之監視器鎖。this
這樣設計有什麼好處呢?一種觀點是,將鎖機制內置爲語法的一部分,有利於jvm對其進行深度優化提高性能,如java的鎖升級機制。
java的Object不只能夠認爲內嵌了一把鎖,還內嵌了一個條件變量。操做條件變量的函數:
public final native void notify(); public final native void notifyAll(); public final native void wait(long timeout) throws InterruptedException;
wait
將當前線程在條件變量上阻塞,通常是爲了等待其餘線程的某件事情執行完成。當其餘線程的事情執行完成後,在條件變量上調用notify
或notifyAll
來喚醒阻塞的線程。
能夠看到,這三個方法都是native
,jvm原生實現。
wait
還有兩個重載形式:
public final void wait() throws InterruptedException { wait(0); } public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeout++; } wait(timeout); }
比較有意思的是第二個。
原生實現的wait(long timeout)
,只能設置毫秒級別的超時時間。可是這個wait(long timeout, int nanos)
卻能設置納秒級別的超時時間。怎麼實現的?
if (nanos > 0) { timeout++; }
笑哭了。。。。難道是我下載的jdk平臺不對?
Object類提供了這三個函數的默認實現。來看一下:
public native int hashCode(); public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } public boolean equals(Object obj) { return (this == obj); }
能夠看到,hashCode的默認方法是原生實現,究竟是不是指針不清楚。
equals方法的默認實現僅僅簡單比較了是否爲同一引用。
toString()
方法打印出的是類名及十六進制的hash值。
裝箱拆箱機制的存在的緣由是:
爲了解決這個衝突,只好設計一組對象,中間包裹基本類型,而且語法層次內建裝箱類與基本類型的自動轉換機制,也即自動裝箱拆箱。
下面以Integer爲例分析裝箱拆箱類的源碼。
大體看一下Integer中的組成。能夠發現有三個不一樣的部分:
先來看屬性。
private final int value; public Integer(int value) { this.value = value; }
對的,Integer對象中,只有包含這麼一個數據,被裝箱的原始值。
簡單到不能再簡單。
咱們知道,通常來講,在java中,使用工廠方法代替構造函數是更好的設計。在Integer裏,就體現了它的好處之一。
Integer提供了一組靜態工廠方法:
public static Integer valueOf(String s) throws NumberFormatException { return Integer.valueOf(parseInt(s, 10)); } public static Integer valueOf(String s, int radix) throws NumberFormatException { return Integer.valueOf(parseInt(s,radix)); } public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
前兩個工廠方法都利用最後一個工廠方法實現。最重要的是最後一個。
很是明顯,當被裝箱的原始類型i
在IntegerCache.low
和IntegerCache.high
之間時,則返回緩存的Integer對象。
來看IntegerCache
:
private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }
可發現:
-128
到127
。java.lang.Integer.IntegerCache.high
來設置。這樣,若是在某些場景下Integer影響性能,能夠經過jvm手動修改該參數空間換時間。總結一下,因爲Integer是對象,而對整數的操做是代碼裏很是頻繁的地方。裝箱機制會致使程序產生大量的Integer對象,這致使:
所以,採用緩存機制,儘可能下降裝箱對性能的影響。
其它裝箱類的代碼這裏就不分析了。重點關注下各裝箱類的緩存範圍。
首先,Boolean,只有兩個值,固然能夠都緩存。
浮點類型,Double和Float,沒有緩存:
public static Float valueOf(float f) { return new Float(f); } public static Double valueOf(double d) { return new Double(d); }
Short,緩存範圍爲-128到127,和默認的Integer同樣。最重要的是,這個範圍沒法修改。
public static Short valueOf(short s) { final int offset = 128; int sAsInt = s; if (sAsInt >= -128 && sAsInt <= 127) { // must cache return ShortCache.cache[sAsInt + offset]; } return new Short(s); } private static class ShortCache { private ShortCache(){} static final Short cache[] = new Short[-(-128) + 127 + 1]; static { for(int i = 0; i < cache.length; i++) cache[i] = new Short((short)(i - 128)); } }
一樣:
能夠發現,只有Integer的緩存範圍可以修改,其它的裝箱類型都不行。