java反射机制与动态代理(二)

本文深入探讨Java反射机制,包括无参构造器与带参数构造器的使用方式,并演示如何通过反射调用方法及更改字段值。

欲生成对象实体,在Reflection 动态机制中有两种作法,一个针对“无自变量ctor”,一个针对“带参数ctor”。如果欲调用的是“带参数ctor“就比较麻烦些,不再调用Class的newInstance(),而是调用Constructor 的newInstance()。首先准备一个Class[]做为ctor的参数类型,然后以此为自变量调用getConstructor(),获得一个专属ctor。接下来再准备一个Object[] 做为ctor实参值,调用上述专属ctor的newInstance()。

下面做个例子,该例子的反射对象没有构造方法(实际上是默认的构造方法),无自变量,动态生成“Class object 所对应之class”的对象实体,代码如下:

首先建立com.lansin.ghk包,在其下建立两个类:Person.java和Test1.java。

Person.java代码
  1. packagecom.lansin.ghk;
  2. publicclassPerson{
  3. privateStringname;
  4. privateStringaddress;
  5. publicStringgetName(){
  6. returnname;
  7. }
  8. publicvoidsetName(Stringname){
  9. this.name=name;
  10. }
  11. publicStringgetAddress(){
  12. returnaddress;
  13. }
  14. publicvoidsetAddress(Stringaddress){
  15. this.address=address;
  16. }
  17. @Override
  18. publicStringtoString(){
  19. return"名称为"+this.getName()+",地址为"+this.getAddress();
  20. }
  21. }
package com.lansin.ghk;
public class Person{
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "名称为" + this.getName() + ", 地址为" + this.getAddress();
}
} 

Test1.java代码
  1. packagecom.lansin.ghk;
  2. importjava.lang.reflect.Field;
  3. importjava.lang.reflect.Method;
  4. publicclassTest1{
  5. publicstaticvoidmain(String[]args)throwsException{
  6. Class<?>classType=Class.forName("com.lansin.ghk.Person");
  7. Objectobj=null;
  8. obj=classType.newInstance();
  9. Fieldfields[]=classType.getDeclaredFields();
  10. for(inti=0;i<fields.length;i++){
  11. Fieldfield=fields[i];
  12. StringfieldName=field.getName();
  13. StringfirstLetter=fieldName.substring(0,1).toUpperCase();
  14. StringgetMethodName="get"+firstLetter+fieldName.substring(1);
  15. StringsetMethodName="set"+firstLetter+fieldName.substring(1);
  16. MethodgetMethod=classType.getMethod(getMethodName);
  17. MethodsetMethod=classType.getMethod(setMethodName,field.getType());
  18. if("name".equals(fieldName)){
  19. setMethod.invoke(obj,"迈克·泰森");
  20. }
  21. elseif("address".equals(fieldName)){
  22. setMethod.invoke(obj,"美国");
  23. }
  24. }
  25. System.out.println(obj);
  26. }
  27. }
package com.lansin.ghk;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test1 {
public static void main(String[] args) throws Exception {
Class<?> classType = Class.forName("com.lansin.ghk.Person");
Object obj = null;
obj = classType.newInstance();
Field fields[] = classType.getDeclaredFields();
for(int i=0; i<fields.length;i++){
Field field = fields[i];
String fieldName = field.getName();
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getMethodName = "get" + firstLetter + fieldName.substring(1);
String setMethodName = "set" + firstLetter + fieldName.substring(1);
Method getMethod = classType.getMethod(getMethodName);
Method setMethod = classType.getMethod(setMethodName,field.getType());
if("name".equals(fieldName)){
setMethod.invoke(obj, "迈克·泰森");
}
else if("address".equals(fieldName)){
setMethod.invoke(obj, "美国");
}
}
System.out.println(obj);
}
}

运行结果为:“名称为迈克·泰森, 地址为美国”。

下面做个例子,该例子的反射对象包含构造方法,有自变量,动态生成“Class object 所对应之class”的对象实体,代码如下:

有构造方法的Person类只需在上面的Person类里加一个构造方法;Test2类“反射”Person。

修改后的person.java代码
  1. packagecom.lansin.ghk;
  2. publicclassPerson{
  3. privateStringname;
  4. privateStringaddress;
  5. publicPerson(Stringname,Stringaddress){
  6. this.name=name;
  7. this.address=address;
  8. }
  9. publicStringgetName(){
  10. returnname;
  11. }
  12. publicvoidsetName(Stringname){
  13. this.name=name;
  14. }
  15. publicStringgetAddress(){
  16. returnaddress;
  17. }
  18. publicvoidsetAddress(Stringaddress){
  19. this.address=address;
  20. }
  21. @Override
  22. publicStringtoString(){
  23. return"名称为"+this.getName()+",地址为"+this.getAddress();
  24. }
  25. }
package com.lansin.ghk;
public class Person{
private String name;
private String address;
public Person(String name, String address){
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "名称为" + this.getName() + ", 地址为" + this.getAddress();
}
} 

