Java语法糖系列二:自动装箱/拆箱和条件编译

96
Eric新之助
2016.12.05 17:34* 字数 697

目录:
Java语法糖系列一:可变长度参数和foreach循环
http://www.jianshu.com/p/628568f94ef8

Java语法糖系列二:自动装箱/拆箱和条件编译
http://www.jianshu.com/p/946b3c4a5db6

Java语法糖系列三:泛型与类型擦除
http://www.jianshu.com/p/4de08deb6ba4

Java语法糖系列四:枚举类型
http://www.jianshu.com/p/ae09363fe734

Java语法糖系列五:内部类和闭包
http://www.jianshu.com/p/f55b11a4cec2


上一篇写到可变长参数和foreach循环,这篇讨论下java的自动装箱/拆箱和条件编译这两个语法糖。

自动装箱/拆箱

自动拆箱/装箱是在编译期,依据代码的语法,决定是否进行拆箱和装箱动作。

装箱过程:把基本类型用它们对应的包装类型进行包装,使基本类型具有对象特征。

拆箱过程:与装箱过程相反,把包装类型转换成基本类型。

public static void main(String[] args){
    int i=1;
    Integer a = 1;
    Integer b = 1;
    Long c = 1L;
    System.out.println(a == b);
    System.out.println(a.equals(i));
    System.out.println(c.equals(a));
 }

结果是

 true
 true
 false

编译出来的代码如下

public static void main(String[] paramArrayOfString)
  {
      int i = 1;
       Integer localInteger1 = Integer.valueOf(1);
    Integer localInteger2 = Integer.valueOf(1);
       Long localLong = Long.valueOf(1L);
    System.out.println(localInteger1 == localInteger2);
       System.out.println(localInteger1.equals(Integer.valueOf(i)));
       System.out.println(localLong.equals(localInteger1));
  }

可以看到在自动装箱的时候,Java虚拟机会自动调用Integer的valueOf方法;
在自动拆箱的时候,Java虚拟机会自动调用Integer的intValue方法。这就是自动拆箱和自动装箱的原理。

注:上述例子的代码应该尽量避免自动拆箱与装箱。

条件编译

很多编程语言都提供了条件编译的途径,C,C++中使用#ifdef。
看如下C代码,在debug模式编译代码块1,其他编译代码块2

#define DEBUG  
#IFDEF DEBUUG  
  /* 
   code block 1 
   */   
#ELSE  
  /* 
   code block 2 
  */  
#ENDIF  

Java语言并没有提供这种预编译功能,但是Java也能实现预编译。
源代码

public static void main(String[] args){
    // TODO Auto-generated method stub
    if(true){  
        System.out.println("true");  
    }else{  
        System.out.println("false");  
    }  
}

编译后的代码

 public static void main(String[] paramArrayOfString)
  {
        System.out.println("true");
  }

可以看到编译器会对代码进行优化,对于条件永远为false的语句,JAVA编译器将不会对其生成字节码。这一动作发生在编译器解除语法糖阶段。所以说,可以利用条件语句来实现预编译。

至于运用当然是在bebug模式下打log啦

public static void main(String[] args){
    // TODO Auto-generated method stub
    boolean DEBUG=true;
    if(DEBUG) {  
        log.info("Syntactic Sugar!");  
    }  
}

一点拓展

看如下代码

public static void main(String[] args){
    // TODO Auto-generated method stub
         Integer i1 = 127;
        Integer i2 = 127;
        Integer i3 = 128;
        Integer i4 = 128;
 
        System.out.println(i1 == i2);
        System.out.println(i3 == i4);
    
        Double d1 = 127.0;
        Double d2 = 127.0;
        Double d3 = 128.0;
        Double d4 = 128.0;
 
        System.out.println(d1 == d2);
        System.out.println(d3 == d4);
}

结果是

true
false
false
false

产生这样的结果的原因是:Byte、Short、Integer、Long、Char这几个装箱类的valueOf()方法是以128位分界线做了缓存的,假如是[-128,127]区间的值是会取缓存里面的引用的,以Integer为例,其valueOf(int i)的源代码为:

static final int low = -128;
static final int high=127;

 public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

而Float、Double不会的原因也很简单,因为byte、Short、integer、long、char在某个范围内的整数个数是有限的,但是float、double这两个浮点数却不是。

Java