JAVA 常量池与String

[size=small] 提到常量池,一般是指运行时常量池,是方法区的一部分。方法区就是通常说的永久代。那么常量池中会存储那些数据呢?

①编译期生成的各种字面量和符号引用
②也有可能将运行期间的常量放入常量池[/size]

[b][size=medium]先看第一种:[/size][/b][size=small]编译期生成的各种字面量和符号引用,这部分数据经过编译后存在.class文件的‘常量池’中,注意这个所谓的‘常量池’是‘静态常量池’,静态常量池的数据会在类加载后放入运行时常量池。举个例子
测试一[/size]

public class ConstantPoolAndStrTest {
String string1 = "Hello";
public void methodTest(){
String string2 = "pool";
}
}

[size=small]将上面代码编译后生成ConstantPoolAndStrTest.class文件,现在我们要想知道数据成员string1=“Hello”和局部变量string2=”pool“会不会进入常量池,只需要解析ConstantPoolAndStrTest.class文件,看其常量池(静态)中是否包含“Hello”和“pool”。而.class文件中存储的是只有JVM能够读懂的字节码,所以我们要想识别其内容要借助JDK自带的反汇编工具javap。执行命令javap -verbose ConstantPoolAndStrTest.class 可以得到下面数据:[/size]
[img]http://dl2.iteye.com/upload/attachment/0120/0982/9de20e5b-182c-3833-acd6-3e482460f965.png[/img]

[size=small] 上图只贴出了常量池(静态)的数据。可以看到string1和string2都被放入了常量池,String 之所以会被放到常量池里是因为她是final型,而这部分数据会在类加载后放入运行时常量池。
经过同上的测试发现:
测试二[/size]

String strTest = "my"+"string";

[size=small] 编译期会将上面代码优化为strTest="mystring","mystring"被存入常量池中,但"my"和"string"并没有进入常量池。
测试三[/size]

String string1 = "a";
String string2 = "b";
String string3 = string1+string2;
String string4 = string1+"c";


[size=small] 上面的代码,string3="ab"和string4="bc"都没有进入常量池。结合测试二、测试三可知:java编译器会对字面量的运算(测试一)进行优化,但不会对含有引用的运算(测试二)进行优化。
下面说一个稍微绕一点的情况:
测试四[/size]

String newStrtest = new String("pool");

[size=small]编译,javap 可得到下面结果[/size]

[img]http://dl2.iteye.com/upload/attachment/0120/0992/9a4e3e3c-0ba5-3cae-a165-2dbcbf516717.png[/img]
[size=small]到这里可能会有一点迷惑了:
new String("pool")不是在堆中创建对象么?怎么会跑到了常量池里?
没错,new String("pool")是在堆中分配,但new的操作要等到运行时才能执行,编译期并不会执行new操作,而编译器只是把字面量"pool"放入常量池中。运行时执行new操作会重新在堆中创建对象,并将引用 newStrtest指向这个对象。即测试四创建了两个对象,一个在常量池中,一个在堆中。下面这代码输出结果为false,可以证明这一点。[/size]

String s1 = "hey";
String s2 = new String("hey");
System.out.println(s1==s2);//==比较的是引用,s1指向常量池,s2指向堆中,引用指向不同地址,因此输出false



[b][size=medium]再看第二种:[/size][/b][size=small]也有可能将运行期间的常量放入常量池
比如
string.intern()
会检查常量池中是否存在string则返回池里的字符串引用;如果不存在则将string加入到常量池再返回其引用。这样做可以避免在堆中不断的创建字符串对象,起到节省空间的作用。
测试五(下面这段代码来自博客[url]http://txy821.iteye.com/blog/760957[/url])[/size]

String str1 = "a";
String str2 = "bc";
String str3 = "a"+"bc";
String str4 = str1+str2;

System.out.println(str3==str4);
str4 = (str1+str2).intern();
System.out.println(str3==str4);

[size=small]输出结果为false,true ‘测试三’中说过:编译器不会优化包含引用的运算,str4会在运行期,在堆中分配内存,所以第一个输出为false;而经过[/size]
str4 = (str1+str2).intern();

[size=small]s4指向了常量池中的"abc"所以第二个输出为true。这样就可以证明上面提到的intern()的作用。[/size]

[b][size=medium]最后[/size][/b],[size=small]在JDK源码String.intern()方法的注释中有这样一段话,"A pool of strings, initially empty, is maintained privately by the class {@code String}."
说"有一个字符串池,初始为空,由String类私有的维护"。但注释中并没有说这个字符串池位于JVM内存分区的哪一个部分。是在方法区?堆区?这个困扰了我很长时间。查资料看到有人说位于堆中,但如果这样的话将会和方法区中常量池中的数据大量重复。而且网上的资料看到很多人都在说"字符串池"怎样怎样,但JVM的内存划分中并没有这样一块区域。
所以我把"字符串池"理解成"常量池中字符串的集合"

以上,如有错误,请指正[/size]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值