【Java基础】字符串与部分包装类的常量池(缓存池)

  • 字符串常量池
  • 基本数据类型的包装类的常量池
  • 总结

字符串常量池

  • 由于字符串是一个程序运行过程中使用频繁程度最高的,如果作为一个最基础的数据类型,大量频繁地创建对象,对程序性能的影响其实是比较大的;因此,JVM为了优化性能,在堆内存中(字符串常量池JDK1.7+在堆中)开辟了一部分空间,用来缓存字符串常量;在每次需要创建字符串对象的时候,先查询是否在字符串常量池中能找到这个字符串,如果找到,就直接返回引用,找不到的话,创建一个字符串对象,并放入字符串常量池中;
  • 字符串常量池的位置变化:
    • Jdk1.6及之前:有永久代, 运行时常量池在永久代,运行时常量池包含字符串常量池;
    • Jdk1.7:有永久代,但已经逐步“去永久代”,字符串常量池从永久代里的运行时常量池分离到堆里;
    • Jdk1.8及之后:无永久代,运行时常量池在元空间,字符串常量池里依然在堆里;

字符串的一些操作

  • 赋值,如图中所示,创建了1个对象,"maoway"这个字面量会存入字符串常量池中,然后str会将引用指向常量池;
String str = "maoway";
  • 显式创建对象,如图中所示,创建了2个对象,"maoway"这个字面量会存入字符串常量池中,然后会在堆内存中又创建一个String对象,并将str指向这个新创建的对象;
String str = new String("maoway");
  • intern()方法:当调用intern()方法时,如果字符串常量池中已经包含一个等于此String对象的字符串(用equals(oject)方法确定),则返回字符串常量池中的字符串;否则,将intern返回的引用指向当前字符串str1(jdk1.6版本之前会将str1复制到字符串常量池里)
String str1 = new String("maoway");
String str2 = str1.intern();

//字符串常量池里有,返回false
System.out.println(str1 == str2);
String str1 = new String("mao") + new String("way");
String str2 = str1.intern();

//字符串常量池里没有,返回true
System.out.println(str1 == str2);
  • 对于如下例子,如果一个字符串变量右边都是字面量的话,JVM会在编译器进行优化,以下代码等价于String str1 = "Maoway";
String str1 = "Mao" + "way";
  • 对于如下例子,当一个字符串变量右边有变量时,变量之间+号进行字符串连接,在底层会使用StringBuilder的append()方法进行拼接;
String str1 = "Mao";
String str2 = "way";

String = str1 + str2;

基本数据类型的包装类的常量池

  • Java中基本类型的包装类的大部分都实现了常量池技术(严格来说应该叫对象池),这些类是 Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现常量池技术。此外,Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用对象池,也即对象不负责创建和管理大于127的这些类的对象;因为一般这种比较小的数用到的概率相对较大;
  • 我们知道,基本数据类型的自动拆箱和装箱实际上(从编译后的字节码文件中可以得知)都是用的对应包装类的valueOf()方法实现的;我们来看看各个包装类的valueOf()方法源码实现;
//Long类型的valueOf()
public static Long valueOf(long l) {
    final int offset = 128;
    if (l >= -128 && l <= 127) { // will cache
        return LongCache.cache[(int)l + offset];
    }
    return new Long(l);
}

//Short类型的valueOf()
public static Short valueOf(short s) {
    final int offset = 128;
    int sAsInt = s;
    if (sAsInt >= -128 && sAsInt <= 127) { // must cache
        return ShortCache.cache[sAsInt + offset];
    }
    return new Short(s);
}

//Integer类型的valueOf()
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 =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            ......
            
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

//Byte类型的valueOf()
public static Byte valueOf(byte b) {
    final int offset = 128;
    return ByteCache.cache[(int)b + offset];
}

//Double类型的valueOf()
public static Double valueOf(double d) {
    return new Double(d);
}

//Float类型的valueOf()
public static Float valueOf(float f) {
    return new Float(f);
}

//Boolean类型的valueOf()
public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

//Character类型的valueOf()
public static Character valueOf(char c) {
    if (c <= 127) { // must cache
        return CharacterCache.cache[(int)c];
    }
    return new Character(c);
}
  • 接下来利用上面的valueOf()源码看一下下面这个例子:
//小于等于127,使用常量池
Integer i1 = 127; 
Integer i2 = 127;

//输出true
System.out.println(i1 == i2);

//大于127,直接创建对象,不使用常量池
Integer i3 = 128;
Integer i4 = 128;

//输出false
System.out.println(i3 == i4);

//两种浮点型包装类没有实现常量池技术,这边d1与d2会创建2个对象
Double d1 = 1.0;
Double d2 = 1.0;

//输出false 
System.out.println(d1 == d2);

总结

  • 只有对字符串常量池和基本数据类型的包装类常量池的底层细节有所了解后,在编写业务代码时才会更好地运用这些特性,在改善程序性能的同时也可以避免低级bug的出现。

注意:本文归作者所有,未经作者允许,不得转载

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Maoway稻草人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值