Java學習-第一部分-第三階段-第二節:反射

来源:https://www.cnblogs.com/wenjie2000/archive/2022/09/13/16687903.html
-Advertisement-
Play Games

反射 筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html) 一個需求引出反射 請看下麵的問題 根據配置文件 re.properties 指定信息,創建對象並調用方法(以下為文件內容) classfullpath=com.hspedu.Ca ...


反射

筆記目錄:(https://www.cnblogs.com/wenjie2000/p/16378441.html)

一個需求引出反射

請看下麵的問題

  1. 根據配置文件 re.properties 指定信息,創建對象並調用方法(以下為文件內容)

    classfullpath=com.hspedu.Cat
    method=hi

    思考:使用現有技術,你能做的嗎?

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

  3. 快速入門

    import com.hspedu.Cat;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.util.Properties;
    
    /**
     * 反射問題的引入
     */
    @SuppressWarnings({"all"})
    public class ReflectionQuestion {
        public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
    
            //根據配置文件 re.properties 指定信息, 創建Cat對象並調用方法hi
    
            //傳統的方式 new 對象 -》 調用方法
    //        Cat cat = new Cat();
    //        cat.hi(); ===> cat.cry() 修改源碼.
    
            //我們嘗試做一做 -> 明白反射
    
            //1. 使用Properties 類, 可以讀寫配置文件
            Properties properties = new Properties();
            properties.load(new FileInputStream("src\\re.properties"));
            String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat"
            String methodName = properties.get("method").toString();//"hi"
            System.out.println("classfullpath=" + classfullpath);
            System.out.println("method=" + methodName);
    
            //2. 創建對象 , 傳統的方法,行不通 =》 反射機制
            //new classfullpath();
    
            //3. 使用反射機制解決
            //(1) 載入類, 返回Class類型的對象cls
            Class cls = Class.forName(classfullpath);
            //(2) 通過 cls 得到你載入的類 com.hspedu.Cat 的對象實例
            Object o = cls.newInstance();
            System.out.println("o的運行類型=" + o.getClass()); //運行類型
            //(3) 通過 cls 得到你載入的類 com.hspedu.Cat 的 methodName"hi"  的方法對象
            //    即:在反射中,可以把方法視為對象(萬物皆對象)
            Method method1 = cls.getMethod(methodName);
            //(4) 通過method1 調用方法: 即通過方法對象來實現調用方法
            System.out.println("=============================");
            method1.invoke(o); //傳統方法 對象.方法() , 反射機制 方法.invoke(對象)
        }
    }
    
    //這部分程式中的Cat在下麵的代碼中還會用到
    package com.hspedu;
    
    public class Cat {
    
        private String name = "招財貓";
        public int age = 10; //public的
    
        public Cat() {} //無參構造器
    
        public Cat(String name) {
            this.name = name;
        }
    
        public void hi() { //常用方法
            //System.out.println("hi " + name);
        }
        public void cry() { //常用方法
            System.out.println(name + " 喵喵叫..");
        }
    
    }
    

反射機制

Java Reflection

  1. 反射機制允許程式在執行期藉助於Reflection API取得任何類的內部信息(比如成員變數,構造器,成員方法等等),並能操作對象的屬性及方法。反射在設計模式和框架底層都會用到
  2. 載入完類之後,在堆中就產生了一個Class類型的對象(一個類只有一個Class對象),這個對象包含了類的完整結構信息。通過這個對象得到類的結構。這個對象就像一面鏡子,透過這個鏡子看到類的結構,所以,形象的稱之為:反射

Java反射機制原理示意圖

image

反射相關的主要類:

  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.reflection

