在java开发设计过程中,了解java运行时和编译时的区别非常有必要。
如下从几个问题来描述两者的区别
line A是在编译时计算值,line B是在运行时计算值。
当该类编译后,如果使用一些反编译器(如jd-gui)反编译后可以看到,实际代码如下:
java编译时会做一些优化操作,如替换一些final的不可变更的参数。
由于number1和number2都是final修饰,那么product1的值是确定的,这里就会在编译时计算出product1的值。
除如上的final修饰情况下代码优化。
java中的泛型在编译时也会做优化,通过编译文件可以非常方便的看到其对应的实际类型,如下例子:
反编译后代码如下:
编译后的文件中,Parent类会显示的被实际类型取代。
因为,在编译时,如果调用了一个重载的方法,那么编译时必须确定他调用的方法是哪个。
这个也常被称为运行时多态的体现。编译器没有办法知道它调用的到底是那个方法,相反的,只有在jvm执行过程中,才知晓到底是父子类中哪个方法被调用。
如:
编译器会在编译时对泛型类型进行检测,并把它重写成实际的对象类型(非泛型代码),这样就可以被JVM执行了。这个过程被称为"类型擦除"。
类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并在必要的时候添加类型检查和类型转换的方法。
类型擦除可简单理解为将泛型java代码转换为普通java代码。不过编译器更直接点,将泛型java代码直接转换成普通java字节码。
类型擦除的主要过程如下:
java中的@Override注解就是典型的编译时注解,它在编译时会检查一些简单的如拼写的错误(与父类方法不相同)等
同样的@Test注解是junit框架的注解,它是一个运行时注解,它可以在运行时动态的配置相关信息如timeout等。
RuntimeException是一个用于指示编译器不需要检查的异常。RuntimeException是在jvm运行过程中抛出异常的父类。对于运行时异常是不需要再方法中显示的捕获或者处理的,如NullPointerException,ArrayIndexOutOfBoundsException
已检查的异常是被编译器在编译时候已检查过的异常,这些异常需在try/catch块中处理的异常。
1). 编译时:当你拥有源码的时候,AOP编译器(AspectJ编译器)能够编译源码并生成编织后的class。这些编织进入的额外功能是在编译时放进去的。
2). 预编译时:织入过程有时候也叫二进制织入,它是用来织入到哪些已经存在的class文件或者jar中的。
3). 运行时:当被织入的对象已经被加载如jvm中后,可以动态的织入到这些类中一些信息。
继承是一个多态的工具,而非重用工具。在没有多态关联关系的对象间,一些程序员倾向于用继承来保持重用。但事实是只有当子类和父类的关系为"is a"的关系时候,继承才会使用。
1. 不要使用继承来实现代码的重用。如果两者之间没有"is a"的关系,那么使用组合来实现重用。当父类的某个方法修改后,子类的相关实现也有可能会被更改。
2. 不要为了多态而使用继承。如果你只是为了实现多态而采用继承模式,那么实际上组合模式更加适合你,而且更加简洁和灵活。
这也就是为什么GoF设计模式中常说"组合优于继承"的原因。