godtrue

      上篇博文int和Integer之间的区别和联系,是试验的结果,当我看了一下《Effective Java 中文版第2版》中的介绍之后,我发现自己写漏了一些东西,为了加深印象,提高自己的认知,我又做了一下试验,在此记上一笔,以备后用。另外,我发现将编译以后的例子,对照起来看,更加容易明白为什么是这样而不是那样,所以,我们就将编译后的代码栗子也贴出来对照看看吧!

 

1:此例用来表明——基本类型和装箱基本类型具有的属性有所不同,基本类型只有值,而装箱基本类型则具有与他们的值不同的同一性。

import java.util.Comparator;

/**
 * Created by qianyingjie1 on 2017/2/13.
 */
public class IntegerComparator {
    public static void main(String[] args){
        Comparator<Integer> naturalOrder = new Comparator<Integer>(){
            public int compare(Integer first,Integer second){
                return first<second ? -1 :(first == second ? 0 : 1);
            }
        };
        //1,注意,这里是错误的,原因是因为对于装箱基本类型运用==操作符几乎总是错误的
        System.out.println(naturalOrder.compare(new Integer(99),new Integer(99)));

        Comparator<Integer> naturalOrder_ = new Comparator<Integer>(){
            public int compare(Integer first,Integer second){
                int f=first;//自动拆箱
                int s=second;//自动拆箱
                System.out.println("first==f is : "+(first==f));//当基本类型和装箱基本类型使用==比较的时候会做拆箱处理的
                return f<second ? -1 :(f == s ? 0 : 1);
            }
        };
        //0,我们做了自动拆箱的处理,只比较我们关心的整数值之间的大小关系
        System.out.println(naturalOrder_.compare(new Integer(99),new Integer(99)));
        /**
         * 结论1:
         * 基本类型只有值,而装箱基本类型则具有与他们的值不同的同一性。简单点讲就是,基本类型只有值,是什么值就是什么值没有别的
         * 即使声明的时候没有赋值,系统也会默认的给他一个默认值0。而装箱基本类型不同,两个装箱基本类型可以具有相同的值,并且还
         * 可以有不同的同一性,比:他们的引用地址不同。
         */
    }
}
当基本类型和装箱基本类型使用==比较的时候会做拆箱处理的
import java.util.Comparator;

public class IntegerComparator {
    public IntegerComparator() {
    }

    public static void main(String[] args) {
        Comparator naturalOrder = new Comparator() {
            public int compare(Integer first, Integer second) {
                return first.intValue() < second.intValue()?-1:(first == second?0:1);
            }
        };
        System.out.println(naturalOrder.compare(new Integer(99), new Integer(99)));
        Comparator naturalOrder_ = new Comparator() {
            public int compare(Integer first, Integer second) {
                int f = first.intValue();
                int s = second.intValue();
                System.out.println("first==f is : " + (first.intValue() == f));//看这里上面的结论是不是很好理解了
                return f < second.intValue()?-1:(f == s?0:1);
            }
        };
        System.out.println(naturalOrder_.compare(new Integer(99), new Integer(99)));
    }
}

上面代码段执行的输出结果

"C:\Program Files\Java\jdk1.7.0_71\bin\java" -Didea.launcher.port=7533 "-Didea.launcher.bin.path=C:\Program Files (x86)\JetBrains\IntelliJ IDEA 2016.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_71\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\rt.jar;D:\workspace_test\hello-world\out\production\hello-world;C:\Program Files (x86)\JetBrains\IntelliJ IDEA 2016.1.3\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain com.jd.test.autoInteger.IntegerComparator
1
first==f is : true
0

Process finished with exit code 0

 

2:此例表明——装箱基本类型的默认值是null,但是int的默认值是0,并且int类型的变量不能赋值为null

/**
 * Created by qianyingjie1 on 2017/2/13.
 */
public class IntegerAutoUnboxing {
    static Integer i;
    static int i_;
    public static void main(String[] args){
        /**
         * i_默认是0,所以能够进入并且打印 99!=i_ is true
         */
        if(99!=i_){
            System.out.println("99!=i_ is true");
        }
        /**
         * i默认是null,按照预想的应该可以进入并且打印 99!=i is true,但是没有而且抛出来了一个空指针异常
         * 原因是因为:当在一项操作中混合使用基本类型和装箱基本类型时,装箱基本类型就会自动拆箱。如果null对象
         * 引用被自动拆箱,就会得到一个NullPointerException异常。修正的方式如上所示,声明i是个int而不是Integer
         */
        if(99!=i){
            System.out.println("99!=i is true");
        }
        /**
         * 结论2:
         *基本类型只有功能完备的值,而每个装箱基本类型除了它对应的基本类型的所有功能之外,还有一个非功能值null
         */
    }
}
当基本类型和装箱基本类型使用!=比较的时候也会做拆箱处理的,但是装箱基本类型的默认值是null,所以当心会报空指针异常的
public class IntegerAutoUnboxing {
    static Integer i;
    static int i_;

    public IntegerAutoUnboxing() {
    }

