乐趣区

关于java:Java面试指北反射1-初识反射

如果你被问到:什么是反射?为什么须要反射、以及反射的利用?你会如何答复呢?
本篇会带大家初识反射,理解反射概念和根本利用。反射的原理以及深刻源码的探索将会在前面几篇介绍。

一、什么是反射?

要了解什么是反射,咱们先看看什么是「正射」,一个常见的获取 Student 的正射如下:

Student student = new Student();

通常 咱们都是间接申明,或者通过 new Student() 间接获取一个 Student 类,而后再应用。而一个反射的例子如下:

// 这里的“com.demo.Student”是须要反射的类的全限定名(包名 + 类名)Class clz = Class.forName("com.demo.Student")    
Object stu = clz.newInstance();

先获取实例的 Class 类,而后再通过其 Class 类生成一个 Student 的 Instance。以上两种形式(new Student 和 clz.newInstance)是成果是等价的,都是获取到了一个 Student 的实例。

那么什么是反射呢?反射是 Java 中的一个重要的个性,应用反射能够在运行时动静生成对象、获取对象属性以及调用对象办法。
Oracle 官网对反射的解释是:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

反射的外围是 JVM 在运行时才动静加载类或调用办法 / 拜访属性,它不须要当时(写代码的时候或编译期)晓得运行对象是谁。

反射的问题:
这里先简略提一下:反射相当于一系列解释操作,告诉 JVM 要做的事件,性能比间接的 Java 代码要慢很多。

二、为什么须要反射?

举一个直观的例子(仅为了阐明其中一种用法):
如果我让你写一个依据运行时输出的名字进行打印输出,你会写出相似上面的代码:

public void sayHello(String name) {
    // 在运行前基本不晓得 name 是什么,只有在运行时 name 才会被确认并打印进去
    System.out.println("hello," + name);
}

那么同样的,在写代码时可能也不晓得要用什么类,运行时才晓得。比方加载数据库驱动的时候,你能够间接 new 进去具体的驱动类,但要是换了数据库呢,还要批改源码从新打包更新么?

new com.mysql.jdbc.Driver();

那你可能会说,我多写几个 if else 不就行了,相似上面这样:

if (xxx == "mysql") {new com.mysql.jdbc.Driver();
else if (xxx == "redis") {new com.redis.jdbc.Driver();
else if (...){}

这样的问题是,在编译期就要凑齐所有的 jdbc 连贯库,甭管用不必这些都会被加载到内存中,数据库类型多了会有极大的节约。
那么这种状况,就能够用反射来解决,在运行时才去动静的加载对应类。你也能够在配置文件中指明要应用哪种数据库类,连贯不同的数据库都能够应用这一份程序。

// 反射的形式动静加载类
Class.forName("com.mysql.jdbc.Driver");

三、反射的根本应用

上面介绍通过反射都能做什么:

一)取得 Class 对象

// 1 应用 Class 类的 forName 静态方法
 Class.forName(driver);

// 2 间接获取某一个对象的 class
Class<?> cl = int.class;

// 3 调用某个对象的 getClass() 办法
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();

二)判断是否为某个类的实例

public static void displayObjectClass(Object o) {if (o instanceof Vector)
           System.out.println("对象是 java.util.Vector 类的实例");
      else if (o instanceof ArrayList)
           System.out.println("对象是 java.util.ArrayList 类的实例");
       else
           System.out.println("对象是" + o.getClass() + "类的实例");
}

三)创立实例

Class<?> c = String.class;
Object str = c.newInstance();

四)获取办法

getDeclaredMethods() 办法返回类或接口申明的所有办法,包含公共、爱护、默认(包)拜访和公有办法,但不包含继承的办法。
getMethods() 办法返回某个类的所有专用(public)办法,包含其继承类的专用办法。
getMethod() 办法返回一个特定的办法,其中第一个参数为办法名称,前面的参数为办法的参数对应 Class 的对象。

public class ReflectDemo {public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> c = MyClass.class;

        Method[] methods = c.getMethods();
        Method[] declaredMethods = c.getDeclaredMethods();
        Method method = c.getMethod("add", int.class, int.class);

        System.out.println("getMethods 获取的办法:");
        for(Method m:methods)
            System.out.println(m);

        System.out.println("getDeclaredMethods 获取的办法:");
        for(Method m:declaredMethods)
            System.out.println(m);
    }
}

class MyClass {public int add(int a, int b) {return a + b;}
    public int sub(int a, int b) {return a - b;}
}

// 输入
/*
getMethods 获取的办法:public int com.shuofxz.basic.ReflectDemo$MyClass.add(int,int)
public int com.shuofxz.basic.ReflectDemo$MyClass.sub(int,int)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

getDeclaredMethods 获取的办法:public int com.shuofxz.basic.ReflectDemo$MyClass.add(int,int)
public int com.shuofxz.basic.ReflectDemo$MyClass.sub(int,int)
*/

五)调用办法

当咱们从类中获取了一个办法后,咱们就能够用 invoke() 来调用这个办法。

public class ReflectDemo {public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> mc = MyClass.class;
        Object obj = mc.newInstance();
        // 获取 methodClass 类的 add 办法
        Method method = mc.getMethod("add", int.class, int.class);
        // 调用 method 对应的办法 => add(1,4)
        Object result = method.invoke(obj, 1, 4);
        System.out.println(result);
    }
}

六)获取结构器、类的成员变量(字段)信息

  • 通过 Class 类的 getConstructor 办法失去 Constructor 类的一个实例
  • getFiled:拜访私有的成员变量
  • getDeclaredField:所有已申明的成员变量,但不能失去其父类的成员变量

四、小结

本篇文章初步介绍了反射机制。让大家理解了反射是什么,为什么会有反射这个性能,以及一些根本应用形式。后续文章将会反射的机制和原理做进一步的解说。

退出移动版