内容部分参考链接:(写得更加详细和全面)
1.String 是 final 修饰的.
2.jdk8 及以前版本 String 是 char 数组存储数据, jdk9 开始 String 是 byte 数组存储数据, 以及有 coder 字段控制字符串编码.
注: 因为 String 存储单位也是 final 修饰的, 所以 String 是不可变的.
3. 不可变的好处
1). 可以缓存 hash 值, 因为不可变性使得 hash 值也不可变
2).String pool 字符常量池需要
3). 安全性, 用于网络参数
4). 线程安全
添加字符串到常量池并返回其引用的两种方式 "123".intern(); 和 String a = "123";
至多创建一个对象, 就是如果字符串常量池里面存在 "123" 的字符串, 直接返回它的引用地址.
new String("123");
至少创建一个对象, 至多创建两个对象.
一个是编译放入 String pool 里面指向 "123" 字符串, 一个是 new 的方式放在堆里的对象
intern() 函数
在 1.6 中, intern 的处理是 先判断字符串常量是否在字符串常量池中, 如果存在直接返回该常量, 如果没有找到, 则将该字符串常量加入到字符串常量区, 也就是在字符串常量区建立该常量;
在 1.7 中, intern 的处理是 先判断字符串常量是否在字符串常量池中, 如果存在直接返回该常量, 如果没有找到, 说明该字符串常量在堆中, 则处理是把堆区该对象的引用加入到字符串常量池中, 以后别人拿到的是该字符串常量的引用, 实际存在堆中;
测试栗子
- String str = new String("11");// 创建两个对象, 返回堆中的对象引用
- String str1 = str.intern();// 常量池已有, 返回常量池对象引用
- String str2 = "11";// 同 Str1
- String str3 = "11";// 同 Str1
- System.out.println(str==str1);//false
- System.out.println(str==str2);//false
- System.out.println(str1==str2);//true
- System.out.println(str2==str3);//true
- // 实际是通过 StringBuffer 的 append 拼接之后调用 toString,new 出来的, 所以在堆中. 和直接 new String("22") 不同.
- //"2" + new String("2"); 也是如此
- String t2 = new String("2") + new String("2");
- //1.7 及以后先看常量池有没有, 再看堆中有没有, 这时堆中有, 讲堆的引用放入常量池.
- // 说的是 1.6 是直接在常量池再创建 (未实操所以占时不知道)
- String t3 = t2.intern();// 这个地方 1.6 和 1.7 放入常量池是不同的
- String t4 = "22";
- System.out.println(t2 == t3);//jdk 不同结果不同 1.7 true
- System.out.println(t3 == t4);//true
- String t5 = "33" + "33";//jvm 编译优化, 相当于 "1111"
- String t6 = "3333";
- String t7 = "33";
- String t8 = "33";
- System.out.println(t5 == t6);//true
- System.out.println("33" + "33"==t6);//true jvm 编译的时候遇到字符双引号会优化, 即 "11"+"11" 相当于在编译阶段的 "1111"
- System.out.println(t7 + t8 == t6);//false(应该和上述 new 两个字符串对象拼接底层实现一致, 上述也是参考大佬的理论)
来源: http://www.bubuko.com/infodetail-3462464.html