String的不可变性

来源:互联网 发布:中国影子银行规模数据 编辑:程序博客网 时间:2024/06/10 03:40

String的不可变性

首先明确一点,String类被声明为final与String对象是immutable的没有必然的联系。

那么为什么说String对象是不可变的呢?

1、 我们知道String实现依靠的是char[], 那么首先看下String源码:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {    /** The value is used for character storage. */    private final char value[];

在程序内部这个数组被声明为private的,同时是个final的。
Private可以保证value数组不被调用者访问到,final可以确保数组的地址是不变的。

注意,声明数组为final的时仅仅可以保证数组的地址是不变的,并不能保证数组里面的值不变。

请看下面的例子:
这里写图片描述

总结:内部的value数组被声明为private,确保了value的值不会被外部程序改变。

2、 String类被声明为final的,它的意义是什么呢?

我们知道,类被声明为final的时候,这个类是不可以为继承的,这样就不能修改这个类里面的方法实现。
比方说如果String类可以被继承。那么我们就可以写一个方法去改变value数组,比如:

//改变value数组public void setChar(char c , int index){        value[index] = c;}//或者直接去改变String对象:  public static StringBuilder append(StringBuilder stringBuilder){        return stringBuilder.append("122");    }

3、 String对象的不可变的好处

(1)以set为例

@Test    public void test3() {        Set<StringBuilder> stringBuilders = new HashSet<StringBuilder>();       // StringBuilder类型的变量分别指向堆上的"a" 和"ab",        StringBuilder stringBuilder = new StringBuilder("a");        StringBuilder stringBuilder1 = new StringBuilder("ab");        //将这两个变量放进HashSet中        stringBuilders.add(stringBuilder);        stringBuilders.add(stringBuilder1);        //创建一个新的StringBuilder stringBuilder2变量指向 stringBuilder 所在的地址        StringBuilder stringBuilder2 = stringBuilder;        //在stringBuilder2上追加一个字符串"b",        // 注意:由于StringBuilder是可变的,此时会改变stringBuilder的值        //此时会导致Set上出现两个相等的值,从而破坏了HashSet的键值唯一性        stringBuilder2.append("b");        stringBuilders.add(stringBuilder2);    }

(2)String的不可变性确保了线程安全。在并发场景下,对共享资源的写操作会引发静态条件,只有对资源进行写操作才引发资源状态的不一致,而不可变对象不可写,因此是线程安全的。

总结:String类通过将类设置为final来确保类不会被继承,从而可以保证方法不被覆盖,因此确保了String类的所有方法都唯一被SUN的工程师设计和控制。我们在使用的过程中需要注意,千万不要用可变类型做HashMap和HashSet键值,在使用自定义的对象作为键值的时候,一定要重写HashCode和Equals方法。

4、 上面讲过了String类不可以被外部修改,那么如果在内部被修改的话,同样不能保证是immutable的。

现在来看内部是如何保证String对象不可变的
首先:我们知道String是一个引用类型,如果对它本身进行修改是不需要返回的,通过观察源码可以发现,任何写操作都会返回一个新的String对象。
例如replace方法:

public String replace(char oldChar, char newChar) {        if (oldChar != newChar) {            int len = value.length;            int i = -1;            char[] val = value; /* avoid getfield opcode */            while (++i < len) {                if (val[i] == oldChar) {                    break;                }            }            if (i < len) {                char buf[] = new char[len];                for (int j = 0; j < i; j++) {                    buf[j] = val[j];                }                while (i < len) {                    char c = val[i];                    buf[i] = (c == oldChar) ? newChar : c;                    i++;                }                return new String(buf, true);            }        }        return this;    }

总结:因为在String类的内部,程序员巧妙的保证了String的不可变,即:任何对String的写操作,都是返回新的String对象

总结:

综上:
String不可变的原因:
防止被外部改变:
(1) 类设计为final的,确保类不被继承,方法不被覆盖
(2) 内部实现上用的是private final的数组
内部实现上避免改变原字符串:
(1) 任何对String的写操作都不是在原来的字符串上进行的修改,而是创建的新对象
好处:
(1) 线程安全
(2) 在使用String作为HashMap和HashSet的key时,可以确保键值唯一性

1 0