聊聊 Integer 吧

简介: 当我们开发的越久,越能体会到基础知识的重要性。抽空捋一下 JDK 源码,权当查漏补缺。读完之后,你会发现 JDK 源码真的会给你很多惊喜

写在前面

当我们开发的越久,越能体会到基础知识的重要性。抽空捋一下 JDK 源码,权当查漏补缺。读完之后,你会发现 JDK 源码真的会给你很多惊喜。

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

为什么需要引入 Integer(包装类)

在 Java 中,万物皆对象,所有的操作都要求用对象的形式进行描述。但是 Java 中除了对象(引用类型)还有八大基本类型,它们不是对象。那么,为了把基本类型转换成对象,最简单的做法就是将基本类型作为一个类的属性保存起来,也就是把基本数据类型包装一下,更加符合面向对象的思想,这也就是包装类的由来。

创建 Integer

创建 Integer 的方式:

Integer a = 1;
Integer b = new Integer(2);
Integer c = Integer.valueOf(3);
复制代码

第一种较为常见,后两种看似更符合 java 语法的概念,众所周知,java 类型分为基本类型和对象类型两种,基本类型为 char,int,double 等,对象类型有 jdk 对象和自己创建的对象,所以 Integer 属于对象类型,int 属于基本类型,对 jvm 而言,类似于 Integer a = 1 这种将基本类型赋值给对象类型的操作是不允许的,但是在日常开发过程中使用这种创建对象的方式并不会报错,原因是编译器的语法糖技术,(语法糖是指在不改变 jvm 语法的情况下,用于方便编程人员编码的操作)。

Integer 缓存

在 Integer 类内部定义了一个私有的静态类 IntegerCache,用来实现 Integer 的缓存常量池。它负责存储(high - low)个静态 Integer 对象,并且在静态代码块中初始化。默认范围是【-128,127】,所以这里默认只实例化了 256 个 Integer 对象,当 Integer 的值范围在【-128,127】时则直接从缓存中获取对应的 Integer 对象,不必重新实例化。这些缓存值都是静态且 final 的,避免重复的实例化和回收。

如果不去配置虚拟机参数,这个值不会变。配合 valueOf(int) 方法,可以节省创建对象造成的资源消耗。另外如果想改变这些值缓存的范围,在启动 JVM 时可以通过 -Djava.lang.Integer.IntegerCache.high=xxx 就可以改变缓存值的最大值。

直接深入源码进行分析:

// Integer类中私有的静态类 承载 cache 的实现
private static class IntegerCache {
    static final int low = -128;// 最小支持为-128
    static final int high;// 最大支持
    static final Integer cache[];// 用来装载缓存  常量池
    static {
        // -128~127 这个范围的整数值是使用最广泛的
        int h = 127;
        // Java8 中可以通过调整JVM启动参数来设置最大值
        // 根据应用程序的实际情况 灵活的调整来提高性能
        String integerCacheHighPropValue =
        sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                // 获取较大者
                i = Math.max(i, 127);
                // 设置最大值不能超过 Inter.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // 如果该值配置错误则忽略该参数配置的值,使用默认范围-128~127
            }
        }
        high = h;
        // 初始化数组容量为127 + 128 + 1(以默认区间为参考)
        cache = new Integer[(high - low) + 1];
        int j = low;
        // 缓存通过 for 循环来实现,创建范围内的整数对象并存储到 cache 数组中
        // 程序第一次使用 Integer 的时候需要一定的额外时间来初始化该缓存
        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() {}
}
复制代码

hashCode()

@Override
public int hashCode() {
    return Integer.hashCode(value);
}
public static int hashCode(int value) {
    return value;
}
复制代码

即 Integer 的 hashCode 值返回对象本身的 value 值。

equals()

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}
public int intValue() {
    return value;
}
复制代码

equals 方法比较的是两个对象的 value 值,即两个 Integer 对象只要逻辑上数值一致,则 equals 方法返回 true。

Integer 和 int 区别

1、Integer 是 int 的包装类,int 则是 java 的一种基本数据类型。

2、Integer 变量必须实例化后才能使用,而 int 变量不需要。

3、Integer 实际是对象的引用,当 new 一个 Integer 时,实际上是生成一个指针指向此对象;而 int 则是直接存储数据值。

4、Integer 的默认值是 null,int 的默认值 0。

关于 Integer 和 int 比较

通过分析六种不同类型的比较,进而真正弄懂 Integer 与 int 的区别,这也是面试中经常会问到的地方。

一、两个 Integer 对象通过 == 比较

Integer a = new Integer(100);
Integer b = new Integer(100);
System.out.println(a == b); // 输出false
复制代码

因为 a 和 b 是通过对 Integer 对象的引用,进行 new 操作产生了两个内存地址不同的对象,对象之间使用 == 实际上是在比较地址,所以是 false。

二、两个对象通过 equals 方法比较

Integer a = new Integer(100);
Integer b = new Integer(100);
System.out.println(a.equals(b)); // 输出true
复制代码

当调用 equals 方法进行比较时,只要两个对象的值相同,则返回 true。Integer 重写了 equals 方法,核心逻辑是判断对象表示的 value 值是否相同。

三、基本类型和 Integer 类型 通过 == 比较

Integer a = new Integer(100);
int b = 100;
System.out.println(a == b); // 输出true
复制代码

因为 Integer 和 int 在进行比较时,java 会将 Integer 自动拆包装为 int 类型,所以实际上是两个 int 变量进行比较,如果值相等的话,则结果为 true。

四、在缓存范围内的比较

Integer a = 100;
Integer b = 100;
System.out.println(a == b); // 输出true
System.out.println(a.equals(b)); // 输出true
复制代码

对应非 new 生成的 Integer 对象,Java 会通过自动装箱机制,调用 Integer 的 valueOf 方法将 int 类型转为 Integer 类型。如果该 int 值在【-128,127】区间范围内,则生成的对象引用都会指向 Integer 内部的常量池中对应的值的对象,因此两个引用指向的内存地址也相同即 == 时返回 true,调用 equals 时比较的是 value 值,因此也返回 true。

五、在缓存范围外的比较

Integer a = 128;
Integer b = 128;
System.out.println(a == b); // 输出false
System.out.println(a.equals(b)); // 输出true
复制代码

非 new 生成的 Integer 对象,如果 int 值超出了【-128,127】这个区间,则不会使用常量池中的对象,由 valueOf 方法可知会重新 new 一个 Integer 对象。因此两个引用 a 和 b,调用 == 比较时是比较的对象内存地址,所以返回 false;但是调用 equals 方法比较的是 value 值,因此返回 true。

六、new 生成的 Integer 对象与直接赋值的 Integer 对象的比较

Integer a = new Integer(100);
Integer b = 100;
System.out.println(a == b); // 输出false
System.out.println(a.equals(b)); // 输出true
复制代码

当非 new 生成的对象和 new 生成的对象进行比较时,非 new 生成的对象根据其 value 值是否在默认缓存区间内而选择是否复用对象,通过 new 生成的对象是在堆中重新开辟的内存空间,因此两者指向的内存地址肯定不一样,所以调用 == 时返回 false;equals 方法比较对象的逻辑值 value,因此返回 true。

开发建议

1、int 是基本数据类型,只占用 4 个字节,Integer 是一个对象,当表示一个值时 Integer 占用的内存空间要高于 int 类型,从节省内存空间考虑,建议使用 int 类型。

2、Integer 类型必须进行初始化后才能使用,否则会引起 NullPointerException 异常。

3、针对一些特殊的场景比如考试成绩分为没有参加考试和成绩为 0,int 的默认值为 0,显然不合适这种场景;Integer 可以区分出未赋值和值为 0 的区别。

4、使用 Integer 类型时,建议采用直接赋值的形式而不是通过 new 产生新对象,提高对内存的利用率。

Integer a = 100;
替代
Integer a = new Integer(100);
复制代码

5、当程序中大量使用数值时,可以根据实际情况适当扩展常量池缓冲区的区间上限,修改 JVM 的启动参数 -Djava.lang.Integer.IntegerCache.high=xxx,进而节省内存,提升性能。

6、当使用 Integer 类型时,在进行两个对象比较的时候,推荐使用 equals 方法,而不是直接调用 “==”。

扩展

Java 中还有与 Integer 类似的是 Long ,它也有一个缓存,在区间【-128,127】范围内获取缓存的值,而 Long 与 long 比较的时候先转换成 long 类型再做值的比较。

Double 类型,它没有缓存,但是当 Double 与 double 比较的时候会先转换成 double 类型,再做值的比较。

结语

有时候往往越简单的知识越容易掉坑里,所以要保持自己的求知欲,不断巩固的基础,才能让自己在面试的时候不会栽跟头。

相关文章
|
5月前
|
Java API
将`List<String>`转换为`List<Long>`
将`List<String>`转换为`List<Long>`
|
6月前
|
Java 编译器
Integer类超详解-1
Integer类超详解
86 0
|
6月前
|
Java
Integer类超详解-2
Integer类超详解
39 0
|
缓存 Java API
Integer和int的区别
Integer和int的区别
|
安全
Synchroinzed对Integer的问题
Synchroinzed对Integer的问题
118 0
Synchroinzed对Integer的问题
Zp
|
缓存 Java
Integer的比较和注意点
Integer的比较和注意点
Zp
92 0
Integer的比较和注意点
|
存储 缓存 Java
【每天一道面试题】Integer、 new Integer() 、int的==比较,你全都了解吗
【每天一道面试题】Integer、 new Integer() 、int的==比较,你全都了解吗
155 0
【每天一道面试题】Integer、 new Integer() 、int的==比较,你全都了解吗
|
Java
Java数据类型中String、Integer、int相互间的转换
Java数据类型中String、Integer、int相互间的转换
196 0
|
存储 Java
int 和 Integer 有什么区别
引用类型和原始类型的行为完全不同,并且它们具有不同的语义。
64 0