Java中的Final關鍵字解析
這篇文章主要講解Java中final關鍵字的使用,對于final大家從字面意思就能看出來,主要是“最終的不可改變的意思”??梢孕揎楊?、方法和變量。先給出這篇文章的大致脈絡。
首先,先給出final關鍵字的三種使用場景,也就是修飾類,方法和變量
然后,深入分析final關鍵字主要注意的幾個問題
最后,總結一下final關鍵字
OK,開始今天的文章
一、final關鍵字的基本使用
1、認識final關鍵字
final可以修飾類、方法、變量。那么分別是什么作用呢?
(1)修飾類:表示類不可被繼承
(2)修飾方法:表示方法不可被覆蓋
(3)修飾變量:表示變量一旦被賦值就不可以更改它的值。java中規定final修飾成員變量必須由程序員顯示指定變量的值。
2、final關鍵字修飾類
final關鍵字修飾類表示這個類是不可被繼承的,如何去驗證呢?
3、final關鍵字修飾方法
final修飾的方法不能被重寫。但是可以重載。下面給出了一個代碼例子。主要注意的是:父類中private的方法,在子類中不能訪問該方法,但是子類與父類private方法相同的方法名、形參列表和返回值的方法,不屬于方法重寫,只是定義了一個新的方法。
- public class FinalClass{
- public final void test(){}
- public final void test(int i){}
- }
4、final關鍵字修飾變量
final關鍵字修飾變量,是比較麻煩的。但是我們只需要對其進行一個分類介紹就能理解清楚了。
(1)修飾成員變量
- 如果final修飾的是類變量,只能在靜態初始化塊中指定初始值或者聲明該類變量時指定初始值。
- 如果final修飾的是成員變量,可以在非靜態初始化塊、聲明該變量或者構造器中執行初始值。
(2)修飾局部變量
系統不會為局部變量進行初始化,局部變量必須由程序員顯示初始化。因此使用final修飾局部變量時,即可以在定義時指定默認值(后面的代碼不能對變量再賦值),也可以不指定默認值,而在后面的代碼中對final變量賦初值(僅一次)。
下面使用代碼去驗證一下這兩種情況
- public class FinalVar {
- final static int a = 0;//再聲明的時候就需要賦值
- public static void main(String[] args) {
- final int localA; //局部變量只聲明沒有初始化,不會報錯,與final無關。
- localA = 0;//在使用之前一定要賦值
- //localA = 1; 但是不允許第二次賦值
- }
- }
(3)修飾基本類型數據和引用類型數據
- 如果是基本數據類型的變量,則其數值一旦在初始化之后便不能更改;
- 如果是引用類型的變量,則在對其初始化之后便不能再讓其指向另一個對象。但是引用的值是可變的。
修飾基本類型的數據,在上面的代碼中基本上能夠看出,下面主要是描述引用類型的變量
- public class FinalReferenceTest{
- public static void main(){
- final int[] iArr={1,2,3,4};
- iArr[2]=-3;//合法
- iArr=null;//非法,對iArr不能重新賦值
- final Person p = new Person(25);
- p.setAge(24);//合法
- p=null;//非法
- }
- }
二、final關鍵字需要注意的幾個問題
1、final和static的區別
其實如果你看過我上一篇文章,基本上都能夠很容易得區分開來。static作用于成員變量用來表示只保存一份副本,而final的作用是用來保證變量不可變。下面代碼驗證一下
- public class FinalTest {
- public static void main(String[] args) {
- AA aa1 = new AA();
- AA aa2 = new AA();
- System.out.println(aa1.i);
- System.out.println(aa1.j);
- System.out.println(aa2.i);
- System.out.println(aa2.j);
- }
- }
- //j值兩個都一樣,因為是static修飾的,全局只保留一份
- //i值不一樣,兩個對象可能產生兩個不同的值,
- class AA {
- public final int i = (int) (Math.random()*100);
- public static int j = (int) (Math.random()*100);
- }
- //結果是 65、23、67、23
2、為什么局部內部類和匿名內部類只能訪問局部final變量?
為了解決這個問題,我們先要去使用代碼去驗證一下。
- public class Test {
- public static void main(String[] args) {
- }
- //局部final變量a,b
- public void test(final int b) {
- final int a = 10;
- //匿名內部類
- new Thread(){
- public void run() {
- System.out.println(a);
- System.out.println(b);
- };
- }.start();
- }
- }
上段代碼中,如果把變量a和b前面的任一個final去掉,這段代碼都編譯不過。
這段代碼會被編譯成兩個class文件:Test.class和Test1.class。默認情況下,編譯器會為匿名內部類和局部內部類起名為Outter1.class。
原因是為什么呢?這是因為test()方法里面的參數a和b,在運行時,main線程快要結束,但是thread還沒有開始。因此需要有一種機制,在使得運行thread線程時候能夠調用a和b的值,怎辦呢?java采用了一種復制的機制,
也就說如果局部變量的值在編譯期間就可以確定,則直接在匿名內部里面創建一個拷貝。如果局部變量的值無法在編譯期間確定,則通過構造器傳參的方式來對拷貝進行初始化賦值。
三、總結
final關鍵字主要用在三個地方:變量、方法、類。
- 對于一個final變量,如果是基本數據類型的變量,則其數值一旦在初始化之后便不能更改;如果是引用類型的變量,則在對其初始化之后便不能再讓其指向另一個對象。
- 當用final修飾一個類時,表明這個類不能被繼承。final類中的所有成員方法都會被隱式地指定為final方法。
- 使用final方法的原因有兩個。第一個原因是把方法鎖定,以防任何繼承類修改它的含義;第二個原因是效率。在早期的Java實現版本中,會將final方法轉為內嵌調用。但是如果方法過于龐大,可能看不到內嵌調用帶來的任何性能提升(現在的Java版本已經不需要使用final方法進行這些優化了)。類中所有的private方法都隱式地指定為final。
好了,final關鍵字就寫到這里,喜歡的還請大家給個關,謝謝支持,如有不對的地方還請批評。
本文轉載自微信公眾號「愚公要移山」,可以通過以下二維碼關注。轉載本文請聯系愚公要移山公眾號。