概況
Java的Long類主要的作用就是對基本類型long進行封裝,提供了一些處理long類型的方法,比如long到String類型的轉換方法或String類型到long類型的轉換方法,當然也包含與其他類型之間的轉換方法。除此之外還有一些位相關的操作。
Java long數據類型
long數據類型是64位有符號的Java原始數據類型。當對整數的計算結果可能超出int數據類型的范圍時使用。
long數據類型范圍是-9,223,372,036,854,775,808至9,223,372,036,854,775,807(-2^63至2^63-1)。
long數據類型范圍內的所有整數稱為long類型的整數字面量。long類型的整數常數總是以大寫L或小寫l結尾。
以下是使用long類型的整數字面量的示例:
1
2
3
4
5
|
long num1 = 0L; long num2 = 4L; long mum3 = - 3 ; long num4 = 8 ; long num5 = -1L; |
本文主要介紹的是通過JDK源碼分析Long類的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。
繼承結構
1
2
3
|
--java.lang.Object --java.lang.Number --java.lang.Long |
主要屬性
1
2
3
4
5
|
public static final long MIN_VALUE = 0x8000000000000000L; public static final long MAX_VALUE = 0x7fffffffffffffffL; public static final int BYTES = SIZE / Byte.SIZE; public static final int SIZE = 64 ; public static final Class<Long> TYPE = (Class<Long>) Class.getPrimitiveClass( "long" ); |
- MIN_VALUE靜態變量表示long能取的最小值,為-2的63次方,被final修飾說明不可變。
- 類似的還有MAX_VALUE,表示long最大值為2的63次方減1。
- SIZE用來表示二進制補碼形式的long值的比特數,值為64,靜態變量且不可變。
- BYTES用來表示二進制補碼形式的long值的字節數,值為SIZE除于Byte.SIZE,結果為8。
-
TYPE的toString的值是long。
Class的getPrimitiveClass是一個native方法,在Class.c中有個Java_java_lang_Class_getPrimitiveClass方法與之對應,所以JVM層面會通過JVM_FindPrimitiveClass函數根據”long”字符串獲得jclass,最終到Java層則為Class<Long>。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
JNIEXPORT jclass JNICALL Java_java_lang_Class_getPrimitiveClass(JNIEnv *env, jclass cls, jstring name) { const char *utfName; jclass result; if (name == NULL) { JNU_ThrowNullPointerException(env, 0 ); return NULL; } utfName = (*env)->GetStringUTFChars(env, name, 0 ); if (utfName == 0 ) return NULL; result = JVM_FindPrimitiveClass(env, utfName); (*env)->ReleaseStringUTFChars(env, name, utfName); return result; } |
當TYPE執行toString時,邏輯如下,則其實是getName函數決定其值,getName通過native方法getName0從JVM層獲取名稱,
1
2
3
4
|
public String toString() { return (isInterface() ? "interface " : (isPrimitive() ? "" : "class " )) + getName(); } |
getName0根據一個數組獲得對應的名稱,JVM根據Java層的Class可得到對應類型的數組下標,比如這里下標為11,則名稱為”long”。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
const char * type2name_tab[T_CONFLICT+ 1 ] = { NULL, NULL, NULL, NULL, "boolean" , "char" , "float" , "double" , "byte" , "short" , "int" , "long" , "object" , "array" , "void" , "*address*" , "*narrowoop*" , "*conflict*" }; |
LongCache內部類
1
2
3
4
5
6
7
8
9
10
|
private static class LongCache { private LongCache(){} static final Long cache[] = new Long[-(- 128 ) + 127 + 1 ]; static { for ( int i = 0 ; i < cache.length; i++) cache[i] = new Long(i - 128 ); } } |
LongCache是Long的一個內部類,它包含了long可能值的Long數組,默認范圍是[-128,127],它不會像Byte類將所有可能值緩存起來,因為long類型范圍很大,將它們全部緩存起來代價太高,而Byte類型就是從-128到127,一共才256個。這里默認只實例化256個Long對象,當Long的值范圍在[-128,127]時則直接從緩存中獲取對應的Long對象,不必重新實例化。這些緩存值都是靜態且final的,避免重復的實例化和回收。
主要方法
parseLong方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
public static long parseLong(String s) throws NumberFormatException { return parseLong(s, 10 ); } public static long parseLong(String s, int radix) throws NumberFormatException { if (s == null ) { throw new NumberFormatException( "null" ); } if (radix < Character.MIN_RADIX) { throw new NumberFormatException( "radix " + radix + " less than Character.MIN_RADIX" ); } if (radix > Character.MAX_RADIX) { throw new NumberFormatException( "radix " + radix + " greater than Character.MAX_RADIX" ); } long result = 0 ; boolean negative = false ; int i = 0 , len = s.length(); long limit = -Long.MAX_VALUE; long multmin; int digit; if (len > 0 ) { char firstChar = s.charAt( 0 ); if (firstChar < '0' ) { // Possible leading "+" or "-" if (firstChar == '-' ) { negative = true ; limit = Long.MIN_VALUE; } else if (firstChar != '+' ) throw NumberFormatException.forInputString(s); if (len == 1 ) // Cannot have lone "+" or "-" throw NumberFormatException.forInputString(s); i++; } multmin = limit / radix; while (i < len) { // Accumulating negatively avoids surprises near MAX_VALUE digit = Character.digit(s.charAt(i++),radix); if (digit < 0 ) { throw NumberFormatException.forInputString(s); } if (result < multmin) { throw NumberFormatException.forInputString(s); } result *= radix; if (result < limit + digit) { throw NumberFormatException.forInputString(s); } result -= digit; } } else { throw NumberFormatException.forInputString(s); } return negative ? result : -result; } |
兩個parseLong方法,主要看第二個即可,第一個參數是待轉換的字符串,第二個參數表示進制數。怎么更好理解這個參數呢?舉個例子,Long.parseLong("100",10)
表示十進制的100,所以值為100,而Long.parseLong("100",2)
表示二進制的100,所以值為4。另外如果Long.parseLong("10000000000000000000",10)會拋出java.lang.NumberFormatException
異常。
該方法的邏輯是首先判斷字符串不為空且進制數在Character.MIN_RADIX和Character.MAX_RADIX之間,即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$。從中可以推出規則了,從左到右遍歷字符串的每個字符,然后乘以進制數,再加上下一個字符,接著再乘以進制數,再加上下個字符,不斷重復,直到最后一個字符。除此之外另外一個不同就是上面的轉換不使用加法來做,全都轉成負數來運算,其實可以看成是等價了,這個很好理解,而為什么要這么做就要歸咎到long類型的范圍了,因為負數Long.MIN_VALUE
變化為正數時會導致數值溢出,所以全部都用負數來運算。
構造函數
1
2
3
4
5
6
|
public Long(String s) throws NumberFormatException { this .value = parseLong(s, 10 ); } public Long( long value) { this .value = value; } |
包含兩種構造函數,分別可以傳入long和String類型。它是通過調用parseLong方法進行轉換的,所以轉換邏輯與上面的parseLong方法一樣。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
static void getChars( long i, int index, char [] buf) { long q; int r; int charPos = index; char sign = 0 ; if (i < 0 ) { sign = '-' ; i = -i; } while (i > Integer.MAX_VALUE) { q = i / 100 ; // really: r = i - (q * 100); r = ( int )(i - ((q << 6 ) + (q << 5 ) + (q << 2 ))); i = q; buf[--charPos] = Integer.DigitOnes[r]; buf[--charPos] = Integer.DigitTens[r]; } int q2; int i2 = ( int )i; while (i2 >= 65536 ) { q2 = i2 / 100 ; r = i2 - ((q2 << 6 ) + (q2 << 5 ) + (q2 << 2 )); i2 = q2; buf[--charPos] = Integer.DigitOnes[r]; buf[--charPos] = Integer.DigitTens[r]; } for (;;) { q2 = (i2 * 52429 ) >>> ( 16 + 3 ); r = i2 - ((q2 << 3 ) + (q2 << 1 )); buf[--charPos] = Integer.digits[r]; i2 = q2; if (i2 == 0 ) break ; } if (sign != 0 ) { buf[--charPos] = sign; } } |
該方法主要做的事情是將某個long型數值放到char數組里面,比如把357按順序放到char數組中。這里面處理用了較多技巧,將long拆成高位4個字節和低位4個字節處理分開處理,while (i >= Integer.MAX_VALUE)
部分就是處理高位的4個字節,每次處理2位數,這里有個特殊的地方((q << 6) + (q << 5) + (q << 2))其實等于q*100,Integer.DigitTens
和Integer.DigitOnes
數組在前面Integer文章中已經講過它的作用了,用來獲取十位和個位。
接著看怎么處理低4個字節,它繼續將4個字節分為高位2個字節和低位2個字節,while (i >= 65536)
部分就是處理高位的兩個字節,每次處理2位數,處理邏輯與高位4個字節的處理邏輯一樣。
再看接下去的低位的兩個字節怎么處理,其實本質也是求余思想,但又用了一些技巧,比如(i * 52429) >>> (16+3)
其實約等于i/10
, ((q << 3) + (q << 1))
其實等于q*10,然后再通過Integer.digits
數組獲取到對應的字符??梢钥吹降臀惶幚頃r它盡量避開了除法,取而代之的是用乘法和右移來實現,可見除法是一個比較耗時的操作,比起乘法和移位。另外也可以看到能用移位和加法來實現乘法的地方也盡量不用乘法,這也說明乘法比起它們更加耗時。而高位處理時沒有用移位是因為做乘法后可能會溢出。
toString方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
public static String toString( long i) { if (i == Long.MIN_VALUE) return "-9223372036854775808" ; int size = (i < 0 ) ? stringSize(-i) + 1 : stringSize(i); char [] buf = new char [size]; getChars(i, size, buf); return new String(buf, true ); } public String toString() { return toString(value); } public static String toString( long i, int radix) { if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) radix = 10 ; if (radix == 10 ) return toString(i); char [] buf = new char [ 65 ]; int charPos = 64 ; boolean negative = (i < 0 ); if (!negative) { i = -i; } while (i <= -radix) { buf[charPos--] = Integer.digits[( int )(-(i % radix))]; i = i / radix; } buf[charPos] = Integer.digits[( int )(-i)]; if (negative) { buf[--charPos] = '-' ; } return new String(buf, charPos, ( 65 - charPos)); } |
一共有3個toString方法,兩個靜態方法一個是非靜態方法,第一個toString方法很簡單,就是先用stringSize得到數字是多少位,再用getChars獲取數字對應的char數組,最后返回一個String類型。第二個toString調用第一個toString,沒啥好說。第三個toString方法是帶了進制信息的,它會轉換成對應進制的字符串。凡是不在2到36進制范圍之間的都會被處理成10進制,我們都知道從十進制轉成其他進制時就是不斷地除于進制數得到余數,然后把余數反過來串起來就是最后結果,所以這里其實也是這樣子做的,得到余數后通過digits數組獲取到對應的字符,而且這里是用負數的形式來運算的。
valueOf方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public static Long valueOf( long l) { final int offset = 128 ; if (l >= - 128 && l <= 127 ) { // will cache return LongCache.cache[( int )l + offset]; } return new Long(l); } public static Long valueOf(String s) throws NumberFormatException { return Long.valueOf(parseLong(s, 10 )); } public static Long valueOf(String s, int radix) throws NumberFormatException { return Long.valueOf(parseLong(s, radix)); } |
有三個valueOf方法,核心邏輯在第一個valueOf方法中,因為LongCache緩存了[-128,127]值的Long對象,對于在范圍內的直接從LongCache的數組中獲取對應的Long對象即可,而在范圍外的則需要重新實例化了。
decode方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
public static Long decode(String nm) throws NumberFormatException { int radix = 10 ; int index = 0 ; boolean negative = false ; Long result; if (nm.length() == 0 ) throw new NumberFormatException( "Zero length string" ); char firstChar = nm.charAt( 0 ); if (firstChar == '-' ) { negative = true ; index++; } else if (firstChar == '+' ) index++; if (nm.startsWith( "0x" , index) || nm.startsWith( "0X" , index)) { index += 2 ; radix = 16 ; } else if (nm.startsWith( "#" , index)) { index ++; radix = 16 ; } else if (nm.startsWith( "0" , index) && nm.length() > 1 + index) { index ++; radix = 8 ; } if (nm.startsWith( "-" , index) || nm.startsWith( "+" , index)) throw new NumberFormatException( "Sign character in wrong position" ); try { result = Long.valueOf(nm.substring(index), radix); result = negative ? Long.valueOf(-result.longValue()) : result; } catch (NumberFormatException e) { String constant = negative ? ( "-" + nm.substring(index)) : nm.substring(index); result = Long.valueOf(constant, radix); } return result; } |
decode方法主要作用是解碼字符串轉成Long型,比如Long.decode("11")
的結果為11;Long.decode("0x11")
和Long.decode("#11")
結果都為17,因為0x和#開頭的會被處理成十六進制;Long.decode("011")
結果為9,因為0開頭會被處理成8進制。
xxxValue方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public byte byteValue() { return ( byte )value; } public short shortValue() { return ( short )value; } public int intValue() { return ( int )value; } public long longValue() { return value; } public float floatValue() { return ( float )value; } public double doubleValue() { return ( double )value; } |
包括shortValue、intValue、longValue、byteValue、floatValue和doubleValue等方法,其實就是轉換成對應的類型。
hashCode方法
1
2
3
4
5
6
|
public int hashCode() { return Long.hashCode(value); } public static int hashCode( long value) { return ( int )(value ^ (value >>> 32 )); } |
可以看到hashCode方法返回的事int類型,首先將long型值無符號右移32位,再和原來的值進行異或運算,最后返回int類型值。
hashCode方法
1
2
3
4
5
6
|
public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false ; } |
比較是否相同時先判斷是不是Long類型再比較值。
compare方法
1
2
3
|
public static int compare( long x, long y) { return (x < y) ? - 1 : ((x == y) ? 0 : 1 ); } |
x小于y則返回-1,相等則返回0,否則返回1。
無符號轉換
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
private static BigInteger toUnsignedBigInteger( long i) { if (i >= 0L) return BigInteger.valueOf(i); else { int upper = ( int ) (i >>> 32 ); int lower = ( int ) i; return (BigInteger.valueOf(Integer.toUnsignedLong(upper))).shiftLeft( 32 ). add(BigInteger.valueOf(Integer.toUnsignedLong(lower))); } } public static String toUnsignedString( long i) { return toUnsignedString(i, 10 ); } public static String toUnsignedString( long i, int radix) { if (i >= 0 ) return toString(i, radix); else { switch (radix) { case 2 : return toBinaryString(i); case 4 : return toUnsignedString0(i, 2 ); case 8 : return toOctalString(i); case 10 : long quot = (i >>> 1 ) / 5 ; long rem = i - quot * 10 ; return toString(quot) + rem; case 16 : return toHexString(i); case 32 : return toUnsignedString0(i, 5 ); default : return toUnsignedBigInteger(i).toString(radix); } } } |
toUnsignedBigInteger方法將long轉成BigInteger類型,主要用BigInteger.valueOf進行轉換,如果小于0則需要先轉成高4字節和低4字節,然后再轉換。
toUnsignedString方法中,對于大于0的long值直接用toString轉換,而小于0的則要按照進制不同分別做不同處理。
bitCount方法
1
2
3
4
5
6
7
8
9
10
|
public static int bitCount( long i) { // HD, Figure 5-14 i = i - ((i >>> 1 ) & 0x5555555555555555L); i = (i & 0x3333333333333333L) + ((i >>> 2 ) & 0x3333333333333333L); i = (i + (i >>> 4 )) & 0x0f0f0f0f0f0f0f0fL; i = i + (i >>> 8 ); i = i + (i >>> 16 ); i = i + (i >>> 32 ); return ( int )i & 0x7f ; } |
該方法主要用于計算二進制數中1的個數。一看有點懵,都是移位和加減操作。先將重要的列出來,0x5555555555555555L等于0101010101010101010101010101010101010101010101010101010101010101,0x3333333333333333L等于0011001100110011001100110011001100110011001100110011001100110011,0x0f0f0f0f0f0f0f0fL等于0000111100001111000011110000111100001111000011110000111100001111。它的核心思想就是先每兩位一組統計看有多少個1,比如10011111則每兩位有1、1、2、2個1,記為01011010,然后再算每四位一組看有多少個1,而01011010則每四位有2、4個1,記為00100100,接著每8位一組就為00000110,接著16位,32位,64位,最終在與0x7f進行與運算,得到的數即為1的個數。
highestOneBit方法
1
2
3
4
5
6
7
8
9
10
|
public static long highestOneBit( long i) { // HD, Figure 3-1 i |= (i >> 1 ); i |= (i >> 2 ); i |= (i >> 4 ); i |= (i >> 8 ); i |= (i >> 16 ); i |= (i >> 32 ); return i - (i >>> 1 ); } |
該方法返回i的二進制中最高位的1,其他全為0的值。比如i=10時,二進制即為1010,最高位的1,其他為0,則是1000。如果i=0,則返回0。如果i為負數則固定返回-2147483648,因為負數的最高位一定是1,即有1000,0000,0000,0000,0000,0000,0000,0000。這一堆移位操作是什么意思?其實也不難理解,將i右移一位再或操作,則最高位1的右邊也為1了,接著再右移兩位并或操作,則右邊1+2=3位都為1了,接著1+2+4=7位都為1,直到1+2+4+8+16+32=63都為1,最后用i - (i >>> 1)自然得到最終結果。
lowestOneBit方法
1
2
3
|
public static long lowestOneBit( long i) { return i & -i; } |
與highestOneBit方法對應,lowestOneBit獲取最低位1,其他全為0的值。這個操作較簡單,先取負數,這個過程需要對正數的i取反碼然后再加1,得到的結果和i進行與操作,剛好就是最低位1其他為0的值了。
numberOfLeadingZeros方法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public static int numberOfLeadingZeros( long i) { if (i == 0 ) return 64 ; int n = 1 ; int x = ( int )(i >>> 32 ); if (x == 0 ) { n += 32 ; x = ( int )i; } if (x >>> 16 == 0 ) { n += 16 ; x <<= 16 ; } if (x >>> 24 == 0 ) { n += 8 ; x <<= 8 ; } if (x >>> 28 == 0 ) { n += 4 ; x <<= 4 ; } if (x >>> 30 == 0 ) { n += 2 ; x <<= 2 ; } n -= x >>> 31 ; return n; } |
該方法返回i的二進制從頭開始有多少個0。i為0的話則有64個0。這里處理其實是體現了二分查找思想的,先看高32位是否為0,是的話則至少有32個0,否則左移16位繼續往下判斷,接著右移24位看是不是為0,是的話則至少有16+8=24個0,以此類推,直到最后得到結果。
numberOfTrailingZeros方法
1
2
3
4
5
6
7
8
9
10
11
|
public static int numberOfTrailingZeros( long i) { int x, y; if (i == 0 ) return 64 ; int n = 63 ; y = ( int )i; if (y != 0 ) { n = n - 32 ; x = y; } else x = ( int )(i>>> 32 ); y = x << 16 ; if (y != 0 ) { n = n - 16 ; x = y; } y = x << 8 ; if (y != 0 ) { n = n - 8 ; x = y; } y = x << 4 ; if (y != 0 ) { n = n - 4 ; x = y; } y = x << 2 ; if (y != 0 ) { n = n - 2 ; x = y; } return n - ((x << 1 ) >>> 31 ); } |
與前面的numberOfLeadingZeros方法對應,該方法返回i的二進制從尾開始有多少個0。它的思想和前面的類似,也是基于二分查找思想,詳細步驟不再贅述。
reverse方法
1
2
3
4
5
6
7
8
9
|
public static long reverse( long i) { i = (i & 0x5555555555555555L) << 1 | (i >>> 1 ) & 0x5555555555555555L; i = (i & 0x3333333333333333L) << 2 | (i >>> 2 ) & 0x3333333333333333L; i = (i & 0x0f0f0f0f0f0f0f0fL) << 4 | (i >>> 4 ) & 0x0f0f0f0f0f0f0f0fL; i = (i & 0x00ff00ff00ff00ffL) << 8 | (i >>> 8 ) & 0x00ff00ff00ff00ffL; i = (i << 48 ) | ((i & 0xffff0000L) << 16 ) | ((i >>> 16 ) & 0xffff0000L) | (i >>> 48 ); return i; } |
該方法即是將i進行反轉,反轉就是第1位與第64位對調,第二位與第63位對調,以此類推。它的核心思想是先將相鄰兩位進行對換,比如10100111對換01011011,接著再將相鄰四位進行對換,對換后為10101101,接著將相鄰八位進行對換,最后把64位中中間的32位對換,然后最高16位再和最低16位對換。
toHexString和toOctalString方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public static String toHexString( long i) { return toUnsignedString0(i, 4 ); } public static String toOctalString( long i) { return toUnsignedString0(i, 3 ); } public static String toBinaryString( long i) { return toUnsignedString0(i, 1 ); } static String toUnsignedString0( long val, int shift) { int mag = Long.SIZE - Long.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1 )) / shift), 1 ); char [] buf = new char [chars]; formatUnsignedLong(val, shift, buf, 0 , chars); return new String(buf, true ); } static int formatUnsignedLong( long val, int shift, char [] buf, int offset, int len) { int charPos = len; int radix = 1 << shift; int mask = radix - 1 ; do { buf[offset + --charPos] = Integer.digits[(( int ) val) & mask]; val >>>= shift; } while (val != 0 && charPos > 0 ); return charPos; } |
這幾個方法類似,合到一起講??疵志椭擂D成2進制、8進制和16進制的字符串。可以看到都是間接調用toUnsignedString0方法,該方法會先計算轉換成對應進制需要的字符數,然后再通過formatUnsignedInt方法來填充字符數組,該方法做的事情就是使用進制之間的轉換方法來獲取對應的字符。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:http://blog.csdn.net/wangyangzhizhou/article/details/78026810