Java核心技术-反射

能够分析类能力的程序称为反射。
反射机制可以用来:
1)在运行中分析类的能力。
2)在运行中查看对象,例如,编写一个toString方法供所有类使用。
3)实现通用的数组操作代码。
4)利用Method对象,这个对象很像C++中的函数指针。
。。。。。。

Class类
在程序运行期间,Java运行时系统始终为所有的对象维护一个被成为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。
获得Class类对象的三种方式:
1)Employee e ;
Class c1 = e.getClass();
2)String className = “java.util.Date”;
Class c1 = Class.forName(className);
3)类名.class
虚拟机为每个类型管理一个Class对象。因此,可以用==运算符实现两个类对象比较的操作。eg:
if(e.getClass()==Employee.class)…
将forName与newInstance配合使用来创建对象。
String s = “java.util.Date”;
Object m = Class.forName(s).newInstance();
newInstance方法调用默认的构造器(没有参数的构造器),初始化新创建的对象,如果这个类没有默认的构造器,就会抛出一个异常。
如果要以这种方式向希望按名称创建的类构造器提供参数,就不要使用上面那条语句,而必须使用Constructor类中newInstance方法。

利用反射分析类的能力:
在java.lang.reflect包中有三个类Field,Method和Constructor分别用于描述类的域,方法和构造器。这三个类中都有一个叫做getName的方法,用来返回项目的名称。Field类中有个getType方法,用来返回描述域所属类型的Class对象,Method和Constructor类有能够报告参数类型的方法,Method类还有一个可以报告返回类型的方法。这三个类还有一个叫做getModifiers的方法,他将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用情况。另外,还可以利用java.lang.reflect包中的Modifier类的静态方法分析getModifiers返回的整型数值。例如,可以使用Modifier类中的isPublic,isPrivate或isFinal判断方法或构造器是否是public,private,final。
Class类中的getFields,getMethod和getConstructors方法将分别返回类提供的public域,方法和构造器,其中包括超类的公有成员。Class类的getDeclareFields,getDeclareMethods和getDeclareConstructors方法将分别返回类中声明的全部域,方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。

Field[] getFileds()
Field[] getDeclaredFields()
getFileds()将返回一个包含Field对象的数组,这些对象记录这个类或其超类的公有域。
getDeclaredFields()也将返回包含Field对象的数组,这些对象记录这个类的全部域。
如果类中没有域,或者Class对象描述的基本类型或数组类型,这些方法将返回一个长度为0的数组。
Method[] getMethods()
Method[] getDeclaredMethods()
返回包含Method对象的数组:getMethods将返回所有的公有方法,包括从超类继承来的公有方法;getDeclaredMethods返回这个类或接口的全部方法,但不包括由超类继承了的方法。
Constructor [] getConstructors()
Constructor [] getDeclaredConstructors()
返回包含Constructor对象的数组,其中包含了Class对象所描述的类的所有公有构造器(getConstructors)或所有构造器(getDeclaredConstructors)。

Employee harry = new Employee("harry",3400);
Class c1 = harry.getClass();
Field f = c1.getDeclaredField("name");
Object v = f.get(harry);
    这段代码存在一个问题,由于name域是私有域,所以get方法会抛出一个IllegalAccessException。只有利用get方法才能得到可访问域的值。除非拥有访问权限,否则Java安全机制只允许查看任意对象有哪些域,而不允许读取他们的值。
    反射机制默认行为受限于Java的访问控制,然而,如果一个Java程序没有受到安全管理的控制,就可以覆盖访问控制。为了达到这个目的,需要调用Field,Method或Constructor对象的setAccessible方法:
    f.setAccessible(true);


