轉(zhuǎn)載:
http://www.cnblogs.com/dolphin0520/p/3780005.html
http://blog.csdn.net/chengzhezhijian/article/details/9628251
一.什么是裝箱泽西?什么是拆箱圾亏?
Java為每種基本數(shù)據(jù)類型都提供了對應(yīng)的包裝器類型。
在Java SE5之前斩狱,如果要生成一個數(shù)值為10的Integer對象柬姚,必須這樣進行:
Integer i = new Integer(10);
而在從Java SE5開始就提供了自動裝箱的特性,如果要生成一個數(shù)值為10的Integer對象,只需要這樣就可以了:
Integer i = 10;
這個過程中會自動根據(jù)數(shù)值創(chuàng)建對應(yīng)的 Integer對象,這就是裝箱艰匙。
那什么是拆箱呢?顧名思義霞揉,跟裝箱對應(yīng)旬薯,就是自動將包裝器類型轉(zhuǎn)換為基本數(shù)據(jù)類型:
Integer i = 10; //裝箱
int n = i; //拆箱
簡單一點說:
裝箱就是自動將基本數(shù)據(jù)類型轉(zhuǎn)換為包裝器類型;
拆箱就是自動將包裝器類型轉(zhuǎn)換為基本數(shù)據(jù)類型适秩。
下表是基本數(shù)據(jù)類型對應(yīng)的包裝器類型:
基本數(shù)據(jù) | 包裝器類型 |
---|---|
int(4字節(jié)) | Integer |
byte(1字節(jié)) | Byte |
short(2字節(jié)) | Short |
long(8字節(jié)) | Long |
float(4字節(jié)) | Float |
double(8字節(jié)) | Double |
char(2字節(jié)) | Character |
boolean(未定) | Boolean |
二.裝箱和拆箱是如何實現(xiàn)的绊序?
我們就以Interger類為例,下面看一段代碼:
public class Main {
public static void main(String[] args) {
Integer i = 10;
int n = i;
}
}
反編譯class文件之后得到如下內(nèi)容:
public class Main
{
public static void main(String[] args)
{
Integer i = Integer.valueOf(10);
int n = i.intValue();
}
}
從反編譯得到的字節(jié)碼內(nèi)容可以看出秽荞,在裝箱的時候自動調(diào)用的是Integer的valueOf(int)方法骤公。而在拆箱的時候自動調(diào)用的是Integer的intValue方法。
其他的也類似扬跋,比如Double阶捆、Character。
因此可以用一句話總結(jié)裝箱和拆箱的實現(xiàn)過程:
裝箱過程是通過調(diào)用包裝器的valueOf方法實現(xiàn)的钦听,而拆箱過程是通過調(diào)用包裝器的 xxxValue方法實現(xiàn)的洒试。(xxx代表對應(yīng)的基本數(shù)據(jù)類型)。
三.面試中相關(guān)的問題
- 下面這段代碼的輸出結(jié)果是什么朴上?
public class Main {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
}
也許有些朋友會說都會輸出false垒棋,或者也有朋友會說都會輸出true。但是事實上輸出結(jié)果是:
true
false
為什么會出現(xiàn)這樣的結(jié)果痪宰?
輸出結(jié)果表明i1和i2指向的是同一個對象叼架,而i3和i4指向的是不同的對象。
此時只需一看源碼便知究竟衣撬,下面這段代碼是Integer的valueOf方法的具體實現(xiàn):
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
而其中IntegerCache類的實現(xiàn)為:
/**
* 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() {}
}
從這2段代碼可以看出乖订,在通過valueOf方法創(chuàng)建Integer對象的時候,如果數(shù)值在[-128,127]之間具练,便返回指向IntegerCache.cache中已經(jīng)存在的對象的引用乍构;否則創(chuàng)建一個新的Integer對象。
The Java Language Specification, 3rd Edition 寫道:
If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.
為了節(jié)省內(nèi)存扛点,對于下列包裝對象的兩個實例哥遮,當它們的基本值相同時,他們總是==:
Boolean:(全部緩存)
Byte:(全部緩存)
Character: (\u0000 - \u007f)(<= 127緩存)
Short:(-128 — 127緩存)
Long:(-128 — 127緩存)
Float:(沒有緩存)
同樣對于垃圾回收器來說:
Integer i = 100;
i = null;//will not make any object available for GC at all.
這里的代碼不會有對象符合垃圾回收器的條件占键,這兒的i雖然被賦予null昔善,但它之前指向的是cache中的Integer對象,而cache沒有被賦null畔乙,所以Integer(100)這個對象還是存在君仆。
而如果i大于127或小于-128則它所指向的對象將符合垃圾回收的條件:
Integer i = 10000;
i = null;//will make the newly created Integer object available for GC.
使用Oracle/Sun JDK 6,在server模式下牲距,使用-XX:AutoBoxCacheMax=NNN參數(shù)即可將Integer的自動緩存區(qū)間設(shè)置為[-128,NNN]返咱。注意區(qū)間的下界固定在-128不可配置。
在client模式下該參數(shù)無效牍鞠。這個參數(shù)是server模式專有的咖摹,在c2_globals.hpp中聲明,默認值是128难述;不過這個默認值在默認條件下不起作用萤晴,要手動設(shè)置它的值或者是開啟-XX:+AggressiveOpts參數(shù)才起作用吐句。
在設(shè)置了-XX:+AggressiveOpts啟動參數(shù)后,AutoBoxCacheMax的默認值會被修改為20000并且生效店读。
參考arguments.cpp:
// Aggressive optimization flags -XX:+AggressiveOpts
void Arguments::set_aggressive_opts_flags() {
#ifdef COMPILER2
if (AggressiveOpts || !FLAG_IS_DEFAULT(AutoBoxCacheMax)) {
if (FLAG_IS_DEFAULT(EliminateAutoBox)) {
FLAG_SET_DEFAULT(EliminateAutoBox, true);
}
if (FLAG_IS_DEFAULT(AutoBoxCacheMax)) {
FLAG_SET_DEFAULT(AutoBoxCacheMax, 20000);
}
// Feed the cache size setting into the JDK
char buffer[1024];
sprintf(buffer, "java.lang.Integer.IntegerCache.high=" INTX_FORMAT, AutoBoxCacheMax);
add_property(buffer);
}
// ...
#endif
}
測試代碼:
// run with:
// java -server -XX:AutoBoxCacheMax=1000 TestAutoBoxCache
public class TestAutoBoxCache {
public static void main(String[] args) {
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b);
Integer c = 1001;
Integer d = 1001;
System.out.println(c == d);
Integer e = 20000;
Integer f = 20000;
System.out.println(e == f);
}
}
在命令行上測試:
$ javac TestAutoBoxCache.java
$ java TestAutoBoxCache
false
false
false
$ java -server TestAutoBoxCache
false
false
false
$ java -Djava.lang.Integer.IntegerCache.high=1000 TestAutoBoxCache
true
false
false
$ java -server -Djava.lang.Integer.IntegerCache.high=1000 TestAutoBoxCache
true
false
false
$ java -Djava.lang.Integer.IntegerCache.high=1001 TestAutoBoxCache
true
true
false
$ java -server -Djava.lang.Integer.IntegerCache.high=1001 TestAutoBoxCache
true
true
false
$ java -XX:AutoBoxCacheMax=1000 TestAutoBoxCache
Unrecognized VM option 'AutoBoxCacheMax=1000'
Could not create the Java virtual machine.
$ java -server -XX:AutoBoxCacheMax=1000 TestAutoBoxCache
true
false
false
$ java -server -XX:AutoBoxCacheMax=1001 TestAutoBoxCache
true
true
false
$ java -server -XX:+AggressiveOpts TestAutoBoxCache
true
true
true
中間報Unrecognized VM option 'AutoBoxCacheMax=1000'錯誤是因為這個參數(shù)只能在HotSpot Server VM上使用嗦枢,在HotSpot Client VM上不支持。
- 下面程序的輸出結(jié)果是什么屯断?
public class Main {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
System.out.println(c==d);
System.out.println(e==f);
System.out.println(c==(a+b));
System.out.println(c.equals(a+b));
System.out.println(g==(a+b));
System.out.println(g.equals(a+b));
System.out.println(g.equals(a+h));
}
}
運行結(jié)果:
true
false
true
true
true
false
true
這里面需要注意的是:當 “==”運算符的兩個操作數(shù)都是 包裝器類型的引用文虏,則是比較指向的是否是同一個對象,而如果其中有一個操作數(shù)是表達式(即包含算術(shù)運算)則比較的是數(shù)值(即會觸發(fā)自動拆箱的過程)殖演。
另外氧秘,對于包裝器類型,equals方法并不會進行類型轉(zhuǎn)換趴久。