久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

淺談Java內(nèi)部類的四個(gè)應(yīng)用場(chǎng)景 - blind - JavaEye技術(shù)網(wǎng)站

 昵稱2578135 2010-10-09

淺談Java內(nèi)部類的四個(gè)應(yīng)用場(chǎng)景

文章分類:Java編程
Java內(nèi)部類是Java言語的一個(gè)很重要的概念,,《Java編程思想》花了很大的篇幅來講述這個(gè)概念。但是我們?cè)趯?shí)踐中很少用到它,,雖然我們?cè)诤芏鄷r(shí)候會(huì)被動(dòng)的使用到它,,但它仍然像一個(gè)幕后英雄一樣,不為我們所知,,不為我們所用,。
本文不試圖來講述Java內(nèi)部類的今生前世、來龍去脈,,這些在網(wǎng)絡(luò)上都已經(jīng)汗牛充棟,。如果讀者想了解這些,可以在網(wǎng)絡(luò)上搜索來學(xué)習(xí),。Java內(nèi)部類總是躲在它的外部類里,,像一個(gè)幕后英雄一樣。但是幕后英雄也有用武之地,,在很多時(shí)候,,恰當(dāng)?shù)氖褂肑ava內(nèi)部類能起到讓人拍案叫絕的作用。本文試圖談一談讓這個(gè)幕后英雄也有用武之地的四個(gè)場(chǎng)景,,希望引起大家對(duì)使用Java內(nèi)部類的興趣,。
以下的文字,要求大家熟悉Java內(nèi)部類的概念后來閱讀,。


場(chǎng)景一:當(dāng)某個(gè)類除了它的外部類,,不再被其他的類使用時(shí)
我們說這個(gè)內(nèi)部類依附于它的外部類而存在,,可能的原因有:1,、不可能為其他的類使用,;2、出于某種原因,,不能被其他類引用,,可能會(huì)引起錯(cuò)誤。等等,。這個(gè)場(chǎng)景是我們使用內(nèi)部類比較多的一個(gè)場(chǎng)景,。下面我們以一個(gè)大家熟悉的例子來說明。
在我們的企業(yè)級(jí)Java項(xiàng)目開發(fā)過程中,,數(shù)據(jù)庫(kù)連接池是一個(gè)我們經(jīng)常要用到的概念,。雖然在很多時(shí)候,我們都是用的第三方的數(shù)據(jù)庫(kù)連接池,,不需要我們親自來做這個(gè)數(shù)據(jù)庫(kù)連接池,。但是,作為我們Java內(nèi)部類使用的第一個(gè)場(chǎng)景,,這個(gè)數(shù)據(jù)庫(kù)連接池是一個(gè)很好的例子,。為了簡(jiǎn)單起見,以下我們就來簡(jiǎn)單的模擬一下數(shù)據(jù)庫(kù)連接池,,在我們的例子中,,我們只實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池的一些簡(jiǎn)單的功能。如果想完全實(shí)現(xiàn)它,,大家不妨自己試一試,。
首先,我們定義一個(gè)接口,,將數(shù)據(jù)庫(kù)連接池的功能先定義出來,,如下:
Java代碼 復(fù)制代碼
  1. public interface Pool extends TimerListener   
  2. {   
  3.         //初始化連接池   
  4.         public boolean init();   
  5.         //銷毀連接池   
  6.         public void destory();   
  7.         //取得一個(gè)連接   
  8.         public Connection getConn();   
  9.         //還有一些其他的功能,這里不再列出   
  10.         ……   
  11. }  

