1. 程式人生 > >Java基礎:Java基本資料型別以及包裝類

Java基礎:Java基本資料型別以及包裝類

1. 前言

最近在研究Java 基礎知識,作為Java最重要的資料型別。Java有八大基本資料型別,相對應的有八種包裝類。我們需要對這些基本資料型別進行理解和掌握。

2.Java基本資料型別

Java基本型別共有八種,基本型別可以分為三類,字元型別char,布林型別boolean以及數值型別byte、short、int、long、float、double。數值型別又可以分為整數型別byte、short、int、long和浮點數型別float、double。JAVA中的數值型別不存在無符號的,它們的取值範圍是固定的,不會隨著機器硬體環境或者作業系統的改變而改變。實際上,JAVA中還存在另外一種基本型別void,它也有對應的包裝類 java.lang.Void,不過我們無法直接對它們進行操作。8 中型別表示範圍如下:

  • byte:8位,最大儲存資料量是255,存放的資料範圍是-128~127之間。
  • short:16位,最大資料儲存量是65536,資料範圍是-32768~32767之間。
  • int:32位,最大資料儲存容量是2的32次方減1,資料範圍是負的2的31次方到正的2的31次方減1。
  • long:64位,最大資料儲存容量是2的64次方減1,資料範圍為負的2的63次方到正的2的63次方減1。
  • float:32位,資料範圍在3.4e-45~1.4e38,直接賦值時必須在數字後加上f或F。
  • double:64位,資料範圍在4.9e-324~1.8e308,賦值時可以加d或D也可以不加。
  • boolean:只有true和false兩個取值。
  • char:16位,儲存Unicode碼,用單引號賦值。

3. Java包裝類

Java決定了每種簡單型別的大小。這些大小並不隨著機器結構的變化而變化。這種大小的不可更改正是Java程式具有很強移植能力的原因之一。下表列出了Java中定義的簡單型別、佔用二進位制位數及對應的封裝器類。

簡單型別 boolean byte char short Int long float double void
二進位制位數 1 8 16 16 32 64 32 64
封裝類 Boolean Byte Character Short Integer Long Float Double Void

對於數值型別的基本型別的取值範圍,我們無需強制去記憶,因為它們的值都已經以常量的形式定義在對應的包裝類中了。如:

  • 基本型別byte 二進位制位數:Byte.SIZE最小值:Byte.MIN_VALUE最大值:Byte.MAX_VALUE
  • 基本型別short二進位制位數:Short.SIZE最小值:Short.MIN_VALUE最大值:Short.MAX_VALUE
  • 基本型別char二進位制位數:Character.SIZE最小值:Character.MIN_VALUE最大值:Character.MAX_VALUE
  • 基本型別double 二進位制位數:Double.SIZE最小值:Double.MIN_VALUE最大值:Double.MAX_VALUE

基本型別的優勢:資料儲存相對簡單,運算效率比較高

包裝類的優勢:有的容易,比如集合的元素必須是物件型別,滿足了java一切皆是物件的思想

4. 資料型別之間的轉換

4.1 簡單型別資料間的轉換

簡單型別資料間的轉換,有兩種方式:自動轉換和強制轉換,通常發生在表示式中或方法的引數傳遞時。

自動轉換

具體地講,當一個較"小"資料與一個較"大"的資料一起運算時,系統將自動將"小"資料轉換成"大"資料,再進行運算。而在方法呼叫時,實際引數較"小",而被呼叫的方法的形式引數資料又較"大"時(若有匹配的,當然會直接呼叫匹配的方法),系統也將自動將"小"資料轉換成"大"資料,再進行方法的呼叫,自然,對於多個同名的過載方法,會轉換成最"接近"的"大"資料並進行呼叫。這些型別由"小"到"大"分別為 (byte,short,char)–int–long–float—double。這裡我們所說的"大"與"小",並不是指佔用位元組的多少,而是指表示值的範圍的大小。

強制轉換

將"大"資料轉換為"小"資料時,你可以使用強制型別轉換。即你必須採用下面這種語句格式: int n=(int)3.14159/2;可以想象,這種轉換肯定可能會導致溢位或精度的下降。

