JVM-常量池

学习JVM之后,发现对常量池的理解又深入了一些。下面就从JVM的层面去理解JAVA程序中那些常见的语句。
读过笔者博客“JVM-类的生命周期“ http://yizhenn.iteye.com/blog/2290619的读者都应该已经知道,JVM在加载class文件时经历了装载,连接,初始化的过程,其中连接又包括验证,准备和解析。我们就来说说这个解析。

所谓解析,就是将class文件中的静态常量池中的符号引用解析为直接引用,说白了,就是为class常量池中的一些常量创建对象。比如String str="abc";理论上来讲直接在class静态常量池中存放"abc"即可。但是我们知道,JAVA中除了8种基本类型之外,其他的都是引用。因此,对于静态常量池中的"abc",JVM会在堆中创建一个对象,静态常量池中的str就指向这个刚刚创建的对象。像str这样的常量对象所在的空间,叫做动态常量池。

在JAVA中,String和Integer,Short,Long,Character,Boolean,Byte这六种基本类型的封装类都实现了动态常量池机制。对于六种基本类型的封装类,我们以Integer为代表进行说明。

如下代码:
String a="123";
String b="123";

在解析的时候,发现静态常量池中有a="123",就会在动态常量池中寻找是否有值为"123"的对象,结果没有找到,就会执行new String("123"),然后使静态常量池中的a指向该对象,当发现静态常量池中有b="123",就会在动态常量池中寻找是否有值为"123"的对象,结果找到了之前创建的那个对象,就会把那个对象的地址返回给静态常量池中的b。

上面的过程是在解析的时候做的,当然你也可以认为在执行的时候做的。但不管怎样,一定存在一个常量池, 当执行String str="xx";的时候,先到常量池中去寻找值为xx的对象,如果存在,就直接返回该对象地址,否则在动态常量池中创建一个新对象并返回地址。

如下代码:
String a=new String("123");
String b=new String("123");

第一行代码在执行的时候, JVM见到"123",会先到动态常量池中看是否有值为"123"的对象,如果没有,在常量池中创建一个对象String("123").这个过程和a没有半毛钱关系,你可以认为这是JVM常量池自学习的过程。接着,在堆区创建一个对象String("123");并将该对象的引用返回给a;
第二行代码也是这样的过程, 所以上面的两行代码可能产生2或3个对象。

对于String类,还存在一个 str.intern()方法,他的作用是检查动态常量池中是否存在值与str对象相同的对象,如果存在,直接返回该对象的引用。如果不存在,就在常量池中创建一个对象,然后返回他的引用。
如下代码:
String a="123";
String b="123";
String c=new String("123");
String d=c.intern();

对于上面的代码,你应该能理解,a和b和d都指向的是动态常量池中的那个对象String("123"),而c指向的是堆中的对象String("123");

介绍了String类的常量池,我们来说Integer类的常量池。Integer类型的取值不像String类型那么广泛, String常量池中的对象收集自程序。Integer常量池中的对象是固定的,只有取值为-128~127的这256个对象。当Integer.valueOf(i)中i的值是-128~127时,直接返回常量池中的对象,否则在堆区新建一个对象并返回他的引用。这可以从jdk源码中得到证明,在jdk源码中,Integer有这样的一个方法:
    public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }


这个方法是自动装包时候用的方法,何谓装包?把int这个基本类型转化为Integer这个引用类型就是装包。
代码如下:
Integer a=1;
Integer b=1;
Integer c=1111;
Integer d=1111;

对于上面的代码,实际上如下:
Integer a=Integer.valueOf(1);
Integer b=Integer.valueOf(1);
Integer c=Integer.valueOf(1111);
Integer d=Integer.valueOf(1111);

因此a和b指向Integer常量池中的同一个对象,他们的值相等。c和d指向堆区中的不同对象,他们的值不等。

如下代码:
Integer a=new Integer(1);
Integer b=new Integer(1);

由于使用了new,强制的在堆区创建了两个对象,没用到常量池优化,因此a和b的值不等。

这里顺便提一下装包和拆包,将基本类型转化为对应的封装类的过程叫装包,反之叫拆包。当基本类型和其对应的封装类执行比较的时候,比如Integer对象和int型比较,这会使用拆包,将Integer类型拆包为int,这个过程调用Integer的intValue()方法。

关于上面提到的其余5中包装类的常量池与Integer类似,在此不再赘述。

笔者开设了一个知乎live,详细的介绍的JAVA从入门到精通该如何学,学什么?

提供给想深入学习和提高JAVA能力的同学,欢迎收听https://www.zhihu.com/lives/932192204248682496


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值