java中的编译时与运行时

时间:2021-01-22 03:55:09

----?基础知识

    -- 编译时

    编译器将源代码翻译成机器能够读懂的代码,如java中就是翻译成jvm能够读懂的字节码文件。简单说,编译时就是机器帮我们检查代码是否有出现语法错误,关键字写错之类的,是为之后的类加载做好准备,所以,在这个过程中并不会出现什么分配内存之类的操作。

   -- 运行时

    这个过程是指将编译好后的储存在磁盘上的字节码文件(.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 = number1 * number2; //line A
 
 int product2 = number3 * number4; //line B
 
 }
 
}

--- 分析

    同时被static和final修饰的常量称作编译时常量,所以number1 和 number2在编译时已经被加载了,即product1 在编译期间就已经确定好了值为多少。而number3 和number4 只有在运行时,分配好了内存空间并且才能被成功赋值,所以product2 的值只有在运行时才能够确定是多少。反编译如下:

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

 

---- 举个栗子

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

public class Test{
 public static void A(String param1); // method #1
 public static void A(int param1); // method #2
}

   如果编译器调用的方法是下面

new Test().A("classlodaer");

   那么它就会在编译的时候自己去寻找menthod #1的方法

 

---- 举个栗子

   方法覆盖:这个是在运行时发生的。方法重载被称为运行时多态,因为在编译期编译器不知道并且没法知道该去调用哪个方法。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;
 } 
}

   如果编译器遇到如下代码,就在编译时就无法判断究竟传入的参数是A类型还是B类型,只有在运行时才能够进行确定,进而来判断要调用方法#3还是#4

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

 

---- 举个栗子

  泛型(又称类型检验):这个是发生在编译期的。编译器负责检查程序中类型的正确性,然后把使用了泛型的代码翻译或者重写成可以执行在当前JVM上的非泛型代码。这个技术被称为“类型擦除“。

    public class Test4 {
        public static void main(String[] args) {
            ArrayList<String> arrayList1=new ArrayList<String>();
            arrayList1.add("abc");
            ArrayList<Integer> arrayList2=new ArrayList<Integer>();
            arrayList2.add(123);
            System.out.println(arrayList1.getClass()==arrayList2.getClass());
        }
    }

---- 分析

   在这个例子中,我们定义了两个ArrayList数组,不过一个是ArrayList<String>泛型类型,只能存储字符串。一个是ArrayList<Integer>泛型类型,只能存储整形。最后,我们通过arrayList1对象和arrayList2对象的getClass方法获取它们的类的信息,最后发现结果为true。说明泛型类型String和Integer都被擦除掉了,只剩下了原始类型

 

---- 举个栗子

  异常:分为编译时异常和运行时异常

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

   例如:NullPointerException,ArrayIndexOutOfBoundsException,等等

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