final關鍵字可以用於何處 修飾類:該類不可被繼承 修飾變數:該變數一經初始化就不能被重新賦值,即使該值跟初始化的值相同或者指向同一個對象,也不可以 + 類變數: + 實例變數: + : 註意可以修飾形參 + 局部變數 修飾方法:該方法不可被重寫 final修飾成員變數 final修飾成員變數,必 ...
final關鍵字可以用於何處
- 修飾類:該類不可被繼承
- 修飾變數:該變數一經初始化就不能被重新賦值,即使該值跟初始化的值相同或者指向同一個對象,也不可以
- 類變數:
- 實例變數:
形參
: 註意可以修飾形參- 局部變數
- 修飾方法:該方法不可被重寫
final修飾成員變數
- final修飾成員變數,必須顯式的指定初始值,系統不會為final成員進行隱式初始化,不能在初始化前訪問。
- 因為,不管是類變數還是實例變數,都有個初始化的過程,初始化賦值後便不能再賦值,如果不顯式的指定初始值,那麼這些變數就沒有存在的意義
- 修飾類變數:
- 要麼聲明時指定
- 要麼在靜態塊中指定
- 二者互斥,只能是其一
- 修飾實例變數:
- 要麼聲明時指定
- 要麼非靜態塊中指定
- 要麼構造方法中指定
- 三者任意兩者互斥,只能是這三者之其一
final修飾局部變數
- 局部變數,不管有沒有final修飾,都是必須顯式初始化的
- final修飾的形參,是不能賦值的
final修飾引用類型變數
- final可以用來修飾一個引用類型變數,但只能保證這個變數始終指向同一地址
- 不能保證指向的對象本身發生改變
final與直接量
- 一個變數,不管是類變數、實例變數、局部變數,滿足下麵三個條件,便是一個直接量
- 用final修飾
- 在
定義
該final變數時指定了初始值。註意是定義
時,不能在代碼塊中或者構造方法中 - 該初始值可以在編譯時就被確定下來
- 對於直接量,在編譯的時候,會用直接量把變數名替換掉,編譯後的文件中是不存在這個變數的
- Java用常量池來管理使用過的字元串直接量
final修飾方法
- final修飾的方法不能被重寫,比如作為根父類的Object就有一個.getClass()方法是用final修飾的
- 如果父類有一個private final修飾的方法,子類也有這個方法,private final修飾,方法簽名也相同,註意這不是重寫,這是兩個無關的方法,
final修飾類
- final修飾的類不能被繼承,比如:
- public final class Math extends Object {...}
不可變(immutable)類
- 不可變類:創建實例後,實例變數不可變,即實例的狀態不能發生改變
- 8個包裝類和String都是不可變類
- 如何定義一個不可變類:
- 用private和final修飾成員變數
- 提供帶參數的構造器,用來初始化成員變數
- 只提供成員變數的get方法,不提供set方法
- 有必要的話重寫equals()和hashCode()方法
- 如果成員變數是引用類型
- 這種情況要特別註意,上面的寫法只能用於基本類型
- 對引用類型不可變的寫法,基本原則就是隔絕外部對它的訪問,主要要做好兩點:
- 傳遞進來的引用類型對象不要直接用,而要根據其實例變數重新創建一個
- 外部訪問時,根據這個對象的實例變數的值,重新創建一個對象返回
- 看下麵的示例代碼:
示例一:該不可變類對引用類型變數無效
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歲],不可變對象還是變了,再看下麵一段代碼
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歲]。沒發生改變
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;
}
}
緩存實例的不可變類
- 如果一個不可變類的部分實例,需要被頻繁的訪問到,那麼可以考慮把這部分實例緩存起來,以提高性能
- Integer類就把[-128,127]的每個整數的Integer對象緩存了,見 0024 Java學習筆記-面向對象-包裝類、對象的比較、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]; //靜態變數
private static int pos=0; //下一個要緩存的對象在cache中的索引號
private final String name; //CacheImmutable就這一個實例變數,用private final修飾,name是String類型,也是不可變的
private CacheImmutable(String name){ //通過帶參數的構造器初始化final的實例變數,但不允許外部通過它創建對象
this.name=name;
}
public String getName(){ //提供了get方法,沒提供set方法,
return name;
}
public static CacheImmutable valueOf(String name){ //要獲得該類的對象,只能通過這個靜態方法
for (int i=0;i<MAX_SIZE;i++){ //先遍歷一遍,看緩存中是否已經有了name對應的對象
if (cache[i]!=null && cache[i].getName().equals(name)){ //?為何不直接訪問name,有何區別
return cache[i]; //如果已經有了就將其返回,方法調用結束
}
}
if (pos==MAX_SIZE){
cache[0]=new CacheImmutable(name); //如果已經緩存了10個(cache索引號9),那就從頭開始覆蓋
pos=1;
}else{
cache[pos]=new CacheImmutable(name); //如果還不到10個,那就創建個對象,放在pos位置
pos++;
}
return cache[pos-1]; //返回新創建並緩存的對象,方法調用結束
}
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();
}
}