第 10章 Java语言的反射机制
在 Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意
一个对象,能否调用它的任意一个方法?
答案
八年级地理上册填图题岩土工程勘察试题省略号的作用及举例应急救援安全知识车间5s试题及答案
是肯定的。这种动态获取类的信息,以及动态
调用对象的方法的功能来自于 Java 语言的反射(Reflection)机制。Java 反射机制主要提供
了以下功能:
l 在运行时判断任意一个对象所属的类;
l 在运行时构造任意一个类的对象;
l 在运行时判断任意一个类所具有的成员变量和方法;
l 在运行时调用任意一个对象的方法;
l 生成动态代理。
本章首先介绍了 Java Reflection API的用法,然后介绍了一个远程方法调用的例子,在
这个例子中客户端能够远程调用服务器端的一个对象的方法。服务器端采用了反射机制提供
的动态调用方法的功能,而客户端则采用了反射机制提供的动态代理功能。
10.1 Java Reflection API简介
在 JDK 中,主要由以下类来实现 Java 反射机制,这些类都位于 java.lang.reflect
包中。
l Class类:代表一个类。
l Field类:代表类的成员变量(成员变量也称为类的属性)。
l Method类:代表类的方法。
l Constructor类:代表类的构造方法。
l Array类:提供了动态创建数组,以及访问数组元素的静态方法。
如例程 10-1所示 DumpMethods类演示了 Reflection API的基本作用,它读取命令
行参数指定的类名,然后打印这个类所具有的方法信息:
例程 10-1 DumpMethods.java
import java.lang.reflect.*;
public class DumpMethods {
public static void main(String args[]) throws Exception{
//加载并初始化命令行参数指定的类
Class classType = Class.forName(args[0]);
//获得类的所有方法
Method methods[] = classType.getDeclaredMethods();
for(int i = 0; i < methods.length; i++)
System.out.println(methods[i].toString());
}
}
PDF 文件使用 "pdfFactory Pro" 试用版本创建 à www.fineprint.cn
240 Java Netword Programming
运行命令“java DumpMethods java.util.Stack”,就会显示 java.util.Stack类所具有的
方法,程序的打印结果如下:
public synchronized java.lang.Object java.util.Stack.pop()
public java.lang.Object java.util.Stack.push(java.lang.Object)
public boolean java.util.Stack.empty()
public synchronized java.lang.Object java.util.Stack.peek()
public synchronized int java.util.Stack.search(java.lang.Object)
如例程 10-2所示 ReflectTester 类进一步演示了 Reflection API的基本使用方法。
ReflectTester 类有一个 copy(Object object)方法,这个方法能够创建一个和参数 object
同样类型的对象,然后把 object对象中的所有属性复制到新建的对象中,并将它返回。
这个例子只能复制简单的 JavaBean,假定 JavaBean的每个属性都有 public类型的
getXXX()和 setXXX()方法。
例程 10-2 ReflectTester.java
import java.lang.reflect.*;
public class ReflectTester {
public Object copy(Object object) throws Exception{
//获得对象的类型
Class classType=object.getClass();
System.out.println("Class:"+classType.getName());
//通过默认构造方法创建一个新的对象
Object objectCopy=classType.getConstructor(new Class[]{}).
newInstance(new Object[]{});
//获得对象的所有属性
Field fields[]=classType.getDeclaredFields();
for(int i=0; i
步骤
新产品开发流程的步骤课题研究的五个步骤成本核算步骤微型课题研究步骤数控铣床操作步骤
。
(1)获得对象的类型:
Class classType=object.getClass();
System.out.println("Class:"+classType.getName());
在 java.lang.Object类中定义了 getClass()方法,因此对于任意一个 Java对象,都可
以通过此方法获得对象的类型。Class类是 Reflection API中的核心类,它有以下方法。
l getName():获得类的完整名字。
l getFields():获得类的 public类型的属性。
l getDeclaredFields():获得类的所有属性。
l getMethods():获得类的 public类型的方法。
l getDeclaredMethods():获得类的所有方法。
l getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参
数指定方法的名字,parameterTypes参数指定方法的参数类型。
l getConstrutors():获得类的 public类型的构造方法。
l getConstrutor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes
PDF 文件使用 "pdfFactory Pro" 试用版本创建 à www.fineprint.cn
242 Java Netword Programming
参数指定构造方法的参数类型。
l newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
(2)通过默认构造方法创建一个新的对象:
Object objectCopy=classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
以上代码先调用 Class 类的 getConstructor()方法获得一个 Constructor 对象,它代
表默认的构造方法,然后调用 Constructor对象的 newInstance()方法构造一个实例。
(3)获得对象的所有属性:
Field fields[]=classType.getDeclaredFields();
Class类的 getDeclaredFields()方法返回类的所有属性,包括 public、protected、默
认和 private访问级别的属性。
(4)获得每个属性相应的 getXXX()和 setXXX()方法,然后执行这些方法,把原
来对象的属性复制到新的对象中:
for(int i=0; i
流程
快递问题件怎么处理流程河南自建厂房流程下载关于规范招聘需求审批流程制作流程表下载邮件下载流程设计
如下。
(1)SimpleClient 创建一个 Call 对象,它包含了调用 HelloService 接口的 echo()
方法的信息。
(2)SimpleClient通过对象输出流把 Call对象发送给 SimpleServer。
( 3) SimpleServer 通过对象输入流读取 Call 对象,运用反射机制调用
HelloServiceImpl对象的 echo()方法,把 echo()方法的执行结果保存到 Call对象中。
(4)SimpleServer 通过对象输出流把包含了方法执行结果的 Call 对象发送给
SimpleClient。
(5)SimpleClient通过对象输入流读取 Call对象,从中获得方法执行结果。
如例程 10-9和例程 10-10所示分别是 SimpleServer和 SimpleClient的源程序。
例程 10-9 SimpleServer.java
package remotecall;
import java.io.*;
import java.net.*;
import java.util.*;
import java.lang.reflect.*;
public class SimpleServer {
private Map remoteObjects=new HashMap(); //存放远程对象的缓存
/** 把一个远程对象放到缓存中 */
public void register(String className,Object remoteObject){
remoteObjects.put( className,remoteObject);
}
public void service()throws Exception{
ServerSocket serverSocket = new ServerSocket(8000);
System.out.println("服务器启动.");
while(true){
Socket socket=serverSocket.accept();
InputStream in=socket.getInputStream();
ObjectInputStream ois=new ObjectInputStream(in);
OutputStream out=socket.getOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(out);
Call call=(Call)ois.readObject(); //接收客户发送的 Call对象
System.out.println(call);
call=invoke(call); //调用相关对象的方法
oos.writeObject(call); //向客户发送包含了执行结果的 Call对象
ois.close();
oos.close();
socket.close();
}
}
public Call invoke(Call call){
PDF 文件使用 "pdfFactory Pro" 试用版本创建 à www.fineprint.cn
Java Network Programming 247
第 10章
Java语言的反射机制
Object result=null;
try{
String className=call.getClassName();
String methodName=call.getMethodName();
Object[] params=call.getParams();
Class classType=Class.forName(className);
Class[] paramTypes=call.getParamTypes();
Method method=classType.getMethod(methodName,paramTypes);
Object remoteObject=remoteObjects.get(className); //从缓存中取出相关的远程对象
if(remoteObject==null){
throw new Exception(className+"的远程对象不存在");
}else{
result=method.invoke(remoteObject,params);
}
}catch(Exception e){result=e;}
call.setResult(result); //设置方法执行结果
return call;
}
public static void main(String args[])throws Exception {
SimpleServer server=new SimpleServer();
//把事先创建的 HelloServiceImpl对象加入到服务器的缓存中
server.register("remotecall.HelloService",new HelloServiceImpl());
server.service();
}
}
例程 10-10 SimpleClient.java
package remotecall;
import java.io.*;
import java.net.*;
import java.util.*;
public class SimpleClient {
public void invoke()throws Exception{
Socket socket = new Socket("localhost",8000);
OutputStream out=socket.getOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(out);
InputStream in=socket.getInputStream();
ObjectInputStream ois=new ObjectInputStream(in);
//Call call=new Call("remotecall.HelloService","getTime",
new Class[]{},new Object[]{});
Call call=new Call("remotecall.HelloService","echo",
new Class[]{String.class},new Object[]{"Hello"});
oos.writeObject(call); //向服务器发送 Call对象
call=(Call)ois.readObject(); //接收包含了方法执行结果的 Call对象
System.out.println(call.getResult());
ois.close();
oos.close();
socket.close();
}
public static void main(String args[])throws Exception {
PDF 文件使用 "pdfFactory Pro" 试用版本创建 à www.fineprint.cn
248 Java Netword Programming
new SimpleClient().invoke();
}
}
先运行命令“ java remotecall.SimpleServer”,再运行命令“ java remotecall.
SimpleClient”,SimpleClient 端将打印“echo:Hello”。该打印结果是服务器端执行
HelloServiceImpl 对象的 echo()方法的返回值。如图 10-1 所示显示了 SimpleClient 与
SimpleServer的通信过程。
图 10-1 SimpleClient与 SimpleServer的通信过程
10.3 代理模式
代理模式是常用的 Java
设计
领导形象设计圆作业设计ao工艺污水处理厂设计附属工程施工组织设计清扫机器人结构设计
模式,它的特征是代理类与
委托类有同样的接口,如图 10-2所示。代理类主要负责为委
托类预处理消息、过滤消息、把消息转发给委托类,以及事
后处理消息等。代理类与委托类之间通常会存在关联关系,
一个代理类的对象与一个委托类的对象关联,代理类的对象
本身并不真正实现服务,而是通过调用委托类的对象的相关
方法,来提供特定的服务。
按照代理类的创建时期,代理类可分为两种。
l 静态代理类:由程序员创建或由特定工具自动生成
源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
l 动态代理类:在程序运行时,运用反射机制动态创建而成。
10.3.1 静态代理类
如图 10-3所示,HelloServiceProxy类(如例程 10-13所示)是代理类,HelloServiceImpl
类(如例程 10-12所示)是委托类,这两个类都实现了 HelloService接口(如例程 10-11
所示)。其中 HelloServiceImpl类是 HelloService接口的真正实现者,而 HelloServiceProxy
类是通过调用 HelloServiceImpl类的相关方法来提供特定服务的。HelloServiceProxy类的
echo()方法和 getTime()方法会分别调用被代理的 HelloServiceImpl 对象的 echo()方法和
getTime()方法,并且在方法调用前后都会执行一些简单的打印操作。由此可见,代理类
可以为委托类预处理消息、把消息转发给委托类和事后处理消息等。
图 10-2 代理模式
PDF 文件使用 "pdfFactory Pro" 试用版本创建 à www.fineprint.cn
Java Network Programming 249
第 10章
Java语言的反射机制
图 10-3 HelloServiceProxy类是 HelloService的代理类
例程 10-11 HelloService.java
package proxy;
import java.util.Date;
public interface HelloService{
public String echo(String msg);
public Date getTime();
}
例程 10-12 HelloServiceImpl.java
package proxy;
import java.util.Date;
public class HelloServiceImpl implements HelloService{
public String echo(String msg){
return "echo:"+msg;
}
public Date getTime(){
return new Date();
}
}
例程 10-13 HelloServiceProxy.java
package proxy;
import java.util.Date;
public class HelloServiceProxy implements HelloService{
private HelloService helloService; //表示被代理的 HelloService实例
public HelloServiceProxy(HelloService helloService){
this.helloService=helloService;
}
public void setHelloServiceProxy(HelloService helloService){
this.helloService=helloService;
}
public String echo(String msg){
System.out.println("before calling echo()"); //预处理
String result=helloService.echo(msg); //调用被代理的 HelloService实例的 echo()方法
System.out.println("after calling echo()"); //事后处理
return result;
}
public Date getTime(){
System.out.println("before calling getTime()"); //预处理
PDF 文件使用 "pdfFactory Pro" 试用版本创建 à www.fineprint.cn
250 Java Netword Programming
Date date=helloService.getTime(); //调用被代理的 HelloService实例的 getTime()方法
System.out.println("after calling getTime()"); //事后处理
return date;
}
}
在 Client1类(如例程 10-14所示)的 main()方法中,先创建了一个 HelloServiceImpl
对象,又创建了一个 HelloServiceProxy对象,最后调用 HelloServiceProxy对象的 echo()
方法。
例程 10-14 Client1.java
package proxy;
public class Client1{
public static void main(String args[]){
HelloService helloService=new HelloServiceImpl();
HelloService helloServiceProxy=new HelloServiceProxy(helloService);
System.out.println(helloServiceProxy.echo("hello"));
}
}
运行 Client1类,打印结果如下:
before calling echo()
after calling echo()
echo:hello
如图 10-4所示显示了 Client1调用 HelloServiceProxy类的 echo()方法的时序图。
图 10-4 Client1调用 HelloServiceProxy类的 echo()方法的时序图
例程 10-13 的 HelloServiceProxy类的源代码是由程序员编写的,在程序运行前,
它的.class文件就已经存在了,这种代理类称为静态代理类。
10.3.2 动态代理类
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由 Java
反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工
作,而且提高了软件系统的可扩展性,因为 Java 反射机制可以生成任意类型的动态
代理类。java.lang.reflect包中的 Proxy类和 InvocationHandler接口提供了生成动态代
理类的能力。
Proxy类提供了创建动态代理类及其实例的静态方法。
(1)getProxyClass()静态方法负责创建动态代理类,它的完整定义如下:
public static Class> getProxyClass(ClassLoader loader, Class>[] interfaces)
PDF 文件使用 "pdfFactory Pro" 试用版本创建 à www.fineprint.cn
Java Network Programming 251
第 10章
Java语言的反射机制
throws IllegalArgumentException
参数 loader指定动态代理类的类加载器,参数 interfaces指定动态代理类需要实现
的所有接口。
(2)newProxyInstance()静态方法负责创建动态代理类的实例,它的完整定义如下:
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces,
InvocationHandler handler) throws IllegalArgumentException
参数 loader指定动态代理类的类加载器,参数 interfaces指定动态代理类需要实现
的所有接口,参数 handler指定与动态代理类关联的 InvocationHandler对象。
以下两种方式都创建了实现 Foo接口的动态代理类的实例:
/**** 方式一 ****/
//创建 InvocationHandler对象
InvocationHandler handler = new MyInvocationHandler(...);
//创建动态代理类
Class proxyClass = Proxy.getProxyClass(
Foo.class.getClassLoader(