现在的位置: 首页 > 综合 > 正文

JavaSE第六十一讲:Java反射机制深度剖析

2013年03月23日 ⁄ 综合 ⁄ 共 5403字 ⁄ 字号 评论关闭

1. Java中的反射是所有以后学习框架的基础,java中的反射是Java中难点和重点

Java语言的反射

1. 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息以及动态调用对象的方法的功能来自于Java
语言的反射(Reflection)机制。
2. Java 反射机制主要提供了以下功能

1)在运行时判断任意一个对象所属的类。

2)在运行时构造任意一个类的对象。[在编译的时候通过new的方式构造一个对象,但是在反射里面是通过一个间接的方式生成]

3) 在运行时判断任意一个类所具有的成员变量和方法。

4) 在运行时调用任意一个对象的方法

3.Reflection 是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection
APIs取得任何一个已知名称的class的内部信息[这边已知名称是类的全称,是包名 + 类名,比如java.lang.Object],包括其modifiers(诸如public, static 等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods。

[也就是在运行期,我只要知道一个类的名称,就可以知道上面所述的那些信息,比如类的修饰符,这个类的父类,这个类实现的接口,这个类包含那些成员变量和方法的信息,以及动态修改在运行期里面的任意一个方法,包括私有的方法等]


2. 一般而言,开发者社群说到动态语言,大致认同的一个定义是:“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl,Python,Ruby是动态语言,C++,Java,C#不是动态语言。与之对应,Java就是静态语言,在web开发中,javascript也是一种动态语言。

  如果掌握一门静态语言的同时,最好学习一门动态语言,比如Ruby之类以适应不同情况下的开发


3.
尽管在这样的定义与分类下,Java不是动态语言,它却有着一个非常突出的动态相关机制:Reflection。这个字的意思是“反射、映象、倒影”,用在Java身上指的是我们可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体、或对其fields设值、或唤起其methods。这种“看透class”的能力(the
ability of the program to examine itself)被称为introspection(内省、内观、反省)。Reflection和introspection是常被并提的两个术语

4.
在JDK中,主要由以下类来实现Java反射机制,这些类大多数都位于java.lang.reflect包中

1)Class类:代表一个类[这个包位于java.lang包下,下面的几个位于java.lang.reflect包下]
2)Field
类:代表类的成员变量(成员变量也称为类的属性)。

3)Method类:代表类的方法。
4)Constructor类:代表类的构造方法。
5)Array类:提供了动态创建数组,以及访问数组的元素的静态方法

查看JDK
Doc文档查看他们相应类的说明

Class类代表着类,每一个类都会有一个与之关联的Class类。

使用反射的时候,前面四个类是一定会被用到的。

5.
例程DumpMethods类演示了Reflection API的基本作用,它读取命令行参数指定的类名,然后打印这个类所具有的方法信息。


package com.ahuier.reflect;

import java.lang.reflect.Method;

public class DumpMethods {
	public static void main(String[] args) throws Exception{
		/*
		 * 使用反射,第一步一定要获得要操作类的Class对象,这方式反射的入口
		 * 查看JDK文档Class类的forName()方法。
		 */
		Class<?> classType = Class.forName("java.lang.Object"); //获得了java.lang.String这个类的Class对象
		
		//获得到Class对象 classType 之后,就可以通过这个对象操作Class类里面的方法了
		
		/*
		 * 调用getDeclaredFields()方法后返回一个数组,这个数组里面是所有的这个类的方法的信息
		 */
		Method[] methods = classType.getDeclaredMethods();
		
		for(Method method : methods){
			System.out.println(method);
		}
	}
}

编译执行结果:

private
static native void java.lang.Object.registerNatives()
public final native java.lang.Class java.lang.Object.getClass()
public native int java.lang.Object.hashCode()
public boolean java.lang.Object.equals(java.lang.Object)
protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException
public java.lang.String java.lang.Object.toString()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
protected void java.lang.Object.finalize() throws java.lang.Throwable

【说明】:这边输出的Object类的方法的所有信息

