`
xyheqhd888
  • 浏览: 409272 次
  • 性别: 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语句操作中,主要使用了Java反射机制来获取JavaBean对象的信息,然后根据这些信息生成对应的sql语句。例如,通过Java反射机制可以获取JavaBean对象的成员...

    C#,利用反射动态创建对象

    综上所述,C#的反射机制为开发者提供了在运行时动态操作代码的能力,尤其在处理不确定类型的对象或者需要实现动态行为时,反射成为了一种不可或缺的工具。然而,需要注意的是,由于其内在的性能成本,应当合理地在...

    在反射动态机制生成对象的实体

    反射是Java编程语言的一个核心特性,它允许程序在运行时检查、操作类的信息以及创建对象。通过反射,开发者可以实现高度灵活的应用程序,例如框架、插件系统等,其中非常重要的一环就是能够动态地创建对象。本文将...

    C#泛型、反射实例、自动生成sql语句

    在C#中,利用泛型和反射可以创建灵活的数据库操作库,自动根据实体类生成对应的SQL语句。例如,我们可以定义一个泛型方法,接受一个类型参数,这个类型对应于数据库表的实体模型: ```csharp public void Save(T ...

    C#反射生成SQL实例

    这个"C#反射生成SQL实例"可能包含一个具体的代码示例,展示了如何结合以上知识点来动态构建和执行SQL。通过学习这个实例,开发者可以更好地理解和应用C#反射在实际项目中的功能,提升代码的灵活性和可维护性。

    注解反射生成SQL语句

    例如,我们可以创建一个自定义的处理器,该处理器会在编译期间检查带有特定注解的类,并自动生成相应的DAO(数据访问对象)类,这些DAO类已经包含了基于注解信息生成的SQL方法。 总的来说,结合注解和反射,开发者...

    java使用反射创建并操作对象的方法

    Java 使用反射创建并操作对象的方法 Java 的反射机制允许程序在运行时动态地创建和操作对象,这是 Java 程序设计中非常重要的一种技术。下面我们将详细介绍 Java 使用反射创建并操作对象的方法。 反射机制概述 ...

    动态代码的使用(反射和动态生成类)

    反射是.NET Framework 提供的一种机制,允许程序在运行时动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。使用反射可以动态地调用类中的方法,...

    Qt 使用QMetaObject实现反射机制代码demo

    在编程领域,反射机制是一种强大的特性,允许程序在运行时检查自身的行为,包括类的信息、对象的状态以及函数的调用等。在C++的世界里,Qt框架提供了一种实现反射的手段,即QMetaObject系统。这个系统允许我们动态地...

    仿hibernate动态生成sql保存对象

    在IT行业中,尤其是在Java开发领域,Hibernate是一个广泛使用的对象关系映射(ORM)框架,它极大地简化了数据库操作。本文将深入探讨如何模仿Hibernate的功能,动态生成SQL来保存对象,以及与之相关的技术如注解...

    利用java反射将json字符串转成对象.zip

    现在我们有了一个包含JSON数据的Map,可以开始使用反射来创建对应的Java对象。假设我们有一个User类: ```java public class User { private String name; private int age; // getters and setters } ``` 接...

    JAVA反射机制动态调用 计算器对象的加法,减法函数;JAVA反射机制修改一个学生对象 的私有数据成员

    总结起来,Java反射机制提供了一种强大的工具,让我们可以在运行时动态地操作类和对象,这包括调用方法和访问私有字段。但同时,也需要注意其可能带来的副作用。在上述示例中,我们展示了如何利用反射来实现计算器的...

    使用反射获取和设置对象的值

    - **代码生成和编译**:例如,Apache的Velocity和FreeMarker模板引擎使用反射生成运行时代码。 - **元编程**:允许在运行时检查和修改程序的行为。 - **插件系统**:允许加载未知类并执行其方法。 - **序列化和反...

    反射查询SQL框架

    在IT行业中,反射查询SQL框架是一种高级编程技术,它结合了面向对象的编程特性与数据库操作,使得开发者可以通过对象和属性来动态构建SQL语句,从而实现对数据库的增、删、改、查(CRUD)操作。这种技术极大地提高了...

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

    Java反射 JavaBean 对象自动生成插入、更新、删除、查询 SQL 语句操作 Java 反射是 Java 语言中一个强大的功能,它允许开发者在运行时检查和修改类、方法、字段的行为。Java 反射机制可以动态地创建对象、调用方法...

    C# 根据字符串动态生成控件(反射原理),可鼠标拖动

    通过反射,我们可以获取类、接口、属性、方法和事件等元数据信息,并在运行时创建和操作对象。在C#中,`System.Reflection`命名空间提供了关于反射所需的所有类和方法。 动态生成控件是指在程序运行时根据需求创建...

    学习反射的一个好实例

    反射通常在Java、Python、C#等面向对象的语言中被广泛使用,它能增加代码的灵活性和动态性。 反射的基本操作包括: 1. **类的动态加载**:在运行时根据字符串形式的类名加载对应的类,无需在编译时就确定所有要用到...

    java 代码反射生成工具_generatorConfig

    Java 反射生成工具_generatorConfig 是一个用于自动化生成Java Bean及Mapper XML文件的实用工具。在Java开发中,反射机制是一种强大的技术,它允许程序在运行时检查和修改类、对象及其属性。generatorConfig则是利用...

    反射+注解自定义ORM

    在Java编程中,ORM(Object-Relational Mapping)是一种技术,它允许程序员使用面向对象的方式来操作数据库,将数据库中的表映射为Java对象,而无需编写大量的SQL语句。本主题探讨的是如何通过反射和注解来自定义ORM...

    java反射全解(反射原理+反射API详解+反射与数组+反射与泛型+反射源码与性能开销+反射优缺点+反射与内省)

    要想使用反射,首先需要获得待操作的类所对应的 Class 对象。java.lang.Class 是所有反射 API 的入口点。通过 Class 对象,可以获悉整个类的结构,包括其modifiers、superclass、实现的 interfaces、fields 和 ...

Global site tag (gtag.js) - Google Analytics