java基礎之反射

java基礎之反射數組

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

1.1 類的加載

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

1.2 類的鏈接

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

  • 驗證:確保被加載類的正確性
  • 準備:負責爲類的靜態成員分配內存,並設置默認初始化值
  • 解析:將類中的符號引用替換爲直接引用

1.3 類的初始化

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

  • 類加載的時機
  1. 建立類的實例的時候
  2. 訪問類的靜態變量的時候
  3. 調用類的靜態方法的時候
  4. 使用反射方式來強制建立某個類或接口對應的java.lang.Class對象
  5. 初始化某個類的子類的時候
  6. 直接使用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。那麼咱們即可以更靈活的編寫代碼,代碼能夠在運行時裝配,無需在組件之間進行源代碼連接,下降代碼的耦合度;還有動態代理的實現等等。code

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 反射的基本實現

  實驗類

//因篇幅緣由,實驗代碼未展現set,get等等經常使用方法
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]
相關文章
相關標籤/搜索