Java反射机制

    反射机制使得人们可以通过在运行时查看域和方法,让人们编写出更具有通用性的程序。这种功能对于编写系统程序来说极其实用,但是通常不适用于编写应用程序。现在让我们来了解一下反射机制。我们把能够分析类能力的程序称为反射(reflective)。反射可以用于以下几个方面:

  • 在运行中分析类的能力;
  • 在运行中查看对象;
  • 实现通用的数组操作代码;
  • 利用Method对象,这个对象类似于C++中的函数指针;

在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。通过这个信息可以跟踪着每个对象所属的类。虚拟机就是利用运行时类型信息选择相应的方法执行。在Java中,有一个专门的类来保存这些运行时的信息,这个类称为Class类。一个Class对象将表示一个特定类的属性。最常用的的Class方法是getName(),这个方法将返回类型的名字。可以通过三种方式获取一个Class类型的实例:

  1. Object类中的getClass()方法返回的就是一个Class类型的实例。例如:
    Date date;
    Class cl = date.getClass( );
  2. 通过调用静态方法forName获得类名对应的Class对象。例如:
    String className = “java.util.Date”;
    Class cl = Class.forName(className);

  3. 如果T是任意的Java类型,则T.class将代表匹配的类对象。例如:
    Class cl = Date.class;

       当获取了一个类型的实例Class后,可以通过newInstance( )方法快速创建一个类的实例。例如:e.getClass( ).newInstance( );将创建一个与e具有相同类类型的实例。需要注意的是,newInstance方法调用的是默认的构造器初始化新创建的对象。如果这个类没有默认的构造器,就会抛出一个异常。将forName和newInstance配合起来使用,可以根据存储在字符串中的类名创建一个对象。例如:
String className = "java.util.Date";
Object m = Class.forName(className).newInstance();

 

  对于反射比较重要的部分是其分析类的能力,检查类的结构。在java.lang.reflect包中有三个类Filed、Method和Constructor,他们分别用于描述类的域、方法和构造器。这三个类都有一个叫做getName()的方法,用来返回项目的名称。Field类有一个getType方法,用来返回描述域所属类型的Class对象。这三个类还有一个叫做getModifiers()的方法,它将返回一个整型数组。每个整型都是用不同的位开关(0或1)来描述public和static这样的修饰符的使用状况。例如用8位的整型来表示这些public和static修饰符的组合,则可能的结果是10010010,相应位为1表示有相应的修饰符,为0则表示不存在相应的修饰符。另外,我们还可以利用java.lang.reflect包中Modifier类的静态方法分析getModifiers()返回的整型数值,同时我们也可以利用Modifier.toString()方法将修饰符打印出来。

  在Class类中的getFileds()、getMethods()和getConstructors()方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类的getDeclareFileds()、getDeclareMethods()和getDeclareConstructors()方法则将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。下面我们通过一个实例来演示反射函数的使用:

package reflection;
import java.lang.reflect.*;//插入反射所需要的类
import java.util.*;

public class ReflectionTest {

	public static void main(String[] args) {
		String name = "java.lang.Double";//类的名称
		//异常处理机制
		try {
			Class cl = Class.forName(name);//创建类的对象
			Class supercl = cl.getSuperclass();//创建超类的对象
			String modifiers = Modifier.toString(cl.getModifiers());//返回public、static等修饰符
			if (modifiers.length() > 0) {
				System.out.print(modifiers + " ");
			}
			System.out.print("class " + name);
			if (supercl != null && supercl != Object.class) {
				System.out.print(" entends " + supercl.getName());
			}
			
			System.out.print("\n{\n");
			printConstructors(cl);//打印类对象的构造函数
			System.out.println();
			printMethod(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 constructor : constructors) {
			String name = constructor.getName();//返回构造器的名字
			System.out.print("  ");
			String modifiers = Modifier.toString(cl.getModifiers());//返回修饰构造器的描述符组合
			if (modifiers.length() > 0) {
				System.out.print(modifiers + " ");
			}
			System.out.print(name + "(");
			
			Class[] paramTypes = constructor.getParameterTypes();//返回构造函数的参数数组
			for (int i = 0; i < paramTypes.length; i++) {
				if(i > 0)
					System.out.print(", ");
				System.out.print(paramTypes[i].getName());
			}
			System.out.println(");");
		}
	}
	
	public static void  printMethod(Class cl) {
		Method[] methods = cl.getDeclaredMethods();//返回类提供的方法数组
		for (Method method : methods) {
			Class retType = method.getReturnType();//返回方法的返回值类对象
			String name = method.getName();//获取对象的名字
			System.out.print("  ");
			String modifiers = Modifier.toString(method.getModifiers());//返回修饰方法的描述符组合
			if (modifiers.length() > 0) {
				System.out.print(modifiers + " ");
			}
			System.out.print(retType.getName() + " " + name + "(");
			
			Class[] paramTypes = method.getParameterTypes();//返回方法的参数类型数组
			for (int i = 0; i < paramTypes.length; i++) {
				if (i > 0) {
					System.out.print(", ");
				}
				System.out.print(paramTypes[i].getName());
			}
			System.out.println(");");
		}
	}
	
	public static void printFields(Class cl) {
		Field[] fields = cl.getDeclaredFields();//返回类提供的public域数组
		for (Field field : fields) {
			Class type = field.getType();//返回域的类型
			String name = field.getName();//返回域的名字
			System.out.print("  ");
			String modifiers = Modifier.toString(field.getModifiers());//返回修饰域的描述符组合
			if (modifiers.length() > 0) {
				System.out.print(modifiers + " ");
			}
			System.out.println(type.getName() + " " + name + ";");
		}
	}
}

