如果你被問到:什麼是反射?為什麼需要反射、以及反射的應用?你會如何回答呢? 本篇會帶大家初識反射,瞭解反射概念和基本應用。反射的原理以及深入源碼的探究將會在後面幾篇介紹。 ...
如果你被問到:什麼是反射?為什麼需要反射、以及反射的應用?你會如何回答呢?
本篇會帶大家初識反射,瞭解反射概念和基本應用。反射的原理以及深入源碼的探究將會在後面幾篇介紹。
一、什麼是反射?
要理解什麼是反射,我們先看看什麼是「正射」,一個常見的獲取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:所有已聲明的成員變數,但不能得到其父類的成員變數
四、小結
本篇文章初步介紹了反射機制。讓大家瞭解了反射是什麼,為什麼會有反射這個功能,以及一些基本使用方式。後續文章將會反射的機制和原理做進一步的講解。