前言
本节我们将学习一下JAVA的放射机制。
什么是JAVA反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性、方法、构造器、包、父类信息等等;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。反射是框架设计的灵魂。JAVA机制实现的一般步骤是什么?
图出处(https://blog.csdn.net/sinat_38259539/article/details/71799078)- 创建一个JAVA 类,通过编译(javac.exe),生成对应的.class文件。之后我们使用java.exe加载(JVM的类加载器完成的)。
- JAVA 类的.class文件被加载到内存中之后(上图的new Student()方法),就是一个运行时类,存在在缓存区。那么这个运行时类本身就是一个Class的实例。
- 创建某个.class的实例,然后使用JAVA反射机制(Reflection API-java.lang.reflect )来获取该实例的属性、方法、构造器等等(此时还需注意属性、方法的作用域,后面我们将会讲解到)。
使用JAVA反射机制我们能做什么?
- 创建对应的运行时类的对象。
- 获取对应的运行时类的完整结构(属性、方法、构造器、内部类、父类、所在的包、异常、注解、…)。
- 调用对应的运行时类的指定的结构(属性、方法、构造器)。
- 将反射机制应用于动态代理。
什么场景下我们会使用JAVA 反射机制?
- 逆向代码 ,例如反编译。
- 与注解相结合的框架 例如Retrofit
- 单纯的反射机制应用框架 例如EventBus 2.x
- 动态生成类框架 例如Gson
概述
下面我们将从如何实现JAVA反射机制的一般步骤角度来学习以下JAVA反射机制。
创建一个java类,包含一些常规的属性、方法(静态方法)、实现的接口、继承类(父类)、多种构造器等等。
Broker.java
import java.sql.SQLDataException; public class Broker extends BrokerFamer implements BrokerForSalesInterface,BrokerForBankInterface { //常规私有属性 private String area; private int zoom; //默认属性 protected String nation; String company; //公有属性 public String group; public String getArea() { return area; } public void setArea(String area) { this.area = area; } public int getZoom() { return zoom; } public void setZoom(int zoom) { this.zoom = zoom; } public String getNation() { return nation; } public void setNation(String nation) { this.nation = nation; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } //常规方法 public void callBroker(String area,Integer zoom){ System.out.println("Call defaultBroker"+area+" within "+zoom+"zoom"); } public void callBrokerWithoutParams(){ System.out.println("Call defaultBroker without params"); } //静态方法 public static void callBrokerStatic(String area,Integer zoom){ System.out.println("Call staticBroker"+area+" within "+zoom+"zoom"); } //私有方法 private void callBrokerPrivate(String area,Integer zoom) throws SQLDataException{ System.out.println("Call privateBroker"+area+" within "+zoom+"zoom"); } //含有私有构造器的类无法被继承被实例化,因而本例子中讨论无意义. //常规无参构造器 public Broker() { super(); // TODO Auto-generated constructor stub } //常规无参构造器 public Broker(String area,String nation){ super(); this.area = area; this.nation = nation; } @Override public String currenceForBank(String currence) { return currence; } @Override public String currenceForSales(String currence) { // TODO Auto-generated method stub return currence; } @Override public String toString() { return "Broker [area=" + area + ", zoom=" + zoom + ", nation=" + nation + ", company=" + company + ", group=" + group + "]"; } }
- BrokerForBankInterface.java ``` public interface BrokerForBankInterface { String currenceForBank(String currence); } ```
BrokerForSalesInterface.java
public interface BrokerForSalesInterface { String currenceForSales(String currence); }
获取运行时指定的Class对象
/** * 创建并获取JAVA 运行时的class对象 */ @Test public void getClassInstance() throws Exception { // java中获取Class对象主要有三种实现方法: // 1.直接调用运行时类的.class属性 Class clazz = Broker.class; System.out.println(clazz); //2.通过运行时类的对象调用起getClass方法 Broker broker = new Broker(); Class clazz1 = broker.getClass(); System.out.println(clazz1); //3.调用Class的静态方法:forName String className = "coreJavaReview.reflect.Broker" ; Class clazz2 = Class.forName(className); System.out.println(clazz2); }
获取并调用对应的运行时类的完整结构
属性
属性
/** * 通过获取运行时Class对象获取属性 */ @Test public void getBrokerFielsInfo(){ //子类中/父类的Public修饰的属性 Class clazz = Broker.class; Field[] fields = clazz.getFields(); for(int i = 0;i < fields.length;i++){ System.out.println(fields[i]); } //只获取父类中Public修饰的属性 /*Class superClazz = clazz.getSuperclass(); Field[] supeFields = superClazz.getFields(); for(int i = 0;i < supeFields.length;i++){ System.out.println(supeFields[i]); }*/ System.out.println(); //2.getDeclaredFields():获取运行时类本身声明的所有的属性 Field[] fields1 = clazz.getDeclaredFields(); for(Field f : fields1){ System.out.println(f.getName()); } //只获取父类中所有的属性 /*System.out.println(); Class superClazz = clazz.getSuperclass(); Field[] superFields = superClazz.getDeclaredFields(); for(Field f : superFields){ System.out.println(f.getName()); }*/ }
1、获取父类属性时需要调用 Class superClazz = clazz.getSuperclass();
2、getFields()方法只能获取子类或父类中被public 修饰的属性,其他类型的属性需要使用getDeclaredFields()来强制获取获取权限的权限修饰符 变量类型 变量名
/** * 获取属性的各种属性 */ @Test public void getBrokerFieldsProperty(){ Class clazz = Broker.class; Field[] fields1 = clazz.getDeclaredFields(); for(Field f : fields1){ //1.获取每个属性的权限修饰符 int i = f.getModifiers(); String str1 = Modifier.toString(i); System.out.print(str1 + " "); //2.获取属性的类型 Class type = f.getType(); System.out.print(type.getName() + " "); //3.获取属性名 System.out.print(f.getName()); System.out.println(); } }
调用指定的属性
/** * 调用指定Clazz的属性 */ @Test public void getBrokerFieldsInvok() throws Exception{ Class clazz = Broker.class; //1.获取指定的属性 //getField(String fieldName):获取运行时类中声明为public的指定属性名为fieldName的属性 Field name = clazz.getField("group"); //2.创建运行时类的对象 Broker broker = (Broker)clazz.newInstance(); System.out.println(broker); //3.将运行时类的指定的属性赋值 name.set(broker,"Dustyone"); System.out.println(broker); System.out.println("%"+name.get(broker)); System.out.println(); //getDeclaredField(String fieldName):获取运行时类中指定的名为fieldName的属性 Field zoom = clazz.getDeclaredField("zoom"); //由于属性权限修饰符的限制,为了保证可以给属性赋值,需要在操作前使得此属性可被操作。 zoom.setAccessible(true); zoom.set(broker,10); System.out.println(broker); }
1、没有被public修饰的字段需要使用 getDeclaredField()调用并且使用 zoom.setAccessible(true)方法来获取权限。建议在获取和调用Class对象的属性是统一使用 getDeclaredField(),然后使用 setAccessible 统一授权。
方法
方法
/** * 获取Class对象的方法 * */ @Test public void getBrokerMethod(){ Class clazz = Broker.class; //1.getMethods():获取运行时类及其父类中所有的声明为public的方法 Method[] m1 = clazz.getMethods(); for(Method m : m1){ System.out.println(m); } System.out.println(); //2.getDeclaredMethods():获取运行时类本身声明的所有的方法 Method[] m2 = clazz.getDeclaredMethods(); for(Method m : m2){ System.out.println(m); } }
获取方法属性
/** * 获取方法属性 * */ @Test public void getBrokeMethodProperty(){ Class clazz = Broker.class; Method[] m2 = clazz.getDeclaredMethods(); for(Method m : m2){ //1.注解 Annotation[] ann = m.getAnnotations(); for(Annotation a : ann){ System.out.println(a); } //2.权限修饰符 String str = Modifier.toString(m.getModifiers()); System.out.print(str + " "); //3.返回值类型 Class returnType = m.getReturnType(); System.out.print(returnType.getName() + " "); //4.方法名 System.out.print(m.getName() + " "); //5.形参列表 System.out.print("("); Class[] params = m.getParameterTypes(); for(int i = 0;i < params.length;i++){ System.out.print(params[i].getName() + " args-" + i + " "); } System.out.print(")"); //6.异常类型 Class[] exps = m.getExceptionTypes(); if(exps.length != 0){ System.out.print("throws "); } for(int i = 0;i < exps.length;i++){ System.out.print(exps[i].getName() + " "); } System.out.println(); } }
调用方法
/** * 调用方法 */ @Test public void getBrokeMethodInvok() throws Exception{ Class clazz = Broker.class; //getMethod(String methodName,Class ... params):获取运行时类中声明为public的指定的方法 Method m1 = clazz.getMethod("callBroker",String.class,Integer.class); Broker broker = (Broker)clazz.newInstance(); //调用指定的方法:Object invoke(Object obj,Object ... obj) Object returnValue1 = m1.invoke(broker,"Europe",1457);// System.out.println(returnValue1); Method m2 = clazz.getMethod("toString"); Object returnVal1 = m2.invoke(broker); System.out.println(returnVal1);// //对于运行时类中静态方法的调用 Method m3 = clazz.getMethod("callBrokerStatic",String.class,Integer.class); m3.invoke(Broker.class,"Asia",17541); //getDeclaredMethod(String methodName,Class ... params):获取运行时类中声明了的指定的方法 Method m4 = clazz.getDeclaredMethod("callBrokerPrivate",String.class,Integer.class); m4.setAccessible(true); Object value = m4.invoke(broker,"Africa",14120);// System.out.println(value);//10 }
构造器
/** * 获取构造器属性 * @throws Exception */ @Test public void getBrokerConstructorProperty() throws Exception { Class clazz = Broker.class; Constructor[] constr = clazz.getConstructors(); for (Constructor constructor : constr) { System.out.println(constructor.getName() + "paramsCount" + constructor.getParameterCount() + "params" + constructor.getParameters()); ; } //获取父类构造器信息 Class superClazz = clazz.getSuperclass(); Constructor<BrokerFamer> [] surConstr = superClazz.getConstructors(); for (Constructor superConstructor : surConstr) { System.out.println(superConstructor.getName() + "paramsCount" + superConstructor.getParameterCount() + "params" + superConstructor.getParameters()); ; } }
父类信息(属性、方法、构造器等)
参考以上信息实现类信息
获取注解
/** * 获取Class对象中的所有注解信息 * @throws Exception */ @Test public void getBrokerAnnotations() throws Exception{ Class clazz = Broker.class; Annotation[] anns = clazz.getAnnotations(); for(Annotation a : anns){ System.out.println(a); } }
获取所在包信息
/** * 获取Class对象的所在包信息 * @throws Exception */ @Test public void getBrokerPacageInfo() throws Exception{ Class clazz = Broker.class; Package packageInfo = clazz.getPackage(); System.out.println(packageInfo); }
获取实现接口类信息
/** * 获取Class对象实现接口信息 * @throws Exception */ @Test public void getBrokerInterface() throws Exception{ Class clazz = Broker.class; Class[] interfaces = clazz.getInterfaces(); for(Class interfaceInfo : interfaces){ System.out.println(interfaceInfo); } }
- …..
小结
- 必须在JAVA程序运行或者应用程序运行时才能动态地获取指定Class实例。(java文件通过javax.exe编译、java.exe加载之到内存之后)。
- 获取指定的Class实例之后可以使用reflect API 来获取指定Class。并且要想获取非public修饰的属性或方法时需要额外的获取权限处理。
- 通过反射机制调用类的方法时需要实例化Class对象如 Broker broker = (Broker)clazz.newInstance()。