JAVA常量池,一篇文章就足够入门了。(含图解)

JAVA常量池,一篇文章就足够入门了。(含图解)前言一直在 深入理解 JVM 对常量池只有一个浅薄的了解 之前也遇到过这种题目 今天还是要挑出来进行一次全方位的了解 常量池分类常量池大体可以分为 静态常量池 运行时常量池 静态常量池存在于 class 文件中 比如经常使用的 javap verbose 中 常量池总是在最前面把 运行时常量池呢 就是在 class 文件被加载进了内存之后 常量池保存在了方法区中 通常说的常量池值的

前言

一直在《深入理解JVM》对常量池只有一个浅薄的了解,之前也遇到过这种题目,今天还是要挑出来进行一次全方位的了解。

常量池分类

常量池大体可以分为:静态常量池,运行时常量池。

  • 静态常量池 存在于class文件中,比如经常使用的javap -verbose中,常量池总是在最前面把?
  • 运行时常量池呢,就是在class文件被加载进了内存之后,常量池保存在了方法区中,通常说的常量池 值的是运行时常量池。所以呢,讨论的都是运行时常量池

字符串常量池

最最最流行的、最典型的就是字符串了

典型范例:

String a = "abc"; String b = new String("abc"); System.out.println(a == b); ----*---- 结果:false

这里写图片描述
这个是第一个需要理解的地方,a指向哪片内存,b又指向哪片内存呢?对象储存在堆中,这个是不用质疑的,而a作为字面量一开始储存在了class文件中,之后运行期,转存至方法区中。它们两个就不是同一个地方存储的。知道了它之后我们就可以通过实例直接进一步了解了

实例

 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

需要注意的特例

1、常量拼接

 public static final String a = "123"; public static final String b = "456"; public static void main(String[] args) { String c = ""; String d = a + b; System.out.println(c == d); } ------反编译结果------- 0: ldc #2 // String  2: astore_1 3: ldc #2 // String  5: astore_2 6: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;

我们可以发现,对于final类型的常量它们已经在编译中被确定下来,自动执行了+号,把它们拼接了起来,所以就相当于直接”123” + “456”;

2、static 静态代码块

 public static final String a; public static final String b; static { a = "123"; b = "456"; } public static void main(String[] args) { String c = ""; String d = a + b; System.out.println(c == d); } ------反编译结果------- 3: getstatic #3 // Field a:Ljava/lang/String; 6: getstatic #4 // Field b:Ljava/lang/String; 9: invokedynamic #5, 0 // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; 

上个例子是在编译期间,就已经确定了a和b,但是在这段代码中,编译期static不执行的,a和b的值是未知的,static代码块,在初始化的时候被执行,初始化属于类加载的一部分,属于运行期。看看反编译的结果,很明显使用的是indy指令,动态调用返回String类型对象。一个在堆中一个在方法区常量池中,自然是不一样的。

包装类的常量池技术(缓存)

简单介绍

相信学过java的同学都知道自动装箱和自动拆箱,自动装箱常见的就是valueOf这个方法,自动拆箱就是intValue方法。在它们的源码中有一段神秘的代码值得我们好好看看。除了两个包装类Long和Double 没有实现这个缓存技术,其它的包装类均实现了它。

public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
private static class IntegerCache { 
    static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }

分析:我们可以看到从-128~127的数全部被自动加入到了常量池里面,意味着这个段的数使用的常量值的地址都是一样的。一个简单的实例

Integer i1 = 40; Integer i2 = 40; Double i3 = 40.0Double i4 = 40.0; System.out.println("i1=i2 " + (i1 == i2)); System.out.println("i3=i4 " + (i3 == i4)); -----结果---- true false

3、编译阶段已经将代码转变成了调用valueOf方法,使用的是常量池,如果超过了范围则创建新的对象

 2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

