目录html
学习了许久的Java,咱们知道Java是一种面向对象的语言,万物皆对象。可是咱们以前在说到Java基本数据类型的时候,因为处理对象须要额外的系统开销,因而出于对性能的考虑,基本数据类型并不作为对象使用。
既然是面向对象的,在Java中许多方法须要把对象做为参数,可是基本类型变量身上没有任何方法和属性,因而Java提供了一个简单的方法,就是为每个基本数据类型类型都配套提供一个包装类型,咱们即可以在二者之间来回反复地横跳。java
先看一波包装类型的继承图:
缓存
数值类型都直接继承于父类Number类,非数值类型Character和Boolean直接继承于Object类。工具
除此以外,包装类型的名字也很是好记,除了int->Integer
和char->Character
两个比较特殊以外,其余都是基本数据类型的首字母改成大写便可,如:byte->Byte
。性能
经过查看官方文档,咱们能够发现,数值类型继承的Number类实际上是一个抽象类,那么可想而知,该类中的抽象方法已经在这几个数值类型中获得实现,看一波:
学习
很明显,除了最后一个serialVersionUID(这个之后再总结),其余的方法在数值型包装类中都存在,能够经过这些方法将对象“转换”为基本类型的数值。this
咱们再来看看包装类型的构造器,咱们再查看全部包装类以后,发现:.net
Integer i1 = new Integer(5);//5 Integer i2 = new Integer("5");//5
this.value = value
,一目了然。public Integer(String s) throws NumberFormatException { this.value = parseInt(s, 10); }
深究一下,parse(String s,int radix)中的radix其实表明着进制信息,而咱们的构造器默认让radix为10,表明着输出字符串s在十进制下的数,因此除了数字0-9以外,字符串中不能有其余的玩意儿,不然会抛出NumberFormatException的异常。debug
咱们在上面说过,基本数据类型和包装类型之间的转换涉及到装箱与拆箱的操做,为了简化代码,在JDK1.5以后,Java容许基本类型和包装类型之间能够自动转换。指针
将基本类型直接赋值给对应的引用类型,编译器在底层自动调用对应的valueOf方法。
就像下面这样:
int i = 5; Integer in = i;
咱们利用debug调试工具设上断点,发如今执行Integer in = i;
时,将会自动调用下面的方法:
继续深究其底层实现,咱们发现IntegerCache实际上是Integer包装类的一个内部类,咱们进入IntegerCache一探究竟:
咱们会发现全部的整数类型的(包括Character)包装类里都有相似的玩意儿,因此大体运行的规则应该大体相同,在这里就总结几点不太同样的:
int num = 100; Integer i1 = num; Integer i2 = num; System.out.println(i1==i2);//true //num改成200,结果为false
Integer i1 = 100; Integer i2 = new Integer(100); System.out.println(i1 == i2);//false
将引用类型字节赋值给对应的基本类型,编译器在底层自动调用对应的xxxvalue方法(如intValue)。
Integer in = 5; int i = in;
自动拆箱相对来讲就稍微简单一点了,咱们仍是利用debug工具,发现上面的代码将会自动调用下面的方法
int num = 100; Integer i1 = num; Integer i2 = num; //都是包装器类型的引用时,比较是否指向同一对象。 System.out.println(i1==i2);//true Integer i1 = 128; int i2 = 128; //若是包含算数运算符,则底层自动拆箱,即比较数值。 System.out.println(i1 == i2);//true Integer i3 = 1; Integer i4 = 129; System.out.println(i4 == i1+i3);//true
equals比较的是同一包装类型,即比较二者数值是否相等
Integer i1 = 5; Integer i2 = 5; Integer i3 = 10; //同一包装类型,比较数值是否相等 System.out.println(i1.equals(i2));//true System.out.println(i3.equals(i1+i2));//true Long l1 = 5L; Long l2 = 10L; //Long与Integer比较,不是同一类型,false System.out.println(l1.equals(i1));//false //先自动拆箱,i1先转为int,l转为long,int自动类型提高转为long,最后相等 System.out.println(l2.equals(l1+i1));//true
Integer sum = 0; for(int i = 500;i<5000;i++){ //先自动拆箱,然后自动装箱 sum+=i; }
在拆箱装箱操做以后,因为sum数值超过缓存范围,因此会new出4500个毫无用处的实例对象,大大影响了程序的性能。因此在循环语句以前,务必声明正确的变量类型。
private static Integer sum; public static void setSum(Integer num,boolean flag){ sum = (flag)?num:-1; }
上面的代码,当num传入为null时,即会引起空指针异常,由于包装类在进行算术运算时(上述是三目运算),若是数据类型不一致,将会先自动拆箱转换成基本类型进行运算,而null若是调用了intValue()方法就会造成空指针。
改进方案:
public static void setSum(Integer num,boolean flag){ //这样类型一致,便不会自动拆箱了 sum = (flag)?num:Integer.valueOf(-1); }
参考连接: