您的位置:首页 > 编程语言 > Java开发

Java反射学习笔记

2015-12-27 23:55 477 查看
一 定义及功能

Java的反射机制指的是,Java程序在运行过程中,对于任意一个类,都能够动态的获得这个类的任意的属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取对象属性和方法的功能称为Java语言的反射机制。

Java反射机制可以提供一下功能:

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

2 在运行时调用任意一个对象的方法;

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

4 在运行时构造任意一个类的对象;

5 生成动态代理;

我们首先使用一个例子来说明Java反射机制是如何工作的。

<span style="font-size:18px;">package reflect.test;</span>
<span style="font-size:18px;">
import java.lang.reflect.Method;

//测试类
class Demo {
	
}

public class ReflectTest {
	public static void main(String[] args) throws ClassNotFoundException {
		
	<span style="white-space:pre">	</span>Demo demo = new Demo();
		System.out.println(demo.getClass().getName());
	}
}</span>


输出结果:

reflect.test.Demo

这样就可以获得demo对象所属的类的命名空间和类名。这个过程使用了对象的getClass()方法来载入指定的类,然后调用getName()方法来获取名称。

二 获取步骤及方式

使用Java的反射机制,需要遵循三个步骤:

1 获得你要操作的类的Class对象;

2 通过第一步获得的class对象,获取要操作的类的方法名或者属性对象;

3 操作第二步获取的属性或者对象。

Java运行的时候,无论某个类实例化多少个对象,他们都会对应一个Class对象,它表示正在运行的程序的类和接口。如何获取这个类呢?常用的方法有三个:

1 通过Class的静态方法forName(),直接输入类的全路径;

2 通过对象的getClass()方法,获得所指向的Class对象。

3 通过类名的.class语法,获得Class对象;

下面通过实例来说明,如何通过以上三个方法获取Class对象。

<span style="font-size:18px;">package reflect.test;
import java.lang.reflect.Method;

//测试类
class Demo {

}

