Java自动拆箱装箱机制

目标

了解什么是装箱和拆箱

掌握自动拆装箱机制原理

了解缓存池

初步了解

  1. 装箱:基本数据类型——>包装数据类型(与基本数据类型对应的引用数类型)
  2. 拆箱:包装类型——>基本数据类型
  3. JDK1.5开始提供自动拆装箱机制,JDK1.5之前都是需要手动进行

自动装箱

// 自动装箱机制
int a = 5;
Integer b = a; 
// 或
Integer b = new Integer(a);

装箱的本质

  上面代码中基本数据类型的变量a赋值给了引用数据类型b,这在传统上的认知中是不可能的。

JDK1.4.2

JDK1.4API文档

// 返回一个 Integer对象,保存指定的值为 String 。
public static Integer valueOf(String s) throws NumberFormatException {
    return Integer.valueOf(parseInt(s, 10));
}
// 返回一个 Integer对象,保存从指定的String中 String的值,当用第二个参数给出的基数进行解析时。
public static Integer valueOf(String s, int radix) throws NumberFormatException {
    return Integer.valueOf(parseInt(s,radix));
}

JDK1.5开始

https://docs.oracle.com/javase/1.5.0/docs/api/

// 返回一个 Integer指定的 int值的 Integer实例。
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

  本人经过对比JDK1.4.2与JDK1.5.0的API文档,发现在JDK1.4.2中Byte,Short,Integer,Long,Character这五个包装类是不存在valueOf(int i)方法的,JDK1.5.0中发现这五个包装类的方法说明中都多出一个valueOf(int i)方法。
  同时JDK1.5才开始提供自动拆装箱机制,由此,可以说明Byte,Short,Integer,Long,Character这五个包装类的拆装箱机制是依赖方法valueOf(int i)实现的。
  当然,Double的自动拆装箱机制也是通过valueOf来实现的。Boolean在JDK1.4就存在与之相对应的valueOf,但是我更偏向于Boolean不存在自动拆装箱机制。

小结

  1. 装箱的本质是通过方法valueOf来实现的

  2. 为什么是自动呢?

    个人认为,int a=5; Integer b = a;Integer b = a自动调用了valueOf方法

方法valueOf

  通过查看源码发现,Byte,Short,Integer,LongCharactervalueOf方法有些出入,实现原理一致,但是缓存池数据范围不一致。

  • Byte,Short,Integer,Long的缓存池范围是byte的数据范围,即[-128,127]
/**
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
  • Character的缓存池范围是[0,127]
/**
 * This method will always cache values in the range {@code
 * '\u005Cu0000'} to {@code '\u005Cu007F'}, inclusive, and may
 * cache other values outside of this range.
 *
 * @param  c a char value.
 * @return a <tt>Character</tt> instance representing <tt>c</tt>.
 * @since  1.5
 */
public static Character valueOf(char c) {
    if (c <= 127) { // must cache
        return CharacterCache.cache[(int)c];
    }
    return new Character(c);
}

  对于Double,也是在JDK1.5开始引入了自动拆装箱机制,不过与Byte,Short,Integer,Long,Character不同的是,Double中的valueOf方法不存在缓存池,永远都是创建一个新的对象

/**
 * @param  d a double value.
 * @return a {@code Double} instance representing {@code d}.
 * @since  1.5
 */
public static Double valueOf(double d) {
    return new Double(d);
}

自动拆箱

Integer inte = 2;
int c = inte;
// 本质
int c = inte.intValue();

本质是使用了intValue()

/**
 * Returns the value of this {@code Integer} as an
 * {@code int}.
 */
public int intValue() {
    return value;
}

缓存池

  缓存池可以与String中的字符串常量池做类比,当定义Integer对象时,会先去缓存池中查找是否有对应的对象,如果有就直接引用,不会去创建新的对象;如果没有,才会创建新的对象。

  1. 例子
// 缓存范围:[-128,127]
Integer num1 = 100;
Integer num2 = 100;
Integer num3 = 200;
Integer num4 = 200;
System.out.println(num1 == num2); 
System.out.println(num3 == num4);
Integer num5 = 200;
int num6 = 200;
//Integer与int比较的时候,Integer会自动拆箱转换为int类型
System.out.println(num5 == num6); 
  1. 答案
//true 自动装箱,从缓存池中直接引用,没有创建对象,直接比较字面值
//false 自动装箱,超过缓存池范围,创建对象,所以num3和num4都是指向各自的对象地址,比较对象地址
//true 比较的是字面值
  1. 例子
Double d1 = 1.23;
Double d2 = 1.23;
System.out.println(d1 == d2);
System.out.println(d1.equals(d2));
  1. 答案
//false 自动装箱,永远都是创建各自的Double对象
//true 比较字面值

包装类型与基本类型性能比较

// int与Integer性能
long start = System.currentTimeMillis();
int p = 0;
for(int i=0; i<100000; i++){
    p += i;
}
long end = System.currentTimeMillis();
System.out.println(end-start); // 0ms

long start1 = System.currentTimeMillis();
Integer p1 = 0;
for(int i=0; i<100000; i++){
    p1 += i;
}
long end1 = System.currentTimeMillis();
System.out.println(end1-start1); // 31ms

  当数据量大的时候,对于Integer来说执行效率上会慢一些,因为当超过[-128,127],需要创建Integer实例,比较耗费时间。

总结

自动装箱

  1. 存在缓存池

    • 对于Byte,Short,Integer,Long来说,它们的缓存池范围都是[-128,127],即一个byte的数据范围

    • 对于Character而言,它的缓存池范围{@code '\u005Cu0000'} to {@code '\u005Cu007F'}转换为int来说是[0,127]

  2. 不存在缓存池

    • 对于Double而言,它不存在缓存池,当使用valueOf进行装箱的时候永远都是创建一个新的Double实例

    • 对于Boolean而言,也是使用valueOf方法。(不了解它,不过就false和true两个值)

自动装箱本质
  是通过方法valueOf,以Integer为例,该方法通常是会先去缓存池中查看请求的值存在对应的值(未超过范围),若是请求的值超过缓存范围,则调用构造器Integer(int)创建一个新的实例。当然前提是包装类存在缓存池,否则如Double调用valueOf方法永远都是创建一个新实例。

自动拆箱本质
  是通过包装类中各自已经存在的方法,形式基本数据类型Value(),例如Integer的intValue()、Double的doubleValue()

如何选择使用
  若是需要使用对象,则使用包装类,若无需使用到对象,则使用基本数据类型即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值