20201209——java反射

宮城詩發表於2020-12-09

靜態與動態

動態語言 java c c++
靜態語言 python javascript

就看是否能在執行的時候,是否可以改變其結構

Reflection

反射,是java被視為動態語言的關鍵,反射機制允許程式在執行期藉助於Reflection API取得任何類的內部資訊,並且能直接操作任意物件的內部屬性及方法。

載入完一個類之後,在堆記憶體的方法區就產生了一個Class型別的物件(一個類只能有一個Class物件),這個物件完整的包含了完整的類的結構資訊,我們可以通過這個物件看到類的結構,這個物件就像一面鏡子,透過這個鏡子我們就可以看到類的結構,所以我們形象的稱之為反射

提供的功能應用

在執行時判斷任意一個物件所屬的類
在執行時構造任意一個類的物件
在執行時判斷任意一個類所具有的成員變數和方法
在執行時獲取泛型資訊
在執行時呼叫任意一個物件的成員變數和方法
在執行時處理註解
生成動態代理

反射的優缺點

優點

可以實現動態建立物件和編譯,體現出很大的靈活性

缺點

對效能有影響,使用反射基本上是一種解釋操作,我們可以告訴jvm,我們希望做什麼並且滿足它的要求,這類操作總是慢於直接執行相同的操作。

反射主要相關的API

java.lang.Class 類
java.lang.reflect.Method 類的方法
java.lang.reflect.Field 類的成員變數
java.lang.reflect.Constructor 類的構造器

獲取反射物件

一個類只有一個class物件

/**
 * @Classname TestReflection
 * @Description TODO
 * @Date 2020/12/9 10:57
 * @Created by mmz
 */
public class TestReflection {
    // 反射獲取類的class物件
    public static void main(String[] args) throws ClassNotFoundException {
        Class user = Class.forName("User");
        System.out.println(user);
        Class user1 = Class.forName("User");
        Class user2 = Class.forName("User");
        System.out.println(user1 == user2);

    }
}

// 實體類 pojo Entity
class User{
    private String name;
    private int id;
    private int age;

    public User(){

    }

    public User(String name, int id, int age) {
        this.name = name;
        this.id = id;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

因為最後結果返回的是,user1 == user2 為true

Class類

物件照鏡子的最後可以得到的資訊:某個類的屬性,方法和構造器,某個類到底實現了哪些介面。
對於每個類而言,JRE都為其保留一個不變的Class型別的物件,一個Class物件包含了特定某個結構的有關資訊

class本身也是一個類
class物件只能由系統建立物件
一個載入的類在jvm中只會有一個class例項
一個class物件對應是一個載入到jvmz紅的一個.class檔案
每個類的例項都會記得自己是由哪個Class例項所生成
通過Class可以完整地得到一個類中所有的被載入的結構
Class類是Reflection的根源,針對於任何你想動態載入,執行的類,唯有先獲得相應的Class物件

Class類的常用方法

在這裡插入圖片描述

獲取class類的例項

1)若已經知道具體的類,通過類的class屬性獲取,該方法最為安全可靠,程式效能最高

Class class = Person.class

2)已知某個類的例項,呼叫該例項的getClass()方法獲取class物件

Class class = person.getClass()

3)已知一個類的全類名,且該類在類路徑下面,可通過Class類的靜態方法forName()獲取,可能丟擲ClassNotFoundException

Class class = CLass.forName("java.lang.String")

4)內建基本資料型別可以直接用類名.Type
5)還可以利用ClassLoader

/**
 * @Classname TestCreate
 * @Description TODO
 * @Date 2020/12/9 14:19
 * @Created by mmz
 */
/*測試class類的建立方式有幾種*/
public class TestCreate {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person = new Student();
        System.out.println("這個人是" + person.name);

        // 方式一:通過物件獲得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());

        // 方式二:通過forName獲得
        Class c2 = Class.forName("Student");
        System.out.println(c2.hashCode());

        // 方式三:通過類獲得
        Class c3 = Student.class;
        System.out.println(c3.hashCode());

        // 方式四:基本內建型別的包裝類都有一個Type屬性
        Class<Integer> c4 = Integer.TYPE;
        System.out.println(c4);

        // 獲得父類型別
        Class c5 = c1.getSuperclass();
        System.out.println(c5);

    }
}
class Person{
    String name;

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

    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Student extends Person{
    public Student(){
        this.name = "學生";
    }

}

class Teacher extends Person{
    public Teacher(){
        this.name = "老師";
    }
}

相關文章