Java--反射的逐步理解
阿新 • • 發佈:2019-02-05
層層引入反射的作用
一.類型別的概念:所有類都是物件,是Class類的例項物件,這個物件我們成為該類的類型別
1.下面是一個小的test,以產生3種方式的類型別:
public class test { /** * @param args */ public static void main(String[] args) { foo user = new foo(); //任何一個類都包含一個隱含的靜態成員變數class Class c1 = foo.class; Class c2 = user.getClass();Class c3 = null; try { c3 = Class.forName("test.foo"); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); }
//c1 c2,c3是foo類的類型別
}
class foo{ void print(){ System.out.println("haha"); } }
2.其實資料型別也有自己的類型別
Class class1 = int.class;//int的類型別 Class class2 = String.class; Class class3 = double.class; Class class4 = Double.class; System.out.println(class1); System.out.println(class2.getSimpleName()); System.out.println(class3); System.out.println(class4);
3.類型別可以建立類的例項物件
try { foo h =(foo)c1.newInstance();//類型別可以建立該類的例項物件 //需要有無引數的構造方法 h.print(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); }
二.動態載入類
1.我們有這樣的疑惑,如果一個主函式中有好多個類,其中有一個類出現錯誤,其他類都沒法用,所以我們就想我們要用那個類就讓那個類執行,並且不想在重新編譯主類,這時候動態載入類就派上了用場,不用動態類的時候會出現類似這樣的問題:
當然這種情況只會出現在我們手動採用javac編譯導致的,開發工具讓我們看不到這些細節的東西。.
2.我們先引出動態載入類的概念,class.forName("")是動態載入類,而new class()是靜態載入類,本質區別是,靜態載入類的時候,編譯時類必須存在,而動態載入類不一定存在。
public class sum { public static void main(String[] args) { try { //動態載入類 //args[0]表示的是在main方法裡面傳進來的第一個引數 Class c = Class.forName(args[0]); } catch(Exception e) { e.printStackTrace(); } } }
三.類對類的操作
1.通過Method類獲取成員方法
/** * 獲取成員方法 * Method類,方法物件 * 一個成員方法就是一個Method物件 * getMethods()方法獲取所有的public的函式,包括父類繼承而來的 * getDeclaredMethods()獲取的是所有該類自己宣告的方法,不問訪問許可權 * @param obj */ public static void printClassMessage(Object obj) { Class c1 = obj.getClass(); System.out.println(c1.getName()); Method[] ms = c1.getMethods();//c.getDeclaredMethods() for (int i = 0; i < ms.length; i++) { //得到方法的返回值型別的類型別 Class returnType = ms[i].getReturnType(); System.out.print(returnType.getName()+""); //得到方法的名稱 System.out.print(ms[i].getName()+"("); //獲取引數型別--->得到的是引數列表的型別的類型別 Class[] paramTypes = ms[i].getParameterTypes(); for (Class class1 : paramTypes) { System.out.println(class1.getName()); } System.out.println(")"); } }
2.通過Field類獲取成員變數
/** * 獲取成員變數 * 成員變數也是物件 * java.lang.reflect.Field * Filed類封裝了關於成員變數的操作 * getFileds()方法獲取的是所有的public的成員變數的資訊 * getDeclaredFiled獲取的是該類自己宣告的成員變數的資訊 * @param obj */ public static void printFieldMethod(Object obj) { Class c1 = obj.getClass(); Field[] fs =c1.getDeclaredFields(); for (Field field : fs) { //得到成員變數的型別的類型別 Class fieldType =field.getType(); String typeName = fieldType.getName(); //得到成員變數的名稱 String fieldName=field.getName(); System.out.println(typeName+" "+fieldName); } }
3.通過Constructor類獲取建構函式
/** * 列印物件的建構函式的資訊 * java.lang.Constructor中封裝了建構函式的資訊 * getConstructors獲取所有的public的建構函式 * getDeclaredConstructors得到所有的建構函式 * @param obj */ public static void printConMessage(Object obj){ Class c = obj.getClass(); //Constructor cs = c.getConstructors(); Constructor[] cs =c.getDeclaredConstructors(); for (Constructor constructor : cs) { System.out.println(constructor.getName()+"("); Class[] paramTypes =constructor.getParameterTypes(); for (Class class1 : paramTypes) { System.out.println(class1.getName()+","); } System.out.println(")"); } }
四.反射
1.反射的作用是指定方法名稱呼叫方法,demo如下:
public class demo { public static void main(String[] args) { //獲取類的資訊,就得先獲取類的類型別 A a1 = new A(); Class c1 = a1.getClass(); /* * 獲取方法,名稱和引數列表來決定 * getMethod獲取的是public的方法 * getDelcaredMethod獲取的的是自己宣告的方法 * */ try { Method method = c1.getMethod("print", int.class,int.class); /* * 方法的反射操作 * a1.print(10,20);方法的反射操作是用m物件來進行方法呼叫和a1.print呼叫的效果相同 * 方法如何沒有返回值返回null,有返回值返回具體的返回值 * ...表示有幾個引數傳幾個 */ Object object =method.invoke(a1, 10,20); Method method2 =c1.getMethod("print", new Class[]{String.class,String.class}); method2.invoke(a1, "hello","world"); Method method3 =c1.getMethod("print",null); Object a = method3.invoke(a1); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class A{ public void print() { System.out.println("hello world"); } public void print(int a,int b){ System.out.println(a+b); } public void print(String a,String b){ System.out.println(a.toUpperCase()+b.toLowerCase()); } }
2.案例,通過標準JavaBean的屬性名獲取其屬性值
五.泛型
1.泛型是什麼?看下面兩個宣告
//只能傳String型別的數 ArrayList<String> list = new ArrayList<String>(); ArrayList list2 = new ArrayList();
2.泛型的本質,看下面的demo
public class fanxing { /** * 通過Class,Method來認識泛型的本質 * @param args */ public static void main(String[] args) { //只能傳String型別的數 ArrayList<String> list = new ArrayList<String>(); ArrayList list2 = new ArrayList(); list.add("hello"); //list.add(20); Class c1 = list.getClass(); Class c2 =list2.getClass(); System.out.println(c1==c2); } }
由執行結果可以得出下面結論:
反射的操作都是編譯之後的操作
c1==c2結果返回true說明編譯之後的泛型是去泛型化的
Java中集合的泛型,是防止錯誤輸入的,只是在編譯階段有效
3.利用方法的反射來操作,繞過編譯
try { Method a1 =c1.getMethod("add", Object.class); Object a2 = a1.invoke(list, 2); System.out.println(list.size()); System.out.println(list); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); }
程式碼的執行結果驗證了泛型,得出結論,繞過編譯操作就繞過了泛型
最後謝謝大家的閱讀