什么是反射?


1.對象有編譯類型和運行類型

           Object obj = new java.util.Date();

  編譯類型:Object

  運行類型(其實就是obj對象真實的類型):java.util.Date

  需求:根據對象obj調用Date類中的一個方法,toLocaleString,如何來做?

    obj.toLocaleString()代碼在編譯階段去編譯類型Object中檢查是否有該方法,若沒有,編譯失敗.

 

1.對象有編譯類型和運行類型

           Object obj = new java.util.Date();

  編譯類型:Object

  運行類型(其實就是obj對象真實的類型):java.util.Date

  需求:根據對象obj調用Date類中的一個方法,toLocaleString,如何來做?

    obj.toLocaleString()代碼在編譯階段去編譯類型Object中檢查是否有該方法,若沒有,編譯失敗.

 

 

 

 

 

 

 

 

 

解決方案1:強制轉換obj為Date類型,前提:必須知道對象的真實類型是什么?

      Date d = (Date)obj;

      d.toLocaleString();//YES

 

如果我不知道obj的真實類型,那又如何去調用toLolcaeString方法. 如何去做?

解決方案2: 使用反射

 

 

 

 

 

 

 

 

 

對象使用類描述的,但是Java中一些皆對象,其實所有的類,底層都有一個字節碼對象,用什么來描述這一個個類底層的字節碼對象

 

解決方案 : 使用反射

 

 

 

 

 

 

 

反射:(reflection):在運行時期,動態地去獲取類中的信息(類的信息,方法信息,構造器信息,字段等信息進行操作);

 一個類中包含的信息有: 構造器,字段,方法

 如何使用反射描述這些相關的信息

 Class : 描述類

Method : 描述方法

Constructor :描述構造器

Field :描述字段

 

獲取類的Class實例三種方式:

在反射操作某一個類之前,應該先獲取這個類的字節碼實例

獲取字節碼實例有三種方式

注意 :同一個類在JVM的字節碼實例只有一份

1. 類名.class 

2. 類的對象.getClass()

3. Class.forName(“類的全限定名”)

(1)     全限定名 = 包名 + 類名

 1 public class User {
 2     @Test
 3     public void testName() throws Exception {
 4         //1.使用類名.class 獲取類的字節碼實例
 5         Class<User>  clz1 = User.class;
 6         System.out.println(clz1.toString());
 7         
 8         //2.對象.getClass()
 9         User user = new User();
10         Class<?> clz2 =  user.getClass();
11         System.out.println(clz2);
12         
13         //3.Class.forName("全限定名"):用的最多
14         Class<?> clz3 = Class.forName("cn.sxt.reflect.User");
15         System.out.println(clz3);
16     }    
17 }

 

獲取九大內置類的字節碼實例:

對於對象來說,可以直接使用對象.getClass()或者Class.forName(className); 類名.class都可以獲取Class實例.

 

但是我們的基本數據類型,就沒有類的權限定名,也沒有getClass方法.

問題: 那么如何使用Class類來表示基本數據類型的Class實例?

 

八大基本數據類型和 void關鍵字都是有 字節碼實例的

byte,short,int,long,char,float,double,boolean ,void關鍵字

 

答 : 數據類型/void.class 即可

 

每個基本數據類型都是包裝類型 如 :int ----Integer包裝類型

注意: 基本數據類型和包裝數據類型底層的字節碼實例是不相同

  

獲取8大基本數據類型和void的字節碼實例:byte,short,int,long,char,float,double,boolean;   void關鍵字;

 1 public class BaiscDataTypeClassTest {
 2     @Test
 3     public void testName() throws Exception {
 4         //1.獲取byte的字節碼實例
 5         Class<?> byteClz = byte.class;
 6         System.out.println(byteClz);
 7         //....
 8         //獲取void關鍵字的字節碼實例
 9         Class<?> voidClz =void.class;
10         System.out.println(voidClz);
11         
12         
13         //所有的基本數據類型都有包裝類型
14         //int---Integer 
15         //int 和Integer 的字節碼是絕對不相等的
16         Class<?> intClz1 = int.class;
17         Class<?> intClz2 = Integer.class;
18         System.out.println(intClz1);
19         System.out.println(intClz2);
20         System.out.println(intClz1 == intClz2);    
21     }
22 }

獲取數組類型的字節碼實例

