JAVA——反射(Reflect)

    xiaoxiao2021-03-25  137

    什么是反射:

    In computer science, reflection is the ability of a computer program to examine, introspect, and modify its own structure and behavior at runtime.(From wikipedia)

    也就是说,反射是计算机程序在运行时刻能够获取自身信息的能力。

    为什么要用反射呢?

    首先我们看一个简单的反射的例子:

    public class MyClass { public void print() { System.out.println("print方法被调用了"); } } import java.lang.reflect.Method; public class Main { public static void main(String args[]) { try { Class c = Class.forName("MyClass"); Method m = c.getMethod("print"); m.invoke(c.newInstance()); } catch (Exception e) { e.printStackTrace(); } } } 输出结果:

    print方法被调用了

    为什么调用这个方法要绕来绕去的呢?直接创建实例然后调用它的方法不好吗??

    这就涉及到静态加载和动态加载了。

    静态加载:

    public class Main { public static void main(String args[]) { if (args.equals("MyClass")) { MyClass mc = new MyClass(); mc.print(); } if (args.equals("MyClass2")) { MyClass2 mc2 = new MyClass2(); mc2.print(); } } }

    在这个代码中,就是静态加载类。通过new关键字来静态加载。

    很显然,在上面这个代码中,这是通不过编译的,因为我们根本就没有MyClass2这个类,mc2也没有print方法。

    设想一下,假设Main这个程序是一个窗口程序,上面有若干个按钮,点击不同的按钮就会开启不同的程序。

    那么,在静态加载中,就需要每个类都是写好的,是存在的,并且当我们想要增加程序的时候,就需要对Main来重新编译,这显然是不好的。

    因为我整个程序不能因为其中一个程序不能启动或者不存在,其他程序也跟着不能打开,而增加程序的时候也不应该每次都将整个程序都编译一边,如果程序每次更新,都需要将整个程序都重新安装一次,那么用户体验一定是很差的。

    动态加载:

    public class Main { public static void main(String args[]) { try { Class c = Class.forName("args"); MyClass mc = (MyClass)c.newInstance(); mc.print(); } catch (Exception e) { e.printStackTrace(); } } } 而动态加载则不同,避开了编译,到了运行时类不存在才会报错,这显然就是符合我们的理念的。

    但是这又存在一个问题,在上面这个例子中,还是强制类型转换,转换成了MyClass类,那不是和静态加载没区别,如果我希望启动MyClass2呢?还不是得重新写个代码强制类型转换成MyClass2,再调用吗?

    这个时候就到多态大展身手的时候了。

    public interface IPrinciple { public void print(); }而MyClass和MyClass2只需要简单的实现IPrinciple接口,就可以在Main函数中这样使用了:

    public class Main { public static void main(String args[]) { try { Class c = Class.forName("args"); IPrinciple mc = (IPrinciple)c.newInstance(); mc.print(); } catch (Exception e) { e.printStackTrace(); } } }这样一来,只要实现了同样的接口,Main想调用哪个就调用哪个,并且不会因为某些类不存在而导致存在的类不能被调用的情况。更重要的是,在我们增加功能的时候,Main是不需要进行重新编译的,只需要编译新增加的功能,整个程序就更新了。

    谈了这么多,那到底怎么使用反射呢?

    首先第一步:获取类类型

    获取类类型的方法有三种:

    1.Class c = MyClass.class; // 通过类.class获取类类型

    2.Class c = mc.getClass(); // 通过类的实例的getClass方法获取类类型

    3.Class c = Class.forName("包的全名"); // 通过Class的forName方法获取类类型,需要所在包的全名 那么问题又来了,什么是类类型(class type)呢?

    Class类是所有类(注意是对象)的共有信息的抽象,比如该类实现的接口、对应的加载器、类名等等。而类类型就是当前这个类,比如MyClass的Class类的对象,就是MyClass.class是Class类的对象。这个对象在类被加载后由JVM自动构造。也是由JVM管理的,Class类是没有公共的构造方法的,是私有的native的。

    这样说还是不是很能让人明白,简单来说,类类型就是一个对象,这个对象里存放着类的所有信息,例如类的方法,类的成员变量。就好像是一个设计图纸,通过图纸你可以看到这个类的所有东西和结构,也可以通过这个图纸得到实例对象。

    所以说,要使用反射,就必须先获取类类型。

    注意,以下获得的都是类类型,例如获得Method.class

    获取类的方法信息:

    Method getMethod(String name, Class[] params) // 通过方法名name和形参列表params,是变长参数,获得类的public方法,不存在的话返回null,包括继承来的方法

    Method[] getMethods() // 获得所有的public方法,包括继承来的方法

    Method getDeclaredMethod(String name, Class[] params)// 获得类中定义的所有方法,不受访问权限的限制,但不包括继承来的方法。

    Method[] getDeclaredMethods() // 获得类中定义的所有方法

    获取类的成员变量信息:

    Field getField(String name) // 根据变量名得到相应的public变量

    Field[] getFields() // 获得类中所有的public变量

    Field getDeclaredField(String name) // 根据变量名得到所有在类中定义的变量

    Field[] getDeclaredFields() // 获得类中所有定义的变量

    获取类的构造函数信息:

    Constructor getConstructor(Class[] params)  // 根据形参列表获得对应的public构造函数

    Constructor[] getConstructors() // 获得所有public构造函数

    Constructor getDeclaredConstructor(Class[] params)// 根据形参列表获得对应的所有构造函数

    Constructor[] getDeclaredConstructors() // 获得所有构造函数

    这里就简单的说列举一些API,还有很多API这里就不一一列出了。可以参考javadoc文档。

    不过不管怎么样,都需要首先获取类类型。

    最后,谈一下方法反射的基本操作:

    1.获取某个方法的类类型

    Method m = c.getDeclaredMethod("方法名",可变参数列表(参数类型.class))

    2.方法的反射操作,调用invoke方法。

    m.invoke(对象,参数列表)

    如果没有形参,则不用填写,或者空Object数组。如果没有返回值,则返回null,否则返回Object对象,需要进行强制类型转换。

    转载请注明原文地址: https://ju.6miu.com/read-8569.html

    最新回复(0)