Java包裝類的概述與應用
一、包裝類概述
Java有8種基本數(shù)據類型:整型(byte、short、int、long)、浮點型(float、double)、布爾型boolean、字符型char,相對應地,Java提供了8種包裝類Byte、Short、Integer、Long、Float、Double、Boolean、Character。包裝類創(chuàng)建對象的方式就跟其他類一樣。
Integer num = new Integer(0); //創(chuàng)建一個數(shù)值為0的Integer對象
二、包裝類的自動裝箱、自動拆箱機制
上面的構造對象語句實際上是基本數(shù)據類型向包裝類的轉換。在應用中我們經常需要進行基本類型數(shù)據和包裝類對象之間的互轉。
Integer num1 = new Integer(1); //基本數(shù)據類型轉為包裝類 int num2 = num1.intValue(); //包裝類型轉為基本數(shù)據類型 System.out.println(num1 +" "+ num2);
而Java為了方便我們使用,以及出于其他目的如性能調優(yōu),給我們提供了自動裝箱、拆箱機制。這種機制簡化了基本類型和包裝類型的轉換。
//1、包裝類中的自動裝箱拆箱機制 Integer num1 = 1; //自動裝箱 int num2 = num1; //自動拆箱 System.out.println(num1 +" "+ num2);
當使用jad工具對上面的代碼進行反編譯時,結果如下。
Integer integer = Integer.valueOf(1);
int i = integer.intValue();
System.out.println((new StringBuilder()).append(integer).append("\t").append(i).toString());可見,Java編譯器幫我們完成了轉換操作。另外,我們可以看到,除了使用new關鍵字,還可以使用Integer類的valueOf()方法創(chuàng)建一個Integer對象。這兩個方式是有所區(qū)別的,我們下面會說到。
三、包裝類中的緩存機制
前面說到創(chuàng)建包裝類對象有兩種方式:new關鍵字、valueOf()方法。我們來看一段代碼感受一下它們的區(qū)別。
//2、包裝類中的緩存機制 Integer num3 = 10; Integer num4 = 10; Integer num5 = new Integer(20); Integer num6 = new Integer(20); Integer num7 = 128; Integer num8 = 128; System.out.println((num3==num4) +" "+ num3.equals(num4)); System.out.println((num5==num6) +" "+ num5.equals(num6)); System.out.println((num7==num8) +" "+ num7.equals(num8));
運行結果為

