首先了解一下怎么看java代码解析情况
javac MedianFinder.java
javap -verbose MedianFinder.classes
一.先了解创建了几个对象
1.String s =new String(“a”)创建了几个对象?
2个(默认之前常量池没有)
- 如果常量池已经有“a”了,那么只会在堆中创建一个
- 如果没用,则会堆和常量池各一个
关键点就是不光直接"a"这样的字面量会在常量池创建,new也会在常量池创建,并且在堆也会创建
2.String s = “a”+"b"创建了几个对象?
1个
JVM在编译的时候就会认定为是String s = "ab"所以只会在常量池生成1个
3.String s = “a” + new String("b)创建几个对象
4个
- 首先常量池会创建两个:“a”,“b”
- 堆里首先会创建一个"b",然后会创建一个
StringBuilder
类型的"ab"
String s1 = new String("b)
String s2 = new String("b)
几个对象?
3个
- 常量池1个
- 堆两个
5.String s = new String(“a”) + new String(“a”);几个对象?
4个
- 常量池1个
- 堆3个:2个String,一个StringBuilder
6.String s = new String(“a”) + new String(“b”);几个对象?
5个
- 常量池2个
- 堆3个:2个String,一个StringBuilder
问题:在2中两个""相加的内容也就是”ab“会放入常量池,但是只要有new方法的相加,比如3中的”ab“、5中的”aa“、6中的”ab“都不在常量池中创建
而且要注意
1.“”+"“这样的包括”"都是直接在常量池创建,不在堆创建
2.只要有new的都会在堆创建,并且只要有new参与的+连接,都使用StringBuilder连接的,连接结果不会再常量池创建
3.只要有引用参与的+连接,也低于是都使用StringBuilder连接的,连接结果不会再常量池创建
再补充一点""这种空字符串,也算是和"a"一样的
二.判断地址相等
答案是true,因为两个"“+”"会被JVM直接看成连接后的结果,所以都统一指向一个常量池地址(也就是创建了1个对象)
答案是fasle
当多个字符串引用相加的时候,会在堆中开辟一个空间,是StringBuilder对象进行拼接
首先创建了4个对象,3个再常量池,一个堆里的StringBuilder对象
要注意,这种引用拼接的情况,不会再常量池里创建对象(所以SB对象的值
也就不会指向常量池的abcd
),因此如果是没用s1,常量池中就不会有”abcd“
3个对象,常量池2个,堆一个
不过,字符串使用 final 关键字声明之后,可以让编译器当做常量来处理。
示例代码:
final String str1 = "str";
final String str2 = "ing";
// 下面两个表达式其实是等价的
String c = "str" + "ing";// 常量池中的对象
String d = str1 + str2; // 常量池中的对象
System.out.println(c == d);// true
被 final 关键字修改之后的 String 会被编译器当做常量来处理,编译器在程序编译期就可以确定它的值,其效果就相当于访问常量。
String s = "1";
String s1 = "1";
System.out.println(s == s1);
答案是true,因为都指向常量池地址(创建了一个对象)
String s = new String("1");
String s1 = new String("1");
System.out.println(s == s1);
答案是fasle
引用指向的是两个不同的堆地址,虽然这两个堆里的对象都指向同一个常量池地址(共创建3个对象)
String s = "1";
String s1 = new String("1");
System.out.println(s == s1);
答案是fasle,一个指向堆对象,一个指向常量池。堆对象的内容指向常量池(共2个对象)
三.加入.intern()方法
如果字符串s在字符串常量池中存在对应字面量,则intern()方法返回该字面量的地址;如果不存在,则在字符串常量池创建一个对应的字面量,并返回该字面量的地址
String对象与字面量的intern()区别
public static void main(String[] args) {
String s1 = new String("字符串");
String s2 = "字符串";
System.out.println(s2 == s2.intern());
System.out.println(s1 == s1.intern());
System.out.println(s1.intern() == s2.intern());
}
结果是True / False / True,解释如下:
- 对于字符串字面量s2而言,它本身就是字符串常量池中"字符串"常量的引用,因此s2.intern()返回的是字符串常量池中“字符串”常量的地址,与s2本身是相等的,所以为true
- 对于String对象s1而言,它是一个指向堆空间String对象的引用。String对象中保存着一个final byte[]用于存储字符串的value,该成员又指向了字符串常量池中“字符串”这个字面量。因此调用s1.intern(),返回的是字符串常量池中"字符串"字面量的地址。s1本身存的是堆空间String对象的地址,因此二者不相等
- 不管是String对象,还是字面量,只要他们的值相等,调用intern()都会返回同一个字符串常量池的引用,因此s1.intern() == s2.intern()
// 在堆中创建字符串对象”Java“
// 将字符串对象”Java“的引用保存在字符串常量池中
String s1 = "Java";
// 直接返回字符串常量池中字符串对象”Java“对应的引用
String s2 = s1.intern();
// 会在堆中在单独创建一个字符串对象
String s3 = new String("Java");
// 直接返回字符串常量池中字符串对象”Java“对应的引用
String s4 = s3.intern();
// s1 和 s2 指向的是堆中的同一个对象
System.out.println(s1 == s2); // true
// s3 和 s4 指向的是堆中不同的对象
System.out.println(s3 == s4); // false
// s1 和 s4 指向的是堆中的同一个对象
System.out.println(s1 == s4); //true