1. 程式人生 > >Java支持的數據類型有哪些?什麽時候自動裝拆箱?

Java支持的數據類型有哪些?什麽時候自動裝拆箱?

回來 ring 但是 cache rep 位置 ++ 提高 自動拆箱

java中的8種基本數據類型:boolean byte char short int float double long

自動拆裝箱的問題引入:

由於在一開始學習java的時候,”萬物皆對象“這種面向對象的看問題方式,時刻圍繞在腦海中。因為靜態的變量和基本數據類型不屬於對象,但是由8種基本數據類型的自動裝拆箱解決了基本數據類型不是對象。

在jdk1.5中引入了自動拆裝箱的新特性,在jdk1.5之前,我們想要使用integer類中的方法,我們先要把int變量變成integer類型,可以通過new Integer(intNumber) 或者調用Integer.valueOf(intNumber)方法

自動裝拆箱何時發生?

1、在調用方法時把基本數據類型作為參數,但是參數的類型是基本數據類型的包裝類時。

在學習過javaSe基礎之後,我們知道通過使用集合框架ArrayList或者Map來添加元素的時候,添加的是Object對象,在這裏引入ArrayList.add()的源碼:

/**

* Appends the specified element to the end of this list.

*

* @param e element to be appended to this list

* @return <tt>true</tt> (as specified by {@link Collection#add})

*/

public boolean add(E e) {

ensureCapacity(size + 1); // Increments modCount!!

elementData[size++] = e;

return true;

}

1

2

3

4

5

6

7

8

9

10

11

對於源碼的解讀:首先我們來看看參數中的(E e),為什麽是E ?而不是Object?因為E代表的是元素(集合中存儲的是元素),我們平時在泛型中可以看到 <T extends List<T>> ,T 代表的是Type 類,再比如鍵值對中的Map<K,V>,K 表示的是Key,V 表示的是Value。E K V 等泛型在使用該參數之前就已經限定好了類型,如果賦值給Object的話,就不用再進行強制類型轉換了。

首先把數組的長度+1,這個操作會導致modCount加一,這個變量的作用就是記錄當前數組被操作的次數,然後直接把參數傳進來的對象賦值給上一次長度位置的元素。返回true表示添加成功

當我們調用ArrayList.add()方法的時候,可以直接調用

ArrayList<Integer> arrayList = new ArrayList<Integer>();

arrayList.add(10);

1

2

我們反編譯這段代碼:

public class AutoboxingTest

