一、什么是反射
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。
二、 为什么会有反射,反射解决了什么问题
Java中编译类型有两种:
- . 静态编译:在编译时确定类型,绑定对象即通过。
- 动态编译:运行时确定类型,绑定对象。动态编译最大限度地发挥了Java的灵活性,体现了多态的应用,可以减低类之间的耦合性。
Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public、static等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。
Reflection可以在运行时加载、探知、使用编译期间完全未知的classes。即Java程序可以加载一个运行时才得知名称的class,获取其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。
反射(reflection)允许静态语言在运行时(runtime)检查、修改程序的结构与行为。
在静态语言中,使用一个变量时,必须知道它的类型。在Java中,变量的类型信息在编译时都保存到了class文件中,这样在运行时才能保证准确无误;换句话说,程序在运行时的行为都是固定的。如果想在运行时改变,就需要反射这东西了。
实现Java反射机制的类都位于java.lang.reflect包中:
- Class类:代表一个类
- Field类:代表类的成员变量(类的属性)
- Method类:代表类的方法
- Constructor类:代表类的构造方法
- Array类:提供了动态创建数组,以及访问数组的元素的静态方法
一句话概括就是使用反射可以赋予jvm动态编译的能力,否则类的元数据信息只能用静态编译的方式实现,例如热加载,Tomcat的classloader等等都没法支持。
三、反射的使用
1、获取Class对象
package com.luyu.test;
import java.lang.Class;
/**
* @Author luyu
* @create 2022/02/19
*/
public class test {
public static void main(String[] args) throws Exception {
//获取Class的三种方式
//1.当类没有加载进内存时,通过Class.forName("全类名")-->包名.类名
//将字节码文件加载进内存,返回class对象
//*多用于配置文件,将类名定义在配置文件中,读取文件,加载类
Class cla = Class.forName("com.luyu.test.Cat");
System.out.println(cla);
//2.当字节码已经加载进内存了,可以通过类名.class 来获取Class对象
//*多用于参数传递
Class catClass = Cat.class;
System.out.println(catClass);
//3.已经new 出来对象了,可以通过对象.getClass()方法来获取
//*多用于对象的获取字节码的方式
Cat cat = new Cat();
Class aClass = cat.getClass();
System.out.println(aClass);
//三个class对象一样,可以用 == 运算符来判断
System.out.println(cla == catClass);
System.out.println(catClass == aClass);
}
}
结果
结论:同一个字节码文件(*.class)在一次编译运行过程中,只会加载一次,不论通过哪种方式获取,都是同一个.
2.class对象的功能
(1)获取成员变量们(Field)
package com.luyu.test;
import java.lang.reflect.Field;
/**
* @Author luyu
* @create 2022/2/19 14:17
*/
public class Demo2 {
public static void main(String[] args) throws Exception {
Class cls = Cat.class;
//一.获取成员变量们
//1.class.getField(String name)通过指定名称获取public修饰成员变量
Field name = cls.getField("name");
System.out.println(name);
System.out.println("-----------");
//2.class.getFields()获取所有public修饰的成员变量
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("-----------");
//获取到成员变量后
//获取值 参数是一个对象
Cat cat = new Cat();
Object o = name.get(cat);
System.out.println(o);
//设置值
name.set(cat,"旺财");
System.out.println(cat);
System.out.println("-----------");
//3.class.getDeclaredFields() 获取所以成员变量,且不考虑修饰符
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("-----------");
//4.class.getDeclaredField(String values) 获取所以成员变量,且不考虑修饰符
Field age = cls.getDeclaredField("age");
//私有成员变量不能直接访问
//.setAccessible 设置忽略访问权限修饰符的安全检查
//暴力反射
age.setAccessible(true);
Object o1 = age.get(cat);
System.out.println(age);
System.out.println(o1);
System.out.println("-----------");
}
}
结果:
(2)获取成员方法们(Constructor)
package com.luyu.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* @Author luyu
* @create 2022/2/19 15:02
*/
public class Demo3 {
public static void main(String[] args) throws Exception {
Class cls = Cat.class;
/*
* *class.getConstructor(类<?>... parameterType);
* *class.getConstructors();
*
* *class.getDeclaredConstructor(类<?>... parameterType);
* *class.getDeclaredConstructors();
*
*/
//通过参数传递获取对应的构造方法,返回对应的构造器
//构造方法是用来创建对象的
//有参
Constructor constructor = cls.getConstructor(String.class, int.class);
System.out.println(constructor);
System.out.println("------");
//创建对象
Object cat = constructor.newInstance("旺财", 18);
System.out.println(cat);
System.out.println("------");
//无参
Constructor constructor1 = cls.getConstructor();
System.out.println(constructor);
System.out.println("------");
//创建对象
Object cat1 = constructor1.newInstance();
System.out.println(cat1);
System.out.println("------");
//无参构造创建对象可以用以下方法快捷创建
Object o = cls.newInstance();
System.out.println(o);
}
}
结果:
(3)获取构造方法们(Method)
package com.luyu.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* @Author luyu
* @create 2022/2/19 15:18
*/
public class Demo4 {
public static void main(String[] args) throws Exception {
Class cls = Cat.class;
/*
* *class.getMethod(String name, Class<?>... parameterTypes);
* *class.getMethods();
*
* *class.getDeclaredMethod(String name, Class<?>... parameterTypes);
* *class.getDeclaredMethods();
*
*/
//通过方法名获取Method对象,有参数的方法,还需要传参数
Method say = cls.getMethod("say");
//执行方法之前需要创建对象
Cat cat = new Cat();
say.invoke(cat);
System.out.println("----------");
Method say2 = cls.getMethod("say",String.class);
//执行方法
say2.invoke(cat,"旺财:");
System.out.println("----------");
//获取所有public修饰的方法
Method[] methods = cls.getMethods();
//methods里还有继承Object的方法
//获取方法的名称getName()
for (Method method : methods) {
System.out.println(method.getName());
System.out.println(method);
}
}
}
结果:
(4)获取类名
------->获取的类名为全类名
//获取类名
System.out.println("----------");
String name = cls.getName();
System.out.println(name);
结果: