Java反射

Posted Sakura

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射相关的知识,希望对你有一定的参考价值。

java反射库(reflection library)提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵java代码的程序。这项功能被大量应用于JavaBeans中。

能够分析类能力的程序称为反射。反射机制作用:

1)运行时分析类的能力

2)运行时查看对象

3)实现通用的数组操作代码

4)利用Method对象,这个对象类似于c++函数指针。

Class类

程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应方法执行。

常用相关方法:

static Class forName(String className)       //返回描述类名为className的Class对象

static String getClass(Class classString)     //返回描述类名的String

Object newInstance()  //返回这个类的新实例

Object newInstance(Object[] args)  //构造一个这个构造器所属类的新实例,参数由args给出

利用反射分析类

反射机制最重要的内容--检查类的结构

Class类中的相关方法:

Field[] getFields()  //返回一个包含Field对象的数组,记录了这个类与其超类的公有域

Field[] getDeclaredFields()  //返回一个包含Field对象的数组,记录了这个类的全部域

Method[] getMethods()  //返回一个包含Method对象的数组,记录了所有的公有方法

Method[] get DeclareMethods()  //返回一个包含Method对象的数组,记录了这个类所有或接口的所有方法

Constructor[] getConstructors()  //返回一个包含Constructor对象的数组,记录了所有公有构造器

Constructor[] getDeclareConstructors()  //返回一个包含Constructor对象的数组,记录了所有构造器

反射包中相关方法:

Class getDeclareClass()  //返回一个用于描述类中定义的构造器,方法或域的对象

Class getExceptionTypes()  //返回一个用于描述方法抛出异常类型的Class对象数组

int getModefiers()  //返回一个用于描述构造器,方法或域的修饰符的整形数值。使用Modefier类中的这个方法可以分析这个值

String getName()  //返回一个用于描述构造器,方法或域名的字符串

class[] getParrameterrTypes()  //返回一个用于描述参数类型的Class对象数组

Class getReturnType()  //返回一个用于描述返回类型的Class对象

static String toString(int modifiers)  //返回对应modefiers中位设置的修饰符的字符串表示

static boolean isAbatract【或isFinal/isInterface.....所有修饰符】 (int modefiers)   //检测修饰符对应位  

以下是一个测试用例:

输入一个类,打印所有构造器,方法和域,包括他们的修饰符以及构造器和方法的参数

技术分享图片
  1 package refleection.company;
  2 
  3 import java.lang.reflect.*;
  4 import java.util.Scanner;
  5 
  6 public class ReflectionTest {
  7 
  8     public static void main(String[] args) {
  9         String name;
 10         if (args.length > 0) {
 11             name = args[0];
 12         } else {
 13             Scanner in = new Scanner(System.in);
 14             System.out.println("Enter class name(e.g. java.util.Date):");
 15             name = in.next();
 16         }
 17 
 18         try {
 19             Class c1 = Class.forName(name);//获取对应类名的Class对象
 20             Class superc1 = c1.getSuperclass();//获取超类对象
 21             String modifiers = Modifier.toString(c1.getModifiers());//获取修饰符
 22             if (modifiers.length() > 0) {
 23                 System.out.print(modifiers + " ");
 24             }
 25             System.out.print("class " + name);
 26             if (superc1 != null && superc1 != Object.class) {
 27                 //打印超类对象
 28                 System.out.print("extends" + superc1.getName());
 29             }
 30             System.out.print("\n{\n");
 31             printConstructors(c1);
 32             System.out.println();
 33             printMethods(c1);
 34             System.out.println();
 35             printFields(c1);
 36             System.out.println("}");
 37         } catch (ClassNotFoundException e) {
 38             e.printStackTrace();
 39         }
 40         System.exit(0);
 41     }
 42 
 43 
 44     public static void printConstructors(Class c1) {
 45         Constructor[] constructors = c1.getDeclaredConstructors();//获取所有构造器,存于数组
 46 
 47         for (Constructor c : constructors) {
 48             String name = c.getName();
 49             System.out.print(" ");
 50             String modifiers = Modifier.toString(c.getModifiers());//getModifiers返回一个描述修饰符的整数值,转为字符串
 51             if (modifiers.length() > 0) {
 52                 System.out.print(modifiers + " ");
 53             }
 54             System.out.print(name + "(");
 55             Class[] paramTypes = c.getParameterTypes();//获取描述参数类型的对象数组
 56             for (int j = 0; j < paramTypes.length; j++) {
 57                 if (j > 0) {
 58                     System.out.print(", ");
 59                 }
 60                 System.out.print(paramTypes[j].getName());
 61             }
 62             System.out.print(");");
 63         }
 64     }
 65 
 66     public static void printMethods(Class c1) {
 67         Method[] methods = c1.getDeclaredMethods();//获取所有方法,存于数组
 68 
 69         for (Method m : methods) {
 70             Class retType = m.getReturnType();//获取描述返回类型的对象
 71             String name = m.getName();
 72             System.out.print(" ");
 73             String modifiers = Modifier.toString(m.getModifiers());//描述修饰符的字符串
 74             if (modifiers.length() > 0) {
 75                 System.out.print(modifiers + " ");
 76             }
 77             System.out.print(retType.getName() + " " + name + "(");
 78             Class[] paramTypes = m.getParameterTypes();//获取描述参数类型的对象数组
 79             for (int j = 0; j < paramTypes.length; j++) {
 80                 if (j > 0) {
 81                     System.out.print(", ");
 82                 }
 83                 System.out.print(paramTypes[j].getName());
 84             }
 85             System.out.println(");");
 86         }
 87     }
 88 
 89     public static void printFields(Class c1) {
 90         Field[] fields = c1.getDeclaredFields();
 91 
 92         for (Field f : fields) {
 93             Class type = f.getType();//域所属类型
 94             String name = f.getName();
 95             System.out.print(" ");
 96             String modifiers = Modifier.toString(f.getModifiers());//域修饰符
 97             if (modifiers.length() > 0) {
 98                 System.out.print(modifiers + " ");
 99             }
100             System.out.println(type.getName() + " " + name + ";");
101         }
102     }
103 }
View Code

