天天看點

從JDK源碼角度看Integer

Java的Integer類主要的作用就是對基本類型int進行封裝,提供了一些處理int類型的方法,比如int到String類型的轉換方法或String類型到int類型的轉換方法,當然也包含與其他類型之間的轉換方法。除此之外還有一些位相關的操作。

<code>MIN_VALUE</code>靜态變量表示int能取的最小值,為-2的31次方,被final修飾說明不可變。

類似的還有<code>MAX_VALUE</code>,表示int最大值為2的31次方減1。

<code>SIZE</code>用來表示二進制補碼形式的int值的比特數,值為32,靜态變量且不可變。

<code>BYTES</code>用來表示二進制補碼形式的int值的位元組數,值為<code>SIZE</code>除于<code>Byte.SIZE</code>,結果為4。

<code>TYPE</code>的toString的值是<code>int</code>。

Class的<code>getPrimitiveClass</code>是一個native方法,在<code>Class.c</code>中有個<code>Java_java_lang_Class_getPrimitiveClass</code>方法與之對應,是以JVM層面會通過<code>JVM_FindPrimitiveClass</code>函數根據”int”字元串獲得jclass,最終到Java層則為<code>Class&lt;Integer&gt;</code>。

當<code>TYPE</code>執行toString時,邏輯如下,則其實是<code>getName</code>函數決定其值,<code>getName</code>通過native方法<code>getName0</code>從JVM層擷取名稱,

<code>getName0</code>根據一個數組獲得對應的名稱,JVM根據Java層的Class可得到對應類型的數組下标,比如這裡下标為10,則名稱為”int”。

DigitTens和DigitOnes兩個數組放到一起講更好了解,它們主要用于擷取0到99之間某個數的十位和個位,比如48,通過DigitTens數組直接取出來十位為4,而通過DigitOnes數組取出來個位為8。

digits數組用于表示數字的所有可能的字元,因為int支援從2進制到36進制,是以這裡需要有36個字元才能表示所有不同進制的數字。

sizeTable數組主要用在判斷一個int型數字對應字元串的長度。比如相關的方法如下,這種方法可以高效得到對應字元串長度,避免了使用除法或求餘等操作。

IntegerCache是Integer的一個内部類,它包含了int可能值的Integer數組,預設範圍是[-128,127],它不會像Byte類将所有可能值緩存起來,因為int類型範圍很大,将它們全部緩存起來代價太高,而Byte類型就是從-128到127,一共才256個。是以這裡預設隻執行個體化256個Integer對象,當Integer的值範圍在[-128,127]時則直接從緩存中擷取對應的Integer對象,不必重新執行個體化。這些緩存值都是靜态且final的,避免重複的執行個體化和回收。另外我們可以改變這些值緩存的範圍,再啟動JVM時通過<code>-Djava.lang.Integer.IntegerCache.high=xxx</code>就可以改變緩存值的最大值,比如<code>-Djava.lang.Integer.IntegerCache.high=500</code>則會緩存[-128,500]。

兩個parseInt方法,主要看第一個即可,第一個參數是待轉換的字元串,第二個參數表示進制數。怎麼更好了解這個參數呢?舉個例子,<code>Integer.parseInt("100",10)</code>表示十進制的100,是以值為100,而<code>Integer.parseInt("100",2)</code>表示二進制的100,是以值為4。另外如果<code>Integer.parseInt("10000000000",10)</code>會抛出<code>java.lang.NumberFormatException</code>異常。

