简析Java的反射机制

Java的反射机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。

一.Class类

在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。保存这些信息的类被称为Class。当我们使用Object类中的getClass方法我们将会得到一个Class类型的实例。
例如,我们可以使用Class类中的getName方法来返回类的名字,也可以通过调用Class类的静态方法Class.forName()方法来获得类名对应的Class对象。同时我们还可以直接使用类名.class的方法来获得一个Class对象。我们还可以使用newInstance()方法来快速创建一个类的实例。

二.利用反射分析类的能力

在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。这三个类都有一个叫做getName的方法,用来放回项目的名称。这三个类中都有一个getModifiers的方法,它将返回一个整型值,用不同的位开关描述public 和 static 这样的修饰符的使用情况。我们还可以利Modifier.to-String方法将修饰符打印出来。Class类中的getField、getMethod、getConstruc-tor方法分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类中的get-DeclareField、getDeclareMethod、getDeclareConstructor方法分别返回类中的私有方法和受保护成员,但是不包括超类的成员。
下面是Java核心技术5.7节反射的实例代码:

package chapter5;

import java.lang.reflect.*;
import java.util.*;

public class ReflectionTest {
    public static void main(String args[]){
        Scanner input = new Scanner(System.in);
        System.out.print("请输入完整的类名;");
        String name = input.next();
        try{
            Class cl = Class.forName(name);//得到name的Class对象
            Class supercl = cl.getSuperclass();//得到Class对象实例的父类的Class对象
            String modifiers = Modifier.toString(cl.getModifiers());
            if(modifiers.length()>0) System.out.print(modifiers + " ");//判断是否有类修饰词
            System.out.print("class " + name);
            if(supercl != null &&supercl != Objects.class ) System.out.print(" extends " + supercl.getName());
            //判断该类的父类是否为空或Object
            System.out.print("\n{\n");
            printConstructors(cl);
            System.out.println();
            printMethods(cl);
            System.out.println();
            printFields(cl);
            System.out.println("}");
        }catch(ClassNotFoundException e){
            e.printStackTrace();
        }
        System.exit(0);
    }
    public static void printConstructors(Class cl){
        Constructor[] constructors = cl.getConstructors();//得到全部的构造器数组
        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 + "(");
            Class[] paramTypes = c.getParameterTypes();//得到构造器的方法参数
            for(int j = 0;j<paramTypes.length;j++){
                if(j > 0) System.out.print(",");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }
    public static void printMethods(Class cl){
        Method[] methods = cl.getDeclaredMethods();
        for(Method m:methods){
            String name = m.getName();
            System.out.print("  ");
            String modifiers = Modifier.toString(m.getModifiers());
            if(modifiers.length() > 0) System.out.print(modifiers + " ");
            Class returnType = m.getReturnType();//得到返回值的Class类型
            System.out.print(returnType.getName() + " " + name + "(");
            Class[] paramTypes = m.getParameterTypes();
            for(int j = 0;j<paramTypes.length;j++){
                if(j > 0) System.out.print(",");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }
    public static void printFields(Class cl){
        Field[] fields = cl.getDeclaredFields();
        for(Field f:fields){
            String name = f.getName();
            System.out.print("  ");
            String modifiers = Modifier.toString(f.getModifiers());
            if(modifiers.length() > 0) System.out.print(modifiers + " ");
            Class type = f.getType();//得到f的类型
            System.out.println(type.getName() + " " + name + ";");
        }
    }
}

使用java.lang.reflect.Field得到的测试结果:

请输入完整的类名;java.lang.reflect.Field
public final class java.lang.reflect.Field extends java.lang.reflect.AccessibleObject
{

  public java.lang.Object get(java.lang.Object);
  public boolean equals(java.lang.Object);
  public java.lang.String toString();
  public int hashCode();
  public int getModifiers();
  public boolean getBoolean(java.lang.Object);
  public byte getByte(java.lang.Object);
  public short getShort(java.lang.Object);
  public char getChar(java.lang.Object);
  public int getInt(java.lang.Object);
  public long getLong(java.lang.Object);
  public float getFloat(java.lang.Object);
  public double getDouble(java.lang.Object);
  public java.lang.String getName();
  private synchronized java.util.Map declaredAnnotations();
  public java.lang.annotation.Annotation getAnnotation(java.lang.Class);
  public [Ljava.lang.annotation.Annotation; getAnnotationsByType(java.lang.Class);
  public [Ljava.lang.annotation.Annotation; getDeclaredAnnotations();
  public java.lang.Class getDeclaringClass();
  private sun.reflect.generics.factory.GenericsFactory getFactory();
  private sun.reflect.generics.repository.FieldRepository getGenericInfo();
  public boolean isSynthetic();
  public java.lang.String toGenericString();
  private sun.reflect.FieldAccessor acquireFieldAccessor(boolean);
  java.lang.reflect.Field copy();
  public java.lang.reflect.AnnotatedType getAnnotatedType();
  private sun.reflect.FieldAccessor getFieldAccessor(java.lang.Object);
  private sun.reflect.FieldAccessor getFieldAccessor(boolean);
  private java.lang.String getGenericSignature();
  public java.lang.reflect.Type getGenericType();
  public java.lang.Class getType();
  private native [B getTypeAnnotationBytes0();
  public boolean isEnumConstant();
  public void set(java.lang.Object,java.lang.Object);
  public void setBoolean(java.lang.Object,boolean);
  public void setByte(java.lang.Object,byte);
  public void setChar(java.lang.Object,char);
  public void setDouble(java.lang.Object,double);
  private void setFieldAccessor(sun.reflect.FieldAccessor,boolean);
  public void setFloat(java.lang.Object,float);
  public void setInt(java.lang.Object,int);
  public void setLong(java.lang.Object,long);
  public void setShort(java.lang.Object,short);

  private java.lang.Class clazz;
  private int slot;
  private java.lang.String name;
  private java.lang.Class type;
  private int modifiers;
  private transient java.lang.String signature;
  private transient sun.reflect.generics.repository.FieldRepository genericInfo;
  private [B annotations;
  private sun.reflect.FieldAccessor fieldAccessor;
  private sun.reflect.FieldAccessor overrideFieldAccessor;
  private java.lang.reflect.Field root;
  private transient java.util.Map declaredAnnotations;
}

三.在运行时使用反射分析方法

我们已经知道如果查看任意对象的数据域名称和类型,那么要怎么进一步查看数据域的实际内容呢?我们依旧可以使用反射机制来实现。
查看对象域的关键方法是Field类中的get方法。如果f是一个Field类型的对象,obj是某个包含f域的类的对象,那么f.get(obj)将返回一个对象,其值为obj域的当前值。我们依旧来看Java核心技术里面的一个例子:

private String name;
//...
Employee harry = new Employee("Harry Hacker",35000,10,1,1989);
Class cl = harry.getClass();
Field f = cl.getDeclaredField("name");//name为employee类的一个私有数据域
f.setAccessible(true);//因为name是私有的,需要使用这个方法获得访问权限
Object v = f.get(harry);

通过这段代码我们就能得到Employee类的一个实例harry的数据域name的值“Harray hacker”。
类似的,我们也可以使用f.set(obj,value)将obj对象的f域设置为新值value。

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

下面我们通过一个实例来探究这个问题:
在java.util.Arrays类中有一个copyOf方法可以用来拓展已经填满地数组,那么如何来编写这样一个通用地方法呢?
一个思路是用Object[]数组来代替所有数组类型,然后再用类型转换回来。但是很遗憾,这样往往是不可行地。因为Java数组会记住每个元素地类型,即创建数组时new表达式中使用的元素类型。例如,我们可以暂时地将一个Employee[]转换为Object[],再转换回去。但是我们永远不能将一个一开始的Object[]转换为Employee[]。这时我们就需要调用java.lang.reflect包中的Arrays类的一些方法。最常用的还是静态方法newInstance,它能够创建新数组。在调用它时必须提供两个参数,一个是数组的元素类型,一个是数组的长度。

 Object newArray = Array.newInstance(componentType,newLength);

下面是Java核心技术里的例程:

public static Object copyOf(Object a,int newLength){
	Class cl = a.getClass();
	if(!cl.isArray()) return null;
	Class componentType = cl.getComponentType();
	//通过Class类的getComponentType()方法可以取得一个数组的Class对象
	int length = Array.getLength(a);
	Object newArray = Array.newInstance(componentType,newLength);
	System.out.arraycopy(a,0,newArray,Math.min(length,newLength));
	return newArray;
}

为了能使这个方法使用与所有数组,而不仅仅是对象数组,应该将上述代码中的a设置为Object类型而不是Object[]。

五.调用任意方法

在大多数情况下,Java是不允许像C/C++那样使用函数指针来执行任意函数。然而,反射机制却允许你调用任意方法。
我们通过Method类中的两个方法来实现它,Method中的invoke方法,它允许调用包装在当前Method对象中的方法。
例如,假设用m1来表示Employee类的getName()方法,我们就可以这么调用它:
String name = (String) m1.invoke(harry);//harry 是一个Employee类的实例
那么我们又要如何得到Method类的对象呢?
当然我们可以通过Method类的getDeclareMethods来逐个查找;
我们也可以通过Class类的getMethod(String className)来实现,当然一个类中可能有几个相同名称的类,这时候就需要考虑它们方法的参数类型,即使用:
Method getMethod(String className,Class … parameterTypes)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值