1)裝載:查找并裝載類型的二進制數據 2)連接:執行驗證,準備,和解析(可選) a) 驗證:確保導入類型正確 b) 準備:為類變量分配內存,并將其初始化為默認值 c) 解析:把類型中的符號引用轉換成直接引用 3)初始化:把類變量初始化為默認初值 隨著Java虛擬機裝載了一個類,并執行了一些它選擇進行的驗證之后,類就可以進入準備階 段了。在準備階段,Java虛擬機為類變量分配內存,設置默認初始值:但在到達初始化階段之前, 類變量都沒有被初始化為真正的初始值。(在準備階段是不會執行Java代碼的。)在準備階段,虛 擬機把給類變量新分配的內存根據類型設置為默認值。
為了準備讓一個類或者接口被"首次主動"使用,最后一個步驟就是初始化,也就是為類變量 賦予正確的初始值。這里的”正確”初始值指的是程序員希望這個類變量所具備的起始值。正 確的初始值是和在準備階段賦予的默認初始值對比而言的。前面說過,根據類型的不同,類變 量已經被賦予了默認初始值。而正確的初始值是根據程序員制定的主觀計劃面生成的。
在Java代碼中,一個正確的初始值是通過類變量初始化語句或者靜態初始化語句給出的。 1)一個類變量初始化語句是變量聲明后面的等號和表達式: 2)靜態初始化語句是一個以static開頭的程序塊 example : public class Example1 { // 類變量初始化語句 static int value = (int) (Math.random()*6.0); // 靜態初始化語句 static{ System.out.println("this is example"); } } 所有的類變量初始化語句和類型的靜態初始化器都被Java編譯器收集在—起,放到——個特殊 的方法中。對于類來說,這個方法被稱作類初始化方法;對于接口來說,它被稱為接口初始化 方法。在類和接口的Javaclass文件中,這個方法被稱為”<clinit>”。通常的Java程序方法是無法 調用這個<clinit>方法的。這種方法只能被Java虛擬機調用
clinit>()方法 前面說過,Java編譯器把類變量初始化語句和靜態初始化浯句的代碼都放到class文件的 <clinit>()方法中,順序就按照它們在類或者接門聲明中出現的順序。 example: public class Example1 { static int width; static int height = (int) (Math.random()*6.0);
static{ width = (int) (Math.random()*3.0); } } java 編譯器生成下面<clinit>方法: 0 invokestatic java.lang.Math.random 3 ldc2_w 6.0 (double) 6 dmul 7 d2i 8 putstatic Example1.height 11 invokestatic java.lang.Math.random 14 ldc2_w 3.0 (double) 17 dmul 18 d2i 19 putstatic Example1.width 22 return
clinit 方法首先執行唯一的類變量初始化語句初始化heght,然后在靜態初始化語句中 初始化width(雖然它聲明在height之前,但那僅僅是聲明了類變量而不是類變量初始化語句).
除接口以外,初始化一個類之前必須保證其直接超類已被初始化,并且該初始化過程是由 Jvm 保證線程安全的。 另外,并非所有的類都會擁有一個 <clinit>() 方法。 1)如果類沒有聲明任何類變量,也沒有靜態初始化語句,那么它不會有<clinit>()方法。 2)如果聲明了類變量但是沒有使用類變量初始化語句或者靜態初始化語句初始它們,那么類不會有<clinit>()方法。 example: public class example{ static int val; } 3)如果類僅包含靜態 final 變量的類變量初始化語句,并且類變量初始化語句是編譯時常量表達式,類不會有<clinit>()方法。 example: public class Example { static final String str ="abc"; static final int value = 100; } 這種情況java編譯器把 str 和 value 被看做是常量,jvm會直接使用該類的常量池或者在字節碼中直接存放常量值。該類不會被加載。 如果接口不包含在編譯時解析成常量的字段初始化語句,接口中就包含一個<clinit>()方法。 example: interface Example{ int i =5; int hoursOfSleep = (int) (Math.random()*3.0); } 字段hoursOfSleep會被放在<clinit>()方法中(比較詭異???它被看作類變量了),而字段i被看作是編譯時常量特殊處理(JAVA語法規定,接口中的變量默認自動隱含是public static final)。 java 編譯器生成下面<clinit>方法: 0 invokestatic java.lang.Math.random 3 ldc2_w 3.0 (double) 6 dmul 7 d2i 8 putstatic Example.hoursOfSleep 11 return 主動使用和被動使用 在前面講過,Java虛擬機在首次主動使用類型時初始化它們。只有6種活動被認為是主動使 用: 1)創建類的新實例, 2)調用類中聲明的靜態方法, 3)操作類或者接口中聲明的非常量靜態字段, 4)調用JavaAPI中特定的反射方法 5)初始化一個類的子類; 6)以及指定一個類作為Java虛擬機啟動時的初始化類。 使用一個非常量的靜態字段只有當類或者接口的確聲明了這個字段時才是主動使用、比如, 類中聲明的字段可能會被子類引用;接口中聲明的字段可能會被子接口或者實現了這個接口的 類引用。對于子類、子接口和實現接口的類來說.這就是被動使用(使用它們并不會觸發 它們的初始化)。下面的例子說明了這個原理:
class NewParement{ static int hoursOfSleep = (int) (Math.random()*3.0); static{ System.out.println("new parement is initialized."); } }
class NewbornBaby extends NewParement{ static int hoursOfCry = (int) (Math.random()*2.0); static{ System.out.println("new bornBaby is initialized."); } }
public class Example1 { public static void main(String[] args){ int hours = NewbornBaby.hoursOfSleep; System.out.println(hours); } static{ System.out.println("example1 is initialized."); } } 運行結果: example1 is initialized. new parement is initialized. 0 NewbornBaby 沒有被初始化,也沒有被加載。
對象的生命周期 當java虛擬機創建一個新的類實例時不管明確的還是隱含的,首先要在堆中為保存對象的實例變量分配內存,包含所有在對象類中和它超類中 聲明的變量(包括隱藏的實例變量)都要分配內存。其次賦默認初值,最后賦予正確的初始值。
java編譯器為每個類都至少生成一個實例初始化方法 "<init>()"與構造方法相對應。
如果構造方法調用同一個類中的另一個構造方法(構造方法重載),它對應的init<>(): 1)一個同類init<>()調用。 2)對應構造方法體代碼的調用。 如果構造方法不是通過this()調用開始,且對象不是Object 它對應的init<>(): 1)一個超類init<>()調用。 2)任意實例變量初始化代碼調用。 3)對應構造方法體代碼的調用。 如果上述對象是Object,則去掉第一條。如果構造方法明確使用super()首先調用對應超類init<>()其余不變。 下面的例子詳細說明了實例變量初始化(摘自Java Language Specification) class Point{ int x,y; Point(){x=1;y=1;} } class ColoredPoint extends Point{ int color = OxFF00FF; } class Test{ public static void main(String[] args){ ColoredPoint cp = new ColoredPoint(); System.out.println(cp.color); } } 首先,為新的ColoredPoint實例分配內存空間,以存儲實例變量x,y和color;然后將這些變量初始化成默認值 在這個例子中都是0。 接下來調用無參數的ColoredPoint(),由于ColorPoint沒有聲明構造方法,java編譯器會自動提供如下的構造方 法:ColoredPoint(){super();}。 該構造方法然后調用無參數的Point(),而Point()沒有顯示的超類,編譯器會提供一個對其無參數的構造方法的 隱式調用:Point(){super();x=1;y=1}。 因此將會調用到Object();Object類沒有超類,至此遞歸調用會終止。接下來會調用Object任何實例初始化語句 及任何實例變量初始化語句。 接著執行Object()由于Object類中未聲明這樣的構造方法。因此編譯器會提供默認的構造方法object(){}。 但是執行該構造方法不會產生任何影響,然后返回。 接下來執行Point類實例變量初始化語句。當這個過程發生時,x,y的聲明沒有提供任何初始化表達式,因此這個 步驟未采取任何動作(x,y 仍為0); 接下來執行Point構造方法體,將x,y賦值為1。 接下來會執行類ColoredPoint的實例變量初始化語句。把color賦值0xFF00FF,最后執行ColoredPoint構造方法體 余下的部分(super()調用之后的部分),碰巧沒有任何語句,因此不需要進一步的動作,初始化完成。
與C++不同的是,在創建新的類實例期間,java編程語言不會為方法分派來指定變更的規則。如果調用的方法在被 初始化對象的子類中重寫,那么就是用重寫的方法。甚至新對象被完全初始化前也是如此。編譯和運行下面的例子 class Super{ Super(){printThree();} void printThree{System.out.println("Three");} } class Test extends Super{ int three = (int)Math.PI; // That is 3 public static void main(String args[]){ Test t = new Test(); t.printThree(); } void printThree(){System.out.println(three);} } 輸出: 0 3 這表明Super類中的printThree()沒有被執行。而是調用的Test中的printThree()。
posted @ 2010-07-14 16:18 AK47 閱讀(895) | 評論 (0) | 編輯 收藏
Java虛擬機體系結構
方法區 在Java虛擬機中,被裝載類型的信息存儲在一個邏輯上被稱為方法區的內存中。 當虛擬機裝載某個類型時,它使用類裝載器定位相應的class文件,-->讀入這個class文件(一個線性的二進制流)->將它傳入虛擬機--> 虛擬機提取類型信息,并將信息存入方法區,類型中的類(靜態)變量也存儲在方法區. 方法區特點: 1)所有線程共享方法區。它是線程安全的。 2)方法區大小不是固定的。虛擬機根據需要自行調整。 3)方法區可以被垃圾回收。 對于每個被裝載的類型,虛擬機會在方法區中存儲以下信息。 1)類型的基本信息; a)類型的全限定名 b)類型的直接超類全限定名(除非這個類型是java.lang.Objet,它沒超類)。 c)類型是類類型還是接口類型(就是說是一個類還是一個接口)。 d)類型的訪問修飾符(public ,abstract或final的某個子類) e)任何直接超接口的全限定名的有序列表。 2)該類型的常量池 虛擬機必須為每個被裝載的類型維護一個常量池。常量池就是該類型所用常量的一個有序集合, 包括直接常量(string,integer,floating point常量)和對其他類型、字段和方法的符號引用。 池中的數據項就像數組一樣是通過索引訪問的。因為常量池存儲了相應類型所用到的所有類型、 字段和方法的符號引用,所以它在Java程序的動態連接中起著核心的作用。 3)字段信息 類型中聲明的每一個字段,方法區中必須保存下面的信息,字段在類或接口中聲明的順序也必須保存。 字段名,字段類型,字段修飾符(public private protected static final 等) 4)方法信息 類型中聲明的每一個方法,方法區中必須保存下面的信息,方法在類或接口中聲明的順序也必須保存。 方法名,返回值類型,參數數量和類型(按聲明順序),方法修飾符(public private protected static final 等) 如果方法不是抽象的或本地的還必須保存:方法字節碼,操作數棧和該方法在棧針中局部變量的大小,異常表。 5)除了常量以外的所有類(靜態)變量 這里主要說下編譯時常量:就是那些用final聲明以及編譯時已知的值初始化的類變量(例如:static final int val =5) 每個編譯時常量的類型都會復制它所有常量到它自己的常量池中或者它的字節碼流中(通常情況下編譯時直接替換字節碼)。 6)一個到類classLoader的引用 指向ClassLoader類的引用 每個類型被裝載的時候,虛擬機必須跟蹤它是由啟動類裝載器 還是由用戶自定義類裝載器裝載的。如果是用戶自定義類裝載器裝載的,那么虛擬機必須在類 型信息中存儲對該裝載器的引用:這是作為方法表中的類型數據的一部分保存的。 虛擬機會在動態連按期間使用這個信息。當某個類型引用另一個類型的時候,虛擬機會請求裝載 發起引用類型的類裝載器來裝載被引用的類型。這個動態連接的過程,對于虛擬機分離命名空間 的方式也是至關重要的。為了能夠正確地執行動態連接以及維護多個命名空間,虛擬機需要在方 法表中得知每個類都是由哪個類裝載器裝載的。 7)一個到Class類的引用 指向Class類的引用 對于每一個被裝載的類型(不管是類還是接口),虛擬機都會相應地為 它創建一個java.lang.Class類的實例(Class實例放在內存中的堆區),而且虛擬機還必須以某種方式把這個實例的引用存儲在方法區 為了盡可能提高訪問效率,設計者必須仔細設計存儲在方法區中的類型信息的數據結構,因此, 除了以上時論的原始類型信息,實現中還可能包括其他數據結構以加快訪問原始數據的速度,比如方法表。 虛擬機對每個裝載的非抽象類,都生成一個方法表,把它作為類信息的一部分保存在方法區。方法表是一個數組, 它的元素是所有它的實例可能被調用的實例方法的直接引用,包括那些從超類繼承過來的實例方法:(對于抽象類和接口,方法表沒有什么幫 助,因為程序決不會生成它們的實例。)運行時可以通過方法表快速搜尋在對象中調用的實例方法。 方法區使用的例子
class Lava{ private int speed = 5; void flow(){ } }
public class Volcano { public static void main(String args[]){ Lava lava = new Lava(); lava.flow(); } }
1)虛擬機在方法區查找Volcano這個名字,未果,載入volcano.class文件,并提取相應信息 存入方法區。 2)虛擬機開始執行Volcano類中main()方法的字節碼的時候,盡管Lava類還沒被裝載, 但是和大多數(也許所有)虛擬機實現一樣,它不會等到把程序中用到的所有類都裝載后才開 始運行程序。恰好相反,它只在需要時才裝載相應的類。 3)main()的第一條指令告知虛擬機為列在常量池第一項的類分配足夠的內存。所以虛擬機 使用指向Volcano常量池的指針找到第一項,發現它是一個對Lava類的符號引用,然后它就檢查 方法區,看Lava類是否已經被裝載了。 4)當虛擬機發現還沒有裝載過名為"Lava"的類時,它就開始查找并裝載文件“Lava.class”, 并把從讀入的二進制數據中提取的類型信息放在方法區中。 5)虛擬機以一個直接指向方法區Lava類數據的指針來替換常量池第—項(就是那個 字符串“Lava”)——以后就可以用這個指針來快速地訪問Lava類了。這個替換過程稱為常量池 解析,即把常量池中的符號引用替換為直接引用:這是通過在方法區中搜索被引用的元素實現 的,在這期間可能又需要裝載其他類。在這里,我們替換掉符號引用的“直接引用”是一個本 地指針。 6)虛擬機準備為一個新的Lava對象分配內存。此時,它又需要方法區中的信息。還記 得剛剛放到Volcano類常量池第——項的指針嗎?現在虛擬機用它來訪問Lava類型信息(此前剛放 到方法區中的),找出其中記錄的這樣一個信息:一個Lava對象需要分配多少堆空間。 7)虛擬機確定一個Lava對象大小后,就在堆上分配空間,并把這個對象實例變量speed初始化為默認初始值0 8)當把新生成的Lava對象的引用壓到棧中,main()方法的第一條指令也完成了,指令通過這個引用 調用Java代碼(該代碼把speed變量初始化為正確初始值5).另外用這個引用調用Lava對象引用的flow()方法。
堆 每個java虛擬機實例都有一個方法區以及一個堆,一個java程序獨占一個java虛擬機實例,而每個java程序都有自己的堆空間,它們不會彼此干擾,但同一個java程序的多個線程共享一個堆空間。這種情況下要考慮多線程訪問同步問題。 Java棧 一個新線程被創建時,都會得到自己的PC寄存器和一個java棧,虛擬機為每個線程開辟內存區。這些內存區是私有的,任何線程不能訪問其他線程的PC寄存器和java棧。java棧總是存儲該線程中java方法的調用狀態。包括它的局部變量,被調用時傳進來的參數,它的返回值,以及運算的中間結果等。java棧是由許多棧幀或者說幀組成,一個棧幀包含一個java方法的調用狀態,當線程調用java方法時,虛擬機壓入一個新的棧幀到該線程的java棧中。當方法返回時,這個棧幀被從java棧中彈出并拋棄。 .本地方法棧 任何本地方法接口都會使用某種本地方法餞。當線程調用Java方法時,虛擬機會創建一個新的棧幀井壓人Java棧。 然而當它調用的是本地方法時,虛擬機會保持Java棧不變,不再在線程的Java棧中壓人新的幀,虛擬機只是簡單地動態連接 并直接調用指定的本地方法。可以把這看做是虛擬機利用本地方法來動態擴展自己。
posted @ 2010-07-06 13:47 AK47 閱讀(378) | 評論 (0) | 編輯 收藏
posted @ 2010-06-10 14:17 AK47 閱讀(453) | 評論 (0) | 編輯 收藏
這兩天,突然無法啟動我的MyEclipse6.5了,不知道為什么,提示錯誤: JVM terminated. Exit code=-1。
昨天,我以為是機器運行時間太長,重啟一下,果然好了。但是今天又來了。看了一下錯誤提示,我以為是JVM有問題,就在啟動Eclipse里加個JVM的參數,結果還是不行。
后來在網上找了一下,有人說是JAVA環境配置的問題,我想這不可能,因為以前一直用的好好的。有人說是JVM的問題,這個我剛剛換了一個,也不是這個問題,后來看來有人說是:eclipse.ini中內存設置過大的問題,雖然我不以為然,還是試了一下,以前我修改過內存設置,一直都好好的,之前eclipse.ini的配置如下:
-showsplash com.genuitec.myeclipse.product --launcher.XXMaxPermSize 512m -vmargs -Xms256m -Xmx512m -Duser.language=en -XX:PermSize=256M -XX:MaxPermSize=512M
現在修改了一下,-Xms256m改成-Xms128m,把Xmx512m 改為 Xmx256m,結果還真的好了,沒想到居然是這樣的小問題引起來的。
posted @ 2010-03-05 12:10 AK47 閱讀(262) | 評論 (0) | 編輯 收藏
這樣學習設計模式肯定便于理解: http://hi.baidu.com/xghzlg/blog/item/3288de589071d7202934f06f.html
從追MM談Java的23種設計模式 設計模式做為程序員的“內功心法”,越來越受到.net 社區的重視,這種變化是很可喜的,Java社區走在了我們的前面,但這種狀況也許有一天會發生改變。
從追MM談Java的23種設計模式
1、FACTORY—追MM少不了請吃飯了,麥當勞的雞翅和肯德基的雞翅都是MM愛吃的東西,雖然口味有所不同,但不管你帶MM去麥當勞或肯 德基,只管向服務員說“來四個雞翅”就行了。麥當勞和肯德基就是生產雞翅的Factory.
工廠模式:客戶類和工廠類分開。消費者任何時候需要某種產品,只需向工廠請求即可。消費者無須修改就可以接納新產品。缺點 是當產品修改時,工廠類也要做相應的修改。如:如何創建及如何向客戶端提供。
程序代碼
以下是引用片段:
以下是引用片段: public class Factory{ public String Boy = "boy" ; public String Girl = "girl" ; public People getPeople (String people){ if (people.equals("boy")){ return new Boy(); }else if(people.equals("girl")){ return new Girl(); } } }
2、BUILDER—MM最愛聽的就是“我愛你”這句話了,見到不同地方的MM,要能夠用她們的方言跟她說這句話哦,我有一個多種語言翻譯機,上面每種語言都有一個按鍵,見到MM我只要按對應的鍵,它就能夠用相應的語言說出“我愛你”這句話了,國外的MM也可以輕松搞掂,這就是我的“我愛你”builder。(這一定比美軍在伊拉克用的翻譯機好賣)
建造模式:將產品的內部表象和產品的生成過程分割開來,從而使一個建造過程生成具有不同的內部表象的產品對象。建造模式使得 產品內部表象可以獨立的變化,客戶不必知道產品內部組成的細節。建造模式可以強制實行一種分步驟進行的建造過程。
3、FACTORY METHOD—請MM去麥當勞吃漢堡,不同的MM有不同的口味,要每個都記住是一件煩人的事情,我一般采用Factory Method模 式,帶著MM到服務員那兒,說“要一個漢堡”,具體要什么樣的漢堡呢,讓MM直接跟服務員說就行了。
工廠方法模式:核心工廠類不再負責所有產品的創建,而是將具體創建的工作交給子類去做,成為一個抽象工廠角色,僅負責給出 具體工廠類必須實現的接口,而不接觸哪一個產品類應當被實例化這種細節。
4、PROTOTYPE—跟MM用QQ聊天,一定要說些深情的話語了,我搜集了好多肉麻的情話,需要時只要copy出來放到QQ里面就行了,這就是 我的情話prototype了。(100塊錢一份,你要不要)
原始模型模式:通過給出一個原型對象來指明所要創建的對象的類型,然后用復制這個原型對象的方法創建出更多同類型的對象。原始模型模式允許動態的增加或減少產品類,產品類不需要非得有任何事先確定的等級結構,原始模型模式適用于任何的等級結構。缺點是每一個類都必須配備一個克隆方法。
5、SINGLETON—俺有6個漂亮的老婆,她們的老公都是我,我就是我們家里的老公Sigleton,她們只要說道“老公”,都是指的同一個 人,那就是我(剛才做了個夢啦,哪有這么好的事)
單例模式:單例模式確保某一個類只有一個實例,而且自行實例化并向整個系統提供這個實例單例模式。單例模式只應在有真正的 “單一實例”的需求時才可使用。
以下是引用片段: public class SingLeton{ private static SingLeton instance = new SingLeton(); public static SingLeton getInstance(){ return instance; } }
6、ADAPTER—在朋友聚會上碰到了一個美女Sarah,從香港來的,可我不會說粵語,她不會說普通話,只好求助于我的朋友kent了,他 作為我和Sarah之間的Adapter,讓我和Sarah可以相互交談了(也不知道他會不會耍我)
適配器(變壓器)模式:把一個類的接口變換成客戶端所期待的另一種接口,從而使原本因接口原因不匹配而無法一起工作的兩個類 能夠一起工作。適配類可以根據參數返還一個合適的實例給客戶端。
7、BRIDGE—早上碰到MM,要說早上好,晚上碰到MM,要說晚上好; 碰到MM穿了件新衣服,要說你的衣服好漂亮哦,碰到MM新做的發型, 要說你的頭發好漂亮哦。不要問我“早上碰到MM新做了個發型怎么說”這種問題,自己用BRIDGE組合一下不就行了
橋梁模式:將抽象化與實現化脫耦,使得二者可以獨立的變化,也就是說將他們之間的強關聯變成弱關聯,也就是指在一個軟件系統的 抽象化和實現化之間使用組合/聚合關系而不是繼承關系,從而使兩者可以獨立的變化。
8、COMPOSITE—Mary今天過生日。“我過生日,你要送我一件禮物。”“嗯,好吧,去商店,你自己挑。”“這件T恤挺漂亮,買,這條裙子好看,買,這個包也不錯,買。”“喂,買了三件了呀,我只答應送一件禮物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻煩你包起來。”“……”,MM都會用Composite模式了,你會了沒有?
合成模式:合成模式將對象組織到樹結構中,可以用來描述整體與部分的關系。合成模式就是一個處理對象的樹結構的模式。合成 模式把部分與整體的關系用樹結構表示出來。合成模式使得客戶端把一個個單獨的成分對象和由他們復合而成的合成對象同等看待。
9、DECORATOR—Mary過完輪到Sarly過生日,還是不要叫她自己挑了,不然這個月伙食費肯定玩完,拿出我去年在華山頂上照的照片,在背面寫上“最好的的禮物,就是愛你的Fita”,再到街上禮品店買了個像框(賣禮品的MM也很漂亮哦),再找隔壁搞美術設計的Mike設計了一個漂亮的盒子裝起來……,我們都是Decorator,最終都在修飾我這個人呀,怎么樣,看懂了嗎?
裝飾模式:裝飾模式以對客戶端透明的方式擴展對象的功能,是繼承關系的一個替代方案,提供比繼承更多的靈活性。動態給一個 對象增加功能,這些功能可以再動態的撤消。增加由一些基本功能的排列組合而產生的非常大量的功能。
10、FA?ADE—我有一個專業的Nikon相機,我就喜歡自己手動調光圈、快門,這樣照出來的照片才專業,但MM可不懂這些,教了半天也不會。幸好相機有Fa?ade設計模式,把相機調整到自動檔,只要對準目標按快門就行了,一切由相機自動調整,這樣MM也可以用這個相機給我拍張照片了。
門面模式:外部與一個子系統的通信必須通過一個統一的門面對象進行。門面模式提供一個高層次的接口,使得子系統更易于使用 。每一個子系統只有一個門面類,而且此門面類只有一個實例,也就是說它是一個單例模式。但整個系統可以有多個門面類。
11、FLYWEIGHT—每天跟MM發短信,手指都累死了,最近買了個新手機,可以把一些常用的句子存在手機里,要用的時候,直接拿出來,在前面加上MM的名字就可以發送了,再不用一個字一個字敲了。共享的句子就是Flyweight,MM的名字就是提取出來的外部特征,根據上下文情況使用。
享元模式:FLYWEIGHT在拳擊比賽中指最輕量級。享元模式以共享的方式高效的支持大量的細粒度對象。享元模式能做到共享的關鍵是區分內蘊狀態和外蘊狀態。內蘊狀態存儲在享元內部,不會隨環境的改變而有所不同。外蘊狀態是隨環境的改變而改變的。外蘊狀態不能影響內蘊狀態,它們是相互獨立的。將可以共享的狀態和不可以共享的狀態從常規類中區分開來,將不可以共享的狀態從類里剔除出去。客戶端不可以直接創建被共享的對象,而應當使用一個工廠對象負責創建被共享的對象。享元模式大幅度的降低內存中對象的數量。
12、PROXY—跟MM在網上聊天,一開頭總是“hi,你好”,“你從哪兒來呀?”“你多大了?”“身高多少呀?”這些話,真煩人,寫個程序 做為我的Proxy吧,凡是接收到這些話都設置好了自動的回答,接收到其他的話時再通知我回答,怎么樣,酷吧。
代理模式:代理模式給某一個對象提供一個代理對象,并由代理對象控制對源對象的引用。代理就是一個人或一個機構代表另一個人或者一個機構采取行動。某些情況下,客戶不想或者不能夠直接引用一個對象,代理對象可以在客戶和目標對象直接起到中介的作用。客戶端分辨不出代理主題對象與真實主題對象。代理模式可以并不知道真正的被代理對象,而僅僅持有一個被代理對象的接口,這時候代理對象不能夠創建被代理對象,被代理對象必須有系統的其他角色代為創建并傳入。
以下是引用片段: public interface FactoryProxy{ public People createBoy(); public People creteGirl(); }
13、CHAIN OF RESPONSIBLEITY—晚上去上英語課,為了好開溜坐到了最后一排,哇,前面坐了好幾個漂亮的MM哎,找張紙條,寫上 “Hi,可以做我的女朋友嗎?如果不愿意請向前傳”,紙條就一個接一個的傳上去了,糟糕,傳到第一排的MM把紙條傳給老師了,聽說是個老處女呀,快跑!
責任鏈模式:在責任鏈模式中,很多對象由每一個對象對其下家的引用而接
起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個對象決定處理此請求。客戶并不知道鏈上的哪一個對象最終處理這個請求,系統可以在不影響客戶端的情況下動態的重新組織鏈和分配責任。處理者有兩個選擇:承擔責任或者把責任推給下家。一個請求可以最終不被任何接收端對象所接受。
14、COMMAND—俺有一個MM家里管得特別嚴,沒法見面,只好借助于她弟弟在我們倆之間傳送信息,她對我有什么指示,就寫一張紙條讓她弟弟帶給我。這不,她弟弟又傳送過來一個COMMAND,為了感謝他,我請他吃了碗雜醬面,哪知道他說:“我同時給我姐姐三個男朋友送 COMMAND,就數你最小氣,才請我吃面。”,
命令模式:命令模式把一個請求或者操作封裝到一個對象中。命令模式把發出命令的責任和執行命令的責任分割開,委派給不同的對象。命令模式允許請求的一方和發送的一方獨立開來,使得請求的一方不必知道接收請求的一方的接口,更不必知道請求是怎么被接收,以及操作是否執行,何時被執行以及是怎么被執行的。系統支持命令的撤消。
15、INTERPRETER—俺有一個《泡MM真經》,上面有各種泡MM的攻略,比如說去吃西餐的步驟、去看電影的方法等等,跟MM約會時,只 要做一個Interpreter,照著上面的腳本執行就可以了。
解釋器模式:給定一個語言后,解釋器模式可以定義出其文法的一種表示,并同時提供一個解釋器。客戶端可以使用這個解釋器來解釋這個語言中的句子。解釋器模式將描述怎樣在有了一個簡單的文法后,使用模式設計解釋這些語句。在解釋器模式里面提到的語言是指任何解釋器對象能夠解釋的任何組合。在解釋器模式中需要定義一個代表文法的命令類的等級結構,也就是一系列的組合規則。每一個命令對象都有一個解釋方法,代表對命令對象的解釋。命令對象的等級結構中的對象的任何排列組合都是一個語言。
16、ITERATOR—我愛上了Mary,不顧一切的向她求婚。
Mary:“想要我跟你結婚,得答應我的條件”
我:“什么條件我都答應,你說吧”
Mary:“我看上了那個一克拉的鉆石”
我:“我買,我買,還有嗎?”
Mary:“我看上了湖邊的那棟別墅”
Mary:“我看上那輛法拉利跑車”
我腦袋嗡的一聲,坐在椅子上,一咬牙:“我買,我買,還有嗎?”
……
迭代子模式:迭代子模式可以順序訪問一個聚集中的元素而不必暴露聚集的內部表象。多個對象聚在一起形成的總體稱之為聚集,聚集對象是能夠包容一組對象的容器對象。迭代子模式將迭代邏輯封裝到一個獨立的子對象中,從而與聚集本身隔開。迭代子模式簡化了聚集的界面。每一個聚集對象都可以有一個或一個以上的迭代子對象,每一個迭代子的迭代狀態可以是彼此獨立的。迭代算法可以獨立于聚集角色 變化。
17、MEDIATOR—四個MM打麻將,相互之間誰應該給誰多少錢算不清楚了,幸虧當時我在旁邊,按照各自的籌碼數算錢,賺了錢的從我這 里拿,賠了錢的也付給我,一切就OK啦,俺得到了四個MM的電話。
調停者模式:調停者模式包裝了一系列對象相互作用的方式,使得這些對象不必相互明顯作用。從而使他們可以松散偶合。當某些對象之間的作用發生改變時,不會立即影響其他的一些對象之間的作用。保證這些作用可以彼此獨立的變化。調停者模式將多對多的相互作用轉化為一對多的相互作用。調停者模式將對象的行為和協作抽象化,把對象在小尺度的行為上與其他對象的相互作用分開處理。
18、MEMENTO—同時跟幾個MM聊天時,一定要記清楚剛才跟MM說了些什么話,不然MM發現了會不高興的哦,幸虧我有個備忘錄,剛才與 哪個MM說了什么話我都拷貝一份放到備忘錄里面保存,這樣可以隨時察看以前的記錄啦。
備忘錄模式:備忘錄對象是一個用來存儲另外一個對象內部狀態的快照的對象。備忘錄模式的用意是在不破壞封裝的條件下,將一 個對象的狀態捉住,并外部化,存儲起來,從而可以在將來合適的時候把這個對象還原到存儲起來的狀態。
19、OBSERVER—想知道咱們公司最新MM情報嗎?加入公司的MM情報郵件組就行了,tom負責搜集情報,他發現的新情報不用一個一個通知 我們,直接發布給郵件組,我們作為訂閱者(觀察者)就可以及時收到情報啦
觀察者模式:觀察者模式定義了一種一隊多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態上發生 變化時,會通知所有觀察者對象,使他們能夠自動更新自己。
20、STATE—跟MM交往時,一定要注意她的狀態哦,在不同的狀態時她的行為會有不同,比如你約她今天晚上去看電影,對你沒興趣的 MM就會說“有事情啦”,對你不討厭但還沒喜歡上的MM就會說“好啊,不過可以帶上我同事么?”,已經喜歡上你的MM就會說“幾點鐘?看完電影再去泡吧怎么樣?”,當然你看電影過程中表現良好的話,也可以把MM的狀態從不討厭不喜歡變成喜歡哦。
狀態模式:狀態模式允許一個對象在其內部狀態改變的時候改變行為。這個對象看上去象是改變了它的類一樣。狀態模式把所研究的對象的行為包裝在不同的狀態對象里,每一個狀態對象都屬于一個抽象狀態類的一個子類。狀態模式的意圖是讓一個對象在其內部狀態改變的時候,其行為也隨之改變。狀態模式需要對每一個系統可能取得的狀態創立一個狀態類的子類。當系統的狀態變化時,系統便改變所選的子 類。
21、STRATEGY—跟不同類型的MM約會,要用不同的策略,有的請電影比較好,有的則去吃小吃效果不錯,有的去海邊浪漫最合適,單目 的都是為了得到MM的芳心,我的追MM錦囊中有好多Strategy哦。
策略模式:策略模式針對一組算法,將每一個算法封裝到具有共同接口的獨立的類中,從而使得它們可以相互替換。策略模式使得算法可以在不影響到客戶端的情況下發生變化。策略模式把行為和環境分開。環境類負責維持和查詢行為類,各種算法在具體的策略類中提供。由于算法和環境獨立開來,算法的增減,修改都不會影響到環境和客戶端。
22、TEMPLATE METHOD——看過《如何說服女生上床》這部經典文章嗎?女生從認識到上床的不變的步驟分為巧遇、打破僵局、展開追求、接吻、前戲、動手、愛撫、進去八大步驟(Template method),但每個步驟針對不同的情況,都有不一樣的做法,這就要看你隨機應變啦(具體實現);
模板方法模式:模板方法模式準備一個抽象類,將部分邏輯以具體方法以及具體構造子的形式實現,然后聲明一些抽象方法來迫使子類實現剩余的邏輯。不同的子類可以以不同的方式實現這些抽象方法,從而對剩余的邏輯有不同的實現。先制定一個頂級邏輯框架,而將邏輯的細節留給具體的子類去實現。
23、VISITOR—情人節到了,要給每個MM送一束鮮花和一張卡片,可是每個MM送的花都要針對她個人的特點,每張卡片也要根據個人的特點來挑,我一個人哪搞得清楚,還是找花店老板和禮品店老板做一下Visitor,讓花店老板根據MM的特點選一束花,讓禮品店老板也根據每個人特點選一張卡,這樣就輕松多了;
訪問者模式:訪問者模式的目的是封裝一些施加于某種數據結構元素之上的操作。一旦這些操作需要修改的話,接受這個操作的數據結構可以保持不變。訪問者模式適用于數據結構相對未定的系統,它把數據結構和作用于結構上的操作之間的耦合解脫開,使得操作集合可以相對自由的演化。訪問者模式使得增加新的操作變的很容易,就是增加一個新的訪問者類。訪問者模式將有關的行為集中到一個訪問者對象中,而不是分散到一個個的節點類中。當使用訪問者模式時,要將盡可能多的對象瀏覽邏輯放在訪問者類中,而不是放到它的子類中。訪問者模式可以跨過幾個類的等級結構訪問屬于不同的等級結構的成員類。
posted @ 2010-01-04 16:48 AK47 閱讀(251) | 評論 (0) | 編輯 收藏
看了下面的文章才徹底明白了ThreadLocal 與 getCurrentSession的關系 http://hi.baidu.com/%B7%C7%D4%C2%CE%DE%D0%C4/blog/item/8b14b8db49b40961d1164e54.html
1 getCurrentSession創建的session會和綁定到當前線程,而openSession不會。
2 getCurrentSession創建的線程會在事務回滾或事物提交后自動關閉,而openSession必須手動關閉
這里getCurrentSession本地事務(本地事務:jdbc)時 要在配置文件里進行如下設置
* 如果使用的是本地事務(jdbc事務) <property name="hibernate.current_session_context_class">thread</property> * 如果使用的是全局事務(jta事務) <property name="hibernate.current_session_context_class">jta</property>
getCurrentSession () 使用當前的session openSession() 重新建立一個新的session
在一個應用程序中,如果DAO 層使用Spring 的hibernate 模板,通過Spring 來控制session 的生命周期,則首選getCurrentSession ()。
使用Hibernate的大多數應用程序需要某種形式的“上下文相關的” session,特定的session在整個特定的上下文范圍內始終有效。然而,對不同類型的應用程序而言,要為什么是組成這種“上下文”下一個定義通常是困難的;不同的上下文對“當前”這個概念定義了不同的范圍。在3.0版本之前,使用Hibernate的程序要么采用自行編寫的基于 ThreadLocal的上下文session,要么采用HibernateUtil這樣的輔助類,要么采用第三方框架(比如Spring或Pico),它們提供了基于代理(proxy)或者基于攔截器(interception)的上下文相關session。從3.0.1版本開始,Hibernate增加了SessionFactory.getCurrentSession()方法。一開始,它假定了采用JTA事務,JTA事務定義了當前session的范圍和上下文(scope and context)。Hibernate開發團隊堅信,因為有好幾個獨立的JTA TransactionManager實現穩定可用,不論是否被部署到一個J2EE容器中,大多數(假若不是所有的)應用程序都應該采用JTA事務管理。基于這一點,采用JTA的上下文相關session可以滿足你一切需要。
更好的是,從3.1開始,SessionFactory.getCurrentSession()的后臺實現是可拔插的。因此,我們引入了新的擴展接口 (org.hibernate.context.CurrentSessionContext)和新的配置參數 (hibernate.current_session_context_class),以便對什么是“當前session”的范圍和上下文(scope and context)的定義進行拔插。
請參閱 org.hibernate.context.CurrentSessionContext接口的Javadoc,那里有關于它的契約的詳細討論。它定義了單一的方法,currentSession(),特定的實現用它來負責跟蹤當前的上下文session。Hibernate內置了此接口的兩種實現。
org.hibernate.context.JTASessionContext - 當前session根據JTA來跟蹤和界定。這和以前的僅支持JTA的方法是完全一樣的。詳情請參閱Javadoc。
org.hibernate.context.ThreadLocalSessionContext - 當前session通過當前執行的線程來跟蹤和界定。詳情也請參閱Javadoc。
這兩種實現都提供了“每數據庫事務對應一個session”的編程模型,也稱作每次請求一個session。Hibernate session的起始和終結由數據庫事務的生存來控制。假若你采用自行編寫代碼來管理事務(比如,在純粹的J2SE,或者 JTA/UserTransaction/BMT),建議你使用Hibernate Transaction API來把底層事務實現從你的代碼中隱藏掉。如果你在支持CMT的EJB容器中執行,事務邊界是聲明式定義的,你不需要在代碼中進行任何事務或 session管理操作。請參閱第 11 章 事務和并發一節來閱讀更多的內容和示例代碼。
hibernate.current_session_context_class 配置參數定義了應該采用哪個org.hibernate.context.CurrentSessionContext實現。注意,為了向下兼容,如果未配置此參數,但是存在org.hibernate.transaction.TransactionManagerLookup的配置,Hibernate會采用org.hibernate.context.JTASessionContext。一般而言,此參數的值指明了要使用的實現類的全名,但那兩個內置的實現可以使用簡寫,即"jta"和"thread"。
1、getCurrentSession()與openSession()的區別?
* 采用getCurrentSession()創建的session會綁定到當前線程中,而采用openSession() 創建的session則不會 * 采用getCurrentSession()創建的session在commit或rollback時會自動關閉,而采用openSession() 創建的session必須手動關閉 2、使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置: * 如果使用的是本地事務(jdbc事務) <property name="hibernate.current_session_context_class">thread</property> * 如果使用的是全局事務(jta事務) <property name="hibernate.current_session_context_class">jta</property>
利于ThreadLocal模式管理Session 早在Java1.2推出之時,Java平臺中就引入了一個新的支持:java.lang.ThreadLocal,給我們在編寫多線程程序 時提供了一種新的選擇。ThreadLocal是什么呢?其實ThreadLocal并非是一個線程的本地實現版本,它并不是一個Thread, 而是thread local variable(線程局部變量)。也許把它命名為ThreadLocalVar更加合適。線程局部變量(ThreadLocal) 其實的功用非常簡單,就是為每一個使用某變量的線程都提供一個該變量值的副本,是每一個線程都可以獨立地改變自己的副本, 而不會和其它線程的副本沖突。從線程的角度看,就好像每一個線程都完全擁有一個該變量。 ThreadLocal是如何做到為每一個線程維護變量的副本的呢?其實實現的思路很簡單,在ThreadLocal類中有一個Map, 用于存儲每一個線程的變量的副本。比如下面的示例實現(為了簡單,沒有考慮集合的泛型): public class HibernateUtil {
public static final ThreadLocal session =new ThreadLocal();
public static final SessionFactory sessionFactory; static { try { sessionFactory = new Configuration().configure().buildSessionFactory(); } catch (Throwable ex) { throw new ExceptionInInitializerError(ex); } }
public static Session currentSession() throws HibernateException { Session s = session.get(); if(s == null) { s = sessionFactory.openSession(); session.set(s); } return s; }
public static void closeSession() throws HibernateException { Session s = session.get(); if(s != null) { s.close(); } session.set(null); } }
原來一切都是那么簡單。
posted @ 2009-11-26 14:35 AK47 閱讀(680) | 評論 (0) | 編輯 收藏
<?xml version="1.0" encoding='UTF-8'?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping> <class> <!-- 設置該持久化類的二級緩存并發訪問策略 read-only read-write nonstrict-read-write transactional--> <cache usage="read-write"/> </class>
</hibernate-mapping>
posted @ 2009-11-18 14:12 AK47 閱讀(227) | 評論 (0) | 編輯 收藏
事務就是一系列的操作,這些操作完成一項任務.只要這些操作里有一個操作沒有成功,事務就操作失敗,發生回滾事件.即撤消前面的操作,這樣可以保證數據的一致性.而且可以把操作暫時放在緩存里,等所有操作都成功有提交數據庫,這樣保證費時的操作都是有效操作. 如果沒有特殊聲明,事務就是指數據庫事務簡單的講就是對數據庫表的添加、刪除、修改和查詢操作。 從編程的角度來說事務可由程序員來設置,(何時開啟,何時提交,何時回滾)如果沒有設置則按數據庫默認自動劃分事務。而事務最終在數據庫上執行.所以要求數據庫支持事務。
事務具有四個特征:原子性( Atomicity )、一致性( Consistency )、隔離性( Isolation )和持續性( Durability )。這四個特性簡稱為 ACID 特性。 1 、原子性 事務是數據庫的邏輯工作單位,事務中包含的各操作要么都做,要么都不做 2 、一致性 事務執行的結果必須是使數據庫從一個一致性狀態變到另一個一致性狀態。因此當數據庫只包含成功事務提交的結果時,就說數據庫處于一致性狀態。如果數據庫系統 運行中發生故障,有些事務尚未完成就被迫中斷,這些未完成事務對數據庫所做的修改有一部分已寫入物理數據庫,這時數據庫就處于一種不正確的狀態,或者說是 不一致的狀態。 3 、隔離性 一個事務的執行不能其它事務干擾。即一個事務內部的操作及使用的數據對其它并發事務是隔離的,并發執行的各個事務之間不能互相干擾。 4 、持續性 也稱永久性,指一個事務一旦提交,它對數據庫中的數據的改變就應該是永久性的。接下來的其它操作或故障不應該對其執行結果有任何影響。 數據庫系統是允許多個用戶共享數據庫資源,尤其是多個用戶可以同時存取相同數據。(多用戶同時對一個表操作也就是并發) 我們主觀上雖不想這么做,可是這種情況是存在的,沒有原因。而并發會破壞事務ACID特性 (隔離性,一致性)。
并發會帶來下列問題: 臟讀:一個事務讀取了未提交的事務 不可重復讀:同一個事務中多次讀取同一個數據返回的結果不同 幻讀:一個事務讀取到了另一個事務已提交的insert數據。 如果應用程序使用完全隔離的事務,那么同時執行多個事務的效果將與串行執行(一個接一個的順序執行)完全等效。為解決事務之間的并發帶來的個問題,必須在事務之間建立隔離關系(使用隔離級別)。
事務的隔離級別:就是對事務并發控制的等級,ANSI/ISO SQL將其分為串行化(SERIALIZABLE)、可重復讀(REPEATABLE READ)、讀已提交(READ COMMITED)、讀未提交(READ UNCOMMITED)四個等級 1 Serializable:最嚴格的級別,事務串行執行,資源消耗最大; 2 REPEATABLE READ:讀取數據的事務允許其他事務繼續訪問該行數據,但是未提交的寫事務將會禁止其他事務訪問該行。避免了“臟讀取”和“不可重復讀取”的情況,但是帶來了更多的性能損失。 3 READ COMMITTED:大多數主流數據庫的默認事務等級,保證了一個事務不會讀到另一個并行事務已修改但未提交的數據,避免了“臟讀取”。該級別適用于大多數系統。 4 Read Uncommitted:最低的事務隔離級別,保證了讀取過程中不會讀取到非法數據。
共享鎖:共享鎖用于讀取數據操作,它允許其他事務同時讀取某鎖定的資源,但不允許其他事務更新它。 排他鎖:排它鎖用于修改數據的場合。它鎖定的資源,其他事務不能讀取也不能修改。 更新鎖:更新鎖在更新操作的初始化階段用來鎖定可能要被修改的資源,從而避免使用共享鎖造成的死鎖現象
常見的并發控制鎖
http://hahalzb.blogbus.com/logs/19150842.html 心晴怡然
樂觀鎖
處理并發更新的一種方式是使用樂觀鎖(optimistic locking)。樂觀鎖的工作原理是讓應用程序檢查它即將更新的數據是否已被另一個事務修改(自該數據上次讀取以來)。實現樂觀鎖的一種常見做法是在每個表里添加一個版本字段,每次應用程序更新數據表記錄時就增加這個版本字段。每個UPDATE語句中的WHERE子句會根據上次讀取的值來判斷這個版本號是否改變。通過查看PreparedStatement.executeUpdate()返回的記錄數,應用程序可以判斷UPDATE語句是否成功。如果這條記錄已被另一個事務更新或刪除,應用程序可以回滾這個事務,并重新開始。 在直接執行SQL語句的應用程序中,樂觀鎖機制的實現非常容易。不過,使用諸如JDO和Hibernate的持久層構架時,實現更為容易,因為它們已將樂觀鎖作為配置選項提供。一旦啟用該配置選項,持久層框架會自動生成SQL UPDATE語句,執行版本檢查。第12章將分析樂觀鎖的使用時機及其缺點,并向你展示怎樣在iBATIS、JDO和Hibernate中使用樂觀鎖。 樂觀鎖的名稱源自如下假定情況,即并發更新的幾率極小,此外應用程序并不阻止并發更新,而是檢測并發更新,并從并發更新中恢復過來。另一種方式是使用悲觀鎖(pessimistic locking),它假定并發更新將會發生,因此必須預先阻止。
悲觀鎖
不同于樂觀鎖的另一種方式是使用悲觀鎖。當讀取某些記錄時,事務先鎖住這些記錄,這樣可以防止其他事務訪問這些數據記錄。具體細節要視數據庫而定,不過糟糕的是,并非所有數據庫都支持悲觀鎖。如果數據庫支持悲觀鎖,在直接執行SQL語句的應用程序中,實現悲觀鎖非常 容易。然而,正如你所預料的,在JDO或Hibernate應用程序中使用悲觀鎖更為容易。JDO以配置選項的方式提供悲觀鎖,而Hibernate則提供一個簡單實用的API,來鎖定對象。同樣,在第12章,你將學習何時使用悲觀鎖,分析其缺點,并看看怎樣在iBATIS、JDO和Hibernate中使用悲觀鎖。
posted @ 2009-11-11 17:23 AK47 閱讀(988) | 評論 (0) | 編輯 收藏
package com.kangdy.test;
public interface UserManager { public void addUser(String userName); }
public class UserManagerImpl implements UserManager {
public void addUser(String userName) { System.out.println("用戶 : "+userName+" 添加成功"); }
}
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy;
public class JDKStaticProxy implements InvocationHandler{ //目標對象索引 private Object targetObject; /* * 通過構造方法引入目標對象 */ public JDKStaticProxy(Object targetObject){ this.targetObject = targetObject; } /* * 創建代理對象 */ public Object createProxyObject(){ Object proxyObject = Proxy.newProxyInstance( this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this); return proxyObject; } /* * proxyObject:代理對象 * method: 被攔截到的目標對象的method * args: 被攔截到的目標對象的method的參數 * (non-Javadoc) * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) */ public Object invoke(Object proxyObject, Method method, Object[] args) throws Throwable { //添加業務邏輯 busniessLogic(); //代理運行目標對象的method Object result = method.invoke(this.targetObject, args); return result; } /* * 添加業務邏輯,這里只是簡單打印一句話。 */ private void busniessLogic(){ System.out.println("這是代理方法"); } }
代理類我添加很多注釋。應該很清楚了。這里我簡單說一下流程:當代理對象被調用的時候先會執行invoke方法,在此方法里面我們可添加 自己的業務邏輯代碼,然后才會執行目標對象的真實方法:method.invoke(this.targetObject, args);目標對象方法可能會有返回值,在這 里當存在返回值的時候我們返回一個Object.
下面代碼是客戶端調用和調用結果:
import org.junit.Test;
public class TestJDKStaticProxy { @Test public void testJDKStaticProxy(){ JDKStaticProxy proxy = new JDKStaticProxy(new UserManagerImpl()); UserManager userManager = (UserManager) proxy.createProxyObject(); userManager.addUser("張三"); } }
posted @ 2009-11-05 16:06 AK47 閱讀(994) | 評論 (0) | 編輯 收藏
以前做過Structs 的項目,可是一直沒做太深的研究,尤其是關于線程安全的 在網上搜了一下很多這方面的資料,引用了一些,總結了一下:
這篇文章對什么是線程安全的代碼和如何使用線程安全的代碼做了詳細闡述 http://hi.baidu.com/niujunkai/blog/item/021964adc130660a4a36d6ab.html 下面是它內容的引用:
1.什么是線程安全的代碼 在多線程環境下能正確執行的代碼就是線程安全的。 安全的意思是能正確執行,否則后果是程序執行錯誤,可能出現各種異常情況。
2.如何編寫線程安全的代碼 很多書籍里都詳細講解了如何這方面的問題,他們主要講解的是如何同步線程對共享資源的使用的問題。主要是對synchronized關鍵字的各種用法,以及鎖的概念。Java1.5中也提供了如讀寫鎖這類的工具類。這些都需要較高的技巧,而且相對難于調試。
但是,線程同步是不得以的方法,是比較復雜的,而且會帶來性能的損失。等效的代碼中,不需要同步在編寫容易度和性能上會更好些。 我這里強調的是什么代碼是始終為線程安全的、是不需要同步的。如下: 1)常量始終是線程安全的,因為只存在讀操作。 2)對構造器的訪問(new 操作)是線程安全的,因為每次都新建一個實例,不會訪問共享的資源。 3)最重要的是:局部變量是線程安全的。因為每執行一個方法,都會在獨立的空間創建局部變量,它不是共享的資源。局部變量包括方法的參數變量。 struts user guide里有: Only Use Local Variables - The most important principle that aids in thread-safe coding is to use only local variables, not instance variables , in your Action class. 譯:只使用用局部變量。--編寫線程安全的代碼最重要的原則就是,在Action類中只使用局部變量,不使用實例變量。
總結: 在Java的Web服務器環境下開發,要注意線程安全的問題。最簡單的實現方式就是在Servlet和Struts Action里不要使用類變量、實例變量,但可以使用類常量和實例常量。如果有這些變量,可以將它們轉換為方法的參數傳入,以消除它們。 注意一個容易混淆的地方:被Servlet或Action調用的類中(如值對象、領域模型類)中是否可以安全的使用實例變量?如果你在每次方法調用時 新建一個對 象,再調用它們的方法,則不存在同步問題---因為它們不是多個線程共享的資源,只有共享的資源才需要同步---而Servlet和Action的實例對于多個線程是共享 的。 換句話說,Servlet和Action的實例會被多個線程同時調用,而過了這一層,如果在你自己的代碼中沒有另外啟動線程,且每次調用后續業務對象時都是先 新建一個實例再調用,則都是線程安全的。
如果想加深理解servlet的多線程可以讀讀此文,闡述的很詳細。 http://hi.baidu.com/platon/blog/item/64a20ff3f96e7fce0b46e031.html
posted @ 2009-10-29 16:26 AK47 閱讀(1036) | 評論 (0) | 編輯 收藏
Powered by: BlogJava Copyright © AK47