增强 for 循环
为什么有了普通的 for 循环后,还要有增强 for 循环呢?想一下,普通 for 循环你不是需要知道遍历次数?每次还需要知道数组的索引是多少,这种写法明显有些繁琐。增强 for 循环与普通 for 循环相比,功能更强并且代码更加简洁,你无需知道遍历的次数和数组的索引即可进行遍历。
增强 for 循环的对象要么是一个数组,要么实现了 Iterable 接口。这个语法糖主要用来对数组或者集合进行遍历,其在循环过程中不能改变集合的大小。
public static void main(String[] args) { String[] params = new String[]{"hello","world"}; //增强for循环对象为数组 for(String str : params){ System.out.println(str); } List<String> lists = Arrays.asList("hello","world"); //增强for循环对象实现Iterable接口 for(String str : lists){ System.out.println(str); } }
经过编译后的 class 文件如下
public static void main(String[] args) { String[] params = new String[]{"hello", "world"}; String[] lists = params; int var3 = params.length; //数组形式的增强for退化为普通for for(int str = 0; str < var3; ++str) { String str1 = lists[str]; System.out.println(str1); } List var6 = Arrays.asList(new String[]{"hello", "world"}); Iterator var7 = var6.iterator(); //实现Iterable接口的增强for使用iterator接口进行遍历 while(var7.hasNext()) { String var8 = (String)var7.next(); System.out.println(var8); } }
如上代码所示,如果对数组进行增强 for 循环的话,其内部还是对数组进行遍历,只不过语法糖把你忽悠了,让你以一种更简洁的方式编写代码。
而对继承于 Iterator 迭代器进行增强 for 循环遍历的话,相当于是调用了 Iterator 的 hasNext()
和 next()
方法。
Switch 支持字符串和枚举
switch
关键字原生只能支持整数
类型。如果 switch 后面是 String 类型的话,编译器会将其转换成 String 的hashCode
的值,所以其实 switch 语法比较的是 String 的 hashCode 。
如下代码所示
public class SwitchCaseTest { public static void main(String[] args) { String str = "cxuan"; switch (str){ case "cuan": System.out.println("cuan"); break; case "xuan": System.out.println("xuan"); break; case "cxuan": System.out.println("cxuan"); break; default: break; } } }
我们反编译一下,看看我们的猜想是否正确
根据字节码可以看到,进行 switch 的实际是 hashcode 进行判断,然后通过使用 equals 方法进行比较,因为字符串有可能会产生哈希冲突的现象。
条件编译
这个又是让小伙伴们摸不着头脑了,什么是条件编译呢?其实,如果你用过 C 或者 C++ 你就知道可以通过预处理语句来实现条件编译。
那么什么是条件编译呢?
一般情况下,源程序中所有的行都参加编译。但有时希望对其中一部分内容只在满足一定条件下才进行编译,即对一部分内容指定编译条件,这就是 条件编译(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 进行拼接。
学习语法糖的意义
互联网时代,有很多标新立异的想法和框架层出不穷,但是,我们对于学习来说应该抓住技术的核心。然而,软件工程是一门协作的艺术,对于工程来说如何提高工程质量,如何提高工程效率也是我们要关注的,既然这些语法糖能辅助我们以更好的方式编写备受欢迎的代码,我们程序员为什么要 抵制
呢?
语法糖也是一种进步,这就和你写作文似的,大白话能把故事讲明白的它就没有语言优美、酣畅淋漓的把故事讲生动的更令人喜欢。
我们要在敞开怀抱拥抱变化的同时也要掌握其 屠龙之技
。