这是由.java源码文件转为 .class二进制字节码文件的过程。
我们编写好的源代码,就是*.java文件。使用“javac test.java”就可以编译test.java文件。
编译过程主要有三步:
详细过程:
源代码文件*.java -> 词法分析器 -> tokens流 -> 语法分析器 -> 语法树/抽象语法树 -> 语义分析器 -> 注解抽象语法树 -> 字节码生成器 -> JVM字节码文件*.class
最后剩成的JVM字节码文件,使用命令“javap -c test”可以查看test.class的字节码信息,主要包含三项内容:
结构信息:class文件相关信息。
元数据:Java源码中的声明和常量信息。
方法信息:Java源码语句和表达式对应的字节码。
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转化解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。
加载:获取class文件并放入内存,在内存中的方法区生成一个java.lang.Class对象,作为访问入口。
连接
-验证:确保Class文件的字节流中包含的信息符合当前虚拟机的要求。
-准备:正式为类变量(被static修饰的变量,不包括实例变量,实例变量在对象实例化时随对象分配到java堆中)分配内存并设置类变量初始值,且初始值是指数据类型的零值。
-解析:将常量池内的符号引用替换为直接引用
初始化:执行类构造器
附:静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块中可以赋值但不能访问
public class Test {
static {
i = 0; //给变量赋值可以正常通过
System.out.println(i); //错。编译器提示“非法向前引用”
}
static int i =1;
}
有五种情况必须初始化(加载、连接自然要在这之前进行)
1.遇到new、getstatic、putstatic或invokestatic这四条字节码指令时
2.使用java.lang.reflect包的方法对类进行反射调用的时候
3.当初始化一个类时,如果父类还没有进行初始化,需要先触发其父类的初始化
4.当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的类),虚拟机会先初始化这个类
5.当使用jdk1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,需要先初始化
这五种称为对一个类的主动引用,除此之外的都不会触发初始化,称为被动引用。
例:
1.通过子类引用父类的静态字段,不会导致子类的初始化
2.通过数组定义来引用类
3.常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会初始化
类加载器的双亲委派模型:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,自加载器才会尝试自己去加载。
双亲委派模型组织类加载器的好处是,Java类随着它的类加载器一起具备了一种带有优先级的层级关系。
例如类java.lang.Object,它存放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于模型最顶端的启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。
分派分为静态分派(重载)和动态分派(重写),分派调用过程将会揭示多态性特征的一些最基本的体现。
Human man = new Man();
Human为变量的静态类型,Man为变量的实际类型。两者都在程序中发生一些变化,区别是静态类型是在编译器可知的,而实际类型变化的结果在运行期才可确定。
重载时是通过参数的静态类型而不是实际类型来判断的。
public class Test {
public static void main(String[] args) {
Human man = new Man();
Test test = new Test();
test.sayHello(man);
}
public void sayHello (Human guy) {
System.out.println("Human");
}
public void sayHello (Man guy) {
System.out.println("Man");
}
public void sayHello (Woman guy) {
System.out.println("Woman");
}
}
abstract class Human {
}
class Man extends Human {
}
class Woman extends Human {
}
程序的输出结果为 :Human
Human man = new Man();