java反射機制的簡單介紹

来源:https://www.cnblogs.com/xumBlog/archive/2018/04/19/8882489.html
-Advertisement-
Play Games

參考博客: 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,因為這是靜態方法,不需要藉助實例運行


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Getting Started Getting Started. 1 1. Introduction. 1 2.Quick Start-Strandalone HBase. 1 2.1 JDK版本選擇... 1 2.2 Get Started With HBase. 1 2.3 偽分散式本地安裝.. ...
  • 正則表達式通常稱為regexes,是文本處理中模式匹配的一個標準,也是處理字元串的一個強有力的工具。使用正則表達式時,需要指定一個字元串作為模式串去檢索目標字元串。你可以使用正則表達式來查找字元串中匹配該正則表達式表示的模式的子串,也可以進行文本替換或者從目標文本中提取子串。 參考資料《iOS編程指 ...
  • 一個曲線 圖例: 多個曲線 圖例: ...
  • 一個簡單的界面: item.xml: 代碼: 開許可權: ...
  • 首先,我們都知道NSObject是大多數類的根類,但是,這個類的是怎麼實現的呢?我們可以去下載開源的Runtime源碼,探究下NSObject類的實現。 1. NSObject.h文件 我們可以直接使用Command點NSOject進去看到它的頭文件,可以看到,NSObject.h文件中有兩塊內容: ...
  • 前言: 這篇文章是我從事ISP研究數年來的一些經驗總結,沒有用到深奧的理論知識,有的只是根據實際狀況來處理的一些常規方法,以及曾經犯過的錯誤總結。我想把ISP function的原理用簡單淺顯的語言描述出來,希望對初學者有所幫助。這裡的ISP主要是指從CMOS sensor輸出的bayer patt ...
  • 先導入 然後在 info.plist 文件中添加 Privacy - Location Always and When In Use Usage Description 和 Privacy - Location When In Use Usage Description 兩個就可以了。 Locati ...
  • 一、Android Color設置 1、在xml文件中 想設置顏色直接設置background的屬性或者其他的color屬性。隨便設置一個顏色如#000,再點擊左邊的顏色方塊,彈出顏色選擇器選擇顏色 2、在java代碼中 ①Color.parseColor("#000"); 【提示】可以在佈局文件中 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...