Java基礎(chǔ)語法回顧(一)

Java基礎(chǔ)語法

Java規(guī)范

image.png

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)換。

  1. 兩種類型是兼容的
  2. 目標(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)用,下面是一些常用方式。

  1. 在for循環(huán)內(nèi)部聲明循環(huán)變量。
for(int i=0; i<n; i++) {
}
  1. 多變量使用逗號分隔。
for(int a=0,b=10; a<b; a++,b--) {
}
  1. 省略初始化部分和迭代部分
for( ; !done; ){
}
  1. 無限循環(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è)限制:

  1. 靜態(tài)方法中,只能訪問靜態(tài)變量。
  2. 靜態(tài)方法中,只能調(diào)用靜態(tài)方法。
  3. 靜態(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)用該方法
    }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一:java概述: 1,JDK:Java Development Kit,java的開發(fā)和運(yùn)行環(huán)境,java的開發(fā)...
    慕容小偉閱讀 1,959評論 0 10
  • Win7下如何打開DOS控制臺? a:開始--所有程序--附件--命令提示符 b:開始--搜索程序和文件--cmd...
    逍遙嘆6閱讀 1,723評論 4 12
  • 整理來自互聯(lián)網(wǎng) 1,JDK:Java Development Kit,java的開發(fā)和運(yùn)行環(huán)境,java的開發(fā)工具...
    Ncompass閱讀 1,627評論 0 6
  • (一)Java部分 1、列舉出JAVA中6個(gè)比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨(dú)云閱讀 7,279評論 0 62
  • 我有一個(gè)兩歲的表弟,長得矮矮的,特別愛吃東西,現(xiàn)在成了一個(gè)小胖子,表弟今天要來我家玩,我出去買了一個(gè)炸雞和一瓶可樂...
    林俊杰sx閱讀 429評論 0 0

友情鏈接更多精彩內(nèi)容