文章目录
前置知识
String str = "xxx"
到底new了几个对象?new String("xxx")
到底new了几个对象?new String("xxx") + new String("xx")
到底new了几个对象?string.intern()
在不同jdk版本的变化?
String str = "xxx"
到底new了几个对象?
- 下面通过字节码的角度来观察,
String str = "xxx"
的字节码如下
0 ldc #2 <xxx>
2 astore_1
3 return
答案:String str = "xxx"
一共创建了一个对象,返回的是字符串常量池中的地址,其中ldc
指令会在字符串常量池中创建一个对象
思考?
new String(char[] chars)
或new String(byte[] bytes)
创建了几个对象?;
答案:只创建了一个对象,并没有去调用ldc
指令,即在常量池中不存在这个字符串对象,字节码如下:
public static void main(String[] args) {
char[] chars = new char[]{'x','x','x'};
String str = new String(chars);
}
0 iconst_3
1 newarray 5 (char)
3 dup
4 iconst_0
5 bipush 120
7 castore
8 dup
9 iconst_1
10 bipush 120
12 castore
13 dup
14 iconst_2
15 bipush 120
17 castore
18 astore_1
19 new #2 <java/lang/String>
22 dup
23 aload_1
24 invokespecial #3 <java/lang/String.<init>>
27 astore_2
28 return
new String(“xxx”)到底new了几个对象?
public static void main(String[] args) {
String str = new String("xxx");
}
- 反编译后的字节码如下
0 new #2 <java/lang/String>
3 dup
4 ldc #3 <xxx>
6 invokespecial #4 <java/lang/String.<init>>
9 astore_1
10 return
答案:new String(“xxx”)一共创建了2个对象,一个对象在堆中,一个在常量池中,并且返回的是堆中的地址
new String("ab") + new String("c")
到底new了几个对象?
public static void main(String[] args) {
String str = new String("ab") + new String("c");
}
- 反编译后的字节码如下
0 new #2 <java/lang/StringBuilder> // 第一个
3 dup
4 invokespecial #3 <java/lang/StringBuilder.<init>>
7 new #4 <java/lang/String> // 第二个
10 dup
11 ldc #5 <ab> // 第三个
13 invokespecial #6 <java/lang/String.<init>>
16 invokevirtual #7 <java/lang/StringBuilder.append>
19 new #4 <java/lang/String> // 第四个
22 dup
23 ldc #8 <c> // 第五个
25 invokespecial #6 <java/lang/String.<init>>
28 invokevirtual #7 <java/lang/StringBuilder.append>
31 invokevirtual #9 <java/lang/StringBuilder.toString> // 第六个
34 astore_1
35 return
答案:new String("xxx") + new String("xx")
一共创建了6个对象,凡是涉及到变量的拼接操作,都会创建一个StringBuilder对象,转为String调用的是StringBuilder.toString()方法,返回的是StringBuilder.toString()下new String()
堆中的地址
思考?
字符串对象"abc"在字符串常量池中吗?
答案:
- 字符串"abc"不存在字符串常量池中,查看StringBuilder.toString()方法源码如下,因为new String(char[] chars),上面有讲到
/**
* The value is used for character storage.
*/
char[] value;
// ...
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
string.intern()
在不同jdk版本的变化?
string.intern()方法的作用
-
intern()是String中的静态方法,调用该方法会先从字符串常量池中判断该字符串是否存在,若存在则直接返回字符串常量池中的地址,若不存在则在字符串常量池中创建这个字符串对象,然后再返回。
-
从jdk7开始,字符串常量池从方法区移动到堆中,
-
jdk6及以前:调用intern方法符合上面的概述。
-
jdk7及以后:在调用intern方法时,若在字符串常量池中不存在这个字符串对象,但是在堆中存在这个字符串对象,这个时候并不会在字符串常量池中创建这个对象,而是直接引用这堆中这个字符串对象的地址,以达到节省空间的效果。,
public static void main(String[] args) {
// 以字面量的方式定义字符串,会在字符串常量池中创建对象
String abc = new StringBuilder("abc").toString();
// StringBuilder.toString(): 不会在字符串常量池中创建"def"字符串对象,但在堆中创建了这个"def"字符串对象
String def = new StringBuilder("de").append("f").toString();
// jdk7及以后: 若在堆中存在这个字符串对象,但在字符串常量池中不存在
// 这个时候会直接引用堆中字符串对象的地址
abc.intern();
def.intern();
System.out.println(abc == "abc"); // false
System.out.println(def == "def"); // true
}
面试题
@Test
public void test1(){
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEE" + "hadoop";
String s4 = "javaEEhadoop";
String s5 = "javaEE" + s2;
String s6 = s1 + "hadoop";
String s7 = s1 + s2;
System.out.println(s3 == s4); // true
System.out.println(s3 == s5); // false
System.out.println(s3 == s6); // false
System.out.println(s3 == s7); // false
System.out.println(s5 == s6); // false
System.out.println(s6 == s7); // false
String s8 = s6.intern();
System.out.println(s3 == s8); // true
}
@Test
public void test2(){
String s1 = new String("a");
s1.intern();
String s2 = "a";
System.out.println(s1 == s2); // jdk6及以前: false jdk7及以后: false
String s3 = new String("a") + new String("a");
s3.intern();
String s4 = "aa";
System.out.println(s3 == s4); // jdk6及以前: false jdk7及以后: true
}
@Test
public void test3(){
String s3 = new String("a") + new String("a");
String s4 = "aa";
String s5 = s3.intern();
System.out.println(s3 == s4); // false
System.out.println(s4 == s5); // true
}
@Test
public void test4(){
String s1 = new String("a") + new String("a");
String s2 = s1.intern();
System.out.println(s1 == "aa"); // jdk6及以前: false jdk7及以后: true
System.out.println(s2 == "aa"); // jdk6及以前: false jdk7及以后: true
}
@Test
public void test5(){
String x = "aa";
String s1 = new String("a") + new String("a");
String s2 = s1.intern();
System.out.println(s1 == "aa"); // false
System.out.println(s2 == "aa"); // true
}
@Test
public void test6(){
String s1 = new String("ab");
s1.intern();
String s2 = "ab";
System.out.println(s1 == s2); // false
}