1、String
String对象是不可变的,即一旦一个 String 对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。
那么我们new一个String对象,比如
String a = new String("A") String a2 = new String("A")
和直接创建一个字符串,比如
String b = "A"
这两种方法有什么区别呢。
对于第一种方法,当new一个String对象时会在栈空间开辟一块空间存放引用a,在堆空间中开辟一块空间存放一个新建的"A"对象,栈空间的引用指向堆空间中的新对象,如果存在两个相同的字符串,则会分配不同的内存地址,调用intern方法,intern方法会从字符串常量池中查询当前字符串是否存在,如果存在,就直接返回当前字符串,如果不存在就会将当前字符串放入常量池中,之后再返回;对于第二种方法,直接创建的时候,会先查看字符串常量池有没有相同的字符串,如果没有"A"会存放在字符串常量池,如果有引用则直接指向常量池中的地址。
说到这里关于字符串常量池,究竟是在方法区中,还是在堆中可以看一下这篇文章
字符串常量池和运行时常量池是在堆还是在方法区?
2、StringBuilder
Sting对象是不可变的,那么关于字符串的拼接是怎么实现的呢。
public static void main(String[] args) { String b = "cd"; String s = "ab" + b + "ef"; System.out.println(s); //abcdef }
编译器会创建一个StringBuilder对象,用以构造最终的String,并为每个字符串调用一次StringBuilder的append()方法,总计三次。最终调用toString()生成结果。
StringBuilder在进行append连接字符串的时候并不是用String存储,而是存放到一个名为value的char数组当中,Sring中的value由final修饰,而另外两种可变字符串对象的value数组是可以扩容的,这样就不需要不停创建对象了。
数组默认的初始长度是16,扩容系数是value.length * 2 + 2,也即 (value.length << 1) + 2,而且只有当append之后的数据长度大于value.length时才会扩容一次,并不是每次连接都会进行扩容操作。
3、StringBuffer
StringBuilder 和 StringBuffer 功能基本相似,方法也差不多。不同的是,StringBuffer的所有公开方法都是synchronized修饰的,而StringBuilder并没有synchronized修饰。StringBuffer 是线程安全的,而 StringBuilder 则没有实现线程安全功能,所以性能略高。因此在通常情况下,如果需要创建一个内容可变的字符串对象,则应该优先考虑使用 StringBuilder 类。
————————————————
原文链接:https://blog.csdn.net/weixin_44153131/article/details/126187193