java面試 final、static關(guān)鍵字

一、final

根據(jù)程序上下文環(huán)境,java中的final關(guān)鍵字有無(wú)法修改的、最終形態(tài)的含義。它可以修飾非抽象類、非抽象成員方法和變量。
final關(guān)鍵字修飾的類不能被繼承、沒有子類,其類中的方法也默認(rèn)是final的。
final修飾的方法不能被子類中的方法覆蓋,但是可以被繼承。
final修飾的成員變量表示常量,只能被賦值一次,且賦值后值就不再改變。
final不能用于修飾構(gòu)造方法。
值得注意的一點(diǎn)是:父類中的private私有方法是不能被子類方法覆蓋的,因此,private類型的方法默認(rèn)是final類型的。

1. final類

如上說(shuō)明,final類不能被繼承,因此其內(nèi)的成員方法也不能被覆蓋,默認(rèn)都是final的。我們?cè)谠O(shè)計(jì)一個(gè)類的時(shí)候,如果不需要有子類,類的實(shí)現(xiàn)細(xì)節(jié)不允許改變,且能夠確信這個(gè)類不會(huì)被再次擴(kuò)展,那么就可以將這個(gè)類設(shè)計(jì)為final類。例如String類就是final類,代碼如下:

public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence

我們不能繼承或者重寫String類,而能直接使用該類。

2. final方法

我們下面使用子類繼承的方式來(lái)演示final修飾符在實(shí)際中修飾方法的應(yīng)用
testfinal.java

public class testfinal {
    public void method1() {
        System.out.println("This is method1");
    }
    //不能被改變的方法,此方法無(wú)法被子類覆蓋
    public final void method2() {
        System.out.println("This is final method2");
    }
    public void method3() {
        System.out.println("This is method3");
    }
    //私有方法,不能被子類覆蓋,也不能被子類繼承
    private void method4() {
        System.out.println("This is private method4");
    }
}

keywordfinal.java

public class keywordfinal extends testfinal {
    //對(duì)于父類中的method1方法進(jìn)行了覆蓋
    public void method1() {
        System.out.println("This is keywordfinal's method1");
    }
    
    public static void main(String[] args) {
        keywordfinal keywordfinal = new keywordfinal();
        keywordfinal.method1();
        keywordfinal.method2();
        keywordfinal.method3();
        //keywordfinal.method4();//父類中的private方法,子類無(wú)法繼承和覆蓋
    }
}

執(zhí)行結(jié)果為

This is keywordfinal's method1
This is final method2
This is method3

通過(guò)上述演示的結(jié)果,我們可以發(fā)現(xiàn),在父類中聲明的final方法,無(wú)法在子類覆蓋
編譯器在遇到final方法的時(shí)候,會(huì)轉(zhuǎn)入內(nèi)嵌機(jī)制,這種方式可以大大提高代碼的執(zhí)行效率。

3. final變量(常量)

使用final修飾符修飾的變量,用于表示常量,因?yàn)橹狄坏┙o定,就無(wú)法改變!
可以使用final修飾的變量有三種:靜態(tài)變量、實(shí)例變量和局部變量,這三種類型分別可以代表三種類型的常量。
我們可以在類中使用PI值的時(shí)候,將其聲明為常量,這樣就可以在整個(gè)運(yùn)行過(guò)程中,值都不會(huì)改變。
在聲明final變量的時(shí)候,可以先聲明,不給定初始值,這種變量也可以稱之為final空白。無(wú)論什么情況,編譯器都必須確final空白在被使用之前初始化值。但是這種方式也提供了更大的靈活性,我們可以實(shí)現(xiàn),一個(gè)類中的final常量依據(jù)對(duì)象的不同而有所不同,但是又能保持其恒定不變的特征。
下面的代碼對(duì)于上面的說(shuō)明進(jìn)行了具體實(shí)現(xiàn):

public class Finalword {
    private final String finalS = "finalS";
    private final int finalI = 100;
    public final int finalIntB = 90;
    
    public static final int staticfinalC = 80;
    private static final int staticfinalD=70;
    
    public final int finalIntE;//final空白,必須要在初始化對(duì)象的時(shí)候給定初始值,如果聲明為靜態(tài)變量,必須要給定初始值。
    public Finalword(int e) {
        this.finalIntE = e;
    }
    
    //public Finalword() {}//在類中有未給定值的final常量時(shí),無(wú)法聲明不給定初始值的構(gòu)造方法,會(huì)提示finalIntE未初始化。
    
