目录
(五)StringBuilder类和StringBuffer类
(3) String StringBuilder StringBuffer区别
(4)StringBuilder和StringBuffer的方法
(三)字符串常量池
(1)常量池思想
"池化"思路:是程序设计中的常用思路,节省内存,提高效率。字符串字面量经常会被重复使用,因此放入常量池,当再次使用该内容时,省去了创建对象,开辟内存的时间,直接复用已有对象。
(2)直接使用字符串常量进行赋值
存储一系列的字符串对象,当字符串常量第一次出现,且常量池中没有该值字符串对象,就会将该对象置入常量池。
String str="hello";
String str1="hello";str指向的'"hello"第一次出现,JVM产生该对象并置入常量池。
此时常量池已经有了"hello"的字符对象,JVM直接将池中对象地址赋值给str1,不在产生新对象。
(3)通过new创建对象
若采用直接赋值的方式申明字符串引用,若常量中存在该对象,则直接返回常量池的字符对象,不会创建新对象。
String str=new String("hello");
这行代码背后的逻辑:每一个字符串字面量都是一个字符串常量,也就是一个字符串对象 。
在堆中开辟一个String类对象的地址空间,并在字符串常量池中开辟一段地址空间,将字面量"hello"放入字符串常量池(上文已经说过字符串字面量经常被重复使用,因此放入常量池)。str1、str2都是直接调用常量池的"hello"。
补充:String str;这个str就是字符串。str="hello"这个就是用字符串字面量初始化字符串str。
(4)intern()方法
关于intern方法,字符串的intern方法是一个本地方法。尝试当前字符串对象置入常量池,若常量池没有该值,则将当前对象置入常量池。若常量池已经包含了该值的对象,则不会将对象置入常量池,intern方法直接返回常量池对应的对象。
实例演练:
public class StringTheory {
public static void main(String[] args) {
char []ch=new char[]{'a','b','c'};//堆中开辟空间存放'a','b','c'(0x400)
String s1=new String(ch);//字符数组不是字符串!!!此处ch不是字面量不会放入字符串常量池
s1.intern();//此时常量池中没有"abc"这个字符串对象,就会将s1指向的字符串放入常量池(0x100)
String s2="abc";//此时常量池中有"abc"(0x100)
//true
System.out.println(s1==s2);//(0x100==0x100)
}
}
关于intern()方法是否接收带来的问题:0x100、0x300表示自定义的对象地址
public class StringTheory {
public static void main(String[] args) {
String str="hello";//在字符串常量池中开辟空间,将"hello"(0X100)置入常量池
String str1="hello";//str1直接引用常量池中的"hello"(0x100)
String str2=new String("hello");//常量池已经包含了"hello"(0x100),则不会将对象(0x300)置入常量池
str2=str2.intern();//intern方法直接返回常量池对应的对象(0x100),,用str2接收intern的返回值。指向常量池中的"hello"(0x100)
System.out.println(str==str1);//true (0x100==0x100)
System.out.println(str==str2);//true (0x100==0x100)
}
}
public class StringTheory {
public static void main(String[] args) {
String str="hello";//在字符串常量池中开辟空间,将"hello"(0X100)置入常量池
String str1="hello";//str1直接引用常量池中的"hello"(0x100)
String str2=new String("hello");//常量池已经包含了"hello"(0x100),则不会将对象(0x300)置入常量池
str2.intern();//intern方法直接返回常量池对应的对象(0x100),但str2(0x300)没有接收
System.out.println(str==str1);//true (0x100==0x100)
System.out.println(str==str2);//flase (0x100==0x300)
}
}
(四)字符串的不可变性
(1)定义
当一个字符串的对象产生之后,他的内容就无法修改,这称之为字符串对象的不可变性
//不可变的变量,就叫常量
final int a=10;
//这里指的是不可变性是指内容不可变,而不是字符串的引用
String str="hello";
str+="world";//+表示字符串的拼接,字符串的引用(指向)发生改变
System.out.println(str);
(2)设置String不可变性的原因
1.方便实现字符串对象池,如果String可变,那么对象池就需要考虑写时拷贝的问题了
2.不可变对象是线程安全的
3.不可变对象更方便缓存hash code,作为key时可以更高效的保存到HashMap
(3)经典面试题
怎么理解字符串不可变,如何做到内容不可修改?
public final class String
private final char value[];//value字符数组是真正保存字符串内容的
final修饰引用的数据类型的变量,不能改变的是这个引用的指向不变,至于引用的对象内部如何变化,是可以的。因此字符串对象内容不可变与final修饰没啥关系,final只能确保value的指向不变!字符串对象的内容不可变,本质在于private封装,出了String这个类,外部是无法接触到value的,要想操作这个属性只能使用该类提供的getter()和setter()方法。
(补充:被final修饰的变量天然线程安全)
(五)StringBuilder类和StringBuffer类
(1)String->StringBuilder对象转换
a.通过StringBuilder类的构造方法
b.通过StringBuilder类的append()方法,调用append方法在当前对象的后面追加任何数据类型
(javac编译器其实也会将String+=操作优化为StringBuilder类的append操作!)
(2)StringBuilder->String对象转换
a.调用toString()方法
b.String ret =sb1.toString()
代码实例:
public class StringBuilderTest {
public static void main(String[] args) {
String str="abc";
//String类变为StringBuilder类
//方法一
StringBuilder sb=new StringBuilder(str);
//方法二
StringBuilder sb1=new StringBuilder();
sb1.append(str);
//StringBuilder类变为String
sb1.append("123");//append类似于+=
String ret =sb1.toString();//将StringBuilder类转换成String类
System.out.println(ret);//abc123
}
}
(3) String StringBuilder StringBuffer区别
a.String的内容不无法修改,StringBuilder StringBuffer的对象可以修改,因此我们一般在字符串对象频繁修改的时候使用StringBuilder或StringBuffer类,后两个内容可变,因此提供的方法都是在同一个对象上进行修改。
b.StringBuilder StringBuffer的用法完全一致,StringBuffer采用同步处理,线程安全,效率低。
c.StringBuilder采用异步处理,线程不安全,效率高,开发中若不考虑线程安全问题,一般推荐使用StringBuilder类
(4)StringBuilder和StringBuffer的方法
StringBuilder
StringBuffer