衆所周知,Java是一門靜態類型的語言,這意味着全部的變量和表達式的類型會在編譯時肯定。同時,Java 仍是一門強類型的語言,所以變量的值或表達式的結果的類型都會受到限制(好比一個聲明爲 String
的變量不的值不多是一個數值 1
),類型之間的運算也會被限制,這有助於在編譯時發現絕大多數的錯誤。html
在 Java中存在兩種類型:基本類型和引用類型。java
PrimitiveType: {Annotation} NumericType {Annotation} boolean NumericType: IntegralType FloatingPointType IntegralType: (one of) byte short int long char FloatingPointType: (one of) float double
Java Language Specification (Java SE 8 Edition) §4.2 Primitive Types and Values
值得注意的是,基本類型的值的狀態不會被共享。oracle
好比下面這個例子:code
int i = 0; int j = i; i += 1; System.out.println(j); AtomicInteger i = new AtomicInteger(0); AtomicInteger j = i; i.addAndGet(1); System.out.println(j);
上述代碼將輸出:orm
0 1
整數類型 (IntegralType) 包含了如下五種類型:htm
類型 | 長度 / 位 (bit) | 取值範圍 |
---|---|---|
byte |
有符號 8 | -128 ~ 127 |
short |
有符號 16 | -32768 ~ 32767 |
int |
有符號 32 | -214783648 ~ 2147483647 |
long |
有符號 64 | -9223372036854775808 ~ 9223372036854775807 |
char |
無符號 16 | \u0000 ~ \uffff 等價於 0 ~ 65535 |
<
、<=
、>
、>=
、==
、!=
,其結果爲 boolean
類型;數值運算符:對象
+
、-
*
、/
、%
+
、-
++
, 分爲前綴自增 (++i
) 和後綴自增 (i++
)--
, 分爲前綴自減 (--i
) 和後綴自減 (i--
)位移運算符:內存
<<
>>
>>>
~
&
、^
、|
? :
cast
+
這裏面加號出現了好幾回,包括 一元運算符、加法運算符、自增運算符、字符串拼接運算符,後三者運算符就如它們的字面意思般,很好理解。ci
那麼 一元運算符 是什麼意思呢?字符串
讓咱們來看看下面這份代碼:
static void is (short i) { System.out.println("short"); } static void is (int i) { System.out.println("int"); } static void is (long i) { System.out.println("long"); } static void main (String[] args) { short i = 5; int j = 10; is(i); is(+i); is(-i); is(j); is(+j); is(-j); }
上述代碼將輸出:
short int int int int int
很顯然,第 17~19 行的調用執行的是參數類型爲 int
的方法,然而第 20~21 行的調用執行的並非參數類型爲 long
的方法。
我在 JSL § 15.15.3 Unary Plus Operator + 中並未看出一元運算符的具體影響,根據實驗結果只能推測一元運算符會將低於 int
的數值類型提高到 int
類型 (你能夠聲明一個 byte h = 0
,is(+h)
仍然會調用參數類型爲 int
的方法),並且對於 +
和 -
都是提高類型的做用,並非直覺意義上的一個升一個降。
關於這個 一元運算符 的用法其實並非不少,有下列幾種:
// 若是方法接受的是 int 類型的話,僅僅用來明確這是個正數仍是負數。 func(+1); func(-1); // 輸出字符的代碼值時的小技巧 // 由於字符類型 char 是低於 int 的整數類型 System.out.println(+'c'); // 99
對於移位運算符之外的整數運算而言,若是兩個操做數中至少有一個 long
,那麼此次運算將會按 64位精度進行計算,而且其計算結果也是 long
類型,此時若是另外一個操做數不是 long
,那麼會將它提高到 long
類型再計算;若是兩個操做數都不是 long
,那麼會按 32位精度進行計算,而且計算結果爲 int
,若是任何一個操做數不是 int
, 那麼都將提高到 int
類型後再計算。
整數運算符會在如下狀況拋出異常:
NullPointerException
;ArithmeticException
;OutOfMemoryError
來看看規範中給出的示例代碼:
class Test { public static void main (String[] args) { int i = 1000000; System.out.println(i * i); long l = i; System.out.println(l * l); System.out.println(20296 / (l - i)); } }
上述代碼將輸出:
-727379968 1000000000000 ArithmeticException
對於 int
來講,1000000^2 太大了,而因爲以前的運算規則,i * i
只能保存結果的低32位,十進制下也就是 -727379968
。
Java 中的浮點類型遵循 IEEE 754
標準的定義。
IEEE 754-1985 - Wikiwand
在 IEEE 754 標準中,定義了 32位精度的 float
、64位精度的 double
,還有正負數、正負0、正負無窮和特殊的 NaN
。
NaN
用於表示無效操做的結果,好比 0.0 / 0.0
(0 / 0
才適用整數運算中的 右操做數爲0 的異常規則),你能夠在 Float.NaN
和 Double.NaN
中找到。
NaN
是無序的,所以
NaN
,則比較運算符 (<
、<=
、>
、>=
) 都會返回 false
NaN
,則相等運算符 (==
) 返回 false
若是 x
或 y
是 NaN
,則 (x < y) == !(x >= y)
將返回 false
NaN
, 則不等式運算符 !=
將返回 true
當且僅當 x
爲 NaN
時,x != x
將返回 true
<
、<=
、>
、>=
、==
、!=
,其結果爲 boolean
類型;數值運算符:
+
、-
*
、/
、%
+
、-
++
, 分爲前綴自增 (++i
) 和後綴自增 (i++
)--
, 分爲前綴自減 (--i
) 和後綴自減 (i--
)? :
cast
+
若是一次計算中,至少有一個二元運算符的操做數是浮點類型的,那麼該操做就是一個浮點運算,即便另外一個操做數是整數。
System.out.println(10 * 0.1); // 1.0
對於浮點運算而言,若是兩個操做數中至少有一個 double
,那麼此次運算將會按 64位精度進行計算,而且其計算結果也是 double
類型,此時若是另外一個操做數不是 double
,那麼會將它提高到 double
類型再計算;若是兩個操做數都不是 double
,那麼會按 32位精度進行計算,而且計算結果爲 float
,若是任何一個操做數不是 float
, 那麼都將提高到 float
類型後再計算。
浮點運算有溢出 (overflows) 和下溢 (underflows),其中溢出將產生有符號的無窮大,而下溢則產生一個非標準化 (denormalized) 的值或是一個有符號的0。
數學上沒法肯定結果的浮點運算將產生 NaN
。
全部 NaN
參與的浮點運算都會產生 NaN
。
在下列狀況中,浮點運算會拋出異常:
NullPointerException
;OutOfMemoryError
。接下來看看規範中給出的示例代碼:
class Test { public static void main(String[] args) { // 溢出 double d = 1e308; System.out.print("溢出產生了無窮大: "); System.out.println(d + "*10==" + d*10); // 漸變下溢 (gradual underflow) d = 1e-305 * Math.PI; System.out.print("漸變下溢: " + d + "\n "); for (int i = 0; i < 4; i++) System.out.print(" " + (d /= 100000) + "\n"); System.out.println(); // 產生 NaN System.out.print("0.0/0.0 產生的不是數字: "); d = 0.0/0.0; System.out.println(d); // 產生不精確結果的四捨五入: System.out.print("單精度下的不精確結果:"); for (int i = 0; i < 100; i++) { float z = 1.0f / i; if (z * i != 1.0f) System.out.print(" " + i); } System.out.println(); // 另外一個產生不精確結果的四捨五入: System.out.print("雙精度下的不精確結果:"); for (int i = 0; i < 100; i++) { double z = 1.0 / i; if (z * i != 1.0) System.out.print(" " + i); } System.out.println(); // 轉換到整數時發生的結果階段: System.out.print("強制轉換到整數: "); d = 12345.6; System.out.println((int)d + " " + (int)(-d)); } }
上述代碼將輸出
溢出產生了無窮大: 1.0e + 308 * 10 == Infinity 漸變下溢: 3.141592653589793E-305 3.1415926535898E-310 3.141592653E-315 3.142E-320 0.0 0.0 / 0.0 產生的不是數字:NaN 單精度下的不精確結果:0 41 47 55 61 82 83 94 97 雙精度下的不精確結果:0 49 98 強制轉換到整數:12345 -12345
值得注意的是,在 漸變下溢 的例子中,咱們能夠看到精度逐漸喪失。
boolean
類型表示兩個邏輯量,true
和 false
。
布爾運算符是:
==
和!=
(!
&
, ^
和 |
&&
和 ||
? :
+
,當給定一個String
操做數和一個 boolean
操做數時,它將把 boolean
操做符轉換爲一個String
( "true"
或"false"
),而後產生一個新建立的String
,其值爲兩個字符串的鏈接結果。布爾表達式決定了幾種語句中的控制流:
if
語句while
語句do
語句for
語句一個boolean
表達式還決定在 ? :
運算符中使用哪一個子表達式的值做爲結果 。
只有Boolean
表達式和Boolean
表達式能夠在控制流程語句中使用。
經過表達式 x!=0
,能夠將整數或浮點表達式 x
轉換爲 boolean
值,這遵循了 C 語言約定,即任何非零值爲true
。
經過表達式 obj!=null
,能夠將對象引用 obj
轉換爲boolean
值,這一樣遵循了 C 語言約定(除null
以外的任何引用爲true
。
參考資料: Java Language Specification (Java SE 8 Edition) § 4.2