    public static void main(String[] args) {
        if(99 != i_) {
            System.out.println("99!=i_ is true");
        }

        if(99 != i.intValue()) {//看这里,报空指针异常是不是挺好理解的
            System.out.println("99!=i is true");
        }

    }
}

上面代码段执行的输出结果

"C:\Program Files\Java\jdk1.7.0_71\bin\java" -Didea.launcher.port=7535 "-Didea.launcher.bin.path=C:\Program Files (x86)\JetBrains\IntelliJ IDEA 2016.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_71\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\rt.jar;D:\workspace_test\hello-world\out\production\hello-world;C:\Program Files (x86)\JetBrains\IntelliJ IDEA 2016.1.3\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain com.jd.test.autoInteger.IntegerAutoUnboxing
Exception in thread "main" java.lang.NullPointerException
    at com.jd.test.autoInteger.IntegerAutoUnboxing.main(IntegerAutoUnboxing.java:21)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
99!=i_ is true

Process finished with exit code 1

 

3:此例表明——基本类型通常比装箱基本类型更节省时间和空间,当变量被反复的装箱和拆箱,会导致明显的性能下降。

public class IntegerAutoBoxing {
    public static void main(String args []){
        //定义求和的变量
        Long sum = 0L;
        long sum_ = 0;
        //定义循环的次数,方便比较各个循环量的差异
        long cycleCount = Integer.MAX_VALUE;
        //计算的开始计算
        long startTime = System.currentTimeMillis();

        //变量被反复的装箱和拆箱是的效率
        for(long i=0;i<cycleCount;i++){
            sum+=i;
        }
        System.out.println("sum  is : "+ sum +"   此次循环"+cycleCount+"次,所用的时间为:"+(System.currentTimeMillis()-startTime));

        //变量没被反复的装箱和拆箱是的效率
        startTime = System.currentTimeMillis();
        for(long i=0;i<cycleCount;i++){
            sum_+=i;
        }
        System.out.println("sum_ is : "+ sum_ +"   此次循环"+cycleCount+"次,所用的时间为:"+(System.currentTimeMillis()-startTime));
        /**
         * 结论3:
         * 基本类型通常比装箱基本类型更节省时间和空间,当变量被反复的装箱和拆箱,会导致明显的性能下降。
         * 因为当程序装箱了基本类型的值是,导致高的开销和不必要的对象创建
         */
    }
}

基本类型和装箱基本类型之间,自动的拆箱和装箱是需要消耗性能的

public class IntegerAutoBoxing {
    public IntegerAutoBoxing() {
    }

    public static void main(String[] args) {
        Long sum = Long.valueOf(0L);
        long sum_ = 0L;
        long cycleCount = 2147483647L;
        long startTime = System.currentTimeMillis();

        long i;
        for(i = 0L; i < cycleCount; ++i) {
            sum = Long.valueOf(sum.longValue() + i);//看这里,输出的性能有差异,从这里是不是得到了充分的理解了
        }

        System.out.println("sum  is : " + sum + "   此次循环" + cycleCount + "次,所用的时间为:" + (System.currentTimeMillis() - startTime));
        startTime = System.currentTimeMillis();

        for(i = 0L; i < cycleCount; ++i) {
            sum_ += i;
        }

        System.out.println("sum_ is : " + sum_ + "   此次循环" + cycleCount + "次,所用的时间为:" + (System.currentTimeMillis() - startTime));
    }
}

上面代码段执行的输出结果,同时我们也看到,他们的性能差别还是挺大的

"C:\Program Files\Java\jdk1.7.0_71\bin\java" -Didea.launcher.port=7533 "-Didea.launcher.bin.path=C:\Program Files (x86)\JetBrains\IntelliJ IDEA 2016.1.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.7.0_71\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jce.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jfxrt.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\resources.jar;C:\Program Files\Java\jdk1.7.0_71\jre\lib\rt.jar;D:\workspace_test\hello-world\out\production\hello-world;C:\Program Files (x86)\JetBrains\IntelliJ IDEA 2016.1.3\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain com.jd.test.autoInteger.IntegerAutoBoxing
sum  is : 2305843005992468481   此次循环2147483647次,所用的时间为:10338
sum_ is : 2305843005992468481   此次循环2147483647次,所用的时间为:1033

Process finished with exit code 0

 

总结论

        /**
         * 总结论:
         * 1:基本类型优于装箱基本类型,因为,基本类型更简单、更快速。
         *
         * 2:下面的情况下,才优先使用装箱基本类型
         * 2-1:当作为集合中的元素、键、值时
         * 2-2:在进行反射的方法调用时
         *
         * 3:当必须使用装箱基本类型的时候需要注意一下几点
         * 3-1:比较对象的值是否相等,不能使用==一定要使用equals
         * 3-2:当程序进行涉及装箱和拆箱基本类型的混合类型计算是,它会进行拆箱,当程序进行拆箱时,可能会抛出空指针异常。
         * 3-3:当程序装箱了基本类型值时,会导致高开销和不必要的对象创建。
         */

 

posted on 2017-02-18 15:40  godtrue  阅读(938)  评论(2编辑  收藏  举报