什么是反射?
Java反射機制是在運行狀態(tài)中姿骏,對于任意一個類身笤,都能夠知道這個類的所有屬性和方法豹悬;對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性液荸;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為Java語言的反射機制瞻佛。
簡單的來說:
1.通過new關(guān)鍵字創(chuàng)建對象操作對象,在編譯時就已經(jīng)確定。
2.通過反射可以在程序運行過程中動態(tài)的操作對象伤柄,可以獲得編譯期無法獲得的信息绊困,動態(tài)操作最大限度發(fā)揮了java擴展性。
反射是框架的靈魂适刀,它可以有效地降低類之間的耦合秤朗,很多框架都運用了反射原理,例如hibernate 的實體類笔喉,Spring 的 AOP等等都有反射的實現(xiàn)
反射的具體實現(xiàn)
想要實現(xiàn)反射取视,就必須先拿到該類的字節(jié)碼文件對象(.class),通過字節(jié)碼文件對象,就能夠通過該類中的方法獲取到我們想要的所有信息常挚,每一個類對應(yīng)著一個字節(jié)碼文件也就對應(yīng)著一個Class類型的對象作谭,也就是字節(jié)碼文件對象.
1. 獲取字節(jié)碼文件對象(Class)的三種方式
/**
* 方式一:
* Object中的getClass方法來獲取Class對象
* 使用這方式必須有具體的類,并創(chuàng)建對象待侵。
* 這種方式使用的少丢早,一般是傳的是Object,不知道類型的時候才使用秧倾。
*/
Object obj=new Proson();
Class clazz1 =obj.getClass();
System.err.println("通過getClass():"+clazz1);
/**
* 方式二:
* 直接通過 類名.class來獲取Class對象怨酝。
* 任何類型中都具有隱含的靜態(tài)成員變量class,使用簡單,但是擴展性還是不足那先。
*/
Class clazz2=Proson.class;
System.err.println("通過類名.class:"+clazz2);
/**
* 方式三:
* 通過Class 對象的forName()靜態(tài)方法來獲取Class對象农猬。
* 使用最多叶摄,通過類的全限定名看铆,但可能拋出ClassNotFoundException異常
*/
Class clazz3 = Class.forName("com.xiaoli.bean.Proson");
System.err.println("通過類的全限定名:"+clazz3);
//比較三種方法對象是否是相同實例。
System.err.println(clazz1==clazz2);
System.err.println(clazz1==clazz3);
System.err.println(clazz2==clazz3);
運行結(jié)果得知
三種方法都可以獲取字節(jié)碼文件對象阿纤。
一個類在 JVM 中只會有一個 Class 實例(比較之后全為true)
2. 創(chuàng)建對象的方式揖闸。
Proson類
public class Proson {
//私有屬性
private String name;
//公有屬性
public Integer age;
//無參構(gòu)造
public Proson() {
}
//有參構(gòu)造
public Proson(String name, Integer age) {
this.name = name;
this.age = age;
}
//私有方法
private void method1(){
System.err.println("method1——run");
}
//公有方法
public void method2(String param){
System.err.println("method1=2——run :"+param);
}
@Override
public String toString() {
return "Proson{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
實例化對象的方式
通過new關(guān)鍵字與反射相互對比加深對反射的認識揍堕,詳解代碼如下:
// 不用反射: 創(chuàng)建對象,需要手動new對象不能動態(tài)創(chuàng)建汤纸。
// new的時候根據(jù)new的類名尋找字節(jié)碼文件衩茸,加載進入內(nèi)存,創(chuàng)建Class對象贮泞,并接著創(chuàng)建對應(yīng)的Proson對象楞慈。
Proson p=new Proson();
//通過反射:只需要一個名字,就可以創(chuàng)建對象啃擦。
String className="com.xiaoli.bean.Proson";
//尋找該名稱的類文件囊蓝,加載進內(nèi)存,并創(chuàng)建Class對象令蛉。
Class clazz = Class.forName(className);
//通過Class對象的newInstance()創(chuàng)建類對象聚霜。
Object o = clazz.newInstance();
調(diào)用有參構(gòu)造方法初始化變量
通過有參構(gòu)造對比反射,詳解代碼如下:
//不用反射:通過構(gòu)造方法實例化并初始化變量
Object obj1=new Proson("name",18);
System.err.println("不用反射: "+obj1);
//通過反射:Class對象的getConstructor方法拿到構(gòu)造器。
Constructor constructor = clazz.getConstructor(String.class, Integer.class);
//通過構(gòu)造器的newInstance方法實例化并初始化變量
Object obj2=constructor.newInstance("name2",22);
System.err.println("通過反射: "+obj2);
得到結(jié)果
Class 類中方法詳解
到這里應(yīng)該理解到Class對反射的重要性了吧俯萎?那么Class類中有什么常用方法呢傲宜?
獲取公共構(gòu)造器 getConstructors()
獲取所有構(gòu)造器 getDeclaredConstructors()
獲取該類對象 newInstance()
獲取類名包含包路徑 getName()
獲取類名不包含包路徑 getSimpleName()
獲取類公共類型的所有屬性 getFields()
獲取類的所有屬性 getDeclaredFields()
獲取類公共類型的指定屬性 getField(String name)
獲取類全部類型的指定屬性 getDeclaredField(String name)
獲取類公共類型的方法 getMethods()
獲取類的所有方法 getDeclaredMethods()
獲得類的特定公共類型方法: getMethod(String name, Class[] parameterTypes)
獲取內(nèi)部類 getDeclaredClasses()
獲取外部類 getDeclaringClass()
獲取修飾符 getModifiers()
獲取所在包 getPackage()
獲取所實現(xiàn)的接口 getInterfaces()
通過一些例子來演示以上常用方法,詳解代碼如下夫啊。
//這里方便操作演示用第二種發(fā)方法獲取字節(jié)碼對象
Class clazz2=Proson.class;
//通過反射調(diào)用有參構(gòu)造函數(shù)初始化對象(上面演示過)
Constructor constructor = clazz2.getConstructor(String.class, Integer.class);
Object obj = constructor.newInstance("lixiaoli", 18);
//獲得類完整的名字
String className1 = clazz2.getName();
System.err.println("1類完整的名字: "+className1);
//獲得類名不包含包路徑
String className2 = clazz2.getSimpleName();
System.err.println("2類名不含路徑: "+className2);
//獲得類中公共類型(public)屬性
Field[] fields = clazz2.getFields();
String fieldName="";
for(Field field : fields){
fieldName+=field.getName()+" ";
}
System.err.println("3類中公共屬性: "+fieldName);
//獲得類中全部類型(包括私有)屬性
Field[] fieldsAll = clazz2.getDeclaredFields();
fieldName="";
for(Field field : fieldsAll){
fieldName+=field.getName()+" ";
}
System.err.println("4類中全部屬性: "+fieldName);
//獲得公共指定屬性值
Field age = clazz2.getField("age");
Object o = age.get(obj);
System.err.println("5公共指定屬性: "+o);
//獲得私有指定屬性值
Field name = clazz2.getDeclaredField("name");
// Field name = clazz2.getField("name");只能獲取共有屬性 故此方法會拋出NoSuchFieldException異常,所以選用getDeclaredField();
name.setAccessible(true); //設(shè)置為true才能獲取私有屬性
Object o2 = name.get(obj);
System.err.println("6類中私有指定屬性值: "+o2);
//獲取類所有公共類型方法 這里包括 Object 類的一些方法
Method[] methods = clazz2.getMethods();
String methodsName="";
for(Method method : methods){
methodsName+=method.getName()+" ";
}
System.err.println("7類公共類型方法: "+methodsName);
//獲取該類中的所有方法(包括私有)
Method[] methodsAll = clazz2.getDeclaredMethods();
methodsName="";
for(Method method : methodsAll){
methodsName+=method.getName()+" ";
}
System.err.println("8類中的所有方法: "+methodsName);
//獲取并使用指定方法
Method method1 = clazz2.getDeclaredMethod("method1");//獲取無參私有方法
method1.setAccessible(true);//設(shè)置為true才能獲取私有方法
method1.invoke(obj);//調(diào)用無參方法
Method method2 = clazz2.getMethod("method2",String.class);//獲取有參數(shù)方法
method2.invoke(obj,"666");//調(diào)用有參方法
運行結(jié)果
反射總結(jié)
熟練的使用反射能讓我們的程序耦合度降低函卒,當(dāng)然會損失一部分性能,畢竟有失才有得撇眯。