转自:https://1fishman.github.io/2019/04/21/java%E7%BC%96%E8%AF%91%E4%BC%98%E5%8C%96/
java编译
java编译器为我们做了很多优化,比如在java中泛型并不是真正的泛型,在编译的时候会进行泛型擦除,使用的时候再进行类型转换.或者Integer自动装箱和拆箱.foreach循环遍历等等.
泛型擦除
在java中泛型并不是真正的泛型,因为有一个java早期没有泛型的时候都是通过Object来代替泛型的,因为java中每个对象都是继承自Object的.在通过类型转换来实现泛型.现在有了泛型.通过泛型来指定类型.但是这个泛型也不是真正的泛型.在编译期间都会进行泛型擦除.使其变为普通的类型.下面来看个例子
源代码
ArrayList<Integer> ls = new ArrayList<>(); ls.add(1); ls.add(2); int a = ls.get(0); System.out.println(a);
编译之后的字节码反编译过来的文件.
ArrayList var1 = new ArrayList(); var1.add(1); var1.add(2); int var2 = (Integer)var1.get(0); System.out.println(var2);
这里可以看到这个时候哦ArrayList已经没有泛型了,只是他原来的类型.在获取字段的时候会有一个类型转换的操作.所以有一点就是在进行方法重载的时候ArrayList 和 ArrayList 类型是一样的,编译会出现错误.
也正是有了泛型擦除,就有了一个问题,你可以向一个定义了类型的容器中添加其他类型的变量.看下面代码:
1 |
List<String> ls = new ArrayList<>(); |
这里通过反射向list中添加Integer变量,并没有报错,但是,这个时候如果在运行时候调用get()方法,那么如上所说,会在get方法前加入强制类型转换,所以会在运行时期报错.
Integer. Double 自动装箱与拆箱
在java中提供了自动装箱与拆箱的功能,就是把int变成Integer对象或者反过来.因为在泛型中只能存储对象而不能是普通值.而且在Integer或者Long中都有自己的数字缓存.都缓存了从-128~127之间的数字.意思就是在这些数字范围内的Integer对象都引用的是同一个对象.在看一个例子
1 |
public static void main(String[] args) { |
这里可以看到从128开始就不缓存了.但是在128之前的数字都是缓存的.都引用的是同一个缓存的对象.但是在代码中最后还是使用equals来比较对象.这是最稳妥的.
foreach循环遍历
foreach循环遍历是代码中很常见的一个用法,但是他底层是怎么实现的呢,很多人不知道.其实也是很简单的,下面看实例;
1 |
public static void main(String[] args) { |
上面代码编译过后就能看到
1 |
public static void main(String[] var0) { |
循环遍历在普通容器中变成了迭代器遍历,在数组中变成了普通的for遍历.这也是为什么循环遍历的容器必须实现iterator接口的原因.
变长参数
变长参数其实就是一个数组,取决于你传入了几个参数.
1 |
public static void close(Closeable... objs) throws IOException { |
字节码反编译过后的代码
1 |
public static void close(Closeable... var0) throws IOException { |
能够看到实际上objs就是一个数组,应该就是通过得到一个数组,然后在循环遍历.所以说尽量少用变长参数,因为变长参数会有一个内部的数组建立的过程,所以速度肯定会降低.
int short 优化
平时编写代码的时候可能不注意,但是看的话会发现编译器做了很多优化,比如就是int优化,这也是不小心发现的.例如下面的例子
1 |
public static void main(String[] args) { |
编译过后
1 |
public static void main(String[] var0) { |
可以看到当int的值比较小的时候可能会用byte或者short来代替int.
当此值变成比这个值更大的值的时候就能发现这个变量变了.var2变成var10,在之后用到var2的地方也都变成了var10.
但是细看这里的编译过后的代码就会发现一个问题,就是在char变量g变成了boolean类型.这里也就是编译器的第二个优化了.这里它会看此变量是否用过,如果在只有的程序中没有用过,那么就会赋值一个boolean类型.因为boolean类型可能是占用内存最小的了.