Java 反射

Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。
能够分析类能力的程序成为反射。反射机制的功能极其强大。
反射机制可以用来:
1.在运行时分析类的能力
2.在运行时查看对象,列如:编写一个toString方法供所有类使用
3.实现通用的数组操作代码
4.利用Method对象,这个对象很像C++中的函数指针。

利用反射分析类的能力,以下程序显示了如何打印一个类的全部信息的方法。
假如输入:java.lang.Double
则程序会输出:

public final class java.lang.Double extends java.lang.Number { public java.lang.Double(double); public java.lang.Double(java.lang.String); public boolean equals(java.lang.Object); public static java.lang.String toString(double); public java.lang.String toString(); public int hashCode(); public static int hashCode(double); public static double min(double, double); public static double max(double, double); public static native long doubleToRawLongBits(double); public static long doubleToLongBits(double); public static native double longBitsToDouble(long); public volatile int compareTo(java.lang.Object); public int compareTo(java.lang.Double); public byte byteValue(); public short shortValue(); public int intValue(); public long longValue(); public float floatValue(); public double doubleValue(); public static java.lang.Double valueOf(double); public static java.lang.Double valueOf(java.lang.String); public static java.lang.String toHexString(double); public java.util.Optional describeConstable(); public volatile java.lang.Object resolveConstantDesc(java.lang.invoke.MethodHandles$Lookup); public java.lang.Double resolveConstantDesc(java.lang.invoke.MethodHandles$Lookup); public static int compare(double, double); public static boolean isNaN(double); public boolean isNaN(); public static boolean isInfinite(double); public boolean isInfinite(); public static boolean isFinite(double); public static double sum(double, double); public static double parseDouble(java.lang.String); public static final double POSITIVE_INFINITY; public static final double NEGATIVE_INFINITY; public static final double NaN; public static final double MAX_VALUE; public static final double MIN_NORMAL; public static final double MIN_VALUE; public static final int MAX_EXPONENT; public static final int MIN_EXPONENT; public static final int SIZE; public static final int BYTES; public static final java.lang.Class TYPE; private final double value; private static final long serialVersionUID; }

