文章目录
前言
- 反射的概述:
- 允许对封装类的字段、方法和构造函数的信息进行编程访问
- 允许对成员变量、成员方法和构造方法的信息进行编程访问
一、反射机制
- 反射机制
是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意属性和方法;
这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制
二、获取Class类对象的三种方式
2.1 三种方式概述
-
三种方式分类:——> 都是获取某类的字节码文件对象
- 类名.class属性
- 对象名.getClass()方法
- Class.forName(全类名)方法
-
图解:
-
应用场景
2.2 反射获取构造方法并使用——Constructor
2.2.1 Class类获取构造方法对象的方法
-
方法介绍
方法名 说明 Constructor<?>[] getConstructors() 返回所有公共构造方法对象的数组 Constructor<?>[] getDeclaredConstructors() 返回所有(任何权限修饰符修饰的)构造方法对象的数组 Constructor getConstructor(Class<?>… parameterTypes) 返回单个公共构造方法对象 Constructor getDeclaredConstructor(Class<?>… parameterTypes) 返回单个(任何权限修饰符修饰的)构造方法对象 -
获取构造方法对象的步骤
因为构造方法是在字节码文件中,因此首先都要获取到字节码文件对象- 获取class字节码文件对象
用获取到的字节码文件对象调用方法来获取想要的构造方法
- 获取class字节码文件对象
2.2.2 Constructor类用于创建对象的方法
-
方法介绍
方法名 说明 T newInstance(Object…initargs) 根据指定的构造方法创建对象 setAccessible(boolean flag) 设置为true,表示取消访问检查 -
方法setAccessible(boolean flag)
注意:创建被私有的对象需要先临时取消权限校验后才能使用newInstance(Object…initargs)方法进行创建对象
2.2.3 小结
-
获取class对象
三种方式: Class.forName(“全类名”), 类名.class, 对象名.getClass() -
获取里面的构造方法对象
getConstructor (Class<?>... parameterTypes) getDeclaredConstructor (Class<?>… parameterTypes) -
如果是public的,直接创建对象
newInstance(Object… initargs) -
如果是非public的,需要临时取消检查,然后再创建对象
setAccessible(boolean) 暴力反射
2.3 反射获取成员变量并使用——Field
2.3.1 Class类获取成员变量对象的方法
-
方法分类
方法名 说明 Field[] getFields() 返回所有公共成员变量对象的数组 Field[] getDeclaredFields() 返回所有成员变量对象的数组 Field getField(String name) 返回单个公共成员变量对象 Field getDeclaredField(String name) 返回单个成员变量对象 -
方法示例
2.3.2 Field类用于给成员变量赋值的方法
- 方法介绍
方法名 | 说明 |
---|---|
void set(Object obj, Object value) | 赋值 |
Object get(Object obj) | 获取值 |
2.4 反射获取成员方法并使用——Method
2.4.1 Class类获取成员方法对象的方法
-
方法分类
方法名 说明 Method[] getMethods() 返回所有公共成员方法对象的数组,包括继承的 Method[] getDeclaredMethods() 返回所有成员方法对象的数组,不包括继承的 Method getMethod(String name, Class<?>… parameterTypes) 返回单个公共成员方法对象 Method getDeclaredMethod(String name, Class<?>… parameterTypes) 返回单个成员方法对象 -
方法示例
2.4.2 Method类用于执行方法的方法
-
方法介绍
方法名 说明 Object invoke(Object obj, Object… args) 运行方法 参数一: 用obj对象调用该方法
参数二: 调用方法的传递的参数 (如果没有就不写)
返回值: 方法的返回值 (如果是void就不写)
- 方法示例
- 如果运行的方法有返回值
三、代码示例
- 学生类
package com.wedu.fanshe;
public class Student {
private String name;
private int age;
private String number;
public Student() {
}
private Student(String name){
this.name = name;
}
protected Student(int age){
this.age = age;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
private void show1(){
System.out.println("这是私有方法");
}
private String show(int b){
System.out.println("这是私有方法,有返回值");
System.out.println("参数数据为:"+b);
return "执行";
}
private void show2(String str,String arr){
System.out.println("这是公共方法"+str);
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
- 测试类
package com.wedu.fanshe;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
public class FansheDemo01 {
public static void main(String[] args) throws Exception {
/**
* 获取com.wedu.fanshe包下的Student类的字节码文件对象
* 使用的是 Class.forName(全类名)方法
*/
Class<?> aClass = Class.forName("com.wedu.fanshe.Student");
/*
* 构造方法的反射应用
*/
Constructor<?>[] constructors = aClass.getConstructors();//获取该类所有公共的构造方法
for (Constructor<?> constructor : constructors) {
System.out.println("公共:"+constructor);
int modifiers = constructor.getModifiers();//调用该方法可以返回不同修饰符关键字的常量字段值
System.out.println("修饰符的常量字段值为:"+modifiers);
}
//获取 该类形参为String,且只有String 的构造方法
Constructor<?> dConstructor = aClass.getDeclaredConstructor(String.class);
System.out.println(dConstructor);
System.out.println("私有构造的常量字段值为:"+dConstructor.getModifiers());
Constructor<?> dC = aClass.getDeclaredConstructor(String.class, int.class);
/**
* 用字节码文件对象得到的相应构造方法对象调用.getParameters()方法
* 获取该构造方法对象的所有形参中的成员变量(因为调用者是构造方法对象)
*
* 该方法的调用者是谁 就返回谁的所有参数的对象
* 例:获取到的方法的对象m m.getParameters() ——> 返回方法内的所有参数,包括参数类型和参数名
*/
Parameter[] parameters = dC.getParameters();
for (Parameter parameter : parameters) {
System.out.println(dC+"中的参数:"+"\t"+parameter);
}
System.out.println("============================");
/*
* 成员变量的反射应用
*/
Field[] fields = aClass.getFields();//获取所有公共成员变量对象
for (Field field : fields) {
System.out.println("所有公共成员变量:"+field);
}
//获取指定的单个成员变量对象(公共、私有都可)
Field number = aClass.getDeclaredField("number");
System.out.println("成员变量全称为:"+number);
System.out.println("指定成员变量的名字为:"+number.getName());
System.out.println("指定成员变量的类型为:"+number.getType());
Field name = aClass.getDeclaredField("name");
/*
* 获取已有的学生对象的某个成员变量的值
* 先根据获取到的构造方法 暴力反射创建学生对象
*/
dC.setAccessible(true);//临时取消权限校验
// dC ——> 上面获取到的 学生类的有参构造方法 的对象
Student stu = (Student) dC.newInstance("张三", 22);
System.out.println("修改前:"+stu);
//获取学生对象的指定变量的值
name.setAccessible(true);//每暴力反射一次,都需要临时取消权限
String stuName = (String) name.get(stu);//因为返回值为Object类型的对象,但是已知返回的是字符串类型,因此直接强转即可
System.out.println("获取到stu对象的name的值,即该学生对象的姓名为:"+stuName);
//为指定学生对象的成员变量赋值
name.set(stu,"李四");
System.out.println("修改后:"+stu);
System.out.println("==============================");
/*
* 成员方法的反射应用
*/
//获取该类所有公共成员方法对象,包括继承的父类的公共方法
Method[] methods = aClass.getMethods();
System.out.println("所有公共方法展示:");
for (Method method : methods) {
System.out.println(method);
}
//获取指定的单个成员方法对象
Method show2 = aClass.getDeclaredMethod("show2",String.class,String.class);
// 注意:实体类该方法中有几个形参 此处就应该有几个xx.class
System.out.println("已找到指定成员方法对象,是:\t"+show2);
Parameter[] parameters1 = show2.getParameters();
System.out.println("这是show2方法对象的所有形参对象:");
for (Parameter parameter : parameters1) {
System.out.println(parameter);
}
Method show = aClass.getDeclaredMethod("show",int.class);
System.out.println("已找到指定成员方法对象2,是:\t"+show);
//用反射来执行学生类中的私有方法
Student student = new Student();
//如果该方法是私有的 即需要临时取消权限 反之不需要
if(show.getModifiers()!=1){
//获取该方法的常量字段值 —— public的常量字段值为1
show.setAccessible(true);//临时取消权限校验
Object result=show.invoke(student,44);
System.out.println("已运行,返回值为:"+result);
}else {
Object result=show.invoke(student,44);
System.out.println("已运行,返回值为:"+result);
}
}
}