Java 基本功之反射

tech2026-06-15  0

反射 -- 就是通过Class实例获取Class信息的方法称为反射

获取Class的三中方式

1、直接通过一个Class的静态变量class获取 Class clazz = String.class; 2、如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取 String str="abc"; Class cls = s.getClass(); 3、如果知道一个class的完整类名,可以通过静态方法Class.forName()获取 Class cls = Class.forName("java.lang.String");

JVM为每个加载的class及interface创建了对应的Class实例来保存class及interface的所有信息;可以通过Class,获取一些信息;例如:

Class clazz = String.class; Log.e(TAG, "getClass=" + clazz.getName()); Log.e(TAG, "getSimpleName=" + clazz.getSimpleName()); Log.e(TAG, "getPackage=" + clazz.getPackage().getName()); Log.e(TAG, "isInterface=" + clazz.isInterface()); Log.e(TAG, "isArray=" + clazz.isArray()); Log.e(TAG, "isPrimitive=" + clazz.isPrimitive()); try { String str = (String) clazz.newInstance(); } catch (IllegalAccessException | InstantiationException e) { e.printStackTrace(); }

日志如下:

2020-09-04 16:20:09.594 27372-27372/com.xiaoma.demo E/DemoActivity: getClass=java.lang.String 2020-09-04 16:20:09.594 27372-27372/com.xiaoma.demo E/DemoActivity: getSimpleName=String 2020-09-04 16:20:09.594 27372-27372/com.xiaoma.demo E/DemoActivity: getPackage=java.lang 2020-09-04 16:20:09.594 27372-27372/com.xiaoma.demo E/DemoActivity: isInterface=false 2020-09-04 16:20:09.594 27372-27372/com.xiaoma.demo E/DemoActivity: isArray=false 2020-09-04 16:20:09.595 27372-27372/com.xiaoma.demo E/DemoActivity: isPrimitive=false

其中class.newInstance()相当于创建了一个无参的String实例;它的局限是:只能调用public的无参数构造方法。带参数的构造方法,或者非public的构造方法都无法通过Class.newInstance()被调用

访问字段

Field getField(name):根据字段名获取某个public的field(包括父类)Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)Field[] getFields():获取所有public的field(包括父类)Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

示例如下:

public class Person { public String name; private int age; public String getName() { return name; } public void hello(){ System.out.println("Person:hello"); } } public class Employee extends Person { public int number; private int salary; public Employee(int number, int salary) { this.number = number; this.salary = salary; } public int getNumber(String type) { number++; return number; } private int getSalary(int num) { return salary; } public void hello(){ System.out.println("Employee:hello"); } @Override public String toString() { return "Employee{" + "number=" + number + ", salary=" + salary + '}'; } } Class employee = Employee.class; Field[] fields = employee.getFields(); for (Field field : fields) { Log.e(TAG, "getFields:" + field.getName()); } try { //获取public字段"number" Log.e(TAG, "getField: " + employee.getField("number")); //获取继承的public字段"name" Log.e(TAG, "getField: " + employee.getField("name")); //获取private字段"salary" Log.e(TAG, "getDeclaredField: " + employee.getDeclaredField("salary")); } catch (NoSuchFieldException e) { e.printStackTrace(); }

运行结果:

2020-09-04 16:55:00.854 30097-30097/com.xiaoma.demo E/DemoActivity: getFields:number 2020-09-04 16:55:00.854 30097-30097/com.xiaoma.demo E/DemoActivity: getFields:name 2020-09-04 16:55:00.854 30097-30097/com.xiaoma.demo E/DemoActivity: getField: public int com.xiaoma.studyretrofit.build.Employee.number 2020-09-04 16:55:00.854 30097-30097/com.xiaoma.demo E/DemoActivity: getField: public java.lang.String com.xiaoma.studyretrofit.build.Person.name 2020-09-04 16:55:00.854 30097-30097/com.xiaoma.demo E/DemoActivity: getDeclaredField: private int com.xiaoma.studyretrofit.build.Employee.salary

一个Field对象包含了一个对象的所有信息

getName():返回字段名称,例如,"name";getType():返回字段类型,也是一个Class实例,例如,String.class;getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义

例子:

Class employee = Employee.class; try { Field field = employee.getField("name"); Log.e(TAG, "getName:"+field.getName()); Log.e(TAG, "getType:"+field.getType()); Log.e(TAG, "getModifiers:"+field.getModifiers()); Log.e(TAG, "Modifier.isPublic:"+ Modifier.isPublic(field.getModifiers())); } catch (NoSuchFieldException e) { e.printStackTrace(); }

运行结果:

2020-09-04 17:03:16.210 31497-31497/com.xiaoma.demo E/DemoActivity: getName:name 2020-09-04 17:03:16.210 31497-31497/com.xiaoma.demo E/DemoActivity: getType:class java.lang.String 2020-09-04 17:03:16.210 31497-31497/com.xiaoma.demo E/DemoActivity: getModifiers:1 2020-09-04 17:03:16.210 31497-31497/com.xiaoma.demo E/DemoActivity: Modifier.isPublic:true

获取字段值,并且可以重新设置字段值

利用反射拿到字段的一个Field实例只是第一步,我们还可以拿到一个实例对应的该字段的值

例如,对于一个EmPloyee实例,我们可以先拿到name字段对应的Field,再获取这个实例的name字段的值;如果对于私有的变量,需要设置setAccessible(true)

Employee employee = new Employee(101, 10000); Class clazz = employee.getClass(); try { Field numberField = clazz.getField("number"); //通过Field的get方法,可以拿到employee实例的字段值 int number = (int) numberField.get(employee); Log.e(TAG, "number: "+number); Field salaryField = clazz.getDeclaredField("salary"); salaryField.setAccessible(true); int salary = (int) salaryField.get(employee); Log.e(TAG, "salary: "+salary); Log.e(TAG, "employee: "+employee.toString()); //通过Field的set方法,可以重新设置employee对应的值 salaryField.set(employee,15000); int newsalary = (int) salaryField.get(employee); Log.e(TAG, "newsalary: "+newsalary); Log.e(TAG, "newemployee: "+employee.toString()); } catch (Exception e) { e.printStackTrace(); }

运行结果

2020-09-04 17:22:30.240 3062-3062/com.xiaoma.demo E/DemoActivity: number: 101 2020-09-04 17:22:30.241 3062-3062/com.xiaoma.demo E/DemoActivity: salary: 10000 2020-09-04 17:22:30.241 3062-3062/com.xiaoma.demo E/DemoActivity: employee: Employee{number=101, salary=10000} 2020-09-04 17:22:30.241 3062-3062/com.xiaoma.demo E/DemoActivity: newsalary: 15000 2020-09-04 17:22:30.241 3062-3062/com.xiaoma.demo E/DemoActivity: newemployee: Employee{number=101, salary=15000}

调用方法

Method getMethod(name, Class...):获取某个public的Method(包括父类)Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)Method[] getMethods():获取所有public的Method(包括父类)Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

示例 

Class clazz=Employee.class; try { Log.e(TAG, "getNumber:"+clazz.getMethod("getNumber",String.class)); Log.e(TAG, "getSalary:"+clazz.getDeclaredMethod("getSalary",int.class)); Log.e(TAG, "getName:"+clazz.getMethod("getName")); } catch (NoSuchMethodException e) { e.printStackTrace(); }

首先获取Student的Class实例,然后获取public方法、private方法、继承的public方法

运行结果:  

2020-09-07 09:57:06.928 17143-17143/com.xiaoma.demo E/DemoActivity: getNumber:public int com.xiaoma.studyretrofit.build.Employee.getNumber(java.lang.String) 2020-09-07 09:57:06.928 17143-17143/com.xiaoma.demo E/DemoActivity: getSalary:private int com.xiaoma.studyretrofit.build.Employee.getSalary(int) 2020-09-07 09:57:06.928 17143-17143/com.xiaoma.demo E/DemoActivity: getName:public java.lang.String com.xiaoma.studyretrofit.build.Person.getName()

一个Method对象包含一个方法的所有信息:

getName():返回方法名称,例如:"getScore";getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。

通过Method实例可以调用某个对象的方法:Object invoke(Object instance, Object... parameters)

示例

Employee employee = new Employee(100, 15000); try { Class clazz = employee.getClass(); Method method = clazz.getMethod("getNumber", String.class); Log.e(TAG, "getName:"+method.getName()); Log.e(TAG, "getName:"+method.getReturnType()); //利用Method,获取employee对象的方法返回值//或者操作方法 Log.e(TAG, "method.invoke:"+(int) method.invoke(employee, "xiaoma")); //调用私有方法 Method getSalary = clazz.getDeclaredMethod("getSalary", int.class); getSalary.setAccessible(true); Log.e(TAG, "getSalary: "+(int)getSalary.invoke(employee,10)); //调用静态方法,因为无需指定实例对象,所以invoke方法传入的第一个参数永远是null Method parseInt = Integer.class.getMethod("parseInt", String.class); Log.e(TAG, "parseInt: "+(Integer) parseInt.invoke(null,"12345")); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); }

运行结果

 

2020-09-07 10:23:12.017 21536-21536/com.xiaoma.demo E/DemoActivity: getName:getNumber 2020-09-07 10:23:12.017 21536-21536/com.xiaoma.demo E/DemoActivity: getName:int 2020-09-07 10:23:12.017 21536-21536/com.xiaoma.demo E/DemoActivity: method.invoke:101 2020-09-07 10:23:12.017 21536-21536/com.xiaoma.demo E/DemoActivity: getSalary: 15000 2020-09-07 10:23:12.017 21536-21536/com.xiaoma.demo E/DemoActivity: parseInt: 12345

通过反射调用方法时,仍然遵循多态原则

try { Method hello = Person.class.getMethod("hello"); hello.invoke(new Employee(10,100)); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } //运行结果 2020-09-07 10:36:51.931 22153-22153/com.xiaoma.demo I/System.out: Employee:hello

调用构造方法

getConstructor(Class...):获取某个public的Constructor;getDeclaredConstructor(Class...):获取某个Constructor;getConstructors():获取所有public的Constructor;getDeclaredConstructors():获取所有Constructor。

调用Class.newInstance()的局限是,它只能调用该类的public无参数构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用;

调用非public的Constructor时,必须首先通过setAccessible(true)设置允许访问

通过Constructor实例可以创建一个实例对象:newInstance(Object... parameters); 通过设置setAccessible(true)来访问非public构造方法

try { Constructor constructor = Employee.class.getConstructor(int.class, int.class); Employee employee= (Employee) constructor.newInstance(20, 20000); Log.e(TAG, "Constructor: "+employee.toString()); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } //运行结果 2020-09-07 10:51:38.186 23757-23757/com.xiaoma.demo E/DemoActivity: Constructor: Employee{number=20, salary=20000}

获取继承关系

通过Class对象可以获取继承关系:

Class getSuperclass():获取父类类型;Class[] getInterfaces():获取当前类实现的所有接口。

通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现

Class clazz=Employee.class; Class superclass = clazz.getSuperclass(); Log.e(TAG, "superclass: "+superclass.getName()); Log.e(TAG, "isAssignableFrom: "+Person.class.isAssignableFrom(Employee.class)); Class<?>[] interfaces = String.class.getInterfaces(); for (Class<?> anInterface : interfaces) { Log.e(TAG, "getInterfaces: "+anInterface.getName()); } //运行结果 2020-09-07 11:23:54.949 26085-26085/com.xiaoma.demo E/DemoActivity: superclass: com.xiaoma.studyretrofit.build.Person 2020-09-07 11:23:54.949 26085-26085/com.xiaoma.demo E/DemoActivity: isAssignableFrom: true 2020-09-07 11:23:54.949 26085-26085/com.xiaoma.demo E/DemoActivity: getInterfaces: java.io.Serializable 2020-09-07 11:23:54.949 26085-26085/com.xiaoma.demo E/DemoActivity: getInterfaces: java.lang.Comparable 2020-09-07 11:23:54.949 26085-26085/com.xiaoma.demo E/DemoActivity: getInterfaces: java.lang.CharSequence

 

最新回复(0)