如果你被問到:什么是反射?為什么需要反射、以及反射的應用?你會如何回答呢?
本篇會帶大家初識反射,了解反射概念和基本應用,反射的原理以及深入原始碼的探究將會在后面幾篇介紹,
一、什么是反射?
要理解什么是反射,我們先看看什么是「正射」,一個常見的獲取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:所有已宣告的成員變數,但不能得到其父類的成員變數
四、小結
本篇文章初步介紹了反射機制,讓大家了解了反射是什么,為什么會有反射這個功能,以及一些基本使用方式,后續文章將會反射的機制和原理做進一步的講解,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/540239.html
標籤:其他
下一篇:Python面向物件
