Java的类加载
Java在真正需要使用一个类时才会去加载类,而不是在启动程序时就载入所有的类,因为大多数使用者都只使用到程序的部分资源,在需要某些功能时再载入某些资源,可以让系统资源运用的更高效。
类的加载指的是将类的
.class文件中的二进制数据读入到
内存中,将其放在Jvm的
方法区内,然后在
堆区创建一个
java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的
Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
Java 中的所有类型包括基本类型(
int,
long,
float等等),即使是数组都有与之关联的 Class 类的对象。
Class对象是由Jvm自动生成的,每当一个类被载入时,Jvm就自动为其生成一个Class对象
Class对象
实例.getClass()
通过Object的
getClass()获取每一个
实例对应的Class对象
类名.class
你也可以直接使用一下方式来获取String类的Class对象
Class.forName()
在一些应用中,你无法事先知道使用者将载入什么类别,你可以使用Class的静态方法
forName()来动态加载类别
Class.forName()有两个版本,上面的版本只指定了全限定类名,而另一个版本可以让你指定类名,载入时是否执行静态代码块,执行类加载器(
ClassLoader)
从Class对象中获取信息
Class对象表示所载入的类别,获取Class对象后,你就可以获取类别相关的信息,入
package,
constructor,
field,
method等信息。
而每一种信息,都有相对应的类别
- package: java.lang.reflect.Package
- constructor: java.lang.reflect.Constructor
- field: java.lang.reflect.Field
- method: java.lang.reflect.Method
ClassLoader 类加载器
Java在需要使用类的时候才会将类载入,Java中类的载入是由
Class Loader来实现的.
当你尝试执行
java xxx命令时,java会尝试找到
JRE的安装目录,然后寻找
jvm.dll,接着启动JVM并进行初始化操作,接着产生
BootstrapLoader,
Bootstrap Loader会载入
Extended Loader, 并设定
Extended Loader 的parent 为
BootstrapLoader, 接着
Bootstrap Loader 会载入
Application Loader, 并将
Application Loader 的parent 设定为
Extended Loader
启动类加载器
BootstrapLoader搜寻
sun.boot.library.path中指定的类, 你可以使用
System.getProperty("sun.boot.library.path")来获取
扩展类加载器
Extended Loader(
sun.misc.Launcher$ExtClassLoader) 是由Java编写的,会搜寻系统参数
java.ext.dirs中指定的类别,可以通过
System.getProperty("java.ext.dirs")来获取
应用程序类加载器
Application Loader
(
sun.misc.Launcher$AppClassLoader) 是由Java编写的,会搜寻系统参数
java.class.path中指定的类别,可以通过
System.getProperty("java.class.path")来获取, 在使用
java xxx命令执行
.class字节码文件时,可以通过
-cp参数设定
classpath
类加载器之间的关系
类加载有三种方式:
- 命令行启动应用时候由JVM初始化加载
- 通过
Class.forName()方法动态加载 - 通过
ClassLoader.loadClass()方法动态加载
Class.forName()和ClassLoader.loadClass()区别
-
Class.forName():将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块; -
ClassLoader.loadClass():只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。 -
Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象。
JVM类加载机制
-
全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入 -
父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类 -
缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效
双亲委派模型
双亲委派模型的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
双亲委派机制:
- 当
AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。 - 当
ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。 - 如果
BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载; - 若
ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException。
ClassLoader源码分析:
双亲委派模型意义:
- 系统类防止内存中出现多份同样的字节码
- 保证Java程序安全稳定运行
自定义加载器
自定义类加载器一般都是继承自
ClassLoader类,从上面对
loadClass方法来分析来看,我们只需要重写
findClass 方法即可。下面我们通过一个示例来演示自定义类加载器的流程:
自定义类加载器的
核心在于
对字节码文件的获取,如果是加密的字节码则需要在该类中对文件进行解密。由于这里只是演示,我并未对class文件进行加密,因此没有解密的过程.
其它
使用反射创建对象
你可以使用Class的
newInstance()方法来实例化
调用方法
使用反射可以取回类中的方法,方法对应的类为
java.lang.reflect.Method, 你可以使用它的
invoke()方法来调用指定的方法
更多大厂面试资料以及Android资料可以进群一起交流:(Android进阶学习⑥群:345659112)
作者:sheng_ding
链接:
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。