一篇文章帶你認識Java反射

Java 反射定義

指在 Java 程序運行狀態中,動態獲取類的內容以及動態調用對象的方法和獲取屬性的機制.就叫做 JAVA 的反射機制

Java 反射的優缺點

優點:
1.增加程序的靈活性,避免將固有的邏輯程序寫死到代碼裏
2.代碼簡潔,可讀性強,可提高代碼的複用率
缺點
1.在量大的情景下反射性能下降,就是慢
2.內部暴露和安全隱患
反射到底慢在哪些地方:
1.尋找類 Class 字節碼的過程
2.安全管理機制的權限驗證等等
3.調用 native 方法時,會使用JNI技術很消耗性能

反射技術的主要組成

學java的都知道萬物皆對象,我們定義的類其實從面向對象的角度來分析,它其實也是一個具體的對象,它是一個描述類的實例.描述這個類中有哪些屬性,行爲等等內容。
比如,學生是不是都有姓名,年齡,發言權。這些學生共有的屬性,行爲我們可以抽取成一個公有的Student類來描述。

class Student{
        String name;
        int age;
        void talk(){
            System.out.println("學生可以發言");
        }
    }

Student類就相當於一個模板,我們可以基於這個模板創建具體不同的 Student 實例,而每一個實例都具有姓名,年齡,發言的行爲。
那麼,對於抽取這個思想,我們可以思考這樣一個問題,在Java中是不是每一個類在定義的時候,是不是也可以抽取共性的東西,比如,每一個類都有包名,類名,屬性定義,行爲(方法),構造器等等。
所以說,我們能想到的東西,那James Gosling肯定比咱們想的周到,這個類定義的創建模板就是我們 java 語言中的 java.lang.Class 類。
深入Class 內部
在這裏插入圖片描述
通過上面的內容,我們已經瞭解到我們創建的每一個自定義的Class實例(也就是我們創建的每一個類)都是基於他的模板類 java.lang.Class
類。在大家每一個編寫的類實例中,都會定義這個類的包名,類名,訪問域,特徵符,構造器,字段,函 數,父類,接口等等內容。這些內容在我們的 Class 類中都提供了對應的獲取方法進行獲取(既然是屬性,肯定有對應的get方法是吧)。

注意:以下所有clazz 代表對應的 Class 類實例

簡單說一下Class類實例的四種獲取方式:
1.Class clazz = Student.class;
2.Class clazz = new Student().getClass();
3.Class clazz = Class.forName(“com.czy.demo.Student”);(會有ClassNotFoundException異常,需要捕獲)
4.Class clazz = Main(當前類名).class.getClassLoader().loadClass(“com.czy.demo.Student”);(ClassNotFoundException異常,需要捕獲)

反射-基本信息操作

int modifier = clazz.getModifiers(); //獲取類的修飾符

注意:該返回的是int類型而不是字符串‘public’,‘private’ 等修飾符。
官方api中給出,修飾符所對應的數字:在這裏插入圖片描述
若類的定義爲,public abstract Class ClassXXX{ } int modify = clazz.getModifers(); 返回值爲 public 值+ abstract 的值 = 1025

Package package= clazz.getPackage(); //獲取類的包名
String fullClassName = clazz.getName(); //獲取類的全路徑名稱
String simpleClassName = clazz.getSimpleName(); //獲取類的簡單名稱
ClassLoader classLoader = clazz.getClassLoader(); //獲取類的類加載器
Class[] interfacesClasses = clazz.getInterfaces(); //獲取類實現的接口列表
Class fc= clazz.getSuperclass(); //獲取類的父類
Annotation[] annotations= clazz.getAnnotations(); //獲取類的註解列表

反射-類的屬性操作

