基本數(shù)據(jù)類型與引用類型
????????Java是面向?qū)ο蟮恼Z(yǔ)言,在java中一切都可以視為對(duì)象。然而,為什么卻會(huì)引入基本數(shù)據(jù)類型這樣一個(gè)明顯不是對(duì)象的元素呢?個(gè)人覺(jué)得有兩個(gè)方面的原因:一是邏輯上的必然結(jié)果;二是對(duì)計(jì)算機(jī)本身結(jié)構(gòu)的妥協(xié)。
????????面向?qū)ο缶幊痰幕驹瓌t:一切都是對(duì)象;對(duì)象可以分解為對(duì)象(我胡說(shuō)的,沒(méi)有驗(yàn)證過(guò))。這意味著,對(duì)象的組成成分也是對(duì)象,每個(gè)對(duì)象都是由其他對(duì)象構(gòu)成的。然而在實(shí)際編程時(shí),卻是自底向上的構(gòu)建更復(fù)雜的對(duì)象,而不是自頂向下的逐步分解對(duì)象。但是在這個(gè)規(guī)則下,卻沒(méi)辦法找到這樣底層對(duì)象,因?yàn)樗€可以分解為更底層的。這樣,在邏輯上就陷入了尋找最細(xì)粒度對(duì)象的死循環(huán)中。
????????解決辦法就是破壞規(guī)則:
- 引入一種不可分割的元素,它不是對(duì)象,并且最“底層”的對(duì)象完全由這種元素構(gòu)成,這樣就破壞了規(guī)則一;
- 或者使一些對(duì)象不可以繼續(xù)分割,其他對(duì)象都有這種對(duì)象構(gòu)建而成,這就破壞了規(guī)則二。兩種方案的共同點(diǎn)是終止“對(duì)象可以無(wú)限分割”這個(gè)死循環(huán)。
????????顯然Java采用了第一種方案,在語(yǔ)法中加入了基本數(shù)據(jù)類型這種非對(duì)象元素。Java的前輩,SmallTalk語(yǔ)言采用了方案二。表面上看,SmallTalk中似乎一切都是對(duì)象了,被稱為“純面向?qū)ο蟆闭Z(yǔ)言。然而,這是構(gòu)建在一種特殊對(duì)象基礎(chǔ)之上的,這些基礎(chǔ)對(duì)象是否真的符合對(duì)象的定義值得懷疑。其他所謂“純面向?qū)ο蟆钡恼Z(yǔ)言同理。
????????Java采用方案一的另一個(gè)原因是受制于計(jì)算機(jī)的結(jié)構(gòu)。長(zhǎng)期實(shí)踐中,編程領(lǐng)域基本上形成了一些基本的數(shù)據(jù)類型,如int,float,char等,這些基本數(shù)據(jù)類型可以被計(jì)算機(jī)很好的支持,甚至計(jì)算機(jī)中的很多指令都是和這些數(shù)據(jù)類型耦合的。所以任何一門編程語(yǔ)言,都逃脫不了基本數(shù)據(jù)類型的限制。Java在此基礎(chǔ)上,規(guī)定了8種基本數(shù)據(jù)類型,作為所有對(duì)象的基礎(chǔ);當(dāng)然java的基本數(shù)據(jù)類型是與平臺(tái)無(wú)關(guān)的,屏蔽了底層細(xì)節(jié),比如int類型無(wú)論在哪種機(jī)器上對(duì)外表現(xiàn)都是四個(gè)字節(jié)。其他“純面向?qū)ο蟆钡恼Z(yǔ)言,最底層對(duì)象本質(zhì)上就是這些基本數(shù)據(jù)類型,只不過(guò)稍微加了一層包裝,讓其看起來(lái)像個(gè)對(duì)象而已。
????????Java為什么沒(méi)采用方案二呢?這是考慮到了執(zhí)行效率,直接使用計(jì)算機(jī)原生的數(shù)據(jù)類型,比將其包裝成對(duì)象運(yùn)算要快得多——因?yàn)樵鷶?shù)據(jù)類型可以直接保存到棧上,而對(duì)象則保存在堆上,通過(guò)保存到棧上的指針或引用去獲取——顯然獲取對(duì)象數(shù)據(jù)因?yàn)槔@了一個(gè)彎,而損失了一部分效率。
基本數(shù)據(jù)類型
在java中一共有8種基本數(shù)據(jù)類型,其中整數(shù)類型有四種:byte,short,int,long;浮點(diǎn)數(shù)類型:float,double;布爾類型:boolean;字符類型:char。
byte
byte類型表示一個(gè)整數(shù), 一個(gè)byte類型的變量可以存放一個(gè)字節(jié)的數(shù)據(jù),一個(gè)字節(jié)等于8個(gè)比特,即1 Byte = 8 bit,所以可以表示的數(shù)的范圍就是0000 0000 ~ 1111 1111。而java中所有的數(shù)字類型都是有符號(hào)數(shù),于是開(kāi)頭的比特位用來(lái)表示正負(fù),0代表正數(shù),1代表負(fù)數(shù)。所以實(shí)際能表示的數(shù)的范圍是:1111 1111 ~ 0111 1111,用十進(jìn)制表示就是 -127 ~ 127。這好像跟教材說(shuō)的不一樣:難道不應(yīng)該是 -128 ~ 127 ?這是因?yàn)閯偛潘鍪褂玫氖嵌M(jìn)制的原碼表示法,簡(jiǎn)單直接,容易理解。然而Java使用的是補(bǔ)碼表示法,在形式上,負(fù)數(shù)需要在原碼的基礎(chǔ)上將各個(gè)數(shù)據(jù)bit位取反(不包括符號(hào)bit位),然后加1;而正數(shù)繼續(xù)使用原碼。于是 -127應(yīng)該用二進(jìn)制表示應(yīng)該是1000 0001,127則是0111 1111。1000 0001可以繼續(xù)減1得到1000 0000, 所以下限應(yīng)該是 -128(即-127 - 1);但0111 1111卻不能再加1了,因?yàn)?br>
0111 1111 + 1 = 1000 0000 = -127
這意味著,一旦采用了補(bǔ)碼表示法(限定二進(jìn)制長(zhǎng)度為8位),從邏輯上,1000 0000只能表示-127, 而不是128。
0 = -127 + 127 = 1000 0001 + 0111 1111 = 1 0000 0000 = 0000 0000 (溢出,舍去最高位)
-1 = -128 + 127 = 1000 0000 + 0111 1111 = 1111 1111
注意,這里說(shuō)的僅僅是補(bǔ)碼表示法的表面運(yùn)算規(guī)則,而底層原理請(qǐng)參考這篇文章。
byte類型是最小的數(shù)據(jù)類型,只有一個(gè)字節(jié),即8個(gè)比特。雖然表示信息的最小單位是比特,但是計(jì)算機(jī)操作信息的最小單位卻是字節(jié),即每次運(yùn)算時(shí),都是一次性操作至少一個(gè)字節(jié),因?yàn)橐员忍貫椴僮鲉挝坏脑挘速M(fèi)效率了。所以,無(wú)論是內(nèi)存還是硬盤,或者CPU,傳輸信息時(shí),都是以字節(jié)為單位,即使存在一定的浪費(fèi)(有些信息用不了一個(gè)字節(jié)的容量)。
在實(shí)際編程中,很少有直接操作byte變量的場(chǎng)景;使用byte一般都是直接定義一個(gè)byte數(shù)組,用來(lái)存放二進(jìn)制數(shù)據(jù),比如典型的IO操作。直接操作byte的畫(huà)風(fēng)可能是這樣的:
public class Byte {
public static void main(String[] args) {
byte b = 128;
}
}
編譯結(jié)果如下:
Byte.java:5: 錯(cuò)誤: 不兼容的類型: 從int轉(zhuǎn)換到byte可能會(huì)有損失
byte b = 128;
^
1 個(gè)錯(cuò)誤
這是因?yàn)檎蛿?shù)字的字面量默認(rèn)都是int類型(4個(gè)字節(jié))的,如果賦值給比int類型短的的類型(byte和short等),這個(gè)字面值的范圍必須要在要求類型的范圍內(nèi),比如byte類型的變量只能賦值-128~127之間的整數(shù),超出這個(gè)范圍就會(huì)編譯報(bào)錯(cuò)。
另外,還可以用十六進(jìn)制數(shù)字直接給整型變量賦值,這是從Java SE7開(kāi)始有的特性,如:
byte a = 0x1f
語(yǔ)法上,十六進(jìn)制數(shù)以“0x”開(kāi)頭,大小寫隨意,后面的十六進(jìn)制數(shù)字同樣如此。這里稍微說(shuō)一下字節(jié)和十六進(jìn)制的關(guān)系,一個(gè)字節(jié)8個(gè)位,而一個(gè)十六進(jìn)制數(shù)字最多要用4個(gè)位表示(2的 4次 方,正好16),故而一個(gè)字節(jié)可以容納兩個(gè)十六進(jìn)制數(shù)字。
short
short類型表示一個(gè)有符號(hào)整數(shù), 一個(gè)short類型的變量可以存放兩個(gè)字節(jié)的數(shù)據(jù),所以可以表示的數(shù)的范圍就是0000 0000 0000 0000 ~ 1111 1111 1111 1111,翻譯成補(bǔ)碼形式的十進(jìn)制就是-32768~32767。
short類型在平時(shí)的編碼中還是很少用到的,沒(méi)有必要為了節(jié)省空間而特意使用short類型。
下面寫個(gè)程序驗(yàn)證一下,short是不是真的只占兩個(gè)字節(jié)。
package com.explore;
public class Size {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().freeMemory());
short[] tmp = new short[10000];
for (int i = 0; i < tmp.length; i++) {
tmp[i] = 1;
}
System.out.println(Runtime.getRuntime().freeMemory());
}
}
上面的程序中,Runtime.freeMemory()方法可以獲取到當(dāng)前jvm進(jìn)程所擁有的空閑內(nèi)存。注意,這個(gè)內(nèi)存是總內(nèi)存,包括堆內(nèi)存和棧內(nèi)存等。第一次打印到第二次打印之間,占用內(nèi)存的包括一個(gè)short類型數(shù)組以及這個(gè)數(shù)組的引用。其中引用,也就是tmp變量分配在棧上,數(shù)組本身分配在堆中。而for循環(huán)中定義的臨時(shí)變量i,在for循環(huán)結(jié)束后就釋放了(google一下局部變量的作用域與生存期),相當(dāng)于沒(méi)有占用內(nèi)存。
執(zhí)行過(guò)程如下:
? java javac com/explore/Size.java
? java java -Xms1025k -Xmx1025k -cp . com.explore.Size
1174728
1154712
執(zhí)行的時(shí)候,通過(guò)-Xms和-Xmx參數(shù)限定了jvm的堆內(nèi)存就是1025K,這個(gè)數(shù)值是能讓jvm啟動(dòng)的最小值(還沒(méi)研究過(guò)這個(gè)值是怎么來(lái)的)。打印結(jié)果表明數(shù)組以及數(shù)組引用一共占據(jù)了20016個(gè)字節(jié)的空間。可以猜到,引用占了16個(gè)字節(jié),數(shù)組占了20000個(gè)字節(jié);而數(shù)組中存放了10000個(gè)short類型的整數(shù),所以每個(gè)short類型的變量占用兩個(gè)字節(jié)。
為了證明上述猜想,將程序中的數(shù)組長(zhǎng)度改為5000,再次執(zhí)行的結(jié)果如下:
? java javac com/explore/Size.java
? java java -Xms1025k -Xmx1025k -cp . com.explore.Size
1174728
1164712
就是一個(gè)二元一次方程組,解出來(lái)即可。
注意,上述程序中,數(shù)組的長(zhǎng)度不可太小,否則測(cè)試將出現(xiàn)偏差,具體原因尚未明確。同樣的道理也可以測(cè)出其他基本類型的實(shí)際容量或大小。 另外,在JDK1.8中,基本類型的包裝類有個(gè)BYTES屬性,直接指明了其對(duì)應(yīng)的基本類型所占據(jù)的字節(jié)數(shù);JDK1.5添加的SIZE屬性也指明了所占據(jù)的位數(shù)。
int
int屬于最常使用的整數(shù)類型,而且java中規(guī)定了整數(shù)的字面量默認(rèn)就是int類型的。int類型占用四個(gè)字節(jié),其能表示的整數(shù)范圍是:0000 0000 0000 0000 0000 0000 0000 0000 ~ 1111 1111 1111 1111 1111 1111 1111 1111,翻譯成十進(jìn)制就是 -2147483648 ~ 2147483647,大概是-21.47億到21.47億,大部分情況下用到的數(shù)不會(huì)超出這個(gè)范圍。
上面提到了字面量這個(gè)概念,什么是字面量?說(shuō)白了,字面量就是值。我們常說(shuō)給變量賦值,變量的本質(zhì)是內(nèi)存的一塊空間,而字面量就是這塊空間存放的具體內(nèi)容。字面量表示的是一個(gè)固定的值,代表了程序中初始的輸入或狀態(tài)。一個(gè)程序中,不可能所有的元素都是變量,它總是需要從外界獲取初始的輸入——網(wǎng)卡,鍵盤,或者直接在代碼中預(yù)先定義。離開(kāi)計(jì)算機(jī)領(lǐng)域,字面量就是人類日常書(shū)寫用到的全部文字,包括數(shù)字以及字符串;只是到了計(jì)算機(jī)領(lǐng)域后,需要嚴(yán)格的和變量等其他編程元素區(qū)別開(kāi)來(lái)。字面量是人類和計(jì)算機(jī)直接溝通交流的方式。
long
long類型也是比較經(jīng)常使用的整數(shù)類型,尤其是數(shù)目比較大以至于int無(wú)法容納時(shí)。long類型占用8個(gè)字節(jié),能表示的整數(shù)范圍是:-9223372036854775808~9223372036854775807,大概是922.33億億,真的好大,我寫代碼遇到過(guò)使用long類型的場(chǎng)合一般也就是各種數(shù)據(jù)庫(kù)表的id字段了。
從編譯器的角度,源代碼就是一堆字符串,字面量和變量都是這堆字符串中的符號(hào)而已,只是有的符號(hào)被編譯成變量有的被編譯成字面量,取決于編譯器的實(shí)現(xiàn)。上面提到j(luò)ava中整數(shù)類型的字面量,其默認(rèn)的類型是int。那么給long類型的變量賦值的時(shí)候,字面量的值如果超過(guò)了int類型多能表示的范圍時(shí),需要在數(shù)字后面添加l或L(建議大寫,容易識(shí)別),以表明這個(gè)數(shù)字字面量是long類型的。如下:
long a = 2147483648L
char
char類型的變量代表一個(gè)字符,雖然在底層存儲(chǔ)的時(shí)候還是二進(jìn)制,然而取出來(lái)解讀的時(shí)候,卻表示成一個(gè)字符。比如下面這段代碼:
char a = 't';
System.out.println(a);
最終打印到屏幕上的,會(huì)是字符“t”。這其中用到了兩種技術(shù):字符編碼和字符集,那是另一個(gè)話題了。
在語(yǔ)法上,字符字面量需要用單引號(hào)括起來(lái),這是因?yàn)樵创a本身已經(jīng)是字符(串)了,所以要在源碼中表示另外的‘字符’,需要加以區(qū)別,告訴編譯器這個(gè)字符是字符字面量,而不是其他語(yǔ)法元素。字符串用雙引號(hào)括起來(lái)是一樣的道理。
char類型和byte類型的大小是一樣的,都是1個(gè)字節(jié)。
boolean
boolean類型表示布爾變量,在java中,它的值只能是兩個(gè)字面量之一:true或false,或者這個(gè)兩個(gè)單詞的全大寫。這種類型表示邏輯判斷中的真假,在C語(yǔ)言中,任何非0值都可以表示為true,而0表示false;或者null表示false,非null表示true;但是在Java中,真值被單獨(dú)抽象出來(lái),成為一個(gè)類型,不再用其他類型“兼職”。boolean類型的大小同樣可以通過(guò)上面的程序測(cè)試出來(lái),結(jié)果是1 byte。
float
float類型的變量用來(lái)存放浮點(diǎn)數(shù),大小為4個(gè)字節(jié)。先說(shuō)下浮點(diǎn)數(shù)在計(jì)算機(jī)中的二進(jìn)制表示法,再確定它所能表示的數(shù)的范圍。參考了阮一峰的文章,浮點(diǎn)數(shù)二進(jìn)制采用科學(xué)計(jì)數(shù)法,公式如下:
V = (-1) ^ s * M * 2 ^ E
其中:
(-1) ^ s決定正負(fù),s為0時(shí)是正數(shù);s為1時(shí)是負(fù)數(shù);
(2) 2 ^ E 代表指數(shù),其中E是無(wú)符號(hào)整數(shù),也就是只包含正數(shù)和0;
(3) M 代表有效數(shù)字,且 1<= M < 2。
float類型占用4個(gè)字節(jié),也就是32個(gè)bit,它們是這樣分配的:第一個(gè)bit代表符號(hào),就是s的值;接下來(lái)的8位代表指數(shù),也就是E的值;剩下的23位代表有效數(shù)字,也就是M的值。
由于M>=1,而E>=0,所以M * 2 ^ E >= 1,這樣就沒(méi)辦法表示0和1之間的小數(shù),所以E的取值必須包含負(fù)數(shù),方法就是增加一個(gè)偏移量P,P的大小是2 ^ (t - 1) - 1,t表示E所占用的bit位數(shù),偏移的方向是負(fù)。也就是,在計(jì)算指數(shù)部分的時(shí)候,實(shí)際的大小為2 ^ (E - P)。對(duì)于float類型,P=2^(8-1) -1 = 127,而E的范圍是0~255,所以冪數(shù)e的范圍是-127~128。
1<= M < 2的意思是,通過(guò)將原數(shù)左移或右移,使得其總是只保留一位整數(shù)數(shù)字,例如原數(shù)是11.01011,可以將其右移一位變成1.101011,對(duì)應(yīng)的指數(shù)部分同時(shí)擴(kuò)大2倍,整體大小保持不變。由于整數(shù)部分是固定的1,于是可以將其省略,M只保存小數(shù)部分,取值的時(shí)候,自動(dòng)加1即可,這樣可以多出來(lái)一個(gè)bit,提高精度。
但是有幾種特殊情況,指數(shù)取偏移的方法就行不通了,需要特殊處理:
(1)E全為1,如果此時(shí)M也全為0,則V表示正負(fù)無(wú)窮大;否則,M不全為0,則V不是一個(gè)數(shù),用NaN表示。這意味著,上面的e不可能取到128。
(2)E全為0,則此時(shí)e = 1 - P = -126,計(jì)算M時(shí)不再加1,這樣是為了表示0以及非常接近0的數(shù)。
舉個(gè)例子:
有個(gè)整數(shù)轉(zhuǎn)換成二進(jìn)制后是101.11001,那么存儲(chǔ)它的時(shí)候,首先移位:1.0111001 * 2 ^ 2,然后指數(shù)部分的E = 2 + 127 = 129,尾數(shù)部分的M = 0111001,符號(hào)是0,所以V = 01000000 10111001 00000000 00000000
逆運(yùn)算也是簡(jiǎn)單的,這里就不舉例了。然后談一談二進(jìn)制和十進(jìn)制小數(shù)之間的轉(zhuǎn)換。比如101.11轉(zhuǎn)換成十進(jìn)制應(yīng)該是:1 * 2 ^ 2 + 0 * 2 ^ 1 + 1 * 2 ^ 0 + 1 * 2 ^ (-1) + 1 * 2 ^ (-2) = 5.75,顯然小數(shù)部分是一個(gè)無(wú)窮級(jí)數(shù)1/2 + 1/4 + 1/8 + 1/16 + 1/32 + ……,用這個(gè)級(jí)數(shù)去無(wú)限逼近想要的小數(shù),但是除了一些特殊的小數(shù)之外,絕大部分都只是近似,而且由于M的長(zhǎng)度是有限的,所以也不可能無(wú)限逼近,只能有限逼近。比如計(jì)算機(jī)其實(shí)無(wú)法真正的表達(dá)0.1,只能用0.0625 + 0.03125 + ...去近似,這也算是二進(jìn)制的弊端吧。
了解了浮點(diǎn)數(shù)在計(jì)算機(jī)中的存儲(chǔ)機(jī)制后,下面將探討一下float類型的范圍,方便起見(jiàn),只討論V > 0的情況,負(fù)數(shù)只需取反即可。首先需要確定float可以表示的最小的正數(shù),顯然指數(shù)的下限只能是2 ^ (-126),尾數(shù)M此時(shí)小于1,所以只保留最后一個(gè)bit位是1即可(其他bit都是0),由上面的例子可知,此時(shí)M = 2 ^ (-23),從而得出V的最小值是2 ^ (-149)。小于這個(gè)數(shù)float就無(wú)能為力了;當(dāng)然,即使大于這個(gè)數(shù),float能表示的數(shù)也是不連續(xù)的,絕大部分只能近似。對(duì)于最大值,指數(shù)部分不可能取到 2 ^ 128,最大只能是2 ^ 127,而尾數(shù)M全為1,整體(加1后)也是小于2的(這個(gè)無(wú)窮級(jí)數(shù)收斂到1),于是最大值就是2 ^ 128(小于)。
參考文章
綜上,float變量V的取值范圍是 -2 ^ 128 < V <= -2 ^ (-149) || V = 0 || 2 ^ (-149) <= V < 2 ^ 128
另外在語(yǔ)法上,浮點(diǎn)數(shù)的字面量,其默認(rèn)類型為double,所以給float類型的變量賦值時(shí),需要在字面量的后面加上大寫或小寫的字母“f”。
計(jì)算機(jī)的存儲(chǔ)資源是有限的,而浮點(diǎn)數(shù)也就是數(shù)學(xué)中的實(shí)數(shù)是無(wú)限的,所以計(jì)算機(jī)中的浮點(diǎn)數(shù)其實(shí)是對(duì)實(shí)數(shù)的近似,而非完全相等,這就產(chǎn)生了精度的問(wèn)題。比如,兩個(gè)浮點(diǎn)數(shù)之間做比較,不能直接使用==運(yùn)算符,這將產(chǎn)生誤差。如下:
System.out.println(1f == 0.99f);
System.out.println(1f == 0.99999999f);
將打印如下結(jié)果:
false
true
再比如浮點(diǎn)數(shù)的運(yùn)算結(jié)果不能輕易取用:
System.out.println(0.01f + 0.05f);
System.out.println(1.0f - 0.9f);
直接取用可能會(huì)造成嚴(yán)重后果,尤其是在商業(yè)計(jì)算中:
0.060000002
0.100000024
double
double類型同float類型一樣,用來(lái)表示浮點(diǎn)數(shù),但是它有8個(gè)字節(jié),所以能表示的數(shù)的范圍更大一些,精度也更高。double類型的8個(gè)字節(jié)是這么使用的: 首bit依舊表示符號(hào),指數(shù)部分占據(jù)11位,剩下的52位是尾數(shù)。
計(jì)算規(guī)則同float,所以可以得出double類型浮點(diǎn)數(shù)的范圍是:
- 2 ^ 1024 < V <= - 2 ^ (- 1074) || V =0 || 2 ^ (- 1074) <= V < 2 ^ 1024
關(guān)于上面提到的精度問(wèn)題,有以下幾種解決方案:
(1)運(yùn)算使用BigDecimal類,犧牲一定的運(yùn)算速度,但是提升了精度;
(2)比較大小,可以直接使用“<”和“>”;
(3)比較相等,不能直接使用“==”,可以考慮Math.abs()方法,包裝類的一些api,如:
Double.compare(d1, d2) == 0)
Double.doubleToLongBits(d1) == Double.doubleToLongBits(d2)
Double.valueOf(d1).equals(d2)
引用類型(對(duì)象)
引用類型的變量并不是對(duì)象的本身,可以將其視為保存了一個(gè)地址,通過(guò)這個(gè)地址,就能找到對(duì)象在內(nèi)存中的起始位置。地址也是數(shù)據(jù),所以引用類型的變量也需要占據(jù)一定的內(nèi)存空間,但是這個(gè)空間有多大呢?這個(gè)問(wèn)題可以等同于,內(nèi)存到底有多大?
但是內(nèi)存的大小不是確定的,隨時(shí)可以通過(guò)添加內(nèi)存條的方式來(lái)擴(kuò)大內(nèi)存,這時(shí)內(nèi)存地址顯然更多了,那么原來(lái)的引用類型變量是否足夠大以便容納新增的地址范圍呢?
通過(guò)上面的測(cè)試程序,可以得出在我的電腦上,引用類型變量的大小是4個(gè)字節(jié)。然而,java是跨平臺(tái)的語(yǔ)言,所以無(wú)論在什么樣的機(jī)器上,引用類型的變量的大小是固定的,都將是4個(gè)字節(jié)。
然而,固定的空間大小如何來(lái)應(yīng)對(duì)內(nèi)存的變化呢?我猜一種可能是,引用類型變量所存儲(chǔ)的地址其實(shí)是抽象的,不是真實(shí)的物理地址,最后將由操作系統(tǒng)映射到真實(shí)物理地址上,所以不用擔(dān)心內(nèi)存大小的變化。