Java 反射詳解。(入門篇)

一、什麼是反射?

請結合註解學習

1.定義

將類的各個組成部分封裝爲其他對象(Field,Constructor,Method),就是反射機制

2. 比如將class文件中的成員變量封裝爲第二階段Class類中的Field類

在這裏插入圖片描述

二、反射有什麼用?

  1. 運行的時候操作這些對象,
    比如:你使用idea時候的class. 然後自動彈出的可用的提示方法
    在這裏插入圖片描述
  2. 降低程序的耦合性。

三、如何獲取Class對象的3種方式,

1. 三種方式

  1. Class.forName(“全類名”) : 階段1可以使用,將class文件,加載進內存並返回Class對象
    多用於配置文件,讀取文件,加載類

  2. Student.class : 階段2 可用,類的的屬性class **
    多用於
    傳參**

  3. student.getClass() : 階段3可用,對象實例的方法
    多用於對象獲取字節碼文件

2. 代碼示例

package Java學習.Java高級.註解和反射.反射.獲取Class類的方法;

/**
 * 1. Class.forName("全類名") : 階段1可以使用,**將class文件,加載進內存並返回Class對象**
 * 多用於**配置文件,讀取文件,加載類**
 *
 *
 * 2. Student.class : 階段2 可用,**類的的屬性class **
 * 多用於**傳參**
 *
 * 3. student.getClass() : 階段3可用,**對象實例的方法**
 * 多用於**對象**獲取字節碼文件
 */
public class Demo01黑馬 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.
        System.out.println("----1. Class.forName(\"全類名\")------");
        System.out.println(Class.forName("Java學習.Java高級.註解和反射.反射.獲取Class類的方法.Student01"));
        //2.
        System.out.println("--------2. Student.class---------");
        System.out.println(Student01.class);
        //3.
        System.out.println("-----3. student.getClass()-----");
        System.out.println(new Student01().getClass());
        System.out.println("-------123是否相等----------");
        System.out.println(new Student01().getClass().hashCode());
        System.out.println(Student01.class.hashCode());
        System.out.println(Class.forName("Java學習.Java高級.註解和反射.反射.獲取Class類的方法.Student01").hashCode());
        System.out.println("HashCode的值相等,地址值相等,");
        System.out.println("結論: 同一個class字節碼文件的在程序的運行中,只會被加載一次" +
                "\n無論通過哪一種方式獲取的class對象都是同一個.");

    }
}
class Student01{

}

Run:
----1. Class.forName(“全類名”)------
class Java學習.Java高級.註解和反射.反射.獲取Class類的方法.Student01
--------2. Student.class---------
class Java學習.Java高級.註解和反射.反射.獲取Class類的方法.Student01
-----3. student.getClass()-----
class Java學習.Java高級.註解和反射.反射.獲取Class類的方法.Student01
-------123是否相等----------
1915910607
1915910607
1915910607
HashCode的值相等,地址值相等,
結論: 同一個class字節碼文件的在程序的運行中,只會被加載一次
無論通過哪一種方式獲取的class對象都是同一個.

Process finished with exit code 0

四、獲取的反射對象有什麼用?通過Class 獲取該類的Field,Constructor和Method對象,並且可以使用對應部分的方法(例如fiead.get())

1. Field

(1)知識點
 * 一、獲取成員變量Field
 * 1. getField("publicField") (public權限)1個字段publicField
 * 2. getFields() (public權限)全部字段們
 *
 * 3. getDeclaredField("privateField") (所有權限)的1個字段privateField
 * 4. getDeclaredFields() (所有權限)的字段們
 * 二、使用field
 *  1.get():    .get(person02)
 *  2.set():    .set(person02,"設置的值")
 *  3.暴力反射:獲取非public字段後需要暴力反射才能使用privateField的方法(比如get())
 *  privateField1.setAccessible(true);
(2) 代碼
package Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.獲取字段Field021;

import Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02;

import java.lang.reflect.Field;

