前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java 面试题问与答:编译时与运行时?

Java 面试题问与答:编译时与运行时?

作者头像
技术从心
发布于 2019-08-08 07:58:23
发布于 2019-08-08 07:58:23
62300
代码可运行
举报
文章被收录于专栏:技术从心技术从心
运行总次数:0
代码可运行

在开发和设计的时候,我们需要考虑编译时运行时以及构建时这三个概念。理解这几个概念可以更好地帮助你去了解一些基本的原理。下面是初学者晋级中级水平需要知道的一些问题。

Q.下面的代码片段中,行A和行B所标识的代码有什么区别呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 A
 
          int product2 = number3 * number4;         //line B
 
    }
 
}

A.在行A的代码中,product的值是在编译期计算的,行B则是在运行时计算的。如果你使用Java编译器(例如,jd-gui)来反编译ConstantFolding.class文件的话,那么你就会从下面的结果里得到答案。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.

方法重载:这个是发生在编译时的。方法重载也被称为编译时多态,因为编译器可以根据参数的类型来选择使用哪个方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class {
     public static void evaluate(String param1);  // method #1
     public static void evaluate(int param1);   // method #2
}

如果编译器要编译下面的语句的话:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
evaluate(“My Test Argument passed to param1”);

它会根据传入的参数是字符串常量,生成调用#1方法的字节码

方法覆盖:这个是在运行时发生的。方法重载被称为运行时多态,因为在编译期编译器不知道并且没法知道该去调用哪个方法。JVM会在代码运行的时候做出决定。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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(..)方法。如果编译器遇到下面的代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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的兼容性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List<String> myList = new ArrayList<String>(10);

编译后成为了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
List myList = new ArrayList(10);

注解(Annotation):你可以使用运行时或者编译时的注解。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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,这个功能已经是编译器的一部分了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MyTest{
    @Test
     public void testEmptyness( ){
         org.junit.Assert.assertTrue(getList( ).isEmpty( ));
     }
 
     private List getList( ){
        //implemenation goes here
     }
}

@Test是JUnit框架用来在运行时通过反射来决定调用测试类的哪个(些)方法的注解。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Test (timeout=100)
public void testTimeout( ) {
    while(true);   //infinite loop
}

如果运行时间超过100ms的话,上面的测试用例就会失败。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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”关键字来产生子类的方式实现的,如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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里,代理的典型实现方式如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Parent {
    public String saySomething( ) {
          return “Parent is called”;
    }
}
 
public class Child  {
     public String saySomething( ) {
          return new Parent( ).saySomething( ) +, Child is called”;
    }
}

子类代理了父类的调用。组合可以按照下面的方式来实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

觉得文章不错,记得转发分享给更多同学哦~