    public static void main(String[] args) {
        Finalword finalword = new Finalword(60);
        //finalword.finalI = 101;//提示值已分配錯(cuò)誤,final變量的值一旦給定,無(wú)法進(jìn)行改變
        //finalword.finalIntB = 91;//提示值已分配錯(cuò)誤,final變量的值一旦給定,無(wú)法進(jìn)行改變
        //finalword.staticfinalC = 81;//提示值已分配錯(cuò)誤,final變量的值一旦給定,無(wú)法進(jìn)行改變
        //finalword.staticfinalD = 71;//提示值已分配錯(cuò)誤,final變量的值一旦給定,無(wú)法進(jìn)行改變
        
        System.out.println(finalword.finalI);
        System.out.println(finalword.finalIntB);
        System.out.println(finalword.staticfinalC);//不推薦使用實(shí)例的方式調(diào)用靜態(tài)常亮,推薦使用類名.常量名的方式,例如Finalword.staticfinalC
        System.out.println(finalword.staticfinalD);//不推薦使用實(shí)例的方式調(diào)用靜態(tài)常亮,推薦使用類名.常量名的方式,例如Finalword.staticfinalD
        System.out.println(Finalword.staticfinalC);
        System.out.println(Finalword.staticfinalD);
        
        //System.out.println(Finalword.finalIntE);//無(wú)法調(diào)用非靜態(tài)變量
        System.out.println(finalword.finalIntE);
        
        Finalword finalword2 = new Finalword(50);
        System.out.println(finalword2.finalIntE);//final空白變量finalIntE可以根據(jù)實(shí)例化時(shí)給定值的不同而不同
    }

    private void testMethod() {
        final int a;//final空白,在需要的時(shí)候才賦值 
        final int b = 4;//局部常量--final用于局部變量的情形 
        final int c;//final空白,一直沒有給賦值.
        a = 3; 
        //a=4;//出錯(cuò),已經(jīng)給賦過(guò)值了. 
        //b=2;//出錯(cuò),已經(jīng)給賦過(guò)值了. 
    }
}

4. final參數(shù)

當(dāng)函數(shù)的參數(shù)為final類型時(shí),在方法內(nèi)部可以讀取和使用該參數(shù),但是無(wú)法改變值

public class FinalWord2 {
    public void method1(final int i) {
        //i++;//提示值已初始化錯(cuò)誤,final修飾的參數(shù)的值不允許改變
        System.out.println(i);
    }
    public static void main(String[] args) {
        new FinalWord2().method1(5);
    }
}

2. static

static表示有“全局”或者“靜態(tài)”的意思,用來(lái)修飾成員變量和成員方法,可以形成靜態(tài)static代碼塊,但是目前在java語(yǔ)言中并沒有全局變量的概念。

被static修飾的成員變量和成員方法獨(dú)立于該類的任何對(duì)象。也就是說(shuō),它并不依賴類特定的實(shí)例,被類的所有實(shí)例共享。只要這個(gè)類被加載,java虛擬機(jī)就能根據(jù)類名在運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi)找到靜態(tài)內(nèi)容。也是因此,static修飾的對(duì)象可以在他的任何對(duì)象創(chuàng)建之前訪問(wèn),無(wú)需引用任何對(duì)象。

使用public修飾的static成員變量和成員方法本質(zhì)上就是全局變量和全局方法,當(dāng)聲明其類的對(duì)象時(shí),不生成static變量的副本,而是類的所有實(shí)例共享同一個(gè)static變量。

static變量前的權(quán)限修飾,影響static的可調(diào)用范圍,如果使用private修飾,則表示該變量可以在類的靜態(tài)代碼塊中,或者類的其他靜態(tài)成員方法中調(diào)用,也可以用于非靜態(tài)成員方法,但是不能在其他類中通過(guò)類名直接引用。因此static是不需要實(shí)例化就可以使用,而權(quán)限控制前綴只是限制其使用范圍。

調(diào)用靜態(tài)成員或者靜態(tài)方法的方式也很簡(jiǎn)單,可以直接使用類名來(lái)訪問(wèn),語(yǔ)法如下:

類名.靜態(tài)方法名(參數(shù))
類名.靜態(tài)變量名

使用static修飾的代碼塊就是靜態(tài)代碼塊,當(dāng)java虛擬機(jī)(JVM)加載類的時(shí)候,就會(huì)執(zhí)行該代碼塊。

