走進 JDK 之 談談基本類型

回顧一下前面的一系列文章,java

走進 JDK 之 Integer數組

走進 JDK 之 Long緩存

走進 JDK 之 Floatbash

走進 JDK 之 Byte微信

走進 JDK 之 Boolean架構

除了 chardouble,基本涵蓋了 Java 的全部基本類型。今天就來總結一下基本類型的相關知識。佈局

基本類型概述

Java 中有 8 種基本類型,以下表所示:post

基本類型 大小 最大值 最小值 包裝類 虛擬機中符號
boolean - - - Boolean Z
char 16 bits 65536 0 Character C
byte 8 bits 127 -128 Byte B
short 16 bits 215-1 - 215 Short S
int 32 bits 231-1 231 Integer I
long 64 bits 263-1 -263 Long J
float 32 bits 3.4028235e+38f -3.4028235e+38f Float F
double 64 bits 1.7976931348623157e+308 -1.7976931348623157e+308 Double D

每種基本類型所佔的存儲空間大小不會隨着機器硬件架構的變化而變化,具備良好的可移植性。編碼

數值類型有 byteshortintlongfloatdouble。其中 byteshortintlong 是整數類型。floatdouble 是浮點數類型,分別表示單精度和雙精度,遵循 IEEE 754 浮點數標準。Java 中全部數值類型都是有符號數,最高位表示符號位。spa

boolean 是布爾類型,只有兩個值 TRUEFALSE,在 JVM 中當作 int 處理,兩個值分別爲 10

char 是字符類型,爲 Unicode 編碼。在某些應用場景下,能夠把 char 看成無符號數來處理。

對於上面表格中的內容,還有幾點須要說明一下:

溢出問題

整數類型都有本身的取值範圍,若是強行超出它的範圍會怎麼樣呢?以下面代碼:

public void test() {
    byte max = Byte.MAX_VALUE;
    byte over = (byte) (max + 1);
    System.out.println(over);
}
複製代碼

打印結果是 -128,即 Byte.MIN_VALUE。你能夠把值域範圍想象成時鐘的刻度,最大值是 12 ,最小值是 0 ,已經到了最大值 12 ,你還要再加 1 ,就發生了溢出,又回到了最小值 0 。

棧上大小

在棧上,byteshortbooleanchar 佔用的空間和 int 是一致的,都是佔一個 slotlongdouble 是佔用兩個 slot。具體緣由在以前的文章中具體分析過,這裏再總結一下:

  • 局部變量表能夠當作一個 slot 數組,這樣設計方便使用索引來獲取數據
  • 操做碼是單字節的,最多隻有 256 個字節碼指令,不可能爲每個基本數據類型提供完整的指令支持。

固然,這僅僅只是針對棧上,對於堆上和數組中分配的基本類型,其大小仍是和表中匹配的。

  • 不一樣類型的字節碼指令處理

這塊內容在 走進 JDK 之 Boolean 中詳細介紹過。操做碼是單字節的,最多隻有 256 個字節碼指令,不可能爲每個基本數據類型提供完整的指令支持。大部分的指令都沒有支持 bytecharshortboolean 則更慘,沒有任何指令支持 boolean 類型。對於這些不支持的指令類型,一概使用 int 的相關指令代替。

對於不肯定的狀況,你能夠看一下 class 文件中的字節碼:

javac Test.java
javap -v Test.class
複製代碼

爲何須要基本類型 ?

基本全部語言都有基本數據類型,Java 固然也是如此。標榜 萬物皆對象 的 Java 爲何須要基本數據類型的存在呢?回顧一下你的職業生涯中寫過的代碼,基本數據類型出現的機率能夠說至關之高,基本數據類型是直接存儲在棧內存的,而 new 出來的對象是存儲在堆內存中的。顯然對象佔用的內存是高於基本數據類型的。對象在內存中存儲的佈局能夠分爲 3 塊區域:對象頭實例數據對齊填充。若是把你的代碼中全部基本類型所有替換爲其包裝類,無疑會佔用更多的內存,也下降了運行效率。

爲何須要包裝類 ?

就衝那句 萬物皆對象 ,固然也得有包裝類了!開個玩笑而已 ... 基本數據類型佔用內存更少,運行速度更快,但它也有辦不到的事情,好比我要構造一個包含 intList 集合:

List list = new ArrayList<int>();
複製代碼

顯然這是無法經過編譯的。這裏咱們傳進去的必須是一個對象,因此基本數據類型還必須得有一個包裝類。包裝類中包裝了基本類型的數值,而且提供了一系列處理數值的方法,就像前面分析的過得源碼中各類方法,豐富了基本數據類型的功能。

自動拆箱與自動裝箱

把基本數據類型轉換成包裝類的過程叫作裝箱。

把包裝類轉換成基本數據類型的過程叫作拆箱。

在Java SE 5.0 以前,裝箱和拆箱須要手動進行。可能 Java 開發者們以爲這樣實在太不人性化了,一切應該以提升生產力爲主,在 Java SE 5.0 中就推出了自動裝箱和拆箱。以 Integer 爲例:

Integer i = 10;  //自動裝箱
int b = i;     //自動拆箱
複製代碼

咱們已經解析過 Integer 的源碼,不難想象,自動裝箱和拆箱必然是調用了包裝類的某些方法,javap 看一下字節碼:

2: invokestatic  #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: aload_1
7: invokevirtual #3 // Method java/lang/Integer.intValue:()I
複製代碼

很明瞭,以前的代碼通過編譯器編譯,已經改變了模樣:

Integer i = Integer.valueOf(10);  //裝箱
int b = i.intValue();     //拆箱
複製代碼

還記得 valueOf() 方法嗎?

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

經過 IntegerCache 緩存了 -128127。在此範圍內直接返回緩存的實例,不然建立新對象。

不只僅是 Integer,其餘基本數據類型也都是使用相似 valueOf()xxxValue() 方法來進行自動裝箱和拆箱的。

如今你應該知道了自動裝箱和自動拆箱其實是編譯器在裏面作了手腳,和 Java 虛擬機並無什麼關係。編譯器在生成類的字節碼時,插入必要的方法調用。虛擬機只是執行這些字節碼。

最後

走進 JDK 系列的基本數據類型部分就說到這裏了,還有什麼疑問的話,歡迎關注個人公衆號 秉心說 給我留言。

下一篇是 走進 JDK 之 String,不出意外的話應該要過幾天,String 的內容仍是比較多的,能夠先閱讀閱讀我以前的一篇文章 String 爲何不可變?

文章首發於微信公衆號: 秉心說 , 專一 Java 、 Android 原創知識分享,LeetCode 題解,歡迎關注!

相關文章
相關標籤/搜索