什么是反射?
正常編譯執行java文件時,會生成一個.class文件,反射就是一個反編譯的過程,它可以通過.class文件得到一個java對象.一個類會有很多組成部分,比如成員變量,成員方法,構造方法等,反射可以通過加載類,解剖出類的各個組成部分.
為什么要用反射?
我們需要訪問一個類的方法或字段的時候,直接new一個該類的對象然后調用就好了,干嘛要用反射呢,剛學的時候我也不懂,並且學了也沒機會用到,或者根本用不到,后來開始接觸strut2,hibernate等一些框架的時候,才慢慢懂得了一點反射的強大之處,在框架中會有很多配置文件,而配置文件中一般只有一些類的全路徑的字符串,而就是通過這些個字符串,就能得到這個類的對象,以及類中所有的信息.我給你這個類,當然你可以輕松得到它的實例,但如果我給你的只是這個類的路徑的字符串呢?沒有反射你就傻眼了吧.學習反射一般只會在寫框架的時候用到,如果你沒有到達寫框架的高度,能夠看懂別人的框架也是極好的.
這里面還涉及到一個靜態編譯和動態編譯的概念,前者的意思就是在編譯的時候已經綁定了對象,確定了對象,而動態編譯就是知道運行的時候,才根據需要去綁定對象.
加載類:
加載類就是獲得類的字節碼,要想通過反射獲取到一個類的內部信息,首先得先獲取到這個類的字節碼對象.
加載類有三種方式:
現在我有一個類User,我希望加載這個類,獲得它的字節碼對象:
package com.wang.reflect; public class Demo01 { public static void main(String[] args) throws ClassNotFoundException { //第一種方式 通過Class的forName方法,注意使用目標類的全路徑
Class clazz=Class.forName("com.wang.reflect.User"); //第二種方式 通過Object類的getClass()方法
Class clazz1=new User().getClass(); //第三種方式 通過類的.class屬性
Class clazz2=User.class; } }
一般第一種方式最常用,也最好用,因為看代碼我們可以發現,后兩種在編譯前就必須要知道具體的類的,否則就無法通過編譯,而第一種方式,沒有那么大的強制性,他只是提供了一個空間,運行時你可以想把誰傳給它就把誰傳給它,這個類甚至可以不存在,后果只是拋出一個異常.
反射類的構造函數(Constructior):
獲得了Class對象以后就可以調用class的很多方法了,比如getConstructor(..),方法就可以獲得構造器的對象.下面分別通過獲取User類的無參和帶參構造器,進而得到該類對象的例子,感受一下反射技術.
public class User { private String name; public User(){ System.out.println("無參構造函數"); } public User(String name){ this.name=name; System.out.println("帶一個參數的構造函數:::"+name); } } //-----------------------------------------------------------------------------
@Test public void test01() throws Exception{ //通過Class的forName方法,注意使用目標類的全路徑
Class clazz=Class.forName("com.wang.reflect.User"); //通過無參構造器得到該類對象,
Constructor c=clazz.getConstructor(null); User user=(User) c.newInstance(null); //通過帶參構造器獲得該類對象
Constructor c1=clazz.getConstructor(String.class); User user1=(User)c1.newInstance("nick"); }
注意.在通過無參構造函數獲得對象時,還可以直接調用User user=(User)clazz.newInstance();得到,內部的原理也是先調用了無參構造器,如上.注意,使用"newInstance"方法只能調用類的無參構造方法,這個操作實際相當於使用了new進行對象實例化.
反射類的方法(Method):
一個類中總會有實現某些功能的方法,這些方法可能有返回值也可能沒有返回值,可能有參數也可能沒參數,可能是靜態方法也可能是普通方法.下面通過例子分別講解:
package com.wang.reflect; public class User { private String name; public User(){ System.out.println("無參構造函數"); } public User(String name){ this.name=name; System.out.println("帶一個參數的構造函數:::"+name); } //1無參無返回值的方法login
public void login(){ System.out.println("用戶登陸了"); } //2帶參有返回值的方法eat
public String eat(String name){ System.out.println("name"+"去吃飯去了"); return "eat"; } //3帶參無返回值的靜態方法
public static void sleep(String name){ System.out.println(name +"去睡覺了"); } } //-----------------------------------------------------------------
@Test public void test02() throws Exception{ User u=new User(); //通過Class的forName方法,注意使用目標類的全路徑
Class clazz=Class.forName("com.wang.reflect.User"); //1獲取無參無返回值的方法login //獲得一個方法名為"login"但是沒有參數的Method對象
Method method=clazz.getMethod("login", null); //第一個參數:要想執行這個login方法你總得告訴她是哪個User的login方法吧,第二個參數就是login方法的參數,這里為null
method.invoke(u, null); //2獲取有參有返回值的方法eat
Method method1=clazz.getMethod("eat", String.class); String agrs=(String)method1.invoke(u, "超人"); //3獲取帶參無返回值的靜態方法sleep,
//靜態方法 可以不提供User對象,傳一個null即可
Method method2=clazz.getMethod("sleep", String.class); method2.invoke(null, "蝙蝠俠"); }
//執行之后打印結果為:
無參構造函數
用戶登陸了
超人去吃飯去了
蝙蝠俠去睡覺了
反射類的字段:
@Test public void test03() throws Exception{ User u=new User(); u.setName("鋼鐵俠"); //通過Class的forName方法,注意使用目標類的全路徑 Class clazz=Class.forName("com.wang.reflect.User"); //注意需要用getDeclareField方法,getField只能獲取到public的字段 Field f=clazz.getDeclaredField("name"); f.setAccessible(true); String name=(String)f.get(u); System.out.println(name); }
下面介紹一個使用反射的簡單例子:
package com.wang.reflect; import java.lang.annotation.Annotation; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ResourceBundle; interface Person { public void location(String location); } class Student implements Person{ @Override public void location(String location) { System.out.println("學生的工作地點是"+location); } } class Teacher implements Person{ @Override public void location(String location) { System.out.println("老師的工作地點是"+location); } } class Factory{ public static Person getInstance(String str) throws Exception, IllegalAccessException, ClassNotFoundException{ Person person=(Person)Class.forName(str).newInstance(); return person; } } public class TestReflect { public static void main(String[] args) throws Exception { //Person p=(Person)Class.forName("com.wang.reflect.Student").newInstance(); //p.location("學校");
Person p=Factory.getInstance("com.wang.reflect.Teacher"); p.location("學校"); } }
這個例子使用了簡單的工廠模式,調用person的location方法,通過反射,實際應用中並不會出現這樣的代碼,這里只是做一個反射的運用的演示.在測試代碼中,只要簡單的修改ForName方法中的字符串,就可以調用相應的方法.