前言
做了這么久的Java開(kāi)發(fā),也非常喜歡這門語(yǔ)言,卻對(duì)它本身了解不多。而我又報(bào)名參加了2020年的藍(lán)橋杯Java組,并且打算參加軟件杯和C4,為了能把這些比賽打好,在下學(xué)期的Java程序設(shè)計(jì)課里拿個(gè)高分(雖然能不能選上這門課還不一定),也為了能對(duì)Java語(yǔ)言有一個(gè)更深入、系統(tǒng)的了解,不在以后的開(kāi)發(fā)中對(duì)語(yǔ)言本身感到困惑,所以打算寫這個(gè)系列,從零開(kāi)始重新學(xué)習(xí)Java基礎(chǔ)知識(shí),并對(duì)第一次學(xué)習(xí)掌握得不好的部分重新記錄。
參考資料
javac和java命令做了什么?
我們?cè)诎惭b好JDK之后開(kāi)始寫HelloWorld程序時(shí),都會(huì)用到javac和java兩條命令。那么這兩條命令為我們做了些什么呢?
javac命令啟動(dòng)了一個(gè)Java編譯器,它把后綴名為.java的Java源碼文件編譯成了后綴名為.class的字節(jié)碼文件。java命令則啟動(dòng)了JRE——Java運(yùn)行時(shí)環(huán)境(Java Runtime Environment)和JVM——Java虛擬機(jī)(Java Virtual Machine),裝載剛才編譯好的.class文件并以解釋的方式執(zhí)行其中的字節(jié)碼。