/**
 * 一、獲取成員變量Field
 * 1. getField("publicField") (public權限)1個字段publicField
 * 2. getFields() (public權限)全部字段們
 * <p>
 * 3. getDeclaredField("privateField") (所有權限)的1個字段privateField
 * 4. getDeclaredFields() (所有權限)的字段們
 * 二、使用field
 * 1.get():    .get(person02)
 * 2.set():    .set(person02,"設置的值")
 * 3.暴力反射:獲取非public字段後需要暴力反射才能使用privateField的方法(比如get())
 * privateField1.setAccessible(true);
 * <p>
 * --------------------------------------------------------------------------
 * 一、獲取構造方法Constructor
 * 1. getConstructor(String.class) (public權限)1個String參數的Constructor
 * 2.3.4.同理
 * 二、使用constructor
 * 1.newInstance(): 新建Person的實例 ,newInstance(String "name",int age)
 * <p>
 * -----------------------------------------------
 * 一、獲取成員方法Method
 * 1. getMethod("方法名eat",String.class) public權限的字段
 * String.class方法的參數的類型String
 * 2.3.4同上
 * 二、Method的使用
 * 1.invoke(): 調用方法 invoke("蛇皮怪"),蛇皮怪就是方法的參數。
 * <p>
 * -----------------------------------------------
 * 一、獲取類名getName
 * 1. getName()
 */
public class Demo02 {
    public static void main(String[] args) throws Exception {
        Person02 person02 = new Person02();
        Class<? extends Person02> class1 = person02.getClass();
        /**
         *  * 一、獲取成員變量Field
         *  * 1. getField(String name) public權限的字段
         *  * 2. getFields() 全部public權限字段們
         *  *
         *  * 3. getDeclaredField(String name) 獲取所有權限的(public --private)的字段
         *  * 4. getDeclaredFields() 獲取所有權限的(public --private)的字段們
         *  拓展
         *  5.獲取class1對象下字段privateField的值 privateField.get(class1)
         *  6.設置值: privateField.set(class1,"設置的值")
         *  7.暴力反射獲取
         *  privateField1.setAccessible(true);
         */
        Field publicField = class1.getField("publicField");
        System.out.println("-----------一、.---------");
        System.out.println("1.getField(\"publicField\");-------");
        System.out.println(publicField);
        Object[] fields;
        fields = class1.getFields();
        System.out.println("2.getFields()-------");
        for (int i = 0; i < fields.length; i++) {
            System.out.println(fields[i]);
        }
        System.out.println("結論:只能獲取public類型的field");
        System.out.println("4.getDeclaredFields()--------");
        fields = class1.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            System.out.println(fields[i]);
        }
        /**
         *          *  拓展
         *          *  5.獲取class1對象下字段privateField的值 privateField.get(class1)
         *          *  6.設置值: privateField.set(class1,"設置的值")
         *          *  7.暴力反射獲取
         *              privateField1.setAccessible(true);
         */
        System.out.println("5.獲取class1對象下字段privateField的值 privateField.get(person02)-------\n" +
                "6.設置值: privateField.set(person02,\"設置的值\")-------");

        Field privateField = class1.getField("publicField");
        System.out.println("privateField.get(person02): " + privateField.get(person02));
        privateField.set(person02, "set的值");
        System.out.println("privateField.set(person02,\"set的值\"): " + privateField.get(person02));

        System.out.println("暴力反射privateField1.setAccessible(true);----------");
        Field privateField1 = class1.getDeclaredField("privateField");
        //暴力反射前訪問私有報錯java.lang.IllegalAccessException
        //System.out.println(privateField1.get(person02));
        privateField1.setAccessible(true);
        System.out.println("暴力反射就能獲取私有Field: " + privateField1.get(person02));
    }
}

Run:
-----------一、.---------
1.getField(“publicField”);-------
public java.lang.String Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02.publicField
2.getFields()-------
public java.lang.String Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02.publicField
結論:只能獲取public類型的field
4.getDeclaredFields()--------
private java.lang.String Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02.privateField
java.lang.String Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02.defaltField
protected java.lang.String Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02.protectField
public java.lang.String Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02.publicField
5.獲取class1對象下字段privateField的值 privateField.get(person02)-------
6.設置值: privateField.set(person02,“設置的值”)-------
privateField.get(person02): null
privateField.set(person02,“set的值”): set的值
暴力反射privateField1.setAccessible(true);----------
暴力反射就能獲取私有Field: null

Process finished with exit code 0