表示數組的Class實例:

   String[] sArr1 = {"A","C"};

   Class clz = String[].class;//此時clz表示就是一個String類型的一位數組類型

 

所有具有相同元素類型和維數的數組才共享同一份字節碼(Class對象);

注意:和數組中的元素沒有一點關系.

 

 1 package cn.sxt.reflect._01.getclass;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import org.junit.Test;
 6 
 7 //獲取數組類型的字節碼實例
 8 public class ArrayClassTest {
 9     @Test
10     public void testName() throws Exception {
11         //定義數組
12         int[] arr = {1,2,3};
13         Class<int[]> clz1 = (Class<int[]>) arr.getClass();
14         System.out.println(clz1);
15         Class<int[]>  clz2= int[].class;
16         System.out.println(clz2);
17         
18         
19         int[] arr2 = {2,3,4};
20         Class<int[]> clz3 = (Class<int[]>) arr2.getClass();
21         System.out.println(clz3);
22         
23         
24         System.out.println(clz1 == clz2);//true
25         System.out.println(clz1 == clz3);//true
26     }
27 }

 

構造函數-Constructor,獲取構造函數

類的構函數有 有參數構造函數,無參構造函數,公共構造函數,非公共構造函數,根據不同的構造函數 Class提供了幾種獲取不同構造函數的方法; 

 Constructor<?>[]

getConstructors()
獲取所有的公共構造函數

 Constructor<?>[]

getDeclaredConstructors()
獲取所有的構造函數,和訪問權限無關

 Constructor<T>

getConstructor(Class<?>... parameterTypes)
獲取指定的公共構造函數

parameterTypes : 如果構造函數有參數,傳遞的是參數的字節碼實例

 Constructor<T>

getDeclaredConstructor(Class<?>... parameterTypes)
獲取和訪問權限無關的指定的構造函數

  

字節碼實例:

 1 public class ConstructorTest {
 2     @Test
 3     public void testName() throws Exception {
 4         
 5         //1.獲取Student的字節碼實例
 6         Class<?>  stuClz = Student.class;
 7         
 8         //2.獲取所有的公共構造函數
 9         Constructor<?>[] cts1 = stuClz.getConstructors();
10         for (Constructor<?> ct : cts1) {
11             System.out.println(ct);
12         }
13         System.out.println("----------------------");
14         //3.獲取所有的構造函數包括私有的
15         Constructor<?>[] cts2 = stuClz.getDeclaredConstructors();
16         for (Constructor<?> ct : cts2) {
17             System.out.println(ct);
18         }
19         System.out.println("----------------------");
20         
21         //4.獲取指定的構造函數(clz.getConstructor(...))只能獲取公共的構造函數
22         Constructor<?> ct1 = stuClz.getConstructor();
23         System.out.println(ct1);
24         
25         Constructor<?> ct2 =stuClz.getConstructor(String.class);
26         System.out.println(ct2);
27         //4.獲取指定的構造函數(clz.getDeclaredConstructor(...))獲取的構造函數和權限沒有關系
28         Constructor<?> ct3=stuClz.getDeclaredConstructor(String.class,int.class);
29         System.out.println(ct3);
30     }
31 }

 

 調用構造函數創建對象:

調用構造器,創建對象

Constructor<T>類:表示類中構造器的類型,Constructor的實例就是某一個類中的某一個構造器

常用方法:

public T newInstance(Object... initargs):如調用帶參數的構造器,只能使用該方式.

    參數:initargs:表示調用構造器的實際參數

    返回:返回創建的實例,T表示Class所表示類的類型

如果:一個類中的構造器可以直接訪問,同時沒有參數.,那么可以直接使用Class類中的newInstance方法創建對象.

 public Object newInstance():相當於new 類名();

調用私有的構造器:

 

1.Constructor 創建對象的方法

 T

newInstance(Object... initargs)
          使用此 Constructor 對象表示的構造方法來創建該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。

 

2.Class類中創建對象的方法, 如果使用Class直接創建對象,必須保證類中有一個無參數公共構造函數

 T

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

 

3.設置忽略訪問權限

 

如果是私有構造方法,反射默認是無法直接執行的,找到父類中AccessibleObject的方法,設置為true,即可忽略訪問權限

 void

setAccessible(boolean flag)
如果執行私有方法,設置忽略訪問權限 為 true即可

 

 

 

