11. Final關鍵字
??1掌栅、final修飾類:當用final修飾一個類時,表明這個類不能被繼承码泛。也就是說猾封,如果一個類你永遠不會讓他被繼承,就可以用final進行修飾噪珊。final類中的成員變量可以根據(jù)需要設為final晌缘,但是要注意final類中的所有成員方法都會被隱式地指定為final方法。
??在使用final修飾類的時候痢站,要注意謹慎選擇磷箕,除非這個類真的在以后不會用來繼承或者出于安全的考慮,盡量不要將類設計為final類阵难。
??2岳枷、final修飾方法:“使用final方法的原因有兩個。第一個原因是把方法鎖定呜叫,以防任何繼承類修改它的含義空繁;第二個原因是效率。在早期的Java實現(xiàn)版本中朱庆,會將final方法轉為內(nèi)嵌調(diào)用盛泡。但是如果方法過于龐大,可能看不到內(nèi)嵌調(diào)用帶來的任何性能提升椎工。在最近的Java版本中饭于,不需要使用final方法進行這些優(yōu)化了蜀踏。”
??因此掰吕,如果只有在想明確禁止該方法在子類中被覆蓋的情況下才將方法設置為final的果覆。注:類的private方法會隱式地被指定為final方法。
??3殖熟、final修飾變量:對于一個final變量局待,如果是基本數(shù)據(jù)類型的變量,則其數(shù)值一旦在初始化之后便不能更改菱属;如果是引用類型的變量钳榨,則在對其初始化之后便不能再讓其指向另一個對象。
??當final變量是基本數(shù)據(jù)類型以及String類型時纽门,如果在編譯期間能知道它的確切值薛耻,則編譯器會把它當做編譯期常量使用。也就是說在用到該final變量的地方赏陵,相當于直接訪問的這個常量饼齿,不需要在運行時確定。
關鍵字final的好處小結
??1蝙搔、final關鍵字提高了性能缕溉。JVM和Java應用都會緩存final變量。
??2吃型、final變量可以安全的在多線程環(huán)境下進行共享证鸥,而不需要額外的同步開銷。
??3勤晚、使用final關鍵字枉层,JVM會對方法、變量及類進行優(yōu)化运翼。
??4返干、對于不可變類,它的對象是只讀的血淌,可以在多線程環(huán)境下安全的共享矩欠,不用額外的同步開銷。
12. Static關鍵字
static關鍵字的用途
??《Java編程思想》:“static方法就是沒有this的方法悠夯。在static方法內(nèi)部不能調(diào)用非靜態(tài)方法癌淮,反過來是可以的。而且可以在沒有創(chuàng)建任何對象的前提下沦补,僅僅通過類本身來調(diào)用static方法乳蓄。這實際上正是static方法的主要用途∠Π颍”
??這段話雖然只是說明了static方法的特殊之處虚倒,但是可以看出static關鍵字的基本作用美侦,簡而言之,一句話來描述就是:
??方便在沒有創(chuàng)建對象的情況下來進行調(diào)用(方法/變量)魂奥。
??很顯然菠剩,被static關鍵字修飾的方法或者變量不需要依賴于對象來進行訪問,只要類被加載了耻煤,就可以通過類名去進行訪問具壮。
??static可以用來修飾類的成員方法、類的成員變量哈蝇,另外可以編寫static代碼塊來優(yōu)化程序性能棺妓。
??1)、static方法
??static方法一般稱作靜態(tài)方法炮赦,由于靜態(tài)方法不依賴于任何對象就可以進行訪問怜跑,因此對于靜態(tài)方法來說,是沒有this的吠勘,因為它不依附于任何對象妆艘,既然都沒有對象,就談不上this了看幼。并且由于這個特性,在靜態(tài)方法中不能訪問類的非靜態(tài)成員變量和非靜態(tài)成員方法幌陕,因為非靜態(tài)成員方法/變量都是必須依賴具體的對象才能夠被調(diào)用诵姜。
??但是要注意的是,雖然在靜態(tài)方法中不能訪問非靜態(tài)成員方法和非靜態(tài)成員變量搏熄,但是在非靜態(tài)成員方法中是可以訪問靜態(tài)成員方法/變量的棚唆。
??因此,如果說想在不創(chuàng)建對象的情況下調(diào)用某個方法心例,就可以將這個方法設置為static宵凌。我們最常見的static方法就是main方法,至于為什么main方法必須是static的止后,現(xiàn)在就很清楚了瞎惫。因為程序在執(zhí)行main方法的時候沒有創(chuàng)建任何對象,因此只有通過類名來訪問译株。
??2)瓜喇、static變量
??static變量也稱作靜態(tài)變量,靜態(tài)變量和非靜態(tài)變量的區(qū)別是:靜態(tài)變量被所有的對象所共享歉糜,在內(nèi)存中只有一個副本乘寒,它當且僅當在類初次加載時會被初始化。而非靜態(tài)變量是對象所擁有的匪补,在創(chuàng)建對象的時候被初始化伞辛,存在多個副本烂翰,各個對象擁有的副本互不影響。
??static成員變量的初始化順序按照定義的順序進行初始化蚤氏。
??3)甘耿、static代碼塊
??static關鍵字還有一個比較關鍵的作用就是用來形成靜態(tài)代碼塊以優(yōu)化程序性能。static塊可以置于類中的任何地方瞧捌,類中可以有多個static塊棵里。在類初次被加載的時候,會按照static塊的順序來執(zhí)行每個static塊姐呐,并且只會執(zhí)行一次殿怜。
??為什么說static塊可以用來優(yōu)化程序性能,是因為它的特性:只會在類加載的時候執(zhí)行一次曙砂。因此头谜,很多時候會將一些只需要進行一次的初始化操作都放在static代碼塊中進行。
static關鍵字的誤區(qū)
??1)鸠澈、static關鍵字會改變類中成員的訪問權限嗎柱告?
??Java中的static關鍵字不會影響到變量或者方法的作用域。在Java中能夠影響到訪問權限的只有private笑陈、public际度、protected(包括包訪問權限)這幾個關鍵字。
??2)涵妥、能通過this訪問靜態(tài)成員變量嗎乖菱?
??雖然對于靜態(tài)方法來說沒有this,那么在非靜態(tài)方法中能夠通過this訪問靜態(tài)成員變量嗎蓬网?主要考察隊this和static的理解窒所。在這里永遠要記住一點:靜態(tài)成員變量雖然獨立于對象,但是不代表不可以通過對象去訪問帆锋,所有的靜態(tài)方法和靜態(tài)變量都可以通過對象訪問(只要訪問權限足夠)吵取。
??3)、static能作用于局部變量么锯厢?
??在Java中切記:static是不允許用來修飾局部變量皮官,這是Java語法的規(guī)定。
??4)哲鸳、java中是否可以覆蓋(override)一個private方法或者static方法臣疑?
??都不能
??覆蓋,也就是我們常說的重寫徙菠,是子類繼承父類讯沈,且子類中的方法和父類中的方法,方法名相同,參數(shù)個數(shù)和類型相同缺狠,返回值相同问慎。
??private修飾的方法,不能被繼承挤茄,所以也不存在重寫(覆蓋)
??static修飾的方法如叼,是靜態(tài)方法,在編譯時就和類名就行了綁定穷劈。而重寫發(fā)生在運行時笼恰,動態(tài)綁定的。何況static方法歇终,跟類的實例都不相關社证,所以概念上也適用。
??5)评凝、靜態(tài)導包
??Static還有一種不太常用的用法追葡,即靜態(tài)導包用法,將類的方法直接導入到當前類中奕短,從而直接使用“方法名”即可調(diào)用類方法宜肉,更加方便。
常見的筆試面試題
1翎碑、下面這段代碼的輸出結果是什么谬返?
public class TestMain {
static {
System.out.println("static block1");
}
public static void main(String[] args) {
// 在執(zhí)行main方法之前會首先加載這個類,所以即使main方法中沒有語句日杈,靜態(tài)代碼塊還是會執(zhí)行
}
static {
System.out.println("static block2");
}
}
2朱浴、下面這段代碼的輸出結果是什么?
public class Test1 extends Base {
static {
System.out.println("test static");
}
public Test1() {
System.out.println("test constructor");
}
public static void main(String[] args) {
new Test1();
}
}
class Base {
static {
System.out.println("base static");
}
public Base() {
System.out.println("base constructor");
}
}
??先來想一下這段代碼具體的執(zhí)行過程达椰,在執(zhí)行開始,先要尋找到main方法项乒,因為main方法是程序的入口啰劲,但是在執(zhí)行main方法之前,必須先加載Test1類檀何,而在加載Test1類的時候發(fā)現(xiàn)Test1類繼承自Base類蝇裤,因此會轉去先加載Base類,在加載Base類的時候频鉴,發(fā)現(xiàn)有static塊栓辜,便執(zhí)行了static塊。在Base類加載完成之后垛孔,便繼續(xù)加載Test1類藕甩,然后發(fā)現(xiàn)Test1類中也有static塊,便執(zhí)行static塊周荐。在加載完所需的類之后狭莱,便開始執(zhí)行main方法僵娃。在main方法中執(zhí)行new Test1()的時候會先調(diào)用父類的構造器,然后再調(diào)用自身的構造器腋妙。因此默怨,便出現(xiàn)了上面的輸出結果。
3骤素、下面這段代碼的輸出結果是什么匙睹?
public class Test2 {
Person person = new Person("Test");
static {
System.out.println("test static");
}
public Test2() {
System.out.println("test constructor");
}
public static void main(String[] args) {
new MyClass();
}
}
class Person {
static {
System.out.println("person static");
}
public Person(String str) {
System.out.println("person " + str);
}
}
class MyClass extends Test2 {
Person person = new Person("MyClass");
static {
System.out.println("myclass static");
}
public MyClass() {
System.out.println("myclass constructor");
}
}
??首先加載Test類,因此會執(zhí)行Test類中的static塊济竹。接著執(zhí)行new MyClass()痕檬,而MyClass類還沒有被加載,因此需要加載MyClass類规辱。在加載MyClass類的時候谆棺,發(fā)現(xiàn)MyClass類繼承自Test類,但是由于Test類已經(jīng)被加載了罕袋,所以只需要加載MyClass類改淑,那么就會執(zhí)行MyClass類的中的static塊。在加載完之后浴讯,就通過構造器來生成對象朵夏。而在生成對象的時候,必須先初始化父類的成員變量榆纽,因此會執(zhí)行Test中的Person person = new Person()仰猖,而Person類還沒有被加載過,因此會先加載Person類并執(zhí)行Person類中的static塊奈籽,接著執(zhí)行父類的構造器饥侵,完成了父類的初始化,然后就來初始化自身了衣屏,因此會接著執(zhí)行MyClass中的Person person = new Person()躏升,最后執(zhí)行MyClass的構造器。
4狼忱、下面這段代碼的輸出結果是什么膨疏?
public class Test3 {
// 由于類只加載一次,所以看效果時只能執(zhí)行一句
public static void main(String[] args) {
/**
* 只輸出classB
* 但是當str沒有final修飾時钻弄,會輸出
* A
* B
* classB
*/
// System.out.println(B.str);
/**
* 輸出
* A
* C
* classC
*/
// System.out.println(C.str);
/**
* 均輸出
* A
* D
* 200
*/
// System.out.println(D.bb);
// System.out.println(D.aa);
/**
* 只輸出200
*/
System.out.println(D.cc);
}
}
class A {
static {
System.out.println("A");
}
}
class B extends A {
static {
System.out.println("B");
}
public static final String str = "calssB";
}
class C extends A {
static {
System.out.println("C");
}
public static final String str = new String("classC");
}
class D extends A {
static {
System.out.println("D");
}
public static final int cc = 200;
public static final Integer aa = 100;
public static final Integer bb = new Integer(200);
}
總結:調(diào)用類的靜態(tài)成員或方法佃却,會引起類的初始化,調(diào)用類中常量成員則不會引起類的初始化窘俺。
13. this和super
??1饲帅、從本質上講,this是一個指向本對象的指針,然而super是一個Java關鍵字洒闸,用來對父類進行調(diào)用染坯。
??2、this:它代表當前對象的引用(在程序中易產(chǎn)生二義性之處丘逸,應使用this來指明當前對象单鹿;如果函數(shù)的形參與類中的成員數(shù)據(jù)同名,這時需用this來指明成員變量名)深纲。
??3仲锄、可以使用super關鍵字來引用父類(最近父類)的成員變量,方法與構造器湃鹊。(用來訪問直接父類中被隱藏的成員數(shù)據(jù)或函數(shù)儒喊,基類與派生類中有相同成員定義時,如:super.變量名币呵、super.成員函數(shù)名(實參))怀愧。
??4、super(參數(shù)):調(diào)用基類中的某一個構造函數(shù)(應該為構造函數(shù)中的第一條語句)余赢。
??調(diào)用super()必須寫在子類構造方法的第一行芯义,否則編譯不通過。每個子類構造方法的第一條語句妻柒,都是隱含地調(diào)用super()扛拨,如果父類沒有這種形式的構造函數(shù),那么在編譯的時候就會報錯举塔。
??5绑警、this(參數(shù)):調(diào)用本類中另一種形成的構造函數(shù)(應該為構造函數(shù)中的第一條語句)。盡管可以用this調(diào)用一個構造器央渣,但卻不能調(diào)用兩個计盒。
??super()和this()類似,均需放在構造方法內(nèi)第一行芽丹。區(qū)別是章郁,super()從子類中調(diào)用父類的構造方法,this()在同一類內(nèi)調(diào)用其它構造方法志衍。
??this和super不能同時出現(xiàn)在一個構造函數(shù)里面,因為this必然會調(diào)用其它的構造函數(shù)聊替,其它的構造函數(shù)必然也會有super語句的存在楼肪,所以在同一個構造函數(shù)里面有相同的語句,就失去了語句的意義惹悄,編譯器也不會通過春叫。(而且從this和super都要求放在構造函數(shù)的第一行來看,它們也無法同時存在一個構造方法里面)。
??6暂殖、this()和super()都指的是對象价匠,所以,均不可以在static環(huán)境中使用呛每。包括:static變量踩窖,static方法,static語句塊晨横。