1. static變量

類中變量根據(jù)是否使用static進(jìn)行修飾,可以分為兩種:
一種是使用static修飾的,成為靜態(tài)變量或者類變量
另一種是沒有使用static修飾的,成為實(shí)例變量

靜態(tài)變量在內(nèi)存中只有一個(gè)拷貝(節(jié)省內(nèi)存),JVM中只為靜態(tài)變量分配一次內(nèi)存,在類加載的過(guò)程中就完成了對(duì)于靜態(tài)變量的內(nèi)存分配,可以使用類名直接訪問(wèn),也可以使用實(shí)例化后的對(duì)象進(jìn)行訪問(wèn)(這種方式不推薦)。
實(shí)例變量是每創(chuàng)建一個(gè)實(shí)例,就會(huì)為實(shí)例變量分配一次內(nèi)存,實(shí)例變量可以在內(nèi)存中存在多個(gè)拷貝,且互不影響,相較于靜態(tài)變量來(lái)講更為靈活。

2. 靜態(tài)方法

靜態(tài)方法使用類名可以直接進(jìn)行調(diào)用,任何實(shí)例也可以直接調(diào)用,因此靜態(tài)方法中不能使用this和super關(guān)鍵字,不能直接訪問(wèn)所屬類的實(shí)例變量和實(shí)例方法(指的是不使用static修飾符修飾的方法和變量),只能訪問(wèn)所屬類中的靜態(tài)成員變量和靜態(tài)成員方法。這個(gè)問(wèn)題主要是因?yàn)閷?shí)例成員對(duì)象同特定的對(duì)象關(guān)聯(lián),而靜態(tài)方法同具體的實(shí)例無(wú)關(guān)。

因?yàn)閟tatic方法獨(dú)立于任何對(duì)象,所以就要求static方法必須被實(shí)現(xiàn),且不能是抽象abstract的。

3. static代碼塊

static代碼塊也成為靜態(tài)代碼塊,指的是類中獨(dú)立于類成員的static語(yǔ)句塊,在一個(gè)類中可以存在多個(gè)靜態(tài)代碼塊,位置也可以隨便放,它不在任何的方法體內(nèi),JVM加載類的時(shí)候會(huì)首先執(zhí)行這些靜態(tài)代碼塊,如果static代碼塊有多個(gè),則JVM會(huì)根據(jù)它們?cè)陬愔谐霈F(xiàn)的先后順序依次執(zhí)行,每個(gè)代碼塊都只會(huì)被執(zhí)行一次,例如:

public class StaticBlock {
    private static int a;
    private int b;
    
    static {
        StaticBlock.a = 5;
        System.out.println(a);
        
        StaticBlock staticBlock = new StaticBlock();
        staticBlock.f();
        staticBlock.b = 1000;
        System.out.println(staticBlock.b);
    }
    
    static {
        StaticBlock.a = 4;
        System.out.println(a);
    }
    
    public static void main(String[] args) {
        StaticBlock staticBlock = new StaticBlock();
        staticBlock.b = 990;
        System.out.println("This is method main()");
        System.out.println(staticBlock.b);
        System.out.println(StaticBlock.a);
    }
    static {
        StaticBlock.a = 6;
        System.out.println(a);
    }
    
    public void f() {
        System.out.println("This is method f()");
    }
}

執(zhí)行結(jié)果

5
This is method f()
1000
4
6
This is method main()
990
6

從上可以看出,我們可以使用靜態(tài)代碼塊對(duì)于靜態(tài)變量進(jìn)行賦值。main方法也是靜態(tài)的,這樣JVM在運(yùn)行main方法的時(shí)候可以直接調(diào)用,而不需要?jiǎng)?chuàng)建實(shí)例調(diào)用。靜態(tài)變量、靜態(tài)方法、靜態(tài)方法塊的運(yùn)行都在main方法執(zhí)行之前。

三、 static同final一起使用

static final用來(lái)修飾成員變量和成員方法,可以理解為“全局常量”。
對(duì)于變量,表示一旦給定初始值,就不可以修改,而且可以直接通過(guò)類名訪問(wèn)。
對(duì)于方法,表示不可覆蓋,而且可以通過(guò)類名直接訪問(wèn)。

對(duì)于被static final修飾過(guò)的實(shí)例常量,實(shí)例本身不能再改變,但是對(duì)于一些容器類型,例如(ArrayList、HashMap),不可以改變?nèi)萜髯兞勘旧?,但是可以修改容器?nèi)存放的對(duì)象。這種特性在編程中用到很多。

