String

  1. 什么是不可变
    java角度来讲就是被final修饰
    假设给字符串赋值为abcd,第二次重新赋值为abcdef,这时候并不是在原内存地址上修改数据,而是重新执行一个新对象,新地址:


    string.png
  2. String 为什么不可变
    源码

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
  • 首先String 类是用final修饰的,这说明String不可继承
  • 其次String类的主力成员字段value是个char[] 数组,而且是用final修饰的,final修饰的字段创建后就不可变。

注意:虽然value确实是final的,不可变,但也只是value这个引用地址不可变,挡不住Array数组是可变的事实

string2.png

也就是说Array变量只是stack上的一个引用,数组的本体结构在堆,String类的value属性用final修饰,只是说stack里的这个叫valu的引用地址不可变,没有说堆里的array本身数据不可变,如下的例子:

final int[] value = {1, 2, 3};
int[] value2 = {11, 22, 33};
value = value2;    //编译报错,value用final修饰,指向的地址不可变

但是下面这个是可以的

final int[] value = {1, 2, 3};
value[0] = 11;  //这时候数组里已经变成{11, 2, 3}了

String不可变的关键不是一个final不可被继承,而是关键在于底层实现,value的修饰符private作用都比final大,再加上类是final的防止继承,避免被破坏。

  1. 不可变的好处
    安全
//不可变的String
    public static String appendStr(String str) {
        str += "abc";
        return str;
    }

    public static StringBuilder appendSb(StringBuilder sb) {
        return sb.append("def");
    }
    
    public static void main(String[] args) {
        //String做参数
        String s1 = new String("abc");
        String s2 = Demo2.appendStr(s1);
        System.out.println("-------------" + s1.toString());
        
        //StringBuilder做参数
        StringBuilder sb = new StringBuilder("abc");
        StringBuilder sb2 = Demo2.appendSb(sb);
        System.out.println("-------------" + sb.toString());
        
    }

结果:

-------------abc
-------------abcdef

可以发现String的值没有改变,这也正是它安全所在。

再看下面这个例子

public static void main(String[] args) {
        HashSet<StringBuilder> hs = new HashSet<StringBuilder>();
        StringBuilder sb1 = new StringBuilder("abc");
        StringBuilder sb2 = new StringBuilder("abc123");
        hs.add(sb1);
        hs.add(sb2);  //这时候HashSet里是{"abc", "abc123"}
        System.out.println(hs.toString());
        
        StringBuilder sb3 = sb1;
        sb3.append("123"); //这时候HashSet里是{"abc123", "abc123"}
        System.out.println(hs.toString());
    }

结果:

[abc, abc123]
[abc123, abc123]

StringBuilder变量sb1和sb2分别指向了堆内存的字面量“abc123”和“abc”,把他们都插入一个HaseSet,到这步没问题。但后面把变量sb3也指向sb1的地址,再改变sb3的值,因为StringBuilder没有不可变的保护。sb3直接在原先“abc”的地址上改,导致sb1的值也变了。这时候HashSet上就出现了两个相等的键值“abc123”。破坏了HashSet键值的唯一性,所以千万不要用可变类型做HashMap和HaseSet键值。
还有就是多个线程同时去读一个资源,是不会引发竟态条件的。只有对资源做写操作才会有危险,用不可变对象不能被写,所以线程安全。

String自带常量池的属性,如下


string3.png

堆内存只会有一块,节省内存空间,提高效率。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 概述 在分析String的源码之前,打算先介绍一点关于JVM的内存分布,这样有助于我们更好地去理解String的设...
    wustor阅读 4,134评论 0 2
  • 1.String是不可变类。 不可变类呢?一般认为:当对象一旦创建完成后,在正常情况下,对象的状态不会因外界的改变...
    时待吾阅读 3,203评论 0 2
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 32,277评论 18 399
  • 电影《漫长的告别》里,马洛是主角,而在我心里,特里·论诺克斯才是主角。 电影里,马洛感到自己被骗,最后杀了特里,可...
    萌之刺刺阅读 2,087评论 0 2
  • 今天和来自无锡的小朋友聊了很多的事情,其中一个可能是未来的小徒。我总在和朋友笑谈我好为人师,其实是有点曲高和寡,很...
    荞爸阅读 1,441评论 0 0

友情链接更多精彩内容