一、定义
什么叫语法糖?顾名思义,我的理解是,编程语言给程序员提供的一些个便利的操作,在保证性能的基础上提高开发效率。
帖一下百度百科解释: 语法糖(Syntactic sugar),是由Peter J. Landin(和图灵一样的天才人物,是他最先发现了Lambda演算,由此而创立了函数式编程)创造的一个词语,它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。
二、java语言中的语法糖
Java中的语法糖包括但不限于以下10颗:泛型与类型擦除、自动装箱和拆箱、遍历循环、变长参数、条件编译、内部类、枚举类、断言语句、对枚举和字符串的switch支持、在try语句中定义和关闭资源。
1、泛型与类型擦除,前面博文中有介绍,java的泛型是伪泛型,在编译的时候进行擦除。
2、自动装箱和拆箱,前面博文中也有,Java基本数据类型都有封装类型,Integer i=100,就是自动装箱,int j = i,就是自动拆箱,如果int k = 100,先装箱后拆箱。特殊性就是在-128~127之间的整数,Integer可以缓存,重用。
3、foreach循环遍历
List list = new ArrayList();
for(Object object:list){
...
}
或者
List<Integer> list = new ArrayList<Integer>();
for(Integer num : list){
System.out.println(num);
}
Foreach要求被历遍的对象要实现Iterable接口,由此可想而知,foreach迭代也是调用底层的迭代器实现的。反编译上面源码的字节码:
List list = new ArrayList();
Integer num;
Integer num;
for (Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(num)){
num = (Integer) iterator.next();
}
4、条件编译
if(true){
System.out.println("oliver");
}else{
System.out.println("lee");
}
编译后只有System.out.println("oliver");在编译器中,将会把分支不成立的代码消除,这一动作发生在编译器解除语法糖阶段。所以说,可以利用条件语句来实现预编译
5、枚举
啊...原来枚举是一块糖啊,记得前段时间,加的一个java群里面的人讨论枚举类型,到底是数据类型还是类?当时很是迷惑...查看java api如下:
public abstract class Enum<E extends Enum<E>>extends Objectimplements Comparable<E>, Serializable
java doc上说它是一个所有 Java 语言枚举类型的公共基本类。
枚举类型其实并不复杂,在JVM字节码文件结构中,并没有“枚举”这个类型。
其实源程序的枚举类型,会在编译期被编译成一个普通了类。利用继承和反射,这是完全可以做到的。
看下面一个枚举类:
public enum TestEnum {
A,B
}
反编译之后会怎样?(jad.exe -s java xx.class)
public final class TestEnum extends Enum
{
private TestEnum(String s, int i)
{
super(s, i);
}
public static TestEnum[] values()
{
TestEnum atestenum[];
int i;
TestEnum atestenum1[];
System.arraycopy(atestenum = ENUM$VALUES, 0, atestenum1 = new TestEnum[i = atestenum.length], 0, i);
return atestenum1;
}
public static TestEnum valueOf(String s)
{
return (TestEnum)Enum.valueOf(javaTest/TestEnum, s);
}
public static final TestEnum A;
public static final TestEnum B;
private static final TestEnum ENUM$VALUES[];
static
{
A = new TestEnum("A", 0);
B = new TestEnum("B", 1);
ENUM$VALUES = (new TestEnum[] {
A, B
});
}
}
可见,字节码中已经被改成类的的实现了。
通过以上分析,java枚举是一个类,不是语言本身实现的,而是编译器实现的,我们可以直接调用里面的方法。Enum 本身就是个普通的 class, 可以有很多自定义方法用来实现不同的功能。如果我们不自定义里面的方法,编译器就能初始化,默认顺序从0递增。我们也可以自定义方法,这样就能随便赋值。
public enum TestEnum {
A(1),B(2);
private final int value;
public int getValue(){
return this.value;
}
TestEnum(int value){
this.value=value;
}
}
6、变长参数
上代码
public void function(String...arg){
}
反编译之后,还是用jad来做
public transient void function(String as[])
{
}
参数变成了String数组,但是要注意的是,变长参数必须是方法参数的最后一项。
例如:
public void function(int abc, String...arg){
}
7、内部类
内部类:成员内部类(类中定义),静态内部类(加上static),局部内部类(在方法内),匿名类,接口类,具体详细介绍后面会写博文学习
这里看两个简单的:普通的内部类和静态内部类
public class InnerClass {
private int OUT;
class InnerA{
private int A;
}
static class InnerB{
private int B;
}
}
反编译之后
public class InnerClass
{
class InnerA
{
private int A;
final InnerClass this$0;
InnerA()
{
this$0 = InnerClass.this;
super();
}
}
static class InnerB
{
private int B;
InnerB()
{
}
}
public InnerClass()
{
}
private int OUT;
}
发现InnerA中编译器给加上了外部内的一个引用,让其访问外部资源。静态内部类没加,而且发现InnerA要实例化的时候需要调用super(),也就是需要外部类的实力,静态内部类就不需要,直接用类名就可以。这里编译器给的糖是给普通内部类代码中添加了些代码。
8、断言语句
略过...
9、对枚举和字符串的switch支持
利用枚举的那个类,写一个switch
TestEnum te = null;
switch(te){
case A:
System.out.println("A");
break;
case B:
System.out.println("B");
break;
default:
System.out.println("Null");
}
还是反编译一下看看呗。
public static void main(String args[])
{
TestEnum te = null;
switch($SWITCH_TABLE$javaTest$TestEnum()[te.ordinal()])
{
case 1: // '\001'
System.out.println("A");
break;
case 2: // '\002'
System.out.println("B");
break;
default:
System.out.println("Null");
break;
}
}
static int[] $SWITCH_TABLE$javaTest$TestEnum()
{
$SWITCH_TABLE$javaTest$TestEnum;
if($SWITCH_TABLE$javaTest$TestEnum == null) goto _L2; else goto _L1
_L1:
return;
_L2:
JVM INSTR pop ;
int ai[] = new int[TestEnum.values().length];
try
{
ai[TestEnum.A.ordinal()] = 1;
}
catch(NoSuchFieldError _ex) { }
try
{
ai[TestEnum.B.ordinal()] = 2;
}
catch(NoSuchFieldError _ex) { }
return $SWITCH_TABLE$javaTest$TestEnum = ai;
}
private static int $SWITCH_TABLE$javaTest$TestEnum[];
呀呀呀,看看都什么变化。。。这个糖变化最大。。。
重点是case后面跟的是整型,说明switch的糖就是貌似可以支持枚举,但是还是利用的枚举中的成员的初始化值。
在来看一个字符串的switch
public static void main(String[] args) {
String abc="";
switch(abc){
case "A":
System.out.println("A");
break;
case "B":
System.out.println("B");
break;
default:
System.out.println("Null");
}
}
反编译之后
public static void main(String args[])
{
label0:
{
String abc = "";
String s;
switch((s = abc).hashCode())
{
default:
break;
case 65: // 'A'
if(s.equals("A"))
{
System.out.println("A");
break label0;
}
break;
case 66: // 'B'
if(!s.equals("B"))
break;
System.out.println("B");
break label0;
}
System.out.println("Null");
}
}
其实就是比较的字符串的hashcode。
10、在try语句中定义和关闭资源jdk7提供了try-with-resources,可以自动关闭相关的资源(只要该资源实现了AutoCloseable接口,jdk7为绝大部分资源对象都实现了这个接口)
Java7增强了try语句的功能,它允许在try关键字后紧跟一对圆括号,圆括号可以声明、初始化一个或多个资源(此处的资源是指那些必须在程序结束时显式关闭的资源,比如数据库连接,网络连接等),try-with-resources 是一个定义了一个或多个资源的try 声明,try语句在该语句结束时自动关闭这些资源。try-with-resources确保每一个资源在处理完成后都会被关闭。
上代码
public static void main(String[] args) {
try (
// 声明、初始化两个可关闭的资源
BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java"));
PrintStream ps = new PrintStream(new FileOutputStream("readme.txt"))
){
// 使用两个资源
System.out.println(br.readLine());
ps.println("test");
}catch(Exception e){
e.printStackTrace();
}
// 自动关闭资源的try语句相当于包含了隐式的finally块,用于关闭资源。
反编译看看
public static void main(String args[])
{
Exception exception;
exception = null;
Object obj = null;
BufferedReader br = new BufferedReader(new FileReader("AutoCloseTest.java"));
PrintStream ps = new PrintStream(new FileOutputStream("readme.txt"));
System.out.println(br.readLine());
ps.println("test");
if(ps != null)
ps.close();
break MISSING_BLOCK_LABEL_82;
exception;
if(ps != null)
ps.close();
throw exception;
if(br != null)
br.close();
break MISSING_BLOCK_LABEL_150;
Exception exception1;
exception1;
if(exception == null)
exception = exception1;
else
if(exception != exception1)
exception.addSuppressed(exception1);
if(br != null)
br.close();
throw exception;
exception1;
if(exception == null)
exception = exception1;
else
if(exception != exception1)
exception.addSuppressed(exception1);
throw exception;
Exception e;
e;
e.printStackTrace();
}
很明显了,给全部关掉了,但是貌似java7这个功能,我还没有用到,先学习吧。