Java反射
1.Class 類
-
類是對象,類是java.lang.Class類的實例對象
public class MyClass { public static void main(String[] args) { // People的實例對象 People people = new People(); // 任何一個類,都是java.lang.Class的實例對象 } } // People本身就是一個對象,是java.lang.Class的對象 class People { }
-
Class類的三種表達方式
Class c1 = People.class; // 方法2:通過已知類對象的getClass方法 Class c2 = people.getClass(); // 方法3:通過Class類靜態方法forName() Class c3 = Class.forName("com.nj.reflect.People"); /* * c1、c2表示了People類的類類型(class type) * 類也是對象,是Class類的實例對象,這個對象我們稱爲該類的類類型 */ // 不管是c1、c2還是c3都代表People的類類型,一個類只可能是Class類的一個實例對象。 System.out.print(c1 == c2); // true System.out.print(c1 == c3); // true
-
獲取對象實例
// 通過People類的類類型c1、c2或c3創建該類的實例對象。 People people1 = (People) c1.newInstance(); // 需要有無參構造方法
2.動態加載類
-
Class.forName(“類的全稱”)
- 不僅表示了類的類類型,還代表了動態加載類
- 編譯時刻加載類是靜態加載類,運行時刻加載類是動態加載類。
-
靜態加載類
public class Animal { public static void main(String[] args) { if (args[0].equals("Dog")) { Dog dog = new Dog(); dog.eat(); } if (args[0].equals("Cat")) { Cat cat = new Cat(); cat.eat(); } } } class Dog { public void eat() { } } class Cat { public void eat() { } }
-
靜態加載類的缺點
1.如果Dog或者Cat不存在,會報錯。但是Dog和Cat不一定會使用。
2.通過new創建對象是靜態加載類,在編譯時刻,就需要加載所有可能使用到的類。
-
-
動態加載類
通過Class.forName()動態加載類,就不會在編譯時報錯了。
public class AnimalManager { public static void main(String[] args){ try { // 動態加載類,在運行時刻加載。編譯不會再報錯。 Class c = Class.forName(args[0]); // 創建對象,通過類類型創建該類的對象 Object o = c.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
但是我們通過c.newInstance()創建類對象的時候,問題來了,是該轉換成Dog,還是該轉換成Cat。所以,需要統一標準,創建Animals接口,使Dog和Cat都實現Animals接口。
public class AnimalManager { public static void main(String[] args){ try { // 動態加載類,在運行時刻加載。編譯不會再報錯。 Class c = Class.forName(args[0]); // 創建對象,通過類類型創建該類的對象 Animals animals = (Animals) c.newInstance(); animals.eat(); } catch (Exception e) { e.printStackTrace(); } } } interface Animals{ void eat(); } class Dog implements Animals{ @Override public void eat() {} } class Cat implements Animals{ @Override public void eat() {} }
-
優點
1.動態加載,更加靈活
2.方便拓展,如果想要加個Pig類,直接創建Pig類,實現Animals接口和方法即可,無需再修改AnimalManager類。
-
3.反射的使用
-
獲取class對象的相關信息
1.獲取class對象的成員變量
// 獲取class1對象的所有成員變量,不包括父類的成員變量 Field[] allFields = class1.getDeclaredFields(); // 獲取class1對象的public成員變量,包括父類裏面的成員變量 Field[] publicFields = class1.getFields(); // 獲取class1對象指定的成員變量,父類的無法獲取 Field name1 = class1.getDeclaredField("name"); // 獲取class1對象指定的public成員變量,包括查找父類 Field name2 = class1.getField("name"); // 獲取變量的名稱 String name = field.getName(); // 獲取變量的類型 Class type = field.getType();
2.獲取class對象的方法
// 獲取class1對象的所有方法,不包括父類的方法 Method[] allMethods = class1.getDeclaredMethods(); // 獲取class1對象的public方法,包括父類的public方法 Method[] publicMethods = class1.getMethods(); // 獲取class1對象指定的方法,父類的無法獲取 Method method1 = class1.getDeclaredMethod("getName"); // 獲取class1對象指定的方法,並且有一個int類型參數的方法 Method method2 = class1.getDeclaredMethod("getName", int.class); // 獲取class1對象有兩個參數的指定方法 Method method3 = class1.getDeclaredMethod("getName", int.class, String.class); // 獲取class1對象指定的public方法,包括父類的public方法。 Method method4 = class1.getMethod("getName");
3.獲取class對象的構造函數
// 獲取class1對象所有的構造函數 Constructor[] allConstructors = class1.getDeclaredConstructors(); // 獲取class1對象public的構造函數 Constructor[] publicConstructors = class1.getConstructors(); // 獲取class1對象指定參數的構造函數 Constructor constructor1 = class1.getDeclaredConstructor(int.class); // 獲取class1對象指定參數並且爲public的構造函數 Constructor constructor2 = class1.getConstructor(int.class, String.class);
4.class對象的其他方法
Annotation[] annotations = class1.getAnnotations();// 獲取class對象的所有註解 Annotation annotation = class1.getAnnotation(Override.class);// 獲取class對象指定註解 Class superclass = class1.getSuperclass(); // 獲取class對象直接父類的類對象 Type genericSuperclass = class1.getGenericSuperclass(); // 獲取class對象的直接父類的Type Type[] genericInterfaces = class1.getGenericInterfaces(); // 獲取class對象所有接口的Type集合 String name = class1.getName(); // 獲取class對象名稱,包括報名路徑 String simpleName = class1.getSimpleName(); // 獲取class類名 Package aPackage = class1.getPackage(); // 獲取class包信息 boolean annotation = class1.isAnnotation();// 判斷是否爲註解類 boolean anEnum = class1.isEnum();// 判斷是否爲枚舉 boolean anInterface = class1.isInterface();// 判斷是否爲接口類 boolean array = class1.isArray();//判斷是否爲集合類型 boolean primitive = class1.isPrimitive(); // 判斷是否爲原始類型(8個基礎類型) boolean anonymousClass = class1.isAnonymousClass(); // 判斷是否是匿名內部類 boolean annotation1 = class1.isAnnotationPresent(Override.class);//判斷是否被某個註解類修飾
5.Method的相關方法
Method method = class1.getMethod("getName", String.class); String name = method.getName(); // 獲取方法名稱 // 獲取方法的返回值類型的類類型 Class returnType = method.getReturnType(); // 獲取方法的參數列表類型的類類型的數組 Class[] parameterTypes = method.getParameterTypes();
-
Java反射的使用
1.生成來的實例對象
// 方法一:調用newInstance()方法,但是這個方法要求class1的類必須有無參構造函數。 Dog dog = (Dog) class1.newInstance(); // 方法二:通過getConstructor獲取有參的Constructor構造函數,再調用newInstance()創建實例 Constructor constructor = class1.getConstructor(String.class); Dog dog1 = (Dog) constructor.newInstance("dog");
2.調用類的方法
// 第一步:生成類對象 Dog dog = (Dog) class1.newInstance(); // 第二步:通過class對象的getMethod()等方法,獲得具體需要執行的Method對象。 Method method = class1.getDeclaredMethod("getName", String.class); // 如果方法是private修飾,直接調用invoke,會報錯,Java要求程序必須有調用該方法的權限。 // 調用setAccessible,並將參數設置爲true,就會取消Java語言的訪問權限檢查。 method.setAccessible(true); // 調用Method對象的invoke方法,第一個參數是調用該方法的實例對象,第二個參數是對應需要傳入的參數。 // invoke的返回值:如果方法的返回值爲void,則invoke返回null,如果不是void,則返回具體的返回值 // 相當於 dog.getName("dog"); Object o = method.invoke(dog, "dog");
3.訪問成員變量的值
// 第一步:生成類對象 Dog dog = (Dog) class1.newInstance(); // 第二步:通過getDeclaredField等方法,獲取相應的Field對象 Field field = class1.getDeclaredField("age"); // 取消Java權限檢查 field.setAccessible(true); // 獲取dog對象中age成員變量的值 int age = field.getInt(dog); // 修改dog對象中age成員變量的值 field.setInt(dog, 25); // 獲取dog對象中String類型的成員變量 Field field1 = class1.getDeclaredField("name"); // 取消Java權限檢查 field1.setAccessible(true); // 獲取name變量的值 Object o = field1.get(dog); // 修改name變量的值 field1.set(dog, "sam");
參考資料
1.Java反射