码迷,mamicode.com
首页 > 编程语言 > 详细

Java反射总结(2):反射的应用

时间:2015-10-28 21:04:51      阅读:248      评论:0      收藏:0      [点我收藏+]

标签:

操作对象属性(域)的值

    前面已经知道如何查看任意对象的数据域名称和类型。在编写程序时,如果想要查看域名和类型是很简单的事情,而反射机制可以查看在编译时还不清楚的对象域。

利用get方法获取域(属性)值

    查看对象域的关键方法是Field类的get方法。如果f是一个Field类型的对象,obj是某个包含f域的类的对象,f.get(obj)将返回一个对象,类型为Object,其值为obj域的当前值。示例:

package com.xiaoxiaoyihan.reflection;

import java.lang.reflect.Field;

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }
}
public class ReflectionDemo2 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Person p = new Person("萧萧弈寒");

        Class clz = p.getClass();
        Field f = clz.getDeclaredField("name");
        //f.setAccessible(true);
        Object name = f.get(p);

        System.out.println(name);
    }
}

【运行结果】:

Exception in thread "main" java.lang.IllegalAccessException: Class com.xiaoxiaoyihan.reflection.ReflectionDemo2 can not access a member of class com.xiaoxiaoyihan.reflection.Person with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:109)
……

说明:因为反射机制的默认行为受限于Java的访问控制。如果一个Java程序没有受限于安全管理器的控制,就可以覆盖访问控制。Field,Method或Constructor对象提供了setAccessible方法用于覆盖访问控制。

get方法还需要解决另一个问题,get方法的返回值是Object类型的,上面的name是String类型的,将String类型值,赋给Object对象没有问题,但是如果出现double类型的域呢?Java中的数值类型不是对象。可以通过Field类的getDouble方法,也可以调用get方法然后进行强制类型转换。反射机制会自动地将这个域值打包到相应的对象包装器中,对于double类型,将打包成Double。

package com.xiaoxiaoyihan.reflection;

import java.lang.reflect.Field;

class Person {
    private double salary;

    public Person(double salary) {
        this.salary = salary;
    }
}
public class ReflectionDemo2 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Person p = new Person(100);

        Class clz = p.getClass();

        Field f = clz.getDeclaredField("salary");
        f.setAccessible(true);
//        double salary = (double) f.get(p);
        double salary = f.getDouble(p);
        System.out.println("薪水:" + salary);
    }
}

【运行结果】:
薪水:100.0

利用set方法设置域(属性)值

当然也可以使用set方法,对obj对象的f域设置新值。set方法的签名是:

void set(obj, value)    // obj:操作的对象;value:新值

示例:

package com.xiaoxiaoyihan.reflection;

import java.lang.reflect.Field;

class Person {
    private String name;
    private double salary;

    public Person(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public double getSalary() {
        return salary;
    }

    public String getName() {
        return name;
    }
}
public class ReflectionDemo2 {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Person p = new Person("张三", 100);

        System.out.println(p.getName() + "-----" + p.getSalary());

        Class clz = Person.class;

        Field f = clz.getDeclaredField("name");
        f.setAccessible(true);
        f.set(p, "萧萧弈寒");

        f = clz.getDeclaredField("salary");
        f.setAccessible(true);
        f.set(p, 200);

        System.out.println(p.getName() + "-----" + p.getSalary());

    }
}

调用任意方法

invoke方法

与Field类的get方法查看对象的域相似,Method类提供了一个invoke方法,它允许调用包装在当前Method对象中的方法。invoke方法的签名是:

Object invoke(Object obj, Object...args)

第一个参数是隐式参数,其余的对象提供了显示参数。

例如,m1代表Person的getName方法,那么通过invoke方法获取name的形式为:

String n = m1.invoke(p);

如果返回的是基本类型,invoke方法会返回其包装器类型。例如,m2表示getSalary方法,那么返回的对象实际是一个Double,必须响应的完整类型转换。

double s = m2.invoke(p);

根据Java反射总结(1):解析类结构中的介绍,可以通过Class类的静态方法getDeclaredMethods获取Method对象数组,然后对返回的数组进行遍历,找到指定的方法。与getField方法类似,getField方法通过表示域名的字符串,返回一个Field对象。然而,由于方法存在重载的情况,有可能获得若干个相同名字的方法。因此,还必须提供方法的参数类型。getMethod方法的签名:

Method getMethod(String name, Class...paramTypes)

示例:

package com.xiaoxiaoyihan.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Person {
    private String name;
    private double salary;

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public void raiseSalary(double raise) {
        salary += raise;
    }

    public Person(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

}

public class ReflectionDemo2 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person p = new Person("萧萧弈寒", 100);

        // 获取getName方法对象
        Method m1 = p.getClass().getMethod("getName");
        // 调用getName方法
        String name = (String)m1.invoke(p);
        System.out.println("我的名字是:" + name);

        // 获取raiseSalary方法对象,需要传double类型的参数
        Method m2 = p.getClass().getMethod("raiseSalary", double.class);
        // 调用raiseSalary方法,并传入参数值
        m2.invoke(p, 200);
        System.out.println("加薪后:salary=" + p.getSalary());
    }
}

【运行结果】:
我的名字是:萧萧弈寒
加薪后:salary=300.0

对于静态方法,invoke的第一个参数可以被忽略,既可以将它设置为null。

package com.xiaoxiaoyihan.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Person {
    public static void speak(String name) {
        System.out.println("木头!" + name + ",你倒是说话啊!");
    }

}

public class ReflectionDemo2 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method m = Person.class.getMethod("speak", String.class);
        m.invoke(null, "萧萧弈寒");
    }
}

【运行结果】:
木头!萧萧弈寒,你倒是说话啊!

注意:invoke的参数和返回值必须是Object类型的。这就意味着必须进行多次的类型转换。这样做将会是编译器错过检查代码的机会。此外,通过反射获得方法指针的代码比直接调用方法效率低。

Java反射总结(2):反射的应用

标签:

原文地址:http://www.cnblogs.com/xiaoxiaoyihan/p/4917095.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!