Java 進階教學 : Wrapper classes And Auto Boxing / Auto UnBoxing

Java Road Java Road


好的頻道需要你的支持,謝謝你使用Youtube影片的超級感謝功能給我們鼓勵。

影片1 Wrapper Classes 是什麼? 它們可以取代資本基料型別嗎?


好的頻道需要你的支持,謝謝你使用Youtube影片的超級感謝功能給我們鼓勵。

一、         Wrapper Classes

用基本資料型別所宣告的變數,與用類別宣告的參考變數,儲存的內容不一樣。變數儲存的是值參考變數儲存的是記憶體位址。變數只能儲存一個值,而這個值的運算,一定要靠設計師一行一行的撰寫程式碼;參考變數儲存的物件代號,代表一個物件。物件中儲存著一個或一個以上的值,呼叫物件的方法,就可以針對這些值做特定的運算。

 

Java  API 中的 java.lang package 提供了相當多的類別。其中有8個與基本資料型別相對應的類別,我們統稱為 Wrapper Classes 類別。這些類別的物件,可以儲存一個對應的基本型別的值,可搭配物件的方法取值,但其值不可改變。

 

這群類別的物件在使用上並不是很方便,而且運算時會影響執行效率,建議不要用來取代基本資料型別。雖然,後面會介紹 Auto Boxing   Auto UnBoxing 功能可以簡化這群類別的物件使用上的複雜寫法,但非必要還是不要用。

 

這群類別的物件主要是使用在全物件的運算環境中。例如我們後面要介紹的集合物件。在這裡,大家只須要先知道這群類別的存在即可。

 

1.  建構方法

基本資料型別

Wrapper Classes中對應基本資料型別的類別

建構方法的參數型別

byte

Byte

byte

String

short

Short

short

String

int

Integer

int

String

long

Long

long

String

float

Float

float

double

String

double

Double

double

String

char

Character

char

boolean

Boolean

boolean

String

 

2.  物件方法

 

l   type xxxValue()

運算結果:取得該物件 xxx 型別的值,xxx 是基本資料型別的意思。例:intValue() floatValue()

 

 

Boolean

Character

Byte

Short

Integer

Long

Float

Double

booleanValue

x

 

 

 

 

 

 

 

charValue

 

x

 

 

 

 

 

 

byteValue

 

 

x

x

x

x

x

x

shortValue

 

 

x

x

x

x

x

x

intValue

 

 

x

x

x

x

x

x

longValue

 

 

x

x

x

x

x

x

floatValue

 

 

x

x

x

x

x

x

doubleValue

 

 

x

x

x

x

x

x

標示‘x’者表示該類別有宣告此方法。

 

l   String toString()

取得物件值轉換後的字串物件。

 

3. 類別方法

  關於這群類別,大家應該把焦點集中在這些類別方法。在許多運算中,它們是一定會用到的。

l   static xxx parseXxx (String s)

Wrapper Classes 中除了 Character 外,其它類別宣有此方法。運算結果是將 String 物件的字串值轉換成 xxx 型別的值。

l   static xxx parseXxx (String s, int radix)

若是轉成整數可以用第2個參數來指定進位方式2為2進位,8為8進位,16為16進位。只有四個整數類別才有宣告這個方法。

 

特別要注意的是 ,此方法是轉換為基本資料型別的值,不是建構成物件。

 

另外,用來轉換為布林值的字串值,沒有大小寫的區分。但只要不是 true 的大小寫組合,便一律轉換為 false。而除了 Boolean 外的其他類別,若字串物件參數的內容與其資料型態不符,會產生執行時期的錯誤。

 

l   static ClassName valueOf(type value)

l   static ClassName valueOf(type value, int radix)

 

ClassName

Boolean

Character

Byte

Short

Integer

Long

Float

Double

type

boolean

String

char

byte

String

String,int

short

String

String,int

int

String

String,int

long

String

String,int

float

String

double

String

 

Wrapper Classes 均有宣告此方法,但參數樣式不完全一樣。會用參數來建構該類別之物件。請注意它和 parseXxx 的差異性。

 

l   static String toString(type value)

Wrapper Classes 均有這個方法。它會依照參數值建構出字串物件,大部份用來執行基本資料型別直接轉換為字串,可以省略建構成物件的手續。

l   static String toString(int value, int radix)

整數型態的類別,一樣擁有可以指定進位方式的方法。

 

4.    物件比對

和 String 類別一樣,Wrapper Classes 中所有類別的 equals 方法,都提供物件內容比對的運算。也就是說:“==”用來比對參考變數是否代表同一個物件,equals 用來比對兩個物件的值是否相同。



二、         Auto Boxing / Auto UnBoxing

Java 提供了與基本資料型別相對應的類別 (Wrapper Classes),來提供基本資料型別的進階運算。但是,在舊版 (Java 1.5 之前) 的 Java 中,參考變數是沒有辦法像值一樣,直接放在運算式中使用的。

例:Integer i = new Integer(8);

  i++;

上述程式碼在舊版的 Java 中會造成編譯錯誤,因為 i 是參考變數,i 是代表一個物件,i 所儲存的是物件代號,所以 i++ 是無法運算的。若要運算 i 物件的值加 1,必需寫成這樣:

i = new Integer( i.intValue() + 1);

先取得 i 物件的值,加 1 後再建構成新的物件,再指派給 i。我們就會發現,在基礎運算的部份,這些類別的運算反而不如基本資料型別方便。

 

Auto Boxing / Auto UnBoxing 就是針對這個部份新增的功能,這個功能讓代表物件的參考變數(用 Wrapper Classes 建構的物件),可以像一般變數一樣,直接使用於運算式之中。

 

對初學者而言,本章其實略看即可。初學者只要了解不要用 Wrapper Classes 取代基本資料型別就可以了。

 

1.  基本觀念

1-1 Auto Boxing (依值建構物件)

Wrapper Classes 物件建構時,若用來建構物件的值,與宣告參考變數的類別是相對應的,則設計師可以不必指定建構方法,編譯器會自動以該值建構成物件後,再將物件代號指派給參考變數。

 

例一:

        Byte b = 1;

        Short s = 10;

        Integer I = 100;

        Long l = 1000L;

        Float f = 3.5F;

        Double d = 35.35;

        Character c = 'a';

        Boolean bo = true;

設計師不必用 new 來呼叫建構方法,編譯器會自動建構成物件。但是一定要注意:這個功能只限定於相對應的值與類別,若型態不符,並不會 Promotion 後再建物件。

在“Long l = 1000L;”這一句,若把 1000L 改為 1000,那麼 1000 int 型別的值,這個值並不會先 Promotion long 型別的值,再來建構成物件,而是產生編譯錯誤。

在“Float f = 3.5F;”這一句,若把 3.5F 改為 3.5,那麼 3.5 double 型別的值,這個值並不會因為 float 類別的建構方法中,有一個是可以接受 double 型別的參數,而去自動呼叫那一個建構方法,它一樣會產生編譯錯誤。

 

總而言之:「 Auto Boxing 的指派功能只限定於相對應的值與類別,它不是依照類別宣告的建構方法的參數,也不會自動轉型,也不可以超過該型別的最大值或最小值的範圍」。

1-2 Auto UnBoxing (取得物件的值)

將代表物件的參考變數置於任何運算式之中,編譯器均會自動取得該物件的值,等同呼叫 xxxValue()

 

例一:

        Integer i = 1000;

        int i2 = i * 2;

        byte b = (byte)(i/10);

        boolean bo = i > 100;

以上運算式均合法。

 

1-3 記憶體配置

為了增加運算的效率,環境在執行 Auto Boxing 時,如果該值佔用的記憶體大小在一個 byte之內,則此物件會建構在一個專屬的記憶體區塊,在這個區塊中的物件的記憶體位址,會重複指派給需要相同值的參考變數 ( String 類別)

 

例一:

        Integer i = 8;

        Integer i2 = 8;

        System.out.println(i == i2);

此例中的 Integer 物件會建構在專屬記憶體區塊,在 i2 的敍述句執行的時候,環境不會再建構新物件,而會直接把 i 物件的記憶體位址指派給 i2。所以 i i2 參照到同一個物件。

 

符合此配置機制的類別及其範圍為:

類別名稱

Boolean

Character

Byte

Short

Integer

值的範圍

true/false

0~127

-128~127

-128~127

-128~127

 

Short, Integer Character 類別的建構值超過此範圍,此機制便不會執行。

       

2.  == equals

因為有了 Auto Boxing / Auto UnBoxing 的功能,參考變數可以置於任意運算式之中直接運算,當然包含條件運算式,但是如果考慮到一個 byte 之內的記憶體配置問題,那本節就值得仔細推敲一下了。

 

例一:

                Integer i = 12345;

                Integer i2 = 12345;

                System.out.println(i == i2);

                System.out.println(i.equals(i2));

列印結果:false, true

i i2 代表不同的 Integer 物件,但兩個物件值均為 12345

 

例二:

                Integer i = 123;

                Integer i2 = 123;

                System.out.println(i == i2);

                System.out.println(i.equals(i2));

列印結果:true, true

i i2 代表相同 Integer 物件。

 

例三:

                Integer i = 123;

                int i2 = 123;

                System.out.println(i == i2);

                System.out.println(i.equals(i2));

列印結果:true, true

i == i2 運算時,編譯器會自動取得 i 物件的值與 i2的值比對;在 i.equals(i2) 執行時,編譯器會自動把 i2 建構為物件再與 i 的物件內容做比對。

 

例四:

                Integer i = 123;

                int i2 = 123;

                System.out.println(i2 == i);

                //System.out.println(i2.equals(i));

列印結果:true

除了指派運算符號之外,參考變數無論是置於運算符號的左側或右側,均會執行 Auto UnBoxing。若把第四行敍述句的註解取消,則會造成編譯錯誤。因為 i2不是參考變數,它不能呼叫方法。

 

3.  if

if的小括號中也可以用 Wrapper Classes 的參考變數運算。

 

例一:

                Integer i = 8;

                if(i > 5)

                        System.out.println("Hello");

列印結果:Hello

 

例二:

                Boolean b = true;

                if(b)

                        System.out.println("Hello");

列印結果:Hello

 

例三:

                Boolean b = false;

                if(!b)

                        System.out.println("Hello");

列印結果:Hello

 

4.  switch

switch後面的小括號,也能放 Wrapper Classes 的參考變數。

 

例一:

                Scanner s=new Scanner(System.in);

 

                System.out.print("請輸入學生成績-->");

                Integer i=s.nextInt();

                switch(i/10){

                        case 0:

                        case 1:

                        case 2:

                        case 3:

                        case 4:

                        case 5:

                        case 6:

                                System.out.println("C");

                                break;

                        case 7:

                                System.out.println("B");

                                break;

                        case 8:

                                System.out.println("A-");

                                break;

                        case 9:

                                System.out.println("A");

                                break;

                        case 10:

                                System.out.println("A+");

                                break;

                        default:

                                System.out.println("請輸入正確分數");       

                }

本例會依照輸入的學生成績來列印等級。

 

例二:

                Integer i=8;

                switch(4*4){

                        case i:

                                System.out.printnl("Hello");

                }

本例會產生編譯錯誤,因為 case 後必須放常數。

 

例三:

                final Integer i=8;

                switch(4*4){

                        case i:

                                System.out.println("Hello");

                }

特別注意,本例還是會產生編譯錯誤。因為 case 後面只能放編譯時期就確定的值。Wrapper Classes 的物件雖然是 immutable 物件,但它畢竟是物件。物件就一定是執行時期才建立的,所以它不是編譯時期就可以確定的。自然不能作為 case 後面的值。

 

5.  Parameter And Return Value

呼叫方法時所用的參數,及方法運算後的結果,也都可以運用 Auto Boxing / Auto UnBoxing

 

例一:

                Integer i = 0;

                String s = "Java";

                System.out.println(s.charAt(i));

列印結果:JString 類別中宣告的方法原型是void charAt(int index)。表示參數型別為 int,但本例使用 Auto UnBoxing,以 Integer 的物件為參數。

 

例二:

                String s = "Java";

                Integer i = s.length();

                System.out.println(i);

列印結果:4String 類別中宣告的方法原型是 int length()。表示其返回型別為 int 型別,但本例使用 Auto Boxing,將該值指派給用 Integer 宣告的參考變數。

 

6.  Overload 的呼叫順序

Match -> Promotion -> Auto Boxing -> Var-args

例:

若有一個方法有下列五 Overload

public void methodA(int x){}

public void methodA(long x){}

public void methodA(Integer x){}

public void methodA(Long x){}

public void methodA(int... x){}

 

現在有一個呼叫敍述:

methodA(8);

會呼叫參數為 int methodA

 

若有一個方法有下列四式 Overload

public void methodA(long x){}

public void methodA(Integer x){}

public void methodA(Long x){}

public void methodA(int... x){}

現在有一個呼叫敍述:

methodA(8);

會呼叫參數為 long methodA

 

若有一個方法有下列三式 Overload

public void methodA(Integer x){}

public void methodA(Long x){}

public void methodA(int... x){}

現在有一個呼叫敍述:

methodA(8);

會呼叫參數為 Integer methodA

 

若有一個方法有下列兩式 Overload

public void methodA(Long x){}

public void methodA(int... x){}

現在有一個呼叫敍述:

methodA(8);

會呼叫參數為 int... methodA


若有一個方法。

public void methodA(Long x){}

現在有一個呼叫敍述:

methodA(8);

會產生編譯錯誤。

因為值 8 Auto Boxing Integer 的物件。而 Integer 物件不會 Promotion Long 物件。

而值 8 也不會先 promotion long 的值後再 Auto Boxing Long 物件。

作者 : 許裕永


許老師的回答: