上篇博文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:当程序装箱了基本类型值时,会导致高开销和不必要的对象创建。 */