5. 自動拆箱和自動裝箱

自動裝箱

在Java SE5之前,如果要生成一個數值為10的Integer物件,必須這樣進行:

Integer i = new Integer(10);

但是在Java SE5之後,開始提供了自動裝箱的功能,如果要生成一個數值為10的Integer物件,只需要這樣就可以了:

Integer i = 10;

自動拆箱

顧名思義,跟裝箱對應,就是自動將包裝器型別轉換為基本資料型別:

Integer i = 10;  //裝箱
int n = i;   //拆箱

簡單一點說,裝箱就是 自動將基本資料型別轉換為包裝器型別;拆箱就是 自動將包裝器型別轉換為基本資料型別。

下面我們來看這樣一個程式碼:

public class JVM {
    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;

        out.println(c == d);
        out.println(e == f);
        out.println(c == (a+b));
        out.println(c.equals(a+b));
        out.println(g == (a+b));
        out.println(g.equals(a+b));
    }
}

執行結果:

true
false
true
true
true
false

首先我們知道包裝類的資料:

包裝類資料,如Integer, String, Double,Long等將相應的基本資料型別包裝起來的類。這些類資料全部存在於堆中。

下面我再分析一下Long包裝類的原始碼:

private static class LongCache {
    private LongCache(){}

    static final Long cache[] = new Long[-(-128) + 127 + 1];

    static {
        for(int i = 0; i < cache.length; i++)
        cache[i] = new Long(i - 128);
    }
}

從原始碼中我們可以看出Long包裝類從類被載入的時候開始,就已經把-128到127之間的Long資料快取到一個Long[]陣列中去了,從JVM記憶體模型的角度來看。Long包裝類已經把-128到127之間的Long資料生成得到java堆中。

總結:

  1. 包裝類的“==”運算在不遇到算術運算的情況下不會自動拆箱,equals()方法不處理資料轉型的關係。
  2. 在自動裝箱時,把int變成Integer的時候,是有規則的,當你的int的值在-128-IntegerCache.high(127) 時,返回的不是一個新new出來的Integer物件,而是一個已經快取在堆中的Integer物件,(我們可以這樣理解,系統已經把-128到127之 間的Integer快取到一個Integer陣列中去了,如果你要把一個int變成一個Integer物件,首先去快取中找,找到的話直接返回引用給你就 行了,不必再新new一個),如果不在-128-IntegerCache.high(127) 時會返回一個新new出來的Integer物件。

所以在e==f時,由於321沒有在堆中快取。所以堆中會建立兩個Integer物件,並且這兩個物件的值都是321。然後由e和f兩個引用指向這兩個物件。所以e==f返回false(因為它們兩個引用指向的是兩個物件)。c==d是因為在堆中已經快取了-128L到127L的值的Long物件,所以c和d兩個引用指向的是同一個物件。c==(a+b):由於a+b是一個操作運算,所以包裝類會執行自動拆箱成兩個基本資料型別進行運算,返回3. 然後c==3。 "=="運算遇到算式運算時會進行自動拆箱,c自動拆箱成基本資料型別進行比較,返回true。

最後我來解釋一下最後兩個的執行結果為什麼是true與false,先來看一下後兩句話在進行編譯之後在.class檔案中的樣子吧:

System.out.println(g.longValue() == (long)(a.intValue() + b.intValue()));
System.out.println(g.equals(Integer.valueOf(a.intValue() + b.intValue())));

我們可以看到編譯器對out.println(g == (a+b));進行編譯的時候,進行了拆箱與向上轉型的操作,所以此時比較的僅僅是兩個變數的字面值,與基本資料型別的比較是一樣的,所以是true,而最後仍然比較的是物件中的資料並且對a沒有進行向上轉型,Long中存在的資料肯定就和Integer中存在的資料不等了,所以為false。

重要:我們能將字面值直接賦給Integer類是因為Java語法的存在,實際上Integer a = 1在經過編譯之後是這樣的:Integer a = new Integer(1),語法糖幫助我們簡化了語法。