1、字符串的创建
字面量直接赋值
先去常量池中找有没有这个字符串,有就直接指向常量池中该字符串;没有直接创建
String str1 = "hello" ;
String str2 = "hello" ;
String str3 = "world";
new 创建
new一个字符串时,做了两件事。首先在堆中生成了该字符串对象,然后去看常量池中有没有该字符串,如果有就不管了,没有就往常量池中添加一个
String str1 = new String("hello");
String str2 = new String("hello");
String str3 = new String("world");
在堆中创建三个对象,常量池中两个共5个。
小结:两种创建方式一个在堆中,一个在常量池中。
2、字符串拼接
(1)常量拼接
String str = "hello"+"world";
//两边都是常量,在编译阶段会自动拼接变成
String str = "helloworld";
直接去常量池找 “helloworld”,有就直接指向它,没有就在常量池创建。
(2)有final的拼接
final String str1 = "hello";
final String str2 = "world";
String str3 = str1 + str2;
因为final 修饰的变量是常量,所以在编译期直接会变成
String str3 = "hello" + "world";
再根据常量拼接最终变成
String str3 = "hello" + "world";
3、变量和常量拼接
变量和常量拼接的时候会调用StringBuilder
的append
方法生成新对象。
情况1:
String str1 = "hello";
String str2 = str1 + "world";
str1在常量池中,world
在常量池中,调用StringBuilder的append方法在堆中生成新对象
helloworld
,str2指向堆中的helloworld
对象。共生成三个对象,常量池中有 hello
和 world
,堆中有 helloworld
.
情况2:
String str1 = new String("hello");
String str2 = str1 + "world";
先在堆中创建一个 hello
,再把 hello
添加到常量池;然后把 world
添加到常量池,拼接的时候,会在堆中创建一个 helloworld
。共4个对象,堆中 hello
、helloworld
常量池中 hello world
。
4、变量和变量拼接
变量和变量拼接底层也会调用 StringBuilder
的append
方法生成新对象。
情况一:
String str1 = "hello";
String str2 = "world";
String str3 = str1 + str2;
这段代码,首先会有一个"hello"在常量池中,然后有个"world"在常量池,第三行代码会调用append方法,在堆中生成一个"helloworld"。所以总共有3个对象。
情况二:
String str1 = "hello";
String str2 = new String("world");
String str3 = str1 + str2;
这段代码,首先在常量池中搞一个"hello",然后在堆中new一个"world",同时把"world"也搞到常量池中去,第三步拼接就会在堆中生成一个"helloworld"。所以总共有4个对象。
情况三:
String str1 = new String("hello");
String str2 = new String("world");
String str3 = str1 + str2;
第一行代码创建了两个对象,堆中一个常量池一个,第二行代码也是一样,第三行代码就在堆中创建了一个"helloworld"。所以总共创建了5个对象。
5、intern方法(jdk8)
例一:
String str1 = new String("str")+new String("01");
str1.intern();
String str2 = "str01";
System.out.println(str2==str1); //true
在堆中创建一个str,同时添加到常量池中;再创建一个01,同事添加到常量池中,拼接。
在堆中生成对象str01
, str1.intern()
把它的地址值添加到常量池中去;执行String str2 = str01
时候,去常量池找 str01
,发现常量池中有 x001
地址值,为对应的要找的 str01
。最终str1
指向地址值为 x001
对象,str2 指向地址值为 x001
对象。
例二:
String str1 = new String("str")+new String("01");
String str2 = "str01";
str1.intern();
System.out.println(str2==str1); //false
执行第二步时,往常量池中找str01,发现没有,那就添加一个;再执行str1.intern()时,发现常量池中有str01了,就不进行地址值的拷贝了。最终str1指向堆中的str01,str2指向常量池的str01,所以结果是false。
例三:
String str1 = new String("str")+new String("01");
String str2 = "str01";
str1 = str1.intern();
System.out.println(str2==str1);//true
就是把例二的str1.intern()改成str1 = str1.intern(),看看会有什么变化:
本来str1是指向堆中的str01的,然后重新将str1.intern()赋给str1,str1.intern()是指向常量池的,赋给str1后,所以此时str1也是指向常量池。所以结果就是true。