Java反射机制的深入应用
2017-10-01 08:24
330 查看
前言
在上一篇文章中介绍了Java反射的基本概念以及基本应用,不熟悉的朋友可以点这里本篇文章将重点介绍反射机制的深入应用
反射除了可以取得一个类的完整结构外,还可以调用类中的指定方法或指定属性,并且可以通过反射完成对数组的操作。
通过反射调用类中的方法
如果要使用反射调用类中的方法可以通过Method类完成,操作步骤如下:(1) 通过Class类的getMethod(String name,Class….parameterTypes)方法取得一个Method的对象,并设置此方法操作时所需要的参数类型。
(2)之后可以使用invoke()进行调用,并向方法中传递要设置的参数。
下面通过一个具体的例子来为读者演示操作,此操作主要完成调用Person类中sayChina()方法的功能。(person类的定义在上一篇文章)
import java.lang.reflect.Method; public class invokeSayChinaDemo{ public static void main(String[] args){ Class<?> c1=null; //声明Class对象 try{ c1=Class.forName("org.ljz.demo.Person"); //实例化Class对象 }catch(ClassNotFoundException e){ e.printStackTrace(); } try{ Method met=c1.getMethod("sayChina"); //此方法没有参数 met.invoke(c1.newInstance()); //调用方法,必须传递对象实例 }catch(Exception e){ e.printStackTtace(); } } }
程序运行结果:
作者: ljz, 国籍: China
以上程序通过Class类的getMethod()方法根据一个类中的方法名称取得Method对象,并通过invoke()调用指定的方法。在使用invoke()方法时必须传入一个类的实例化对象,因为在sayChina()方法上没有任何的参数,所以此处没有设置参数类型和参数内容,本程序的操作如图所示
下面再为读者演示一个向方法中传递参数的实例,以调用Person类中的sayHello(String name;int age)方法为例,此方法需要传递两个参数。
package org.ljz.demo.invokedemo; import java.lang.reflect.Method; public class invokeSayHelloDemo{ public static void main(String[] args){ class<?> c1=null; //声明Class对象 try{ c1=Class.forName("org.ljz.demo.Person"); //实例化Class对象 }catch(ClassNotFoundException e){ e.printStackTrace(); } try{ Method met=c1.getMethod("sayHello", String.class,int.class); //此方法需要两个参数 String rv=null; //接收方法的返回值 //调用方法,必须传递对象实例,同时传递两个参数值 rv=(String)met.invoke(c1.newInstance(),"ljz",23); System.out.println(rv); }catch(Exception e){ e.printStackTrace(); } } }
程序运行结果:
ljz,你好!我今年23岁了!
以上程序中,因为sayHello()方法本身要接收两个参数,所以在使用getMethod()方法调用时除了要指定调用的方法名称外,也需要接收参数的类型,由于sayHello()方法调用完之后存在返回值,而且返回值的类型是String,所以使用了一个字符串接收方法返回的内容。
调用setter及getter方法
熟悉面向对象的读者就知道”类中的属性必须封装,封装之后的属性要通过setter及getter”方法设置和取得,那么在使用反射进行调用方法操作中,最重要的是调用类中的setter及getter方法,这一点在Java的开发中随处可见,下面为读者演示如何完成这样的功能。直接调用Person类中的setter及getter方法代码如下:
package org.ljz.demo.invokedemo; import java.lang.reflect.Method; public class InvokeSetGetDemo{ public static void main(String[] args){ Class<?> c1=null; //声明Class对象 Object obj=null; //声明一个对象 try{ c1=Class.forName("org.ljz.demo.Person");//实例化对象 }catch(ClassNotFountException e){ e.printStackTrace(); } try{ obj=c1.newInstance(); //实例化操作对象 }catch(InstantiationException e){ e.printStackTrace(); }catch(IllegalAccessException e){ e.printStackTrace(); } setter(obj,"name","ljz",String.class); //调用setter方法 setter(obj,"age",23,int.class); //调用setter方法 System.out.print("姓名:"); getter(obj,"name"); //调用getter方法 System.out.print("年龄:"); getter(obj,"age"); //调用getter方法 } /** *@param obj 操作的对象 *@param att 操作的属性 *@param value 设置的值 *@param type 参数的类型 */ public static void setter(Object obj,String att,Object value, Class<?> type){ //调用setter方法 try{ Method met=obj.getClass().getMethod("set"+initStr(att),type);//设置方法参数类型 met.invoke(obj,value); //调用方法 }catch(Exception e){ e.printStackTrace(); } } public static void getter(Object obj,String att){ //调用getter方法 try{ Method met=obj.getClass().getMethod("get" + initStr(att)); //此方法不需要参数 System.out.println(met.invoke(obj)); //接收方法的返回值 }catch(Exception e){ e.printStackTrace(); } } public static String initStr(String old){ //单词首字母大写 String str=old.substring(0,1).toUpperCase()+old.substring(1); return str; } }
程序运行结果:
姓名: ljz
年龄:23
以上程序完成了调用类中setter及getter方法的功能,下面我们分步介绍本程序的思路
(1) 在设置方法名称时,本程序直接传入的是类中的属性名称,例如name或age。但是实际上方法名称是setName(0,getName(),setAge(),getAge(),这样,所有属性名称的首字母需要大写,所以为了解决这样的问题,单独设置了一个方法initStr(),通过此方法将字符串中的首字母大写。首字母大写之后在增加set及get字符串以找到相应的方法。
(2)调用setter()方法时,传入了实例化对象,要操作的属性名称(在方法中会将其首字母大写),要设置的参数内容以及具体的参数类型,这样做是为了满足getMethod() 和invoke()方法的使用要求。
(3)在调用getter()方法时,也同样传入同一个实例化对象,因为其本身不需要任何的接收参数,所以只传入属性名称(在方法中会将其首字母大写), 并在此方法中将内容打印输出。
以上程序是在反射调用方法时的重要应用,读者一定掌握其原理。因为在以后的开发中,许多系统会为开发者实现好以上的功能。
通过反射操作属性
在反射操作中虽然可以使用Method调用类中的setter及getter方法设置和取得属性,但是这样操作毕竟很麻烦,所以在反射机制中也可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。但是在操作前首先需要注意的是,在类中的所有属性已经都设置成私有的访问权限,所以在使用set()或get()方法时首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置成可以被外部访问。代码如下:
package org.ljz.demo.invokedemo; import java.lang.reflect.Field; public class invokeFieldDemo{ public static void main(String[] args) throws Exception{ Class<?> c1=null; //声明Class对象 Object obj=bull; //声明一个对象 c1=Class.forName("org.ljz.demo.Person");//实例化Class对象 obj=c1.newInstance(); //实例化对象 Field nameField=null; //表示name属性 Field ageField=null; //表示age属性 naneField=c1.getDeclaredField("name");//取得name属性 ageField=c1.getDeclaredField("age");// 取得age属性 nameField.set(obj,"ljz"); //设置name属性内容 ageField.setAccessible(true); //将age属性设置成可以被外部访问 ageField.set(obj,23); //设置age属性内容 System.out.println("姓名:"+nameField.get(obj)); System.out.println("年龄:"+ageField.get(obj)); } }
程序运行结果:
姓名:ljz
年龄:23
从以上代码不难看出,明显比之前使用setter或getter方法操作属性的代码更加简单,方便。
通过反射操作数组
反射机制不仅只能用在类上,还可以应用在任意的引用数据类型的数据上,当然,这也就包含了数组,即可以使用反射操作数组。可以通过Class类的以下方法取得一个数组的Class对象。public Class<?> getComponentType()
在反射操作包java.lang.reflect中使用Array类表示一个数组,可以通过此类取得数组长度,取得数组内容的操作。Array类的常用方法如下所示:
下面通过两个范例让读者了解以上方法的使用
1. 取得数组信息并修改数组内容
package org.ljz.arraydemo; import java.lang.reflect.Array; public class ClassArrayDemo{ public static void main(String[] args)throws Exception{ int temp[]={1,2,3}; //声明一个整形数组 Class<?> c=temp.getClass().getComponentType();//取得数组的Class对象 System.out.println("类型" +c.getNmae()); //取得数组类型的名称 System.out.println("长度"+Array.getLength(temp));//得到数组的长度 System.out.println("第一个内容:"+Array.get(temp,0));//得到第一个内容 Array.set(temp,0,6); //修改第一个内容 System.out.println("第一个内容"+Array.get(temp,0));//得到第一个内容 } }
程序运行结果:
类型: int
长度:3
第一个内容:1
第一个内容: 6
以上程序中通过Array类取得了数组的相关信息,并通过Array类中的set()方法修改了数组中的元素内容。
在应用中还可以通过Array类根据自己已有的数组类型来开辟新的数组对象,下面我们就来使用Array完成一个可以修改已有数组大小的功能。
2.修改数组的大小
package org.ljz.arraydemo import java.lang.reflect.Array; public class ChangeArrayDemo{ public static void main(String[] args)throws Exception{ int temp[]={1,2,3}; int newTemp[]=(int[])arrayInc(temp,5);//扩大数组的长度 String t[]={"zhangsan","lisi","wangwu"};//声明一个字符串数组 String nt[]=(String[])arrayInc(t,6);//扩大数组长度 print(nt);//打印数组信息 } public static Object arrayinc(Object obj,int len){ //修改数组长度 Class<?> c=obj.getClass(); //通过数组得到Class对象 Class<?> arr=c.getComponentType();//得到数组的Class对象 Object newO=Array.newInstance(arr,len);//重新开辟新的数组空间 int co=Array.getLength(obj);//取得数组长度 System.arraycopy(obj,0,newO,0,co)//复制数组内容 return newO; } public static void print(Object obj){ //输出数组 Class<?> c=obj.getClass(); //通过数组得到Class对象 if(!c.isArray()){ //判断是否是数组 return; } Class<?> arr=c.getComponentType();//取得数组的Class System.out.println(arr.getName()+"数组的长度:"+Array.getLength(obj));//输出数组的信息 for(int i=0;i<Array.getLength(obj);i++){ //循环输出 System.out.print(Array.get(obj,i)+",");//通过Array输出 } } }
程序运行结果:
int数组的长度是:5
1,2,3,4,5
java.lang.String数组的长度是6
zhangsan ,lisi, wangwu, null, null, null
后续
下一篇我将介绍动态代理以及反射应用到工厂设计模式的相关应用,感兴趣的朋友可以关注一下!相关文章推荐
- JAVA反射机制深入学习(二)实例演示JAVA反射机制的应用
- JAVA反射机制深入学习(二)实例演示JAVA反射机制的应用
- java事务 深入Java事务的原理与应用
- [原创] Enterprise Library深入解析与灵活应用(6):自己动手创建迷你版AOP框架
- java反射机制的原理与应用
- 探讨代理模式与Java反射机制的应用
- 声波通信(声波传输)在iOS、Android中的应用场景深入分析(含部分声波通信源代码)
- Java反射机制深入详解
- [转]深入解读PHP类phpExcel应用
- 深入理解Java对象的序列化与反序列化的应用
- 深入分析Windows和Linux动态库应用异同
- 深入对C#中委托的理解及应用
- 深入了解sql语句以及应用实例
- Android开发者必须深入学习的10个应用开源项目
- 深入正则表达式应用
- Enterprise Library深入解析与灵活应用(8):WCF与Exception Handling AppBlock集成[上]
- 从零开始学习 ASP.NET MVC 1.0 (三) Controller/Action 深入解析与应用实例
- 深入了解Struts2返回JSON数据的原理及具体应用范例
- WPF基础到企业应用系列7——深入剖析依赖属性(WPF/Silverlight核心)
- 深入探讨C++父类子类中虚函数的应用