编译器是一种计算机程序, 它主要的目的是将便于人编写、阅读、维护的高级计算机语言所写的源代码程序, 翻译为计算机能解读、运行的低阶机器语言的程序, 即可执行文件。而 javac 就是java语言中的编译器, 它用于将 .java 文件转换成JVM能识别的 .class 字节码文件, 反编译则是将 .class 文件转换成 .java 文件。
语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。
java中的语法糖只存在于编译期, 在编译器将 .java 源文件编译成 .class 字节码时, 会进行解语法糖操作, 还原最原始的基础语法结构。关于反编译工具, 其实在JDK中自带了一个javap命令,可以使用反编译查看语法糖编译做了什么。
1、字符串拼接
String string = ""; String[] strs = {"iniy","init","zzz"} ; for (String str : strs) { string += str ; //编译时语法糖每次循环都会创建一个StringBuilder }编译后
当我们使用+号进行字符串拼接操作时, 编译时会自动创建一个StringBuilder对象。所以当在循环中拼接字符串时, 应避免使用+号操作, 否则每次循环都会创建一个StringBuilder对象再回收, 造成较大的开销。
2、条件编译
if(false) { System.out.println("语法糖会把这端代码消除"); }else { System.out.println("最终保留"); } //编译后 System.out.println("最终保留");javac编译器在编译时期的解语法糖阶段, 会将条件分支不成立的代码进行消除。
3、断言
public void assertTest(String s) { assert (!s.equals("Fred")); System.out.println(s); }编译后
当断言结果为true时, 程序继续正常执行, 当断言结果为false时, 则抛出AssertionError异常来打断程序的执行
4、可变参数
public void varargsTest(String ... arr) { for (String s : arr) { System.out.println(s); } }编译后
可变参数其实就是一个不定长度的数组, 数组长度随传入方法的对应参数个数来决定。可变参数只能在参数列表的末位使用。
5、枚举与Switch语句、字符串与Switch
6、自动装箱、自动拆箱
public Double autoBoxingTest(Integer i, Double d) { return d + i; }首先我们知道, 基本类型与包装类型在某些操作符的作用下, 包装类型调用valueOf()方法的过程叫做装箱, 调用xxxValue()方法的过程叫做拆箱。所以上面的结果很容易看出, 先对两个包装类进行拆箱, 再对运算结果进行装箱。
关于Integer
Integer integer = new Integer(null) ; // Ingeter 源码 public Integer(String s) throws NumberFormatException { this.value = parseInt(s, 10); } public static int parseInt(String s, int radix) throws NumberFormatException { if (s == null) { throw new NumberFormatException("null"); } }执行此代码会报错,因为Integer初始化的时候会自动给内部类的int value赋值,null会抛异常
Integer a = new Integer(1000) ; Integer b = new Integer(1000) ; Integer c = new Integer(100) ; Integer d = new Integer(100) ; Integer e = 100, f = 100; Integer g = 1000, h = 1000; Integer i = Integer.valueOf(100); System.out.println(a == b); //false System.out.println(c == d); //false System.out.println(e == f); //true System.out.println(g == h); //false System.out.println(a.equals(b)); //true System.out.println(a.intValue() == b.intValue()); //true System.out.println(i == c); //false System.out.println(i == e); //true //equals会先进行数值类型对比,在进行值对比 public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; } // Integer e = 100 等同于 Integer e = Integer.valueOf(100); // IntegerCache.low = -128 IntegerCache.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); }注意:1、Integer和Integer对比可以用equals,Integer和int可以用intvalue拆箱。
2、new Integer会创建一个类对象,是不相等的,静态方法valueof(或者直接装箱)是有一个IntegerCache保存-128到127之间的数值,是相等的。
7、泛型擦除
public void genericEraseTest() { List<String> list = new ArrayList<String>(); }在JVM中没有泛型这一概念, 只有普通方法和普通类, 所有泛型类的泛型参数都会在编译时期被擦除, 所以泛型类并没有自己独有的Class类对象比如List<Integer>.class, 而只有List.class对象
8、增强for循环
String[] qingshanli = {"a", "b", "c", "d"}; List<String> list = Arrays.asList(qingshanli); for (String s : list) { s = "xxxxx" ; } System.out.println(list); for(int i = 0; i<list.size();i++) { list.set(i,"xxxxx") ; } System.out.println(list); //结果 [a, b, c, d] [xxxxx, xxxxx, xxxxx, xxxxx]增强for循环的底层其实还是通过迭代器来实现的, 这也就解释了为什么增强for循环中不能进行增删改操作。
不用定义局部变量以及判断list长度的循环成为增强for循环。
9、try-with-resources语句
传统关闭资源方式,try-catch-finally,避免finally出现异常还要try-catch,很繁琐
public static void main(String[] args) { FileInputStream inputStream = null; try { inputStream = new FileInputStream(new File("test")); System.out.println(inputStream.read()); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } } } }try-with-resource语法
public static void main(String[] args) { try (FileInputStream inputStream = new FileInputStream(new File("test"))) { System.out.println(inputStream.read()); } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } }把流的初始化放在了try()里面,在语法糖编译时会还原成try-catch-finally
10、JDK10 局部变量
//初始化局部变量 var string = "qingshanli";编译时会把var局部变量编译成真的数据类型,因为JAVA是强语言。var i = "10"-1; 这种可以在js中实现,JAVA不可以。
jdk10 var只允许初始化值,且不为null。因为外部调用时给var放不同的数值类型会让JVM不知道到底是什么数据类型。