目录
(一)反射机制定义和用途
(1)定义
反射是一种允许程序在运行时修改程序自身的行为和结构的机制,也就是说反射机制允许程序在运行时动态地获取对象的状态信息并对其进行操作。
(2)用途
反射机制最主要的用途就是用来开发框架,比如spring框架中的Bean的创建管理,spring的动态代理,都用到了反射的机制
(二)Class类
想要通过反射机制获取到类的信息,就需要拥有类的对象,就像人照镜子,人想通过通过镜子获取到自己的样貌信息,就需要自己站在镜子前让镜子获取到你的信息,镜子才能通过反射把你的样貌呈现给你,那么这个类对象从哪来呢?
当jvm读取到一个类或者接口的时候,会为这个类或者接口创建一个Class类型的实例,并关联起来,用来保存类和接口的所有信息
(1)获取类的Class类型的实例
下面介绍三种获取类的Class类型实例的方式
1.1通过Class的静态方法forname()获取对象
package reflect;
//人的共有特性
class People {
public String name;
}
//一个学生类
class Student extends People {
public int score;
private int grade;
}
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException {
//通过forname静态方法获取Class
Class class1 = Class.forName("reflect.Student");//里面是类的全限定类名
System.out.println(class1);
}
}
1.2直接通过类名.class得到,更为安全,性能更高
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException {
//通过类名获取Class
Class class2 = Student.class;
System.out.println(class2);
}
}
1.3通过类对象的getclass方法获取
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException {
Student student = new Student();
Class class3 = student.getClass();
System.out.println(class3);
}
}
(三)获取字段
如上我们获取到了Class类型的实例,那么下一步就是获取相关的字段信息
如下是Class类中相关获取字段操作方法
返回类型 | 方法 | 作用 |
---|---|---|
Field | getField(name) | 根据字段名获取某个public修饰的Field(包括父类) |
Field | getDeclaredField(name) | 根据字段名获取任意修饰限定符的Field(不包括父类) |
Field[] | getFields() | 获取所有public修饰的Field(包括父类) |
Field[] | getDeclaredFields() | 获取所有任意修饰限定符的Field(不包括父类) |
(1)获取字段
注:所有用到的实体类在第一个代码段中
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException {
Student student = new Student();
Class class3 = student.getClass();
//获取字段
Field scoreField = class3.getField("score");
System.out.println(scoreField);
System.out.println(class3.getField("name"));
System.out.println(class3.getDeclaredField("grade"));
System.out.println(class3.getFields());
System.out.println(class3.getDeclaredFields());
}
}
运行结果
public int reflect.Student.score
public java.lang.String reflect.People.name
private int reflect.Student.grade
[Ljava.lang.reflect.Field;@1540e19d
[Ljava.lang.reflect.Field;@677327b6
(2)获取字段信息
当获取到一个字段时返回的是一个Field类型的对象,这个对象包含了字段的所有信息
以下是Field类中的常用方法
方法名 | 作用 |
getName() | 返回字段的名称 |
getType() | 返回字段的类型,也是一个Class类实例 |
getModifiers() | 返回一个int,不同的值代表不同的修饰符 |
简单用法示例
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException {
Student student = new Student();
Class class3 = student.getClass();
//获取字段
Field scoreField = class3.getField("score");
System.out.println(scoreField.getName());//获取名称
System.out.println(scoreField.getType());//获取类型
// System.out.println(scoreField.get(student));
int m = scoreField.getModifiers();//返回修饰符
System.out.println(Modifier.isFinal(m));
System.out.println(Modifier.isPublic(m));
System.out.println(Modifier.isPrivate(m));
System.out.println(Modifier.isStatic(m));
}
}
(3)获取字段值
如下代码中,先获取到Field对象,再根据Field对象调用get()方法获取到字段的值,如果是私有字段,在获取字段的值之前需要将字段设置为可访问
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException {
Student student = new Student(1);
student.name = "zhangsan";
student.score = 90;
Class class3 = student.getClass();
//获取字段
Field scoreField = class3.getField("score");
Object obj = scoreField.get(student);//获取score字段的值
Field gradeField = class3.getDeclaredField("grade");
gradeField.setAccessible(true);//如果是私有字段,需要设置可访问
Object obj2 = gradeField.get(student);//获取grade字段的值
System.out.println(obj);
System.out.println(obj2);
}
}
(4)修改字段值
如下代码使用set()方法将name字段的值修改为lisi,同样要修改private字段的值之前,需要将字段设置为可访问
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException {
Student student = new Student(1);
student.name = "zhangsan";//将字段名字设置为zhangsan
//获取Class实例
Class class3 = student.getClass();
//获取字段
Field scoreField = class3.getField("name");
scoreField.set(student, "lisi");//将字段名字修改为lisi
System.out.println(student.name);
}
}
(四)获取方法
我们能够通过Class实例获取字段信息,同样也能通过Class实例获取方法信息
以下代码为需要用到的实体类
class People {
public String name;
public String getName(){
return name;
}
}
//一个学生类
class Student extends People {
public int score;
private int grade;
public Student(int score, int grade) {
this.score = score;
this.grade = grade;
}
public static int getGrade(int year){
return 2019;
}
public int getScore(String type){
return 90;
}
}
(1)获取方法操作
如下是Class类中获取方法的操作方法
返回类型 | 方法 | 作用 |
---|---|---|
Method | getMethod(String name, Class<?>... parameterTypes) | 根据方法名及其参数返回public的Method对象(包括父类) |
Method | getDeclaredMethod(String name, Class<?>... parameterTypes) | 根据方法名和参数返回某一个Mehod对象(不包括父类) |
Method[] | getMethods() | 获取所有public的Method(包括父类) |
Method[] | getDeclaredMethods() | 获取当前类所有Method(不包括父类) |
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException, NoSuchMethodException {
Student student = new Student();
Class<?> clazz = Class.forName("reflect.Student");
System.out.println(clazz.getMethod("getScore", String.class));//获取getScore方法
System.out.println(clazz.getDeclaredMethod("getGrade", int.class));
System.out.println(clazz.getMethods());
System.out.println(clazz.getDeclaredMethods());
}
}
上述代码先获取Student的实例,再根据不同方法,获得不同的方法,并返回一个Method对象
运行结果
public int reflect.Student.getScore(java.lang.String)
private int reflect.Student.getGrade(int)
[Ljava.lang.reflect.Method;@1540e19d
[Ljava.lang.reflect.Method;@677327b6
(2)获取方法信息操作
一个Mehod对象包括许多信息,以下是获取方法信息的常用操作方法
返回类型 | 方法名 | 作用 |
---|---|---|
String | getName() | 获取方法名 |
Class | getReturnType() | 获取方法的返回类型 |
Class[] | getParameterTypes() | 获取参数的类型 |
int | getModifiers() | 获取方法的访问权限,不同数值代表不同访问权限 |
简单用法示例
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException, NoSuchMethodException {
Student student = new Student();
Class<?> clazz = Class.forName("reflect.Student");
Method getScoreMethod = clazz.getMethod("getScore", String.class);
System.out.println(getScoreMethod.getName());//获取方法名
System.out.println(getScoreMethod.getReturnType());//获取返回值类型
System.out.println(getScoreMethod.getModifiers());//获取访问权限
System.out.println(getScoreMethod.getParameterTypes());//获取返回值类型
}
}
(3)调用方法
下列代码为首先获取Student实例,再获取其方法,然后通过invoke方法调用方法,其中,第一个参数是调用的哪个实例的方法,第二个参数,是被调用方法所需要的参数
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Student student = new Student();
Class<?> clazz = Class.forName("reflect.Student");
Method getScore = clazz.getMethod("getScore", String.class);//获取方法
System.out.println(getScore.invoke(student, "math"));//调用方法并打印
}
}
若是调用静态方法,由于无需指定实例对象,所以invoke方法传入的第一个参数永远为null,例如
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Student student = new Student();
Class<?> clazz = Class.forName("reflect.Student");
Method getGrade = clazz.getMethod("getGrade", int.class);//获取静态方法
getGrade.invoke(null, 2019);//调用静态方法,传入null
}
}
(五)获取构造方法
如果我们要用反射来创建一个新的实例,可以调用Class.newInstance方法,但是这个方法只能调用无参的构造方法,不能直接调用有参的构造方法,所以,Java的反射API提供了Constructor
对象,它包含一个构造方法的所有信息,可以创建一个实例。Constructor
对象和Method非常类似,不同之处仅在于它是一个构造方法,并且,调用结果总是返回实例:
返回类型 | 方法名 | 作用 |
---|---|---|
Constructor | getConstructor(Class...) | 获取某个public的Constructor |
Constructor | getDeclaredConstructor(Class..) | 获取某个Constructor |
Constructor[] | getConstructors() | 获取所有public的Constructor |
Constructor[] | getDeclaredConstructors() | 获取所有的Constructor |
简单使用示例
public class Main {
public static void main(String[] args) throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
Student student = new Student(10,20);
Class<?> clazz = Class.forName("reflect.Student");
Constructor constructor = clazz.getConstructor(int.class,int.class);//获取有参构造器
Student student1 = (Student)constructor.newInstance(90,2019);//通过反射创建有参实例对象
}
}
注意Constructor
总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。
调用非public
的Constructor
时,必须首先通过setAccessible(true)
设置允许访问。setAccessible(true)
可能会失败。