Java反射探索研究

          林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankakay

          摘要:本文详细深入讲解是Java中反射的机制,并介绍了如何通过反射来生成对象、调用函数、取得字段、设置字段的方法。最后,给出了一些反射常用到的实例。

一、反射

(1)概念

   反射含义:可以获取正在运行的Java对象。
(2)功能
        1)在运行时判断任意一个对象所属的类
        2)在运行时构造任意一个类的对象
        3) 在运行时判断任意一个类所具有的成员变量和方法
       4)在运行时调用任意一个对象的方法
(3)实现Java反射的类
  1)Class:它表示正在运行的Java应用程序中的类和接口
  2)Field:提供有关类或接口的属性信息,以及对它的动态访问权限
  3)Constructor:提供关于类的单个构造方法的信息以及对它的访问权限
  4)Method:提供关于类或接口中某个方法信息
  注意:Class类是Java反射中最重要的一个功能类,所有获取对象的信息(包括:方法/属性/构造方法/访问权限)都需要它来实现

(4)取得class的三种方法

		Dog dog = new Dog();				 
		 Class<?> dogClass = dog.getClass();
		 Class<?> dogClass1 = Dog.class;
		 Class<?> dogClass2 = Class.forName("com.lin.Dog");//注意要添加异常抛出

(5)关键方法

方法关键字

含义

getDeclaredMethods()

获取所有的方法

getReturnType()

获得方法的放回类型

getParameterTypes()

获得方法的传入参数类型

getDeclaredMethod("方法名",参数类型.class,……)

获得特定的方法

 

 

构造方法关键字

含义

getDeclaredConstructors()

获取所有的构造方法

getDeclaredConstructor(参数类型.class,……)

获取特定的构造方法

 

 

父类和父接口

含义

getSuperclass()

获取某类的父类

getInterfaces()

获取某类实现的接口


(6)一些区别函数

public Method[] getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。

public Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。

getFields()获得某个类的所有的公共(public)的字段,包括父类。

getDeclaredFields()获得某个类的所有申明的字段,即包括public、private和proteced,
但是不包括父类的申明字段。

下面来看一个例子说明:

动物接口

package com.lin;

public interface Aminal {
	
	public String eat(String obj);
	
	public int run(int obj);

}

实现类:

package com.lin;

import java.util.jar.Attributes.Name;

public class Dog implements Aminal {
	
	private String name;
	
	private int age;
	
	public Dog() {
		// TODO 自动生成的构造函数存根
	}
	
	public Dog(String name,int age) {
		this.name = name;
		this.age = age;
	}	
	
	public Dog(String name) {
		this.name = name;
		this.age = 10;
	}
	
	private void sleep(int x) {
		System.out.println(name + "睡觉" + x + "分钟");
	}
	
	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 eat(String obj) {
		System.out.println(name + "吃"+ obj);
		return null;
	}

	@Override
	public int run(int obj) {
		System.out.println("跑,速度:"+ obj);
		return 0;
	}
	
	@Override
	public String toString() {
		return "狗名:" + name + "  狗的年纪:" + age;
	}
	
	private static void play() {
		System.out.println("狗狗自己玩啊玩");
	}
}

来看看各自的调用:

package com.lin;

import java.lang.reflect.Method;

public class ReflectLearning {

	public static void main(String[] args) throws ClassNotFoundException {
		Dog dog = new Dog();		
		 System.out.println(dog.getClass()); 
		 System.out.println(dog.getClass().getName()); 
		 
		 Class<?> dogClass = dog.getClass();
		 Class<?> dogClass1 = Dog.class;
		 Class<?> dogClass2 = Class.forName("com.lin.Dog");
		 
		 Method[] methods1 = dogClass.getMethods();
		 System.out.println("====================通过getMethods取得方法开始====================");
		 for (Method method : methods1) {
			 System.out.println(method); 
		}
		 System.out.println("====================通过getMethods取得方法结束====================");
		 
		 
		 Method[] methods2 = dogClass.getDeclaredMethods();
		 System.out.println("====================通过getDeclaredMethods取得方法开始====================");
		 for (Method method : methods2) {
			 System.out.println(method); 
		}
		 System.out.println("====================通过getDeclaredMethods取得方法结束====================");



	}
	

}

