int和Integer解析

个人博客:www.letus179.com

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

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

  • 原始类型: booleancharbyteshortintlongfloatdouble
  • 包装类型:BooleanCharacterByteShortIntegerLongFloatDouble

两个常见的面试例子

先看两个常见的例子,后面会针对例子加以分析。
例1

public static void main(String[] args) {
        Integer a = new Integer(8);
        Integer b = 8;                  
        int c = 8;
        System.out.println(a == b);     
        System.out.println(a == c);     
    }
执行结果: false, true

例2

  public static void main(String[] args) {
        Integer f1 = 100, f2 = 100, f3 = 250, f4 = 250;
        System.out.println(f1 == f2);
        System.out.println(f3 == f4);
    }
执行结果: true, false

知识点一:自动拆箱与自动包装

概念

1.自动拆箱: 自动将包装器类型转换为基本数据类型

2.自动包装: 自动将基本数据类型转换为包装器类型

具体分析

例1

Integer b = 8;    //自动装箱
//Integer a = new Integer(8);
//int c = 8;
System.out.println(a == c) // 自动拆箱

注意
Integer与int比较时,会把Integer类型变量拆箱成int类型,然后比较。拆箱调用的是intValue()方法。

例1反编译看看(这里用jad来反编译), Test是例子中的类名。
下面命令将输出带字节码注释和源码

  • -a表示用JVM字节格式来注解输出;
  • -o表示无需确认直接覆盖输出;
  • -s表示定义输出文件的扩展名,默认的扩展名是jad;
  • java表示我们想要的反编译后输出java格式文件

jad详细命令参见反编译小工具:jad常用命令介绍

jad -a -o -s java Test.class

反编译结果:

package test;

import java.io.PrintStream;

public class Test {

  public Test() {
    //    0    0:aload_0         
    //    1    1:invokespecial   #8   <Method void Object()>
    //    2    4:return          
  }

  public static void main(String args[]) {
    Integer a = new Integer(8);
    //    0    0:new             #16  <Class Integer>
    //    1    3:dup             
    //    2    4:bipush          8
    //    3    6:invokespecial   #18  <Method void Integer(int)>
    //    4    9:astore_1        
    Integer b = Integer.valueOf(8);
    //    5   10:bipush          8
    //    6   12:invokestatic    #21  <Method Integer Integer.valueOf(int)>
    //    7   15:astore_2        
    int c = 8;
    //    8   16:bipush          8
    //    9   18:istore_3        
    System.out.println(a == b);
    //   10   19:getstatic       #25  <Field PrintStream System.out>
    //   11   22:aload_1         
    //   12   23:aload_2         
    //   13   24:if_acmpne       31
    //   14   27:iconst_1        
    //   15   28:goto            32
    //   16   31:iconst_0        
    //   17   32:invokevirtual   #31  <Method void PrintStream.println(boolean)>
    System.out.println(a.intValue() == c);
    //   18   35:getstatic       #25  <Field PrintStream System.out>
    //   19   38:aload_1         
    //   20   39:invokevirtual   #37  <Method int Integer.intValue()>
    //   21   42:iload_3         
    //   22   43:icmpne          50
    //   23   46:iconst_1        
    //   24   47:goto            51
    //   25   50:iconst_0        
    //   26   51:invokevirtual   #31  <Method void PrintStream.println(boolean)>
    //   27   54:return          
  }
}

可以看到第20, 22行,调用了Integer方法.valueOf(int)自动装箱

Integer b = 8;
Integer b = Integer.valueOf(8);

36行,调用了Integer方法.intValue()自动拆箱

System.out.println(a == c); 
System.out.println(a.intValue() == c);

所以:a == c的结果为true

例1

Integer a = new Integer(8);
Integer b = 8;
System.out.println(a == b);

结果为何为false

刚讲到了

Integer b = 8;

调用了Integer方法.valueOf(int)自动装箱,我们来看下.valueOf(int)源码实现:

public static Integer valueOf(int i) {
        assert IntegerCache.high >= 127;
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

也就是说最后会new出来一个Integer对象或者返回缓存中的数据。
注意:
+ ==符号在比较对象时,比较的是内存地址;
+ 对于原始数据类型(如上面a == c)直接比对的是数据值

这里又涉及到了堆栈内存了,需要清楚2点:
1. new出来的对象或创建的数组会在中开辟内存空间;
2. 对象的引用(即对象在堆内存中的地址,如a)和基本数据类型存储在中;

由此可知a,b引用指向的对象不是同一个,所以结果是false


知识点二:Integer缓存

在上面的.valueOf(int)源码中我们能看到IntegerCache类,看名称就知道是和缓存有关。我们来看下Integer类的静态内部类IntegerCache源码实现:

    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) {
                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);
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        }

        private IntegerCache() {}
    }

该类中有一个静态数组

static final Integer cache[];

还有一个静态代码块:

static {...}

既然是在static静态类的静态代码快中,也就是说在类加载的时候就会执行这部分代码逻辑。我们可以看到静态代码快主要是向静态数组中添加了[-128,127],也就要是说,调用方法.valueOf(int)传入的int值在[-128,127]这个范围内时,直接从IntegerCache的缓存数组中获取, 不会去在堆内存中new

[-128,127]期间的数字比较常用,这一行为有助于节省内存、提高性能。

if (i >= IntegerCache.low && i <= IntegerCache.high)
       return IntegerCache.cache[i + (-IntegerCache.low)];

所以在例2中:
1.自动装箱调用方法Integer.valueOf(int)

 public static void main(String args[])
    {
        Integer f1 = Integer.valueOf(100);
    //    0    0:bipush          100
    //    1    2:invokestatic    #16  <Method Integer Integer.valueOf(int)>
    //    2    5:astore_1        
        Integer f2 = Integer.valueOf(100);
    //    3    6:bipush          100
    //    4    8:invokestatic    #16  <Method Integer Integer.valueOf(int)>
    //    5   11:astore_2        
        Integer f3 = Integer.valueOf(250);
    //    6   12:sipush          250
    //    7   15:invokestatic    #16  <Method Integer Integer.valueOf(int)>
    //    8   18:astore_3        
        Integer f4 = Integer.valueOf(250);
    //    9   19:sipush          250
    //   10   22:invokestatic    #16  <Method Integer Integer.valueOf(int)>
    //   11   25:astore          4
    }

2.通过Integer.valueOf(int)内部调用IntegerCache类实现。
由于f1,f2对应的基本值在[-128,127]之间,结果返回true;
f3,f4对应的基本值不在范围内,结果返回false


其他的包装类型也可以类似分析。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值