java代码在计算机中经历的三个阶段:
1.Source源代码阶段(代码还是在硬盘上,并没有进入内存) Student.java 通过javac编译 Student.class字节码文件
**2.类加载器ClassLoader将字节码文件加载进入内存,成为Class类对象(成员变量Field[ ] fields、构造方法Constructor[ ] constructors、成员方法Method[ ] methods)
**
3.运行时阶段new Student后
Student s=new Student(); 是我们自己创建的一个类,java加载类时,用到哪个类会将该类加载。(边用边加载)
JVM如何加载Student这个类?(加载-链接-初始化)
1.JVM把Student这个类的Class对象加载进来了
第一次加载该类时,JVM先从磁盘上找到该类的字节码文件(Student.class),JVM从系统环境变量的CLASSPATH里面找字节码文件的搜索路径。
.;%JAVA_HOME%\lib;%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt.jar(.代表CLASSPATH工程目录下(如要找Student类,要找对应的Module下找要加载的类),如果不是自己写的,会在后面的jdk库里面找字节码文件(如:System类.out等),将系统写好的打包放在架包里)
通过类加载器,从Student.class字节码文件中,把这个类的各种信息(类名、类的属性和方法)先加载到JVM中,这些信息的集合(相当于人的户口信息)称作Class对象(大写C)
2.链接:验证类的Class对象的合法性,开辟内存空间。
准备、验证的过程,验证当前类能够在JVM上运行(检车类的Class对象),给类的static静态成员开辟内存空间,如果当前类还有基类,继续加载其基类(执行1.2.3步)。
3.初始化
给static静态成员初始化,调用类的static静态初始化块
到此,类的加载完成了!
实例分析:
package com.load;
/**
* 验证类的加载
* 任何东西.的时候,后面都会有class;
* 2019/10/31
*/
public class ClassLoadTest {
public static void main(String[] args) throws Exception{
//第一种加载类的方式,只是加载类的Class对象(只做了第一步)
Class c1=Student.class;//获取类的Class对象
//第二种加载类的方式 forName()方法(做了三步,加载-链接-初始化), //里面参数是包名.类名,会给静态变量分配空间并初始化、加载静态方法和静态块
Class c2=Class.forName("com.load.Student");//包路径 //new对象加载类 Student s3=new Student();
}
}
class Student{
private String name;
private int age;
private String sex;
private static String school=method01();
static {
System.out.println("类的静态初始化块!");
school="西安工业大学";
}
private static String method01() {
System.out.println("初始化类的静态成员变量!");
return "default";
}
public Student(String name,int age,String sex){
this.name=name;
this.age=age;
this.sex=sex;
}
}
三种加载类的方式:
1. 第一种加载类的方式,只是加载类的Class对象(只做了第一步)
Class c1=Student.class; //获取类的Class对象
多用于参数的传递
2. 第二种加载类的方式 forName()方法(做了三步,加载-链接-初始化),
里面参数是包名.类名,会给静态变量分配空间并初始化、加载静态方法和静态块
Class c2=Class.forName("com.load.Student"); //包路径当加载Student类时,到第二步给成员变量分配内存空间时,要看这个类有没有基类,有基类时要先加载基类,执行一二三步,该基类若还有基类,则继续向上递归执行;
多用于配置文件,将类名定义在配置文件中。读取文件,加载类!
3.生成对象,首先会加载类,会打印实例化块和构造函数
Student s=new Student; Class c=s.getClass();
多用于对象的获取字节码的方式
结论:同一个字节码文件(*.class)在依次程序运行过程中,只会被加载一次,不管通过哪种方式获取的Class对象都是同一个。
JVM方法区(method area), Class对象信息,static 成员变量。类加载的时候就在方法区开辟空间,一般持续到程序结束。
类加载器:
访问类的第三种方式:Student s=new Student(); Class c3=s.getClass();( //通过访问类.class(第一步))
System.out.println(s3.getClassLoad()); //(获取类的加载信息) 打印结果:sun.misc.Launcher$AppClassLoader@18b4aac2
JVM的功能之一:提供类加载器加载类
打印系统定义的类的加载器时,结果为null System.out.println(String.class.getClassLoader());
打印AppClassLoader的父类加载器sun.misc.Launcher$ExtClassLoader@1b6d3586 System.out.println(c3.getClassLoader().getParent());
再打印ExtClassLoader的父类为null System.out.println(c3.getClassLoader().getParent().getParent());
因为java的jJVM是用C/C++写的,启动JVM时,本质上是一个C++程序。
BootstrapClassLoader(原生类加载器) C:\Program Files\Java\jdk1.8.0_192\jre\lib
是用原生代码编写(c&c++),负责加载JVM运行依赖的jar包
可知,三个加载器的继承关系: JVM默认提供的类加载器
* BootstrapClassLoader(原生类加载器) C:\Program Files\Java\jdk1.8.0_192\jre\lib
是用原生代码编写(c&c++),负责加载JVM运行依赖的jar包
|
* ExtClassLoader(扩展类加载器) 加载java的扩展库 C:\Program Files\Java\jdk1.8.0_192\jre\lib\ext
|
* AppClassLoader(应用类加载器) CLASSPATH路径下去加载相应的字节码文件
(SystemClassLoader)
类加载器都是通过"双亲委托模型"来加载类的,什么意思?好处是什么?
双亲委托模型:遇见一个需要加载的类,Student类理应由AppClassLoader来加载,实际上AppClassLoader先请求它的父类ExtClassLoader看能否加载Student类, ExtClassLoader在往上请求BootstrapClassLoader能否加载Student类;实际上BootstrapClassLoader无法加载Student类,ExtClassLoader也无法加载Student类,最后只能由AppClassLoader自己加载Student类,Student类就是这样加载起来的,把这种加载方式称为“双亲委托模型"
**好处:**是防止类被重复的加载 String str = new String();