环境
java8
正文
1.字符串常量池
相信不少小伙伴都遇到过这样的面试题,但真正了解背后的原理吗?
下面我们来看一下题目
String s1 = "张三";
String s2 = "张三";
String s3 = new String("张三");
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //false
答案很简单,直接给出了答案,此时内存中的关系可以如图表示:
s1代码执行的时候会先查看字符串常量池存不存在"张三"这个字符串,如果存在,直接把"张三"的地址赋给s1,如果不存在,先在字符串常量池新建一个"张三",然后把地址返回给s1;
s2一样的道理,但是由于"张三"已经在字符串常量池,所以直接把"张三"的地址返回给s2;
s3的时候由于是new了一个String对象,所以s3是一个全新的对象,对象地址自然是堆中新对象的地址,但是这个对象中的"张三"还是指向的字符串常量池中的"张三";
下面我们加个方法
2.intern()
String s1 = "张三";
String s2= new String("张三");
String s3 = s2.intern();
System.out.println(s1 == s2); //false
System.out.println(s1 == s3); //true
System.out.println(s2 == s3); //false
也没什么难度,此时关系如下
String s3 = s2.intern()方法的意思是
如果字符串常量池存在"张三",那么就直接返回"张三"的地址
如果不存在,先在字符串常量池新建一个"张三",然后返回给变量,即s3,没什么争议。
既然知道是什么情况了,我们先来热下身
3.热身运动
- 第一节
String s1 = "张三";
String s2 = new String("张三");
String s3 = new String("张三");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s2 == s3);
根据上边的知识,应该没问题
false
false
false
- 第二节
String s1 = "张三";
String s2 = new String("张三").intern();
String s3 = new String("张三").intern();
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s2 == s3);
没问题
true
true
true
下面我们加大点难度
4. 不可能,一定是你错了
4.1 我不可能错
String name1 = "张";
String name2 = "三";
String s1 = name1 + name2;
String s2 = new String("张三").intern();
String s3 = new String("张三").intern();
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s2 == s3);
猜猜这次的结果是什么
false
false
true由于字符串相加的效率并不高,所以java编译器编译的时候会把+操作转换成StringBuilder的append()操作,最后返回结果集的时候时候调用的方法的原理就是new String();感兴趣的小伙伴可以用javap命令反编译class文件看一下
如果说这次是因为不了解底层原理,好,那我们接下来看下一个
4.2 这不怪我
我们先看一个正常的
String s1 = "张三";
String s2 = new String("张") + new String("三");
String s3 = s2.intern();
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // false
System.out.println(s1 == s3); // true
这个没问题,下边我们把s1换个位置
String s2 = new String("张") + new String("三");
String s3 = s2.intern();
String s1 = "张三";
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s2 == s3);
我们这次只把s1的位置放到了s3的下边,能猜到结果吗
true
true
true
没错,三个结果都是true,从这里可以体现出字符串常量池在堆中,下面我们看下此时的关系图
s2运行的时候,"张"和"三"被单独放到了字符串常量池中,而"张三"只是被作为一个对象放到了堆中,并没有放到字符串常量池(编译期可以确认的才放到字符串常量池),所以s2在执行intern方法时,发现常量池没有,就把s2引用的"张三"这个对象的地址放到了字符串常量池(可通过地址直接访问s2的"张三"),最终张三只有一份,所以s2 == s3。
当执行String s1 = "张三" 时,根据运行时常量池地址发现存在"张三这条记录",所以就直接返回给了s1,所以三个是同一个对象。
看完以后是不是觉得自己又收获了很多,我们最后再加点代码巩固一下
5.最后的倔强
String s1 = new String("张") + new String("三");
String s2 = new String("张") + new String("三");
String s3 = new String("张") + new String("三"));
String s4 = s1.intern();
String s5 = s2.intern();
String s6 = s3.intern()
String s7 = "张三";
System.out.println(s1 == s2); // false
System.out.println(s1 == s3); // false
System.out.println(s1 == s4); // true
System.out.println(s2 == s5); // false
System.out.println(s3 == s6); // false
System.out.println(s1 == s7); // true
System.out.println(s2 == s7); // false
System.out.println(s3 == s7); // false
根据上边的经验,这个应该不费什么劲就看出来了
什么时候字符串会被放到字符串常量池?
- 调用intern()方法,并且字符串常量池没有的情况下。
- 编译期就可以确认的字符串,并且字符串常量池没有的情况下。
最后
看到这,相信字符串之间的关系已经了解的差不多了,以后使用过程中也不会混乱了。
最后说下,最后两个实例,不同的java版本,运行的结果是不一样的,主要是因为在java7之前字符串常量池所在位置不一样,我使用的是Java8版本,结果以8版本为准。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。