`
xyheqhd888
  • 浏览: 403685 次
  • 性别: Icon_minigender_1
  • 来自: 秦皇岛
社区版块
存档分类
最新评论

使用反射生成与操作对象(一)

阅读更多

1. 使用反射机制,可以在运行时期动态加载类并生成对象,操作对象上的方法、改变类成员的值,甚至连私有成员的值也可以改变。

2. 生成对象:可以使用Class的newInstance()方法来实例化一个对象,实例化的对象以Object类型返回。例如:

  

Class c = Class.forName(className);
Object obj = c.newInstance();

 下面是一个简单的示范,可以动态加载实现了List接口的类。

package ysu.hxy;
import java.util.*;

public class NewInstanceDemo 
{
	public static void main(String[] args) 
	{
		try
		{
			Class c = Class.forName(args[0]);
			List list = (List)c.newInstance();

			for(int i = 0;i < 5;i++)
			{
				list.add("element " + i);
			}

			for(Object o: list.toArray())
			{
				System.out.println(o);
			}
		}catch(ClassNotFoundException e)
		{
			System.out.println("找不到指定的类");
		}
		catch(InstantiationException e)
		{
			e.printStackTrace();
		}
		catch(IllegalAccessException e)
		{
			e.printStackTrace();
		}
	}
}

 

运行结果如下:

D:\hxy>java ysu.hxy.NewInstanceDemo java.util.ArrayList
element 0
element 1
element 2
element 3
element 4

     实际上如果想要使用反射来动态加载类,通常是对对象的接口或者类型都一无所知,也就无法像上面范例中那样对newInstance()返回的对象进行接口转换动作。后面会介绍如何以反射来调用方法以操作newInstance()所返回的对象。

     如果加载的类中具备无参数的构造函数,则可以无参数的newInstance()来构建一个不指定初始变量的对象。如果在动态加载及生成对象时指定对象的初始化变量,则要先指定参数类型、取得Constructor对象、使用Constructor的newInstance()并指定参数的接受值。

     下面以一个例子来说明,先来定义一个Student类。

package ysu.hxy;

public class Student1
{
	private String name;
	private int score;

	public Student1()
	{
		name = "N/A";
	}

	public Student1(String name,int score)
	{
		this.name = name;
		this.score = score;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public void setScore(int score)
	{
		this.score = score;
	}

	public String getName()
	{
		return name;
	}

	public int getScore()
	{
		return score;
	}

	public String toString()
	{
		return name + ":" + score;
	}
}

 可以用Class.forName()来加载student1类,并使用第二个有参数的构造函数来构建student实例。如下所示:

package ysu.hxy;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class NewInstanceDemo2
{
	public static void main(String[] args) 
	{
		try
		{
			Class c = Class.forName(args[0]);

			//指定参数类型
			Class[] params = new Class[2];
			//第一个参数是String
			params[0] = String.class;
			//第二个参数是int,在指定基本类型时要使用对应的包类并使用.TYPE。例如指定int类型时,使用Integer.TYPE,如果要指定Integer类型的参数,才是使用Integer.class。
			params[1] = Integer.TYPE;
			
			//取得对应参数列的构造函数
			Constructor constructor = 
				c.getConstructor(params);

			//指定变量内容
			Object[] argObjs = new Object[2];
			argObjs[0] = "caterpillar";
			argObjs[1] = new Integer(90);

			//给定变量并实例化
			Object obj = constructor.newInstance(argObjs);
			//调用toString()来查看描述
			System.out.println(obj);
		}
		catch(ClassNotFoundException e)
		{
			System.out.println("找不到类");
		}
		catch(SecurityException e)
		{
			e.printStackTrace();
		}
		catch(NoSuchMethodException e)
		{
			System.out.println("没有指定的方法");
		}
		catch(IllegalArgumentException e)
		{
			e.printStackTrace();
		}
		catch(InstantiationException e)
		{
			e.printStackTrace();
		}
		catch(IllegalAccessException e)
		{
			e.printStackTrace();
		}
		catch(InvocationTargetException e)
		{
			e.printStackTrace();
		}
	}
}

 注意,在指定基本类型时,要使用对应的包类(Wrapper)并使用.TYPE。例如指定int类型时,则使用Integer.TYPE,如果要指定Integer类型的参数,才是使用Integer.class。上面的范例会根据指定的变量调用对应的构造函数,运行结果如下:

        D:\hxy>java ysu.hxy.NewInstanceDemo2 ysu.hxy.Student1
        caterpillar:90

3. 调用方法:

   使用反射可以取回类的方法的对象代表,方法的对象代表是java.lang.reflect.Method的实例,可以使用它的invoke() 方法来动态调用指定的方法。如调用上面Student1类的setName()方法,这里以下面范例作为示范:

 

package ysu.hxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class InvokeMethodDemo
{
	public static void main(String[] args) 
	{
		try
		{
			Class c = Class.forName(args[0]);
			//使用无参数构造函数建立对象
			Object targetObj = c.newInstance();
			//设定参数类型
			Class[] param1 = {String.class};
			//根据参数类型取回方法对象
			Method setNameMethod = c.getMethod("setName",param1);
			//设定变量值
			Object[] argObjs1 = {"caterpillar"};
			//给定变量调用指定对象上的方法,使用方法代表的invoke()方法
			setNameMethod.invoke(targetObj,argObjs1);

			Class[] param2 = {Integer.TYPE};
			Method setScoreMethod = c.getMethod("setScore",param2);
			Object[] argObjs2 = {new Integer(90)};
			setScoreMethod.invoke(targetObj,argObjs2);
			//显示对象描述
			System.out.println(targetObj);
		}
		catch(ClassNotFoundException e)
		{
			System.out.println("找不到类");
		}
		catch(SecurityException e)
		{
			e.printStackTrace();
		}
		catch(NoSuchMethodException e)
		{
			System.out.println("没有这个方法");
		}
		catch(IllegalArgumentException e)
		{
			e.printStackTrace();
		}
		catch(IllegalAccessException e)
		{
			e.printStackTrace();
		}
		catch(InvocationTargetException e)
		{
			e.printStackTrace();
		}
		catch(InstantiationException e)
		{
			e.printStackTrace();
		}
	}
}

 

 运行结果:

D:\hxy>java ysu.hxy.InvokeMethodDemo ysu.hxy.Student1
caterpillar:90

      此范例指定加载Student1类并生成实例,接着可以动态调用setName()与setScore()方法。范例中参数类型与变量值的设定与前面的范例是类似的,由于调用setName()和setScore()所给定的变量是caterpillar与90,故运行结果也是一样的。

      很少的情况下,会需要突破Java的存取限制来调用受保护的或私有的方法(例如有一个组件,但没法修改它的源代码来改变某个私有方法的权限,又一定要调用某个私有方法),这时可以使用反射机制来达到目的。一个存取私有方法的例子如下:

Method privateMethod = 
           c.getDeclaredMethod("somePrivateMethod",new Class[0]);
privateMethod.setAccessible(true);
privateMethod.invoke(targetObj,argObjs);

    使用反射来动态调用方法的实际例子之一是JavaBean的设定,例如在JSP/Servlet中,可以根据使用者的请求名称与JavaBean的属性名称自动比对,将字符串请求值设定至指定的JavaBean上,并自动根据参数类型作类型转换。下面是一个简单的示例,可以给CommandUtil工具类一个Map对象与类名称,然后取得一个更新了值的实例,其中参数Map对象的键为要调用的setter方法名称(不含set,如setName()方法,只要给定键为name即可),而值为要设定给setter的变量。

package ysu.hxy;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;

public class CommandUtil
{
	//给定Map对象及要产生的Bean类名称
	//可以取回已经设定完成的对象
	public static Object getCommand(Map requestMap,String commandClass) throws Exception
	{
        Class c = Class.forName(commandClass);
		Object o = c.newInstance();

		return updateCommand(requestMap,o);
	}

	//使用reflection自动找出要更新的属性
	public static Object updateCommand(Map requestMap,Object command) throws Exception
	{
		Method[] methods = command.getClass().getDeclaredMethods();

		for(int i = 1;i < methods.length; i++)
		{
            //略过private、protected成员且找出必须是以set开头的方法名称
			if(!Modifier.isPrivate(methods[i].getModifiers()) &&
				   !Modifier.isProtected(methods[i].getModifiers()) &&
				      methods[i].getName().startsWith("set"))
			{
				//取得不包括set的名称
				String name = methods[i].getName().substring(3).toLowerCase();

				//如果setter名称与键值相同
				//调用对应的setter并设定值
				if(requestMap.containsKey(name))
				{
					String param = (String)requestMap.get(name);
					Object[] values = findOutParamValues(param,methods[i]);
					methods[i].invoke(command,values);
				}
			}
		}
		return command;
	}

	//转换为对应类型的值
	private static Object[] findOutParamValues(String param,Method method)
	{
        Class[] params = method.getParameterTypes();
		Object[] objs = new Object[params.length];
        
		for(int i = 0;i < params.length;i++)
		{
			if(params[i] == String.class)
			{
				objs[i] = param;
			}
			else if(params[i] == Short.TYPE)
			{
				short number = Short.parseShort(param);
				objs[i] = new Short(number);
			}
			else if(params[i] == Integer.TYPE)
			{
				Integer number = Integer.parseInt(param);
				objs[i] = new Integer(number);
			}
			else if(params[i] == Long.TYPE)
			{
				Long number = Long.parseLong(param);
				objs[i] = new Long(number);
			}
			else if(params[i] == Float.TYPE)
			{
				Float number = Float.parseFloat(param);
				objs[i] = new Float(number);
			}
			else if(params[i] == Double.TYPE)
			{
				Double number = Double.parseDouble(param);
				objs[i] = new Double(number);
			}
			else if(params[i] == Boolean.TYPE)
			{
				Boolean number = Boolean.parseBoolean(param);
				objs[i] = new Boolean(number);
			}
		}
        return objs;
	}
};

 CommandUtil可以自动根据方法上的参数类型,将Map对象中的值对象转换为属性上的对应类型,目前它可以转换基本类型与String类型的属性。一个使用CommandUtil类的例子如下:

package ysu.hxy;
import java.util.*;

public class CommandUtilDemo
{
	public static void main(String[] args) throws Exception
	{
		Map<String,String> request = new HashMap<String,String>();
		request.put("name","caterpillar");
		request.put("score","90");
		Object obj = CommandUtil.getCommand(request,args[0]);
		System.out.println(obj);
	}
}

 可以使用此范例加载Student1类,使用CommandUtil.getCommand()方法可以返回一个设定好值的Student实例。虽然设定给request的值是字符串类型,但CommandUtil会使用反射机制来自动转换为属性上的对应类型。一个运行的范例如下:

           D:\hxy>java ysu.hxy.CommandUtilDemo ysu.hxy.Student1
           caterpillar:90

通过规范方法的命名方式,就可以再通过反射机制加上方法名称的比对,以正确调用对应的方法。  

分享到:
评论

相关推荐

    Java反射 JavaBean对象自动生成插入,更新,删除,查询sql语句操作.docx

    Java反射 JavaBean对象自动生成插入,更新,删除,查询sql语句操作.docx

    仿hibernate动态生成sql保存对象

    反射对象生成sql,保存对象。 里面还差,存储过程、批量操作、动态sql 还差一个配套的工具(根据数据库字段生成对象)

    Java反射 JavaBean对象自动生成插入,更新,删除,查询sql语句操作

    主要介绍了Java反射 JavaBean对象自动生成插入,更新,删除,查询sql语句操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

    使用反射技术和Facade模式演示封装数据库操作--ORM原理

    那么书写操作数据库的代码很简单:只需呼叫FacadeForDAO.findModel(表名)方法,在表名参数给出实际数据库中表的名称就可以返回一个包含对应POJO类的对象的集合--非常简单--输入表名得到Java对象的集合,然后根据需要...

    xml与反射.txt

    反射是一种间接操作目标对象的机制,在程序程序运行时获取或者设置对象自身的信息。 只要给定类的名字,就可以通过反 射获取类的所有信息,接着便能调用它的任何一个方法和属性。 反射的步骤有哪些? 第一:获取类...

    ORM框架-VB/C#.Net实体代码生成工具(EntitysCodeGenerate)【ECG 4.2】 2011迎新版

    是一款专门为VB/C#.Net数据库程序员开发量身定做的(ORM框架)代码生成工具,所生成的代码基于面向对象、分层架构设计、ORM并参考微软Petshop中的经典思想,使用改进的抽象工厂设计模式及反射机制等。目前直接支持...

    C#.Net实体代码生成工具 v3.0

    C#.Net实体代码生成工具(EntitysCodeGenerate)是一款专门为 C#.Net 数据库程序员开发量身定做的自动代码生成工具,所生成的代码基于面向对象的思想和分层架构设计,并参考了微软Petshop中经典的思想和设计模式,融入...

    C#.Net实体代码生成工具 v3.1

    是一款专门为 C#.Net 数据库程序员开发量身定做的自动代码生成工具,所生成的代码基于面向对象的思想、分层架构设计及ORM,并参考了微软Petshop中经典的思想,融入了工厂模式等设计模式,反射机制等。从数据库中提取...

    java反射.ppt

    每当一個类被载入时,JVM就自动为其生成一个Class对象,通过操作class对象,我们可以得到该对象的所有成员并操作它们 public class ClassDemo { public static void main(String[] args) { String name = “ACCP"; ...

    C#.Net实体代码生成工具 v3.0 (20090830最新版)

    是一款专门为 C#.Net 数据库程序员开发量身定做的自动代码生成工具,所生成的代码基于面向对象的思想、分层架构设计及ORM,并参考了微软Petshop中经典的思想,融入了工厂模式等设计模式,反射机制等。从数据库中提取...

    java 反射Reflection;Class类

    是被视为动态语言的关键,反射机制允许程序在执行期借助Reflection API取得任何类的内部信息并能直接操作任意对象的内部属性及方法。 java反射机制所提供的功能: 生成动态代理 在运行时判断任意一个对象所属的类 在...

    java反射机制原理详解.docx

    我们创建一个类,通过编译,生成对应的.calss文件,之后使用java.exe加载(jvm的类加载器)此.class文件,此.class文件加载到内存以后,就是一个运行时类,存在缓存区,那么这个运行时类的本身就是一个class的实例 ...

    Java高级程序设计实战教程第三章-Java反射机制.pptx

    对于任意一个对象,都能够调用它的任意一个方法,常见的应用如下 逆向代码 ,例如反编译 与注解相结合的框架 例如Retrofit 单纯的反射机制应用框架 例如EventBus 2.x 动态生成类框架 例如Gson Java高级程序设计实战...

    ORM框架-VB/C#.Net实体代码生成工具(EntitysCodeGenerate)【ECG 4.3】

    VB/C#.Net实体代码生成工具(EntitysCodeGenerate)【ECG】是一款专门为VB/C#.Net数据库程序员开发量身定做的(ORM框架)代码生成工具,所生成的代码基于面向对象、分层架构、ORM,使用改进的抽象工厂设计模式及反射机制...

    Codematic .Net代码自动生成器 Beta版(.Net 2.0版)

    Codematic (2.0版)是一款为 C# 数据库程序员设计的自动代码生成器,Codematic 生成的代码基于基于面向对象的思想和三层架构设计,结合了Petshop中经典的思想和设计模式,融入了工厂模式,反射机制等等一些思想。...

    ORM框架-VB/C#.Net实体代码生成工具(EntitysCodeGenerate)ECG4.3.pdf

    摘要:VB/C#.Net实体代码生成工具(EntitysCodeGenerate)【ECG】是一款专门为.Net数据库程序开发量身定做的(ORM框架)代码生成工具,所生成的程序代码基于面向对象、分层架构、ORM及反射+工厂设计模式等。支持.Net1.1...

    LTP.Net代码自动生成器(DbToCode)

    DbToCode 是一款为 C# 数据库程序员设计的自动代码生成器,DbToCode 生成的代码基于基于面向对象的思想和三层架构设计,结合了Petshop中经典的思想和设计模式,融入了工厂模式,反射机制等等一些思想。采用 Model +...

    VB/C#.Net实体代码生成工具(EntitysCodeGenerate)【ECG 4.1】2010年11月最新版

    是一款专门为VB/C#.Net数据库程序员开发量身定做的代码生成工具,所生成的代码基于面向对象、分层架构设计、ORM并参考微软Petshop中的经典思想,使用改进的抽象工厂设计模式及反射机制等。工具目前直接支持Oracle、...

    傲.NET代码生成器 1.2.4

    生成的代码基于面向对象的思想和三层架构设计,结合了目前最经典的设计思想和设计模式,融入了工厂模式,反射机制等等一系列操作。能实现对ACCESS,mysql,SQLSERVER2000,ORACLE等多种数据库进行操作,提供了 SQL查询...

    傲世源.NET代码生成器V1.0

    傲世源代码生成器是一款为C#数据库程序员设计的自动代码生成器,傲世源 生成的代码基于面向对象的思想和三层架构设计,结合了目前最经典的设计思想和设计模式,融入了工厂模式,反射机制等等一系列操作。能实现对...

Global site tag (gtag.js) - Google Analytics