常量池

Posted loveer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了常量池相关的知识,希望对你有一定的参考价值。

Java中的常量池

Java常量池实际上分为两种形态:静态常量池和运行时常量池
    常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享。


静态常量池:

    静态常量池是Class文件常量池(编译后的class文件的常量池),存放各个字面量值,符号引用的数据。

    主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References)。

    字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等。


运行时常量池(具备动态性):

    是jvm虚拟机在完成类装载操作后,将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

//对于所有包含new方式新建对象(包括null)的“+”连接表达式,它所产生的新对象都不会被加入字符串池中
System.out.println(s1 == s4);  // false

//s7、s8作为两个变量,都是不可预料的,编译器毕竟是编译器,不可能当解释器用,不能在编译期被确定
//所以不做优化,只能等到运行时,在堆中创建s7、s8拼接成的新字符串,在堆中地址不确定,不可能与方法区常量池中的s1地址相同
System.out.println(s1 == s9);  // false

//二者都在堆中,但地址不同
System.out.println(s4 == s5);  // false

//s5在堆中,内容为Hello ,intern方法会尝试将Hello字符串添加到常量池中,并返回其在常量池中的地址
//因为常量池中已经有了Hello字符串,所以intern方法直接返回地址
System.out.println(s1 == s6);  // true


特例一

public static final String A = "ab"; // 常量A
public static final String B = "cd"; // 常量B
     String s = A + B;    // 将两个常量用+连接对s进行初始化 
     String t = "abcd";    //s == t 成立
A和B都是常量,值是固定的,因此s的值也是固定的,它在类被编译时就已经确定了。
也就是说:String s=A+B; 等同于:String s="ab"+"cd";


特例二

    public static final String a;
    public static final String b;

    static 
        a = "123";
        b = "456";
    

    public static void main(String[] args)
    
        String c = "123456";
        String d = a + b;
        System.out.println(c == d);
    

编译期间,就已经确定了c,放在字符串常量池(堆中),但编译期static不执行的,a和b的值是未知的,
static代码块,在初始化的时候被执行,初始化属于类加载的一部分,属于运行时常量池(方法区中)。
运行时是这样的String s6=new StringBuilder().append(s3).append(s4).toString();这里的过程是通过StringBuilder这个类实现的
它是通过new String()的方式来作为值进行返回的,所以是在堆中开辟的一块空间。所以和常量池中的不一样

1,在java 中,直接使用==操作符,比较的是两个字符串的引用地址,并不是比较内容,比较内容请用String.equals()

2,程序运行时,除非手动向常量池中添加常量(比如调用intern方法),否则jvm不会自动添加常量到常量池。

整型常量池、浮点型常量池等

Byte,Short,Integer,Long,Character,Boolean都实现了常量池技术

数值类型的常量池不可以手动添加常量,程序启动时常量池中的常量就已经确定了,
    比如整型常量池中的常量范围:-128~127,
    Byte,Short,Integer,Long,Character,Boolean这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据,
    但是超出此范围仍然会去创建新的对象

-128到127之 间的Integer会缓存到一个Integer数组中去了:
    如果你要把一个int变成一个Integer对象,首先去缓存中找,找到的话直接返回引用给你,不必再新new一个,
    如果不在-128-IntegerCache.high(127) 时会返回一个新new出来的Integer对象

Integer源码,里面有个静态类IntegerCache
它对Integer进行了缓存,范围是[-128,127],只要是这个范围内的数字都会缓存到这个里面,做成常量池进行管理


private static class IntegerCache 
    static final int high;
    static final Integer cache[];

    static 
        final int low = -128;
        // high value may be configured by property
        int h = 127;
        if (integerCacheHighPropValue != null) 
            // Use Long.decode here to avoid invoking methods that
            // require Integer's autoboxing cache to be initialized
            int i = Long.decode(integerCacheHighPropValue).intValue();
            i = Math.max(i, 127);
            // Maximum array size is Integer.MAX_VALUE
            h = Math.min(i, Integer.MAX_VALUE - -low);
        
        high = h;
        cache = new Integer[(high - low) + 1];
        int j = low;
        for (int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);
    

    private IntegerCache() 
    

字符串常量池(String Constant Pool)

存储编译期类中产生的字符串类型数据

JDK6.0及之前版本,字符串常量池是放在Perm Gen区(也就是方法区)中
JDK7.0版本,字符串常量池被移到了堆中了

运行时常量池和字符串常量池是独立的

String.intern()
检查字符串常量池中是否存在String并返回池里的字符串引用
若池中不存在,则将其加入池中,并返回其引用。 
这样做主要是为了避免在堆中不断地创建新的字符串对象

class常量池(Class Constant Pool)

每一个Java类被编译后,就会形成一份class文件

class文件中除了包含类的版本、字段、方法、接口等描述信息外

还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)

技术图片

运行时常量池(Runtime Constant Pool)

运行时常量池包含了类的运行时常量和静态方法等Class常量池的数据

JVM中运行时常量池在方法区中,是class常量池被加载到内存之后的版本

当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中

运行时常量池和字符串常量池是独立的

new关键字

使用new关键字当然是每次都是新建一个,分配自己的空间

以上是关于常量池的主要内容,如果未能解决你的问题,请参考以下文章

好好说说Java中的常量池之Class常量池

java常量池共享是啥意思?每个类都有自己的一个常量池吗?

java方法区中包含哪些内容,常量池中包含哪些内容

浅析Java常量池

java中 关于常量池 栈内存 堆内存

JAVA字节码文件之第二篇(常量池)