在java中一共有8种基本类型,并且在jdk1.4的时候添加了对应的八个包装类型如下表所示:
int | Integer |
short | Short |
long | Long |
double | Double |
float | Float |
char | Character |
byte | Byte |
boolean | Boolean |
都知道,java中的类型可以分为两类,基本数据类型以及包装类,他们在java虚拟机上的分配有所不同(这里不多做叙述,有兴趣的童鞋可以看看”深入理解Java虚拟机“一书)。总的来说基本数据类型在java虚拟机在分配时无论时间还是空间上来讲可以说时基本类型要优于包装类型的。那么为什么还有有包装类型呢?举一个简单的例子,如果有一个列表List,它的内部元素时int类型该如何定义?
// java1.5之前
List ints = new ArrayList();
// java1.5之后
List<Integer> nums = new ArrayList<>();
很直观,如果是在1.5之前没有泛型的时候我们可以直接声明,但是有一个问题就是这个列表是可以添加除了int以外的其他类型的。这就比较尴尬了,这就与我们定义这个列表的目的相悖了。那么就需要类型的限制,那么就是泛型。而使用泛型限制类型的时候是不允许使用基本类型的如:
List<int> ints = new ArrayList<>();
这种写法编译器是不通过的。这样基本类型的包装类型就显得尤为重要了。
言归正传,那么什么是自动装箱和自动拆箱呢?
通俗一点的语言来讲就是基本类型自动转换为包装类型就是自动装箱,自动拆箱与之相反。
Integer a=235;
int b = 669;
System.out.println(a==b);
如上诉代码所示,我们平时也没少干这种事儿把?他们实际上的可以正常运行的,其中的原理就是变量a在比较之前自动拆箱为基本类型然后在于b进行比较。如果不去自动拆箱的话,试想一下,两个不同类型的变量是怎么进行比较呢?在现实生活中也是没有办法比较的,你不能把一辆列车和你的房子进行比较,也不能把我们吃的食物和手机、电视比较,因为他们类型不同。程序反应的是现实中的问题,所以在java中一般情况下也是不能的(当然可以通过重写Object的equals方法,当然即便如此,也是在两个不同的类的实例上寻找两个相同的可以比较的点)。于是java不得已只能将Integer类型的变量转换为int类型才得以进行比较。
上诉例子中的比较过程有自动拆箱,那么自动装箱呢?
其实a变量的赋值过程就是一个自动装箱的过程。要知道235本身就是一个类型为int的基本数据类型,而变量a为Integer的包装类型,将一个基本类型直接赋值给包装类型那么这个过程就是java内部进行了自动装箱,否则他们是编译不通过的。如果没有自动装箱这个a变量的声明就应该是如下所示:
//第一种方法:使用构造方法
Integer a = new Integer(2);
//第二种方法:使用Integer的静态工厂方法
Integer b = Integer.valueOf(10);
自动装箱和自动拆箱的定义其实很简单的,java虽然给我们提供了方便,但是自动装箱和拆箱真的好吗?其实不难想象,自动装箱个自动拆箱的过程一定是消耗性能的。
public static void main(String[] args) {
long start = System.currentTimeMillis();
Long sum=0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum+=i;
}
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.printf("本次执行时间为{%d}",(end-start));
}
他的执行结果为
如果修改为如下形式:
public static void main(String[] args) {
long start = System.currentTimeMillis();
long sum=0L;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum+=i;
}
System.out.println(sum);
long end = System.currentTimeMillis();
System.out.printf("本次执行时间为{%d}",(end-start));
}
执行结果如下
其中相差近6秒的时间,不难发现,这两个程序中唯一的不同之处在于sum变量在第一个程序中使用包装类Long声明,而第二个程序中使用的是基本类型long声明,而他们的循环中声明的i都是基本类型long所以在第一个程序中的加法运算中存在自动拆箱和自动装箱的过程所以导致他执行的慢。
千万不要想着把循环体中的i变量声明为包装类型Long,这样只会导致上诉方法执行的更慢。
上诉实例取自《Effective Java》一书,在该书中明确指出如非必要,尽量使用基本类型。原因很简单,前边也提到过,基本类型无论是在时间还是空间上都是优于引用类型的。