使用構造器創建對象

 1 public class NewInstanceTest {
 2     @Test
 3     public void testName() throws Exception {
 4         
 5         //1.獲取Student的字節碼實例
 6         Class<?> clz = Class.forName("cn.sxt.reflect.Student");
 7         
 8         //1.1如果類有無參數公共構造函數,直接可以使用類的字節碼實例就創建對象
 9         Student stu0 = (Student) clz.newInstance();
10         
11         
12         //2.獲取一個參數的構造函數
13         Constructor<Student> ct1 = (Constructor<Student>) clz.getConstructor(String.class);
14         //2.1.創建對象
15         Student stu1 = ct1.newInstance("東方不敗");
16         
17         //3.獲取私有構造函數並創建對象
18         Constructor<Student> ct2 =  (Constructor<Student>) clz.getDeclaredConstructor(String.class,int.class);
19         //3.1設置權限可以創建對象
20         ct2.setAccessible(true); 21         //3.2創建對象
22         Student stu2 = ct2.newInstance("西門吹雪",50);
23     }
24 }

 

操作方法-Method

一個類創建對象以后,一般就要執行對象的方法等等,使用反射操作對象

首先要獲取方法,再去執行;

獲取方法和方法的執行

一個類中的方法有很多,無參,有參,靜態,可變參數私有方法,等等,針對不同的方法處理,提供了不同的獲取方案

使用Class 獲取對應的方法

 Method[]

getMethods()
獲取所有的公共方法,包括父類的公共方法

 Method[]

getDeclaredMethods()
獲取所有本類的方法,包括本類的私有方法

 Method

getDeclaredMethod(String name, Class<?>... parameterTypes)
獲取指定方法名稱的方法,和訪問權限無關

 

Name : 指定的方法名稱

parameterTypes : 方法參數的類型

 

 

 

Method執行方法

 方法獲取以后就需要執行。Method對象中提供方法執行的功能

 

 Object

invoke(Object obj, Object... args)
執行方法

Obj :如果是對象方法,傳指定的對象,如果是類方法,傳 null

Args: 方法的參數

如果方法有返回結果,可以接收

 

 設置忽略訪問權限,同上

 

 

 void

setAccessible(boolean flag)
如果執行私有方法,設置忽略訪問權限 為 true即可

方法操作的代碼:

Student類:

 1 package cn.sxt.reflect._03method;
 2 
 3 import java.util.Arrays;
 4 
 5 public class Person {
 6     
 7     public void hell1() {
 8         System.out.println("我是無參數無返回值方法");
 9     }
10     
11     public String hello2(String name) {
12 
13         return "你好 :"+name;
14     }
15     
16     private String hello3(String name,int age) {
17         return "我是 :"+name+",今年 :"+age;
18     }
19     
20     
21     
22     public static void  staticMethod(String name) {
23         System.out.println("我是靜態方法 :"+name);
24     }
25     
26     
27     
28     public static void method1(int[] intArr) {
29         System.out.println(Arrays.toString(intArr));
30     }
31     
32     public static void method2(String[] strArr) {
33         System.out.println(Arrays.toString(strArr));
34     }
35     
36 }

測試類:

 1 package cn.sxt.reflect._03method;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import java.lang.reflect.Method;
 6 
 7 import org.junit.Test;
 8 
 9 //獲取Person類的方法
10 public class GetMethodTest {
11 
12     @Test
13     public void testName() throws Exception {
14         // 1.獲取Person字節碼實例
15         Class<Person> clz = Person.class;
16         // 2.創建對象
17         Person p = clz.newInstance();
18 
19         // 3.獲取方法(使用反射),獲取所有公共方法,包含父類的公共方法
20         Method[] methods1 = clz.getMethods();
21         for (Method method : methods1) {
22             System.out.println(method);
23         }
24         System.out.println("------------------------------");
25         // 4.獲取自己類中的所有方法(包括私有)
26         Method[] methods2 = clz.getDeclaredMethods();
27         for (Method method : methods2) {
28             System.out.println(method);
29         }
30         System.out.println("------------------------------");
31         // 4.獲取單個指定名稱的方法
32         Method method = clz.getMethod("hello2", String.class);
33         System.out.println(method);
34 
35         // 4.1執行方法
36         Object res = method.invoke(p, "陸小鳳");
37         System.out.println(res);
38 
39         // 5.獲取私有的方法
40         Method hello3 = clz.getDeclaredMethod("hello3", String.class, int.class);
41         System.out.println(hello3);
42 
43         // 5.1設置忽略訪問權限
44         hello3.setAccessible(true);
45 
46         Object res1 = hello3.invoke(p, "葉孤城", 30);
47         System.out.println(res1);
48 
49         // 6.獲取靜態方法
50         Method staticMethod = clz.getMethod("staticMethod", String.class);
51 
52         // 6.1執行靜態方法
53         staticMethod.invoke(null, "花滿樓");
54 
55         // 7.獲取有整數數組參數的方法
56         Method method1 = clz.getMethod("method1", int[].class);
57         method1.invoke(null, new Object[] {new int[] { 1, 2, 3, 4 }});
58 
59         // 8.獲取有整數數組參數的方法
60         /*
61          * 如果反射傳遞的參數是引用類型,底層有一個拆箱的功能,會將數組的元素拆成一個個參數傳遞過來
62          * 解決方案: 將數組外面在包裝一層數組,如果拆箱一次,得到還是一個數組
63          */
64         Method method2 = clz.getMethod("method2", String[].class);
65         method2.invoke(null,new Object[] {new String[] {"AA","BB","CC"}});
66     }
67 }

可變參數的方法執行

如果方法中有可變參數(數組),如果反射傳遞的可變參數是引用類型,底層有一個拆箱的功能,會將數組的元素拆成一個個參數傳遞過來

解決方案: 將數組外面在包裝一層數組,如果拆箱一次,得到還是一個數組

 

 1 package cn.sxt.reflect._03method;
 2 
 3 import java.util.Arrays;
 4 
 5 public class Person {
 6     
 7     public static void method1(int... intArr) {
 8         System.out.println(Arrays.toString(intArr));
 9     }
10     
11     public static void method2(String...strArr) {
12         System.out.println(Arrays.toString(strArr));
13     }
14 }
15 
16         // 7.獲取有整數數組參數的方法
17         Method method1 = clz.getMethod("method1", int[].class);
18         method1.invoke(null, new Object[] {new int[] { 1, 2, 3, 4 }});
19 
20         // 8.獲取有整數數組參數的方法
21         /*
22          * 如果反射傳遞的參數是引用類型,底層有一個拆箱的功能,會將數組的元素拆成一個個參數傳遞過來
23          * 解決方案: 將數組外面在包裝一層數組,如果拆箱一次,得到還是一個數組
24          */
25         Method method2 = clz.getMethod("method2", String[].class);
26         method2.invoke(null,new Object[] {new String[] {"AA","BB","CC"}});

 

操作字段(成員變量)-Field

類中的字段有各種數據類型和各種訪問權限,針對這些情況,反射操作有對應的方法來獲取和處理

Class中獲取字段方法

 Field[]

getFields()

獲取當前Class所表示類中所有的public的字段,包括繼承的字段.

 Field[]

getDeclaredFields()

獲取當前Class所表示類中所有的字段,不包括繼承的字段.

 Field

getField(String name)

獲取當前Class所表示類中

 Field

getDeclaredField(String name)

:獲取當前Class所表示類中該fieldName名字的字段,不包括繼承的字段.

Field操作設置值/獲取值

給某個類中的字段設置值和獲取值:

1,找到被操作字段所在類的字節碼

2,獲取到該被操作的字段對象

3,設置值/獲取值

Field類常用方法:

 void setXX(Object obj, XX value) :為基本類型字段設置值,XX表示基本數據類型

 void set(Object obj, Object value) :表示為引用類型字段設置值

  參數:

      obj:    表示字段底層所屬對象,若該字段是static的,該值應該設為null

      value:  表示將要設置的值

-------------------------------------------------------------------------------------

 XX getXX(Object obj) :獲取基本類型字段的值,XX表示基本數據類型

 Object get(Object obj) :表示獲取引用類型字段的值

   參數:

      obj:    表示字段底層所屬對象,若該字段是static的,該值應該設為null

   返回:返回該字段的值.

 

void

set(Object obj, Object value)

設置引用類型的值,非基本數據類型

Obj: 要設置值得對象