2, Constructor

(1)知識點
 * 一、獲取構造方法Constructor
 * 1. getConstructor(String.class) (public權限)1個String參數的Constructor
 * 2.3.4.同理
 * 二、使用constructor
 * 1.newInstance(): 新建Person的實例 ,newInstance(String "name",int age)
(2)code
package Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.獲取構造方法Method022;


import Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02;

import java.lang.reflect.Constructor;

public class Demo022 {
    public static void main(String[] args) throws Exception {
        /**
         * 一、獲取構造方法Constructor
         * 1. getConstructor(String.class) (public權限)1個String參數的Constructor
         * 2.3.4.同理
         * 二、使用constructor
         * 1.newInstance(): 新建Person的實例 ,newInstance(String "name",int age)
         */
        Person02 person02 = new Person02();
        Class<? extends Person02> aClass = person02.getClass();
        Constructor<? extends Person02> constructor = aClass.getConstructor();//獲取無參構造
        Constructor<? extends Person02> constructor1 = aClass.getConstructor(String.class);//獲取一個參數的構造
        System.out.println("無參構造: " + constructor);
        System.out.println("1參構造: " + constructor1);
        System.out.println("-------二、獲取構造的作用,newInstance()新建person對象實例------------");
        Person02 person021 = constructor1.newInstance("參數1");
        System.out.println(person021);

    }
}

Run:
無參構造: public Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02()
1參構造: public Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02(java.lang.String)
-------二、獲取構造的作用,newInstance()新建person對象實例------------
Person02{privateField=‘參數1’, defaltField=‘null’, protectField=‘null’, publicField=‘null’}

Process finished with exit code 0

3.Method

(1)知識點
 * 一、獲取成員方法Method
 * 1. getMethod("方法名eat",String.class) public權限的字段
 * String.class方法的參數的類型String
 * 2.3.4同上
 * 二、Method的使用
 * 1.invoke(): 調用方法 invoke("蛇皮怪"),蛇皮怪就是方法的參數。
(2)code
package Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.獲取成員方法們Method023;

import Java學習.Java高級.註解和反射.反射.heima.使用Class對象02.Person02;

import java.lang.reflect.Method;

/**
 *  * 一、獲取成員方法Method
 *  * 1. getMethod("方法名eat",String.class) public權限的字段
 *  * String.class方法的參數的類型String
 *  * 2.3.4同上
 *  * 二、Method的使用
 *  * 1.invoke(): 調用方法 invoke("蛇皮怪"),蛇皮怪就是方法的參數。
 *  
 * 1.使用class獲取Method
 * 2.使用Method
 * (1)使用method調用方法
 * (2)使用method獲取方法名稱
 */
public class Demo_023 {
    public static void main(String[] args) throws Exception {
        //1.使用class獲取Method
        Person02 person02 = new Person02();
        Class<? extends Person02> aClass = person02.getClass();
        Method eatMethod = aClass.getMethod("eat", String.class);
        //2.使用method調用方法invoke()
        eatMethod.invoke(person02, "個蛇皮");
        System.out.println(eatMethod.getName());
    }
}

Run:
eat…個蛇皮
eat

Process finished with exit code 0

五、案例練習.

1. 需求:

  1. 寫一個框架,不能改變類的任何的代碼的情況下,創建類的對象,並且執行其中的任意代碼

2. 實現原理:

  1. 配置文件
  2. 反射

3. 實現步驟

  1. 需要將創建對象的全類名和需要執行的文件定義在配置文件
className=Java學習.Java高級.註解和反射.反射.heima.反射項目.Person
methodName=eat
  1. 在程序中加載讀取配置文件
//2.在程序中**加載讀取配置文件**----------
//2.1創建Properties對象
Properties properties = new Properties();
//2.2加載load()方法,加載配置文件,儲存key value 的值在properties中
//2.2.1 :load() 需要傳遞參數InputStream
String configuration = "D:\\Program Files\\JetBrains\\test1\\Lab\\src\\Java學習\\Java高級\\註解和" +
        "反射\\反射\\heima\\反射項目\\configuration.properties";
FileInputStream fileInputStream = new FileInputStream(configuration);
properties.load(fileInputStream);
//2.3 獲取配置文件的數據:
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
  1. 使用反射技術加載類進內存
