我们编写的程序 = 数据 + 指令;
源码(source code)一般存储在以*.java 结尾的文件中,称为java源文件。
Java程序的具体运行过程如下:
类加载机制是在我们的Java的运行阶段中的其中一个阶段。
JVM将类的加载分为3个步骤:装载(Load
)、链接(Link
)、初始化(Initialize
)。
这个阶段也做三件事:验证、准备和解析。
// 分配内存,初始化为0而不是1
public static int a = 1;
// 分配内存,初始化为0,而不是2
public final static int b = 2;
// 不分配内存
public int c = 3;
这个阶段主要是执行 java 代码的静态部分,进行相关初始化的动作。这时候就执行静态代码块,为静态变量赋值,这里的赋值才是代码里面的赋值。
我们上面程序中写的静态变量 a = 1
、b = 2
以及 static{}
代码块中的赋值都会在这个阶段执行初始化。
注意:
①类加载一定是从上到下按顺序执行的。
②如果存在继承关系,则会先加载父类,再加载子类。
类加载器:讲到类加载不得不讲到类加载的顺序和类加载器。
Java 中大概有四种类加载器,分别是:
启动类加载器(Bootstrap ClassLoader
),加载SE下的标准类 (String、List、InputStream)
扩展类加载器(Extension ClassLoader
),加载SE下的扩展类
应用类加载器(Application ClassLoader
),加载我们写的类,Maven等第三方引入的类
自定义类加载器(Custom ClassLoader)
它们依次属于继承关系(注意这里的继承不是 Java 类里面的 extends,而是内部存在关系)
如果一个类加载器需要加载类,那么首先它会把这个类加载请求委派给父类加载器去完成,如果父类还有父类则接着委托,每一层都是如此。
一直递归到顶层。
当父加载器无法完成这个请求时,子类才会尝试去加载,一直向下到底层,如果还没能完成,就会抛出ClassNotFountException
异常。
双亲委派模型的优点:
当出现上面这种情况是,JVM是加载我们定义的String类,还是JDK中的String类?
demo.Main类被哪个类加载器加载? ApplicationClassLoader 去加载。
Main用到了java.lang.String,则默认让 ApplicationClassLoader 去加载。
如果没有双亲委派,加载的就是我们自己写的java.lang.String了么。
因为有双亲委派的存在,则 ApplicationClassLoader 优先让父加载器 BootStrapClassLoader
去加载。所以能加载到的是 rt.jar 下的。而不会加载我们自己写的。
类文件放在哪?
答:硬盘中,以文件的形式出现最为常见
类文件是怎么来的?
答:经过编译器,将Java源文件编译而来
类文件中的主要数据有哪些?
答:按照规范,主要有基本信息(静态属性)、方法(方法信息、指令(字节码) )、常量)
为什么要进行类的加载?
答:按照冯诺依曼体系, CPU无法直接读取硬盘中的数据,需要先加载到内存中
为什么类文件要按需加载,并且以类为单位加载?
答: 相对来说,节省内存空间,实现方便
类名是什么?
1)权威类名=包名+类名称
2)类加载器+权威类名
什么时候会去加载类(什么时候用到这个类) ?
答:实例化对象、访问静态属性、调用静态方法、子类用到父类
类在内存中只会存在一份
类的加载过程
加载(loading)、链接(inking) 、 初始化(initializing)
类的初始化时会执行我们的哪些代码,顺序是什么?
1.属性的初始化赋值
2.静态构造代码块
3.父类的初始化一定在子类之前完成
4.按照书写顺序
类被加载到内存的什么位置?
答:逻辑上,放在方法区。但不同JVM的实现,可以有进一步讨论空间!
默认的类加载器有哪些 : 启动类加载器、扩展类加载器、应用类加载器
加载时,ClassLoader怎么知道一个类对应的类文件所在?
答:启动、扩展类加载器根据固定位置找。应用类加载器,根据class-path的路径依次查找
如果加载时, 一个类不存在,会出现异常(ClassNotFound、 NoClassDef …)
总结:
提示:这里对文章进行总结:
以上就是今天的学习内容,本文是JVM的学习,学习了JVM运行java源文件的过程,JVM的类加载机制,以及认识了双亲委派模型。之后的学习内容将持续更新!!!