自动装箱(autoboxing)与自动拆箱语(auto-unboxing)法糖

来源:互联网 发布:python counter 升序 编辑:程序博客网 时间:2024/05/16 00:33

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">Java中存在两种数据类型,一种是原始数据类型(primitive type),另一种就是引用数据类型(reference type),对于每一种基本数据类型都有与之相对应的引用类型,例如:int,boolean,long等分别对应着Integer, Boolean, Long等引用数据类型。Java 1.5引入了autoboxing与auto-unboxing实现了原始数据类型与对应引用类型的自动转换,这个决定很难说是否正确,首先了解一下两种数据类型之间的区别:</span>

(1)对于基本数据类型来说,若两个基本数据类型的变量的值相等,则可以说这两个变量相等,即a == b;对于引用类型来说这可未必,两个引用类型变量所指向的对象的值相等,并不一定意味着这两个对象相等,Java中默认两个引用相等指的是两个引用指向的是同一个对象。

(2)对于基本数据类型的变量来说,其值只能是相应数值范围内的数;而对于引用变量来说,除了可以指向具体的对象之外还有可能是空引用(null)。

(3)相对而言,使用原始数据类型会占用更少的内存空间以及具有较高的执行效率。

大家可以试想一下以下程序的输出结果:

import java.util.Comparator;public class Test {public static void main(String[] args) {Comparator<Integer> naturalComp = new Comparator<Integer>() {@Overridepublic int compare(Integer num1, Integer num2) {return (num1 < num2) ? -1 : (num1 == num2) ? 0 : 1;}};int result = naturalComp.compare(new Integer(10), new Integer(10));System.out.println(result);}}
输出是:1

上述程序有一个陷进在num1 == num2的比较上,如果是单独看那肯定不会出错,num1和num2分别是指向Integer对象的引用,若要使得num1 == num2则要求num1与num2指向同一个Integer对象,显然上述并不满足,之所以可能会出现这个问题就是没有准确的区分原始数据类型和引用数据类型。再来看下一个:

public class Test {private static Integer value;public static void main(String[] args) {if(value != 10)System.out.println("Oh, my God...");}}
你是否会觉得输出:Oh, my God呢?

事实上输出的是:

Exception in thread "main" java.lang.NullPointerException
at Test.main(Test.java:5)

注意value声明为static Integer即为指向Integer对象的引用类型,由于没有初始化故value默认为null,进行value != 10比较操作时,value自动解包,由于value自身是null,故产生空指针异常,这就是没有弄清原始类型与引用类型所造成的错误。

public class Test {public static void add_long() {long startTime = System.currentTimeMillis();long result = 0L;for(int i=0;i<Integer.MAX_VALUE;i++) {result += i;}long endTime = System.currentTimeMillis();System.out.println("add_long executive time is:" + (endTime-startTime) + " the result is: " + result);}public static void add_Long() {long startTime = System.currentTimeMillis();Long result = 0L;for(int i=0;i<Integer.MAX_VALUE;i++) {result += i;}long endTime = System.currentTimeMillis();System.out.println("add_Long executive  time is :" + (endTime-startTime) + " the result is: " + result);}public static void main(String[] args) {add_long();add_Long();}}
输出结果:

add_long executive time is :1055 the result is: 2305843005992468481
add_Long executive  time is :8690 the result is: 2305843005992468481
请仔细区分上述两个方法,是什么造成执行时间是的巨大差异呢?注意add_Long函数中result的声明为Long,也就是说在接下来的result += i中,result要不断的执行装箱与拆箱工作,造成了时间上的延误。那么到底怎样正确使用原始类型与引用类型呢?在一下三种情况下可以使用原始类型对应的引用类型:

(1)作为集合类的元素,Map的键,值时需要使用引用类型。

(2)作为参数类型是必须使用引用类型。

什么情况下会发生自动拆箱与自动装箱呢?

当你在一个操作符的两侧混合原始类型与引用类型时则会使得引用类型发生自动解包,需要注意的是,如该引用类型的变量为null则在发生空指针异常。总而言之:==操作数一端是原始数据类型,另一端是引用类型则自动拆箱,比较值;如果操作数都是对象,那么比较引用地址。但是有一点需要注意的是在-128127这个区间,如果创建Integer对象的时候(1Integer i = 1; 2 Integer i = Integer.valueOf(1);如果是这两种情况创建出来的对象,那么其实只会创建一个对象,这些对象已经缓存在一个叫做IntegerCache里面了,所以==比较是相等的。

equals需要根据具体对象的实现来判断,在Integer里面是判断值是否相等。 

Byte,Short,Integer,Long,Character5种整型的包装类也只是在对应值小于等于127并且大于等于-128时才可使用常量池,因为他们只占用一个字节(-128~127);

再者Integer.valueOf方法中也有判断,如果传递的整型变量>= -128并且<=127时会返回IntegerCache类中一个静态数组中的某一个对象,否则会返回一个新的Integer对象,代码如下:

public static Integer valueOf(int i) {        assert IntegerCache.high >= 127;        if (i >= IntegerCache.low && i <= IntegerCache.high)            return IntegerCache.cache[i + (-IntegerCache.low)];        return new Integer(i);}<span style="color:#0000ff;"></span>
以下面的代码作为本文的收尾,加深理解。

public class Test {public static void main(String[] args) {        Integer i01=59;int i02=59;Integer i03=Integer.valueOf(59);Integer i04=new Integer(59);System.out.println(i01==i02);System.out.println(i01==i03);System.out.println(i03==i04);System.out.println(i02==i04);    }}
输出结果:

true

true

false

true

当i01与i02进行比较时:比较的是值,i01会发生自动拆箱与i02进行值比较,由于值都是59,故输出true;

当i01与i03比较时:比较的是引用,由于都是使用的Integer缓冲区内的对象,故指向同一个对象,返回true;

当i03与i04比较时:比较的是引用,由于i04指向的是创建出来的Integer对象,故与i03指向不同的对象,返回false;

当i02与i04比较时:比较的是值,i04会发生自动拆箱,由于值都是59,故输出true。



0 0
原创粉丝点击