Java匹馬行天下之JavaSE核心技術——反射機制

                     Java反射機制

 

1、什麼是反射?

 

    在運行狀態中,對於任意一個類,都可以獲取到這個類的全部屬性和方法,對於任意一個對象,都可以調用它的任意一個方法和屬性(包括私有的方法和屬性),這種動態獲取的信息以及動態調用對象的方法的功能就稱爲java語言的反射機制。通俗點講,經過反射,該類對咱們來講是徹底透明的,想要獲取任何東西均可以。java

    想要使用反射機制,就必需要先獲取到該類的字節碼文件對象(.class),經過字節碼文件對象,就可以經過該類中的方法獲取到咱們想要的全部信息(方法,屬性,類名,父類名,實現的全部接口等等),每個類對應着一個字節碼文件也就對應着一個Class類型的對象,也就是字節碼文件對象。數組

 

JAVA反射機制是在運行狀態中,
  對於任意一個類,都可以知道這個類的全部屬性和方法(動態獲取的信息);
  對於任意一個對象,都可以調用它的任意一個方法和屬性(動態調用對象的方法);
  這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制

  簡言之:經過字節碼文件對象,去使用該文件中的成員變量、構造方法、成員方法。
ide

 

    獲取字節碼文件對象的三種方式。this

       一、Class class1 = Class.forName("全限定類名");  //經過Class類中的靜態方法forName,直接獲取到一個類的字節碼文件對象,此時該類仍是源文件階段,並無變爲字節碼文件。加密

       二、Class class2  = Person.class;    //當類被加載成.class文件時,此時Person類變成了.class,在獲取該字節碼文件對象,也就是獲取本身, 該類處於字節碼階段。spa

       三、Person p = new Person();對象

                              Class class3 = p.getClass();    //經過類的實例獲取該類的字節碼文件對象,該類處於建立對象階段 blog

 

 

第一種和後兩種的區別        
        後兩種你必須明確Person類型。
        第一種須要這種類型的字符串就行(開發中用)。
        這種擴展更強,不須要知道具體的類,只提供字符串,按照配置文件加載就能夠了。

 

 反射的特色:
        1.Class類中的靜態方法forName()傳入的字符串未來會作成配置信息文件,因此之後你不知道程序運行的是誰(是哪一個類)
        2.反射是不會看到類的任何信息的。即經過構造方法對象Constructor、成員方法對象Method,調用他們的方法返回值都是Object類型。
          (由於任何類型均可以用Object類型接收,基本數據類型會自動裝箱爲引用數據類型)。
        3.反射能夠訪問私有的東西(前提是class文件未被加密)。接口

 

三種獲取字節碼文件對應的Class類型的對象的方式內存

 

  要想解剖一個類,必須先要獲取到該類的字節碼文件對象。
  而解剖使用的就是Class類中的方法,因此先要獲取到每個字節碼文件對應的Class類型的對象。

 

  .class文件   --> Class類
    成員變量    --> Field類
    構造方法    --> Constructor類
    成員方法    --> Method類

 

 

    有了字節碼文件對象才能得到類中全部的信息,咱們在使用反射獲取信息時,也要考慮使用上面哪一種方式獲取字節碼對象合理,視不一樣狀況而定。下面介紹Class類的功能。

 

2、具體操做實例

實例一:

 

package com.my.fanshe;
 2 
 3 public class Person {
 4     private String name;
 5     int age;
 6     public String address;
 7 
 8     public Person() {
 9     }
10 
11     private Person(String name) {
12         this.name = name;
13     }
14 
15     Person(String name, int age) {
16         this.name = name;
17         this.age = age;
18     }
19 
20     public Person(String name, int age, String address) {
21         this.name = name;
22         this.age = age;
23         this.address = address;
24     }
25 
26     public void show() {
27         System.out.println("show");
28     }
29 
30     public void method(String s) {
31         System.out.println("method " + s);
32     }
33 
34     public String getString(String s, int i) {
35         return s + "---" + i;
36     }
37 
38     private void function() {
39         System.out.println("function");
40     }
41 
42     @Override
43     public String toString() {
44         return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
45     }
46 
47 }

  

 
 
package com.my.fanshe;

/* 4 * 反射:就是經過class文件對象,去使用該文件中的成員變量,構造方法,成員方法。 5 * 6 * Person p = new Person(); 7 * p.使用; 8 * 9 * 要想這樣使用,首先你必須獲得class文件對象,其實也就是獲得Class類的對象。 10 * .class文件 --> Class類 11 * 成員變量 --> Field類 12 * 構造方法 --> Constructor類 13 * 成員方法 --> Method類 14 * 15 * 獲取class文件對象的方式: 16 * A:Object類的getClass()方法 17 * B:數據類型的靜態屬性class(任意數據類型都具有一個class靜態屬性) 18 * C:Class類中的靜態方法(將類名做爲字符串傳遞給Class類中的靜態方法forName) 19 * public static Class forName(String className) 20 * 21 * 通常咱們到底使用誰呢? 22 * A:本身玩 任選一種,第二種比較方便 23 * B:開發時 第三種 24 * 爲何呢?由於第三種是一個字符串,而不是一個具體的類名。這樣咱們就能夠把這樣的字符串配置到配置文件中。 25 */ 26 public class ReflectDemo { 27 public static void main(String[] args) throws ClassNotFoundException { 28 // 方式A 29 Person p = new Person(); 30 Class c = p.getClass(); 31 32 Person p2 = new Person(); 33 Class c2 = p2.getClass(); 34 35 System.out.println(p == p2); // false 36 System.out.println(c == c2); // true 37 38 // 方式B 39 Class c3 = Person.class; 40 // int.class; 41 // String.class; 42 System.out.println(c == c3); // true 43 44 // 方式C 45 // ClassNotFoundException 須要類的全路徑(帶包名的路徑) 46 Class c4 = Class.forName("com.my.fanshe.Person"); 47 System.out.println(c == c4); // true 48 } 49 }

 

  

 

 

實例二:

package com.my.fanshe;

public interface JieKou {
    public void print();
}

  

package com.my.fanshe;

public class A implements JieKou {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }


    @Override
    public void print() {

        System.out.println("實現A接口方法");
    }

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

    public int getAge() {
        return age;
    }
}

  

package com.my.fanshe;

public class B implements JieKou {

    @Override
    public void print() {
        System.out.println("實現B接口方法");
    }
}

  

package com.my.fanshe;

public class C {
    public void show(){
        System.out.println("實現C方法");
    }
}

  

package com.my.fanshe;

public class FansheDemo {
    public static void main(String[] args) throws Exception{
        //方法一:經過對象得到Class類類型(靜態加載)
        A a = new A("張三",20);
        Class c = a.getClass();
        System.out.println("Class類類型爲:"+c);
        //方法二:經過類名得到Class類類型
        Class c1 = B.class;
        System.out.println("Class類類型爲:"+c1);
        //方法三:經過動態加載
        Class c2 = Class.forName("com.my.fanshe.C");
        System.out.println("Class類類型爲:"+c2);
    }
}

  

運行結果:

Class類類型爲:class com.my.fanshe.A
Class類類型爲:class com.my.fanshe.B
Class類類型爲:class com.my.fanshe.C

  

二、獲取類的信息

 

package com.my.fanshe;

public interface JieKou {
    public void print();
}

  

package com.my.fanshe;

public class A implements JieKou {
    public String name;
    private int age;

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

    public String getName() {
        return name;
    }


    @Override
    public void print() {

        System.out.println("實現A接口方法");
    }

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

    public int getAge() {
        return age;
    }

}

  

package com.my.fanshe;

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

public class FansheDemo {
    public static void main(String[] args) throws Exception{
        //動態加載類,Class是表示當前類的類類型
        //獲取字節碼文件
        Class c = Class.forName("com.my.fanshe.A");
        // 經過Class類類型得到類的實例
        Constructor constructor = c.getConstructor(String.class,int.class);
        A a = (A)constructor.newInstance("張三",20);
        //獲取公共屬性的成員變量
        Field f = c.getField("name");
        System.out.println(f.get(a));
        //獲取私有屬性的成員變量
        Field f1 = c.getDeclaredField("age");
        System.out.println(f1.getType()+" "+f1.getName());
        //打開權限
        f1.setAccessible(true);
        System.out.println(f1.get(a));
        //獲取方法並執行
        Method method = c.getMethod("getName");
        System.out.println(method.invoke(a));

        Method method1 = c.getMethod("getAge");
        System.out.println(method1.invoke(a));

        Method method2 = c.getMethod("print");
        method2.invoke(a);

        Method method3 = c.getMethod("toString");
        System.out.println(method3.invoke(a));




    }
}

  

運行結果:

張三
int age
20
張三
20
實現A接口方法
A{name='張三', age=20}

  

三、反射思惟導圖

 

三.反射相關理解的知識

1:反射(理解)
    (1)類的加載
        當程序要使用某個類時,若是該類還未被加載到內存中,則系統會經過加載鏈接初始化三步來實現對這個類進行初始化。
        
        加載 
            就是指將class文件讀入內存,併爲之建立一個Class對象。
            任何類被使用時系統都會創建一個Class對象。
        鏈接
            驗證:是否有正確的內部結構,並和其餘類協調一致。
            準備:負責爲類的靜態成員分配內存,並設置默認初始化值。
            解析:將類的二進制數據中的符號引用替換爲直接引用初始化 
            就是咱們之前講過的初始化步驟。
        
        注意:Object類的方法:
                public final Class getClass()    返回對象的字節碼文件對象
             Class類的方法:
                public String getName()    以 String 的形式返回此 Class 對象所表示的實體名稱。(實體包括:類、接口、數組名、基本類型或 void)
                    即:能夠經過Class類中的一個方法,獲取對象的真實類的全名稱 (2)類的初始化時機 1.建立類的實例時。
        2.訪問類的靜態變量,或者爲靜態變量賦值時。
        3.調用類的靜態方法時。
        4.使用反射方式來強制建立某個類或接口對應的java.lang.Class對象時。
        5.初始化某個類的子類時。
        6.直接使用java.exe命令來運行某個主類時。
 (3)類加載器
        負責將.class文件加載到內存中,併爲之生成對應的Class對象。
        雖然咱們不須要關心類加載機制,可是瞭解這個機制咱們就能更好的理解程序的運行。
    (4)類加載器的組成
            Bootstrap ClassLoader     類加載器
            Extension ClassLoader     擴展類加載器
            Sysetm ClassLoader        系統類加載器
        
        Bootstrap ClassLoader     類加載器
            也被稱爲引導類加載器,負責Java核心類的加載。
            好比System類,String類等。在JDK中JRE的lib目錄下rt.jar文件中(JDK8之前版本中的位置,JDK9/10位置變化了)。
            
        Extension ClassLoader     擴展類加載器
            負責JRE的擴展目錄中jar包的加載。
            在JDK中JRE的lib目錄下ext目錄。
            
        Sysetm ClassLoader        系統類加載器
            負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類路徑。
            通常咱們本身寫的類經過系統類加載器來加載的。

如若對你有用,記得推薦,如如有誤,歡迎指正!

本人原創,轉載請說明出處https://www.cnblogs.com/zyx110/

相關文章
相關標籤/搜索