final修飾變量 與 不可變對象

一、 final修飾變量

  1. final修改基本數據類型,則基本數據類型的值不能修改
  2. final修改引用類型變量,則該引用不能修改,但是該變量可以修改。
public class Test4 {

    public static void main(String[] args) {

        final int a = 10;
//      a = 20; 編譯錯誤

        final Test t = new Test("d",5);
//      t44 = new Test(); 編譯錯誤

        System.out.println("修改前name:"+t.getName());

        //可以修改對象的內部成員的值
        t.setName("c");
        System.out.println("修改後name:"+t.getName());
    }
}
public class Test {

    private String name;
    private int age;

    public Test() {
    }

    public Test(String name,int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}
修改前name:d
修改後name:c

二、不可變對象

我們經常聽到這樣一句話“String 對象不可以修改”,這到底是什麼意思呢?
先來看下不可變對象的定義:

 不可變對象是指一個對象的狀態在對象被創建之後就不再變化。不可變對象對於緩存是非常好的選擇,因爲你不需要擔心它的值會被更改。

創建一個不可變類:

  1. 將類聲明爲final,所以它不能被繼承;
  2. 將所有的成員聲明爲私有的,這樣就不允許直接訪問這些成員;
  3. 對變量不要提供setter方法;
  4. 將所有可變的成員聲明爲final,這樣只能對它們賦值一次;
  5. 通過構造器初始化所有成員,進行深拷貝(deep copy);
  6. 在getter方法中,不要直接返回對象本身,而是克隆對象,並返回對象的拷貝;

如果構造器傳入的對象直接賦值給成員變量,還是可以通過對傳入對象的修改進而導致改變內部變量的值。例如:

public final class ImmutableDemo {  
    private final int[] myArray;  
    public ImmutableDemo(int[] array) {  
        this.myArray = array; // wrong  
    }  
}

這種方式不能保證不可變性,myArray和array指向同一塊內存地址,用戶可以在ImmutableDemo之外通過修改array對象的值來改變myArray內部的值。
爲了保證內部的值不被修改,可以採用深度copy來創建一個新內存保存傳入的值。正確做法:

public final class MyImmutableDemo {  
    private final int[] myArray;  
    public MyImmutableDemo(int[] array) {  
        this.myArray = array.clone();   
    }   
}

三、String對象的不可性

看下String部分源代碼

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
{
    /** The value is used for character storage. */
    private final char value[];
    /** The offset is the first index of the storage that is used. */
    private final int offset;
    /** The count is the number of characters in the String. */
    private final int count;
    /** Cache the hash code for the string */
    private int hash; // Default to 0
    ....
    public String(char value[]) {
         this.value = Arrays.copyOf(value, value.length); // deep copy操作
     }
    ...
     public char[] toCharArray() {
     // Cannot use Arrays.copyOf because of class initialization order issues
        char result[] = new char[value.length];
        System.arraycopy(value, 0, result, 0, value.length);
        return result;
    }
    ...
}

如上代碼所示,可以觀察到以下設計細節:

  1. String類被final修飾,不可繼承
  2. string內部所有成員都設置爲私有變量
  3. 不存在value的setter
  4. 並將value和offset設置爲final。
  5. 當傳入可變數組value[]時,進行copy而不是直接將value[]複製給內部變量.
  6. 獲取value時不是直接返回對象引用,而是返回對象的copy.
  7. 這都符合上面總結的不變類型的特性,也保證了String類型是不可變的類。

    四、String對象是否正的不可變

    其實可以通過反射機制,改變其值。

public class Test {
    public static void main(String[] args) throws Exception {
        String s="0123456789";
        System.out.println("改變前:s=" + s);
        Field f = s.getClass().getDeclaredField("value");
        f.setAccessible(true);
        f.set(s, new char[]{'a', 'b', 'c'});
        System.out.println("改變後:s=" + s);
    }
}

參考文章:
http://www.cnblogs.com/jaylon/p/5721571.html
http://www.cnblogs.com/wcyBlog/p/4073725.html
http://blog.csdn.net/z69183787/article/details/44085031

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章