-
在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法; 对于任意一个对象,都能够调用它的任意属性和方法; 这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
反射的应用场景
虽然在基础阶段,我们好像没有碰到用反射的场景,但是在:
逆向代码 ,例如反编译
与注解相结合的框架,例如 Retrofit
单纯的反射机制应用框架,例如 EventBus
动态生成类框架 例如Gson
等场景我们都需要用到反射,所以学习反射尤为重要。
反射有以下优缺点
优点:增强了程序的灵活性,避免将固有的逻辑程序写死到代码里。可以提高代码的复用性。
缺点:相较于直接调用,在量大的情况下,反射的性能下降的厉害,内部暴露,有安全隐患。
反射获取类对象的三种方法:
由于反射是通过已知的类或者类名来获取类的属性和方法以及一些详细的信息,所以首先我们得获得类的字节码对象。
-
类名.class属性(一般在类加载到内存之后使用该方法)
-
对象名.getClass()方法(一般是在创建了类的实例对象之后使用该方法)
-
Class.forName(全类名)方法(一般是在类还未加载,处于源代码时使用该方法)
反射获取构造方法
当我们获取到类对象时,我们就可以根据获取到的类对象获取类的构造方法
Constructor<?>[] getConstructors() | 返回所有公共构造方法对象的数组 |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造方法对象的数组 |
Constructor<T> getConstructor(Class<?>... parameterTypes) | 返回单个公共构造方法对象 |
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) | 返回单个构造方法对象 |
下面使用例子来学习这些方法
//反射的基本操作:获取类对象->获取构造方法->通过构造方法创建对象(如果是私有构造方法还得将私有权限打开,打开不用及时关闭) public class FansheExer { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { //反射首先得获取到类对象,才能对类对象进行之后的操作 //首先通过类名调用class或者通过Class.forName("类的包名到类的路径")或通过创建类的对象调用getClass获取到类对象。 Class<Person> per = Person.class;//类名调用class System.out.println(per); Class aClass = Class.forName("fanshe.fansheExer.Person");//通过Class.forName("类的包名到类的路径") System.out.println(aClass); Person p1 = new Person();//创建类的对象调用getClass获取到类对象 Class<? extends Person> aClass1 = p1.getClass(); System.out.println(aClass1); Class<Integer> integerClass = Integer.class; System.out.println(integerClass); Class<Character> charClass = Character.class; System.out.println(charClass); Class<? extends Class> aClass2 = charClass.getClass(); System.out.println(aClass2); //然后通过类对象获取类的构造方法 Constructor<Person> cons = per.getConstructor(); System.out.println("获取无参构造方法"+cons); //当前将有参构造改为私有。所以无法直接使用getConstructor获取有参构造 //用declaredConstructor获取构造方法,包括(私有) Constructor<Person> declaredConstructor = per.getDeclaredConstructor(String.class, Integer.class, String.class); System.out.println("获取有参构造方法:"+declaredConstructor); System.out.println("================="); Annotation[] dec = per.getDeclaredConstructors();//获取所有构造方法(包括私有的构造方法)的数组 //如果要获取所有公共的构造方法返回的数组,可以通过getConstructors()获取 System.out.println(dec); for(Annotation d:dec){ System.out.println(d); } //获取构造方法后,我们可以根据构造方法创建实例对象,但由于有参构造是私有化,所以得把私有有参构造的权限打开 declaredConstructor.setAccessible(true);//setAccessible打开私有权限 Person person = (Person) declaredConstructor.newInstance("张三", 15, "男"); System.out.println(person); //根据构造方法对象调用newInstance来创建对象,和张三那条一样 Person person1 = declaredConstructor.newInstance("章", 19, "女"); declaredConstructor.setAccessible(false); System.out.println(per == aClass);//得到的结果是true,证明通过三种方式获取到的类对象都是同一个类对象,因为他们的地址都一致。 } }
反射获取成员变量
获取到类对象之后我们可以通过类对象获取成员变量,并修改其值。
Field[] getFields() | 返回所有公共成员变量对象的数组 |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组 |
Field getField(String name) | 返回单个公共成员变量对象 |
Field getDeclaredField(String name) | 返回单个成员变量对象 |
反射给成员变量赋值
void set(Object obj, Object value) | 赋值
Object get(Object obj) | 获取值
//反射获取成员属性,并设置成员属性 //获取类对象->根据类对象获取成员属性 // ->根据类对象获取构造方法(如果构造方法私有,应打开私有权限)->根据构造方法创建实例对象->根据获取的属性调用set(实例对象,要设置的具体值) public class FansheExer2 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchMethodException { Class<?> aClass = Class.forName("fanshe.fansheExer.Person"); System.out.println(aClass); Person person = new Person(); Class<? extends Person> aClass1 = person.getClass(); System.out.println(aClass1); Class<Person> personClass = Person.class; System.out.println(personClass); Class<?> superclass = aClass.getSuperclass();//获取当前类对象的父类对象 System.out.println(superclass); Field[] field = aClass.getFields();//获取所有公共的public的成员变量 for(Field f:field){ System.out.println(f.getName());//因为当前类的成员变量都是私有,所以无法获取,只能获取公共的方法 } Field[] declaredFields = aClass.getDeclaredFields();//获取全部属性(包括私有)的数组 for(Field f:declaredFields){ System.out.println(f.getName());//getName是获取名字,不会显示路径 System.out.println(f);//获取属性的全部信息 } Field name = aClass.getDeclaredField("name");//获取任意属性的信息 System.out.println(name); Field gender = aClass.getDeclaredField("gender"); System.out.println(gender); Constructor<?> constructor = aClass.getConstructor(); Object o = (Person)constructor.newInstance(); name.setAccessible(true); name.set(o,"wangfang"); System.out.println(name.get(o)); name.setAccessible(false); gender.setAccessible(true); gender.set(o,"21"); System.out.println(gender.get(o)); System.out.println(o); } }
反射获取成员方法,并调用成员方法
Method[] getMethods() | 返回所有公共成员方法对象的数组,包括继承的 |
Method[] getDeclaredMethods() | 返回所有成员方法对象的数组,不包括继承的 |
Method getMethod(String name, Class<?>... parameterTypes) | 返回单个公共成员方法对象 |
Method getDeclaredMethod(String name, Class<?>... parameterTypes) | 返回单个成员方法对象 |
调用成员方法
Object invoke(Object obj, Object... args)运行方法
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; //反射获取成员方法:获取类对象->通过类对象调用getMethod或者getDeclaredMethod获取成员方法 //通过类对象获取构造方法->通过构造方法创建实例对象->通过获取到的成员方法调用invoke方法,将实例对象传入,能运行该成员方法 public class FansheExer3 { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Class<Person> personClass = Person.class;//获取类对象 Constructor<Person> cons = personClass.getDeclaredConstructor();//获取构造方法 cons.setAccessible(true);//打开私有化的构造方法权限 Person person = cons.newInstance();//通过构造方法调用newInstance创建实例对象 Method drink = personClass.getMethod("drink");//通过类对象调用getMethod Method eat = personClass.getDeclaredMethod("eat");//获取任意成员方法,包括私有的 Method[] declaredMethods = personClass.getDeclaredMethods();//获取全部成员方法,包括私有,将其返回成一个数组 for(Method m:declaredMethods){ System.out.println(m.getName()); } drink.invoke(person);//获取到的成员方法调用invoke将其运行 eat.setAccessible(true);//由于eat是私有化的方法,所以要将其权限放开 eat.invoke(person); } }