我們看下它的反編譯代碼
Integer integer = Integer.valueOf(10);
Integer integer1 = Integer.valueOf(10);
Integer integer2 = new Integer(20);
Integer integer3 = new Integer(20);
Integer integer4 = Integer.valueOf(128);
Integer integer5 = Integer.valueOf(128);
System.out.println((new StringBuilder()).append(integer == integer1).append("\t").append(integer.equals(integer1)).toString());
System.out.println((new StringBuilder()).append(integer2 == integer3).append("\t").append(integer2.equals(integer3)).toString());
System.out.println((new StringBuilder()).append(integer4 == integer5).append("\t").append(integer4.equals(integer5)).toString());首先,我們查看Integer的valueOf()方法的源碼
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}再查看下Integer的內部類IntegerCache的cache數(shù)組成員、low、high成員
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;
}可以發(fā)現(xiàn),只要Integer類第一次被使用到,Integer的靜態(tài)內部類就被加載,加載的時候會創(chuàng)建-128到127的Integer對象,同時創(chuàng)建一個數(shù)組cache來緩存這些對象。當使用valueOf()方法創(chuàng)建對象時,就直接返回已經緩存的對象,也就是說不會再新建對象;當使用new關鍵字or使用valueOf()方法創(chuàng)建小于-128大于127的值對象時,就會創(chuàng)建新對象。
//2、包裝類中的緩存機制 Integer num3 = 10; Integer num4 = 10; Integer num5 = new Integer(20); Integer num6 = new Integer(20); Integer num7 = 128; Integer num8 = 128;
由于num3、num4都小于等于127,它們指向的是同一個緩存的Integer對象,所以用==進行比較的結果是true;num5、num6由于使用new關鍵字指向的是兩個不同的新對象,結果為false;num7、num8雖然是采用自動裝箱的方式,但執(zhí)行valueOf()方法的時候,由于不滿足條件i >= IntegerCache.low && i <= IntegerCache.high,而同樣新建了兩個不同的新對象,結果同樣是false。
接著,我們再來看看源碼中Integer的equals()方法的實現(xiàn)
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}可見equals()方法比較的是Integer對象的值,而不是像==一樣比較的是對象是否是同一個對象。所以,當需要比較兩個Integer對象的值是否相等時,記住要用equals()方法。用==比較的話由于緩存機制的存在,可能產生一些讓人困擾的結果。
此外,在8種包裝類型中,有緩存區(qū)的有Character、Byte、Short、Integer、Long,而且它們的實現(xiàn)方式基本一樣,都是-128到127的緩存范圍。Boolean雖然沒有緩存區(qū),但是因為只有兩個值true、false,所以Boolean在成員變量中就創(chuàng)建了兩個相應的對象。沒有緩存區(qū)的只有Float、Double,之所以沒有原因很簡單,即便是0到1這么小的范圍,浮點數(shù)也有無數(shù)個,使用緩存區(qū)緩存它們不具備可能性和實用性。
緩存區(qū)的存在使得常用的包裝類對象可以得到復用,這有利于提升性能。當我們需要創(chuàng)建新對象的時候再new一個,增加了靈活性。
四、包裝類的四則運算、位運算、比較運算、邏輯運算
1、四則運算和位運算
//四則運算、位運算 Integer num9 = 1; Integer num10 = 2; Integer num11 = num9 + num10; Short num12 = 5; Integer num13 = num9 + num12; Long num14 = num9 + 10L; System.out.println(num9 << 1); //位運算 System.out.println(num9 +" "+ num10 +" "+ num11 +" "+ num12 +" "+ num13 +" "+ num14);