复杂实例[-128~127]

 Integer i1 = 40; Integer i2 = 40; Integer i3 = 0; Integer i4 = new Integer(40); Integer i5 = new Integer(40); Integer i6 = new Integer(0); System.out.println("i1=i2 " + (i1 == i2)); System.out.println("i1=i2+i3 " + (i1 == i2 + i3)); System.out.println("i1=i4 " + (i1 == i4)); System.out.println("i4=i5 " + (i4 == i5)); System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); System.out.println("40=i5+i6 " + (40 == i5 + i6)); ----结果---- (1)i1=i2 true (2)i1=i2+i3 true (3)i1=i4 false (4)i4=i5 false (5)i4=i5+i6 true (6)40=i5+i6 true

2、==这个符号,既可以比较普通基本类型,也可以比较内存地址看比较的是什么了

分析:
(1)号成立不用多说
(2)号成立是因为运算符自动拆箱
(3)(4)号是因为内存地址不同
(5)(6)号都是自动拆箱的结果








PS:equals方法比较的时候不会处理数据之间的转型,比如Double类型和Integer类型。

超过范围

假设一下,如果超出了这个范围之后呢?正如前文所言,所有的都将成为新的对象

 Integer i1 = 400; Integer i2 = 400; Integer i3 = 0; Integer i4 = new Integer(400); Integer i5 = new Integer(400); Integer i6 = new Integer(0); Integer i7 = 1; Integer i8 = 2; Integer i9 = 3; System.out.println("i1=i2 " + (i1 == i2)); System.out.println("i1=i2+i3 " + (i1 == i2 + i3)); System.out.println("i1=i4 " + (i1 == i4)); System.out.println("i4=i5 " + (i4 == i5)); System.out.println("i4=i5+i6 " + (i4 == i5 + i6)); System.out.println("400=i5+i6 " + (400 == i5 + i6)); ----结果---- i1=i2 false i1=i2+i3 true i1=i4 false i4=i5 false i4=i5+i6 true 400=i5+i6 true

总结

关于常量池部分的总结到这里,通过实际的例子和绘图来熟悉了下字符串常量池和包装类的常量池的使用。其中还包括了装箱和拆箱的小知识。收获还是丰厚的,终于明白了常量池的内容了。~happy-。-,如有笔误,还望纠正

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/199059.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月26日 下午1:56
下一篇 2026年3月26日 下午1:57


相关推荐

  • Python join() 函数 连接字符串

    Python join() 函数 连接字符串Python 中有 join 和 os path join 两个函数 具体作用如下 join 连接字符串数组 将字符串 元组 列表中的元素以指定的字符 分隔符 连接生成一个新的字符串 os path join 将多个路径组合后返回

    2025年11月15日
    7
  • Multi-Agent记忆系统MIRIX:比RAG性能飙升35%,存储减少99.9%

    Multi-Agent记忆系统MIRIX:比RAG性能飙升35%,存储减少99.9%

    2026年3月16日
    2
  • 谈谈网站防盗链

    谈谈网站防盗链#开启重写引擎RewriteEngineon#图片防盗链RewriteCond%{HTTP_REFERER}!http://rewrite/.*RewriteRule.*\.(jpg|jpeg|png|gif)daotu.jpg引子:明明引用了一个正确的图片地址,但显示出来的却是一个红叉或写有“此图片仅限于***网站用户交流沟

    2022年7月23日
    12
  • 关于学习的名言_classnotdeffounderror

    关于学习的名言_classnotdeffounderror 现在java编程中经常碰到ClassCastException错误,ClassCastException是JVM在检测到两个类型间的转换不兼容时引发的运行时异常。此类错误通常会终止用户请求。本模式试图为您提供了解和排除ClassCastException错误最常见成因的一些基本要素。为什么发生此问题?在执行几乎任何子系统(Web容器、EJB、JCA、群集等)的应用程序代码或…

    2025年10月16日
    6
  • navicat永久激活【2021最新】

    (navicat永久激活)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容https://javaforall.net/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~00OE5RWT28-eyJsaWNlb…

    2022年3月30日
    77
  • murmurhash java_默默hash(MurmurHash)

    murmurhash java_默默hash(MurmurHash)MurmurHash 可以将一个字符串 hash 出一个碰撞率极低的 long 型数值 且效率很高 packagecom trs util importjava nio ByteBuffer importjava nio ByteOrder 根据字符串生成 long 型数据 id authoryush 2018 年 11 月 6 日上午 11 02 00 publicclassA

    2025年10月14日
    7

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号