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

操作对象属性(域)的值

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

利用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类型的。这就意味着必须进行多次的类型转换。这样做将会是编译器错过检查代码的机会。此外,通过反射获得方法指针的代码比直接调用方法效率低。

时间: 2024-11-08 22:49:59

Java反射总结(2):反射的应用的相关文章

java的泛型与反射机制

什么是泛型? 泛型,即"参数化类型".顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参). 为什么要使用泛型? 先看如下代码: public class GenericTest { public static void main(String[] args) { List list = new ArrayList(); list.add("qqyumidi"

java工厂类与反射机制

java 简单工厂类 2012-04-22 15:44:07|  分类: java |  标签:java工厂类  简单工厂类  |举报|字号 订阅 简单工厂模式需要由以下角色组成: 接口                        接口的实现类(简单工厂模式里面的具体产品角色)                        工厂注意对比以下三个实例的不同实例1: package org.jzkangta.factorydemo01;//定义接口interface Car{    public 

java 反射和暴力反射 两个DEMO

该类为反射函数 获取和暴力获取ReflectPoin类中的属性 package com.tuozou.test; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectTest { public ReflectTest() { // TODO Auto-generated con

编程基础知识——C++能不能支持Java和ObjC的反射?

C++能不能支持Java和ObjC的反射? 要回答这个问题,首先我们要清楚什么是反射.什么是反射? 教科书的解释我就不说了,(^o^)其实我也记不得.实际开发应用的反射就是在没有某个类型的头文件或者类结构定义的情况下,存取这个类型的对象的成员字段的值,调用这个对象的成员函数(方法). 比如我有定义了一个类型 Class  A,里面有 a,b,c三个字段,有fun()函数.现在我手里只有一个 void* pA,注意它的类型只是一个void指针,我手里也没有Class的头文件,我要怎么样得到,a,b

java学习:用反射构造bean

先贴一些反射的基本知识:-------------------------------------------------------------------- 一.什么是反射:反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.这一概念的提 出很快引发了计算机科学领域关于应用反射性的研究.它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩.其中 LEAD/LEAD++ .OpenC++ .MetaXa和OpenJava

Java 反射机制[Field反射]

Java 反射机制[Field反射] 1.  反射概念及功能 反射就是把Java类中的各种成分映射成相应的Java类.例如一个Java类中用一个Class类的对象来表示.一个类中的组成部分分为成员变量,方法,构造方法,包等等. Java反射机制主要提供了以下功能: 判断在运行时任意一个对象所属的类:在运行时构造任意一个类的对象:判断在运行时任意一个类所具有的成员变量和方法:在运行时调用任意一个对象的方法:生成动态代理. 2.  Field反射 以下代码将obj对象中的String类型的字段对应的

Java基础学习笔记二十三 Java核心语法之反射

类加载器 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接,初始化三步来实现对这个类进行初始化. 加载就是指将class文件读入内存,并为之创建一个Class对象.任何类被使用时系统都会建立一个Class对象. 链接指的是将Java类的二进制代码合并到JVM的运行状态之中的过程.在链接之前,这个类必须被成功加载.类的链接包括验证.准备和解析等几个步骤. 验证:是否有正确的内部结构,并和其他类协调一致. 准备:负责为类的静态成员分配内存,并设置默认初始化值 解析:

学习Spring必学的Java基础知识(1)----反射

引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓“登高必自卑,涉远必自迩”.以下几项Java知识和Spring框架息息相关,不可不学(我将通过一个系列分别介绍这些Java基础知识,希望对大家有所帮助.): [1] Java反射知识-->Spring IoC :http://www.iteye.com/topic/1123081 [2] Java动态代理-->Spring AOP :http://www.iteye.com/topic/1123293 [3] 属性

【GoLang】golang 如何像Java 一样通过类名反射对象?

结论: golang不支持解析string然后执行. golang的反射机制只能存在于已经存在的对象上面. 不知道后续的版本有没有规划,现在只能先加载注册,然后实现类似Java工厂模式的反射. 代码示例: t := reflect.ValueOf(Human{}).Type() // h := reflect.New(t).Elem() // new return address pointer h := reflect.New(t).Interface() fmt.Println(h) hh

Java 反射机制[Method反射]

Java 反射机制[Method反射] 接着上一篇Java 反射机制[Field反射],通过调用Person类的setName方法将obj的name字段的Value设置为"callPersonSetNameMethod"来了解什么是Method反射.示例代码很简单,很容易理解. 可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理. 真正的反射是调用MethodAccessor.invoke()真