Java String解析

String 为什么不可变:

  • 保存字符串的数组被 final 修饰且为私有的,并且 String 类没有提供/暴露修改这个字符串的方法。
  • String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。

不可变的好处:

  • 可以缓存 hash 值

    因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。

  • 字符串常量池的需要

    字符串池的实现有一个前提条件:String对象是不可变的。因为这样可以保证多个引用可以同时指向字符串池中的同一个对象。如果字符串是可变的,那么一个引用操作改变了对象的值,对其他引用会有影响,这样显然是不合理的。

  • 线程安全

    String 不可变性天生具备线程安全,可以在多个线程中安全地使用(因为没有一个线程可以修改其 内部状态和数据,且其内部状态和数据也不会自行发生改变)。设置成final,可能子类会破坏方法内的行为。

String.intern():

  • String.intern() 方法可以使得所有含相同内容的字符串都共享同一个内存对象,可以节省内存空间。

    JVM 中,存在一个字符串常量池,字符串的值都存放在这个池中。当调用 intern 方法时,如果字符串常量池中已经存在该字符串,那么返回池中的字符串引用;否则将此字符串添加到字符串常量池中,并返回字符串的引用。

  • 可参考:美团技术团队-深入解析 String#intern

String s1 = new String(“123”)和String s2 = "123"的区别

  • 两个语句都会去字符常量池中检查是否已经存在"123",如果有则直接返回池中对象引用地址,没有则会在常量池中创建"123"对象。

  • 不同的是,String s1 = new String(“123”)还会在堆里创建一个"123"字符串对象的实例,返回堆中对象的地址。

    【采用字面值创建一个字符串对象】

    JVM首先会去字符串池中查找是否存在"aaa"这个对象,如果不存在,则在字符串池中创建"aaa"这个对象,然后将池中"aaa"这个对象的引用地址返回给字符串常量str,这样str会指向池中"aaa"这个字符串对象;如果存在,则不创建任何对象,直接将池中"aaa"这个对象的地址返回,赋给字符串常量。

    【采用new关键字创建一个字符串对象】

    JVM首先在字符串常量池中查找有没有"aaa"这个字符串对象,如果有,则不在池中再去创建"aaa"这个对象了,直接在堆中创建一个"aaa"字符串对象,然后将堆中的这个"aaa"对象的地址返回赋给引用str1,这样,str1就指向了堆中创建的这个"aaa"字符串对象;如果没有,则首先在字符串常量池池中创建一个"aaa"字符串对象,然后再在堆中创建一个"aaa"字符串对象,然后将堆中这个"aaa"字符串对象的地址返回赋给str1引用,这样,str1指向了堆中创建的这个"aaa"字符串对象。

String s1 = new String(“123”)创建了几个对象

一个或两个。如果字符串常量池已经有"123",则是一个,否则,两个:

  • 一个是字符串"123"所对应的字符串常量池中的实例
  • 另一个是通过new String创建并初始化的,在堆中,内容指向字符串常量池
  • String s1 = "a";:StringTable[“a”]。在常量池中查找有没有"a" 这个对象,如果有,就让s1指向那个"a".如果没有,在常量池中新建一个“aaa”对象,并让s1指向在常量池中新建的对象"a"。
  • String s2 = "b";// StringTable[“a”, “b”]。
  • String s3 = 'ab';// StringTable[“a”, “b”, “ab”]。
  • String s4 = s1 + s2;// 两变量拼接,在堆里。= new StringBuilder().append(“a”).append(“b”).toString() -> new String(“ab”);
  • String s5 = "a" + "b"; // “ab”。两常量拼接,在串池中。javac在编译期间优化,结果已经在编译期确定为ab。
  • String s6 = s4.intern()//常量池中有对象则返回常量池中的对象,没有则s4入池。
  • sout(s3 == s4); // false:s4是在堆里new了一个对象,s3在在串池 String table 中。
  • sout(s3 == s5); // true,s5引用常量池中已有的对象。
  • sout(s3 == s6);// true
  • String a2 = s1 + "b";//= new StringBuilder().append(s1).append(“b”).toString();new String(“ab”)。
  • String a1 = new String("a");//在堆中建立的对象"a" ,在栈中创建堆中"a" 对象的内存地址。
  • String x2 = new String("c") + new String("d"); // new String(“cd”)
  • String x1 = "cd"; // “cd”
  • x2.intern(); //入池失败。
  • sout(x1 == x2); //false

String、StringBuffer 和 StringBuilder 的区别:

  • String:String类使用final修饰的,被创建后不能被修改,任何对String的修改都会引发新的String对象的生成
  • StringBuffer:和String类似,但是值可以被修改的,使用synchronized保证线程的安全
  • StringBuilder:String的非线程安全版本,性能上更高一些

性能:String < StringBuffer < StringBuilder

  • String 类中使用 final 关键字修饰字符数组来保存字符串,private final byte[] value,所以String对象是不可变的。
  • StringBuilder 与 StringBuffer 都继承⾃ AbstractStringBuilder 类,在 AbstractStringBuilder 中 也是使用字符数组保存字符串 char[] value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
  • StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Guanam_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值