有了這個(gè)功能接口,,我們就可以在它的基礎(chǔ)上實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池的部分功能了。我們首先想到這個(gè)數(shù)據(jù)庫(kù)連接池類的操作對(duì)象應(yīng)該是由Connection對(duì)象組成的一個(gè)數(shù)組,,既然是數(shù)組,,我們的池在取得Connection的時(shí)候,就要對(duì)數(shù)組元素進(jìn)行遍歷,,看看Connection對(duì)象是否已經(jīng)被使用,,所以數(shù)組里每一個(gè)Connection對(duì)象都要有一個(gè)使用標(biāo)志。我們?cè)賹?duì)連接池的功能進(jìn)行分析,,會(huì)發(fā)現(xiàn)每一個(gè)Connection對(duì)象還要一個(gè)上次訪問時(shí)間和使用次數(shù),。
通過上面的分析,我們可以得出,,連接池里的數(shù)組的元素應(yīng)該是由對(duì)象組成,,該對(duì)象的類可能如下:
Java代碼 復(fù)制代碼
  1. public class PoolConn   
  2. {   
  3.         private Connection conn;   
  4.         private boolean isUse;   
  5.         private long lastAccess;   
  6.         private int useCount;   
  7.         ……   
  8. }  

下面的省略號(hào)省掉的是關(guān)于四個(gè)屬性的一些get和set方法,。我們可以看到這個(gè)類的核心就是Connection,其他的一些屬性都是Connection的一些標(biāo)志,??梢哉f這個(gè)類只有在連接池這個(gè)類里有用,其他地方用不到,。這時(shí)候,,我們就該考慮是不是可以把這個(gè)類作為一個(gè)內(nèi)部類呢?而且我們把它作為一個(gè)內(nèi)部類以后,,可以把它定義成一個(gè)私有類,,然后將它的屬性公開,這樣省掉了那些無謂的get和set方法,。下面我們就試試看:
Java代碼 復(fù)制代碼
  1. public class ConnectPool implements Pool   
  2. {   
  3.         //存在Connection的數(shù)組   
  4.         private PoolConn[] poolConns;   
  5.         //連接池的最小連接數(shù)   
  6.         private int min;   
  7.         //連接池的最大連接數(shù)   
  8.         private int max;   
  9.         //一個(gè)連接的最大使用次數(shù)   
  10.         private int maxUseCount;   
  11.         //一個(gè)連接的最大空閑時(shí)間   
  12.         private long maxTimeout;   
  13.         //同一時(shí)間的Connection最大使用個(gè)數(shù)   
  14.         private int maxConns;   
  15.         //定時(shí)器   
  16.         private Timer timer;   
  17.         public boolean init()   
  18.         {   
  19.                try  
  20.                {   
  21.                       ……   
  22.                       this.poolConns = new PoolConn[this.min];   
  23.                       for(int i=0;i<this.min;i++)   
  24.                       {   
  25.                              PoolConn poolConn = new PoolConn();   
  26.                              poolConn.conn = ConnectionManager.getConnection();   
  27.                              poolConn.isUse = false;   
  28.                              poolConn.lastAccess = new Date().getTime();   
  29.                              poolConn.useCount = 0;   
  30.                              this.poolConns[i] = poolConn;   
  31. }   
  32. ……   
  33. return true;   
  34.                }   
  35.                catch(Exception e)   
  36.                {   
  37.                       return false;   
  38. }   
  39. }   
  40. ……   
  41. private class PoolConn   
  42. {   
  43.        public Connection conn;   
  44.        public boolean isUse;   
  45. public long lastAccess;   
  46.        public int useCount;   
  47. }   
  48. }  

因?yàn)楸疚牟皇菍n}來講述數(shù)據(jù)庫(kù)連接池的,,所以在上面的代碼中絕大部分的內(nèi)容被省略掉了。PoolConn類不大可能被除了ConnectionPool類的其他類使用到,,把它作為ConnectionPool的私有內(nèi)部類不會(huì)影響到其他類,。同時(shí),我們可以看到,,使用了內(nèi)部類,,使得我們可以將該內(nèi)部類的數(shù)據(jù)公開,ConnectionPool類可以直接操作PoolConn類的數(shù)據(jù)成員,,避免了因set和get方法帶來的麻煩,。
上面的一個(gè)例子,是使用內(nèi)部類使得你的代碼得到簡(jiǎn)化和方便,。還有些情況下,,你可能要避免你的類被除了它的外部類以外的類使用到,這時(shí)候你卻不得不使用內(nèi)部類來解決問題,。

