1. 自动装箱、拆箱
在《深入理解Java虚拟机》的语法糖这一章节中,看到了一个关于Java中自动装箱与拆箱的机制。书中有一个例子如下:
public static void main(String[] args){
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
System.out.println(c == d);
System.out.println(e == f);
System.out.println(c == (a + b));
System.out.println(c.equals(a + b));
System.out.println(g == (a + b));
System.out.println(g.equals(a + b));
}
最开始,本着==
判断的都是引用,equals判断的都是值的方式,初步判断的结果为:
false
false
false
true
false
false
但是运行之后的结果为:
true
false
true
true
true
false
当时就大吃一惊,后来通过查资料发现原因如下(也可以通过javap
反编译查看):
注:Integer.valueOf()
方法是装箱的体现,Integer.intValue()
方法是拆箱的体现。
上面的代码等价于以下代码:
public static void main(String[] args) {
// Integer a = 1;
// Integer b = 2;
// Integer c = 3;
// Integer d = 3;
// Integer e = 321;
// Integer f = 321;
// Long g = 3L;
//
// System.out.println(c == d);
// System.out.println(e == f);
// System.out.println(c == (a + b));
// System.out.println(c.equals(a + b));
// System.out.println(g == (a + b));
// System.out.println(g.equals(a + b));
Integer a = Integer.valueOf(1);
Integer b = Integer.valueOf(2);
Integer c = Integer.valueOf(3);
Integer d = Integer.valueOf(3);
Integer e = Integer.valueOf(321);
Integer f = Integer.valueOf(321);
Long g = Long.valueOf(3L);
System.out.println(c == d);
System.out.println(e == f);
System.out.println(c.intValue() == (a.intValue() + b.intValue()));
System.out.println(c.equals(Integer.valueOf(a.intValue() + b.intValue())));
System.out.println(g.longValue() == Integer.valueOf((a.intValue() + b.intValue())));
System.out.println(g.equals(Integer.valueOf(a.intValue() + b.intValue())));
}
原因如下:
- 包装类的
==
运算在不遇到算术运算符的情况下不会自动拆箱 - 包装类的
equals()
方法不处理数据类型转换的关系。下面是equals的源码:(即如果传入对象不和自己类型一样,直接返回false,这个编码规则在《Effective Java》中也有提到。)
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
还有一个问题,那为什么有c==d
结果为true
,而e==f
的结果为false
呢?
因为在Java
的Integer
实现中,默认已经将Java的[-128, 127]
的数据放入缓存中,需要的时候直接在缓存中取就可以了,超过这个范围的才会新生成对象,因此c
与d
为缓存中的相同对象,但是e
与f
为新生成的对象。
以下是JDK中的源码:
//static final int low = -128;
//static final int high;
//static final Integer cache[];
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
2 条件编译
许多程序设计语言都提供了条件编译的途径,如C、C++ 中使用预处理器指示符(#ifdef)完成条件编译。C、C++ 的预处理器最初的任务是解决编译时的代码依赖关系(如#include),而在 Java 语言之中并没有使用预处理器,因为 Java 语言天然的编译方式(不一个个编译 Java 文件,而是将所有编译单元的语法树顶级结点输入到待处理列表后再进行编译,因此各个文件之间能够互相提供符号信息) 无需使用预处理器。
Java 语言也可以进行条件编译,方法就是使用条件为常量的 if 语句。如下面代码:
public static void main(String[] args) {
if(true){
System.out.println("block 1");
}else{
System.out.println("block 2");
}
}
编译后Class文件的反编译结果:
public static void main(String args[])
{
System.out.println("block1");
}