day42-反射01

来源:https://www.cnblogs.com/liyuelian/archive/2022/09/26/16732313.html
-Advertisement-
Play Games

Java反射01 1.反射(reflection)機制 1.1反射機制問題 一個需求引出反射 請看下麵問題: 根據配置文件 re.properties 指定信息,創建Cat對象並調用方法hi classfullpath=li.reflection.Cat method=hi 使用現有的技術,你能做的 ...


Java反射01

1.反射(reflection)機制

1.1反射機制問題

一個需求引出反射

請看下麵問題:

  1. 根據配置文件 re.properties 指定信息,創建Cat對象並調用方法hi
classfullpath=li.reflection.Cat
method=hi

使用現有的技術,你能做的到嗎?

  1. 這樣的需求在學習框架時特別多,即通過外部文件配置,在不修改源碼的情況下來控製程序,也符合設計模式的ocp原則(開閉原則)

    開閉原則:不修改源碼,擴展功能

例子:

re.properties:

classfullpath=li.reflection.Cat
method=cry

Cat:

package li.reflection;

public class Cat {
    private String name = "招財貓";

    public void hi() {
        System.out.println("hi " + name);
    }

    public void cry() {
        System.out.println(name + " cry");
    }
}

ReflectionQuestion:

package li.reflection;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class ReflectionQuestion {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        //根據配置文件 re.properties 指定信息,創建Cat對象 並調用方法hi

        //1.傳統方法 new 對象 -->調用方法
        //Cat cat = new Cat();
        //cat.hi();

        //2.嘗試使用讀取文件的方法
        //2.1使用properties類,可以讀寫配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpatch = properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();
        System.out.println("classfullpath" + classfullpatch);
        System.out.println("method" + methodName);
        //2.2創建對象
        //使用傳統的方法行不通

        //3.使用反射機制解決
        //3.1載入類,返回一個Class類型的對象cls(這裡的Class就是一個類,他的類名就叫Class)
        Class cls = Class.forName(classfullpatch);

        //3.2通過cls得到載入的類 li.reflection.Cat 的一個對象實例
        Object o = cls.newInstance();
        System.out.println("o的運行類型=" + o.getClass());//o的運行類型

        //3.3通過 cls 得你載入的類 li.reflection.Cat 的methodName"hi" 的方法對象
        //   即:在反射機制中,可以把方法視為一個對象(萬物皆對象)
        Method method1 = cls.getMethod(methodName);
        //3.4通過 method1 調用方法:即通過方法對象來實現調用方法
        System.out.println("========");
        method1.invoke(o);//傳統方法: 對象.方法() , 反射機制:方法.invoke(對象)

        //意義在與反射機制可以通過不求該源碼就完成功能的拓展
        /**
         * 例如,在Cat類中有兩個方法,hi()和cry(),現在要求將調用用的hi方法改為調用cry方法,
         * 這時候只需要在配置文件re.properties中修改引用的方法名即可
         * 不需像想傳統方法一樣,需要在源碼中修改
         */
    }
}
image-20220926160603213

1.2反射機制

1.2.1Java Reflection

  1. 反射機制允許程式在執行期間藉助於Reflection API取得任何類的內部信息(比如成員變數,構造器,成員方法等等,包括private許可權的屬性和方法),並能夠操作對象的屬性以及方法。

    反射在設計模式和框架底層都會用到

  2. 載入完類之後,在堆中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象包含了類的完整結構信息。通過這個對象得到類的結構。這個Class對象就像一面鏡子,透過這個鏡子看到了的結構,所以形象地稱之為:反射

例如:一個Person類型的實例對象叫 p

則 p 對象 - -> 類型 Person類

Class 對象 cls --> 類型 Class 類(這個類的名字就叫Class)

1.2.2Java反射機制原理圖

  • Java反射機制可以完成
    1. 在運行時判斷任意一個對象所屬的類
    2. 在運行時構造任意一個類的對象
    3. 在運行時得到任意一個類所具有的成員變數和方法
    4. 在運行時調用任意一個對象的成員變數和方法
    5. 在運行時獲取泛型信息
    6. 在運行時處理註解
    7. 生成動態代理

1.2.3反射相關類

反射相關的主要類:

  1. java.lang.Class:代表一個類,Class的對象表示某個類載入過後在堆中的對象
  2. java.lang.reflect.Method:表示類的方法(Method對象表示某個類的方法)
  3. java.lang.reflect.Field:表示類的成員變數(Field對象表示某個類的成員變數)
  4. java.lang.reflect.Constructor:表示類的構造方法(Constructor對象表示某個類的構造器)

​ 這些類在 java.lang.reflect

例子:

Cat:

package li.reflection;

public class Cat {
    private String name = "招財貓";
    public int age = 10;

    public Cat() {
    }//無參構造器

    public Cat(String name) {
        this.name = name;
    }

    public void hi() {
        System.out.println("hi " + name);
    }

    public void cry() {
        System.out.println(name + " cry");
    }
}

re.properties:

classfullpath=li.reflection.Cat
method=hi

Reflection01:

package li.reflection;

import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;