(圖片來(lái)自Java300集)
所以,我們可以很清楚地看出為什么Java是半編譯半解釋的語(yǔ)言——因?yàn)樵创a需要先編譯成字節(jié)碼,字節(jié)碼在JVM中又是解釋執(zhí)行的。
這里如果展開(kāi)了說(shuō)還會(huì)涉及到Java語(yǔ)言、JVM的底層原理,比如JVM是怎么加載這個(gè)類,怎么執(zhí)行其中的字節(jié)碼,還有JIT即時(shí)編譯、雙委派模型之類的,這里先不考慮這些細(xì)節(jié),在后面學(xué)習(xí)JVM基礎(chǔ)知識(shí)的部分再去詳細(xì)展開(kāi)。
JDK、JRE和JVM的關(guān)系
JRE并不等于JVM。
除了JVM之外,JRE其實(shí)還包含一些庫(kù)函數(shù)和必須的文件,所以JRE其實(shí)是包含JVM的。
JDK則在JRE的基礎(chǔ)上又增加了編譯器、調(diào)試器等開(kāi)發(fā)需要的東西。
JDK的目錄結(jié)構(gòu)
- bin:二進(jìn)制文件,比如
java、javac之類的,也就是我們用到的命令 - db:存放一些數(shù)據(jù)
- include:一些需要包含的頭文件
- jre:就是JRE
- lib:庫(kù)目錄,存放了一些jar包
- src.zip:JDK源碼,不過(guò)為啥我macOS版的Java11沒(méi)這個(gè)呢?
標(biāo)識(shí)符
標(biāo)識(shí)符其實(shí)就是一個(gè)名字,用來(lái)給變量、包、類等命名。
標(biāo)識(shí)符有一定的規(guī)則——
- 所有的標(biāo)識(shí)符都應(yīng)該以字母(A-Z 或者 a-z),美元符($)、或者下劃線(_)開(kāi)始
- 首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下劃線(_)或數(shù)字的任何字符組合
-
class等Java關(guān)鍵字不能用作標(biāo)識(shí)符
注意:Java使用的是Unicode標(biāo)準(zhǔn)字符集,所以可以用中文命名變量。
數(shù)據(jù)類型
在Java中,一共有8種基本類型(primitive type),其中有4種整型、2種浮點(diǎn)類型、1種用于表示Unicode 編碼的字符單元的字符類型char和1種用于表示真值的boolean類型。
除此之外,都是引用數(shù)據(jù)類型。它們占四個(gè)字節(jié),用來(lái)表示一個(gè)對(duì)象的地址(類似于C++的指針)。
注意:Java有一個(gè)能夠表示任意精度的算術(shù)包,通常被稱為大數(shù)值(BigDecimal),但它并不是一種新的Java類型,而是一個(gè)Java對(duì)象。
變量的類型、長(zhǎng)度與范圍
整數(shù)類型
| 類型 | 長(zhǎng)度 | 取值范圍 |
|---|---|---|
| int | 4字節(jié) | -2147483648~2147483647 |
| short | 2字節(jié) | -32768~32767 |
| long | 8字節(jié) | -9223372036854775808~9223372036854775807(-264~264-1) |
| byte | 1字節(jié) | -128~127 |
注意:
長(zhǎng)整型數(shù)值有一個(gè)后綴 L 或 l (如 4000000000L) 十六進(jìn)制數(shù)值有一個(gè)前綴 0x 或 0X ( 如 0xCAFEL 八進(jìn)制有一個(gè)前綴0,例如,010 對(duì)應(yīng)八進(jìn)制中的 8。 很顯然, 八進(jìn)制表示法比較容易混淆, 所以建議最好不要使用八進(jìn)制常數(shù)。并且,由于l容易和數(shù)字1、大寫的I混淆,所以一般都使用大寫的L表示長(zhǎng)整型數(shù)。
從 Java 7 開(kāi)始, 加上前綴 0b 或 0B 就可以寫二進(jìn)制數(shù)。 例如, 0b1001就是 9。 另外, 同樣是 從Java7開(kāi)始,還可以為數(shù)字字面量加下劃線,如用1_000_000表示一百萬(wàn),不會(huì)影響到編譯結(jié)果。
關(guān)于boolean的長(zhǎng)度問(wèn)題
Java規(guī)范中并沒(méi)有明確地指出boolean類型的長(zhǎng)度。但是,并不能說(shuō)boolean的長(zhǎng)度就不確定了:
在《Java虛擬機(jī)規(guī)范》一書(shū)中的描述:“雖然定義了boolean這種數(shù)據(jù)類型,但是只對(duì)它提供了非常有限的支持。在Java虛擬機(jī)中沒(méi)有任何供boolean值專用的字節(jié)碼指令,Java語(yǔ)言表達(dá)式所操作的boolean值,在編譯之后都使用Java虛擬機(jī)中的int數(shù)據(jù)類型來(lái)代替,而boolean數(shù)組將會(huì)被編碼成Java虛擬機(jī)的byte數(shù)組,每個(gè)元素boolean元素占8位”。也就是說(shuō)JVM規(guī)范指出boolean當(dāng)做int處理,也就是4字節(jié),boolean數(shù)組當(dāng)做byte數(shù)組處理,這樣我們可以得出boolean類型占了單獨(dú)使用是4個(gè)字節(jié),在數(shù)組中是確定的1個(gè)字節(jié)。
from: https://blog.csdn.net/YuanMxy/article/details/74170745
雖說(shuō)單獨(dú)使用的boolean是跟int一樣占用四個(gè)字節(jié),但是boolean和int不能相互轉(zhuǎn)換。
浮點(diǎn)類型
| 類型 | 長(zhǎng)度 | 取值范圍 |
|---|---|---|
| float | 4字節(jié) | 大約 ± 3.402 823 47E+38F (有效位數(shù)為 6 ~ 7 位) |
| double | 8字節(jié) | 大約 ± 1.797 693 134 862 315 70E+308 (有效位數(shù)為 15 位) |
浮點(diǎn)數(shù)不僅可以用小數(shù)點(diǎn)表示,也可以用科學(xué)計(jì)數(shù)法來(lái)表示,例如314e2、314E2。
注意:float類型的數(shù)值有一個(gè)后綴F或f (例如,3.14F) 沒(méi)有后綴F的浮點(diǎn)數(shù)值(如3.14) 默 認(rèn)為 double 類型。當(dāng)然, 也可以在浮點(diǎn)數(shù)值后面添加后綴 D 或 d ( 例如,3.14D)。
注意2:Java中有三個(gè)特殊的浮點(diǎn)數(shù)值,它們可以用一些特殊的常量來(lái)表示:
- 正無(wú)窮大——
Double.POSITIVE_INFINITY - 負(fù)無(wú)窮大——
Double.NEGATIVE_INFINIT - NaN(不是一個(gè)數(shù)字)——
Double.NaN
可以用Double.isXxx方法,例如Double.isNaN來(lái)檢測(cè)需要檢測(cè)的數(shù)是不是這三個(gè)常數(shù)。
注意3:與C/C++一樣,浮點(diǎn)數(shù)類型在計(jì)算時(shí)可能會(huì)產(chǎn)生誤差,因?yàn)楦↑c(diǎn)數(shù)并不是精確存儲(chǔ)的,可能產(chǎn)生舍入誤差,所以也最好不要比較兩個(gè)浮點(diǎn)數(shù)。如果業(yè)務(wù)要求不能產(chǎn)生誤差或者必須要比較兩個(gè)浮點(diǎn)數(shù),需要使用BigInteger(整數(shù))或者BigDecimal(浮點(diǎn)數(shù))類。
注意4:Java提供了PI與E的近似值常量——Math.PI、Math.E。
字符類型
char類型可以表示的字符范圍為\u0000到\uffff。
注意:Unicode轉(zhuǎn)義序列會(huì)在解析代碼之前被處理,所以必須小心注釋中的\u,它們可能會(huì)引起一些意想不到的錯(cuò)誤。
自動(dòng)類型轉(zhuǎn)換
下圖為Java不同數(shù)值類型之間的合法轉(zhuǎn)換(實(shí)線代表不會(huì)損失精度,虛線代表會(huì)損失精度。圖片來(lái)自《Java核心技術(shù)》):

強(qiáng)制類型轉(zhuǎn)換
Java的強(qiáng)制類型轉(zhuǎn)換與C/C++基本相同,但是在面向?qū)ο蟛糠郑行┣闆r下不可以使用強(qiáng)制類型轉(zhuǎn)換,這一點(diǎn)在后面復(fù)習(xí)面向?qū)ο髸r(shí)再詳細(xì)總結(jié)。
變量
變量的本質(zhì)
變量的本質(zhì)其實(shí)就是內(nèi)存中的一個(gè)可操作、固定長(zhǎng)度的存儲(chǔ)空間(不過(guò)據(jù)說(shuō)boolean的長(zhǎng)度是不確定的),在內(nèi)存中的地址是確定的,但是里面放置什么值是由程序運(yùn)行決定的。我們可通過(guò)變量名來(lái)訪問(wèn)對(duì)應(yīng)的存儲(chǔ)空間,從而操縱這個(gè)存儲(chǔ)空間里存儲(chǔ)的值。
Java是一種強(qiáng)類型語(yǔ)言,聲明變量必須在變量名前先聲明其類型,但是在Java11中,能自動(dòng)推斷出類型的可以不顯示指定類型而使用var關(guān)鍵字指定,非常方便。
變量在使用前必須聲明, 只有在變量聲明以后,才能為其分配相應(yīng)長(zhǎng)度的存儲(chǔ)空間,所以在使用變量之前必須聲明。
注意:使用沒(méi)有初始化局部的變量會(huì)報(bào)錯(cuò),但是聲明了變量不使用則不會(huì)報(bào)錯(cuò)(這跟Go語(yǔ)言不一樣)。
變量的分類
Java的變量與C++一樣,分為三種:
- 局部變量
- 靜態(tài)變量
- 成員變量/實(shí)例變量
成員變量的初值
成員變量從屬于對(duì)象,生命周期伴隨對(duì)象始終。
成員變量如果沒(méi)有初始化,會(huì)有一個(gè)默認(rèn)值:
| 類型 | 默認(rèn)值 |
|---|---|
| byte | 0 |
| short | 0 |
| int | 0 |
| long | 0 |
| float | 0.0 |
| double | 0.0 |
| char | '\u0000' |
| boolean | false |
| String等引用類型 | null |
靜態(tài)變量
靜態(tài)變量(使用static聲明的變量)從屬于類,是生命周期最長(zhǎng)的,生命周期為從類的加載到類的卸載。
常量
與C/C++不同,常量使用final關(guān)鍵字聲明,一旦有值,其值就不能發(fā)生變化。
final關(guān)鍵字除了用于常量(不能修改的變量)之外,還有以下作用:
- 使用
final修飾的類不能被繼承 - 使用
final修飾的方法不能被重寫
這在后面復(fù)習(xí)面向?qū)ο蟮臅r(shí)候再詳細(xì)學(xué)習(xí)。
雖然Java不使用const修飾常量,但是const是Java的一個(gè)保留關(guān)鍵字,不能使用。
運(yùn)算符
Java中有以下運(yùn)算符(圖片來(lái)自Java300集):

