反射

反射的作用

  在运行时,动态发现对象或类的真实信息,拥有的构造方法列表,普通方法列表,类属性,注解等相关信息来构造类的对象,调用相应的一些方法。为了解决这个问题,我们有两种做法:

  1. 第一种是假设在编译和运行时都完全知道类型的具体信息,这种情况下,我们可以直接先使用instanceof运算符进行判断,再利用强制类型转换成其运行时类型的变量即可。
  2. 第二种是编译时根本无法预知该对象和类可能属于那些类,程序只依靠运行时信息来发现该对象和类的真实信息,这就必须使用反射。  

获得Class对象 

  每个类被加载之后,系统就会为该类生成一个对应的Class对象,通过该Class对象就可以访问到JVM中的这个类。Java程序中获得Class对象通常有如下三种方式:

  1. 使用Class类的forName()静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名。(添加完整包名)。
        try {
            Class clazz2 = Class.forName("java.lang.String");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

 

  2.调用某个类的class属性来获取该类对应的Class对象。例如Person.class将会返回Person类对应的Class对象。

Class clazz1 = String.class;

  3.调用某个对象的getClass()方法,该方法是java.lang.Object类中的一个方法,该方法将会返回该对象所属类对应的Class对象。 

String str = new String();
Class clazz3 = str.getClass();

从Class中获取信息

1.反射得到包含的构造器

 

  2.反射得到包含的方法列表:

  3.反射得到包含的属性列表:

  4.反射得到包含的注解信息:

反射还可以得到包名等。

 使用反射生成并操作对象

  Class对象可以获得该类里包括的方法(由Method对象表示)、构造器(由Constructor对象表示)、Field(由Field对象表示),这三个类都定义在java.lang.reflect包下,并实现了java.lang.reflect.Member接口。程序可以通过Method对象来执行对应的方法,通过COnstructor对象来调用对应的构造器创建对象,能通过Field对象直接访问并修改对象的属性值。

通过反射来生成对象如下两种方式:

  1. 使用Class对象的newInstance()方法来创建Class对象对应类的实例,这种方式要求该Class对象的对应类有默认构造器,而执行newInstance()方法时实际上是利用默认构造器来创建该类的实例。
// 使用无参构造器创建对象 可能会抛出异常
Class clazz4 = String.class;
clazz4.newInstance();

  2.先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInsance()方法来创建该Class对象对应类的实例,这种方式可以指定选择使用某个构造器来创建实例。

//使用指定的构造方法构造对象
Constructor<String> constructor = clazz4.getConstructor(String.class);
String str2 = constructor.newInstance("Hello World");

通过反射来调用方法:

  当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或者getMethod()方法来获取全部方法或指定方法——这两个方法的返回值是Method对象数组,或者Method对象。每个Method对象对应一个方法,获得Method对象后,程序就可以通过该Method来调用对应的方法。在Method里包含以讹invoke方法,该方法的签名如下:

  • Object invoke(Object obj ,Object... args) :该方法中的obj是执行该方法的主调,后面的args是执行该方法时传入该方法的实参。

  代码:

Class clazz1 = String.class;
Method method = clazz1.getMethod("substring", int.class);
String str3 = "ABCDEFG";
String str6 = (String) method.invoke(str3, 3);
System.out.println(str6);

  当通过Method的invoke方法来调用对应的方法时,Java会要求程序必须有调用该方法的权限,如果程序确实需要调用某个对象的private方法,可以先调用Method对象的setAccessible(boolean flag):将Method对象的accessible标志设置为指示的布尔值。值为true则表示该Method在使用时应该取消Java语言访问权限检查,值为false则反之。

通过反射访问属性值

通过Class对象的getFields()或getField()方法可以获取该类所包括的全部Field(属性)或指定Field。Field提供了如下两组方法来访问属性:

  1. getXxx(Object obj) : 获取obj对象该Field的属性值。此处的Xxx对应8个基本类型,如果该属性的类型是引用类型则取消get后面的Xxxx,直接使用set(Object obj)
  2. setXxx(Object obj ,Xxxx val):将obj对象的该Field设置为val值。此处的Xxx对应8个基本类型,如果该属性的类型是引用类型则取消set后面的Xxx。
class Person
{
	private String name;
	private int age;
	public String toString()
	{
		return "Person [ name:" + name + 
			" , age:" + age + " ]";
	}
}
public class FieldTest
{
	public static void main(String[] args) 
		throws Exception
	{
		//创建一个Person对象
		Person p = new Person();
		//获取Person类对应的Class对象
		Class<Person> personClazz = Person.class;
		//获取Person类名为name的属性
		//使用getDeclaredField,表明可获取各种访问控制符的field
		Field nameField = personClazz.getDeclaredField("name");
		//设置通过反射访问该Field时取消访问权限检查
		nameField.setAccessible(true);
		//调用set方法为p对象的指定Field设置值
		nameField.set(p , "Yeeku.H.Lee");
		//获取Person类名为age的属性
		Field ageField = personClazz.getDeclaredField("age");
		//设置通过反射访问该Field时取消访问权限检查
		ageField.setAccessible(true);
		//调用setInt方法为p对象的指定Field设置值
		ageField.setInt(p , 30);
		System.out.println(p);
	}
}

 反射操作数组

在java.lang.reflect包下还提供了一个Array类,Array对象可以代表所有的数组。程序可以通过使用Array来动态创建数组,和操作数组元素等。

public class ArrayTest1 
{
    public static void main(String args[])
    {
        try
        {
            //创建一个元素类型为String ,长度为10的数组
            Object arr = Array.newInstance(String.class, 10);
            //依次为arr数组中index为5、6的元素赋值
            Array.set(arr, 5, "Struts2权威指南");
            Array.set(arr, 6, "ROR敏捷开发最佳实践");
            //依次取出arr数组中index为5、6的元素的值
            Object book1 = Array.get(arr , 5);
            Object book2 = Array.get(arr , 6);
            //输出arr数组中index为5、6的元素
            System.out.println(book1);
            System.out.println(book2);
        }
        catch (Throwable e)
        {
            System.err.println(e);
        }
    }
}

 

posted @ 2015-10-08 16:12  做个有梦想的咸鱼  阅读(273)  评论(0编辑  收藏  举报