結合前面案例演示:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
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 {

        //1. 使用Properties 類, 可以讀寫配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();//"com.hspedu.Cat"
        String methodName = properties.get("method").toString();//"hi"
        

        //2. 使用反射機制解決
        //(1) 載入類, 返回Class類型的對象cls
        Class cls = Class.forName(classfullpath);
        //(2) 通過 cls 得到你載入的類 com.hspedu.Cat 的對象實例
        Object o = cls.newInstance();
        System.out.println("o的運行類型=" + o.getClass()); //運行類型
        //(3) 通過 cls 得到你載入的類 com.hspedu.Cat 的 methodName"hi"  的方法對象
        //    即:在反射中,可以把方法視為對象(萬物皆對象)
        Method method1 = cls.getMethod(methodName);
        //(4) 通過method1 調用方法: 即通過方法對象來實現調用方法
        System.out.println("=============================");
        method1.invoke(o); //傳統方法 對象.方法() , 反射機制 方法.invoke(對象)

        //java.lang.reflect.Field: 代表類的成員變數, Field對象表示某個類的成員變數
        //得到name欄位
        //getField不能得到私有的屬性
        Field nameField = cls.getField("age"); //
        System.out.println(nameField.get(o)); // 傳統寫法 對象.成員變數 , 反射 :  成員變數對象.get(對象)

        //java.lang.reflect.Constructor: 代表類的構造方法, Constructor對象表示構造器
        Constructor constructor = cls.getConstructor(); //()中可以指定構造器參數類型, 返回無參構造器
        System.out.println(constructor);//Cat()


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

反射優點和缺點

  1. 優點:可以動態的創建和使用對象(也是框架底層核心),使用靈活,沒有反射機制,框架技術就失去底層支撐。
  2. 缺點:使用反射基本是解釋執行,對執行速度有影響.(相比傳統方法執行效率非常低)
  3. 應用實例:

反射調用優化-關閉訪問檢查(能夠一定程度提升效率,但提升較小。但相比傳統方法依然很慢)

  1. Method和Field、Constructor對象都有setAccessible()方法

  2. setAccessible作用是啟動和禁用訪問安全檢查的開關

  3. 參數值為true表示反射的對象在使用時取消訪問檢查,提高反射的效率。參數值為false則表示反射的對象執行訪問檢查

***.setAccessible(true);//取消訪問檢查,提高反射的效率

***.setAccessible(false);//執行訪問檢查

package com.hspedu.reflection;

import com.hspedu.Cat;

import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
* 測試反射調用的性能,和優化方案
*/
public class Reflection02 {
  public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
      //Field
      //Method
      //Constructor
      m1();
      m2();
      m3();
  }

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

  //反射機制調用方法hi
  public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

      Class cls = Class.forName("com.hspedu.Cat");
      Object o = cls.newInstance();
      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 ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

      Class cls = Class.forName("com.hspedu.Cat");
      Object o = cls.newInstance();
      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));
  }
}

Class類

image

基本介紹:

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

  2. Class類對象不是new出來的,而是系統創建的[演示]

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

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

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

  6. Class對象是存放在堆的

  7. 類的位元組碼二進位數據,是放在方法區的,有的地方稱為類的元數據(包括方法代碼,變數名,方法名,訪問許可權等等)https://www.zhihu.com/question/38496907【示意圖】

    image

import com.hspedu.Cat;

import java.util.ArrayList;

/**
 * 對Class類特點的梳理
 */
public class Class01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //看看Class類圖
        //1. Class也是類,因此也繼承Object類
        //Class
        //2. Class類對象不是new出來的,而是系統創建的
        //(1) 傳統new對象
        /*  ClassLoader類
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                return loadClass(name, false);
            }
         */
        //Cat cat = new Cat();
        //(2) 反射方式, 剛纔老師沒有debug到 ClassLoader類的 loadClass, 原因是,我沒有註銷Cat cat = new Cat();
        /*
            ClassLoader類, 仍然是通過 ClassLoader類載入Cat類的 Class對象
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                return loadClass(name, false);
            }
         */
        Class cls1 = Class.forName("com.hspedu.Cat");

        //3. 對於某個類的Class類對象,在記憶體中只有一份,因為類只載入一次
        Class cls2 = Class.forName("com.hspedu.Cat");
        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode());
        Class cls3 = Class.forName("com.hspedu.Dog");
        System.out.println(cls3.hashCode());
    }
}

Class類的常用方法

方法名 功能說明
static Class forName(String name) 返回指定類名name的Class對象
Object newlnstance() 調用預設構造函數,返回該Class對象的一個實例
getName() 返回此Class對象所表示的實體(類、介面、數組類、基本類型等)名稱
Class[] getInterfaces() 獲取當前Class對象的介面
ClassLoader getClassLoader() 返回該類的類載入器
Class getSuperclass() 返回表示此Class所表示的實體的超類的Class
Constructor[] getConstructors() 返回一個包含某些Constructor對象的數組
Field[] getDeclaredFields() 返回Field對象的一個數組
Method getMethod(String name Class ... paramType) 返回一個Method對象,此對象的形參類型為paramType

應用實例:

import java.lang.reflect.Field;

/**
 * 演示Class類的常用方法
 */
public class Class02 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {

        String classAllPath = "com.hspedu.Car";
        //1 . 獲取到Car類 對應的 Class對象
        //<?> 表示不確定的Java類型
        Class<?> cls = Class.forName(classAllPath);
        //2. 輸出cls
        System.out.println(cls); //顯示cls對象, 是哪個類的Class對象 com.hspedu.Car
        System.out.println(cls.getClass());//輸出cls運行類型 java.lang.Class
        //3. 得到包名
        System.out.println(cls.getPackage().getName());//包名
        //4. 得到全類名
        System.out.println(cls.getName());
        //5. 通過cls創建對象實例
        Car car = (Car) cls.newInstance();
        System.out.println(car);//car.toString()
        //6. 通過反射獲取屬性 brand
        Field brand = cls.getField("brand");
        System.out.println(brand.get(car));//寶馬
        //7. 通過反射給屬性賦值
        brand.set(car, "賓士");
        System.out.println(brand.get(car));//賓士
        //8 我希望大家可以得到所有的屬性(欄位)
        System.out.println("=======所有的欄位屬性====");
        Field[] fields = cls.getFields();
        for (Field f : fields) {
            System.out.println(f.getName());//名稱
        }
    }
}

class Car {
    public String brand = "寶馬";//品牌
    public int price = 500000;
    public String color = "白色";

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                ", color='" + color + '\'' +
                '}';
    }
}

獲取Class類對象

  1. 前提:已知一個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()獲取,可能拋出ClassNotFoundException,實例:Class cls1 =Class.forName( "java.lang.Cat" );

    應用場景:多用於配置文件,讀取類全路徑,載入類.

  2. 前提:若已知具體的類,通過類的class 獲取,該方式最為安全可靠,程式性能最高實例:Class cls2 = Cat.class;

    應用場景:多用於參數傳遞,比如通過反射得到對應構造器對象.

  3. 前提:已知某個類的實例,調用該實例的getClass()方法獲取Class對象,實例:Class clazz=對象.getClass0;

    應用場景:通過創建好的對象,獲取Class對象.

  4. 其他方式

    ClassLoader cl =對象.getClass().getClassLoader();

    Class clazz4 = cl.loadClass(“類的全類名”);

  5. 基本數據(int, char,boolean float,double,byte,long,short)按如下方式得到Class類對象

    Class cls =基本數據類型.class

  6. 基本數據類型對應的包裝類,可以通過.TYPE得到Class類對象

    Class cls =包裝類.TYPE

/**
 * 演示得到Class對象的各種方式(6)
 */
public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {

        //1. Class.forName
        String classAllPath = "com.hspedu.Car"; //通過讀取配置文件獲取
        Class<?> cls1 = Class.forName(classAllPath);
        System.out.println(cls1);

        //2. 類名.class , 應用場景: 用於參數傳遞
        Class cls2 = Car.class;
        System.out.println(cls2);

        //3. 對象.getClass(), 應用場景,有對象實例
        Car car = new Car();
        Class cls3 = car.getClass();
        System.out.println(cls3);

        //4. 通過類載入器【4種】來獲取到類的Class對象
        //(1)先得到類載入器 car
        ClassLoader classLoader = car.getClass().getClassLoader();
        //(2)通過類載入器得到Class對象
        Class cls4 = classLoader.loadClass(classAllPath);
        System.out.println(cls4);

        //cls1 , cls2 , cls3 , cls4 其實是同一個對象
        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode());
        System.out.println(cls3.hashCode());
        System.out.println(cls4.hashCode());

        //5. 基本數據(int, char,boolean,float,double,byte,long,short) 按如下方式得到Class類對象
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        Class<Boolean> booleanClass = boolean.class;
        System.out.println(integerClass);//int

        //6. 基本數據類型對應的包裝類,可以通過 .TYPE 得到Class類對象
        Class<Integer> type1 = Integer.TYPE;
        Class<Character> type2 = Character.TYPE; //其它包裝類BOOLEAN, DOUBLE, LONG,BYTE等待
        System.out.println(type1);

        System.out.println(integerClass.hashCode());//?
        System.out.println(type1.hashCode());//?
    }
}
class Car {
    public String brand = "寶馬";//品牌
    public int price = 500000;
    public String color = "白色";

    @Override
    public String toString() {
        return "Car{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                ", color='" + color + '\'' +
                '}';
    }
}

哪些類型有Class類對象

如下類型有Class對象

  1. 外部類,成員內部類,靜態內部類,局部內部類,匿名內部類
  2. interface:介面
  3. 數組
  4. enum:枚舉
  5. annotation:註解
  6. 基本數據類型
  7. void
import java.io.Serializable;

/**
 * 演示哪些類型有Class對象
 */