 程序将输出:

public final class java.lang.Double entends java.lang.Number
{
  public final java.lang.Double(java.lang.String);
  public final java.lang.Double(double);

  public boolean equals(java.lang.Object);
  public java.lang.String toString();
  public static java.lang.String toString(double);
  public int hashCode();
  public static native long doubleToRawLongBits(double);
  public static long doubleToLongBits(double);
  public static native double longBitsToDouble(long);
  public int compareTo(java.lang.Double);
  public volatile int compareTo(java.lang.Object);
  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 static int compare(double, double);
  public boolean isNaN();
  public static boolean isNaN(double);
  public boolean isInfinite();
  public static boolean isInfinite(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 java.lang.Class TYPE;
  private final double value;
  private static final long serialVersionUID;
}

  反射机制还有一个比较重要的功能是允许你调用任意方法。在C/C++中是通过函数指针执行任意函数。而在Java中没有提供方法指针,而是通过Method类实现的。在Method类中有一个invoke方法,它允许调用包装在当前Method对象中的方法。invoke方法的签名是:Object invoke(Object obj,Object...args)。其中第一个参数是隐式参数,其余的对象提供了显示参数。对于静态方法,第一个参数可以被忽略,即可以将它设置为null。如果返回类型是基本类型,invoke方法会返回其包装器类型。那么如何获得Method对象呢?有两种方式:一种是通过调用getDeclareMethods()方法,然后对返回的Method对象数组进行查找,直到发现想要的方法为止;另外一种是通过getMethod()方法返回指定名字的那个方法。getMethod的签名是:
Method getMethod(String name,Class...parameterType)
  下面通过一个示例来演示如何通过反射机制调用方法:

<span style="font-family:微软雅黑;">package methods;
import java.lang.reflect.*;
public class MethodPointerTest {
	public static void main(String[] args) throws Exception {
		Method sqrt = Math.class.getMethod("sqrt", double.class);//返回Method对象
		Method square = MethodPointerTest.class.getMethod("square", double.class);
		printTable(1, 10, 10, square);
		printTable(1, 10, 10, sqrt);
		
	}
	
	public static  double square(double x) {
		return x*x;
	}
	public static void  printTable(double from,double to,int n,Method f) {
		System.out.println(f);
		
		double dx = (to-from)/(n-1);
		for(double x = from; x <= to; x += dx){
			try {
				double y = (Double) f.invoke(null, x);//通过反射机制调用函数
				System.out.printf("%10.4f | %10.4f\n",x,y);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}</span><span style="font-family:Courier New;">
</span>

程序返回结果为:

public static double methods.MethodPointerTest.square(double)
    1.0000 |     1.0000
    2.0000 |     4.0000
    3.0000 |     9.0000
    4.0000 |    16.0000
    5.0000 |    25.0000
    6.0000 |    36.0000
    7.0000 |    49.0000
    8.0000 |    64.0000
    9.0000 |    81.0000
   10.0000 |   100.0000
public static double java.lang.Math.sqrt(double)
    1.0000 |     1.0000
    2.0000 |     1.4142
    3.0000 |     1.7321
    4.0000 |     2.0000
    5.0000 |     2.2361
    6.0000 |     2.4495
    7.0000 |     2.6458
    8.0000 |     2.8284
    9.0000 |     3.0000
   10.0000 |     3.1623

  虽然反射机制可以通过invoke方法触发方法调用,但是这种通过Method对象实现方法调用的出错可能性也比较大。如果在调用方法的时候提供了一个错误的参数,那么invoke方法将会抛出一个异常。另外,invoke的参数和返回值必须是Object类型的,这就意味着必须进行多次的类型转换,这样做将会使编译器错过检查代码的机会。因此,仅在必要的时候才调用Method对象,最好还是使用内部类和接口来实现相同的功能。








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值