1、看到的一篇关于String类的讨论与思考
https://bbs.csdn.net/topics/370250506?page=2
public class Test {
private static void test(String s) {
s = s.concat("123");
}
private static void test2(StringBuilder sb) {
sb.append("123");
}
public static void main(String[] args) {
String t = "hello";
test(t);
System.out.println(t);
StringBuilder sb = new StringBuilder("hello");
test2(sb);
System.out.println(sb.toString());
}
}
输出结果:
hello
hello123
该楼主的解释为:
我按照自己的理解,画了下图:
后来看到对该段代码的反驳:
public class Test {
private static void test(String s) {
// s = s.concat("123");
s = null;
}
private static void test2(StringBuilder sb) {
//sb.append("123");
sb = null;
}
public static void main(String[] args) {
String t = "hello";
test(t);
System.out.println(t);
StringBuilder tb = new StringBuilder("hello");
test2(tb);
System.out.println(sb.toString());
}
}
输出结果:
hello
hello
2、别再问我 new 字符串创建了几个对象了!我来证明给你看!
https://juejin.cn/post/6844904129752465416
我的理解:
因为String中是使用char[]数组来存储字符串的,且该数组变量被定义为final,所以被放置到字符串常量池中。(这个理解貌似是错误的。。https://juejin.cn/post/6844904191324864520#comment)
而StringBuilder中用来存储字符串的char[]数组,并未定义为final类型,所以StringBuilder中声明的字符串并不会被放置到常量池中,且其字符串的值是可以被更改的,相当于char[] 数组可以被更改。
3、字符串常量池
https://juejin.cn/post/6855574934698229768
4、StringBuffer与StringBuilder
https://zhuanlan.zhihu.com/p/76303946
String对象一旦创建后,String中的内容是不能够改变的,每次改变String都会创建新的对象。而StringBuffer和StringBuilder的内容却是能够动态改变的。StringBuffer和StringBuilder的区别在于StringBuffer是线程安全的(StringBuffer中的绝大部分都加了同步关键字),而StringBuilder是线程不安全的。因为StringBuilder是线程不安全的,所以其运行效率会更高。如果一个字符串是在方法里面定义的话,这种情况仅仅可能有一个线程访问它,不存在不安全的因素,这时推荐使用StringBuilder。如果一个实例变量是在类里面定义的,并且在多线程下会访问到,这时最好使用StringBuffer。
5、从字节码和 JVM 内存结构的角度来看一道面试题
https://juejin.cn/post/6844904191324864520
6、关于final的例子
final只对引用的“值”(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。
final StringBuffer a = new StringBuffer(“111”);
final StringBuffer b = new StringBuffer(“222”);
a = b; //编译不通过
- final不可变
- final是针对stack而言的,immutable其实更多的是heap里面的不可变,String之所以这么设计,是因为String本身提供的方法已经很完备了,已经没有多态设计的空间了,所以final
- 之所以有字符串常量池,是因为String在java中是不可变的。
- 在Java程序中,有很多东西是永恒的,不会在运行过程中发生变化。比如一个类的名字、一个类
7、String 对象的加号“+”连接的内存表示
例1:
String str1 = new String("abc");
String str2 = "abc";
String str3 = "a" + "b" + "c";
System.out.println(str1.intern() == str2); //输出true
System.out.println(str2 == str3); //输出true
字符串常量是编译时候确定的,编译完成,生成class文件,那就不会再变了。
在定义str3的时候,在编译时候,编译器会将字符串常量直接放在一起,然后查找常量池里面,有没有对应的字符串。 所以str2 和 str3 是相等的,也就是指向相同的内存区域。
例2:
String str1 = "abc";
String str2 = "ab";
String str4 = str2 + "c";
System.out.println(str1 == str4); //输出false
在定义str4的时候,是用str2+“c“的形式。这个时候str4的值不是编译时候能确定的,它已经不再会往常量池存放,是一个字符串变量。
这个时候,底层是通过StringBuffer的append方法,最终返回new的string。所以str4的地址只的不是常量池区域的地址,而是指向堆内存中的区域。