該方法的邏輯是首先判斷字元串不為空且進制數在<code>Character.MIN_RADIX</code>和<code>Character.MAX_RADIX</code>之間,即2到36。然後判斷輸入的字元串的長度必須大于0,再根據第一個字元可能為數字或負号或正号進行處理。核心處理邏輯是字元串轉換數字,n進制轉成十進制辦法基本大家都知道的了,假如357為8進制,則結果為3*8^2+5*8^1+7*8^0 = 239,假如357為十進制,則結果為3*10^2+5*10^1+7*10^0 = 357,上面的轉換方法也差不多是根據此方法,隻是稍微轉變了思路,方式分别為((3*8+5)*8+7) = 239和((3*10+5)*10+7)=357。從中可以推出規則了,從左到右周遊字元串的每個字元,然後乘以進制數,再加上下一個字元,接着再乘以進制數,再加上下個字元,不斷重複,直到最後一個字元。除此之外另外一個不同就是上面的轉換不使用加法來做,全都轉成負數來運算,其實可以看成是等價了,這個很好了解,而為什麼要這麼做就要歸咎到int類型的範圍了,因為負數<code>Integer.MIN_VALUE</code>變化為正數時會導緻數值溢出,是以全部都用負數來運算。

包含兩種構造函數,分别可以傳入int和String類型。它是通過調用parseInt方法進行轉換的,是以轉換邏輯與上面的parseInt方法一樣。

該方法主要做的事情是将某個int型數值放到char數組裡面,比如把357按順序放到char數組中。這裡面處理用了較多技巧,int高位的兩個位元組和低位的兩個位元組分開處理,<code>while (i &gt;= 65536)</code>部分就是處理高位的兩個位元組,每次處理2位數,這裡有個特殊的地方<code>((q &lt;&lt; 6) + (q &lt;&lt; 5) + (q &lt;&lt; 2))</code>其實等于<code>q*100</code>,<code>DigitTens</code>和<code>DigitOnes</code>數組前面已經講過它的作用了,用來擷取十位和個位。再看接下去的低位的兩個位元組怎麼處理,其實本質也是求餘思想,但又用了一些技巧,比如<code>(i * 52429) &gt;&gt;&gt; (16+3)</code>其實約等于<code>i/10</code>,<code>((q &lt;&lt; 3) + (q &lt;&lt; 1))</code>其實等于<code>q*10</code>,然後再通過digits數組擷取到對應的字元。可以看到低位處理時它盡量避開了除法,取而代之的是用乘法和右移來實作,可見除法是一個比較耗時的操作,比起乘法和移位。另外也可以看到能用移位和加法來實作乘法的地方也盡量不用乘法,這也說明乘法比起它們更加耗時。而高位處理時沒有用移位是因為做乘法後可能會溢出。

一共有3個toString方法,兩個靜态方法一個是非靜态方法,第一個toString方法很簡單,就是先用stringSize得到數字是多少位,再用getChars擷取數字對應的char數組,最後傳回一個String類型。第二個toString調用第一個toString,沒啥好說。第三個otString方法是帶了進制資訊的,它會轉換成對應進制的字元串。凡是不在2到36進制範圍之間的都會被處理成10進制,我們都知道從十進制轉成其他進制時就是不斷地除于進制數得到餘數,然後把餘數反過來串起來就是最後結果,是以這裡其實也是這樣子做的,得到餘數後通過digits數組擷取到對應的字元,而且這裡是用負數的形式來運算的。

有三個valueOf方法,核心邏輯在第一個valueOf方法中,因為IntegerCache緩存了[low,high]值的Integer對象,對于在範圍内的直接從IntegerCache的數組中擷取對應的Integer對象即可,而在範圍外的則需要重新執行個體化了。

decode方法主要作用是解碼字元串轉成Integer型,比如<code>Integer.decode("11")</code>的結果為11;<code>Integer.decode("0x11")</code>和<code>Integer.decode("#11")</code>結果都為17,因為0x和#開頭的會被處理成十六進制;<code>Integer.decode("011")</code>結果為9,因為0開頭會被處理成8進制。

包括shortValue、intValue、longValue、byteValue、floatValue和doubleValue等方法,其實就是轉換成對應的類型。

hashCode方法很簡單,就是直接傳回int類型的值。

比較是否相同時先判斷是不是Integer類型再比較值。

x小于y則傳回-1,相等則傳回0,否則傳回1。

轉成無符号long型。

該方法主要用于計算二進制數中1的個數。一看有點懵,都是移位和加減操作。先将重要的列出來,<code>0x55555555</code>等于<code>01010101010101010101010101010101</code>,<code>0x33333333</code>等于<code>110011001100110011001100110011</code>,<code>0x0f0f0f0f</code>等于<code>1111000011110000111100001111</code>。它的核心思想就是先每兩位一組統計看有多少個1,比如<code>10011111</code>則每兩位有1、1、2、2個1,記為<code>01011010</code>,然後再算每四位一組看有多少個1,而<code>01011010</code>則每四位有2、4個1,記為<code>00100100</code>,接着每8位一組就為<code>00000110</code>,接着16位,32位,最終在與<code>0x3f</code>進行與運算,得到的數即為1的個數。

該方法傳回i的二進制中最高位的1,其他全為0的值。比如i=10時,二進制即為1010,最高位的1,其他為0,則是1000。如果i=0,則傳回0。如果i為負數則固定傳回-2147483648,因為負數的最高位一定是1,即有<code>1000,0000,0000,0000,0000,0000,0000,0000</code>。這一堆移位操作是什麼意思?其實也不難了解,将i右移一位再或操作,則最高位1的右邊也為1了,接着再右移兩位并或操作,則右邊1+2=3位都為1了,接着1+2+4=7位都為1,直到1+2+4+8+16=31都為1,最後用<code>i - (i &gt;&gt;&gt; 1)</code>自然得到最終結果。

與highestOneBit方法對應,lowestOneBit擷取最低位1,其他全為0的值。這個操作較簡單,先取負數,這個過程需要對正數的i取反碼然後再加1,得到的結果和i進行與操作,剛好就是最低位1其他為0的值了。

該方法傳回i的二進制從頭開始有多少個0。i為0的話則有32個0。這裡處理其實是展現了二分查找思想的,先看高16位是否為0,是的話則至少有16個0,否則左移16位繼續往下判斷,接着右移24位看是不是為0,是的話則至少有16+8=24個0,直到最後得到結果。

與前面的numberOfLeadingZeros方法對應,該方法傳回i的二進制從尾開始有多少個0。它的思想和前面的類似,也是基于二分查找思想,詳細步驟不再贅述。

該方法即是将i進行反轉,反轉就是第1位與第32位對調,第二位與第31位對調,以此類推。它的核心思想是先将相鄰兩位進行對換,比如10100111對換01011011,接着再将相鄰四位進行對換,對換後為10101101,接着将相鄰八位進行對換,最後把32位中中間的16位對換,然後最高8位再和最低8位對換。

這兩個方法類似,合到一起講。看名字就知道轉成8進制和16進制的字元串。可以看到都是間接調用toUnsignedString0方法,該方法會先計算轉換成對應進制需要的字元數,然後再通過formatUnsignedInt方法來填充字元數組,該方法做的事情就是使用進制之間的轉換方法(前面有提到過)來擷取對應的字元。

以下是廣告和相關閱讀

========廣告時間========

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/74080321">為什麼寫《Tomcat核心設計剖析》</a>

=========================

相關閱讀:

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/73743876">從JDK源碼角度看Object</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/72933108">談談Java基礎資料類型</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51455094">從JDK源碼角度看并發鎖的優化</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51468764">從JDK源碼角度看線程的阻塞和喚醒</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51433204">從JDK源碼角度看并發競争的逾時</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51397266">從JDK源碼角度看java并發線程的中斷</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51371416">從JDK源碼角度看Java并發的公平性</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51360228">從JDK源碼角度看java并發的原子性如何保證</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/74557125">從JDK源碼角度看Byte</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/73350488">從JDK源碼角度看Boolean</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/76557578">從JDK源碼角度看Short</a>

歡迎關注:

從JDK源碼角度看Integer
上一篇: LSTM神經網絡