Java資料型別系列之包裝類

包裝類

在Java5 中添加了兩個新特性,那就是自動裝箱和拆箱,因為基本型別的廣泛使用,但是Java 又是面向物件的語言,所以提供了包裝型別的支援

我們知道基本資料型別包括byte, short, int, long, float, double, char, boolean,對應的包裝類分別是Byte, Short, Integer, Long, Float, Double, Character, Boolean。關於基本資料型別的介紹可參考八大基本資料型別

在這一節裡我們主要以Integer和int 進行講解,其他的可以類比

那麼為什麼需要包裝類

JAVA是面向物件的語言,很多類和方法中的引數都需使用物件(例如集合),但基本資料型別卻不是面向物件的,這就造成了很多不便

如:List = new ArrayList<>();,就

無法編譯透過

為了解決該問題,我們引入了包裝類,顧名思義,就是將基本型別“

包裝起來

“,

使其具備物件的性質

,包括可以新增屬性和方法,位於java。lang包下。

拆箱與裝箱

既然有了基本資料型別和包裝類,就必然存在它們之間的轉換,如:

public static void main(String[] args) { Integer a = 0; for(int i = 0; i < 100; i++){ a += i; }}

將基本資料型別轉化為對應型別的包裝類的過程叫“

裝箱

”;將包裝類轉為對應型別的基本資料型別的過程叫“

拆箱

在上面中給變數a賦值的時候就發生了裝箱,而在參與計算的過程中就發生了拆箱,但是我們好像什麼都沒做,這句是自動拆裝箱

下面我們再給一個例子

裝箱

int num = 25;Integer i = Integer。valueOf(num);Integer i = 25;

拆箱

Integer i = new Integer(10);//unboxingint num = i;Float f = new Float(56。78);float fNum = f;

自動拆箱與自動裝箱

Java為了簡便拆箱與裝箱的操作,提供了自動拆裝箱的功能

,這極大地方便了程式設計師們。

那麼到底是如何實現的呢?

。這個特性是在java5中引入的

上面的例子就是一個自動拆箱與裝箱的過程,透過反編譯工具我們得到,

public static void main(String[] args) { Integer a = Integer。valueOf(0); for (int i = 0; i < 100; i++) { a = Integer。valueOf(a。intValue() + i); }}

我們不難發現,主要呼叫了兩個方法,

Integer.intValue()

Integer.valueOf( int i)

方法

檢視Integer原始碼,我們找到了對應程式碼:

/** * Returns the value of this {@code Integer} as an {@code int}。 * 返回一個 Integer 的int 值 */ public int intValue() { return value; }/** * Returns an {@code Integer} instance representing the specified {@code int} value。 * 返回一個代表特定int 值的 Integer 物件 * If a new {@code Integer} instance is not required, this method should generally be used in preference to the constructor {@link #Integer(int)}, * 如果不需要一個新的Integer 例項,則通常應優先使用此方法而不是建構函式 * 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。 * 此方法將始終快取-128到127(含)範圍內的值,並可能快取此範圍之外的其他值。 * @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);}

很明顯,我們我們得出,Java幫你隱藏了內部細節。拆箱的過程就是透過Integer 實體呼叫intValue()方法;裝箱的過程就是呼叫了 Integer。valueOf(int i) 方法,幫你直接new了一個Integer物件

什麼時候進行自動拆裝箱?

其實很簡單(就是在需要物件的時候就裝箱需要基本型別的時候就拆箱)

1。新增到集合中時,進行自動裝箱

2。涉及到運算的時候,“加,減,乘, 除” 以及 “比較 equals,compareTo”,進行自動拆箱

例如下面這一段程式碼,則發生了自動裝箱

List li = new ArrayList<>();for (int i = 1; i < 10; i++){ li。add(i);}

注意的點

在上述的程式碼中,關於Integer valueOf(int i)方法中有IntegerCache類,在自動裝箱的過程中有個條件判斷

if (i >= IntegerCache。low && i <= IntegerCache。high)

結合註釋

This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range

大意是:該方法總是快取-128 到 127之間的值,同時針對超出這個範圍的值也是可能快取的。

那麼為什麼可能快取?其實在IntegerCache原始碼中可以得到答案

/** * 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=} 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() {}}

因為快取最大值是可以配置的。這樣設計有一定好處,我們可以根據應用程式的實際情況靈活地調整來提高效能。

可以在vm中修改即可調整快取大小 java -Djava。lang。Integer。IntegerCache。high = xxxx Aclass。class 或者 -XX:AutoBoxCacheMax=xxxx

但是這裡有問題需要注意,就是從上面程式碼的邏輯我們可看出,我們

只能設定high而不能設定low,還有就是如果high 小於127 將不能生效

,其中的low的下限是最開始的時候就固定死了的 final int low = -128;而其中的high雖然是設定了final int的,但是最初的時候,是還沒有賦值的;所以我們可以透過設定 java -Djava。lang。Integer。IntegerCache。high = xxxx Aclass。class 或者 -XX:AutoBoxCacheMax=xxxx

接下來我們透過一段程式碼進行驗證一下,首先我們看一下程式碼,當執行之後我們可以設定引數-XX:AutoBoxCacheMax=1000 再執行一次

public class TestAutoBoxCache { @Test public void test() { Integer a = 127; Integer b = 127; System。out。println(a == b); Integer c = 128; Integer d = 128; System。out。println(c == d); Integer e = 1000; Integer f = 1000; System。out。println(e == f); Integer g = 1001; Integer h = 1001; System。out。println(g == h); Integer i = 20000; Integer j = 20000; System。out。println(i == j); }}// 沒有設定引數之前的輸出truefalsefalsefalsefalse// 設定引數之後的輸出 -XX:AutoBoxCacheMax=1000 truetruetruefalsefalse

因為快取設定的實1000,也就是在[-128,1000] 之間的就快取了,也驗證了我們上面的說法是正確的

與之類似的快取還有:

Byte與ByteCache,快取值範圍-128到127,固定不可配置(其實就是全部快取了)

Short與ShortCache,快取值範圍-128到127,固定不可配置

Long與LongCache,快取值範圍-128到127,固定不可配置

Character與CharacterCache,快取值範圍0到127,固定不可配置

包裝型別與switch

public static void main(String[] args) { int iVal = 7; switch(iVal) { case 1: System。out。println(“First step”); break; case 2: System。out。println(“Second step”); break; default: System。out。println(“There is some error”); }}

包裝型別和基本型別一樣,或者說是和String 一樣都是支援switch 的

總結

為什麼需要包裝類:

JAVA是面向物件的語言,很多類和方法中的引數都需使用物件(例如集合),但基本資料型別卻不是面向物件的,這就造成了很多不便

拆裝箱的概念:將基本資料型別轉為包裝類的過程叫“

裝箱

”,將包裝類轉為基本資料型別的過程叫“

拆箱

自動拆裝箱:

Java為了簡便拆箱與裝箱的操作,提供了自動拆裝箱的功能

,對於Integer, 拆箱的過程就是透過Integer 實體呼叫intValue()方法;裝箱的過程就是呼叫了 Integer。valueOf(int i) 方法,幫你直接new了一個Integer物件

建議使用valueOf() 方法建立一個包裝類例項而不是直接使用構造方法,因為該方法可以走快取提高效能

Java資料型別系列之包裝類

作者:劉不二

連結:https://juejin。cn/post/6918337220948525069