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

int和Integer有什么区别?

https://www.nowcoder.com/questionTerminal/17f69e2a0f4e44728c66e304f3d431e7


💨 作者:laker,因为喜欢LOL滴神faker,又是NBA湖人队🏀(laker)粉丝儿(主要是老詹的粉丝儿),本人又姓,故取笔名:laker
❤️喜欢分享自己工作中遇到的问题和解决方案以及一些读书笔记和心得分享
🌰本人创建了微信公众号【Java大厂面试官】,用于和大家交流分享
🏰 个人微信【lakernote】,加作者备注下暗号:cv之道


面试题

定义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 万个整数的开销可不是一个数量级的,不管是内存使用还是处理速度,光是对象头的空间占用就已经是数量级的差距了。

参考:


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

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

全部评论

相关推荐

面试摇了我吧:啊哈哈面试提前五个小时发,点击不能参加就是放弃
点赞 评论 收藏
分享
我见java多妩媚:大外包
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务