一、类初始化
1.1 类初始化
类初始化(或类加载)的三个阶段:
- 类的加载
- 将 class 文件读入内存,并为之创建一个 java.lang.Class 对象;
- 类的连接
- 验证阶段:检测被加载的类的内部结构是否正确;
- 准备阶段:为类分配内存并设置默认初始化值;
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用;
- 类的初始化
- 对类的变量进行初始化;
类的初始化步骤
- 假如类还未被加载和连接,则程序先进行加载并连接该类;
- 假如该类的直接父类还未被初始化,则先初始化其直接父类;
- 假如类中有初始化语句,则系统依次执行这些初始化语句;
类的初始化时机:
- 创建类的实例;
- 调用类的类方法;
- 访问类或接口的类变量,或者为该变量赋值;
- 使用反射方式来强制创建某个类或接口对应的 java.lang.Class 对象
- 初始化某个类的子类;
- 直接使用 java.exe 命令来运行某个主类;
1.2 类加载器
类加载器的作用:
- 将 .class 文件加载到内存中,并生成对应的 java.lang.Class 对象;
JVM 的类加载机制:
- 全盘负责:当一个类加载器进行加载某个 class 时会将该 class 所依赖或引用的其他 class 也进行加载;
- 父类委托:当一个类加载器进行加载某个 class 时会让父类试图加载该 class ;
- 缓存机制:缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区;
ClassLoader:是负责加载类的对象
java 运行时具有以下内置类加载器
- Bootstrap class loader:虚拟机的内置加载类,通常表示为 null ;
- Platform class loader:平台加载器可以看到所有的平台类,平台类包括由平台类加载器或其祖先定义的平台 API,其实现类别和 JDK 特定的运行时类;
- System class loader:它也被称为应用程序类加载器,与平台加载类不一样,系统类加载器通常用于定义应用程序类路径,模块路径和 JDK 特定工具上的类;
- 类加载器的继承关系:Sysrem 的父加载器为 Platform,而 Platform 的父加载器为 Bootstrap;
ClassLoader 中的两个方法
- static ClassLoader getSystemClassLoader():返回委托的系统类加载器;
- ClassLoader getParent():返回父类加载器进行委派;
二、反射
2.1 反射概述
- java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制;
2.2 获取 Class 类的对象
通过反射去使用一个类,首先要获取该类的字节码文件对象,获取 Class 类型的对象有如下几种方法:
- 使用类的 class 属性来获取该类对应的 Class 对象;
- 调用类的 getClass() 方法,返回该对象所属类对应的 Class 对象;(因为 getClass() 方法是 Object 类中的方法,而所有的类都直接或间接地继承自 Object ,也就是说所有的类都可以使用 getClass() 方法)
- 使用 Class 类中的静态方法 forName( String className ),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径;
示例:
public class Student { /*类结构*/ } public class getClassDemo { public static void main(String[] args) throws ClassNotFoundException { //使用类的class属性来获取该类对应的class对象 Class<Student> c1 = Student.class; //使用getClass方法获取该类对应的class对象 Student student = new Student(); Class<? extends Student> c2 = student.getClass(); //使用class中的静态方法forName(String className)获取class对象 Class<?> c3 = Class.forName("ClassLoader2.Student"); System.out.println( c1 + "\n" + c2 + "\n" + c3 ); } }
2.3 反射获取构造方法并使用
Class 类中用于获取构造方法的方法
- Constructor<?>[] getConstructors():返回一个包含 Constructor 对象的数组,Constructor 对象反映了由该 class 对象表示的类的所有公共构造函数;
//获取class对象 Class<?> c = Class.forName("ClassLoader.Student"); //使用 getConstructors() 获取构造方法 Constructor<?>[] con1 = c.getConstructors(); for ( Constructor con : con1 ) { System.out.println(con); }
- Constructor<?>[] getDeclaredConstructors() :返回一个包含 Constructor 对象的数组 Constructor 对象反映了由该 class 对象表示的类的所有构造函数;
//获取class对象 Class<?> c = Class.forName("ClassLoader.Student"); Constructor<?>[] con2 = c.getDeclaredConstructors(); for ( Constructor con : con2 ) { System.out.println(con); }
- Constructor getConstructor(Class<?>… parameterTypes) :(参数是需要获取的构造方法参数的个数和数据类型对应的字节码文件)返回一个包含 Constructor 的对象该对象反映由该 class 对象表示的类的指定公共构造函数
//获取class对象 Class<?> c = Class.forName("ClassLoader.Student"); //需要抛出异常 //无参 Constructor<?> con3 = c.getConstructor(); //有参,多个参数时用逗号隔开,基本数据类型也可以通过 .class 得到对应的 class 类型 Constructor<?> con4 = c.getConstructor(String.class);
- Constructor getDeclaredConstructor(Class<?>… parameterTypes):(参数是需要获取的构造方法参数的个数和数据类型对应的字节码文件) 返回一个包含 Constructor 的对象该对象反映由该 class 对象表示的类的指定构造函数
//获取class对象 Class<?> c = Class.forName("ClassLoader.Student"); //需要抛出异常 //无参 Constructor<?> con4 = c.getDeclaredConstructor(); //有参,多个参数时用逗号隔开,基本数据类型也可以通过 .class 得到对应的 class 类型 Constructor<?> con5 = c.getDeclaredConstructor(String.class);
Constructor 类中用于创建对象的方法
- T newInstance( Object… initargs ):根据指定的构造方法创建对象;
//如果构造方法有参则需要输入相等数量且数量相等的参数 Object obj = con.newInstance();
- public void setAccessible( boolean flag ):值为 true 时取消修饰符检测
public class Student { private String name; private Student(String name) { this.name = name; } } public class ReflectDemo01 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //获取 class 对象 Class<?> c = Class.forName("ClassLoader4.Student"); //获取由一个 String 类型的参数组成的构造函数 Constructor<?> con = c.getDeclaredConstructor(String.class); //取消修饰符检测 con.setAccessible(true); //生成对象 Object obj = con.newInstance("李四"); }
2.4 反射获取成员
Class 中用于获取成员变量的方法:
- Field getDeclaredField(String name):返回一个对象,反映了 Field指定声明字段的类或接口的类对象表示;
- Field[] getDeclaredFields():返回 Field 物体反射所有字段的类或接口的 类对象表示声明数组。
- Field getField(String name):返回一个 Field对象反映的类或接口的 类对象表示的指定公共成员。
- Field[] getFields():返回一个数组包含 Field物体反射的类或接口的 类对象代表所有可访问的公共领域。
- void set(Object obj, Object value):设置域为代表的这 Field对象指定对象上的参数指定的新价值。
//获取 Class 对象 Class<?> c1 = Class.forName("ClassLoader.Student"); //使用 getDeclaredFields() 方法获取所有成员变量; Field[] fieldArray1 = c1.getDeclaredFields(); ` //使用 Field getField(String name) 方法获取一个公共的成员变量 Field f = c1.getField("address"); //使用 Field[] getFields() 方法获取所有公有的成员变量; Field[] fieldArray2 = c1.getFields(); //使用 void set(Object obj, Object value) 更新值; Constructor<?> con = c1.getConstructor();//获取一个无参的构造方法 Object obj = con.newInstance();//实例化一个对象 Field address = c1.getField("address");//获取名为 address 的一个共有成员变量 address.set(obj,"广州");//给 obj 的成员变量 address 赋值
Class 中用于获取成员方法的方法:
- Method getMethod(String name, 类<?>… parameterTypes):返回一个 方法对象反映的类或接口的 类对象表示的指定公共成员方法。
- Method[] getMethods():返回一个数组包含 方法物体反射所有的类或接口的 类对象表示的公共方法,包括那些由类或接口的超类和超接口继承的声明。( 注:获取方法是无序的 )
- Object invoke(Object obj, Object… args):调用底层的方法,这 方法对象表示,对指定对象的指定参数。
- Method getDeclaredMethod(String name, Class<?>…parameterTypes):返回一个 方法对象反映指定声明方法的类或接口的 类对象表示。
- Method[] getDeclaredMethods():返回一个数组包含 方法物体反射所有声明的方法的类或接口的 类对象,代表包括公众、保护,默认(包)的访问,和私有方法,但不包括继承的方法。
//获取 Class 对象 Class<?> c = Class.forName("ClassLoader7.Student"); //获取无参构造方法 Constructor<?> con = c.getConstructor(); //创建一个对象 Object student = con.newInstance(); //使用 getMethod 获得一个公有方法 Method m1 = c.getMethod("method1"); //使用 invoke(Object obj, Object... args) 调用方法。 m1.invoke(student); Method m2 = c.getMethod("method2", String.class); m2.invoke(student,"张三"); //使用 getMethods() 获取所有的公有方法 Method[] mArray = c.getMethods(); //使用 getDeclaredMethod(String name, Class<?>... parameterTypes) 获取一个私有方法 Method m4 = c.getDeclaredMethod("method3", String.class, int.class); //私有方法在调用时需要先取消修饰符 m4.setAccessible(true); m4.invoke(student,"张三",18); //使用 getDeclaredMethods() 获取所有公有方法 Method[] mArray2 = c.getDeclaredMethods();