Java基本數據按類型能夠分爲四大類:布爾型,整數型,浮點型,字符型,這四大類包含8中基本數據類型。緩存
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一個新的對象。
各包裝類高頻區域的取值範圍:
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))); }
-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之間的數都被緩存了。
Integer age = 10; Integer age2 = 10; Integer age3 = 133; Integer age4 = 133; System.out.println((age == age2) + "," + (age3 == age4));
答: true, false
Double num = 10d; Double num2 = 10d; Double num3 = 133d; Double num4 = 133d; System.out.println((num == num2) + "," + (num3 == num4));
答: false, false
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 .
final int iMax = Integer.MAX_VALUE; System.out.println(iMax + 1);
A: 2147483648
B: -2147483648
C: 程序報錯
D: 以上都不是
答: B
題目解析: 這是由於整數在內存中使用的是補碼的形式表示,最高位是符號位 0 表示正數, 1 表示負數, 當執行 +1 時, 最高位就變成了 1, 結果就成了 -2147483648.
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 .
答: 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回收 。
答: 會報錯,由於值3.4是double類型,float類型級別小於double類型,因此會報錯.
答: 須要包裝類的緣由有兩個.
答: 不正確,只有包裝類高頻區域數據纔有緩存.
答: 不正確,基本數據類型的包裝類只有Double和Float沒有高頻區域的緩存.
答: 包裝類由於有高頻區域數據緩存,因此推薦使用equals()方法進行值比較.
答:包裝類提供的功能有如下幾個.
詳見正文"包裝類型"部份內容.
答: 泛型不能使用基本數據類型.泛型在JVM(Java 虛擬機)編譯的時候會類型擦除,好比代碼 List<Integer> list 在JVM編譯的時候會轉換爲List list, 由於泛型是在JDK5時提供的,而JVM的類型擦除是爲了兼容之前代碼的一個這種方案,類型擦除以後就變成了Object, 而Object不能存儲基本數據類型,但可使用基本數據類型對應的包裝類,因此像 List<int> list 這樣的代碼是不被容許的,編譯器階段會檢查報錯,而List<Integer> list是被容許的.
答: 咱們知道正確的使用包裝類,能夠提供程序的執行效率,看可使用已有的緩存,通常狀況下選擇基本數據類型仍是選擇包裝類原則有如下幾個.
答: 基本數據類型不必定存儲在棧中,由於基本類型的存儲位置取決於聲明的做用域,來看具體的解釋.
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)則會使用緩存池中的對象.
答: 返回值爲: false.
題目解析: 由於有些浮點數不能徹底精確的表示出來,以下代碼:
System.out.println(3 * 0.1);
返回的結果是: 0.30000000000000004