您的位置:首页 > 编程语言 > Java开发

java-拆包和装包简单分析

2016-01-09 15:42 871 查看
###前言
  最近,也是期末了,在准备复习之余还是抽出点时间研究一下技术,时间不多,所以就研究一下之前研究过的,但是还不是非常清楚的一个知识点—java的自动拆装包。

  为了找些资料,找翻了百度的角角落落,但是还是找不到让我满意的资料,或许是有些大神写的过于高深,令我无法理解,万般无奈还是去了stackoverflow。。。,算了,不多扯了,现在开始吧。

  首先让我们先看国外的一个大神对拆装包的分析:

When in doubt, check the bytecode:
Integer n = 42;
becomes:
0: bipush        42
2: invokestatic  #16                 //Methodjava/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1

So in actuality, valueOf() is used as opposed to the constructor (and the same goes for the other wrapper classes). This is beneficial since it allows for caching, and doesn't force the creation of a new object on each boxing operation.

The reverse is the following:
int n = Integer.valueOf(42);
which becomes:
0: bipush        42
2: invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: invokevirtual #22                 // Method java/lang/Integer.intValue:()I
8: istore_1

i.e. intValue() is used (again, it's analogous for the other wrapper types as well). This is really all auto(un)boxing boils down to.

  更多细节可以直接点过去:http://stackoverflow.com/questions/22648627/how-java-auto-boxing-unboxing-works
他的这个分析非常的到位,直接从字节码的方向分析,很有说服力。下面我们自己分析一下,这个高大上的技术到底是怎么实现的。

  首先如果我们定义一个Integer对象,例如 Integer n = 42; 编译后会生成字节码 2: invokestatic #16 实际上该段字节码的目的就是调用静态方法Integer.valueOf(),废话不多说,直接找到这个方法,看一下到底做了什么?

public static Integer valueOf(int i) {
return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
}
private static final Integer[] SMALL_VALUES = new Integer[256];
static {
for (int i = -128; i < 128; i++) {
SMALL_VALUES[i + 128] = new Integer(i);
}
}

  我把需要用到的函数都copy过来了,现在我们慢慢的分析这个过程。 当调用valueOf方法的时候,虚拟机会将参数42放入到valueOf中

return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];

  这里会有一个判断,当i>= 128 或者 i<-128的时候,会重新创建对象,而在i∈[-127,128]之间的时候,会直接返回出SMALL_VALUES [i + 128]。 这个SMALL_VALUES是个什么鬼?先不管它是什么,我们看到在static代码块中,有一个代码段是对其进行初始化,初始化的内容其实就是i∈[-127,128]的Integer对象。

  前面讲的有点乱,讲到这里,我们总结一下。当装包操作的时候,也就是将一个基本类型转换成对应的包装对象的时候,会调用一个静态方法valueOf,当需要包装的数在[-127,128]之间,就直接通过一个之前已经存在的对象数组返回这个对象,否则的话,就重新创建对象。

  在这里其实使用了一个缓存的概念,也就是说,Integer类缓存了[-127,128]之间的数的对象。通过这种缓存的机制,可以减少对于小数装包过程的性能消耗,可以提高性能。

  我们经常会遇到一些题目,如下:

Integer i=100;
Integer j=100;
//print true
System.out.println(i==j);

上面的代码运行后返回是true;

Integer i=100;
Integer j=100;
//print true
System.out.println(i==j);

上面的代码就很奇迹般的返回为false

  以前是不是很迷惑呢?明明都是一样的语句,为什么就换个数就变的结果?如果你看懂了上面的分析我相信你还是可以理解的。我们简单的分析一下吧! 对于第一段代码,因为i=100 在[-127,128] 之间,所以在装包的过程中,会返回缓存数组中的对象,因为两个值都是100,所以就会返回相同的对象,于是i==j也就是true。

  对于第二段代码,300 是不属于[-127,128]区间内的,对于会创建新的对象,自然返回出来的两个对象是不同,所以返回值为false。 上面就是装包过程,下面我们来说说解包过程吧! 其实道理都一样的,就简要的说一下吧!

  还从一个例子开始吧!

int n = Integer.valueOf(42);

  这回看一下字节码

invokestatic  #16                 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
invokevirtual #22

  除了会调用valueOf方法外,还会调用intValue方法

public int intValue() {
return value;
}

  直接就返回了Integer对象的字面量,就是一个int值。后面的分析相信大家都懂,就不多说了。这两天的复习简直摧残着我啊!不说了,放假后就可以安心的研究技术了,然后试试找个实习,大家晚安了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: