条件编译
一般情况下,源程序中所有的行都参加编译。但有时希望对其中一部分内容只在满足一定条件下才进行编译,即对一部分内容指定编译条件,这就是 条件编译(conditional compile)。
#define DEBUG
#IFDEF DEBUUG
/*
code block 1
*/
#ELSE
/*
code block 2
*/
#ENDIF
但是在 Java 中没有预处理和宏定义这些内容,那么我们想实现条件编译,应该怎样做呢?
使用 final + if 的组合就可以实现条件编译了。如下代码所示
public static void main(String[] args) {
final boolean DEBUG = true;
if (DEBUG) {
System.out.println("Hello, world!");
} else {
System.out.println("nothing");
}
}
这段代码会发生什么?我们反编译看一下
我们可以看到,我们明明是使用了 if …else 语句,但是编译器却只为我们编译了 DEBUG = true 的条件,
所以,Java 语法的条件编译,是通过判断条件为常量的 if 语句实现的,编译器不会为我们编译分支为 false 的代码。
断言
你在 Java 中使用过断言作为日常的判断条件吗?
断言:也就是所谓的 assert 关键字,是 jdk 1.4 后加入的新功能。它主要使用在代码开发和测试时期,用于对某些关键数据的判断,如果这个关键数据不是你程序所预期的数据,程序就提出警告或退出。当软件正式发布后,可以取消断言部分的代码。它也是一个语法糖吗?现在我不告诉你,我们先来看一下 assert 如何使用。
//这个成员变量的值可以变,但最终必须还是回到原值5
static int i = 5;
public static void main(String[] args) {
assert i == 5;
System.out.println("如果断言正常,我就被打印");
}
如果要开启断言检查,则需要用开关 -enableassertions 或 -ea 来开启。其实断言的底层实现就是 if 判断,如果断言结果为 true,则什么都不做,程序继续执行,如果断言结果为 false,则程序抛出 AssertError 来打断程序的执行。
assert 断言就是通过对布尔标志位进行了一个 if 判断。
try-with-resources
JDK 1.7 开始,java引入了 try-with-resources 声明,将 try-catch-finally 简化为 try-catch,这其实是一种语法糖,在编译时会进行转化为 try-catch-finally 语句。新的声明包含三部分:try-with-resources 声明、try 块、catch 块。它要求在 try-with-resources 声明中定义的变量实现了 AutoCloseable 接口,这样在系统可以自动调用它们的 close 方法,从而替代了 finally 中关闭资源的功能。
如下代码所示
public class TryWithResourcesTest {
public static void main(String[] args) {
try(InputStream inputStream = new FileInputStream(new File("xxx"))) {
inputStream.read();
}catch (Exception e){
e.printStackTrace();
}
}
}
我们可以看一下 try-with-resources 反编译之后的代码
可以看到,生成的 try-with-resources 经过编译后还是使用的 try…catch…finally 语句,只不过这部分工作由编译器替我们做了,这样能让我们的代码更加简洁,从而消除样板代码。
字符串相加
这个想必大家应该都知道,字符串的拼接有两种,如果能够在编译时期确定拼接的结果,那么使用 + 号连接的字符串会被编译器直接优化为相加的结果,如果编译期不能确定拼接的结果,底层会直接使用 StringBuilder 的 append 进行拼接,如下图所示。
public class StringAppendTest {
public static void main(String[] args) {
String s1 = "I am " + "cxuan";
String s2 = "I am " + new String("cxuan");
String s3 = "I am ";
String s4 = "cxuan";
String s5 = s3 + s4;
}
}
上面这段代码就包含了两种字符串拼接的结果,我们反编译看一下
首先来看一下 s1 ,s1 因为 = 号右边是两个常量,所以两个字符串拼接会被直接优化成为 I am cxuan。而 s2 由于在堆空间中分配了一个 cxuan 对象,所以 + 号两边进行字符串拼接会直接转换为 StringBuilder ,调用其 append 方法进行拼接,最后再调用 toString() 方法转换成字符串。
而由于 s5 进行拼接的两个对象在编译期不能判定其拼接结果,所以会直接使用 StringBuilder 进行拼接。
学习语法糖的意义
互联网时代,有很多标新立异的想法和框架层出不穷,但是,我们对于学习来说应该抓住技术的核心。然而,软件工程是一门协作的艺术,对于工程来说如何提高工程质量,如何提高工程效率也是我们要关注的,既然这些语法糖能辅助我们以更好的方式编写备受欢迎的代码,我们程序员为什么要 抵制 呢?
语法糖也是一种进步,这就和你写作文似的,大白话能把故事讲明白的它就没有语言优美、酣畅淋漓的把故事讲生动的更令人喜欢。
我们要在敞开怀抱拥抱变化的同时也要掌握其 屠龙之技。
文章为转载