Class.forName("全類名")
  1. 創建Person對象
//4.創建Person對象------
//4.1因爲Class類的創建Person對象方法newInstance()已經棄用
//我們先創建獲得構造函數類Constructor在創建Person對象
//獲得構造函數類Constructor 無參
Constructor<?> constructor = aClass.getConstructor();
//4.2 newInstance()創建Person對象
Object person = constructor.newInstance();
  1. **5.用aClass獲取getMethod對象,然後再使用新建的person對象invoke(person)執行方法 **
//5.用aClass獲取getMethod對象,然後再使用新建的person對象invoke(person)執行方法---------------
Method method = aClass.getMethod(methodName);
method.invoke(person);

4. 代碼:

1.配置文件 configuration.properties

className=Java學習.Java高級.註解和反射.反射.heima.反射項目.Person
methodName=eat

2.Person 類:Person.java

package Java學習.Java高級.註解和反射.反射.heima.反射項目;

public class Person {
    private String privateField;
    String defaltField;
    protected String protectField;
    public String publicField;
    public void eat(){
        System.out.println("eat....");
    }
    public void eat(String food){
        System.out.println("eat..."+food);
    }
    public Person() {
        this.privateField = privateField;
    }

    public Person(String privateField) {
        this.privateField = privateField;
    }

    @Override
    public String toString() {
        return "Person02{" +
                "privateField='" + privateField + '\'' +
                ", defaltField='" + defaltField + '\'' +
                ", protectField='" + protectField + '\'' +
                ", publicField='" + publicField + '\'' +
                '}';
    }

    public Person(String privateField, String defaltField, String protectField, String publicField) {
        this.privateField = privateField;
        this.defaltField = defaltField;
        this.protectField = protectField;
        this.publicField = publicField;
    }

    public String getPrivateField() {
        return privateField;
    }

    public void setPrivateField(String privateField) {
        this.privateField = privateField;
    }

    public String getDefaltField() {
        return defaltField;
    }

    public void setDefaltField(String defaltField) {
        this.defaltField = defaltField;
    }

    public String getProtectField() {
        return protectField;
    }

    public void setProtectField(String protectField) {
        this.protectField = protectField;
    }

    public String getPublicField() {
        return publicField;
    }

    public void setPublicField(String publicField) {
        this.publicField = publicField;
    }
}

Demo_01.java

package Java學習.Java高級.註解和反射.反射.heima.反射項目;

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

/**
 * 一、Properties類是Map的子類,性質類
 * 1. Properties 對象可以把以properties結尾的文件
 * 用load方法讀取到內存裏面形成一個集合
 *
 */
public class Demo_01 {
    public static void main(String[] args) throws Exception {
        //2.在程序中**加載讀取配置文件**----------
        //2.1創建Properties對象
        Properties properties = new Properties();
        //2.2加載load()方法,加載配置文件,儲存key value 的值在properties中
        //2.2.1 :load() 需要傳遞參數InputStream
        String configuration = "D:\\Program Files\\JetBrains\\test1\\Lab\\src\\Java學習\\Java高級\\註解和" +
                "反射\\反射\\heima\\反射項目\\configuration.properties";
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(configuration),"GBK");
//        解碼出錯,properties是GBK格式

        properties.load(inputStreamReader);
        //2.3 獲取配置文件的數據:
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");
        System.out.println(className);
        //3. 使用**反射**技術加載類**進內存**-----------
        Class aClass = Class.forName(className);
        //4.創建Person對象------
        //4.1因爲Class類的創建Person對象方法newInstance()已經棄用
        //我們先創建獲得構造函數類Constructor在創建Person對象
        //獲得構造函數類Constructor 無參
        Constructor<?> constructor = aClass.getConstructor();
        //4.2 newInstance()創建Person對象
        Object person = constructor.newInstance();
        //5.用aClass獲取getMethod對象,然後再使用新建的person對象invoke(person)執行方法---------------
        Method method = aClass.getMethod(methodName);
        method.invoke(person);

    }
}

Run:
Java學習.Java高級.註解和反射.反射.heima.反射項目.Person
eat…

Process finished with exit code 0

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