前言
反射机制确实是Java基础重点,当然本文概括的并不是很全面,仅仅是对反射进行着手,要全面理解反射,必不可少的前提知识是Class
类,这篇反射文章讲的比较全,可以参考如下文章
如何定义反射: 一句话定义反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法,所谓反射其实是获取类的字节码文件,也就是.class文件
如何理解反射:平时我们要调用某个类中的方法的时候都需要创建该类的对象,通过对象去调用类里面的方法,反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了,在这种情况下(没有创建对象)我们都能够对它的方法和属性进行调用,我们把这种 动态获取对象信息和调用对象方法的功能称之为反射机制。
正常的操作类
package com.FanSe;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class DemoOne {
private String name;
public int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//正常操作类
DemoOne one=new DemoOne();
one.setName("我");
System.out.println(one.getName()+"好帅"); //我好帅
));
}
}
反射操作类
package com.FanSe;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class DemoOne {
private String name;
public int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 1、获取类的 Class 对象实例
Class demo=Class.forName("com.FanSe.DemoOne");
// 2、通过类的实例的getmethod方法得到Method对象
Method setNameMethod = demo.getMethod("setName", String.class);
// 3、根据class对象实例的getconstructor方法获取Constructor对象
Constructor demooneConstructor = demo.getConstructor();
// 4、使用constructor对象的newinstance方法获取反射类对象实例
Object newInstance = demooneConstructor.newInstance();
// 5、利用invoke方法调用方法
setNameMethod.invoke(newInstance, "我");
// 6、获取方法的method对象
Method getName =demo.getMethod("getName");
System.out.println(getName.invoke(newInstance)+"好帅"); //我
}
}
上面两段代码的执行结果,其实是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(FanSe),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.FanSe.DemoOne)。
一般情况下我们使用反射获取一个对象的步骤:
1、获取类的 Class 对象实例的三种方式
第一种,使用Class.forName 静态方法。
当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象最常用,必须掌握。
Class classDemo = Class.forname("com.FanSe.DemoOne");
第二种,使用 .class方法。
这种方法只适合在编译前就知道操作的 Class,但是这种方法需要导入类的包,依赖性太强,所以用的比第一种稍微要少【重点】
Class classDemo = 类名.class;
第三种,使用类对象的 getClass() 方法。
这种方法已经创建了对象,那么这个时候就不需要去进行反射了,显得有点多此一举。【不常用,了解即可】
ClassDemo str = new ClassDemo();
Class clz = str.getClass();
2、根据 Class 对象实例获取 Constructor(构造函数) 对象
Constructor demoConstructor = classDemo.getConstructor();
3、使用 Constructor 对象的 newInstance 方法(实例)获取反射类对象
Object classDemoObj = demoConstructor.newInstance();
如果要反射某一个方法,则需要经过下面的步骤(开发中经常反射类方法):
一:通过类的实例的getmethod方法得到Method对象
Method setNameMethod = classDemo.getmethod("setName", String.class);
注意:Method Class.getMethod(String name, Class<?>… parameterTypes)的作用是获得对象所声明的公开方法
getMethod方法的第一个参数name是要获得方法的名字,而第二个参数parameterTypes是按声明顺序标识该方法形参类型
//如果获得classDemo对象的setName方法中没有形参,那么parameterTypes为null,如下:
classDemo.getClass().getMethod("classDemo", null);
二:利用一中得到的Method对象的invoke方法调用函数方法
setNameMethod.invoke(newInstance, "我");
注意
Method中invoke(Object obj,Object…args)方法的第一个参数为类的实例**,**第二个参数为相应函数中的参数
在 JDK 中,反射相关的 API 可以分为下面几个方面:
获取反射的 Class 对象、通过反射创建类对象、通过反射获取类属性方法及构造器
通过反射创建类对象(两种方法)
通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。
第一种:通过 Class 对象的 newInstance() 方法【只能默认无参数构造方法】
Class clz = classDemo.class;
ClassDemo classDemo = (ClassDemo)clz.newInstance();
第二种:通过 Constructor 对象的 newInstance() 方法【可以选择任何构造方法】
Class clz = classDemo.class;
Constructor constructor = clz.getConstructor();
ClassDemo classDemo= (ClassDemo)constructor.newInstance();
两种方法的区别:
通过 Constructor 对象(第二种)创建类对象可以选择特定构造方法,而通过 Class 对象(第一种)则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化,这种情况就只能使用第二种方法创建类对象。
Class clz = ClassDemo.class;
Constructor constructor = clz.getConstructor(String.class, int.class);
ClassDemo classDemo= (ClassDemo)constructor.newInstance("小明", 15); //有参构造方法
反射获取类属性、方法、构造器
我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。
Class clz = ClassDemo.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
输出结果是:
price
而如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:
Class clz = ClassDemo.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
输出结果是:
name
age
与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。
反射的重要性
当我们懂得了如何使用反射后,或许大家平时没有使用过反射,但是在开发 Web 项目的时候会经常用到反射,正所谓,无反射不框架!可见反射的重要性。