27.01_類的加載概述和加載時機
當(dāng)程序要使用某個類時潮改,如果該類還未被加載到內(nèi)存中徙菠,則系統(tǒng)會通過加載,連接鹤盒,初始化三步來實現(xiàn)對這個類進行初始化。
-
加載
- 就是指將
class
文件讀入內(nèi)存侦副,并為之創(chuàng)建一個Class
對象侦锯。任何類被使用時系統(tǒng)都會建立一個Class
對象。
- 就是指將
-
連接
- 1.驗證 是否有正確的內(nèi)部結(jié)構(gòu)秦驯,并和其他類協(xié)調(diào)一致
- 2.準備 負責(zé)為類的靜態(tài)成員分配內(nèi)存尺碰,并設(shè)置默認初始化值
- 3.解析 將類的二進制數(shù)據(jù)中的符號引用替換為直接引用
初始化 就是我們以前講過的初始化步驟
B:加載時機
* 創(chuàng)建類的實例
* 訪問類的靜態(tài)變量,或者為靜態(tài)變量賦值
* 調(diào)用類的靜態(tài)方法
* 使用反射方式來強制創(chuàng)建某個類或接口對應(yīng)的java.lang.Class對象
* 初始化某個類的子類
* 直接使用java.exe命令來運行某個主類
27.02_類加載器的概述和分類
- A:類加載器的概述 : 負責(zé)將
.class
文件加載到內(nèi)存中译隘,并為之生成對應(yīng)的Class
對象亲桥。雖然我們不需要關(guān)心類加載機制,但是了解這個機制我們就能更好的理解程序的運行固耘。 - B:類加載器的分類
-
Bootstrap ClassLoader
根類加載器 -
Extension ClassLoader
擴展類加載器 -
Sysetm ClassLoader
系統(tǒng)類加載器
-
- C:類加載器的作用
- Bootstrap ClassLoader 根類加載器
- 也被稱為引導(dǎo)類加載器题篷,負責(zé)Java核心類的加載
- 比如System,String等。在JDK中JRE的lib目錄下rt.jar文件中
- Extension ClassLoader 擴展類加載器
- 負責(zé)JRE的擴展目錄中jar包的加載厅目。
- 在JDK中JRE的lib目錄下ext目錄
- Sysetm ClassLoader 系統(tǒng)類加載器
- 負責(zé)在JVM啟動時加載來自java命令的class文件番枚,以及classpath環(huán)境變量所指定的jar包和類路徑
27.03_反射概述
- 1.1JAVA反射機制是在運行狀態(tài)中,對于任意一個類损敷,都能夠知道這個類的所有屬性和方法葫笼;
- 1.2對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性拗馒;
- 1.3這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制路星。
- 1.4要想解剖一個類,必須先要獲取到該類的字節(jié)碼文件對象。
- 1.5而解剖使用的就是
Class
類中的方法诱桂,所以先要獲取到每一個字節(jié)碼文件對應(yīng)的Class
類型的對象洋丐。 - 推薦2篇詳細講解Java反射的優(yōu)秀文章:
Java基礎(chǔ)與提高干貨系列——Java反射機制
Java反射詳解
Class clazz1 = Class.forName("com.bean.Person");
Class<?> clazz2 = Person.class;
Class<?> clazz3 = new Person().getClass(); //加不加<?>都一樣呈昔,
System.out.println(clazz1 == clazz2);
Class<?> 相當(dāng)于 Class<? extends Object>
?是個通配符,可以用任何由Object派生的類型代替
反射的三個階段.png
27.04_反射(Class.forName()讀取配置文件舉例)
// 反射和配置文件
BufferedReader br = new BufferedReader(new FileReader("config.plist"));
Class<?> clazz = Class.forName(br.readLine()); //從文件中讀取了類名(全類名)
br.close();
Juicer j = new Juicer();
j.run( (Apple)clazz.newInstance() ); //使用 Class 創(chuàng)建了對象
--------------------分割線-----------------------------
class Juicer {
public void run(Apple a) { a.squeeze(); }
}
class Apple {
public void squeeze() { System.out.println("炸出一杯蘋果汁"); }
}
27.05_通過反射獲取帶參構(gòu)造方法并使用
- 關(guān)鍵類:
Constructor
構(gòu)造方法類
Class
類的newInstance()
方法是使用該類無參的構(gòu)造函數(shù)創(chuàng)建對象, 如果一個類沒有無參的構(gòu)造函數(shù), 就不能這樣創(chuàng)建了,可以調(diào)用Class
類的getConstructor(String.class,int.class)
方法獲取一個指定的構(gòu)造函數(shù)然后再調(diào)用Constructor
類的newInstance("張三",20)
方法創(chuàng)建對象
Class<?> clazz = Class.forName("com.bean.Person");
Constructor<?> c = clazz.getConstructor(String.class,int.class); //獲取class的構(gòu)造(有參)方法類
Person p = (Person) c.newInstance("張六",23); //使用有參構(gòu)造方法類創(chuàng)建對象
System.out.println(p);
System.out.println(clazz.getConstructors()); //獲取所有構(gòu)造方法
27.06_通過反射獲取成員變量并使用
- 關(guān)鍵類:
Field
成員變量類
Class.getField(String)
方法可以獲取類中的指定字段(可見的), 如果是私有的可以用getDeclaedField("name")
方法獲取,通過set(obj, "李四")
方法可以設(shè)置指定對象上該字段的值, 如果是私有的需要先調(diào)用setAccessible(true)
設(shè)置訪問權(quán)限,用獲取的指定的字段調(diào)用get(obj)
可以獲取指定對象中該字段的值
// 通過反射友绝,獲取 成員變量
Class<?> clazz = Class.forName("com.bean.Person");
Constructor<?> c = clazz.getConstructor(String.class,int.class); //獲取class的構(gòu)造(有參)方法類
Person p = (Person) c.newInstance("張六",23); //使用有參構(gòu)造方法類創(chuàng)建對象
//Field f = clazz.getField("name"); //獲取name成員屬性 只能獲取public的成員屬性
Field f = clazz.getDeclaredField("name"); //暴力反射堤尾,獲取私有字段(成員屬性/變量)
f.setAccessible(true); //去除私有權(quán)限,必須這樣只會九榔,才能修改值
f.set(p, "我日哀峻,這也可以"); //修改字段的值
System.out.println(p);
27.07_通過反射獲取方法并使用
- 關(guān)鍵類:
Method
成員方法類
Class.getMethod(String, Class...)
和Class.getDeclaredMethod(String, Class...)
方法可以獲取類中的指定方法,調(diào)用invoke(Object, Object...)
可以調(diào)用該方法,比如Class.getMethod("eat") invoke(obj)
和Class.getMethod("eat",int.class) invoke(obj,10)
Class<?> clazz = Class.forName("com.bean.Person");
Constructor<?> c = clazz.getConstructor(String.class , int.class);
Person p = (Person) c.newInstance("大叔",40);
Method m = clazz.getMethod("eat"); //獲取eat方法,沒有參數(shù)
m.invoke(p); //運行獲取的eat方法哲泊,沒有參數(shù)
Method m3 = clazz.getMethod("eat", int.class); //獲取eat方法剩蟀,帶參數(shù)的
m3.invoke(p, 12); //運行獲取的eat方法。帶參數(shù)切威。
Method m2 = clazz.getDeclaredMethod("sleep"); //獲取私有方法育特,沒有參數(shù)
m2.setAccessible(true); //想要運行私有方法,必須先讓方法可見
m2.invoke(p); //運行獲取的私有方法先朦,沒有參數(shù)
27.08_通過反射越過泛型檢查)
-
ArrayList<Integer>
的一個對象缰冤,在這個集合中添加一個字符串?dāng)?shù)據(jù),如何實現(xiàn)呢喳魏?
// 1.不通過反射的辦法, 泛型擦除棉浸,泛型只是在編譯期做語法檢查的,運行期泛型會被擦除掉
ArrayList list = null;
ArrayList<Integer> intList = new ArrayList<>();
intList.add(100);
list = intList; //改變list的內(nèi)存指向
list.add("XXOO...我是字符串..."); //實際此時已經(jīng)把字符串 添加到 ArrayList<Integer> 里面了
System.out.println(intList);
// 2.通過 反射
ArrayList<Integer> intList = new ArrayList<>();
intList.add(100);
Class<?> clazz = Class.forName("java.util.ArrayList");
Method m = clazz.getMethod("add", Object.class); //獲取add方法
m.invoke(intList, "我是字符串刺彩,我進入int泛型集合了迷郑!");
System.out.println(intList);
27.09_通過反射寫一個通用的設(shè)置某個對象的某個屬性為指定的值
/**
* 把某個對象的屬性,改成自己想要的值
* @param obj 對象
* @param k 對象的屬性名稱创倔,字符串
* @param v 要改成的值
*/
public static void setPropertyByName(Object obj, String k, Object v) throws Exception {
Class<?> clazz = obj.getClass();
Field f = clazz.getDeclaredField(k); //不管私有方法還是public的嗡害,都能獲取 暴力反射
f.setAccessible(true); //去除權(quán)限
f.set(obj, v);
}
27.11_動態(tài)代理的概述和實現(xiàn)
- 動態(tài)代理:在程序運行過程中產(chǎn)生的這個對象,而程序運行過程中產(chǎn)生對象其實就是我們剛才反射講解的內(nèi)容,所以畦攘,動態(tài)代理其實就是通過反射來生成一個代理霸妹。
- 在Java中
java.lang.reflect
包下提供了一個Proxy
類和一個InvocationHandler
接口,通過使用這個類和接口就可以生成動態(tài)代理對象知押。JDK提供的代理只能針對接口做代理叹螟。我們有更強大的代理cglib
(JavaEE講),Proxy
類中的方法創(chuàng)建動態(tài)代理類對象.
interface Animal { void eat(); }
class Dog implements Animal {
public void eat() {
System.out.println("狗改不了吃屎朗徊!");
}
}
---------------分割線---------------
class MyInvocationHandler implements InvocationHandler { //自定義代理功能類
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("先做一些事情...");
method.invoke(target, args); //執(zhí)行被代理target對象的方法首妖,多個參數(shù)本的本質(zhì)是數(shù)組
System.out.println("后做一些事情...");
return null;
}
}
---------------分割線---------------
Dog dog = new Dog();
MyInvocationHandler delegate = new MyInvocationHandler(dog);
ClassLoader l = dog.getClass().getClassLoader(); //固定寫法
Class<?>[] inter = dog.getClass().getInterfaces(); //固定寫法
Animal animal = (Animal) Proxy.newProxyInstance(l, inter, delegate);
animal.eat(); //等于是讓代理類,在方法運行時 多處理了一些事情爷恳。
27.12_設(shè)計模式(模版(Template)設(shè)計模式概述和使用)
模版方法模式就是定義一個算法的骨架,而將具體的算法延遲到子類中來實現(xiàn)
- a:優(yōu)點 : 使用模版方法模式象踊,在定義算法骨架的同時温亲,可以很靈活的實現(xiàn)具體的算法棚壁,滿足用戶靈活多變的需求
- b:缺點 : 如果算法骨架有修改的話,則需要修改抽象類
// 一個抽象類栈虚,目的是計算一段代碼運行的耗時
// 但是運行什么代碼袖外,是不知道的,需要子類去實現(xiàn)具體的代碼
abstract class GetTime {
public final void getTime() { // final目的是不讓子類重寫
long s = System.currentTimeMillis();
code();
long e = System.currentTimeMillis();
System.out.println(e - s);
}
public abstract void code() ; // abstract必須讓子類重寫
}
27.14_設(shè)計模式
- 目前已學(xué)已講的設(shè)計模式:
1,裝飾 : 在不必改變原類文件和使用繼承的情況下魂务,動態(tài)地擴展一個對象的功能曼验。典型就是JavaIO框架。
2,單例 : 一個類有且僅有一個實例粘姜,并且自行實例化向整個系統(tǒng)提供鬓照。
3,簡單工廠 : 專門生產(chǎn)勢力的類,把類的實例過程抽取到一個專門的類里孤紧。
4,工廠方法 : 創(chuàng)建一個工廠接口和創(chuàng)建多個工廠實現(xiàn)類豺裆,這樣一旦需要增加新的功能,直接增加新的工廠類就可以了
5,適配器 : 將一個類的接口轉(zhuǎn)換成客戶希望的另外一個接口号显。典型就是GUI里各種事件的處理臭猜。
6,模版 : 定義一個操作中的算法的骨架,而將步驟延遲到子類中。
7,動態(tài)代理 : 在實現(xiàn)階段不用關(guān)心代理類押蚤,而在運行階段才指定哪一個對象蔑歌。
- 總體來說設(shè)計模式分為三大類:
- 創(chuàng)建型模式,共五種:工廠方法模式揽碘、抽象工廠模式次屠、單例模式、建造者模式钾菊、原型模式帅矗。
- 結(jié)構(gòu)型模式,共七種:適配器模式煞烫、裝飾器模式浑此、代理模式、外觀模式滞详、橋接模式凛俱、組合模式、享元模式料饥。
-
行為型模式蒲犬,共十一種:策略模式、模板方法模式岸啡、觀察者模式原叮、迭代子模式、責(zé)任鏈模式、命令模式奋隶、備忘錄模式擂送、狀態(tài)模式、訪問者模式唯欣、中介者模式嘹吨、解釋器模式。
其實還有兩類:并發(fā)型模式和線程池模式境氢。用一個圖片來整體描述一下:
設(shè)計模式1.png
設(shè)計模式2.png
END蟀拷。
我是小侯爺。
在魔都艱苦奮斗萍聊,白天是上班族问芬,晚上是知識服務(wù)工作者。
如果讀完覺得有收獲的話脐区,記得關(guān)注和點贊哦愈诚。
非要打賞的話,我也是不會拒絕的牛隅。