反射

 

第一講:透徹分析反射的基礎_Class類java

一,反射的基石——Class類的瞭解:web

    1. 此類的由來:Java程序中java類屬於同一類事物,描述這一類同一事物的類就是Class類。
    2. 類的定義:public final class Class<T>extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement    ==在包java.lang中==
    3. Class類的做用:經過Class類能夠獲得一個類的方方面面的信息,例如:父類,方法,成員,實現的接口。
    4. 當使用一個類的時候,java虛擬機將類的字節碼文件加載到內存中,在內存中的字節碼文件,便是這個類的Class對象的內容。

二,獲得字節碼的三種方式:算法

    1. 類名.class          例:System.class                 ==編譯階段識別==
    2. 對象.getClass()  例: new Date().getClass();       ====此方法在Object類中定義===
    3. Class.forName() 例:Class.forName("java.util.Date");           ===注意:此處必須明確指出類名,路徑名===

三,八個基本數據類型都有對應的Class對象,而且與其包裝類所對應的Class對象不一樣。void 也有對應的Class對象。設計模式

四,代碼練習:數組

 

 1 public class ReflectTest {
 2                 public static void main(String[] args) throws ClassNotFoundException {
 3                     
 4                             //建立一個String對象
 5                             String str = "abc";
 6                             
 7                             //經過三個方式得到String的Class對象。
 8                             Class c1 = str.getClass();
 9                             Class c2 = String.class;
10                             Class c3 = Class.forName("java.lang.String");
11                             
12                             //判斷是不是同一份字節碼。
13                             System.out.println(c1==c2);
14                             System.out.println(c2==c3);
15                             
16                             //是不是基本類型
17                             System.out.println(c1.isPrimitive());
18                             
19                             //基本類型和包裝類型的字節碼文件是不一樣的
20                             System.out.println(int.class==Integer.class);
21                             
22                             //能夠經過包裝類型得到其包裝的基本類型的字節碼
23                             System.out.println(int.class==Integer.TYPE);
24                             
25                             //判斷數字是不是一個原始類型
26                             System.out.println(int[].class.isPrimitive());
27                             
28                             //判斷一個字節碼是不是數組類型
29                             System.out.println(int[].class.isArray());
30                             
31                 }
32 }

 

第二講:理解反射的概念eclipse

 

一,反射:把java類中的各類成分映射成響應的java類。ide

    1. 概念:Java反射機制是在運行狀態中,對於任意一個類,都可以知道這個類中的全部屬性和方法;對於任意一個對象,都可以調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
    2. 做用:反射技術能夠對類進行解剖。大大提升了程序的擴展性。

二,Class類中獲取信息的方法:工具

    1. public ClassLoader  getClassLoader()    返回該類的類加載器。
    2. public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException,   SecurityException   返回一個 Constructor 對象,它反映此 Class 對象所表示的類的指定公共構造方法。
    3. public Constructor <?>[] getConstructors() throws SecurityException    返回一個包含某些 Constructor 對象的數組,這些對象反映此 Class
      對象所表示的類的全部公共構造方法。
    4. public Constructor<?>[] getDeclaredConstructors() throws SecurityException  返回 Constructor 對象的一個數組,這些對象反映此 Class 對象表示的類聲明的全部構造方法。
    5. public Field getField(String name)  throws NoSuchFieldException,   SecurityException    返回一個 Field 對象,它反映此 Class 對象所表示的類或接口的指定公共成員字段。
    6. public Method[] getMethods()  throws SecurityException       返回一個包含某些 Method 對象的數組

第三講:構造方法的反射應用性能

一,Constructor類的瞭解:學習

    1. public final class Constructor<T>extends AccessibleObject  implements GenericDeclaration, Member   Constructor 提供關於類的單個構造方法的信息以及對它的訪問權限。
    2. public Class<T> getDeclaringClass()     返回 Class 對象,該對象表示聲明由此 Constructor 對象表示的構造方法的類。
    3. public int getModifiers()          以整數形式返回此 Constructor 對象所表示構造方法的 Java 語言修飾符。
    4. public String  getName()         以字符串形式返回此構造方法的名稱。它老是與構造方法的聲明類的簡單名稱相同。
    5. public Class <?>[] getParameterTypes()       按照聲明順序返回一組 Class 對象,這些對象表示此 Constructor對象所表示構造方法的形參類型。若是底層構造方法不帶任何參數,則返回一個長度爲 0 的數組。
    6. public boolean isVarArgs()           若是聲明此構造方法能夠帶可變數量的參數,則返回 true;不然返回 false
    7. public T newInstance(Object... initargs)  throws InstantiationException,   IllegalAccessException,  IllegalArgumentException,  InvocationTargetException         使用此 Constructor 對象表示的構造方法來建立該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。

二,明確程序的,編譯時和運行時:

    1. 編譯時:在程序的編譯期間起做用,簡單的做一些翻譯工做。檢查有沒有粗心寫錯關鍵字,詞法分析,語法分析之類的過程。
    2. 運行時:就是代碼跑起來了.被裝載到內存中去了。在內存中作些操做,作些判斷。

三,建立一個對象的三種方式:

    1. 經過new 關鍵字和構造方法進行實例化。例:  String s = new String("abc");
    2. 經過Class.newInstance ,進行實例化。此方法調用類中的無參構造方法。        例:  String s = String.class.newInstance();        ==注:這種方式只會使用無參構造方法===
    3. 經過類的構造方法的 Constructor 對象的 newInstance(Object... initargs)   方法進行實例化       例: Constructor c = String.class.getConstructro(null);     String s = c.newInstance(null);          ==注:此種方式可使用特定構造方法===

四,代碼練習:

 

 1 import java.lang.reflect.*;
 2 
 3 
 4 public class Reflect_Method {
 5                 public static void main(String[] args) throws Exception {
 6                             
 7                             //獲取一個Class對象的指定構造方法
 8                             Constructor m = String.class.getConstructor(StringBuffer.class);
 9                             
10                             // 經過方法對象,建立類的對象
11                             String s = (String) m.newInstance(new StringBuffer("abc"));
12                             
13                             //經過Class對象建立類對象
14                             String s2 = String.class.newInstance();
15                             
16                             s2="a";
17                             
18                             //直接實例化
19                             String s3 = new String("abc");
20                             
21                             
22                             System.out.println(s.charAt(2));
23                             
24                             System.out.println(s2);
25                             
26                             System.out.println(s3);
27                 }
28 }

 

第四講,第五講:成員變量的反射,成員變量反射的綜合案例

一,Field 類的瞭解:

    1. public final class Fieldextends AccessibleObject  implements Member      Field 提供有關類或接口的單個字段的信息,以及對它的動態訪問權限。反射的字段多是一個類(靜態)字段或實例字段。
    2. 方法:public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException   返回指定對象上此 Field 表示的字段的值。若是該值是一個基本類型值,則自動將其包裝在一個對象中。
    3. 方法:public boolean getBoolean(Object obj) throws IllegalArgumentException, IllegalAccessException    獲取一個靜態或實例 boolean 字段的值。====此方法根據多種數據類型有多個重載方法。====
    4. 方法:public int getModifiers()        以整數形式返回由此 Field 對象表示的字段的 Java 語言修飾符。應該使用 Modifier 類對這些修飾符進行解碼。
    5. 方法:public String getName()         返回此 Field 對象表示的字段的名稱。
    6. 方法:public void setBoolean(Object obj,boolean z) throws IllegalArgumentException, IllegalAccessException    將字段的值設置爲指定對象上的一個 boolean 值。。====此方法根據多種數據類型有多個重載方法。====

=========Field對象表示的不是對象的成員,而是類的成員==========

二,代碼練習:

 

 1 import java.lang.reflect.*;
 2 
 3 
 4 //定義用來反射操做的類
 5 class ReflectPoint {
 6             private int x;
 7             public int y;
 8             
 9             public String str1 = "ball";
10             public String str2 = "basketball";
11             public String str3 = "itcase";
12             
13             public ReflectPoint(int x, int y) {
14                 this.x = x;
15                 this.y = y;
16             }
17             
18             public String toString(){
19                 return str1+"::"+str2+"::"+str3;
20             }
21 }

 

 

 1 import java.util.Arrays;
 2 
 3 
 4 public class Reflect_Array {
 5             public static void main(String[] args) {
 6                         
 7                         //分別定義不一樣維數,不一樣類型的數組
 8                         int[] a1 = new int[]{1,2,3};
 9                         int[] a2 = new int[5];
10                         int[][] a3 = new int[2][4];
11                         String[] a4 = new String[]{"a","b","c"};
12                         
13                         //具備相同維數相同類型的數組是共享一個字節碼對象的
14                         System.out.println(a1.getClass() == a2.getClass());
15                         
16                         //下面這句話編譯期出錯
17                         //System.out.println(a1.getClass() == a4.getClass());
18                         
19                         //獲取數組的字節碼名字即類名
20                         System.out.println(a1.getClass().getName());
21                         System.out.println(a4.getClass().getName());
22                         
23                         //獲取數組的父類名字
24                         System.out.println(a4.getClass().getSuperclass().getName());
25                         
26                         
27                         //數組是Object的子類,向上轉型
28                         Object obj = a1;
29                         Object obj2 = a3;
30                         
31                         //Object[] objs = a1;
32                         
33                         //引用類型數組,能夠向父類型數組轉型
34                         Object[] objs2 = a4;
35                         Object[] objs3 = a3;
36                         
37                         System.out.println(obj.getClass().getName());
38                         
39                         System.out.println(obj2.getClass().getName());
40                         
41                         System.out.println(objs2.getClass().getName());
42                         
43                         //使用Arrays操做數組
44                         System.out.println(Arrays.asList(a4));
45             }
46 }

 

 

