`
newleague
  • 浏览: 1499920 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类

Java网络编程精解之Java语言的反射机制一

阅读更多

在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的通信过程。

 

图10-1  SimpleClient与SimpleServer的通信过程

相关文章链接:Java网络编程精解之Java语言的反射机制二

分享到:
评论

相关推荐

    Java网络编程精解(孙卫琴)电子教案

    《Java网络编程精解》是孙卫琴老师的一本经典教程,主要涵盖了Java语言在网络编程领域的深度解析。这本书深入浅出地介绍了如何使用Java进行网络通信,包括基本的TCP/IP协议、套接字编程、多线程技术以及HTTP、FTP等...

    Java编程案例精解源代码

    Java编程是信息技术领域中最受欢迎的编程语言之一,尤其在企业级应用开发中占据主导地位。"Java编程案例精解源代码"提供了丰富的实例,帮助学习者深入理解和掌握Java编程技术。这个压缩包文件包含了与书本配套的完整...

    Java网络编程精解PPT课件.ppt

    Java网络编程精解PPT课件.ppt 本资源摘要信息是关于Java网络编程的PPT课件,主要介绍了基于UDP的数据报和套接字的相关知识点。 UDP协议简介 UDP(User Datagram Protocol,用户数据报协议)是一种传输层协议,...

    读书笔记:JAVA网络编程精解.zip

    读书笔记:JAVA网络编程精解

    Java网络编程精解之ServerSocket用法详解

    Java网络编程的核心在于客户端与服务器端的交互,而ServerSocket是Java中用于服务器端的类,它使得服务器能够监听特定端口,接收客户端的连接请求。本文将详细讲解ServerSocket的使用方法及其在多线程环境下的应用。...

    读书笔记:孙卫琴《Java网络编程精解》源码.zip

    读书笔记:孙卫琴《Java网络编程精解》源码

    java编程案例精解

    Java编程是计算机科学领域中最广泛使用的编程语言之一,尤其在企业级应用开发中占据着核心地位。本资源“Java编程案例精解”旨在通过实际案例深入解析Java编程的各个方面,帮助学习者掌握Java编程的核心技术和实战...

    java网络编程(非阻塞与阻塞编程)

    在深入探讨Java网络编程中的非阻塞与阻塞编程之前,我们先来了解这两个概念的基本含义。阻塞编程,通常指的是在程序执行过程中,当某一部分代码遇到I/O操作时,如读写文件或网络通信,整个程序会暂停运行,等待I/O...

    Java编程案例精解素材.rar

    本资源“Java编程案例精解素材.rar”包含了一系列实用的Java编程示例,旨在帮助学习者深入理解和掌握Java的核心概念与技术。 1. **使用邮件客户端工具**:JavaMail API是Java中用于处理电子邮件的库,它允许开发者...

    Java编程案例精解

    《Java编程案例精解》是一本深入探讨Java编程实践的书籍,其光盘资料包含了丰富的实例代码和教学资源,旨在帮助读者深入理解Java语言的核心概念和技术。这份资料涵盖了从基础语法到高级特性的广泛内容,适合初学者和...

    JavaScript 编程精解 中文第三版

    JavaScript 编程精解 中文第三版 JavaScript 编程精解 中文第三版

    java网络精解源码

    1. **Java网络API**: Java提供了一系列的API用于网络编程,包括Socket、ServerSocket、DatagramSocket和DatagramPacket等类。Socket用于实现TCP连接,ServerSocket则用于创建服务器端的监听套接字,而DatagramSocket...

    java网络编程

    "Java网络编程精解-孙卫琴.rar"可能是孙卫琴老师的著作,她是一位知名的Java教育专家。这本书可能详细解析了Java网络编程的各个方面,包括URL类的使用、HTTP协议的实现、NIO(非阻塞I/O)以及高级网络编程技巧。通过...

    Java数据库编程技术精解

    Java数据库编程技术精解,是Java开发者必备的技能之一,主要围绕JDBC(Java Database Connectivity)接口进行深入探讨。JDBC是Java平台中用于与各种数据库进行交互的标准API,无论你是初学者还是经验丰富的开发人员...

    Java编程精解(孙卫琴)

    Java编程精解(孙卫琴) 经典Java书籍,你值得拥有

    《JavaScript编程精解》.pdf

    文件标题是“《JavaScript编程精解》.pdf”,描述中提到了“JavaScript 编程精解 中文第一版”,而标签同样为“JavaScript 编程精解”。部分内容重复提及了访问“稀酷客”网站的链接,这可能是出版商提供的额外资源...

    高级语言C++程序设计编程范例与精解

    《高级语言C++程序设计编程范例与精解》是一本深入探讨C++编程语言的教材,旨在帮助读者从基础知识到高级概念全面掌握C++。作为《高级语言C++程序设计》的配套书籍,它提供了丰富的实例和详尽的解析,以实践驱动的...

    java网络编程_孙卫琴_有书签

    《Java网络编程精解》是孙卫琴先生的一本经典著作,主要针对Java开发者,深入讲解了Java在网络编程领域的核心知识。这本书涵盖了从基础概念到高级应用的多个方面,旨在帮助读者理解并掌握Java如何进行网络通信。以下...

    Java多线程编程精解

    【Java多线程编程精解】 Java语言的多线程特性使得开发者可以在同一个程序中同时运行多个线程,实现并发处理,从而提高程序的效率和响应性。在Java中,多线程编程主要涉及到以下几个核心知识点: 1. **线程与线程...

Global site tag (gtag.js) - Google Analytics