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();
		}
	}

}













  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值