文章目錄
final關鍵宇可用于修飾類、方法和變量,被它修飾的類、方法和變量不可改變。
成員變量是随類初始化或對象初始化而初始化的 。
- 當類初始化時,系統會為該類的類變量配置設定記憶體,并配置設定預設值 ;
- 當建立對象時,系統會為該對象的執行個體變量配置設定記憶體,并配置設定預設值。
對于 final 修飾的成員變量而言,一旦有了初始值,就不能被重新指派,如果既沒有在定義成員變量時指定初始值,也沒有在初始化塊、構造器中為成員變量指定初始值,那麼這些成員變量的值将一直是系統預設配置設定的0、’\u000’、false或 null ,這些成員變也就完全失去了存在的意義。 是以:
Java文法規定 final 修飾的成員變量必須由程式員顯式地指定初始值。
歸納起來, final 修飾的類變量、執行個體變量能指定初始值的地方如下:
- 類變量 : 必須在靜态初始化塊中指定初始值或聲明該類變量時指定初始值,而且隻能在兩個地方的其中之一指定 。
- 執行個體變量:必須在非靜态初始化塊、聲明該執行個體變量或構造器中指定初始值 , 而且隻能在三個地方的其中之一指定 。
final成員變量執行個體
public class FinalVariableTest{
//定義成員變量時指定預設值,合法
final int a = 6;
//下面變量将在構造器或初始化塊中配置設定初始值
final String str;
final int c;
final static double d ;
//既沒有指定預設值 ,又沒有在初始化塊、構造器中指定初始值
//下面定義的 ch 執行個體變量是不合法的
// final char ch;
//初始化塊 ,可對沒有指定預設值的執行個體變量指定初始值
//;在初始化塊中為執行個體變囊指定初始值,合法
str = "He110";
//定義 a 執行個體變量時已經指定了預設值
//不能為 a 重新指派,是以下面指派語句非法
// a = 9;
//靜态初始化塊,可對沒有指定預設值的類變量指定初始值
static{
//在靜态初始化塊中為類變量指定初始值,合法
d = 5 . 6;
}
//構造器,可對既沒有指定預設值, 又沒有在初始化塊中
//指定初始值的執行個體變量指定初始值
public FinalVariableTest (){
//如果在初始化塊中已經對 str 指定了初始值
//那麼在構造器中不能對 final 變量重新指派,下面指派語句非法
// str = "java";
c = 5;
}
public void changeFinal( ){
//普通方法不能為 final 修飾的成員變量指派
//d= 1. 2;
//不能在普通方法中為 final 成員變量指定初始值
// ch = ' a ';
}
publ i c static void main(String[] args){
FinalVariableTest ft = new FinalVariableTest();
System.out . println(ft . a);
System . out.println(ft.c) ;
System . out.println(ft.d);
}
}
系統不會對局部變量進行初始化,局部變量必須由程式員顯式初始化 。 是以:
使用 final 修飾局部變量時 , 既可以在定義時指定預設值,也可以不指定預設值 。
- 如果 final 修飾的局部變量在定義時沒有指定預設值,則可以在後面代碼中對該 final 變量賦初始值,但隻能一次,不能重複指派 ;
- 如果 final 修飾的局部變量在定義時己經指定預設值,則後面代碼中不能再對該變量指派 。
final修飾局部變量、形參執行個體
pub1ic c1ass Fina1Loca1Variab1eTest{
pub1ic void test( final int a){
// 不能對 fina1 修飾的形參指派,下面語句非法
// a = 5;
}
pub1ic static void main(String[] args){
//定義 fina1 局部變量時指定預設值,則 str 變量無法重新指派
final String str = "hello";
//下面指派語句非法
// str = " Java";
// 定義 fina1 局部變量時沒有指定預設值,則 d 變量可被指派一 次
final double d;
// 第一次賦初始值,成功
d = 5.6;
// 對 fina1 變量重複指派 , 下面語句非法
// d = 3.4;
}
}
- 當使用 final 修飾基本類型變量時,不能對基本類型變量重新指派,是以基本類型變量不能被改變 。
- 但對于引用類型變量而言 ,它儲存的僅僅是一個引用, final 隻保證這個引用類型變量所引用的位址不會改變,即 一直引用同一個對象,但這個對象完全可以發生改變 。
final 修飾基本類型變量和引用類型變量的差別執行個體
c1ass Person{
private int age;
pub1ic Person() {
}
// 有參數的構造器
pub1ic Person(int age){
this.age = age;
//省略 age 的 setter 和 getter 方法
//age 的 setter 和 getter 方法
}
}
pub1ic c1ass Fina1ReferenceTest{
pub1ic static void main(String[] args){
//fina1 修飾數組變量, iArr 是一個引用變量
fina1 int[] iArr = (5 , 6, 12, 9) ;
System.out.print1n(Arrays.toString(iArr));
//對數組元素進行排序,合法
Arrays.sort(iArr);
System.out.println(Arrays.toString(iArr));
//對數組元素指派,合法
iArr[2] = - 8 ;
System.out.println(Arrays.toString (iArr) );
// 下面語句對 iArr 重新指派,非法
// iArr = null;
// final 修飾 Person 變量 , p 是一個引用變量
final Persoηp =new Person(45) ;
// 改變 Person 對象的 age 執行個體變量 ,合法
p.setAge(23) ;
System.out.println(p.getAge()) ;
//下面語句對 p 重新指派 ,非法
// p = null;
}
}
之是以要使用final 方法,可能是出于對兩方面理由的考慮。
- 第一個是為方法“上鎖”,防止任何子類改變它的本來含義。用final修飾的方法的行為在繼承期間保持不變,而且不可被重寫。
- 采用final 方法的第二個理由是程式執行的效率——将一個方法設成 final 後,編譯器就可以把對那個方法的所有調用都置入“嵌入”調用裡。隻要編譯器發現一個final 方法調用,就會(編譯器判斷)忽略為執行方法調用機制而采取的正常代碼插入方法(将變量壓入堆棧;跳至方法代碼并執行它;跳回來; 清除堆棧變量; 最後對傳回值進行處理)。相反,它會用方法主體内實際代碼的一個副本來替換方法調用。這樣做可避免方法調用時的系統開銷。
public class Test{
public final void changeName(){
// 方法體
}
}
對于一個 private 方法 , 因為它僅在目前類中可見, 其子類無法通路該方法 , 是以子類無法重寫該方法一一如果子類中定義一個與父類 private 方法有相同方法名、相同形參清單、相同傳回值類型 的方法 ,也不是方法重寫,隻是重新定義了 一個新方法。是以, 即使使用 final 修飾一個 private 通路權限的方法,依然可 以在其子類中定義與該方法具有相同方法名 、 相同形參清單、相同傳回值類型的方法。
public class PrivateFinalMethodTest{
private final void test(){}
}
class Sub extends PrivateFinalMethodTest{
//下面的方法定義不會出現 問題
public void test(){}
}
final 修飾的方法不能被重寫 , 但是可以被重載 。
final 修飾的類不可被繼承。
public final class Test {
// 類體
}
不可變( immutable ) 類的意思是建立該類的執行個體後,該執行個體 的執行個體變量是不可改變的。 Java 提供的 8 個包裝類和 java.lang.String 類都是不可變類 , 當建立它們的執行個體後 , 其執行個體的執行個體變量不可改變。
Double d = new Double(6.5) ;
String str =new String( "Hello");
如果需要建立自定義的不可變類,可遵守如下規則 。
- 使用 private 和 final 修飾符來修飾該類的成員變量。
- 提供帶參數構造器,用于根據傳入參數來初始化類裡的成員變量 。
- 僅為該類的成員變量提供 getter 方法,不要為該類的成員變量提供 setter 方法 ,因為普通方法無法修改 final 修飾的成員變量。
- 如果有必要,重寫 Object 類的 hashCode()和 equals()方法, equals()方法根據關鍵成員變量來作為兩個對象是否相等的标準,除此之外,還應該保證兩個用 equals()方法判斷為相等的對象的 hashCode()也相等。
定義一個不可變的 Address 類,程式把 Address 類的 detail 和 postCode 成員變量都使用 private隐藏起來,并使用 final 修飾這兩個成員變量 , 不允許其他方法修改這兩個成員變量的值。
public class Address{
private final String detail ;
private final String postCode ;
// 在構造器裡初始化兩個執行個體變量
public Address(){
this .detail = "";
this.postCode = "";
}
pub1ic Address(String detai1 , String postCode){
this.deta = deta ;
this.postCode = postCode ;
}
//僅為兩個執行個體變量提供 getter 方法
pub1ic String getDetai1(){
return this.detai1 ;
}
pub1ic String getPostCode(){
return this.postCode;
}
//重寫 equa1s ()方法,判斷兩個對象是否相等
pub1ic boo1ean equa1s(Object obj){
if (this == obj ){
return true ;
}
if(obj != nu11 && obj.getC1ass() == Address.c1ass{
Address ad = (Address)obj;
//當 detai1 和 postCode 相等時 , 可認為兩個 Address 對象相等
if(this . getDeta 工 1() .equa1s (ad . getDetai1())&& this.getPostCode (} . equa1s(ad.getPostCode())){
return true ;
}
}
return fa1se ;
}
pub1ic int hashCode(){
return detai1.hashCode() + postCode.hashCode()*31;
}
}
參考:
【1】:《瘋狂Java講義》
【2】:
https://www.runoob.com/java/java-modifier-types.html【3】:《Java程式設計思想》