Android 技能圖譜學習路線系列-Java基礎之反射機制
Java反射機制 一、什麼是反射機制 JAVA反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意方法和屬性;這種動態獲取資訊以及動態呼叫物件方法的功能稱為java語言的反射機制。 二、反射機制的作用 在執行時判斷任意一個物件所屬的類; 在執行時構造任意一個類的物件; 在執行時判斷任意一個類所具有的成員變數和方法; 在執行時呼叫任意一個物件的方法; 生成動態代理 三. 反射機制的優點與缺點 反射機制的優點:可以實現動態建立物件和編譯,體現出很大的靈活性(特別是在J2EE的開發中它的靈活性就表現的十分明顯)。通過反射機制我們可以獲得類的各種內容,進行了反編譯。對於JAVA這種先編譯再執行的語言來說,反射機制可以使程式碼更加靈活,更加容易實現面向物件。 反射機制的缺點:使用反射基本上是一種解釋操作,用於欄位和方法接入時要遠慢於直接程式碼。因此Java反射機制只應用在對靈活性和擴充套件性要求很高的系統框架上,普通程式不建議使用。使用反射會模糊程式內部邏輯:程式設計師希望在程式碼中看到程式的邏輯,反射等繞過了原始碼的技術,因而會帶來維護問題。反射程式碼比相應的直接程式碼更復雜。 四. 反射機制的示例 1.通過一個物件獲得完整的包名和類名
所有類的物件其實都是Class的例項。
1 package Reflect;
2
3 class Demo{
4 //other codes...
5 }
6
7 class hello{
8 public static void main(String[] args) {
9 Demo demo=new Demo();
10 System.out.println(demo.getClass().getName());
11 }
12 }
13 //【執行結果】:Reflect.Demo
2.例項化Class類物件
1 package Reflect; 2 3 class Demo{ 4 //other codes... 5 } 6 7 class hello{ 8 public static void main(String[] args) { 9 Class<?> demo1=null; 10 Class<?> demo2=null; 11 Class<?> demo3=null; 12 try{ 13 //一般儘量採用這種形式 14 demo1=Class.forName("Reflect.Demo"); 15 }catch(Exception e){ 16 e.printStackTrace(); 17 } 18 demo2=new Demo().getClass(); 19 demo3=Demo.class; 20 21 System.out.println("類名稱 "+demo1.getName()); 22 System.out.println("類名稱 "+demo2.getName()); 23 System.out.println("類名稱 "+demo3.getName()); 24 } 25 } 26 //【執行結果】: 27 //類名稱 Reflect.Demo 28 //類名稱 Reflect.Demo 29 //類名稱 Reflect.Demo
3.通過Class例項化其他類的物件
1 package Reflect; 2 3 class Person{ 4 public String getName() { 5 return name; 6 } 7 public void setName(String name) { 8 this.name = name; 9 } 10 public int getAge() { 11 return age; 12 } 13 public void setAge(int age) { 14 this.age = age; 15 } 16 @Override 17 public String toString(){ 18 return "["+this.name+" "+this.age+"]"; 19 } 20 private String name; 21 private int age; 22 } 23 24 class hello{ 25 public static void main(String[] args) { 26 Class<?> demo=null; 27 try{ 28 demo=Class.forName("Reflect.Person"); 29 }catch (Exception e) { 30 e.printStackTrace(); 31 } 32 Person per=null; 33 try { 34 per=(Person)demo.newInstance(); 35 } catch (InstantiationException e) { 36 // TODO Auto-generated catch block 37 e.printStackTrace(); 38 } catch (IllegalAccessException e) { 39 // TODO Auto-generated catch block 40 e.printStackTrace(); 41 } 42 per.setName("Rollen"); 43 per.setAge(20); 44 System.out.println(per); 45 } 46 } 47 //【執行結果】: 48 //[Rollen 20]
但是注意一下,當我們把Person中的預設的無參建構函式取消的時候,比如自己定義只定義一個有引數的建構函式之後,會出現錯誤:
比如定義了一個建構函式:
1 public Person(String name, int age) {
2 this.age=age;
3 this.name=name;
4 }
然後繼續執行上面的程式,會出現:
java.lang.InstantiationException: Reflect.Person
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at Reflect.hello.main(hello.java:39)
Exception in thread "main" java.lang.NullPointerException
at Reflect.hello.main(hello.java:47)
所以大家以後再編寫使用Class例項化其他類的物件的時候,一定要自己定義無參的建構函式。
4.通過Class呼叫其他類中的建構函式 (也可以通過這種方式通過Class建立其他類的物件)
1 package Reflect;
2
3 import java.lang.reflect.Constructor;
4
5 class Person{
6 public Person() {
7
8 }
9 public Person(String name){
10 this.name=name;
11 }
12 public Person(int age){
13 this.age=age;
14 }
15 public Person(String name, int age) {
16 this.age=age;
17 this.name=name;
18 }
19 public String getName() {
20 return name;
21 }
22 public int getAge() {
23 return age;
24 }
25 @Override
26 public String toString(){
27 return "["+this.name+" "+this.age+"]";
28 }
29 private String name;
30 private int age;
31 }
32
33 class hello{
34 public static void main(String[] args) {
35 Class<?> demo=null;
36 try{
37 demo=Class.forName("Reflect.Person");
38 }catch (Exception e) {
39 e.printStackTrace();
40 }
41 Person per1=null;
42 Person per2=null;
43 Person per3=null;
44 Person per4=null;
45 //取得全部的建構函式
46 Constructor<?> cons[]=demo.getConstructors();
47 try{
48 per1=(Person)cons[0].newInstance();
49 per2=(Person)cons[1].newInstance("Rollen");
50 per3=(Person)cons[2].newInstance(20);
51 per4=(Person)cons[3].newInstance("Rollen",20);
52 }catch(Exception e){
53 e.printStackTrace();
54 }
55 System.out.println(per1);
56 System.out.println(per2);
57 System.out.println(per3);
58 System.out.println(per4);
59 }
60 }
61 //【執行結果】:
62 //[null 0]
63 //[Rollen 0]
64 //[null 20]
65 //[Rollen 20]
5.返回一個類實現的介面
1 package Reflect;
2
3 interface China{
4 public static final String name="Rollen";
5 public static int age=20;
6 public void sayChina();
7 public void sayHello(String name, int age);
8 }
9
10 class Person implements China{
11 public Person() {
12
13 }
14 public Person(String sex){
15 this.sex=sex;
16 }
17 public String getSex() {
18 return sex;
19 }
20 public void setSex(String sex) {
21 this.sex = sex;
22 }
23 @Override
24 public void sayChina(){
25 System.out.println("hello ,china");
26 }
27 @Override
28 public void sayHello(String name, int age){
29 System.out.println(name+" "+age);
30 }
31 private String sex;
32 }
33
34 class hello{
35 public static void main(String[] args) {
36 Class<?> demo=null;
37 try{
38 demo=Class.forName("Reflect.Person");
39 }catch (Exception e) {
40 e.printStackTrace();
41 }
42 //儲存所有的介面
43 Class<?> intes[]=demo.getInterfaces();
44 for (int i = 0; i < intes.length; i++) {
45 System.out.println("實現的介面 "+intes[i].getName());
46 }
47 }
48 }
49 //【執行結果】:
50 //實現的介面 Reflect.China
(以下幾個例子,都會用到這個例子的Person類,所以為節省篇幅,此處不再貼上Person的程式碼部分,只貼上主類hello的程式碼)
6.取得其他類中的父類
1 class hello{
2 public static void main(String[] args) {
3 Class<?> demo=null;
4 try{
5 demo=Class.forName("Reflect.Person");
6 }catch (Exception e) {
7 e.printStackTrace();
8 }
9 //取得父類
10 Class<?> temp=demo.getSuperclass();
11 System.out.println("繼承的父類為: "+temp.getName());
12 }
13 }
14 //【執行結果】
15 //繼承的父類為: java.lang.Object
7.獲得其他類中的全部建構函式
1 //這個例子需要在程式開頭新增import java.lang.reflect.*;
2 class hello{
3 public static void main(String[] args) {
4 Class<?> demo=null;
5 try{
6 demo=Class.forName("Reflect.Person");
7 }catch (Exception e) {
8 e.printStackTrace();
9 }
10 Constructor<?>cons[]=demo.getConstructors();
11 for (int i = 0; i < cons.length; i++) {
12 System.out.println("構造方法: "+cons[i]);
13 }
14 }
15 }
16 //【執行結果】:
17 //構造方法: public Reflect.Person()
18 //構造方法: public Reflect.Person(java.lang.String)
1 class hello{
2 public static void main(String[] args) {
3 Class<?> demo=null;
4 try{
5 demo=Class.forName("Reflect.Person");
6 }catch (Exception e) {
7 e.printStackTrace();
8 }
9 Constructor<?>cons[]=demo.getConstructors();
10 for (int i = 0; i < cons.length; i++) {
11 Class<?> p[]=cons[i].getParameterTypes();
12 System.out.print("構造方法: ");
13 int mo=cons[i].getModifiers();
14 System.out.print(Modifier.toString(mo)+" ");
15 System.out.print(cons[i].getName());
16 System.out.print("(");
17 for(int j=0;j<p.length;++j){
18 System.out.print(p[j].getName()+" arg"+i);
19 if(j<p.length-1){
20 System.out.print(",");
21 }
22 }
23 System.out.println("){}");
24 }
25 }
26 }
27 //【執行結果】:
28 //構造方法: public Reflect.Person(){}
29 //構造方法: public Reflect.Person(java.lang.String arg1){}
8.取得其他類的全部屬性,將這些整理在一起,也就是通過class取得一個類的全部框架
1 class hello {
2 public static void main(String[] args) {
3 Class<?> demo = null;
4 try {
5 demo = Class.forName("Reflect.Person");
6 } catch (Exception e) {
7 e.printStackTrace();
8 }
9 System.out.println("===============本類屬性========================");
10 // 取得本類的全部屬性
11 Field[] field = demo.getDeclaredFields();
12 for (int i = 0; i < field.length; i++) {
13 // 許可權修飾符
14 int mo = field[i].getModifiers();
15 String priv = Modifier.toString(mo);
16 // 屬性型別
17 Class<?> type = field[i].getType();
18 System.out.println(priv + " " + type.getName() + " "
19 + field[i].getName() + ";");
20 }
21 System.out.println("===============實現的介面或者父類的屬性========================");
22 // 取得實現的介面或者父類的屬性
23 Field[] filed1 = demo.getFields();
24 for (int j = 0; j < filed1.length; j++) {
25 // 許可權修飾符
26 int mo = filed1[j].getModifiers();
27 String priv = Modifier.toString(mo);
28 // 屬性型別
29 Class<?> type = filed1[j].getType();
30 System.out.println(priv + " " + type.getName() + " "
31 + filed1[j].getName() + ";");
32 }
33 }
34 }
35 //【執行結果】:
36 //===============本類屬性========================
37 //private java.lang.String sex;
38 //===============實現的介面或者父類的屬性========================
39 //public static final java.lang.String name;
40 //public static final int age;
9.通過反射呼叫其他類中的方法
1 class hello {
2 public static void main(String[] args) {
3 Class<?> demo = null;
4 try {
5 demo = Class.forName("Reflect.Person");
6 } catch (Exception e) {
7 e.printStackTrace();
8 }
9 try{
10 //呼叫Person類中的sayChina方法
11 Method method=demo.getMethod("sayChina");
12 method.invoke(demo.newInstance());
13 //呼叫Person的sayHello方法
14 method=demo.getMethod("sayHello", String.class,int.class);
15 method.invoke(demo.newInstance(),"Rollen",20);
16 }catch (Exception e) {
17 e.printStackTrace();
18 }
19 }
20 }
21 //【執行結果】:
22 //hello ,china
23 //Rollen 20
10.呼叫其他類的set和get方法
1 class hello {
2 public static void main(String[] args) {
3 Class<?> demo = null;
4 Object obj=null;
5 try {
6 demo = Class.forName("Reflect.Person");
7 } catch (Exception e) {
8 e.printStackTrace();
9 }
10 try{
11 obj=demo.newInstance();
12 }catch (Exception e) {
13 e.printStackTrace();
14 }
15 setter(obj,"Sex","男",String.class);
16 getter(obj,"Sex");
17 }
18
19 /**
20 * @param obj 操作的物件
21 * @param att 操作的屬性
22 * */
23 public static void getter(Object obj, String att) {
24 try {
25 Method method = obj.getClass().getMethod("get" + att);
26 System.out.println(method.invoke(obj));
27 } catch (Exception e) {
28 e.printStackTrace();
29 }
30 }
31
32 /**
33 * @param obj 操作的物件
34 * @param att 操作的屬性
35 * @param value 設定的值
36 * @param type 引數的屬性
37 * */
38 public static void setter(Object obj, String att, Object value,
39 Class<?> type) {
40 try {
41 Method method = obj.getClass().getMethod("set" + att, type);
42 method.invoke(obj, value);
43 } catch (Exception e) {
44 e.printStackTrace();
45 }
46 }
47 }// end class
48 //【執行結果】:
49 //男
11.通過反射操作屬性
1 class hello {
2 public static void main(String[] args) throws Exception {
3 Class<?> demo = null;
4 Object obj = null;
5
6 demo = Class.forName("Reflect.Person");
7 obj = demo.newInstance();
8
9 Field field = demo.getDeclaredField("sex");
10 field.setAccessible(true);
11 field.set(obj, "男");
12 System.out.println(field.get(obj));
13 }
14 }// end class
12.通過反射取得並修改陣列的資訊
1 import java.lang.reflect.*;
2
3 class hello{
4 public static void main(String[] args) {
5 int[] temp={1,2,3,4,5};
6 Class<?>demo=temp.getClass().getComponentType();
7 System.out.println("陣列型別: "+demo.getName());
8 System.out.println("陣列長度 "+Array.getLength(temp));
9 System.out.println("陣列的第一個元素: "+Array.get(temp, 0));
10 Array.set(temp, 0, 100);
11 System.out.println("修改之後陣列第一個元素為: "+Array.get(temp, 0));
12 }
13 }
14 //【執行結果】:
15 //陣列型別: int
16 //陣列長度 5
17 //陣列的第一個元素: 1
18 //修改之後陣列第一個元素為: 100
13.通過反射修改陣列大小
1 class hello{
2 public static void main(String[] args) {
3 int[] temp={1,2,3,4,5,6,7,8,9};
4 int[] newTemp=(int[])arrayInc(temp,15);
5 print(newTemp);
6 System.out.println("=====================");
7 String[] atr={"a","b","c"};
8 String[] str1=(String[])arrayInc(atr,8);
9 print(str1);
10 }
11 /**
12 * 修改陣列大小
13 * */
14 public static Object arrayInc(Object obj,int len){
15 Class<?>arr=obj.getClass().getComponentType();
16 Object newArr=Array.newInstance(arr, len);
17 int co=Array.getLength(obj);
18 System.arraycopy(obj, 0, newArr, 0, co);
19 return newArr;
20 }
21 /**
22 * 列印
23 * */
24 public static void print(Object obj){
25 Class<?>c=obj.getClass();
26 if(!c.isArray()){
27 return;
28 }
29 System.out.println("陣列長度為: "+Array.getLength(obj));
30 for (int i = 0; i < Array.getLength(obj); i++) {
31 System.out.print(Array.get(obj, i)+" ");
32 }
33 }
34 }
35 //【執行結果】:
36 //陣列長度為: 15
37 //1 2 3 4 5 6 7 8 9 0 0 0 0 0 0 =====================
38 //陣列長度為: 8
39 //a b c null null null null null
14.動態代理
首先來看看如何獲得類載入器:
1 class test{
2
3 }
4 class hello{
5 public static void main(String[] args) {
6 test t=new test();
7 System.out.println("類載入器 "+t.getClass().getClassLoader().getClass().getName());
8 }
9 }
10 //【程式輸出】:
11 //類載入器 sun.misc.Launcher$AppClassLoader
其實在java中有三種類類載入器。
1)Bootstrap ClassLoader 此載入器採用c++編寫,一般開發中很少見。
2)Extension ClassLoader 用來進行擴充套件類的載入,一般對應的是jre\lib\ext目錄中的類
3)AppClassLoader 載入classpath指定的類,是最常用的載入器。同時也是java中預設的載入器。
如果想要完成動態代理,首先需要定義一個InvocationHandler介面的子類,已完成代理的具體操作。
1 package Reflect;
2
3 import java.lang.reflect.*;
4
5 //定義專案介面
6 interface Subject {
7 public String say(String name, int age);
8 }
9
10 // 定義真實專案
11 class RealSubject implements Subject {
12 @Override
13 public String say(String name, int age) {
14 return name + " " + age;
15 }
16 }
17
18 class MyInvocationHandler implements InvocationHandler {
19 private Object obj = null;
20
21 public Object bind(Object obj) {
22 this.obj = obj;
23 return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
24 .getClass().getInterfaces(), this);
25 }
26
27 @Override
28 public Object invoke(Object proxy, Method method, Object[] args)
29 throws Throwable {
30 Object temp = method.invoke(this.obj, args);
31 return temp;
32 }
33 }
34
35 class hello {
36 public static void main(String[] args) {
37 MyInvocationHandler demo = new MyInvocationHandler();
38 Subject sub = (Subject) demo.bind(new RealSubject());
39 String info = sub.say("Rollen", 20);
40 System.out.println(info);
41 }
42 }
43 //【執行結果】:
44 //Rollen 20
類的生命週期
在一個類編譯完成之後,下一步就需要開始使用類,如果要使用一個類,肯定離不開JVM。在程式執行中JVM通過裝載,連結,初始化這3個步驟完成。
類的裝載是通過類載入器完成的,載入器將.class檔案的二進位制檔案裝入JVM的方法區,並且在堆區建立描述這個類的java.lang.Class物件。用來封裝資料。 但是同一個類只會被類裝載器裝載以前
連結就是把二進位制資料組裝為可以執行的狀態。
連結分為校驗,準備,解析這3個階段: 校驗 一般用來確認此二進位制檔案是否適合當前的JVM(版本), 準備 就是為靜態成員分配記憶體空間。並設定預設值 解析 指的是轉換常量池中的程式碼作為直接引用的過程,直到所有的符號引用都可以被執行程式使用(建立完整的對應關係)。
完成之後,型別也就完成了初始化,初始化之後類的物件就可以正常使用了,直到一個物件不再使用之後,將被垃圾回收。釋放空間。當沒有任何引用指向Class物件時就會被解除安裝,結束類的生命週期。