Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class對象
傳入一個包名+類名的字符串就可以得到Class對象
第一步:獲得對象,構造方法,普通方法,屬性
Object obj = cls.newInstance() //反射實例化對象 Constructor<?> cons = cls.getConstructor(String.class, int.class);//獲得構造方法 Method m3 = cls.getDeclaredMethod("getName"); //獲得get方法 Field nameField = cls.getDeclaredField("name"); // 獲得name屬性
第二步:獲得之后---如何用的問題
1. 雖然用此方法創造的對象比new出來的復雜,但是有利於程序的解耦,增加擴展性
8/22框架技術都是通過此技術進行解耦操作的
2. Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class對象
Constructor<?> cons = cls.getConstructor(String.class, int.class); Object obj = cons.newInstance("張三", 20); // 為構造方法傳遞參數 System.out.println(obj); // Person [name=張三, age=20]
3. Method m = cls.getDeclaredMethod("say"); //say是自己在類中自定義的方法 m.invoke(obj);//輸出123456789,這個obj可以是new出來的或newInstance出來的
4. Object obj = Class.forName("Person").newInstance();
Field nameField = cls.getDeclaredField("name"); // 獲得name屬性
nameField.setAccessible(true); //解除封裝
nameField.set(obj, "張三"); // Person對象.name = "張三"
System.out.println(nameField.get(obj)); // Person對象.name
9/3日再學習
servlet MVC框架的老祖宗
反射之中包含了一個“反”的概念,所以要想解釋反射就必須先從“正”開始解釋,一般而言,當用戶使用一個類的時候,應該先知道這個類,而后通過這個類產生實例化對象,但是“反”指的是通過對象找到類。
packagecn.mldn.demo; classPerson {} publicclassTestDemo { publicstaticvoidmain(String[] args) throwsException { Person per = new Person() ; // 正着操作-由類到對象 System.out.println(per.getClass().getName()); // 反着來-由對象到類 } }
以上的代碼使用了一個getClass()方法,而后就可以得到對象所在的“包.類”名稱,這就屬於“反”了,但是在這個“反”的操作之中有一個getClass()就作為發起一切反射操作的開端。
Person的父類是Object類,而上面所使用getClass()方法就是Object類之中所定義的方法。
·取得Class對象:public final Class<?> getClass(),反射之中的所有泛型都定義為?,返回值都是Object。
而這個getClass()方法返回的對象是Class類的對象,所以這個Class就是所有反射操作的源頭。但是在講解其真正使用之前還有一個需要先解釋的問題,既然Class是所有反射操作的源頭,那么這個類肯定是最為重要的,而如果要想取得這個類的實例化對象,Java中定義了三種方式:
方式一:通過Object類的getClass()方法取得,基本不用:
packagecn.mldn.demo; classPerson {} publicclassTestDemo { publicstaticvoidmain(String[] args) throwsException { Person per = newPerson() ; // 正着操作 Class<?> cls = per.getClass() ; // 取得Class對象 System.out.println(cls.getName()); // 反着來 } }
方式二:使用“類.class”取得,在日后學習Hibernate開發的時候使用
packagecn.mldn.demo;
classPerson {}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Person.class; // 取得Class對象
System.out.println(cls.getName()); // 反着來
}
}
方式三:使用Class類內部定義的一個static方法,主要使用
·取得Class類對象:public static Class<?> forName(String className) throws ClassNotFoundException;
packagecn.mldn.demo;
classPerson {}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
System.out.println(cls.getName()); // 反着來
}
}
那么現在一個新的問題又來了,取得了Class類的對象有什么用處呢?對於對象的實例化操作之前一直依靠構造方法和關鍵字new完成,可是有了Class類對象之后,現在又提供了另外一種對象的實例化方法:
·通過反射實例化對象(通過new實例化對象):public T newInstance() throws InstantiationException, IllegalAccessException;
范例:通過反射實例化對象,實例化出的這個對象就可以調用類里面的方法和屬性
packagecn.mldn.demo;
classPerson {
@Override
publicString toString() {
return"Person Class Instance .";
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
Object obj = cls.newInstance() ; // 實例化對象,和使用關鍵字new一樣
Person per = (Person) obj ; // 向下轉型
System.out.println(per);
}
}
那么現在可以發現,對於對象的實例化操作,除了使用關鍵字new實例化對象之外又多了一個反射機制操作,而且這個操作要比之前使用的new復雜一些,可是有什么用?
對於程序的開發模式之前一直強調:盡量減少耦合,而減少耦合的最好做法是使用接口,但是就算使用了接口也逃不出關鍵字new,所以實際上new是造成耦合的關鍵元凶。
范例:回顧一下之前所編寫的工廠設計模式
packagecn.mldn.demo;
interface Fruit {
public void eat() ;
}
classApple implements Fruit {
public void eat() {
System.out.println("吃蘋果。");
};
}
class Factory {
public static Fruit getInstance(String className) {
if("apple".equals(className)){
return new Apple() ;
}
returnnull;
}
}
publicclassFactoryDemo {
publicstaticvoidmain(String[] args) {
Fruit f = Factory.getInstance("apple") ; //靜態方法
f.eat() ;
}
}
以上為之前所編寫最簡單的工廠設計模式,但是在這個工廠設計模式之中有一個最大的問題:如果現在接口的子類增加了,那么工廠類肯定需要修改,這是它所面臨的最大問題,而這個最大問題造成的關鍵性的病因是new,那么如果說現在不使用關鍵字new了,變為了反射機制呢?
反射機制實例化對象的時候實際上只需要“包.類”就可以,於是根據此操作,修改工廠設計模式。
packagecn.mldn.demo;
interfaceFruit {
publicvoideat() ;
}
classApple implementsFruit {
publicvoideat() {
System.out.println("吃蘋果。");
};
}
classOrange implementsFruit {
publicvoideat() {
System.out.println("吃橘子。");
};
}
classFactory {
publicstaticFruit getInstance(String className) {
Fruit f = null;
try{
f = (Fruit) Class.forName(className).newInstance() ;
//獲得Class對象----實例化一個類的對象
} catch(Exception e) {
e.printStackTrace();
}
returnf ;
}
}
publicclassFactoryDemo {
publicstaticvoidmain(String[] args) {
Fruit f = Factory.getInstance("cn.mldn.demo.Orange") ;
//只要傳入字符串進入就可以實例化對象--深度解耦了
f.eat() ;
}
}
發現,這個時候即使增加了接口的子類,工廠類照樣可以完成對象的實例化操作,這個才是真正的工廠類,可以應對於所有的變化。如果單獨從開發角度而言,與開發者關系不大,但是對於日后學習的一些框架技術這個就是它實現的命脈,在日后的程序開發上,如果發現操作的過程之中需要參數傳遞了一個完整的“包.類”名稱的時候幾乎都是反射機制作用。
8.22在此做筆記
向上塑性的好處是,增加extensible,
要是接口的實現類B換成了實現類c,照樣可以用A的引用變量.say()
在上面的類中,增加一個草莓類,但是工程這個類可以在不修改的情況下繼續使用
這邊進行了增加或者刪除,不用修改 另外一邊,降低了耦合度,增加了可擴展性
8/21日在次做筆記深入理解
開發中用到的bean工廠,傳入一個字符串就獲得相應的對象
3.12.2 、反射的深入應用
以上只是利用了Class類作為了反射實例化對象的基本應用,但是對於一個實例化對象而言,它需要調用類之中的構造方法、普通方法、屬性,而這些操作都可以通過反射機制完成。
3.12.2 .1、調用構造
反射獲得構造方法cons,cls是Class類型的對象
Constructor<?> cons = cls.getConstructor(String.class, int.class);
Object obj = cons.newInstance("張三", 20); //獲得了這個構造如何用? 為構造方法傳遞參數
class類里面的提供構造方法
使用反射機制也可以取得類之中的構造方法,這個方法在Class類之中已經明確定義了:
以下兩個方法
取得一個類的全部構造:
public Constructor<?>[] getConstructors() throws SecurityException
取得一個類的指定參數構造:
public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
現在發現以上的兩個方法返回的都是java.lang.reflect.Constructor類的對象。
范例:取得一個類之中的全部構造
packagecn.mldn.demo;
importjava.lang.reflect.Constructor; //不在java語言包
classPerson { // CTRL + K
publicPerson() {}
publicPerson(String name) {}
publicPerson(String name,intage) {}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
Constructor<?> cons [] = cls.getConstructors() ; // 取得全部構造
for(intx = 0; x < cons.length; x++) {
System.out.println(cons[x]);
}
}
}
驗證:在之前強調的一個簡單Java類必須存在一個無參構造方法
范例:觀察沒有無參構造的情況
packagecn.mldn.demo;
classPerson { // CTRL + K
privateString name;
privateintage;
publicPerson(String name,intage) {
this.name= name ;
this.age= age ;
}
@Override
publicString toString() {
return"Person [name="+ name+ ", age="+ age+ "]";
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
Object obj = cls.newInstance(); // 實例化對象
System.out.println(obj);
}
}
/*在進行spring開發的時候看見---進行構造器注入的時候,若提供了有參的構造方法
則無參數的構造方法必須人為提供,否則系統不會自動提供
*/
此時程序運行的時候出現了錯誤提示“java.lang.InstantiationException”,因為以上的方式使用反射實例化對象時需要的是類之中要提供無參構造方法,但是現在既然沒有了無參構造方法,那么就必須明確的找到一個構造方法,而后利用Constructor類之中的新方法實例化對象:
·實例化對象:public T newInstance(Object... initargs) throws InstantiationException, IllegalAccessException,IllegalArgumentException, InvocationTargetException
package cn.mldn.demo; import java.lang.reflect.Constructor; class Person { // CTRL + K private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } public void say(){ System.out.println("hi"); } } public class TestDemo { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class對象 // 取得指定參數類型的構造方法 Constructor<?> cons = cls.getConstructor(String.class, int.class); Object obj = cons.newInstance("張三", 20); // 為構造方法傳遞參數 System.out.println(obj); // Person [name=張三, age=20] Object obj1 = cls.newInstance();//里面不能使用傳統的構造函數的裝入newInstance("張三", 20) Person I = (Person)obj; I.say();// hi } }
很明顯,調用無參構造方法實例化對象要比調用有參構造的更加簡單、方便,所以在日后的所有開發之中,凡是有簡單Java類出現的地方,都一定要提供無參構造。
3.12.2 .2、調用普通方法
Class類下面的2個方法
當取得了一個類實例化對象之后,下面最需要調用的肯定是類之中的方法,所以可以繼續使用Class類取得一個類中所定義的方法定義:
·取得全部方法:public Method[] getMethods() throws SecurityException;
·取得指定方法:public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException
發現以上的方法返回的都是java.lang.reflect.Method類的對象。
范例:取得一個類之中所定義的全部方法
packagecn.mldn.demo;
importjava.lang.reflect.Method;
classPerson {
privateString name;
publicvoidsetName(String name) {
this.name= name;
}
publicString getName() {
returnname;
}
}
publicclassTestDemo {
publicstaticvoidmain(String[] args) throwsException {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 取得Class對象
Method met [] = cls.getMethods() ; // 取得全部方法
for(intx = 0; x < met.length; x++) {
System.out.println(met[x]);
}
}
}
但是取得了Method類對象最大的作用不再於方法的列出(方法的列出都在開發工具上使用了),但是對於取得了Method類對象之后還有一個最大的功能,就是可以利用反射調用類中的方法:
·調用方法:public Object invoke(Object obj, Object... args) throws IllegalAccessException,IllegalArgumentException, InvocationTargetException
之前調用類中方法的時候使用的都是“對象.方法”,但是現在有了反射之后,可以直接利用Object類調用指定子類的操作方法。(同時解釋一下,為什么setter、getter方法的命名要求如此嚴格)。
范例:利用反射調用Person類之中的setName()、getName()方法
package cn.mldn.demo; import java.lang.reflect.Method; class Person { private String name; public void setName(String name) { this.name = name; } public String getName() { return name; } public void say(){ System.out.println(123456789); } } public class TestDemo { public static <Mehtod> void main(String[] args) throws Exception { Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class對象 Object obj = cls.newInstance(); // 實例化對象,沒有向Person轉型 String attribute = "name"; // 要調用類之中的屬性 Method setMet = cls.getMethod("set" + initcap(attribute), String.class);// setName() Method getMet = cls.getMethod("get" + initcap(attribute));// getName() setMet.invoke(obj, "張三"); // 等價於:Person對象.setName("張三") System.out.println(getMet.invoke(obj));// 等價於:Person對象.getName() 輸出張三 /*自己測試-------------------------------------------------*/ Person p =(Person)obj; p.say();//輸出123456789 Method m = cls.getDeclaredMethod("say"); m.invoke(obj);//輸出123456789 /*自己測試-------------------------------------------------*/ Method m2 = cls.getDeclaredMethod("setName", String.class);//獲得了set方法 Method m3 = cls.getDeclaredMethod("getName"); //get方法 m2.invoke(obj, "張三");//如何用 System.out.println(m3.invoke(obj)); // 張三 } public static String initcap(String str) { return str.substring(0, 1).toUpperCase().concat(str.substring(1)); } }
在日后的所有框架技術開發之中,簡單Java類都是如此應用的,所以必須按照標准進行。
3.12.2 .3、調用成員
Class類里面的方法
類之中最后一個組成部分就是成員(Field,也可以稱為屬性),如果要通過反射取得類的成員可以使用方法如下:
·取得本類的全部成員:public Field[] getDeclaredFields() throws SecurityException;
·取得指定的成員:public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException;
這兩個方法的返回值類型是java.lang.reflect.Field類的對象,下面首先觀察如何取得一個類之中的全部屬性。
范例:取得一個類之中的全部屬性
packagecn.mldn.demo;
importjava.lang.reflect.Field;
classPerson {
privateString name;
}
public class TestDemo {
public static void main(String[] args) throws Exception {
Class<?> cls = Class.forName("cn.mldn.demo.Person") ; // 第一步:取得Class對象
Field field [] = cls.getDeclaredFields() ; // 第二步:取得全部屬性
for(intx = 0; x < field.length; x++) {
System.out.println(field[x]);
}
}
}
但是找到Field實際上就找到了一個很有意思的操作,在Field類之中提供了兩個方法:
·設置屬性內容(類似於:對象.屬性= 內容):public void set(Object obj, Object value)
throws IllegalArgumentException, IllegalAccessException;
·取得屬性內容(類似於:對象.屬性):public Object get(Object obj)
throws IllegalArgumentException, IllegalAccessException
可是從類的開發要求而言,一直都強調類之中的屬性必須封裝,所以現在調用之前要想辦法解除封裝。
·解除封裝:public void setAccessible(boolean flag) throws SecurityException;
范例:利用反射操作類中的屬性
import java.lang.reflect.Field; class Person { private String name; int age = 88; public void say() { System.out.println(123456789); } } public class TestDemo { public static void main(String[] args) throws Exception { Class<?> cls = Class.forName("Person"); // 取得Class對象 Object obj = cls.newInstance(); // 對象實例化(不通過new--obj和以前的new出來的對象用法一樣)屬性才會分配空間 Field nameField = cls.getDeclaredField("name"); // 獲得name屬性 nameField.setAccessible(true); // 如何用解除封裝了 nameField.set(obj, "張三"); // Person對象.name = "張三" System.out.println(nameField.get(obj)); // Person對象.name
/*如果里面有get方法,可以通過obj.getName()獲得這個屬性*/ /*證明通過反射獲得類的對象和以前new出來的是一樣的*/ Person pp = (Person) obj; pp.age = 99; System.out.println(pp.age); pp.say(); } }
資料來源:經過加工整理
鏈接:https://www.zhihu.com/question/24304289/answer/38218810
來源:知乎