JVM--常量池与自动装箱、拆箱

1 运行时常量池

运行时常量池存在于方法区中,常量池里面主要存储字符串常量和基本类型常量(public static final)。

字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new 出来的)才能确定的就存储在堆中。对于 equals 相等的字符串,在常量池中永远只有一份,在堆中有多份。

如以下代码:

String s1 = "china"; 
String s2 = "china";
String s3 = "china"; 
String ss1 = new String("china"); 
String ss2 = new String("china"); 
String ss3 = new String("china");   

这里解释一下,对于通过 new 产生一个字符串(假设为“china”),会先去常量池中查找是否已经有了“china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中“china”对象的拷贝对象。

也就是有道面试题,String s = new String(“xyz”); 产生几个对象?

答案是一个或两个。如果常量池中原来没有“xyz”,就是两个。如果原来的常量池中存在“xyz”时,就是一个。

基础类型的变量和常量:局部变量存储在栈中,成员变量存储在堆中,常量存储在常量池中。

如以下代码:

int i1 = 9; 
int i2 = 9; 
int i3 = 9;  
public static final int INT1 = 9; 
public static final int INT2 = 9; 
public static final int INT3 = 9;

2 自动拆箱与自动装箱

public class JVM {
    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;

        Long g = 3L;

        out.println(c == d);
        out.println(e == f);
        out.println(c == (a+b));
        out.println(c.equals(a+b));
        out.println(g == (a+b));
        out.println(g.equals(a+b));
    }
}

来看一下程序的运行结果是否符合你的预期:

true
false
true
true
true
false

包装类数据:如 Integer, String, Double,Long 等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中。

自动装箱与拆箱:

  1. 包装类的 “==” 运算在不遇到算术运算的情况下不会自动拆箱,equals()方法不处理数据转型的关系
  2. 在自动装箱,把 int 变成 Integer 的时候,是有规则的,当你的 int 的值在 -128-IntegerCache.high(127) 时,返回的不是一个新 new 出来的 Integer 对象,而是一个已经缓存在堆中的 Integer 对象(可以这样理解,系统已经把 -128 到 127 之间的 Integer 缓存到一个 Integer 数组中去了,如果你要把一个 int 变成一个 Integer 对象,首先去缓存中找,找到的话直接返回引用给你就行了,不必再新 new 一个),如果不在 -128-IntegerCache.high(127) 时会返回一个新 new 出来的 Integer 对象。

对于第二点,不仅 Integer 有这种特性,其它包装类数据也具有,我们可以看一下 Long 的源码:

private static class LongCache {
	private LongCache(){}

	static final Long cache[] = new Long[-(-128) + 127 + 1];

	static {
		for(int i = 0; i < cache.length; i++)
		cache[i] = new Long(i - 128);
	}
}

在了解了上面的概念之后,我相信你已经基本能够正确解释代码运行的结果了。

最后我来解释一下最后两个的运行结果为什么是 true 与 false,先来看一下后两句话在进行编译之后在 .class 文件中的样子:

System.out.println(g.longValue() == (long)(a.intValue() + b.intValue()));
System.out.println(g.equals(Integer.valueOf(a.intValue() + b.intValue())));

我们可以看到编译器对 out.println(g == (a+b)); 进行编译的时候,进行了拆箱与向上转型的操作,所以此时比较的仅仅是两个变量的字面值,与基本数据类型的比较是一样的,所以是 true。而最后仍然比较的是对象中的数据并且对 a 没有进行向上转型,Long 中存在的数据肯定就和 Integer 中存在的数据不等了,所以为 false。

再说一点,我们能将字面值直接赋给 Integer 类是因为 Java 语法糖的存在,实际上 Integer a = 1 在经过编译之后是这样的:Integer a = new Integer(1),语法糖帮助我们简化了语法。

评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值