String是只读字符串,它并不是基本数据类型,而是一个引用类型的对象。从底层源码来看是一个final
修饰的类,表明类不能被继承,属性value是String类储存数据的char字符数组(JDK8),(JDK11改成了byte字节数组),同时被final
修饰,即所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成新的String对象。 源码可以看出。
问题一
引用数据类型使用==
判断的是对象引用是否是同一个引用,判断字符串相等要用equals
方法
public static void main(String[] args) {
String a = "taobao";
String b = "tao"+"bao";
String c = new String("taobao");
System.out.println(a == b); // true
System.out.println(a == c); // false
System.out.println(b == c); // false
}
String a = “taobao”,String a = “tao”+“bao”,String a = new String(“taobao”)之间的区别?
String a = "taobao";执行过程是:
到方法区的常量池中寻找"taobao",
1、如果存在,则直接将"taobao"对象的地址传递给a;
2、如果不存在,则在常量池中创建"taobao",然后将地址传递给a.
String b = "tao"+"bao";这句话执行过程是:
1、在hotspot虚拟机中 编译"tao"+"bao"将直接优化成"taobao"
2、然后和String a = "taobao";执行过程一样,所以未true
String a = new String("taobao");执行过程是:
首先在堆内存中创建对象"taobao",然后在常量池中寻找"taobao",
1、如果存在,把堆内存地址传递给a;
2、如果不存在,在常量池中创建"taobao"对象,然后把堆内存地址传递给a;
总结: 直接赋值形式的新建string对象是从常量池中拿数据;最多创建一个string对象,虽少创建0个string对象;new形式新建string对象无论怎样会首先在堆内存中创建一个string对象,然后确保常量池中也有一个相同内容的string对象;最多创建2个,最少创建1个。
由于直接赋值方式可能节约内存,推荐使用该方式。
问题二
重新赋值st字符串变量,是否发送改变?
public static void main(String[] args) {
String st = "hello";
st = "world";
System.out.println(st); // world
}
问题三
public class Question_6 {
private static final String Message = "taobao";
public static void main(String[] args) {
String a = "tao"+"bao";
String b = "tao";
String c = "bao";
System.out.println(a == Message); // true
System.out.println((b+c) == Message); // false
}
}
b+c不会优化,因为不知道在之前的步骤中bc会不会发生改变,而针对b+c则是用语法糖,新建一个StringBuilder来处理,所以在堆中产生对象,故和Message的地址不相等
问题四
public class Question_6 {
private static final String Message = "taobao";
public static void main(String[] args) {
String a = "tao"+"bao";
final String b = "tao";
final String c = "bao";
System.out.println(a == Message); // true
System.out.println((b+c) == Message); // true
}
}
final
关键字修饰的局部字符串变量相连接时,Hotspot
虚拟机在编译时会优化成"taobao"
,因为被final
修饰的变量是不可变的常量。如果在字符串相加中,只要有一个是非final
类型的变量,编译器就不会优化,因为这样的变量可能发生改变,所以编译器不可能将这样的变量替换成常量。例如将变量b的final
去掉,结果(b+c) == Message
变成了false
。这也就意味着会用到StringBuilder对象,在堆内存中产生新对象。