一文干翻Integer、int等基础数据类型和包装类型相关问题


本文编写灵感来源我之前整理的面经的题目之一:

2020年11月最新互联网大厂面试经验分享【网易、阿里、腾讯、京东、百度、爱奇艺、字节、小米、美团、搜狐、58】

定义Integer x=20 Integer y=200 在内存里是个什么过程?

首先这题,会牵扯出来好几个知识点,我自己能力范围内看到的如下:

  • 自动拆装箱
  • 静态工厂使用的缓存机制

下面我们来带着问题分析解读。

开始分析

1、什么是自动拆装箱?

其实就是基本数据类型和其包装类由 javac 相互转换。

Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转换。

Java 为每个原始类型提供了包装类型:

原始类型: boolean,char,byte,short,int,long,float,double

包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

举例 Integer:

装箱:调用 Integer.valueOf(),int转Integer

拆箱:调用 Integer.intValue(),Integer转int

自动装箱拆箱是在编译期实现的,可以通过反编译字节码文件求证。

源码:

Integer a = 3; // java/lang/Integer.valueOf (I)
int b = new Integer(100); // java/lang/Integer.intValue ()I

字节码

   L0
    LINENUMBER 5 L0
    ICONST_3
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    ASTORE 1
   L1
    LINENUMBER 6 L1
    NEW java/lang/Integer
    DUP
    BIPUSH 100
    INVOKESPECIAL java/lang/Integer.<init> (I)V
    INVOKEVIRTUAL java/lang/Integer.intValue ()I
    ISTORE 2
   L2

2、为什么会存在原始类型、包装类型以及自动拆装箱机制呢?

原始数据类型和 Java 泛型并不能配合使用,之前没有自动拆装箱机制的时候,开发者就必须每次手动显示转换,才有了Java5中引入自动拆装箱机制。

List<int> // 这个是编译不过的
List<Integer>

原始类型在内存中存的是值,所以找到内存位置,就可以获得值;

包装类型存的是引用地址,找到对象的引用内存位置,还要根据引用内存位置找下一个内存空间,要产生更多的IO,所以计算性能比原始类型差,但是包装类型具备泛化的能力,更抽象,解决业务问题编程效率高。

如果开发者要做计算,就应该使用原始类型,如果开发者要处理业务问题,就应该使用object,采用Generic机制;反正JAVA有auto-boxing/unboxing机制,对开发者来讲也不需要注意什么。然后为了弥补object性能的不足,还设计了static valueOf()方法提供缓存机制,算是一个弥补。

3、静态工厂使用的缓存机制到底是个啥?

在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。

这种 Integer 缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器创建的 Integer 对象不能被缓存

  • 在自动装箱(autoboxing)的时候,调用valueOf()方法。

    这里是使用了享元模式实现的范围缓存支持

public static Integer valueOf(int i) {
    // low -128 , high 127
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
  • 这个类是用来实现缓存支持,并支持 -128127 之间的自动装箱过程。最大值 127 可以通过 JVM 的启动参数 -XX:AutoBoxCacheMax=size 修改.
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;
    }

相关题目

基础知识

对于对象引用类型:== 比较的是对象的内存地址。

对于基本数据类型:== 比较的是值。

== 比较题目

如果整型字面量的值在 -128 到 127 之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围 return new Integer(i);

    Integer a = new Integer(3);
    Integer b = 3;  // 将3自动装箱成Integer类型
    int c = 3;
    System.out.println(a == b); // false 两个引用没有引用同一对象
    System.out.println(a == c); // true a自动拆箱成int类型再和c比较
    System.out.println(b == c); // true
    
    Integer a1 = 128;
    Integer b1 = 128;
    System.out.println(a1 == b1); // false

    Integer a2 = 127;
    Integer b2 = 127;
    System.out.println(a2 == b2); // true

int和Integer有什么区别?

Integer相关设计模式?

总结

其他整型类型的缓存机制

  • 这种缓存行为不仅适用于Integer对象。我们针对所有整数类型的类都有类似的缓存机制。

​ Byte,Short,Long 有固定范围: -128 到 127。对于 Character, 范围是 0 到 127。除了 Integer 可以通过参数改变范围外,其它的都不行。

  • 特别注意 Double Float 没有使用到缓存机制
Double i = 100.0;
Double j = 100.0;
System.out.print(i == j);// false

装箱和拆箱在编程实际中注意点

​ 建议避免无意中的装箱、拆箱行为,尤其是在性能敏感的场合,创建 10 万个 Java 对象和 10 万个整数的开销可不是一个数量级的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。

参考:

  • 杨晓峰的极客时间专栏
  • https://www.jianshu.com/p/734ee940f431

这是面试题解读的第一篇,后面还会继续,希望大家批评指正。
我想成为架构师
QQ群【837324215
关注我的公众号【Java大厂面试官】,回复:架构资源等关键词(更多关键词,关注后注意提示信息)获取更多免费资料。

公众号也会持续输出高质量文章,和大家共同进步。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

lakernote

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

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

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

打赏作者

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

抵扣说明:

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

余额充值