Integer (int)类型分析及相关面试题

760 阅读2分钟

Integer类是Java开发中最常用的基础类之一,也是基本数据类型int对应的包装类。Integer能使在Java中像类一样使用整型数字,自Java 5引入自动装箱拆箱机制以后,能够混合使用Integerint来表示整型数字,极大的方便了对数字的操作。同样如果使用不当,也有意想不到的结果产生。所以通过之前的学习和开发过程中踩过的坑,本文总结一下Integer需要注意的一些使用问题和常遇到的相关面试题。

Integer对象 和 int 进行比较

Integer是类,有两种创建方式:= 赋值和new关键字,int是基本类型,只能通过 = 赋值。那么Integerint比较,结果如何呢?请看下面的代码例子:

public void intCompareInteger() {
        for (int i = -200; i < 200; i++) {
            int num = i;
            Integer integer = i;
            Integer integer2 = new Integer(i);
            System.out.println(num == integer);
            System.out.println(num == integer2);
        }
}

// 执行结果:
true
true
true
...
true

运行结果表示数值相同的Integer对象和int变量是相等的,而不会使用对象的比较方法,这也是因为自动拆箱的存在,当基本数据类型与其包装类使用关系操作符 == 进行比较时,其包装类对象会自动拆箱进行运算,因此以上代码是转换为了两个int进行比较。

另外当包装类参与算术操作符运算时,也会被自动转成基本数据类型,请看下面的案例:

public void calIntAndInteger() {
        int a = 20;
        int b = 38;
        Integer c = 18;
        Integer c1 = new Integer(18);
        System.out.println(b == c + a);
        System.out.println(b == c1 + a);
}

// 执行结果:
true
true

因为算术运算符并不适用于Java中的对象,所以参与运算的对象被自动拆箱成了基本数据类型。以上两种现象也是自动拆箱机制所起到的一项重要作用,包装类参与到操作符运算时会先拆箱成基本数据类型,再运算。

自动装箱拆箱解析

上文的案例代码中多处用到了自动装箱拆箱的机制,可以通过字节码了解到具体是如何实现的:

// Java源代码
public static void main(String[] args) {
        Integer a = 15;
        int b = a;
}

// class字节码
public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 10 L0
    BIPUSH 15
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    ASTORE 1
   L1
    LINENUMBER 11 L1
    ALOAD 1
    INVOKEVIRTUAL java/lang/Integer.intValue ()I
    ISTORE 2
   ...
   ...                 
}

有上面的字节码可以看出来:

  1. Integer a = 15对应L0,主要是调用了静态方法Integer.valueOf(15),方法内部实现在下文说明;
  2. int b = a对应L1,主要调用了成员方法a.intValue(),即直接返回了15。

Integer两种创建方式对比

Integer除了正常创建对象外,还可以通过自动装箱机制,直接接收整型数字。那么这两种方式创建出来的对象又有什么不一样呢?

public void compareInteger() {
        // 1
        for (int i = -150; i < 150; i++) {
            Integer a = i;
            Integer b = new Integer(i);
            System.out.println("i = " + i + ": " + (a == b));
        }
        // 2
        for (int i = -150; i < 150; i++) {
            Integer c = new Integer(i);
            Integer d = new Integer(i);
            System.out.println("i = " + i + ": " + (c == d));
        }
        // 3
        for (int i = -150; i < 150; i++) {
            Integer e = i;
            Integer f = i;
            System.out.println("i = " + i + ": " + (e == f));
        }
}

// 执行结果:
// 1
i = -150: false
i = -149: false
i = -148: false
...
// 2
i = -150: false
i = -149: false
i = -148: false
...
// 3
i = -150: false
...
i = -128: true
...
i = 127: true
i = 128: false
...

根据案例中的执行结果分析,循环1中a和b均不相等,因为有明显的新对象产生,而 == 是比较两个对象的地址值,因此结果容易理解。循环2也是如此。但是循环3中却出现了不同结果,只有在区间 [-128,127] 之间是相等的,那么到底有没有产生新对象呢?为了一探究竟,查看了上文提到的Integer.valueOf()方法的源码:

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 {
            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);
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                }
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            assert IntegerCache.high >= 127;
        }
        private IntegerCache() {}
}

先将该值和IntegerCache类中的两个静态常量high和low对比。那就先看IntegerCache类,它是属于Integer中的一个静态内部类。重点需要关注的有以下两点:

  1. 包含两个int类型的常量 low = -128 和high 以及一个Integer数组。
  2. 静态初始化块中,主要将 high 赋值为127,将以 [-128,127] 区间的整数为值的Integer对象存入cache数组。

再来回头看valueOf方法首先判断 i 是否在 low 和 high 这个区间内,是,则直接返回cache数组中值为 i 的Integer对象,否则就会返回新建的一个对象。这也就说明了为什么只在 [-128,127] 之间结果为true的原因,因为此时的对象均为同一个对象。

通过上述问题引出了Integer类中的缓存类,同样的在另外几个包装类中也存在类似的缓存机制,分别缓存了相应的一些值。

基本类型包装类取值范围缓存范围
byteByte-128 ~ 127-128 ~ 127
shortShort-32768 ~ 32767-128 ~ 127
intInteger-2^31 ~ 2^31-1-128 ~ 127
longLong-2^63 ~ 2^63-1-128 ~ 127
floatFloat请参见源码--
doubleDouble请参见源码--
charCharacter\u0000 ~ \uFFFF\u0000 ~ \u007F
booleanBooleantrue,falsetrue,false

原文地址 --- Integer (int)类型分析及相关面试题