Test2.java代码
  1. packagecom.lansin.ghk;
  2. importjava.lang.reflect.Constructor;
  3. publicclassTest2{
  4. publicstaticvoidmain(String[]args)throwsException{
  5. Class<?>c=Class.forName("com.lansin.ghk.Person");
  6. Class[]pTypes=newClass[]{String.class,String.class};
  7. Constructorctor=c.getConstructor(pTypes);
  8. Objectobj=null;
  9. Object[]arg=newObject[]{"迈克·泰森","美国"};
  10. obj=ctor.newInstance(arg);
  11. System.out.println(obj);
  12. }
  13. }
package com.lansin.ghk;
import java.lang.reflect.Constructor;
public class Test2 {
public static void main(String[] args) throws Exception {
Class<?> c = Class.forName("com.lansin.ghk.Person");
Class[] pTypes = new Class[]{String.class, String.class};
Constructor ctor = c.getConstructor(pTypes);
Object obj = null;
Object[] arg = new Object[]{"迈克·泰森", "美国"};
obj = ctor.newInstance(arg);
System.out.println(obj);
}
}

运行,和上个程序结果一样:“名称为迈克·泰森, 地址为美国”。

比较上面两段程序:首先要提供一个对象类的地址全称(包名+类名)。

(一)对于没有构造函数的类,在运行时刻创建该对象所属类的对象实例:

先声明一个泛型Class,

Class<?> classType = Class.forName("com.lansin.ghk.Person");

然后由泛型对象classType生成实例,

Object obj= classType.newInstance();

接下来调用反射机制提供的各种方法进行动态处理;

(二)对于有构造函数的类,在运行时刻创建该对象所属类的对象实例:

同样要先声明一个泛型Class,

Class<?> classType = Class.forName("com.lansin.ghk.Person");

创建一个“类型类”集合,因为Person类的构造函数有两个string类型的形参,

Class[] pTypes = new Class[]{String.class, String.class};

接下来由生成的“由对象在运行时所生成所属类的对象”来创建一个带有形参(是个集合)的构造器,

Constructor ctor = classType .getConstructor(pTypes);

最后由构造器生成一个实例对象,但是首先要设定实参,

设定实参:Object[] arg = new Object[]{"迈克·泰森", "美国"};

实例化对象:Object obj= ctor.newInstance(arg);

OK了。

其实到这里我还有很多细节没有说,这个要在以后的工作中多多学习,多多参考文档,java api是个好东西。

下面的例子是在运行时调用Method,代码如下:

Invoketester.java代码
  1. publicclassInvokeTester{
  2. publicintadd(intparam1,intparam2){
  3. returnparam1+param2;
  4. }
  5. publicStringecho(Stringmsg){
  6. return"echo:"+msg;
  7. }
  8. publicstaticvoidmain(String[]args)throwsException{
  9. Class<?>classType=InvokeTester.class;
  10. ObjectinvokeTester=classType.newInstance();
  11. //调用InvokeTester对象的add()方法
  12. MethodaddMethod=classType.getMethod("add",newClass[]{int.class,
  13. int.class});
  14. Objectresult=addMethod.invoke(invokeTester,newObject[]{
  15. newInteger(100),newInteger(200)});
  16. System.out.println((Integer)result);
  17. //调用InvokeTester对象的echo()方法
  18. MethodechoMethod=classType.getMethod("echo",
  19. newClass[]{String.class});
  20. result=echoMethod.invoke(invokeTester,newObject[]{"Hello"});
  21. System.out.println((String)result);
  22. }
  23. }
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);
}
} 

这个动作和上述调用“带参数之ctor”相当类似。首先准备一个Class[]做为参数类型(本例指定其中一个是String,另一个是Hashtable),然后以此为自变量调用getMethod(),获得特定的Method object。接下来准备一个Object[]放置自变量,然后调用上述所得之特定Method object的invoke()。
为什么获得Method object时不需指定回返类型?

因为method overloading机制要求signature必须唯一,而回返类型并非signature的一个成份。换句话说,只要指定了method名称和参数列,就一定指出了一个独一无二的method。

下面的类是运行时变更Field的内容,比较简单,代码如下:

Testfield.java代码
  1. packagecom.lansin.ghk;
  2. importjava.lang.reflect.Field;
  3. publicclassTestField{
  4. publicdoubled;
  5. publicstaticvoidmain(String[]args)throwsException{
  6. Classc=Class.forName("com.lansin.ghk.TestField");
  7. Fieldf=c.getField("d");
  8. TestFieldobj=newTestField();
  9. System.out.println("d="+(Double)f.get(obj));
  10. f.set(obj,12.34);
  11. System.out.println("d="+obj.d);
  12. }
  13. }
package com.lansin.ghk;
import java.lang.reflect.Field;
public class TestField {
public double d;
public static void main(String[] args) throws Exception {
Class c = Class.forName("com.lansin.ghk.TestField");
Field f = c.getField("d");
TestField obj = new TestField();
System.out.println("d= " + (Double)f.get(obj));
f.set(obj, 12.34);
System.out.println("d= " + obj.d);
}
} 

与先前两个动作相比,“变更field内容”轻松多了,因为它不需要参数和自变量。首先调用Class的getField()并指定field名称。获得特定的Field object之后便可直接调用Field的get()和set()。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值