Java基本數據類型及其封裝器的一些千絲萬縷的糾葛

一些概念

想必大家都知道Java的基礎數據類型有:char、byte、short、int、long、float、double、boolean 這幾種,與C/C++等語言不同的是,Java的基礎數據的位數是固定不變的。
????Java作為一門面向對象的語言,自然少不了對象了,因此基礎數據類型都對應存在一個基本類型封裝器類,它們的封裝器分別為:Character、Byte、Short、Integer、Long、Float、Double、Boolean。
????在JDk1.5之前,在基礎數據類型與其封裝器之間的轉化必須手動進行,其中將基本數據類型轉換為封裝器類型需要調用封裝類的靜態方法T.valueOf(),T代表封裝器類型的名稱,而將封裝器類型轉換為基本數據類型需要調用它的實例方法xValue();x代表基礎數據類型的名稱,這個過程比較繁瑣,故從JDK1.5開始,JDK提供了自動裝箱的特性,不再需要手動進行了。所謂裝箱指:將基本數據類型轉換為對應的封裝器類型,拆箱指:將封裝器類型轉換為對應的基本數據類型。eg:
Integer a = 1;//裝箱,底層實現:Integer a = Integer.valueOf(1);
int b = a; //拆箱,底層實現:int b = a.intValue();
eg:

public class PackageAndUnpackageDemo{
    public static void main(String[] args) {
        Integer a = 1;//裝箱,底層實現:Integer a = Integer.valueOf(1);
        int b = a;    //拆箱,底層實現:int b = a.intValue();
    }
}

反編譯結果

PackageAndUnpackageDemo.png

從圖片中我們可以看到,裝拆箱確實是這樣實現的。
????此外,為了提高封裝器的效率,Java將常用的數據緩存起來放到封裝器對象數組里面,裝箱的時候判斷如果數據在緩存的范圍內則從緩存里面取出對象,否則將new一個出來。
Integer相關的源碼如下:

Integer_valueOf.png

我們可以看到,當數字的范圍在low與high之間時,直接從cache對象數組中獲取,否則就new一個對象,cache的源碼如下:

Integer_cache.png

Integer的cache數組與其他的有些區別是,Integer可以自定義數組的上限,而其他封裝器的cache是固定的。
封裝器的緩存的大小以及他們對應的基礎數據類型的范圍見下表:

基本類型 封裝器 字節數 最大值 最小值 緩存范圍
byte Byte 1byte 2^7 - 1 -2^7 -128~127
short Short 2byte 2^15 - 1 -2^15 -128~127
char Character 2byte 2^16 - 1 0 0~127
int Integer 4byte 2^31 - 1 -2^31 -128~127
long Long 8byte 2^63 - 1 -2^63 -128~127
float Float 4byte 3.4e+38 1.4e-45 無緩存
double Double 8byte 1.8e+308 4.9e-324 無緩存
boolean Boolean 1byte/4byte/不明確 - - false、true

我們注意到Boolean與Byte全部被緩存了,boolean類型沒有給出精確的定義,《Java虛擬機規范》給出了4個字節,和boolean數組1個字節的定義,具體還要看虛擬機實現是否按照規范來,所以1個字節、4個字節都是有可能的,這個在這里不做深究。
看到這里想必您現在應該能夠理解下面的一些現象吧:

public class CacheDemo{
    public static void main(String[] args) {
        Integer a = 127;
        Integer b = 127;
        System.out.println(a == b);//true
        Integer c = 128;
        Integer d = 128;
        System.out.println(c == d);//false
        Integer e = -128;
        Integer f = -128;
        System.out.println(e == f);//true
        Integer g = -129;
        Integer h = -129;
        System.out.println(g == h);//false
    }
}

由于127與-128都在緩存數組緩存的范圍內,所以他們是直接從緩存數組中取得的對象,故引用相同,而128與-129不在該范圍內,所以是new出來的,故引用不相同。當然得到這個結果的前提條件是我們沒有指定Integer緩存數組的上限,直接使用它的默認上限127。您可以通過-Djava.lang.Integer.IntegerCache.high=128 指定上限為128,那么運行結果就有所不同了。
java CacheDemo

