[Java开发之路](14)反射机制

最后更新于:2022-04-01 09:59:53

### 1.Class类 普通对象构造方式: ~~~ // 创建Book实例对象 Book book = new Book(); ~~~ 对于Class的实例对象如何构造呢? Class的构造函数是私有的,只有JVM才能创建实例对象 ~~~ // Class的构造函数是私有的,只有JVM才能创建Class实例对象 Class class1 = new Class(); // 错误 ~~~ ~~~ public final class Class<T> implements java.io.Serializable, java.lang.reflect.GenericDeclaration, java.lang.reflect.Type, java.lang.reflect.AnnotatedElement { /* * Constructor. Only the Java Virtual Machine creates Class * objects. */ private Class() {} .... } ~~~ Class有三种表示方式: (1)XXX.class  XXX为类名   实际再告诉我们任何一个类都有一个隐含的已经太成员变量class ~~~ Class class1 = Book.class; ~~~ (2)XXX.getClass()  XXX为对象名称 已知该类的实例对象,通过getClass()方法获取 ~~~ Book book = new Book(); Class class2 = book.getClass(); ~~~ (3)通过Class类的forName方法获取   ~~~ try { Class class3 = Class.forName("com.qunar.bean.Book"); System.out.println(class1 == class2); System.out.println(class1 == class3); } catch (ClassNotFoundException e) { e.printStackTrace(); } ~~~ 我们完全可以通过类的类类型创建该类的对象实例,通过class1,class2以及class3创建Book的实例 ~~~ try { Class class3 = Class.forName("com.qunar.bean.Book"); // 通过类类型的newInstance方法创建实例对象 Book book2 = (Book)class3.newInstance(); book2.setPrice("23.4"); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } ~~~ ### 2.Class动态加载类 Class.forName("类的全称") 不仅表示了类的类类型,还代表了动态加载类。 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类。 ### 3.反射 反射机制--用来检查可用的方法,并返回方法名。 人们想要在运行时获取类的信息的另一个动机,便是希望提供在跨网络的远程平台上创建和运行对象的能力。这被称为远程方法调用,它允许一个Java程序将对象分布到多台机器上。 class类与java.lang.reflect类库一起对反射的概念进行了支持,该类库包含了Filed,Method以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。这样你就可以使用Constructor创建新的对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与method对象关联的方法。另外,还可以调用getFields(),getMethods()和getConstructors()等很便利的方法,以返回表示字段,方法以及构造器的对象的数组。这样,匿名对象的类信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。 其实,反射机制并没有什么神奇之处。当通过反射与一个未知类型的对象打交道时,JVM只知道简单的检查这个对象,看它属于哪个特定的类。在用它做其他事情之前,我们必须先加载这个类的class对象。因此,那个类的.class文件对于JVM来说必须是可获取的,要么在本地机器上,要么可以通过网络可以获得。对于反射机制而言,在编译时不能取得.class文件,只能在运行时打开和检查.class文件。 3.1 获取方法信息 ~~~ package com.qunar.reflect; public class ReflectDemo { public static void main(String[] args) { // int的类类型 Class class1 = int.class; Class class2 = String.class; Class class3 = double.class; Class class4 = Double.class; Class class5 = void.class; System.out.println("class1->" + class1.getName()); System.out.println("class2->" + class2.getName()); System.out.println("class3->" + class3.getName()); System.out.println("class4->" + class4.getName()); System.out.println("class5->" + class5.getName()); } } ~~~ 运行结果: <table cellspacing="0" cellpadding="0" style="margin:8px 0px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:892px"><tbody><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:875px"><br/><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">class1-&gt;int</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">class2-&gt;java.lang.String</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">class3-&gt;double</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">class4-&gt;java.lang.Double</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">class5-&gt;void</span></div></td></tr></tbody></table> ~~~ // 只打印简单名称(不包含包名称) System.out.println("class2->" + class2.getSimpleName()); // String ~~~ ~~~ /** * 打印类成员方法信息(public函数,包括父类继承而来的) * @param object */ public static void PrintClassPublicFunction(Object object){ // 获取类的信息,首先获取类的类类型 // 传递的是哪个子类的对象 c 就是该子类的类类型 Class c = object.getClass(); System.out.println("类的全称是:" + c.getName()); // 一个成员方法就是一个method对象 // getMethods方法是获取的是所有public的函数,包括父类继承而来的 Method[] methods = c.getMethods(); for (Method method : methods) { // 获取方法返回值类型的类类型 Class returnType = method.getReturnType(); System.out.print(returnType.getName() + " "); // 获取方法的名称 System.out.print(method.getName() + "("); // 获取方法参数 // 得到方法参数列表中类型的类类型 Class[] paramTypes = method.getParameterTypes(); int size = paramTypes.length; for (int i = 0;i < size;++i) { if(i != 0){ System.out.print(","); }//if System.out.print(paramTypes[i].getName()); }//for System.out.println(")"); }//for } ~~~ 运行结果: <table cellspacing="0" cellpadding="0" style="margin:8px 0px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:892px"><tbody><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:875px"><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">boolean startsWith(java.lang.String)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">boolean startsWith(java.lang.String,int)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.CharSequence subSequence(int,int)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String substring(int,int)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String substring(int)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">[C toCharArray()</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String toLowerCase(java.util.Locale)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String toLowerCase()</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String toUpperCase()</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String valueOf([C)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.Class getClass()</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">void notify()</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">void notifyAll()</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">void wait(long)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">void wait(long,int)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">void wait()</span></div>....</td></tr></tbody></table> 3.2 获取成员变量信息 ~~~ /** * 打印类成员变量信息 * @param object */ public static void PrintClassFiled(Object object){ // 获取类的信息,首先获取类的类类型 // 传递的是哪个子类的对象 c 就是该子类的类类型 Class c = object.getClass(); /* 成员变量也是对象,java.lang.reflect.Field 类封装了关于成员变量的操作 * getFields()方法获取的是所有的public的成员变量的信息 * getDeclaredFields()获取的是该类自己声明的成员变量的信息 */ Field[] fields = c.getDeclaredFields(); for (Field field : fields) { // 得到成员变量的类型的类类型 Class fieldType = field.getType(); // 得到成员变量的类型 System.out.print(fieldType.getName() + " "); // 得到成员变量的名称 System.out.println(field.getName()); }//for } ~~~ 运行结果: <table cellspacing="0" cellpadding="0" style="margin:8px 0px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:892px"><tbody><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:875px"><br/><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">int MIN_VALUE</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">int MAX_VALUE</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.Class TYPE</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">[C digits</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">[C DigitTens</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">[C DigitOnes</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">[I sizeTable</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">int value</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">int SIZE</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">long serialVersionUID</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">boolean $assertionsDisabled</span></div></td></tr></tbody></table> 3.3 获取构造函数信息 ~~~ /** * 打印类构造函数信息 * @param object */ public static void PrintClassConstructor(Object object){ // 获取类的信息,首先获取类的类类型 // 传递的是哪个子类的对象 c 就是该子类的类类型 Class c = object.getClass(); /* 构造函数也是对象,java.lang.reflect.Constructor 类封装了关于构造函数的操作 * getConstructors()方法获取的是所有的public的构造函数的信息 * getDeclaredConstructors()获取的是该类自己声明的构造函数的信息 */ Constructor[] constructors = c.getConstructors(); for (Constructor constructor : constructors) { // 构造函数的名称 System.out.print(constructor.getName() + "("); // 获取构造函数的参数列表,得到的是参数列表的类类型 Class[] paramTypes = constructor.getParameterTypes(); int size = paramTypes.length; for(int i = 0;i < size;++i){ if(i != 0){ System.out.print(","); }//if // 得到参数名称 System.out.print(paramTypes[i].getName()); }//for System.out.println(")"); }//for } ~~~ 运行结果: <table cellspacing="0" cellpadding="0" style="margin:8px 0px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:892px"><tbody><tr><td style="padding:4px 8px; border-collapse:collapse; border:1px solid rgb(187,187,187); width:875px"><br/><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([B)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([B,int,int)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([B,java.nio.charset.Charset)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([B,java.lang.String)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([B,int,int,java.nio.charset.Charset)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String(java.lang.StringBuilder)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String(java.lang.StringBuffer)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([I,int,int)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([C,int,int)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([C)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String(java.lang.String)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String()</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([B,int,int,java.lang.String)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([B,int)</span></div><div style="margin:0px"><span style="font-size:14pt; color:windowtext; font-family:微软雅黑">java.lang.String([B,int,int,int)</span></div></td></tr></tbody></table> 3.4 方法的反射 如何获取某个方法?方法的名称和方法的参数列表才能唯一决定某个方法。 如何进行操作?通过method.invoke(对象,参数列表) ~~~ package com.qunar.reflect; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import com.qunar.bean.Calculates; public class ReflectDemo { public static void main(String[] args) { // 获取类的信息,首先获取类的类类型 Calculates calculates = new Calculates(); calculates.setNum1(20); calculates.setNum2(40); Class c = calculates.getClass(); /* getMethod()方法获取的是public的方法信息 * getDeclaredMethod()获取的是该类自己声明的方法的信息 */ try { // 获取方法 名称和参数列表共同决定 // Method method = c.getDeclaredMethod("add", new Class[]{int.class,int.class}); Method method = c.getDeclaredMethod("add", int.class,int.class); // 方法的反射 // 对于calculates.add(10,40)来说,方法的反射操作是用method方法调用 和 calculates.add(10,40)的效果一样 // int result = (int)method.invoke(calculates, new Object[]{10,40}); int result = (int)method.invoke(calculates, 10,40); System.out.println(result); // 对于没有参数的方法 Method method2 = c.getDeclaredMethod("print"); method2.invoke(calculates); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } ~~~
';