posts - 22, comments - 32, trackbacks - 0, articles - 73
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          2009年10月12日

          最近在公司有點時間所以深入研究了下數據庫索引btree/b+tree數據結構和原理,由此牽引出了好多問題,請看如下帶著問題研究。

          1:為什么 btree/b+tree 數據結構適合數據庫索引,它到底是怎么樣一個原理和結構?

          btree/b+tree 數據結構:

          在之前的文章中我們介紹過AVL樹,紅黑樹,它們都屬于二叉樹,即每個節點最多只能擁有2個子節點,而B-tree(B樹)的每個節點可以擁有2個以上的子節點,所以我們簡單概括一下:B-tree就是一顆多路平衡查找樹,它廣泛應用于數據庫索引和文件系統中。

          首先我們介紹一下一顆 m 階B-tree的特性,那么這個 m 階是怎么定義的呢?這里我們以一個節點能擁有的最大子節點數來表示這顆樹的階數。舉個例子,如果一個節點最多有 n 個key,那么這個節點最多就會有 n+1 個子節點,這棵樹就叫做 n+1(m=n+1)階樹。一顆 m 階B-tree包括以下5條特性:

          1. 每個節點最多有 m 個子節點
          2. 除根節點和葉子節點,其它每個節點至少有 [m/2] (向上取整的意思)個子節點
          3. 若根節點不是葉子節點,則其至少有2個子節點
          4. 所有NULL節點到根節點的高度都一樣
          5. 除根節點外,其它節點都包含 n 個key,其中 [m/2] -1 <= n <= m-1

          這些特性可能看著不太好理解,下面我們會介紹B-tree的插入,在插入節點的過程中我們就會慢慢理解這些特性了。B-tree的插入比較簡單,就是一個節點至下而上的分裂過程。下面我們具體以一顆4階樹來展示B-tree的插入過程。

          首先我們 插入 200,300,400,沒有什么問題,直接插入就好。

          | 200 | 300 | 400 |

          現在我們接著插入500,這個時候我們發現有點問題,根據定義及特性1我們知道一顆4階B-tree的每個節點最多只能有3個key,插入500后這個節點就有4個key了。

          | 200 | 300 | 400 | 500 |

          這個時候我們就需要分裂,將中間的key上移到父節點,左邊的作為左節點,右邊的作為右節點,如下圖所示:

          這個時候我們是不是就明白特性3了,如果根節點不是葉子節點,那么它肯定發生了分裂,所以至少會有2個子節點。同樣我們接著插入600,700,800,900插入過程如下圖所示:

          現在根節點也已經滿了,如果我們繼續插入910,920,會怎樣呢?根節點就會繼續分裂,樹繼續向上生長。看下圖:

          通過整個的插入過程我們也會發現,B-tree和二叉樹的一個顯著的區別就是,B-tree是從下往上生長,而二叉樹是從上往下生長的。現在我們想想特性2和特性5是為什么?首先我們知道子節點的個數是等于key的數目+1,然后一個節點達到m個key后就會分裂,所以分裂后的節點最少能得到 m/2 - 1個key 。為啥還要減一呢?因為還要拿一個作為父節點。所以這個節點最少回擁有 m/2 - 1 + 1 = m/2 個子節點。同樣得到特性5,因為最少有m/2個子節點,所以最少就含有m/2-1個key,m 階樹,每個節點存到了m個key就會分裂,所以最多就有 m-1個key。

          根據以上特性我們能推出一棵含有N個總關鍵字數的m階的B-tree樹的最大高度h的值,

          樹的高度h: 1, 2, 3 , 4 ,.......... , h

          節點個數s: 1, 2, 2*(m/2), 2*(m/2)(m/2), ........ ,2*(m/2)的h-2次方

          s = 1 + 2(1 - (m/2)^{h-1} )/(1- (m/2))

          N = 1 + s * ((m/2) - 1) = 2 * ((m/2)^{h-1} ) - 1

          h = log┌m/2┐((N+1)/2 )+1

          2:為什么btree/b+tree 為常用數據庫索引結構?

          上文說過,紅黑樹等數據結構也可以用來實現索引,但是文件系統及數據庫系統普遍采用B-/+Tree作為索引結構,這一節將結合計算機組成原理相關知識討論B-/+Tree作為索引的理論基礎。

          一般來說,索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程中就要產生磁盤I/O消耗,相對于內存存取,I/O存取的消耗要高幾個數量級,所以評價一個數據結構作為索引的優劣最重要的指標就是在查找過程中磁盤I/O操作次數的漸進復雜度。換句話說,索引的結構組織要盡量減少查找過程中磁盤I/O的存取次數。下面先介紹內存和磁盤存取原理,然后再結合這些原理分析B-/+Tree作為索引的效率。

          主存存取原理

          目前計算機使用的主存基本都是隨機讀寫存儲器(RAM),現代RAM的結構和存取原理比較復雜,這里本文拋卻具體差別,抽象出一個十分簡單的存取模型來說明RAM的工作原理。

          圖5

          從抽象角度看,主存是一系列的存儲單元組成的矩陣,每個存儲單元存儲固定大小的數據。每個存儲單元有唯一的地址,現代主存的編址規則比較復雜,這里將其簡化成一個二維地址:通過一個行地址和一個列地址可以唯一定位到一個存儲單元。圖5展示了一個4 x 4的主存模型。

          主存的存取過程如下:

          當系統需要讀取主存時,則將地址信號放到地址總線上傳給主存,主存讀到地址信號后,解析信號并定位到指定存儲單元,然后將此存儲單元數據放到數據總線上,供其它部件讀取。

          寫主存的過程類似,系統將要寫入單元地址和數據分別放在地址總線和數據總線上,主存讀取兩個總線的內容,做相應的寫操作。

          這里可以看出,主存存取的時間僅與存取次數呈線性關系,因為不存在機械操作,兩次存取的數據的“距離”不會對時間有任何影響,例如,先取A0再取A1和先取A0再取D3的時間消耗是一樣的。

          磁盤存取原理

          上文說過,索引一般以文件形式存儲在磁盤上,索引檢索需要磁盤I/O操作。與主存不同,磁盤I/O存在機械運動耗費,因此磁盤I/O的時間消耗是巨大的。

          圖6是磁盤的整體結構示意圖。

          圖6

          一個磁盤由大小相同且同軸的圓形盤片組成,磁盤可以轉動(各個磁盤必須同步轉動)。在磁盤的一側有磁頭支架,磁頭支架固定了一組磁頭,每個磁頭負責存取一個磁盤的內容。磁頭不能轉動,但是可以沿磁盤半徑方向運動(實際是斜切向運動),每個磁頭同一時刻也必須是同軸的,即從正上方向下看,所有磁頭任何時候都是重疊的(不過目前已經有多磁頭獨立技術,可不受此限制)。

          圖7是磁盤結構的示意圖。

          圖7

          盤片被劃分成一系列同心環,圓心是盤片中心,每個同心環叫做一個磁道,所有半徑相同的磁道組成一個柱面。磁道被沿半徑線劃分成一個個小的段,每個段叫做一個扇區,每個扇區是磁盤的最小存儲單元。為了簡單起見,我們下面假設磁盤只有一個盤片和一個磁頭。

          當需要從磁盤讀取數據時,系統會將數據邏輯地址傳給磁盤,磁盤的控制電路按照尋址邏輯將邏輯地址翻譯成物理地址,即確定要讀的數據在哪個磁道,哪個扇區。為了讀取這個扇區的數據,需要將磁頭放到這個扇區上方,為了實現這一點,磁頭需要移動對準相應磁道,這個過程叫做尋道,所耗費時間叫做尋道時間,然后磁盤旋轉將目標扇區旋轉到磁頭下,這個過程耗費的時間叫做旋轉時間。

          局部性原理與磁盤預讀

          由于存儲介質的特性,磁盤本身存取就比主存慢很多,再加上機械運動耗費,磁盤的存取速度往往是主存的幾百分分之一,因此為了提高效率,要盡量減少磁盤I/O。為了達到這個目的,磁盤往往不是嚴格按需讀取,而是每次都會預讀,即使只需要一個字節,磁盤也會從這個位置開始,順序向后讀取一定長度的數據放入內存。這樣做的理論依據是計算機科學中著名的局部性原理:

          當一個數據被用到時,其附近的數據也通常會馬上被使用。

          程序運行期間所需要的數據通常比較集中。

          由于磁盤順序讀取的效率很高(不需要尋道時間,只需很少的旋轉時間),因此對于具有局部性的程序來說,預讀可以提高I/O效率。

          預讀的長度一般為頁(page)的整倍數。頁是計算機管理存儲器的邏輯塊,硬件及操作系統往往將主存和磁盤存儲區分割為連續的大小相等的塊,每個存儲塊稱為一頁(在許多操作系統中,頁得大小通常為4k),主存和磁盤以頁為單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置并向后連續讀取一頁或幾頁載入內存中,然后異常返回,程序繼續運行。

          B-/+Tree索引的性能分析

          到這里終于可以分析B-/+Tree索引的性能了。

          上文說過一般使用磁盤I/O次數評價索引結構的優劣。先從B-Tree分析,根據B-Tree的定義,可知檢索一次最多需要訪問h個節點。數據庫系統的設計者巧妙利用了磁盤預讀原理,將一個節點的大小設為等于一個頁,這樣每個節點只需要一次I/O就可以完全載入。為了達到這個目的,在實際實現B- Tree還需要使用如下技巧:

          每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁里,加之計算機存儲分配都是按頁對齊的,就實現了一個node只需一次I/O。

          B-Tree中一次檢索最多需要h-1次I/O(根節點常駐內存),漸進復雜度為O(h)=O(logdN)。一般實際應用中,出度d是非常大的數字,通常超過100,因此h非常小(通常不超過3)。

          綜上所述,用B-Tree作為索引結構效率是非常高的。

          而紅黑樹這種結構,h明顯要深的多。由于邏輯上很近的節點(父子)物理上可能很遠,無法利用局部性,所以紅黑樹的I/O漸進復雜度也為O(h),效率明顯比B-Tree差很多。

          上文還說過,B+Tree更適合外存索引,原因和內節點出度d有關。從上面分析可以看到,d越大索引的性能越好,而出度的上限取決于節點內key和data的大小:

          dmax = floor(pagesize / (keysize + datasize + pointsize))  (pagesize – dmax >= pointsize)

          dmax = floor(pagesize / (keysize + datasize + pointsize)) - 1  (pagesize – dmax < pointsize)

          floor表示向下取整。由于B+Tree內節點去掉了data域,因此可以擁有更大的出度,擁有更好的性能。

          這一章從理論角度討論了與索引相關的數據結構與算法問題,下一章將討論B+Tree是如何具體實現為MySQL中索引,同時將結合MyISAM和InnDB存儲引擎介紹非聚集索引和聚集索引兩種不同的索引實現形式。



          posted @ 2018-01-25 13:44 為自己代言 閱讀(3834) | 評論 (0)編輯 收藏

          同事遇到問題是一個java web 工程,依賴了一個jar包,但是jar包中也有自己一套配置文件(例spring 配置文件,資源文件等),這樣如果讓web工程中的war 包去加載jar 中的配置文件和資源文件呢?
          我當時也有一個思想誤區,以為web中加載不到jar中的文件,但是經過一番研究,終于明白了,按J2EE規范中web-inf/lib目錄下是web工程的classpath 目錄,容器會自動去掃描這個目錄下所有的配置文件和jar加載到容器中,即:像jar中有自己一套配置文件,war 中又要依賴jar包,這樣只需要把這些jar包打成war時候放到web-inf/lib下即可。
          注意:1:jar包和war包中配置文件和一些資源文件不能重名。
                  
                   2:要在war包中的spring 配置文件中引入jar包中的配置文件。



          posted @ 2015-04-24 20:42 為自己代言 閱讀(6937) | 評論 (0)編輯 收藏

          1、不同的tomcat的啟動文件startup.sh 中要指定各自的CATALINA_HOME和CATALINA_BASE這兩個環境變量。

          2、不同的tomcat啟動和關閉監聽不同的端口

          很多人喜歡把CATALINA_HOME和CATALINA_BASE配置到系統環境變量中去,我們不這么做,我們要做的只是把JDK及CLASSPATH配置到環境變量中去即可,因為這個可以通用。
          CATALINA_HOME和CATALINA_BASE的區別。簡單的說,CATALINA_HOME是Tomcat的安裝目 錄,CATALINA_BASE是Tomcat的工作目錄。如果我們想要運行Tomcat的 多個實例,但是不想安裝多個Tomcat軟件副本。那么我們可以配置多個工作 目錄,每個運行實例獨占一個工作目錄,但是共享同一個安裝目錄

          下面講具體的配置方法。

          找到Tomcat的startup.sh文件,打開進行編輯。

          在文件的開始位置,可以在一大堆注釋的后面,加入
          export CATALINA_BASE=/usr/ratest/apache-tomcat-7.0.16
          export CATALINA_HOME=/usr/ratest/apache-tomcat-7.0.16

          /usr/ratest/apache-tomcat-7.0.16這個就是tomcat的安裝文件夾位置,不同的tomcat指定相應的文件夾即可。

          注意,這兩句話一定要在exec “$PRGDIR”/”$EXECUTABLE” start “$@”這句話的前面,我們放在文件的開始位置了,所以,就可以不考慮了。

          然后就是要修改shutdown.sh文件,同樣在頭部加入上面的兩行即可,也要在exec “$PRGDIR”/”$EXECUTABLE” stop “$@”的前面。

          好了,解決了第一個問題,下面說第二個問題的解決方法。

          找到并打開server.xml文件,里面有諸如8080,8009,8443等等端口配置,統一給這些數字加上100,或者1000或者其他什么數字,只要是不跟其他Tomcat或者當前linux上其他服務的端口重復即可。

          現在進入Tomcat的bin文件夾,運行./startup.sh看看是不是可以啟動多個了。

          posted @ 2014-03-28 19:58 為自己代言 閱讀(466) | 評論 (0)編輯 收藏

          1,設置跟路徑時,三種方式

          在Tomcat默認安裝后,tomcat的主目錄是webapps/root目錄,所以如果想改變tomcat的主目錄的話可以如下所做,所以
          第一種方法是:
          打開C:/Tomcat/conf/server.xml,在<host></host>之間
          加入代碼:<Context docBase="d:/Tomcat 5.5/webapps/medi" path="" debug="0" reloadable="true"/>
          這樣重新啟動tomcat,我們的主目錄就被設置為dolphin這個項目了。

          第二種方法是:
          將tomcat安裝目錄下的ROOT下的所有文件全部刪除,然后將工程的解壓后的文件全部拷進去。

          第三種方法是:
          Tomcat5.0以下版本在d:/Tomcat/conf/Catalina/localhost目錄下會自動生成了一個ROOT.Xml,
          但是5.0以上版本不再生成此文件,所以可以新建個ROOT.xml,在里面加入如下代碼:
          <?Xml version='1.0' encoding='utf-8'?>
          <Context crossContext="true" docBase="d:/Tomcat 5.5/webapps/medi" path="" reloadable="true">
          </Context>

          2,注意
          此時即然配置了 默認訪問目錄,則不要再tomcat 部署工程了,
          比如 在 conf\Catalina\localhost 增加配置文件 指定的工程路徑 此文件要去掉, 否則會重復加載工程。

          posted @ 2013-11-06 18:44 為自己代言 閱讀(696) | 評論 (0)編輯 收藏

          很多tomcat進程退出(或者進程假死),都是由于頻繁的拋出OutOfMemeoryError導致的。

          為了讓tomcat退出前或者發生OutOfMemeoryError時自動dump堆棧信息,方便事后排查問題,我們可以做如下操作:

          1、 在tomcat啟動參數中加入兩個參數 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/export/home/tomcat/domains/server2/oom.hprof

          2、 重啟tomcat

          參數說明
          (1)-XX:+HeapDumpOnOutOfMemoryError 表示當JVM發生OOM時,自動生成DUMP文件。
          (2)-XX:HeapDumpPath=存儲文件/目錄 表示生成DUMP文件的路徑

          posted @ 2013-10-30 13:51 為自己代言 閱讀(6212) | 評論 (2)編輯 收藏

          想從JAVA轉到obj-c,看了些基礎的東西,感覺很奇怪,可能不太習慣,在網上看到一個不錯文章有助理解obj-c的一些核心機制。

          Objective-C——消息、Category和Protocol

          2012-06-22 20:13 by 池建強, 2627 閱讀, 11 評論, 收藏, 編輯

          面向對象永遠是個可以吐槽的話題,從開始提出到推崇備至,到充滿質疑,一路走來讓人唏噓不已。面向對象的思想可謂歷史悠久,20世紀70年代的Smalltalk可以說是面向對象語言的經典,直到今天我們依然將這門語言視為面向對象語言的基礎。
          面向對象是大部分編程語言的基本特性,像 C++、Java、Objective-C這樣的靜態語言,Ruby、Python這樣的動態語言都是面向對象的語言。但是如何編寫面向對象的程序卻一直是困擾人們的話題,即使是Smalltalk,也有人認為這是一個有缺陷的面向對象的語言實現。
           
          我在2010年翻譯過的一篇InfoQ的文章,《面向對象編程──走錯了路》中提到,面向對象編程的三個原則是:基于消息傳遞機制,對象分離和多態。文章中還舉了Erlang例子,認為Erlang具備了這些原則,“所以可能是唯一的面向對象語言”。除了之前提到的三個特征,單繼承和動態類型也被引用為面向對象語言的“絕對需求”。基于這些考慮,文章指出,Smalltalk在實現對象思想時的“錯誤”──例如,只關注狀態和行為,在類和基于映像的語言里缺乏良好的并發模型和消息機制。
           
          這篇文章中的核心就是,面向對象思想中除了對象的狀態、行為,還應該關注其并發機制、消息機制,后者更為重要。這一點事實上是我在接觸了Objective-C之后才有了更深入的體會。
           
          Ojbective-C的語法設計主要基于Smalltalk,除了提供傳統的面向對象編程特性之外,還增加了很多類似動態語言Ruby、Python才具有的特性,例如動態類型、動態加載、動態綁定等等,同時強化了消息傳遞機制和表意(Intention Revealing Interface)接口的概念。
           
          —消息—
          消息傳遞模型(Message Passing)是Objective-C語言的核心機制。在Objective-C中,沒有方法調用這種說法,只有消息傳遞。在C++或Java中調用某個類的方法,在Objective-C中是給該類發送一個消息。在C++或Java里,類與類的行為方法之間的關系非常緊密,一個方法必定屬于一個類,且于編譯時就已經綁定在一起,所以你不可能調用一個類里沒有的方法。而在Objective-C中就比較簡單了,類和消息之間是松耦合的,方法調用只是向某個類發送一個消息,該類可以在運行時再確定怎么處理接受到的消息。也就是說,一個類不保證一定會響應接收到的消息,如果收到了一個無法處理的消息,那么程序就是簡單報一個錯。甚至你可以向一個值為nil的空對象發送消息,系統都不會出錯或宕掉。這種設計本身也比較符合軟件的隱喻。
           
          在表意接口(Intention Revealing Interface)方面,Objective-C也是設計的比較出色的語言。面向對象語言的特性之一就是通過API把實現封裝起來,為上層建筑提供服務。但是需要注意的一點就是,你封裝的API最好能夠讓調用者看到接口描述就知道怎么使用。如果為了使用一個API必須要去研究它的實現,那么就失去了封裝的意義。Objective-C通過顯式的API描述,讓開發者不自覺的寫出滿足表意接口的API,比如下圖中的API描述。

           
          上圖中描述了一個傳統意義的實例方法,但和Java或C++不同的是,其方法關鍵字由多個字符串組成,在這個例子是insertObject和atIndex,(id)anObject和 (NSUInterger)index分別表示參數類型和參數名稱。整個方法看上去就像一個英語句子,我們可以很容易的知道,這個方法就是在索引為 index處插入一個對象。如果你是從其他語言轉到Objective-C,那么開始的時候會感覺這種寫法有些繁復,但是一旦理解并習慣了你會感受到其巨大的好處,這種寫法會強制你寫出優美易讀的代碼和API,而且有了XCode強大的提示功能,再長的方法也是一蹴而就。
           
          下面我們來說說多態和繼承。
           與Java一樣,Objective-C一樣不支持多重繼承,但是通過類別(Category)和協議(Protocol)可以很好的實現代碼復用和擴展。
           
          —Category—
          首先我們來談談Category。
           
          Objective-C提供了一種與眾不同的方式——Catagory,可以動態的為已經存在的類添加新的行為。這樣可以保證類的原始設計規模較小,功能增加時再逐步擴展。使用Category 對類進行擴展時,不需要訪問其源代碼,也不需要創建子類。Category使用簡單的方式,實現了類的相關方法的模塊化,把不同的類方法分配到不同的分類文件中。
           
          實現起來很簡單,我們舉例說明。
          SomeClass.h
          @interface SomeClass : NSObject{
          }
          -(void) print;
          @end 
           
          這是類SomeClass的聲明文件,其中包含一個實例方法print。如果我們想在不修改原始類、不增加子類的情況下,為該類增加一個hello的方法,只需要簡單的定義兩個文件 SomeClass+Hello.h和SomeClass+Hello.m,在聲明文件和實現文件中用“()”把Category的名稱括起來即可。聲明文件代碼如下:
           
          #import "SomeClass.h"
           
          @interface SomeClass (Hello)
          -(void)hello;
          @end
          實現文件代碼如下
          #import "SomeClass+Hello.h"
          @implementationSomeClass (Hello)
          -(void)hello{
              NSLog (@"name:%@ ", @"Jacky");
          }
          @end 
          其中Hello是Category的名稱,如果你用XCode創建Category,那么需要填寫的內容包括名稱和要擴展的類的名稱。這里還有一個約定成俗的習慣,將聲明文件和實現文件名稱統一采用“原類名+Category”的方式命名。
          調用也非常簡單,毫無壓力,如下:
          首先引入Category的聲明文件,然后正常調用即可。
          #import "SomeClass+Hello.h"
           
          SomeClass * sc =[[SomeClass alloc] init];
          [sc hello] 
          執行結果是:
          nameJacky 
           
          Category的使用場景:
          1、當你在定義類的時候,在某些情況下(例如需求變更),你可能想要為其中的某個或幾個類中添加方法。
          2、一個類中包含了許多不同的方法需要實現,而這些方法需要不同團隊的成員實現
          3、當你在使用基礎類庫中的類時,你可能希望這些類實現一些你需要的方法。
           
          遇到以上這些需求,Category可以幫助你解決問題。當然,使用Category也有些問題需要注意,
          1、Category可以訪問原始類的實例變量,但不能添加變量,如果想添加變量,可以考慮通過繼承創建子類。
          2、Category可以重載原始類的方法,但不推薦這么做,這么做的后果是你再也不能訪問原來的方法。如果確實要重載,正確的選擇是創建子類。
          3、和普通接口有所區別的是,在分類的實現文件中可以不必實現所有聲明的方法,只要你不去調用它。
           
          用好Category可以充分利用Objective-C的動態特性,編寫出靈活簡潔的代碼。
           
          —Protocol— 
          下面我們再來看Protocol。
          Protocol,簡單來說就是一系列不屬于任何類的方法列表,其中聲明的方法可以被任何類實現。這種模式一般稱為代理(delegation)模式。你通過Protocol定義各種行為,在不同的場景采用不同的實現方式。在iOS和OS X開發中,Apple采用了大量的代理模式來實現MVC中View和Controller的解耦。
           
          定義Protocol很簡單,在聲明文件(h文件)中通過關鍵字@protocol定義,然后給出Protocol的名稱,方法列表,然后用@end表示Protocol結束。在@end指令結束之前定義的方法,都屬于這個Protocol。例如:
          @protocol ProcessDataDelegate <NSObject>
          @required
          - (void) processSuccessful: (BOOL)success;
          
          @optional
          - (id) submitOrder: (NSNumber *) orderid;
          @end
           
          以上代碼可以單獨放在一個h文件中,也可以寫在相關類的h文件中,可以視具體情況而定。該Protocol包含兩個方法,processSuccessful和submitOrder。這里還有兩個關鍵字,@required和@optional,表示如果要實現這個協議,那么processSuccessful方法是必須要實現的,submitOrder則是可選的,這兩個注解關鍵字是在Objective-C 2.0之后加入的語法特性。如果不注明,那么方法默認是@required的,必須實現。
           
          那么如何實現這個Protocol呢,很簡單,創建一個普通的Objective-C類,取名為TestAppDelegate,這時會生成一個h文件和m文件。在h文件中引入包含Protocol的h文件,之后聲明采用這個Protocol即可,如下:
          @interface TestAppDelegate : NSObject<ProcessDataDelegate>;
          
          @end
          用尖括號(<...>)括起來的ProcessDataDelegate就是我們創建的Protocol。如果要采用多個Protocol,可以在尖括號內引入多個Protocol 名稱,并用逗號隔開即可。例如<ProcessDataDelegate,xxxDelegate>
           
          m文件如下:
          @implementation TestAppDelegate
          
          - (void) processSuccessful: (BOOL)success{
              if (success) {
                  NSLog(@"成功");
              }else {
                  NSLog(@"失敗");
              }
          }
          
          @end 
          由于submitOrder方法是可選的,所以我們可以只實現processSuccessful。
           
          Protocol一般使用在哪些場景呢?Objective-C里的Protocol和Java語言中的接口很類似,如果一些類之間沒有繼承關系,但是又具備某些相同的行為,則可以使用 Protocol來描述它們的關系。不同的類,可以遵守同一個Protocol,在不同的場景下注入不同的實例,實現不同的功能。其中最常用的就是委托代理模式,Cocoa框架中大量采用了這種模式實現數據和UI的分離。例如UIView產生的所有事件,都是通過委托的方式交給Controller完成。根據約定,框架中后綴為Delegate的都是Protocol,例如UIApplicationDelegate,UIWebViewDelegate 等,使用時大家可以留意一下,體會其用法。
           
          使用Protocol時還需要注意的是:
          1、Protocol本身是可以繼承的,比如:
          @protocol A
               -(void)methodA;
          @end
          @protocol B <A>
               -(void)methodB;
          @end

          如果你要實現B,那么methodA和methodB都需要實現。 

          2、Protocol是類無關的,任何類都可以實現定義好的Protocol。如果我們想知道某個類是否實現了某個Protocol,還可以使用conformsToProtocol進行判斷,如下:
          [obj conformsToProtocol:@protocol(ProcessDataDelegate)] 
           
          好吧,具體的語言特性這次就介紹這么多。從某種意義上來說,Objective-C是一門古老的語言,發明于1980年。1988年,喬布斯的Next公司獲得了Objective-C語言的授權,并開發出了Objective-C的語言庫和NEXTSTEP的開發環境。NextStep是以Mach和BSD為基礎,Objective-C是其語言和運行庫,后來的事大家都清楚,蘋果買了Next,喬布斯回歸蘋果,開始神奇的蘋果振興之路,NextStep成了Max OS X的基礎。以后發展越來越好,Objctive-C成了Apple的當家語言,現在基本上是Apple在維護Objctive-C的發展。
           
          在蘋果的AppStore推出之前,Objective-C一直相對小眾,但是其優秀的語言特性似乎一直在為后面的爆發積蓄力量,當蘋果平臺級的應用出現之后,Objective-C開始大放異彩,靜態語言的效率和動態語言的特性得到眾多程序員的喜愛,目前它已經以火箭般的速度躥升TIOBE語言排行版第四位。
           
          對于喜愛蘋果技術的技術人員來說,Objective-C是你必須深入了解和值得學習的一門語言,希望以后有機會多寫一些相關的文章。

          轉載于:http://www.cnblogs.com/chijianqiang/archive/2012/06/22/objc-category-protocol.html

           

          posted @ 2012-10-17 13:49 為自己代言 閱讀(358) | 評論 (0)編輯 收藏

               摘要: 我們大部分情況使用默認的或者chain或者redirect,其實struts2還有很多其他類型的,今天我們就來看一下都有哪些類型。 struts2的源碼中struts-default.xml文件有全部類型下邊解釋下: 類型 chain 描述 用來處理Action鏈,被跳轉的action中仍能獲取上個頁面的值,如request信息。 使用的類...  閱讀全文

          posted @ 2012-06-01 12:56 為自己代言 閱讀(418) | 評論 (0)編輯 收藏

           org.apache.cxf.binding.soap.SoapFault: "http://schemas.xmlsoap.org/wsdl/" is  not a valid SOAP version.

          在寫好服務器端測試沒有問題,在客戶端調用時候出現上面錯誤,經過分析原面如下:

          1:CXF 有兩種客戶端調用,一種是動態工廠形式, 第二種是通過CXF 中wsdl2java 命令先把客戶端類生成到本地,在調用;

          第一種: 1:  用org.apache.cxf.jaxws.JaxWsProxyFactoryBean配置

          <bean id="msgWebServiceSoap" class="com.jd.sms.MsgWebServiceSoap" factory-bean="msgClientFactory"
                    factory-method="create"/>
              <bean id="msgClientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
                  <property name="serviceClass" value="com.jd.sms.MsgWebServiceSoap"/>
                  <property name="address" value="    </bean>


          第二種: 2:  

             用標簽<jaxws:client直接配置:

          <jaxws:client id="orderClient" serviceClass=

                          "demo.order.OrderProcess" address=

                           "http://localhost:8080/orderapp/OrderProcess" />

          以上兩種webService 的接口地址都不需要在后面加上?wsdl這個后綴,因為這樣spring 調用時候要先通過 CXF/bin 命令行wsdl2java 這個命令生成本地客戶端調用,把生成的本地客戶端代碼復制到客戶端工程中去,在spring 配置文件中通過上面的任意一種配置方式選擇配置,即可調用。
          以上CXF異常,就是用這種方式調用,但是接口URL 多了?wsdl后綴的原面;
          附wsdl2java 使用方式cd CXF/bin目錄下:

          其作用上面的build.xml作用一樣。
          附加:wsdl2java用法:
          wsdl2java -p com -d src -all aa.wsdl
          -p 指定其wsdl的命名空間,也就是要生成代碼的包名:
          -d 指定要產生代碼所在目錄
          -client 生成客戶端測試web service的代碼
          -server 生成服務器啟動web service的代碼
          -impl 生成web service的實現代碼
          -ant 生成build.xml文件


          posted @ 2012-05-22 14:00 為自己代言 閱讀(2625) | 評論 (0)編輯 收藏

          首先在web工程src目錄下新建一個database.properties 文件
          內容如下:

          user=root
          password=root
          databaseType=com.mysql.jdbc.Driver
          url=jdbc:mysql://192.168.2.232:3306/oa? seUnicode=true&amp;characterEncoding=UTF8&amp;zeroDateTimeBehavior=convertToNull

           這里的內容隨自己的合適而變化,這里不多說了;

          在新建一個讀取.properties文件新類:

          package com.junhai.tamsys.util;

          import java.io.FileInputStream;
          import java.io.FileNotFoundException;
          import java.io.FileOutputStream;
          import java.io.IOException;
          import java.util.Properties;

          public class DatabaseConfigure {
           private Properties property;
           private FileInputStream inputFile;
           private FileOutputStream outputFile;

           public DatabaseConfigure() {
            property = new Properties();
           }

           public DatabaseConfigure(String filePath) {
            property = new Properties();
            try {
             inputFile = new FileInputStream(filePath);
             property.load(inputFile);
             inputFile.close();
            } catch (FileNotFoundException e) {
             System.out.println("讀取屬性文件--->失敗!- 原因:文件路徑錯誤或者文件不存在");
             e.printStackTrace();
            } catch (IOException e) {
             e.printStackTrace();
            }
           }

           /*
            * 重載函數,得到key的值 @param key 取得其值的鍵 @return key的值
            */
           public String getValue(String key) {
            if (property.containsKey(key)) {
             return property.getProperty(key);

            } else
             return "";
           }

           /*
            * 重載函數,得到key的值
            *
            * @param fileName propertys文件的路徑+文件名 @param key 取得其值的鍵 @return key的值
            */
           public String getValue(String fileName, String key) {
            try {
             String value = "";
             inputFile = new FileInputStream(fileName);
             property.load(inputFile);
             inputFile.close();
             if (property.containsKey(key)) {
              value = property.getProperty(key);
              return value;
             } else
              return value;
            } catch (FileNotFoundException e) {
             e.printStackTrace();
             return "";
            } catch (IOException e) {
             e.printStackTrace();
             return "";
            } catch (Exception ex) {
             ex.printStackTrace();
             return "";
            }
           }

           /*
            * 清除properties文件中所有的key和其值
            */
           public void clear() {
            property.clear();
           }

           /*
            * 改變或添加一個key的值,當key存在于properties文件中時該key的值被value所代替, 當key不存在時,該key的值是value
            * @param key 要存入的鍵 @param value 要存入的值
            */
           public void setValue(String key, String value) {
            property.setProperty(key, value);
           }

           /*
            * 將更改后的文件數據存入指定的文件中,該文件可以事先不存在。 @param fileName 文件路徑+文件名稱 @param
            * description 對該文件的描述
            */
           public void saveFile(String fileName, String description) {
            try {
             outputFile = new FileOutputStream(fileName);
             property.store(outputFile, description);
             outputFile.close();
            } catch (FileNotFoundException e) {
             e.printStackTrace();
            } catch (IOException ioe) {
             ioe.printStackTrace();
            }
           }

           public static void main(String[] args) {
            DatabaseConfigure test=new DatabaseConfigure("./src/database.properties");
            System.out.println(test.getValue("user"));
            System.out.println(test.getValue("databaseType")+";"+test.getValue("url"));
            
           }
          }


          這樣就可以通過key得到相應的value了;
          想在這里多說一點是路徑問題,java工程和web 工程讀取.properties路徑是不一樣的,我在這里就花了不少時間。
          JAVA工程: DatabaseConfigure test=new DatabaseConfigure("./src/database.properties");這樣讀取就可以了:
          web工程這樣讀取:DatabaseConfigure  dc = new DatabaseConfigure(Thread.currentThread().getContextClassLoader()
                                                          .getResource("").getPath()+"database.properties");
           
          因為當服務器啟動后工程里面東西會編譯后加到\WEB-INF\classes這個目錄,服務也是從這個目錄下讀取信息的。所以先取到這個路徑,才能正確讀取到database.properties這里邊的內容。

          posted @ 2010-01-28 15:46 為自己代言 閱讀(2978) | 評論 (0)編輯 收藏

               摘要:   Struts2 攔截器詳細配置過程 1:所有攔截器的超級接口Interceptor ,Action去實現這個接口;  Interceptor 它其中有三個方法(init(),destroy() ,interceptor()):       Init()方法:在服務器起動的時候加載一次,并且只加載一次;  ...  閱讀全文

          posted @ 2009-10-12 22:41 為自己代言 閱讀(33102) | 評論 (6)編輯 收藏

          主站蜘蛛池模板: 马山县| 麻阳| 慈利县| 文山县| 景谷| 双桥区| 宝应县| 雷波县| 石狮市| 高要市| 江川县| 新河县| 盈江县| 丰台区| 铁岭县| 衡南县| 道孚县| 镇坪县| 峨边| 都安| 于田县| 周至县| 浪卡子县| 清水河县| 通城县| 鹤壁市| 贵溪市| 建昌县| 道孚县| 建平县| 邛崃市| 芜湖县| 雷山县| 桓台县| 彝良县| 东城区| 扎赉特旗| 灵川县| 抚宁县| 昌乐县| 武定县|