例子如下:

public class TestStaticFinal {
    private static final String strStaticFinalVar = "aaa";//全局常量
    private static String strStaticVar = null;//靜態(tài)變量
    private final String strFinalVar = null;//不可變常量
    private static final int intStaticFinalVar = 0;
    private static final Integer integerStaticFinalVar = new Integer(8);
    private static final ArrayList<String> arrStaticFinalVar = new ArrayList<String>();
    
    private void test() {
        System.out.println("-------------值處理前----------");
        System.out.println("strStaticFinalVar = " + strStaticFinalVar);
        System.out.println("strStaticVar = " + strStaticVar);
        System.out.println("strFinalVar = " + strFinalVar);
        System.out.println("intStaticFinalVar = " + intStaticFinalVar);
        System.out.println("integerStaticFinalVar = " + integerStaticFinalVar);
        System.out.println("arrStaticFinalVar = " + arrStaticFinalVar);
        
        //strStaticFinalVar = "新值";//錯(cuò)誤,final變量修飾,不可變,所以不能修改
        strStaticVar = "新的靜態(tài)變量值";//正確,static修飾的變量表示全局變量,可以改變值
        //strFinalVar = "新的不可變值";//錯(cuò)誤,final變量修飾,在使用前必須給出初始值,null值也算,且該初始值給定之后就不可修改。
        //intStaticFinalVar = 1;//錯(cuò)誤,final變量修飾,不可變,所以不能修改
        //integerStaticFinalVar = new Integer(9);//錯(cuò)誤,final變量修飾,不可變,所以不能修改
        arrStaticFinalVar.add("item1");//正確,容器變量本身沒有變化,變的是其內(nèi)部的存放內(nèi)容。該方法使用范圍較為廣泛
        arrStaticFinalVar.add("item2");//正確,容器變量本身沒有變化,變的是其內(nèi)部的存放內(nèi)容。該方法使用范圍較為廣泛
        
        System.out.println("-------------值處理后----------");
        System.out.println("strStaticFinalVar = " + strStaticFinalVar);
        System.out.println("strStaticVar = " + strStaticVar);
        System.out.println("strFinalVar = " + strFinalVar);
        System.out.println("intStaticFinalVar = " + intStaticFinalVar);
        System.out.println("integerStaticFinalVar = " + integerStaticFinalVar);
        System.out.println("arrStaticFinalVar = " + arrStaticFinalVar);
    }
    public static void main(String[] args) {
        new TestStaticFinal().test();
    }
}

執(zhí)行結(jié)果

-------------值處理前----------
strStaticFinalVar = aaa
strStaticVar = null
strFinalVar = null
intStaticFinalVar = 0
integerStaticFinalVar = 8
arrStaticFinalVar = []
-------------值處理后----------
strStaticFinalVar = aaa
strStaticVar = 新的靜態(tài)變量值
strFinalVar = null
intStaticFinalVar = 0
integerStaticFinalVar = 8
arrStaticFinalVar = [item1, item2]

通過(guò)上面的例子可以總結(jié)得出,使用final修飾的變量給定初始值之后就不可以改變了,但是使用final修飾的容器在不改變?nèi)萜鞅旧淼那闆r下,修改容器內(nèi)部存儲(chǔ)的內(nèi)容,這個(gè)改動(dòng)是允許的。

參考:http://lavasoft.blog.51cto.com/62575/18771/

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

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 34,662評(píng)論 18 399
  • Advanced Language Features 知識(shí)點(diǎn):一. static修飾符 static修飾符可以用來(lái)...
    風(fēng)景涼閱讀 505評(píng)論 0 0
  • Java關(guān)鍵字final、static使用總結(jié) 一、final 根據(jù)程序上下文環(huán)境,Java關(guān)鍵字final有“這...
    孑孖閱讀 432評(píng)論 0 0
  • final final類 final類不能被繼承,因此final類的成員方法沒有機(jī)會(huì)被覆蓋,默認(rèn)都是final的。...
    吃瓜群眾liu閱讀 390評(píng)論 0 1
  • Java關(guān)鍵字final 在設(shè)計(jì)程序時(shí),出于效率或者設(shè)計(jì)的原因,有時(shí)候希望某些數(shù)據(jù)是不可改變的。這時(shí)候可以使用fi...
    獅_子歌歌閱讀 885評(píng)論 1 4

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