反射
反射允许对封装类的字段,方法和构造函数的信息进行编程访问
获取
获取class对象 Class
构造方法 Constructor
字段(成员变量) Field
成员方法 Method
使用
获取class对象的三种方式
1.(源代码阶段)最常用
Class.forName("全类名");
2.(加载阶段)当做参数进行传递
类名.class
3.(运行阶段)已经有了这个类的对象时,才可以使用
对象.getClass();
示例代码(Student自行创建):
public class reflexDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
//第一种方式
Class c1 = Class.forName("itemFanShe.Student");
//第二种方式
Class c2 = Student.class;
//第三种方式
Student student = new Student();
Class c3 = student.getClass();
//对比
System.out.println(c1 == c2); //t
System.out.println(c2 == c3); //t
}
}
利用反射获取构造器
在 java.lang.Class 类中,用于获取构造方法的方法有以下几种:
getConstructor(Class<?>... parameterTypes):
这个方法用于获取指定参数类型的公共构造方法(即声明为 public 的构造方法)。 参数 parameterTypes 是一个可变参数,用于指定构造方法的参数类型。 如果找不到指定参数类型的公共构造方法,会抛出 NoSuchMethodException 异常。
getDeclaredConstructor(Class<?>... parameterTypes):
这个方法用于获取指定参数类型的所有构造方法(包括公共、保护、默认(包)访问以及私有构造方法),但不包括父类的构造方法。 参数 parameterTypes 是一个可变参数,用于指定构造方法的参数类型。 如果找不到指定参数类型的构造方法,会抛出 NoSuchMethodException 异常。 getConstructors():
这个方法用于获取类的所有公共构造方法。 返回一个包含 Constructor 对象的数组,每个 Constructor 对象对应一个公共构造方法。
getDeclaredConstructors():
这个方法用于获取类的所有构造方法(包括公共、保护、默认(包)访问以及私有构造方法),但不包括父类的构造方法。 返回一个包含 Constructor 对象的数组,每个 Constructor 对象对应一个构造方法。
public class reflexDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
/*
getConstructor(Class<?>... parameterTypes)
getDeclaredConstructor(Class<?>... parameterTypes)
getConstructors()
getDeclaredConstructors()
*/
//获取class字节码文件对象
Class c1 = Class.forName("itemFanShe.Student");
//获取构造方法
System.out.println("==========getConstructors==========");
Constructor[] cons1 = c1.getConstructors();
for (Constructor con : cons1) {
System.out.println(con);
}
System.out.println("==========getDeclaredConstructors==========");
Constructor[] cons2 = c1.getDeclaredConstructors();
for (Constructor con : cons2) {
System.out.println(con);
}
System.out.println("==========getDeclaredConstructor==========");
Constructor con1 = c1.getDeclaredConstructor(); //获取空参构造
System.out.println(con1);
Constructor con2 = c1.getDeclaredConstructor(String.class); //获取String类型有参构造器
System.out.println(con2);
Constructor con3 = c1.getDeclaredConstructor(int.class); //获取int类型有参构造器
System.out.println(con3);
Constructor con4 = c1.getDeclaredConstructor(int.class,String.class); //同时获取两个
System.out.println(con4);
int modifiers = con4.getModifiers(); //获取构造方法的权限修饰符 以整数的方式进行返回
System.out.println(modifiers);
int parameterCount = con4.getParameterCount(); //获取构造器参数的个数
System.out.println(parameterCount);
Class[] parameterTypes = con4.getParameterTypes(); //获取参数的类型
for (Class parameterType : parameterTypes) {
System.out.println(parameterType);
}
//暴力反射:表示临时取消权限校验
con2.setAccessible(true);
Student stu = (Student) con2.newInstance("牛马"); //私有构造器
System.out.println(stu); //Student{age=0, name='牛马'}
System.out.println("==========getConstructor==========");
Constructor con5 = c1.getConstructor(int.class);
System.out.println(con5);
}
}
Student类
/**
* @author hyk~
*/
public class Student {
private int age;
public String name;
private String gender;
public char sex;
public Student(int age, String name) {
this.age = age;
this.name = name;
}
protected Student() {
}
private Student(String name) {
this.name = name;
}
public Student(int age){
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
利用反射获取成员变量
-
getField(String name)
:- 这个方法用于获取指定名称的公共成员变量(即声明为
public
的成员变量)。 - 参数
name
是要获取的成员变量的名称。 - 如果找不到指定名称的公共成员变量,会抛出
NoSuchFieldException
异常。
- 这个方法用于获取指定名称的公共成员变量(即声明为
-
getDeclaredField(String name)
:- 这个方法用于获取指定名称的所有成员变量(包括公共、保护、默认(包)访问以及私有成员变量),但不包括父类的成员变量。
- 参数
name
是要获取的成员变量的名称。 - 如果找不到指定名称的成员变量,会抛出
NoSuchFieldException
异常。
-
getFields()
:- 这个方法用于获取类的所有公共成员变量。
- 返回一个包含
Field
对象的数组,每个Field
对象对应一个公共成员变量。
-
getDeclaredFields()
:- 这个方法用于获取类的所有成员变量(包括公共、保护、默认(包)访问以及私有成员变量),但不包括父类的成员变量。
- 返回一个包含
Field
对象的数组,每个Field
对象对应一个成员变量。
/**
* @author hyk~
*/
public class reflexDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
/*
getField(String name)
getDeclaredField(String name)
getFields()
getDeclaredFields()
*/
//获取class字节码文件对象
Class c1 = Class.forName("itemFanShe.Student");
System.out.println("=============获取公共的成员变量=============");
Field[] fields = c1.getFields(); //获取所有公共的成员变量
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=============获取所有成员变量=============");
Field[] declaredFields = c1.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("=============获取指定名称的公共成员变量=============");
Field field = c1.getField("name"); //括号里面写变量的名字 无法获取私有
System.out.println(field);
System.out.println("=============获取指定名称的所有成员变量=============");
Field declaredField = c1.getDeclaredField("gender");
System.out.println(declaredField);
//获取权限修饰符
int modifiers = declaredField.getModifiers();
System.out.println(modifiers); //2
//获取成员变量名
String name = declaredField.getName();
System.out.println(name); //gender
//获取成员变量的数据类型
Class<?> type = declaredField.getType();
System.out.println(type); //class java.lang.String
}
}
利用反射获取成员方法
-
getMethod(String name, Class<?>... parameterTypes)
:- 这个方法用于获取指定名称和参数类型的公共方法(即声明为
public
的方法)。 - 参数
name
是要获取的方法的名称,parameterTypes
是一个可变参数,用于指定方法的参数类型。 - 如果找不到指定名称和参数类型的公共方法,会抛出
NoSuchMethodException
异常。
- 这个方法用于获取指定名称和参数类型的公共方法(即声明为
-
getDeclaredMethod(String name, Class<?>... parameterTypes)
:- 这个方法用于获取指定名称和参数类型的所有方法(包括公共、保护、默认(包)访问以及私有方法),但不包括父类的方法。
- 参数
name
是要获取的方法的名称,parameterTypes
是一个可变参数,用于指定方法的参数类型。 - 如果找不到指定名称和参数类型的方法,会抛出
NoSuchMethodException
异常。
-
getMethods()
:- 这个方法用于获取类的所有公共方法。
- 返回一个包含
Method
对象的数组,每个Method
对象对应一个公共方法。
-
getDeclaredMethods()
:- 这个方法用于获取类的所有方法(包括公共、保护、默认(包)访问以及私有方法),但不包括父类的方法。
- 返回一个包含
Method
对象的数组,每个Method
对象对应一个方法。
public class reflexDemo4 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class c1 = Class.forName("itemFanShe.Student");
System.out.println("获取里面所有的方法对象(包含父类中所有的公共方法)");
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("获取里面所有的方法对象(不能获取父类的,但可以获取本类中私有的方法)");
Method[] declaredMethods = c1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("获取指定的单一方法");
Method declaredmethod = c1.getDeclaredMethod("eat",String.class);
System.out.println(declaredmethod);
//获取方法的修饰符
int modifiers = declaredmethod.getModifiers();
System.out.println(modifiers);
//获取方法的名字
System.out.println(declaredmethod.getName());
//获取方法的形参
Parameter[] parameters = declaredmethod.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
//获取方法抛出的异常
Class[] exceptionTypes = declaredmethod.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
//方法运行
Student student = new Student();
declaredmethod.setAccessible(true); //临时取消权限
/*
student:表示方法的调用者
汉堡包:表示在调用方法的时候传递的实际参数
*/
String result = (String) declaredmethod.invoke(student,"汉堡包");
//获取方法的返回值
System.out.println(result);
}
}
反射的作用
反射是一种强大的编程技术,它使得程序在运行时能够获取和操作类的信息,包括类的属性、方法、构造函数等。反射的主要作用包括以下几个方面:
-
动态加载类: 可以通过反射动态加载类,而不需要在编译时知道类的名称。这使得程序可以根据需要在运行时加载和使用类,从而实现更灵活的架构和功能。
-
动态创建对象: 反射可以在运行时动态地创建类的对象实例。通过调用类的构造函数,可以在程序运行时根据不同的条件创建不同类型的对象。
-
动态调用方法: 反射可以在运行时动态地调用类的方法。程序可以根据需要调用特定名称和参数的方法,而不需要提前在代码中硬编码方法调用。
-
动态访问和修改属性: 反射可以在运行时动态地访问和修改类的属性。程序可以通过反射获取类的属性并读取或修改其值,从而实现灵活的属性操作。
-
实现通用框架和工具: 反射可以用于实现通用的框架和工具,例如依赖注入、对象映射、动态代理等。通过反射,可以编写通用的代码来处理各种类型的对象和类。
-
支持编写插件化程序: 反射可以用于编写插件化程序,使得程序可以在运行时加载和使用插件。通过反射,程序可以动态地加载插件类,并调用其方法和访问其属性。
总的来说,反射使得程序可以在运行时动态地获取和操作类的信息,从而实现更灵活、更动态的编程。它在很多领域都有广泛的应用,包括框架开发、ORM(对象关系映射)、测试框架、插件化开发等。但是需要注意的是,过度使用反射可能会导致性能问题和代码可读性降低,因此在使用反射时需要权衡利弊。
反射练习
1.对于任意一个对象,都可以把对象所有的字段名和值,保存到文件中去
测试类
public class reflexTest {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException {
Student1 s1 = new Student1("小明",19,'男',100);
Teacher t1 = new Teacher("牛老师",10000);
saveObject(s1);
}
public static void saveObject(Object obj) throws ClassNotFoundException, IOException, IllegalAccessException {
//获取字节码文件对象
Class c1 = obj.getClass();
//创建IO流
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\ideaProject\\study\\AdvancedJava3\\src\\itemFanShe\\Test\\a.txt"));
//获取所有的成员变量
Field[] declaredFields = c1.getDeclaredFields();
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
//获取成员变量的名字
String name = declaredField.getName();
//获取成员变量的值
Object value = declaredField.get(obj);
//写出数据
bw.write(name+"="+value);
bw.newLine(); //换行
}
bw.close();
}
}
学生类
public class Student1 {
private String name;
private int age;
private char sex;
private int result;
public Student1(String name, int age, char sex, int result) {
this.name = name;
this.age = age;
this.sex = sex;
this.result = result;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getResult() {
return result;
}
public void setResult(int result) {
this.result = result;
}
@Override
public String toString() {
return "Student1{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", result='" + result + '\'' +
'}';
}
}
老师类
/**
* @author hyk~
*/
public class Teacher {
private String name;
private int salary;
public Teacher(String name, int salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", salary=" + salary +
'}';
}
}
2.反射可以跟配置文件结合的方式,动态的创建对象,并调用方法
配置文件 config.properties
,其中包含了类名和方法名的配置信息
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectConfigExample {
public static void main(String[] args) {
try {
// 加载配置文件
Properties properties = new Properties();
FileInputStream fis = new FileInputStream("config.properties");
properties.load(fis);
fis.close();
// 从配置文件中获取类名和方法名
String className = properties.getProperty("class");
String methodName = properties.getProperty("method");
// 使用反射动态加载类
Class<?> clazz = Class.forName(className);
// 创建类的实例
Object obj = clazz.getDeclaredConstructor().newInstance();
// 获取方法对象
Method method = clazz.getMethod(methodName);
// 调用方法
method.invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
}
config.properties
文件内容如下:
class=com.example.MyClass
method=hello
创建一个 com.example.MyClass
类,其中包含一个名为 hello
的方法
package com.example;
public class MyClass {
public void hello() {
System.out.println("Hello, world!");
}
}
运行 ReflectConfigExample
类时,它会读取配置文件中的类名和方法名,然后使用反射动态地加载类并调用方法。这样就实现了通过配置文件动态地创建对象并调用方法的功能。