本文基于JDK 1.8.0_45
基本數據類型
Java中的基本數據類型及其所占字節大小如下所示。基本數據類型是在棧中字節分配內存,其中一個char為兩個字節,這樣一個char中即可存儲一個中文漢字。
類型 | 字節 |
---|---|
byte | 1 |
char | 2 |
short | 2 |
integer | 4 |
long | 8 |
float | 4 |
double | 8 |
boolean | 1/8 |
注:1 byte是8 bits
在定義long的時候需要在數字末尾加上l或者L,在定義float的時候可以在數字末尾加上f或者F(默認如果數字中有小數點則為float),在定義double的時候需要在數字末尾加上d或者D。一般建議在數字末尾加上大寫字母來標示數據類型,這是因為特別是小寫字母l和數字1在印刷體中較難區分,使用大寫字母L更易于閱讀。如:
byte b = 1;
char c = 1;
short s = 1;
int i = 1;
long l = 1L;
float f = 1F;
double d = 1D;
在各種數據類型之間可以進行轉換,轉換順序如下所示:
byte -> short, char -> int -> long
float -> double
int -> float
long -> double
包裝類
在Java中基本數據類型是存儲在棧中,不是對象。為了在一些需要使用對象的場景中使用這些基本數據類型,Java給每種數據類型定義一種相對應的包裝類,對應關系如下。
類型 | 包裝類型 |
---|---|
byte | Byte |
char | Character |
short | Short |
integer | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
這些基本類型和包裝類型可以直接進行轉換,這被稱為自動裝箱、拆箱。自動裝箱、拆箱是一個比較消耗內存的操作,盡量避免使用,下面會介紹Java對這個操作的一些優化。
Number
其中Byte、Short、Integer、Long、Float、Double均繼承自abstract類Number,在Number類中定義了獲取這六種基本數據類型數據的方法,其中方法byteValue和方法shortValue是通過先獲取intValue再將integer的數據強轉為byte或short類型實現的。
基本方法
在每種Number類型中均定義了最大值、最小值、基本數據類型的Class對象、二進制長度和字節長度,另外還提供了眾多的util方法,如有符號無符號轉換,與各種進制(Java目前支持2-36進制,其他均需要自行實現)的string的互相轉換,從系統屬性中直接獲取包裝類型的屬性值的get*。
parse*和valueOf方法的區別是前者返回的是基本數據類型,后者返回的是包裝類的實例。
Integer中有一個很贊的方法decode,接收一個string,根據string中的標示自動判斷該數據的進制并轉換為Integer。
二進制相關方法方法
另外還有一些二進制相關的方法,以下以Integer中的方法為例:
System.out.println("Integer.toBinaryString(5) = " + Integer.toBinaryString(5));// 5的二進制為101
System.out.println("Integer.highestOneBit(5) = " + Integer.highestOneBit(5));// 最高位置為1,其他為0:二進制為100, 十進制為4
System.out.println("Integer.lowestOneBit(5) = " + Integer.lowestOneBit(5));// 最低位置為1,其他為0:二進制為001, 十進制為1
System.out.println("Integer.numberOfLeadingZeros(5) = " + Integer.numberOfLeadingZeros(5));// 二進制中開頭有多少個0:29
System.out.println("Integer.numberOfTrailingZeros(5) = " + Integer.numberOfTrailingZeros(5));// 二進制中結果有多少個0:0
System.out.println("Integer.bitCount(5) = " + Integer.bitCount(5));// 二進制中有多少個1:2
System.out.println("Integer.rotateLeft(5, 2) = " + Integer.rotateLeft(5, 2));// 向左旋轉:二進制為10100,十進制為20
System.out.println("Integer.rotateRight(5, 2) = " + Integer.rotateRight(5, 2));// 向右旋轉:二進制為1000000000000000000000000000001,十進制為1073741825
System.out.println("Integer.reverse(16777216) = " + Integer.reverse(16777216));// 將二進制反轉:128
System.out.println("Integer.signum(-100) = " + Integer.signum(-100));// 符號位為負時返回-1
System.out.println("Integer.signum(0) = " + Integer.signum(0));// 入參為0時返回0
System.out.println("Integer.signum(100) = " + Integer.signum(100));// 符號位為正時返回1
System.out.println("Integer.reverseBytes(1) = " + Integer.reverseBytes(1));// 按字節反轉:16777216
System.out.println("Integer.reverseBytes(16777216) = " + Integer.reverseBytes(16777216));// 按字節反轉:16777216
除以0?
一般我們認為0是不能作為分子進行除法運算的,否則會拋出ArithmeticException異常。但是事實上在Java中對浮點數做除法運算的時候0可以可以作為除數的,如下
System.out.println(-1.0F / 0.0F);// -Infinity
System.out.println(1.0F / 0.0F);// Infinity
System.out.println(0.0F / 0.0F);// NaN
System.out.println(1 / 0);// java.lang.ArithmeticException: / by zero
Number的緩存——享元模式
所謂享元模式即為將一部分最經常使用的對象緩存在內存中,但需要使用這些對象的時候直接從內存中獲取已經實例化好的對象而不是重新實例化一個,這樣可以很好的節約內存并減少對象實例化的開銷。
上面說到自動裝箱、拆箱是一個比較消耗內存的操作,而數字是程序中最經常使用的東西,為了解決這個問題Java使用享元模式對這個操作進行了優化,對Byte、Short、Integer、Long的緩存范圍是-128~127。其中Integer的緩存最大值是可以在JVM啟動的時候通過-XX:AutoBoxCacheMax=size進行自定義。如以下程序在執行的時候添加參數-XX:AutoBoxCacheMax=130即可全部返回true。
System.out.println(Integer.valueOf("127") == Integer.valueOf("127"));
System.out.println(Integer.valueOf("128") == Integer.valueOf("128"));
Boolean
Boolean中定義了true和false的實例、基本數據類型的class對象,與基本數據類型boolean、string直接的互相轉換,另外還提供了一些and、or、xor的util方法。
Character
Character對自動裝箱拆箱也做了跟Number相關類型類似的優化,對0-127之間的char進行了緩存。另外提供了很多其他的util方法,比如大小寫轉換、與string、number之間的互相轉換等。