Java的语法糖,就是编译器为了方便大家写代码,对于一些代码,做了一些简化的语法,可以让大家在平时写代码的时候,更方便写出容易阅读的代码,但是在编译完之后,编译器会把这些代码,再还原回来。在Java中,有1个大家最经常使用的语法糖,就是整形变量的自动装箱,Integer a = 10;很明显10不是一个对象,Integer是一个对象,不同的东西,不能赋值,为啥还可以这么做呢? 这个东西就是Java的语法糖。
Java中的语法糖有以下几种:
泛型(JDK1.5)
自动装箱和拆箱(JDK1.5)
变长参数(JDK1.5)
实现Iterable接口的foreach语法(JDK1.5)
接口AutoCloseable支持的try-with-resource用法(JDK1.7)
switch支持枚举和String(JDK1.7)
1、泛型(JDK1.5) 泛型是1个编程语言是否友好的重要标志,时下火热的Go语言,至今还没有实现泛型,Java中的泛型是通过类型擦除实现的,并不是真正意义上的泛型,由于本文不打算深入讲解泛型,在此不过多叙述。看以下泛型编译之后,被JVM实际执行的代码是什么样子的
源代码(1.5之前)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 List arr = new ArrayList(); arr.add("this is first" ); arr.add(123 ); for (Object obj : arr) { System.out.println(obj); } List arr = new ArrayList(); arr.add("this is first" ); arr.add(123 ); Iterator var6 = arr.iterator(); while (var6.hasNext()) { Object obj = var6.next(); System.out.println(obj); }
JDK1.5之后,为了兼容1.5之前的代码,老代码可以保持不变,新代码如果是:
1 2 3 List<String> arr = new ArrayList<String>(); arr.add("this is first" ); arr.add(123 );
会报编译器错误,原理是一样的,都会在编译期转变成Object对象,然后强制转换成需要的类型。
2、自动装箱和拆箱(JDK1.5) 直接上代码吧,这个比较容易理解,就是JAVA会自动在基本类型和基本类型的包装类之间,进行自动转换,当然这只是一种语法糖,代码编译成class文件的时候,就会自动把这个转换成对应的方法:
1 2 3 4 5 6 7 8 9 10 11 int a = 1290 ;Integer b = 1000 ; Integer a1 = a; int b1 = b.intValue(); int a = 1290 ;Integer b = new Integer(1000 ); Integer a1 = Integer.valueOf(a); int b1 = b;
其它的如:
1 2 3 4 5 6 7 8 9 10 11 Character ch = 'a' String s = "abc" ; Integer a = 10 ; int b = a + 100 ; int c = add(10 ,30 );private int add (Integer a,Integer b) {return a + b;}
Java中的以下几种类型都支持自动拆箱,装箱。
Primitive type Wrapper class
boolean Boolean
byte Byte
char Character
float Float
int Integer
long Long
short Short
double Double
资料来源:https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html
3、变长参数(JDK1.5) 变长参数指的是,一个方法的参数,可以有多个,具体有多少个,不确定,如果没有语法糖,我们一般会传一个List的引用进去,下面来看看,Java的这个变长参数是怎么实现的(强制转换为数组),不过平时我用的也不多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private void printStr (String... arr) { for (String s : arr) { System.out.println(s); } } private void printStr (String... arr) { String[] var2 = arr; int var3 = arr.length; for (int var4 = 0 ; var4 < var3; ++var4) { String s = var2[var4]; System.out.println(s); } }
4、实现Iterable接口的foreach语法(JDK1.5) 这个是平时用到的最多的一个语法糖了,如果你刚开始写Java,会遇到很多这种代码,算是用的比较多的一个了,其实上面代码中有体现,直接转Iterator迭代实现,所以需要实现了Iterable接口的类,才可以使用这个语法糖。
1 2 3 4 5 6 7 8 9 10 11 List<String> arr = new ArrayList<String>(); for (String s : arr) { System.out.println(s); } Iterator var6 = arr.iterator(); while (var6.hasNext()) { String s = (String)var6.next(); System.out.println(s); }
5、接口AutoCloseable支持的try-with-resource用法(JDK1.7) 这个用的就更少了,我很少在实际的项目中见到这样的代码,也可能是因为平时读取资源的文件不多,平时很多时候都是CRUD了,仅仅是1个糖,编译后,又还原了最初的样子,还是传统的try{} catch() {} finally{} 这个结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 String content = "this is what you want to write to file" ; try (BufferedWriter out = new BufferedWriter(new FileWriter("/tmp/a.txt" , true ))) { out.write(content); System.out.println("content is :" + content); } catch (IOException e) { } finally { } try { BufferedWriter out = new BufferedWriter(new FileWriter("/tmp/a.txt" , true )); Throwable var8 = null ; try { out.write(content); System.out.println("content is :" + content); } catch (Throwable var26) { var8 = var26; throw var26; } finally { if (out != null ) { if (var8 != null ) { try { out.close(); } catch (Throwable var25) { var8.addSuppressed(var25); } } else { out.close(); } } } } catch (IOException var28) { } finally { }
6、switch支持枚举和String(JDK1.7) 这个就比较冷门了,平时编程使用switch的时候,很少使用String来做枚举,一般都是用int的比较多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 String s = "abc" ; switch (s) { case "abc" : System.out.println("this is abc" ); break ; case "c" : System.out.println("this is c" ); break ; default : break ; } String s = "abc" ; byte var3 = -1 ;switch (s.hashCode()) { case 99 : if (s.equals("c" )) { var3 = 1 ; } break ; case 96354 : if (s.equals("abc" )) { var3 = 0 ; } } switch (var3) { case 0 : System.out.println("this is abc" ); break ; case 1 : System.out.println("this is c" ); }
可见是计算了string的hashcode,然后把hashcode转换为了整型的switch,为了处理hash冲突,使用equals来做最后的确认,并不是在底层就支持String,所以具体在使用的时候,需要知道原理是什么,如果String串特别长,很明显性能就不太好,不适合使用。
结尾 语法糖是java中的一个小小的插曲,不会对你的编程生涯造成什么太大的影响,即使不专门学习,也能在项目中学习到,因为用到的实在太多了,学了一下,感觉也没有提升太多的知识,归根到底,还是因为这个东西太简单了,如果说非要有一点要注意的地方,就是整形的自动装箱和拆箱,会有一个小小的坑,那就是日常使用的时候,经常会写这样的代码:
1 2 3 4 5 6 7 Integer a = 10 ; int b = 20 ;if (a != null && a == b) { do something } else { do something }
当我们在比较a和b的值的时候,需要心里清楚,这是Java的自动拆箱,有的时候也会有空指针异常,所以一定要保证a != null 说到包装类的比较,还有一个小知识点,需要注意的地方,就是包装类的缓存:
1 2 3 4 5 6 7 8 9 10 Integer a = 127 ; Integer b = 127 ; boolean b1 = a == b;Integer a0 = 128 ; Integer b0 = 128 ; boolean b2 = a0 == b0;System.out.println("b1 is :" + b1); System.out.println("b2 is :" + b2)
执行上面的代码,b1和b2是true还是false呢? 实际的结果是b1 is : true b2 is : false,为什么呢? 因为Java对于包装类,有1个缓存,a和b指向的是同1个对象,这个缓存的范围是 -128 到 127 之间,在这个范围内,2个包装类都是相等的。感兴趣的话可以看Integer的源码,里面有1个IntegerCache的内部类。
文本完。