反射机制深入研究

目标:

1,使用反射调用类中指定方法

2,使用反射完成setter和getter方法调用

3,使用反射直接操作类中属性。

4,使用反射操作数组

内容

通过反射调用类中指定方法

正常情况下,一个类的对象功能产生后,就可以直接调用类中方法,如果要调用,必须清楚方法名是什么,之后通过Class类中的方法

public Method getMethod(String name,
                        Class<?>... parameterTypes)
                 throws NoSuchMethodException,
                        SecurityException
 返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。 

得到一个Method对象,之后通过这个对象执行方法

 

实例:

先定义一个Person类。

package 类集;

interface China{    // 定义China接口
    public static final String NATIONAL = "China" ;    // 定义全局常量
    public static final String AUTHOR = "小华" ;    // 定义全局常量
    public void sayChina() ;        // 无参的,没有返回值的方法
    public String sayHello(String name,int age) ;    // 定义有两个参数的方法,并返回内容
}
public class Person implements China{
    private String name ;
    private int age ;
    public Person(){    // 无参构造
    }
    public Person(String name){
        this.name = name ;    // 设置name属性
    }
    public Person(String name,int age){
        this(name) ;
        this.age = age ;
    }
    public void sayChina(){    // 覆写方法
        System.out.println("作者:" + AUTHOR + ",国籍:" + NATIONAL) ;
    }
    public String sayHello(String name,int age){
        return name + ",你好!我今年:" + age + "岁了!" ;
    }
    public void setName(String name){
        this.name = name ;
    }
    public void setAge(int age){
        this.age = age ;
    }
    public String getName(){
        return this.name ;
    }
    public int getAge(){
        return this.age ;
    }
};

调用SayChina()方法。因为此方法没有任何参数。

执行调用的方法:Method类中的invoke()

 Object invoke(Object obj, Object... args) 
          对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。 

执行方法的时候,需要传递参数,而且需要实例化对象

调用方法如下:

package 类集;

import java.lang.reflect.Method;
public class GetInterfaceDemo {
    public static void main(String[] args) {
        Class<?> c1 = null ;
        try{
            c1 = Class.forName("类集.Person") ;    // 实例化Class对象
        }catch(Exception e){}
        try{
            Method  met = c1.getMethod("sayChina") ;    // 找到sayChina()方法
            met.invoke(c1.newInstance()) ;    // 调用方法,newInstance之前讲过,是Class类中的方法。
        }catch(Exception e){
            e.printStackTrace() ;
        }
    }
}

newInstance()方法

 T newInstance() 
          创建此 Class 对象所表示的类的一个新实例。 

输出结果:

作者:小华,国籍:China

程序说明

通过Class类的getMethod()方法,根据方法名得到Method对象,并通过Invoke()调用指定方法,但是在invoke方法必须传递一个类的实例化对象,因为sayChina()没有参数,所有不需要设置参数。

如果调用的方法中存在参数,则必须设置参数类型及内容

调用sayHello()方法。

package 类集;

import java.lang.reflect.Method;
public class GetInterfaceDemo {
    public static void main(String[] args) {
        Class<?> c1 = null ;
        try{
            c1 = Class.forName("类集.Person") ;    // 实例化Class对象
        }catch(Exception e){}
        try{
            Method  met = c1.getMethod("sayHello",String.class,int.class) ;    // 找到sayChina()方法
            String rv = null ;
            rv = (String)met.invoke(c1.newInstance(),"小华",30) ;    // 调用方法
            System.out.println(rv) ;
        }catch(Exception e){
            e.printStackTrace() ;
        }
    }
}

输出结果:

小华,你好!我今年:30岁了!

调用Setter和getter

setter和getter是一个属性标准访问方法,如果一个类被封装,必须通过setter和getter方法设置和取得,实际上此方法这样规定,主要是因为

反射机制可以给予支持。

  通过反射调用setter和getter()方法。

如果要想调用setter,例如:name属性设置,则setName()方法写,方法第二个字母要大写。

以下就是通过setter和getter设置属性,和获取属性。

package 类集;
import java.lang.reflect.Method;
public class GetInterfaceDemo {
    public static void main(String[] args) {
        Class<?> c1 = null ;
        Object obj = null ;
        try{
            c1 = Class.forName("类集.Person") ;    // 实例化Class对象
        }catch(Exception e){}
        try{
            obj = c1.newInstance() ;
        }catch(Exception e){}
        setter(obj,"name","李兴华",String.class) ;    // 调用setter方法
        setter(obj,"age",30,int.class) ;    // 调用setter方法
        System.out.print("姓名:") ;
        getter(obj,"name") ;
        System.out.print("年龄:") ;
        getter(obj,"age");
    }

/*

* obj 要操作的对象
* att 要操作属性
* value 要设置的内容
* type 要设置的属性类型
* */

public static void setter(Object obj,String att,Object value,Class<?> type){
        try{
            Method met = obj.getClass().getMethod("set"+initStr(att),type) ;    // 得到setter方法
            met.invoke(obj,value) ;    // 设置setter的内容
        }catch(Exception e){
            e.printStackTrace() ;
        }
    }
    public static void getter(Object obj,String att){
        try{
            Method met = obj.getClass().getMethod("get"+initStr(att)) ;    // 得到setter方法
            System.out.println(met.invoke(obj)) ;    // 调用getter取得内容
        }catch(Exception e){
            e.printStackTrace() ;
        }
    }
    public static String initStr(String old){    // 将单词的首字母大写
        String str = old.substring(0,1).toUpperCase() + old.substring(1) ;
        return str ;
    }
}

输出结果;

姓名:李兴华
年龄:30

补习知识:

实例化Class对象,getClass(),作用与forName()类似,参考前面讲解

 Class<?> getClass() 
          返回此 Object 的运行时类

 以上操作,在开发中,用户也许不会直接涉及具体代码开发,但是操作原理用户必须掌握清楚

 

通过反射调用属性

如果要操作一个类中的属性,则可以通过Filed完成,而不必麻烦的通过setter和getter。

得到公共属性,Class类里面方法

Field getField(String name) 
          返回一个 Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。 

得到本类属性,Class类里面方法

 Field getDeclaredField(String name) 
          返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。 

取得属性内容,Field类里面有get()方法

 Object get(Object obj) 
          返回指定对象上此 Field 表示的字段的值。 

设置属性内容,Field类里面的set()方法。

 void set(Object obj, Object value) 
          将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 

访问私有属性的时候,必须让这个属性对外可见

 void setAccessible(boolean flag) 
          将此对象的 accessible 标志设置为指示的布尔值。 

实例:

package 类集;

import java.lang.reflect.Field;
public class GetInterfaceDemo {
    public static void main(String[] args) throws Exception {  //在这里抛出所有异常
        Class<?> c1 = null ;
        Object obj = null ;
        try {
            c1 = Class.forName("类集.Person") ;
        } catch (ClassNotFoundException e) {
        }    // 实例化Class对象
        obj = c1.newInstance() ;
        Field nameField = null ;
        Field ageField = null ;
        nameField = c1.getDeclaredField("name") ;    // 取得name属性
        ageField = c1.getDeclaredField("age") ;    // 取得name属性
        nameField.setAccessible(true) ;    // 此属性对外部可见
        ageField.setAccessible(true) ;    // 此属性对外部可见
        nameField.set(obj,"李兴华") ;    // 设置name属性内容
        ageField.set(obj,30) ;            // 设置age属性内容
        System.out.println("姓名:" + nameField.get(obj)) ;
        System.out.println("年龄:" + ageField.get(obj)) ;
    }
}

输出结果:

姓名:李华
年龄:30

但是以上操作中是否需要类的setter和getter方法支持呢?证明以上操作与setter及getter无关,为了保证程序安全性,最好通过setter和getter

方法完成调用

通过反射操作数组。

数组使用Array类完成操作数组。

class类中有以下方法:

 Class<?> getComponentType() 
          返回表示数组组件类型的 Class 

Array类有以下方法:

得到数组指定下标的内容。

static Object get(Object array, int index) 
          返回指定数组对象中索引组件的值。 

修改指定下标内容

static void set(Object array, int index, Object value) 
          将指定数组对象中索引组件的值设置为指定的新值。 

开辟新数组:newInstance

static Object newInstance(Class<?> componentType, int... dimensions) 
          创建一个具有指定的组件类型和维度的新数组。 

取得数组信息,并修改数组。

取得数组长度:

static int getLength(Object array) 
          以 int 形式返回指定数组对象的长度。 

 

实例:

package 类集;

import java.lang.reflect.Array;
public class GetInterfaceDemo {
    public static void main(String[] args) throws Exception {
        int temp[] = {1,2,3} ;// 声明一整型数组
        Class<?> c = temp.getClass().getComponentType() ;    // 取得数组的Class对象
        System.out.println("类型:" + c.getName()) ;    // 取得数组类型名称
        System.out.println("长度:" + Array.getLength(temp)) ;
        System.out.println("第一个内容:" + Array.get(temp,0)) ;
        Array.set(temp,0,6) ;
        System.out.println("第一个内容:" + Array.get(temp,0)) ;
    }
}

输出结果:

类型:int
长度:3
第一个内容:1
第一个内容:6

复习:getName()

 String getName() 
          以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称 

使用Array类还可以修改数组大小,使用newInstance()、

修改的过程实际上是一个创建的过程,创建一个新数组,所有还要把旧数组拷贝到新数组中去

实例:

package 类集;

import java.lang.reflect.Array;
public class GetInterfaceDemo {
    public static void main(String[] args) throws Exception {
        int temp[] = {1,2,3} ;// 声明一整型数组
        int newTemp[] = (int []) arrayInc(temp,5) ;    // 重新开辟空间5,强制转换,由Object类型向下转型
        print(newTemp) ;
        System.out.println("\n-------------------------") ;
        String t[] = {"lxh","mldn","mldnjava"} ;
        String nt[] = (String [])arrayInc(t,8) ;
        print(nt) ;
    }
    public static Object arrayInc(Object obj,int len){
        Class<?> c = obj.getClass() ;
        Class<?> arr = c.getComponentType() ;    // 得到数组的
        Object newO = Array.newInstance(arr,len) ;    // 开辟新的大小
        int co = Array.getLength(obj) ;
        System.arraycopy(obj,0,newO,0,co) ;    // 拷贝内容,旧数组obj从0开始,拷贝到new0中从0开始,拷贝长度是co。
        return newO ;
    }
    public static void print(Object obj){    // 数组输出
        Class<?> c = obj.getClass() ;
        if(!c.isArray()){    // 判断是否是数组
            return;
        }
        Class<?> arr = c.getComponentType() ;
        System.out.println(arr.getName()+"数组的长度是:" + Array.getLength(obj)) ;     // 输出数组信息
        for(int i=0;i<Array.getLength(obj);i++){
            System.out.print(Array.get(obj,i) + "、") ;    // 通过Array输出
        }
    }
}

输出结果:

int数组的长度是:5
1、2、3、0、0-------------------------
java.lang.String数组的长度是:8
lxh、mldn、mldnjava、nullnullnullnullnull

 

posted @ 2017-03-20 23:55  美好的明天  阅读(299)  评论(0编辑  收藏  举报