`

java基础2_编译期和运行期

    博客分类:
  • java
 
阅读更多

有3个概念:

编译时

运行时

构建时

理解这3个概念可以很好的帮助我们去理解一些基本的概念。

 

  1. 方法重载 -> 编译期,编译时多态,根据参数类型,决定生成调用哪个方法的字节码
  2. 方法覆盖  -> 运行期,   运行时多态,   根据对象的类型, 决定调用哪个实例方法
  3. 继承        -> 编译期,因为是静态的。
  4. 泛型(又称类型检验)-> 发生在编译期的。编译器负责检查程序中类型的正确性,然后把使用了泛型的代码翻译或者重写成可以执行在当前JVM上的非泛型代码。

  5. 注解Annotation -> 编译时注解@Override,可以用来捕获类似于在子类中把toString()写成tostring()这样的错误;Junit的@Test运行时注解
  6. 异常Exception -> 编译时/运行时。运行时异常不需要编译器来检测
  7. AOP切面
  8. Spring的IOC,
  • Class.forname()
  • 组合优于继承
  • BeanFactory根据xml文件,结合Class.forname

代理或组合->运行时, "设计模式 -> 组合优于继承

 

继承是一种多态工具,而不是一种代码复用工具。是否使用继承的规则:是继承只能用在类之间有“父子”关系的情况下。

  • 不要仅仅为了代码复用而继承。过度使用继承(通过“extends”关键字)的话,如果修改了父类,会损坏所有的子类。这是因为子类和父类的紧耦合关系是在编译期产生的。
  • 不要仅仅为了多态而继承。如果你的类之间没有继承关系,并且你想要实现多态,那么你可以通过接口和组合的方式来实现,这样不仅可以实现代码重用,同时也可以实现运行时的灵活性。

 

 

面试者会在你的答案里着重关注这几个词语——耦合”,“静态还是动态”,以及“发生在编译期还是运行时”。运行时的灵活性可以通过组合来实现,因为类可以在运行时动态地根据一个结果有条件或者无条件地进行组合。但是继承却是静态的。

 

Java语言本身不支持运行时继承,但是有一种替代的方案叫做“代理”或者“组合”,它表示在运行时组件一个层次对象的子类。这样可以模拟运行时继承的实现。

 

 -------------------------------

Class.forName("xx.xx")等同于 
Class.forName("xx.xx",true,ClassLoader.getSystemClassLoader());//此时已经初始化实例对象了 

而ClassLoader loader = ClassLoader.getSystemClassLoader(); 
Class className=loader.loadClass("xx.xx");//此时class没有实例化对象 
className.newInstance();//此时才真正的初始化实例对象 

综上所述它们的区别在于

  • Class.forName("xx.xx")已经实例化类对象了
  • ClassLoader.loadClass("xx.xx");没有实例化类对象,需要调用newInstance方法进行实例化

 

 

 

----------------------------------

 

看下面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 A
 
          int product2 = number3 * number4;         //line B
 
    }
 
}

 

 

 

在行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命令都是查看编译后的代码(例如,字节码)的利器。

---------------------

1. 方法重载

 

 

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

 

 

public class {
     public void evaluate(A a);  // method #1
     public void evaluate(B b);   // method #2
     public void evaluate(C c); //method 3, C extends A    
}

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

 

 

C c = new C();
evaluate(c);

 会根据传入的参数类型,调用method3,如果没有method3,则向上转型调用method1

 

 

注意:静态方法可以被继承,但不能被覆盖

 

 

B继承A
A有静态方法test()
B也有静态方法test()
测试:
A a = new B();
a.test();//调用的是A的静态方法

 

 

------------------

 

2.方法覆盖:

 

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

 

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(..)方法。如果编译器遇到下面的代码:

 

 

public int evaluate(A reference, int arg2)  {
     int result = reference.compute(arg2);
}

 编译器是没法知道传入的参数reference的类型是A还是B

 

因此,只能够在运行时,根据赋给输入变量“reference”的对象的类型(例如,A或者B的实例)来决定调用方法#3还是方法#4.

 

-------------------

 

3.泛型(又称类型检验):

 

这个是发生在编译期的。

编译器负责检查程序中类型的正确性,然后把使用了泛型的代码翻译或者重写成可以执行在当前JVM上的非泛型代码。这个技术被称为“类型擦除“。换句话来说,编译器会擦除所有在尖括号里的类型信息,来保证和版本1.4.0或者更早版本的JRE的兼容性。

 

List<String> myList = new ArrayList<String>(10);

编译后

List myList = new ArrayList(10);

 

 

-------------------

 

4.注解(Annotation):

 

 

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

 

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,这个功能已经是编译器的一部分了

 

 

public class MyTest{
    @Test
     public void testEmptyness( ){
         org.junit.Assert.assertTrue(getList( ).isEmpty( ));
     }
 
     private List getList( ){
        //implemenation goes here
     }

}

 

 

 

 

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

 

@Test (timeout=100)
public void testTimeout( ) {
    while(true);   //infinite loop
}

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


@Test (expected=IndexOutOfBoundsException.class)
public void testOutOfBounds( ) {
       new ArrayList<Object>( ).get(1);
}

//如果上面的代码在运行时没有抛出IndexOutOfBoundsException或者抛出的是其他的异常的话,那么这个用例就会失败。用户自定义的注解可以在运行时通过Java反射API里新增的AnnotatedElement和”Annotation”元素接口来处理。

 

 

 

5.异常(Exception

 

你可以使用运行时异常或者编译时异常。

运行时异常(RuntimeException)也称作未检测的异常(unchecked exception),这表示这种异常不需要编译器来检测。RuntimeException是所有可以在运行时抛出的异常的父类。一个方法除要捕获异常外,如果它执行的时候可能会抛出RuntimeException的子类,那么它就不需要用throw语句来声明抛出的异常。

例如:NullPointerExceptionArrayIndexOutOfBoundsException,等等

 

受检查异常(checked exception)都是编译器在编译时进行校验的,通过throws语句或者try{}cathch{} 语句块来处理检测异常。编译器会分析哪些异常会在执行一个方法或者构造函数的时候抛出。

 

6.面向切面的编程(Aspect Oriented Programming-AOP):

切面可以在编译时,运行时或,加载时或者运行时织入。

 

编译期:编译期织入是最简单的方式。如果你拥有应用的代码,你可以使用AOP编译器(例如,ajc – AspectJ编译器)对源码进行编译,然后输出织入完成的class文件。AOP编译的过程包含了waver的调用。切面的形式可以是源码的形式也可以是二进制的形式。如果切面需要针对受影响的类进行编译,那么你就需要在编译期织入了。

 

编译后:这种方式有时候也被称为二进制织入,它被用来织入已有的class文件和jar文件。和编译时织入方式相同,用来织入的切面可以是源码也可以是二进制的形式,并且它们自己也可以被织入切面。

 

装载期:这种织入是一种二进制织入,它被延迟到JVM加载class文件和定义类的时候。为了支持这种织入方式,需要显式地由运行时环境或者通过一种“织入代理(weaving agent)“来提供一个或者多个“织入类加载器(weaving class loader)”。

运行时:对已经加载到JVM里的类进行织入



--------------------------------------------------------

7.继承 – 发生在编译时,因为它是静态的

代理或者组合 – 发生在运行时,因为它更加具有动态性和灵活性。

 

Q.你有没有听说过“组合优于继承”这样的说法呢?如果听说过的话,那么你是怎么理解的呢?

A.继承是一种多态工具,而不是一种代码复用工具。有些开发者喜欢用继承的方式来实现代码复用,即使是在没有多态关系的情况下。是否使用继承的规则是继承只能用在类之间有“父子”关系的情况下。

不要仅仅为了代码复用而继承。当你使用组合来实现代码复用的时候,是不会产生继承关系的。过度使用继承(通过“extends”关键字)的话,如果修改了父类,会损坏所有的子类。这是因为子类和父类的紧耦合关系是在编译期产生的。

不要仅仅为了多态而继承。如果你的类之间没有继承关系,并且你想要实现多态,那么你可以通过接口和组合的方式来实现,这样不仅可以实现代码重用,同时也可以实现运行时的灵活性。

这就是为什么四人帮(Gang of Four)的设计模式里更倾向于使用组合而不是继承的原因。面试者会在你的答案里着重关注这几个词语——“耦合”,“静态还是动态”,以及“发生在编译期还是运行时”。运行时的灵活性可以通过组合来实现,因为类可以在运行时动态地根据一个结果有条件或者无条件地进行组合。但是继承却是静态的。

 

Q.你能够通过实例来区别编译期继承和运行时继承,以及指出Java支持哪种吗?

 

A.“继承”表示动作和属性从一个对象传递到另外一个对象的场景。Java语言本身只支持编译期继承,它是通过“extends”关键字来产生子类的方式实现的,如下所示:

 

 

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 calledChild is Called”,因为,子类的调用继承了父类的“Parenet is called”。关键字“super”是用来调用“Parent”类的方法。运行时继承表示在运行时构建父/子类关系。Java语言本身不支持运行时继承,但是有一种替代的方案叫做“代理”或者“组合”,它表示在运行时组件一个层次对象的子类。这样可以模拟运行时继承的实现。在Java里,代理的典型实现方式如下:

 

 

public class Parent {
    public String saySomething( ) {
          return “Parent is called”;
    }
}
 
public class Child  {
     public String saySomething( ) {
          return new Parent( ).saySomething( ) +  “, Child is called”;
    }
}

 

 

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

 

 

public class Child  {
     private Parent parent = null;
 
     public Child( ){
          this.parent = new Parent( );
     }
 
     public String saySomething( ) {
          return this.parent.saySomething( ) +  “, Child is called”;
    }
}

 

 

 

分享到:
评论

相关推荐

    java编译和运行

    java的一些基础知识以及编译和运行的操作和过程。

    java的编译时多态和运行时多态

    java的编译时多态和运行时多态,保证一看就会

    小白自己学习Java,记录Java基础.rar

    - 运行期:JVM加载.class并运行.class(0和1) &gt; 特点:跨平台、一次编译到处运行 - 名词解释: - JVM:java虚拟机 - 加载.class并运行.class - JRE:java运行环境 - 除了包含JVM以外还包含了运行java...

    java基础入门教程

    Java 语 言 能 在 执 行 码 (二 进 制 码 )上 兼 容 ,这 样 以 前 所开 发 的软 件 就 能 运行 在 不 同 的 机 器 上 ,只 要 所 用 的 机 器 能 提供 Java 语 言 解 释 器 即可 。 Java 语 言 将 对 未 来 软 件 ...

    JAVA范例 四)异常处理---编译时异常、运行时异常

    NULL 博文链接:https://kellhan.iteye.com/blog/1129310

    Java 语言基础 —— 非常符合中国人习惯的Java基础教程手册

    (2) 类名是由程序员自己定义的 Java 标识符,每个类说明必须有 class 和类名。 (3) 类说明修饰符包括:  abstract 说明一个类为抽象类,抽象类是指不能直接实例化对象的类。  final 说明一个类为最终类,即...

    深入理解_Java_虚拟机 JVM_高级特性与最佳实践

    前 言 致 谢 第一部分 走近Java 第1章 走近Java / 2 1.1 概述 / 2 1.2 Java技术体系 / 3 1.3 Java发展史 / 5 1.4 展望Java技术的未来 / 9 1.4.1 模块化 / 9 1.4.2 混合语言 / 9 1.4.3 多核并行 / 11 ...

    java基础的注解和反射的相关知识点总结

    Java的反射是指程序在运行期可以拿到一个对象的所有信息。 反射的优点和缺点: 优点:可以实现动态创建对象和编译,灵活性大 缺点:对性能有影响,反射操作总是慢于直接执行相同操作 反射机制: Java的反射...

    JavaBasic:Java基础

    JavaBasicJava基础1.Linux:1)开源的操作系统、免费的主要是服务器操作系统...上一级目录2.Java开发环境:1)Java编译运行过程:----------常见面试题1.1)编译期:.java源文件,经过编译,生成.class字节码文件1.2)运行期:JV

    Java进阶教程解密JVM视频教程

    还会涉及从编译期的语法糖处理,到类加载的各个阶段,直至运行期的各项优化的详细讲解。最后不要错过方法反射优化的底层分析。 * 最后的加餐环节是带着你理解 Java 内存模型:见识多线程读写共享数据的原子性、可见...

    AST_demo:利用JavaParser框架在编译时修改语法树(源码)的demo-修改

    2,删除指定的方法和成员变量。 3,检测方法中是否有新的线程代码。 4,生成新的类和方法。并在运行时验证。 5,解析类文件:MainActivity.java,并在打印输出类信息。 6,修改类中的方法。将结果保存在工程目录下的...

    java 混淆工具,不可逆 jocky

    我们知道,Java是一种跨平台的编程语言,其源码(.java文件)被编译成与平台无关的字节码(.class文件),然后在运行期动态链接。 这样,编译后的类文件中将包含有符号表,从而使得Java程序很容易被反编译。相信每一个...

    Thinking in java4(中文高清版)-java的'圣经'

    2.4.2 基本成员默认值 2.5 方法、参数和返回值 2.5.1 参数列表 2.6 构建一个Java程序 2.6.1 名字可见性 2.6.2 运用其他构件 2.6.3 static 关键字 2.7 你的第一个J ava程序 编译和运行 2.8 注释和嵌入式文档 2.8.1 ...

    jocky(java代码混淆器)

    我们知道,Java是一种跨平台的编程语言,其源码(.java文件)被编译成与平台无关的字节码(.class文件),然后在运行期动态链接。这样,编译后的类文件中将包含有符号表,从而使得Java程序很容易被反编译。相信每一个...

    java虚拟机知识点整理

    编译期编译优化 运行期优化 高效并发-java内存模型与线程 线程安全与锁优化 1 标记-清除算法:首先标记所有需要回收的对象(引用计数或可达性分析算法标记),在标记完成后统一回收所有被标记的对象。 缺点:效率问题...

    AIC的Java课程1-6章

     清楚如何通过向上转型(引用的隐式转型),方法重写,运行期绑定来实现多态性。  体会多态性如何使系统可扩展和可维护。  理解运行时判定引用对象的类型(instanceof),进行强制转型(即引用的显示转型...

    Java思维导图

    Java代码在进行Javac编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态链接。也就是说,在Class文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法的...

    Java高级程序设计实战教程第三章-Java反射机制.pptx

    Java高级程序设计 第3章 Java反射机制 3.1 应用场景 3.2 相关知识3.3 实施过程 3.4 拓展知识3.5 拓展训练 3.6 课后小结3.7 课后习题 3.8 上机实训 Java高级程序设计实战教程第三章-Java... 优点:运行期类型的判断,

    Java虚拟机

    第2版在第1版的基础上做了很大的改进:根据最新的JDK1.7对全书内容进行了全面的升级和补充;增加了大量处理各种常见JVM问题的技巧和最佳实践;增加了若干与生产环境相结合的实战案例;对第1版中的错误和不足之处的...

    java 混淆工具,不可逆 jocky 也许是最好的了

    我们知道,Java是一种跨平台的编程语言,其源码(.java文件)被编译成与平台无关的字节码(.class文件),然后在运行期动态链接。这样,编译后的类文件中将包含有符号表,从而使得Java程序很容易被反编译。相信每一个...

Global site tag (gtag.js) - Google Analytics