Java的自动拆装箱介绍,Java拆装箱介绍

本文由码农网 –
袁延勇原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

Java的自动拆装箱介绍,Java拆装箱介绍

在面试过程中,常常会有面试官问到基础的问题的时候都会问到Java的拆装箱,关于这个问题其实不是很难,但是如果平时进行自学的时候不是注意,就可能一脸懵逼,所以笔者就这个问题进行一些总结,共同促进!

如果我说“仅仅修改下面代码中的一个字符就可以获得五倍于当前的运行速度”你会怎么想呢?

一、拆装箱概念

所谓的拆装箱,就是自从JDK1.5之后,java的基本类型和引用类型之间的相互转换。

long t = System.currentTimeMillis();
Long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println("total:" + sum);
System.out.println("processing time: " + (System.currentTimeMillis() - t) + " ms") ;

1.1拆箱

拆箱就是把Long,Integer,Double,Float
等将基本数据类型的首字母大写的相应的引用类型转化为基本数据类型的动作就叫拆箱。

结果如下:

1.2装箱

装箱就是把byte ,int ,short, long ,double,float,boolean,char
这些Java的基本数据类型在定义数据类型时不声明为相对应的引用类型,在编译器的处理下自动转化为引用类型的动作就叫做装箱。

total:2305843005992468481
processing time: 6756 ms

二、拆装箱的相关应用

在JDK1.5后,当我们进行基本类型和引用类型的转换的时候就会方便:

package com.hzp.CZX;
/**
 * 测试拆装箱
 * @author 夜孤寒
 * @version 1.1.1
 */
public class TestDemo {
    /**
     * 拆装箱JDK1.5后
     */
    public static void first(){
        Integer i=7;//基本类型-->引用类型
        int j=i;//引用类型-->基本类型
        System.out.println(j);
    }
    /**
     * 拆装箱JDK1.4
     */
    public static void second(){
        Integer i=new Integer(78);
        int j=i.intValue();
        System.out.println(j);
    }
    /**
     * 测试方法
     * @param args
     */
    public static void main(String[] args) {
        first();
        second();
    }
}

上面介绍了关于拆装箱的一些基本点和使用方式,但是要使用拆装箱的话还有一些注意点需要注意,下面将这些注意点进行一些总结。

译者本机测试结果:

三、注意点

首先贴一段代码如下:

package com.ygh.CZX;
/**
 * 关于java的拆装箱范围剖析
 * @author 夜孤寒
 * @version 1.1.1
 */
public class Test {
    /**
     * 以Integer类型为例
     */
    public static void first(){
        Integer i=new Integer(124);
        Integer j=new Integer(124);
        System.out.println(i==j);//false
        Integer a1=-128;
        Integer a2=-128;
        System.out.println(a1==a2);//true
        Integer b1=-129;
        Integer b2=-129;
        System.out.println(b1==b2);//false
        Integer c1=127;
        Integer c2=127;
        System.out.println(c1==c2);//true
        Integer d1=128;
        Integer d2=128;
        System.out.println(d1==d2);//false
    }
    public static void main(String[] args) {
        first();

    }
}

简单解释一下:

第一个结果为false的原因是因为创建了不同的对象,所以两者不一样;

但是第二个和第三个的结果为什么不一样?

下面贴出关于Integer类的源码,从源码的角度来分析这个问题:

    /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * 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);
    }

上面的代码是说,进行自动拆装箱的时候,是有一个范围的,一旦超出这个范围,那么指向的就不是同一个对象,而是返回一个新创建的对象了,这个范围在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) {
                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() {}
    }

从这里我们可以看出,范围值为[-128,127]之间。

注意,Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。
Double、Float的valueOf方法的实现是类似的。

总结:这些进行自动拆装箱的基本类型的范围如下:

  1. boolean类型的值

2.所有的byte的值

3.在-128~127的short类型的值

澳门新葡亰网站注册,4.在-128~127的int类型的值

5.在 u0000~ u00ff 之间的char类型的值

而其中double和float又有所不同,我们就以double为例子,贴出代码讨论:

package com.ygh.CZX;

/**
 * 关于java的拆装箱范围剖析
 * 
 * @author 夜孤寒
 * @version 1.1.1
 */
public class Test {
    /**
     * Double
     */
    public static void first() {
        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;
        System.out.println(i1 == i2);//false
        System.out.println(i3 == i4);//false
    }
    /**
     * 测试方法
     */
    public static void main(String[] args) {
        first();
    }
}

注意为什么上面的代码的输出结果都是false呢?同样的我们依旧以Double类中的valueOf方法来讨论,贴出源码就一目了然了:

    /**
     * Returns a {@code Double} instance representing the specified
     * {@code double} value.
     * If a new {@code Double} instance is not required, this method
     * should generally be used in preference to the constructor
     * {@link #Double(double)}, as this method is likely to yield
     * significantly better space and time performance by caching
     * frequently requested values.
     *
     * @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);
    }

也就是说不管你的double是什么范围的值,他都是给你返回一个新的对象。float同double,就不过多赘述了。

以上就是笔者对于拆装箱的一些整理,如果读者有不同的看法可以在评论区提出,笔者再进行修改!

在面试过程中,常常会有面试官问到基础的问题的时候都会问到Java的拆装箱,关于这个问题其实不是…

total:2305843005992468481
processing time: 8369 ms

一番思考之后,我们可以采用如下更加快速的代码:

long t = System.currentTimeMillis();
//Long sum = 0L;
long sum = 0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println("total:" + sum);
System.out.println("processing time: " + (System.currentTimeMillis() - t) + " ms") ;

结果如下:

total:2305843005992468481
processing time: 1248 ms

译者本机测试结果: