2012年7月24日
版權信息: 可以任意轉載, 轉載時請務必以超鏈接形式標明文章原文出處, 即下面的聲明.
原文出處:http://blog.chenlb.com/2009/06/java-classloader-architecture.html
jvm classLoader architecture:
- Bootstrap ClassLoader/啟動類加載器
主要負責jdk_home/lib目錄下的核心 api 或 -Xbootclasspath 選項指定的jar包裝入工作。 - Extension ClassLoader/擴展類加載器
主要負責jdk_home/lib/ext目錄下的jar包或 -Djava.ext.dirs 指定目錄下的jar包裝入工作。 - System ClassLoader/系統類加載器
主要負責java -classpath/-Djava.class.path所指的目錄下的類與jar包裝入工作。 - User Custom ClassLoader/用戶自定義類加載器(java.lang.ClassLoader的子類)
在程序運行期間, 通過java.lang.ClassLoader的子類動態加載class文件, 體現java動態實時類裝入特性。
類加載器的特性:
- 每個ClassLoader都維護了一份自己的名稱空間, 同一個名稱空間里不能出現兩個同名的類。
- 為了實現java安全沙箱模型頂層的類加載器安全機制, java默認采用了 " 雙親委派的加載鏈 " 結構。

classloader-architecture

classloader-class-diagram
類圖中, BootstrapClassLoader是一個單獨的java類, 其實在這里, 不應該叫他是一個java類。因為,它已經完全不用java實現了。它是在jvm啟動時, 就被構造起來的, 負責java平臺核心庫。
自定義類加載器加載一個類的步驟

classloader-load-class
ClassLoader 類加載邏輯分析, 以下邏輯是除 BootstrapClassLoader 外的類加載器加載流程:
- // 檢查類是否已被裝載過
- Class c = findLoadedClass(name);
- if (c == null ) {
- // 指定類未被裝載過
- try {
- if (parent != null ) {
- // 如果父類加載器不為空, 則委派給父類加載
- c = parent.loadClass(name, false );
- } else {
- // 如果父類加載器為空, 則委派給啟動類加載加載
- c = findBootstrapClass0(name);
- }
- } catch (ClassNotFoundException e) {
- // 啟動類加載器或父類加載器拋出異常后, 當前類加載器將其
- // 捕獲, 并通過findClass方法, 由自身加載
- c = findClass(name);
- }
- }
線程上下文類加載器
java默認的線程上下文類加載器是 系統類加載器(AppClassLoader)。
- // Now create the class loader to use to launch the application
- try {
- loader = AppClassLoader.getAppClassLoader(extcl);
- } catch (IOException e) {
- throw new InternalError(
- "Could not create application class loader" );
- }
-
- // Also set the context class loader for the primordial thread.
- Thread.currentThread().setContextClassLoader(loader);
以上代碼摘自sun.misc.Launch的無參構造函數Launch()。
使用線程上下文類加載器, 可以在執行線程中, 拋棄雙親委派加載鏈模式, 使用線程上下文里的類加載器加載類.
典型的例子有, 通過線程上下文來加載第三方庫jndi實現, 而不依賴于雙親委派.
大部分java app服務器(jboss, tomcat..)也是采用contextClassLoader來處理web服務。
還有一些采用 hotswap 特性的框架, 也使用了線程上下文類加載器, 比如 seasar (full stack framework in japenese).
線程上下文從根本解決了一般應用不能違背雙親委派模式的問題.
使java類加載體系顯得更靈活.
隨著多核時代的來臨, 相信多線程開發將會越來越多地進入程序員的實際編碼過程中. 因此,
在編寫基礎設施時, 通過使用線程上下文來加載類, 應該是一個很好的選擇。
當然, 好東西都有利弊. 使用線程上下文加載類, 也要注意, 保證多根需要通信的線程間的類加載器應該是同一個,
防止因為不同的類加載器, 導致類型轉換異常(ClassCastException)。
為什么要使用這種雙親委托模式呢?
- 因為這樣可以避免重復加載,當父親已經加載了該類的時候,就沒有必要子ClassLoader再加載一次。
- 考慮到安全因素,我們試想一下,如果不使用這種委托模式,那我們就可以隨時使用自定義的String來動態替代java核心api中定義類型,這樣會存在非常大的安全隱患,而雙親委托的方式,就可以避免這種情況,因為String已經在啟動時被加載,所以用戶自定義類是無法加載一個自定義的ClassLoader。
java動態載入class的兩種方式:
- implicit隱式,即利用實例化才載入的特性來動態載入class
- explicit顯式方式,又分兩種方式:
- java.lang.Class的forName()方法
- java.lang.ClassLoader的loadClass()方法
用Class.forName加載類
Class.forName使用的是被調用者的類加載器來加載類的。
這種特性, 證明了java類加載器中的名稱空間是唯一的, 不會相互干擾。
即在一般情況下, 保證同一個類中所關聯的其他類都是由當前類的類加載器所加載的。
- public static Class forName(String className)
- throws ClassNotFoundException {
- return forName0(className, true , ClassLoader.getCallerClassLoader());
- }
-
- /** Called after security checks have been made. */
- private static native Class forName0(String name, boolean initialize,
- ClassLoader loader)
- throws ClassNotFoundException;
上面中 ClassLoader.getCallerClassLoader 就是得到調用當前forName方法的類的類加載器
static塊在什么時候執行?
- 當調用forName(String)載入class時執行,如果調用ClassLoader.loadClass并不會執行.forName(String,false,ClassLoader)時也不會執行.
- 如果載入Class時沒有執行static塊則在第一次實例化時執行.比如new ,Class.newInstance()操作
- static塊僅執行一次
各個java類由哪些classLoader加載?
- java類可以通過實例.getClass.getClassLoader()得知
- 接口由AppClassLoader(System ClassLoader,可以由ClassLoader.getSystemClassLoader()獲得實例)載入
- ClassLoader類由bootstrap loader載入
NoClassDefFoundError和ClassNotFoundException
- NoClassDefFoundError:當java源文件已編譯成.class文件,但是ClassLoader在運行期間在其搜尋路徑load某個類時,沒有找到.class文件則報這個錯
- ClassNotFoundException:試圖通過一個String變量來創建一個Class類時不成功則拋出這個異常
一:使用場景
1)使用的地方:樹形結構,分支結構等
2)使用的好處:降低客戶端的使用,為了達到元件與組合件使用的一致性,增加了元件的編碼
3)使用后的壞處:代碼不容易理解,需要你認真去研究,發現元件與組合件是怎么組合的
二:一個實際的例子
畫圖形,這個模式,稍微要難理解一點,有了例子就說明了一切,我畫的圖是用接口做的,代碼實現是抽象類為基類,你自己選擇了,接口也可以。

1)先建立圖形元件
package com.mike.pattern.structure.composite;
/**
* 圖形元件
*
* @author taoyu
*
* @since 2010-6-23
*/
public abstract class Graph {
/**圖形名稱*/
protected String name;
public Graph(String name){
this.name=name;
}
/**畫圖*/
public abstract void draw()throws GraphException;
/**添加圖形*/
public abstract void add(Graph graph)throws GraphException;
/**移掉圖形*/
public abstract void remove(Graph graph)throws GraphException;
}
2)建立基礎圖形圓
package com.mike.pattern.structure.composite;
import static com.mike.util.Print.print;
/**
* 圓圖形
*
* @author taoyu
*
* @since 2010-6-23
*/
public class Circle extends Graph {
public Circle(String name){
super(name);
}
/**
* 圓添加圖形
* @throws GraphException
*/
@Override
public void add(Graph graph) throws GraphException {
throw new GraphException("圓是基礎圖形,不能添加");
}
/**
* 圓畫圖
*/
@Override
public void draw()throws GraphException {
print(name+"畫好了");
}
/**
* 圓移掉圖形
*/
@Override
public void remove(Graph graph)throws GraphException {
throw new GraphException("圓是基礎圖形,不能移掉");
}
}
3)建立基礎圖形長方形
package com.mike.pattern.structure.composite;
import static com.mike.util.Print.print;
/**
* 長方形
*
* @author taoyu
*
* @since 2010-6-23
*/
public class Rectangle extends Graph {
public Rectangle(String name){
super(name);
}
/**
* 長方形添加
*/
@Override
public void add(Graph graph) throws GraphException {
throw new GraphException("長方形是基礎圖形,不能添加");
}
/**
* 畫長方形
*/
@Override
public void draw() throws GraphException {
print(name+"畫好了");
}
@Override
public void remove(Graph graph) throws GraphException {
throw new GraphException("長方形是基礎圖形,不能移掉");
}
}
4)最后簡歷組合圖形
package com.mike.pattern.structure.composite;
import java.util.ArrayList;
import java.util.List;
import static com.mike.util.Print.print;
/**
* 圖形組合體
*
* @author taoyu
*
* @since 2010-6-23
*/
public class Picture extends Graph {
private List<Graph> graphs;
public Picture(String name){
super(name);
/**默認是10個長度*/
graphs=new ArrayList<Graph>();
}
/**
* 添加圖形元件
*/
@Override
public void add(Graph graph) throws GraphException {
graphs.add(graph);
}
/**
* 圖形元件畫圖
*/
@Override
public void draw() throws GraphException {
print("圖形容器:"+name+" 開始創建");
for(Graph g : graphs){
g.draw();
}
}
/**
* 圖形元件移掉圖形元件
*/
@Override
public void remove(Graph graph) throws GraphException {
graphs.remove(graph);
}
}
5)最后測試
public static void main(String[] args)throws GraphException {
/**畫一個圓,圓里包含一個圓和長方形*/
Picture picture=new Picture("立方體圓");
picture.add(new Circle("圓"));
picture.add(new Rectangle("長方形"));
Picture root=new Picture("怪物圖形");
root.add(new Circle("圓"));
root.add(picture);
root.draw();
}
6)使用心得:的確降低了客戶端的使用情況,讓整個圖形可控了,當是你要深入去理解,才真名明白采用該模式的含義,不太容易理解。
一:使用場景
1)使用的地方:我想使用兩個不同類的方法,這個時候你需要把它們組合起來使用
2)目前使用的情況:我會把兩個類用戶組合的方式放到一起,編程思想think in java里已經提到個,能盡量用組合就用組合,繼承一般考慮再后。
3)使用后的好處:你不需要改動以前的代碼,只是新封裝了一新類,由這個類來提供兩個類的方法,這個時候:一定會想到facade外觀模式,本來是多個類使用的情況,我新封裝成一個類來使用,而這個類我采用組合的方式來包裝新的方法。我的理解是,設計模式本身就是為了幫助解決特定的業務場景而故意把模式劃分對應的模式類別,其實大多數情況,都解決了同樣的問題,這個時候其實沒有必要過多的糾纏到模式的名字上了,你有好的注意,你甚至取一個新的名字來概括這樣的使用場景。
4)使用的壞處:適配器模式,有兩種方式來實現。一個是組合一個是繼承,我覺得,首先應該考慮組合,能用組合就不要用繼承,這是第一個。第二個,你采用繼承來實現,那肯定會加大繼承樹結構,如果你的繼承關系本身就很復雜了,這肯定會加大繼承關系的維護,不有利于代碼的理解,或則更加繁瑣。繼承是為了解決重用的為題而出現的,所以我覺得不應該濫用繼承,有機會可以考慮同樣別的方案。
二:一個實際的例子
關聯營銷的例子,用戶購買完商品后,我又推薦他相關別的商品
由于減少代碼,方法我都不采用接口,直接由類來提供,代碼只是一個范例而已,都精簡了。
1)創建訂單信息
public class Order {
private Long orderId;
private String nickName;
public Order(Long orderId,String nickName){
this.orderId=orderId;
this.nickName=nickName;
}
/**
* 用戶下訂單
*/
public void insertOrder(){
}
}
2)商品信息
public class Auction {
/**商品名稱*/
private String name;
/**制造商*/
private String company;
/**制造日期*/
private Date date;
public Auction(String name,String company, Date date){
this.name=name;
this.company=company;
this.date=date;
}
/**
* 推廣的商品列表
*/
public void commendAuction(){
}
}
3)購物
public class Trade {
/**用戶訂單*/
private Order order;
/**商品信息*/
private Auction auction;
public Trade(Order order ,Auction auction){
this.order=order;
this.auction=auction;
}
/**
* 用戶產生訂單以及后續的事情
*/
public void trade(){
/**下訂單*/
order.insertOrder();
/**關聯推薦相關的商品*/
auction.commendAuction();
}
}
4)使用心得:其實外面采用了很多繼承的方式,order繼承auction之后,利用super .inserOrder()再加一個auction.recommendAuction(),實際上大同小異,我到覺得采用組合更容易理解以及代碼更加優美點。
一:使用場景
1)使用到的地方:如果你想創建類似汽車這樣的對象,首先要創建輪子,玻璃,桌椅,發動機,外廓等,這些部件都創建好后,最后創建汽車成品,部件的創建和汽車的組裝過程本身都很復雜的情況,希望把部件的創建和成品的組裝分開來做,這樣把要做的事情分割開來,降低對象實現的復雜度,也降低以后成本的維護,把汽車的部件創建和組裝過程獨立出兩個對應的工廠來做,有點類似建立兩個對應的部件創建工廠和汽車組裝工廠兩個工廠,而工廠只是創建一個成品,并沒有把里面的步驟也獨立出來,應該說Builder模式比工廠模式又進了一步。
2)采用Builder模式后的好處:把一個負責的對象的創建過程分解,把一個對象的創建分成兩個對象來負責創建,代碼更有利于維護,可擴性比較好。
3)采用Builder模式后的壞處:實現起來,對應的接口以及部件的對象的創建比較多,代碼相對來講,比較多了,估計剛開始你會有點暈,這個可以考慮代碼精簡的問題,增加代碼的可讀性。
二:一個實際的例子
汽車的組裝
1)首先創建汽車這個成品對象,包含什么的成員
public class Car implements Serializable{
/**
* 汽車序列號
*/
private static final long serialVersionUID = 1L;
/**汽車輪子*/
private Wheel wheel;
/**汽車發動機*/
private Engine engine;
/**汽車玻璃*/
private Glass glass;
/**汽車座椅*/
private Chair chair;
public Wheel getWheel() {
return wheel;
}
public void setWheel(Wheel wheel) {
this.wheel = wheel;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public Glass getGlass() {
return glass;
}
public void setGlass(Glass glass) {
this.glass = glass;
}
public Chair getChair() {
return chair;
}
public void setChair(Chair chair) {
this.chair = chair;
}
}
2)創建對應汽車零部件
public class Wheel {
public Wheel(){
print("--汽車輪子構建完畢--");
}
}
public class Engine {
public Engine(){
print("--汽車發動機構建完畢--");
}
}
public class Glass {
public Glass(){
print("--汽車玻璃構建完畢--");
}
}
public class Chair {
public Chair(){
print("--汽車座椅構建完畢--");
}
}
3)開始重點了,汽車成品的組裝過程
public interface Builder {
/**組裝汽車輪子*/
public void buildWheel();
/**組裝汽車發動機*/
public void buildEngine();
/**組裝汽車玻璃*/
public void buildGlass();
/**組裝汽車座椅*/
public void buildChair();
/**返回組裝好的汽車*/
public Car getCar();
}
以及實現類
public class CarBuilder implements Builder {
/**汽車成品*/
private Car car;
public CarBuilder(){
car=new Car();
}
/**組裝汽車輪子*/
@Override
public void buildChair() {
car.setChair(new Chair());
}
/**組裝汽車發動機*/
@Override
public void buildEngine() {
car.setEngine(new Engine());
}
/**組裝汽車玻璃*/
@Override
public void buildGlass() {
car.setGlass(new Glass());
}
/**組裝汽車座椅*/
@Override
public void buildWheel() {
car.setWheel(new Wheel());
}
/**返回組裝好的汽車*/
@Override
public Car getCar() {
buildChair();
buildEngine();
buildGlass();
buildWheel();
print("--整個汽車構建完畢--");
return car;
}
}
4)最后汽車創建測試
public static void main(String[] args) {
/**創建汽車組裝*/
Builder carBuilder=new CarBuilder();
Car car=carBuilder.getCar();
}
最后輸出:
--汽車座椅構建完畢--
--汽車發動機構建完畢--
--汽車玻璃構建完畢--
--汽車輪子構建完畢--
--整個汽車構建完畢--
5)體會心得:Builder模式實際的重點就把汽車的組裝過程和零部件的生產分開來實現,零部件的生成主要靠自己的對象來實現,我上面只是在構造函數里創建了,比較簡單,而重點汽車的組裝則交給CarBuilder來實現,最終由builder來先負責零部件的創建,最后返回出成品的汽車。
一:使用場景
1)經常使用的地方:一個類只有一個實例,eg:頁面訪問統計pv,統計的個數就只能保證一個實例的統計。
2)我們目前使用的情況:比如我想創建一個對象,這個對象希望只有一份實例的維護,在內存的保存也只有一份,也就是在同一個jvm的java堆里只保存一份實例對象,所以你會想一辦法,在創建這個對象的時候,就已經能保證只有一份。
3)怎么改進:定義該對象的時候,就保證是同一份實例,比如:定義為私有構造函數,防止通過new的方式可以創建對象,然后在對象里定義一個靜態的私有成員(本身對象的一個實例),然后再創建一個外面訪問該對象的方法就好了。
4)改進的好處:代碼在編譯代碼這個級別就被控制了,不至于在jvm里運行的時候才來保證,把唯一實例的創建保證在編譯階段;jvm里內存只有一份,從而內存占有率更低,以及更方便java垃圾回收
5)改進后的壞處:只能是代碼稍微需要更多點,其實大家最后發現改進后的壞處,都是代碼定義比之間要多一點,但以后的維護代碼就降下來了,也短暫的代碼量偏大來換取以后代碼的精簡。
二:一個實際的例子
總體的例子
package com.mike.pattern.singleton;
/**
* 總統
*
* @author taoyu
*
* @since 2010-6-22
*/
public class President {
private President(){
System.out.println("總統已經選舉出來了");
}
/**總統只有一個*/
private static President president=new President();
/**
* 返回總統
*/
public static President getPresident(){
return president;
}
/**
* 總統宣布選舉成功
*/
public void announce(){
System.out.println("偉大的中國人民,我將成你們新的總統");
}
}
/**
* @param args
*/
public static void main(String[] args) {
President president=President.getPresident();
president.announce();
}
1.使用場景
1)子類過多,不容易管理;構造對象過程過長;精簡代碼創建;
2)目前我們代碼情況: 編寫代碼的時候,我們經常都在new對象,創建一個個的對象,而且還有很多麻煩的創建方式,eg:HashMap<String,Float> grade=new HashMap<String,Float>(),這樣的代碼創建方式太冗長了,難道你沒有想過把這個創建變的短一點么,比如:HashMap<String,Float>grade=HashMapFactory.new(),可以把你創建精簡一點;你也可以還有別的需求,在創建對象的時候,你需要不同的情況,創建統一種類別的對象,eg:我想生成不同的汽車,創建小轎車,創建卡車,創建公交汽車等等,都屬于同種類別:汽車,你難道沒有想過,我把這些創建的對象在一個工廠里來負責創建,我把創建分開化,交給一人來負責,這樣可以讓代碼更加容易管理,創建方式也可以簡單點。
比如:Car BMW=CarFactory.create(bmw); 把創建new由一個統一負責,這樣管理起來相當方便
3)怎么改進:這個時候,你會想到,創建這樣同類別的東西,我把這個權利分出去,讓一個人來單獨管理,它只負責創建我的對象這個事情,所以你單獨簡歷一個對象來創建同類的對象,這個時候,你想這個東西有點像工廠一樣,生成同樣的產品,所以取了個名字:工廠模式,顧名思義,只負責對象的創建
4)改進后的好處:代碼更加容易管理了,代碼的創建要簡潔很多。
5)改進后的壞處:那就是你需要單獨加一個工廠對象來負責創建,多需要寫點代碼。
2.一個實際的例子
創建寶馬汽車與奔馳汽車的例子
1)先提取出一個汽車的公用接口Car
public interface Car{
/**行駛*/
public void drive();
}
2)寶馬和奔馳汽車對象
public class BMWCar implements Car {
/**
* 汽車發動
*/
public void drive(){
System.out.println("BMW Car drive");
}
}
public class BengCar implements Car {
/**
* 汽車發動
*/
public void drive(){
System.out.println("BengChi Care drive");
}
}
3)單獨一個汽車工廠來負責創建
public class FactoryCar {
/**
* 制造汽車
*
* @param company 汽車公司
* @return 汽車
* @throws CreateCarException 制造汽車失敗異常
*/
public static Car createCar(Company company)throws CreateCarException{
if(company==Company.BMW){
return new BMWCar();
}else if(company==Company.Beng){
return new BengCar();
}
return null;
}
}
4)最后的代碼實現:
Car BMWCar=FactoryCar.createCar(Company.BMW);
BMWCar.drive();
1. 我說下我對設計模式的理解:任何一樣事物都是因為有需求的驅動才誕生的,所以設計模式也不例外,我們平時在編寫代碼的時候,隨著時間的深入,發現很多代碼很難維護,可擴展性級差,以及代碼的效率也比較低,這個時候你肯定會想辦法讓代碼變的優美又能解決你項目中的問題,所以在面向對象語言里,你肯定會去發現很多可以重用的公用的方法,比如:接口的存在,你自然就想到了,讓你定義的方法與你的實現分開,也可以很方便把不同的類與接口匹配起來,形成了一個公用的接口,你會發現這樣做,好處會是非常多的,解決了你平時想把代碼的申明與邏輯實現的分開。
2. 這個時候,你發現了,本身面向對象的語言里,已經暗藏了很多好處,你肯定會仔細去分析面向對象這個語言,認真去挖掘里面更多的奧秘,最后,你發現了,原來你可以把面向對象的特性提取成一個公用的實現案例,這些案例里能幫助你解決你平時編寫代碼的困擾,而這樣一群人,就是所謂gof的成員,他們從平時設計建筑方面找到了靈感,建筑的設計也可以公用化以及重用化,所以他們也提取了相關的軟件設計方面的公用案例,也就有了下面的相關的所謂23種設計模式,而里面這么多模式,你也可以把他們歸類起來,最后發現就幾類模式:創建,結構,行為等模式類別,而這些現成的方案,也可以在實際應用中充分發揮作用,隨著大家的使用以及理解,發現其實這些所謂的模式里,你的確可以讓你的代碼變的更加優美與簡練。
3. 我比較喜歡把代碼變的更加優美與簡練,優美的代碼就是一看就懂,結構很清晰,而簡歷就是一目了然,又可以解決你的問題,就是代碼又少效率又高,所以平時要養成寫java doc的習慣,這樣的代碼才為清晰,所以才會更加優美。
4. 這些就是我對設計模式的理解,所以這么好的寶貝,我們不去深入的了解,的確可惜了,這就叫站到巨人的肩膀上.....
一:網絡配置
1.關掉防火墻
1) 重啟后生效
開啟: chkconfig iptables on
關閉: chkconfig iptables off
2) 即時生效,重啟后失效
開啟: service iptables start
關閉: service iptables stop
2.下載軟件
wget curl
3.安裝和解壓
安裝 rpm -ivh
升級 rpm -Uvh
卸載 rpm -e
tar -zxvf
二:網卡設置
1、 設置ip地址(即時生效,重啟失效)
#ifconfig eth0 ip地址 netmask 子網掩碼
2、 設置ip地址(重啟生效,永久生效)
#setup
3、 通過配置文件設置ip地址(重啟生效,永久生效)
#vi /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0 #設備名,與文件同名。
ONBOOT=yes #在系統啟動時,啟動本設備。
BOOTPROTO=static
IPADDR=202.118.75.91 #此網卡的IP地址
NETMASK=255.255.255.0 #子網掩碼
GATEWAY=202.118.75.1 #網關IP
MACADDR=00:02:2D:2E:8C:A8 #mac地址
4、 重啟網絡服務
#service network restart //重啟所有網卡
5、 禁用網卡,啟動網卡
#ifdown eth0
#ifup eth0
6、 屏蔽網卡,顯示網卡
#ifconfig eth0 down
#ifconfig eth0 up
7、 配置DNS客戶端(最多三個)
#vi /etc/resolv.conf
nameserver 202.99.96.68
8、更改主機名(即時生效)
#hostname 主機名
9、更改主機名(重啟計算機生效,永久生效)
#vi /etc/sysconfig/network
HOSTNAME=主機名
三:兩臺linux拷貝命令:scp
1.安裝scp:yum install openssh-clients
2.scp -r 本地用戶名@IP地址:文件名1 遠程用戶名@IP地址:文件名2