包裝類
1.為什麼需要包裝類?
Java在設計之初有一個基本原則:一切皆物件,一切的操作都要求用物件的形式進行描述。但是這裡面就會出現一個矛盾,基本資料型別不是物件。那麼我們會如何修復這個BUG呢?最簡單的做法是將基本資料型別作為一個類的屬性儲存起來,這樣就相當於把基本資料型別包裝了一下.
實現基本資料型別的包裝類
package com.shxt.demo01;
public class Int {//類
private int number;//基本資料型別
public Int(int number){//提供一個引數的建構函式,傳入基本資料型別
this.number = number;
}
public int intValue(){//取得包裝類中的資料
return this.number;
}
//還可以提供其他的方法
}
複製程式碼
package com.shxt.demo01;
public class MyTest {
public static void main(String[] args) {
Int temp = new Int(100); //將基本資料型別包裝後變為類
int result = temp.intValue(); //從類中取得基本資料型別
System.out.println(result+result);
}
}
複製程式碼
程式碼分析:
我們實現了基本資料型別轉成Java物件的方式,Java中給我們提供了類似的實現類
包裝類表格
基本資料型別 | 包裝類 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
我們觀察上述的表格發現除了int->Integer,char->Character,其他的都是基本資料型別的首字母大寫,很好記憶的.
但是以上給出的包裝類又分為兩種子型別:
- 物件型包裝類(Object直接子類):Character、Boolean。
- 數值型包裝類(Number直接子類):Byte、Short、Integer、Float、Double、Long。
Number是一個抽象類,裡面一共定義了六個操作方法:intValue()、shortValue()、byteValue()、floatValue()、longValue()、doubleValue()。
2.裝箱和拆箱
現在已經存在有基本資料型別與包裝類,那麼這兩種變數間的轉換就通過以下方式定義。
- 裝箱操作:將基本資料型別變為包裝類的形式。
- 每個包裝類的構造方法都可以接收各自資料型別的變數。
- 拆箱操作:從包裝類之中取出被包裝的資料。
- 利用Number類中提供的一系列的:xxxValue()方法完成。
示例1-以int和Integer為例演示裝箱和拆箱操作的過程
public class Demo01 {
public static void main(String args[]) {
Integer obj = new Integer(10); // 將基本資料型別裝箱
int temp = obj.intValue(); // 將基本資料型別拆箱
System.out.println(temp * 2); // 結果為20
}
}
複製程式碼
之前使用所編寫的Int類,現在換成了Integer這個系統類。
示例2-以double和Double為例演示裝箱和拆箱操作的過程
public class Demo02 {
public static void main(String args[]) {
Double obj = new Double(10.2); // 將基本資料型別裝箱
double temp = obj.double Value(); // 將基本資料型別拆箱
System.out.println(temp * 2); // 結果為20.4
}
}
複製程式碼
示例3-以boolean和Boolean為例演示裝箱和拆箱操作的過程(不是Number子類)
public class Demo03 {
public static void main(String args[]) {
Boolean obj = new Boolean(false); // 將基本資料型別裝箱
boolean temp = obj.booleanValue(); // 將基本資料型別拆箱
System.out.println(temp); // 結果為false
}
}
複製程式碼
現在可以發現,所有的包裝類都使用了同樣形式的方法進行操作。 在JDK1.5之前能夠使用的操作都是以上形式的程式碼,但是JDK1.5之後,Java為了方便開發提供了自動裝箱與自動拆箱的機制,並且可以直接利用包裝類的物件進行數學計算。
示例4-以int和Integer為例觀察自動裝箱和自動拆箱操作的過程
public class Demo04 {
public static void main(String args[]) {
Integer obj = 10; // 自動裝箱 int->Integer
int temp = obj; // 自動拆箱 Integer->int 實際上呼叫了obj.intValue()方法
obj ++;
System.out.println(temp * obj); // 結果為110
}
}
複製程式碼
示例5-以boolean和Boolean為例觀察自動裝箱和自動拆箱操作的過程(不是Number子類)
public class Demo05 {
public static void main(String args[]) {
Boolean obj = false; // 自動裝箱 boolean->Boolean
boolean flag = obj; // 自動拆箱 Boolean->boolean
if(!flag){
System.out.println("Boolean不是Number的子類");
}
}
}
複製程式碼
重點:正式因為Java提供了自動裝箱和自動拆箱的機制,那麼Object可以接收一切的資料型別(Object可以統一天下了)
轉換流程:基本資料型別 → 自動裝箱(成為物件) → 向上轉型為Object。
示例6-以Object接收int資料型別演示轉換過程
public class Demo06 {
public static void main(String args[]) {
Object obj = 10; // int->Integer(自動裝箱為物件)->Object
int temp = (Integer)obj; // Object->Integer(強制型別轉換為包裝類)->自動拆箱
obj ++;
System.out.println(temp * obj); // 結果為110
}
}
複製程式碼
“莫名其妙”的NullPointException
在我們開發過程中,碰到過不少因為請求引數或者介面定義欄位設定為int(或者其他基本型別)而導致NullPointException(空指標異常)。程式碼大致地執行步驟如下所示,當然不會跟這個完全一樣。
public class Demo06 {
public static void main(String args[]) {
Integer a = null;
int b = a; // 丟擲NullPointException a.intValue();
}
}
複製程式碼
程式碼分析:
上面的程式碼可以編譯通過,但是會丟擲空指標異常(NullPointException)。
前面已經說過了,
int b = a
實際上是int b = a.intValue()
,由於a的引用值為null,在空物件上呼叫方法就會丟擲NullPointException。
3.==和equlas()
大家都應該清楚明瞭的瞭解兩者的區別,
一句話說就是 ==
比較的是記憶體中地址,equlas()
對比的為數值,因為基本型別相同的數值指向的同一塊記憶體,所以可以用==來比較,而引用型別則不可以。
public class Demo07 {
public static void main(String args[]) {
Integer obja = 10; // 直接賦值
Integer objb = 10; // 直接賦值
Integer objc = new Integer(10); // 構造方法
System.out.println(obja == objb); // true 不是比較記憶體地址嗎?為什麼?
System.out.println(obja == objc); // false
System.out.println(objb == objc); // false
System.out.println(obja.equals(objc)); // true
}
}
複製程式碼
程式碼分析:
obja == objb
不是應該比較記憶體地址嗎?為什麼能相等呢?我們需要解決這個問題,原始碼分析在使用包裝類的時候很少會利用構造方法完成,幾乎都是直接賦值(這一點與String相同),但是在內容是否相等的時候,請一定要記住使用equals()方法。
兩個包裝類引用相等性
在Java中,“==”符號判斷的記憶體地址所對應的值得相等性,具體來說,基本型別判斷值是否相等,引用型別判斷其指向的地址是否相等。看看下面的程式碼,兩種類似的程式碼邏輯,但是得到截然不用的結果。
public class Demo08 {
public static void main(String args[]) {
Integer a1 = 127;
Integer a2 = 127;
System.out.println(a1 == a2); // true
Integer b1 = 128;
Integer b2 = 128;
System.out.println(b1 == b2); // false
}
}
複製程式碼
這個必須從原始碼中才能找到答案。Integer
類中的valueOf()
方法的原始碼如下:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high) // 判斷實參是否在可快取範圍內,預設為[-128, 127]
return IntegerCache.cache[i + (-IntegerCache.low)]; // 如果在,則取出初始化的Integer物件
return new Integer(i); // 如果不在,則建立一個新的Integer物件
}
複製程式碼
程式碼分析:
由於127屬於[-128, 127]集合範圍內,所以valueOf()每次都會取出同一個Integer物件,故第一個“==”判斷結果為true;而128不屬於[-128, 127]集合範圍內,所以valueOf()每次都會建立一個新的Integer物件,由於兩個新建立的物件的地址不一樣,故第一個“==”判斷結果為false。
再次分析比較過程
package com.shxt.demo01;
public class MyTest {
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); // 快取機制,返回值true
System.out.println(e==f); // 超出了快取機制, 返回值false
// (a+b) 計算了自動拆箱變成了int型別, c比較需要自動拆箱變成int , 返回值true
System.out.println(c==(a+b));
//(a+b) 計算了自動拆箱變成了int型別,因為使用equals比較內容(a+b)的結果有自動裝箱,返回值true
System.out.println(c.equals(a+b));
// (a+b) 計算了自動拆箱變成了int型別, g自動拆箱變成long型別, (a+b)的結果自動型別轉換為long , 返回值true
System.out.println(g==(a+b));
//(a+b) 計算了自動拆箱變成了int型別,因為使用equals比較內容(a+b)的結果有自動裝箱Integer
//Long和Integer比較 , 返回值false
System.out.println(g.equals(a+b));
//涉及到自動型別轉換, 返回值true
System.out.println(g.equals(a+h));
}
}
複製程式碼
4.資料型別轉換(核心)
使用包裝類最多的情況實際上是它的資料型別轉換功能上,在包裝類裡面,最大的優點就是提供將String型資料變為基本資料型別的方法,使用幾個代表的類做說明:
- Integer類:public static int parseInt(String s)。
- Double 類:public static double parseDouble(String s)。
- Boolean類:public static boolean parseBoolean(String s)。
**特別注意:**Character類裡面並不存在字串變為字元的方法,因為String類有一個charAt()的方法可以根據索引取出字元內容,並且一個字元的長度才有一位。
範例1:將字串變為int型資料
public class Demo01 {
public static void main(String args[]) {
String str = "123"; // 字串
int temp = Integer.parseInt(str);//String->int
System.out.println(temp * 2);
}
}
複製程式碼
此時實現了字串變為基本資料型別的操作。但是在這樣的轉換過程之中請一定要注意:被轉換為數字的字串一定要由數字所組成。如果不是數字組成,轉換過程中會報異常:NumberFormatException
範例2:錯誤程式碼
public class Demo02 {
public static void main(String args[]) {
String str = "1sss3"; // 字串
int temp = Integer.parseInt(str);
System.out.println(temp * 2);
}
}
複製程式碼
Exception in thread "main" java.lang.NumberFormatException:For input string:"1sss3"
複製程式碼
範例3:將字串變為double型資料
public class Demo03 {
public static void main(String args[]) {
String str = "13"; // 字串
double temp = Double.parseDouble(str);
System.out.println(temp * 2); //輸出結果 26.0
}
}
複製程式碼
範例4:將字串變為boolean型資料
public class Demo04 {
public static void main(String args[]) {
String str = "true"; // 字串
boolean flag = Boolean.parseBoolean(str);
if(flag) {
System.out.println("滿足條件!");
} else {
System.out.println("不滿足條件!");
}
}
}
//控制檯輸出: 條件滿足
複製程式碼
範例5:將字串變為boolean型資料
public class Demo05 {
public static void main(String args[]) {
String str = "hanpang"; // 錯誤字串
boolean flag = Boolean.parseBoolean(str);
if(flag) {
System.out.println("滿足條件!");
} else {
System.out.println("不滿足條件!");
}
}
}
//控制檯輸出: 不滿足條件!
複製程式碼
程式碼分析:
在Boolean進行轉換的過程裡面,如果要轉換的字串不是true或者是false,那麼將統一按照false進行處理。
現在既然實現了字串變為基本資料型別操作,那麼也一定可以實現基本資料型別變為字串的操作,對於此類操作有兩種做法:
-
操作一:任何基本資料型別與字串使用了“+”操作之後都表示變為字串。
public class Demo06 { public static void main(String args[]) { int num = 100; String str = num + ""; // 變為String System.out.println(str.replaceAll("0", "9")); } } //控制檯輸出: 199 複製程式碼
這樣的操作雖然可以簡單的完成,但是會存在有垃圾的問題。
-
操作二:public static String valueOf(資料型別 變數) 開發推薦
public class Demo07 { public static void main(String args[]) { int num = 100; String str = String.valueOf(num); // 變為String System.out.println(str.replaceAll("0", "9")); } } 複製程式碼
這樣的轉換不會產生垃圾,所以在開發時往往會使用以上做法。
5.小結
- 一定要清楚JDK1.5之後才提供有自動裝箱與拆箱操作。
- 字串與基本資料型別的互相轉換:
- 字串變為基本資料型別,依靠包裝類的parseXxx()方法。
- 基本資料型別變為字串,依靠String.valueOf(資料型別 變數)方法。