程序代码:
class Solution { public static void main(String[] args) { //read class name from command line args or user input String name; if(args.length>0) name=args[0]; else { Scanner in=new Scanner(System.in); System.out.println("Enter class name(e.g. java.util.Date): "); name=in.next(); }try { //print class name and superclass name(if != Object) Class c1=Class.forName(name); Class superc1=c1.getSuperclass(); String modifiers=Modifier.toString(c1.getModifiers()); if(modifiers.length()>0) System.out.print(modifiers+" "); System.out.print("class "+name); if(superc1!=null && superc1!=Object.class) System.out.print(" extends "+superc1.getName()); System.out.print("\n{\n"); printConstructors(c1); System.out.println(); printMethods(c1); System.out.println(); printFields(c1); System.out.println("}"); } catch(ClassNotFoundException e) { e.printStackTrace(); } System.exit(0); } public static void printConstructors(Class c1) { Constructor[] constructors=c1.getDeclaredConstructors(); for(Constructor c : constructors) { String name=c.getName(); System.out.print(""); String modifiers=Modifier.toString(c.getModifiers()); if(modifiers.length()>0) System.out.print(modifiers + " "); System.out.print(name+"("); //print parameter types Class[] paramTypes=c.getParameterTypes(); for(int j=0; j0) System.out.print(","); System.out.print(paramTypes[j].getName()); } System.out.println("); "); } } public static void printMethods(Class c1) { Method[] methods=c1.getDeclaredMethods(); for(Method m : methods) { Class retType=m.getReturnType(); String name=m.getName(); System.out.print(""); //print modifiers, return type and method name String modifiers = Modifier.toString(m.getModifiers()); if(modifiers.length()>0) System.out.print(modifiers+" "); System.out.print(retType.getName()+" "+name+"("); //print parameter types Class[] paramTypes=m.getParameterTypes(); for(int j=0; j0) System.out.print(", "); System.out.print(paramTypes[j].getName()); } System.out.println("); "); } } public static void printFields(Class c1) { Field[] fields=c1.getDeclaredFields(); for(Field f : fields) { Class type=f.getType(); String name=f.getName(); System.out.print(""); String modifiers=Modifier.toString(f.getModifiers()); if(modifiers.length()>0) System.out.print(modifiers+" "); System.out.println(type.getName()+" "+name+"; "); } } }


我将直接通过一个例子介绍反射的基本用法:
Book.java
package org.yuan.myproject; public class Book { private final static String tag="BookTag"; public String date; private String name; private String author; @Override public String toString() { return "Book{" + "name='" +name+ '\'' + ". author='" + author + '\'' +'}'; } public Book() { } private Book(String name,String author) { this.name=name; this.author=author; } public String getName() { return name; } public void setName(String name) { this.name=name; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author=author; } private String declaredMethod(int index) { String string=null; switch(index) { case 0: string="I am declaredMethod 1 !"; break; case 1: string="I am declaredMethod 2 !"; break; default: string="I am declaredMethod 1 !"; }return string; } }

ReflectClass.java
package org.yuan.myproject; import java.lang.System.Logger; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import jdk.internal.net.http.common.Log; public class ReflectClass { public static void main(String[] args) { //获取Class对象 System.out.println("------------------------------------------->>>>> Class 对象"); //使用类名.class获取Class对象 Class myObjectClass1=Book.class; try { //使用全类名(包括包名)字符串来获取Class对象 Class myObjectClass2=Class.forName("org.yuan.myproject.Book"); }catch(ClassNotFoundException e) { e.printStackTrace(); } System.out.println(myObjectClass1.getName()); //获取类名字符串 System.out.println("------------------------->>>>>> 类名"); //获取全类名字符串(包含包名:org.yuan.myproject) System.out.println("getName=" + myObjectClass1.getName()); //获取类名(不包含包名,外部类名:Book) System.out.println("getSimpleName=" + myObjectClass1.getSimpleName()); //返回java语言规范定义的基础类的规范名称. System.out.println("getCanonicalName=" + myObjectClass1.getCanonicalName()); //获取访问修饰符 System.out.println("-------------------------->>>>>> java访问修饰符"); //返回该类或接口的java语言修饰符对应的整数编码。 int modifiers=myObjectClass1.getModifiers(); System.out.println("isPublic=" + Modifier.isPublic(modifiers)); //获取包信息 System.out.println("--------------------------->>>>>> 获取包信息"); //获取package信息。 Package aPackage=myObjectClass1.getPackage(); System.out.println("Package=" + aPackage.getName()); //获取父类class System.out.println("---------------------------->>>>>> 获取父类Class"); Class superClass=myObjectClass1.getSuperclass(); System.out.println("SupperPackage=" + superClass.getName()); //获取实现的接口 System.out.println("---------------------------->>>>>> 获取实现的接口"); Class[] interfaces=myObjectClass1.getInterfaces(); //获取类的构造方法 System.out.println("---------------------------->>>>>> 获取类的构造方法"); //获取所有public的构造器 Constructor[] constructors=myObjectClass1.getConstructors(); //获取方法 //获取类的所有public方法,包括父类的。 Method[] methods=myObjectClass1.getMethods(); //获取当前类定义的所有方法(不仅仅是public),不包括父类的方法。 Method[] declaredMethods=myObjectClass1.getDeclaredMethods(); try { //通过方法名称和参数类型来获取方法。(含父类) Method methodName=myObjectClass1.getMethod("getAuthor", null); //通过方法名称和参数类型来获取方法。(不含父类) Method thisMethodName=myObjectClass1.getDeclaredMethod("getAuthor",null); }catch (NoSuchMethodException e) { e.printStackTrace(); }//获取成员变量 //获取所有的public成员变量(包括父类的) Field[] fields=myObjectClass1.getFields(); //获取当前类定义的所有的(包含private,projected)成员变量,不包括父类的 Field[] declaredFields=myObjectClass1.getDeclaredFields(); try { //通过成员变量名来获取成员变量(包括父类) Field fieldName=myObjectClass1.getField("date"); //通过成员变量名来获取成员变量(不包括父类) Field thisClassFieldName=myObjectClass1.getDeclaredField("name"); }catch(NoSuchFieldException e) { e.printStackTrace(); }//获取所有注解 //获取所有注解(含父类) Annotation[] annotations=myObjectClass1.getAnnotations(); //获取当前类注解(不含父类) Annotation[] declaredAnnotations=myObjectClass1.getDeclaredAnnotations(); //根据注解类型获取注解 Annotation annotation=myObjectClass1.getAnnotation(null); Annotation declaredAnnotation=myObjectClass1.getDeclaredAnnotation(null); //类型判断相关方法 myObjectClass1.isAnnotation(); myObjectClass1.isArray(); myObjectClass1.isEnum(); myObjectClass1.isInstance(null); myObjectClass1.isInterface(); //创建对象 try { //使用默认构造方法创建对象 Book instance=(Book)myObjectClass1.newInstance(); //有参数的可以使用构造器的方法 //constructors[0].newInstance(...) }catch(InstantiationException e) { e.printStackTrace(); }catch(IllegalAccessException e) { e.printStackTrace(); } } public static final String MyObjectClassStringName="org.yuan.myproject"; public static class Myobject implements MyInterface{ } public interface MyInterface{ } }

使用反射编写泛型数组代码
java.lang.reflect包中的Array类允许动态的创建数组。列如,将这个特性应用到Array类中的copyOf方法实现中,应该记得这个额方法可以用于扩展已经填满的数组。
Employee[] a=new Employee[100]; ... //array is full a=Arrays.copyOf(a,2*a.length);

如何编写这样一个通用的方法呢?正好能够将Employee[]数组转换为Object[]数组,这让人感觉很有希望。下面进行一次尝试:
public static Object[] badCopyOf(Object[] a,int newLength) { Object[] newArray=new Object[newLength]; System.arraycopy(a, 0, newArray, 0, Math.min(a.length, newLength)); return newArray; }

然而,在实际使用结果数组时会遇到一个问题。这段代码返回的数组类型是对象数组(Object[])类型,然而一个对象数组不能转换成雇员数组(Employee[])。如果这样做,则在运行时java将会产生ClassCastException异常。
我们知道java数组会记住每个元素的类型,即创建数组时new表达式中使用的元素类型。将一个Employee临时的转换成Object数组,然后再把它转换回来是可以的,但一个从开始就是Object的数组却永远不能转换成Employee数组。
为了能够编写这类通用的数组代码,需要能够创建与原数组类型相同的新数组。为此,需要java.lang.reflect包中Array类的一些方法。其中最关键的是Array类中的静态方法newInstance,它能够构造新数组。在调用它时必须提供两个参数:数组的元素类型和数组的长度。
Object newArray=Array.newInstance(componentType, newLength);

获取数组的长度较容易,我们可以通过调用Array.getLength(a)获得数组的长度,也可以通过Array类的静态getLength方法的返回值得到任意数组的长度。而要获得新数组的元素类型,就需要进行以下工作:
1. 首先获得a数组的类对象2. 确认它是一个数组3.使用Class类(只能定义表示数组的类对象)的getComponentType方法确定数组对应的类型。
Class c1=a.getClass(); if(!c1.isArray()) return null; Class componentType=c1.getComponentType(); int length=Array.getLength(a); Object newArray=Array.newInstance(componentType, newLength); System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength)); return newArray;

请注意,这个CopyOf方法可以用来扩展任意类型的数组,而不仅是对象数组。
int[] a= {1,2,3,4,5}; a=(int[])goodCopyOf(a,10);

【Java 反射】为了能够实现上述操作,应该将goodCopyOf的参数声明为Object类型,而不要声明为对象型数组(Object[])。整型数组类型int[]可以被转换成Object,但不能转换成对象数组。

    推荐阅读