1. 程式人生 > >JAVA基礎-反射

JAVA基礎-反射

情況下 name屬性 權限 override tde struct opera ati java反射機制

一、反射的介紹

JAVA反射機制是在運行狀態中,能夠獲取任意一個類的所有屬性和方法,對於任意一個對象,都能夠調用它的任意
一個方法。這種動態獲取的以及動態調用對象的方法的功能稱為java語言的反射機制。JAVA編譯時是先獲取到類,然
後才是類裏邊的屬性和方法,而反射則和編譯相反,他是先獲取類裏邊的對象和方法然後在告訴他是哪個類裏的。簡單
來說, 就可以把.class文件比做動物的屍體, 而反射技術就是對屍體的一種解剖.通過反射技術, 我們可以拿到該字節碼
文件中所有的東西, 例如成員變量, 成員方法, 構造方法, 而且還包括私有。想要反射首先要獲取到程序的“屍體”也就
是.class文件。

二、字節碼文件的獲取

獲取字節碼對象有3種方式:
1、類名.class - 這是一個靜態的屬性, 只要知道類名, 就可以獲取
2、對象名.getClass() - Object類裏的getClass()方法,對象已經存在的情況下, 可以使用這種方式
3、Claire.forName("類的全類名(包名+類名)") - 通過Class類裏的靜態方法forName來獲取節碼對象
舉例:

 1 public static void main(String[] args) throws ClassNotFoundException {
 2 // 通過Object的getClass()方法獲取,必須要有對象
 3 Student s = new
Student(); 4 Class clazz = s.getClass(); 5 // 通過類名獲取字節碼對象 6 Class clazz2 = Student.class; 7 // 通過Class類裏的靜態方法forName來獲取節碼對象 8 Class clazz3 = Class.forName("com.fanshe.Student"); 9 System.out.println(clazz == clazz2); 10 System.out.println(clazz == clazz3); 11 System.out.println(clazz); 12 13 }

字節碼文件其實就是描述.class文件的對象。

三、對構造方法的操作

*通過反射獲取公有構造方法的兩種方式:
1、返回一個 Constructor 對象,它反映此 Class 對象所表示的類的指定公共構造方法,可以獲取無參構造也可以
根據傳入的類型來匹配對應的構造方法:getConstructor(Class<?>... parameterTypes)
2、返回一個包含某些 Constructor 對象的數組,這些對象反映此 Class 對象所表示的類的所有公共構造方法:
getConstructors()
3、創建此 Class 對象所表示的類的一個新實例:
newInstance()

*暴力獲取(可以獲取全部權限的):
1、返回一個 Constructor 對象,該對象反映此 Class 對象所表示的類或接口的指定構造方法:
getDeclaredConstructor(Class<?>... parameterTypes)
2、返回 Constructor 對象的一個數組,這些對象反映此 Class 對象表示的類聲明的所有構造方法:
getDeclaredConstructors()
舉例:

 1 public static void main(String[] args) throws ReflectiveOperationException {
 2 // Class.forName()獲取字節碼對象
 3 Class<?> forName = Class.forName("com.fanshe.Student");
 4 // 獲取所有公共構造方法
 5 Constructor<?>[] constructors = forName.getConstructors();
 6 // 遍歷
 7 for (Constructor<?> constructor : constructors) {
 8 // 打印結果
 9 System.out.println(constructor);
10 }
11 System.out.println("--------------------------------------");
12 // 暴力獲取,可以獲取所有的構造方法(包括私有的)
13 Constructor<?> c1 = forName.getDeclaredConstructor();
14 c1.setAccessible(true);
15 System.out.println(c1);
16 //獲取有參構造
17 Constructor<?> c2 = forName.getConstructor(String.class, int.class);
18 System.out.println(c2);
19 }

四、對成員變量和方法的操作

*公有成員變量獲取方法:
1、返回一個 Field 對象,它反映此 Class 對象所表示的類或接口的指定公共成員字段:
getField(String name) - 參數為要返回的變量名
2、返回一個包含某些 Field 對象的數組,這些對象反映此 Class 對象所表示的類或接口的所有可訪問公共字段:
getFields() - 參數為要返回的變量名

*任意成員變量獲取方法:
1、返回一個 Field 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段:
getDeclaredField(String name) - 參數為要返回的變量名
2、返回 Field 對象的一個數組,這些對象反映此 Class 對象所表示的類或接口所聲明的所有字段:
getDeclaredFields() - 參數為要返回的變量名

*獲取公有的方法:
1、返回一個 Method 對象,它反映此 Class 對象所表示的類或接口的指定公共成員方法:
getMethod(String name, Class<?>... parameterTypes) - name 方法名、parameterTypes 參數列表
2、返回一個包含某些 Method 對象的數組,這些對象反映此 Class 對象所表示的類或接口(包括那些由該類或接口
聲明的以及從超類和超接口繼承的那些的類或接口)的公共 member 方法:
getMethods()

*暴力獲取方法:
1、 返回一個 Method 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明方法:
getDeclaredMethod(String name, Class<?>... parameterTypes) - name 方法名、parameterTypes
參數列表
2、返回 Method 對象的一個數組,這些對象反映此 Class 對象表示的類或接口聲明的所有方法,包括公共、保護、
默認(包)訪問和私有方法,但不包括繼承的方法:
getDeclaredMethods()
需要註意的是想要獲取私有的變量或者方法時應使用AccessibleObject類裏的setAccessible(boolean flag)方法 - 參數
值為 true 則指示反射的對象在使用時應該取消 Java 語言訪問檢查。值為 false 則指示反射的對象應該實施 Java 語言訪問
檢查。
Constructor, Field, Method都是AccessibleObject的子類所以可以直接使用父類的setAccessible(boolean flag)方法。

**通過反射獲得變量的流程:
1. 通過反射獲取該類的字節碼對象
2. 創建該類對象
3. 獲取該類中需要操作的字段(成員變量)
4. 通過字段對象中的方法修改屬性值

**通過反射執行方法的流程
1. 通過反射獲取該類的字節碼對象
2. 創建該類對象
3. 調用getMethod方法獲取Method對象, 方法形參接受方法的名字
4. 調用Method方法中的invoke()將方法運行
舉例:
*被反射的學生01類

 1 public class Student01 {
 2 public String name;
 3 private int age;
 4 
 5 public Student01() {
 6 super();
 7 // TODO Auto-generated constructor stub
 8 }
 9 
10 public Student01(String name, int age) {
11 super();
12 this.name = name;
13 this.age = age;
14 }
15 
16 public void name() {
17 System.out.println("測試");
18 
19 }
20 
21 public String getName() {
22 return name;
23 }
24 
25 public void setName(String name) {
26 this.name = name;
27 }
28 
29 public int getAge() {
30 return age;
31 }
32 
33 public void setAge(int age) {
34 this.age = age;
35 }
36 
37 private void mane1() {
38 System.out.println("這是個萌萌噠私有的");
39 
40 }
41 
42 @Override
43 public String toString() {
44 return "Student01 [name=" + name + ", age=" + age + "]";
45 }
46 
47 }

*對學生01類進行反射

 1 public static void main(String[] args) throws ReflectiveOperationException {
 2 // 獲取字節碼對象
 3 Class<?> clazz = Class.forName("com.fanshe.Student01");
 4 // 創建該類的對象
 5 Object stu = clazz.newInstance();
 6 // System.out.println(stu);
 7 // 獲取學生類的name變量
 8 Field f1 = clazz.getField("name");
 9 /*
10 * set將指定對象變量上此 Field 對象表示的字段設置為指定的新值。
11 * 為stu對象裏的name變量賦值
12 */
13 f1.set(stu, "李四");
14 // get()返回指定對象上此 Field 表示的字段的值。
15 Object name = f1.get(stu);
16 // 暴力獲取age字段
17 Field f2 = clazz.getDeclaredField("age");
18 System.out.println(f2);
19 // 讓jvm不檢查權限
20 f2.setAccessible(true);
21 // 為其賦值
22 f2.set(stu, 24);
23 // 獲取stu對象的f2字段的值
24 Object age = f2.get(stu);
25 System.out.println(name);
26 System.out.println(age);
27 // 公有無參無返回值,name()
28 Method method = clazz.getMethod("name");
29 // 使用Method類的invoke方法執行name方法
30 method.invoke(stu);
31 // 公有代參無返回值,參數為String類型
32 Method m2 = clazz.getMethod("setName", String.class);
33 // 執行stu對象的setName方法,傳入參數
34 m2.invoke(stu, "李晨宇");
35 // 公有無參有返回值
36 Method m3 = clazz.getMethod("getName");
37 // 返回值為invoke
38 Object invoke = m3.invoke(stu);
39 System.out.println(invoke);
40 // 私有
41 Method m4 = clazz.getDeclaredMethod("mane1");
42 // 讓jvm不檢查權限
43 m4.setAccessible(true);
44 // 執行stu對象的mane1方法
45 m4.invoke(stu);
46 }

需要註意的是,反射無參構造時被反射的類一定要有無參構造方法,默認生成的也算。
五、反射的應用

我們在開發的時候,由於要考慮到代碼的重用性,就會用反射來處理一些問題。而JAVA的一些常用jar包和主流框架的配置
都用到了反射的原理,學習反射有助於我們對源碼的閱讀和理解。BeanUtils工具類(Apache開發的便於操作JavaBeen的工具類)
就用到了反射的方法。
*BeanUtils的部分實現:

 1 public class MyBeanUtils {
 2 //因為是工具類,不需要實例化。所以私有構造方法
 3 private MyBeanUtils() {
 4 super();
 5 // TODO Auto-generated constructor stub
 6 }
 7 
 8 /*
 9 * 給對象中的屬性賦值 傳入類的對象類型,和要修改的屬性的值不確定所以用Object類型,屬性名用String類型
10 */
11 public static void setProrerty(Object object, String name, Object values)
12 throws ReflectiveOperationException, SecurityException {
13 // 獲取傳入對象的字節碼文件
14 Class clazz = object.getClass();
15 // 根據傳入的屬性獲取Field對象,因為不確定屬性的權限,用的暴力反射
16 Field field = clazz.getDeclaredField(name);
17 // 讓jvm不檢查權限
18 field.setAccessible(true);
19 // 為object對象裏的name屬性賦值
20 field.set(object, values);
21 
22 }
23 
24 // 獲取對象中的屬性
25 public static String getProrerty(Object object, String name)
26 throws ReflectiveOperationException, SecurityException {
27 // 獲取傳入對象的字節碼文件
28 Class clazz = object.getClass();
29 // 根據傳入的屬性獲取Field對象,因為不確定屬性的權限,用的暴力反射
30 Field field = clazz.getDeclaredField(name);
31 // 讓jvm不檢查權限
32 field.setAccessible(true);
33 // 獲取name屬性的值
34 Object object2 = field.get(object);
35 // System.out.println(object);
36 // 將值返回
37 return object2.toString();
38 }
39 
40 // 給對象中的屬性賦值(通過Map的方式),Map裏key存的是屬性名,value存的是要賦的值
41 public static void populat(Object object, Map map) throws ReflectiveOperationException, SecurityException {
42 // 獲取傳入對象的字節碼文件
43 Class clazz = object.getClass();
44 // 返回此集合中的key集合
45 Set keySet = map.keySet();
46 // 遍歷key
47 for (Object object2 : keySet) {
48 // 獲得value值
49 Object value = map.get(object2);
50 try {
51 // 根據傳入的key(屬性)獲取Field對象,因為不確定屬性的權限,用的暴力反射
52 Field field = clazz.getDeclaredField(object2.toString());
53 // 讓jvm不檢查權限
54 field.setAccessible(true);
55 // 賦值
56 field.set(object, value);
57 } catch (NoSuchFieldException e) {
58 // 出現異常,給出友好型提示
59 System.out.println("Mdzz,屬性都記不住");
60 }
61 }
62 }
63 }

*測試工具類:

 1 public static void main(String[] args) throws RuntimeException, ReflectiveOperationException {
 2 // 創建學生01對象
 3 Student01 s1 = new Student01();
 4 // 使用MyBeanUtils工具類為學生01對象賦值
 5 MyBeanUtils.setProrerty(s1, "name", "啦啦");
 6 MyBeanUtils.setProrerty(s1, "age", 15);
 7 // 使用MyBeanUtils工具類為學生01對象取值
 8 String name = MyBeanUtils.getProrerty(s1, "name");
 9 String age = MyBeanUtils.getProrerty(s1, "age");
10 // 打印出來
11 System.out.println(name);
12 System.out.println(age);
13 System.out.println("------------------------------------ ");
14 // 創建HashMap作為數據源
15 HashMap<String, Object> hashMap = new HashMap<>();
16 // 為HashMap賦值
17 hashMap.put("qqqqq", "大大的"); // 屬性不存在會給出友好型提示
18 hashMap.put("name", "大大的");
19 hashMap.put("age", 110);
20 // 使用MyBeanUtils工具類為學生01對象賦值
21 MyBeanUtils.populat(s1, hashMap);
22 System.out.println(s1);
23 }

JAVA基礎-反射