Java高級編程之類加載
當(dāng)程序要使用某個類時却嗡,如果該類還未被加載到內(nèi)存中闰渔,則系統(tǒng)會通過加載,連接秉颗,初始化三步來實現(xiàn)對這個類進行初始化痢毒。
- 加載
就是指將class文件讀入內(nèi)存,并為之創(chuàng)建一個Class對象蚕甥。
任何類被使用時系統(tǒng)都會建立一個Class對象哪替。 - 連接
- 驗證 是否有正確的內(nèi)部結(jié)構(gòu),并和其他類協(xié)調(diào)一致
- 準(zhǔn)備 負責(zé)為類的靜態(tài)成員分配內(nèi)存菇怀,并設(shè)置默認初始化值
- 解析 將類的二進制數(shù)據(jù)中的符號引用替換為直接引用
- 初始化 就是我們以前講過的初始化步驟
類初始化時機
在以下情況下會對類進行初始化
- 創(chuàng)建類的實例
- 訪問類的靜態(tài)變量凭舶,或者為靜態(tài)變量賦值
- 調(diào)用類的靜態(tài)方法
- 使用反射方式來強制創(chuàng)建某個類或接口對應(yīng)java.lang.Class對象
- 初始化某個類的子類
- 直接使用java.exe命令來運行某個主類
類加載器
類加載器的作用
- 負責(zé)將.class文件加載到內(nèi)在中,并為之生成對應(yīng)的Class對象爱沟。
- 雖然我們不需要關(guān)心類加載機制帅霜,但是了解這個機制我們就能更好的理解程序的運行
類加載的組成
- Bootstrap ClassLoader 根類加載器
- Extension ClassLoader 擴展類加載器
- Sysetm ClassLoader 系統(tǒng)類加載器
Bootstrap ClassLoader 根類加載器
也被稱為引導(dǎo)類加載器,負責(zé)Java核心類(支持java運行類)的加載
比如System,String等呼伸。在JDK中JRE的lib目錄下rt.jar文件中
Extension ClassLoader 擴展類加載器
負責(zé)JRE的擴展目錄中jar包的加載身冀。
在JDK中JRE的lib目錄下ext目錄
Sysetm ClassLo*ader 系統(tǒng)類加載器
加載我們自己寫的項目
負責(zé)在JVM啟動時加載來自java命令的class文件,以及classpath環(huán)境變量所指定的jar包和類路徑
Java高級編程之反射
通過類加載我們拿到了class文件括享,接下來就是使用反射來玩搂根。
java反射機制
JAVA反射機制是在運行狀態(tài)中,對于任意一個類铃辖,都能夠知道這個類的所有(包括private)屬性和方法剩愧;對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性娇斩;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制仁卷。
拿到class文件就可以去用它,而不是java文件犬第。
要想解剖一個類,必須先要獲取到該類的字節(jié)碼文件對象锦积。而解剖使用的就是Class類中的方法.所以先要獲取到每一個字節(jié)碼文件對應(yīng)的Class類型的對象.
總之就是通過class文件對象,去使用該文件的成員變量歉嗓,成員方法充包,構(gòu)造方法。
如何獲取Class類型的對象
一個Class類包含:
- 成員變量 Field
- 構(gòu)造方法 Constructor
- 成員方法 Method
Object類中的getClass()方法
Person p=new Person()
Class c=p.getClass()
Person p2=new Person()
Class c2=p2.getClass()
試問(c1==c2)結(jié)果是true還是false遥椿?
通過對象獲取的是Class文件對象(字節(jié)碼文件對象)Person就一個Class文件所以答案為true。
數(shù)據(jù)類型的靜態(tài)屬性class
Person.class String.class
Class類中的靜態(tài)方法
//path必須寫全路徑
Class.forName(path);
一般到底使用誰淆储?
A.自己玩任選一種冠场,第二種更方便
B. 開發(fā)選第三種因為第三種是一個字符串而不是一個具體的類名,這樣我們就可以把這個字符串配置到配置文件中本砰。
通過反射獲取構(gòu)造方法并使用
獲得字節(jié)碼文件對象
Class c =Class.forName("cn.zts.Person")
;-
獲取構(gòu)造方法
//public Constructor[] getConstructors() //獲取公共的構(gòu)造方法 //public Constructor[] getDeclaredConstructors() //獲取所有的構(gòu)造方法包括泛型 //獲取單個構(gòu)造方法(只能獲取公共的) // public Constructor getConstructor(Class<?>... paramterType) //參數(shù)表示的是:你要獲取構(gòu)造方法的構(gòu)造參數(shù)個數(shù)及數(shù)據(jù)類型的CLass字節(jié)碼文件對象.如String.class Constructor conn=c.getConstructor();返回的是構(gòu)造方法的對象 //通過該對象創(chuàng)建該類的實例碴裙。newInstance() Object object=conn.newInstance(Object...initarge);
以上內(nèi)容就相當(dāng)于
Person object=new Person()
;
暴力訪問
獲取私有的構(gòu)造方法
Constructor con=c.getDeclaredConstructor(String.class);
//如果直接訪問會報非法的訪問異常。所以此時我們需要暴力訪問
con.setAccessible(true)
;//值為true時指示反射的對象在使用時取消java語言訪問檢查
Object obj=con.newInstance("zts")
;
通過反射獲取成員變量并且使用
- 獲得字節(jié)碼文件對象
Class c =Class.forName("cn.zts.Person")
; - 獲取成員變量
c.getFields()//獲取所有的公共成員變量
c.getDeclaredFields()//獲取所有的成員變量
//獲取單個成員變量
Field addressField=c.getField("address");
//通過無參構(gòu)造方法創(chuàng)建對象
Constructor con=c.getConstructor();
Object obj=con.newInstance(); - 給成員變量賦值
//public void set(Object obj,Object value);
//將指定對象變量上找Field,對象表示的字段設(shè)置為指定的新值.
addressField.set(obj,"北京");
set方法的意思就是給obj對象的addressField字段設(shè)置值為北京舔株。
暴力訪問
獲取私有的成員變量
Field addressField=c.getDeclaredField("address");
//如果直接訪問會報非法的訪問異常莺琳。所以此時我們需要暴力訪問
con.setAccessible(true)
;//值為true時指示反射的對象在使用時取消java語言訪問檢查
通過反射獲取成員方法并且使用
- 獲得字節(jié)碼文件對象
Class c =Class.forName("cn.zts.Person")
; - 獲取成員方法
//獲取所有成員方法、
Method[] methods=c.getMethods()//獲取自己包括父親的所有公共方法载慈。
Method[] methods=c.getDeclaredMethods()//獲取自己所有的方法
//通過無參構(gòu)造方法創(chuàng)建對象
Constructor con=c.getConstructor();
Object obj=con.newInstance();
//獲取單個方法并且使用getMethod(String name,Class<?> ...paraterType)
Method m1=c.getMethod("show");
//obj.m1()//錯誤
//使用該方法--invoke(Object object,Object...args)惭等;
m1.invoke(obj);//調(diào)用obj對象的m1方法。
eg:
// 獲取字節(jié)碼文件對象
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)
// 第一個參數(shù)表示的方法名办铡,第二個參數(shù)表示的是方法的參數(shù)的class類型
Method m1 = c.getMethod("show");
// obj.m1(); // 錯誤
// public Object invoke(Object obj,Object... args)
// 返回值是Object接收,第一個參數(shù)表示對象是誰辞做,第二參數(shù)表示調(diào)用該方法的實際參數(shù)
m1.invoke(obj); // 調(diào)用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);
通過反射越過泛型檢查
比如。我們有個集合ArrayList<Integer> list我們需要給他添加個字符串類型的數(shù)據(jù)
創(chuàng)建集合對象
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"); // 調(diào)用array的add方法寡具,傳入的值是hello
m.invoke(array, "world");
m.invoke(array, "java");
System.out.println(array);
java高級編程之動態(tài)代理
- 代理:本來應(yīng)該自己做的事情秤茅,卻請了別人來做,被請的人就是代理對象童叠。
- 舉例:春季回家買票讓人代買
- 動態(tài)代理:在程序運行過程中產(chǎn)生的這個對象
而程序運行過程中產(chǎn)生對象其實就是我們剛才反射講解的內(nèi)容框喳,所以,動態(tài)代理其實就是通過反射來生成一個
在Java中java.lang.reflect包下提供了一個Proxy類和一個InvocationHandler接口厦坛,通過使用這個類和接口就可以生成動態(tài)代理對象五垮。JDK提供的代理只能針對接口做代理。我們有更強大的代理cglib
Proxy類中的方法創(chuàng)建動態(tài)代理類對象
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
最終會調(diào)用InvocationHandler的方法(InvocationHandler是個接口粪般,我們需要自定義一個類實現(xiàn)這個接口)
InvocationHandler
Object invoke(Object proxy,Method method,Object[] args)
eg:我們在如下例子實現(xiàn)在增刪改查中添加權(quán)限校驗和日志記錄用動態(tài)代理實現(xiàn)
-
StudentDao.java
public interface StudentDao { public abstract void login(); public abstract void regist(); }
-
StudentDaoImp.java
public class StudentDaoImpl implements StudentDao { @Override public void login() { System.out.println("登錄功能"); } @Override public void regist() { System.out.println("注冊功能"); } }
-
MyInvocationHandler.java
public class MyInvocationHandler implements InvocationHandler { private Object target; // 目標(biāo)對象 public MyInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("權(quán)限校驗"); Object result = method.invoke(target, args); System.out.println("日志記錄"); return result; // 返回的是代理對象 } }
-
Test.java
public class Test { public static void main(String[] args) { UserDao ud = new UserDaoImpl(); ud.add(); ud.delete(); ud.update(); ud.find(); System.out.println("-----------"); // 我們要創(chuàng)建一個動態(tài)代理對象 // Proxy類中有一個方法可以創(chuàng)建動態(tài)代理對象 // public static Object newProxyInstance(ClassLoader loader,Class<?>[] // interfaces,InvocationHandler h) // 我準(zhǔn)備對ud對象做一個代理對象 MyInvocationHandler handler = new MyInvocationHandler(ud); UserDao proxy = (UserDao) Proxy.newProxyInstance(ud.getClass() .getClassLoader(), ud.getClass().getInterfaces(), handler); proxy.add(); proxy.delete(); proxy.update(); proxy.find(); } }