public class AllTypeClass {
    public static void main(String[] args) {

        Class<String> cls1 = String.class;//外部類
        Class<Serializable> cls2 = Serializable.class;//介面
        Class<Integer[]> cls3 = Integer[].class;//數組
        Class<float[][]> cls4 = float[][].class;//二維數組
        Class<Deprecated> cls5 = Deprecated.class;//註解
        //枚舉
        Class<Thread.State> cls6 = Thread.State.class;
        Class<Long> cls7 = long.class;//基本數據類型
        Class<Void> cls8 = void.class;//void數據類型
        Class<Class> cls9 = Class.class;//

        System.out.println(cls1);
        System.out.println(cls2);
        System.out.println(cls3);
        System.out.println(cls4);
        System.out.println(cls5);
        System.out.println(cls6);
        System.out.println(cls7);
        System.out.println(cls8);
        System.out.println(cls9);
    }
}

類載入

基本說明

反射機制是java實現動態語言的關鍵,也就是通過反射實現類動態載入。

  1. 靜態載入:編譯時載入相關的類,如果沒有則報錯,依賴性太強

    例如:Dog dog=new Dog();

  2. 動態載入:運行時載入需要的類,如果運行時不用該類,即使不存在該類,也不報錯,降低了依賴性

    例如:cLass cls = cLass.forName( "Person");

類載入時機

  1. 當創建對象時(new) //靜態載入
  2. 當子類被載入時,父類也載入 //靜態載入
  3. 調用類中的靜態成員時 //靜態載入
  4. 通過反射 //動態載入

類載入過程圖

image

類載入各階段完成任務

image

載入階段

JVM在該階段的主要目的是將位元組碼從不同的數據源(可能是class文件、也可能是jar包,甚至網路)轉化為二進位位元組流載入到記憶體中,並生成一個代表該類的java.lang.Class對象

連接階段-驗證

  1. 目的是為了確保Class文件的位元組流中包含的信息符合當前虛擬機的要求,並且不會危害虛擬機自身的安全。
  2. 包括:文件格式驗證(是否以魔數oxcafebabe開頭)、元數據驗證、位元組碼驗證和符號引用驗證[舉例說明]
  3. 可以考慮使用-Xverify:none 參數來關閉大部分的類驗證措施,縮短虛擬機類載入的時間。

連接階段-準備

  1. JVM 會在該階段對靜態變數,分配記憶體並預設初始化(對應數據類型的預設初始值,如0、0L、null、false 等)。這些變數所使用的記憶體都將在方法區中進行分配

    /**
     * 我們說明一個類載入的鏈接階段-準備
     */
    public class ClassLoad02 {
        public static void main(String[] args) {
        }
    }
    class A {
        //屬性-成員變數-欄位
        //老韓分析類載入的鏈接階段-準備 屬性是如何處理
        //1. n1 是實例屬性, 不是靜態變數,因此在準備階段,是不會分配記憶體
        //2. n2 是靜態變數,分配記憶體 n2 是預設初始化 0 ,而不是20
        //3. n3 是static final 是常量, 他和靜態變數不一樣, 因為一旦賦值就不變 n3 = 30
        public int n1 = 10;
        public static  int n2 = 20;
        public static final  int n3 = 30;
    }
    

連接階段-解析

  1. 虛擬機將常量池內的符號引用替換為直接引用的過程。(可理解為將邏輯上的聯繫直接替換為對方的準確地址)