{

public AutoboxingTest()

{

}

public static void main(String args[])

{

ArrayList arrayList = new ArrayList();

arrayList.add(Integer.valueOf(100));

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

可以看到,編譯器在編譯的時候,檢測到arrayList.add()需要的是Integer對象,所以把int類型自動裝箱成Integer類型。

2、 給基本數據類型的包裝類賦值為基本數據類型的時候。

我們還是以Integer和int類型的變量作為例子:

public class AutoboxingTest2 {

public static void main(String[] args) {

Integer integer = 10;

}

}

1

2

3

4

5

6

7

繼續反編譯例子:

public class AutoboxingTest2

{

public AutoboxingTest2()

{

}

public static void main(String args[])

{

Integer integer = Integer.valueOf(10);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

什麽時候自動裝箱不起作用?

當我們要調用的方法中存在重載的時候,即基本類型數據作為唯一參數的方法與該基本類型包裝類作為唯一參數的方法重載,這時候自動裝箱不起作用。

例子:

public class InvalidateAutoboxing {

public void print(int num) {

System.out.println("i am int !");

}

public void print(Integer num) {

System.out.println("i am integer!");

}

}

public class InvalidateAutoboxingTest {

public static void main(String[] args) {

InvalidateAutoboxing invalidateAutoboxing = new InvalidateAutoboxing();

invalidateAutoboxing.print(5);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

運行結果:

這裏寫圖片描述

使用自動裝箱拆箱需要註意的地方:

1、循環與自動裝箱拆箱

public class CirculateAndAutoboxingAndAutounboxing {

public static void main(String[] args) {

Integer sum = 0;

for (int i = 0; i < 200; i++) {

sum += i;

}

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

反編譯:

public class CirculateAndAutoboxingAndAutounboxing

{

public CirculateAndAutoboxingAndAutounboxing()

{

}

public static void main(String args[])

{

Integer sum = Integer.valueOf(0);

for(int i = 0; i < 200; i++)

sum = Integer.valueOf(sum.intValue() + i);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

反編譯代碼解讀:由於sum是Integer類型,但是sum+=i 需要sum先自動拆箱成int類型(調用intValue()方法),進行相加之後,再自動裝箱成Integer類型把結果賦給sum。

Integer.valueOf源碼解析:

/**

* Returns a Integer instance representing the specified

* int value.

* If a new Integer instance is not required, this method

* should generally be used in preference to the constructor

* Integer(int), as this method is likely to yield

* significantly better space and time performance by caching

* frequently requested values.

*

* @param i an int value.

* @return a Integer instance representing i.

* @since 1.5

*/

public static Integer valueOf(int i) {

final int offset = 128;

if (i >= -128 && i <= 127) { // must cache

return IntegerCache.cache[i + offset];

}

return new Integer(i);

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

首先我們在源碼說明中看到了該構造器緩存經常使用的數據以減少內存的使用和提高效率,Integer把緩沖區的上限腳標設置成128,如果傳進來的數據i在-128~127之中,直接返回緩沖區中的IntegerCache.cache[i + 128] 位中的元素

IntegerCache的源碼解讀:

private static class IntegerCache {

private IntegerCache(){}

static final Integer cache[] = new Integer[-(-128) + 127 + 1];

static {

for(int i = 0; i < cache.length; i++)

cache[i] = new Integer(i - 128);

}

}

1

2

3

4

5

6

7

8

9

10

IntegerCache是Integer的內部類,並且內部數組的上限為256元素,在這個內部類中使用了靜態代碼塊(在類加載的時只執行一次),把-128~127都緩存在數組中。所以以後調用的時候就可以直接返回Integer對象,而不用return new Integer(i); Integer Short Long的緩沖數組是一樣的,但是Character的範圍為0~127,Double和Float沒有緩沖數組

話又說回來,剛剛我們在分析循環與自動裝拆箱的使用需要註意:當參與的數值不在緩存的範圍內,會產生大量的對象,這樣會產生很多垃圾對象,增加GC的工作壓力。

2、自動裝拆箱與三元運算符造成的空指針異常(NPE)

public class AutounboxingWithConditionalOperator {

public static void main(String[] args) {

HashMap<String, Boolean> hashMap = new HashMap<String, Boolean>();

Boolean b = (hashMap != null ? hashMap.get("test") : false);

}

}

1

2

3

4

5

6

7

8

9

10

運行:

這裏寫圖片描述

發生了空指針異常

如果在Map中添加了test這條數據:

public class AutounboxingWithConditionalOperator {

public static void main(String[] args) {

HashMap<String, Boolean> hashMap = new HashMap<String, Boolean>();

hashMap.put("test", true);

Boolean b = (hashMap != null ? hashMap.get("test") : false);

System.out.println(b);

}

}

1

2

3

4

5

6

7

8

9

10

運行:

這裏寫圖片描述

我們再反編譯剛剛NPE異常的代碼:

public class AutounboxingWithConditionalOperator

{

public AutounboxingWithConditionalOperator()

{

}

public static void main(String args[])

{

HashMap hashMap = new HashMap();

Boolean b = Boolean.valueOf(hashMap == null ? false : ((Boolean)hashMap.get("test")).booleanValue());

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

下面是hashMap.get(key)方法的說明:

Returns the value to which the specified key is mapped,

*or null if this map contains no mapping for the key.

由上面可以看出,由於Map.get(key)方法,如果沒有指定的數據,返回的是null。

再由於三元運算符有如下定義:

The type of a conditional expression is determined as follows:

1、If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.

2、If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.

3、If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.

譯文:

三元運算符的類型由以下情況決定:

1、如果第二和第三個操作結果擁有同樣類型(這個類型可能為null),那麽這個類型就是該三元運算符的結果類型

2、如果第二個操作和第三個操作結果其中一個的結果類型為基本數據類型,另外一個操作結果為可以裝箱轉換成與前面一種類型一致的包裝類,那麽這個三元運算符的運算結果類型為基本數據類型,即把包裝類拆裝。

3、如果在第二、三個操作結果中,有一個為null類型,另外一個為引用類型,那麽這個三元運算符的表達式為引用類型。

綜上所述,對於上面出現的情況,對於一個操作結果為基本類型,另外一個為包裝類型的三元運算符表達式來說,為了避免NEP的產生,可以通過把它們變成同樣類型(參考三元運算符定義的第一條)。

參考資料:

Conditional Operator ? :

Boxing Conversion

The Java? Tutorials about Autoboxing and Unboxing

What is Autoboxing and Unboxing in Java

Java中的自動裝箱與拆箱

要裝箱還是拆封

4.2 自動裝箱、拆箱

Java 自動裝箱和拆箱

Java泛型中K T V E ? object等的含義

自動拆箱導致空指針異常

Java支持的數據類型有哪些?什麽時候自動裝拆箱?