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提供了幾種獲取不同構造函數的方法;
|
|
|
|
|
|
|
|
|
|
|
|
字節碼實例:
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 創建對象的方法
|
|
2.Class類中創建對象的方法, 如果使用Class直接創建對象,必須保證類中有一個無參數公共構造函數
|
|
3.設置忽略訪問權限

如果是私有構造方法,反射默認是無法直接執行的,找到父類中AccessibleObject的方法,設置為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執行方法
方法獲取以后就需要執行。Method對象中提供方法執行的功能
|
Obj :如果是對象方法,傳指定的對象,如果是類方法,傳 null Args: 方法的參數 如果方法有返回結果,可以接收 |
設置忽略訪問權限,同上

|
|
|
方法操作的代碼:
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[] |
獲取當前Class所表示類中所有的public的字段,包括繼承的字段. |
| Field[] |
獲取當前Class所表示類中所有的字段,不包括繼承的字段. |
| 獲取當前Class所表示類中 |
|
| 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 返回:返回該字段的值. |
|
|
設置引用類型的值,非基本數據類型 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 方法
思路清晰
