05- 反射

反射基礎

概念

​ JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;

​ 這種動態獲取的信息以及動態調用對象方法的功能稱爲java語言的反射機制。

反射機制是構建框架技術的基礎

無反射 不框架

IDE Eclipse IEDA 代碼提示功能

反射的原理

.java文件 =編譯器=> .class 文件 =虛擬機=> 運行 .class文件

反射把該過程 逆向推理

正在運行的.class 文件 =編譯器=> ?

反射就是把java類中的各種成分映射成一個個的Java對象

例如:一個類有:成員變量、方法、構造方法、包等等信息,利用反射技術可以對一個類進行解剖,把個個組成部分映射成一個個對象。

(其實:一個類中這些成員方法、構造方法、在加入類中都有一個類來描述)

電腦本身是一個對象

解剖成爲 一個一個的組成部分 CPU 硬盤 GPU 內存 風扇 網卡 …

Class類

  • Class類的表示正在運行的Java應用程序中的類和接口。

Object 中 有一個方法 getClass() 返回值 是 Class 類型 表示 正在運行的類的實際類型

所有的類都有該方法

三種Class 對象的獲取方式

第一種/ 根據對象獲取

Student stu1 = new Student();
Student stu2 = new Student();
System.out.println(stu1 == stu2);

Class cla1 = stu1.getClass();
Class cla2 = stu2.getClass();  
System.out.println(cla1 == cla2);

程序運行開始 , 在創建對象的時候, 如果同一個類創建多個對象 ,實際運行的類型只有一個

第二種 / 根據類獲取

類名.class 屬性 
Class cla = Student.class;

第三種 / 根據字符串獲取

// 通過一個類的 全路徑 獲取該類的字節碼文件對象
Class cla = Class.forName("s0731.ref.Student");

後期使用反射推薦使用第三種, 只依賴於一個字符串 Class類常用方法

屬性類 Field

獲取屬性的方法

getField(屬性名)  只能獲取公共(public)的屬性
getDeclaredField(屬性名)  獲取任意修飾符的屬性
getFields() 獲取所有公共(public)的屬性  返回值爲Field 數組
getDeclaredFields()  獲取任意修飾符的屬性集合

屬性類 Field

構造類 Constructor

獲取構造的方法

getConstructor(參數列表類型 類<?>... parameterTypes)  
// 方法重載 
getDeclaredConstructor(參數列表類型 類<?>... parameterTypes)
getConstructors()  
getDeclaredConstructors() 

Constructor 構造包裝類的方法

newInstance(Object... initargs) 
使用此 Constructor對象表示的構造函數,使用指定的初始化參數來創建和初始化構造函數的聲明類的新實例。 

方法類 Method

invoke(Object obj, Object... args) 
在具有指定參數的 方法對象上調用此 方法對象表示的底層方法。 

獲取方法的方法

getMethod(String name, 類<?>... parameterTypes) 
getDeclaredMethod(String name, 類<?>... parameterTypes) 
getMethods() 
getDeclaredMethods() 

反射應用

基礎數據

public class Student {
    public String name;
    int age;
    private String address;

    public Student(){}

    public void study(){
        System.out.println("正在學習!!");
    }

    private void play(){
        System.out.println("隨便玩");
    }
    private void play(String thing){
        System.out.println("玩"+ thing);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", address='" + address + '\'' +
                '}';
    }
}

案例1 公共屬性

獲取對象的公共屬性並且賦值

// 獲取到了 該類的字節碼文件 .class 文件
Class stuCla = Class.forName("s0731.ref.Student");
// 獲取該類的構造方法
Constructor constructor = stuCla.getConstructor();
// 獲取的就是 public Student(){} 的包裝對象
Object obj1 = constructor.newInstance();
Object obj2 = constructor.newInstance();
// 獲取屬性
Field nameField = stuCla.getField("name");
nameField.set(obj1,"張三");
nameField.set(obj2,"李四");
System.out.println(obj1);
System.out.println(obj2);

案例2 私有屬性

獲取對象的私有屬性並且賦值

public static void main(String[] args) throws Exception{
    Class stuCla = Class.forName("s0731.ref.Student");
    Constructor constructor = stuCla.getConstructor();
    Object obj = constructor.newInstance();
    // 獲取私有屬性的包裝類
    Field addressField = stuCla.getDeclaredField("address");
    // 雖然能夠獲取到 private 修飾的屬性
    // private 不能被外界訪問和修改
    // 設置 取消 private 的 限制
    addressField.setAccessible(true);
    addressField.set(obj,"青島");
    
    Field nameField = stuCla.getField("name");
    nameField.set(obj,"張三");
    
    Field ageField = stuCla.getDeclaredField("age");
    ageField.set(obj,20);
    System.out.println(obj);
}

案例3 方法

獲取公共的方法並調用

Class stuCla = Class.forName("s0731.ref.Student");
Constructor constructor = stuCla.getConstructor();
Object obj = constructor.newInstance();
/ 獲取公共方法 方法名爲 study 的無參方法
Method study = stuCla.getMethod("study");
study.invoke(obj);

獲取私有的無參方法

// 獲取私有方法 方法名爲 play 的無參方法
 Method play = stuCla.getDeclaredMethod("play");
 play.setAccessible(true);
 play.invoke(obj);

獲取私有的有參方法

// 獲取私有方法 方法名爲 play 的有參方法
Method play = stuCla.getDeclaredMethod("play",String.class);
play.setAccessible(true);
play.invoke(obj,"籃球");

獲取方法的返回值

如果play 方法有返回值 
Object Parma = play.invoke(obj,"籃球");

實際應用

避開list 泛型檢查

public static void main(String[] args) throws Exception{
    List<String> list = new ArrayList<String>();
    list.add("張三");
    list.add("李四");
    // list.add(123);
    // 任務 向 泛型爲 String 的 list 集合中 成功添加一個 123 的整數類型
    /*  步驟
    1- 獲取list 的運行時 字節碼對象
    2- 獲取 list 的 add 方法封裝對象
    3, 使用 封裝對象 調用 add 方法添加內容
    泛型實際上僅僅是在編譯期生效
    */
    Class<?> clazz = Class.forName("java.util.ArrayList");
    Method add = clazz.getMethod("add", Object.class);
    add.invoke(list,123);
    System.out.println(list);
}

properties

基本使用

新建文件

abc.properties

name=於老闆
age=50

通過程序加載

public static void main(String[] args) throws Exception {
    // 1- 創建 properties 對象
    // 2- 對象 通過 輸入流 加載 本地文件
    // 3- 分別通過鍵 獲取值
    Properties pro = new Properties();
    FileInputStream fis = new FileInputStream("src\\s0731\\abc.properties");
    pro.load(fis);
    fis.close();
    
    String name = pro.getProperty("name");
    String age = pro.getProperty("age");
    System.out.println(name + "\t"+age);

通過配置文件自定義執行內容

public class Demo1 {
    public static void main(String[] args) throws Exception {
        //        Test1 t1 = new Test1();
        //        t1.m1();
        Properties pro = new Properties();
        FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\abc.properties");
        pro.load(fis);
        fis.close();

        String className = pro.getProperty("class");
        String methodName = pro.getProperty("method");

        Class<?> clazz = Class.forName(className);
        Object obj = clazz.newInstance();
        Method method = clazz.getMethod(methodName);
        method.invoke(obj);
    }
}

需要把整個項目打成一個可執行 jar 包

可以通過修改配置文件 , 來動態的指定我們需要運行的程序

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