Java反射笔记(1)

感谢张龙老师的视频:http://www.verycd.com/topics/2838873/

 

在Java运行时环境中,对于任意一个类,通过反射能够知道这个类有哪些属性和方法,并且构造这个类的实例,对于任意一个类,能调用任意一个方法。这种动态地获取类的信息以及动态调用对象方法的功能来自于JAVA语言的反射(Reflection)机制。

反射机制主要提供了以下功能:

1、在运行时判断任意一个对象所属的类型

2、在运行时构造任意一个类的对象

3、在运行时判断任意一个类所具有的成员变量和方法

4、在运行时调用任意一个对象的方法

 

动态语言 :程序运行时,允许改变程序结构或者变量类型。Perl,Python,Ruby都是动态语言,而C#C++,JAVA都不是动态语言。

JAVA具有非常突出的动态的相关机制:Reflection,在java中是指可以用于在运行时加载、探知、使用编译期间完全未知的classes,换句话说,java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但是不包含methods定义),并生成该类的对象实体、或对其fields设值、或唤起其methods,这种“看透class”的能力被称为introspection。

JDK中,实现Reflection的类位于java.lang.Reflect包中:

Class类:代表一个类

Filed类:代表类的成员变量(成员变量也称为类的属性)

Method类:代表类的方法

Constructor:代表类的构造方法

Array类:提供了动态创建数组,以及访问数组的元素的静态方法


java的反射必须从Class类开始,Class类位于java.lang包中。

 

public class DumpMethods {
	
	public int add(int a, int b) {
		return a+b;
	}
	
	public void echo(String mesg) {
		System.out.println(mesg);
	}
	/*
	 * 通过反射调用DumpMethods的add方法和echo方法
	 */
	public static void main(String[] args) {
		
		try {
			//首先通过类名获得该类的class对象,Class类是java反射的入口点
//			Class<?> classType = Class.forName(args[0]);
			//Class中的每一个方法都会对应一个Method实例
//			Method[] methods = classType.getDeclaredMethods();
			Class classType = DumpMethods.class;
			//通过Class的newInstance()获得该Class对象代表的class的实例
			Object dumpObj = classType.newInstance();
			//下面两句代码同直接调用DumpMethod实例的add(100,200),通过反射调用add方法,首先要获得该add方法
			//代表的Method对象,是利用Class类的getMethod方法,并且传递方法名和该方法的Class参数数组,只能获取public的方法实例。
			//另外在调用该方法时,调用该Method对象的invoke方法,并将该方法所属的实例和参数的对象数组传入,返回一个Object对象
			Method addMethod = classType.getMethod("add", new Class[]{int.class, int.class});
			Object resultObj = addMethod.invoke(dumpObj, new Object[]{100,200});
			System.out.println(resultObj);
			
			Method echoMethod = classType.getMethod("echo", new Class[]{String.class});
			Object obj = echoMethod.invoke(dumpObj, new Object[]{"zhutong is a good boy"});
			
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
}

 

注意Class对象的newInstance()只会调用Class对象对应的class的不带参数的构造方法,如果该class中的构造函数被重载了,那么直接使用Class对象的newInstance方法来构造实例是不成功的。

那我们还想得到Class的实例怎么办呢?和Class,Method类似,在一个类中,每个构造方法也对应了一个对象,即Constructor对象,可以通过Class的getConstructor (Class <?>... parameterTypes) 方法来获得Class对应类的某个构造方法,为什么是某个呢?构造方法重载仅仅是它们的参数不一样,可能是参数个数,参数类型以及参数顺序不一样,这里通过getConstructor (Class <?>... parameterTypes) 方法的参数中的Class数组来获得不同的Constructor对象,和获得Method对象一样,需要将构造方法的参数的类型构造为一个Class数组通过这个Class数组来判断获取的到底是哪个构造方法。那么问一个小问题,如果要通过getConstructor方法来调用不带参数的构造方法怎么办呢,很简单,传递一个空的Class数组不就行啦。

怎么通过Constructor来获得Class的实例呢?在Constructor中也包含了一个newInstance (Object ... initargs) 方法,但是该方法带有一个Object可变数组作为参数,当然就是传递给构造函数的参数啦,这里的Object可变参数的长度一定是与Class对象调用getConstructor方法中传递的Class可变参数的长度一致的,道理很简单,那么如何给空的构造方法传参数呢?呵呵,大家都懂的。。。

 

好了,通过反射现在可以在运行时得到类的实例,得到并且动态调用类的方法,以及得到带参数的构造方法并且进行实例化,那么我们可以得到类的字段吗?呵呵,当然可以,方法有Method对应,构造方法有Constructor对应,字段也有Field对应,Class中有两个方法:

Field

 

getDeclaredField (String  name) 通过字段名来获得该字段对应的Field对象
Field []

getDeclaredFields ()  返回该类中所有的Field对象,以数组返回

 

 

代码:

public class ReflectTester {
	/*
	 * 利用反射机制动态生成一个对象的object的克隆对象
	 */
	public Object copy(Object object) throws Exception {
		//获得对象所属的Class对象
		Class classType = object.getClass();
		System.out.println("class: " + classType);
		//通过Class的getConstructor方法获得某个构造方法,并且调用newInstance()方法构造对象
		Customer customer = (Customer) classType.getConstructor(new Class[]{}).newInstance(new Object[]{});
		//获得类的所有字段对应的Field对象
		Field[] fields = classType.getDeclaredFields();
		for(int i=0; i<fields.length; i++) {
			Field field = fields[i];
			String fieldName = field.getName();
			String firstLetter = fieldName.substring(0, 1).toUpperCase();
			//通过field.getName()获取字段的名字,并构造getXXX和setXXX方法
			String getMethodStr = "get" + firstLetter + fieldName.substring(1);
			String setMethodStr = "set" + firstLetter + fieldName.substring(1);
			//通过调用Class的getMethod方法,并传入方法名和参数Class数组得到指定方法的Method对象
			Method getMethod = classType.getMethod(getMethodStr, new Class[]{});
			Method setMethod = classType.getMethod(setMethodStr, new Class[]{field.getType()});
			//通过调用传入地对象object的getXXX方法获得指定的字段值
			Object value = getMethod.invoke(object, new Object[]{});
			System.out.println(fieldName + ":" + value);
			//通过调用新创建的对象object的setXXX方法设置指定的字段值
			setMethod.invoke(customer, new Object[]{value});
		}
		return customer;
	}
	
	public static void main(String[] args) throws Exception {
		Customer customer = new Customer();
		customer.setId(200807058);
		customer.setAge(24);
		customer.setName("张三");
		ReflectTester rf = new ReflectTester();
		Customer newCustomer = null;
		newCustomer = (Customer) rf.copy(customer);
		System.out.println(newCustomer.getId() + ":" + newCustomer.getName() + ":" + newCustomer.getAge());
	}
}

class Customer {
	/*
	 * 默认的构造方法不能通过Class的getConstructor动态获得
	 */
	public Customer() {
		
	}
	private long id;
	private String name;
	private int age;
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	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;
	}
	
}
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值