欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

final修饰变量 与 不可变对象

程序员文章站 2022-07-14 11:46:33
...

一、 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