一. 反射機制概念
主要是指程序可以訪問,檢測和修改它本身狀態或行爲的一種能力,並能根據自身行爲的狀態和結果,調整或修改應用所描述行爲的狀態和相關的語義。在java中,只要給定類的名字, 那麼就可以通過反射機制來獲得類的所有信息。
反射是Java中一種強大的工具,能夠使我們很方便的創建靈活的代碼,這些代碼可以再運行時裝配,無需在組件之間進行源代碼鏈接。但是反射使用不當會成本很高!
類中有什麼信息,利用反射機制就能可以獲得什麼信息,不過前提是得知道類的名字。
二. 反射機制的作用
1. 在運行時判斷任意一個對象所屬的類;
2. 在運行時獲取類的對象;
3. 在運行時訪問java對象的屬性,方法,構造方法等。
三. 反射機制的優點與缺點
首先要搞清楚爲什麼要用反射機制?直接創建對象不就可以了嗎,這就涉及到了動態與靜態的概念。
靜態編譯:在編譯時確定類型,綁定對象,即通過。
動態編譯:運行時確定類型,綁定對象。動態編譯最大限度發揮了java的靈活性,體現了多態的應用,有以降低類之間的藕合性。
反射機制的優點:可以實現動態創建對象和編譯,體現出很大的靈活性(特別是在J2EE的開發中它的靈活性就表現的十分明顯)。通過反射機制我們可以獲得類的各種內容,進行了反編譯。對於JAVA這種先編譯再運行的語言來說,反射機制可以使代碼更加靈活,更加容易實現面向對象。
比如,一個大型的軟件,不可能一次就把把它設計的很完美,當這個程序編譯後,發佈了,當發現需要更新某些功能時,我們不可能要用戶把以前的卸載,再重新安裝新的版本,假如這樣的話,這個軟件肯定是沒有多少人用的。採用靜態的話,需要把整個程序重新編譯一次纔可以實現功能的更新,而採用反射機制的話,它就可以不用卸載,只需要在運行時才動態的創建和編譯,就可以實現該功能。
反射機制的缺點:對性能有影響。使用反射基本上是一種解釋操作,我們可以告訴JVM,我們希望做什麼並且它 滿足我們的要求。這類操作總是慢於只直接執行相同的操作。
四. 反射機制的示例
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 publicString 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 // TODOAuto-generated catch block
37 e.printStackTrace();
38 } catch (IllegalAccessException e) {
39 // TODOAuto-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
atjava.lang.Class.newInstance0(Class.java:340)
atjava.lang.Class.newInstance(Class.java:308)
at Reflect.hello.main(hello.java:39)
Exception inthread "main" java.lang.NullPointerException
atReflect.hello.main(hello.java:47)
所以大家以後再編寫使用Class實例化其他類的對象的時候,一定要自己定義無參的構造函數。
4.通過Class調用其他類中的構造函數 (也可以通過這種方式通過Class創建其他類的對象)
1 package Reflect;
2
3 import java.lang.reflect.Constructor;
4
5 class Person{
6 publicPerson() {
7
8 }
9 publicPerson(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 implementsChina{
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.Stringarg1){}
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 * @paramobj 操作的對象
21 * @paramatt 操作的屬性
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 * @paramobj 操作的對象
34 * @paramatt 操作的屬性
35 * @param value設置的值
36 * @paramtype 參數的屬性
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) throwsException {
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接口的子類,已完成代理的具體操作。
View Code
類的生命週期
在一個類編譯完成之後,下一步就需要開始使用類,如果要使用一個類,肯定離不開JVM。在程序執行中JVM通過裝載,鏈接,初始化這3個步驟完成。
類的裝載是通過類加載器完成的,加載器將.class文件的二進制文件裝入JVM的方法區,並且在堆區創建描述這個類的java.lang.Class對象。用來封裝數據。 但是同一個類只會被類裝載器裝載以前
鏈接就是把二進制數據組裝爲可以運行的狀態。
鏈接分爲校驗,準備,解析這3個階段:
校驗一般用來確認此二進制文件是否適合當前的JVM(版本),
準備就是爲靜態成員分配內存空間,。並設置默認值
解析指的是轉換常量池中的代碼作爲直接引用的過程,直到所有的符號引用都可以被運行程序使用(建立完整的對應關係)。
完成之後,類型也就完成了初始化,初始化之後類的對象就可以正常使用了,直到一個對象不再使用之後,將被垃圾回收。釋放空間。當沒有任何引用指向Class對象時就會被卸載,結束類的生命週期。
五. IoC原理
Spring中的IoC的實現原理就是工廠模式加反射機制。
1.我們首先看一下不用反射機制時的工廠模式:
1 /**
2 * 工廠模式
3 */
4 interface fruit{
5 public abstract void eat();
6 }
7
8 class Apple implementsfruit{
9 public void eat(){
10 System.out.println("Apple");
11 }
12 }
13
14 class Orange implementsfruit{
15 public void eat(){
16 System.out.println("Orange");
17 }
18 }
19 // 構造工廠類
20 // 也就是說以後如果我們在添加其他的實例的時候只需要修改工廠類就行了
21 class Factory{
22 public static fruit getInstance(String fruitName){
23 fruit f=null;
24 if("Apple".equals(fruitName)){
25 f=new Apple();
26 }
27 if("Orange".equals(fruitName)){
28 f=new Orange();
29 }
30 return f;
31 }
32 }
33
34 class hello{
35 public static void main(String[] a){
36 fruit f=Factory.getInstance("Orange");
37 f.eat();
38 }
39 }
當我們在添加一個子類的時候,就需要修改工廠類了。如果我們添加太多的子類的時候,改的就會很多。
2. 利用反射機制的工廠模式:
1 package Reflect;
2
3 interface fruit{
4 public abstract void eat();
5 }
6
7 class Apple implementsfruit{
8 public void eat(){
9 System.out.println("Apple");
10 }
11 }
12
13 class Orange implementsfruit{
14 public void eat(){
15 System.out.println("Orange");
16 }
17 }
18
19 class Factory{
20 public static fruit getInstance(String ClassName){
21 fruit f=null;
22 try{
23 f=(fruit)Class.forName(ClassName).newInstance();
24 }catch (Exception e) {
25 e.printStackTrace();
26 }
27 return f;
28 }
29 }
30
31 class hello{
32 public static void main(String[] a){
33 fruit f=Factory.getInstance("Reflect.Apple");
34 if(f!=null){
35 f.eat();
36 }
37 }
38 }
現在就算我們添加任意多個子類的時候,工廠類就不需要修改。
使用反射機制的工廠模式可以通過反射取得接口的實例,但是需要傳入完整的包和類名。而且用戶也無法知道一個接口有多少個可以使用的子類,所以我們通過屬性文件的形式配置所需要的子類。
3.使用反射機制並結合屬性文件的工廠模式(即IoC)
首先創建一個fruit.properties的資源文件:
1 apple=Reflect.Apple
2 orange=Reflect.Orange
然後編寫主類代碼:
1 package Reflect;
2
3 import java.io.*;
4 import java.util.*;
5
6 interface fruit{
7 public abstract void eat();
8 }
9
10 class Apple implementsfruit{
11 public void eat(){
12 System.out.println("Apple");
13 }
14 }
15
16 class Orange implementsfruit{
17 public void eat(){
18 System.out.println("Orange");
19 }
20 }
21 //操作屬性文件類
22 class init{
23 public static Properties getPro() throwsFileNotFoundException, IOException{
24 Properties pro=new Properties();
25 File f=new File("fruit.properties");
26 if(f.exists()){
27 pro.load(new FileInputStream(f));
28 }else{
29 pro.setProperty("apple", "Reflect.Apple");
30 pro.setProperty("orange", "Reflect.Orange");
31 pro.store(new FileOutputStream(f), "FRUITCLASS");
32 }
33 return pro;
34 }
35 }
36
37 class Factory{
38 public static fruit getInstance(String ClassName){
39 fruit f=null;
40 try{
41 f=(fruit)Class.forName(ClassName).newInstance();
42 }catch (Exception e) {
43 e.printStackTrace();
44 }
45 return f;
46 }
47 }
48
49 class hello{
50 public static void main(String[] a) throwsFileNotFoundException, IOException{
51 Properties pro=init.getPro();
52 fruit f=Factory.getInstance(pro.getProperty("apple"));
53 if(f!=null){
54 f.eat();
55 }
56 }
57 }
58 //【運行結果】:Apple