程序员你真的理解final关键字吗?
文章目錄
- 1、修飾類
- 2、修飾方法
- 3、修飾變量
- 4、final變量修飾變量(成員變量、局部變量)
- 4、1 final修飾成員變量:
- 4、2 final修飾局部變量:
- 5、final變量和普通變量的區別
- 6、final與static的藕斷絲連
- 7、關于final修飾參數的爭議
前言
提到final關鍵字,想必大家都不陌生,可是程序員你真的理解final嗎?就比如網上流傳的”方法中不需要改變作為參數的對象變量時,使用final進行聲明,可以防止你無意的修改而影響到調用方法外的變量“ 針對這句話你怎么看?反正博主不認同,這句話顯然太過于決定,至于原因后續文章將講到…
在使用匿名內部類的時候會經常用到final關鍵字。而且在Java中String類就是一個final類,從本篇文章開始,咋們一起來揭開final的神秘面紗…
1、修飾類
final修飾一個類時,表明這個類不能被繼承。
package FinalDemo;final class Father{} class Son extends Father{ //編譯報錯,不能繼承final修飾的類}2、修飾方法
final修飾方法,方法不可以重寫,但是可以被子類訪問 【前提:方法不是 private 類型】。
package FinalDemo;class Fu{public final void speak(){System.out.println("粑粑:不,你不想拉粑粑");} } class Zi extends Fu{//直接編譯失敗,被final修飾的方法不能被重寫 // public void speak(){ // System.out.println("熊孩子:粑粑,我想拉粑粑"); // } }public class EmbellishMethod {public static void main(String[] args) {Zi z =new Zi();z.speak(); //訪問final修飾的方法} }運行結果: 粑粑:不,你不想拉粑粑3、修飾變量
final用得最多的時候就是修飾變量
如果被final修飾的是基本數據類型的變量,則其數值一旦在初始化之后便不能更改;如果是引用類型的變量,則在對其初始化之后便不能再讓其指向另一個對象。
package FinalDemo;public class EmbellishVariable {public final int a=1;public void method(){// final修飾基本數據類型的變量,其數值一旦在初始化之后便不能更改a=2;// final修飾引用類型的變量,其初始化之后便不能再讓其指向另一個對象final String str=new String();str=new String();} }
以上是final關鍵字的基本用法,很多同學都看的沒有激情,好的,從下面開始我們慢慢來深入final關鍵字…
4、final變量修飾變量(成員變量、局部變量)
首先,變量分為成員變量和局部變量
4、1 final修飾成員變量:
1、成員變量必須在定義時或者構造器中進行初始化賦值
public class FinalAndVariable {public int t; //編譯成功public final int b; //編譯失敗public final int c = 1; //編譯成功 }
如果在定義成員變量的時候不初始化行不行呢,答案是可以,對博主沒有寫錯是可以的,前提是在構造方法中將成員變量b進行初始化,代碼如下:
2、final變量一旦被初始化賦值之后,就不能再被賦值了。【注意是成員變量】
4、2 final修飾局部變量:
1、只需要保證在使用之前被初始化賦值即可
5、final變量和普通變量的區別
為了加深各位對final變量和普通變量之間的區別,先來做一道程序:
public class FinalAndVariableDifference {public static void main(String[] args) {String a = "helloWord1";final String b = "helloWord";String F = "helloWord";String c = b + 1;String e = F + 1;System.out.println((a == c));System.out.println((a == e));} }猜想一下上面程序運行的結果…估計很多小白童鞋要GG,運行結果如下:
true false大家可以先想一下這道題的輸出結果,Why?顯然這里就體現了final變量和普通變量的區別了!
當final變量修飾基本數據類型以及String類型時,編譯期間能知道它的確切值時,編譯器會把它當做編譯期常量使用。也就是說在用到該final變量的地方,相當于直接訪問的這個常量,不需要在運行時確定。有C語言基礎的童鞋應該都知道這種騷操作類似C語言的宏替換。
分析上面代碼:由于變量b被final修飾,因此會被當做編譯器常量,所以在使用到b的地方會直接將變量b 替換為它的值(這種情況我們成為編譯器的優化)。而對于變量F的訪問卻需要在運行時才能連接確定,所以返回false
注意:只有在編譯期間能確切知道final變量值的情況下,編譯器才會進行這樣的優化,那是不是只要是被final修飾的變量就會進行優化呢?當然不是!比如下面的這段代碼就不會進行優化:
final可以在編譯(類加載)時初始化,也可以在運行時初始化,初始化后不能被改變。下面這個程序便是在運行時初始化的典型事例…
public class FinalAndVariableDifference {public static void main(String[] args) {String a = "helloWord2";final String b = getHello(); //盡管是final修飾,但不會進行優化,因為它要運行時初始化才被確定String c = b + 2;System.out.println((a == c));}public static String getHello() {return "helloWord";} }運行結果:false被final修飾的變量不一定會進行優化,優化的前提是編譯時就已經能夠確定!
除此之外,還必須要清楚的一點是:被final修飾的引用變量一旦初始化賦值之后指向的對象不可變但該對象的內容可變
怎么理解呢?來看一個程序:
class AA{int i=1; } public class EmbellishQuote {public static void main(String[] args) {final AA a = new AA(); //final修飾引用變量a=new AA(); //編譯失敗,說明被final修飾的引用變量一旦初始化賦值之后指向的對象不可變System.out.println( ++a.i ); //輸出值為2,說明內容可變} }//運行結果: 26、final與static的藕斷絲連
到這里,是否對final重新認識了?當然final的使用始終離不開static字眼,二者可謂藕斷絲連,常常繁見,那么一起來看看下面這個程序吧。
package Demo;class FinalDemo {public final double i = Math.random();public static double t = Math.random(); }public class DemoDemo {public static void main(String[] args) {FinalDemo demo1 = new FinalDemo();FinalDemo demo2 = new FinalDemo();System.out.println("final修飾的 i=" + demo1.i);System.out.println("static修飾的 t=" + demo1.t);System.out.println("final修飾的 i=" + demo2.i);System.out.println("static修飾的 t=" + demo2.t);System.out.println("t+1= "+ ++demo2.t ); // System.out.println( ++demo2.i );//編譯失敗} } 運行結果:final修飾的 i=0.7282093281367935static修飾的 t=0.30720545678577604final修飾的 i=0.8106990945706758static修飾的 t=0.30720545678577604t+1= 1.307205456785776這是啥咩…不是說好的final修飾基本數據類型的變量時,則其數值一旦在初始化之后便不能更改咩?為啥子這里final修飾的基本類型值反而不一致,static修飾的基本類型卻一致?
是的,我是在前面說過這些話,但是你注意到了這句話的核心前提咩:final修飾基本數據類型的變量時,則其數值一旦在初始化之后便不能更改。
是的,已經很明顯了,上面代碼中被final修飾的變量是在運行時才初始化的,并沒有在編譯期就被初始化!由于值為隨機數,運行時被初始化是不確定的一個值,也就是個隨機數,僅僅當運行之后被初始化之后他的值才會不變!
至于static修飾的變量沒有發生變化是因為static作用于成員變量只是用來表示保存一份副本,其不會發生變化。怎么理解這個副本呢?其實static修飾的在類加載的時候就加載完成了(初始化),而且只會加載一次也就是說初始化一次,所以不會發生變化!
關于static關鍵字,詳細的講解可以看這篇深入理解static關鍵字
7、關于final修飾參數的爭議
到這里,我相信各位都對final有一個大概的系統性了解了,那么我們一起來回到關于開篇的問題。
關于不認同網上流傳的”方法中不需要改變作為參數的對象變量時,使用final進行聲明,可以防止你無意的修改而影響到調用方法外的變量“這句話,首先要想理解這句話可以先看下面final修飾的程序1代碼:
package FinalDemo;class Parameter{public void method(final int a){ //使用final修飾參數// a++; //編譯失敗// a=1; //編譯失敗System.out.println(a);} } public class ParameterAndFinal {public static void main(String[] args) {Parameter par=new Parameter();int a=2;par.method(a);int b=4;par.method(b);} } 運行結果: 2 41、至于上面代碼中注釋的代碼編譯失敗,我相信各位都能知其原因!
2、運行結果,我們也知道,方法是運行時才初始化的,執行到 int b=4時 par.method(a)方法以及結束,再到par.method(b)的時候,又重新初始化了,所以打印結果如上。
3、要想理解還得看沒有final修飾的程序2:
看完之后發現,確實,使用final修飾的程序1中,因為使用了final,所以不會影響到你在外部傳遞的參數,而不使用final修飾的程序1中,因為沒使用final,不管你傳遞啥,沒無效,不得不說確實是驗證了網上的那句話…
但是對于下面這個程序來說,就不怎么管用了喔
package FinalDemo;class BB {public void method(final StringBuffer buffer) {buffer.append("波波小菜雞");} } public class ParameterAndFinal {public static void main(String[] args) {BB b = new BB();StringBuffer buffer = new StringBuffer("乾坤未定你我皆是");b.method(buffer);System.out.println(buffer.toString());}}//運行結果: 乾坤未定你我皆是波波小菜雞為啥子我要說不管用了捏?因為你可是試著把final去掉再次運行,你會發現,結果同樣都是: 乾坤未定你我皆是波波小菜雞…
總結
以上是生活随笔為你收集整理的程序员你真的理解final关键字吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统设计与实现第3版笔记与minix
- 下一篇: HashMap的底层原理