java之 ------ 类反射【详解】

一、什么是类反射

★JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动

态调用对象的方法的功能称为Java语言的反射机制。

反射(Reflection)是Java程序开发语言的特征之一,它允许运行中的Java程序对自身进行检查, 也称自审,并能直接操作程序的内部属性。例如,使用它能获得Java类中各成员的名

称并显示出来。

Java的这一能力在实际应用中应用得很多,在其它的程序语言中根本就不存在这一牲。例如,Pascal、C或者C++中就没有办法在程序中获得函数定义相关的信息。

JavaBean是类反射的实际应用之一,它能让一些工具可视化的操作软件组件。这些工具通过类反射动态的载入并取得Java组件(类)的属性。后面学习的各种框架,基本上都会有反

射的使用。

反射最大的好处是解耦。

示例一:

package reflect;
import java.lang.reflect.Method;
public class ReflectionHelloWorld {
	public static void main(String[] args) {
		try {
			//new UserModel();
			Class c = Class.forName("reflect.vo.Person");//依赖//<span style="color: rgb(255, 0, 0);font-size:14px; white-space: pre;">★</span><span style="color:#ff0000;">这里还要注意一个细节问题:就是这里只需要类名,不要后缀.java</span>
			Method methods[] = c.getMethods();
			for(int i=0;i<methods.length;i++){
				System.out.println(methods[i].toString());
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

运行结果:

public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

示例二:就是从一个usb.comfig(内容:name=reflect.usb.impl.Usb2 )文件中读出属性name,这个name记录了要运行的类名,获得这个类名后,然后通过类反射返回一个此类的对象,并

调用其中的work()方法。(采用DAO模式-----见DAO模式)

api模块

USB.java(接口)

package reflect.usb.api;

public interface USB {
	public abstract void work();
}

impl模块

Usb1.java

package reflect.usb.impl;
import reflect.usb.api.USB;
public class Usb1 implements USB {
	@Override
	public void work() {
		System.out.println("Usb1 is working....");
	}
}

Usb2.java

package reflect.usb.impl;

import reflect.usb.api.USB;

public class Usb2 implements USB {
	@Override
	public void work() {
		System.out.println("Usb2.......working......");
	}
}

factory模块

USBFactory.java

package reflect.usb.factory;

import java.io.FileInputStream;
import java.util.Properties;

import reflect.usb.api.USB;

public class USBFactory {
	public static USB getUSB(){
		//return new Usb2();
		try {
			Properties p = new Properties();//<code>Properties</code> <span style="color: rgb(255, 0, 0);font-size:14px; white-space: pre;">★</span>类表示了一个持久的属性集
			FileInputStream fin = new FileInputStream("usb.config");
			p.load(fin);//<span style="color: rgb(255, 0, 0);font-size:14px; white-space: pre;">★</span>从输入流中读取属性列表
			String name = p.getProperty("name").trim();//<span style="color: rgb(255, 0, 0);font-size:14px; white-space: pre;">★</span>用指定的键在此属性列表中搜索属性,并且除去多余的空格
			Class c = Class.forName(name);
			//Class c = Class.forName("reflect.usb.impl.Usb2");
			return (USB) c.newInstance();//<span style="color: rgb(255, 0, 0);font-size:14px; white-space: pre;">★</span>创建此 <tt>Class</tt> 对象所表示的类的一个新实例(new这个类的对象)
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

运行:

package reflect.usb;

import reflect.usb.factory.USBFactory;

public class Client {

	public static void main(String[] args) {
		USBFactory.getUSB().work();
	}

}

结果:Usb2.......working......

二、反射使用的三个步骤

用于反射的类,如Method,可以在java.lang.reflect包中找到。使用这些类的时候必须要遵循三个步骤:

第一步:获得你想操作的类的java.lang.Class对象。在运行中的Java程序中,用java.lang.Class类来描述类和接口等。

第二步:调用诸如getDeclaredMethods的方法,取得该类中定义的所有方法的列表。

第三步:使用反射的API来操作这些信息。

如下面这段代码:

Class c = Class.forName("java.lang.String");
Method ms[] = c.getDeclaredMethods();
System.out.println(ms[0].toString());

它将以文本方式打印出String中定义的第一个方法的原型。

三、获取Class对象的三种方式

★ 方式一

通过对象的getClass方法进行获取。这种方式需要具体的类和该类的对象,以及调用getClass方法。

private static void getClassObj1(){
	Person p = new Person("Jack",25);
	Class clazz = p.getClass();
	System.out.println(clazz);
}

★ 方式二

任何数据类型(包括基本数据类型)都具备着一个静态的属性class,通过它可直接获取到该类型对应的Class对象。这种方式要使用具体的类,然后调用类中的静态属性class完成,无需调

用方法,性能更好。

private static void getClassObj2(){
	//Class clazz =Person.class;
	//Class clazz =int.class;
	Class clazz =Integer.class;
	System.out.println(clazz);
}

★ 方式三

通过Class.forName()方法获取。这种方式仅需使用类名(字符串),就可以获取该类的Class对象,更有利于扩展。可以实现解耦

private static void getClassObj2(){
	//Class clazz =Person.class;
	//Class clazz =int.class;
	Class clazz =Integer.class;
	System.out.println(clazz);
}

四、类的解剖(获取类的定义信息)

★ 获取类的方法

找出一个类中定义了些什么方法,这是一个非常有价值也非常基础的反射用法。

getMethods() : 获取当前类及其父类声明的public方法

getDeclaredMethods() :获取当前类声明的所有方法,包括private等其它非public方法

private static void fetchMethods(String className) throws Exception {
	Class c = Class.forName(className);
	//Method methods[] = c.getMethods();
	Method methods[] = c.getDeclaredMethods();
	for(int i=0;i<methods.length;i++){
		Method m = methods[i];
		System.out.println("name= "+ m.getName());
		System.out.println("delaringClass= "+m.getDeclaringClass());
		Class paramTypes[] = m.getParameterTypes();
		for(int j=0; j<paramTypes.length; j++){
			System.out.println("param#"+j+": "+ paramTypes[j]);
		}
		Class exceptions[] = m.getExceptionTypes();
		for(int j=0; j<exceptions.length; j++){
			System.out.println("exception#"+j+" :"+ exceptions[j]);
		}
		System.out.println("returnType= " + m.getReturnType());//获得返回值类型
		System.out.println("-------------------");
	}
}

★ 获取类的构造器

找出一个类中定义的构造方法,构造器没有返回类型。

getConstructors() : 获取当前类的public构造方法

getDeclaredConstructors() :获取当前类声明的所有构造方法,包括private等其它非public方法

private static void fetchConstructors(String className) throws Exception {
	Class c = Class.forName(className);
	//Constructor cons[] = c.getConstructors();
	Constructor cons[] = c.getDeclaredConstructors();//构造方法的类
	for(int i=0;i<cons.length;i++){
		Constructor con = cons[i];
		System.out.println("name= "+ con.getName());
		System.out.println("delaringClass= "+con.getDeclaringClass());//获得次构造方法对象的类

		Class paramTypes[] = con.getParameterTypes();//获得其中参数的类型
		for(int j=0; j<paramTypes.length; j++){
			System.out.println("param#"+j+": "+ paramTypes[j]);
		}

		Class exceptions[] = con.getExceptionTypes();//获得异常类型
		for(int j=0; j<exceptions.length; j++){
			System.out.println("exception#"+j+" :"+ exceptions[j]);
		}

		System.out.println("-------------------");
	}
}

★ 获取类的属性字段

找出一个类中定义了哪些属性字段。

getFields() : 获取当前类及其父类声明的所有可访问公共字段

getDeclaredFields() :获取当前类声明的所有可访问公共字段,包括private等其它非public的

private static void fetchFields(String className) throws Exception {
	Class c = Class.forName(className);
	//Field fields[] = c.getFields();
	Field fields[] = c.getDeclaredFields();
	for(int i=0;i<fields.length;i++){
		Field fld = fields[i];
		System.out.println("name= "+ fld.getName());
		System.out.println("delaringClass= "+fld.getDeclaringClass());
		System.out.println("type= "+ fld.getType());
		int mod = fld.getModifiers();
		//System.out.println("modifiers= "+ mod);
		System.out.println("modifiers= "+ Modifier.toString(mod));
		System.out.println("-----------");
	}
}

五、类的调用(调用类中的成员)

★ 构造类对象

使用构造器新建对象。根据指定的参数类型找到相应的构造函数,传入相应参数调用执行,以创建一个新的对象实例。

private static void operateConstructor(String className) throws Exception{
	Class clazz = Class.forName(className);
	//调无参public的构造方法,太简单了,直接调newInstance();
	//Object obj = clazz.newInstance();

	//相当于要实现: Person p = new Person("Jack",10);
	//1先获取“Person(String name, int age)”这个构造方法--Constructor对象--con
	Class parameterTypes[] = new Class[2]; //组织形参
	parameterTypes[0] = String.class;
	//parameterTypes[1] = int.class;//※※※Person类中的构造方法为“public Person(String name, int age)”时,OK。但构造方法为“public Person(String name, Integer age)”时,WA。
	//parameterTypes[1] = Integer.class; //当构造方法为“public Person(String name, int age)”时,不行,因为它会在匹配构造方法时,严格匹配第2个参数为“Integer”的构造方法,不会自动装箱拆箱
	parameterTypes[1] = Integer.TYPE; //※※※该TYPE属性即是int。当Person类中的构造方法为“public Person(String name, int age)”时,OK。但构造方法为“public Person(String name, Integer age)”时,WA。

	Constructor con = clazz.getConstructor(parameterTypes);
	//2然后把参数“Jack,10”传入,调用该"Constructor对象--con"的newInstance方法,进行创建对象
	  //组织实参
	  Object params[] = new Object[2];
	  params[0] = new String("Jack");
	  params[1] = new Integer(20);
	  //调用构造方法
	  Object retObj = con.newInstance(params);//new Person("Jack",10);
	System.out.println(retObj);
}

★ 调用方法

根据方法名称执行方法。根据方法名与参数类型匹配指定的方法,传入相应参数与对象进行调用执行。若是静态方法,则不需传入具体对象。

private static void callMethod(String className) throws Exception{
	Class clazz = Class.forName(className);
	//※调用空参方法
	//1先获取Method对象
	Method method = clazz.getMethod("toString", null);
	Object obj = clazz.newInstance();
	//2再执行该Method对象的invoke()方法
	Object returnValue = method.invoke(obj, null);//obj.toString();
	//Person p = new Person("Jack",30);
	//Object returnValue = method.invoke(p, null);
	System.out.println(returnValue);

	//※调用带参数的方法
	//1先获取Method对象--m--需要先构造形参,通过形参找到对应的方法
	Class paramTypes[] = new Class[2];
	paramTypes[0] = Integer.TYPE;
	paramTypes[1] = double.class;
	Method m = clazz.getMethod("sum",paramTypes );

	//2再执行该Method对象的invoke()方法  -----需要一个对象obj,还需要相应的实参
	Object obj2 = clazz.newInstance();
	Object params2[] = new Object[2];
	params2[0] = 100;
	params2[1] = 123.5;
	Object retObj2 = m.invoke(obj2,params2 );
	System.out.println("retObj2= "+retObj2);

	//※调用静态方法
	Method m2 = clazz.getMethod("show", null);
	m2.invoke(null, null);//对象用null

	//※暴力访问
	Class paramTypes3[] = new Class[]{Integer.TYPE} ;
	//用clazz.getDeclaredMethods()可以获取非public方法的名称,本例中这一步省了,直接指定方法名“sum”
	Method m3 = clazz.getDeclaredMethod("sum",paramTypes3 );

	m3.setAccessible(true);//◎◎强行设置为可访问

	Object obj3 = clazz.newInstance();
	Object params3[] = { 100 };
	Object retObj3 = m3.invoke(obj3, params3);
	double d = (Double)retObj3;
	System.out.println("=============");
	System.out.println(d);

}

★ 获取与设置属性值

根据属性名称读取与修改属性的值,访问非静态属性需传入对象为参数。

private static void changeFieldValue(String className) throws Exception{
	Class clazz = Class.forName(className);
	Field fld = clazz.getField("age");
	Object p = clazz.newInstance();
	//fld.setInt(p, 100);
	//System.out.println(fld.getInt(p));
	fld.set(p, 66);
	Object retObj = fld.get(p);
	System.out.println(retObj);

	//※暴力访问
	Field fld2 = clazz.getDeclaredField("name");//必须用getDeclaredField()方法才能获得非public字段
	fld2.setAccessible(true);//打开访问权限的开关
	Object obj = clazz.newInstance();
	fld2.set(obj, "Tom");
	Object ret = fld2.get(obj);
	System.out.println(ret);
}

六、练习(模拟Java内省的功能)

★ 准备工作

定义一个Model类,里面所有的属性都是private的,然后为每个属性提供getter和setter方法;

再准备一个Map,map的key值都是类里面的属性字段的字符串表示,值任意。

★ 真正的工作

设计一个方法Object getModel(Map map,Class cls),传入一个包含所有值的Map,然后再传入Model类的class,那么返回Model类的实例,这个实例里面已经包含好了所有相关的数据。

也就是把Map中的数据通过反射,设置回到Model类实例中。

源代码:

Person.java

package reflect2.spring.vo;

public class Person {
	private String name;
	public int age;
	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}
	public Person(){
	}

	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;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
}

UserModel.java

package reflect2.spring.vo;
import java.io.Serializable;
public class UserModel{
	private String uuid;
	private String name;
	private int type;
	private String pwd;

	public UserModel(String uuid, String name, int type, String pwd) {
		this.uuid = uuid;
		this.name = name;
		this.type = type;
		this.pwd = pwd;
	}

	public UserModel(String uuid, int type){
		this.uuid = uuid;
		this.type = type;
	}
	public UserModel(){
	}
	private UserModel(String uuid){
		this.uuid = uuid;
	}
	public String getUuid() {
		return uuid;
	}

	public void setUuid(String uuid) {
		this.uuid = uuid;
	}

	public String getName() {
		return name;
	}

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

	public int getType() {
		return type;
	}

	public void setType(int type) {
		this.type = type;
	}

	public String getPwd() {
		return pwd;
	}

	public void setPwd(String pwd) {
		this.pwd = pwd;
	}

	public String toString() {
		return "{"+uuid+","+name+","+type+","+pwd+"}";
	}

}

SpringDaoUtil.java

package reflect2.spring;

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

public class SpringDaoUtil {
	private SpringDaoUtil(){
	}
	public static Object setValues(Map map, Class c) throws Exception{
		Object obj = c.newInstance();

		Field flds[] = c.getDeclaredFields();
		for(Field fld: flds){
			Object value = map.get(fld.getName());
			if(value==null){
				System.out.println(fld.getName()+"字段的数据为空");
			}else{
				//通过“字段名”得到相应set方法的名称如:age --> setAge
				String methodName = "set" + fld.getName().substring(0, 1).toUpperCase() + fld.getName().substring(1);
				Class paramTypes[] = new Class[1];
				paramTypes[0] = fld.getType();
				Method m = c.getDeclaredMethod(methodName, paramTypes);
				Object objs[] = new Object[]{value};
				m.invoke(obj, objs);
			}
		}

		return obj;
	}

}

Client.java

package reflect2.spring;

import java.util.HashMap;
import java.util.Map;

import reflect.vo.UserModel;
import reflect2.spring.vo.Person;

public class Client {

	public static void main(String[] args) {

		try {

			Map<String, String> map = new HashMap<String, String>();
			map.put("uuid", "100");
			map.put("name", "Jack");
			//map.put("type", "1");
			map.put("pwd", "1");
			UserModel user = (UserModel) SpringDaoUtil.setValues(map, UserModel.class);
			System.out.println(user);
			System.out.println("888888888888888888888888");

			Map<String, Object> map2 = new HashMap<String, Object>();
			map2.put("name", "张三");
			map2.put("age", 20);
			//Person p = (Person) SpringDaoUtil.setValues(map2, Person.class);
			UserModel p = (UserModel) SpringDaoUtil.setValues(map2, UserModel.class);
			System.out.println(p);

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-15 23:49:24

java之 ------ 类反射【详解】的相关文章

Java学习系列(二十)Java面向对象之反射详解

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/45289391 前言 今天讲讲Java中的反射.我们常见的反射多用于JDBC中的加载驱动程序Class.forName("com.mysql.jdbc.Driver");.Struts的MVC.Hibernate中的ORM.Spring中的IOC还有一些其他框架等等.那它有什么好处呢?它的好处就是能够动态的创建对象和编译且能够访问某个类中的所有(包括私有)属性方法及对象的属性方法

java基础之反射 详解

首先,我们在开始前提出一个问题: 1.在运行时,对于一个java类,能否知道属性和方法:能否去调用它的任意方法? 答案是肯定的. 本节所有目录如下: 什么是JAVA的反射机制 JDK中提供的Reflection API JAVA反射机制提供了什么功能 获取类的Class对象 获取类的Fields 获取类的Method 获取类的Constructor 新建类的实例       Class<T>的函数newInstance       通过Constructor对象的方法newInstance 调

java Object类源代码详解 及native (转自 http://blog.csdn.net/sjw890821sjw/article/details/8058843)

Java代码  package java.lang; public class Object { /* 一个本地方法,具体是用C(C++)在DLL中实现的,然后通过JNI调用.*/ private static native void registerNatives(); /* 对象初始化时自动调用此方法*/ static { registerNatives(); } /* 返回此 Object 的运行时类.*/ public final native Class<?> getClass();

Java枚举类enum详解

枚举类enum是JDK1.5引入的,之前都是用public static final int enum_value来代替枚举类的.枚举类enum是一种特殊的类,它默认继承了类java.lang.Enum.和其它普通类一样,enum同样可以有成员变量.方法.构造器,也可以实现一个或多个接口,区别是: 如果有构造器,必须用private修饰. 枚举类不能派生子类. 枚举类所有的实例必须在第一行显示定义.系统会自动给这些实例加上public static final修饰,无须程序员显示定义. 枚举类默

Java文件类File详解

java.io.File类 凡是与输入.输出相关的类.接口等都定义在java.io包下 File是一个类,可以有构造器创建其对象.此对象对应着一个文件(.txt .avi .doc .ppt .mp3 .jpg)或文件目录 File类对象是与平台无关的 File中的方法,仅涉及到如何创建.删除.重命名等等.只要涉及文件内容的,File是无能为力的,必须由io流来完成 File类的对象常作为io流的具体类的构造器的形参 一.createNewFile()  delete()  lastModifi

Java工具类ToStringStyle详解

1.ToStringStyleToStringBuilder是字符串格式控制类,主要供公共接口总是通过ToStringBuilder来实现,这些类的目的是用作单例模式,没有必要每次都初始化一个新样式,程序通常使用这个类中预定义的常量之一,另外StandardToStringStyle类可以用来设置各种设置,因此大多是时候没有必要实现它的子类:如果是必须的,那么子类可以像它要求的那样覆盖尽可能多的方法,每种对象类型(从long到int到object)都有它对应的输出类型,大多数有两种版本,详情和摘

Properties类使用详解

Java Properties类使用详解 概述 Properties 继承于 Hashtable.表示一个持久的属性集,属性列表以key-value的形式存在,key和value都是字符串. Properties 类被许多Java类使用.例如,在获取环境变量时它就作为System.getProperties()方法的返回值. 我们在很多需要避免硬编码的应用场景下需要使用properties文件来加载程序需要的配置信息,比如JDBC.MyBatis框架等.Properties类则是properti

java 反射 详解

本文来自:blog.csdn.net/ljphhj JAVA反射机制:   通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们. 理论的东东太多也没用,下面我们看看实践 Demo - Demo: package cn.lee.demo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import

Java反射详解

Java反射详解 分类:java, 基础日期:2012-07-20作者:ticmy 19 反射,是Java中非常重要的一个功能,如果没有反射,可以说很多框架都难以实现. 什么是反射?说白了就是可以通过Java代码获取装载到方法区的类信息的手段. 当装载一个类时,会在方法区产生一个数据结构,该结构中包含着装载的类的相关信息.字节码可以看成是数据流,那么方法区的这种数据结构可以说是字节码数据流的结构化表现.装载的最终产物就是java.lang.Class类的一个对象,它是Java程序与方法区内部数据

java反射详解 (转至 http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html)

本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解. 下面开始正文. [案例1]通过一个对象获得完整的包名和类名 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package Reflect; /**  * 通过一个对象获得完整的包名和类名  * */ class Demo{     //other codes... } class hello{     pu