自動拆箱&自動裝箱以及String 和基本資料型別封裝類生成的物件是否相等
自動拆箱(unboxing)&自動裝箱(boxing)
@author 李東秀|| qq:1028659927
本文主要為自己理解所做的學習筆記,如有不對的地方,
望各位看官不吝指出,程式碼執行環境:Ubuntu 14.04,jdk1.7版本
在jdk 1.5之前,如果你想要定義一個value為100的Integer物件,則需要如下定義:
Integer i=new Integer (100);
但有了自動拆裝箱之後就可以直接把基本型別賦值給Integer物件。
int intNum1=100;//普通常量 Integer intNum2=intNum1;//自動裝箱 int intNum3=intNum2;//自動拆箱 Integer intNum4=100;//自動裝箱
上面的程式碼中,intNum2為一個Integer型別的例項,intNum1為Java中的基礎資料型別,
將intNum1賦值給intNum2便是自動裝箱;而將intNum2賦值給intNum3則是自動拆箱。
Java為我們提供了八種基本資料型別: boolean byte char shrot int long float double ,所生成的變數相當於常量;
對應基本型別包裝類:Boolean Byte Character Short Integer Long Float Double。
自動拆箱和自動裝箱定義:
自動裝箱是將一個java定義的基本資料型別賦值給相應封裝類的變數。
拆箱與裝箱是相反的操作,自動拆箱則是將一個封裝類的變數賦值給相應基本資料型別的變數。
自動封箱和自動裝箱的原理
自動封箱和自動拆箱是java自動幫我們完成的,通過除錯可以看到執行過程,如果除錯過程中無法進入原始碼中的函式,可能因為設定的是JRE執行環境,替換成jdk就可以了,【Window】-【Preference】-【java】-【Installed JREs】
int intNum1=100;//普通常量
Integer intNum2=intNum1;//自動裝箱
int intNum3=intNum2;//自動拆箱
Integer intNum4=100;//自動裝箱
在每一行程式碼新增斷點,進入除錯模式,
第一行程式碼無法進入函式,說明中間沒有呼叫其他的函式,
第二行程式碼和第四行程式碼進入了Integer.valueOf(int);
第三行程式碼進入了函式Integer.intValue();
第二行程式碼進入如下程式碼:
public static Integer valueOf(int i) {
//對於Integer型別變數,如果i在IntegerCache.low-IntegerCache.low之間,
//就直接在快取中取出i的Integer型別物件 ,而不需要生成新的物件。
if (i >= IntegerCache.low && i <= IntegerCache.low
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);//否則在堆上建立新的物件
}
此程式碼會去判斷是否在可快取的範圍內(if (i >= IntegerCache.low && i <= IntegerCache.high)),如果滿足快取條件進入return IntegerCache.cache[i + (-IntegerCache.low)];否則new 一個新的物件。所以可以看到IntegerintNum2=intNum1;並不是每次都會生成新物件。
第三行進入intValue函式直接返回基本型別。
public int intValue() {
return value;
}//其他型別則會呼叫xxValue()
下面是快取資料的程式碼,說明了快取的範圍
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
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;
}
private IntegerCache() {}
}
注意此處程式碼:
String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
說明integerCacheHighPropValue的大小是從VM虛擬機器中儲存的屬性取到的,
所以integerCacheHighPropValue 是可以通過vm命令進行設定的。
可以通過調整虛擬機器-XX:AutoBoxCacheMax=<size>選項,調整“自動裝箱池”的大小 ,
具體設定方法需要設定虛擬機器模式,這裡不做講解。
自動封箱&自動拆箱的使用場景:
(1)賦值過程中:
此過程不在舉例
(2)函式引數:
publicstaticintsubFun(Integer num){
returnnum;
}
呼叫此函式就會發生自動拆箱
publicstatic Integer subFun(intnum){
return num;
}
上述兩種場景在工作過程中最常見,另外常見的自動拆箱和裝箱的場景就是集合操作,集合只能接收物件,但我們傳入普通型別也是可以的,它內部就完成了自動裝箱。
特別需要注意地方:
1、 是否生成新的物件:
/**
* 基本型別的常量池說明(-127-128)
* @author 李東秀(1028659927)
*/
Integer num1 = 100;
Integer num2 = 100;
System.out.println("num1==num2: " + (num1 == num2));// true 兩個自動裝箱的物件,都是對常量池中物件的引用,所以相同。
Integer num3 = 200;
Integer num4 = 200;
System.out.println("num3>num4: " + (num3 > num4)); // false 將兩個物件拆箱,再比較大小 ,類似的操作還有加減乘除取餘等
Integer num5 = new Integer(100);
Integer num6 = new Integer(100);
System.out.println("num5==num6: " + (num5 == num6)); // false 都會生成新的物件,無論與誰比較都是false
int intNum1 = 100;
System.out.println("num1==int1: " + (num1 == intNum1));// true Integer快取物件拆箱後與int比較 ,已經不是物件地址的比較
int intNum2 = 200;
System.out.println("num3==intNum2: " + (num3 == intNum2));// true Integer物件拆箱後與int比較
所以new物件的方式一定會產生新物件,但自動裝箱的方式則不一定產生新物件,對於Integer物件和基本物件進行運算操作或者邏輯操作,則會首先進行拆箱,之後進行值大小的比較。非new方式生成的Integer這裡用快取解釋,深層的原因是jvm方法區的常量池的存在,快取的物件大小-128-127,基本型別中Byte快取大小為(-128-127),Long快取大小為()等:
java中基本型別的包裝類的大部分都實現了常量池技術,這些類是Byte,Short,Integer,Long,Character,Boolean,另外兩種浮點數型別的包裝類則沒有實現。Byte,Short,Integer,Long,Character這5種整型的包裝類也只是在對應值小於等於127時才可使用常量池,也即物件不負責建立和管理大於127的這些類的物件。
其它基本資料型別對應的包裝型別的自動裝箱池大小(可以在各個型別的快取類中找到範圍):
- Boolean :全部快取
- Byte :全部快取
- Character : <=127快取
- Short : (-128,127)快取
- Long : (-128,127)快取
- Float : (沒有快取)
- Double : (沒有快取)
//5種整形的包裝類Byte,Short,Integer,Long,Character的物件,
//在值小於127時可以使用常量池
Integer intdemo1=127;
Integer intdemo2=127;
System.out.println(intdemo1==intdemo2); //輸出true
//值大於127時,不會從常量池中取物件
Integer intdemo3=128;
Integer intdemo4=128;
Byte bdemo1=12;
Byte bdemo2=12;
System.out.println(bdemo1==bdemo2); //輸出true
Character chdemo1='a';//a=97
Character chdemo2='a';
System.out.println(chdemo1==chdemo2);//true
//.......整形包裝類後面就不在實驗
System.out.println(intdemo3==intdemo4); //輸出false
//Boolean類也實現了常量池技術
Boolean booldemo1=true;
Boolean booldemo2=true;
System.out.println(booldemo1==booldemo2); //輸出true
//浮點型別的包裝類沒有實現常量池技術
Double ddemo1=1.0;
Double ddemo2=1.0;
System.out.println(ddemo1==ddemo2); //輸出false
Java利用記憶體中儲存拘留字串也為String變數提供了快取特性。
Integer I1 = 20;
Integer I2 = 20;
Integer I3 = 0;
Integer I4 = 129;
Integer I5 = 129;
Integer I6 = new Integer("20");
Integer I7 = new Integer("20");
Integer I8 = new Integer("0");
// ==
System.out.println(I1 == I2);// 因為小於127所以都是引用的常量池彙總物件,相同
System.out.println(I1 == I2 + I3);// 拆箱,不存在新物件的產生
System.out.println(I4 == I5);// 無法存入常量池,會生成新的物件
System.out.println(I4 == I5 + I3);// 拆箱比較普通值
System.out.println(I1 == I6);// 生成新物件
System.out.println(I6 == I7);// 生成新物件
System.out.println(I6 == I7 + I8);//
System.out.println("================");
// equals比較值是否相同
System.out.println(I1.equals(I2));
System.out.println(I1.equals(I2 + I3));
System.out.println(I4.equals(I5));
System.out.println(I4.equals(I5 + I3));
System.out.println(I6.equals(I7));
System.out.println(I6.equals(I7 + I8));
/**
* String 型別常量池
*/
System.out.println("================");
String str1 = "ab";
String str2 = "ab";
String str3 = "cd";
String str6 = "abcd";
String str4 = "a";
String str5 = "bcd";
System.out.println(str1 == str2);// 證明常量池存在沒有生成新的物件
System.out.println(str6 == str1 + str2);// 沒有拆箱過程所以不存在常量池的常量直接比較
System.out.println(str6 == "abcd");//
// 涉及到變數(不全是常量)的相加,所以會生成新的物件,其內部實現是先new一個StringBuilder
System.out.println(str2 + str3 == str4 + str5);// 變數的改變,涉及到新建變數
System.out.println(str6.equals(str2 + str3));
// 下面重新定義,因為常量池中已存在abcd
String str10 = new String("AB");
String str11 = new String("AB");
System.out.println("================");
System.out.println(str10 == str11);
String str7 = "AB";
System.out.println(str10 == str7);
String str8 = "CD";
String str9 = "ABCD";
System.out.println(str9 == str7 + str8);
System.out.println(str9 == str10 + str11);
String str12 = new String("CD");// 生成新的物件,CD 放入常量池
String str13 = new String("ABCD");
System.out.println(str9 == str13);
System.out.println(str13 == str11 + str12);
System.out.println(str13 == "ABCD");
String str14 = new String("ABC") + new String("DEF");// 形成的ABCDEF不會被放入常量池
str14.intern();// 新增進常量池中(快取)
String str15 = "ABCDEF";
System.out.println(str14 == str15);
String str16 = new String("AAA");// AAA會被放入常量池中,由於是new產生的物件多以str16不和任何物件相同
String str17 = "AAA";
System.out.println(str16 == str17);
結果:true true false true false false true
================
true true true true true true
================
true false true false true
================
False false false false false false false true false
除了八種基本型別還有java提供的一種比較特殊的型別String(複合)。Java也為它提供了快取機制,原始碼中所有相同字面值的字串常量只可能建立唯一 一個拘留字串物件。 實際上JVM是通過一個記錄了拘留字串引用的內部資料結構來維持這一特性的。在Java程式中,可以呼叫String的intern()方法來使得一個常規字串物件成為拘留字串物件。
String主要使用方法有兩種:如果是使用引號宣告的字串都是會直接在字串常量池中生成,而 new 出來的 String 物件是放在 JAVA Heap 區域。
提到此處就必須得提String.intern方法,intern方法的作用如下:直接使用雙引號宣告出來的String物件會直接儲存在常量池中,如果不是用雙引號宣告的String物件,可以使用String提供的intern方法。intern 方法會從字串常量池中查詢當前字串是否存在,若不存在就會將當前字串放入常量池中。
String s = new String("A");
會建立兩個物件一個“A”儲存在常量池中,另外一個建立在堆上,由於兩個物件所在位置就不相同。所以一定是不相同的。
String s2 = new String("B") + new String("C");//此種方式建立的s2不會被放在常量池中,可以呼叫intern()方法把s2放入常量池中
Jdk1.7中:
String str14=new String("ABC")+new String("DEF");//此種方式生成的str14不會被放在常量池中
String str15="ABCDEF";
System.out.println(str14==str15);
結果:false
String str14=new String("ABC")+new String("DEF");
str14.intern();
String str15="ABCDEF";
System.out.println(str14==str15);
結果:true
看過jvm執行時記憶體分配的都知道,Intern方法的使用在Jdk7中和jdk6中稍有不同:
將String常量池從Perm區移動到了Java Heap區,所以呼叫intern 方法時,如果存在堆中的物件,會直接儲存物件的引用,而不會重新建立物件。(這部分內容還正在看,由於工作比較忙,最近剛開始寫blog,會在後面逐漸寫一系列文章)
2 Java函式過載
過載的定義為:函式引數型別不同或者引數個數不同則說明兩函式過載。
但現在存在自動裝箱,自動拆箱功能是否導致過載的函式為一個函式。
public static int add(Integer num){
return num;
}
public static int add(int num){
return num;
}
這樣定義是可以的,使用時相應引數傳進函式也不存在混淆問題,所以過載不會受到影響。
3 定義的物件可以為空,但是當自動拆箱賦值給普通型別時或者和基本型別比較時,則會報錯誤。
Integer num1=null;
int num2;
num2=num1;
System.out.println(num2);//或者比較也會發生此類錯誤
程式碼能過通過編譯,但執行會報java.lang.NullPointerException錯誤。
4 迴圈過程的重複物件建立
Integer num4=0;
for(inti=0;i<10;i++){
num4=num4+i;
}
這麼簡單的一段程式,打斷點檢視迴圈過程,會發現每次迴圈都要先拆箱再裝箱,裝箱過程中會生成新的物件,所以會生成很多無用的中間物件,降低程式的效能並且加重了垃圾回收的工作量。因此在我們程式設計時,需要注意到這一點,正確地宣告變數型別,避免因為自動裝箱引起的效能問題。
總結:
自動拆箱和自動裝箱給變成帶來了很多便利,但同時也存在許多弊端,java致力於不讓程式設計者擔心記憶體的分配,由垃圾收集器自動完成記憶體的管理,這不意味著我們可以隨意的使用記憶體而不加以限制。變成過程中儘量避免可能多次發生的拆箱裝箱操作,避免不必要物件的建立。(QQ:1028659927,歡迎指導!)
相關推薦
自動拆箱&自動裝箱以及String 和基本資料型別封裝類生成的物件是否相等
自動拆箱(unboxing)&自動裝箱(boxing) @author 李東秀|| qq:1028659927 本文主要為自己理解所做的學習筆記,如有不對的地方, 望各位看官不吝指出,程式碼執行環境:Ubuntu 14.04,jdk1.7版本 在j
Java String和基本資料型別的相互轉換
1.String->基本資料型別 int:Integer.parseInt(Str) double:Double.parseDouble(Str) float:Float.parseFloat(Str) byte:Byte.parseByte(Str) long:Long.
String、基本資料型別包裝類、集合和泛型
String類: · 字串是一個特殊的物件,在java中只要被雙引號引起來的都是字串物件 · 字串一旦初始化就不可以被改變 · String類複寫了Object類中的equals方法,該用法用於判斷字串是否相同 · String s1 = "abc" 和 String s
String類和基本資料型別包裝類
-----------android培訓、java培訓、java學習型技術部落格、期待與您交流!------------ 第一講 String類 一、概述 String是字
java中的自動拆箱、裝箱是指什麼?
JAVA語言中有個名詞叫自動拆箱、裝箱,那這個自動拆箱、裝箱到底是指啥? 自動拆箱、裝箱是從JDK1.5開始才有的特性,其實它主要就是指基本型別與包裝類的自動轉換。 如int 與Integer型別。 int 是基本型別,而Integer是int的包裝類,在
js教程--從入門到精通 第一篇 js的前世今生以及js中基本資料型別和引入方式
1、Javascript前世今生 1.1、什麼是Javascript Javascript運行於Javascript 【直譯器/引擎】中的解釋性指令碼語言 &nb
易學筆記-go語言-第4章:基本結構和基本資料型別/4.4 變數/4.4.3 函式體內最簡單的變數初始化
函式體內最簡單的變數賦值 格式: 變數名 := 值 舉例: var goos string = os.Getenv("GOOS") fmt.Printf("The operating system is: %s\n", goos) //函式體內最
易學筆記-go語言-第4章:基本結構和基本資料型別/4.4 變數/4.4.2 宣告和賦值語句結合
宣告和賦值語句結合 格式:var identifier [type] = value 這裡的type是可選的,具體的型別參照: 第4章:基本結構和基本資料型別/4.2 Go 程式的基本結構和要素/4.2.8 型別 顯式型別舉例: //整型 var a&nbs
易學筆記-go語言-第4章:基本結構和基本資料型別/4.4 變數/4.4.4 函式體內並行初始化
函式體內並行賦值 在 第4章:基本結構和基本資料型別/4.4 變數/4.4.3 函式體內最簡單的變數賦值基礎上,多個變數同時賦值 舉例: 程式碼: a, b, c := 5, 10, "易學筆記" fmt.Printf("a&n
易學筆記-Go語言-第4章:基本結構和基本資料型別/4.5 基本型別/4.5.2 整形
整形 固定位元組數整形:與作業系統無關 int 和 uint 在 32 位作業系統上,它們均使用 32 位(4 個位元組),在 64 位作業系統上,它們均使用 64 位(8 個位元組)。 uintptr 存放指標 指定位元組
易學筆記-Go語言-第4章:基本結構和基本資料型別/4.5 基本型別/4.5.1 bool型別
bool型別 關鍵字:bool,兩個結果:true 或者 false 何時回產生bool型別 ==:相等性筆記 !=:不相等性筆記 >、>=、<、<=:比較 可以進行的邏輯運算
易學筆記-Go語言-第4章:基本結構和基本資料型別/4.4 變數/4.4.7 變數的作用域
變數的作用域 變數的作用域有幾種: 包間變數:也是在函式外宣告的變數,而且第一個字母是大寫,所有本包函式或者包外函式都可見 全域性變數:在函式外宣告的變數,所有函式都可見 區域性變數:在本函式內部都可見 塊變數:僅僅在某個塊中可見,
numpy學習3:物件屬性和基本資料型別
一、ndarray物件屬性 ndim 陣列軸(維度)的個數,軸的個數被稱作秩 shape 陣列的維度, 例如一個2排3列的矩陣,它的shape屬性將是(2,3),這個元組的長度顯然是秩,即維度或者ndi
Object類、Scanner類、String類、StringBuffer類、Integer類、基本資料型別包裝類
Object類 ==號和equals()的區別 ==是一個比較運算子,能比較基本型別和引用資料型別 ==比較的是兩個值是否相等 ==比較引用資料型別,比較的是,地址值是否相同 equals()是object類中的一種方法,這種方法預設比較的是兩個地址值是否相
第4章:基本結構和基本資料型別/4.2 Go 程式的基本結構和要素/4.2.5 可見性
易學筆記 十年IT經驗個人學習筆記分享: 開發語言:C/C++/JAVA/PYTHON/GO/JSP WEB架構:Servlets/springMVC/springBoot/springClound 容器架構:Docker容器/Docker叢集/Docker與微服務整合/
第4章:基本結構和基本資料型別/4.2 Go 程式的基本結構和要素/4.2.4 import:匯入包
易學筆記 十年IT經驗個人學習筆記分享: 開發語言:C/C++/JAVA/PYTHON/GO/JSP WEB架構:Servlets/springMVC/springBoot/springClound 容器架構:Docker容器/Docker叢集/Docker與微服務整合/
第4章:基本結構和基本資料型別/4.2 Go 程式的基本結構和要素/4.2.6 函式
易學筆記 十年IT經驗個人學習筆記分享: 開發語言:C/C++/JAVA/PYTHON/GO/JSP WEB架構:Servlets/springMVC/springBoot/springClound 容器架構:Docker容器/Docker叢集/Docker與微服務整合/
Python 基礎之運算子和基本資料型別
1. 運算子 1.1 結果是具體值(數字或字串)的運算子1.1.1 算數運算1.1.2 賦值運算 1.2 結果是布林值的運算子1.2.1 比較運算 1.2.2 邏輯運算 1.2.3 成員運算 2. 基本資料型別入門2.1 字串2.1.1 字串介紹在 python 中,字串可以用單引號、雙引號、三個單引號和
易學筆記-Go語言-第4章:基本結構和基本資料型別/4.6 字串概述/4.6.1 字串表示
易學筆記 十年IT經驗個人學習筆記分享: 開發語言:C/C++/JAVA/PYTHON/GO/JSP WEB架構:Servlets/springMVC/springBoot/springClound 容器架構:Docker容器/Docker叢集/Docker與微服務整合/
易學筆記-Go語言-第4章:基本結構和基本資料型別/4.5 基本型別/4.5.6 位運算
易學筆記 十年IT經驗個人學習筆記分享: 開發語言:C/C++/JAVA/PYTHON/GO/JSP WEB架構:Servlets/springMVC/springBoot/springClound 容器架構:Docker容器/Docker叢集/Docker與微服務整合/