forName

public
static Class<?> forName(String className)

                       
throws ClassNotFoundException

Parameters:
className
- the fully qualified name of the desired class. [访问那个类的全称]

Returns:

the
Class object for the class with the specified name.[返回与这个类对于的Class对象,是泛型的]

Java 中,无论生成某个类的多少个对象,这些对象都会对应于同一个 Class 对象。可以通过这个Class对象探知这个对象里面有哪些属性,那些方法。这个在多线程中会再讲这个知识点

继续写一个例子,掌握反射的基本方法:

package com.ahuier.reflect;

import java.lang.reflect.Method;

public class InvokeTester {
	public int add(int param1, int param2) {
		return param1 + param2;
	}

	public String echo(String message) {
		return "hello: " + message;
	}

	public static void main(String[] args) throws Exception {

		// 使用非反射方式调用两个方法
		/*
		 * InvokeTester test = new InvokeTester();
		 * System.out.println(test.add(1, 2));
		 * System.out.println(test.echo("tom"));
		 */

		// 通过放射的方式调用两个方法

		/*
		 * 获取Class对象,对比上一个程序第一种的获取Class方式调用forName()
		 * 现在使用第二种方式,通过Java内置的一种语法:InvokeTester.class
		 */
		Class<?> classType = InvokeTester.class; // 获取Class对象

		/*
		 * 查看JDK newInstance()方法,获得到Class对象之后就可以调用这个方法生成与这个类所对应的对象 相当于 new
		 * InvokeTester();
		 */
		Object invokeTester = classType.newInstance();
		// System.out.println(invokeTester instanceof InvokeTester);

		/*
		 * 如果要使用反射,就要获得操作这个类所对应的Class对象 如果要调用方法,就要获得与这个方法所对应的Method对象
		 * 如果要使用属性,就要获得与这个属性所关联的field对象 如果要调用构造方法,就要获得与这个构造方法对应的construct对象
		 */

		/*
		 * 查看JDk Doc文档,使用getMethod()方法获得与add()方法所对应的Method对象
		 */
		Method addMethod = classType.getMethod("add", new Class[] { int.class,
				int.class });//int.class 使用第二种获得Class对象的方式,这边可以看到原生数据类型int也有一个class对象
		/*
		 * 得到了Method对象addMethod之后,就可以通过Method里面的方法调用add了。
		 * 查看JDK文档里面的invoke方法,表示addMethod对象调用的目标方法
		 */
		Object result = addMethod.invoke(invokeTester, new Object[]{1, 2});//装箱
		System.out.println((Integer)result); //使用反射返回方法add()类型 int的包装类
		System.out.println("---------------------------------");
		
		//调用第二个方法,与调用第一个方法实现类似
		Method echoMethod = classType.getMethod("echo", new Class[]{String.class});
		Object result2 = echoMethod.invoke(invokeTester, new Object[]{"tom"});
		System.out.println((String)result2);
	}
}

编译执行结果:

3
---------------------------------
hello: tom

查看JDK
Doc文档中的getMethod()方法:

getMethod
public Method getMethod(String name,
                        Class<?>... parameterTypes)
                 throws NoSuchMethodException,
                        SecurityException

[调用这个方法来获得与这个方法所对应的Method对象。 name:表示需要调用获得Method对象的方法名称,parameterTypes表示调用的这个方法里面的参数对应的Class对象所构成的一个class类型的数组。这边用可变参数的原因是因为Java中的方法有重载的,单纯从名字上指定不指定的方法,所以通过方法的名称,参数的列表,就可以唯一的确定我调用的是
哪一个方法
]


查看JDK Doc文档中的invoke()方法;

invoke
public Object invoke(Object obj,
                     Object... args)
              throws IllegalAccessException,
                     IllegalArgumentException,
                     InvocationTargetException

[调用这个方法可以调用到Method对象所对应的目标方法。obj:表示方法被调用的那个对象;
args:表示方法接受的可变参数,以数组的方式传]


抱歉!评论已关闭.