我以前在写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. 用javascript做一个视频播放器

    以前我们在网页上播放视频,都是要麻烦flash来实现.看着那一大段的<object>真心觉得累.随着html5的不断普及,现在是时候使用html5提供的video元素来做点正经事了,但是要 ...

  2. MySQL监控利器-Innotop

    Innotop是一款十分强大的MySQL监控工具,用perl所写,通过文本模式展示MysQL服务器和Innodb的运行状况. 安装innotop 下载地址:https://github.com/inn ...

  3. 小菜学习Winform(六)剪切板和拖放复制

    前言 在做winform项目的时候有时候会用到复制粘贴,在.net中提供了Clipboard类来操作剪切板,我们来看下. clipbrd.exe clipbrd是系统剪切板程序,但是在vista及以上 ...

  4. C语言 基础练习40题

    一.题目 1.输入2个整数,求两数的平方和并输出. 2. 输入一个圆半径(r)当r>=0时,计算并输出圆的面积和周长,否则,输出提示信息. 3.函数y=f(x)可表示为: 4.编写一个程序,从4 ...

  5. DX9入门笔记1-D3D初始化

    对3D编程期待已久,却一直叶公好龙浅尝辄止.近期在公司实习却无具体的工作安排,琢磨着学习个新的手艺,就又想起了3D Programming.这次从大名鼎鼎的龙书(Introduction to 3D ...

  6. “全能”选手—Django 1.10文档中文版Part4

    第一部分传送门 第二部分传送门 第三部分传送门 3.2 模型和数据库Models and databases 3.2.2 查询操作making queries 3.3.8 会话sessions 2.1 ...

  7. NSPredicate 的使用

    NSPredicate是什么? NSPredicate 是预测的意思 但是我们常翻译成谓词 它可以干什么? 使用NSPredicate可以定义模糊查询条件 根据一定的条件 我们就可以从一个数组中快速找 ...

  8. 2.简单的Code First例子(EF Code-First系列)

    现在假想,我们想要为讴歌学校创建一个应用程序,这个程序需要能够来添加或者更新学生,分数,教师还有课程信息. 代替之前我们的做法:先是创建数据库,现在我们不这么做,我们先来创建领域类,首先我来创建两个简 ...

  9. 在CentOS 6.5上安装python2.7

    1.yum groupinstall “Development tools” 2.安装编译Python需要的组件 yum install zlib-devel bzip2-devel openssl- ...

  10. 自定义分页控件PageList

    using System; using System.Collections; using System.Collections.Generic; using System.Linq; using S ...