Java官方对于反射的解释是这样的:
反射允许对封装类的字段、方法和构造函数的信息进行编程访问
也就是说Java的反射机制能够获取类的所有信息,包括任何访问权限的字段,方法和构造器,并且获取之后还能够对齐进行调用和访问。
下面将分别介绍Java反射机制获取类——Class,方法——Method,字段——Field,构造器——Constructor。
Class
用于描述字节码文件的。我们知道java文件被编译后会形成class文件,java文件中的每一个类都会形成一个class文件,而Class类就是用于描述class文件的。这个类有一些方法我们需要了解。
获取Class
我们有三种方式可以获取去某个类的字节码文件的对象。
- 利用Class类中的静态方法forName获取
方法名 作用 static Class<?> forName(String className) 根据类的全类名获取它的类信息 - 通过类名.class获取
- 通过对象.getClass()获取
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
//1. 利用Class类中的静态方法forName获取
Class clazz1 = Class.forName("java.util.HashMap");
//2. 通过类名.class获取
Class clazz2 = HashSet.class;
//3. 通过对象.getClass()获取
ArrayList<Integer> list = new ArrayList<>();
Class clazz3 = list.getClass();
System.out.println(clazz1);
System.out.println(clazz2);
System.out.println(clazz3);
}
}
运行结果如下:
获取某个类的Class对象之后进而可以利用它去获取类的构造器、方法、字段等信息。
获取Constructor
获取一个类的Class对象后我们可以利用这个Class对象的下面方法获取此类的各种构造器。
方法名 | 作用 |
---|---|
Constructor<?>[] getConstructors() | 获取本类全部的public修饰的构造器 |
Constructor getConstructor(Class<?>… parameterTypes) | 根据类型参数获取指定的public构造器 |
Constructor<?>[] getDeclaredConstructors() | 获取本类的任何构造器 |
Constructor getDeclaredConstructor(类<?>… parameterTypes) | 根据类型参数获取任何指定的构造器 |
这里我们使用两个方法来演示一下用法,另外两个同理。
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//获取指定类的Class对象
Class clazz = Class.forName("java.util.HashMap");
//利用Class对象获取构造器
Constructor constructor = clazz.getDeclaredConstructor(int.class);
Constructor[] constructors = clazz.getDeclaredConstructors();
//输出构造器对象
System.out.println("获取指定的任何构造器:" + constructor);
for(Constructor c : constructors){
System.out.println("获取所有构造器: " + c);
}
}
}
运行结果
获取了构造器对象之后,构造器对象可以干很多事,比如用它实例化对象,获取它的名字等
下面演示一下使用获取到的构造器对象实例化一个对象。
Class clazz = Class.forName("java.util.HashMap");
Constructor constructor = clazz.getDeclaredConstructor(int.class);
//利用获取到的构造器实例化对象
HashMap<String, String> map = (HashMap<String, String>) constructor.newInstance(12);
map.put("及时雨呼保义孝义黑三郎", "宋江");
map.put("玉麒麟", "卢俊义");
System.out.println(map);
获取Field
套路类似,先获取Class对象,再根据Class对象获取对象的字段。
方法名 | 作用 |
---|---|
Field getField(String name) | 根据字段名获取指定public修饰的字段 |
Field[] getFields() | 获取此类的所有本类的public修饰的字段 |
Field getDeclaredField(String name) | 根据字段名获取任何指定的字段 |
Field[] getDeclaredFields() | 获取所有本类的字段 |
这里我们仍然用两个方法演示,其他的用法同理。
//获取Class对象
Class clazz = Class.forName("java.lang.String");
//获取字段
Field[] fields = clazz.getDeclaredFields();
Field field = clazz.getDeclaredField("value");
//输出字段
System.out.println("获取指定的任何字段: " + field);
for(Field f : fields){
System.out.println("获取所有的字段: " + f);
}
我们获取字段之后可以获取、修改指定对象这个字段的值。但是如果这个字段在当前类里访问权限不够,需要使用field.setAccessible(true);暂时设置该字段可以访问。
Class clazz = Class.forName("java.lang.String");
Field field = clazz.getDeclaredField("value");
String str = "reflect";
//暂时取消此字段的权限校验
field.setAccessible(true);
//获取str对象的value属性并输出
char[] value = (char[]) field.get(str);
System.out.println(value);
//修改str的value属性
field.set(str, new char[]{'h', 'e', 'l', 'l', 'o'});
//再次获取str的value属性并输出
value = (char[]) field.get(str);
System.out.println(value);
输出结果
获取Method
方法仍然是跟前面套路类似,但是也有些不同,下面会做出讲解。
方法名 | 作用 |
---|---|
Method getMethod(String name, Class<?>… parameterTypes) | 根据方法的名字和形参列表获取指定public修饰的方法 |
Method[] getMethods() | 获取所有包括父类的public修饰的方法 |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 根据方法名和形参列表获取任何指定的方法 |
Method[] getDeclaredMethods() | 获取所有本类的方法 |
注意getMethods方法能获取父类的方法,这与前面的不同,前面的都是只能获取本类中的信息。
定义一个Student类,其中有四种访问权限的方法
下面演示对Student类中方法的获取
//获取Class对象
Class clazz = Class.forName("Student");
//用三种方式分别获取Method
Method method = clazz.getDeclaredMethod("sleep", int.class);
Method[] methods1 = clazz.getDeclaredMethods();
Method[] methods2 = clazz.getMethods();
//分别输出method
System.out.println("获取指定的方法:" + method);
for(Method m : methods1){
System.out.println("获取本类中所有的方法" + m);
}
for(Method m : methods2){
System.out.println("获取父类和本类中所有的方法" + m);
}
输出结果:
我们获取到方法后可以利用Method的invoke方法执行指定对象的这个方法,参数为指定的对象和需要传入的参数。如果权限不允许那就利用setAccessible(true)暂时取消权限校验
Class clazz = Class.forName("Student");
Method method = clazz.getDeclaredMethod("sleep", int.class);
Student s = new Student(18, "小明");
method.setAccessible(true);
method.invoke(s, 22);
执行结果: