1.类加载:
1.1类加载流程
类加载实际上并不复杂,我们知道Java是面向对象的编程语言,程序的运行过程中无时不刻不与类和对象打交道。而类加载是程序运行的第一步。
实际上一般来说一个Java程序的完整运行有一下几步:
1.编译:就是将我们编写的代码编译成字节码,没什么好说的。有时间的话,我会写一些有关字节码结构的文章,期待吧。
2.装载:就是说找到类的.class文件,然后将这种二进制的文件数据加载到内存中
3.链接:链接比较复杂,它一共有三个步骤。
(1).验证
就是验证一个类文件中的字节码的合法性正确性
(2).准备
将一个类中所有的静态变量分配内存,并且给他们初始化一个默认值。
(3).解析
我们在编写代码的过程中除了有基本变量之外,还有一些引用变量。而解析的过程就是将这些引用变量的地址为对应类存在的地址。
4.初始化:
初始化的概念比较简单,就是给Java类中所有的成员变量与静态变量赋一个初始值,如果我们在编写的时候有说明该变量值则为该值,如果没有说明该变量的值是什么则保留它的默认值。
初始化有一个比较特殊的概念,就是说所有的类只初始化一次,并且初始化实在第一次直接调用它的时候才会初始化。在Java中对类的直接初始化调用有7中分别是:
- new 一个类的实例
- 访问某个类或者接口的静态方法
- 调用类的静态方法
- 反射生成一个对象的时候
- 初始化一个子类的时候(初始化一个子类,首先要初始化该子类的所有父类,父类依次递归)
- JVM启动时标明为启动类的类(简单来说就是Main函数存在的类)
- 对动态语言的支持
除了上面所说的这7中之外,其他的全部都是间接调用。也就不会导致类的初始化
5.使用: 就是正常的使用啊,各种创建实例调用方法等等
6.卸载:放一个类不再被使用的时候就可以被卸载了,当然其条件又很多。
1.2类加载器
上面的呢就是类加载的一个过程,在类加载的过程中呢最为核心的一个方面呢就是关于类加载器的知识。
Java的类加载过程中最最逃不开的就是有类加载器了,因为我l们要想创建一个类的实例,首先在内存中你就要有一个类的class对象,要想有这个class对象就要通过类加载。
在Java中呢一盘情况下有两种类型的加载器
第一种:Java自带的类加载器:
Java自带的类加载器有三个
- 根类加载器(BootStrap)
- 扩展类加载器(Extension)
- 系统(应用)类加载器(System/App)
第二种:自定义类加载器:
这个Java中类加载器如下图所示:
每一个类加载器所能加载的类都是不同的。
Java的类加载呢使用的是双亲委派机制,如下图所示:
这个图有些大,不过也没关系啦。具体来说双亲委派机制就是说,加入我现在有一个自定义的类加载器(MyClassLoader)要加载这个Class1类,这个类加载器呢并不是由它直接加载,而是交给它的父加载器来加载这个类。在这个图里面呢也就是交给(System/App ClassLoader)来加载这个类。到了System/App ClassLoader的时候呢也是交给它的父加载器,一直直到根加载器。
到了根加载器之后首先会判断当前类有没有被加载,如果有被加载的话则不再重复加载,如果没有被加载的话判断这个类能不能被根加载器加载,如果不能则交给根加载器的子加载器区加载,知道将这个类加载到内存中。
为什么要双亲委派呢,首先明确一点:jvm如何认定两个对象同属于一个类型,必须同时满足下面两个条件:
- 都是用同名的类完成实例化的。
- 两个实例各自对应的同名的类的加载器必须是同一个。比如两个相同名字的类,一个是用系统加载器加载的,一个扩展类加载器加载的,两个类生成的对象将被jvm认定为不同类型的对象。
- 所以,为了系统类的安全,类似“ java.lang.Object”这种核心类,jvm需要保证他们生成的对象都会被认定为同一种类型。即“通过代理模式,对于 Java 核心库的类的加载工作由引导类加载器来统一完成,保证了 Java 应用所使用的都是同一个版本的 Java 核心库的类,是互相兼容的”。
就类似于写一个java.lang.System类能不能够被加载呢,实际上时不能被加载的,因为所有的类是由BootStrap首先加载的,当你写了一个java.lang.System的时候想让它加载的时候,BootStrap已经加载完成了系统自带的java.lang.System就不可能加载你自己写的java.lang.System类了。