初始化階段

  1. 到初始化階段,才真正開始執行類中定義的Java程式代碼,此階段是執行<clinit>()方法的過程。
  2. <clinit>(方法是由編譯器按語句在源文件中出現的順序,依次自動收集類中的靜態變數的賦值動作和靜態代碼塊中的語句,併進行合併。
  3. 虛擬機會保證一個類的<clinit>()方法在多線程環境中被正確地加鎖、同步,如果多個線程同時去初始化一個類,那麼只會有一個線程去執行這個類的<clinit>()方法,其他線程都需要阻塞等待,直到活動線程執行<clinit>()方法完畢[debug源碼]
/**
 * 演示類載入-初始化階段
 */
public class ClassLoad03 {
    public static void main(String[] args) throws ClassNotFoundException {
        //老韓分析
        //1. 載入B類,並生成 B的class對象
        //2. 鏈接 num = 0
        //3. 初始化階段
        //    依次自動收集類中的所有靜態變數的賦值動作和靜態代碼塊中的語句,併合並
        /*
                clinit() {
                    System.out.println("B 靜態代碼塊被執行");
                    //num = 300;
                    num = 100;
                }
                合併: num = 100
         */

        //new B();//類載入
        //System.out.println(B.num);//100, 如果直接使用類的靜態屬性,也會導致類的載入

        //看看載入類的時候,是有同步機制控制
        /*
        protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
        {
            //正因為有這個機制,才能保證某個類在記憶體中, 只有一份Class對象
            synchronized (getClassLoadingLock(name)) {
            //....
            }
            }
         */
        B b = new B();
    }
}

class B {
    static {
        System.out.println("B 靜態代碼塊被執行");
        num = 300;
    }
    static int num = 100;
    public B() {//構造器
        System.out.println("B() 構造器被執行");
    }
}

通過反射獲取類的結構信息

第一組: java.lang.Class類

  1. getName:獲取全類名

  2. getSimpleName:獲取簡單類名

  3. getFields:獲取所有public修飾的屬性,包含本類以及父類的

  4. getDeclaredFields:獲取本類中所有屬性

  5. getMethods:獲取所有public修飾的方法,包含本類以及父類的

  6. getDeclaredMethods:獲取本類中所有方法

  7. getConstructors:獲取所有public修飾的構造器,包含本類

  8. getDeclaredConstructors:獲取本類中所有構造器

  9. getPackage:以Package形式返回包信息

  10. getSuperClass:以Class形式返回父類信息

  11. getlnterfaces:以Class形式返回介面信息

  12. getAnnotations:以Annotation[]形式返回註解信息

第二組: java.lang.reflect.Field類

  1. getModifiers:以int形式返回修飾符
    [說明:預設修飾符是0,public是1,private是2,protected是4,static是8, final是16], public(1) + static (8) = 9
  2. getType:以Class形式返回類型
  3. getName:返回屬性名

第三組: java.lang.reflect.Method類

  1. getModifiers:以int形式返回修飾符
    [說明:預設修飾符是0,public是1,private是2,protected是4static是8,final是16]
  2. getReturnType:以Class形式獲取返回類型
  3. getName:返回方法名
  4. getParameterTypes:以Class[]返回參數類型數組

第四組: java.lang.reflect.Constructor類

  1. getModifiers:以int形式返回修飾符

  2. getName:返回構造器名(全類名)

  3. getParameterTypes:以Class[]返回參數類型數組

import org.junit.jupiter.api.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * 演示如何通過反射獲取類的結構信息
 */
public class ReflectionUtils {
    public static void main(String[] args) {

    }

    @Test
    public void api_02() throws ClassNotFoundException, NoSuchMethodException {
        //得到Class對象
        Class<?> personCls = Class.forName("com.hspedu.reflection.Person");
        //getDeclaredFields:獲取本類中所有屬性
        //規定 說明: 預設修飾符 是0 , public  是1 ,private 是 2 ,protected 是 4 , static 是 8 ,final 是 16
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本類中所有屬性=" + declaredField.getName()
                    + " 該屬性的修飾符值=" + declaredField.getModifiers()
                    + " 該屬性的類型=" + declaredField.getType());
        }

        //getDeclaredMethods:獲取本類中所有方法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本類中所有方法=" + declaredMethod.getName()
                    + " 該方法的訪問修飾符值=" + declaredMethod.getModifiers()
                    + " 該方法返回類型" + declaredMethod.getReturnType());

            //輸出當前這個方法的形參數組情況
            Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("該方法的形參類型=" + parameterType);
            }
        }

        //getDeclaredConstructors:獲取本類中所有構造器
        Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("====================");
            System.out.println("本類中所有構造器=" + declaredConstructor.getName());//這裡老師只是輸出名

            Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
            for (Class<?> parameterType : parameterTypes) {
                System.out.println("該構造器的形參類型=" + parameterType);
            }
        }
    }

    //第一組方法API
    @Test
    public void api_01() throws ClassNotFoundException, NoSuchMethodException {

        //得到Class對象
        Class<?> personCls = Class.forName("com.hspedu.reflection.Person");
        //getName:獲取全類名
        System.out.println(personCls.getName());//com.hspedu.reflection.Person
        //getSimpleName:獲取簡單類名
        System.out.println(personCls.getSimpleName());//Person
        //getFields:獲取所有public修飾的屬性,包含本類以及父類的
        Field[] fields = personCls.getFields();
        for (Field field : fields) {//增強for
            System.out.println("本類以及父類的屬性=" + field.getName());
        }
        //getDeclaredFields:獲取本類中所有屬性
        Field[] declaredFields = personCls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本類中所有屬性=" + declaredField.getName());
        }
        //getMethods:獲取所有public修飾的方法,包含本類以及父類的
        Method[] methods = personCls.getMethods();
        for (Method method : methods) {
            System.out.println("本類以及父類的方法=" + method.getName());
        }
        //getDeclaredMethods:獲取本類中所有方法
        Method[] declaredMethods = personCls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本類中所有方法=" + declaredMethod.getName());
        }
        //getConstructors: 獲取所有public修飾的構造器,包含本類
        Constructor<?>[] constructors = personCls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("本類的構造器=" + constructor.getName());
        }
        //getDeclaredConstructors:獲取本類中所有構造器
        Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("本類中所有構造器=" + declaredConstructor.getName());//這裡老師只是輸出名
        }
        //getPackage:以Package形式返回 包信息
        System.out.println(personCls.getPackage());//com.hspedu.reflection
        //getSuperClass:以Class形式返回父類信息
        Class<?> superclass = personCls.getSuperclass();
        System.out.println("父類的class對象=" + superclass);//
        //getInterfaces:以Class[]形式返回介面信息
        Class<?>[] interfaces = personCls.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println("介面信息=" + anInterface);
        }
        //getAnnotations:以Annotation[] 形式返回註解信息
        Annotation[] annotations = personCls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("註解信息=" + annotation);//註解
        }


    }
}

class A {
    public String hobby;

    public void hi() {
    }
    public A() {
    }
    public A(String name) {
    }
}
interface IA {
}
interface IB {
}

@Deprecated
class Person extends A implements IA, IB {
    //屬性
    public String name;
    protected static int age; // 4 + 8 = 12
    String job;
    private double sal;
    //構造器
    public Person() {
    }
    public Person(String name) {
    }
    //私有的
    private Person(String name, int age) {
    }
    //方法
    public void m1(String name, int age, double sal) {
    }
    protected String m2() {
        return null;
    }
    void m3() {
    }
    private void m4() {
    }
}

通過反射創建對象

  1. 方式一:調用類中的public修飾的無參構造器

  2. 方式二:調用類中的指定構造器

  3. Class類相關方法

    newlnstance :調用類中的無參構造器,獲取對應類的對象
    getConstructor(Class...clazz):根據參數列表,獲取對應的public構造器對象getDecalaredConstructor(Class..clazz):根據參數列表,獲取對應的所有構造器對象

  4. Constructor類相關方法

    setAccessible:暴破

    newlnstance(Object...obj):調用構造器

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
 * 演示通過反射機制創建實例
 */
public class ReflecCreateInstance {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //1. 先獲取到User類的Class對象
        Class<?> userClass = Class.forName("com.hspedu.reflection.User");
        //2. 通過public的無參構造器創建實例
        Object o = userClass.newInstance();
        System.out.println(o);
        //3. 通過public的有參構造器創建實例
        /*
            constructor 對象就是
            public User(String name) {//public的有參構造器
                this.name = name;
            }
         */
        //3.1 先得到對應構造器
        Constructor<?> constructor = userClass.getConstructor(String.class);
        //3.2 創建實例,並傳入實參
        Object hsp = constructor.newInstance("hsp");
        System.out.println("hsp=" + hsp);
        //4. 通過非public的有參構造器創建實例
        //4.1 得到private的構造器對象
        Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
        //4.2 創建實例
        //暴破【暴力破解】 , 使用反射可以訪問private構造器/方法/屬性, 反射面前,都是紙老虎
        constructor1.setAccessible(true);
        Object user2 = constructor1.newInstance(100, "張三豐");
        System.out.println("user2=" + user2);
    }
}

class User { //User類
    private int age = 10;
    private String name = "韓順平教育";
    public User() {//無參 public
    }
    public User(String name) {//public的有參構造器
        this.name = name;
    }
    private User(int age, String name) {//private 有參構造器
        this.age = age;
        this.name = name;
    }
    public String toString() {
        return "User [age=" + age + ", name=" + name + "]";
    }
}

通過反射訪問類中的成員

訪問屬性

  1. 根據屬性名獲取Field對象
    Field f = clazz對象.getDeclaredField(屬性名);

  2. 暴破:f.setAccessible(true);//f 是Field

  3. 訪問

    f.set(o,值);//設置對象o中f的值//o表示對象

    System.out.println(f.get(o));//輸出對象o中f的值//o表示對象

  4. 註意:如果是靜態屬性,則set和get中的參數o,可以寫成null

案例演示:

import java.lang.reflect.Field;

/**
 * 演示反射操作屬性
 */
public class ReflecAccessProperty {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        //1. 得到Student類對應的 Class對象
        Class<?> stuClass = Class.forName("com.hspedu.reflection.Student");
        //2. 創建對象
        Object o = stuClass.newInstance();//o 的運行類型就是Student
        System.out.println(o.getClass());//Student
        //3. 使用反射得到age 屬性對象
        Field age = stuClass.getField("age");
        age.set(o, 88);//通過反射來操作屬性
        System.out.println(o);//
        System.out.println(age.get(o));//返回age屬性的值

