Java的反射机制

前言


反射提供了一种动态的功能,这种动态功能非常强大。它主要体现在通过反射相关的API,就可以知道一个陌生


的Java类的所有信息,包括属性、方法、构造器等。而且元素完全可以在运行时动态的进行创建或调用,而不必在


JVM运行时就进行确定。通过查阅相关资料,进行了整理,本篇也是基于下面文章进行扩充。


       http://blog.csdn.net/mark_yangs/article/details/47427037》             

           

反射的原理是什么


严格意义上来讲,Java并不是动态语言,它并没有Perl和Python那样灵活。但是,Java的反射机制却让它显得动


态起来。


一般情况下,如果想生成这个类的对象时,运行这个程序的JVM会确认这个类的Class对象是否已经加载。如果


尚未加载,JVM就会根据类名查找.class文件,并将其载入,一旦这个类的Class对象被载入内存,它就可以被用来创


建这个类的所有对象。


另外,对于一个未知类型的引用来说,通常会采用强制类型转换的形式来得到开发者想要的类型引用,如果执行


了一个错误的类型转换,就会抛出一个ClassCastException异常。


  在以上两个过程中,Class类一直都在起作用。因为Class类实例包含的是一个Java类的全部信息,包括类名、方


法、属性等。换句话来说,也就是Class对象就是代表一个类的类。


Java用Class类来代表了所有的类,方便了开发者掌控类信息。通过Class,开发者可以得到属性、方法、构造


器、修饰符等信息。在动态的获取这些信息以后,开发者就可以用该Class创建实例、调用任何方法、访问任何属性


等操作,这些也是反射的主要用途。


反射机制相关的API主要集中在java.lang.reflect包下面,开发者也就是利用该包下面的接口和类进行相关的反射


开发。大多数的框架,如Struts、Hibernate和Spring,都会频繁地使用反射API来完成它们动态的功能。


Class类的含义和作用是什么


Class类在反射机制中起到了非常关键的作用,它是开发者进行反射开发的入口。从上面可以知道Class是用来记


录一个类的各种信息,它伴随着类的加载而 创建。那么,类会在什么时候被加载到JVM中呢?概括来说,一个普通的


Java类,会在以下几种情况,被加载到JVM中。


(1)需要使用该类创建对象。如下代码就会导致Stundent类被加载到JVM类中:


Student mStudent=new Student();

(2)访问该类的静态成员,实例代码如下:


public class Student {
	static int count=2;
}

public class Main {
	public static void main(String[] args) {
		System.out.println(Student.count);
	}
}

(3)使用Class类的静态forName()方法,动态地加载一个指定类名的类。如果类未找到,则抛出


ClassNotFoundException异常,实例代码如下:


public class Main {
	public static void main(String[] args) throws ClassNotFoundException {
		Class clazz=Class.forName("com.example.bill.Student");
	}
}


不管通过什么样的形式,类一旦被加载进入JVM以后,就会为它创建一个Class类的实例对象。那么,开发者又


该如何得到这个Class对象呢?概括来说,要得到一个类的Class对象,可以通过以下几种途径。


(1)Class的forName()方法的返回值就是Class类型,也就是动态导入类的Class对象的引用。forName()方法的完


整定义如下:


public static Class<?> forName(String className) throws ClassNotFoundException


(2)每个类都会有一个名称为Class的静态属性,通过它也是可以获取到Class对象的,实例代码如下:


Class<Student> clazz=Student.class;

(3)Object类中有一个名为getClass的成员方法,它返回的是对象的运行时类的Class对象。因为Object类是所有


类的父类,所以,所有的对象都可以使用该方法得到它运行时类的Class对象,实例代码如下:


public class Main {
	public static void main(String[] args) throws ClassNotFoundException {
		Student mStudent=new Student();
		Class<Student> clazz=(Class<Student>) mStudent.getClass();
	}
}

获取到Class对象以后,就可以通过调用它的一些成员方法来获取它所代表的类的属性、方法、修饰符等信息,


以及调用newInstance()方法来创建新的实例对象,还有其他一些功能方法。


如何操作类的成员变量


Field类,代表的是类的属性(字段),也被称为成员变量。反射可以获取到它的对象,Field对象通过Class类的


