反射

反射是一种功能强大且复杂的机制,反射机制可以用来:

  • 在运行中分析类的能力
  • 在运行中查看对象
  • 实现通用的数组操作代码
  • 利用Method对象

Class类

在程序运行期间,java运行时系统始终为所有对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。可以通过专门的java类访问这些信息,保存这些信息的类被称为Class。
关于Class类的详尽描述:https://docs.oracle.com/javase/6/docs/api/java/lang/Class.html

1、Object类的getClass()方法将会返回一个Class类型的实例

  Employee e = new Employee();
        Class c1 = e.getClass();
        //输出结果:class com.hhr.reflect.Employee
        System.out.println(c1);

如同一个Employee对象表示一个特定的雇员属性一样,一个Class对象将表示一个特定类的属性。
最常用的一个Class方法是getName,将返回类的名字:

 Employee employee = new Employee("Joy");
        String name = employee.getClass().getName();
        //输出结果:com.hhr.reflect.Employee
        System.out.println(name);

2、调用静态方法forName获得类名对应的Class对象

try {
            String className = "java.util.Date";
            Class c1 = Class.forName(className);
            //输出结果:class java.util.Date
            System.out.println(c1);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

此方法只有在className是类名或者接口名时才能够执行。否则会抛出ClassNotFoundException。无论何时使用这个方法,都应该提供一个异常处理器。
3、T.class (T为任意的java类型)

  Class c1 = int.class;
  Class c2 = Date.class;
  Class c3 = Double[].class;

请注意,Class对象实际上表示的是一种类型,而这个类型未必一定是一种类。int不是类,但int.class是一个Class类型的对象。
鉴于历史原因,getName方法应用于数组类型时会返回一个很奇怪的名字,如Double[].class会返回:

class [Ljava.lang.Double;

虚拟机为每个类型管理一个Class对象,因此可以用==运算符实现两个类对象比较的操作。

if (e.getClass()==Employee.class){

还有一个很有用的方法,newInstance(),可以用来快速的创建一个类的实例。

e.getClass().newInstance()

newInstance()调用默认的构造器初始化新创建的对象,如果没有默认的构造器,将会抛出异常。

利用反射分析类的能力

在java.lang.reflect包中有三个类:Field、Method、Constructor,分别用于描述类的域、方法、构造器。
具体描述:
https://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Field.html
https://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Method.html
https://docs.oracle.com/javase/6/docs/api/java/lang/reflect/Constructor.html
这三个类都有一个getName方法,用来返回项目的名称。
这三个类还有一个getModifiers()方法,将返回一个整型数值,描述public和static这样的修饰符的使用情况。
另外还可以利用java.lang.reflect包中的Modifier类的静态方法分析getModifiers()返回的整型数值。利用 Modifier.toString将修饰符打印出来。
Class类中的getFields、getMethods合getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。
Class类的getDeclaredFields、getDeclaredMethods和getDeclaredConstructors方法将分别返回泪中声明的全部域、方法和构造器,其中包括私有和受保护的成员,但不包括超类的成员。
程序示例:程序清单-打印一个类的全部信息

在运行时使用反射分析对象

现在已经知道任意对象的数据域名称和类型:

  • 获得对应的Class对象
  • 通过Class对象调用etDeclaredFields()

查看对象域的关键方法是Field类中的get方法。

 Employee employee = new Employee("Harry");
 Class cl =  employee.getClass();
 Field field = cl.getDeclaredField("name");
 Object v = field.get(employee);

实际上,上面这段代码会抛出一个IllegalAccessException,因为name是一个私有域。只有利用get方法才能得到可访问域的值。除非拥有访问权限,否则Java安全机制只允许查看任意对象有那些域而不允许读取它们的值。
反射机制的默认行为受限于Java的访问控制。然而,如果一个Java程序没有受到安全管理器的控制,就可以覆盖访问控制了。为此,可以调用Field、Constructor、Method对象的setAccessible方法。

field.setAccessible(true);

程序清单-一个可供任意类使用的toString方法
程序清单-利用反射扩展任意类型的数组

调用任意方法

反射机制允许你调用任意方法。
Method类中有一个invoke方法,它允许调用包装在当前Method对象中的方法。

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

第一个参数是隐式参数,其余的对象提供了显式参数。对于静态方法,第一个参数可以忽略,即可以将它设置为null。

 String m = (String) method.invoke(a);

如果返回的是基本类型,invoke方法会返回其包装器类型。
由于invoke的参数和返回值必须是Object类型,必须通过多次的类型转换。这样会使编译器错过检查代码的机会。不仅如此,使用反射获得方法指针的代码要比仅仅直接调用方法明显慢一些。
建议不要使用Method对象的回调功能。使用接口进行回调会使得代码执行速度更快

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值