標籤:
1. 概念
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態擷取的資訊以及動態調用對象的方法的功能稱為java語言的反射機制。
2. 擷取Class對象
public class ClassDemo1 {public static void main(String[] args) {try {Entity foo1 = new Entity();// 獲得Class對象的兩種方式:Class c1 = Entity.class;Class c2 = foo1.getClass();Class c3 = Class.forName("com.thr.reflect.Entity");// 不管c1還是c2,都代表了Entity的class的對象System.out.println(c1 == c2);// c3也是Entity的class對象System.out.println(c1 == c3);} catch (ClassNotFoundException e) {e.printStackTrace();}}}class Entity {}三種方式擷取的Class對象是一致的。
通過Class對象我們還可以建立類:
Entity en = (Entity) c1.newInstance();
通過擷取的這個對象就可以像用new建立出來的對象一樣使用Entity內部的方法等。
3. 動態載入類
首先建立一個介面:
public interface Singable {void sing();}然後寫兩個實作類別:
public class Bird implements Singable {@Overridepublic void sing() {System.out.println("Bird sing...");}}
public class Cat implements Singable {@Overridepublic void sing() {System.out.println("Cat sing...");}}
最後再主程式中動態載入類:
public class World {public static void main(String[] args) {try {Class c = Class.forName(args[0]);Singable singable = (Singable) c.newInstance();singable.sing();} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}
運行時要傳入類的全名參數。
4. 擷取方法資訊
首先來看基礎資料型別 (Elementary Data Type)的Class對象:
public class ClassDemo2 {public static void main(String[] args) {Class[] cs = new Class[] { int.class, String.class, double.class,Double.class, void.class, Class.class };for (Class c : cs) {System.out.println(c.getName());System.out.println(c.getSimpleName());}}}我們編寫一個擷取類的方法的工具方法:
public static void printClassMethods(Object obj) {// 首先擷取Class對象Class c = obj.getClass();// 擷取類的名稱System.out.println("類的名稱是:" + c.getName());// 擷取類的方法,一個Method的對象就是一個成員方法,通過getMethods()擷取的是所有public的方法,包括從父類繼承而來的// getDeclaredMethods()擷取的是所有該類自己生命的方法,不問存取權限Method[] ms = c.getMethods();for (Method m : ms) {// 擷取傳回值類型Class returnType = m.getReturnType();System.out.print(returnType.getName() + " ");// 擷取方法名System.out.print(m.getName() + "(");// 擷取參數類型Class[] paramTypes = m.getParameterTypes();for (Class type : paramTypes) {System.out.print(type.getName() + ",");}System.out.println(")");}}使用它:
public static void main(String[] args) {String s = new String();ClassUtil.printClassMethods(s);Integer i = 1;ClassUtil.printClassMethods(i);}
5. 擷取成員變數和建構函式資訊
擷取成員變數資訊:
public static void printClassFields(Object obj) {// 首先擷取Class對象Class c = obj.getClass();// 使用getFields()擷取的是所有public的成員變數資訊,使用getDeclaredFields擷取的是該類自己聲明的成員變數資訊,不問存取權限Field[] fs = c.getDeclaredFields();for (Field f : fs) {// 擷取成員變數的類型Class fieldType = f.getType();String typeName = fieldType.getName();// 擷取成員變數的名字String filedName = f.getName();System.out.println(typeName + " " + filedName);}}擷取構造方法資訊:
public static void printClassConstructor(Object obj) {// 首先擷取Class對象Class c = obj.getClass();Constructor[] cons = c.getDeclaredConstructors();for (Constructor con : cons) {// 擷取方法名System.out.print(con.getName() + "(");// 擷取參數類型Class[] paramTypes = con.getParameterTypes();for (Class type : paramTypes) {System.out.print(type.getName() + ",");}System.out.println(")");}}
6. 方法反射的基本操作
所有的方法對象都有一個invoke方法我們調用method.invoke(對象, 參數列表)就可以執行方法:
public class MethodDemo1 {public static void main(String[] args) {try {// 擷取print(int, int)方法A a = new A();Class c = a.getClass();Method m1 = c.getMethod("print", int.class, int.class);// 執行方法,使用o來接受傳回值// 如果有傳回值,那麼Object就是傳回值的類型;沒有傳回值就返回nullObject o1 = m1.invoke(a, 10, 10);// 擷取print(String, String)方法Method m2 = c.getMethod("print", new Class[] { String.class,String.class });Object o2 = m2.invoke(a, "s1", "s2");// 擷取print()方法Method m3 = c.getMethod("print");Object o3 = m3.invoke(a);System.out.println(o1);System.out.println(o2);System.out.println(o3);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}}class A {public void print() {System.out.println("hello");}public int print(int a, int b) {System.out.println(a + b);return a + b;}public String print(String a, String b) {System.out.println(a.toUpperCase() + "," + b.toLowerCase());return a + b;}}
7. 泛型的本質
public static void main(String[] args) {List list1 = new ArrayList();List<String> list2 = new ArrayList<String>();Class c1 = list1.getClass();Class c2 = list2.getClass();System.out.println(c1 == c2);// 反射的操作都是編譯之後的操作// c1==c2是true說明編譯之後集合的泛型是去泛型化的// Java中集合的泛型是防止錯誤輸入的,只在編譯階段有效,繞過編譯就無效了// 我們可以通過方法的反射來繞過編譯Method m;try {m = c1.getMethod("add", Object.class);m.invoke(list1, 100);System.out.println(list1.size());System.out.println(list1);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (SecurityException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (IllegalArgumentException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}通過這個例子可以看到通過Java的反射機制可以繞過編譯,是在運行時刻執行的,自然就可以繞過集合的泛型。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
【Java】(2)Java反射