基本數據類型和包裝類

基本數據類型

Java基本數據按類型能夠分爲四大類:布爾型,整數型,浮點型,字符型,這四大類包含8中基本數據類型。緩存

  • 布爾型:boolean
  • 整數型:byte、short、int、long
  • 浮點型:float、double
  • 字符型:char

8種基本類型取值以下:jvm

數據類型 表明含義 默認值 取值 包裝類
boolean 布爾型 false 0(false)/1(true) Boolean
byte 字節型 (byte)0 -128到127 Byte
char 字符型 '\u0000'(空) '\u0000'到'\uFFFF' Character
short 短整數型 (short)0 -2^15到2^15 Short
int  整數型 0 -2^31到2^31-1 Integer
long 長整數型 0L -2^63到2^63-1 Long
float 單浮點型 0.0f 1.4e-45到3.4e+38 Float
double 雙浮點型 0.0d 4.9e-324到1.798e+308 Double

 

 除char的包裝類Character和int的包裝類Integer以外,其餘基本數據類型的包裝類只須要首字母大寫便可。包裝類的做用和特色,本文下半部分詳細講解。spa

咱們能夠在代碼中,查看某種類型的取值範圍,代碼以下:設計

    public static void main(String[] args) {
        // Byte 取值:-128 ~ 127
        System.out.println(String.format("Byte 取值: %d ~ %d", Byte.MIN_VALUE,Byte.MAX_VALUE));
        // Integer 取值: -2147483648 ~ 2147483647
        System.out.println(String.format("Integer 取值: %s ~ %s", Integer.MIN_VALUE,Integer.MAX_VALUE));
    }

 

包裝類型

咱們知道8種基本數據類型都有其對應的包裝類,由於Java的設計思想是萬物既對象,有不少時候咱們須要以對象的形式操做器某項功能,好比說獲取哈希值(hashcode)或獲取類(getClass)等。3d

那包裝類特性有哪些呢?code

1.功能豐富orm

包裝類本質上是一個對象,對象就包含屬性和方法,好比hashCode、getClass、max、min等。對象

2.可定義泛型類型參數blog

包裝類能夠定義泛型,而基本類型不行。接口

好比使用Integer定義泛型,代碼:

List<Integer> list = new ArrayList<>();

若是使用int定義就會報錯,代碼:

3.序列化

由於包裝類都實現了Serializable接口,因此包裝類自然支持序列化和反序列化。好比Integer的類圖以下:

4.類型轉換

包裝類提供了類型轉換的方法,能夠很方便的實現類型之間的轉換,好比Integer類型轉換代碼:

        String age = "18";
        int ageInt = Integer.parseInt(age) + 2;
        //輸出結果:20
        System.out.println(ageInt);

5.高頻區間的數據緩存

此特性爲包裝類很重要的用途之一,用於高頻區間的數據緩存,以Integer爲例來講,在數值區間爲-128~127時,會直接複用已有對象,在這區間以外的數字纔會在堆上產生。

咱們使用 == 對 Integer 進行驗證,代碼以下:

    public static void main(String[] args) {
        // Integer 高頻區緩存範圍 -128 ~ 127
        Integer num1 = 127;
        Integer num2 = 127;
        // Integer 取值127 == 結果爲 true (值127 num1==num2 =>true)
        System.out.println("值127 num1==num2 =>" + (num1 == num2));

        Integer num3 = 128;
        Integer num4 = 128;
        // Integer 取值128 == 結果爲 false (值128 num3==num4 =>false)
        System.out.println("值128 num3==num4 =>" + (num3 == num4));
    }

從上面的代碼很明顯能夠看出,Integer爲127時複用了已有的對象,當值爲128時,從新在堆上生成了新對象。

爲何會產生高頻區域數據緩存呢?咱們查看源碼就能發現「線索」,源碼版本 JDK8,源碼以下:

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

因而可知,高頻區域的數值會直接使用已有對象,非高頻區域的數值會從新new一個新的對象。

各包裝類高頻區域的取值範圍:

  • Boolean: 使用靜態final定義,就會返回靜態值
  • Byte:緩衝區 -128~127
  • Short:緩衝區 -128~127
  • Character:緩衝區 0~127
  • Long:緩衝區 -128~127
  • Integer:緩衝區 -128~127

