我以前在写Android项目的时候,估计写得最多最熟练的几句话就是:

  

List<Integer> list = new ArrayList<Integer>();
list.add(); //把一个整数加入到集合中
int i = list.get(); //从集合中取出元素

  ArrayList用起来是多么的顺手!当时我只知道尖括号<>里面只能加入大写字母开头的Object类型,不能加入int、char、double这些原始类型,至于原因没研究过,这么规定就这么用呗。

  但是随着对“码农”式无脑学习法的逐渐厌倦,我开始重新审视Java代码内部的东西。

  首当其冲的就是每个项目一定用到的ArrayList。在我的另一篇博客中已经对ArrayList的源码实现做了大体的分析。然而还有几个源码中看不出来,但是确实存在疑点的问题亟待解决。

  

List<Integer> list = new ArrayList<Integer>();

这句代码中每个元素是Integer类型,那么往list里面add新元素的时候必须为Integer,比如加个String进去,代码下面就会出现红色波浪线。
但是这句list.add(1) 众所周知,代码里面随便写个不带小数点的数字,那它就是个int;把一个int加到一个只能有Integer的List中不报错,不觉得有猫腻吗? 同样地,int i = list.get(0),取出list中索引为0的元素,也应该是个Integer,为什么接收的变量就是个int呢?这是一个多么明显的类型不匹配错误啊! 以前,我确实听说过“包装类”这个概念,但是忽视了它,因为我一直觉得Integer,Float这些东西,说难听点就是摆出来装装逼的,只是因为List不接受int,float类型,迫不得已发明了Integer,Float,实际并没有卵用。 最近看了《Effective Java》里面的一节,名字叫“Prefer primitive types to boxed primitives”。里面罗列了很多原始类型和包装类型混用的例子,搞得我晕头转向的。下面是其中一段代码:
Long sum = 0L;
for (long i = ; i < Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println(sum);

据书中讲,这是一段运行效率低到不可救药的代码,你能看出其中的问题吗?
反正我当时看到这段代码就明显感觉到,Java对于原始类型与相应的Object类型的转化,在编译过程中肯定做了什么见不得人的事情…… 下面正式引出本文的话题:AutoBoxing and Unboxing(自动装箱&自动拆箱) 看一个最简单的例子:
Character ch = 'a';  //Character是char的包装类

这里没有出现任何错误,其实编译器在代码优化的时候,暗中转化成了下面的代码:
Character ch = Character.valueOf('a'); 

这就是说,"="右侧自动调用Character类对应的静态方法构造出了一个Character的实例。
为了进一步说明,这里稍微看一下valueOf方法
    public static Character valueOf(char c) {
return c < 128 ? SMALL_VALUES[c] : new Character(c);
} //如果字符在缓冲区中,直接取出Character实例,否则要重新构造 private static final Character[] SMALL_VALUES = new Character[128]; //类中自带一个静态的缓冲区,保存128个常用ASCII码字符对应的Character实例,免去每次重新构造实例的麻烦 static {
for (int i = 0; i < 128; i++) {
SMALL_VALUES[i] = new Character((char) i); //调用构造函数
}
}

对于Integer等其他包装类,自身都带有一个静态的valueOf方法。每次编译器检查到需要把一个int传给Integer时,就自动对代码进行转化。
比如上面的list.add(1),在编译过程中编译器发现要传进去的参数是int,但是要接收的是Integer,于是代码变为:
list.add(Integer.valueOf(1));

以上就是自动装箱(auto-boxing)的过程。

自动装箱一般在两种情况下会发生(以int和Integer为例):
1、把int作为一个方法的参数传进来,但是方法体里面希望得到的参数是Integer;
2、在赋值过程中,"="左边是Integer变量,右边是int变量。 这样一来,自动拆箱的过程就顺理成章了。看以下代码:
public static int sumEven(List<Integer> li) {
int sum = 0;
for (Integer i: li)
if (i % 2 == 0)
sum += i;
return sum;
}

在循环体内做了两次拆箱操作,编译器会转换成以下代码:
public static int sumEven(List<Integer> li) {
int sum = 0;
for (Integer i: li)
if (i.intValue() % 2 == 0)
sum += i.intValue();
return sum;
}

Integer的intValue方法就简单多了,直接返回被包装的int值
    @Override
public int intValue() {
return value; //value是Integer的成员变量
}

自动拆箱的用处跟自动装箱正好相反,也是用在参数传递和赋值过程中,这里就不赘述了。

我们再来分析一下那段超级低效的代码吧,经过自动拆装箱转换之后应该是这样子的:
Long sum = Long.valueOf(0L);
for (long i = 0; i < Integer.MAX_VALUE; i++) {
sum = Long.valueOf(sum.longValue() + i); //低效所在
}
System.out.println(sum.toString());

在循环体里面,简简单单只有一句话,竟然包含一次拆箱和一次装箱操作,在经过20多亿次的循环之后,效率损耗得难以置信!
既然拆箱和装箱可以看做“逆运算”,那么为什么还要进行多余的操作呢?直接用原始值运算,然后一次装箱不是更省事吗
Long sum = 0L;
long s = sum;
for (long i = 0; i < Integer.MAX_VALUE; i++) {
s += i;
}
sum = Long.valueOf(s);
System.out.println(sum);

参考资料:https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

Java暗箱操作之自动装箱与拆箱的更多相关文章

  1. 《Java中的自动装箱和拆箱功能.》

    //Java中的自动装箱和拆箱功能. class AutoboxingUnboxing { public static void main(String[] args) { //直接把一个基本类型变量 ...

  2. 深入剖析Java中的自动装箱和拆箱过程

    深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱 ...

  3. Java中的自动装箱与拆箱

    自动装箱和拆箱从Java 1.5开始引入,目的是将原始类型值转自动地转换成对应的对象.自动装箱与拆箱的机制可以让我们在Java的变量赋值或者是方法调用等情况下使用原始类型或者对象类型更加简单直接. 如 ...

  4. java -关于包装类自动装箱与拆箱拓展+整形常量池

    关于自动装箱与拆箱 1.包装类与基本数据类型的自动转换,叫装箱和拆箱(类型自动转换) 2.自动装箱拆箱是在编译器,编译器自动配转换方法,实现装箱和拆箱.所以这个过程发生在编译期 3.只有需要相互类型转 ...

  5. java中的自动装箱和拆箱

    一.什么是自动装箱和拆箱: 我们知道java为8种基本类型分别提供了对应的包装类型,在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i=new I ...

  6. Java基础 【自动装箱和拆箱、面试题】

    JDK 1.5 (以后的版本)的新特性自动装箱和拆箱 1. 自动装箱:把基本类型转换为包装类类型 int a =10; Integer i = new Integer(a); Integer valu ...

  7. java包装类,自动装箱,拆箱,以及基本数据类型与字符串的转换

    package cn.learn; import java.util.ArrayList; /* 包装类 java.lang中,基本运算类型效率高 装箱:把基本类型数据包装为包装类 1.构造方法 In ...

  8. java 中的自动装箱和拆箱操作

    在前面的文章中提到,Java为每种基本数据类型都提供了对应的包装器类型,至于为什么会为每种基本数据类型提供包装器类型在此不进行阐述,有兴趣的朋友可以查阅相关资料.在Java SE5之前,如果要生成一个 ...

  9. Java 自动装箱与拆箱

    Java 自动装箱与拆箱(Autoboxing and unboxing)   什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供 ...

随机推荐

  1. MapReduce的理解

    1 什么是MapReduce? Map本意可以理解为地图,映射(面向对象语言都有Map集合),这里我们可以理解为从现实世界获得或产生映射.Reduce本意是减少的意思,这里我们可以理解为归并前面Map ...

  2. 字符串模式匹配之KMP算法图解与 next 数组原理和实现方案

    之前说到,朴素的匹配,每趟比较,都要回溯主串的指针,费事.则 KMP 就是对朴素匹配的一种改进.正好复习一下. KMP 算法其改进思想在于: 每当一趟匹配过程中出现字符比较不相等时,不需要回溯主串的 ...

  3. 主机巡检脚本:OSWatcher.sh

    主机巡检脚本:OSWatcher.sh 2016-09-26更新,目前该脚本只支持Linux操作系统,后续有需求可以继续完善. 注意: 经测试,普通用户执行脚本可以顺利执行前9项检查: 第10项,普通 ...

  4. js构建ui的统一异常处理方案(一)

    从早期从事基于java的服务器端开发,再到之后从事基于web和js的ui开发,总体感觉基于web页面的ui开发远不如服务器端健壮.主要是早期ie浏览器功能太弱小,很多业务被迫放到服务器端去实现,浏览器 ...

  5. ajax+php+js实现异步刷新表单验证

    创建ajax对象 //创建对象 function createAjax(){ var request =false; //IE浏览器,window对象存在ActiveXObject属性 if(wind ...

  6. AJAX 详解注释很全来自互联网

    1: //用户名校验的方法 2: //这个方法使用XMLHTTPRequest对象进行AJAX的异步数据交互 3: var xmlhttp; 4: function verify(){ 5: //1. ...

  7. Git 初始化版本库

    创建带工作区的版本库 在开始一个新项目时,首先就要创建并初始化代码库.如果是在本机的工作目录中,那么: $ git init 也就够用了.如果想要初始化的版本库不在当前目录,需要为 git init ...

  8. 【WP8】WebBrowser相关

    2014年09月02日更新 今天用了一下WebBrowser,在使用过程中也遇到了一些问题,在这里做一下记录 虽然WebBrowser比较重,会比较影响性能(除非一定要用到它,否则尽量少用),但有时候 ...

  9. javascript图片展示墙特效

    查看效果:http://hovertree.com/code/javascript/pwl4bhoi.htm 代码如下: <!DOCTYPE html> <html> < ...

  10. luogg_java学习_08_设计模式_API

    这篇博客总结了1天整,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 , 设计模式 在长期开发过程中,为了解决某些固定问题, 总结出的 ...