反射Class類,Constructor類,Field類

本文章轉載自 https://www.cnblogs.com/ktlshy/p/4716838.html

一、概述

反射技術:

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

    簡單一句話:反射技術可以對類進行解剖。因此反射技術很強大!

二、應用場景

以實際來說,主板上的要預留顯卡、聲卡等的插槽,提高了二者間的獨立性。如果我們要更新聲卡,則不需要將主板扔掉,從而提高了複用性,而程序中常用的作法,會提供一個配置文件,來供以後實現此程序的類來擴展功能。對外提供配置文件,讓後期出現的子類直接將類名字配置到配置文件中即可。該應用程序直接讀取配置文件中的內容。並查找和給定名稱相同的類文件。進行如下操作:

    1)加載這個類。

    2)創建該類的對象。

    3)調用該類中的內容。

應用程序使用的類不確定時,可以通過提供配置文件,讓使用者將具體的子類存儲到配置文件中。然後該程序通過反射技術,對指定的類進行內容的獲取。

    好處:反射技術大大提高了程序的擴展性。

反射涉及的對象

一、概述

反射就是把Java類中的各種成分映射成相應的java類。

例如,一個Java類中用一個Class類的對象來表示,一個類中的組成部分:成員變量,方法,構造方法,包等等信息也用一個個的Java類來表示。就像汽車是一個類,汽車中的發動機,變速箱等等也是一個個的類。表示java類的Class類顯然要提供一系列的方法,來獲得其中的變量,方法,構造方法,修飾符,包等信息,這些信息就是用相應類的實例對象來表示,它們是Field、Method、Contructor、Package等等。

一個類中的每個成員都可以用相應的反射API類的一個實例對象來表示,通過調用Class類的方法可以得到這些實例對象後,得到這些實例對象後有什麼用呢?怎麼用呢?這正是學習和應用反射的要點。

二、反射的基石——Class類

1、所有的類文件都有共同屬性,所以可以向上抽取,把這些共性內容封裝成一個類,這個類就叫Class(描述字節碼文件的對象)。

Class類中就包含屬性有field(字段)、method(方法)、construction(構造函數)。

而field中有修飾符、類型、變量名等複雜的描述內容,因此也可以將字段封裝稱爲一個對象。用來獲取類中field的內容,這個對象的描述叫Field。同理方法和構造函數也被封裝成對象Method、Constructor。要想對一個類進行內容的獲取,必須要先獲取該字節碼文件的對象。該對象是Class類型。

Class類描述的信息:類的名字,類的訪問屬性,類所屬於的包名,字段名稱的列表,方法名稱的列表等。每一個字節碼就是class的實例對象。如:classcls=Data.class;

小知識:什麼叫字節碼?

當源程序中用到類時,首先要從硬盤把這個類的那些二進制代碼,一個類編譯成class放在硬盤上以後,就是一些二進制代碼,要把這些二進制代碼加載到內存中裏面來,再用這些字節碼去複製出一個一個對象來。

2、Class和class的區別

1)class:Java中的類用於描述一類事物的共性,該類事物有什麼屬性,沒有什麼屬性,至於這個屬性的值是什麼,則由此類的實例對象確定,不同的實例對象有不同的屬性值。

2)Class:指的是Java程序中的各個Java類是屬於同一類事物,都是Java程序的類,這些類稱爲Class。例如人對應的是Person類,Java類對應的就是Class。Class是Java程序中各個Java類的總稱;它是反射的基石,通過Class類來使用反射。

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

    加載XX.class文件進內存時就被封裝成了對象,該對象就是字節碼文件對象。如何獲取Class對象呢?

方式一:

    通過對象的getClass方法進行獲取。

    如:Class clazz=new Person().getClass();//Person是一個類名

    麻煩之處:每次都需要具體的類和該類的對象,以及調用getClass方法。

方式二:

    任何數據類型都具備着一個靜態的屬性class,這個屬性直接獲取到該類型的對應Class對象。

    如:Class clazz=Person.class;//Person是一個類名

    比第一種較爲簡單,不用創建對象,不用調用getClass方法,但是還是要使用具體的類,和該類中的一個靜態屬性class完成。

方式三:

    這種方式較爲簡單,只要知道類的名稱即可。不需要使用該類,也不需要去調用具體的屬性和行爲。就可以獲取到Class對象了。

    如:Class clazz=Class.forName("包名.Person");//Person是一個類名

    這種方式僅知道類名就可以獲取到該類字節碼對象的方式,更有利於擴展。

注:

    1、九個預定義的Class:

            1)包括八種基本類型(byte、short、int、long、float、double、char、boolean)的字節碼
            對象和一種返回值爲void類型的void.class。

            2)Integer.TYPE是Integer類的一個常量,它代表此包裝類型包裝的基本
            類型的字節碼,所以和int.class是相等的。基本數據類型的字節碼都可以用與之對應的包裝類中的TYPE常量表示

    2、只要是在源程序中出現的類型都有各自的Class實例對象,如int[].class。數
    組類型的Class實例對象,可以用Class.isArray()方法判斷是否爲數組類型的。

4、Class類中的方法

    static Class forName(String className)

    返回與給定字符串名的類或接口的相關聯的Class對象。

    Class getClass()

    返回的是Object運行時的類,即返回Class對象即字節碼對象

    Constructor getConstructor()

    返回Constructor對象,它反映此Class對象所表示的類的指定公共構造方法。

    Field getField(String name)

    返回一個Field對象,它表示此Class對象所代表的類或接口的指定公共成員字段。

    Field[] getFields()

    返回包含某些Field對象的數組,表示所代表類中的成員字段。

    Method getMethod(String name,Class… parameterTypes)

    返回一個Method對象,它表示的是此Class對象所代表的類的指定公共成員方法。

    Method[] getMehtods()

    返回一個包含某些Method對象的數組,是所代表的的類中的公共成員方法。

    String getName()

    以String形式返回此Class對象所表示的實體名稱。

    String getSuperclass()

    返回此Class所表示的類的超類的名稱

    boolean isArray()

    判定此Class對象是否表示一個數組

    boolean isPrimitive()

    判斷指定的Class對象是否是一個基本類型。

    T newInstance()

    創建此Class對象所表示的類的一個新實例。

5、通過Class對象獲取類實例

    通過查看API我們知道,Class類是沒有構造方法的, 因此只能通過方法獲取類實例對象。之前我們用的已知類,創建對象的做法:

    1)查找並加載XX.class文件進內存,並將該文件封裝成Class對象。

    2)再依據Class對象創建該類具體的實例。

    3)調用構造函數對對象進行初始化。

         如:Person p=new Person();

現在用Class對象來獲取類實例對象的做法:

    1)查找並加載指定名字的字節碼文件進內存,並被封裝成Class對象。

    2)通過Class對象的newInstance方法創建該Class對應的類實例。

    3)調用newInstance()方法會去使用該類的空參數構造函數進行初始化。

         如:

                 String className="包名.Person";

                 Class clazz=Class.forName(className);

                 Object obj=clazz.newInstance();
//Person類
      package cn.itheima;//將網址域名倒過來,當做包名 一般不會重複
      
      public class Person {
          private String name;
          public int age;
          public Person(){
              System.out.println("Person is run");
          }
         public Person(String name,int age)//
         {
             this.age=age;
             this.name=name;
         }
         
         public String toString()//複寫輸出方法
         {
             return name+":"+age;
         }
     }
     //示例
     package cn.itheima;
     
     public class CreateClassDemo {
         public static void main(String[] args) throws Exception
         {
             createPersonClass();
         }
         //通過Class對象創建類實例方法
         public static void createPersonClass() throws Exception
         {
             //獲取Person類的Class對象
             String className="cn.itheima.Person";
             Class clazz=Class.forName(className);
             //通過newInstance方法獲取類的無參構造函數實例
             Person p=(Person)clazz.newInstance();
    }
 }

三、Constructor類

1、概述

如果指定的類中沒有空參數的構造函數,或者要創建的類對象需要通過指定的構造函數進行初始化。這時怎麼辦呢?這時就不能使用Class類中的newInstance方法了。既然要通過指定的構造函數進行對象的初始化。就必須先獲取這個構造函數——Constructor。Constructor代表某個類的構造方法。

2、獲取構造方法:

    1)得到這個類的所有構造方法:如得到上面示例中Person類的所有構造方法

          Constructor[] cons = Class.forName(“cn.itheima.Person”).getConstructors();

    2)獲取某一個構造方法:

          Constructor con=Person.class.getConstructor(String.class,int.class);

3、創建實例對象:

    1)通常方式:Person p = new Person(“lisi”,30);

     2)反射方式:Person p= (Person)con.newInstance(“lisi”,30);

注:

    1、創建實例時newInstance方法中的參數列表必須與獲取Constructor的方法getConstructor方法中的參數列表一致。

     2、newInstance():構造出一個實例對象,每調用一次就構造一個對象。

     3、利用Constructor類來創建類實例的好處是可以指定構造函數,而Class類只能利用無參構造函數創建類實例對象。

示例:

 package cn.itheima;
 
 public class CreateClassDemo 
  {
      public static void main(String[] args) throws Exception
      {
          createPersonClass_2();
      }
  
 public static void createPersonClass_2() throws Exception//通過Constructor對象來創建類實例方法
     {
         //獲取Person類的Class對象
         String className="cn.itheima.Person";
         Class clazz=Class.forName(className);
         //Class clazz=Person.class;
             
         //獲取指定構造函數的類實例
         Constructor con=clazz.getConstructor(String.class,int.class);
         Person p=(Person) con.newInstance("lisi",30);
         System.out.println(p.toString());
     }
 }

四、Field類

1、Field類代表某個類中一個成員變量

2、方法

   Field getField(String s);//只能獲取公有和父類中公有

    Field getDeclaredField(String s);//獲取該類中任意成員變量,包括私有

    setAccessible(ture);

    //如果是私有字段,要先將該私有字段進行取消權限檢查的能力。也稱暴力訪問。

    set(Object obj, Object value);//將指定對象變量上此Field對象表示的字段設置爲指定的新值。

    Object get(Object obj);//返回指定對象上Field表示的字段的值。

示例:

public static void getPersonField() throws Exception//獲取Person對象的成員變量
  {    
      //如果想要給該變量賦值,必須先要有對象。
      Class clazz=Class.forName("cn.itheima.Person");
      Person p=(Person)clazz.newInstance();
          
      //獲取所以的成員變量
      Field[] fs=clazz.getFields();
      for(Field f:fs)
     {
         System.out.println(f);
     }
         
    //獲取指定的成員變量
     Field fage=clazz.getField("age");
     Field fname=clazz.getDeclaredField("name");
         
     //顯示改變後的值
    fage.set(p, 20);
     System.out.println(fage.get(p));
         
     //暴力訪問私有變量
     fname.setAccessible(true);
     fname.set(p, "zhangsan");
     System.out.println(fname.get(p));
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章