Field[] fields = clazz.getFields(); 獲取類中所有的公有字段 包含繼承
Field field=clazz.getField(“xxx”); 獲取類中擁有的具體屬性名的公有屬性 (含繼承,實現)
Field[] fields=clazz.getDeclaredFields(); 獲取當前類定義的所有屬性(公有,私有)
Field field=clazz.getDeclaredField(“xxx”); 獲取當前類定義的具體屬性名的屬性(公 有,私有)
Field 對象操作
int modifers =filed.getModifers() 獲取變量的修飾(參見類的修飾)
field.get(object) 獲取 object 對象的具體屬性值
field.set(object,objectValue) 給指定的對象的屬性賦值
field.set(null,objectValue) 給靜態變量賦值 objectValue(static 修飾)(指定對象可爲任意對象,約定規範爲null)
如果屬性是私有的private 修飾 ,需在set方法或者 setXXX 調用前,設置可訪問權限filed.setAccessable(true);也就是強制訪問私有的屬性

反射-類的方法操作

Method[] methods = clazz.getDeclaredMethods(); //獲取當前類中定義的全部方法(公有,私有)
Method pugMethod = clazz.getDeclaredMethod("methodName",String.calss) //獲取類中定義指定名稱和參數類型的方法
Method[] methods = clazz.getMethods(); //獲取類中擁有的公有方法(含繼承,實現)
Method method = clazz.getMethod(“xxx”); //獲取類中擁有的指定名公有方法(含繼承,實現)
method對象操作
int modifers = method.getModifers(); //獲取方法的修飾符
Class cls = method.getReturnType(); //獲取方法的返回值類型
String methodName = m.getName(); //獲取方法的名字
Class[] clazzes = m.getParameterTypes(); //獲取方法參數列表的類型
Class[] clazzes =m.getExceptionTypes(); //獲取方法拋出的異常類型
Object obj=method.invoke(obj,args); //在指定的對象上執行方法
Object obj=method.invoke(null,args); //執行類的靜態方法
若方法是私有方法,權限不允許操作,可以執行 method.setAccessable(true)來強制使用,設置方法使用權之後,在執行 invoke

反射-類的構造器操作

Constructor[] cons = clazz.getConstructors(); //獲取類中所有的公有構造器
Constructor[] cons = clazz.getDeclaredConstructors(); //獲取類中所有的構造器
Constructor conNoParam= clazz.getDeclaredConstructor(); //獲取類中無參的構造器
Constructor con= clazz.getDeclaredConstructor(String.class,String.class); //獲取類中有參構造
constructor 對象操作
int modifers = con.getModifiers(); //獲取構造器的修飾符
con.newInstance(); //無參構造器實例對象
con.newInstance('xxx','xxx'); //有參構造器實例對象
con.setAccessible(true); //指定構造器強制訪問

class.newInstacne(); //class直接調用newInstacne(),底層還是用con.newInstance()默認調用無參的構造函數

加粗樣式

單例模式也許並不單例

單例模式的特徵:
1.私有化構造函數
2.全局唯一的公有訪問點
這裏我們就已懶漢式單例模式代碼爲例:

public class Lazy {
    private static Lazy instance;
    private Lazy(){}
    public static Lazy getInstance(){
        if (instance == null){
            synchronized (Lazy.class){
                if (instance == null) {
                    instance = new Lazy();
                }
            }
        }
        return instance;
    }
}

反射如何破壞單例:

public class SingletonDestroyer {
    public static void main(String[] args) throws Exception {
        Lazy lazy = Lazy.getInstance();//這是Lazy對外提供的獲取方式
        Constructor constructor = Lazy.class.getDeclaredConstructor();
         //因爲Lazy的構造函數私有了,所以這裏要設置強制訪問
        constructor.setAccessible(true);
        Lazy lazyInstanceReflect = (Lazy) constructor.newInstance();//這是反射機制用構造器獲取的Lazy
        System.out.println(lazy  == lazyInstanceReflect);//--> false
    }
}

輸出結果爲false。可以試一下,所以說反射是能破壞單例的,單例模式也許並不單例。

要說把Java反射機制發揮的淋漓盡致的那肯定非SpringIoc莫屬。

>>下一篇:springIoc是用反射機制如何實現的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章