编译器是一种计算机程序, 它主要的目的是将便于人编写、阅读、维护的高级计算机语言所写的源代码程序, 翻译为计算机能解读、运行的低阶机器语言的程序, 即可执行文件。而 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 list = new ArrayList();
}
在JVM中没有泛型这一概念, 只有普通方法和普通类, 所有泛型类的泛型参数都会在编译时期被擦除, 所以泛型类并没有自己独有的Class类对象比如List
8、增强for循环
String[] qingshanli = {"a", "b", "c", "d"};
List list = Arrays.asList(qingshanli);
for (String s : list) {
s = "xxxxx" ;
}
System.out.println(list);
for(int i = 0; i
增强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不知道到底是什么数据类型。