淺析Java中為什么要設(shè)計(jì)包裝類(lèi)
一、為什么需要包裝類(lèi)
在 Java 中,萬(wàn)物皆對(duì)象,所有的操作都要求用對(duì)象的形式進(jìn)行描述。但是 Java 中除了對(duì)象(引用類(lèi)型)還有八大基本類(lèi)型,它們不是對(duì)象。那么,為了把基本類(lèi)型轉(zhuǎn)換成對(duì)象,最簡(jiǎn)單的做法就是將基本類(lèi)型作為一個(gè)類(lèi)的屬性保存起來(lái),也就是把基本數(shù)據(jù)類(lèi)型包裝一下,這也就是包裝類(lèi)的由來(lái)。
這樣,我們先自己實(shí)現(xiàn)一個(gè)簡(jiǎn)單的包裝類(lèi),以包裝基本類(lèi)型 int
為例:
// 包裝類(lèi) MyInt public class MyInt { private int number; // 基本數(shù)據(jù)類(lèi)型 public Int (int number){ // 構(gòu)造函數(shù),傳入基本數(shù)據(jù)類(lèi)型 this.number = number; } public int intValue(){ // 取得包裝類(lèi)中的數(shù)據(jù) return this.number; } }
測(cè)試一下這個(gè)包裝類(lèi):
public static void main(String[] args) { MyInt temp = new Int(100); // 100 是基本數(shù)據(jù)類(lèi)型, 將基本數(shù)據(jù)類(lèi)型包裝后成為對(duì)象 int result = temp.intValue(); // 從對(duì)象中取得基本數(shù)據(jù)類(lèi)型 System.out.println(result); }
當(dāng)然,我們自己實(shí)現(xiàn)的這個(gè)包裝類(lèi)非常簡(jiǎn)單,Java 給我們提供了更完善的內(nèi)置包裝類(lèi):
基本類(lèi)型 | 對(duì)應(yīng)的包裝類(lèi)(位于 java.lang 包中) |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
前 6 個(gè)類(lèi)派生于公共的超類(lèi) Number
,而 Character
和 Boolean
是 Object
的直接子類(lèi)。
來(lái)看看包裝類(lèi)的聲明,以 Integer
為例:
被 final
修飾,也就是說(shuō) Java 內(nèi)置的包裝類(lèi)是無(wú)法被繼承的。
二、裝箱與拆箱
OK,現(xiàn)在我們已經(jīng)知道了,存在基本數(shù)據(jù)類(lèi)型與其對(duì)應(yīng)的包裝類(lèi),那么,他們之間互相的轉(zhuǎn)換操作就稱(chēng)為裝箱與拆箱:
- 裝箱:將基本數(shù)據(jù)類(lèi)型轉(zhuǎn)換成包裝類(lèi)(每個(gè)包裝類(lèi)的構(gòu)造方法都可以接收各自數(shù)據(jù)類(lèi)型的變量)
- 拆箱:從包裝類(lèi)之中取出被包裝的基本類(lèi)型數(shù)據(jù)(使用包裝類(lèi)的 xxxValue 方法)
下面以 Integer
為例,我們來(lái)看看 Java 內(nèi)置的包裝類(lèi)是如何進(jìn)行拆裝箱的:
Integer obj = new Integer(10); // 自動(dòng)裝箱 int temp = obj.intValue(); // 自動(dòng)拆箱
可以看出,和上面我們自己寫(xiě)的包裝類(lèi)使用方式基本一樣,事實(shí)上,Integer
中的這兩個(gè)方法其底層實(shí)現(xiàn)和我們上述寫(xiě)的代碼也是差不多的。
不知道各位發(fā)現(xiàn)沒(méi),value
被聲明為 final
了,也就是說(shuō)一旦構(gòu)造了包裝器,就不允許更改包裝在其中的值。
另外,需要注意的是,這種形式的代碼是 JDK 1.5 以前的!??!JDK 1.5 之后,Java 設(shè)計(jì)者為了方便開(kāi)發(fā)提供了自動(dòng)裝箱與自動(dòng)拆箱的機(jī)制,并且可以直接利用包裝類(lèi)的對(duì)象進(jìn)行數(shù)學(xué)計(jì)算。
還是以 Integer
為例我們來(lái)看看自動(dòng)拆裝箱的過(guò)程:
Integer obj = 10; // 自動(dòng)裝箱. 基本數(shù)據(jù)類(lèi)型 int -> 包裝類(lèi) Integer int temp = obj; // 自動(dòng)拆箱. Integer -> int obj ++; // 直接利用包裝類(lèi)的對(duì)象進(jìn)行數(shù)學(xué)計(jì)算 System.out.println(temp * obj);
看見(jiàn)沒(méi)有,基本數(shù)據(jù)類(lèi)型到包裝類(lèi)的轉(zhuǎn)換,不需要像上面一樣使用構(gòu)造函數(shù),直接 =
就完事兒;同樣的,包裝類(lèi)到基本數(shù)據(jù)類(lèi)型的轉(zhuǎn)換,也不需要我們手動(dòng)調(diào)用包裝類(lèi)的 xxxValue 方法了,直接 =
就能完成拆箱。這也是將它們稱(chēng)之為自動(dòng)的原因。
我們來(lái)看看這段代碼反編譯后的文件,底層到底是什么原理:
Integer obj = Integer.valueOf(10); int temp = obj.intValue();
可以看見(jiàn),自動(dòng)裝箱的底層原理是調(diào)用了包裝類(lèi)的 valueOf
方法,而自動(dòng)拆箱的底層調(diào)用了包裝類(lèi)的 intValue()
方法。
三、不簡(jiǎn)單的 Integer.valueOf
我們上面已經(jīng)看過(guò)了用于自動(dòng)拆箱的 intValue
方法的源碼,非常簡(jiǎn)單。接下來(lái)咱來(lái)看看用于自動(dòng)裝箱的 valueOf
,其他包裝類(lèi)倒沒(méi)什么好說(shuō)的,不過(guò) Integer
中的這個(gè)方法還是有點(diǎn)東西的:
IntegerCache
又是啥,點(diǎn)進(jìn)去看看:
IntegerCache
是 Integer
類(lèi)中的靜態(tài)內(nèi)部類(lèi),綜合這兩段代碼,我們大概也能知道,IntegerCache
其實(shí)就是個(gè)緩存,其中定義了一個(gè)緩沖區(qū) cache
,用于存儲(chǔ) Integer
類(lèi)型的數(shù)據(jù),緩存區(qū)間是 [-128, 127]。
回到 valueOf
的源碼:它首先會(huì)判斷 int 類(lèi)型的實(shí)參 i 是否在可緩存區(qū)間內(nèi),如果在,就直接從緩存 IntegerCache
中獲取對(duì)應(yīng)的 Integer
對(duì)象;如果不在緩存區(qū)間內(nèi),則會(huì) new 一個(gè)新的 Integer
對(duì)象。
結(jié)合這個(gè)特性,我們來(lái)看一個(gè)題目,兩種類(lèi)似的代碼邏輯,但是卻得到完全相反的結(jié)果。:
public static void main(String args[]) { Integer a1 = 127; Integer a2 = 127; System.out.println(a1 == a2); // true Integer b1 = 128; Integer b2 = 128; System.out.println(b1 == b2); // false }
我們知道,==
擁有兩種應(yīng)用場(chǎng)景:
- 對(duì)于引用類(lèi)型來(lái)說(shuō),判斷的是內(nèi)存地址是否相等
- 對(duì)于基本類(lèi)型來(lái)說(shuō),判斷的是值是否相等
從 a1 開(kāi)始看,由于其值在 InterCache
的緩存區(qū)間內(nèi),所以這個(gè) Integer
對(duì)象會(huì)被存入緩存。而在創(chuàng)建 a2 的時(shí)候,由于其值和 a1 相等,所以直接從緩存中取出值為 127 的 Integer
對(duì)象給 a2 使用,也就是說(shuō),a1 和 a2 這兩個(gè) Integer
的對(duì)象引用都指向同一個(gè)地址。
對(duì)于 b1 和 b2 來(lái)說(shuō),由于 128 不在 IntegerCache
的緩存區(qū)間內(nèi),那就只能自己老老實(shí)實(shí)開(kāi)辟空間了,所以 b1 和 b2 指向不同的內(nèi)存地址。
很顯然,由于 InterCache
緩存機(jī)制的存在,可能會(huì)讓我們?cè)诰幊痰臅r(shí)候出現(xiàn)困惑,因此最好使用 .equals
方法來(lái)比較 Integer
值是否相等。Integer
重寫(xiě)了 .equals
方法:
當(dāng)然,其他包裝類(lèi)雖然沒(méi)有緩存機(jī)制,但是也都重載了 .equals
方法,用于根據(jù)值來(lái)判斷是否相等。因此,得出結(jié)論,使用 equals
方法來(lái)比較兩個(gè)包裝類(lèi)對(duì)象的值。
四、Object 類(lèi)可以接收所有數(shù)據(jù)類(lèi)型
綜上,有了自動(dòng)拆裝箱機(jī)制,基本數(shù)據(jù)類(lèi)型可以自動(dòng)的被轉(zhuǎn)為包裝類(lèi),而 Object
是所有類(lèi)的父類(lèi),也就是說(shuō),Object
可以接收所有的數(shù)據(jù)類(lèi)型了(引用類(lèi)型、基本類(lèi)型)?。?!
不信你可以試試,直接用 Object
類(lèi)接收一個(gè)基本數(shù)據(jù)類(lèi)型 int
,完全是可以的。
Object obj = 10; int temp = (Integer) obj;
解釋一下上面這段代碼發(fā)生了什么,下面這張圖很重要,大家仔細(xì)看:
五、包裝類(lèi)在集合中的廣泛使用
其實(shí)包裝類(lèi)最常見(jiàn)的使用就是在集合中,因?yàn)榧喜辉试S存儲(chǔ)基本類(lèi)型的數(shù)據(jù),只能存儲(chǔ)引用類(lèi)型的數(shù)據(jù)。那如果我們想要存儲(chǔ) 1、2、3 這樣的基本類(lèi)型數(shù)據(jù)怎么辦?舉個(gè)例子,我們可以如下聲明一個(gè) Integer
對(duì)象的數(shù)組列表:
ArrayList<Integer> list = new ArrayList<>();
往這個(gè)列表中添加 int
型數(shù)據(jù):
list.add(3);
上面這個(gè)調(diào)用在底層將會(huì)發(fā)生自動(dòng)裝箱操作:
list.add (Integer.valueOf(3));
基本數(shù)據(jù)類(lèi)型 int
會(huì)被轉(zhuǎn)換成 Integer
對(duì)象存入集合中。
我們?cè)賮?lái)從這個(gè)集合中根據(jù)某個(gè)下標(biāo) i 獲取對(duì)應(yīng)的 Integer
對(duì)象,并用基本數(shù)據(jù)類(lèi)型 int
接收:
int n = list.get(i);
上面這個(gè)調(diào)用在底層將會(huì)發(fā)生自動(dòng)拆箱操作:
int n = list.get(i).intValue();
六、數(shù)據(jù)類(lèi)型轉(zhuǎn)換
另外,除了在集合中的廣泛應(yīng)用,包裝類(lèi)還包含一個(gè)重要功能,那就是提供將String型數(shù)據(jù)變?yōu)榛緮?shù)據(jù)類(lèi)型的方法,使用幾個(gè)代表的類(lèi)做說(shuō)明:
Integer
:
Double
:
Boolean
:
這些方法均被 static
標(biāo)識(shí),也就是說(shuō)它們被各自對(duì)應(yīng)的所有對(duì)象共同維護(hù),直接通過(guò)類(lèi)名訪問(wèn)該方法。舉個(gè)例子:
String str = "10"; int temp = Integer.parseInt(str);// String -> int System.out.println(temp * 2); // 20
需要特別注意的是:Character
類(lèi)里面并不存在字符串變?yōu)樽址姆椒ǎ驗(yàn)?String
類(lèi)中已經(jīng)有一個(gè) charAt()
的方法可以根據(jù)索引取出字符內(nèi)容。
以上就是淺析Java中為什么要設(shè)計(jì)包裝類(lèi)的詳細(xì)內(nèi)容,更多關(guān)于Java 設(shè)計(jì)包裝類(lèi)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- java 如何給對(duì)象中的包裝類(lèi)設(shè)置默認(rèn)值
- JAVA基本類(lèi)型包裝類(lèi) BigDecimal BigInteger 的使用
- Java包裝類(lèi)之自動(dòng)裝箱與拆箱
- 淺談Java包裝類(lèi)型Long的==操作引發(fā)的低級(jí)bug
- Java基礎(chǔ)之匿名內(nèi)部類(lèi)、包裝類(lèi)
- java基礎(chǔ)之包裝類(lèi)的介紹及使用
- 詳細(xì)總結(jié)Java基礎(chǔ)類(lèi)和包裝類(lèi)
- Java基礎(chǔ)之包裝類(lèi)
- Java面向?qū)ο笾b類(lèi)的用途與實(shí)際使用
相關(guān)文章
spring boot 添加admin監(jiān)控的方法
這篇文章主要介紹了spring boot 添加admin監(jiān)控的相關(guān)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-02-02Java面試題-實(shí)現(xiàn)復(fù)雜鏈表的復(fù)制代碼分享
這篇文章主要介紹了Java面試題-實(shí)現(xiàn)復(fù)雜鏈表的復(fù)制代碼分享,小編覺(jué)得還是挺不錯(cuò)的,具有參考價(jià)值,需要的朋友可以了解下。2017-10-10Spring Boot中擴(kuò)展XML請(qǐng)求與響應(yīng)的支持詳解
這篇文章主要給大家介紹了關(guān)于Spring Boot中擴(kuò)展XML請(qǐng)求與響應(yīng)的支持的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-09-09spring-boot通過(guò)@Scheduled配置定時(shí)任務(wù)及定時(shí)任務(wù)@Scheduled注解的方法
這篇文章主要介紹了spring-boot通過(guò)@Scheduled配置定時(shí)任務(wù),文中還給大家介紹了springboot 定時(shí)任務(wù)@Scheduled注解的方法,需要的朋友可以參考下2017-11-11Java按照List內(nèi)存儲(chǔ)的對(duì)象的某個(gè)字段進(jìn)行排序的實(shí)例
下面小編就為大家?guī)?lái)一篇Java按照List內(nèi)存儲(chǔ)的對(duì)象的某個(gè)字段進(jìn)行排序的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-12-12