第六講:成員方法的反射

一,Method類的瞭解:

    1. public final class Methodextends AccessibleObject implements GenericDeclaration, Member   Method 提供關於類或接口上單獨某個方法(以及如何訪問該方法)的信息。所反映的方法多是類方法或實例方法(包括抽象方法)。
    2. 方法:public int getModifiers()      以整數形式返回此 Method 對象所表示方法的 Java 語言修飾符。應該使用 Modifier 類對修飾符進行解碼。
    3. 方法:public String getName()       以 String 形式返回此 Method 對象表示的方法名稱。
    4. 方法:public Class<?>[] getParameterTypes()      按照聲明順序返回 Class 對象的數組,這些對象描述了此 Method 對象所表示的方法的形參類型。若是底層方法不帶參數,則返回長度爲 0 的數組。
    5. 方法:public Class<?> getReturnType()          返回一個 Class 對象,該對象描述了此 Method 對象所表示的方法的正式返回類型。
    6. 方法:public boolean isBridge()          若是此方法是 bridge 方法,則返回 true;不然,返回 false
    7. 方法:public boolean isVarArgs()       若是將此方法聲明爲帶有可變數量的參數,則返回 true;不然,返回 false
    8. 方法:public Object invoke(Object obj,Object... args) throws IllegalAccessException,IllegalArgumentException, InvocationTargetException   對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。個別參數被自動解包,以便與基本形參相匹配,基本參數和引用參數都隨需服從方法調用轉換。

 二,jdk1.4與jdk1.5 invoke 方法的區別:

                                    jdk1.4  public Object invoke(Object obj,Object[] args)

                                    jdk1.5  public Object invoke(Object obj,Object... args)   ====注:這是jdk1.5的新特性。可變參數====

三,專家設計模式:

        專家模式:誰調用這個數據,就是誰在調用它的專家。

          如人關門:

                  調用者:是門調用關的動做,對象是門,由於門知道如何執行關的動做,經過門軸之類的細節實現。

                  指揮者:是人在指揮門作關的動做,只是給門發出了關的信號,讓門執行。

                  總結:變量使用方法,是方法自己知道如何實現執行的過程,也就是「方法對象」調用方法,才執行了方法的每一個細節的。

四,代碼練習:

 1 import java.lang.reflect.*;
 2 
 3 public class Reflect_Method {
 4             public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 5                 
 6                         //定義Method對象,表示String中的charAt方法
 7                         Method m = String.class.getMethod("charAt", int.class);
 8                         
 9                         //實例化String對象
10                         String str = "abcd";
11                         
12                         //經過Method對象調用方法
13                         System.out.println(m.invoke(str, 1));
14                         
15                         //jdk1.4 方法調用
16                         System.out.println(m.invoke(str, new Object[]{2}));
17             }
18 }

 

 

第七講:對接收數組參數的成員方法進行反射

一,練習目標:

        寫一個程序實現根據用戶提供的類名去動態的調用它的main() 方法。

二,爲何要使用反射:

            在寫源程序時,並不知道使用者傳入的類名是什麼,可是雖然傳入的類名不知道,而知道的是這個類中的方法有main這個方法。因此能夠經過反射的方式,經過使用者傳入的類名(可定義字符串型變量做爲傳入類名的入口,經過這個變量表明類名),內部經過傳入的類名獲取其main方法,而後執行相應的內容。

三,遇到的問題:

         經過反射方式來調用這個main方法時,如何爲invoke方法傳遞參數呢?按jdk1.5的語法,整個數組是一個參數,而按jdk1.4的語法,數組中的每一個元素對應一個      參數,當把一個字符串數組做爲參數傳遞給invoke方法時,javac會到底按照哪一種語法進行處理呢?jdk1.5確定要兼容jdk1.4的語法,會按jdk1.4的語法進行處理,即把數組打散成爲若干個單獨的參數。因此,在給main方法傳遞參數時,不能使用代碼mainMethod.invoke(null,new String[]{「xxx」}),javac只把它看成jdk1.4的語法進行理解,而不把它看成jdk1.5的語法解釋,所以會出現參數類型不對的問題。

四,解決辦法:

      mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

                       mainMethod.invoke(null,(Object)new String[]{"xxx"});

五,代碼練習:

 

 1 import java.lang.reflect.Method;
 2 
 3 
 4 //定義一個測試類
 5 class Test{
 6     public static void main(String[] args){
 7         for(String arg : args){
 8             System.out.println(arg);
 9         }
10     }
11 }
12 
13 
14 //用反射方式根據用戶提供的類名,去執行該類中的main方法。
15 public class PerformedMain{
16 
17     public static void main(String[] args) throws Exception {
18         //普通方式
19         Test.main(new String[]{"123","456","789"});
20         System.out.println("-----------------------------");
21                 
22         //反射方式
23         String className=args[0];
24         Class clazz=Class.forName(className);
25                 
26         Method method_Main=clazz.getMethod("main",String[].class);
27         //方式一:強制轉換爲超類Object,不用拆包
28         method_Main.invoke(null, (Object)new String[]{"123","456","789"});
29         //方式二:將數組打包,編譯器拆包後就是一個String[]類型的總體 
30         method_Main.invoke(null, new Object[]{new String[]{"123","456","789"}});
31     }
32 }

============此示例用eclipse運行時,須要在Run As——>RunConfigurations——>Arguments——>Program arguments中添加要執行的類名,如:Reflect.Test。========

 

第八講:數組與Object的關係及其反射類型

 一,具備相同Class 文件的數組斷定  :具備相同維數和元素類型的數組屬於同一個類型,即具備相同的Class實例對象。數組字節碼的名字:有[和數組對應類型的縮寫,如int[]數組的名稱爲:[I

2、Object[]與String[]沒有父子關係,Object與String有父子關係,因此new Object[]{「aaa」,」bb」}不能強制轉換成new String[]{「aaa」,」bb」}; Object x =「abc」能強制轉換成String x =「abc」。

        =======注:全部數組的父類是Object 因此數組類能夠轉換爲Object對象:Object obj = new int[3];   全部引用類型都是Object 的子類。因此引用類型的數組能夠轉換爲Object的數組: Object  obj[] = new String[3];。==========

三,如何獲得某個數組中的某個元素的類型,

        例:

              int a = new int[3];Object[] obj=new Object[]{」ABC」,1};

        沒法獲得某個數組的具體類型,只能獲得其中某個元素的類型,

        如:

               Obj[0].getClass().getName()獲得的是java.lang.String。

四,Array工具類用於完成對數組的反射操做。

        Array.getLength(Object obj);//獲取數組的長度

        Array.get(Object obj,int x);//獲取數組中的元素

五,代碼練習:

 

 1 import java.util.Arrays;
 2 
 3 
 4 public class Reflect_Array {
 5             public static void main(String[] args) {
 6                         
 7                         //分別定義不一樣維數,不一樣類型的數組
 8                         int[] a1 = new int[]{1,2,3};
 9                         int[] a2 = new int[5];
10                         int[][] a3 = new int[2][4];
11                         String[] a4 = new String[]{"a","b","c"};
12                         
13                         //具備相同維數相同類型的數組是共享一個字節碼對象的
14                         System.out.println(a1.getClass() == a2.getClass());
15                         
16                         //下面這句話編譯期出錯
17                         //System.out.println(a1.getClass() == a4.getClass());
18                         
19                         //獲取數組的字節碼名字即類名
20                         System.out.println(a1.getClass().getName());
21                         System.out.println(a4.getClass().getName());
22                         
23                         //獲取數組的父類名字
24                         System.out.println(a4.getClass().getSuperclass().getName());
25                         
26                         
27                         //數組是Object的子類,向上轉型
28                         Object obj = a1;
29                         Object obj2 = a3;
30                         
31                         //下面這個表達式不成立編譯期出錯
32                         //Object[] objs = a1;
33                         
34                         //引用類型數組,能夠向父類型數組轉型
35                         Object[] objs2 = a4;
36                         Object[] objs3 = a3;
37                         
38                         System.out.println(obj.getClass().getName());
39                         
40                         System.out.println(obj2.getClass().getName());
41                         
42                         System.out.println(objs2.getClass().getName());
43                         
44                         //使用Arrays操做數組
45                         System.out.println(Arrays.asList(a4));
46             }
47 }

 

 

第九講:數組的反射應用

一,學習目標:

      經過Array 類實現對數組的反射操做。

二,Arrays.asList()  方法處理 int[] 和 String[] 的差別:

      由於該方法參數定義爲public static <T> List<T> asList(T... a)  ,由於String[] 能夠轉換爲Object[] 因此處理它是按照jdk1.5新特性,可變參數處理的,它做爲一個參數傳入。而int[] 因爲不能轉換爲Object[] 因此他的處理是按照jdk1.4的規則做爲多個參數進行處理,即把數組展開了。

三,代碼練習:

 

 1 import java.lang.reflect.Array;
 2 
 3 
 4 
 5 public class Reflect_Array2 {
 6                 public static void main(String[] args) {
 7                     
 8                     //建立一個數組做爲參數傳入
 9                     String[] s = new String[]{"abc","d","feg"};
10                     
11                     //普通對象
12                     String str = "lisi";
13                     printObject(s);
14                     printObject(str);
15                 }
16                 
17                 public static void printObject(Object obj){
18                     if(obj.getClass().isArray()){
19                         int len = Array.getLength(obj);
20                         for(int i = 0 ; i<len; i++){
21                                 System.out.println(Array.get(obj,i));
22                         }
23                     }else{
24                         System.out.println(obj);
25                     }
26                 }
27 }

 

第十講:ArrayList_HashSet的比較及Hashcode分析

1、哈希算法的由來:

        若在一個集合中查找是否含有某個對象,一般是一個個的去比較,找到後還要進行equals的比較,對象特別多時,效率很低。有這麼一種HashCode算法,有一個集合,把這個集合分紅若干個區域,每一個存進來的對象,能夠算出一個hashCode值,根據算出來的值,就放到相應的區域中去。當要查找某一個對象,只要算出這個對象的hashCode值,看屬於第幾個區域,而後到相應的區域中去尋找,看是否有與此對象相等的對象。這樣查找的性能就提升了。

 

2、要想HashCode方法有價值的話,前提是對象存入的是hash算法這種類型的集合當中纔有價值。若是不存入是hashCode算法的集合中,則不用複寫此方法。

3、若是沒有複寫hashCode方法,對象的hashCode值是按照內存地址進行計算的。這樣即便兩個對象的內容是想等的,可是存入集合中的內存地址值不一樣,致使hashCode值也不一樣,被存入的區域也不一樣。因此兩個內容相等的對象,就能夠存入集合中。

        因此就有這樣的說法:若是兩個對象equals相等的話,你應該讓他們的hashCode也相等。若是對象存入的不是根據hash算法的集合中,就不須要複寫hashCode方法。

4、當一個對象存儲進HashSet集合中之後,就不能修改這個對象中的那些參與計算哈希值的字段了,不然對象修改後的哈希值與最初存儲進HashSet集合中的哈希值就不一樣了。在這種狀況下,調用contains方法或者remove方法來尋找或者刪除這個對象的引用,就會找不到這個對象。從而致使沒法從HashSet集合中單獨刪除當前對象,從而形成內存泄露。

五,代碼練習:

 1 import java.util.*;
 2 
 3 public class HashCodeDemo {
 4             public static void main(String[] args) {
 5                 
 6                     HashCodeTest h1 = new HashCodeTest(4,4);
 7                     HashCodeTest h2 = new HashCodeTest(4,4);
 8                     HashCodeTest h3 = new HashCodeTest(5,5);
 9                 
10                     Collection  collections = new HashSet();
11                     
12                     collections.add(h1);
13                     collections.add(h2);
14                     collections.add(h3);
15                     System.out.println(collections.size());
16             } 
17 }
18 
19 class HashCodeTest{
20         private int x;
21         private int y;
22         
23         
24         public HashCodeTest(int x, int y) {
25             this.x = x;
26             this.y = y;
27         }
28 
29 
30         @Override
31         public int hashCode() {
32             final int prime = 31;
33             int result = 1;
34             result = prime * result + x;
35             result = prime * result + y;
36             return result;
37         }
38 
39 
40         @Override
41         public boolean equals(Object obj) {
42             if (this == obj)
43                 return true;
44             if (obj == null)
45                 return false;
46             if (getClass() != obj.getClass())
47                 return false;
48             HashCodeTest other = (HashCodeTest) obj;
49             if (x != other.x)
50                 return false;
51             if (y != other.y)
52                 return false;
53             return true;
54         }
55         
56         
57 }
相關文章
相關標籤/搜索