Java反射
反射的定义:
Java反射是指在运行时动态地获取类的信息,并可以通过该信息来操作类或对象。通过反射,我们可以在运行时获取类的字段、方法、构造函数等信息,并能够动态地创建对象、调用方法、访问和修改字段的值。
反射相关的类:
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量/字段 |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
获得反射的三种方式:
一、Class.forName("全类名") 【最常用】
//1、Class.forName("全类名")
Class clazz1= Class.forName("org.example.Student");
System.out.println(clazz1);
二、类名.class
//2、类名.class
Class clazz2 = Student.class;
System.out.println(clazz2);
System.out.println(clazz2 == clazz1);
三、对象.getClass();
//3、对象.getClass();
Student s = new Student();
Class clazz3 = s.getClass();
System.out.println(clazz3);
上面显示的两个ture证明,该三种方式获得的Class字节码文件对象是一样的。
反射获取构造方法
Class类中用于获取构造方法的方法:
Constructor<?>[]getConstructors():返回所有公共构造方法对象的数组
Constructor<?>[]getDeclaredConstructors():返回所有构造方法对象的数组
Constructor<T>getConstructor(Class<?>...parameterTypes):返回单个公共构造方法对象
Constructor<T>getDelcaredConstructor(Class<?>...parameterTypes):返回单个构造方法对象
Constructor类中用于创建对象的方法:
T newInstance(Obeject... initargs):根据指定的构造方法创建对象
setAccessible(boolean flag):设置为true,表示取消访问检查
Demo举例
package org.example.reflect;
public class Student {
private String name;
private int age;
public Student(){
}
public Student(String name) {
this.name = name;
}
private 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;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
package org.example.reflect;
import java.lang.reflect.Constructor;
public class myReflect {
/** Class类中用于获取构造方法的方法
Constructor<?>[]getConstructors():返回所有公共构造方法对象的数组
Constructor<?>[]getDeclaredConstructors():返回所有构造方法对象的数组
Constructor<T>getConstructor(Class<?>...parameterTypes):返回单个公共构造方法对象
Constructor<T>getDelcaredConstructor(Class<?>...parameterTypes):返回单个构造方法对象
Constructor类中用于创建对象的方法
T newInstance(Obeject... initargs):根据指定的构造方法创建对象
setAccessible(boolean flag):设置为true,表示取消访问检查
**/
public static void main(String[] args) throws ClassNotFoundException {
//1、获取class字节码文件的对象
Class clazz = Class.forName("org.example.reflect.Student");
//2、获取构造方法
Constructor[] cons = clazz.getConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
}
}
返回所有公共构造方法对象的数组
获取到两个public构造方法,一个空参,一个参数是String类型
返回所有构造方法对象数组
Constructor[] cons2 = clazz.getDeclaredConstructors();
for (Constructor con : cons2) {
System.out.println(con);
获取单个(包括公共)构造方法
//Constructor<T>getDelcaredConstructor(Class<?>...parameterTypes):返回单个构造方法对象
Constructor con1 = clazz.getDeclaredConstructor();
System.out.println(con1);
单个参数,传入String类型的字节码文件:String.class
//Constructor<T>getDelcaredConstructor(Class<?>...parameterTypes):返回单个构造方法对象
Constructor con1 = clazz.getDeclaredConstructor();
System.out.println(con1);
Constructor con2 = clazz.getDeclaredConstructor(String.class);
System.out.println(con2);
Constructor con3 = clazz.getDeclaredConstructor(int.class);
System.out.println(con3);
Constructor con4 = clazz.getDeclaredConstructor(String.class,int.class);
System.out.println(con4);
(参数要和构造方法里面的参数保持一致)
单个参数,传入String类型的字节码文件:String.class
两个参数,传入int.class和String.class
构造方法之后
我们获得到构造方法之后我们就能做下面这些事情
一、获取修饰符
Constructor con4 = clazz.getDeclaredConstructor(String.class,int.class);
System.out.println(con4);
//获取修饰符
int modifiers = con4.getModifiers();
System.out.println(modifiers);
打印出了一个2
这里为什么是2呢?
在JDK api文档中的《常量字段值》,其中指出private的字段值为2
二、获取名字。
获取对象的名称
String name = con4.getName();
System.out.println(name);
三、获取形参。
//获取参数
Parameter[] parameters = con4.getParameters();
for (Parameter parameter : parameters) {
System.out.println(parameter);
}
四、创建对象。
con4.Accessible(true);
Rsecret = (Student) con4.newInstance("Rsecret",16);
System.out.println(Rsecret);
直接创建对象是会报错的,原因是:(私有方法)
于是需要再调用一个方法 setAccessible()
这样对象就可以创建成功了
反射获取成员变量
Class类中用于获取成员变量方法:
Field[] getFields():返回所有公共成员变量对象的数组
Field[] getDeclaredFields():返回所有成语变量对象的数组
Field[] getField(String name):返回单个公共成员变量对象
Field[] getDeclaredField(String name):返回单个成员变量对象
Field类中用于创建对象的方法
void set(Object obj,Object value):赋值
Object get(Object obj):获取值
Demo举例
package org.example.reflect1;
public class Student {
private String name;
private int age;
public String gender;
public Student(){
}
public Student(String name, int age, String gender){
this.name=name;
this.age=age;
this.gender=gender;
}
/**
* 获取
* @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;
}
/**
* 获取
* @return gender
*/
public String getGender() {
return gender;
}
/**
* 设置
* @param gender
*/
public void setGender(String gender) {
this.gender = gender;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + ", gender = " + gender + "}";
}
}
一、获取class字节码文件对象
Class clazz = Class.forName("org.example.reflect1.Student");
二、获取成员变量
获取所有成员变量
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
获取全部的成员变量 需要加Declared
eg:
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
获取单个成员变量
Field gender = clazz.getDeclaredField("name");
System.out.println(gender);
//这里的gender变量名可以换做name更好看
获取权限修饰符
int modifiers = name.getModifiers();
System.out.println(modifiers);
获取成员变量的名字和数据类型
//获取成员变量的名字
String n =name.getName();
System.out.println(n);
//获取成员变量的数据类型
Class<?> type = name.getType();
System.out.println(type);
获取成员变量记录的值
这里报错的原因就是:name和age都是private私有的,所以我们需要在上面加
name.setAccessible(true);
//获取成员变量记录的值
Student s = new Student("Rsecret",15,"男");
name.setAccessible(true);
String value = (String) name.get(s);
System.out.println(value);
如图:输出了Rsecret
修改对象里面记录的值
//修改对象里面记录的值
name.set(s,"2Rsecret");
System.out.println(s);
反射获取成员方法
Class类中用于获取成员方法的方法:
Method[] getMethods():返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name,Class<?>..parameterTypes):返回个人公共成员方法对象
Method getDeclaredMethod(String name,Class<?>...parameterTypes):返回单个成员方法对象
Method类中用于创建对象的方法:
Object invoke(Object obj,Object...args):运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
Demo举例
package org.example.reflect2;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student(String name) {
this.name = name;
}
/**
* 获取
*
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
*
* @return age
*/
public int getAge() {
return age;
}
public int getAge(int age) {
this.age = age;
return age;
}
public void sleep() {
System.out.println("睡觉");
}
private void eat(String something) {
System.out.println("在吃" + something);
}
public String toString() {
return "Student{name = " + name + "}";
}
}
获取class字节码文件对象
Class clazz = Class.forName("org.example.reflect2.Student");
获取所有的方法对象
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
由于没有使用Declared,因此打印出来的都是public,
为什么那么多public呢? 因为也包括父类的
加上Declared,不能获取父类的了 ,但是可以获取自己所有的方法对象了
获取指定的单一方法
Method m = clazz.getDeclaredMethod("eat", String.class);
System.out.println(m);
获取方法的修饰符
Method m = clazz.getDeclaredMethod("eat", String.class);
System.out.println(m);
int modifiers = m.getModifiers();
System.out.println(modifiers);
私有属性,因此打印出的2
获取方法的名字
String name = m.getName();
System.out.println(name);
获取方法的形参
Parameter[] parameters = m.getParamet
for (Parameter parameter : parameters
System.out.println(parameter);
获取方法的返回值
获取方法抛出的异常
Class<?>[] exceptionTypes = m.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
System.out.println(exceptionType);
}
方法运行
Method类中用于创建对象的方法:
Object invoke(Object obj,Object...args):运行方法
参数一:用obj对象调用该方法
参数二:调用方法的传递的参数(如果没有就不写)
返回值:方法的返回值(如果没有就不写)
Student s = new Student();
m.setAccessible(true);
Object Rsecret = m.invoke(s, "Rsecret","苹果");
获取返回值
将上面的 Rsecret,打印出来就行了
并且注意eat方法的返回值不要再设置void了,因为我们返回的是字符串,所以要设置为String类型