一、Class類的使用 1.Java的反射機制是在編譯並不確定是哪個類被載入了,而是在程式運行的時候才載入、探知、自審。使用在編譯期並不知道的類。這樣的特點就是反射。 2.面向對象的世界里,萬物皆對象(除靜態的成員,普通數據類型不是對象),類是對象類,是java.lang.Class的實例對象3.怎 ...
一、Class類的使用
1.Java的反射機制是在編譯並不確定是哪個類被載入了,而是在程式運行的時候才載入、探知、自審。使用在編譯期並不知道的類。這樣的特點就是反射。
2.面向對象的世界里,萬物皆對象(除靜態的成員,普通數據類型不是對象),類是對象類,是java.lang.Class的實例對象
3.怎樣表示Class的實例
public class ClassDemo1 { public static void main(String[] args) { //創建Foo類的實例對象foo1 Foo foo1 = new Foo(); //任何一個類都是Class的實例對象,並且這些對象由JVM創建,這個實例對象有三種表示方式 //第一種表示方式-->實際在告訴我們任何一個類都有一個隱含的靜態成員變數class Class c1 = Foo.class; //第二種表示方式-->已經知道該類的對象通過getClass方法獲取 Class c2 = foo1.getClass(); /*官網表示:c1,c2表示了Foo類的類類型(class type) * 類類型:類也是對象,是Class的實例對象,這個對象就是該類的類類型 * */ //一個類只可能是Class的一個對象 System.out.println(c1 == c2); //第三種表達方式-->通過Class.forName() Class c3 = null; try { c3 = Class.forName("JavaSE.Reflect.Foo"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(c2 == c3); /* * 可以通過類的類類型創建該類的對象實例-->通過c1,c2,c3創建Foo的實例對象 * */ try { //調用newInstance()時需要有無參數的構造方法 Foo foo2 = (Foo) c1.newInstance(); Foo foo3 = (Foo) c2.newInstance(); Foo foo4 = (Foo) c3.newInstance(); foo2.print(); foo3.print(); foo4.print(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } class Foo { void print() { System.out.println("I am method print in Foo"); } }
4.Class.forName()不僅表示了類類型,還代表了動態載入類
編譯時刻載入類是靜態載入類,運行時刻載入類是動態載入類
ClassDemo2:
public class ClassDemo2 { public static void main(String[] args) { /* * 此時運行會報錯,找不到C1類,C1的start(),C2類,C2的start() * 但是如果我們只想調用C1的方法,添加了C1類及其方法,程式仍然會報錯,因為還是找不到C2及它的方法 * 所以,new創建對象是靜態載入類,在編譯時刻就需要載入所有的可能使用到的類 * 通過動態載入類可以解決該問題 * */ if ("C1".equals(args[0])) { C1 c1 = new C1(); c1.start(); } if ("C2".equals(args[0])) { C2 c2 = new C2(); c2.start(); } } }
ClassDemo3:
public class ClassDemo3 { public static void main(String[] args) { try { //動態載入類,在運行時載入類 Class c = Class.forName(args[0]); //通過類類型,創建該類對象 CAble cc = (CAble) c.newInstance(); cc.start(); } catch (Exception e) { e.printStackTrace(); } } }
CAble:
public interface CAble { void start(); }
C1:
public class C1 implements CAble{ public void start() { System.out.println("C1 start run..."); } }
C2:
public class C2 implements CAble { public void start() { System.out.println("C2 start run..."); } }
5.基本的數據類型
void關鍵字也存在類類型
public class ClassDemo4 { public static void main(String[] args) { Class c1 = int.class; //int的類類型 System.out.println(c1); Class c2 = String.class; //String的類類型 Class c3 = double.class; Class c4 = Double.class; System.out.println(c3 == c4); Class c5 = void.class; System.out.println(c5); Class c6 = C1.class; System.out.println(c6.getName()); System.out.println(c6.getSimpleName()); } }
二、方法的反射
ClassUtil:
/** * 列印類的信息,包括成員函數 * * @param obj */ public static void printClassMethodMessage(Object obj) { //1.獲取類的類類型 Class c = obj.getClass(); //傳遞的是哪個子類的對象,c就是該子類的類類型 //2.獲取類的名稱 System.out.println("類的名稱: " + c.getName()); //3.獲取類的方法 /** * Method類,方法的類類型 * 一個成員方法就是一個Method對象 * getMethods()方法獲取的是所有public函數,包括父類繼承而來的 * getDeclaredMethods()獲取的是所有該類自己聲明的方法,不問訪問許可權 */ Method[] ms = c.getMethods(); 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.print(class1.getName() + ","); } System.out.print(")"); System.out.println(); } }
三、成員變數的反射
ClassUtil:
/** * 獲取成員變數的信息 * * @param obj */ public static void printFieldVariableMessage(Object obj) { Class c = obj.getClass(); /** * 成員變數也是對象 * java.lang.reflect.Field * Field類封裝了關於成員變數的操作 * getField()方法獲取的是所有的public的成員變數的信息 * getDeclaredFields()方法獲取的是該類自己聲明的成員變數的信息 */ Field[] fs = c.getDeclaredFields(); for (Field field : fs) { //得到成員變數的類型的類類型 Class fieldType = field.getType(); String typeName = fieldType.getName(); //得到成員變數的名稱 String fieldName = field.getName(); System.out.println(typeName + " " + fieldName); } }
四、構造函數的反射
ClassUtil:
/** * 獲取構造器的信息 * * @param obj */ public static void printConMessage(Object obj) { Class c = obj.getClass(); /** * 構造函數也是對象 * java.lang.Constructor中封裝了構造函數的信息 * getConctructots獲取所有的public的構造函數 * getDeclaredConstructors得到自己聲明的構造函數 */ // Constructor[] cs=c.getConstructors(); Constructor[] cs = c.getDeclaredConstructors(); for (Constructor constructor : cs) { System.out.print(constructor.getName() + "("); Class[] paramtypes = constructor.getParameterTypes(); for (Class class1 : paramtypes) { System.out.print(class1.getName() + ","); } System.out.println(")"); } }
五、Java類載入機制
MethodDemo1:
public class MethodDemo1 { public static void main(String[] args) { //獲取print(int,int)方法 A a1 = new A(); Class c = a1.getClass(); /** * 獲取方法,名稱和參數列表 * getMethod獲取的是public方法 * getDelcaredMethod獲取自己聲明的方法 */ try { // Method m=c.getMethod("print",new Class[]{int.class,int.class}); Method m = c.getMethod("print", int.class, int.class); // a1.print(10,20);方法的反射操作是用m對象來進行方法調用 // 方法如果沒有返回值返回null,有返回值返回具體的返回值 Object o = m.invoke(a1, new Object[]{10, 20}); System.out.println("======================"); Method m1 = c.getMethod("print", String.class, String.class); o = m1.invoke(a1, "hello", "world"); Method m2 = c.getMethod("print"); m2.invoke(a1); } catch (Exception e) { 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.toUpperCase()); } }
MethodDemo2:
public class MethodDemo2 { public static void main(String[] args) { ArrayList list = new ArrayList(); ArrayList<String> list1 = new ArrayList<String>(); list1.add("hello"); Class c1 = list.getClass(); Class c2 = list1.getClass(); System.out.println(c1 == c2); //true /** * c1==c2結果返回true說明編譯之後集合的泛型是去泛型化的 * Java中集合的泛型是防止錯誤輸入的,只在編譯階段有效 * 繞過編譯就無效了 */ try { Method m = c1.getMethod("add", Object.class); m.invoke(list1, 100);//染過編譯操作就繞過了泛型 System.out.println(list1.size()); System.out.println(list1); //此時不能使用foreach操作,會報ClassCastException } catch (Exception e) { e.printStackTrace(); } } }
六、反射機制的用途和缺點
1.反射的用途:
反射被廣泛地用於那些需要在運行時檢測或修改程式行為的程式中。這是一個相對高級 的特性,只有那些語言基礎非常扎實的開發者才應該使用它。如果能把這句警示時刻放在心 里,那麼反射機制就會成為一項強大的技術,可以讓應用程式做一些幾乎不可能做到的事情。
2.反射的缺點:
(1)性能第一: 反射包括了一些動態類型,所以 JVM 無法對這些代碼進行優化。因此,反射操作的效 率要比那些非反射操作低得多。我們應該避免在經常被 執行的代碼或對性能要求很高的程 序中使用反射。
(2)安全限制: 使用反射技術要求程式必須在一個沒有安全限制的環境中運行。如果一個程式必須在有 安全限制的環境中運行,如 Applet,那麼這就是個問題了。
(3)內部暴露: 由於反射允許代碼執行一些在正常情況下不被允許的操作(比如訪問私有的屬性和方 法),所以使用反射可能會導致意料之外的副作用--代碼有功能上的錯誤,降低可移植性。 反射代碼破壞了抽象性,因此當平臺發生改變的時候,代碼的行為就有可能也隨著變化。