输入java.util.Date得到下面结果

技术分享图片
 1 Enter class name(e.g. java.util.Date):
 2 java.util.Date
 3 public class java.util.Date
 4 {
 5  public java.util.Date(int, int, int, int, int, int); public java.util.Date(); public java.util.Date(int, int, int, int, int); public java.util.Date(java.lang.String); public java.util.Date(long); public java.util.Date(int, int, int);
 6  public void setYear(int);
 7  public int getMonth();
 8  public void setMonth(int);
 9  public void setDate(int);
10  public int getDay();
11  public int getHours();
12  public void setHours(int);
13  public int getMinutes();
14  public void setMinutes(int);
15  public int getSeconds();
16  public void setSeconds(int);
17  private final long getTimeImpl();
18  static final long getMillisOf(java.util.Date);
19  private static final java.lang.StringBuilder convertToAbbr(java.lang.StringBuilder, java.lang.String);
20  public java.lang.String toLocaleString();
21  public java.lang.String toGMTString();
22  public int getTimezoneOffset();
23  private final sun.util.calendar.BaseCalendar$Date getCalendarDate();
24  private static final sun.util.calendar.BaseCalendar getCalendarSystem(long);
25  private static final sun.util.calendar.BaseCalendar getCalendarSystem(sun.util.calendar.BaseCalendar$Date);
26  private static final sun.util.calendar.BaseCalendar getCalendarSystem(int);
27  private static final synchronized sun.util.calendar.BaseCalendar getJulianCalendar();
28  public java.time.Instant toInstant();
29  public static long UTC(int, int, int, int, int, int);
30  public int getYear();
31  public boolean before(java.util.Date);
32  public boolean after(java.util.Date);
33  public void setTime(long);
34  public long getTime();
35  public int getDate();
36  public boolean equals(java.lang.Object);
37  public java.lang.String toString();
38  public int hashCode();
39  public java.lang.Object clone();
40  public int compareTo(java.util.Date);
41  public volatile int compareTo(java.lang.Object);
42  public static java.util.Date from(java.time.Instant);
43  private void readObject(java.io.ObjectInputStream);
44  private void writeObject(java.io.ObjectOutputStream);
45  private final sun.util.calendar.BaseCalendar$Date normalize();
46  private final sun.util.calendar.BaseCalendar$Date normalize(sun.util.calendar.BaseCalendar$Date);
47  public static long parse(java.lang.String);
48 
49  private static final sun.util.calendar.BaseCalendar gcal;
50  private static sun.util.calendar.BaseCalendar jcal;
51  private transient long fastTime;
52  private transient sun.util.calendar.BaseCalendar$Date cdate;
53  private static int defaultCenturyStart;
54  private static final long serialVersionUID;
55  private static final [Ljava.lang.String; wtb;
56  private static final [I ttb;
57 }
输出结果

在运行时使用反射分析对象

有时候希望可以看到数据域的实际数据而不只是类型,用反射同样可以做到。可能用到下列函数

Object get(Object obj)  //返回obj对象中用Field对象表示的域值

void set(Object obj,Object newValue)  //用一个新值设置obj对象中Field对象表示的域

Field getField(String)  //获得指定名称的公有域

Field getDeclareField(String)  //获得给定名称的域

假设有一个Employee的类,该类有一个name私有域,根据以上函数,尝试获取域值

Employee e = new Employee();
Class c1 = e.getClass();
Field f = c1.getDeclareField("name");
Object v = f.get(e)

看似可行,实际上时不可行的,它会抛出一个IllegalAccessException异常。name是私有域,java安全机制不允许没有访问权限的客户读取私有域的值,那么就要用到以下函数

void setAccessible(boolean flag)  //为反射对象设置可访问标志

boolean isAccessible()  //返回反射对象的可访问标志的值

static void setAccessible(AccessibleObject[] array,boolean flag)  //设置对象数组可访问标志

那么上述例子增加一条语句,就不会抛出异常了。

f.setAccessable(true);

以下是一个实例,输入任意类,输出它的所有域以及相应的值

技术分享图片
 1 package objectAnalyzer;
 2 
 3 import java.lang.reflect.AccessibleObject;
 4 import java.lang.reflect.Array;
 5 import java.lang.reflect.Field;
 6 import java.lang.reflect.Modifier;
 7 import java.util.ArrayList;
 8 
 9 public class ObjectAnalyzer {
10     private ArrayList<Object> visited = new ArrayList<>();
11 
12     public String toString(Object obj){
13         if (obj==null){
14             return "null";
15         }
16         if (visited.contains(obj)){
17             return "...";
18         }
19         visited.add(obj);
20         Class c1 = obj.getClass();//获取类名
21         if (c1 == String.class){
22             return (String)obj;
23         }//若为String类型,直接返回String对象
24         if (c1.isArray()){
25             String r = c1.getComponentType()+"[]{";//getComponent返回表示数组元素类型的字符串
26             for (int i = 0;i < Array.getLength(obj);i++){
27                 if (i>0){
28                     r += ",";
29                 }//获取每个数值
30                 Object val = Array.get(obj,i);
31                 if (c1.getComponentType().isPrimitive()){//判断是否为基本类型
32                     r += val;
33                 }
34                 else {
35                     r += toString(val);
36                 }
37             }
38             return r + "}";
39         }
40         String r = c1.getName();//非空,非String,非array,其他类
41         do {
42             r += "[";
43             Field[] fields = c1.getDeclaredFields();//获取所有域
44             AccessibleObject.setAccessible(fields,true);//设置为可见
45             for (Field f:fields){
46                 if (!Modifier.isStatic(f.getModifiers())){//非静态域
47                     if(!r.endsWith("[")){
48                         r += ",";
49                     }
50                     r += f.getName() + "=";//获取名称
51                     try {
52                         Class t = f.getType();
53                         Object val = f.get(obj);
54                         if (t.isPrimitive()){//判断是否为基本类型
55                             r += val;
56                         }
57                         else {
58                             r += toString(val);
59                         }
60                     }catch (Exception e){
61                         e.printStackTrace();
62                     }
63                 }
64             }
65             r += "]";
66             c1 = c1.getSuperclass();
67         } while (c1 != null);
68         return r;
69     }
70 }
View Code
技术分享图片
 1 package objectAnalyzer;
 2 
 3 import java.util.ArrayList;
 4 
 5 public class ObjectAnalyzerTest {
 6     public static void main(String[] args){
 7         ArrayList<Integer> squares = new ArrayList<>();
 8         for (int i = 1;i <= 5;i++){
 9             squares.add(i*i);
10         }
11         System.out.println(new ObjectAnalyzer().toString(squares));
12     }
13 
14 }
测试类

结果

技术分享图片
1 java.util.ArrayList[elementData=class java.lang.Object[]{java.lang.Integer[value=1][][],java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],java.lang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]
View Code

使用反射编写泛型数组代码

java反射包中的Array类允许动态地创建数组,将其拓展为更通用的方法,即可以拓展任意类型的数组。

public static Object goodCopyOf(Object a,int length){
    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;
}

Array类的一些方法

static Object get(Object array,int index) //返回给定数组下标的内容

static xxx getXxx(Object array,int index) //返回给定数组下标的内容,xxx是基本类型

static void set(Object array,int index,Object newValue)  //设置指定下标内容

staticc int getLength(Object array)  //返回数组长度

static Object newInstance(Class componentType,int length)  //返回一个具有给定类型,维数的新数组

static Object newInstance(Class componentType,int[] length)

调用任意方法

Methods类中有一个invoke方法,允许调用包装在当前Method对象中的方法。如下:

public Object invoke(Object implicitParameter,Object[] explicitParamenters)  //调用这个对象描述的方法,传递给定参数,返回方法的返回值。对于静态方法,把null作为隐式参数传递。

 

以上是关于Java反射的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

反射机制入门

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

OpenGL片段着色器不照亮场景