Java 中 int 和 Integer 的区别

一、 简单介绍

Java 是一门面向对象的编程语言,分为:基本类型(primitive types)和引用类型(reference types),详情见博客- 基本类型

1、关系

int 是我们经常使用的基础数据类型,在我们的使用中经常出现,而Integer在我们的日常工作中也会出现,那么他们的关系是什么呢?


Integer 是 int 的包装类,jdk 1.5以后添加了自动拆装箱功能。也就是在编译期可以将int 装箱为 Integer ,也可以将Integer 拆箱为 int ,这里涉及一个缓存的问题下面会讲,

2、注意

虽然自动拆装箱为我们勉去了手动进行这些操作的麻烦,但是我们还是要尽量避免无意中的拆装箱行为,尤其是在对于性能很敏感的地方。【毕竟创建大量对象和创建大量整数的开销不一样,效率差距很大】。

二、 代码层次理解

1、关于Number

所有基础数据类型的包装类都是继承了Number父类

public final class Double extends Number 
implements Comparable<Double> {}

  • Number类代码:
public abstract class Number implements java.io.Serializable {
    public abstract int intValue();
    public abstract long longValue();
    public abstract float floatValue();
    public abstract double doubleValue();
    public byte byteValue() {
        return (byte)intValue();
    }
    public short shortValue() {
        return (short)intValue();
    }
    private static final long serialVersionUID = -8742448824652078965L;
}

所有的包装类型都继承了该类 ,该类又实现了序列化接口。

2、Integer

Integer 实现的时候 内部有成员变量 value 该 变量使用 private final 进行修饰 ,第一点 使用private 是防止我们主动的去获取到内部的value 值,并进行修改,但是因为java 提供了反射能力 ,所以我们还是可以获取到 Integer 内部的 value 所以我们使用final 修饰 直接阻断这一可能。

Integer 内部提供了缓存机制 就是如果我们获取的整数在-128 -127 之间的 直接从缓存中取出。

  • private final int value;
  • 使用valueOf()完成了缓存功能:
public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

如果值得范围在-128 到 127 之间 就从缓冲中去 ,否则 创建新的Integr对象。
IntegerCache 源代码:

扫描二维码关注公众号,回复: 10527893 查看本文章
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;
        }

        private IntegerCache() {}
    }

3、其他

首先 原始数据类型不是线程安全的 ,如果使用原始数据我们需要结合其他手段才可以达到线程安全比如: volitale,此外在我们需要有线程安全的场景时建议使用 : Atomic 原子类。【对于float、long这种比较宽的数据类型甚至会出现不能保证更新操作的原子性,可能出现程序读取到了更新了一半的数值。】。

还有就是 原始数据类型不支持泛型,而泛型中使用的都是引用类型的数据。

原始类型创建 开销小,而引用类型创建开销大【因为需要维护类型 等其他相关信息】。所以我们发现Integer 的缓存也是为了提高效率 ,提升性能的因为我们一般使用的数据都在-128 到127 之间,这样不至于频繁创建Integer对象 。

三、 深层次理解

1、谈一下 对象的组成

对象由三部分组成:对象头、对象实例、对其填充。

  • 对象头:

对象头一般是16个字节,包括两部分第一部分是:哈希吗、锁状态标志、线程持有的锁、偏向锁ID、GC 分代年龄等,第二部分是:类型指针【对象指向他属于的类的信息】。

  • 对象实例:

对象实例就是对象存储的真正有效信息,也是程序中定义各种类型的字段包括父类继承和子类定义的,这部分的存储顺序会被虚拟机和代码中的定义顺序影响【虚拟机的volatile 指令重排会影响】,

  • 对其填充:

相当于占位符的作用,因为内存的使用需要是8字节的倍数。


知道了上面这些 ,我们就应该知道 创建一个对象需要存储的数据是很多的,所以这就是为什么我们要尽量去避免使用包装类型来替代基础类型。
创建一万个 整数 和创建一万个对象,所占用的存储以及效率是有很大区别的。

2、总的谈一下

因为原始类型和Java泛型不能配合使用,所以Java设计了自动拆装箱这个性能【其实也就是基础类型和Object之间的转换】,这样避免了我们在使用的时候自己去显示的进行转换,

对于原始数据我们内存中存的就是一个数,获取的时候直接获取到的就是这个数本身,而对于引用类型 ,我们首先要去获取这个对象的引用 ,然后通过引用地址去找到堆上具体的数,这样多了一次IO,而IO 是很影响性能的,在加之对象存储所占用的空间也大,所以对象的性能 在计算的时候相较于基础类型是要差许多的,所以为了弥补引用类型在计算时性能差的问题 引入了缓存比如Integer 引入了 static valueOf() 的缓存机制,其他的如Boolean 、Char 等也都有相应的实现。

所以我们在进行计算的时候,最好能使用原始数据类型的话 尽量使用 原始数据类型, 而 对于我们需要使用泛型的时候 使用引用类型即可。

发布了122 篇原创文章 · 获赞 32 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/YangzaiLeHeHe/article/details/100131088