true
false
true
false

java -Djava.lang.Integer.IntegerCache.high=128 CacheDemo

true
true
true
false

到這里需要提醒一下大家的是,“Java中對引用類型而言 == 比較的是引用,而equals()比較的是內容”這句話其實嚴格意義上來講不準確。
我們不妨看一下對象之源Object中關于equals()的實現

public boolean equals(Object obj) {
    return (this == obj);
}

OK,Object的equals()方法默認還是使用了 “==”,所以要是自定義的對象沒有Override該方法與hashCode()方法,那么比較的也是引用而不是內容了,當然Java的大部分類都重寫了該方法,但是還有一些類如StringBuffer,StringBuilder沒有重寫該方法,這點需要注意。

注意事項

空指針異常

自動的裝拆箱給我們帶來了很大的方便,大部分情況下我們都將封裝器與基礎數據類型混為一談了,但是二者之間畢竟還是存在著一些差別,在某些場景下要是不注意,自動裝拆箱這個語法糖也會給我們帶來一些意想不到的后果。
????由于引用類型的默認值是null,而不能將null賦值給基本數據類型,在拆箱的過程中可能會導致空指針異常,這個需要我們格外注意要加以判斷:
如下面的情形:

import java.util.List;
import java.util.ArrayList;
public class NullPointInUnpackageDemo{
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>(3);
        list.add(0);
        list.add(1);
        list.add(null);
        for (int n:list){
            System.out.println("n="+n);
        }
    }
}

運行結果:

n=0
n=1
Exception in thread "main" java.lang.NullPointerException
    at NullPointInUnpackageDemo.main(NullPointInUnpackageDemo.java:10)

當然例子出現空指針異常的情況我們一眼就能夠看出,但是實際開發過程可能由于疏忽或者不那么直接的拆箱行為,將會帶來意想不到的錯誤,這是我們需要格外注意的。

封裝器帶來的模糊性問題

在方法的重載某些情形,封裝器帶來的使用可能會帶來方法調用的模糊性問題,給我們帶來一些意想不到的bug,比如下面的情形:

public class BugInOverloading{
    public static void test(Integer num){
        System.out.println("Integer num");
    }
    public static void test(long num){
        System.out.println("long num");
    }
    public static void main(String[] args) {
        test(1);
    }
}

您能夠一眼看出會調用哪個方法嗎?好吧,結果可能與您想象中的可能有點出入,運行結果是

long num

也就是說它居然沒有調用test(Integer num),反而直接向上轉型為long 調用了test(long num),這不很費解嗎?對于該現象的解釋是:Java為了向下兼容,保證程序的正確性,在方法重載時先不考慮自動裝拆箱,而是遵循最精確匹配的原則,找最匹配的類型,由于沒有int類型的方法可以調用,但是有long類型的方法,那么根據這個原則,int向上轉型為long了,而不是自動裝箱,這點需要注意,(ps:關于方法重載調用的問題可以參考博主另外的一篇文章 )額,時間不早了,就先這樣吧,以上便是Java基本數據類型及其封裝器的一些千絲萬縷的糾葛。
圉于博主的水平,理解可能有所偏差,還望各位大佬不吝指正!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,067評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,467評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,184評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,582評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,794評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,343評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,096評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,291評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,513評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,026評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,253評論 2 375

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,729評論 18 399
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,692評論 25 708
  • 維權,還是維權,明天維權。我居然很期待和興奮,一點害怕都沒有...
    DeathKnightR閱讀 222評論 0 0
  • 回家的路,平凡的路。 從廣洋湖開車到鑫寶佳園,大概需要五十分鐘。我抬手看了一眼手表,快18:10了,天色漸漸暗了下...
    劉懋偉閱讀 715評論 1 1
  • 1、今天從演講中學到的最重要的概念是:我們可以通過刻意練習提升我們的傾聽能力。演講者介紹了5項提升傾聽能力的方法,...
    小波有話說閱讀 275評論 0 0