        //4. 使用反射操作name 屬性
        Field name = stuClass.getDeclaredField("name");
        //對name 進行暴破, 可以操作private 屬性
        name.setAccessible(true);
        //name.set(o, "老韓");
        name.set(null, "老韓~");//因為name是static屬性,因此 o 也可以寫出null
        System.out.println(o);
        System.out.println(name.get(o)); //獲取屬性值
        System.out.println(name.get(null));//獲取屬性值, 要求name是static
    }
}

class Student {//類
    public int age;
    private static String name;
    public Student() {//構造器
    }
    public String toString() {
        return "Student [age=" + age + ", name=" + name + "]";
    }
}

訪問方法

  1. 根據方法名和參數列表獲取Method方法對象:Method m =clazz.getDeclaredMethod(方法名,XX.class);
  2. 獲取對象:Object o=clazz.newlnstance();
  3. 暴破:m.setAccessible(true);
  4. 訪問:Object returnValue = m.invoke(o,實參列表);
  5. 註意:如果是靜態方法,則invoke的參數o,可以寫成null!

應用案例:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
 * 演示通過反射調用方法
 */
public class ReflecAccessMethod {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        //1. 得到Boss類對應的Class對象
        Class<?> bossCls = Class.forName("com.hspedu.reflection.Boss");
        //2. 創建對象
        Object o = bossCls.newInstance();
        //3. 調用public的hi方法
        //Method hi = bossCls.getMethod("hi", String.class);//OK
        //3.1 得到hi方法對象
        Method hi = bossCls.getDeclaredMethod("hi", String.class);//OK
        //3.2 調用
        hi.invoke(o, "韓順平教育~");

        //4. 調用private static 方法
        //4.1 得到 say 方法對象
        Method say = bossCls.getDeclaredMethod("say", int.class, String.class, char.class);
        //4.2 因為say方法是private, 所以需要暴破,原理和前面講的構造器和屬性一樣
        say.setAccessible(true);
        System.out.println(say.invoke(o, 100, "張三", '男'));
        //4.3 因為say方法是static的,還可以這樣調用 ,可以傳入null
        System.out.println(say.invoke(null, 200, "李四", '女'));

        //5. 在反射中,如果方法有返回值,統一返回Object , 但是他運行類型和方法定義的返回類型一致
        Object reVal = say.invoke(null, 300, "王五", '男');
        System.out.println("reVal 的運行類型=" + reVal.getClass());//String

        //在演示一個返回的案例
        Method m1 = bossCls.getDeclaredMethod("m1");
        Object reVal2 = m1.invoke(o);
        System.out.println("reVal2的運行類型=" + reVal2.getClass());//Monster
    }
}

class Monster {}
class Boss {//類
    public int age;
    private static String name;

    public Boss() {//構造器
    }

    public Monster m1() {
        return new Monster();
    }

    private static String say(int n, String s, char c) {//靜態方法
        return n + " " + s + " " + c;
    }

    public void hi(String s) {//普通public方法
        System.out.println("hi " + s);
    }
}

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

-Advertisement-
Play Games
更多相關文章
  • 這兩天在對一些ORM進行性能測試(涉及SqlSugar、FreeSql、Fast.Framework、Dapper.LiteSql),測試用的是Winform程式,別人第一眼看到我的程式,說,你這測試沒意義! 可能我的測試程式的某些地方寫的比較變態吧,但我認為有現實意義,並且網上有相關網站崩潰問題的 ...
  • 參考鏈接:https://www.systutorials.com/docs/linux/man/7-netlink/ #1. 監聽Netlink消息類型示例 Netlink是用戶程式與內核通信的socket方法,通過Netlink可以獲得修改內核的配置,常見的有獲得介面的IP地址列表、更改路由表或 ...
  • 0. 前言 可以臨時設置,也可以修改配置文件 1. 修改配置文件 # 打開 配置IP的文件 路徑如下 sudo vi /etc/netplan/01-network-manager-all.yaml 1.1 輸入(修改)以下內容 # This is the network config writte ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 1.結論先行 無論ibp(innodb_buffer_pool_size)是否充足,MySQL的性能都遠不如GreatSQL。 MySQL的性能平均約為 ...
  • 一.數據的存儲方式 特定的文件 / 記憶體 / 第三方雲伺服器 / 資料庫伺服器 二.什麼是資料庫 資料庫按照一定的形式來組織存儲數據,目的是為了便於操作數據 —— 增刪改查 三.資料庫發展歷史 網狀資料庫 -> 層次型資料庫 -> 關係型資料庫 -> 非關係型資料庫(NoSQL) 關係型資料庫邏輯結 ...
  • 本文介紹對vue-plugin-hiprint部分重要代碼的解析,這是一個很好的開源插件,能夠自己自定義列印模板,通過後端傳來的數據進行渲染列印,官方也提供了許多的api供開發者使用。界面採用了antdesign。實現了免預覽的直接列印。 ...
  • 前言 在這個人人都是自媒體的時代,為了擴大個人影響力同時預防文章被盜版至其他平臺,多平臺發佈文章就成了創作者們的一大痛點,為瞭解決這一痛點就需要將文章的編輯到發佈無縫集成。 現在要實現這一功能,開發一個完全可控的Markdown編輯器就是第一步。 本文源碼已上傳Github:Github hxsfx ...
  • 組合模式(Composite Pattern),又叫部分整體模式,是用於把一組相似的對象當作一個單一的對象。組合模式依據樹形結構來組合對象,用來表示部分以及整體層次。這種類型的設計模式屬於結構型模式,它創建了對象組的樹形結構。 ...
