Java包装类常量池,String字符串常量池

前言

基本类型直接存放在栈中,所以只有包装类常量池,
Java在jdk1.5后包装类常量池使用缓存实现,本文使用1.8演示
由于字符串常量池有些特殊,我门先来理解包装类常量池,也有助于接下来理解字符串常量池

包装类常量池

基本概念

  1. Integer 、Long、Short、Byte、Character、Double、Float、Boolean八种基本类型中Double、Float没有常量池,Boolean只有true和value,其他5种基本相同。
  2. java使用缓存维护了一个 [-128,127] 的常量,正好是255个数,1个字节byte所能表示最大值

以Integer为例,源码分析 使用数组存放,最小值限定为-128,最大值可配置,但不能小于127,否则抛出错误。

	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;
           	// 通过jvm获取缓存最大值
            String integerCacheHighPropValue =
                sun.misc.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() {}
    }

Integer 常量池

上代码
Integer a = 128;
Integer b = 128;
Integer a2 = 127;
Integer b2 = Integer.valueOf(127);
System.out.println("1> a == b:" +(a == b));
System.out.println("2> a2 == b2:" + (a2 == b2));

Integer c = new Integer(128);
System.out.println("3> b=c:" + (b == c));

System.out.println("4> b=c.Intner:" + (b == Integer.valueOf(128)));

Integer d = 127;
Integer e = new Integer(127);
System.out.println("5> d==e:" + (d == e));
System.out.println("6> d = Integer.valueOf" + (d == Integer.valueOf(127)));
System.out.println("7> e = Integer.valueOf" + (e == Integer.valueOf(127)));
System.out.println("8> e.equals(d):" + e.equals(d));

// 执行结果
1> a == b:false
2> a2 == b2:true
3> b=c:false
4> b=c.Intner:false
5> d==e:false
6> d = Integer.valueOf true
7> e = Integer.valueOf false
8> e.equals(d):true
分析

1>使用=号赋值,导致强制拆包装,查看字节码文件,可知调用了Integer.valueOf方法。

在这里插入图片描述

调用了Integer.valueOf方法,下面是该方法方法体,可知,如果缓存里有该值,则返回缓存里对象,没有则创建一个新的对象。因为Integer缓存存放[-128,127]之间的数,所以1>为false。
在这里插入图片描述

2> a2=127自动拆装箱等于调用Integer.valueOf()方法,所以等于b2直接使用Integer.valueOf()方法

3> c是直接new对象,存放的是新的内存地址,b=128不在缓存范围中,因此也是直接new Integer,两个对象在内存中的地址不一样,所以false。

4> 与3>情况类似,哈哈,🐶

5> d=127使用自动拆装箱,会到缓存中获取,e为new对象,所以不相同

6> 因为自动拆装箱其实本质也是调用Integer.valueOf()方法,所以,为true

7> e为new对象,Integer.valueOf(127)是获取缓存中的对象,因此false

8> 在阿里java泰山版中,有这样一段话
在这里插入图片描述
说明中已经说明了原因,我就不再赘述,我们一起来看一下equals方法
在这里插入图片描述
实际就是value和intValue进行比较,我们来看一下intValue()和value
在这里插入图片描述在这里插入图片描述
其实就是Integer对象里维护的final常量int值,该int值在对象被加载时,存放在方法区的元空间中(jdk1.8叫法),所以,equals比较的就是这两个int值,不论是直接复制,new对象,调用Integer.valueOf方法,只要是同一个数字,就都相等,所以为true.

字符串常量池

基本概念

  1. 为什么要设计字符串常量池?因为字符串的创建要耗费大量的时间和空间,频繁创建字符串影响程序性能。
  2. 创建(无论是直接赋值,还是new对象)字符串时,首先会判断字符串常量池是否有该字符串,如果有会直接引用,如果没有才会实例化该字符串,并放入常量池
上代码
public static void main(String[] args) {
	String str1 = "abc";
    String str2 = "abc";
    String str3 = new String("abc");
    String str4 = new String("abc");
    String str5 = new String("def");
    System.out.println("1> str1 == str2:" + (str1 == str2));
    System.out.println("2> str3 == str4:" + (str3 == str4));
    System.out.println("3> str1 == str3:" + (str1 == str3));
    System.out.println("4> str1.equals(str3):" + str1.equals(str3));
    System.out.println("5> str1 == str3.intern():" + (str1 == str3.intern()));
    compareString(str1);
}

private static void compareString(String str0) {
    String str = "abc";
    System.out.println("6> str == str0:" + (str == str0));
}

// 执行结果
1> str1 == str2:true
2> str3 == str4:false
3> str1 == str3:false
4> str1.equals(str3):true
5> str1 == str3.intern():true
6> str == str0:true
分析

1> str1和str2都是压栈了String abc,所以是相等的
在这里插入图片描述
2>str3和str4都是创建了新对象,所以是不相等的,但是其 本质还是都引用了常量池中的abc,5>就是证明。

3>str1是直接引用常量池,str3是先指向String对象再指向常量池,是间接引用,所以false

4>equals就是用来解决直接指向和间接指向的,由源码可知其内部通过char进行逐个比较,全部相等则相等。
在这里插入图片描述
5>str1=str3.intern(),为啥就相等了呢?intern方法返回的是什么呢?
在这里插入图片描述
所以,使用intern方法,会获取该字符串的引用。
这时,你可能有另外一个疑问,那会不会是在调用intern方法时才从字符串常量池中获取引用,在new String()时,new的对象指向了另外一个字符串?
别着急,我来来看第6行打印。

6> 在调用compareString()时传入了一个字符串常量池引用,在方法体中又创建了一个字符串常量池的引用,那肯定是true啊!有这个想法就对了,我们来看一下new String()时发生了什么!
在这里插入图片描述
在这里插入图片描述
new String()也是传入了一个字符串常量。
其内部实现就是将字符串常量value char数组引用赋值了new对象中的value,所以new对象本质也是指向了字符串常量的引用地址。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值