一、反射的概述
从使用上来讲,反射就是获取某一类对象的属性、构造方法和方法,并对这些属性和方法进行操作,如设置访问权限、运行方法等。
效果上,反射能够提高代码的复用性,降低耦合度,因此许多的框架中都使用了反射的机制,下面就对反射的相关使用进行介绍,分别对应获取类及对类的操作、获取构造方法及对构造方法的操作、获取熟悉及对属性的操作、获取方法及对方法的操作。并最后给出一个使用实例,更好地体会反射机制所带来的代码复用性的提高。
二、反射的方法概述
在测试开始之前先设置一Student测试类如下所示,该测试类参考
https://blog.csdn.net/sinat_38259539/article/details/71799078
public class Student {
public String name;
protected int age;
char sex;
private String phoneNum;
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", sex=" + sex
+ ", phoneNum=" + phoneNum + "]";
}
//无参构造方法
public Student(){
System.out.println("调用了公有、无参构造方法执行了。。。");
}
//有一个参数的构造方法
public Student(char name){
System.out.println("姓名:" + name);
}
//有多个参数的构造方法
public Student(String name ,int age){
System.out.println("姓名:"+name+"年龄:"+ age);
}
//受保护的构造方法
protected Student(boolean n){
System.out.println("受保护的构造方法 n = " + n);
}
//私有构造方法
private Student(int age){
System.out.println("私有的构造方法 年龄:"+ age);
}
public void show1(String s){
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
protected void show2(){
System.out.println("调用了:受保护的,无参的show2()");
}
void show3(){
System.out.println("调用了:默认的,无参的show3()");
}
private String show4(int age){
System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
return "abcd";
}
}
2.1、关于类本身的反射操作方法
关于获取Class对象,有三种方式,分别是通过类的class属性获得、通过对象的getClass方法获得和通过Class类的静态方法利用类的名称获得,三者都会返回一个Class对象,下面就对这三种方法进行演示
Student stu = new Student("姓名字段",1);
/************下面演示获取Class对象的三种方式**********/
//通过类名.class的方式
Class<Student> studentClass = Student.class;
System.out.println(studentClass);
//通过调用对象的getClass的方式
Class<? extends Student> aClass = stu.getClass();
System.out.println(aClass);
//通过Class的静态方法
Class<?> student = Class.forName("Student");
System.out.println(student);
输出结果如下
2.2、关于构造方法的反射操作方法
2.2.1、构造方法Construcor<?>对象的获取
getConstructors()方法和getDeclaredConstructors()方法分别会获得public修饰的构造方法和所有的构造方法,返回一个Constructor数组
//获取所有的公共构造方法
Constructor<?>[] constructors = studentClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("********分割线**********");
//获取所有的构造方法(不论何种访问权限符修饰)
Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
运行效果:
//根据参数获得构造方法,注意这里的int.class与Integer.class还是不一样的,一定要区分好
Constructor<Student> constructor = studentClass.getConstructor(String.class,int.class);
System.out.println(constructor);
使用getConstructor方法,根据参数可以获得特定的构造方法
这里需要注意两点
第一点是getConstructor方法只能从public修饰的公共方法中获取,第二点是在获取Constructor对象的方法中,Integer.class和int.class是不一样的,要做好区分
2.2.2、反射得到的构造方法的使用
使用Constructor类的newInstance方法进行执行,可以得到一个实例化的对象
Student student = constructor.newInstance("反射构造测试", 1);
System.out.println(student);
运行结果:
2.3、关于属性的反射操作方法
2.3.1、属性Field对象的获取
与构造方法的获取一样,属性同样有获取公共与获取所有的两种方式,下面给出代码和运行结果
//获取所有公共属性并打印
Field[] fields = studentClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("********分界线**********");
//获取所有属性并打印
Field[] declaredFields = studentClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
运行结果:
2.3.2、反射得到的属性的使用
获取到Field对象之后就可以对Filed对象调用set方法进行修改,set方法需要传递两个值,一个是实例化的对象,也就是要修改哪个对象的字段,另一个是修改后的值,set方法是void类型的,不返回值。
值得注意的是,除了public修饰的字段之外,要修改的话需要调用setAccessible方法,传递true,将这一字段设置成可以修改的,下面给出代码和运行结果。
//获取属性
Field name = studentClass.getField("name");
//更改某一对象的这一属性
name.set(student,"属性已更改");
System.out.println(student);
//更改非公有属性的时候就需要调用
Field phoneNum = studentClass.getDeclaredField("phoneNum");
phoneNum.setAccessible(true);
phoneNum.set(student,"123");
System.out.println(student);
运行结果:
2.4、关于类中方法的反射方法
2.4.1、方法Method对象的获取
Method对象的获取也是有获取public修饰的与所有的两种方式,与上文所述基本类似,不再赘述,因此只给出代码使用实例与运行效果如下
//获取所有public修饰的方法
for (Method method : studentClass.getMethods()) {
System.out.println(method);
}
//获取所有方法
for (Method declaredMethod : studentClass.getDeclaredMethods()) {
System.out.println(declaredMethod);
}
运行效果:
从运行结果可以看出getMethods()方法还会输出父类的public方法,包括Object类的
2.4.2、反射得到的方法的使用
Method对象通过invoke方法进行执行,invoke方法需要两组参数,一是一个实例化的对象,第二就是要执行的方法所需要的参数,如果是无参的,可以传递null或者直接不写,下面给出代码和运行效果
//运行特定方法(public的与非public修饰的)
Method show1 = studentClass.getMethod("show1", String.class);
show1.invoke(student,"乌拉!");
Method show2 = studentClass.getDeclaredMethod("show2");
show2.invoke(student);
运行效果:
三、反射的使用实例
在黑马面面项目的前台系统中,通过获取URI来调用不同的方法,代码如下
BaseServlet类的doGet和doPost方法
//运用反射的思想通过不同的路径,调用不同的子类中的路径同名方法,完成了代码的抽取
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//requestURI是不包含?后面的参数的
String url = req.getRequestURI();
//提取URI的包含操作内容的部分
String methodName = url.substring(url.lastIndexOf('/') + 1);
//因为是子类调用doGet方法,所以this此处就是得到的具体实行的子类的类对象
Class clazz = this.getClass();
try {
//获取子类的URI同名方法
Method method = clazz.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);
//执行方法并返回值
Result result = (Result) method.invoke(this, req, resp);
this.returnData(resp,result);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doGet(req, resp);
}
对应的子类的代码
@WebServlet("/member/*")
public class MemberServlet extends BaseServlet {
public Result register(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Member member = getData(req, Member.class);
boolean registerResult = memberService.register(member);
if(registerResult){
return new Result("注册成功!", null);
} else {
return new Result("注册失败!", null);
}
}
}