反編譯結果如下
Integer integer = Integer.valueOf(1);
Integer integer1 = Integer.valueOf(2);
Integer integer2 = Integer.valueOf(integer.intValue() + integer1.intValue());
Short short1 = Short.valueOf((short)5);
Integer integer3 = Integer.valueOf(integer.intValue() + short1.shortValue());
Long long1 = Long.valueOf((long)integer.intValue() + 10L);
System.out.println(integer.intValue() << 1);
System.out.println((new StringBuilder()).append(integer).append("\t").append(integer1).append("\t").append(integer2).append("\t").append(short1).append("\t").append(integer3).append("\t").append(long1).toString());
可以看到Integer num11 = num9 + num10; 這一句被劃分為3個步驟:將兩個Integer對象分別進行拆箱;將拆箱得到的兩個int數(shù)值相加求其和;將和值進行裝箱,從而將num11指向緩存數(shù)組中值為3的Integer對象。
而Short num12 = 5; 這一句則先將5強制轉換成short類型,再將其裝箱把值為5的Short對象的引用賦給num12。
而Integer num13 = num9 + num12; 這一句除了Integer num11 = num9 + num10;的3個步驟,中間還有short+int=int的類型自動提升的過程。
而Long num14 = num9 + 10L; 這一句Integer num11 = num9 + num10;的3個步驟,中間還有強制類型轉換的過程。需要注意的是,如果是Long num14 = num9 + num10; 的話就會出現(xiàn)類型不匹配的錯誤,因為num9、num10拆箱之后相加的和是int類型,而Long.valueOf(long)需要的形參是long類型,自然會出錯。我們也可以看到,當包裝類型對象和基本類型數(shù)據進行四則運算的時候,對象是會被拆箱的,然后再按基本類型數(shù)據的運算規(guī)則進行運算。
另外,如果僅僅是打印兩個包裝類型對象求和的結果,是不會有將和值重新轉換成該包裝類型的步驟的,如下面所示
System.out.println(num9 + num10);
System.out.println(integer.intValue() + integer1.intValue());
這里還需要注意一點,盡管基本類型與自動類型提升/強制類型轉換,包裝類是沒有類似的用法的。下面的做法是錯的。
Short num3 = 10; Integer num4 = num3; //錯誤: 不兼容的類型: Short無法轉換為Integer Long num5 = (Long)num4; //錯誤: 不兼容的類型: Integer無法轉換為Long Double num6 = num5; //錯誤: 不兼容的類型: Long無法轉換為Double
小結:不同包裝類型對象是不能直接轉換的,不過有兩種途徑可以代替:一種是上面討論的不同包裝類對象進行四則運算后賦給某一種類型;另一種就是利用包裝類的方法
Integer a = 20; Long b = a.longValue(); Short c = b.shortValue(); System.out.println(a +" "+ b +" "+ c);
2、比較運算和邏輯運算
Integer num9 = 100; Integer num10 = 200; Short num11 = 50; Long num12 = 50L; System.out.println((num9<num10) +" "+ (num9<200) +" "+ (num9<num11) +" "+ (num9<num12) +" "+ (num9<10L));
反編譯結果為
Integer integer = Integer.valueOf(100);
Integer integer1 = Integer.valueOf(200);
Short short1 = Short.valueOf((short)50);
Long long1 = Long.valueOf(50L);
System.out.println((new StringBuilder()).append(integer.intValue() < integer1.intValue()).append("\t").append(integer.intValue() < 200).append("\t").append(integer.intValue() < short1.shortValue()).append("\t").append((long)integer.intValue() < long1.longValue()).append("\t").append((long)integer.intValue() < 10L).toString());
可以看到,兩個同類型的包裝類對象進行比較時比較的其實是各自的基本類型數(shù)值,如num9 < num10;兩個不同類型的包裝類對象進行比較時則在比較基本類型數(shù)值之前,會有類型提升or強制類型轉換,如num9 < num11,num9 < num12。
當想比較兩個對象是否相等時,注意要使用equals()方法,從前面的討論也知道,使用==的話比較的其實是引用的對象是否同一個,一般不滿足我們的需求。
Integer num13 = new Integer(100); System.out.println(num9.equals(num13) +" "+ num9.equals(50));
反編譯結果為
Integer integer2 = new Integer(100);
System.out.println((new StringBuilder()).append(integer.equals(integer2)).append("\t").append(integer.equals(Integer.valueOf(50))).toString());
邏輯運算舉例:
System.out.println((num9&1));
反編譯結果為
System.out.println(integer.intValue() & 1);
五、包裝類作為方法的形參、返回值
//包裝類作為方法的形參、返回值
public static Integer intToInteger(int i) {
return i;
}
public static int integerToInt(Integer i) {
return i;
}反編譯結果為
public static Integer intToInteger(int i)
{
return Integer.valueOf(i);
}
public static int integerToInt(Integer integer)
{
return integer.intValue();
}六、包裝類作為集合的元素
//包裝類作為集合元素
List list = new ArrayList();
list.add(1);
list.add(new Object());
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}反編譯結果為
ArrayList arraylist = new ArrayList(); arraylist.add(Integer.valueOf(1)); arraylist.add(new Object()); for(Iterator iterator = arraylist.iterator(); iterator.hasNext(); System.out.println(iterator.next()));
可以發(fā)現(xiàn),雖然集合元素要求是對象,add()方法的形參也是對象(public boolean add(E e)),但由于自動裝箱,基本數(shù)據類型也可以直接加入集合中。
List<Integer> list = new ArrayList<>();
for (int i=0; i<5; i++) {
list.add(i);
}
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}反編譯結果為
ArrayList arraylist = new ArrayList();
for(int i = 0; i < 5; i++)
arraylist.add(Integer.valueOf(i));
for(Iterator iterator = arraylist.iterator(); iterator.hasNext(); System.out.println(iterator.next()));七、包裝類使用過程中有可能引起的空指針異常
//注意包裝類可能產生的空引用異常 Boolean flag1 = false; System.out.println(flag1?"命題為真":"命題為假"); Boolean flag2 = null; System.out.println(flag2?"命題為真":"命題為假"); Boolean flag3 = true;
運行結果為

這里只是簡單演示空指針異常。平時使用時需要注意這一點,比如當Boolean的對象作為形參時,在方法執(zhí)行體的頭部需要做下null檢測。
上述代碼的反編譯結果為
Boolean boolean1 = Boolean.valueOf(false);
System.out.println(boolean1.booleanValue() ? "\u547D\u9898\u4E3A\u771F" : "\u547D\u9898\u4E3A\u5047");
Boolean boolean2 = null;
System.out.println(boolean2.booleanValue() ? "\u547D\u9898\u4E3A\u771F" : "\u547D\u9898\u4E3A\u5047");
Boolean boolean3 = Boolean.valueOf(true);
可見三目運算符的條件表達式的位置一定是boolean值,如果你傳入的是Boolean對象,則會自動拆箱轉換為boolean值。
另外,三目運算符的其他兩個表達式位置也是如此,會把包裝類對象轉換為相應的基本類型對象。
八、為什么需要包裝類?有了包裝類又為什么要保留基本數(shù)據類型?(包裝類的優(yōu)缺點)
為什么需要包裝類?
首先,Java語言是一個面向對象的語言,但是Java中的基本數(shù)據類型卻是不面向對象的,將每個基本數(shù)據類型設計一個對應的類進行代表,這種方式增強了Java面向對象的性質。
其次,如果僅僅有基本數(shù)據類型,那么在實際使用時將存在很多的不便,很多地方都需要使用對象而不是基本數(shù)據類型。比如,在集合類中,我們是無法將int 、double等類型放進去的,因為集合的容器要求元素是Object類型。而包裝類型的存在使得向集合中傳入數(shù)值成為可能,包裝類的存在彌補了基本數(shù)據類型的不足。
此外,包裝類還為基本類型添加了屬性和方法,豐富了基本類型的操作。如當我們想知道int取值范圍的最小值,我們需要通過運算,如下面所示,但是有了包裝類,我們可以直接使用Integer.MAX_VALUE即可。
//求int的最大值
int max = 0;
int flag = 1;
for (int i=0; i<31; i++) {
max += flag;
flag = flag << 1;
}
System.out.println(max +" "+ Integer.MAX_VALUE); //2147483647 2147483647為什么要保留基本數(shù)據類型?
我們都知道在Java語言中,用new關鍵字創(chuàng)建的對象是存儲在堆里的,我們通過棧中的引用來使用這些對象,所以,對象本身來說是比較消耗資源的。對于經常用到的類型,如int等,如果我們每次使用這種變量的時候都需要new一個對象的話,就會比較笨重了。所以,Java提供了基本數(shù)據類型,這種數(shù)據的變量不需要使用new在堆上創(chuàng)建,而是直接在棧內存中存儲,因此會更加高效。
到此這篇關于Java包裝類的文章就介紹到這了,更多相關Java包裝類內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring Cache和EhCache實現(xiàn)緩存管理方式
這篇文章主要介紹了Spring Cache和EhCache實現(xiàn)緩存管理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
Springboot項目中定時任務的四種實現(xiàn)方式詳解
Spring的@Scheduled注解是一種非常簡單和便捷的實現(xiàn)定時任務的方式,通過在方法上添加@Scheduled注解,我們可以指定方法在特定的時間間隔或固定的時間點執(zhí)行,本文給大家介紹Springboot項目中定時任務的四種實現(xiàn)方式,感興趣的的朋友一起看看b2024-02-02
詳解spring boot實現(xiàn)websocket
這篇文章主要介紹了詳解spring boot實現(xiàn)websocket,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06

