持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情
我写的代码vs编译后的代码
环境: java8
对比下我们写的代码在编译为class文件之后的一些变化
自动装箱和自动拆箱
Integer a = 1;
int b = a;
我们使用 javap -p -v Test.class 查看详细的执行的字节码指令, 可以看出这两行代码在编译之后多了Integer.valueOf()和Integer.intValue()
也就是多了我们常说的自动装箱和自动拆箱, 写代码过程中应该尽量避免这种没必要产生的性能损耗
字符串拼接
String a = "a" + "b";
我们打开Test.class文件, 在idea反编译后的代码可以看出, 编译后的class文件中代码进行了
常量折叠, 最终只在常量池产生了一个"ab"对象
这里关于常量池还有个有意思的, 之前在看《深入理解Java虚拟机》第二版的时候, 有这样一段测试代码, 当时看的也是非常疑惑
终于由R大解释了这个问题, 并在《深入理解Java虚拟机》第三版中标注出来
String a = "a";
String b = "b";
String c = a + b;
String a = "";
for (int i = 0; i < 10; i++) {
a = a + i;
}
System.out.println(a);
最终class文件中是使用StringBuilder进行拼接的, 我们知道String对象是不可变的, 如果没有编译器优化, 那么使用+符号连接会产生很多String对象
forEach
List<Integer> list = Arrays.asList(1, 2, 3);
for (Integer integer : list) {
System.out.println(integer);
}
forEach循环编译后的代码仍然是使用迭代的方式, 并且我们还可以看到
var2.next()进行了强制转换, Java中的泛型看起来并不是真的泛型,
编译后泛型被擦除, 自动做强制转换, 这样做让我们在编写代码的过程中不必做恶心的强制转换(强制转换总是不那么让人放心),
lambda表达式
Consumer consumer = a -> System.out.println("hello");
同样使用 javap -p -v Test.class
可以看出来
lambda表达式最终是生成了一个lambda$main$0(java.lang.Object)的方法, 还是普通的方法调用, 但是lambda表达式写起来更简单,
我们知道在lambda表达式中不能给变量重新赋值, 为什么呢? 我们把上面的例子稍微改造一下
String s = "";
Map map = new HashMap();
Consumer consumer = a -> {
System.out.println(s);
map.put("a","b");
System.out.println("hello");
};
再使用javap -p -v Test.class, 可以看到生成了
lambda$main$0(java.lang.String, java.util.Map, java.lang.Object)方法, 将变量当做方法参数进行传递,
这也是为什么不能给变量重新赋值的原因了, 但是对象属性或者数组的值可以被操作, 那么我们可以用这样的方式变相的来给变量重新赋值
String s = "s";
String[] ss = new String[]{s};
Consumer consumer = a -> ss[0] = "a";
consumer.accept(1);
s = ss[0];
System.out.println(s);