java中,基本数据类型一共有8种,详细信息以下表:java
类型 | 大小 | 范围 | 默认值 |
byte | 8 | -128 - 127 | 0 |
short | 16 | -32768 - 32768 | 0 |
int | 32 | -2147483648-2147483648 | 0 |
long | 64 | -9233372036854477808-9233372036854477808 | 0 |
float | 32 | -3.40292347E+38-3.40292347E+38 | 0.0f |
double | 64 | -1.79769313486231570E+308-1.79769313486231570E+308 | 0.0d |
char | 16 | \u0000 - u\ffff | \u0000 |
boolean | 16 | true/false | false |
Java语言是一种面向对象的语言,可是Java中的基本数据类型倒是不面向对象的,这在实际使用时存在不少的不便,为了解决这个不足,设计者将每一个基本数据类型单独封装成一个类,这八个和基本数据类型对应的类统称为包装类(Wrapper Class)。程序员
自动装箱:把基本类型用它们对应的包装类包装起来,使它们具备对象的特质,能够调用所对应的包装类所定义的方法,好比toString()等。设计模式
举个例子:app
Integer i0 = new Integer(0); Integer i1 = 2; Integer i1_ = Integer.valueOf(2);
上面的三行代码第一行是最基本的建立一个integer对象的方式。第二行代码就是咱们这里要讲的自动装箱。而第三行代码就是第二行代码的本质,也就是说,当你使用自动装箱来获得一个引用数据类型时,jvm实际上调用了valueOf()方法,稍后咱们会去研究一下java源码。jvm
自动拆箱:跟自动装箱的方向相反,将Integer及Double这样的包装类的对象从新简化为基本类型的数据。函数
举个例子:spa
1.System.out.println(i1+2);
这句代码就使用了自动拆箱。i1是咱们上面经过自动装箱获得的一个integer对象,而这个对象是不能直接进行四则运算的,可是咱们却给它+2,这样就必须将integer对象转变为基本数据类型(int),这个过程就是自动拆箱的过程。设计
p.s.所谓自动,就是说这个过程并不须要程序员去完成,而是jvm自动完成的,jvm会在编译期根据语法决定是否进行装箱和拆箱动做。
另外,自动拆箱与自动装箱的jdk1.5才引入的新特性,因此若是你的jdk版本低于1.5的话,是不能够这样写的。指针
为何java要提供这样一个功能呢?个人理解是这样的:
1.由于懒。假如没有自动拆箱与自动装箱,那么咱们的代码是这样的:code
Integer i = new Integer(2);//假如须要一个integer的对象i,值为2 int b=i.intValue();//又须要一个int型的值,大小与i相等
Integer i = 2; int b = i;
是否是省了很多事,并且看起来代码更简洁了呢?
2.自动装箱的过程其实能够起到节约内存的做用。咱们先看一个例子:
Integer a = 1; Integer b = 1; Integer c = 144; Integer d = 144; Integer a1 = new Integer(1); Integer b1 = new Integer(1); System.out.println(a == b); //true System.out.println(a.equals(b)); //true System.out.println(a1 == b1); //false System.out.println(a1.equals(b1)); //true System.out.println(c == d); //false System.out.println(c.equals(d)); //true
是否是很奇怪,为何第7行为true而第12行为false呢?这是由于,在自动装箱时对于值从–128到127之间的值,它们被装箱为Integer对象后,会存在内存中被重用,始终只存在一个对象 。而若是超过了从–128到127之间的值,被装箱后的Integer对象并不会被重用,即至关于每次装箱时都新建一个 Integer对象。
那么,为何要这么设计呢?通常来讲,小数字的使用频率很高,将小数字保存起来,让其始终仅有一个对象能够节约内存,提升效率。
这其实用到了一种叫作享元模式的设计模式,感兴趣的能够去研究一下这个设计模式。
使用方式经过上面的例子你们应该也都清楚了,自动拆箱与装箱实际上就是jvm帮咱们去调用一些函数,这样可使咱们省很多事,代码也会看起来更简洁一些,不过在这里还有一点须要强调,先看代码:
Integer a = null; int b = a;
这么写彻底是符合java语法规范的,编译也能够正常经过,可是很明显,运行的时候回抛出空指针异常。因此在这里提醒你们,在使用自动拆箱时,必定要确保包装类的引用不为空。
上面提到了几个包装类的方法,咱们一Integer类为例,来看一看java源码是什么样子的。首先是valueOf()方法:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) // 没有设置的话,IngegerCache.high 默认是127 return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
上面讲到,在自动装箱时对于值从–128到127之间的值,它们被装箱为Integer对象后,会存在内存中被重用。如今明白是为何了吧,在调用valueOf()方法的时候,会判断你所给的数是否是在IntegerCache.low 和 i <= IntegerCache.high之间,若是是,那么他就在内存中生成惟一的对象,当你第二次想要生成它的时候,他会把第一次所生成对象的地址给你,不会从新生成。而不在这个范围里的数,你每次所生成的对象都是不一样的。
自动装箱池的大小是怎么定义的呢,Integer.java中有这样一个内部类
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() {} }
IntegerCache类(这个是jdk1.8的源码)定义了Integer自动装箱池的大小。从源码中咱们能够看到,下界是写死的,就是-128,可是上界倒是由参数integerCacheHighPropValue解码得来的,这就代表,其实咱们能够经过改变integerCacheHighPropValue值的大小来自定义自动装箱池的大小,固然,通常没人会去改它。
Integer自动装箱池的范围是-128~127
Byte,Short,Long范围是-128~127
Character范围是0~127
Float和Double没有自动装箱池
Java经过自动装箱和拆箱的机制,节省了部份内存开销和建立对象的开销,提升了效率同时简化了代码。在使用该机制的时候,须要注意如下几点: 1.在进行==比较的时候,在自动装箱池范围内的数据的引用是相同的,范围外的是不一样的。 2。在自动拆箱时,要保证包装类的引用不为空。