1、類加載器
1)類的加載
· 當程序要使用某個類時,如果該類還未被加載到內存中,則系統會通過加載,連接,初始化三步來實現對這個類進行初始化。
· 加載 :就是指將class文件讀入內存,並為之創建一個Class對象。任何類被使用時系統都會建立一個Class對象。
· 連接:
· 驗證 是否有正確的內部結構,並和其他類協調一致
· 准備 負責為類的靜態成員分配內存,並設置默認初始化值
· 解析 將類的二進制數據中的符號引用替換為直接引用
· 初始化:就是我們以前講過的初始化步驟
2)類初始化時機
· 創建類的實例
· 訪問類的靜態變量,或者為靜態變量賦值
· 調用類的靜態方法
· 使用反射方式來強制創建某個類或接口對應的java.lang.Class對象
· 初始化某個類的子類
· 直接使用java.exe命令來運行某個主類
3)類加載器
· 負責將.class文件加載到內在中,並為之生成對應的Class對象。雖然我們不需要關心類加載機制,但是了解這個機制我們就能更好的理解程序的運行。
· 類加載器的組成
· Bootstrap ClassLoader 根類加載器
· Extension ClassLoader 擴展類加載器
· Sysetm ClassLoader 系統類加載器
4)獲取Class文件對象的三種方式
Person類:
package cn.itcast_01; public class Person { private String name; int age; public String address; public Person() { } private Person(String name) { this.name = name; } Person(String name, int age) { this.name = name; this.age = age; } public Person(String name, int age, String address) { this.name = name; this.age = age; this.address = address; } public void show() { System.out.println("show"); } public void method(String s) { System.out.println("method " + s); } public String getString(String s, int i) { return s + "---" + i; } private void function() { System.out.println("function"); } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", address=" + address + "]"; } }
測試類:
package cn.itcast_01; /* * 反射:就是通過class文件對象,去使用該文件中的成員變量,構造方法,成員方法。 * * Person p = new Person(); * p.使用 * * 要想這樣使用,首先你必須得到class文件對象,其實也就是得到Class類的對象。 * Class類: * 成員變量 Field * 構造方法 Constructor * 成員方法 Method * * 獲取class文件對象的方式: * A:Object類的getClass()方法 * B:數據類型的靜態屬性class * C:Class類中的靜態方法 * public static Class forName(String className) * * 一般我們到底使用誰呢? * A:自己玩 任選一種,第二種比較方便 * B:開發 第三種 * 為什么呢?因為第三種是一個字符串,而不是一個具體的類名。這樣我們就可以把這樣的字符串配置到配置文件中。 */ public class ReflectDemo { public static void main(String[] args) throws ClassNotFoundException { // 方式1 Person p = new Person(); Class c = p.getClass(); Person p2 = new Person(); Class c2 = p2.getClass(); System.out.println(p == p2);// false System.out.println(c == c2);// true // 方式2 Class c3 = Person.class; // int.class; // String.class; System.out.println(c == c3); // 方式3 // ClassNotFoundException Class c4 = Class.forName("cn.itcast_01.Person"); System.out.println(c == c4); } }
2、反射
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。
要想解剖一個類,必須先要獲取到該類的字節碼文件對象。而解剖使用的就是Class類中的方法,所以先要獲取到每一個字節碼文件對應的Class類型的對象。
1)通過反射獲取構造方法並使用
· 獲取構造方法
getConstructors
getDeclaredConstructors
· 創建對象
newInstance()
con.newInstance(“zhangsan", 20);
例子1:
package cn.itcast_02; import java.lang.reflect.Constructor; import cn.itcast_01.Person; /* * 通過反射獲取構造方法並使用。 */ public class ReflectDemo { public static void main(String[] args) throws Exception { // 獲取字節碼文件對象 Class c = Class.forName("cn.itcast_01.Person"); // 獲取構造方法 // public Constructor[] getConstructors():所有公共構造方法 // public Constructor[] getDeclaredConstructors():所有構造方法 // Constructor[] cons = c.getDeclaredConstructors(); // for (Constructor con : cons) { // System.out.println(con); // } // 獲取單個構造方法 // public Constructor<T> getConstructor(Class<?>... parameterTypes) // 參數表示的是:你要獲取的構造方法的構造參數個數及數據類型的class字節碼文件對象 Constructor con = c.getConstructor();// 返回的是構造方法對象 // Person p = new Person(); // System.out.println(p); // public T newInstance(Object... initargs) // 使用此 Constructor 對象表示的構造方法來創建該構造方法的聲明類的新實例,並用指定的初始化參數初始化該實例。 Object obj = con.newInstance(); System.out.println(obj); // Person p = (Person)obj; // p.show(); } }
例子2:
package cn.itcast_02; import java.lang.reflect.Constructor; /* * 需求:通過反射去獲取該構造方法並使用: * public Person(String name, int age, String address) * * Person p = new Person("林青霞",27,"北京"); * System.out.println(p); */ public class ReflectDemo2 { public static void main(String[] args) throws Exception { // 獲取字節碼文件對象 Class c = Class.forName("cn.itcast_01.Person"); // 獲取帶參構造方法對象 // public Constructor<T> getConstructor(Class<?>... parameterTypes) Constructor con = c.getConstructor(String.class, int.class, String.class); // 通過帶參構造方法對象創建對象 // public T newInstance(Object... initargs) Object obj = con.newInstance("林青霞", 27, "北京"); System.out.println(obj); } }
例子3:
package cn.itcast_02; import java.lang.reflect.Constructor; /* * 需求:通過反射獲取私有構造方法並使用 * private Person(String name){} * * Person p = new Person("風清揚"); * System.out.println(p); */ public class ReflectDemo3 { public static void main(String[] args) throws Exception { // 獲取字節碼文件對象 Class c = Class.forName("cn.itcast_01.Person"); // 獲取私有構造方法對象 // NoSuchMethodException:每個這個方法異常 // 原因是一開始我們使用的方法只能獲取公共的,下面這種方式就可以了。 Constructor con = c.getDeclaredConstructor(String.class); // 用該私有構造方法創建對象 // IllegalAccessException:非法的訪問異常。 // 暴力訪問 con.setAccessible(true);// 值為true則指示反射的對象在使用時應該取消Java語言訪問檢查。 Object obj = con.newInstance("風清揚"); System.out.println(obj); } }
2)通過反射獲取成員變量並使用
· 獲取所有成員
getFields
getDeclaredFields
getDeclaredFields
· 獲取單個成員
getField
getDeclaredField
· 修改成員的值
set(Object obj,Object value) :將指定對象變量上此 Field 對象表示的字段設置為指定的新值。
例子:
package cn.itcast_03; import java.lang.reflect.Constructor; import java.lang.reflect.Field; /* * 通過發生獲取成員變量並使用 */ public class ReflectDemo { public static void main(String[] args) throws Exception { // 獲取字節碼文件對象 Class c = Class.forName("cn.itcast_01.Person"); // 獲取所有的成員變量 // Field[] fields = c.getFields(); // Field[] fields = c.getDeclaredFields(); // for (Field field : fields) { // System.out.println(field); // } /* * Person p = new Person(); p.address = "北京"; System.out.println(p); */ // 通過無參構造方法創建對象 Constructor con = c.getConstructor(); Object obj = con.newInstance(); System.out.println(obj); // 獲取單個的成員變量 // 獲取address並對其賦值 Field addressField = c.getField("address"); // public void set(Object obj,Object value) // 將指定對象變量上此 Field 對象表示的字段設置為指定的新值。 addressField.set(obj, "北京"); // 給obj對象的addressField字段設置值為"北京" System.out.println(obj); // 獲取name並對其賦值 // NoSuchFieldException Field nameField = c.getDeclaredField("name"); // IllegalAccessException nameField.setAccessible(true); nameField.set(obj, "林青霞"); System.out.println(obj); // 獲取age並對其賦值 Field ageField = c.getDeclaredField("age"); ageField.setAccessible(true); ageField.set(obj, 27); System.out.println(obj); } }
3)通過反射獲取成員方法並使用
· 獲取所有方法
getMethods
getDeclaredMethods
· 獲取單個方法
getMethod
getDeclaredMethod
· 暴力訪問
method.setAccessible(true);
例子1:
package cn.itcast_04; import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class ReflectDemo { public static void main(String[] args) throws Exception { // 獲取字節碼文件對象 Class c = Class.forName("cn.itcast_01.Person"); // 獲取所有的方法 // Method[] methods = c.getMethods(); // 獲取自己的包括父親的公共方法 // Method[] methods = c.getDeclaredMethods(); // 獲取自己的所有的方法 // for (Method method : methods) { // System.out.println(method); // } Constructor con = c.getConstructor(); Object obj = con.newInstance(); /* * Person p = new Person(); p.show(); */ // 獲取單個方法並使用 // public void show() // public Method getMethod(String name,Class<?>... parameterTypes) // 第一個參數表示的方法名,第二個參數表示的是方法的參數的class類型 Method m1 = c.getMethod("show"); // obj.m1(); // 錯誤 // public Object invoke(Object obj,Object... args) // 返回值是Object接收,第一個參數表示對象是誰,第二參數表示調用該方法的實際參數 m1.invoke(obj); // 調用obj對象的m1方法 System.out.println("----------"); // public void method(String s) Method m2 = c.getMethod("method", String.class); m2.invoke(obj, "hello"); System.out.println("----------"); // public String getString(String s, int i) Method m3 = c.getMethod("getString", String.class, int.class); Object objString = m3.invoke(obj, "hello", 100); System.out.println(objString); // String s = (String)m3.invoke(obj, "hello",100); // System.out.println(s); System.out.println("----------"); // private void function() Method m4 = c.getDeclaredMethod("function"); m4.setAccessible(true); m4.invoke(obj); } }
例子2:我給你ArrayList<Integer>的一個對象,我想在這個集合中添加一個字符串數據,如何實現呢?
package cn.itcast.test; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; /* * 我給你ArrayList<Integer>的一個對象,我想在這個集合中添加一個字符串數據,如何實現呢? */ public class ArrayListDemo { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { // 創建集合對象 ArrayList<Integer> array = new ArrayList<Integer>(); // array.add("hello"); // array.add(10); Class c = array.getClass(); // 集合ArrayList的class文件對象 Method m = c.getMethod("add", Object.class); m.invoke(array, "hello"); // 調用array的add方法,傳入的值是hello m.invoke(array, "world"); m.invoke(array, "java"); System.out.println(array); } }
例子3:寫一個方法,public void setProperty(Object obj, String propertyName, Object value){},此方法可將obj對象中名為propertyName的屬性的值設置為value。
Tool類:
package cn.itcast.test; import java.lang.reflect.Field; public class Tool { public void setProperty(Object obj, String propertyName, Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { // 根據對象獲取字節碼文件對象 Class c = obj.getClass(); // 獲取該對象的propertyName成員變量 Field field = c.getDeclaredField(propertyName); // 取消訪問檢查 field.setAccessible(true); // 給對象的成員變量賦值為指定的值 field.set(obj, value); } }
測試類:
package cn.itcast.test; public class ToolDemo { public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { Person p = new Person(); Tool t = new Tool(); t.setProperty(p, "name", "林青霞"); t.setProperty(p, "age", 27); System.out.println(p); System.out.println("-----------"); Dog d = new Dog(); t.setProperty(d, "sex", '男'); t.setProperty(d, "price", 12.34f); System.out.println(d); } } class Dog { char sex; float price; @Override public String toString() { return sex + "---" + price; } } class Person { private String name; public int age; @Override public String toString() { return name + "---" + age; } }