在叙述反射之前,先来简单说明一下什么是类加载器?什么是类加载?
类加载
程序在使用某个类时,若该类未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化对类进行初始化,一般情况下,JVM会连续完成3个步骤,有时统称类加载或者类初始化。
1)类的加载
将class文件读入内存,为之创建一个java.lang.Class对象;其实任何类被使用时,系统会为之建立一个java.lang.Class对象。
2)类的连接
验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。目的是保证加载的字节码是合法、合理并符合规范的。验证内容大概含有格式验证、语义检查、字节码验证以及符号引用验证等。
准备阶段:负责为类的变量分配内存,并设置默认初始化值。基本数据类型:非final修饰的变量,在准备阶段进行默认初始化赋值;final修饰之后,在准备阶段直接进行显式赋值。
解析阶段:将类的二进制数据中的符号引用替换为直接引用。也就是将类、接口、字段和方法的符号引用转为直接引用。以方法为例,通过解析,符号引用可以转变为目标方法在类中方法表中的位置,从而使得方法被成功调用。该操作伴随JVM在执行完初始化后再执行。
3)类的初始化
主要对类变量进行初始化。
步骤:假若类还未被加载和连接,则程序先加载并连接该类;假如该类的直接父类未被初始化,则先初始化该类;假如类中有初始化语句,则系统依次执行这些初始化语句 ;
注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循以上初始化步骤。
主动使用(7种)
-创建类的实例(new);
-访问某个类或接口的静态变量,或者对该静态变量赋值;
-调用类的静态方法;
-通过反射执行以上三种行为;
-初始化子类时,会触发父类的初始化;
-java虚拟机启动时被标明为启动类的(main方法)类(直接使用java.exe命令来运行某个主类);
-JDK1.7开始提供的动态语言支持(了解)
类加载器
负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象。
JVM的类加载机制
1)全盘负责——就是当一个类加载器负责加载某个Class时,该Class所依赖和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入;
2)父类委托——当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类;
3)缓存机制——保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区搜索该Class,只有当缓存区不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换为Class对象,存储到缓存区。
Java中的内置类加载器
1)Bootstrap class loader——它是虚拟机的内置类加载器,通常表示为null,并且没有父null;
2)Platform class loader——平台类加载器可以看到所有平台类,所有平台类加载器或其祖先定义的java SE平台API,其实现类和JDK特定的运行时类;
3)System class loader——应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具的类;
Class Loader中的两个方法
反射
在运行时获取一个类的变量和方法信息,然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展。那么通过反射使用一个类,先要获取类的字节码文件对象,也就是Class类型的对象。
获取Class对象的三种方式
1)类名.class属性
2)对象名.getClass( )方法
3)Class.forName(全类名)方法
反射获取构造方法
1)获取构造方法对象的方法
2)用Constructor类用于创建对象的方法
注意:
1)基本数据类型数据也可以通过.class得到对应的Class类型
2)通过反射我们可以获取私有构造方法并创建对象,采取暴力反射,利用setAccessible方法,将值设置为true,取消访问检查。
反射获取成员变量
1)获取成员变量对象的方法
2)Field类用于给成员变量赋值的方法
反射获取成员方法
1)Class类获取成员方法对象的方法
2)Method类用于执行方法的方法
案例
1)越过泛型检查——例如可以做到Interger类型集合中添加字符串。
2)运行配置文件中指定类的指定方法。
练习
/*利用反射和重载完成以下功能
1)创建Student类,类中有属性name和age并封装属性
2)重载Student的构造函数,一个是无参构造并,另一个是带两个参数的有参构造,要求在构造函数打印提示信息
3)创建带main函数的NewInstanceTest类,利用Class类得到Student对象
4)通过上述获取的Class对象分别调用Student有参函数和无参函数*/
public class Student {
private String name;
private int age;
public Student(){}
public Student(String name, int age) {
this.name = name;
this.age = age;
System.out.println("有参构造!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class NewInstanceTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//获取Class对象
Class<?> c = Class.forName("com.itheima01.Student");
//调用无参和有参
Constructor<?> con = c.getDeclaredConstructor(String.class, int.class);
Constructor<?> con2 = c.getDeclaredConstructor();
//创建对象
Object obj = con.newInstance("罗辑", 30);
Object obj2 = con2.newInstance();
//输出
System.out.println(obj);
System.out.println(obj2);
}
}
/*利用通过反射修改私有成员变量
1. 定义PrivateTest类,有私有name属性,并且属性值为hellokitty,只提供name的getName的公有方法
2. 创建带有main方法ReflectTest的类,利用Class类得到私有的name属性
3. 修改私有的name属性值,并调用getName()的方法打印name属性值*/
public class PrivateTest {
private String name = "hellokitty";
public PrivateTest(){}
public PrivateTest(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//获取Class对象
Class<?> c = Class.forName("com.itheima02.PrivateTest");
//获取构造方法
Constructor<?> con = c.getDeclaredConstructor();
Object obj = con.newInstance();
System.out.println(obj);
//修改name
Field nameField = c.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(obj, "陈雪");
//调用getName()的方法打印name属性值
//获取方法
Method m = c.getDeclaredMethod("getName");
System.out.println(m.invoke(obj));
}
}
/*利用反射和File完成以下功能
1. 利用Class类的forName方法得到File类
2. 在控制台打印File类的所有构造器
3. 通过newInstance的方法创建File对象,并创建D:\mynew.txt文件*/
public class Test {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//获取Class类对象
Class<?> c = Class.forName("java.io.File");
//获取构造器
Constructor<?>[] con = c.getDeclaredConstructors();
//输出File类构造器
System.out.println("File所有构造器:");
for(Constructor<?> cc : con){
System.out.println(cc);
}
System.out.println("-----------------------------------------");
//创建File对象,并创建D:\mynew.txt文件 createNewFile
Constructor<?> con2 = c.getConstructor(String.class);
Object obj = con2.newInstance("D:\\mynew.txt");
//获取方法
Method m1 = c.getDeclaredMethod("createNewFile");
System.out.println(m1.invoke(obj));
}
}
参考:
1)https://blog.youkuaiyun.com/Weixiaohuai/article/details/120047559