共计 2837 个字符,预计需要花费 8 分钟才能阅读完成。
String 根本个性
String:字符串,应用一对 ”” 引起来示意。
String 申明为 final 的。,不可被继承
String 实现了 Serializeable 接口:示意字符串反对序列化的。实现了 Comparable 接口:示意 String 能够比拟大小
String 在 jdk8 及以前外部定义了 final char[] value 用于存储字符串数据。jdk9 时改为 byte[]
String 存储构造变更
String 应用 byte[] 加上编码标记,节约了内存空间
StringBuffer 和 StringBuilder 都也有相应的改变。
String 的根本个性
String 代表不可变的字符序列,即不可变性
- 当对字符串从新赋值时,须要重写指定内存区域赋值,不能应用原有的 value 进行赋值。
- 当对现有的字符串进行间断操作时,也须要从新指定内存区域赋值,不能应用原有的 value 进行赋值
- 当调用 String 的 replace() 办法批改指定字符或字符串时,也须要从新指定内存区域赋值,不能应用原有的 value 进行赋值
- 通过字面量的形式(区别于 new)给一个字符串赋值,此时的字符串值申明在字符串常量池中。
字符串常量池中是不会存储雷同内容的字符串。
String 的 String Pool 是一个固定大小的 Hashtable,默认值大小长度是 1009,如果放进 String Pool 的 String 十分多,就会造成 Hash 抵触,从而导致链表会很长,而链表长了后会造成的影响就是当调用 String.intern 时性能会大幅降落。
应用 -XX:StringTableSize 可设置 StringTable 的长度
在 jdk6 中 StringTable 是固定的,就是 1009 的长度,所以如果常量池中的字符串过多就会导致效率降落地很快。StringTableSize 设置没有要求
在 jdk7 中,StringTable 的长度默认值是 60012,jdk8 开始,1009 是能够设置的最小值。
String 的内存调配
Java6 及之前,字符串常量池寄存在永恒代。
Java7 中 Oracle 的工程师对字符串池的逻辑做了很大的变动,行将字符串常量池的地位调整到 Java 堆内。
- 所有的字符串都保留在堆(Heap)中,就像其余一般对象一样,这样能够让你在进行调优利用时仅需调整堆大小就能够了。
- 字符串常量池概念本来应用得比拟多,然而这个改变使得咱们有足够的理由让咱们重新考虑在 java7 中应用 String.intern()。
Java8 元空间,字符串常量在堆。
String 的基本操作
Java 语言标准里要求完全相同的字符串字面量,应该蕴含同样的 Unicode 字符序列(蕴含同一份码点序列的常量),并且必须是指向同一个 String 类实例。
字符串拼接操作
- 常量与常量的拼接后果是在常量池,原理是编译期优化
- 常量池中不会存在雷同内容的常量
- 只有其中有一个是变量,后果就在堆中。变量拼接的原理是 StringBuilder
- 如果拼接的后果调用 intern()办法,则被动将常量池中还没有的字符串对象放入池中,并返回对象地址。
@Test
public void test3(){
String s1 = "a";
String s2 = "b";
String s3 = "ab";
/*
如下的 s1 + s2 的执行细节:( 变量 s 是我长期定义的)① StringBuilder s = new StringBuilder();
② s.append("a")
③ s.append("b")
④ s.toString() --> 约等于 new String("ab")
补充:在 jdk5.0 之后应用的是 StringBuilder, 在 jdk5.0 之前应用的是 StringBuffer
*/
/*
1. 字符串拼接操作不肯定应用的是 StringBuilder!
如果拼接符号左右两边都是字符串常量或常量援用,则依然应用编译期优化,即非 StringBuilder 的形式。2. 针对于 final 润饰类、办法、根本数据类型、援用数据类型的量的构造时,能应用上 final 的时候倡议应用上。*/
/**
* 如何保障变量 s 指向的是字符串常量池中的数据呢?* 有两种形式:* 形式一:String s = "shkstart";// 字面量定义的形式
* 形式二:调用 intern()
* String s = new String("shkstart").intern();
* String s = new StringBuilder("shkstart").toString().intern();
*
*/
public class StringIntern {public static void main(String[] args) {String s = new String("1");
s.intern();// 调用此办法之前,字符串常量池中曾经存在了 "1"
String s2 = "1";
System.out.println(s == s2);//jdk6:false jdk7/8:false
String s3 = new String("1") + new String("1");//s3 变量记录的地址为:new String("11")
// 执行完上一行代码当前,字符串常量池中,是否存在 "11" 呢?答案:不存在!!s3.intern();// 在字符串常量池中生成 "11"。如何了解:jdk6: 创立了一个新的对象 "11", 也就有新的地址。// jdk7: 此时常量中并没有创立 "11", 而是创立一个指向堆空间中 new String("11") 的地址
String s4 = "11";//s4 变量记录的地址:应用的是上一行代码代码执行时,在常量池中生成的 "11" 的地址
System.out.println(s3 == s4);//jdk6:false jdk7/8:true
}
}
public class StringIntern1 {public static void main(String[] args) {
//StringIntern.java 中练习的拓展:String s3 = new String("1") + new String("1");//new String("11")
// 执行完上一行代码当前,字符串常量池中,是否存在 "11" 呢?答案:不存在!!String s4 = "11";// 在字符串常量池中生成对象 "11"
String s5 = s3.intern();
System.out.println(s3 == s4);//false
System.out.println(s5 == s4);//true
}
}
总结 String 的 intern()的应用
Jdk1.6 中,将这个字符串对象尝试放入串池
- 如果串池中有,则并不会放入,返回已有的串池中的对象的对象地址
- 如果没有,会把此对象赋值一份,放入串池,并返回串池中的对象地址
jdk1.7 起,将这个字符串对象地址尝试放入串池
- 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
- 如果没有,则会把对象的援用地址复制一份,放入串池,并返回串池中 的援用地址。
参考资料:宋红康 JVM 教程