包裝類的注意事項

  • int默認值是0,而Integer的默認值是null
  • 推薦全部包裝類對象之間的值比較實用equals()方法,由於包裝類的非高頻去數據會在堆上產生,而高頻區又會複用已有對象,這樣會致使一樣的代碼,由於取值不一樣,而產生兩種大相徑庭的結果。代碼示例:
    public static void main(String[] args) {
            // Integer 高頻區緩存範圍 -128 ~ 127
            Integer num1 = 127;
            Integer num2 = 127;
            // Integer 取值127 == 結果爲 true (值127 num1==num2 =>true)
            System.out.println("值127 num1==num2 =>" + (num1 == num2));
    
            Integer num3 = 128;
            Integer num4 = 128;
            // Integer 取值128 == 結果爲 false (值128 num3==num4 =>false)
            System.out.println("值128 num3==num4 =>" + (num3 == num4));
            // Integer 取值128 equals 結果爲 true (值128 num3.equals(num4) =>true)
            System.out.println("值128 num3.equals(num4) =>" + (num3.equals(num4)));
        }
  • Float 和 Double不會有緩存,其餘包裝類都有緩存。
  • Integer是惟一一個能夠修改緩存範圍的包裝類,在VM options加入參數:
    -XX:AutoBoxCacheMax=666 即修改緩存最大值爲666。

    示例代碼以下:

        public static void main(String[] args) {
            Integer num1 = 128;
            Integer num2 = 128;
            System.out.println("值爲-128 =>" + (num1 == num2));
            Integer num3 = 666;
            Integer num4 = 666;
            System.out.println("值666 num3==num4 =>" + (num3 == num4));
            Integer num5 = 667;
            Integer num6 = 667;
            System.out.println("值爲667 =>" + (num5 == num6));
        }

    執行結果以下:

    值爲-128 =>true
    值爲666 =>true
    值爲667 =>false

    因而可知將Integer最大緩存修改成666以後,667不會被緩存,而-128~666之間的數都被緩存了。

小測驗哦~

1.如下Integer代碼輸出的結果是?

        Integer age = 10;
        Integer age2 = 10;
        Integer age3 = 133;
        Integer age4 = 133;
        System.out.println((age == age2) + "," + (age3 == age4));

答: true, false 

2.如下Double代碼輸出的結果是?

        Double num = 10d;
        Double num2 = 10d;
        Double num3 = 133d;
        Double num4 = 133d;
        System.out.println((num == num2) + "," + (num3 == num4));

答: false, false 

3.如下程序輸出結果是?

        int i = 100;
        Integer j = new Integer(100);
        System.out.println(i == j);
        System.out.println(j.equals(i));

A: true,true

B: true,false

C: false,true

D: false,false

答: A

題目分析:有人認爲這和Integer高速緩存有關係,但你發現把值改成10000結果也是 true,true ,這是由於  Integer  和  int  比較時,會自動拆箱爲  int  ,至關於兩個  int  比較,值必定是  true, true .

4.如下程序執行的結果是?

        final int iMax = Integer.MAX_VALUE;
        System.out.println(iMax + 1);

A:  2147483648

B:  -2147483648

C:  程序報錯

D:  以上都不是

答:  B

題目解析:  這是由於整數在內存中使用的是補碼的形式表示,最高位是符號位 0 表示正數, 1 表示負數, 當執行 +1 時, 最高位就變成了 1, 結果就成了  -2147483648.

5.如下程序執行的結果是?

        Set<Short> set = new HashSet<>();
        for(short i = 0; i < 5; i++){
            set.add(i);
            set.remove(i - 1);
        }
        System.out.println(set.size());

A:  1

B:  0

C:  5

D:  以上都不是

答:  C

題目解析:  Short 類型 -1 以後轉換成了Int 類型, remove() 的時候在集合中找不到 Int 類型的數據, 因此就沒有刪除任何元素, 執行的結果就是 5 .

6. short s = 2; s = s + 1; 會報錯嗎? short s = 2; s+=1; 會報錯嗎?

答:  s=s+1 會報錯, s+=1 不會報錯, 由於 s = s + 1 會致使short 類型升級爲 int 類型,因此會報錯, s+=1 仍是原來的short 類型, 因此不會報錯.  

其實對此我是有疑問的,繼續探究.分別從源碼和底層原理兩個方面來了解一下這個問題.

從源碼角度:

(1) s = s+1報錯,這句先執行s+1而後把結果賦給s,因爲1爲int類型,因此s+1的返回值是int,編譯器自動進行了隱式類型轉換。因此將一個int類型賦給short就會出錯

(2) s += 1這句不報錯

經過反編譯能夠看到源碼爲

當jvm識別+=且原值爲整型時,會先忽略原值的具體數據類型,先用int計算後,若是計算結果是int就直接轉爲原來的數據類型,若是不是int就強轉爲int而後再轉回原數據類型,例如