- END -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-02-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 技术从心 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
linux日志管理
程序执行的时候,可以通过标准输出(stdout, Standard Output)与标准错误输出 (stderr, Standard Error Output)来输送信息,用户就可以了解该程序执行时发生了什么状况;可是对于在后台执行的服务器程序,或者Linux 内核本身来说,就没有办法这样做了。服务与内核启动后,会切断与终端机(Terminal) 或控制台(Console)的联机,如此一来,即使有信息通过标准输出、标准错误输出传送出去,用户也未必能从屏幕上看到信息。
星哥玩云
2022/09/15
1.6K0
linux系统日志管理详解
日志文件记录了时间,地点,人物,事件四大信息,故系统出现故障时,可以查询日志文件。 系统的日志文件默认都集中放置到/var/log/目录内,其中又以message记录的信息最多。 日志文件的重要性主要体现在以下三方面:
咻一咻
2020/05/29
9.4K0
Linux系统日志分析与管理
Linux系统内核和许多程序会产生各种错误信息、警告信息和其他的提示信息,这些信息对管理员了解系统的运行状态是非常有用的,所以应该把它们写到日志文件中去,完成这个过程的程序就是syslog,syslog可以根据日志的类别和优先级将日志保存到不同的文件中.
王 瑞
2022/12/28
4.9K0
Linux 日志管理
CentOS7.6日志服务是rsyslogd ,CentOS6.x日志服务是syslogd 。rsyslogd功能更强大。rsyslogd的使用、日志文件的格式,和 syslogd 服务兼容的。原理示意图
用户9615083
2022/12/25
5.6K0
Linux 日志管理
Linux系统安全 | Linux日志分析和管理
日志的作用、分类、管理、轮转和级别 日志的作用: 用于记录系统、程序运行中发生的各种事件 通过阅读日志,有助于诊断,解决系统故障 日志文件的分类: 内核及系统日志:由系统服务 rsyslog 统一管理,日志格式相似 用户日志:记录系统用户登录及退出系统的相关信息 程序日志:由各种应用程序独立管理的日志文件,格式不统一 日志管理策略 日志也并不是完全可靠的,高级的黑客在入侵系统后,会删除相应的日志记录,因此需要做好日志的管理工作: 日志的备份和归档 延长日志的保存期限 控制日志的访问权限 集中管理日志。比如,将服务器的日志文件发送到统一到日志文件服务器,这样便于日志信息的统一收集、整理和分析,还可以杜绝日志信息的意外丢失、恶意修改和删除 日志的轮转和切割 随着时间的推移,日志文件肯定会越来越大,而且这个趋势是呈线性增长。所以,需要对之前的日志文件做一些处理。日志轮转和切割指的是实现对当前日志归档,开始新的日志,删除早期的日志。Linux中,日志轮转和切割这个服务是由 logrotate 提供的。logrotate这个程序的目录:/etc/cron.daily/logrotate 。logrotate 是作为 corn 的一个每日任务,周期性执行的。它具备自动轮转、压缩、搬迁 和 邮件通知到日志系统的多项功能。每一个日志文件都可以按照每天、每周、每月周期性处理,或是增长到多大而触发处理。 日志消息的级别 日志消息的级别 level等级 状况 0 EMERG(紧急) 会导致主机系统不可用的情况 1 ALERT (警告) 必须马上采取措施解决的情况 2 CRIT (严重) 比较严重的情况 3 ERR (错误) 运行出现错误 4 WARNING (提醒) 可能会影响系统功能的事件 5 NOTICE (注意) 不会影响系统但值得注意 6 INFO (注意) 一般信息 7 DEBUG(调试) 程序或系统调试信息等
谢公子
2022/01/19
8.6K0
Linux系统安全 | Linux日志分析和管理
【Linux】日志管理基本使用
1) 日志文件是重要的系统信息文件,其中记录了许多重要的系统事件,包括用户的登录信息、系统的启动信息、系统的安全信息、邮件相关信息、各种服务相关信息等。
兮动人
2022/02/27
1K0
linux的服务与如何建立自己的服务
我们在上章就曾经谈过『服务』这东西! 当时的说明是『常驻在记体体中的程序,且可以提供一些系统或网络功能,那就是服务』。而服务一般的英文说法是『 service 』。
小柒吃地瓜
2020/04/23
4.7K0
Linux系统管理—linux计划任务和日志的管理
一种是突发性的,就是这次做完了这个事,就没有下一次了,临时决定,只执行一次的任务
全栈程序员站长
2022/09/14
2.3K0
Linux系统管理—linux计划任务和日志的管理
Linux 文件系统与日志分析「建议收藏」
  Linux,全称 GNU/Linux,是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 的多用户、多任务、支持多线程和多 CPU 的操作系统。伴随着互联网的发展,Linux 得到了来自全世界软件爱好者、组织、公司的支持。它除了在服务器方面保持着强劲的发展势头以外,在个人电脑、嵌入式系统上都有着长足的进步。使用者不仅可以直观地获取该操作系统的实现机制,而且可以根据自身的需要来修改完善Linux,使其最大化地适应用户的需要。   Linux 的基本思想有两点:一切都是文件;每个文件都有确定的用途。其中第一条详细来讲就是系统中的所有都归结为一个文件,包括命令、硬件和软件设备、操作系统、进程等等对于操作系统内核而言,都被视为拥有各自特性或类型的文件。至于说 Linux 是基于 Unix 的,很大程度上也是因为这两者的基本思想十分相近。
