Java編程中基礎反射詳細解析

1. 類的加載、鏈接和初始化

1.1 類的加載

       當程序主動使用某個類時,若是該類尚未被加載到內存中,則系統會經過加載、鏈接、初始化這三個步驟對該類進行初始化。有時會把這一整個流程統稱爲類加載或類初始化。
      類加載指的是將類的class文件讀入內存中,併爲之建立一個 java.lang.Class 對象,也就是說程序使用任何類的時候,都會爲其建立一個class對象。java

1.2 類的鏈接

      類被加載以後,系統會爲之生成一個Class對象,接着會進入鏈接階段,鏈接階段負責把類的二進制數據合併到JRE中。類的鏈接又分爲下面三個階段:數組

驗證:確保被加載類的正確性ide

準備:負責爲類的靜態成員分配內存,並設置默認初始化值spa

解析:將類中的符號引用替換爲直接引用.net

1.3 類的初始化

在java中對類變量指定初始值得方法有兩種:1. 聲明類變量時指定初始值;2. 使用靜態初始化塊爲類變量指定初始值。翻譯

類加載的時機設計

建立類的實例的時候代理

訪問類的靜態變量的時候對象

調用類的靜態方法的時候blog

使用反射方式來強制建立某個類或接口對應的java.lang.Class對象

初始化某個類的子類的時候

直接使用java.exe命令來運行某個主類

1.4 類加載器

類加載器負責將.class文件加載到內存中,併爲之生成對應的Class對象。類加載器負責加載全部的類,系統爲全部加載到內存中的類生成一個java.lang.Class 的實例。

類加載器的組成:
Bootstrap ClassLoader 根類加載器 : 也被稱爲引導類加載器,負責Java核心類的加載,好比System類,在JDK中JRE的lib目錄下rt.jar文件中的類
Extension ClassLoader 擴展類加載器 : 負責JRE的擴展目錄中jar包的加載,在JDK中JRE的lib目錄下ext目錄
System ClassLoader 系統類加載器 : 負責在JVM啓動時加載來自java命令的class文件,以及classpath環境變量所指定的jar包和類路徑,主要是咱們開發者本身寫的類

2. 反射

Java反射就是在運行狀態中,對於任意一個類,都可以知道這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意方法和屬性;而且能改變它的屬性。
反射機制容許程序在運行時取得任何一個已知名稱的class的內部信息,包括包括其modifiers(修飾符),fields(屬性),methods(方法)等,並可於運行時改變fields內容或調用methods。那麼咱們即可以更靈活的編寫代碼,代碼能夠在運行時裝配,無需在組件之間進行源代碼連接,下降代碼的耦合度;還有動態代理的實現等等。

2.1 反射基本信息

java程序中許多對象在運行時會出現兩種類型:運行時類型編譯時類型,例如Person p = new Student();這句代碼中p在編譯時類型爲Person,運行時類型爲Student。程序須要在運行時發現對象和類的真實信心。而經過使用反射程序就能判斷出該對象和類屬於哪些類。

2.1.1 Class對象

Java文件被編譯後,生成了.class文件,JVM此時就要去解讀.class文件。當程序主動去使用某個類時,JVM會經過前面提到的三個步驟:加載、鏈接和初始化三個步驟對類進行初始化。被編譯後的Java文件.class也被JVM解析爲一個對象,這個對象就是java.lang.Class。這樣當程序在運行時,每一個java文件就最終變成了Class類對象的一個實例。咱們經過Java的反射機制應用到這個實例,就能夠去得到甚至去添加改變這個類的屬性和動做,使得這個類成爲一個動態的類。
Class類的概念儘管很抽象,可是無疑,它是反射機制的起源,是Java語言中一個精巧美妙地設計。
下面是翻譯後的中文文檔的描述:
Class類的實例表示正在運行的Java應用程序的類和接口。枚舉是一種類,註釋(註解)是一種接口。每一個數組屬於被映射爲Class對象的一個類,全部具備相同元素類型和維數的數組都共享該Class對象。基本的Java類型(boolean、byte、char、short、int、long、float 和 double)和關鍵字 void 也表示爲 Class 對象。Class沒有公用構造方法。Class對象是在加載類時由JVM以及經過調用類加載器中的defineClass方法自動構造的。

2.1.2 Java反射機制的類庫支持

在深刻到反射機制以前,先探析一下反射機制的定義和應用。反射機制定義:Java反射機制是在運行狀態時,對於任意一個類,都可以直到這個類的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性。
在Java中,Class類和java.lang.reflect類庫一塊兒構成了對Java反射機制的支持。其中最常使用到的類是ConstructorFieldMethod,而這三個類都繼承了一個接口java.lang.reflect.Member

2.2 反射的基本實現

實驗類

public class Cat {
   
private String name = "";
   
private String master = "";
   
private int age = 0;

   
public Cat() {  }
   
private Cat(String name, String master) {   }
   
public Cat(String name, String master, int age) {   }  
   
   
public void eat() {
        System.out.println(
"小魚乾真好吃~");
    }

   
private void play() {
        System.out.println(
"快來陪我玩~");
    }

   
@Override
    public String toString() {
       
return "Cat [name=" + name + ", master=" + master + ", age=" + age + "]";
    }
}

2.2.1 獲取Class對象

在java中獲取Class對象有三種方法:
經過類名獲取:Class c1 = Student.class; 調用某各種的class屬性來獲取Class對象。
經過對象獲取:Class c2 = stu.getClass(); 經過getClass方法,該方法是Object類下的一個方法。
經過全類名獲取:Class c3 = Class.forName("全限定類名"); 會有一個ClassNotFoundException異常

public static void main(String[] args) throws Exception {
    Cat c =
new Cat();
   
    Class cat1 = Class.forName(
"algorithms.sort.Cat");
    Class cat2 = Cat.class;
    Class cat3 = c.getClass();

    System.out.println(cat1 == cat2);
    System.out.println(cat2 == cat3);
}
//輸出結果
true
true

2.2.2 獲取構造器並建立對象

getConstructors():返回了表示此類公共構造方法的Constructor對象數組。
getDeclaredConstructors():這個方法返回Constructor對象的全部構造方法。

獲取構造器

public static void main(String[] args) throws Exception {
    Class cat = Cat.class;
   
//獲取全部公共構造方法
    Constructor<?> cons[] = cat.getConstructors();
   
for (Constructor<?> con : cons) {
        System.out.println(
"getConstructors-----------" + con);
    }
    System.out.println(
"**************************************");
   
//獲取全部構造方法
    Constructor<?> cons2[] = cat.getDeclaredConstructors();
   
for (Constructor<?> con2 : cons2) {
        System.out.println(
"getDeclaredConstructors---" + con2);
    }
}

//輸出結果
getConstructors-----------public algorithms.sort.Cat(java.lang.String,java.lang.String,int)
getConstructors-----------
public algorithms.sort.Cat()
**************************************
getDeclaredConstructors---
public algorithms.sort.Cat(java.lang.String,java.lang.String,int)
getDeclaredConstructors---
private algorithms.sort.Cat(java.lang.String,java.lang.String)
getDeclaredConstructors---
public algorithms.sort.Cat()

建立對象

public static void main(String[] args) throws Exception {
    Class cat = Cat.class;
   
//使用公共構造器實例化對象
    Constructor<?> cons1 = cat.getConstructor();
   
//使用私有構造器實例化對象
    Constructor<?> cons2 = cat.getDeclaredConstructor(String.class,String.class);
    Cat cat1 = (Cat)cons1.newInstance();
   
//私有的構造方法反射後要打開權限才能進行相應操做
    cons2.setAccessible(true);
    Cat cat2 = (Cat)cons2.newInstance(
"tom","denny");
    System.out.println(cat1);
    System.out.println(cat2);
}

//輸出結果
Cat [name=, master=, age=0]
Cat [name=tom, master=denny, age=
0]

在建立對象的過程當中,值得注意的是若是反射的構造方法是私有的,那麼要打開訪問權限才能進行對象的實例化;也就是使用cons2.setAccessible(true);語句的緣由。

2.2.3 獲取成員變量和成員方法

獲取成員變量

public static void main(String[] args) throws Exception {
    Class cat = Cat.class;
   
//獲取構造器
    Constructor<?> cons = cat.getConstructor(String.class,String.class,int.class);
   
//實例化對象
    Cat cat1 = (Cat)cons.newInstance("tom","denny",5);
    System.out.println(cat1);
    System.out.println(
"****************");
   
    Field fields = cat.getDeclaredField(
"name");
   
//打開訪問權限限制
    fields.setAccessible(true);
    fields.set(cat1,
"jack");
    System.out.println(cat1);
}

//輸出結果
Cat [name=tom, master=denny, age=5]
****************
Cat [name=jack, master=denny, age=
5]

獲取成員方法

public static void main(String[] args) throws Exception {
    Class cat = Cat.class;
   
//獲取構造器
    Constructor<?> cons = cat.getConstructor(String.class, String.class, int.class);
   
//實例化對象
    Cat cat1 = (Cat) cons.newInstance("tom", "denny", 5);
    System.out.println(cat1);
    System.out.println(
"****************");

   
//獲取私有和公共成員方法
    Method method1 = cat.getDeclaredMethod("setName", String.class);
    Method method2 = cat.getDeclaredMethod(
"eat");
    method1.setAccessible(
true);
    method1.invoke(cat1,
"petter");
    System.out.println(cat1);
    method2.invoke(cat1);
}

//輸出結果
Cat [name=tom, master=denny, age=5]
****************
Cat [name=petter, master=denny, age=
5]
小魚乾真好吃~

2.2.4 反射越過泛型檢查

public static void main(String[] args) throws Exception  {
   
//泛型只在編譯期進行檢查,在運行期會被擦除
    ArrayList<Integer> list = new ArrayList<>();
    list.add(
111);
    list.add(
222);
   
//拿到字節碼文件,字節碼文件屬於運行期
    Class cla = Class.forName("java.util.ArrayList");
    Method meth = cla.getMethod(
"add", Object.class);
    meth.invoke(list,
"abc");
    System.out.println(list);
}

//輸出結果
[111, 222, abc]

相關文章
相關標籤/搜索