什么是反射
1、在运行状态中,对于任意一个类,都能够知道这个类的属性和方法。
2、对于任意一个对象,都能够调用它的任何方法和属性。
这种动态获取信息以及动态调用对象的方法的功能称为JAVA的反射。
程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl、Python、Ruby是动态语言,C++、Java、C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制——Reflection(反射),用在Java身上指的是可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体(newInstance)或对其fields设值,或唤起(invoke)其methods方法。
反射能做什么
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理。
反射的原理
众所周知Java有个Object 类,是所有Java 类的继承根源,其内声明了数个应该在所有Java 类中被改写的方法:hashCode()、equals()、clone()、toString()、getClass()等。其中getClass()返回一个Class 对象
而这个Class 类十分特殊。它和一般类一样继承自Object,当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM 便自动产生一个Class 对象
当我们获取到这个Class对象之后,我们就可以调用这个Class的方法直接对class字节文件进行操作,获取到类的属性、构造器、普通方法的详细信息和对其进行操作
下面我们通过个例子详细的了解一下反射
首先我们创建一个实体类
package Reflex;
/**
* 学生实体类,五脏俱全
* @author tiancaixiaoniuniu
* @date 2019/4/16 15:16
*/
public class Student {
public String name;
public String sex;
private int age;
//===============================构造方法========================================
public Student(){}
public Student(String name) {
this.name = name;
}
public Student(String name, String sex) {
this.name = name;
this.sex = sex;
}
private Student(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
//=============================普通方法======================================
public void speak(){
System.out.println(name+"在说话");
}
public void speak(String value){
System.out.println(name+"在说话:"+value);
}
private void sing(){
System.out.println(name+"在唱歌");
}
//===============================get&set========================================
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
使用反射第一步,得到Class类,有三种方法
//获取Class
public static void demo01() throws Exception {
//3种获取Class的方法
System.out.println("==================获取Class===================");
//直接类名打点class
Class<?> student1 = Student.class;
System.out.println(student1);
//Class打点调用forName方法 参数是全限定类名:包名加类名 *最常用
Class<?> student2 = Class.forName("Reflex.Student");
System.out.println(student1);
//先生成对象,再打点getClass()
Student student=new Student();
Class<?> student3 = student.getClass();
System.out.println(student3);
}
然后可以获取到属性、构造器、方法信息
//获取属性信息
public static void demo02()throws Exception{
Class<?> student = Class.forName("Reflex.Student");
//获取属性
System.out.println("==================获取属性===================");
//参数为 属性的名字
Field name = student.getField("name");
System.out.println(name);
//私有类型的属性要用getDeclaredField
System.out.println("==================获取私有属性===================");
Field age = student.getDeclaredField("age");
System.out.println(age);
//获取所有的属性(不包含私有)
System.out.println("==================获取获取所有的属性(不包含私有属性)===================");
Field[] fields = student.getFields();
for (Field fieid:fields) {
System.out.println(fieid);
}
//获取真*所有的属性
System.out.println("==================获取获取真*所有的属性===================");
Field[] declaredFields = student.getDeclaredFields();
for (Field fieid:declaredFields) {
System.out.println(fieid);
}
}
//获取方法信息
public static void demo03()throws Exception{
Class<?> student = Class.forName("Reflex.Student");
System.out.println("==================获取普通方法===================");
//参数为方法名字和参数列表,参数列表要用参数类型.class的方式 参数没有填null
Method speak = student.getMethod("speak", null);
System.out.println(speak);
Method speak1 = student.getMethod("speak", String.class);
System.out.println(speak1);
System.out.println("==================获取私有方法===================");
Method sing = student.getDeclaredMethod("sing");
System.out.println(sing);
System.out.println("==================获取所有普通方法(不包含私有方法)===================");
//会将父类和接口的方法也输出显示
Method[] methods = student.getMethods();
for (Method method: methods) {
System.out.println(method);
}
System.out.println("==================获取所有普通方法(包含私密方法)===================");
//父类和接口的方法不会输出显示
Method[] declaredMethods = student.getDeclaredMethods();
for (Method method: declaredMethods) {
System.out.println(method);
}
}
//获取构造方法
public static void demo04()throws Exception{
Class<?> student = Class.forName("Reflex.Student");
System.out.println("==================获取构造方法===================");
Constructor<?> constructor = student.getConstructor(null);
System.out.println(constructor);
//属性是私有的构造方法
Constructor<?> constructor1 = student.getDeclaredConstructor(String.class, String.class, int.class);
System.out.println(constructor1);
System.out.println("==================获取所有的构造方法===================");
//获取所有的构造方法(getConstructors即所有公共的构造方法,不写了)
Constructor<?>[] declaredConstructors = student.getDeclaredConstructors();
for (Constructor<?> Constructor:declaredConstructors) {
System.out.println(Constructor);
}
}
接下来我们具体的使用一下属性和方法
//具体使用
public static void demo05()throws Exception{
Class<?> student = Class.forName("Reflex.Student");
//创建构造器
Constructor<?> constructor = student.getConstructor(null);
System.out.println("=====================使用构造器生成一个对象========================");
//使用构造器生成一个对象
Object object =constructor.newInstance(null);
System.out.println(object);
//为这个对象赋值:使用getField获取属性 打点set方法赋值
// 第一个参数是目标对象,第二个参数是具体给这个参数赋的值
student.getField("name").set(object,"小李");
//如果要给私有属性赋值,要将这个属性访问权限设置一下
Field name = student.getDeclaredField("age");
name.setAccessible(true);
name.set(object,18);
System.out.println("======================调用方法 =======================");
//调用getName方法 使用getMethod获取方法 打点invoke 调用这个方法
//第一个参数是目标对象,第二个参数是具体给这个参数赋的值,没有参数给null
String getName = (String) student.getMethod("getName", null).invoke(object, null);
Integer age = (Integer) student.getMethod("getAge").invoke(object, null);
System.out.println(getName+":"+age);
//调用speak方法 使用getMethod获取方法 打点invoke 调用这个方法
student.getMethod("speak",null).invoke(object);
//如果要调用私有的方法,要将这个方法访问权限设置一下
Method sing = student.getDeclaredMethod("sing");
sing.setAccessible(true);//设置访问权限
sing.invoke(object);
System.out.println("===============或者直接打点newInstance获得对象(使用默认的构造器)=====================");
//或者直接打点newInstance获得对象(使用默认的构造器)
Object o = student.newInstance();
student.getMethod("speak",null).invoke(o);
}
最后我们用修改配置文件的方式提现一下反射如何动态的执行对象的属性和方法
创建一个配置文件
#className=Reflex.Student
#methodName=speak
#fieId=name
#fieId.value=小王
className=Reflex.Teacher
methodName=lecture
fieId=name
fieId.value=大王
再创建一个实体类
/**
* @author tiancaixiaoniuniu
* @date 2019/4/17 10:39
*/
public class Teacher {
public String name;
public void lecture(){
System.out.println(name+"老师在讲课");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
读取到配置文件,并用使用反射执行方法
/**
* 运行时动态调用方法、属性
* java文件不动,只需要修改配置文件就可以调用修改不同类的属性和方法
* @throws Exception
*/
public static void demo06()throws Exception{
//读取文件信息 获取类的全类名和方法名
Properties properties=new Properties();
properties.load(new FileReader("reflex.properties"));
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
String fieId = properties.getProperty("fieId");
String fieIdValue = properties.getProperty("fieId.value");
//获取类
Class<?> clazz = Class.forName(className);
//生成对象
Object obj = clazz.newInstance();
//给参数赋值
clazz.getField(fieId).set(obj,fieIdValue);
//调用方法
clazz.getMethod(methodName).invoke(obj);
}
然后我们写个循环来执行,并在执行时修改配置文件
while (true){
demo06();
Thread.sleep(1000);
}
运行截图
结语
这样,我们就可以用反射来实现类似Spring ioc的功能,实际上它就是用反射来实现的,后续我会手写一个简单的山寨spring,敬请关注
*原创作品,转载请表明出处。有建议或者问题欢迎联系我,QQ1595787997