Java基礎(chǔ)語法
Java規(guī)范

Java字節(jié)碼
java程序是以".java"為擴(kuò)展名,當(dāng)我們編寫完java程序后,要執(zhí)行程序需要經(jīng)過兩個(gè)階段:編譯和運(yùn)行。
編譯器
執(zhí)行編譯程序的稱為編譯器,java將java源文件編譯成為字節(jié)碼(bytecode)。字節(jié)碼是高度優(yōu)化的指令集合,但是字節(jié)碼并不能直接被計(jì)算機(jī)所執(zhí)行,這些指令只有java運(yùn)行時(shí)系統(tǒng)執(zhí)行(又稱Java虛擬機(jī),Java Vitual Machine)。
在Java中,源文件正式名稱是編譯單元(compilation unit),源文件是包含一個(gè)或多個(gè)類的定義的文本文件,文本文件以.java作為擴(kuò)展名。源文件中的主類需要與源文件名稱一致,即一個(gè)源文件有且只有一個(gè)主類,這有利于維護(hù)和組織程序。
將java程序編譯成字節(jié)碼文件,解決了兩方面問題:跨平臺和安全性。將java程序編譯成統(tǒng)一的字節(jié)碼文件,然后針對于不同的操作系統(tǒng)、硬件去實(shí)現(xiàn)Java虛擬機(jī),這樣只要給定的系統(tǒng)有運(yùn)行時(shí)包就可以執(zhí)行java程序,有效的解決了跨平臺問題。因?yàn)镴VM控制程序的執(zhí)行,所以能夠防止程序?qū)λ诘南到y(tǒng)產(chǎn)生負(fù)面作用。
雖然java程序被設(shè)計(jì)為解釋性語言,但是為了提供性能,也可以將字節(jié)碼編譯為本機(jī)代碼。HotSpot為字節(jié)碼提供了即時(shí)編譯器(Just-Time,JIT),如果JVM中包含JIT編譯器,就可以將一部分代碼實(shí)時(shí)編譯為機(jī)器可執(zhí)行代碼。
通過javac命令執(zhí)行java編譯器,會生成HelloWorld.class字節(jié)碼文件。需要注意,源文件中如果包含多個(gè)類,編譯后每個(gè)類都會生成一個(gè)以類名為名稱,以.class結(jié)尾的字節(jié)碼文件。
javac HelloWorld.java
解釋器
Java編譯器生成的字節(jié)碼文件并不能直接被機(jī)器執(zhí)行,需要通過JVM將字節(jié)碼文件解釋為機(jī)器可以識別的代碼,然后才可以執(zhí)行。
通過java命令運(yùn)行程序,并且指定要執(zhí)行的主類名稱。Java會自動搜索包含該類名稱且擴(kuò)展名為.class的文件,如果找到就會執(zhí)行指定類中的代碼。
java HelloWorld
Java關(guān)鍵特性
java具有簡單性、安全性、可移植性、面向?qū)ο?、健壯性、多線程、體系結(jié)構(gòu)中立、解釋執(zhí)行、高性能、分布式、動態(tài)性的特點(diǎn)。
編程范式
程序構(gòu)造目前有兩大范式,即:面向過程編程(process-oriented programming)和面向?qū)ο缶幊?object-oriented programming)。面向過程編程是圍繞著“正在發(fā)生什么”進(jìn)行編寫,這種方式是將程序描述為一系列線性步驟,是將代碼作用于數(shù)據(jù)之上。隨著程序規(guī)模的不斷增長,面向過程編程難以管理復(fù)雜、龐大的程序,這時(shí)就發(fā)明了第二種范式。面向?qū)ο缶幊淌菄@著“將影響誰”,它是圍繞著數(shù)據(jù)以及一系列精心設(shè)計(jì)的接口組織程序,即數(shù)據(jù)控制代碼的訪問。
抽象
面向?qū)ο蟮谋举|(zhì)元素之一就是抽象,通過抽象管理復(fù)雜。而層次化分類是管理抽象的一種有力方式,將復(fù)雜系統(tǒng)的語義進(jìn)行分層,將他們分解為多個(gè)更易于管理的部分。
OOP三原則
面向?qū)ο缶幊烫峁┝擞糜趲椭鷮?shí)現(xiàn)面向?qū)ο竽P偷臋C(jī)制,這些機(jī)制就是封裝、繼承、多態(tài)。
注釋
Java提供三種注釋:單行注釋、多行注釋、文檔注釋。
- 單行注釋用于注釋用于簡單描述,以“//”頭,并作用于到行尾。
- 多行注釋用于更長的注釋,以“/*” 開頭,并以“*/”結(jié)尾。
- 文檔注釋用于生成說明程序的HTML文件,文檔注釋以“/**”開頭,并以“*/”結(jié)束。
代碼的組成
java程序是由空白符、標(biāo)識符、字面值、關(guān)鍵字、運(yùn)算符、注釋和分隔符組成的。
- 空白符:程序中每個(gè)標(biāo)記之間的空白符,可以是空格、制表符、換行符。
- 標(biāo)識符:用于命名事物,由字母(大小寫敏感)、數(shù)字、下劃線和美元符號組成,不能以數(shù)字開頭。從JDK 8開始,不建議使用下劃線作為標(biāo)識符了。
- 字面值:字面值就是常亮的值,比如100、1.1、’X’、“hello”等。
- 分隔符:分隔符包含“()”、“{}”、“[]”、“;”、“,”、“.”、“::”。
- 關(guān)鍵字:java目前定義了50多個(gè)關(guān)鍵字,這些關(guān)鍵字與運(yùn)算符和分隔符的語法組成了java語言的基礎(chǔ)。關(guān)鍵字是系統(tǒng)用詞,不能用作標(biāo)識符,所以不能用作變量、類名和方法名。
數(shù)據(jù)類型
Java是一種強(qiáng)類型化語言,即每個(gè)變量、每個(gè)表達(dá)式具有一種類型,每種類型都是嚴(yán)格定義的。并且所有賦值,不管是顯示的還是方法調(diào)用中通過參數(shù)傳遞的,都要進(jìn)行類型兼容檢查。
基本類型
Java定義了8中基本數(shù)據(jù)類型:byte、short、int、long、float、double、char、boolean。這8種基本類型可以分為:
- 整型:包括byte、shot、int和long,用于表示有符號整數(shù)
- 浮點(diǎn)型:包括float和double,用于表示小數(shù)。
- 字符型:包括char,用于表示字符集中的符號,比如字母和數(shù)字。
- 布爾型:包括boolean,用于表示true/false的特殊類型。
盡管在java中一切皆對象,但是基本類型并不是對象,這樣設(shè)計(jì)的原因是效率,如果將其設(shè)計(jì)為對象會極大地降低性能。這些基本類型在java中是有明確的范圍的(c和c++會隨著執(zhí)行環(huán)境不同,其范圍也不同),因?yàn)閖ava需要具備可移植性。
整數(shù)類型
java中提供了四種整數(shù)類型:byte、short、int和long,這些類型都有符號的,java不支持無符號的、只是正值的整數(shù)。
| 名稱 | 字節(jié)(byte) | 寬度(位) | 范圍 |
|---|---|---|---|
| byte | 1 | 8 | -128 ~ 127 |
| shot | 2 | 16 | -32 768 ~ 32 767 |
| int | 4 | 32 | -2 147 483 648 ~ 2 147 483 648 |
| long | 8 | 64 | -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807 |
所有的整數(shù)值都是整型的字面值,字面值可以以二進(jìn)制(JDK7開始支持)、八進(jìn)制、十六進(jìn)制表示。
int v1 = 123_456_789; //相當(dāng)于x=123456789
int v2 = 0b10__0100__1010; //二進(jìn)制
int v3 = 0735; //八進(jìn)制
int v4 = 10; //十進(jìn)制
int v5 = 0XF25; //十六進(jìn)制
從JDK7開始,可以使用二進(jìn)制指定整型字面值,使用0b或0B作為數(shù)值前綴。
從JDK7開始,整形字面值可以嵌入一個(gè)或多個(gè)下劃線,嵌入下劃線可以使閱讀很大的整數(shù)時(shí)更容易。下劃線只能用于分隔數(shù)字,不能位于字面值得開頭和結(jié)尾,并且可以使用連續(xù)的多個(gè)下劃線。
浮點(diǎn)類型
浮點(diǎn)數(shù)也稱為實(shí)數(shù),當(dāng)計(jì)算需要小數(shù)精度的表達(dá)式時(shí)使用。java實(shí)現(xiàn)了IEEE-754標(biāo)準(zhǔn)集的浮點(diǎn)類型和運(yùn)算符。float為單精度類型,double為雙精度類型。
| 名稱 | 字節(jié) | 寬度(位) | 大致范圍 |
|---|---|---|---|
| float | 4 | 32 | 1.4e-045 ~ 3.4e+038 |
| double | 8 | 64 | 4.9e-324 ~ 1.8e+308 |
浮點(diǎn)字面值可以使用標(biāo)準(zhǔn)計(jì)數(shù)法或科學(xué)計(jì)數(shù)法表示,如2.32、2.3E5、3.14e-3,E或e表示10的冪。java中浮點(diǎn)字面值默認(rèn)為double,如果要制定為float,需要在常量后面添加F或f。
字符類型
用于存儲字符集的類型是char,java所表示的字符集是Unicode字符集。Unicode定義了一個(gè)完全國際化的字符集,Unicode是數(shù)十種字符集的統(tǒng)一體,能夠表示人類語言中的所有字符(至少目前是)。Unicode需要16位寬,所以char類型也需要16位寬。
ASCII標(biāo)準(zhǔn)字符集的范圍是0~127,擴(kuò)展的ISO-Latin-1范圍是0~255。
| 名稱 | 字節(jié) | 寬度(位) | 大致范圍 |
|---|---|---|---|
| char | 2 | 16 | 0 ~ 65536 |
對于char類型既可以直接賦值字符,也可以賦值字符所在Unicode中的所在位置。
char a = 88; //代表'X'
char b = 'Y';
在java正式規(guī)范中,char被當(dāng)做整數(shù)類型。然而在實(shí)際應(yīng)用中,主要將其用來表示Unicode字符,所以將其單獨(dú)分為一類。
字符類型字面值可以使用位于一對單引號中的字符表示,對于那些不可見的字符可以使用轉(zhuǎn)義字符。也可以使用八進(jìn)制或十六進(jìn)制表示,對于八進(jìn)制使用反斜杠后加三位數(shù)字表示,對于十六進(jìn)制以 "\u"后加4位十六進(jìn)制數(shù),比如 '\121'、'\ua432'。
char v7 = 22;
char v8 = 'a';
char v9 = '\121';
char v10 = '\ua245';
| 轉(zhuǎn)義序列 | 描述 |
|---|---|
| \ddd | 八進(jìn)制字符(ddd) |
| \uxxxx | 十六進(jìn)制Unicode序列(xxxx) |
| \ ' | 單引號 |
| \" | 雙引號 |
| \\ | 反斜線 |
| \r | 回車符 |
| \n | 換行符 |
| \f | 換頁符 |
| \t | 制表符 |
| \b | 回格符 |
布爾類型
在java中布爾類型用于表示邏輯值,它只能為true或false,所以它只需要一個(gè)字節(jié)即可。
布爾字面值只有兩個(gè)邏輯值:true和false。true和false不能轉(zhuǎn)換為任何數(shù)字,也就是說 boolean a = 1是不支持的。
變量
在Java中,變量是基本存儲單元。定義變量通過標(biāo)識符、類型以及可選的初始化器來定義。所有變量都有作用域,作用域定義了變量的可見性和生命周期。
type identifier = [value][,indentifier [= value] ...];
變量可以在定義的時(shí)候初始化,也可以動態(tài)的初始化。
double v11 = 1.1;
double v12;
for(int i=0; i<10 ;i++) {
v12 = Math.sqrt( i * i );
}
變量作用域
Java允許在任何代碼塊中定義變量,代碼塊定義了變量的作用域,每個(gè)變量的作用域都是在代碼塊中的。作用域決定了變量的可見性和生存周期。
盡管java通過代碼塊來劃分作用域,在實(shí)際使用中人們經(jīng)常將變量定義在類中的稱為類變量或全局變量,定義在方法中的稱為方法變量或局部變量。
類變量
類變量定義在類體內(nèi)部,方法體外部。類變量可以定義在類中的任何位置,整個(gè)類都可以使用。類變量可以不初始化值直接使用,這樣會根據(jù)數(shù)據(jù)類型使用類型默認(rèn)值。
方法變量
方法中定義的變量和方法內(nèi)代碼塊定義的變量具有相同特性,它們的作用域都是從定義位置開始到代碼塊結(jié)束(和類變量不同),都需要先初始化在使用。
public static void main(String[] args){
double v11;
//double v13 = v11 + 1; //需要先初始化
for(int i=0; i<10 ;i++) {
double v12 = Math.sqrt( i * i );
}
//System.out.println(v12); //v12的作用域在for循環(huán)內(nèi)
}
注意:方法中定義的變量,內(nèi)層代碼塊中不允許與外層代碼塊定義相同的變量名稱
類型轉(zhuǎn)換
Java類型轉(zhuǎn)換分為自動類型轉(zhuǎn)換和強(qiáng)制類型轉(zhuǎn)換,例如int類型總能自動轉(zhuǎn)換為long類型,但是double類型卻不能自動轉(zhuǎn)換為int類型,所以需要強(qiáng)制類型轉(zhuǎn)換(cast)。
自動類型轉(zhuǎn)換
自動類型轉(zhuǎn)換也成擴(kuò)展轉(zhuǎn)換(widening conversion),只要滿足以下兩個(gè)條件就會發(fā)生自動轉(zhuǎn)換。
- 兩種類型是兼容的
- 目標(biāo)類型大于源類型
對于自動擴(kuò)展類型,數(shù)值類型(整數(shù)和浮點(diǎn)型)是相互兼容的,但是數(shù)值類型不能和char或boolean類型的自動轉(zhuǎn)換,char和boolean相互也是不兼容的。
強(qiáng)制類型轉(zhuǎn)換
對于相互之間不兼容的類型,或目標(biāo)類型小于源類型,就需要使用強(qiáng)制類型轉(zhuǎn)換了。強(qiáng)制類型也稱為縮小轉(zhuǎn)換(narrowing conversion),因?yàn)轱@示的將數(shù)值變的更小。
//(target-type) value; 強(qiáng)制類型轉(zhuǎn)換
int v14 = 257;
byte v15 = (byte) v14;
對于整數(shù)類型,當(dāng)進(jìn)行強(qiáng)制類型轉(zhuǎn)換后,如果源類型超出了目標(biāo)類型,將會以數(shù)值除以目標(biāo)類型范圍取模。比如,上面將int類型強(qiáng)制轉(zhuǎn)換為byte類型,并且超過了byte類型范圍,則 v14/256(byte的范圍),結(jié)果為1。
對于浮點(diǎn)類型,如果將浮點(diǎn)類型轉(zhuǎn)換為整數(shù)類型時(shí),則會進(jìn)行截尾,即把小數(shù)點(diǎn)截掉。
表達(dá)式中自動類型提升
除了賦值外,在使用表達(dá)式時(shí)也可能發(fā)生某些類型轉(zhuǎn)換。
- 表達(dá)式中byte、short、char會自動提升為int類型。
- 如果表達(dá)式中有一個(gè)操作數(shù)是long型,則整個(gè)表達(dá)式結(jié)果會提升為long型。
- 如果表達(dá)式中有一個(gè)操作數(shù)是float型,則整個(gè)表達(dá)式結(jié)果為float類型。
- 如果表達(dá)式中有一個(gè)操作數(shù)是double型,則整個(gè)表達(dá)式結(jié)果為double類型。
有時(shí)我們會想如果我們變量本身就在byte范圍就把他定義為byte類型,這樣可以節(jié)省空間、提升效率。但其實(shí)這是錯(cuò)誤的,因?yàn)樵诒磉_(dá)式中byte、char、short需要轉(zhuǎn)換為int類型。
自動類型提升,有時(shí)會引起難以理解的編譯錯(cuò)誤,比如下面的例子:
byte b = 50;
b = b * 2;
會提示編譯錯(cuò)誤,因?yàn)閎 * 2會提升為int類型,但是int類型無法自動轉(zhuǎn)換為byte類型,所以需要強(qiáng)制類型轉(zhuǎn)換,b = (byte) b * 2。
數(shù)組
數(shù)組即一組類型相同的變量,可以創(chuàng)建任意類型、任意維(一維或多維)的數(shù)組,訪問數(shù)組元素是通過數(shù)組下標(biāo)索引來訪問的。
創(chuàng)建數(shù)組:
//定義規(guī)范
type var-name[] = new type[n];
type[] var-name = new type[n];
int[] v16 = new int[10];
int v17[] = new int[19];
如果只聲明數(shù)組,數(shù)組實(shí)際并沒有創(chuàng)建物理存儲地址,需要使用new關(guān)鍵詞為數(shù)組指定數(shù)組長度。數(shù)組定義完成后,會為其設(shè)置默認(rèn)值,對于數(shù)值類型會被自動初始化為0、布爾類型會被初始化為false、引用類型會被初始化為null。
在定義數(shù)組時(shí)候,可以直接為數(shù)組初始化:
int[] v18 = {1,2,3};
在Java中多維數(shù)組實(shí)際上就是數(shù)組的數(shù)組,java允許你只為第一維指定長度,其它維度長度可以自行指定,所以長度可以不同。
int[][] v19 = new int[10][10];
int[][] v20 = new int[10][];
v20[0] = new int[1];
v20[1] = new int[2];
v20[3] = new int[3];
運(yùn)算符
Java提供了豐富的運(yùn)算符,可以將其分為4組:算術(shù)運(yùn)算符、關(guān)系運(yùn)算符、邏輯運(yùn)算符和位運(yùn)算符。另外還有比較特殊的類型比較運(yùn)算符instanceof和箭頭運(yùn)算符->。
算術(shù)運(yùn)算符
算術(shù)運(yùn)算符的操作數(shù)必須是數(shù)值類型,不可以為boolean類型使用算術(shù)運(yùn)算符,但是可以為char類型使用算術(shù)運(yùn)算符,因?yàn)閏har的本質(zhì)是int的子集。
算術(shù)運(yùn)算符包括:+、-、*、/、%、+=、-=、*=、/=、%=、++、--
。其中+=這類叫做賦值復(fù)合運(yùn)算符,a++相當(dāng)于a = a + 1,復(fù)合運(yùn)算符不僅使用方便,而且運(yùn)行效率也高。
使用除法運(yùn)算符,對于整數(shù)其結(jié)果不會包含小數(shù)。
對于自增運(yùn)算符++、--,如果單獨(dú)使用a++和++a沒有區(qū)別,但是如果放到表達(dá)式中c = a++和c = ++a是不同的,c = a++ 會現(xiàn)將a賦值給c之后在進(jìn)行自增,而c = ++a是將a+1執(zhí)行之后再賦值給c。
關(guān)系運(yùn)算符
關(guān)系運(yùn)算符用來判斷一個(gè)操作數(shù)與另一個(gè)操作數(shù)之間的關(guān)系,關(guān)系運(yùn)算符的結(jié)果為布爾值。
關(guān)系運(yùn)算符包括:==、!=、>、>=、<、<=。
在java中true和false不是數(shù)值,它們與是否為0沒有任何關(guān)系(c/c++中true代表任何非零值,false是0),所以下面的語句是不對的:
int a = 1;
if(a)
而應(yīng)該為:
if(a == 1)
邏輯運(yùn)算符
邏輯運(yùn)算符只能操作布爾類型操作數(shù)。
邏輯運(yùn)算符包括:&、|、^、||、&&、!、&=、|=、^=、==、!=、?:。
“?:”是Java提供的特殊三元運(yùn)算符,它可以替代“if-then-else”語句。
expression1 ? expression2 : expression3
當(dāng)expression1為true的時(shí)候,就對expression2求值;否則就對expression3求值。比如:</br>
a = b == 0 ? 1 : 2;
注意&&和||又稱為短路邏輯運(yùn)算符,對于a&&b,如果a為false時(shí)則b就不會被執(zhí)行;對于a || b,當(dāng)a為true時(shí),則b不會被計(jì)算,因?yàn)榻Y(jié)果已經(jīng)為true了。
如果不想使用短路運(yùn)算的特點(diǎn),可以使用單符號&和|來進(jìn)行與、或運(yùn)算。
位運(yùn)算符
位運(yùn)算符可以用于整數(shù)類型char、shot、int、long和byte。
因?yàn)槲贿\(yùn)算符是對整數(shù)進(jìn)行操作的,所以我們需要先了解一下整數(shù)是如何存儲以及如何表示負(fù)數(shù)的。
Java中所有整數(shù)都有由寬度可變的二進(jìn)制數(shù)字表示的,例如byte類型的42為00101010,從右到左代表2的n次冪(從0開始),所以42 = 21 + 23 + 2^5 。
所有整數(shù)類型(除char)都是有符號整數(shù),既可以是正數(shù),也可以是負(fù)數(shù)。Java使用“2的補(bǔ)碼”進(jìn)行編碼,也就是說負(fù)數(shù)的表現(xiàn)方法為:首先翻轉(zhuǎn)所有位(1變?yōu)?,0變?yōu)?),然后再將結(jié)果加1。比如-42,反轉(zhuǎn)11010101,然后在加1為11010110。如果想要將-42在轉(zhuǎn)換為42,也需要反轉(zhuǎn)所有位,然后在加1。
邏輯運(yùn)算符包括:
| 運(yùn)算符 | 描述 | 使用 | |
|---|---|---|---|
| ~ | 按位取反 | 00101010 取反 11010101 | |
| & | 按位與,只有兩個(gè)操作數(shù)都為1,結(jié)果才為1 | 0011 & 1010 = 0010 | |
| | | 按位或,只要兩個(gè)操作數(shù)有一個(gè)為0,結(jié)果就為0 | 0011 | 1010 = 1011 |
| ^ | 按位異或,兩個(gè)操作數(shù)只有一個(gè)操作為1,結(jié)果就為1 | 0011 ^ 1010 = 1001 | |
| >> | 右移,將數(shù)值中所有位向右移動指定的次數(shù),左側(cè)補(bǔ)0(不一致為0,填充符號位)。右移一位相當(dāng)于除2 | 64 >> 2 = 16 | |
| << | 左移,將數(shù)值中所有位向左移動指定次數(shù),右側(cè)補(bǔ)0(不一致為0,填充符號位),注意如果移到符號位,有可能更改正負(fù)數(shù)。左移一位相當(dāng)于乘2 | 64 <<2 = 256 | |
| >>> | 右移零填充,又稱為無符號右移,即不會根據(jù)符號位填充左側(cè)的值,一直都是0 | a = -1 </br> a = a >>> 24 11111111 11111111 11111111 11111111 >>> 24 = 00000000 000000000 000000000 11111111 |
|
| &= | 按位與并賦值 | a = a&2 | |
| |= | 按位或并賦值 | a = a|2 | |
| ^= | 按位異或并賦值 | a= a^2 | |
| >>= | 右移并賦值 | a = a >> 1 | |
| <<= | 左移并賦值 | a = a<< 1 | |
| >>>= | 右移零填充并賦值 | a = a>>> 1 |
通過左右移位可以實(shí)現(xiàn)高效的乘除2操作。
當(dāng)進(jìn)行右移時(shí)右移后的頂部(最左邊)位使用右移前的最高位(最左邊的第一位,符號位)進(jìn)行填充,這稱為符號擴(kuò)展,該特性能夠保留負(fù)數(shù)的符號。比如:
11111000 -8
-8 >> 1
11111100 -4
對于-1無論怎么右移,最后的結(jié)果一直都是-1。
還需要記住,對于byte、short都會自動提升為int類型。
賦值運(yùn)算符
賦值運(yùn)算符使用單個(gè)"="號,
形式:
var = expression
賦值運(yùn)算符允許創(chuàng)建賦值鏈,將一組變量設(shè)置相同值。比如 x = y = z = 10
運(yùn)算符優(yōu)先級
表達(dá)式中的運(yùn)算符優(yōu)先級影響著其表達(dá)式的結(jié)果,Java運(yùn)算符優(yōu)先級從高到低:
自增運(yùn)算符(++、--)
算術(shù)運(yùn)算符(*、/、%、+、-)
移位運(yùn)算符(>>、>>>、<<)
關(guān)系運(yùn)算符(>、>=、<、<=、instanceof)
等值運(yùn)算符(==、!=)
邏輯運(yùn)算符(&、^、|、&&、||)
三元運(yùn)算符(?:)
箭頭運(yùn)算符(->)
賦值運(yùn)算符(=、op=)
在使用中不需要牢記這些,通過括號可以輕松控制優(yōu)先級,并且?guī)椭a理解,而且使用括號不會影響效率。
控制語句
Java的程序控制語句分為:選擇語句、迭代語句和跳轉(zhuǎn)語句。
選擇語句
Java支持兩種選擇語句:if語句和switch語句。
if語句
if語句是Java的條件分支語句,可以使用if語句控制程序執(zhí)行不同的路徑。
if語句語法:
//if-else
if(condition) {
statement1;
}else {
statement2;
}
//if-else-if 從上向下執(zhí)行,一旦某個(gè)條件為true,就會執(zhí)行與之關(guān)聯(lián)if語句,并且會忽略剩余的語句
if(condition) {
statement;
}else if(condition) {
statement;
}
.
.
.
else {
statement;
}
switch語句
switch語句是Java的多分支語句,根據(jù)表達(dá)式的值調(diào)度執(zhí)行代碼的不同部分,switch語句是if-else-if語句更好的替代方法。
switch語句語法:
switch(expression) {
case value1:
statement;
break;
case value2:
statement;
break;
.
.
.
case valueN:
statement;
break;
defalut:
statement;
}
expression表達(dá)式在JDK7之前可以是byte、short、int、char或枚舉類型,在JDK7開始允許為String類型。case后指定的每個(gè)數(shù)值必須是唯一的常量表達(dá)式,不允許重復(fù),并且類型需要和expression的類型兼容??梢酝ㄟ^break語句終止后面的執(zhí)行,跳出switch語句。
注意:statement可以為多行語句,不需要使用{}代碼塊。
sitch語句有三個(gè)重要的特征:
- switch語句只能進(jìn)行相等性比較,即expression與case常量相匹配。
- 在同一switch語句中,不允許具有相同的case常量。對于嵌套switch語句,內(nèi)層與外層可以具有相同的case常量。
- 相對于一系列嵌套的if語句,switch語句更加高效,因?yàn)榫幾g器本身知道所有case常量具有相同的類型,并且只進(jìn)行相等性比較。
迭代語句
Java迭代語句包括for、while和do-while語句,這些語句又稱為循環(huán)語句。
while語句
只要控制表達(dá)式為true,while循環(huán)就重復(fù)執(zhí)行一條語句或代碼塊。while循環(huán)的循環(huán)體可以為空,在Java中空語句的語法是合法的(只包含一個(gè)分號的語句)。
while語句語法:
while(condition) {
//body of loop
}
do-while語句
有時(shí)我們需要至少執(zhí)行一次循環(huán),無論條件表達(dá)式是真還是false,即希望在循環(huán)末端終止循環(huán)??梢允褂胐o-while語句:
do{
//body of loop
} while(condition)
for循環(huán)
for循環(huán)是一種通用的、強(qiáng)大的循環(huán)結(jié)構(gòu),從JKD1.5開始就已經(jīng)有兩種形式了。
傳統(tǒng)for循環(huán)
傳統(tǒng)for循環(huán)是以初始條件(循環(huán)控制變量)、布爾表達(dá)式(測試循環(huán)控制變量)和迭代器(循環(huán)控制變量自增或自減)組成。第一次循環(huán)的時(shí)候執(zhí)行initialization,初始化循環(huán)變量(只會執(zhí)行一次)。然后判斷condition,如果為true則執(zhí)行循環(huán)體,如果為false就停止執(zhí)行。接下來的每次循環(huán)都先執(zhí)行一次iterator,然后判斷condition來決定是否執(zhí)行循環(huán)體。
for(initialization; condition; iterator){
//body
}
for循環(huán)可以靈活運(yùn)用,下面是一些常用方式。
- 在for循環(huán)內(nèi)部聲明循環(huán)變量。
for(int i=0; i<n; i++) {
}
- 多變量使用逗號分隔。
for(int a=0,b=10; a<b; a++,b--) {
}
- 省略初始化部分和迭代部分
for( ; !done; ){
}
- 無限循環(huán)
for( ; ; ){
}
增強(qiáng)for循環(huán)for-each
for-each風(fēng)格的循環(huán)被設(shè)計(jì)為以嚴(yán)格的順序(傳統(tǒng)for循環(huán)是通過使用循環(huán)控制變量,手動索引數(shù)組的)、從頭到尾循環(huán)變量一個(gè)對象集合,比如數(shù)組、集合等。
for(type itr-val : collection){
}
collection指定了需要對什么集合進(jìn)行遍歷,itr-val為每次循環(huán)接受集合中元素的變量。type類型需要為collection元素的類型,因?yàn)閕tr-val是用來接受集合元素的。
需要注意迭代變量是“只讀”的,即不能通過更改迭代變量,來更改集合的內(nèi)部元素。
跳轉(zhuǎn)語句
java支持三種跳轉(zhuǎn)語句:break、continue、return。異常機(jī)制其實(shí)也可以算成一種跳轉(zhuǎn)語句,通過拋出異常來更改當(dāng)前執(zhí)行分支。
break語句
break語句有三種用途:用于終止switch語句、用于退出循環(huán)、goto語句“文明”使用。
- 對于退出循環(huán)需要注意的是,當(dāng)有多層循環(huán)時(shí)break只會終止它所在的那層循環(huán)。
- 一個(gè)循環(huán)中可以出現(xiàn)多個(gè)break語句,但是過多break語句會破壞代碼結(jié)構(gòu)。
- 在某條switch語句中使用break,只會影響該switch,不會結(jié)束任何外層循環(huán)。
break可以作為goto語句文明使用,我們知道goto語句可以隨意進(jìn)入任意一個(gè)程序分支,是一種非結(jié)構(gòu)化的方法,使用goto語句會使代碼難以理解和維護(hù),還會妨礙編譯器優(yōu)化。但是goto語句還是有用武之地的,比如退出深層循環(huán)。java提供的break語句,提供了goto的優(yōu)點(diǎn)、去除了goto語句的缺點(diǎn)。
break label;
label是一個(gè)標(biāo)簽,當(dāng)執(zhí)行到該語句時(shí),會跳轉(zhuǎn)至標(biāo)簽定義的位置,最常用的標(biāo)識一個(gè)代碼塊。
first: {
second: {
third: {
System.out.println("Before break");
if( 1== 1)
break second;
System.out.println("inner third");
}
System.out.println("inner second");
}
System.out.println("inner first");
}
//輸出:
Before break
inner first
label: for(int i=0; i<10; i++) {
for(int j=0; j<100; j++) {
if(j == 10)
break label;
}
}
注意,break語句只能跳到包含該break上層的label上,對于其它的label是無法跳到的。
continue語句
continue用于提前終止一次迭代,當(dāng)執(zhí)行到continue語句時(shí),結(jié)束本次循環(huán),開始下次的接待。continue語句也可以指定標(biāo)簽,描述繼續(xù)執(zhí)行哪個(gè)包含它的循環(huán)。
label: for(int i=0; i<10; i++) {
for(int j=0; j<100; j++) {
if(j == 10)
continue label;
}
}
return語句
return語句是顯示的從方法返回,return語句將將程序執(zhí)行控制轉(zhuǎn)移給方法的調(diào)用者。
類
類基礎(chǔ)
類定義了一種新的數(shù)據(jù)類型,一旦定義好一個(gè)類,就可以使用這種新的類型創(chuàng)建該類型的對象。因此,類是對象的模板,對象是類的實(shí)例。
在類中封裝了數(shù)據(jù)以及對數(shù)據(jù)的操作,即變量和方法,變量和方法都稱為類的成員。在類中定義的變量稱為實(shí)例變量,因?yàn)轭惖拿總€(gè)實(shí)例都包含這些變量的副本,每個(gè)對象的變量和方法都是獨(dú)立和唯一的。
//類定義
class className {
type instance-variable;
...
type method(param-list) {
}
...
}
//創(chuàng)建對象
className object = new className(param-list);
object.instance-variable; //訪問該對象的實(shí)例變量
object.method(param-list); //訪問該對象的方法
對象的變量和方法都是類的副本,每個(gè)對象的變量和方法都是相互獨(dú)立,互不影響的。
對象創(chuàng)建
當(dāng)聲明完類后,就創(chuàng)建了一個(gè)新的數(shù)據(jù)類型。如果要使用該類型的對象需要經(jīng)過兩步:聲明對象變量和獲取對象的實(shí)際物理副本并賦值給對象變量。
聲明對象變量,并沒有定義對象,它只是一個(gè)引用對象的變量。要?jiǎng)?chuàng)建對象獲取對象的實(shí)際物理副本,需要使用new運(yùn)算符動態(tài)的為對象分配內(nèi)容,并返回該對象的引用,而這個(gè)引用就是new為該對象分配的內(nèi)容地址。然后將這個(gè)引用存儲在變量中。
對象的引用和c/c++中的指針類似,但是由于java安全機(jī)制,不能像指針那樣真實(shí)的操作引用,也不能為引用隨意分配內(nèi)容地址
我們知道創(chuàng)建基本類型并不需要使用new創(chuàng)建,是因?yàn)閖ava的基本類型不是作為對象實(shí)現(xiàn)的,而是作為常規(guī)變量實(shí)現(xiàn)的。這樣能夠提高效率,因?yàn)閷ο笥泻芏嗵卣骱蛯傩?,所以它的開銷是與基本類型不同的,所以java更高效的實(shí)現(xiàn)了基本類型,對于這些基本類型的完整對象java也有提供,比如Integer、Double。
Person p1 = new Person();
Person p2 = p1;
當(dāng)將一個(gè)對象引用的變量賦值給另一個(gè)對象的引用的變量時(shí),不會重新創(chuàng)建對象、重新分配內(nèi)存空間,而是將新的對象引用變量也指向了前一個(gè)對象的引用,比如上面的p1和p2都是指向同一個(gè)內(nèi)存空間,修改任何一個(gè)對象內(nèi)容,都會互相影響。
類方法
類中除了定義數(shù)據(jù)的實(shí)例變量外,還有用于操縱數(shù)據(jù)的方法。方法可以帶參數(shù),方法定義時(shí)的參數(shù)稱為形參,用于接受調(diào)用方法傳遞進(jìn)來的參數(shù)變量。當(dāng)調(diào)用方法時(shí)傳遞進(jìn)來的參數(shù)稱為實(shí)參,實(shí)參會賦值給形參。
設(shè)計(jì)良好的程序,應(yīng)該通過方法訪問類的實(shí)例變量,這樣可以改變方法的行為,但不會暴露實(shí)例變量的行為。
構(gòu)造函數(shù)
構(gòu)造函數(shù)是類方法中的一種特殊方法。當(dāng)創(chuàng)建對象時(shí),一般都會進(jìn)行一些初始化操作,而java通過構(gòu)造函數(shù)進(jìn)行對象自身的初始化,構(gòu)造函數(shù)在創(chuàng)建對象之后立即進(jìn)行初始化操作(構(gòu)造函數(shù)的目的就是初始化對象的內(nèi)部狀態(tài))。構(gòu)造函數(shù)名稱與類名稱一致,并且沒有返回值(實(shí)際返回類型是隱式返回類本身),java支持有參構(gòu)造函數(shù)和無參構(gòu)造函數(shù)。當(dāng)不顯示定義構(gòu)造函數(shù)時(shí),會默認(rèn)創(chuàng)建一個(gè)無參的構(gòu)造函數(shù),而默認(rèn)無參的構(gòu)造函數(shù)會進(jìn)行一些默認(rèn)的初始化操作:對所有實(shí)例變量初始化為默認(rèn)值(這就是為什么全局變量不需要顯示初始化操作),數(shù)值類型初始化為0或0.0、引用類型初始化為null、boolean初始化為false。一旦定義自己的構(gòu)造函數(shù),將不會使用默認(rèn)的改造函數(shù)(但還會進(jìn)行實(shí)例變量初始化)。
this關(guān)鍵詞
有時(shí)我們需要在類方法中引用調(diào)用自身的對象,java提供了this關(guān)鍵詞,this總是指向調(diào)用該方法的對象引用(this就是當(dāng)前對象本身)。
String name;
int age;
public Person(String name,int age) {
this.name = name;
this.age = age;
}
方法重載
在java中支持在同一個(gè)類中定義兩個(gè)或多個(gè)共享同一個(gè)名稱的方法,只要他們的參數(shù)聲明不同即可(參數(shù)的個(gè)數(shù)或參數(shù)類型),這個(gè)過程稱為方法重載(method overloading)。方法重載是Java支持多態(tài)性的一種方式。需要注意,方法的返回類型不能作為重載方法選擇的條件。
當(dāng)java遇到方法重載調(diào)用時(shí),會對形參和實(shí)參進(jìn)行匹配,找到對應(yīng)的版本。但是這個(gè)匹配不需要精確的,因?yàn)镴ava本身支持自動類型轉(zhuǎn)換,比如當(dāng)傳入int類型的實(shí)參時(shí),發(fā)現(xiàn)重載方法中沒有int類型對應(yīng)的方法,則它會將類型提升為double類型,尋找double類型的方法。這種自動類型轉(zhuǎn)換,只有當(dāng)沒有找到精確的匹配時(shí)才會發(fā)生。
方法重載的價(jià)值在于允許使用通用名稱訪問相關(guān)方法。雖然允許使用通用名稱重載不想關(guān)的方法,但是不應(yīng)該這么做。除了常規(guī)方法的重載,也可以重載構(gòu)造方法。
參數(shù)傳遞
傳遞參數(shù)通常有兩種方式:值傳遞和引用傳遞。使用值傳遞是將基本類型值的副本傳遞過去了,所以對接受的參數(shù)修改不會影響外部。但是如果傳遞的是對象,因?yàn)閷ο笫峭ㄟ^引用傳遞的,所以修改接受到的對象,就會影響原對象。
可變長參數(shù)
在JDK5之后Java提供了一種定義可變長參數(shù)的方法:
public void methdod(String ... str){
}
用戶在調(diào)用可變長參數(shù)方法時(shí),可以傳入0個(gè)或多個(gè)參數(shù),在方法內(nèi)部接受可變長參數(shù)是通過數(shù)組形式接受的,上面實(shí)例實(shí)際使用一個(gè)String數(shù)組接受可變長參數(shù)。
使用可變長參數(shù)有一些需要注意的點(diǎn):
- 可變長參數(shù)只能在方法參數(shù)列表中最后聲明。
- 一個(gè)方法只能有一個(gè)可變長參數(shù)。
- 可變長參數(shù)可以進(jìn)行重載,但需要避免模糊性調(diào)用。
訪問控制
Java提供的訪問控制包括:private、protected、默認(rèn)訪問和public。
private
如果類的成員變量或方法使用private修飾,則該變量或方法只能被包含這些方法和變量的類所訪問。
默認(rèn)訪問控制權(quán)限
如果類的成員變量或方法采用默認(rèn)訪問控制,則該變量或方法在包內(nèi)是共有的,能夠被包內(nèi)其它類所訪問。
protected
如果類的成員變量或方法使用protected修飾,則該變量或方法在包內(nèi)時(shí)共有的,能夠被包內(nèi)其它類所訪問。除了能被包內(nèi)訪問,也能被子類所訪問,即便子類沒有在該包內(nèi)。
public
如果類的成員變量或方法使用public修飾,則該方法或變量可以被任何代碼訪問。
main()方法之所以使用public修飾,是因?yàn)閙ain()方法需要被Java運(yùn)行時(shí)系統(tǒng)訪問。
良好的程序設(shè)計(jì),不應(yīng)該能夠直接操作變量,而是通過方法操作。
static關(guān)鍵字
使用static關(guān)鍵字修改類的成員變量或方法,則這些變量和方法稱為靜態(tài)變量和靜態(tài)方法。靜態(tài)變量和靜態(tài)方法可以直接使用類名訪問,而不需要?jiǎng)?chuàng)建任何對象。靜態(tài)變量和靜態(tài)方法是被所有對象所共享的(普通變量或方法,每創(chuàng)建一個(gè)對象就會為其創(chuàng)建一個(gè)副本)。
靜態(tài)變量也可以在定義時(shí)不賦值,可以通過靜態(tài)代碼塊進(jìn)行賦值。靜態(tài)代碼塊只執(zhí)行一次,當(dāng)?shù)谝淮渭虞d該類的時(shí)候執(zhí)行。
static {
...
}
靜態(tài)方法有幾個(gè)限制:
- 靜態(tài)方法中,只能訪問靜態(tài)變量。
- 靜態(tài)方法中,只能調(diào)用靜態(tài)方法。
- 靜態(tài)方法中,不能使用this、super關(guān)鍵字(因?yàn)楹蛯ο笥嘘P(guān))。
普通方法可以調(diào)用靜態(tài)變量和方法(靜態(tài)變量和方法被所有對象共享),但是靜態(tài)變量和方法不能被對象所調(diào)用
main()方法就是靜態(tài)訪問,因?yàn)镴VM需要在創(chuàng)建所有對象之前調(diào)用該方法。
final關(guān)鍵字
final關(guān)鍵字一般用來聲明常量使用,被final聲明的變量就不能夠再被重新賦值了,這也意味著在聲明final變量時(shí)需要為其進(jìn)行初始化??梢酝ㄟ^兩種方式為其賦值:第一種就是聲明時(shí)賦值;第二種就是在構(gòu)造函數(shù)中為其賦值。
方法參數(shù)和局部變量也可以聲明為final。為參數(shù)聲明final,可以防止方法中修改參數(shù)。為局部變量聲明為final,可以防止其被多次賦值。
final關(guān)鍵字也可以用于修飾方法,被final修飾過的方法,是不能被子類所重寫的。
final關(guān)鍵字也可以用于修飾類定義,被final修飾過的類,是不能被子類所繼承的。
嵌套類和內(nèi)部類
我們可以在類的內(nèi)部定義另一個(gè)類,這種類被稱為嵌套類。嵌套類的作用域被限制在包含它的類中,也就是說我們只能在包含它的類中使用嵌套類,在外部是無法使用嵌套類的。
嵌套類可以訪問包含它的類的成員變量和方法(包括私有成員變量),但是包含類不能訪問嵌套類的成員(和局部變量類似)。
嵌套類有兩種類型:靜態(tài)嵌套類和非靜態(tài)嵌套類。靜態(tài)嵌套類使用static關(guān)鍵字修飾,但是靜態(tài)類不能直接訪問包含類的非靜態(tài)成員,只能通過創(chuàng)建包含類的對象來訪問,正是由于這個(gè)限制,靜態(tài)嵌套類才很少使用。
public class A {
int a = 0;
static class B{
public void print(){
A a = new A();
System.out.println(a.a);//只能通過創(chuàng)建外部類對象,來訪問外部類非靜態(tài)成員
}
}
}
非靜態(tài)嵌套類是嵌套類的主要類型,被稱為內(nèi)部類。內(nèi)部類可以訪問包含類的所有變量和方法。
public class A {
int a = 0;
class B{
public void print(){
System.out.println(a);//直接使用外部類成員
}
}
}
只能在包含了內(nèi)部類的類中創(chuàng)建內(nèi)部類對象,如果在其他類中創(chuàng)建,會直接編譯失敗。
內(nèi)部類可以在任何代碼塊中定義,無論方法中或者循環(huán)語句中,都可以定義內(nèi)部類。
內(nèi)部類是JDK1.1之后才提供的。
繼承
繼承是面向?qū)ο缶幊?OOP)的基石之一,通過繼承可以創(chuàng)建層次化分類。使用繼承可以創(chuàng)建具有共性的超類,然后子類通過繼承超類來獲取共性特征,子類自身再去實(shí)現(xiàn)自身的特性。
只需要使用extends關(guān)鍵字,就可以繼承另一個(gè)類。
public class A{
private int a;
int b;
}
public class B extends A{
int c = 2;
public void show(){
System.out.println("b:"+b);//不能訪問a,因?yàn)槭歉割愃接凶兞? }
}
子類包含了父類所有的成員,但是子類不能訪問超類的私有成員。因?yàn)槌惐旧硪彩且粋€(gè)獨(dú)立的、單獨(dú)的類,也有自己的所有成員。
使用protected聲明的變量或方法,不僅能夠被包內(nèi)的其它類訪問,也可以被子類所訪問(即便子類沒有在該包中)。protected與默認(rèn)訪問權(quán)限,區(qū)別就在這里,能夠被子類所訪問。
可以將子類對象的引用賦值給超類,但是需要注意這時(shí)候該變量只能訪問子類對象在超類中定義的部分(也就是只能訪問超類中的東西)。因?yàn)榭梢栽L問哪些變量,是由引用類型決定的,而不是由所引用對象的類型決定的。其實(shí)這樣是合理的,因?yàn)閷τ诔悂碚f,它并不知道子類添加了什么內(nèi)容。
A ref = new B();//將子類引用對象賦值給父類變量
ref.b;
ref.c;//錯(cuò)誤,不能訪問子類內(nèi)容
子類只能繼承一個(gè)超類,Java不支持多重繼承。
super關(guān)鍵字
super關(guān)鍵字有兩種用法:第一種是調(diào)用超類的構(gòu)造函數(shù);第二種是訪問被子類隱藏的成員。
使用super調(diào)用父類構(gòu)造方法時(shí),super()方法需要在子類構(gòu)造函數(shù)的第一行使用。
class B extends A{
publicc B(int a){
super(a);
System.out.println("B function")
}
}
super()會根據(jù)參數(shù)列表,匹配調(diào)用父類對應(yīng)的構(gòu)造函數(shù)。當(dāng)多層繼承時(shí),super()總是調(diào)用該類的直接超類(自己繼承的類)
super還可以用來訪問被子類所隱藏的成員,這個(gè)和this關(guān)鍵字有些類似,只不過super只會調(diào)用超類。
class A {
int a = 1;
public void show(){
System.out.println(a);
}
}
class B extends A{
int a = 2;
public void show(){
System.out.println("child a:" + a);
System.out.println("parent a:" + super.a); //因?yàn)楦割恆變量被子類隱藏了,只能使用super關(guān)鍵字調(diào)用
}
}
構(gòu)造函數(shù)的調(diào)用順序
比如我們有一個(gè)繼承關(guān)系:C繼承B,B繼承A。那么當(dāng)我們創(chuàng)建對象C、B、A的構(gòu)造方法都是在什么時(shí)候被調(diào)用呢?
public class A{
public A(){
System.out.println("function A");
}
}
public class B extends A{
public B(){
System.out.println("function B");
}
}
public class C extends B{
public C(){
System.out.println("function C");
}
}
C c = new C();
//打印內(nèi)容:
function A
function B
function C
可以看到構(gòu)造函數(shù)調(diào)用順序是從超類到子類的繼承順序調(diào)用構(gòu)造函數(shù)的。而且無論是否使用super(),因?yàn)閟uper()總是在子構(gòu)造函數(shù)的第一行調(diào)用(這就是為啥要第一行調(diào)用)。
之所以繼承順序調(diào)用,是因?yàn)楦割惐旧聿恢雷宇惾魏吻闆r,并且有可能子類的構(gòu)造函數(shù)的初始化還有可能依賴于父類的初始化。
方法重寫與方法動態(tài)調(diào)度
子類可以重寫父類的方法,那么在子類調(diào)用被重寫的方法時(shí),總會調(diào)用子類定義的版本,除非使用super關(guān)鍵字。
class A{
public void show(){
System.out.println("A");
}
}
class B extends A{
public void show(){
System.out.println("B");
}
}
class C extends A{
public void show(){
System.out.println("C");
}
}
class Test{
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();
//超類引用重寫方法時(shí),會根據(jù)所引用對象的類型決定調(diào)用哪個(gè)版本
a = b;
a.show();//打印B
a = c;
a.show();//打印C
}
}
方法重寫和將子類對象賦值給父類變量,是動態(tài)方法調(diào)度的基礎(chǔ)。動態(tài)方法調(diào)度機(jī)制是通過運(yùn)行時(shí)確定調(diào)用方法,而不是編譯時(shí)就去解析所調(diào)用的方法。
動態(tài)方法調(diào)度是Java運(yùn)行時(shí)多態(tài)的機(jī)理。
抽象類
繼承更多的是應(yīng)用在繼承抽象類中,抽象類定義抽象內(nèi)容,但不提供抽象方法的實(shí)現(xiàn)。
任何包含抽象方法的類都必須是抽象類,抽象類中可以定義抽象方法或者普通方法。子類如果要實(shí)現(xiàn)抽象類,則必須實(shí)現(xiàn)所有抽象方法,否則子類也應(yīng)該被聲明抽象類。
public abstract class A{
abstract void show();
public void showA(){
System.out.println("A")
}
}
public class B extends A{
public void show()
}
抽象類不能使用new關(guān)鍵字創(chuàng)建對象,因?yàn)槌橄箢惖亩x本身就是不完整的。抽象類中也不能聲明抽象的構(gòu)造方法(可以聲明具體實(shí)現(xiàn)的構(gòu)造方法),也不能聲明靜態(tài)方法,但是能夠聲明靜態(tài)變量。
繼承中使用final關(guān)鍵字
final關(guān)鍵字除了定義常量,還有兩種使用方式,就是在方法前使用final修飾和在類定義前使用final修飾。
class A{
final void mothd(){
}
}
final class A{
}
如果使用final修飾方法,則該方法不能被子類所重寫(可以知道abstract和final不能被同時(shí)修飾在方法上)。如果修飾類,則該類不能被繼承。
final修飾的方法,可以提高系統(tǒng)性能。因?yàn)榫幾g器可以自由地內(nèi)聯(lián)這類方法,內(nèi)聯(lián)是final方法才會有的。一般方法需要?jiǎng)討B(tài)分析方法的調(diào)用,這稱為后期綁定。但是final方法不能被重寫,所以final方法調(diào)用可以在編譯時(shí)解析,這稱為早期綁定。
接口
接口定義
使用interface關(guān)鍵字來定義一個(gè)完全抽象的接口(在JDK8接口有默認(rèn)實(shí)現(xiàn)了),接口定義和類相似,但是接口沒有實(shí)例變量(接口中的所有變量都是final static的)。接口可以被任意數(shù)量的類實(shí)現(xiàn),一個(gè)類也可以實(shí)現(xiàn)任意數(shù)量的接口。
access interface name{
return-type method-name(parameter-list);
}
接口的訪問控制和類一樣,但是只有public和默認(rèn)權(quán)限。對于默認(rèn)權(quán)限,只有聲明接口所在的包才能訪問接口(類是只有聲明類的包能夠訪問默認(rèn)權(quán)限類),如果接口被聲明為public,則能夠被所有類訪問。
接口中定義的變量都被隱式的使用public、final和static,所以實(shí)現(xiàn)接口的類不能修改它們(只能直接使用),并且在定義的時(shí)候就需要進(jìn)行初始化。
public Config{
String path="/usr/local/config";
//等價(jià)于
public static final String path="..";
}
接口中的所有訪問也被隱式的標(biāo)識為public訪問類型,所以所有實(shí)現(xiàn)類實(shí)現(xiàn)方法的時(shí)候,都需要使用public修飾實(shí)現(xiàn)方法。接口中的方法,可以省略abstract關(guān)鍵字,這個(gè)是默認(rèn)隱式添加進(jìn)去的。
接口還可以定義在某個(gè)類或某個(gè)接口中,這種接口稱為成員接口或嵌套接口。嵌套接口可以使用public、private或protected修飾,如果在訪問權(quán)限之外使用嵌套接口,那么必須使用外嵌類或外嵌接口名稱加上內(nèi)嵌接口使用。
public class A{
public interface B {
abstract String getString();
}
}
public class C implements A.B{
public String getString(){
}
}
A.B c = new C();
接口可以通過extends繼承另一個(gè)接口,如果一個(gè)類實(shí)現(xiàn)的接口實(shí)現(xiàn)了其它接口,那么這個(gè)實(shí)現(xiàn)類需要實(shí)現(xiàn)這個(gè)接口繼承鏈的所有方法。
實(shí)現(xiàn)接口
實(shí)現(xiàn)接口可以使用implement關(guān)鍵字,一個(gè)類可以實(shí)現(xiàn)多個(gè)接口。
public className [extends superclass] [implements interface[,interface]]{
//class-body
}
接口和抽象類一樣都可以使用接口變量來接受實(shí)現(xiàn)接口類的引用,并且滿足運(yùn)行時(shí)多態(tài),也就是在調(diào)用的過程中根據(jù)引用了類型來調(diào)用具體方法,而不是在編譯過程中知道所調(diào)用的方法,使用方式和抽象類的運(yùn)行時(shí)多態(tài)性一樣。
如果一個(gè)類實(shí)現(xiàn)接口,但是沒有全部實(shí)現(xiàn)接口的中抽象方法,那么這個(gè)類就是抽象類,該抽象類的派生子類必須實(shí)現(xiàn)抽象方法。
默認(rèn)接口方法
JDK8為接口添加了一個(gè)新功能,叫做默認(rèn)方法。默認(rèn)方法允許接口定義默認(rèn)實(shí)現(xiàn)。
之所以開發(fā)默認(rèn)接口主要有以下原因:
- 提供了一個(gè)擴(kuò)展接口的方法,而不破壞現(xiàn)有代碼。當(dāng)我們擴(kuò)展一個(gè)接口時(shí),如果沒有默認(rèn)方法實(shí)現(xiàn),那么所有實(shí)現(xiàn)類都需要去實(shí)現(xiàn)這個(gè)新方法。
- 接口中的方法本質(zhì)是可選的。對于一些實(shí)現(xiàn)類,可能并不需要使用接口中的特定方法,但是實(shí)現(xiàn)接口時(shí)還需要為這個(gè)方法實(shí)現(xiàn)寫一個(gè)占位符。
public interface MyIF{
int getNumber();
default String getString(){
return "interface default str";
}
}
對于實(shí)現(xiàn)類,如果默認(rèn)方法不能夠夠滿足,實(shí)現(xiàn)類就需要重新實(shí)現(xiàn)該方法。
如果一個(gè)實(shí)現(xiàn)類實(shí)現(xiàn)了兩個(gè)接口,這兩個(gè)接心相同名稱默認(rèn)方法,并且實(shí)現(xiàn)類自身也實(shí)現(xiàn)了該方法,那么調(diào)用的時(shí)候會調(diào)用哪一個(gè)?
在所有情況中,類實(shí)現(xiàn)的優(yōu)先級高于接口默認(rèn)實(shí)現(xiàn)的。但是如果類沒有實(shí)現(xiàn)該方法,這時(shí)候編譯器就會報(bào)錯(cuò),因?yàn)樗恢勒{(diào)用哪個(gè)接口的默認(rèn)方法。但是如果要調(diào)用接口中的默認(rèn)方法,則可以使用super關(guān)鍵字。
interfaceName.super.methodName();
JDK8除了能夠定義默認(rèn)方法,還可以在接口中定義靜態(tài)方法。接口中定義的靜態(tài)方法可以獨(dú)立于任何對象使用。
public interface MyIF{
static int getDefaultNumber() {
return 0;
}
}
MyIF.getDefaultNumber();
接口與抽象類的區(qū)別
接口和類之間的決定性區(qū)別就是類可以維護(hù)狀態(tài)信息,但是接口不可以。
包
包(package)是多個(gè)類的容器,它用于保持類的命名空間相互隔離,這樣便于管理類。包是以分層方式進(jìn)行存儲,也就是每一層對應(yīng)著一個(gè)目錄。
//java源文件的第一行
package pkg;
如果要使用包中類有兩種方式:一種是通過import方式將定義包中類引入到當(dāng)前類中,另一種是通過包名+類名的全路徑引用。
import java.util.*;
class MyDate extends Date{
}
class MyDate extends java.util.Date{
}
private、默認(rèn)訪問權(quán)限、protected和public只適用于類成員訪問控制。對于非嵌套類本身只有兩種訪問級別:默認(rèn)級別和共有級別。如果類聲明為public,那么所有代碼都能夠訪問,這時(shí)候這類必須是所在文件中唯一聲明的共有類,并且文件名稱與之相同。如果類使用默認(rèn)訪問級別,則只能被相同包中的其它成員訪問。
包查找與CLASSPATH
當(dāng)我們定義完成包之后,現(xiàn)在有一個(gè)問題,就是Java運(yùn)行時(shí)系統(tǒng)如何才能知道去什么地方查找所創(chuàng)建的包?Java提供了三種機(jī)制查找包路徑。
- 默認(rèn)情況下Java運(yùn)行時(shí)系統(tǒng)使用當(dāng)前工作目錄作為起始點(diǎn),所以如果包位于當(dāng)前目錄的子目錄,就能夠找到它。
- 可以通過CLASSPATH環(huán)境變量指定包路徑。
- 可通過java或javac的-classpath選項(xiàng)為類指定路徑。
后兩種情況,指定路徑也需要指定包的上一級目錄。
垃圾回收
我們知道java對象是通過new運(yùn)算符進(jìn)行創(chuàng)建的,但是可能會想如何銷毀這個(gè)對象,釋放內(nèi)存空間。在C++中,是通過顯示調(diào)用delete運(yùn)算符來手動釋放的,而java中不需要。java是通過自動解除分配的內(nèi)存,而完成該工作的技術(shù)就是垃圾回收(garbage collection)。當(dāng)一個(gè)對象的引用不存在時(shí),就會認(rèn)為該對象不再需要,可以被回收該對象所占的內(nèi)容。
finalize方法
有時(shí)我們可以會依賴一些java系統(tǒng)外部資源,希望當(dāng)對象被銷毀前將這些資源釋放掉,java提供一種終結(jié)器(finalization)機(jī)制。通過使用終結(jié)器機(jī)制,可以定義當(dāng)對象被垃圾回收器收回前進(jìn)行特定的操作。但是一般釋放資源不使用該方法,因?yàn)椴恢朗裁磿r(shí)候才會執(zhí)行該方法,甚至在一些情況下有可能不執(zhí)行該方法(對象超出其作用域時(shí)不會被執(zhí)行)
//finalize()方法定義在Object類中
@Override
protected void finalize() throws Throwable {
//對象回收前會調(diào)用該方法
}