Java 反射机制详解
1. 一个需求引出反射
(1) 根据配置文件re.properties 指定信息,创建对象并调用方法
filePathName=com.dong.reflect.classloader.Cat
method=hello
(2) 这样的需求在创建学习框架的时候特别多,通过外部配置文件,在不修改源码的情况下,来控制程序,也符合设计模式ocp(开闭原则)
(3) 入门案例
Properties properties = new Properties();
properties.load(new FileInputStream("D:\\project\\java_projects\\simpleAlgrithom\\src\\re.properties"));
String filePathName = properties.get("filePathName").toString();
String method = properties.get("method").toString();
//使用反射机制来解决问题
//加载类对象,返回Class类型的类对象
Class aClass = Class.forName(filePathName);
//通过得到的aClass 得到加载类的实例对象 com.hsp.edu.classloader.Cat的实例对象
Object o = aClass.newInstance();
System.out.println(o.getClass());
Method method1 = aClass.getMethod(method);
method1.invoke(o);
//第一种导入类的方式
Class.forName("com.hsp.edu.classloader.Cat");
//第二种导入类的方式
Class<Cat> catClass = Cat.class;
//第三种有对象实例的情况下
Cat cat = new Cat();
Class aClass1 = cat.getClass();
//第四种 通过类加载器的情况
ClassLoader classLoader = cat.getClass().getClassLoader();
Class aClass2 = classLoader.loadClass(filePathName);
2. 反射机制
2.1 Java Reflection
- 反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法,反射在设计模式和框架底层都会用到。
- 加载完类之后,在堆中就产生了一个Class类型的对象(一个类中只有一个Class对象),这个对象包含了类的完整结构信息,通过这个对象得到类的结构,这个Class对象就像一面镜子,透过这个镜子可以看到类的结构,所以形象的称之为:反射
2.2 反射机制可以完成
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
2.3 反射相关的主要类
- java.lang.Class: 代表一个类,Class对象表示某个类加载后在堆中的对象
- java.lang.reflect,Method: 代表类的方法,Method对象表示某个类的方法
- java.lang.reflect.Field: 代表类的成员变量,Field对象表示某个类的成员变量
- java.lang.reflect.Constructor: 代表类的构造方法,Constructor对象表示构造器
2.4 反射机制调用调优,关闭访问检查
- Method和Field,Constructor对象都有setAccessible()方法
- setAccessible作用是启动和禁用访问安全检查的开关
- 参数为true表示反射的对象在使用时取消访问检查,提高反射的效率,参数值为false则表示反射的对象执行访问检查
3. 获取Class对象
- 前提已知一个类的全类名,且在该类的类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,实例:Class cls1 = Class.forName(“java.alng.Cat”);
应用场景:多用于配置文件,读取类全路径,加载类 - 前提:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高
实例:Class cls2 = Cat.class;
应用场景:多用于参数传递,比如通过反射得到对应构造器对象。 - 前提已知某个类实例,电泳该实例的getClass()方法获取Class对象。
实例:CLass clazz = 对象.getClass: // 运行类型
应用场景:通过创建好的对象,获取class对象 - 其他方式
ClassLoader cl = 对象.getClass().getCLassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);
3.1 哪些类型有Class对象
- 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
- interface:接口
- 数组
- enum:枚举
- annotation:注解
- 基本数据类型
- void
3.2 类加载
3.2.1 基本说明
反射机制时java实现动态语言的关键,也就是通过反射实现类动态加载
- 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
- 动态加载:运行时加载需要的类,,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性。
3.2.2 类加载的时机
- 当创建对象时(new) //静态加载
- 当子类被加载时,父类也加载 //静态加载
- 调用类中的静态成员时 // 静态加载
- 通过反射 //动态加载
3.2.3 连接阶段-验证
- 目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全
- 包括:文件格式验证(是否以魔数oxcafebabe开头),元数据验证,字节码验证和符号引用验证
- 可以考虑使用-Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间
3.2.4 Initialization (初始化)
- 到初始化简短,才正真开始执行类中定义的Java程序代码,此阶段是执行<clinit>()方法的过程。
- <clinit>() 方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中所有的静态变量的赋值动作和静态代码块中的语句,并进行合并
- 虚拟机会保证一个类<clinit>()方法会在多线程环境中被正确地加锁,同步。如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行完毕。
3.2.5 类加载信息详解
-
Java程序在计算机中的三个阶段
-
类加载过程图
-
类加载完成任务
3.3 通过反射获取类的结构信息
3.3.1 第一组:java.lang.Class类
- getName:获取全类名
- getSimpleName:获取简单类名
- getFields:获取所有public修饰的属性,包含本类以及父类的
- getDeclaredFields:获取本类的所有属性
- getMethods:获取所有public修饰的方法,包含本类以及父类的
- getDeclaredMethods:获取本类的所有方法
- getConstructors:获取所有public修饰的构造器,包含本类以及父类的
- getDeclaredConstructors:获取本类中的所有构造器
- getPackage:以Package形式返回包信息
- getSuperClass:以Class形式返回父类信息
- getInterfaces:以class[] 形式返回接口信息
- getAnnotations:以Annotation[]形式返回注解信息
3.3.2 第二组:java.lang.reflect.Field类
- getModifiers:以int形式返回修饰符
说明: 默认修饰符是0,public是1,private是2,protected是4,static是8,final是16,public(1) + static(8) = 9 - getType:以Class形式返回类型
- getName:返回属性名
3.4 通过反射创建对象
- 方式一:调用类中public修饰的无参构造器
- 方式二:调用类中的指定构造器
- Class类相关方法
(1)newInstance:调用类中的无参构造器,获取对应类的对象
(2)getConstructor(Class…clazz):根据参数列表,获取对应的public构造器对象
(3)getDeclareConstructor(Class…clazz):根据参数列表,获取对应的构造器对象 - Constructor类相关的方法
(1)setAccessible:爆破
(2)newInstance(Object…onject):调用构造器
3.4 通过反射访问类中的成员
3.4.1 访问属性
- 根据属性名获取Field对象
Field f = clazz对象.getDeclaredField(属性名); - 爆破:d.setAccessible(true);//f是Field
- 访问
f.set(o,值); //表示对象
syso(f.get(o)); //o表示对象 - 如果是静态属性,则set和get中的参数o,可以写成null
3.4.2 访问方法
- 根据方法名和参数列表获取Method方法对象:Method m = clazz.getDeclaerdMethod(方法名,XX.class);
- 获取对象Object o = clazz.newInstance();
- 爆破:m.setAccessible(true);
- 访问:Object retureValue = m.invoke(o, 实参列表);
- 注意如果是静态方法,则invoke中的参数o,可以写成null!
4. 案例1
4.1案例1
//获取类对象
Class<?> personCtl = Class.forName("com.hsp.edu.classloader.Person");
//通过public的无参构造器创建对象
Object o = personCtl.newInstance();
System.out.println(o);
//通过有参构造器创建对象
Constructor<?> constructor = personCtl.getConstructor(String.class);
Object hsp = constructor.newInstance("jack");
System.out.println(hsp);
//得到私有的构造器对象
Constructor<?> declaredConstructor = personCtl.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true); //表示爆破的意思,可以利用private也可以创建对象
Object o1 = declaredConstructor.newInstance("牛逼", 12);
System.out.println(o1);
//获取成员变量
Field name = personCtl.getField("name");
name.set(o, "mmn"); //设置成员变量的属性值
System.out.println(o);
System.out.println(name.get(o));
//通过反射获取成员变量的值
Field sal = personCtl.getDeclaredField("sal");
sal.setAccessible(true); //爆破 直接暴力破解门 一切事物都是纸老虎
sal.set(o, "kkm");
System.out.println(o);
//对成员变量进行操作
Method m1 = personCtl.getMethod("m1", String.class, int.class);
Object re = m1.invoke(o, "你真牛逼", 23);
System.out.println(re);
//调用private static方法
Method declaredMethod = personCtl.getDeclaredMethod("m4", String.class);
declaredMethod.setAccessible(true); //设置爆破选项
System.out.println(declaredMethod.invoke(o, "好牛逼"));
调用的类信息:
@Deprecated
public class Person extends Cat implements A1, A2{
public String name = "赵云";
protected int age = 23;
String job = "code";
private String sal = "asd";
public Person(){}
public Person(String name){
this.name = name;
}
private Person(String name, int age){
this.name = name;
this.age = age;
}
public String m1(String n1, int n2){
return n1 + " " + n2;
} //公共方法
protected String m2(){
return null;
} //保护方法
void m3(){} //默认方法
private static String m4(String str){//私密方法
return str;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", job='" + job + '\'' +
", sal='" + sal + '\'' +
'}';
}
}
interface A1{}
interface A2{}
结果:
Person{name='赵云', age=23, job='code', sal='asd'}
Person{name='jack', age=23, job='code', sal='asd'}
Person{name='牛逼', age=12, job='code', sal='asd'}
Person{name='mmn', age=23, job='code', sal='asd'}
mmn
Person{name='mmn', age=23, job='code', sal='kkm'}
你真牛逼 23
好牛逼
4.2 案例2
public class ReflectionUtils {
public static void main (String[] args) {
}
@Test
public void api_02() throws ClassNotFoundException {
//得到class对象
Class<?> personCls = Class.forName("com.hsp.edu.classloader.Person");
//getFields: 获取所有public所修饰的属性,包含父类以及本类属性
Field[] fields = personCls.getFields();
for (Field field : fields) {
System.out.println("获取属性名字:" + field.getName()
+ " 获取属性修饰符:" +field.getModifiers()
+ " 获取属性的类型 :" + field.getType());
}
//获取
/* Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本类中的所有属性 " + declaredField.getName() +
" 该属性修饰符 " + declaredField.getModifiers() +
" 该属性类型" + declaredField.getType());
}*/
//获取方法信息
Method[] methods = personCls.getDeclaredMethods();
for (Method method : methods) {
System.out.println("本类的所有方法 " + method.getName() +
" 该属性的修饰符 " + method.getModifiers() //表示属性的数字
+ " 该属性的类型 " + method.getReturnType());
//获取方法的参数
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType.getName());
}
}
//获取构造器的所有方法
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) { //获取构造器的参数
System.out.println(parameterType);
}
}
}
@Test
public void api_01() throws ClassNotFoundException {
//得到class对象
Class<?> personCls = Class.forName("com.hsp.edu.classloader.Person");
//getName 获取类全名
System.out.println(personCls.getName());
//获取简单类名
System.out.println(personCls.getSimpleName());
//getFields: 获取所有public所修饰的属性,包含父类以及本类属性
Field[] fields = personCls.getFields();
for (Field field : fields) {
System.out.println(field);
}
//获取本类以及父类的所有属性
// Field[] declaredFields = personCls.getDeclaredFields();
// for (Field declaredField : declaredFields) {
// System.out.println(declaredField);
// }
//获取本类以及父类的所有方法
/* Method[] methods = personCls.getMethods();
for (Method method : methods) {
System.out.println(method);
}*/
//获取本类的所有方法 加上Declare表示获得所有的方法(包括私密)
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
// System.out.println(declaredMethod.getName());
}
//获取本类以及父类的所有构造方法
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor.getName());
}
//获取包的信息
System.out.println(personCls.getPackage());
//获取父类信息
Class<?> superclass = personCls.getSuperclass();
System.out.println(superclass);
//获取接口信息
Class<?>[] interfaces = personCls.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface);
}
//获取注解信息
Annotation[] annotations = personCls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
实验结果:
获取属性名字:name 获取属性修饰符:1 获取属性的类型 :class java.lang.String
获取属性名字:cat 获取属性修饰符:1 获取属性的类型 :class java.lang.String
本类的所有方法 toString 该属性的修饰符 1 该属性的类型 class java.lang.String
本类的所有方法 m1 该属性的修饰符 1 该属性的类型 class java.lang.String
java.lang.String
int
本类的所有方法 m2 该属性的修饰符 4 该属性的类型 class java.lang.String
本类的所有方法 m4 该属性的修饰符 10 该属性的类型 class java.lang.String
java.lang.String
本类的所有方法 m3 该属性的修饰符 0 该属性的类型 void
com.hsp.edu.classloader.Person
class java.lang.String
int
com.hsp.edu.classloader.Person
class java.lang.String
com.hsp.edu.classloader.Person
借鉴
https://space.bilibili.com/651245581/?spm_id_from=333.999.0.0