參考博客: https://blog.csdn.net/mlc1218559742/article/details/52754310 先給出反射機制中常用的幾個方法: 現在開始對java反射機製做簡單的介紹 JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一 ...
參考博客: https://blog.csdn.net/mlc1218559742/article/details/52754310
先給出反射機制中常用的幾個方法:
Class.forName ("類名")
Class.forName ("類名").newInstance
Class.getMethod ("調用的方法名",class[]{參數類列表})
instance.getClass
Method.invoke ("對象名","參數封裝成的對象")
這裡簡單的給出java反射機制的測試小例子, 大家看不懂的話, 可以跳過這裡, 看下麵對java反射機制的介紹, 看完之後, 再回來看這個小例子, 應該很簡單了哦.
Class InvokeTest{ private static String algorithm = "Algorithm"; Object invokeMethod(object instance,String fun, Class[] paras, Object[] paraValue){ Object result = null; Method method; try{ method = instance.getClass.getMethod(fun, paras); result = method.invoke(instance, paraValue); }catch(Exception e) { e.printStackTrace(); result = null; } } public static void main(){ int x = 10, y = 20; invokeMethod(Class.forName(algorithm), newInstance, "add", Class[]{Class.int, Class.int}, Object[]{new Integer(x), new Integer(y)}); } } Class Algorithm{ public int add(int a,int b){ return a + b; } }
現在開始對java反射機製做簡單的介紹
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制.
Java反射機制主要提供了以下功能: 在運行時判斷任意一個對象所屬的類;在運行時構造任意一個類的對象;在運行時判斷任意一個類所具有的成員變數和方法;在運行時調用任意一個對象的方法;生成動態代理.
反射,從這個“反”字可以看出與我們平時正常的使用邏輯肯定不一樣,那麼到底什麼地方不一樣了?想要瞭解“反”,就得先瞭解一下“正”的概念。
在正常情況下,如果要使用一個類,必須要經過以下幾個步驟:
(1)使用important導入類所在的包(類:java.lang.Class)
(2)通過關鍵字new進行類對象實例化(構造方法:java.lang.reflect.Constructor)
(3)產生對象可以使用“對象.屬性”進行類中屬性的調用(屬性:java.lang.reflect.Field)
(4)通過“對象.方法()”調用類中的方法(方法:java.lang.reflect.Method)
括弧中的紅色字體是每個步驟對應反射中使用到的類,如果現在不瞭解,可以先不用管,後面會一一介紹,這裡是為了方便進行比較。
在反射中,使用一個類並不需要導入類的所在包,只要知道類的完整路徑就可以知道該類中的所有信息。
反射不需要有明確的類型對象,所有的對象都使用Object表示。可以直接用Object的與反射機制的混合調用類中的方法。
在認識反射機制之前,必須要介紹一下Class類,Class類是整個反射操作的源頭,該類的定義如下:
public final class Class<T> extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement
Class類的實例表示正在運行的Java應用程式中的類和介面。
如果要想使用Class類進行操作,就必須首先產生Class類這個對象,一共有三種方法:
(1)Object類中提供了一個返回Class類的方法,定義如下:
public final Class<?> getClass()
(2)利用“類.class”取得。
(3)利用Class類的Static方法取得。
public static Class<?> forName(String className) throws ClassNotFoundException
在程式開發過程中,使用第二種方法比較多。但是在程式框架設計中,都是使用第三種方法,也就是反射機制用到的方法。
class類實例化對象:
Class類如果使用forName()方法之後,就可以調用Class類中newInstance()無參構造函數方法進行操作,該方法定義如下:
public T newInstance() throws InstantiationException, IllegalAccessException
該方法表示創建此Class對象所表示的類的一個新實例, 使用方法如下:
class Student {
public Student() {
System.out.println("Student類的構造方法");
}@Override
public String toString() {
return "Student類的toString方法";
}
}public class ReflectDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.iflytek.Student");
// 相當於關鍵字實例化對象,Object obj = new Student();
Object obj = cls.newInstance();
System.out.println(obj);
}}
輸出的結果是:
Student類的構造方法
Student類的toString方法
通過上面的實例可以看出,調用newInstace()方法時,程式會預設調用Student類的無參構造方法,並且獲取到了Student類的實例化對象,可以調用Student類裡面的方法屬性。
在Class類中有兩個方法可以獲取類中的構造方法,分別是
獲取類中所有的構造方法:
public Constructor<?>[] getConstructors() throws SecurityException
獲取類中指定的構造方法:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
下麵是測試例子:
public class ReflectStringDemo {
public static void main(String[] args) throws Exception{
Class<?> cls = Class.forName("java.lang.String");
//獲取所有構造函數
Constructor<?>[] cons = cls.getConstructors();
//迴圈列印
for (int i = 0; i < cons.length; i++) {
System.out.println(cons[i]);
}
}
}列印結果:
public java.lang.String(byte[])
public java.lang.String(byte[],int,int)
public java.lang.String(byte[],java.nio.charset.Charset)
public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int,int,java.nio.charset.Charset)
public java.lang.String(java.lang.StringBuilder)
public java.lang.String(java.lang.StringBuffer)
public java.lang.String(int[],int,int)
public java.lang.String(char[],int,int)
public java.lang.String(char[])
public java.lang.String(java.lang.String)
public java.lang.String()
public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int)
public java.lang.String(byte[],int,int,int)上面實例獲取了String類中的所有構造方法,包括構造方法中的參數、異常等
獲取所有構造方法看上去並不難,如果想要進行指定構造方法的調用,則必須關註Constructor類,使用newInstance()方法進行實例
public T newInstance(Object... initargs) throws
InstantiationException,IllegalAccessException,IllegalArgumentException,InvocationTargetException
下麵是測試例子:
import java.lang.reflect.Constructor;
class Student2 {
private String name;private Integer age;
public Student2(String name, Integer age) {
this.name = name;
this.age = age;
}@Override
public String toString() {
return "name:" + this.name + ";age:" + this.age;
}
}public class ReflectConstructorDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.iflytek.Student2");
Constructor<?> con = cls.getConstructor(String.class, Integer.class);
// 這裡就相當於Object obj = new Student2("xyz",20);
Object obj = con.newInstance("xyz", 20);
System.out.println(obj);
}}
結果:
name:xyz;age:20
通過上面可以看出,如果要實例化一個對象,使用無參構造方法比有參構造方法簡單的多,使用無參直接調用newInstance()方法,使用有參則先獲取有參構造方法,
再通過Constructor中的newInstance()方法,並用指定的初始化參數初始化改實例。很多框架中的底層代碼預設都是使用無參構造方法來實例化對象,
所以在簡單Java類開發中都要明確給出無參構造方法.
獲取類中的方法可以分為兩大類,每個大類中又可以分為兩小類,風別是:
獲取包括父類集成而來的方法:
獲取全部方法:
public Method[] getMethods() throws SecurityException
獲取指定方法:
public Method getMethod(String name,Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
獲取本類中定義的方法:
獲取全部方法:
public Method[] getDeclaredMethods() throws SecurityException
獲取指定方法:
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
下麵是測試例子:
import java.lang.reflect.Method;
class Student3{
public void fun(){};
public void talk(){};
}
public class ReflectMethodStuDemo {
public static void main(String[] args) throws ClassNotFoundException{
Class<?> cls = Class.forName("com.iflytek.Student3");
//獲取本類中定義的方法
Method[] method = cls.getDeclaredMethods();
//迴圈列印
for (int i = 0; i < method.length; i++) {
System.out.println(method[i]);
}
}
}列印結果:
public void com.iflytek.Student3.fun()
public void com.iflytek.Student3.talk()
如果把上述代碼中的getDeclaredMethods()換成getMethods(),列印出來的結果如下
public void com.iflytek.Student3.fun()
public void com.iflytek.Student3.talk()
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()
使用getMethods()方法時,不僅獲取到了Student3類中的方法(紅色字體),Object類中的所有方法也被獲取到.
上面的程式是直接調用了Method類中的toString()方法輸出的,輸出格式並不是很理想,沒有異常等相關信息。如果有需要,我們也可以自己整理拼接方法輸出。
需要用到Method類中的如下幾種方法:
getModifiers():以整數形式返回此 Method 對象所表示方法的 Java 語言修飾符。
getReturnType():返回一個
Class
對象,該對象描述了此Method
對象所表示的方法的正式返回類型。getName():以
String
形式返回此Method
對象表示的方法名稱。getParameterTypes():按照聲明順序返回
Class
對象的數組,這些對象描述了此Method
對象所表示的方法的形參類型。getExceptionTypes():返回
Class
對象的數組,這些對象描述了聲明將此Method
對象表示的底層方法拋出的異常類型。
在這需要註意的是,利用getModifiers()獲取修飾符並不是簡單的輸出public、static等,而是以整數形式返回所表示的方法的Java語言修飾符。
可藉助Modifier類的toString()方法來完成。
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
interface Message {
public void get();
}
class Student1 implements Message {
public void fun() {
}
public void print() {
}
public void get() {
}
}public class ReflectMethodDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.iflytek.Student1");
Method[] me = cls.getMethods();
for (int i = 0; i < me.length; i++) {
// 此時用了method的toString方法輸出,如果有需要,用戶也可以自己拼湊輸出
// System.out.println(me[i]);
// 取得修飾符
System.out.print(Modifier.toString(me[i].getModifiers()) + " ");
// 取得返回值類型
System.out.print(me[i].getReturnType().getSimpleName() + " ");
// 取得方法名稱
System.out.print(me[i].getName() + "(");
// 取得方法參數
Class<?> params[] = me[i].getParameterTypes();
if (params.length > 0) {
for (int j = 0; j < params.length; j++) {
System.out.print(params[j].getSimpleName() + " arg-" + j);
if (j < params.length - 1) {
System.out.print(", ");
}
}
}
System.out.print(") ");
// 取得異常
Class<?>[] exp = me[i].getExceptionTypes();
if (exp.length > 0) {
System.out.print("throws ");
for (int j = 0; j < exp.length; j++) {
System.out.print(exp[j].getSimpleName());
if (j < exp.length - 1) {
System.out.println(", ");
}
}
}
System.out.println("{}");
System.out.println();
}
}
}
列印結果:
public void get() {}
public void print() {}
public void fun() {}
public final void wait(long arg-0, int arg-1) throws InterruptedException{}
public final native void wait(long arg-0) throws InterruptedException{}
public final void wait() throws InterruptedException{}
public boolean equals(Object arg-0) {}
public String toString() {}
public native int hashCode() {}
public final native Class getClass() {}
public final native void notify() {}
public final native void notifyAll() {}
調用(invoke):對帶有指定參數的指定對象調用由此 Method
對象表示的底層方法。
public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
該方法的英文名字是invoke,中文名稱就叫“調用”,該方法在Method類中,Method類本身就代表一個方法,當Method類中的對象調用invoke方法時,
就相當於調用了Method對象所代表的方法,方法裡面傳入對應的參數,實現動態調用方法。這裡可能比較難理解,看一下一個簡單的實例:
import java.lang.reflect.Method;
class Student4 {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ReflectInvokeDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.iflytek.Student4");
// 實例化對象
Object obj = cls.newInstance();
//獲取setName()方法
Method setNameMethod = cls.getMethod("setName", String.class);
//獲取getName()方法
Method getNameMethod = cls.getMethod("getName");
//調用setName()方法,相當於 對象.setName("xyz");
setNameMethod.invoke(obj, "xyz");
//調用getName()方法並輸出
System.out.println(getNameMethod.invoke(obj));
}
}註意上面紅色字體的代碼
調用類中的屬性和調用類中的方法差不多,也是分為兩大類,每個大類裡面分為兩小類,如下:
獲取包括繼承而來的屬性:
獲取全部屬性:
public Field[] getFields() throws SecurityException
獲取指定屬性:
public Field getField(String name) throws NoSuchFieldException, SecurityException
獲取本類定義的屬性:
獲取全部屬性:
public Field[] getDeclaredFields() throws SecurityException
獲取指定的屬性:
public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException
利用反射獲取類中的屬性,是不提倡使用的,因為違背了面向對象的封裝特性。
在Field類中定義了進行屬性調用的方法:
設置屬性內容:
public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException
獲取屬性類容:
public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException
實例:
import java.lang.reflect.Field;
class Student5 {
private String name;
}
public class ReflectFiledDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("com.iflytek.Student5");
//實例化
Object obj = cls.newInstance();
//獲取屬性
Field nameField = cls.getDeclaredField("name");
//取消訪問檢查
<strong>nameField.setAccessible(true);</strong>
//給屬性賦值
nameField.set(obj, "馬小超");
//獲取屬性值並輸出
System.out.println(nameField.get(obj));
}
}
大家在閱讀上面代碼時會看到有一行代碼被加粗標紅了,為什麼了?原因是Student類中的name屬性是private屬性的,不對外開放,如果Field類直接訪問該屬性,
會報許可權錯誤。
在Construction,Method,Field三個類中有一個共同的父類AccessibleObject,定義了取消封裝的操作:setAccessible(Boolean flag)
public void setAccessible(boolean flag) throws SecurityException
該方法預設的是參數是false,表示反射的對象應該實施 Java 語言訪問檢查。值為 true 則指示反射的對象在使用時應該取消 Java 語言訪問檢查。
得到某個類的靜態屬性
public Object getStaticProperty(String className, String fieldName)
throws Exception {
Class ownerClass = Class.forName(className);
Field field = ownerClass.getField(fieldName);
Object property = field.get(ownerClass);
return property;
}Class ownerClass = Class.forName(className) :首先得到這個類的Class。
Field field = ownerClass.getField(fieldName):通過Class得到類聲明的屬性。
Object property = field.get(ownerClass) :這裡和上面例子有些不同,因為該屬性是靜態的,所以直接從類的Class里取。
執行某個類的靜態方法
public Object invokeStaticMethod(String className, String methodName,
Object[] args) throws Exception {
Class ownerClass = Class.forName(className);
Class[] argsClass = new Class[args.length];
for (int i = 0, j = args.length; i < j; i++) {
argsClass[i] = args[i].getClass();
}
Method method = ownerClass.getMethod(methodName,argsClass);
return method.invoke(null, args);
}invoke的一個參數是null,因為這是靜態方法,不需要藉助實例運行。