在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能否调用它的任意一个方法?答案是肯定的。这种动态获取类的信息,以及动态调用对象的方法的功能来自于Java语言的反射(Reflection)机制。Java反射机制主要提供了以下功能:
◆在运行时判断任意一个对象所属的类;
◆在运行时构造任意一个类的对象;
◆在运行时判断任意一个类所具有的成员变量和方法;
◆在运行时调用任意一个对象的方法;
◆生成动态代理。
本章首先介绍了Java Reflection API的用法,然后介绍了一个远程方法调用的例子,在这个例子中客户端能够远程调用服务器端的一个对象的方法。服务器端采用了反射机制提供的动态调用方法的功能,而客户端则采用了反射机制提供的动态代理功能。
10.1 Java Reflection API简介
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中。
◆Class类:代表一个类。
◆Field类:代表类的成员变量(成员变量也称为类的属性)。
◆Method类:代表类的方法。
◆Constructor类:代表类的构造方法。
◆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());
}
}
|
运行命令“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 Field field=fields[i]; String fieldName=field.getName();
String firstLetter=fieldName.substring(0,1).toUpperCase();
//获得和属性对应的getXXX()方法的名字
String getMethodName="get"+firstLetter+fieldName.substring(1);
//获得和属性对应的setXXX()方法的名字
String setMethodName="set"+firstLetter+fieldName.substring(1); //获得和属性对应的getXXX()方法
Method getMethod=classType.getMethod(getMethodName,new Class[]{});
//获得和属性对应的setXXX()方法
Method setMethod=classType.getMethod(setMethodName,new Class[]{field.getType()}); //调用原对象的getXXX()方法
Object value=getMethod.invoke(object,new Object[]{});
System.out.println(fieldName+":"+value);
//调用复制对象的setXXX()方法
etMethod.invoke(objectCopy,new Object[]{value});
}
return objectCopy;
} public static void main(String[] args) throws Exception{
Customer customer=new Customer("Tom",21);
customer.setId(new Long(1)); Customer customerCopy=(Customer)new ReflectTester().copy(customer);
System.out.println("Copy information:"+customerCopy.getName()+""+
customerCopy.getAge());
}
} class Customer{ //Customer类是一个JavaBean
private Long id;
private String name;
private int age;
public Customer(){}
public Customer(String name,int age){
this.name=name;
this.age=age;
}
public Long getId(){return id;}
public void setId(Long id){this.id=id;}
public String getName(){return name;}
public void setName(String name){this.name=name;}
public int getAge(){return age;}
public void setAge(int age){this.age=age;}
}
|
#p#
ReflectTester类的copy(Object object)方法依次执行以下步骤。
(1)获得对象的类型:
Class classType=object.getClass();
System.out.println("Class:"+classType.getName());
|
在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。Class类是Reflection API中的核心类,它有以下方法。
◆getName():获得类的完整名字。
◆getFields():获得类的public类型的属性。
◆getDeclaredFields():获得类的所有属性。
◆getMethods():获得类的public类型的方法。
◆getDeclaredMethods():获得类的所有方法。
◆getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
◆getConstrutors():获得类的public类型的构造方法。
◆getConstrutor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes参数指定构造方法的参数类型。
◆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 Field field=fields[i]; String fieldName=field.getName();
String firstLetter=fieldName.substring(0,1).toUpperCase();
//获得和属性对应的getXXX()方法的名字
String getMethodName="get"+firstLetter+fieldName.substring(1);
//获得和属性对应的setXXX()方法的名字
String setMethodName="set"+firstLetter+fieldName.substring(1); //获得和属性对应的getXXX()方法
Method getMethod=classType.getMethod(getMethodName,new Class[]{});
//获得和属性对应的setXXX()方法
Method setMethod=classType.getMethod(setMethodName,new Class[]{field.getType()}); //调用原对象的getXXX()方法
Object value=getMethod.invoke(object,new Object[]{});
System.out.println(fieldName+":"+value);
//调用复制对象的setXXX()方法
setMethod.invoke(objectCopy,new Object[]{value});
}
|
以上代码假定每个属性都有相应的getXXX()和setXXX()方法,并且在方法名中,“get”和“set”的后面一个字母为大写。例如,Customer类的name属性对应getName()和setName()方法。Method类的invoke(Object obj,Object args[])方法用于动态执行一个对象的特定方法,它的第一个obj参数指定具有该方法的对象,第二个args参数指定向该方法传递的参数。
如例程10-3所示的InvokeTester类的main()方法中,运用反射机制调用一个InvokeTester对象的add()和echo()方法。
例程10-3 InvokeTester.java
import java.lang.reflect.*;
public class InvokeTester {
public int add(int param1,int param2){
return param1+param2;
}
public String echo(String msg){
return "echo:"+msg;
}
public static void main(String[] args) throws Exception{
Class classType=InvokeTester.class;
Object invokeTester=classType.newInstance();
//调用InvokeTester对象的add()方法
Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});
Object result=addMethod.invoke(invokeTester,
new Object[]{new Integer(100),new Integer(200)});
System.out.println((Integer)result); //调用InvokeTester对象的echo()方法
Method echoMethod=classType.getMethod("echo",new Class[]{String.class});
result=echoMethod.invoke(invokeTester,new Object[]{"Hello"});
System.out.println((String)result);
}
}
|
add()方法的两个参数为int类型,获得表示add()方法的Method对象的代码如下:
Method addMethod=classType.getMethod("add",new Class[]{int.class,int.class});
|
Method类的invoke(Object obj,Object args[])方法接收的参数必须为对象,如果参数为基本类型数据,必须转换为相应的包装类型的对象。invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据,那么invoke()方法会把它转换为相应的包装类型的对象,再将其返回。
在本例中,尽管InvokeTester类的add()方法的两个参数及返回值都是int类型,调用addMethod对象的invoke()方法时,只能传递Integer类型的参数,并且invoke()方法的返回类型也是Integer类型,Integer类是int基本类型的包装类:
Object result=addMethod.invoke(invokeTester,
new Object[]{new Integer(100),new Integer(200)});
System.out.println((Integer)result); //result为Integer类型
|
java.lang.Array类提供了动态创建和访问数组元素的各种静态方法。如例程10-4所示的ArrayTester1类的main()方法创建了一个长度为10的字符串数组,接着把索引位置为5的元素设为“hello”,然后再读取索引位置为5的元素的值。
#p#
例程10-4 ArrayTester1.java
import java.lang.reflect.*;
public class ArrayTester1 {
public static void main(String args[])throws Exception {
Class classType = Class.forName("java.lang.String");
//创建一个长度为10的字符串数组
Object array = Array.newInstance(classType, 10);
//把索引位置为5的元素设为"hello"
Array.set(array, 5, "hello");
//读取索引位置为5的元素的值
String s = (String) Array.get(array, 5);
System.out.println(s);
}
}
|
如例程10-5所示的ArrayTester2类的main()方法创建了一个5×10×15的整型数组,并把索引位置为[3][5][10]的元素的值为设37。
例程10-5 ArrayTester2.java
import java.lang.reflect.*;
public class ArrayTester2{
public static void main(String args[]) {
int dims[] = new int[]{5, 10, 15};
Object array = Array.newInstance(Integer.TYPE, dims);
//使arrayObj引用array[3]
Object arrayObj = Array.get(array, 3);
Class cls = arrayObj.getClass().getComponentType();
System.out.println(cls);
//使arrayObj引用array[3][5]
arrayObj = Array.get(arrayObj, 5);
//把元素array[3][5][10]设为37
Array.setInt(arrayObj, 10, 37);
int arrayCast[][][] = (int[][][]) array;
System.out.println(arrayCast[3][5][10]);
}
}
|
10.2 在远程方法调用中运用反射机制
假定在SimpleServer服务器端创建了一个HelloServiceImpl对象,它具有getTime()和echo()方法。HelloServiceImpl类实现了HelloService接口。如例程10-6和例程10-7所示分别是HelloService接口和HelloServiceImpl类的源程序。
例程10-6 HelloService.java
package remotecall;
import java.util.Date;
public interface HelloService{
public String echo(String msg);
public Date getTime();
}
|
例程10-7 HelloServiceImpl.java
package remotecall;
import java.util.Date;
public class HelloServiceImpl implements HelloService{
public String echo(String msg){
return "echo:"+msg;
}
public Date getTime(){
return new Date();
}
}
|
SimpleClient客户端如何调用服务器端的HelloServiceImpl对象的getTime()和echo()方法呢?显然,SimpleClient客户端需要把调用的方法名、方法参数类型、方法参数值,以及方法所属的类名或接口名发送给SimpleServer,SimpleServer再调用相关对象的方法,然后把方法的返回值发送给SimpleClient。
为了便于按照面向对象的方式来处理客户端与服务器端的通信,可以把它们发送的信息用Call类(如例程10-8所示)来表示。一个Call对象表示客户端发起的一个远程调用,它包括调用的类名或接口名、方法名、方法参数类型、方法参数值和方法执行结果。
例程10-8 Call.java
package remotecall;
import java.io.*;
public class Call implements Serializable{
private String className; //表示类名或接口名
private String methodName; //表示方法名
private Class[] paramTypes; //表示方法参数类型
private Object[] params; //表示方法参数值 //表示方法的执行结果
//如果方法正常执行,则result为方法返回值,如果方法抛出异常,那么result为该异常。
private Object result;
public Call(){}
public Call(String className,String methodName,Class[] paramTypes,
Object[] params){
this.className=className;
this.methodName=methodName;
this.paramTypes=paramTypes;
this.params=params;
}
public String getClassName(){return className;}
public void setClassName(String className){this.className=className;} public String getMethodName(){return methodName;}
public void setMethodName(String methodName){this.methodName=methodName;} public Class[] getParamTypes(){return paramTypes;}
public void setParamTypes(Class[] paramTypes){this.paramTypes=paramTypes;} public Object[] getParams(){return params;}
public void setParams(Object[] params){this.params=params;} public Object getResult(){return result;}
public void setResult(Object result){this.result=result;} public String toString(){
return "className="+className+" methodName="+methodName;
}
}
|
SimpleClient调用SimpleServer端的HelloServiceImpl对象的echo()方法的流程如下。
(1)SimpleClient创建一个Call对象,它包含了调用HelloService接口的echo()方法的信息。
(2)SimpleClient通过对象输出流把Call对象发送给SimpleServer。
(3)SimpleServer通过对象输入流读取Call对象,运用反射机制调用HelloServiceImpl对象的echo()方法,把echo()方法的执行结果保存到Call对象中。
(4)SimpleServer通过对象输出流把包含了方法执行结果的Call对象发送给SimpleClient。
(5)SimpleClient通过对象输入流读取Call对象,从中获得方法执行结果。
#p#
如例程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){
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 {
new SimpleClient().invoke();
}
}
|
先运行命令“java remotecall.SimpleServer”,再运行命令“java remotecall. SimpleClient”,SimpleClient端将打印“echo:Hello”。该打印结果是服务器端执行HelloServiceImpl对象的echo()方法的返回值。如图10-1所示显示了SimpleClient与SimpleServer的通信过程。
分享到:
相关推荐
这是因为Java反射机制不仅是Java语言的一项重要特性,也是理解Java动态特性的关键所在。通过本文,我们将深入探讨Java反射机制的核心概念、基本原理及其应用场景。 #### 二、Java反射机制简介 Java反射机制允许...
Java反射机制是Java语言的一个重要特性,它允许程序在运行时获取类的信息并操作对象。Java反射机制的主要作用包括:获取类的所有属性和方法、构造动态实例、调用类的方法等。通过反射,程序可以动态地创建对象和调用...
Java作为一种强大的编程语言,提供了许多高级特性,其中反射机制尤为突出。通过反射,开发者可以在运行时动态地获取类的信息并操纵类的行为,从而实现更加灵活和扩展性强的程序设计。 #### 二、Java中的类反射 ###...
JAVA反射机制是JAVA语言中的一种动态机制,它能够在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法。这种动态获取的信息以及动态调用对象的方法的...
在Java语言中,反射机制是通过JVM(Java虚拟机)逆向执行的过程来实现的。具体来说,反射机制允许在运行时动态创建类的实例,动态调用类的方法,以及访问和设置类的属性等。本文将深入探讨Java反射机制的原理及其在...
此外,LEAD/LEAD++、OpenC++、MetaXa和OpenJava等编程语言都是基于反射机制构建的。 反射不仅限于编程语言领域,近年来也被广泛应用于视窗系统、操作系统以及文件系统中。尽管“反射”一词在计算机科学中有着全新的...
Java 1.5引入的反射机制是其动态性的一个重要体现,它允许程序在运行时检查和操作类、接口、对象及其方法。反射为开发者提供了强大的能力,可以在不知道具体类名的情况下实例化对象,调用方法,访问字段,以及处理...
Java语言的反射机制是Java语言中一个非常重要的机制,它允许程序在运行时加载、探知、使用编译期间完全未知的class。这种“看透class”的能力被称为introspection。反射机制的概念是由Smith在1982年首次提出的,主要...
Java反射机制是Java编程语言...总之,Java反射机制是Java语言中一个强大的工具,它增强了Java的灵活性,允许开发者在运行时探索和操作类的内部结构。尽管反射带来了诸多便利,但也需要合理使用,以平衡灵活性和稳定性。
这是Java语言特有的功能,使得Java程序比其他语言(如C、C++)更加灵活。 ##### 2.1 Reflection的工作机制示例 下面通过一个简单的示例来展示Java反射机制的工作流程: ```java import java.lang.reflect.*; ...
Java反射机制是Java语言的一种重要特性,使得Java成为了一种动态性很强的语言。通过反射,可以在程序运行时获取类的信息,包括类名、父类、接口、字段、方法等,并能够调用类的任意方法。这种能力对于构建高度灵活和...
反射机制是Java编程语言的一个核心特性,它允许程序在运行时动态地获取类的信息,并且能够动态地创建对象和调用对象的方法。简单来说,反射机制使得Java程序可以自我检查、自我调整。 在Java中,所有的类型都是`...
Java语言的反射机制是其强大的特性之一,它允许在运行时检查类、接口、字段和方法的信息,并且能够动态地创建对象和调用方法。这一机制对于理解和利用Java的灵活性至关重要,尤其在元编程、框架设计、插件系统以及...
Java 反射机制是 Java 语言中的一个重要特性,它允许程序在运行时动态地获取类的信息(如类名、属性、方法等)并调用对象的方法,甚至修改对象的状态。这一机制极大地增强了 Java 程序的灵活性和可扩展性,尤其是在...
### Java反射机制总结 ...总之,Java反射机制是Java语言的一个重要特性,它提供了在运行时动态获取类信息和操作类结构的能力。合理使用反射可以提高程序的灵活性和扩展性,但在使用时也需要考虑到其潜在的问题和限制。