面试官:包装类型的缓存机制了解么?

看下面代码

java 复制代码
Integer a = 100;

Integer b = 100;
System.out.println(a == b);//true

Integer c = 200;
Integer d = 200;
System.out.println(c == d);//false

为什么第一个输出的是true,第二个输出的是false?

Integer a = 100的这种直接赋值操作,是调⽤Integer.valueOf(100)方法,从Integer.valueOf()源码可以看到,返回的是Integer对象,但这里的实现并不是简单的new Integer,而是先判断 i 这个值是否在IntegerCache范围内,如果在,直接返回IntegerCache中的值,如果不在则new Integer

java 复制代码
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 =
            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 cache 的下限是-128,上限默认127。当赋值100给Integer时,刚好在这个范围内,所以从cache中取对应的Integer并返回,所以a和b返回的是同一个对象,所以 比较是相等的,当赋值200给Integer时,不在cache 的范围内,所以会new Integer并返回,当然 比较的结果是不相等的。

扩展:Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False

java 复制代码
System.out.println(Integer.valueOf(-128) == Integer.valueOf(-128));//1.true
System.out.println(Integer.valueOf(127) == Integer.valueOf(127));//2.true
System.out.println(Integer.valueOf(128) == Integer.valueOf(128));//3.false
System.out.println(Integer.parseInt("128") == Integer.valueOf(128));//4.true

1、2、3都好理解,缓存范围是 [-128,127],1、2都在范围内,返回的是缓存中的对象,因此输出true,3不在范围内,返回的是新 new 的Integer,因此输出false。

那为什么4输出的是true呢? 128 在缓存范围外,按道理会 new 出一个Integer对象,为什么输出true呢?

  • 首先Integer.parseInt方法返回的是int 基本数据类型,不是对象,也就是说 Integer.parseInt("128") = 128

    java 复制代码
    public static int parseInt(String s) throws NumberFormatException {
        return parseInt(s,10);
    }
  • 当进行比较(==)运算时,会进行自动拆箱,也就是说 Integer.valueOf(128) 生成的 Integer 会自动拆箱成128,那么比较两个相等的额数值自然是true的

注意 :使用==运算符时,需要一边是基本数据类型才会自动拆箱,如果两边都是引用数据类型,是不会自动拆箱的。

当基础类型与它们的包装类有如下几种情况时,编译器会自动进行装箱或拆箱:

  • 赋值操作(装箱或拆箱)
  • 进行加减乘除混合运算 (拆箱)
  • 进行>,<,>=,<=,==比较运算(拆箱)
  • 调用equals进行比较(装箱)
  • ArrayList、HashMap等集合类添加基础类型数据时(装箱)

注意:三目运算符 condition ? 表达式 1:表达式 2 中,高度注意表达式 1 和 2 在类型对齐时,可能抛出因自动拆箱导致的 NPE 异常

  1. 表达式 1 或 表达式 2 的值只要有一个是原始类型。
  2. 表达式 1 或 表达式 2 的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。
java 复制代码
Integer a = 1;
Integer b = 2;
Integer c = null;
Boolean flag = false;
// a*b 的结果是 int 类型,那么 c 会强制拆箱成 int 类型,抛出 NPE 异常
Integer result = (flag ? a * b : c);

缓存机制存在的原因:将频繁被使用的对象缓存起来,可以提升读取的效率,这是一个典型的用空间换时间的例子(其实缓存机制都是这个原理),而Java开发者认为[-128,127]是比较常使用的范围。

相关推荐
考虑考虑14 小时前
Jpa使用union all
java·spring boot·后端
用户37215742613514 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊15 小时前
Java学习第22天 - 云原生与容器化
java
渣哥17 小时前
原来 Java 里线程安全集合有这么多种
java
间彧17 小时前
Spring Boot集成Spring Security完整指南
java
间彧17 小时前
Spring Secutiy基本原理及工作流程
java
Java水解18 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆21 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学21 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole21 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端