getDeclaredField()或getDeclaredFields方法获取到,处于java.lang.reflect包下。Field提供有关类或接口的单个字段


的信息,以及对它的动态访问权限,反射的字段可能是一个静态的字段或实例的字段。


Field的方法主要分为两个类,即getXXX和setXXX。其中getXXX是用于获取某个对象的该字段的值,并且有一


定的类型规定,例如,getFloat(),而setXXX则是用于设置值,它们一般有两个参数,一个是对象引用,另一个则是


需要设置的值。


以下为一个Field的使用示例,通过反射来比较两个对象的大小:


public class FieldTestClass {
	public String name;
	public int age;
	
	public FieldTestClass(String name,int age){
		super();
		this.name=name;
		this.age=age;
	}
}


public class Main {
	public static void main(String[] args) throws ClassNotFoundException {
		FieldTestClass obj1=new FieldTestClass("bill1",100);
		FieldTestClass obj2=new FieldTestClass("bill2", 200);
		System.out.println(compare(obj1, obj2).name);
	}
	
	private static FieldTestClass compare(FieldTestClass obj1,FieldTestClass obj2){
		try {
			Field field1=obj1.getClass().getDeclaredField("age");
			Field field2=obj2.getClass().getDeclaredField("age");
			int val1=(Integer)field1.get(obj1);
			int val2=(Integer)field2.get(obj2);
			if(val1>val2){
				return obj1;
			}else{
				return obj2;
			}
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
}

以上代码输出结果为:bill2


值得注意的是,如果我们将FieldTestClass中的age设置成private,也就是私有属性时,程序会报


IllegalAccessException异常。



如何操作类的方法


Method类,代表的是类的方法。与Field类似,反射可以获取到它的对象,使用它的方式则是动态调用,Method


对象通过Class类的getMethod()或getMethods()方法获取到,也处于java.lang.reflect包下,Method提供关于类或接口


中的某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法在内)。


Method类中使用最多的方法是invoke(),它的含义就是方法调用。它的第一个参数为Class所代表的类的一个实例


对象,以后则是一个不定长的Object类型的参数列表,它是Method对象所代表的方法需要的参数列表。


以下为一个Method的使用实例,通过命令行参数来实现方法的动态调用:


public class MethodTestClass {
	public void m1(){
		System.out.println("m1.....");
	}
	public void m2(){
		System.out.println("m2.....");
	}
}

public class CallMethodTest {
	public static void main(String[] args) {
		args = new String[] { "m1" };
		String methodName = args[0];
		if (methodName != null) {
			Class<MethodTestClass> clazz = MethodTestClass.class;
			try {
				Method m = clazz.getDeclaredMethod(methodName);
				if (m != null) {
					MethodTestClass obj = clazz.newInstance();
					m.invoke(obj);
				}
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	}
}

以上代码输出结果:m1.....


注意如果指定的方法名不存在,程序会报NoSuchMethodException异常;如果将方法的权限设置成private,也就是私


有属性时,程序会报IllegalAccessException异常。


如何利用反射机制访问类的私有成员


根据Java的语法规则,类的私有成员(包括私有的成员变量和方法)只能被内部的方法所访问。但是,通过反射


机制,却可以访问一个类的所有成员,包括私有成员在内。


如果开发者在开发反射机制程序的时候,对待私有成员像公有成员一样,那就会遇到一个异常的抛出,以下程序


可以访问私有成员:

public class PrivateTestClass {
	private String field;
	public PrivateTestClass(String field){
		super();
		this.field=field;
	}
}

public class PrivateTest {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		PrivateTestClass obj=new PrivateTestClass("bill");
		Class clazz=obj.getClass();
		Field f=clazz.getDeclaredField("field");
		f.setAccessible(true);//访问性设置成true
		System.out.println(f.get(obj));
	}
}


程序输出结果:bill


从以上代码来看,私有的成员变量field是可以被成功访问的。其中比较关键一行代码就是调用Field的


setAccessible()方法,让field属性变得可以访问。



-------------------------------------------------------------------------------------------------------------------------------------------------------

转载请注明出处:http://blog.csdn.net/hai_qing_xu_kong/article/details/47614679情绪控



©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页