Int 和 Integer 的联系

主要区别

int 是 Java的8个原始数据类型(Primitive Types,boolean、byte 、short、char、int、foat、double、long)之一。Java语言虽然号称一切都是对象,
但原始数据类型是例外。

Integer是int对应的包装类,它有一个int类型的字段存储数据,并且提供了基本操作,比如数学运算、int和字符串之间转换等。在Java 5中,引入了自动装箱和自动拆箱功能
(boxing/unboxing),Java可以根据上下文,自动进行转换,极大地简化了相关编程。

关于Integer的值缓存,这涉及Java 5中另一个改进。构建Integer对象的传统方式是直接调用构造器,直接new一个对象。但是根据实践,我们发现大部分数据操作都是集中在有
限的、较小的数值范围,因而,在Java 5中新增了静态工厂方法valueOf,在调用它的时候会利用一个缓存机制,带来了明显的性能改进。按照Javadoc,这个值默认缓存
是-128到127之间。

扩展

自动拆、装箱

语法糖

以简单理解为Java平台为我们自动进行了一些转换,保证不同的写法在运行时等价,它们发生在编译阶段,也就是生成的字节码
是一致的。

Integer.valueOf()、Integer.intValue()

javac 在装、拆箱子的过程,会自动调用这两个函数,valueOf() 对应装箱。这样自然能够得到缓存的好处。

并且这样的缓存机制并不是 Integer 独有,其他包装类也有,比如:

  • Boolean,缓存了true/false对应实例,确切说,只会返回两个常量实例Boolean.TRUE/FALSE。
  • Short,同样是缓存了-128到127之间的数值。
  • Byte,数值有限,所以全部都被缓存。
  • Character,缓存范围’\u0000’ 到 ‘\u007F’。

建议

尽量避免无意的拆、装箱,创建 10 万个 java 对象和 10 个整数的开销不是一个数量级。毕竟对象中的对象头的空间就占用很大了。

使用原始数据类型、数组甚至本地代码实现等,在性能极度敏感的场景往往具有比较大的优势,用其替换掉包装类、动态数组(如ArrayList)等可
以作为性能优化的备选项。一些追求极致性能的产品或者类库,会极力避免创建过多对象。

源码

Integer它主要包括各种基础的常量,比如最大值、最小值、位数等;静态工厂方法valueOf();获取环境变量数值的方法;各种转换方法(parseInt()),比如
转换为不同进制的字符串,如8进制,或者反过来的解析方法等。

缓存部分

Integer的缓存范围虽然默认是-128到127,但是是可以调节的,jvm 参数如下:

-xx:AutoBoxCacheMax=N

我们看看 valueOf() 是怎么获取缓存的。

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

可以看出,它主要是通过 IntegerCache 来获取,我们在看看这个 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");
            ...
            ...
            ...
            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() {}
    }

这样一来我们就指定,它的实现方式都在 IntegerCache 的静态初始化代码块里。

构造方法处

典型的构造方式如下:

  private final int value;

    /**
     * Constructs a newly allocated {@code Integer} ...       {@code Integer} object.
     */
    public Integer(int value) {
        this.value = value;
    }

我们知道 String 是不可变的。保证了信息安全和并发线程安全。其实其他包装类,它们存储数值的 value 自动,也是声明为 “private final” 不可变性。这样在对于某些可能会轻易更改数据的场景就有了保障。

线程安全问题

部分比较宽的数据类型,比如foat、double,甚至不能保证更新操作的原子性,可能出现程序读取到只更新了一半数据位的数值!

对比引用类型的局限性

原始数据类型不能和 java 泛型配合使用

Java 的泛型某种程度上可以算作伪泛型,它完全是一种编译期的技巧,Java编译期会自动将类型转换为对应的特定类型,这就决定了使用泛型,必须保证相应类型可以转换为 Object,而原始数据类型就要使用装箱操作。

无法高效的表达数据,也不能表达复制的数据结构

Java的对象都是引用类型,一个原始数据类型数组,它在内存里是一段连续的内存,而对象数组存储的是引用,对象往往是分散地存储在堆的不同位置。这种设计虽然带来了极大灵活性,结果导致了数据操作的低效,CPU缓存机制无法充分利用。

补充

对象的内存布局

对象头

标记字段
  • 存储 Java 虚拟机有关对象的运行数据

    • 哈希码
    • GC
    • 锁信息
  • 64 位虚拟机

    • 占 64 位
类型指针
  • 指向该对象的类,虚拟机通过这个指针来确定这个对象是哪个类的实例

  • 64 位虚拟机

    • 占 64位
    • 占 32 位

实例数据

实例数据部分是对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来。

对齐填充

并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大
小必须是8字节的整数倍。

原创文章 54 获赞 29 访问量 6412

猜你喜欢

转载自blog.csdn.net/qq_37391214/article/details/105869550