在说反射之前我们先来了解一下,类加载与类加载器
类加载
类加载的三个步骤:类的加载、类的连接、类的初始化
类的加载:就是指将class文件读入内存,并为它创建一个Java.lang.Class对象;同理任何类被使用时,系统都会为之建立一个Java.lang.Class对象类的连接:
验证阶段:用于检验被加载的类是否由正确的内部结构,并和其他类协调一致
准备阶段:负责为类的类变量分配内存,并设置默认初始化值
解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化:在该阶段,主要就是对类变量进行初始化
类的初始化步骤:
假如类还未被加载和连接,则程序先加载并连接该类假如该类的直接父类还未被初始化,则先初始化其直接父类假如类中有初始化语句,则系统依次执行这些初始化语句
注意:(在执行第2个步骤的时候,系统直接父类的初始化步骤也遵循初始化步骤1-3)
类的初始化时机:(即什么时候类会被初始化)
创建类的实例调用类的类方法访问类或接口的类变量,或者为该类变量赋值使用反射方式来强制创建某个类或接口对应的java.lang.Class对象初始化某个类的子类(对应上面的第二个步骤)直接使用java.exe命令来运行某个主类
类加载器
前言:虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
类加载器的作用:负责将.Class文件加载到内存中,并为之生成对应的java.lang.Class对象
JVM的类加载机制
全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换为Class对象,存储到缓存区
ClassLoader:是负责加载类的对象
Java运行时具有以下内置类加载器
Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null,并且没有父null
Platform class loader:平台类加载器可以看到所有平台类,平台类包括由平台类加载器或器祖先定义的Java SE平台API,它的实现类和JDK特定的运行时类
System class loader:它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
类加载器的继承关系:System的父类加载器为Platform,而Platform的父类加载器为Bootstrap
ClassLoader中的两个方法:
getSystemClassLoader():返回用于委派的系统类加载器
getParent():返回父类加载器进行委派
接下载我们进入正题
反射
Java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取得到的信息来创建对象,调用方法的一种机制。(通过构造方法对象里面的方法来创建对象)。
理解:反射机制的重点是它是在运行的时候要去获取类的变量和方法信息,所以我们无法通过new去创建该类对象。因为正常情况下,我们通过拿到.Java文件后,可以直接通过new关键字创建一个类的实例对象,然后通过这个实例对象就可以访问这个类的成员变量、构造方法、成员方法了。但是现在没有这个.Java文件,我们只能拿到.class文件。所以我们就需要用到我们的反射。
科普:.class文件与.Java文件的区别(
在Java程序的开发过程中,我们需要先定义一个类-取一个类名,然后将其保存在.java文件中,对.java文件编译后形成.class文件。实际上三者的名字是相同的,在Java的定义中文件名必须与类名相同,而.class文件是.java文件编译后的文件名字没改变。
每篇java文件都可以书写多个Java类,只是说公共类只能有一个,每个Java类在经过编译后都会各自产生一个对应的class文件,也就是说Java类的数目与class 文件的数目是对应的。
)
获取Class对象三种方式:
(以下内容前提我建好了一个Student类,然后在另一个类中获取该类的对象)
1、使用类的class属性来获取该类对应的class对象
Class<Student> c1=Student.class;
2、调用对象的getClass()方法,返回该对象所属类对应的Class对象
Student s= new Student();
Class<? extends Student> c3=s.getClass();
3、使用Class类中的静态方法forName(String className)
Class<?> c4=Class.forName("itheima.Student");
总结:第一种方式使用的时候最方便;第三种可以把字符串的数据放到配置文件中,这样我们就可以随时修改配置文件中的内容,所以如果为了使灵活性更高可以使用第三种方式
反射获取构造方法对象
//通过反射实现如下操作
Students=newStudent("林青霞",30,"西安");
System.out.println(s);
//获取Class对象
Class<?>c=Class.forName("itheima002.Student");
//返回所有公共构造方法对象的数组
Constructor<?>[]cons=c.getConstructors();
for(Constructorcon:cons){
System.out.println(con);
}
//返回所有构造方法对象的数组
Constructor<?>[]declaredCons=c.getDeclaredConstructors();
for(Constructorcon:declaredCons){
System.out.println(con);
}
//获取指定的公共构造函数对象
Constructor<?>con=c.getConstructor(String.class,int.class,String.class);
//根据指定的构造方法创建对象
Objectobj=con.newInstance("林青霞",30,"西安");
System.out.println(obj);
//返回指定的构造方法对象
Constructor<?>declaredCon=c.getDeclaredConstructor(String.class);
Objectobj=declaredCon.newInstance("zhang");
System.out.println(obj);
//返回默认或私有的构造方法对象
Constructor<?>con=c.getDeclaredConstructor(String.class,int.class);
//暴力反射
con.setAccessible(true);
Objectobj=con.newInstance("zhang",20);
System.out.println(obj);
反射获取成员变量
返回所有公共成员变量对象的数组
Field[]fields=c.getFields();
for(Fieldfield:fields){
System.out.println(field);
}
//返回所有成员变量对象的数组
Field[]fields=c.getDeclaredFields();
for(Fieldfield:fields){
System.out.println(field);
}
//返回单个公共成员变量对象
FieldaddressField=c.getField("address");
//此时可通过获取无参构造方法创建对象
Constructor<?>con=c.getConstructor();
Objectobj=con.newInstance();
//位成员变量赋值
addressField.set(obj,"西安");
System.out.println(obj);
//返回单个成员变量对象
FieldnameField=c.getDeclaredField("name");
Constructor<?>con=c.getConstructor();
Objectobj=con.newInstance();
nameField.setAccessible(true);
nameField.set(obj,"张");
System.out.println(obj);
反射获取成员方法
返回所有公共成员方法对象的数组,包括继承的
Method[]methods=c.getMethods();
for(Methodmethod:methods){
System.out.println(method);
}
返回所有成员方法对象的数组,不包括继承的
Method[]declaredMethods=c.getDeclaredMethods();
for(Methodmethod:declaredMethods){
System.out.println(method);
}
//返回单个公共成员方法对象
Methodm=c.getMethod("method1",String.class);
//获取无参构造方法创建对象
Constructor<?>con=c.getConstructor();
Objectobj=con.newInstance();
//调用成员方法
m.invoke(obj,"张");
//返回单个成员方法对象(该方法有返回值)
Methodm=c.getDeclaredMethod("method3",String.class,int.class);
Constructor<?>con=c.getConstructor();
m.setAccessible(true);
Objectobj=con.newInstance();
Objecto=m.invoke(obj,"张",20);
System.out.println(o);
反射之越过泛型
//通过反射向ArrayList<Integer>集合中添加一个字符串数据
ArrayList<Integer>array=newArrayList<Integer>();
Class<?extendsArrayList>aClass=array.getClass();
Methodm=aClass.getMethod("add",Object.class);
m.invoke(array,"zhang");
m.invoke(array,"wang");
System.out.println(array);
反射之配置文件
//通过反射运行配置文件中指定内容
Propertiespro=newProperties();
FileReaderfr=newFileReader("itheima\\class.txt");
pro.load(fr);
fr.close();
StringcName=pro.getProperty("className");
Stringmethod=pro.getProperty("methodName");
//通过反射来使用
Class<?>c=Class.forName(cName);
Constructor<?>con=c.getConstructor();
Objectobj=con.newInstance();
Methodm=c.getMethod(method);
m.invoke(obj);