15. 反射
15.1 反射的简介
反射(Reflect),在Java中,可以在程序运⾏的过程中,动态的获取类、获取类中的成员(属性、⽅法、构造⽅法),并进⾏访问。这⾥的动态获取,可以通过⼀个字符串来进⾏获取。这样的机制,叫做反射。
15.2 Class的描述
15.2.1 Class类的简介
Class是Java中的⼀个类,⽤来描述Java中的类。对⼀个类编译之后的字节码⽂件进⾏的描述。这个类也是继承⾃Object类,主要来描述⼀个类中有什么成员,包括构造⽅法、属性、⽅法。
15.2.2 Class对象的获取
1、Object类中的getClass()⽅法。通过⼀个对象,获取⼀个⽤来描述这个对象对应的类的Class类的对象。
Dog dog = new Dog();
Class cls = dog.getClass();
2、使⽤类.class的⽅式获取。
Class cls = Dog.class;
3、Class.forName(String className)(最常用)
由于反射,会强调动态性,类的获取、属性的获取、⽅法的获取、构造⽅法的获取,都是要通过⼀个字符串(必须是全限定名,从对上层包开始)进⾏获取的,我们甚⾄可以将⼀个需要加载的类名写到⼀个配置⽂件中,在程序中读取这个配置⽂件中的数据,加载不同的类。这样⼀来,当需要进⾏不同的类加载的时候,直接改这个配置⽂件即可,其他程序不⽤修改。再例如,可以获取某些属性的名字,通过反射获取到属性,并进⾏赋值。因此,上述的两种Class对象的获取都不够动态,我们⽤来获取类,更多的使⽤的是这个⽅法。
异常描述:ClassNotFoundException: 字⾯意思,没有找到这个类,可能是类名写错了
try {
Class cls = Class.forName("com.qf.aClass.Dog");
System.out.println(cls);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
15.3 实例化对象
这⾥所谓的“实例化对象”,是通过反射的形式来实例化的。并不是之前的通过new的⽅式实例化。这⾥,我们只需要知道⼀个类的名字字符串即可以完成对象的实例化了,做到了“动态”。
15.3.1 newInstance()
在Class类中,有⼀个⽅法 newInstance() ,这个⽅法的作⽤,就是去实例化⼀个类的对象。在这个
⽅法中,有⼏个异常:
- IllegalAccessException 访问权限不够造成的异常。
- InstantiationException 类中没有⽆参构造构成的异常。
try {
// 1、获取 Class 对象
Class cls = Class.forName("com.qf.bInstance.Dog");
// 2、newInstance
Object obj = cls.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
15.3.2 使⽤指定的构造⽅法实例化
在上述⽅法中,只能通过⽆参构造进⾏对象的实例化,⽐较单⼀;并且,如果这个⽆参构造的权限不
⾜,或者没有这个构造⽅法,会导致异常的出现,⽆法实例化对象。
可也通过反射的⽅式,获取到这个构造⽅法,再使⽤这个构造⽅法实例化对象出来。
- getConstructor(Class<?>… args)
- 获取⼀个类中的指定参数的构造⽅法,这个只能获取到访问权限⾜够的构造⽅法
- getDecarledConstructor(Class<?>… args)
- 获取⼀个类中的指定参数的构造⽅法,这个能够获取每⼀种访问权限的构造⽅法
- getConstructors()
- 获取⼀个类中所有的构造⽅法(只能获取能看得到的构造⽅法,访问权限⾜够的构造⽅法)
- getDecarledConstructors()
- 获取⼀个类中所有的构造⽅法(包含所有权限的构造⽅法)
常⻅的异常:
- NoSuchMethodException : 获取的构造⽅法,在类中不存在
- IllegalArgumentException : 在实例化对象的时候的参数不对
- IllegalAccessException : 要实例化对象使⽤的构造⽅法访问权限不够
构造方法案例:
/**
* 使用反射进行对象的实例化
* ClassNotFoundException : 无法通过类名字符串找到指定的类(类名写错了)
*
* IllegalAccessException : 常发生于访问权限不足
* InstantiationException : 常发生于没有无参构造的情况下,通过默认的无参构造进行实例化
*
* NoSuchMethodException : 使用 getConstructor 方法获取指定的构造方法的时候,没有找到指定的构造方法(参数有误,或者访问权限不够)
* IllegalArgumentException : 形参和实参不匹配
*/
public class ConstructorDemo {
public static void main(String[] args) {
try {
// 1. 获取用来描述这个类的 Class 对象
Class<?> cls = Class.forName("day30.reflect.ConstructorDemo$Person");
// 2. 实例化对象(只能通过类中的无参构造方法进行实例化)
// Object o = cls.newInstance();
// 3. 通过反射获取构造方法,使用构造方法进行对象的实例化
// 3.1. 通过指定的参数, 获取指定的构造方法, 使用Class<?>...形式描述所有的参数
// Constructor<?> constructor1 = cls.getConstructor(String.class, int.class);
// 通过指定的构造方法,实例化对象。在实例化的时候,实参就是构造方法中需要的实参。
// Object o = constructor1.newInstance("xiaoming", 2);
// 3.2. 如果需要获取的构造方法,访问权限不足,需要使用getDeclaredConstructor
// Constructor<?> constructor = cls.getDeclaredConstructor(String.class, int.class);
// 设置一个属性、方法、构造方法,在进行访问的时候权限校验
// setAccessible(boolean flag)
// 如果参数flag是true: 表示在进行成员访问的时候,跳过权限校验。即无论是什么权限的,都可以访问
// 如果参数flag是false: 表示在进行成员访问的时候,需要进行权限校验。如果权限足够,可以访问;如果权限不够,无法访问
// constructor.setAccessible(true);
// Object xiaoming = constructor.newInstance("xiaoming", 2);
// 3.3. 获取所有的构造方法(只能够获取访问权限足够的构造方法)
// Constructor<?>[] constructors = cls.getConstructors();
// for (Constructor<?> constructor : constructors) {
// System.out.println(constructor);
// }
// 3.4. 获取所有的构造方法(所有权限的所有构造方法)
// Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
// for (Constructor<?> declaredConstructor : declaredConstructors) {
// System.out.println(declaredConstructor);
// }
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private static class Person {
String name;
int age;
private Person(String name, int age) {
System.out.println("一个Person对象被实例化了");
}
}
}
15.4 setAccessible
public void setAccessible(boolean flag);
设置是否可访问,但是,这个设置的意义和字⾯理解不⼀样!
参数:boolean类型的变量,代表在进⾏访问的时候,是否需要忽略权限校验。true: 代表访问的时候,不进⾏权限的校验,任何的权限都可以访问。false: 代表访问的时候进⾏权限的校验,如果访问权限⾜够,就可以访问;访问权限不够,不能访问。
15.5 属性的访问
15.5.1 属性的获取
在反射中,使⽤ Filed 描述⼀个属性。可以使⽤如下⽅法,对属性进⾏获取操作
- getField(String fieldName) : 只能获取访问权限⾜够的属性(⽆论静态还是⾮静态)
- getDeclaredField(String fieldName) : 获取任意权限的属性(⽆论静态还是⾮静态)
- getFields() : 获取当前类中的所有的属性
- getDeclaredFields() : 获取当前类中的所有权限的属性
15.5.2 属性的读写
try {
Class cls = Class.forName("com.qf.cField.Person");
// 1、实例化⼀个对象
Object obj = cls.newInstance();
Field age = cls.getField("age");
// 给指定对象的这个属性进⾏赋值
age.set(obj, 20);
// 获取指定对象的这个属性值
System.out.println(age.get(obj));
Field name = cls.getDeclaredField("name");
name.setAccessible(true);
name.set(obj, "xiaobai");
System.out.println(name.get(obj));
Field count = cls.getDeclaredField("count");
count.setAccessible(true);
// 静态的属性,可以使⽤任意的对象进⾏设置和获取
// ⼀般情况下,对于这个静态的属性,会使⽤null来设置、读取
count.set(null, 10);
count.get(null);
System.out.println(obj);
System.out.println(count.getName());
System.out.println(count.getModifiers());
System.out.println(count.getGenericType()); // 获取泛型
} catch (ClassNotFoundException | NoSuchFieldException |
InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
15.6 ⽅法的使⽤
15.6.1 ⽅法的获取
- getMethod(String methodName, Class… paramaterTypes)
- 获取⼀个类中的指定的⽅法
- getDeclaredMethod(String methodName, Class… paramaterTypes)
- 获取⼀个类中的所有的权限的指定的⽅法
- getMethods()
- 获取⼀个类中的可以访问到的⽅法
- getDeclaredMethods()
- 获取⼀个类中的所有的权限的⽅法
方法获取的案例:
/*
NoSuchFieldException : 没有这个属性(有可能是属性名字写错了,也有可能是访问权限不够)
*/
public class FieldsDemo {
public static class Person {
public int publicField;
private int privateField;
public static int publicStaticField;
private static int privateStaticField;
}
public static void main(String[] args) {
try {
// 1. 获取Class对象
Class<?> cls = Class.forName("day30.reflect.FieldsDemo$Person");
// 2. 实例化指定类的对象
Object obj = cls.newInstance();
// 3. 属性访问
// 3.1. public权限的属性获取
Field field1 = cls.getField("publicField");
// 3.2. private权限的属性获取
Field field2 = cls.getDeclaredField("privateField");
// 3.3. public权限的静态属性获取
Field field3 = cls.getField("publicStaticField");
// 3.4. private权限的静态属性获取
Field field4 = cls.getDeclaredField("privateStaticField");
// 4. 获取到属性之后,进行属性的访问
// set(Object obj, Object value) : 将obj对象的这个属性,设置为指定的value
// get(Object obj) : 获取obj对象这个属性的值
// 4.1. public权限的非静态属性访问
// field1.set(obj, 123);
// Object value = field1.get(obj);
// System.out.println(value);
// 4.2. private权限的非静态属性访问
// field2.setAccessible(true);
// field2.set(obj, 123);
// System.out.println(field2.get(obj));
// 4.3. public权限的静态的属性访问
// 对于静态的成员访问,其实可以使用任意的对象进行访问。但是习惯上,会使用null进行访问。
// field3.set(null, 123);
// System.out.println(field3.get(null));
// 4.4. private权限的静态的属性访问
// field4.setAccessible(true);
// field4.set(null, 123);
// System.out.println(field4.get(null));
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchFieldException e) {
e.printStackTrace();
}
}
}
15.6.2 ⽅法的调⽤
-
invoke
try { // 1、获取Class对象 Class cls = Class.forName("com.qf.dMethod.Dog"); // 2、实例化描述的类的对象 Object obj = cls.newInstance(); // 3、获取⼀个⽅法 Method method = cls.getDeclaredMethod("show", int.class, int.class); // 4、调⽤这个⽅法 method.setAccessible(true); Object returnValue = method.invoke(obj, 10, 20); System.out.println(returnValue); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { e.printStackTrace(); }
方法调用的案例:
/**
* 使用反射,调用方法
*/
public class MethodsDemo {
public static class Person {
public void publicMethod() {
System.out.println("public Method");
}
private void privateMethod() {
System.out.println("private Method");
}
public static void publicStaticMethod() {
System.out.println("public Static Method");
}
private static void privateStaticMethod() {
System.out.println("private Static Method");
}
public void show(int a) {
System.out.println("show(" + a + ")");
}
public void show(int a, int b) {
System.out.println("show(" + a + ", " + b + ")");
}
public int show(int a, int b, int c) {
return a + b + c;
}
}
public static void main(String[] args) {
try {
// 1. 获取Class对象
Class<?> cls = Class.forName("day30.reflect.MethodsDemo$Person");
// 2. 实例化对象
Object obj = cls.newInstance();
// 3. 方法的获取
// getMethod(String name, Class<?>... parameterTypes)
// 获取指定的方法
// String name: 表示方法名字
// Class<?>... parameterTypes: 表示方法的参数
// Method publicMethod = cls.getMethod("publicMethod");
// Method privateMethod = cls.getDeclaredMethod("privateMethod");
// Method publicStaticMethod = cls.getMethod("publicStaticMethod");
// Method privateStaticMethod = cls.getDeclaredMethod("privateStaticMethod");
Method show1 = cls.getMethod("show", int.class);
Method show2 = cls.getMethod("show", int.class, int.class);
Method show3 = cls.getMethod("show", int.class, int.class, int.class);
// 调用方法 invoke(Object obj, Object... args)
// Object obj: 调用方法的对象,如果是静态的方法,可以使用null
// Object... args: 调用方法的参数,即方法的实参。需要和形参保持一致(数量、类型、顺序)
// invoke方法的返回值,就是调用的方法的返回值
show1.invoke(obj, 123);
show2.invoke(obj, 123, 456);
Object ret = show3.invoke(obj, 123, 456, 789);
System.out.println(ret);
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
}