Value : 要設置的值

 1 package cn.sxt.reflect._04Field;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import java.lang.reflect.Field;
 6 
 7 import org.junit.Test;
 8 
 9 public class FieldTest {
10     
11     @Test
12     public void testName() throws Exception {
13         //1.獲取People字節碼
14         Class<People> clz = People.class;
15         
16         People p = clz.newInstance();
17         
18         //2.獲取所有公共字段
19         Field[] fields1 = clz.getFields();
20         for (Field field : fields1) {
21             System.out.println(field);
22         }
23         System.out.println("---------------------");
24         //3.獲取所有字段,和訪問權限無關
25         Field[] fields2 = clz.getDeclaredFields();
26         for (Field field : fields2) {
27             System.out.println(field);
28         }
29         System.out.println("---------------------");
30         //3.獲取指定的公共字段
31         Field emialField = clz.getField("emial");
32         System.out.println(emialField);
33         
34         //為字段設置值
35         emialField.set(p, "zhagnsan@qq.com");
36         System.out.println(p);
37         //4.獲取指定所有的字段,和訪問權限無關
38         Field nameFiled = clz.getDeclaredField("name");
39         System.out.println(nameFiled);
40         //設置忽略訪問權限
41         nameFiled.setAccessible(true);
42         nameFiled.set(p, "張三");
43         System.out.println(p);
44         
45         
46         //5 獲取age字段
47         Field ageFile = clz.getDeclaredField("age");
48         ageFile.setAccessible(true);
49         //設置忽略訪問權限
50         ageFile.setInt(p, 18);
51         System.out.println(p);
52         
53     }
54 }

 

Class的其他API方法

 

 1 @Test
 2     public void testName() throws Exception {
 3         
 4         //1.獲取UserDaoImpl的字節碼實例
 5         Class<?> clz = Class.forName("cn.sxt.reflect._05otherapi.UserDaoImpl");
 6         
 7         //2.獲取所有的接口
 8         Class<?>[] interfaces = clz.getInterfaces();
 9         for (Class<?> intface : interfaces) {
10             System.out.println(intface);
11         }
12         
13         //3.獲取全限定名
14         System.out.println(clz.getName());
15         
16         //4.獲取簡單類名
17         System.out.println(clz.getSimpleName());
18         //5.獲取包
19         System.out.println(clz.getPackage().getName());
20     }

 

 

JavaBean, JavaBean就是一個個Java類,在java中符合JavaBean特點類才叫做JavaBean

JavaBean的三個特點:

1.JavaBean類的修飾符必須是public,也就是一個JavaBean必須是一個對應一個類文件

2.JavaBean必須有無參數公共構造方法(以便於反射直接通過直接通過字節碼實例創建對象)

3.JavaBean中的成員變量/字段必須有get/set方法提供對應的屬性

JavaBean中的屬性:

Java類有成員變量,成員變量絕對不是屬性,JavaBean中的屬性是有get/set方法確定的

1.1Get方法確定屬性

public String getName() {
        return name;
}
屬性確定規則 : get方法去掉 get前綴 ,剩余部分 首字母小寫

屬性名稱 : name

1.2Set方法確定屬性

public void setName(String name) {
        this.name = name;
}
屬性確定規則 : set方法去掉 set前綴 ,剩余部分 首字母小寫

屬性名稱 : name

如果一個成員變量get/set方法都有確定的屬性就只有一個

問題 :get/set方法確定確定的屬性一般不就和成員變量一樣啊,為什么要有屬性了

答: 一般情況下,Eclipse工具有自動生成get/set方法的功能,確定的JavaBean的屬性只是恰好和成員變量相同,但是成員變量不是屬性

如下特殊性情況,屬性和成員變量名稱不同;

//Filed
private String firstName;
//Filed
private String lastName;
    
//屬性 : fullName
public String getFullName() {
return this.firstName + this.lastName;
}

 

總結:

1. 理解反射概念?反射能干啥?

(1)     反射: 在jvm運行階段,動態的獲取類的信息(字節碼實例,構造器,方法,字段),動態進行對象的創建,方法執行,字段操作。

2. 反射的常用類

(1)     Class :所有類的字節碼實例的描述

(2)     Constructor :構造器

(3)     Method :方法

(4)     Field :字段

3. JDBC+反射 代碼封裝

(1)     通用 crud 方法

 思路清晰

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM