这两天想着巩固一下基础,昨天突然想起来之前学数据类型这一块,String类型是特殊的引用类型,想看看特殊在何处,然后自己写了个入口,测了一下下面的代码:
String a = "123";
String b = "123";
String c = a;
System.out.println(String.valueOf(a==b)+"--"+String.valueOf(a==c)+"--"+String.valueOf(b==c));
a = "1234";
System.out.println(String.valueOf(a==b)+"--"+String.valueOf(a==c)+"--"+String.valueOf(b==c));
为什么会这么测呢,因为我记得String是引用数据类型嘛,心里感觉像这种String c = a;
会使c与a指向同一个堆内存,这样c永远是==a的,不过结果却跟我想的不一样:
额。。难道因为我没有new对象吗,再试试:
String d = new String("123");
String e = d;
System.out.println(d==e);
d = "1234";
System.out.println(d==e);
这样的话,d会指向我new出的对象在堆里的地址了吧,e和d会引用同一个对象了吧,我再执行:
这我就有点懵了,赶紧去找资料,看原理,发现还有常量池概念:
Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。
所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。
而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
第一段代码中,“123”,“1234”都会放在常量池中,然后a,b,c先指向“123”,之后a指向了“1234”,所以呢,第二个输出结果中,a是与其他两个变量不等的。
第二段代码中,“1234”是放在常量池的,new String("123");
放在堆中,而且一开始d和e同时指向了堆中的string对象,之后d又指向了常量池中的“1234”,这样就导致d和e不等了。
有一段代码段是很好的例子,我引用一下:
String s1 = "Hello";
String s2 = "Hello";
String s3 = "Hel" + "lo";
String s4 = "Hel" + new String("lo");
String s5 = new String("Hello");
String s6 = s5.intern();
String s7 = "H";
String s8 = "ello";
String s9 = s7 + s8;
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // true
System.out.println(s1 == s4); // false
System.out.println(s1 == s9); // false
System.out.println(s4 == s5); // false
System.out.println(s1 == s6); // true
s3这里,编译器会直接拼成”Hello”,所以说s1,s2,s3都是指向常量池中的”Hello”;
s7,s8分别又指向常量池中的“H”和“ello”;
然后s4这里呢,是常量池中的“Hel”与堆中的string对象new String(“lo”)拼接后的string对象放到了堆中,s4指向拼接后的对象;
s5直接指向堆中的new String(“Hello”)对象;
s6这里呢跟s5有关,s5在堆中,内容为Hello ,intern方法会尝试将Hello字符串添加到常量池中,并返回其在常量池中的地址,因为常量池中已经有了Hello字符串,所以intern方法直接返回地址;
最后s9,是由两个变量来赋值出来的,s9在编译的时候,编译器并不知道s7和s8具体是啥,所以呢s9是指向堆中s7对象和s8对象的拼接。
然后联想到一个东西:
String a = "1"+"2"+"3";
String是不可变的,加号的拼接作用,其实会在常量池中,先有”1”,再有”12”,再有”123”,产生了临时变量,造成效率低下。而StringBuffer是可变的。
博文参考:http://blog.csdn.net/qq_27093465/article/details/52033327
里面还有更详细的内容,关于class文件的常量池,暂且不谈。