jdk1.7之前放在方法区,1.7之后放在堆中。
常量池在java用于保存在编译器已确定的,已编译的class文件中的一份数据。它包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s = "java" 这种申明方式;当然也可以扩充,故认为常量池是jvm的一块特殊的内存空间。
在Java程序中,有很多东西是永恒的,不会在运行过程中变化。比如一个类的名字,一个类字段的名字/所属类型,一个类方法的名字/返回类型/参数名与所属类型,一个常量,还有在程序中出现的大量的字面值。
比如下面这段代码中蓝色部分:
public class ClassTest {
private String items = "我们";
private final int iteml = 100;
public void setItemS(String param) {...}
}
java常量池技术
java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间。String类也实现了常量池的技术,同样的,大部分包装类也实现了常量池的技术,具体百度。以下用String做说明。
测试代码如下:
public class Test {
public static void main(String[] args) {
// s1,s2分别位于栈中,指向堆中不同的空间
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // 输出false
// s3,s4位于池中同一空间
String s3 = "hello";
String s4 = "hello";
System.out.println(s3 == s4); // 输出true
}
}
用new String() 创建的字符串不是常量,不能再编译器就确定,所以new String() 创建的字符串不放入常量池中,他们有自己的地址空间。
String对象的不变性机制会使修改String字符串时,产生大量的对象,因为每次改变字符串,都会生成一个新的String对象。java为了更有效的使用内存,常量池在编译器遇见String字符串时,它会检查该池内是否已经存在相同的字符串,如果找到,就把新变量的引用指向现有的字符串对象,不创建任何新的String常量对象,没找到则创建新的对象。所以对一个字符串对象的任何修改,都会产生一个新的字符串对象,原来的依然存在,等待垃圾回收。
String a = "123456";
String b = "123" + "456";
String c = "123" + new String("456");
System.out.println(a == b); // true
System.out.println(b == c); // false
我对于以上代码的理解:
a先去常量池中找"123456",没找到,于是在常量池中创建了"123456"。而b的申明方式相当于"123"先于"456"拼接得到"123456",然后b去常量池中找"123456",找到了,于是b指向常量池中的"123456"。而c的申明方式,相当于在堆中创建了新对象,他不会去常量池中寻找,c会指向堆中其对应的内存空间。因此,a、b指向的是同一块空间,而b、c指向的是两块不同的空间。
再说说String类中的intern()方法。
String a = new String("java");
String b = a.intern();
String c = "java";
System.out.println(a); // java
System.out.println(b); // java
System.out.println(a == b); // false
System.out.println(a.intern() == b); // true
System.out.println(c == b); // true
System.out.println(c == a); // false
intern()方法相当于在常量池中创建一个对象,然后将引用指向该对象。因此b、c指向的是常量池中的同一块地方。而a指向的则是堆中的一块内存。故出现以上结果。
不要使用intern方法了,就是一个**方法。看以下的(jdk1.8):
String str1 = new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern() == str1); // true
String str3 = new StringBuilder("he").append("hehe").toString();
System.out.println(str3.intern() == str3); // true
String a = "jdk";
String str4 = new StringBuilder("j").append("dk").toString();
System.out.println(str4.intern() == str4); // false
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern() == "java"); // true
System.out.println(str2.intern() == str2); // false
System.out.println(str2 == "java"); // false
System.out.println("*****************");
String str5 = new String("heihei");
System.out.println(str5.intern() == str5); // false
System.out.println(str5.intern() == "heihei"); // true
String str6 = "heihei";
System.out.println(str5 == str6); // false
System.out.println(str5.intern() == str6); // true
String str7 = new String("java");
System.out.println(str7.intern() == str7); // false
System.out.println(str7.intern() == "java"); // true
这个咋说的,1.7以后intern方法会先查看常量池是否存在该字符串,不存在的话,会将该字符串的引用扔进去。。。而不是将整个字符串扔进去。。。至于"java"这个字符串,jdk可能默认会在常量池里创建