来看下结果:

getMethods方法

getDeclareMethos方法:


从上面可以看出getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。getDeclaredMethods()对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。

二、通过反射调用构造函数

(1)、列出所有的构造函数:

		 Constructor<?>[] constructors = dogClass.getConstructors();
		
		 System.out.println("====================列出所有的构造函数结束====================");
		 for (Constructor<?> constructor : constructors) {			
			 System.out.println(constructor);
		 }
		 System.out.println("====================列出所有的构造函数结束====================");
输出结果:

(2)、通过反射生成对象

		 System.out.println("====================通过newInstance()来生成对象,一定在有默认构造函数====================");
		 Dog dog1 = (Dog) dogClass.newInstance();
		 dog1.setName("狗狗1号");
		 dog1.setAge(7);
		 System.out.println(dog1);
		 
		 System.out.println("====================通过newInstance(参数)方法一来生成对象====================");
		 Dog dog2 = (Dog)constructors[0].newInstance("狗狗2号");
		 System.out.println(dog2);

		 System.out.println("====================通过newInstance(参数)方法二来生成对象====================");
		 Constructor con1 = dogClass.getConstructor(new  Class[]{String.class,int.class});     //主要就是这句了
		 Dog dog3 = (Dog) con1.newInstance(new Object[]{"狗狗3号",14});
		 System.out.println(dog3);
输出结果:


从上面可以看出,先通过getConstructor(new  Class[]{xxxx.class,yyy.class}),再通过con1.newInstance(new Object[]{"xxxxx",...});的方式是最灵活的,可以自动根据输入的参数类型和个数,找到对应的构造函数来调用。第二种方法需要得到构造函数的数组,并且需要知道对应哪一个构造函数。第一种就只能调用无参构造函数。

三、通过反射调用普通函数、静态函数

(1)取得函数的一些基本信息

		Class<?> dogClass = Dog.class;
		Method[] methods = dogClass.getDeclaredMethods();
		for (Method method : methods) {
			System.out.println("函数名:"+method.getName() +"        函数类型:"+ method.getModifiers() + "         函数返回: "+ method.getReturnType() + "        函数参数个数:" + method.getParameterCount());
			
		}
输出结果:


其中函数类型对应表如下:
PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048

(2)方法调用

这是当前狗类的方法:

package com.lin;

import java.util.jar.Attributes.Name;

public class Dog implements Aminal {
	
	private String name;
	
	private int age;
	
	public Dog() {
		// TODO 自动生成的构造函数存根
	}
	
	public Dog(String name,int age) {
		this.name = name;
		this.age = age;
	}	
	
	public Dog(String name) {
		this.name = name;
		this.age = 10;
	}
	
	private void sleep(int x) {
		System.out.println(name + "睡觉" + x + "分钟");
	}
	
	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 eat(String obj) {
		System.out.println(name + "吃"+ obj);
		return null;
	}

	@Override
	public int run(int obj) {
		System.out.println("跑,速度:"+ obj);
		return 0;
	}
	
	@Override
	public String toString() {
		return "狗名:" + name + "  狗的年纪:" + age;
	}
	
	private static void play() {
		System.out.println("狗狗自己玩啊玩");
	}
	
	

}

不同方法的调用过程:

		//调用私有方法
		Method method1 = dogClass.getDeclaredMethod("sleep", int.class);//不要用getMethod,它只能取到public方法
		Dog dog1 = (Dog) dogClass.getConstructor(new Class[] {String.class}).newInstance(new Object[]{"狗狗1号"});
		method1.setAccessible(true);//私有方法一定要加这句
		method1.invoke(dog1, 12);
		
		//调用私有静态方法
                Method method2 = dogClass.getDeclaredMethod("play");//不要用getMethod,它只能取到public方法 
                method2.setAccessible(true);//私有方法一定要加这句
               method2.invoke(dogClass.newInstance()); 
        
               //调用公共方法
		Method method3 = dogClass.getMethod("eat", String.class);//这里也可以用getDeclaredMethod
		Dog dog3 = new Dog("狗狗3号", 45);
		method3.invoke(dog3, "苹果~");

输出结果:


方法调用这里一定要记住getMethod和getDeclaredMethod的区别,并且在调用私有的方法之前一定要加setAccessible(true)这一句,要不会报错!

四、通过反射取得字段、设置字段值

(1)怎么通过反射获取类的属性
a)Class.getDeclaredField(String name);
返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
b)Class.getDeclaredFields();
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
c)Class.getField(String name);
返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
d)Class.getField();
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。

(2)进行属性获取更改

		Dog dog1 = new Dog("狗狗1号", 12);
		System.out.println(dog1);
		
		Class<?> dogClass = dog1.getClass();
		Field field1 = dogClass.getDeclaredField("name");//注意,getField只能取得public的字段
		field1.setAccessible(true);//私有变量必须先设置Accessible为true
	    System.out.println("原本狗名:" + field1.get(dog1));
		
		field1.set(dog1,"狗狗2号");
		
		System.out.println(dog1);	

输出结果:


值得注意的是获取私有属性的时候必须先设置Accessible为true,然后才能获取。


五、反射常用工具类

(1)bean复制工具

这里可以使用commons-beanutils中的copyProperties()方法,自己写是为了加深对反射的理解。

1、toString的基类

package com.lin;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * bean基類
 * @author lin
 *
 */
public class BaseBean {

	public String toString() { 
	  StringBuffer sb = new StringBuffer();  
	  SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
      Class<?> cls = this.getClass();
      Field[] fields = cls.getDeclaredFields();
	  sb.append(cls.getName() + "{");
      for (Field field : fields) {
	       try {  
          field.setAccessible(true);  
          sb.append(field.getName());  
          sb.append("=");  
          sb.append(field.get(this));  
          sb.append(" ");  
      } catch (Exception e) {  
          e.printStackTrace();  
      } 
	  }
      sb.append("}");
	  return sb.toString();
	}
}

2、bean复制工具

package com.lin;

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

/**
 * 将一个JavaBean风格对象的属性值拷贝到另一个对象的同名属性中 (如果不存在同名属性的就不拷贝)
 **/

public class BeanCopy {
	private static String GET = "get";
	private static String SET = "set";
	
    /**
     * 
     * @param source
     * @param target
     * @throws Exception
     */
	public static void copy(Object source,Object target){
		Class<?> sourceClz = source.getClass();
		Class<?> targetClz = target.getClass();
		// 得到Class对象所表征的类的所有属性(包括私有属性)
		Field[] sourceFields = sourceClz.getDeclaredFields();
		if (sourceFields.length == 0) {
			sourceFields = sourceClz.getSuperclass().getDeclaredFields();
		}
		
		int len = sourceFields.length;
		for (int i = 0; i < len; i++) {
			String fieldName = sourceFields[i].getName();
			Field targetField = null;
			// 得到targetClz对象所表征的类的名为fieldName的属性,不存在就进入下次循环
			try {
				targetField = targetClz.getDeclaredField(fieldName);
			} catch (NoSuchFieldException e) {
				try {
					targetField = targetClz.getSuperclass().getDeclaredField(fieldName);
				} catch (NoSuchFieldException e1) {
					e1.printStackTrace();
				} catch (SecurityException e1) {
					e1.printStackTrace();
				}
			}
			
			if (targetField == null) {
				continue;
			}
			
			// 判断sourceClz字段类型和targetClz同名字段类型是否相同
			if (sourceFields[i].getType() == targetField.getType()) {
				// 由属性名字得到对应get和set方法的名字
				String getMethodName = GET + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
				String setMethodName = SET + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
				// 由方法的名字得到get和set方法的Method对象
				Method getMethod;
				Method setMethod;
				try {
					try {
						getMethod = sourceClz.getDeclaredMethod(getMethodName,new Class[] {});//get方法入參為空
					} catch (NoSuchMethodException e) {
						getMethod = sourceClz.getSuperclass().getDeclaredMethod(getMethodName,new Class[] {});
					}
					try {
						setMethod = targetClz.getDeclaredMethod(setMethodName,sourceFields[i].getType());//set方法入參不為空

					} catch (NoSuchMethodException e) {
						setMethod = targetClz.getSuperclass().getDeclaredMethod(setMethodName,sourceFields[i].getType());
					}
					// 调用source对象的getMethod方法
					Object result = getMethod.invoke(source, new Object[] {});
					// 调用target对象的setMethod方法
					setMethod.invoke(target, result);

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

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

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

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

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

				}
			} else {
				continue;

			}

		}

	}

}
使用:

新建两个类:

package com.lin;

import java.util.Date;

public class Car extends BaseBean{
	
	private String name;
	
	private String id;
	 
	private Boolean sellFlag;
	
	private int age;
	
	private double maxSpeed;
	
	private double minSpeed;
	
	private int driverPeople;
	
	private Date date;

	public String getName() {
		return name;
	}

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

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public Boolean getSellFlag() {
		return sellFlag;
	}

	public void setSellFlag(Boolean sellFlag) {
		this.sellFlag = sellFlag;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getMaxSpeed() {
		return maxSpeed;
	}

	public void setMaxSpeed(double maxSpeed) {
		this.maxSpeed = maxSpeed;
	}

	public double getMinSpeed() {
		return minSpeed;
	}

	public void setMinSpeed(double minSpeed) {
		this.minSpeed = minSpeed;
	}

	public int getDriverPeople() {
		return driverPeople;
	}

	public void setDriverPeople(int driverPeople) {
		this.driverPeople = driverPeople;
	}

	public Date getDate() {
		return date;
	}

	public void setDate(Date date) {
		this.date = date;
	}
	
	
	

}

另一个:

package com.lin;

import java.util.Date;

public class Bus extends BaseBean{
	private String name;
	
	private String id;
	 
	private Boolean sellFlag;
	
	private int age;
	
	private double maxSpeed;
	
	private double minSpeed;
	
	private long driverPeople;//和car類型不同
	
	private int driverYear;//car沒有這個
	
	private Date date;

	public String getName() {
		return name;
	}

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

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public Boolean getSellFlag() {
		return sellFlag;
	}

	public void setSellFlag(Boolean sellFlag) {
		this.sellFlag = sellFlag;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getMaxSpeed() {
		return maxSpeed;
	}

	public void setMaxSpeed(double maxSpeed) {
		this.maxSpeed = maxSpeed;
	}

	public double getMinSpeed() {
		return minSpeed;
	}

	public void setMinSpeed(double minSpeed) {
		this.minSpeed = minSpeed;
	}

	public long getDriverPeople() {
		return driverPeople;
	}

	public void setDriverPeople(long driverPeople) {
		this.driverPeople = driverPeople;
	}

	public int getDriverYear() {
		return driverYear;
	}

	public void setDriverYear(int driverYear) {
		this.driverYear = driverYear;
	}

	public Date getDate() {
		return date;
	}

	public void setDate(Date date) {
		this.date = date;
	}
	
	

}

调用:

	public static void test5() {
		Car car = new Car();
		car.setAge(12);
		car.setDriverPeople(4);
		car.setId("YU1234");
		car.setMaxSpeed(13.66);
		car.setMinSpeed(1.09);
		car.setName("小车");
		car.setSellFlag(false);
		car.setDate(new Date());
		
		
		Bus bus = new Bus();
		BeanCopy.copy(car,bus);
		System.out.println(car);
		System.out.println(bus);
		
		
	}

除了两个不同的字段外,其它的都复制过去了,这在DTO、VO、DOMAIN对象转换时经常用到。




评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值