Java编译期和运行期

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

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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文件的话,那么你就会从下面的结果里得到答案。

?


1

2

3

4

5

6

7

8

9

10

11

12

13

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 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 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”;

    }

}

时间: 2024-10-24 09:55:45

Java编译期和运行期的相关文章

java编译期和运行期和string原理

 编译期:   是指把源码交给编译器编译成计算机可以执行的文件的过程.在Java中也就是把Java代码编成class文件的过程.编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误. 运行期:  是把编译后的文件交给计算机执行.直到程序运行结束.所谓运行期就把在磁盘中的代码放到内存中执行起来.在Java中把磁盘中的代码放到内存中就是类加载过程. 通过如String str = "aaa";字面量形式创建的字符串对象 "aaa&

Java编译期和运行期 &amp; JVM

Java整个编译以及运行的过程相当繁琐,本文通过一个简单的程序来简单的说明整个流程. 首先两张图,描述编译和执行的过程: Java代码编译是由Java源码编译器来完成,流程图如下所示: Java字节码的执行是由JVM执行引擎来完成,流程图如下所示: 如下图,Java程序从源文件创建到程序运行要经过两大步骤:1.源文件由编译器编译成字节码(ByteCode)  2.字节码由java虚拟机解释运行.因为java程序既要编译同时也要经过JVM的解释运行,所以说Java被称为半解释语言( "semi-i

ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗

1.ClassLoader Java中类加载出现在哪个阶段,编译期和运行期? 类加载和类装载是一样的吗? :当然是运行期间啊,我自己有个理解误区,改正后如下:编译期间编译器是不去加载类的,只负责编译而已,去rt.jar拿数据干嘛,不依然是class文件,jvm是只要是class文件就能运行. 2.类加载ClassLoader,各个类加载器执行顺序是什么? :永远是自己写的加载器先去加载,记住并不是真正的加载,而是双亲委派机制,每个加载器都不真正去加载,而是去让父加载器去加载,想一下,自然界亦是如

Java编译期的前端过程

Java编译期的概念 编译期即将源代码转变成本地代码的过程,对于Java来说,这个过程会更加的多样. Java编译期因为采用的编译器类型不同而导致概念也不同,Java编译器可以分为以下三种类型: 前端编译器 前端编译器将java源代码转变成字节码,常见的前端编译器包括Javac编译器.ECJ编译器(Eclipse JDT). 即时(JIT,Just In Time)编译器 即时编译器将字节码转变成本地机器码,常见的即时编译器包括HotSpot 虚拟机的C1.C2编译器. AOT(Ahead of

Java编译期优化与运行期优化技术浅析

Java语言的“编译期”是一段不确定的过程,因为它可能指的是前端编译器把java文件转变成class字节码文件的过程,也可能指的是虚拟机后端运行期间编译器(JIT)把字节码转变成机器码的过程. 下面讨论的编译期优化指的是javac编译器将java文件转化为字节码的过程,而运行期间优化指的是JIT编译器所做的优化. 编译期优化 虚拟机设计团队把对性能的优化集中到了后端的即时编译器(JIT)中,这样可以让那些不是由javac编译器产生的class文件也同样能享受到编译器优化所带来的好处.但是java

java 编译期常量

今天在看书的时候遇到了一个不是很懂的名词,是在think in java 这本书的第七章讲final关键字时讲到的.然后自己在网上查了一下知道了一些. 编译器常量就是:它的值在编译期就可以确定的常量.也就是说在编译的时候就已经为用到这些常量的地方赋好值了.下边这段程序可以很好地帮助我们找出哪些是编译期常量哪些不是. package com.wenge.compileTest; public class CompileTest{ public static void main(String arg

深入了解JVM虚拟机8:Java的编译期优化与运行期优化

java编译期优化 微信公众号[Java技术江湖]一位阿里 Java 工程师的技术小站.作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux.网络.多线程,偶尔讲点Docker.ELK,同时也分享技术干货和学习经验,致力于Java全栈开发!(关注公众号后回复”Java“即可领取 Java基础.进阶.项目和架构师等免费学习资料,更有数据库.分布式.微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南.

c++ 编译期与运行期总结

分享到 一键分享 QQ空间 新浪微博 百度云收藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享 转自:http://hi.baidu.com/zhaoyong200518/item/8516dc59a65be1968d12edff c++ 编译期与运行期总结 一 见识编译期的力量 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #in

java 19 - 4 编译期异常和运行期异常的区别

1 /* 2 编译时异常和运行时异常的区别 3 编译期异常:Java程序必须显示处理,否则程序就会发生错误,无法通过编译 4 运行期异常:无需显示处理,也可以和编译时异常一样处理 5 */ 6 import java.text.ParseException; 7 import java.text.SimpleDateFormat; 8 import java.util.Date; 9 public class ExceptionDemo { 10 public static void main(