字符串String 可以说是最常用的类
一般问题:
String类的不可变怎么做到的?为什么?
首先String类是由final修饰的,不可被继承。
其次String类的char[]数组 是被私有化的,被private修饰,且未提供get()/set()方法。
原因:
1. String类使用太频繁,所有为了搭配字符串常量池使用。
2. 为了线程安全,在多线程环境下 final 可以保证安全
3. 为了同一个String类的hashcode唯一,只需要计算一次就缓存了hashcode,方便做为map的key。
String和StringBuilder和StringBuffer区别
不同点
4. String类的char[]是私有的,其它俩都是可被修改的。
5. String类的每个方法都是返回一共新的String。
6. StringBuilder是单线程下的,而StringBuffer做了多线程安全性的设计
7. 因为StringBuilder和StringBuffer都是可修改的。所以会有扩容机制。
8. StringBuilder扩容机制:如果当前字符+新增字符大于数组大小扩容,扩容为当前数组*2 + 2. 直接new一个新数组,然后System.arraycopy
相同点
都是不可被继承的类。
深入理解
主要就是字符串常量池的问题
一些代码判断
String a = "123";//在字符串常量池创建一个对象
String b = "123";//直接从字符串常量池中拿
System.out.println(a == b);//true
String b = new String("123");//在字符串常量池创建一个对象,另外在堆创建一个对象
String a = "123";//获取字符串常量池中的
String c = b.intern();//通过b获取字符串常量池中的
System.out.println(a == b);//false
System.out.println(a == c);//true
这俩个示例,主要是说明,字符串常量池的作用。如果字符串常量池有某个对象,之后通过字面量的方式获取的都是同一个。
代码示例2
String a = new String("123") + new String("321");
String b = "123321";
String c = a.intern();
System.out.println(a == b);//false
System.out.println(c == b);//true
a 会在堆上创建 "123" "321" "123321" 但是因为没有字面量,所以字符串常量池中并无"123321"
String a = new String("123") + new String("321");
String c = a.intern();
String b = "123321";
System.out.println(a == b);//true
System.out.println(c == b);//true
a == b 是 调用了a.intern() 之后不会再字符串常量池创建一个新的对象,而是直接记录了堆上"123321"的引用。 这就导致 a == b 因为是同一个引用。
原因:是因为堆内存的改变。 在1.6之前,字符串常量池在方法区由永久代实现。1.7被移动到了堆里面。所以在一个堆中就可以直接记录指针,不必浪费资源。
总结:主要就是字符串常量池位置的改变,导致字符串常量池创建对象的规则变化。如果堆中已经存在对象,直接保存指向对象的指针,而不新创建一个对象。