一周排行
    -Advertisement-
    Play Games
  • GoF之工廠模式 @目錄GoF之工廠模式每博一文案1. 簡單說明“23種設計模式”1.2 介紹工廠模式的三種形態1.3 簡單工廠模式(靜態工廠模式)1.3.1 簡單工廠模式的優缺點:1.4 工廠方法模式1.4.1 工廠方法模式的優缺點:1.5 抽象工廠模式1.6 抽象工廠模式的優缺點:2. 總結:3 ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 本章將和大家分享ES的數據同步方案和ES集群相關知識。廢話不多說,下麵我們直接進入主題。 一、ES數據同步 1、數據同步問題 Elasticsearch中的酒店數據來自於mysql資料庫,因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這個就是Elasticsearch與my ...
  • 引言 在我們之前的文章中介紹過使用Bogus生成模擬測試數據,今天來講解一下功能更加強大自動生成測試數據的工具的庫"AutoFixture"。 什麼是AutoFixture? AutoFixture 是一個針對 .NET 的開源庫,旨在最大程度地減少單元測試中的“安排(Arrange)”階段,以提高 ...
  • 經過前面幾個部分學習,相信學過的同學已經能夠掌握 .NET Emit 這種中間語言,並能使得它來編寫一些應用,以提高程式的性能。隨著 IL 指令篇的結束,本系列也已經接近尾聲,在這接近結束的最後,會提供幾個可供直接使用的示例,以供大伙分析或使用在項目中。 ...
  • 當從不同來源導入Excel數據時,可能存在重覆的記錄。為了確保數據的準確性,通常需要刪除這些重覆的行。手動查找並刪除可能會非常耗費時間,而通過編程腳本則可以實現在短時間內處理大量數據。本文將提供一個使用C# 快速查找並刪除Excel重覆項的免費解決方案。 以下是實現步驟: 1. 首先安裝免費.NET ...
  • C++ 異常處理 C++ 異常處理機制允許程式在運行時處理錯誤或意外情況。它提供了捕獲和處理錯誤的一種結構化方式,使程式更加健壯和可靠。 異常處理的基本概念: 異常: 程式在運行時發生的錯誤或意外情況。 拋出異常: 使用 throw 關鍵字將異常傳遞給調用堆棧。 捕獲異常: 使用 try-catch ...
  • 優秀且經驗豐富的Java開發人員的特征之一是對API的廣泛瞭解,包括JDK和第三方庫。 我花了很多時間來學習API,尤其是在閱讀了Effective Java 3rd Edition之後 ,Joshua Bloch建議在Java 3rd Edition中使用現有的API進行開發,而不是為常見的東西編 ...
  • 框架 · 使用laravel框架,原因:tp的框架路由和orm沒有laravel好用 · 使用強制路由,方便介面多時,分多版本,分文件夾等操作 介面 · 介面開發註意欄位類型,欄位是int,查詢成功失敗都要返回int(對接java等強類型語言方便) · 查詢介面用GET、其他用POST 代碼 · 所 ...
  • 正文 下午找企業的人去鎮上做貸後。 車上聽同事跟那個司機對罵,火星子都快出來了。司機跟那同事更熟一些,連我在內一共就三個人,同事那一手指桑罵槐給我都聽愣了。司機也是老社會人了,馬上聽出來了,為那個無辜的企業經辦人辯護,實際上是為自己辯護。 “這個事情你不能怪企業。”“但他們總不能讓銀行的人全權負責, ...