之前在網(wǎng)上找有關(guān)反射機制的資料發(fā)現(xiàn)網(wǎng)上關(guān)于這塊的資料不多,而且不太容易懂,所以試著寫一篇我自己所理解的反射機制希望能多你有所幫助.
首先來看看用反射機制和用以前的方法新建對象實例有什么不同
第一步新建一個Person對象
用以前的方法是:
Person p = new Person();
在內(nèi)存中新建一個Person的實例,對象p對這塊內(nèi)存地址進行引用
用反射機制實現(xiàn) (有三種方法):
第一種:
Class<?> cls=Class.forName("com.fanshe.Person"); //forName(包名.類名)
Person p=(Person)cls.newInstance();
1.通過JVM查找并加載指定的類(上面的代碼指定加載了com.fanshe包中的Person類)
2.調(diào)用newInstance()方法讓加載完的類在內(nèi)存中創(chuàng)建對應的實例,并把實例賦值給p
第二種:
Person p = new Person();
Class<?> cls=p.getClass();
Person p2=(Person)cls.newInstance();
1.在內(nèi)存中新建一個Person的實例,對象p對這個內(nèi)存地址進行引用
2.對象p調(diào)用getClass()返回對象p所對應的Class對象
3.調(diào)用newInstance()方法讓Class對象在內(nèi)存中創(chuàng)建對應的實例,并且讓p2引用實例的內(nèi)存地址
第三種:
Class<?> cls=Person.Class();
Person p=(Person)cls.newInstance();
1.獲取指定類型的Class對象,這里是Person
2.調(diào)用newInstance()方法在讓Class對象在內(nèi)存中創(chuàng)建對應的實例,并且讓p引用實例的內(nèi)存地址
注意:
cls.newInstance()方法返回的是一個泛型T,我們要強轉(zhuǎn)成Person類
cls.newInstance()默認返回的是Person類的無參數(shù)構(gòu)造對象
被反射機制加載的類必須有無參數(shù)構(gòu)造方法,否者運行會拋出異常
先來看看反射的好處
可能有人會有疑問,明明直接new對象就好了,為什么非要用反射呢?代碼量不是反而增加了?
其實反射的初衷不是方便你去創(chuàng)建一個對象,而是讓你在寫代碼的時候可以更加靈活,降低耦合,提高代碼的自適應能力.
怎么樣降低耦合度,提高代碼的自適應能力?
通過接口實現(xiàn),但是接口如果需要用到new關(guān)鍵字,這時候耦合問題又會出現(xiàn)
舉個栗子:
public static void main(String[] args) {
HeroFacrty facrty =new HeroFacrty();
hero iroman= facrty.CreateHero("IronMan");
iroman.attach();
}
public hero CreateHero(String name) {
if ((name).equals("IronMan")) {
return new IronMan();
}
if ((name).equals("Hulk")) {
return new Hulk();
}
return null;
}
interface hero {
public void attach();
}
class IronMan implements hero {
@Override
public void attach() {
System.out.println("Laser Light");
}
}
class Hulk implements hero {
@Override
public void attach() {
System.out.println("fist");
}
}
假設有1000個不同Hero需要創(chuàng)建,那你打算寫1000個 if語句來返回不同的Hero對象?
如果使用反射機制呢?
public static void main(String[] args) {
HeroFacrty facrty = new HeroFacrty();
Hero hero=facrty.CreateHero("test.IroMan");
hero.attack();
}
public Hero CreateHero(String name) {
try {
Class<?> cls = Class.forName(name);
Hero hero = (Hero) cls.newInstance();
return hero;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
class IroMan implements Hero {
@Override
public void attack() {
System.out.println("Laser Light");
}
}
class Hulk implements Hero {
@Override
public void attack() {
System.out.println("Fist");
}
}
interface Hero {
public void attack();
}
利用反射機制進行解耦的原理就是利用反射機制"動態(tài)"的創(chuàng)建對象:向CreateHero()方法傳入Hero類的包名.類名 通過加載指定的類,然后再實例化對象.
對于反射機制舉個不太嚴謹?shù)睦踝?
當你電腦需要使用鼠標時,你向電腦USB接口插入鼠標,鼠標向電腦發(fā)送驅(qū)動請求,驅(qū)動的型號為NFTW-2196的驅(qū)動.這時候電腦在驅(qū)動集合中查找驅(qū)動,找到后運行驅(qū)動鼠標能用了,插入鍵盤同理.
說完了反射機制如何生成類對象,那生成了對象以后當然要開始調(diào)用類的方法了
在調(diào)用方法前先了解Class<?> cls=Class.forName("fanshe.Person");cls內(nèi)部有哪些方法供我們使用.
| 方法關(guān)鍵字 | 含義 |
|---|---|
| getDeclareMethods() | 獲取所有的方法 |
| getReturnType() | 獲取方法的返回值類型 |
| getParameterTypes() | 獲取方法的傳入?yún)?shù)類型 |
| getDeclareMethod("方法名,參數(shù)類型.class,....") | 獲得特定的方法 |
| - | |
| 構(gòu)造方法關(guān)鍵字 | 含義 |
| getDeclaredConstructors() | 獲取所有的構(gòu)造方法 |
| getDeclaredConstructors(參數(shù)類型.class,....) | 獲取特定的構(gòu)造方法 |
| - | |
| 成員變量 | 含義 |
| getDeclaredFields | 獲取所有成員變量 |
| getDeclaredField(參數(shù)類型.class,....) | 獲取特定的成員變量 |
| - | |
| 父類和父接口 | 含義 |
| getSuperclass() | 獲取某類的父類 |
| getInterfaces() | 獲取某類實現(xiàn)的接口 |
以下面的Person來做講解,稍后我們會用反射實現(xiàn)下面的代碼:
public class fanshe03 {
public static void main(String[] args) {
Person person=new Person();
person.setName("Lipt0n");
System.out.print(person.getName);
}
}
class Person {
String name;
public Person() {
}
public Person(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
很簡單步驟分為三步:
- 實例化一個Person類對象
- 調(diào)用Person.setName("name")設置名字
- 在控制臺打印Person.getName()的值
現(xiàn)在我們用反射機制來實現(xiàn)上面的代碼:
public static void main(String[] args) {
try {
Class<?> cls=Class.forName("test.Person");//加載Person類
Object object=(Object) cls.newInstance();//實例化Person
Method setname=cls.getDeclaredMethod("setName", String.class);//獲取setName()方法
setname.invoke(object, "Lipt0n");//設置調(diào)用setName的對象和傳入setName的值
Method getname=cls.getDeclaredMethod("getName");//獲取getName方法
System.out.print(getname.invoke(object, null));//設置調(diào)用getName方法的對象.把值打印到控制臺
} catch (Exception e) {
e.printStackTrace();
}
}
Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構(gòu)造,并生成其對象實體、或?qū)ζ涑蓡T變量賦值、或調(diào)用其方法。這種“看透class”的能力稱為反射.--(摘自網(wǎng)上的一段話,具體出處忘記了)
</br>