谈一谈Java中的反射机制

什么是反射?

JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

简单来说就是反射就是将 Java 类中的各种成分映射成一个个的 Java 对象。

在我们了解了反射的基本概念之后,自然而然的我们就提出了以下问题,反射有什么用?

假如有两个程序员,一个程序员在写程序的时需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码是不能通过编译的。此时,利用 Java 反射的机制,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。

Java 的反射机制它知道类的基本结构,这种对 Java 类结构探知的能力,我们称为Java 类的“自审”。例如 一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。在 Java 中万物皆是对象,反射也是面向对象思想的一种体现。

说了这么多,感觉 好像还是云里雾里。那接下来就开始 show code。

首先 我们来创建一个 Robot 类。

public class Robot {
    private String name;


    public void sayHi(String helloSentence) {
        System.out.println(helloSentence + " " + name);
    }


    private String throwHello(String tag) {
        return "Hello " + tag;
    }


}
复制代码

如代码所示,我们定义了一个私有的成员变量以及两个方法。

接下来我们就通过反射来获取它。

public class ReflectSample {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException {

        Class rc = Class.forName("com.interview.javabasic.reflect.Robot");
        Robot r = (Robot) rc.newInstance();
        System.out.println("Class name is " + rc.getName());
        Method getHello = rc.getDeclaredMethod("throwHello", String.class);
        getHello.setAccessible(true);
        Object str = getHello.invoke(r, "Bob");
        System.out.println("getHello result is " + str);
        Method sayHi = rc.getMethod("sayHi", String.class);
        sayHi.invoke(r, "Welcome");
        Field name = rc.getDeclaredField("name");
        name.setAccessible(true);
        name.set(r, "Alice");
        sayHi.invoke(r, "Welcome");


    }
}

复制代码

首先我们创建一个 Class对象 rc,并且通过 forName方法来给它赋值。这里我先给你提一个小问题,loadClass 和 forName 的区别,你可以先思考一下,答案会在文末给出。

接着我们的例子,我们通过 newInstance() 方法来创造一个实例,并且其强转为Robot对象。 紧接着我们输出 rc 的 name,rc 是通过我们传递的路径得到的,所以 name 自然是我们希望创建的 Robot 类。

我们通过 rc.getDeclaredMethod()方法得到了一个method对象,方法的参数对应着我们希望得到方法的名称。由于该方法是私有方法,所以我们 将 setAccessible() 设为true,表示反射在应用的时候,不会去进行权限检查。

由于 throwHello方法需要参数,我们就传入了Bob,最后输出的结果是 getHello result is Hello Bob

通过上述的代码,我们得到了 Robot对象以及它的方法,算是初步对反射有了一定的应用(后半部分的代码属于换汤不换药,就不再赘述)。

最后,我们来解答文中的问题 loadClass和forName有什么区别?

它们的区别就是 Class.forName得到的class是已经完成初始化完成的,Classloder.loadClass得到的class是还没有链接的。

简单来说就是 forName方法会执行类中静态代码块中的内容,而 loadClass不会执行,我们举一个简单的例子。

以 Class.forName("com.mysql.jdbc.Driver") 为例; Driver 类中有需要执行静态代码块(new Driver) 所以要用forname 不能loadclass。而springIoc 在读取bean的配置的时候 采取的是lazyloading(延时加载)的策略,使用 classloader 不需要执行静态代码块可以加快初始化速度,把相关加载留到真正执行代码的时候再操作。所以说这两种方法各有优劣,需要根据实际情况而定。

在本文中我们讲到了 Java 反射的相关内容,希望你看完本文后有所收获。

猜你喜欢

转载自juejin.im/post/5cfe53765188254fb97442b8