Java 面试题问与答:编译时与运行时
在开发和设计的时候,我们需要考虑编译时,运行时以及构建时这三个概念。理解这几个概念可以更好地帮助你去了解一些基本的原理。下面是初学者晋级中级水平需要知道的一些问题。
Q.下面的代码片段中,行A和行B所标识的代码有什么区别呢?
public class ConstantFolding {static final int number1 = 5;static final int number2 = 6;static int number3 = 5;static int number4= 6;public static void main(String[ ] args) {int product1 = number1 * number2; //line Aint product2 = number3 * number4; //line B}}
A .在行A的代码中,product的值是在 编译期 计算的,行B则是在 运行时 计算的。如果你使用Java反编译器(例如,jd-gui)来反编译ConstantFolding.class文件的话,那么你就会从下面的结果里得到答案。
public class ConstantFolding
{static final int number1 = 5;static final int number2 = 6;static int number3 = 5;static int number4 = 6;public static void main(String[ ] args){int product1 = 30;int product2 = number3 * number4;}
}
常量折叠是一种Java编译器使用的优化技术。由于final变量的值不会改变,因此就可以对它们优化。Java反编译器和javap命令都是查看编译后的代码(例如,字节码)的利器。
Q.你能想出除了代码优化外,在什么情况下,查看编译过的代码是很有帮助的?
A.Java里的泛型是在编译时构造的,可以通过查看编译后的class文件来理解泛型,也可以通过查看它来解决泛型相关的问题。
Q.下面哪些是发生在编译时,运行时,或者两者都有?
A.
方法重载:这个是发生在编译时的。方法重载也被称为编译时多态,因为编译器可以根据参数的类型来选择使用哪个方法。
1
2
3
4
|
public class {
public static void evaluate(String param1); // method #1
public static void evaluate( int param1); // method #2
}
|
如果编译器要编译下面的语句的话:
1
|
evaluate(“My Test Argument passed to param1”);
|
它会根据传入的参数是字符串常量,生成调用#1方法的字节码
方法覆盖:这个是在运行时发生的。方法重载被称为运行时多态,因为在编译期编译器不知道并且没法知道该去调用哪个方法。JVM会在代码运行的时候做出决定。
1
2
3
4
5
6
7
8
9
10
11
12
|
public class A {
public int compute( int input) { //method #3
return 3 * input;
}
}
public class B extends A {
@Override
public int compute( int input) { //method #4
return 4 * input;
}
}
|
子类B中的compute(..)方法重写了父类的compute(..)方法。如果编译器遇到下面的代码:
1
2
3
|
public int evaluate(A reference, int arg2) {
int result = reference.compute(arg2);
}
|
编译器是没法知道传入的参数reference的类型是A还是B。因此,只能够在运行时,根据赋给输入变量“reference”的对象的类型(例如,A或者B的实例)来决定调用方法#3还是方法#4.
泛型(又称类型检验):这个是发生在编译期的。编译器负责检查程序中类型的正确性,然后把使用了泛型的代码翻译或者重写成可以执行在当前JVM上的非泛型代码。这个技术被称为“类型擦除“。换句话来说,编译器会擦除所有在尖括号里的类型信息,来保证和版本1.4.0或者更早版本的JRE的兼容性。
1
|
List<String> myList = new ArrayList<String>( 10 );
|
编译后成为了:
1
|
List myList = new ArrayList( 10 );
|
注解(Annotation):你可以使用运行时或者编译时的注解。
1
2
3
4
5
6
|
public class B extends A {
@Override
public int compute( int input){ //method #4
return 4 * input;
}
}
|
@Override是一个简单的编译时注解,它可以用来捕获类似于在子类中把toString()写成tostring()这样的错误。在Java 5中,用户自定义的注解可以用注解处理工具(Anotation Process Tool ——APT)在编译时进行处理。到了Java 6,这个功能已经是编译器的一部分了。
1
2
3
4
5
6
7
8
9
10
|
public class MyTest{
@Test
public void testEmptyness( ){
org.junit.Assert.assertTrue(getList( ).isEmpty( ));
}
private List getList( ){
//implemenation goes here
}
}
|
@Test是JUnit框架用来在运行时通过反射来决定调用测试类的哪个(些)方法的注解。
1
2
3
4
|
@Test (timeout= 100 )
public void testTimeout( ) {
while ( true ); //infinite loop
}
|
如果运行时间超过100ms的话,上面的测试用例就会失败。
1
2
3
4
|
@Test (expected=IndexOutOfBoundsException. class )
public void testOutOfBounds( ) {
new ArrayList<Object>( ).get( 1 );
}
|
如果上面的代码在运行时没有抛出IndexOutOfBoundsException或者抛出的是其他的异常的话,那么这个用例就会失败。用户自定义的注解可以在运行时通过Java反射API里新增的AnnotatedElement和”Annotation”元素接口来处理。
异常(Exception):你可以使用运行时异常或者编译时异常。
运行时异常(RuntimeException)也称作未检测的异常(unchecked exception),这表示这种异常不需要编译器来检测。RuntimeException是所有可以在运行时抛出的异常的父类。一个方法除要捕获异常外,如果它执行的时候可能会抛出RuntimeException的子类,那么它就不需要用throw语句来声明抛出的异常。
例如:NullPointerException,ArrayIndexOutOfBoundsException,等等
受检查异常(checked exception)都是编译器在编译时进行校验的,通过throws语句或者try{}cathch{} 语句块来处理检测异常。编译器会分析哪些异常会在执行一个方法或者构造函数的时候抛出。
面向切面的编程(Aspect Oriented Programming-AOP):切面可以在编译时,运行时或,加载时或者运行时织入。
- 编译期:编译期织入是最简单的方式。如果你拥有应用的代码,你可以使用AOP编译器(例如,ajc – AspectJ编译器)对源码进行编译,然后输出织入完成的class文件。AOP编译的过程包含了waver的调用。切面的形式可以是源码的形式也可以是二进制的形式。如果切面需要针对受影响的类进行编译,那么你就需要在编译期织入了。
- 编译后:这种方式有时候也被称为二进制织入,它被用来织入已有的class文件和jar文件。和编译时织入方式相同,用来织入的切面可以是源码也可以是二进制的形式,并且它们自己也可以被织入切面。
- 装载期:这种织入是一种二进制织入,它被延迟到JVM加载class文件和定义类的时候。为了支持这种织入方式,需要显式地由运行时环境或者通过一种“织入代理(weaving agent)“来提供一个或者多个“织入类加载器(weaving class loader)”。
- 运行时:对已经加载到JVM里的类进行织入
继承 – 发生在编译时,因为它是静态的
代理或者组合 – 发生在运行时,因为它更加具有动态性和灵活性。
Q.你有没有听说过“组合优于继承”这样的说法呢?如果听说过的话,那么你是怎么理解的呢?
A.继承是一种多态工具,而不是一种代码复用工具。有些开发者喜欢用继承的方式来实现代码复用,即使是在没有多态关系的情况下。是否使用继承的规则是继承只能用在类之间有“父子”关系的情况下。
- 不要仅仅为了代码复用而继承。当你使用组合来实现代码复用的时候,是不会产生继承关系的。过度使用继承(通过“extends”关键字)的话,如果修改了父类,会损坏所有的子类。这是因为子类和父类的紧耦合关系是在编译期产生的。
- 不要仅仅为了多态而继承。如果你的类之间没有继承关系,并且你想要实现多态,那么你可以通过接口和组合的方式来实现,这样不仅可以实现代码重用,同时也可以实现运行时的灵活性。
这就是为什么四人帮(Gang of Four)的设计模式里更倾向于使用组合而不是继承的原因。面试者会在你的答案里着重关注这几个词语——“耦合”,“静态还是动态”,以及“发生在编译期还是运行时”。运行时的灵活性可以通过组合来实现,因为类可以在运行时动态地根据一个结果有条件或者无条件地进行组合。但是继承却是静态的。
Q.你能够通过实例来区别编译期继承和运行时继承,以及指出Java支持哪种吗?
A.“继承”表示动作和属性从一个对象传递到另外一个对象的场景。Java语言本身只支持编译期继承,它是通过“extends”关键字来产生子类的方式实现的,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
|
public class Parent {
public String saySomething( ) {
return “Parent is called”;
}
}
public class Child extends Parent {
@Override
public String saySomething( ) {
return super .saySomething( ) + “, Child is called”;
}
}
|
“Child”类的saySomething()方法的调用会返回“Parent is called,Child is Called”,因为,子类的调用继承了父类的“Parenet is called”。关键字“super”是用来调用“Parent”类的方法。运行时继承表示在运行时构建父/子类关系。Java语言本身不支持运行时继承,但是有一种替代的方案叫做“代理”或者“组合”,它表示在运行时组件一个层次对象的子类。这样可以模拟运行时继承的实现。在Java里,代理的典型实现方式如下:
1
2
3
4
5
6
7
8
9
10
11
|
public class Parent {
public String saySomething( ) {
return “Parent is called”;
}
}
public class Child {
public String saySomething( ) {
return new Parent( ).saySomething( ) + “, Child is called”;
}
}
|
子类代理了父类的调用。组合可以按照下面的方式来实现:
1
2
3
4
5
6
7
8
9
10
11
|
public class Child {
private Parent parent = null ;
public Child( ){
this .parent = new Parent( );
}
public String saySomething( ) {
return this .parent.saySomething( ) + “, Child is called”;
}
}
|
英文原文:java-success,编译:ImportNew - 朱伟杰
译文链接:http://www.importnew.com/1796.html
【如需转载,请在正文中标注并保留原文链接、译文链接和译者等信息,谢谢合作!】
Java 面试题问与答:编译时与运行时相关推荐
- 【转】Java 面试题问与答:编译时与运行时
在开发和设计的时候,我们需要考虑编译时,运行时以及构建时这三个概念.理解这几个概念可以更好地帮助你去了解一些基本的原理.下面是初学者晋级中级水平需要知道的一些问题. Q.下面的代码片段中,行A和行B所 ...
- 【iOS沉思录】Objective-C语言的动态性总结(编译时与运行时)
编译时与运行时 编译时: 即编译器对语言的编译阶段,编译时只是对语言进行最基本的检查报错,包括词法分析.语法分析等等,将程序代码翻译成计算机能够识别的语言(例如汇编等),编译通过并不意味着程序就可以成 ...
- 记一道简单的Java面试题,但答错率很高!
作者:方志宏 https://zhuanlan.zhihu.com/p/57859872 这可能是历史上最简单的一道java面试题了. 题目很简单,完成代码,判断一个整数是否是奇数: public b ...
- 【你问我答】数据库运维遇到问题了?尽快抛过来吧!
点击上方蓝字,可以订阅哦 看了两期Java的[你问我答],后台有很多读者坐不住了: "数据库呢?" "小编搞一次数据库的问答吧,我要问!" "数据库有 ...
- java能调用python吗_如何使用运行时在Java中调用python程序 - java
我想用来自Java的参数调用python程序.但是我的输出是空白.代码在这里. Python代码在这里: import sys print(sys.argv[1]) Java代码在这里: public ...
- [转] 在 Mac OS X 下编译 Objective-C 运行时
原文地址:http://www.mulle-kybernetik.com/weblog/2011/10/how_to_build_libobjc_for_os_x.html Max OS X 版本:1 ...
- 全网最全的 Java 面试题汇总,爱了~
点击关注公众号,实用技术文章及时了解 不断收集整理,汇总网上面试知识点,方便面试前刷题,希望对你有帮助!有哪些方面的内容缺失,欢迎留言,后续不断补充. 01-10期 [01期]Spring,Sprin ...
- java builder pool_每周10道Java面试题:String, String Pool, StringBuilder
每周10道 Java 面试题由 ImportNew 整理编译自网络. 1. 写出下面代码的运行结果. int src = 65536; Integer dst = new Integer(65536) ...
- 吐血整理 这200道阿里P6必备Java面试题,我简直太爱了
前言 梳理了好久,总算是把面试题全部导出来了,毕竟还要上班,这次就给大家总结了一些Java开发岗位的经典面试题. 篇幅较大,阅读过程中可能会有点繁琐! 但请细细观看,文章末尾有留给大家的小惊喜!!! ...
最新文章
- ERR_CONTENT_LENGTH_MISMATCH
- 域控 正在应用计算机设置,域控制器下发的用户组策略在ou内的用户上没有应用...
- java 生成折线图_jfree jsp java 生成折线图(详解带jar)
- PC端连接Android设备进行adb调试
- MAUI中Maui.Graphics.Controls绘制控件
- c语言的跳线帽,电脑主板上跳线帽有什么作用图文介绍
- Webpack配置问题
- 高精度运算一(两个数的运算)
- MySQL使用内置函数来进行模糊搜索locate()与like的不同
- Android Q共享音频输入
- PHP图片与文字合成
- maven项目中通过idea工具打jar包
- 国产数据库--HighGo DB(瀚高数据库)
- 通过qmh启动qt应用
- 又发现个新的全网资源搜索神器
- IM模块-UiCollectionView列表显示气泡图片
- (python) 1200000有多少个约数(只计算正约数)
- 最短路径问题(Dijkstra常用用法总结)
- Epic Games创始人Tim Sweeney:头戴显示技术将颠覆电子产业
- 常用的正则表达式判断手机号邮箱等
热门文章
- Android 利用sharepreference保存应用程序状态
- 【小白学习PyTorch教程】十五、BERT:通过PyTorch来创建一个文本分类的Bert模型
- ‘FactorAnalyzer‘ object has no attribute ‘analyze‘和fa.loadings改成fa.loadings报错解决
- 五十四、Java日期Date,LocalDate类以及格式化输出
- 六十、第一个SpringBoot的 helloworld程序
- 关于数据库插入中文乱码问题
- 神经网络如何处理测试阶段出现的新特征?面向开放环境特征外推的图学习解决方案...
- PTAV:实时高精度目标追踪框架 | ICCV 2017论文解读
- java中文 x_java环境url中文参数乱码处理
- micropython开发idethonny_ESP32 Micropython开发利器Thonny IDE介绍