public class Reflection01 {
    public static void main(String[] args) throws Exception {

        //使用properties類,讀寫配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpatch = properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();
        System.out.println("classfullpath" + classfullpatch);
        System.out.println("method" + methodName);

        //反射機制
        //1.載入類,返回一個Class類型的對象cls(這裡的Class就是一個類,他的類名就叫Class)
        Class cls = Class.forName(classfullpatch);

        //通過cls得到載入的類 li.reflection.Cat 的一個對象實例
        Object o = cls.newInstance();
        System.out.println("o的運行類型=" + o.getClass());//o的運行類型

        //2.通過 cls得到載入的類 li.reflection.Cat 的methodName"hi" 的方法對象
        //   即:在反射機制中,可以把方法視為一個對象(萬物皆對象)
        Method method1 = cls.getMethod(methodName);
        //通過 method1 調用方法:即通過方法對象來實現調用方法
        System.out.println("========");
        method1.invoke(o);//傳統方法: 對象.方法() , 反射機制:方法.invoke(對象)

        //3.java.lang.reflect.Field:表示類的成員變數(Field對象表示某個類的成員變數)
        //得到name欄位
        //getField不能得到私有的屬性
        Field nameField = cls.getField("age");
        System.out.println(nameField.get(o));//傳統方法:對象.成員變數 , 反射:成員變數的對象.get(對象)
        
        //4.java.lang.reflect.Constructor:表示類的構造方法(Constructor對象表示某個類的構造器)
        Constructor constructor1 = cls.getConstructor();//()中可以指定構造器的類型,這裡返回無參構造器
        System.out.println(constructor1);//Cat()

        Constructor constructor2 = cls.getConstructor(String.class);//這裡傳入的String.class 就是String類的Class對象
        System.out.println(constructor2);//Cat(String name)
    }
}
image-20220926182012748

1.2.4反射優點和缺點

  1. 優點:可以動態地創建和使用對象(也是底層框架的核心),使用靈活,沒有反射機制,框架技術就失去底層支撐。
  2. 缺點:使用反射機制基本是解釋執行,對執行速度有影響
  • 調用反射優化-關閉訪問檢查
  1. Method和Field、Constructor對象都有setAccessible()方法
  2. setAccessible作用是啟動和禁用服務安全檢查的開關
  3. 參數值為true表示 反射的對象在使用時 取消訪問檢查,提高反射效率。參數值為false則表示反射的對象執行訪問檢查
image-20220926185959702

例子:測試反射調用的性能 和優化方案

package li.reflection;

import java.lang.reflect.Method;

//測試反射調用的性能 和 優化方案
public class Reflection02 {
    public static void main(String[] args) throws Exception {
        m1();
        m2();
        m3();
    }

    //傳統方法,調用hi
    public static void m1() {
        Cat cat = new Cat();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            cat.hi();
        }
        long end = System.currentTimeMillis();
        System.out.println("m1() 耗時=" + (end - start));
    }

    //反射機制調用hi
    public static void m2() throws Exception {
        //拿到Cat類的Class對象
        Class cls = Class.forName("li.reflection.Cat");//這裡為了方便,就不讀文件了
        //構造Cat類的對象
        Object o = cls.newInstance();
        //得到Cat類的成員方法
        Method hi = cls.getMethod("hi");

        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            hi.invoke(o);//反射機制調用方法
        }
        long end = System.currentTimeMillis();
        System.out.println("m2() 耗時=" + (end - start));
    }

    //反射調用優化
    public static void m3() throws Exception {
        //拿到Cat類的Class對象
        Class cls = Class.forName("li.reflection.Cat");//這裡為了方便,就不讀文件了
        //構造Cat類的對象
        Object o = cls.newInstance();
        //得到Cat類的成員方法
        Method hi = cls.getMethod("hi");
        hi.setAccessible(true);//在反射調用方法時,取消訪問檢查

        long start = System.currentTimeMillis();
        for (int i = 0; i < 900000000; i++) {
            hi.invoke(o);//反射機制調用方法
        }
        long end = System.currentTimeMillis();
        System.out.println("m3()反射調用優化耗時=" + (end - start));
    }
}
image-20220926185830340

2.Class類

2.1基本介紹

image-20220926201405016

  1. Class類也是類,因此也繼承Object類

    image-20220926192116952
  2. Class類對象不是new出來的,而是系統創建的

  3. 對於某個類的Class類對象,在記憶體中只有一份,因為類只載入一次

  4. 每個類的實例都會記得自己是由哪個Class實例所生成

  5. 通過Class對象可以得到一個類的完整結構(通過一系列API)

  6. Class對象是存放在堆的

  7. 類的位元組碼二進位數據,是放在方法區的,有的地方稱為類的元數據(包括 方法代碼,變數名,方法名,訪問許可權等)

    當我們載入完類之後,除了會在堆里生成一個Class類對象,還會在方法區生成一個類的位元組碼二進位數據(元數據)

例子:

package li.reflection.class_;

import li.reflection.Cat;

//對Class類的特點的梳理
public class Class01 {
    public static void main(String[] args) throws ClassNotFoundException {

        //1.Class類對象不是new出來的,而是系統創建的
        //1.1.傳統的 new對象
        /**通過ClassLoader類中的loadClass方法:
         *  public Class<?> loadClass(String name) throws ClassNotFoundException {
         *         return loadClass(name, false);
         *  }
         */
         //Cat cat = new Cat();

        //1.2反射的方式
        /**在這裡debug,需要先將上面的Cat cat = new Cat();註釋掉,因為同一個類只載入一次,否則看不到loadClass方法
         * (這裡也驗證了:3.對於某個類的Class類對象,在記憶體中只有一份,因為類只載入一次)
         * 仍然是通過 ClassLoader類的loadClass方法載入 Cat類的 Class對象
         *  public Class<?> loadClass(String name) throws ClassNotFoundException {
         *         return loadClass(name, false);
         *     }
         */
        Class cls1 = Class.forName("li.reflection.Cat");

        //2.對於某個類的Class類對象,在記憶體中只有一份,因為類只載入一次
        Class cls2 = Class.forName("li.reflection.Cat");
        //這裡輸出的hashCode是相同的,說明cls1和cls2是同一個Class類對象
        System.out.println(cls1.hashCode());//1554874502
        System.out.println(cls2.hashCode());//1554874502     
    }
}

Class類對象不是new出來的,而是系統創建的:

  1. Cat cat = new Cat();處打上斷點,點擊force step into,可以看到

image-20220926192820227

  1. 註釋Cat cat = new Cat();,在Class cls1 = Class.forName("li.reflection.Cat");處打上斷點,可以看到 仍然是通過 ClassLoader類載入 Cat類的 Class對

image-20220926200747357

2.2Class類常用方法

public static Class<?> forName(String className)//傳入完整的“包.類”名稱實例化Class對象
public Constructor[] getContructors() //得到一個類的全部的構造方法
public Field[] getDeclaredFields()//得到本類中單獨定義的全部屬性
public Field[] getFields()//得到本類繼承而來的全部屬性
public Method[] getMethods()//得到一個類的全部方法
public Method getMethod(String name,Class..parameterType)//返回一個Method對象,並設置一個方法中的所有參數類型
public Class[] getInterfaces() //得到一個類中鎖實現的全部介面
public String getName() //得到一個類完整的“包.類”名稱
public Package getPackage() //得到一個類的包
    
public Class getSuperclass() //得到一個類的父類
public Object newInstance() //根據Class定義的類實例化對象
public Class<?> getComponentType() //返回表示數組類型的Class
public boolean isArray() //判斷此class是否是一個數組

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

-Advertisement-
Play Games
更多相關文章
  • 代理設計模式(Proxy Design Pattern)指的是,在不改變原始類(或叫被代理類)代碼的情況下,通過引入代理類來給原始類附加功能,即擴展目標對象的功能。 ...
  • 【1】為什麼要使用線程池? 示例演示: //設置業務模擬 class MyRunnable implements Runnable { private int count; public MyRunnable(int count) { this.count = count; } public int ...
  • 探索密碼學的奇妙之旅。介紹CTR、混合密碼系統、RSA-OAEP相關理論。並基於AES、RSA標準,使用golang crypto包實現了簡單混合加密系統。 ...
  • 1.網站分析因版權原因,網站的地址大家可以私信我或者加我文章結尾的qq,完整的教程群里有,需要的自提,當然遇到問題也可以請教哦。 2.獲取內容我們今天呢,就先做一個通過星座來得知三天的運勢的小玩意, 這裡有十二個星座,我點了第一個和第二個進去,也就是白羊座和金牛座: 就會發現一個規律 通過觀察網址的 ...
  • 本章主要講的是基於Python語言的數據採集,該功能要講起來可以單獨作為一門課程來學習,因為這是一門很重要的課程,一般運用在大數據處理和人工智慧上,該應用提供大量的數據。 1.urllib模塊的學習 urllib模塊是python提供給我們操作互聯網的模塊。接下來我們可以簡單的操作一下,爬取一個網頁 ...
  • 一、Django 請求周期生命流程圖 首先,用戶在瀏覽器中輸入URL,發送一個GET 或 POST 方法的request 請求。 Django 中封裝了socket 的WSGI 伺服器,監聽埠接受這個request 請求。 再進行初步封裝,然後傳送到中間件中,這個request 請求再依次經過中間 ...
  • Python騷操作來了~ 用Python來實現科目一/四自動答題,100分不要太簡單! 最初是表弟最近想買車,但是駕照都沒有,買什麼車,只能先考駕照~ 看他在網頁上練習題目慢吞吞的,我就看不下去了,直接給他來一手揠苗助長~ 當時就用Python整了幾十行代碼,給他實現一下自動答題,我說你什麼時候答題 ...
  • 前幾天剛開始對PAT甲級的刷題,首次看到英語的題目,讓原本就菜的我更是頭禿,但第一題叫了n遍以後滿分通過的時候還是蠻爽的,在此僅記錄一下該題的個人解題心路,菜鳥記錄,技術極低。 Calculate a+b and output the sum in standard format -- that i ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...