全栈程序员站长
2022/08/24
2.2K0
Linux 文件系统与日志分析「建议收藏」
Linux日志管理「建议收藏」
> /nar/log/messages ##清空目录内容
全栈程序员站长
2022/06/27
1.5K0
Linux日志管理「建议收藏」
linux rsyslogd cpu占用资源过高
最近有几次,linux centos 7 服务停了后,重启,再起一些应用后,查看top后,rsyslogd cpu占用率高问题, 先说我这块怀疑导致的原因吧。
iginkgo18
2021/10/10
5.5K0
如何管理Linux日志服务
本文介绍了 rsyslog 日志服务,并将其与较新的 journald 系统进行了比较。它使用实际的命令示例来管理服务和更新配置文件。
云云众生s
2024/08/07
3180
如何管理Linux日志服务
日志管理系统功能_efk日志分析系统
rsyslog是一个开源的软件程序,它负责写入日志。 它记录绝大部分的日志记录,和系统有关的、安全、认证ssh,su、计划任务at,cron等日志。
全栈程序员站长
2022/08/03
1.2K0
日志管理系统功能_efk日志分析系统
计划任务及日志管理
crond的概念和crontab是不可分割的。crontab是一个命令,常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令。该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行
Cyylog
2020/08/19
8260
linux下crontab与anacrontab的使用
每个人或多或少都有一些约会或者是工作,有的工作是例行性的,例如每年一次的加薪、每个月一次的工作报告、每周一次的午餐会报、每天需要的打卡等等; 有的工作则是临时发生的,例如刚好总公司有高官来访,需要你准备演讲器材等等! 用在生活上面,例如每年的爱人的生日、每天的起床时间等等、还有突发性的 3C 用品大降价等等。
小柒吃地瓜
2020/04/23
2.3K0
Linux下使用Rsyslog搭建集中日志服务器
ryslog 是一个快速处理收集系统日志的程序,提供了高性能、安全功能和模块化设计。rsyslog 是syslog 的升级版,它将多种来源输入输出转换结果到目的地。
星哥玩云
2022/07/13
3.3K0
Linux下使用Rsyslog搭建集中日志服务器
日志管理及日志轮询
日志对于安全来说,非常重要,他记录了系统每天发生的各种各样的事情,你可以通过他来检查错误发生的原因,或者受到攻击时攻击者留下的痕迹。
互联网老辛
2020/05/14
1.9K0
Linux进程管理与资源管理
16.3 进程管理 本章一开始就提到所谓的『进程』的概念,包括进程的触发、子进程与父进程的相关性等等,此外,还有那个『进程的相依性』以及所谓的『殭尸进程』等等需要说明的呢!为什么进程管理这么重要呢?
小柒吃地瓜
2020/04/23
1.4K0
日志轮转
日志重要性 Linux系统日志对管理员来说,是了解系统运行的主要途径,因此需要对 Linux 日志系统有个详细的了解。 Linux 系统内核和许多程序会产生各种错误信息、告警信息和其他的提示信息,这些各种信息都应该记录到日志文件中,完成这个过程的程序就是 rsyslog,rsyslog 可以根据日志的类别和优先级将日志保存到不同的文件中。 二、日志系统rsyslog 日志管理基础 rsyslog 日志管理 logrotate日志轮转 一、处理日志的进程 rsyslogd:绝大部分日志记录,和系统操作
用户8639654
2021/08/18
7800
Docker容器日志管理最佳实践
Docker 引擎日志一般是交给了 Upstart(Ubuntu 14.04) 或者 systemd (CentOS 7, Ubuntu 16.04)。前者一般位于 /var/log/upstart/docker.log 下,后者我们一般 通过 journalctl -u docker 来进行查看。
Criss@陈磊
2019/08/01
3.7K0
相关推荐
linux日志管理
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档