摘要:在中存在兩種類型基本類型和引用類型。值得注意的是,基本類型的值的狀態(tài)不會(huì)被共享。浮點(diǎn)類型和它們的值中的浮點(diǎn)類型遵循標(biāo)準(zhǔn)的定義。布爾類型和它們的值類型表示兩個(gè)邏輯量,和。
眾所周知,Java是一門靜態(tài)類型的語(yǔ)言,這意味著所有的變量和表達(dá)式的類型會(huì)在編譯時(shí)確定。同時(shí),Java 還是一門強(qiáng)類型的語(yǔ)言,因此變量的值或表達(dá)式的結(jié)果的類型都會(huì)受到限制(比如一個(gè)聲明為 String 的變量不的值不可能是一個(gè)數(shù)值 1),類型之間的運(yùn)算也會(huì)被限制,這有助于在編譯時(shí)發(fā)現(xiàn)絕大多數(shù)的錯(cuò)誤。
在 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
值得注意的是,基本類型的值的狀態(tài)不會(huì)被共享。
比如下面這個(gè)例子:
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);
上述代碼將輸出:
0 1整數(shù)類型和它們的值
整數(shù)類型 (IntegralType) 包含了以下五種類型:
類型 | 長(zhǎng)度 / 位 (bit) | 取值范圍 |
---|---|---|
byte | 有符號(hào) 8 | -128 ~ 127 |
short | 有符號(hào) 16 | -32768 ~ 32767 |
int | 有符號(hào) 32 | -214783648 ~ 2147483647 |
long | 有符號(hào) 64 | -9223372036854775808 ~ 9223372036854775807 |
char | 無符號(hào) 16 | u0000 ~ uffff 等價(jià)于 0 ~ 65535 |
比較運(yùn)算符: <、<=、>、>= 、==、!= ,其結(jié)果為 boolean 類型;
數(shù)值運(yùn)算符:
一元運(yùn)算符: + 、-
乘法運(yùn)算符: *、/、%
加法運(yùn)算符: + 、-
自增運(yùn)算符: ++, 分為前綴自增 (++i) 和后綴自增 (i++)
自減運(yùn)算符: --, 分為前綴自減 (--i) 和后綴自減 (i--)
位移運(yùn)算符:
左移運(yùn)算符: <<
有符號(hào)右移: >>
無符號(hào)右移: >>>
按位互補(bǔ)運(yùn)算符: ~
整數(shù)按位運(yùn)算符: &、^、|
條件運(yùn)算符: ? :
類型轉(zhuǎn)換運(yùn)算符: cast
字符串拼接運(yùn)算符: +
這里面加號(hào)出現(xiàn)了好幾次,包括 一元運(yùn)算符、加法運(yùn)算符、自增運(yùn)算符、字符串拼接運(yùn)算符,后三者運(yùn)算符就如它們的字面意思般,很好理解。
那么 一元運(yùn)算符 是什么意思呢?
讓我們來看看下面這份代碼:
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 行的調(diào)用執(zhí)行的是參數(shù)類型為 int 的方法,然而第 20~21 行的調(diào)用執(zhí)行的并不是參數(shù)類型為 long 的方法。
我在 JSL § 15.15.3 Unary Plus Operator + 中并未看出一元運(yùn)算符的具體影響,根據(jù)實(shí)驗(yàn)結(jié)果只能推測(cè)一元運(yùn)算符會(huì)將低于 int 的數(shù)值類型提升到 int 類型 (你可以聲明一個(gè) byte h = 0,is(+h) 仍然會(huì)調(diào)用參數(shù)類型為 int 的方法),而且對(duì)于 + 和 - 都是提升類型的作用,并不是直覺意義上的一個(gè)升一個(gè)降。
關(guān)于這個(gè) 一元運(yùn)算符 的用法其實(shí)并不是很多,有下列幾種:
// 如果方法接受的是 int 類型的話,僅僅用來明確這是個(gè)正數(shù)還是負(fù)數(shù)。 func(+1); func(-1); // 輸出字符的代碼值時(shí)的小技巧 // 因?yàn)樽址愋?char 是低于 int 的整數(shù)類型 System.out.println(+"c"); // 99整數(shù)運(yùn)算的溢出和可能引發(fā)的異常
對(duì)于移位運(yùn)算符以外的整數(shù)運(yùn)算而言,如果兩個(gè)操作數(shù)中至少有一個(gè) long ,那么這次運(yùn)算將會(huì)按 64位精度進(jìn)行計(jì)算,并且其計(jì)算結(jié)果也是 long 類型,此時(shí)如果另一個(gè)操作數(shù)不是 long,那么會(huì)將它提升到 long 類型再計(jì)算;如果兩個(gè)操作數(shù)都不是 long,那么會(huì)按 32位精度進(jìn)行計(jì)算,并且計(jì)算結(jié)果為 int,如果任何一個(gè)操作數(shù)不是 int, 那么都將提升到 int 類型后再計(jì)算。
整數(shù)運(yùn)算符會(huì)在以下情況拋出異常:
涉及到對(duì)引用類型拆箱時(shí),如果是空引用,那么會(huì)拋出 NullPointerException;
如果右操作數(shù)為零,那么整數(shù)除法運(yùn)算符和整數(shù)取余運(yùn)算符都會(huì)拋出 ArithmeticException;
在自增和自減的時(shí)候,如果涉及到拆箱裝箱且內(nèi)存不足,會(huì)拋出 OutOfMemoryError
來看看規(guī)范中給出的示例代碼:
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
對(duì)于 int 來說,1000000^2 太大了,而由于之前的運(yùn)算規(guī)則,i * i 只能保存結(jié)果的低32位,十進(jìn)制下也就是 -727379968。
浮點(diǎn)類型和它們的值Java 中的浮點(diǎn)類型遵循 IEEE 754 標(biāo)準(zhǔn)的定義。
IEEE 754-1985 - WikiwandIEEE 754_百度百科
在 IEEE 754 標(biāo)準(zhǔn)中,定義了 32位精度的 float、64位精度的 double,還有正負(fù)數(shù)、正負(fù)0、正負(fù)無窮和特殊的 NaN。
NaN 用于表示無效操作的結(jié)果,比如 0.0 / 0.0 (0 / 0 才適用整數(shù)運(yùn)算中的 右操作數(shù)為0 的異常規(guī)則),你可以在 Float.NaN 和 Double.NaN 中找到。
NaN 是無序的,因此
如果一次運(yùn)算中一個(gè)或兩個(gè)操作數(shù)都是 NaN,則比較運(yùn)算符 (<、<=、>、>=) 都會(huì)返回 false
如果操作數(shù)是 NaN,則相等運(yùn)算符 (==) 返回 false
如果 x 或 y 是 NaN,則 (x < y) == !(x >= y) 將返回 false
如果任一操作數(shù)是 NaN, 則不等式運(yùn)算符 != 將返回 true
當(dāng)且僅當(dāng) x 為 NaN 時(shí),x != x 將返回 true
浮點(diǎn)類型所支持的運(yùn)算比較運(yùn)算符: <、<=、>、>= 、==、!= ,其結(jié)果為 boolean 類型;
數(shù)值運(yùn)算符:
一元運(yùn)算符: + 、-
乘法運(yùn)算符: *、/、%
加法運(yùn)算符: + 、-
自增運(yùn)算符: ++, 分為前綴自增 (++i) 和后綴自增 (i++)
自減運(yùn)算符: --, 分為前綴自減 (--i) 和后綴自減 (i--)
條件運(yùn)算符: ? :
類型轉(zhuǎn)換運(yùn)算符: cast
字符串拼接運(yùn)算符: +
如果一次計(jì)算中,至少有一個(gè)二元運(yùn)算符的操作數(shù)是浮點(diǎn)類型的,那么該操作就是一個(gè)浮點(diǎn)運(yùn)算,即使另一個(gè)操作數(shù)是整數(shù)。
System.out.println(10 * 0.1); // 1.0
對(duì)于浮點(diǎn)運(yùn)算而言,如果兩個(gè)操作數(shù)中至少有一個(gè) double,那么這次運(yùn)算將會(huì)按 64位精度進(jìn)行計(jì)算,并且其計(jì)算結(jié)果也是 double 類型,此時(shí)如果另一個(gè)操作數(shù)不是 double,那么會(huì)將它提升到 double 類型再計(jì)算;如果兩個(gè)操作數(shù)都不是 double,那么會(huì)按 32位精度進(jìn)行計(jì)算,并且計(jì)算結(jié)果為 float,如果任何一個(gè)操作數(shù)不是 float, 那么都將提升到 float 類型后再計(jì)算。
浮點(diǎn)運(yùn)算的溢出和可能引發(fā)的異常浮點(diǎn)運(yùn)算有溢出 (overflows) 和下溢 (underflows),其中溢出將產(chǎn)生有符號(hào)的無窮大,而下溢則產(chǎn)生一個(gè)非標(biāo)準(zhǔn)化 (denormalized) 的值或是一個(gè)有符號(hào)的0。
數(shù)學(xué)上無法確定結(jié)果的浮點(diǎn)運(yùn)算將產(chǎn)生 NaN。
所有 NaN 參與的浮點(diǎn)運(yùn)算都會(huì)產(chǎn)生 NaN。
在下列情況中,浮點(diǎn)運(yùn)算會(huì)拋出異常:
計(jì)算時(shí)需要拆箱,而又是個(gè)空引用時(shí),會(huì)拋出 NullPointerException;
自增自減的情況下,如果需要拆箱裝箱且內(nèi)存不夠時(shí),會(huì)拋出 OutOfMemoryError。
接下來看看規(guī)范中給出的示例代碼:
class Test { public static void main(String[] args) { // 溢出 double d = 1e308; System.out.print("溢出產(chǎn)生了無窮大: "); System.out.println(d + "*10==" + d*10); // 漸變下溢 (gradual underflow) d = 1e-305 * Math.PI; System.out.print("漸變下溢: " + d + " "); for (int i = 0; i < 4; i++) System.out.print(" " + (d /= 100000) + " "); System.out.println(); // 產(chǎn)生 NaN System.out.print("0.0/0.0 產(chǎn)生的不是數(shù)字: "); d = 0.0/0.0; System.out.println(d); // 產(chǎn)生不精確結(jié)果的四舍五入: System.out.print("單精度下的不精確結(jié)果:"); for (int i = 0; i < 100; i++) { float z = 1.0f / i; if (z * i != 1.0f) System.out.print(" " + i); } System.out.println(); // 另一個(gè)產(chǎn)生不精確結(jié)果的四舍五入: System.out.print("雙精度下的不精確結(jié)果:"); for (int i = 0; i < 100; i++) { double z = 1.0 / i; if (z * i != 1.0) System.out.print(" " + i); } System.out.println(); // 轉(zhuǎn)換到整數(shù)時(shí)發(fā)生的結(jié)果階段: System.out.print("強(qiáng)制轉(zhuǎn)換到整數(shù): "); d = 12345.6; System.out.println((int)d + " " + (int)(-d)); } }
上述代碼將輸出
溢出產(chǎn)生了無窮大: 1.0e + 308 * 10 == Infinity 漸變下溢: 3.141592653589793E-305 3.1415926535898E-310 3.141592653E-315 3.142E-320 0.0 0.0 / 0.0 產(chǎn)生的不是數(shù)字:NaN 單精度下的不精確結(jié)果:0 41 47 55 61 82 83 94 97 雙精度下的不精確結(jié)果:0 49 98 強(qiáng)制轉(zhuǎn)換到整數(shù):12345 -12345
值得注意的是,在 漸變下溢 的例子中,我們可以看到精度逐漸喪失。
布爾類型和它們的值boolean 類型表示兩個(gè)邏輯量,true 和 false。
布爾運(yùn)算符是:
關(guān)系運(yùn)算符 ==和!= (
邏輯補(bǔ)足運(yùn)算符 !
邏輯運(yùn)算符& , ^ 和 |
條件運(yùn)算符和條件運(yùn)算符&& 和 ||
條件運(yùn)算符? :
字符串連接運(yùn)算符 + ,當(dāng)給定一個(gè)String操作數(shù)和一個(gè) boolean 操作數(shù)時(shí),它將把 boolean 操作符轉(zhuǎn)換為一個(gè)String ( "true"或"false" ),然后產(chǎn)生一個(gè)新創(chuàng)建的String,其值為兩個(gè)字符串的連接結(jié)果。
布爾表達(dá)式?jīng)Q定了幾種語(yǔ)句中的控制流:
if 語(yǔ)句
while 語(yǔ)句
do 語(yǔ)句
for 語(yǔ)句
一個(gè)boolean表達(dá)式還決定在 ? : 運(yùn)算符中使用哪個(gè)子表達(dá)式的值作為結(jié)果 。
只有Boolean表達(dá)式和Boolean表達(dá)式可以在控制流程語(yǔ)句中使用。
通過表達(dá)式 x!=0 ,可以將整數(shù)或浮點(diǎn)表達(dá)式 x 轉(zhuǎn)換為 boolean 值,這遵循了 C 語(yǔ)言約定,即任何非零值為true 。
通過表達(dá)式 obj!=null ,可以將對(duì)象引用 obj 轉(zhuǎn)換為boolean值,這同樣遵循了 C 語(yǔ)言約定(除null之外的任何引用為true 。
參考資料: Java Language Specification (Java SE 8 Edition) § 4.2
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68875.html
摘要:傳值和傳址有什么區(qū)別是傳值還是傳址開始在傳參時(shí),是傳值還是傳址傳值和傳址假設(shè)要將傳到。傳值和傳址是傳值是傳值。分別是基本類型,對(duì)象和數(shù)組,還有。常量池時(shí),好比是一張紙條,當(dāng)要傳值給時(shí),事實(shí)是把紙條上的內(nèi)容抄給了。 傳值和傳址有什么區(qū)別?Java是傳值還是傳址? 開始 Java在傳參時(shí),是傳值還是傳址? 傳值和傳址 假設(shè)要將A傳到B。如果是傳值,就意味著將A中存放的值復(fù)制一份給B,B存的...
摘要:前言今天的筆記來了解一下原子操作以及中如何實(shí)現(xiàn)原子操作。概念原子本意是不能被進(jìn)一步分割的最小粒子,而原子操作意為不可被中斷的一個(gè)或一系列操作。處理器實(shí)現(xiàn)原子操作處理器會(huì)保證基本內(nèi)存操作的原子性。 showImg(https://segmentfault.com/img/bVVIRA?w=1242&h=536); 前言 今天的筆記來了解一下原子操作以及Java中如何實(shí)現(xiàn)原子操作。 概念 ...
摘要:虛擬機(jī)有個(gè)一加載機(jī)制,叫做雙親委派模型。擴(kuò)展類加載器擴(kuò)展類加載器的父類的加載器是啟動(dòng)類加載器。驗(yàn)證驗(yàn)證的目的就是需要符合虛擬機(jī)的規(guī)范。虛擬機(jī)會(huì)通過加鎖的方式確保方法只執(zhí)行一次。 引言 上一篇文章談到Java運(yùn)行的流程,其中有一環(huán)是類加載。今天就繼續(xù)深入探討JVM如何加載虛擬機(jī)。首先JVM加載類的一般流程分三步:·加載·鏈接·初始化那么是否全部Java類都是這樣三步走的方式加載呢?我們可...
摘要:類最基本的作用,在于通過類獲取到相應(yīng)的對(duì)象,在向?qū)ο蟀l(fā)送消息時(shí)以期望對(duì)象做某些特定的事情。先導(dǎo)概念引用中一切皆對(duì)象,因此采用一個(gè)指向?qū)ο蟮囊脕聿倏v對(duì)象。對(duì)象可以存活于作用域之外。 歡迎各位讀者關(guān)注我的微信公眾號(hào),共同探討Java相關(guān)技術(shù)。生命不止,學(xué)習(xí)不休! showImg(https://segmentfault.com/img/bVboaBO?w=129&h=129); 也許你慢...
摘要:也就是說,一個(gè)實(shí)例變量,在的對(duì)象初始化過程中,最多可以被初始化次。當(dāng)所有必要的類都已經(jīng)裝載結(jié)束,開始執(zhí)行方法體,并用創(chuàng)建對(duì)象。對(duì)子類成員數(shù)據(jù)按照它們聲明的順序初始化,執(zhí)行子類構(gòu)造函數(shù)的其余部分。 類的拷貝和構(gòu)造 C++是默認(rèn)具有拷貝語(yǔ)義的,對(duì)于沒有拷貝運(yùn)算符和拷貝構(gòu)造函數(shù)的類,可以直接進(jìn)行二進(jìn)制拷貝,但是Java并不天生支持深拷貝,它的拷貝只是拷貝在堆上的地址,不同的變量引用的是堆上的...
閱讀 876·2021-10-25 09:45
閱讀 3305·2021-09-22 14:58
閱讀 3861·2021-08-31 09:43
閱讀 924·2019-08-30 15:55
閱讀 924·2019-08-29 13:51
閱讀 1237·2019-08-29 13:02
閱讀 3493·2019-08-29 12:52
閱讀 1968·2019-08-26 13:27