public class ReflectTest {
	public static void main(String[] args) throws ClassNotFoundException {

		// 三种方式获取操作类的class对象
		Class<?> demo1 = null;
		Class<?> demo2 = null;
		Class<?> demo3 = null;
		// 方法一 通过输入String类的全路径
		try {
			demo1 = Class.forName("java.lang.String");
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 方法二 通过实例化后的对象得到Class类
		String strTest = "";
		demo2 = strTest.getClass();
		// 方法三 通过类的class语法,获取Class类
		demo3 = String.class;
		// 输出Class类
		System.out.println(demo1.getName());
		System.out.println(demo2.getName());
		System.out.println(demo3.getName());
	}
}</span>


输出结果:

java.lang.String

java.lang.String

java.lang.String

三 功能详解

前边我们说过了反射的五个主要用途,我们已经试验了第一个用途,通过反射获得对象所属的类,下面我们分别来说明其他的几个用途。

1 通过反射来执行对象的某个方法,代码如下:

<span style="font-size:18px;">package reflect.test;

import java.lang.reflect.Method;

//测试类
class Display {
	// 带有参数的输出方法
	public void show(String name, String content) {
		System.out.println(name + content);
	}
}

public class ReflectMethod {
	public static void main(String[] args) {
		// 1 获取display类的对象
		Display display = new Display();
		Class<?> displayTest = display.getClass();
		// 2 获取操作方法对象
		Method method = null;
		try {
			method = displayTest.getMethod("show", String.class, String.class);
		} catch (Exception e) {
			e.printStackTrace();
		}
		//3 执行获得的方法
		try {
			method.invoke(display, "小盖", "你好!");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}</span>


输出结果:

小盖 你好!

前边说过,使用反射的第一步就是获取这个类,对应我们代码注释的第一步;第二步是获取你将要操作的方法对象,对应我们代码第二步,这个方法包括三个参数,第一个参数是我们要操作的那个方法名,第二个和第三个参数分别是我们要调用的那个方法的参数类型,这个方法的参数个数是根据要调用的方法的参数个数来确定的,所以,参数个数是个不确定的数据。第三步是执行这个方法,即我们的invoke方法,其实调用的就是show方法,这个方法有三个参数,第一个参数是一个对象,使我们要调用的那个类的一个对象,后边的参数是我们要调用的方法需要的参数,这个参数必须与要调用方法的参数一致。

2 通过反射来给某个类的属性赋值,代码如下:

<span style="font-size:18px;">package reflect.test;

import java.lang.reflect.Field;
//测试实体类
class Person{
	private String name;
	private Integer age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
}
public class ReflectAttribute {
	
	public static void main(String[] args){
		//实例化一个源对象并且赋值
		Person fromPerson = new Person();
		fromPerson.setName("小盖");
		fromPerson.setAge(22);
		//实例化一个目标对象
		Person toPerson = new Person();
		try {
			//给目标对象属性赋值
			replace(fromPerson, toPerson);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		}
		System.out.println(toPerson.getName() +" : "+ toPerson.getAge().toString());
	}
	
	public static void replace (Object fromClass,Object toClass) throws SecurityException, NoSuchFieldException{
		//获取源对象的类
		Class<?> fromObject = fromClass.getClass();
		//获取源对象的属性集
		Field[] fromFields = fromObject.getDeclaredFields();
		//获取目标对象的类
		Class<?> toObject = toClass.getClass();
		//定义目标对象的属性集
		Field toField = null;
		//循环源对象属性集
		for (Field fromField : fromFields) {
			//获取源对象的属性名称和对应目标对象的属性名称
			String name = fromField.getName();
			toField = toObject.getDeclaredField(name);
			//设置属性操作权限
			fromField.setAccessible(true);
			toField.setAccessible(true);
			try {
				//给目标对象属性赋值
				toField.set(toClass, fromField.get(fromClass));
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}
	
}</span>


运行结果:

小盖 : 22

反射机制中的类有Class对应,方法有Method对应,属性由Field对应。Field提供了get和set属性,但是由于属性时私有类型,所以需要设置访问权限。本例中是通过循环分别为每个属性设置访问权限,您也可以通过设置属性集的权限来同意修改访问权限,在此不再赘述。

3 通过反射在运行时动态创建类的一个对象,代码如下:

<span style="font-size:18px;">package reflect.test;

import java.lang.reflect.Field;

class Student{
	private String stuName;
	private String stuNo;
	public String getStuName() {
		return stuName;
	}
	public void setStuName(String stuName) {
		this.stuName = stuName;
	}
	public String getStuNo() {
		return stuNo;
	}
	public void setStuNo(String stuNo) {
		this.stuNo = stuNo;
	}
	
}

public class ReflectClass {
	
	public static void main (String[] args){
		//实例化源对象
		Student fromStudent = new Student();
		fromStudent.setStuName("小盖");
		fromStudent.setStuNo("11050241003");
		//调用replace方法生成目标对象
		Student toStudent = (Student)replace(fromStudent);
		System.out.println("姓名: " + toStudent.getStuName());
		System.out.println("学号: " + toStudent.getStuNo());
	}
	
	private static Object replace(Object fromClass){
		//1--2 获得源对象的类和属性集、定义目标对象
		Class<?> fromObject = fromClass.getClass();
		Field[] fromFields = fromObject.getDeclaredFields();
		Object toObject = null;
		try {
			//3 通过类直接创建目标对象
			toObject = fromObject.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		//给目标对象创建属性等
		for (Field fromField : fromFields) {
			fromField.setAccessible(true);
			try {
				fromField.set(toObject, fromField.get(fromClass));
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
		return toObject;
	}
}</span>


输出结果:

姓名: 小盖

学号: 11050241003

Class的newInstance方法只能创建无参数的构造函数的类,如果构造函数带有参数,那么就需要另外一种方式。属性赋值和上例一样,这里不再赘述。

四 注意事项

在获取类的方法、属性和构造函数时,会有getxxx和getDeclatedxxx两种方法。它们的区别是前者只能返回访问权限为public的方法和属性,包括父类的;后者返回的是所有访问权限的方法和属性,不包括父类的。

五 总结

反射的应用范围很广,使用反射可以降低程序之间的耦合性,增加程序的灵活性。

以后会根据实际应用情况更加深入的研究反射,敬请期待。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: