一、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)是允許的。