算術(shù)運(yùn)算符
除了++和--以外都屬于二元運(yùn)算符,二元運(yùn)算符運(yùn)算時(shí)遵守以下規(guī)則:
- 如果兩個(gè)操作數(shù)有一個(gè)為L(zhǎng)ong, 則結(jié)果也為long。
- 沒(méi)有l(wèi)ong時(shí),結(jié)果為int。即使操作數(shù)全為short,byte,結(jié)果也是int。
- 如果兩個(gè)操作數(shù)有一個(gè)為double,則結(jié)果為double。
- 只有兩個(gè)操作數(shù)都是float,則結(jié)果才為float。
- 取模操作其操作數(shù)可以為浮點(diǎn)數(shù),但是一般使用整數(shù)。結(jié)果是“余數(shù)”,“余數(shù)”符號(hào)和左邊操作數(shù)相同,如:7%3=1,-7%3=-1,7%-3=1。
++與--的運(yùn)算規(guī)則與C/C++等語(yǔ)言相同。
賦值運(yùn)算與結(jié)合賦值運(yùn)算
Java一共有以下幾種結(jié)合賦值運(yùn)算符:
+=-=*=\=%=
用法與C/C++相同。
注意:如果整數(shù)除以0會(huì)拋出一個(gè)異常,而浮點(diǎn)數(shù)除以0則會(huì)得到無(wú)窮大或者是NaN。
關(guān)系運(yùn)算符
Java的關(guān)系運(yùn)算符一共有以下幾種:
==!=><>=<=
用法同樣與C/C++相同。
邏輯運(yùn)算符
Java 沿用了 C++ 的做法,使用&&表示邏輯“與”運(yùn)算符,使用||表示邏輯“或”運(yùn)算符。從 != 運(yùn)算符可以想到,感嘆號(hào)!就是邏輯非運(yùn)算符。&&和||運(yùn)算符是按照“短路”方式來(lái)求值的: 如果第一個(gè)操作數(shù)已經(jīng)能夠確定表達(dá)式的值,第二個(gè)操作數(shù)就不必計(jì)算了。
位運(yùn)算符
Java中的位運(yùn)算符與C/C++的基本相同,個(gè)人感覺(jué)這些東西在實(shí)際開(kāi)發(fā)中很難用到,除非是在性能要求非常高的地方。
(圖片來(lái)自Java300集)

運(yùn)算符的優(yōu)先級(jí)
Java運(yùn)算符的優(yōu)先級(jí)也與C/C++基本相同。在實(shí)際使用中一般很少會(huì)遇到需要考慮優(yōu)先級(jí)的問(wèn)題,一般都使用小括號(hào)來(lái)解決運(yùn)算符的優(yōu)先級(jí)帶來(lái)的問(wèn)題,因?yàn)樾±ㄌ?hào)的優(yōu)先級(jí)最高。
(圖片來(lái)自Java300集)
