- 类的加载
(1)加载:把字节码读取到内存中
(2)连接:
验证:确保加载的类信息符合JVM规范;准备:为类变量分配内存并为非final类变量默认初始值,如果是final的静态常量,直接赋常量值,这些内存都将在方法去中分配;解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
验证,准备完成后,在方法区中已经有一个能够代表当前类的Class对象
(3)类的初始化:大多数情况下和类的加载一起完成,有时不是
触发类初始化的操作(类的加载包含类的初始化一起完成):
(1)当虚拟机启动,先初始化main方法所在的类
(2)当初始化一个类,如果其父类没有初始化,则会先初始化它的父类
(3)new一个对象
(4)使用一个类的静态的成员(静态变量和静态方法),但这个静态的变量不能是final
(5)用java.lang.reflect包的方法对类进行反射调用
非触发类初始化的操作(类的加载不包含类的初始化一起完成):
(1)引用静态常量不会触发此类的初始化
(2)当访问一个静态域(静态变量,静态方法)时,只有真正声明这个域的类才会被初始化,即当通过子类引用父类的静态变量,静态方法时,不会导致子类初始化
(3)通过数组定义类引用,不会触发此类的初始化
- 类加载器
(1)引导类加载器/根类加载器(Bootstrap Classloader):加载java的核心库(JAVA_HOME/jre/lib/rt.jar等或sun.boot.class.path路径下的内容)是原生代码(C/C++)来实现的,并不继承自java.lang.ClassLoder,所以通过Java代码获取引导类加载值为null
(2)扩展类加载器(Extension ClassLoader)负责加载java的扩展库(JAVA_HOME/jre/ext/*.jar或java.ext.dirs路径下的内容),
(3)应用程序类加载器(Application Classloader)负责加载java应用程序类路径(classpath, java.class.path)下的内容
(4)自定义类加载器
什么情况下需自定义类加载器?
字节码需要加密和解密;字节码的路径不在常规路径,有自己特定的路径
-
获取某个类的类加载器
先获取某个类的Class对象,再通过Class对象调用getClassLoader()方法获取类加载器对象public class ClientTest { @Test public void test1() throws ClassNotFoundException{ Class clazz=ClientTest.class; //获取类的class对象 ClassLoader loader = clazz.getClassLoader(); //获取它的类加载器对象 sun.misc.Launcher$AppClassLoader@106d69c System.out.println(loader); ClassLoader parent = loader.getParent(); System.out.println(parent); //sun.misc.Launcher$ExtClassLoader@c34f4d ClassLoader parent2 = parent.getParent(); System.out.println(parent2); //null 根类加载器(通过Java代码获取引导类加载值为null } } Class clazz=String.class; ClassLoader loader = clazz.getClassLoader(); System.out.println(loader); //返回null
-
java中类加载器的双亲委托模式
类加载器设计时,这四种类加载器是有层次结构的,但是层次结构不是通过继承关系来实现,而是通过组合方式来实现,通过getParent()获取“父加载器”
双亲委托模式起到安全的作用,保证核心类库的类不会被替换掉,如何实现?
当应用程序类加载器接到某个类的任务时,会先在内存中搜索这个类是否加载过,如果加载过就返回这个类的Class对象,不去加载;如果没找到,会把这个任务提交给“父加载器”
当扩展类加载器接到某个类的任务时,会先在内存中搜索这个类是否加载过,如果加载过就返回这个类的Class对象,不去加载;如果没找到,会把这个任务提交给“父加载器”
当引导类加载器接到某个类的任务时,会先在内存中搜索这个类是否加载过,如果加载过就返回这个类的Class对象,不去加载;如果没找到,会在它负责的范围内尝试加载,如果可以找到,那么返回这个类的Class对象,然后结束;如果没有找到,那么会把这个任务往回传,让“自类加载器”扩展类加载器去加载
“子类加载器”扩展类加载器接到“父加载器”返回的任务后,去它负责的范围内加载,如果可以找到,那么返回这个类的Class对象,然后结束;如果没有找到,那么会把这个任务往回传,让“自类加载器”扩展类加载器去加载
“子类加载器”应用程序类加载器接到“父加载器”返回的任务后,去它负责的范围内加载,如果可以找到,那么返回这个类的Class对象,然后结束;如果没有找到,那么就报错ClassNotFoundException或java.lang.NoClassDefError -
类加载器的作用
加载类;可以加载“类路径下”的资源文件
ClassLoader类加载器的类型:getResourceAsStream(“资源文件的路径名”)
SourceFolder:源代码文件夹,一般会用一个名为config的SourceFolder来装配置文件;在根目录下创建的SourceFolder文件相当于src文件
//jdbc.properties在src目录下@Test public void test3() throws IOException{ Properties pro=new Properties(); Class clazz=ClientTest.class; ClassLoader loader = clazz.getClassLoader(); InputStream in = loader.getResourceAsStream("jdbc.properties"); pro.load(in); System.out.println(pro); } //in.properties在src下的包下 @Test public void test4() throws IOException{ Properties pro=new Properties(); Class clazz=ClientTest.class; ClassLoader loader = clazz.getClassLoader(); InputStream in = loader.getResourceAsStream("Test1/in.properties"); pro.load(in); System.out.println(pro); } //out.properties在项目根路径下 @Test public void test5() throws FileNotFoundException, IOException{ Properties pro=new Properties(); pro.load(new FileInputStream("out.properties")); System.out.println(pro); }