反編譯後爲

輸出結果爲2,損失了精度.

從底層原理:

而i+1 是將heap中數據直接送到寄存器中進行運算,運算結果會直接存放在heap中。

i+=1 運行的底層Heap申請一個區域存放i,在數據區域開闢一個區域存放1,2個內存段的數據被送入到寄存器中進行運算,運算結果被放到heap中,數據區域運算後被自動釋放後由GC回收 。

7. float f = 3.4; 會報錯嗎? 爲何?

答: 會報錯,由於值3.4是double類型,float類型級別小於double類型,因此會報錯.

8.爲何須要包裝類?

答: 須要包裝類的緣由有兩個.

  1. Java的設計思想是萬物既對象,包裝類體現了面向對象的設計理念;
  2. 包裝類包含了不少屬性和方法,比基本數據類型功能多,好比提供的獲取哈希值(hashCode)或獲取類(getClass)的方法等.

9.基本類 int 和包裝類 Integer ,在 -128 ~ 127 之間都會複用已有的緩存對象,這種說法正確嗎?

答: 不正確,只有包裝類高頻區域數據纔有緩存.

10.包裝類 Double 和 Integer 同樣都有高頻區域數據緩存,這種說法正確嗎?

答: 不正確,基本數據類型的包裝類只有Double和Float沒有高頻區域的緩存.

11.包裝類的值比較要是用什麼方法?

答: 包裝類由於有高頻區域數據緩存,因此推薦使用equals()方法進行值比較.

12.包裝類有哪些功能?

答:包裝類提供的功能有如下幾個.

  • 功能豐富 : 包裝類包含了有 hashCode, getClass, max, min等方法
  • 能夠定義泛型類型參數 :  例如List<Integer> list = new ArrayList<>(); ;
  • 序列化 : 包裝類實現了Serializable接口,因此包裝類自然支持序列化和反序列化.
  • 類型轉換 : 包裝類提供了方便的類型轉換方法,好比Integer的parseInt()方法.
  • 高頻區域數據緩存 : 高頻區域可以使用已有的緩存對象;

詳見正文"包裝類型"部份內容.

 13.泛型能夠爲基本數據類型嗎? 爲何?

答: 泛型不能使用基本數據類型.泛型在JVM(Java 虛擬機)編譯的時候會類型擦除,好比代碼 List<Integer> list 在JVM編譯的時候會轉換爲List list, 由於泛型是在JDK5時提供的,而JVM的類型擦除是爲了兼容之前代碼的一個這種方案,類型擦除以後就變成了Object, 而Object不能存儲基本數據類型,但可使用基本數據類型對應的包裝類,因此像 List<int> list 這樣的代碼是不被容許的,編譯器階段會檢查報錯,而List<Integer> list是被容許的.

14.選擇包裝類仍是基本類的原則有哪些?

答: 咱們知道正確的使用包裝類,能夠提供程序的執行效率,看可使用已有的緩存,通常狀況下選擇基本數據類型仍是選擇包裝類原則有如下幾個.

  1. 全部POJO類屬性必須使用包裝類;
  2. RPC方法返回值和參數必須使用包裝類;
  3. 全部局部變量推薦使用基本數據類型;

15.基本數據類型在JVM中必定存儲在棧中嗎? 爲何?

答: 基本數據類型不必定存儲在棧中,由於基本類型的存儲位置取決於聲明的做用域,來看具體的解釋.

  • 當基本數據類型爲局部變量的時候,好比在方法中聲明的變量,則存放在方法棧中,當方法結束,系統會釋放方法棧,在該方法中的變量也會隨着棧的銷燬而結束,這也是局部變量只能在方法中使用的緣由;
  • 當基本數據類型爲全局變量的時候,好比類中的聲明的變量,則存儲在堆上,由於全局變量不會隨着某個方法的執行結束而銷燬.

16.如下程序執行的結果是?

        Integer i1 = new Integer(10);
        Integer i2 = new Integer(10);
        Integer i3 = Integer.valueOf(10);
        Integer i4 = Integer.valueOf(10);
        System.out.println(i1 == i2);
        System.out.println(i2 == i3);
        System.out.println(i3 == i4);

答案: false;false;false

題目解析: new Integer(10) 每次都會建立一個新對象,Integer.valueOf(10)則會使用緩存池中的對象.

17.  3*0.1==0.3  返回值是多少?

答: 返回值爲:  false.

題目解析: 由於有些浮點數不能徹底精確的表示出來,以下代碼:

 System.out.println(3 * 0.1); 

返回的結果是:   0.30000000000000004

相關文章
相關標籤/搜索