序號 | 博主 | 主要內容 |
1 | sinat_38259539 | 總結的較全面的反射內容 |
2 |
一、獲取class類的方法 html
反射首先是要獲取class類,獲取class類有如下幾種方法:java
[參考博文:https://blog.csdn.net/beiyoujiayu/article/details/50451875] (感謝原文博主內容)內容以下,並對其中a、b、c 三點進行了驗證:jvm
#a&b. 內容驗證:ide
public class Test { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { System.out.println("111111"); Class clazz = A.class; System.out.println("222222"); A a = (A)clazz.newInstance(); System.out.println("333333"); System.out.println("\n111111"); Class clazz1 =Class.forName("$reflect.B"); System.out.println("222222"); B b = (B)clazz1.newInstance(); System.out.println("333333"); } } class A{ // 靜態代碼塊 static{ System.out.println("hello A~"); } // 非靜態代碼塊 { System.out.println("world A~"); } } class B{ static{ System.out.println("hello B~"); } { System.out.println("world A~"); } }
運行結果,你們仔細看看,這個結果的輸出也是驚豔到我了,居然不同(底層實現細節還不清楚,哪位大牛看到是否能指教一下):源碼分析
#c. 內容,這個博文寫的不對,應該說class.forName 是動態加載,其餘的是靜態加載,學習
參考博文[java動態加載類和靜態加載類: https://blog.csdn.net/su20145104009/article/details/52935472]測試
還有這篇文章:http://www.javashuo.com/article/p-dnpbdzhm-ee.html this
以及這篇文章:https://blog.csdn.net/zyw23zyw23/article/details/70244825spa
核心差異是:二者的出現的時期不一樣:.net
動態加載(運行時加載): Class.forName()在運行時加載;
靜態加載(編譯時加載): Class.class和getClass()是在編譯器加載
二、反射的過程,構造器、字段、方法 的反射
class獲取到之後,咱們就能夠隨心所欲啦~~~~~~~
測試類:
class Human { public String publicField; protected String protectedField; private String privateField; } class Person extends Human{ public String sex; private Integer age;
public double high; // 注意該字段沒有get set 方法 private Person() { } public Person(Integer age, String sex) { this.age = age; this.sex = sex; } private void privateMethod() { System.out.println("執行私有方法"); } protected void protectedMethod() { System.out.println("執行protected方法"); } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public String toString() { return "Person{" + "sex='" + sex + '\'' + ", age=" + age + '}'; } }
2.構造器的反射:
主要有如下幾個方法,帶 Declare的表示獲取申明的全部構造器:
具體代碼:
Class clazz = Class.forName("$reflect.Person");
System.out.println("獲取公共構造器:");
Constructor[] cons = clazz.getConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
System.out.println("獲取定義的全部構造器:");
cons = clazz.getDeclaredConstructors();
for (Constructor con : cons) {
System.out.println(con);
}
輸出:
獲取公共構造器:
public $reflect.Person(java.lang.Integer,java.lang.String) 獲取定義的全部構造器: private $reflect.Person() public $reflect.Person(java.lang.Integer,java.lang.String)
獲取對象:
Class clazz = Class.forName("$reflect.Person"); System.out.println("測試1:獲取對象[獲取私有構造器報錯]"); try { Constructor con = clazz.getConstructor(); con.newInstance(); } catch (Exception e) { e.printStackTrace(); }
測試1:獲取對象[獲取私有構造器報錯]
java.lang.NoSuchMethodException: $reflect.Person.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.getConstructor(Class.java:1825)
at $reflect.Test2.main(Test2.java:27)
System.out.println("測試2:獲取對象[私有構造器直接獲取對象報錯]"); try { Constructor con = clazz.getDeclaredConstructor(); Object obj = con.newInstance(); //私有構造器獲取對象直接報錯 System.out.println(obj); } catch (Exception e) { e.printStackTrace(); }
測試2:獲取對象[私有構造器直接獲取對象報錯]
java.lang.IllegalAccessException: Class $reflect.Test2 can not access a member of class $reflect.Person with modifiers "private"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Constructor.newInstance(Constructor.java:413)
at $reflect.Test2.main(Test2.java:29)
System.out.println("測試3:獲取對象[成功]"); try { Constructor con = clazz.getDeclaredConstructor();
// 設置爲可讀 con.setAccessible(true); Object obj = con.newInstance(); System.out.println(obj); } catch (Exception e) { e.printStackTrace(); }
測試3:獲取對象[成功]
2.2.字段的反射:
方法:
代碼:
System.out.println("獲取公共字段:"); Field[] fields = clazz.getFields(); for (Field field : fields) { System.out.println(field); }
獲取公共字段:
public java.lang.String $reflect.Person.sex
public double $reflect.Person.high
public java.lang.String $reflect.Human.publicField
System.out.println("獲取定義的全部字段:"); fields = clazz.getDeclaredFields(); for (Field field : fields) { System.out.println(field); }
獲取定義的全部字段:
public java.lang.String $reflect.Person.sex
public double $reflect.Person.high
private java.lang.Integer $reflect.Person.age
設置對象字段:
try { Constructor con = clazz.getDeclaredConstructor(); con.setAccessible(true); Object obj = con.newInstance(); System.out.println(obj); Field field = clazz.getField("sex"); field.set(obj, "男"); System.out.println(obj); //field = clazz.getField("age"); java.lang.NoSuchFieldException: age //field.set(obj, 28);
field = clazz.getDeclaredField("age"); field.setAccessible(true); field.set(obj, 28); System.out.println(obj); } catch (Exception e) { e.printStackTrace(); }
Person{sex='null', age=null}
Person{sex='男', age=null}
Person{sex='男', age=28}
總結1:沒有get/set方法發字段也能夠經過字段反射設置值:
try { Class clazz = Class.forName("$reflect.Person"); Object obj = new Person(28, "男"); System.out.println(obj); Field f = clazz.getField("high"); f.set(obj, 17); System.out.println(f.get(obj)); System.out.println(obj); } catch (Exception e){ e.printStackTrace(); }
Person{sex='男', age=28, high=0.0}
17.0
Person{sex='男', age=28, high=17.0}
2.3.方法的反射:
方法:
獲取方法:
System.out.println("獲取公共方法:"); Method[] methods = clazz.getMethods(); for (Method method : methods) { System.out.println(method); }
獲取公共方法:
public java.lang.String $reflect.Person.toString()
public java.lang.Integer $reflect.Person.getAge()
public void $reflect.Person.setAge(java.lang.Integer)
public java.lang.String $reflect.Person.getSex()
public void $reflect.Person.setSex(java.lang.String)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
System.out.println("獲取定義的全部方法:"); methods = clazz.getDeclaredMethods(); for (Method method : methods) { System.out.println(method); }
獲取定義的全部方法:
public java.lang.String $reflect.Person.toString()
private void $reflect.Person.privateMethod()
protected void $reflect.Person.protectedMethod()
public java.lang.Integer $reflect.Person.getAge()
public void $reflect.Person.setAge(java.lang.Integer)
public java.lang.String $reflect.Person.getSex()
public void $reflect.Person.setSex(java.lang.String)
執行方法:
try { Constructor con = clazz.getConstructor(Integer.class, String.class); Person person = (Person)con.newInstance(10,"男"); System.out.println(person); // Person{sex='男', age=10} Method method = clazz.getMethod("getAge"); System.out.println(method.invoke(person)); // 10 method = clazz.getMethod("setAge", Integer.class); method.invoke(person, 18); System.out.println(person.getAge()); // 18 } catch (NoSuchMethodException e) { e.printStackTrace(); }
注意點1:觀察以上輸出,咱們能夠發現:
一、get Methods/Fields 能獲取自身及父類的公開public修飾的方法和字段。
二、getDeclared Methods/Fields 能自身因此修飾詞的字段和方法,不能獲取父類相關字段或方法。
即:經過本class的反射,不能獲取父類 private/protected/default 的相關字段和方法。
不過能夠經過 clazz.getSuperClass()方法獲取父類class後進行相關反射操做。
三、內省
定義:能夠理解爲是輕量級的反射,主要的做用是針對bean對象字段的操做。
代碼:
public class IntroSpectorTest { public static void main(String[] args) throws Exception { Object obj = new Person1("男",18); BeanInfo person = Introspector.getBeanInfo(Person1.class) ; PropertyDescriptor[] pds = person.getPropertyDescriptors() ; for (PropertyDescriptor pd : pds) { System.out.println("\nname: "+pd.getName()); System.out.println(pd.getReadMethod()); System.out.println(pd.getWriteMethod()); System.out.println(pd.getPropertyType()); System.out.println(pd.getDisplayName()); System.out.println("Get方法invoke: "+pd.getReadMethod().invoke(obj)); } } } class Human1 { public Human1(){} // 父類 public 內省能夠獲取 public Map getMap(){ return null; }
name: map
public java.util.Map $reflect.Human1.getMap()
null
interface java.util.Map
map
Get方法invoke: null
} class Person1 extends Human1{ public String sex; private Integer age;
public double high; // 內省無get、set 方法
public Person1(String sex, Integer age) { this.sex = sex; this.age = age; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; }
name: age
public java.lang.Integer $reflect.Person1.getAge()
public void $reflect.Person1.setAge(java.lang.Integer)
class java.lang.Integer
age
Get方法invoke: 18
// 沒有字段也能夠獲取, 沒有set方法 public Integer getAge1() { return 11; }
name: age1
public java.lang.Integer $reflect.Person1.getAge1()
null
class java.lang.Integer
age1
Get方法invoke: 11
// private 不行 private Integer getAge2() { return 11; } // protected 不行 protected Integer getAge3() { return 11; } // 不以get開頭不行 public Integer age4() { return 11; } // get大小寫敏感,不行 public Integer gETAge41() { return 11; } // 光有set不行,必須有get public void setAge5() {} public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; }
name: sex
public java.lang.String $reflect.Person1.getSex()
public void $reflect.Person1.setSex(java.lang.String)
class java.lang.String
sex
Get方法invoke: 男
}
總結2:觀察以上輸出,咱們能夠發現:
一、內省是根據get方法獲取字段,而非經過字段獲取。
二、對於沒有get/set方法發字段,內省沒法進行值的設置和獲取,此時須要依賴字段反射,這也是爲何上面我說到:內省是輕量級的反射。
總結2-1源碼分析,內省方法構建邏輯主要在這個方法中:
構建細節(看源碼能夠發現:static方法會過濾、is開頭也能夠是內省方法 等更多的邏輯細節):
四、修飾詞:
五、class類原理(源碼分析)、jvm加載類機制,反射源碼分析
四、觸類旁通