Java 進階教學 : Wrapper classes And Auto Boxing / Auto UnBoxing
Java Road Java Road
好的頻道需要你的支持,謝謝你使用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));
列印結果:J。String 類別中宣告的方法原型是void
charAt(int index)。表示參數型別為 int,但本例使用 Auto UnBoxing,以 Integer 的物件為參數。
例二:
String
s = "Java";
Integer
i = s.length();
System.out.println(i);
列印結果:4。String 類別中宣告的方法原型是
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 物件。