黑馬程序員——Java基礎---反射Class類、Constructor類、Field類

黑馬程序員——Java基礎---反射Class類、Constructor類、Field類

------<a href="http://www.itheima.com" target="blank">Java培訓、Android培訓、iOS培訓、.Net培訓</a>、期待與您交流! ------- 

                        反射的應用場景

一、概述

反射技術:

        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();

 

複製代碼
 1 //Person類
 2 package cn.itheima;//將網址域名倒過來,當做包名 一般不會重複
 3 
 4 public class Person {
 5     private String name;
 6     public int age;
 7     public Person(){
 8         System.out.println("Person is run");
 9     }
10     public Person(String name,int age)//
11     {
12         this.age=age;
13         this.name=name;
14     }
15     
16     public String toString()//複寫輸出方法
17     {
18         return name+":"+age;
19     }
20 }
21 //示例
22 package cn.itheima;
23 
24 public class CreateClassDemo {
25     public static void main(String[] args) throws Exception
26     {
27         createPersonClass();
28     }
29     //通過Class對象創建類實例方法
30     public static void createPersonClass() throws Exception
31     {
32         //獲取Person類的Class對象
33         String className="cn.itheima.Person";
34         Class clazz=Class.forName(className);
35         //通過newInstance方法獲取類的無參構造函數實例
36         Person p=(Person)clazz.newInstance();
37     }
38 }
複製代碼

三、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類只能利用無參構造函數創建類實例對象。

示例:

複製代碼
 1 package cn.itheima;
 2 
 3 public class CreateClassDemo 
 4 {
 5     public static void main(String[] args) throws Exception
 6     {
 7         createPersonClass_2();
 8     }
 9 
10 public static void createPersonClass_2() throws Exception//通過Constructor對象來創建類實例方法
11     {
12         //獲取Person類的Class對象
13         String className="cn.itheima.Person";
14         Class clazz=Class.forName(className);
15         //Class clazz=Person.class;
16             
17         //獲取指定構造函數的類實例
18         Constructor con=clazz.getConstructor(String.class,int.class);
19         Person p=(Person) con.newInstance("lisi",30);
20         System.out.println(p.toString());
21     }
22 }
複製代碼

四、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表示的字段的值。

示例:

複製代碼
 1 public static void getPersonField() throws Exception//獲取Person對象的成員變量
 2 {    
 3     //如果想要給該變量賦值,必須先要有對象。
 4     Class clazz=Class.forName("cn.itheima.Person");
 5     Person p=(Person)clazz.newInstance();
 6         
 7     //獲取所以的成員變量
 8     Field[] fs=clazz.getFields();
 9     for(Field f:fs)
10     {
11         System.out.println(f);
12     }
13         
14     //獲取指定的成員變量
15     Field fage=clazz.getField("age");
16     Field fname=clazz.getDeclaredField("name");
17         
18     //顯示改變後的值
19     fage.set(p, 20);
20     System.out.println(fage.get(p));
21         
22     //暴力訪問私有變量
23     fname.setAccessible(true);
24     fname.set(p, "zhangsan");
25     System.out.println(fname.get(p));
26 }
複製代碼

四、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表示的字段的值。

示例:

複製代碼
 1 //獲取Person對象的成員變量
 2 public static void getPersonField() throws Exception{    
 3 //如果想要給該變量賦值,必須先要有對象。
 4     Class clazz=Class.forName("cn.itheima.Person");
 5     Person p=(Person)clazz.newInstance();
 6         
 7     //獲取所以的成員變量
 8     Field[] fs=clazz.getFields();
 9     for(Field f:fs){
10         System.out.println(f);
11     }
12         
13     //獲取指定的成員變量
14     Field fage=clazz.getField("age");
15     Field fname=clazz.getDeclaredField("name");
16         
17     //顯示改變後的值
18     fage.set(p, 20);
19     System.out.println(fage.get(p));
20         

21     //暴力訪問私有變量
22     fname.setAccessible(true);
23     fname.set(p, "zhangsan");
24     System.out.println(fname.get(p));
25 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章