場(chǎng)景二:解決一些非面向?qū)ο蟮恼Z句塊
這些語句塊包括if…else if…else語句,,case語句,等等,。這些語句都不是面向?qū)ο蟮?,給我們?cè)斐闪讼到y(tǒng)的擴(kuò)展上的麻煩。我們可以看看,,在模式中,,有多少模式是用來解決由if語句帶來的擴(kuò)展性的問題。
Java編程中還有一個(gè)困擾我們的問題,,那就是try…catch…問題,,特別是在JDBC編程過程中。請(qǐng)看下面的代碼:
Java代碼 復(fù)制代碼
  1. try  
  2.          {   
  3.                 String[] divisionData = null;   
  4.                 conn = manager.getInstance().getConnection();   
  5.                 stmt = (OracleCallableStatement)conn.prepareCall("{ Call PM_GET_PRODUCT.HEADER_DIVISION(?, ?) }");   
  6.                 stmt.setLong(1 ,productId.longValue() );   
  7.                 stmt.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR); ;   
  8.                 stmt.execute();   
  9.                 ResultSet rs = stmt.getCursor(2);   
  10.                 int i = 0 ;   
  11.                 String strDivision = "";   
  12.                 while( rs.next() )   
  13.                 {   
  14.                              strDivision += rs.getString("DIVISION_ID") + "," ;   
  15.                   }   
  16.                   int length = strDivision.length() ;   
  17.                   if(length != 0 )   
  18.                   {   
  19.                          strDivision = strDivision.substring(0,length - 1);   
  20.                   }   
  21.                   divisionData = StringUtil.split(strDivision, ",") ;   
  22.                   map.put("Division", strDivision ) ;   
  23.                   LoggerAgent.debug("GetHeaderProcess","getDivisionData","getValue + " + strDivision +" " + productId) ;   
  24.        }catch(Exception e)   
  25.         {   
  26.                        LoggerAgent.error("GetHeaderData""getDivisionData",   
  27.                                                      "SQLException: " + e);   
  28.                        e.printStackTrace() ;   
  29.     
  30.        }finally  
  31.         {   
  32.                        manager.close(stmt);   
  33.                        manager.releaseConnection(conn);   
  34.         }  

這是我們最最常用的一個(gè)JDBC編程的代碼示例,。一個(gè)系統(tǒng)有很多這樣的查詢方法,,這段代碼一般分作三段:try關(guān)鍵字括起來的那段是用來做查詢操作的,,catch關(guān)鍵字括起來的那段需要做兩件事,記錄出錯(cuò)的原因和事務(wù)回滾(如果需要的話),,finally關(guān)鍵字括起來的那段用來釋放數(shù)據(jù)庫(kù)連接。
我們的煩惱是:try關(guān)鍵字括起來的那段是變化的,,每個(gè)方法的一般都不一樣,。而 catch和finally關(guān)鍵字括起來的那兩段卻一般都是不變的,每個(gè)方法的那兩段都是一樣的,。既然后面那兩段是一樣的,,我們就非常希望將它們提取出來,做一個(gè)單獨(dú)的方法,,然后讓每一個(gè)使用到它們的方法調(diào)用,。但是,try…catch…finally…是一個(gè)完整的語句段,,不能把它們分開,。這樣的結(jié)果,使得我們不得不在每一個(gè)數(shù)據(jù)層方法里重復(fù)的寫相同的catch…finally…這兩段語句,。
既然不能將那些討厭的try…catch…finally…作為一個(gè)公用方法提出去,,那么我們還是需要想其他的辦法來解決這個(gè)問題。不然我們老是寫那么重復(fù)代碼,,真是既繁瑣,,又不容易維護(hù)。
我們?nèi)菀紫氲?,既然catch…finally…這兩段代碼不能提出來,,那么我們能不能將try…里面的代碼提出去呢?唉喲,,try…里面的代碼是可變的呢,。怎么辦?
既然try…里面的代碼是可變的,,這意味著這些代碼是可擴(kuò)展的,,是應(yīng)該由用戶來實(shí)現(xiàn)的,對(duì)于這樣的可擴(kuò)展內(nèi)容,,我們很容易想到用接口來定義它們,,然后由用戶去實(shí)現(xiàn)。這樣以來我們首先定義一個(gè)接口:
Java代碼 復(fù)制代碼
  1. public interface DataManager   
  2. {   
  3.         public void manageData();   
  4. }  

我們需要用戶在manageData()方法中實(shí)現(xiàn)他們對(duì)數(shù)據(jù)層訪問的代碼,,也就是try…里面的代碼,。
然后我們使用一個(gè)模板類來實(shí)現(xiàn)所有的try…catch…finally…語句的功能,如下:
public class DataTemplate
{
        public void execute(DataManager dm)
        {
               try
               {
                      dm.manageData();
}
catch(Exception e)
{
       LoggerAgent.error("GetHeaderData", "getDivisionData",
                        "SQLException: " + e);
       e.printStackTrace() ;

}finally
{
       manager.close(stmt);
       manager.releaseConnection(conn);
}
}
}
這樣,,一個(gè)模板類就完成了,。我們也通過這個(gè)模板類將catch…finally…兩段代碼提出來了,。我們來看看使用了這個(gè)模板類的數(shù)據(jù)層方法是怎么實(shí)現(xiàn)的:
new DataTemplate().execute(new DataManager()
{
        public void manageData()
        {
                String[] divisionData = null;
                conn = manager.getInstance().getConnection();
                stmt = (OracleCallableStatement)conn.prepareCall("{ Call PM_GET_PRODUCT.HEADER_DIVISION(?, ?) }");
                stmt.setLong(1 ,productId.longValue() );
                stmt.registerOutParameter(2, oracle.jdbc.OracleTypes.CURSOR); ;
                stmt.execute();
                ResultSet rs = stmt.getCursor(2);
                int i = 0 ;
                String strDivision = "";
                while( rs.next() )
                {
                             strDivision += rs.getString("DIVISION_ID") + "," ;
}
                  int length = strDivision.length() ;
                  if(length != 0 )
                  {
                         strDivision = strDivision.substring(0,length - 1);
                  }
                  divisionData = StringUtil.split(strDivision, ",") ;
                  map.put("Division", strDivision ) ;
                  LoggerAgent.debug("GetHeaderProcess","getDivisionData","getValue + " + strDivision +" " + productId) ;
}
});
注意:本段代碼僅供思路上的參考,沒有經(jīng)過上機(jī)測(cè)試,。
我們可以看到,,正是這個(gè)實(shí)現(xiàn)了DataManager接口得匿名內(nèi)部類的使用,才使得我們解決了對(duì)try…catch…finally…語句的改造,。這樣,,第一為我們解決了令人痛苦的重復(fù)代碼;第二也讓我們?cè)跀?shù)據(jù)層方法的編碼中,,直接關(guān)注對(duì)數(shù)據(jù)的操作,,不用關(guān)心那些必需的但是與數(shù)據(jù)操作無關(guān)的東西。
我們現(xiàn)在來回想一下Spring框架的數(shù)據(jù)層,,是不是正是使用了這種方法呢,?


場(chǎng)景之三:一些多算法場(chǎng)合
假如我們有這樣一個(gè)需求:我們的一個(gè)方法用來對(duì)數(shù)組排序并且依次打印各元素,對(duì)數(shù)組排序方法有很多種,,用哪種方法排序交給用戶自己確定,。
對(duì)于這樣一個(gè)需求,我們很容易解決,。我們決定給哪些排序算法定義一個(gè)接口,,具體的算法實(shí)現(xiàn)由用戶自己完成,只要求他實(shí)現(xiàn)我們的接口就行,。
public interface SortAlgor
{
        public void sort(int[] is);
}
這樣,,我們?cè)僭诜椒ɡ飳?shí)現(xiàn)先排序后打印,代碼如下:
public void printSortedArray(int[] is,SortAlgor sa)
{
        ……
       sa.sort(is);
        for(int i=0;i<is.length;i++)
        {
               System.out.print(is[i]+” “);
}
System.out.println();
}
客戶端對(duì)上面方法的使用如下:
int[] is = new int[]{3,1,4,9,2};
printSortedArray(is,new SortAlgor()
{
        public void sort(is)
        {
               int k = 0;
               for(int i=0;i<is.length;i++)
               {
                     for(int j=i+1;j<is.length;j++)
                      {
                             if(is[i]>is[j])
                             {
                                    k = is[i];
                                    is[i] = is[j];
                                    is[j] = k;
                             }
                      }
               }
}
});
這樣的用法很多,,我們都或多或少的被動(dòng)的使用過,。如在Swing編程中,我們經(jīng)常需要對(duì)組件增加監(jiān)聽器對(duì)象,,如下所示:
spinner2.addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
System.out.println("Source: " + e.getSource());
}
}
);
在Arrays包里,,對(duì)元素為對(duì)象的數(shù)組的排序:
Arrays.sort(emps,new Comparator(){
        Public int compare(Object o1,Object o2)
        {
               return ((Employee)o1).getServedYears()-((Employee)o2).getServedYears();
}
});
這樣的例子還有很多,,JDK教會(huì)了我們很多使用內(nèi)部類的方法,。隨時(shí)我們都可以看一看API,看看還會(huì)在什么地方使用到內(nèi)部類呢,?



場(chǎng)景之四:適當(dāng)使用內(nèi)部類,,使得代碼更加靈活和富有擴(kuò)展性
適當(dāng)?shù)氖褂脙?nèi)部類,可以使得你的代碼更加靈活和富有擴(kuò)展性,。當(dāng)然,,在這里頭起作用的還是一些模式的運(yùn)行,但如果不配以內(nèi)部類的使用,這些方法的使用效果就差遠(yuǎn)了,。不信,?請(qǐng)看下面的例子:
我們記得簡(jiǎn)單工廠模式的作用就是將客戶對(duì)各個(gè)對(duì)象的依賴轉(zhuǎn)移到了工廠類里。很顯然,,簡(jiǎn)單工廠模式并沒有消除那些依賴,,只是簡(jiǎn)單的將它們轉(zhuǎn)移到了工廠類里。如果有新的對(duì)象增加進(jìn)來,,則我們需要修改工廠類,。所以我們需要對(duì)工廠類做進(jìn)一步的改造,進(jìn)一步消除它對(duì)具體類的依賴,。以前我們提供過一個(gè)使用反射來消除依賴的方法,;這里,,我們將提供另外一種方法,。
這種方法是將工廠進(jìn)一步抽象,而將具體的工廠類交由具體類的創(chuàng)建者來實(shí)現(xiàn),,這樣,,工廠類和具體類的依賴的問題就得到了解決。而工廠的使用者則調(diào)用抽象的工廠來獲得具體類的對(duì)象,。如下,。
Java代碼 復(fù)制代碼
  1. 我們以一個(gè)生產(chǎn)形體的工廠為例,下面是這些形體的接口:   
  2. package polyFactory;   
  3.     
  4. public interface Shape {   
  5. public void draw();   
  6. public void erase();   
  7.     
  8. }   
  9.     
  10. 通過上面的描述,,大家都可能已經(jīng)猜到,,這個(gè)抽象的工廠肯定使用的是模板方法模式。如下:   
  11. package polyFactory;   
  12.     
  13. import java.util.HashMap;   
  14. import java.util.Map;   
  15.     
  16.     
  17. public abstract class ShapeFactory {   
  18. protected abstract Shape create();   
  19. private static Map factories = new HashMap();   
  20. public static void addFactory(String id,ShapeFactory f)   
  21. {   
  22.        factories.put(id,f);   
  23. }   
  24. public static final Shape createShape(String id)   
  25. {   
  26.        if(!factories.containsKey(id))   
  27.         {   
  28.                try  
  29.                {   
  30.                       Class.forName("polyFactory."+id);   
  31.                }   
  32.                catch(ClassNotFoundException e)   
  33.                {   
  34.                       throw new RuntimeException("Bad shape creation : "+id);   
  35.                }   
  36.         }   
  37.         return ((ShapeFactory)factories.get(id)).create();   
  38. }   
  39. }  

不錯(cuò),,正是模板方法模式的運(yùn)用。這個(gè)類蠻簡(jiǎn)單的:首先是一個(gè)create()方法,,用來產(chǎn)生具體類的對(duì)象,,留交各具體工廠實(shí)現(xiàn)去實(shí)現(xiàn)。然后是一個(gè)Map類型的靜態(tài)變量,,用來存放具體工廠的實(shí)現(xiàn)以及他們的ID號(hào),。接著的一個(gè)方法使用來增加一個(gè)具體工廠的實(shí)現(xiàn)。最后一個(gè)靜態(tài)方法是用來獲取具體對(duì)象,,里面的那個(gè)Class.forName……的作用是調(diào)用以ID號(hào)為類名的類的一些靜態(tài)的東西,。
下面,我們來看具體的類的實(shí)現(xiàn):
Java代碼 復(fù)制代碼
  1. package polyFactory;   
  2.     
  3. public class Circle implements Shape {   
  4.     
  5.     
  6. public void draw() {   
  7.         // TODO Auto-generated method stub   
  8.        System.out.println("the circle is drawing...");   
  9. }   
  10.     
  11.     
  12. public void erase() {   
  13.         // TODO Auto-generated method stub   
  14.        System.out.println("the circle is erasing...");   
  15. }   
  16. private static class Factory extends ShapeFactory   
  17. {   
  18.        protected Shape create()   
  19.         {   
  20.                return new Circle();   
  21.         }   
  22. }   
  23. static {ShapeFactory.addFactory("Circle",new Factory());}   
  24.     
  25. }  

這個(gè)類的其他的地方也平常得很。但就是后面的那個(gè)內(nèi)部類Factory用得好,。第一呢,,這個(gè)類只做一件事,就是產(chǎn)生一個(gè)Circle對(duì)象,,與其他類無關(guān),,就這一個(gè)條也就滿足了使用內(nèi)部類的條件。第二呢,,這個(gè)Factory類需要是靜態(tài)的,,這也得要求它被使用內(nèi)部類,不然,,下面的ShapeFacotry.addFactory就沒辦法add了,。而最后的那個(gè)靜態(tài)的語句塊是用來將具體的工廠類添加到抽象的工廠里面去。在抽象工廠里調(diào)用Class.forName就會(huì)執(zhí)行這個(gè)靜態(tài)的語句塊了,。
下面仍然是一個(gè)具體類:
Java代碼 復(fù)制代碼
  1. package polyFactory;   
  2.     
  3. public class Square implements Shape {   
  4.     
  5. public void draw() {   
  6.         // TODO Auto-generated method stub   
  7.        System.out.println("the square is drawing...");   
  8. }   
  9.     
  10. public void erase() {   
  11.         // TODO Auto-generated method stub   
  12.        System.out.println("the square is erasing...");   
  13. }   
  14. private static class Factory extends ShapeFactory   
  15. {   
  16.        protected Shape create()   
  17.         {   
  18.                return new Square();   
  19.         }   
  20. }   
  21. static {ShapeFactory.addFactory("Square",new Factory());}   
  22.     
  23. }   
  24. 最后,,我們來測(cè)試一下:   
  25. String[] ids = new String[]{"Circle","Square","Square","Circle"};   
  26.         for(int i=0;i<ids.length;i++)   
  27.         {   
  28.                Shape shape = ShapeFactory.createShape(ids[i]);   
  29.                shape.draw();   
  30.                shape.erase();   
  31.         }   
  32. 測(cè)試結(jié)果為:   
  33. the circle is drawing...   
  34. the circle is erasing...   
  35. the square is drawing...   
  36. the square is erasing...   
  37. the square is drawing...   
  38. the square is erasing...   
  39. the circle is drawing...   
  40. the circle is erasing...  

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點(diǎn),。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,,謹(jǐn)防詐騙,。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào),。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多