----------
API
java.lang.reflect.AccessibleObject.
void setAccessible(boolean flag)
为反射对象设置可访问标志,true表示屏蔽Java语言的访问检查,使得对象的私有属性也可以被查询和设置。
boolean isAccessible()
返回反射对象的可访问标志的值。
java.lang.Class.
Field getField(String name)
Field getField()
返回指定名称的公有域,或包含所有域的数组。
Field getDeclaredField(String name)
Field [] getDeclaredFields()
返回类中声明的给定名称的域,或者包含声明的全部域的数组。
java.lang.reflect.Field
Object get (Object obj)
返回obj对象中Field对象表示的域值。
void set(Object obj , Object newValue)
用一个新值设置Obj对象中Field对象表示的域。

反射,可以在运行时期动态创建对象;获取对象的属性、方法;


public class Admin {

    // Field
    private int id = 1000;
    private String name = "匿名";

    // Constructor
    public Admin(){
        System.out.println("Admin.Admin()");
    }
    public Admin(String name){
        System.out.println("Admin.Admin()" + name);
    }

    // Method
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

}


// 反射技术
public class App {

    // 1. 创建对象
    @Test
    public void testInfo() throws Exception {
        // 类全名
        String className = "cn.itcast.c_reflect.Admin";
        // 得到类字节码
        Class<?> clazz = Class.forName(className);

        // 创建对象1: 默认构造函数简写
        //Admin admin = (Admin) clazz.newInstance();

        // 创建对象2: 通过带参数构造器创建对象
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
        Admin admin = (Admin) constructor.newInstance("Jack");

    }
    @Test
    //2. 获取属性名称、值
    public void testField() throws Exception {

        // 类全名
        String className = "cn.itcast.c_reflect.Admin";
        // 得到类字节码
        Class<?> clazz = Class.forName(className);
        // 对象
        Admin admin =  (Admin) clazz.newInstance();

        // 获取所有的属性名称
        Field[]  fs =  clazz.getDeclaredFields();
        // 遍历:输出每一个属性名称、值
        for (Field f : fs) {
            // 设置强制访问
            f.setAccessible(true);
            // 名称
            String name = f.getName();
            // 值
            Object value = f.get(admin);

            System.out.println(name + value);
        }
    }

    @Test
    //3. 反射获取方法
    public void testMethod() throws Exception {

        // 类全名
        String className = "cn.itcast.c_reflect.Admin";
        // 得到类字节码
        Class<?> clazz = Class.forName(className);
        // 对象
        Admin admin =  (Admin) clazz.newInstance();

        // 获取方法对象    public int getId() {
        Method m = clazz.getDeclaredMethod("getId");
        // 调用方法
        Object r_value = m.invoke(admin);

        System.out.println(r_value);
    }

}

调用任意方法:
从表面上看,java没有提供方法指针,即将一个方法的存储地址传给另一个方法,以便第二个方法能够随后调用他。但是,反射机制允许调用任意方法。
为了能够看到方法指针的工作过程,先回忆一下利用Field类的get方法查看对象域的过程,与之类似,在Method方法中有个invoke方法,他允许调用包装在当前Method对象中的方法。
方法签名:不包括返回值类型和修饰符
invoke的方法签名
invoke(Object obj , Object…args)
eg:假设m1代表Employee类的getName方法,
String n = (String) m1.invoke(harry);
如果返回值类型是基本数据类型,invoke方法会返回其包装器类型。
eg:m2表示Employee类的getSalary方法。那么返回的对象实际上是一个Double,必须完成相应的类型转换。可以使用自动拆箱将它转换为一个double:
double s = (Double) m2.invoke(harry);

getMethod的方法签名是:
Method getMethod(String name , Class…parameterTypes)
eg:如何获得Employee类的getName方法和raiseSalary方法的方法指针。
Method m1 = Employee.class.getMethod(“getName”);
Method m2 = Employee.class.getMethod(“raiseSalary”,double.class);

    API 
    public Object invoke(Object implicitParameter,Object[]explicitParameters)
    调用这个方法所描述的方法,传递给定参数,并返回方法的返回值。对于静态方法,把NULL最为隐式参数传递。在使用包装器基本类型的值时,基本类型的返回值必须是未包装的。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值