Java的Long類主要的作用就是對基本類型long進行封裝,提供了一些處理long類型的方法,比如long到String類型的轉換方法或String類型到long類型的轉換方法,當然也包含與其他類型之間的轉換方法。除此之外還有一些位相關的操作。
<code>MIN_VALUE</code>靜态變量表示long能取的最小值,為-2的63次方,被final修飾說明不可變。
類似的還有<code>MAX_VALUE</code>,表示long最大值為2的63次方減1。
<code>SIZE</code>用來表示二進制補碼形式的long值的比特數,值為64,靜态變量且不可變。
<code>BYTES</code>用來表示二進制補碼形式的long值的位元組數,值為<code>SIZE</code>除于<code>Byte.SIZE</code>,結果為8。
<code>TYPE</code>的toString的值是<code>long</code>。
Class的<code>getPrimitiveClass</code>是一個native方法,在<code>Class.c</code>中有個<code>Java_java_lang_Class_getPrimitiveClass</code>方法與之對應,是以JVM層面會通過<code>JVM_FindPrimitiveClass</code>函數根據”long”字元串獲得jclass,最終到Java層則為<code>Class<Long></code>。
當<code>TYPE</code>執行toString時,邏輯如下,則其實是<code>getName</code>函數決定其值,<code>getName</code>通過native方法<code>getName0</code>從JVM層擷取名稱,
<code>getName0</code>根據一個數組獲得對應的名稱,JVM根據Java層的Class可得到對應類型的數組下标,比如這裡下标為11,則名稱為”long”。
LongCache是Long的一個内部類,它包含了long可能值的Long數組,預設範圍是[-128,127],它不會像Byte類将所有可能值緩存起來,因為long類型範圍很大,将它們全部緩存起來代價太高,而Byte類型就是從-128到127,一共才256個。這裡預設隻執行個體化256個Long對象,當Long的值範圍在[-128,127]時則直接從緩存中擷取對應的Long對象,不必重新執行個體化。這些緩存值都是靜态且final的,避免重複的執行個體化和回收。
兩個parseLong方法,主要看第二個即可,第一個參數是待轉換的字元串,第二個參數表示進制數。怎麼更好了解這個參數呢?舉個例子,<code>Long.parseLong("100",10)</code>表示十進制的100,是以值為100,而<code>Long.parseLong("100",2)</code>表示二進制的100,是以值為4。另外如果<code>Long.parseLong("10000000000000000000",10)</code>會抛出<code>java.lang.NumberFormatException</code>異常。
該方法的邏輯是首先判斷字元串不為空且進制數在<code>Character.MIN_RADIX</code>和<code>Character.MAX_RADIX</code>之間,即2到36。然後判斷輸入的字元串的長度必須大于0,再根據第一個字元可能為數字或負号或正号進行處理。核心處理邏輯是字元串轉換數字,n進制轉成十進制辦法基本大家都知道的了,假如357為8進制,則結果為<code>$3*8^2+5*8^1+7*8^0 = 239$</code>,假如357為十進制,則結果為<code>$3*10^2+5*10^1+7*10^0 = 357$</code>,上面的轉換方法也差不多是根據此方法,隻是稍微轉變了思路,方式分别為<code>$((3*8+5)*8+7) = 239$</code>和<code>$((3*10+5)*10+7)=357$</code>。從中可以推出規則了,從左到右周遊字元串的每個字元,然後乘以進制數,再加上下一個字元,接着再乘以進制數,再加上下個字元,不斷重複,直到最後一個字元。除此之外另外一個不同就是上面的轉換不使用加法來做,全都轉成負數來運算,其實可以看成是等價了,這個很好了解,而為什麼要這麼做就要歸咎到long類型的範圍了,因為負數<code>Long.MIN_VALUE</code>變化為正數時會導緻數值溢出,是以全部都用負數來運算。
包含兩種構造函數,分别可以傳入long和String類型。它是通過調用parseLong方法進行轉換的,是以轉換邏輯與上面的parseLong方法一樣。
該方法主要做的事情是将某個long型數值放到char數組裡面,比如把357按順序放到char數組中。這裡面處理用了較多技巧,将long拆成高位4個位元組和低位4個位元組處理分開處理,<code>while (i >= Integer.MAX_VALUE)</code>部分就是處理高位的4個位元組,每次處理2位數,這裡有個特殊的地方<code>((q << 6) + (q << 5) + (q << 2))</code>其實等于<code>q*100</code>,<code>Integer.DigitTens</code>和<code>Integer.DigitOnes</code>數組在前面Integer文章中已經講過它的作用了,用來擷取十位和個位。
接着看怎麼處理低4個位元組,它繼續将4個位元組分為高位2個位元組和低位2個位元組,<code>while (i >= 65536)</code>部分就是處理高位的兩個位元組,每次處理2位數,處理邏輯與高位4個位元組的處理邏輯一樣。
再看接下去的低位的兩個位元組怎麼處理,其實本質也是求餘思想,但又用了一些技巧,比如<code>(i * 52429) >>> (16+3)</code>其實約等于<code>i/10</code>,<code>((q << 3) + (q << 1))</code>其實等于<code>q*10</code>,然後再通過<code>Integer.digits</code>數組擷取到對應的字元。可以看到低位處理時它盡量避開了除法,取而代之的是用乘法和右移來實作,可見除法是一個比較耗時的操作,比起乘法和移位。另外也可以看到能用移位和加法來實作乘法的地方也盡量不用乘法,這也說明乘法比起它們更加耗時。而高位處理時沒有用移位是因為做乘法後可能會溢出。
一共有3個toString方法,兩個靜态方法一個是非靜态方法,第一個toString方法很簡單,就是先用stringSize得到數字是多少位,再用getChars擷取數字對應的char數組,最後傳回一個String類型。第二個toString調用第一個toString,沒啥好說。第三個toString方法是帶了進制資訊的,它會轉換成對應進制的字元串。凡是不在2到36進制範圍之間的都會被處理成10進制,我們都知道從十進制轉成其他進制時就是不斷地除于進制數得到餘數,然後把餘數反過來串起來就是最後結果,是以這裡其實也是這樣子做的,得到餘數後通過digits數組擷取到對應的字元,而且這裡是用負數的形式來運算的。
有三個valueOf方法,核心邏輯在第一個valueOf方法中,因為LongCache緩存了[-128,127]值的Long對象,對于在範圍内的直接從LongCache的數組中擷取對應的Long對象即可,而在範圍外的則需要重新執行個體化了。
decode方法主要作用是解碼字元串轉成Long型,比如<code>Long.decode("11")</code>的結果為11;<code>Long.decode("0x11")</code>和<code>Long.decode("#11")</code>結果都為17,因為0x和#開頭的會被處理成十六進制;<code>Long.decode("011")</code>結果為9,因為0開頭會被處理成8進制。
包括shortValue、intValue、longValue、byteValue、floatValue和doubleValue等方法,其實就是轉換成對應的類型。
可以看到hashCode方法傳回的事int類型,首先将long型值無符号右移32位,再和原來的值進行異或運算,最後傳回int類型值。
比較是否相同時先判斷是不是Long類型再比較值。
x小于y則傳回-1,相等則傳回0,否則傳回1。
<code>toUnsignedBigInteger</code>方法将long轉成BigInteger類型,主要用<code>BigInteger.valueOf</code>進行轉換,如果小于0則需要先轉成高4位元組和低4位元組,然後再轉換。
<code>toUnsignedString</code>方法中,對于大于0的long值直接用toString轉換,而小于0的則要按照進制不同分别做不同處理。
該方法主要用于計算二進制數中1的個數。一看有點懵,都是移位和加減操作。先将重要的列出來,<code>0x5555555555555555L</code>等于<code>0101010101010101010101010101010101010101010101010101010101010101</code>,<code>0x3333333333333333L</code>等于<code>0011001100110011001100110011001100110011001100110011001100110011</code>,<code>0x0f0f0f0f0f0f0f0fL</code>等于<code>0000111100001111000011110000111100001111000011110000111100001111</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位,64位,最終在與<code>0x7f</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+32=63都為1,最後用<code>i - (i >>> 1)</code>自然得到最終結果。
與highestOneBit方法對應,lowestOneBit擷取最低位1,其他全為0的值。這個操作較簡單,先取負數,這個過程需要對正數的i取反碼然後再加1,得到的結果和i進行與操作,剛好就是最低位1其他為0的值了。
該方法傳回i的二進制從頭開始有多少個0。i為0的話則有64個0。這裡處理其實是展現了二分查找思想的,先看高32位是否為0,是的話則至少有32個0,否則左移16位繼續往下判斷,接着右移24位看是不是為0,是的話則至少有16+8=24個0,以此類推,直到最後得到結果。
與前面的numberOfLeadingZeros方法對應,該方法傳回i的二進制從尾開始有多少個0。它的思想和前面的類似,也是基于二分查找思想,詳細步驟不再贅述。
該方法即是将i進行反轉,反轉就是第1位與第64位對調,第二位與第63位對調,以此類推。它的核心思想是先将相鄰兩位進行對換,比如10100111對換01011011,接着再将相鄰四位進行對換,對換後為10101101,接着将相鄰八位進行對換,最後把64位中中間的32位對換,然後最高16位再和最低16位對換。
這幾個方法類似,合到一起講。看名字就知道轉成2進制、8進制和16進制的字元串。可以看到都是間接調用toUnsignedString0方法,該方法會先計算轉換成對應進制需要的字元數,然後再通過formatUnsignedInt方法來填充字元數組,該方法做的事情就是使用進制之間的轉換方法來擷取對應的字元。
以下是廣告和相關閱讀
========廣告時間========
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/74080321" target="_blank">為什麼寫《Tomcat核心設計剖析》</a>
=========================
相關閱讀:
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/73743876" target="_blank">從JDK源碼角度看Object</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/72933108" target="_blank">談談Java基礎資料類型</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51455094" target="_blank">從JDK源碼角度看并發鎖的優化</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51468764" target="_blank">從JDK源碼角度看線程的阻塞和喚醒</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51433204" target="_blank">從JDK源碼角度看并發競争的逾時</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51397266" target="_blank">從JDK源碼角度看java并發線程的中斷</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51371416" target="_blank">從JDK源碼角度看Java并發的公平性</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51360228" target="_blank">從JDK源碼角度看java并發的原子性如何保證</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/74557125" target="_blank">從JDK源碼角度看Byte</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/73350488" target="_blank">從JDK源碼角度看Boolean</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/76557578" target="_blank">從JDK源碼角度看Short</a>
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/77196626" target="_blank">從JDK源碼角度看Integer</a>
歡迎關注: