什么时候加载一个类?
- new一个对象;调用类的静态方法或静态属性;
- 使用反射获取类信息;
- 初始化一个对象,会先初始化其父类;
- Jvm启动,即当前java程序启动时必须调用主类的 main() 方法,主类会先加载;
类加载过程?(5步)
- 加载;加载类的.class文件,生成Class对象(单例对象);
- 验证;检查.class文件的二进制字节流,完成“文件格式、元数据、字节码”等的验证;
- 准备;仅仅为“类变量”初始化;
- 解析;将常量池的符号引用替换为直接引用;
- 初始化;真正的执行java代码的初始化部分;
类加载器分类?(4种,从上往下)
- 启动类加载器,C++代码实现,加载 /lib/ 路径下的类库;
- 扩展类加载器,由Extra Class Loader实现,加载 /lib/ext/ 路径下的类库;
- 应用程序类加载器,由App Class Loader实现,加载ClassPath路径下的类库;为“默认类加载器”;
- 自定义类加载器;
双亲委派模型?为什么?
image.jpeg
双亲委派模型规定,除了最顶层的启动类加载器,类加载器都必须有其“父类加载器”;(父子关系通过“组合”方式实现,而非“继承”)
原理:当一个类加载器收到“去加载一个类”的请求时,会先把这个请求“委派”给其父类类加载器;这样无论哪个层的类加载器,加载请求最终都会委派给顶层的启动类加载器,启动类加载器在其目录下尝试加载该类,找不到该类,子加载器才会自己尝试加载这个类;
为什么?保证加载的类的唯一性;例如 java.lang.Object类,在 /lib/目录下的rt.jar 内,无论哪个类加载器去加载这个类,他们最终都是调用“启动类加载器”加载,即加载的类是同一个类;否则每个类加载器都有自己的“加载路径”,若有重名的类,则会导致程序混乱;
FindClass与 LoadClass ?(对于自定义类加载器)
- LoadClass:自定义类加载器,重写loadClass() 方法,可以让不同的类加载器保留各自的“同名的类”,工作流程是先自己尝试加载这个类,加载成功则返回,加载失败才会交给父类加载器;即“破坏了双亲委派模型和类的唯一性”;
例如:有一个插件容器,现在需要往里面添加A、B两个插件。A、B两个插件都定义了一个“相同名字”的类,如Test.class ,由于A、B插件在定义时都不知道彼此的信息,各自定义的Test类有具有不同的功能,因此“有必要加载这两个具有不同功能的重名的类”;使用“双亲委派模型”只会加载一次,只加载其中一个Test类;
- FindClass:符合“双亲委派模型”,保证一个类只会加载一次;在JDK源码中,findClass() 方法是放在 catch{...} 块内的,因此一般情况是用不到这个方法的;