????:final關(guān)鍵字可以用于何處修飾類:該類不可被繼承修飾變量:該變量一經(jīng)初始化就不能被重新賦值,即使該值跟初始化的值相同或者指向同一個(gè)對(duì)象,也不可以類變量:實(shí)例變量:形參: 注意可以修飾形參局部變量修飾方法:該方法不可被重寫final修飾成員變量final修飾成員變量,必須顯式的指定初始值,系統(tǒng)不會(huì)為final成員進(jìn)行隱式初始化,不能在初始化前訪問。因?yàn)?,不管是類變量還是實(shí)例變量,都有個(gè)初始化的過程
final關(guān)鍵字可以用于何處
修飾類:該類不可被繼承
修飾變量:該變量一經(jīng)初始化就不能被重新賦值,即使該值跟初始化的值相同或者指向同一個(gè)對(duì)象,也不可以
類變量:
實(shí)例變量:
形參: 注意可以修飾形參
局部變量
修飾方法:該方法不可被重寫
final修飾成員變量
final修飾成員變量,必須顯式的指定初始值,系統(tǒng)不會(huì)為final成員進(jìn)行隱式初始化,不能在初始化前訪問。
因?yàn)?,不管是類變量還是實(shí)例變量,都有個(gè)初始化的過程,初始化賦值后便不能再賦值,如果不顯式的指定初始值,那么這些變量就沒有存在的意義
修飾類變量:
要么聲明時(shí)指定
要么在靜態(tài)塊中指定
二者互斥,只能是其一
修飾實(shí)例變量:
要么聲明時(shí)指定
要么非靜態(tài)塊中指定
要么構(gòu)造方法中指定
三者任意兩者互斥,只能是這三者之其一
final修飾局部變量
局部變量,不管有沒有final修飾,都是必須顯式初始化的
final修飾的形參,是不能賦值的
final修飾引用類型變量
final可以用來修飾一個(gè)引用類型變量,但只能保證這個(gè)變量始終指向同一地址
不能保證指向的對(duì)象本身發(fā)生改變
final與直接量
一個(gè)變量,不管是類變量、實(shí)例變量、局部變量,滿足下面三個(gè)條件,便是一個(gè)直接量
用final修飾
在定義該final變量時(shí)指定了初始值。注意是定義時(shí),不能在代碼塊中或者構(gòu)造方法中
該初始值可以在編譯時(shí)就被確定下來
對(duì)于直接量,在編譯的時(shí)候,會(huì)用直接量把變量名替換掉,編譯后的文件中是不存在這個(gè)變量的
Java用常量池來管理使用過的字符串直接量
final修飾方法
final修飾的方法不能被重寫,比如作為根父類的Object就有一個(gè).getClass()方法是用final修飾的
如果父類有一個(gè)private final修飾的方法,子類也有這個(gè)方法,private final修飾,方法簽名也相同,注意這不是重寫,這是兩個(gè)無(wú)關(guān)的方法,
final修飾類
final修飾的類不能被繼承,比如:
public final class Math extends Object {...}
不可變(immutable)類
不可變類:創(chuàng)建實(shí)例后,實(shí)例變量不可變,即實(shí)例的狀態(tài)不能發(fā)生改變
8個(gè)包裝類和String都是不可變類
如何定義一個(gè)不可變類:
用private和final修飾成員變量
提供帶參數(shù)的構(gòu)造器,用來初始化成員變量
只提供成員變量的get方法,不提供set方法
有必要的話重寫equals()和hashCode()方法
如果成員變量是引用類型
傳遞進(jìn)來的引用類型對(duì)象不要直接用,而要根據(jù)其實(shí)例變量重新創(chuàng)建一個(gè)
外部訪問時(shí),根據(jù)這個(gè)對(duì)象的實(shí)例變量的值,重新創(chuàng)建一個(gè)對(duì)象返回
這種情況要特別注意,上面的寫法只能用于基本類型
對(duì)引用類型不可變的寫法,基本原則就是隔絕外部對(duì)它的訪問,主要要做好兩點(diǎn):
看下面的示例代碼:
示例一:該不可變類對(duì)引用類型變量無(wú)效
public class Test{ public static void main(String[] args) { Name n=new Name("師兄","大"); System.out.println(n); //Name@15db9742 Person p=new Person(n,100); System.out.println(p); //輸出:[大師兄100歲] System.out.println(p.getName()); //Name@15db9742 n.setFirstName("師兄"); n.setLastName("二"); System.out.println(p); //輸出:[二師兄100歲],不可變對(duì)象還是變了,再看下面一段代碼 System.out.println(p.getName()); //Name@15db9742 } }class Person{ private final Name name; private final int age; public Person(Name name,int age){ this.name=name; this.age=age; } public Name getName(){ return name; } public String toString(){ return "["+name.getLastName()+name.getFirstName()+age+"歲]"; } }class Name{ private String firstName; private String lastName; public Name(){} public Name(String first,String last){ firstName=first; lastName=last; } public String getFirstName(){ return firstName; } public void setFirstName(String firstName){ this.firstName=firstName; } public String getLastName(){ return lastName; } public void setLastName(String lastName){ this.lastName=lastName; } }
示例代碼二:改寫了兩行
public class Test{ public static void main(String[] args) { Name n=new Name("師兄","大"); System.out.println(n); //Name@15db9742 Person p=new Person(n,100); System.out.println(p); //輸出:[大師兄100歲] System.out.println(p.getName()); //Name@6d06d69c n.setFirstName("師兄"); n.setLastName("二"); System.out.println(p); //輸出:[大師兄100歲]。沒發(fā)生改變 System.out.println(p.getName()); //Name@7852e922 } }class Person{ private final Name name; private final int age; public Person(Name name,int age){ this.name=new Name(name.getFirstName(),name.getLastName()); //此行改寫 this.age=age; } public Name getName(){ return new Name(name.getFirstName(),name.getLastName()); //此行改寫 } public String toString(){ return "["+name.getLastName()+name.getFirstName()+age+"歲]"; } }class Name{ private String firstName; private String lastName; public Name(){} public Name(String first,String last){ firstName=first; lastName=last; } public String getFirstName(){ return firstName; } public void setFirstName(String firstName){ this.firstName=firstName; } public String getLastName(){ return lastName; } public void setLastName(String lastName){ this.lastName=lastName; } }
緩存實(shí)例的不可變類
如果一個(gè)不可變類的部分實(shí)例,需要被頻繁的訪問到,那么可以考慮把這部分實(shí)例緩存起來,以提高性能
Integer類就把[-128,127]的每個(gè)整數(shù)的Integer對(duì)象緩存了,見 0024 Java學(xué)習(xí)筆記-面向?qū)ο?包裝類、對(duì)象的比較、String常量池問題
示例:
public class T1{ public static void main(String[] args) { CacheImmutable c1=CacheImmutable.valueOf("A"); CacheImmutable c2=CacheImmutable.valueOf("A"); System.out.println(c1==c2); //返回true } }class CacheImmutable{ private static int MAX_SIZE = 10; //緩存的大小 private static CacheImmutable[] cache=new CacheImmutable[MAX_SIZE]; //靜態(tài)變量 private static int pos=0; //下一個(gè)要緩存的對(duì)象在cache中的索引號(hào) private final String name; //CacheImmutable就這一個(gè)實(shí)例變量,用private final修飾,name是String類型,也是不可變的 private CacheImmutable(String name){ //通過帶參數(shù)的構(gòu)造器初始化final的實(shí)例變量,但不允許外部通過它創(chuàng)建對(duì)象 this.name=name; } public String getName(){ //提供了get方法,沒提供set方法, return name; } public static CacheImmutable valueOf(String name){ //要獲得該類的對(duì)象,只能通過這個(gè)靜態(tài)方法 for (int i=0;i<MAX_SIZE;i++){ //先遍歷一遍,看緩存中是否已經(jīng)有了name對(duì)應(yīng)的對(duì)象 if (cache[i]!=null && cache[i].getName().equals(name)){ //?為何不直接訪問name,有何區(qū)別 return cache[i]; //如果已經(jīng)有了就將其返回,方法調(diào)用結(jié)束 } } if (pos==MAX_SIZE){ cache[0]=new CacheImmutable(name); //如果已經(jīng)緩存了10個(gè)(cache索引號(hào)9),那就從頭開始覆蓋 pos=1; }else{ cache[pos]=new CacheImmutable(name); //如果還不到10個(gè),那就創(chuàng)建個(gè)對(duì)象,放在pos位置 pos++; } return cache[pos-1]; //返回新創(chuàng)建并緩存的對(duì)象,方法調(diào)用結(jié)束 } public boolean equals(Object obj){ if (this==obj) { return true; } if (obj!=null && obj.getClass()==CacheImmutable.class) { CacheImmutable ci=(CacheImmutable)obj; return name.equals(ci.getName()); } return false; } public int hashCode(){ return name.hashCode(); } }