小方的Java博客

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            27 隨筆 :: 17 文章 :: 115 評論 :: 0 Trackbacks

          置頂隨筆 #

          Let my heart hibernate and I'm waiting for spring!
          posted @ 2006-07-20 16:34 方佳瑋 閱讀(278) | 評論 (0)編輯 收藏

          2006年6月17日 #

          Struts的Token(令牌)機制能夠很好的解決表單重復提交的問題,基本原理是:服務器端在處理到達的請求之前,會將請求中包含的令牌值與保存在當前用戶會話中的令牌值進行比較,看是否匹配。在處理完該請求后,且在答復發送給客戶端之前,將會產生一個新的令牌,該令牌除傳給客戶端以外,也會將用戶會話中保存的舊的令牌進行替換。這樣如果用戶回退到剛才的提交頁面并再次提交的話,客戶端傳過來的令牌就和服務器端的令牌不一致,從而有效地防止了重復提交的發生。
            
            這時其實也就是兩點,第一:你需要在請求中有這個令牌值,請求中的令牌值如何保存,其實就和我們平時在頁面中保存一些信息是一樣的,通過隱藏字段來保存,保存的形式如: 〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,這個value是TokenProcessor類中的generateToken()獲得的,是根據當前用戶的session id和當前時間的long值來計算的。第二:在客戶端提交后,我們要根據判斷在請求中包含的值是否和服務器的令牌一致,因為服務器每次提交都會生成新的Token,所以,如果是重復提交,客戶端的Token值和服務器端的Token值就會不一致。下面就以在數據庫中插入一條數據來說明如何防止重復提交。
            
            在Action中的add方法中,我們需要將Token值明確的要求保存在頁面中,只需增加一條語句:saveToken(request);,如下所示:
          public?ActionForward?add(ActionMapping?mapping,?ActionForm?form,
            
            HttpServletRequest?request,?HttpServletResponse?response)
            
            
          //前面的處理省略
            
            saveToken(request);
            
            
          return?mapping.findForward("add");
            
            }在Action的insert方法中,我們根據表單中的Token值與服務器端的Token值比較,如下所示:
            
            
          public?ActionForward?insert(ActionMapping?mapping,?ActionForm?form,
            
            HttpServletRequest?request,?HttpServletResponse?response)
            
            
          if?(isTokenValid(request,?true))?{
            
            
          //?表單不是重復提交
            
            
          //這里是保存數據的代碼
            
            }
          ?else?{
            
            
          //表單重復提交
            
            saveToken(request);
            
            
          //其它的處理代碼
            
            }

            
            }
          其實使用起來很簡單,舉個最簡單、最需要使用這個的例子:
            
            一般控制重復提交主要是用在對數據庫操作的控制上,比如插入、更新、刪除等,由于更新、刪除一般都是通過id來操作(例如:updateXXXById, removeXXXById),所以這類操作控制的意義不是很大(不排除個別現象),重復提交的控制也就主要是在插入時的控制了。
            
            先說一下,我們目前所做項目的情況:
            
            目前的項目是用Struts+Spring+Ibatis,頁面用jstl,Struts復雜View層,Spring在Service層提供事務控制,Ibatis是用來代替JDBC,所有頁面的訪問都不是直接訪問jsp,而是訪問Structs的Action,再由Action來Forward到一個Jsp,所有針對數據庫的操作,比如取數據或修改數據,都是在Action里面完成,所有的Action一般都繼承BaseDispatchAction,這個是自己建立的類,目的是為所有的Action做一些統一的控制,在Struts層,對于一個功能,我們一般分為兩個Action,一個Action里的功能是不需要調用Struts的驗證功能的(常見的方法名稱有add,edit,remove,view,list),另一個是需要調用Struts的驗證功能的(常見的方法名稱有insert,update)。
            
            就拿論壇發貼來說吧,論壇發貼首先需要跳轉到一個頁面,你可以填寫帖子的主題和內容,填寫完后,單擊“提交”,貼子就發表了,所以這里經過兩個步驟:
            
            1、轉到一個新增的頁面,在Action里我們一般稱為add,例如:
          public?ActionForward?add(ActionMapping?mapping,?ActionForm?form,
            
            HttpServletRequest?request,?HttpServletResponse?response)
            
            
          throws?Exception?{
            
            
          //這一句是輸出調試信息,表示代碼執行到這一段了
            
            log.debug(
          "::?action?-?subject?add");
            
            
          //your?code?here
            
            
          //這里保存Token值
            
            saveToken(request);
            
            
          //跳轉到add頁面,在Structs-config.xml里面定義,例如,跳轉到subjectAdd.jsp
            
            
          return?mapping.findForward("add");
            
            }

          2、在填寫標題和內容后,選擇 提交 ,會提交到insert方法,在insert方法里判斷,是否重復提交了。
          public?ActionForward?insert(ActionMapping?mapping,?ActionForm?form,
            
            HttpServletRequest?request,?HttpServletResponse?response)
          {
            
            
          if?(isTokenValid(request,?true))?{
            
            
          //?表單不是重復提交
            
            
          //這里是保存數據的代碼
            
            }
          ?else?{
            
            
          //表單重復提交
            
            saveToken(request);
            
            
          //其它的處理代碼
            
            }

            
            }

          下面更詳細一點(注意,下面所有的代碼使用全角括號):
            
            1、你想發貼時,點擊“我要發貼”鏈接的代碼可以里這樣的:
            
            〈html:link action="subject.do?method=add"〉我要發貼〈/html:link〉
            
            subject.do 和 method 這些在struct-config.xml如何定義我就不說了,點擊鏈接后,會執行subject.do的add方法,代碼如上面說的,跳轉到subjectAdd.jsp頁面。頁面的代碼大概如下:
            〈html:form?action="subjectForm.do?method=insert"〉
            
            〈html:text?property="title"?/〉
            
            〈html:textarea?property="content"?/〉
            
            〈html:submit?property="發表"?/〉
            
            〈html:reset?property="重填"?/〉
            
            〈html:form〉
          如果你在add方法里加了“saveToken(request);”這一句,那在subjectAdd.jsp生成的頁面上,會多一個隱藏字段,類似于這樣〈input type="hidden" name="org.apache.struts.taglib.html.TOKEN" value="6aa35341f25184fd996c4c918255c3ae"〉,
            
            2、點擊發表后,表單提交到subjectForm.do里的insert方法后,你在insert方法里要將表單的數據插入到數據庫中,如果沒有進行重復提交的控制,那么每點擊一次瀏覽器的刷新按鈕,都會在數據庫中插入一條相同的記錄,增加下面的代碼,你就可以控制用戶的重復提交了。
          if?(isTokenValid(request,?true))?{
            
            
          //?表單不是重復提交
            
            
          //這里是保存數據的代碼
            
            }
          ?else?{
            
            
          //表單重復提交
            
            saveToken(request);
            
            
          //其它的處理代碼
            
            }

          注意,你必須在add方法里使用了saveToken(request),你才能在insert里判斷,否則,你每次保存操作都是重復提交。
            
            記住一點,Struts在你每次訪問Action的時候,都會產生一個令牌,保存在你的Session里面,如果你在Action里的函數里面,使用了saveToken(request);,那么這個令牌也會保存在這個Action所Forward到的jsp所生成的靜態頁面里。
            
            如果你在你Action的方法里使用了isTokenValid,那么Struts會將你從你的request里面去獲取這個令牌值,然后和Session里的令牌值做比較,如果兩者相等,就不是重復提交,如果不相等,就是重復提交了。
            
            由于我們項目的所有Action都是繼承自BaseDispatchAction這個類,所以我們基本上都是在這個類里面做了表單重復提交的控制,默認是控制add方法和insert方法,如果需要控制其它的方法,就自己手動寫上面這些代碼,否則是不需要手寫的,控制的代碼如下:
          public?abstract?class?BaseDispatchAction?extends?BaseAction?{
            
            
          protected?ActionForward?perform(ActionMapping?mapping,?ActionForm?form,
            
            HttpServletRequest?request,?HttpServletResponse?response)
            
            
          throws?Exception?{
            
            String?parameter?
          =?mapping.getParameter();
            
            String?name?
          =?request.getParameter(parameter);
            
            
          if?(null?==?name)?{?//如果沒有指定?method?,則默認為?list
            
            name?
          =?"list";
            
            }

            
            
          if?("add".equals(name))?{
            
            
          if?("add".equals(name))?{
            
            saveToken(request);
            
            }

            
            }
          ?else?if?("insert".equals(name))?{
            
            
          if?(!isTokenValid(request,?true))?{
            
            resetToken(request);
            
            saveError(request,?
          new?ActionMessage("error.repeatSubmit"));
            
            log.error(
          "重復提交!");
            
            
          return?mapping.findForward("error");
            
            }

            
            }

            
            
          return?dispatchMethod2(mapping,?form,?request,?response,?name);
            
            }

            
            }
          posted @ 2006-06-17 21:23 方佳瑋 閱讀(378) | 評論 (0)編輯 收藏

          2006年2月1日 #

          前兩天給朋友寫的一段代碼,特此總結在此。除Oracle以外的數據庫應當都可以使用以下代碼,Oracle的操作方法Hibernate.org的站長roobin有一篇文章有講。


          首先建立實體類。二進制的字段是一個java.sql.Blob類型
          private java.sql.Blob image;

          xdoclet要用的注釋:
          @hibernate.property
          column="image"


          接下來就是保存文件的代碼了,以下僅給出偽碼,也很簡單
          String fname = "c:\\javalogo.gif";//要入庫的文件
          File f = new File(fname);
          fin = new FileInputStream(f);

          要保存的實體類 jtdsBlob = new 要保存的實體類();
          jtdsBlob.setTitle("Test1");
          jtdsBlob.setImage(Hibernate.createBlob(fin));

          /*
          * 保存實體的代碼可以換成你自己的方式,當然,如果你要了解ParadiseSDK請訪問
          * http://paradisesdk.dev.java.net/
          */
          IParadiseDAO dao = DAOFactory.getInstance();
          dao.save(jtdsBlob);
          dao.execute();

          posted @ 2006-02-01 12:51 方佳瑋 閱讀(1337) | 評論 (0)編輯 收藏

               摘要: 一、安裝篇   jspSmartUpload是由www.jspsmart.com網站開發的一個可免費使用的全功能的文件上傳下載組件,適于嵌入執行上傳下載操作的JSP文件中。該組件有以下幾個特點: 1、使用簡單。在JSP文件中僅僅書寫三五行JAVA代碼就可以搞定文件的上傳或下載,方便。 2、能全程控制上傳。利用jspSmartUpload組件提供的對象及其操作方法,可以獲得全部上傳文件的信息(包括文...  閱讀全文
          posted @ 2006-02-01 12:48 方佳瑋 閱讀(1159) | 評論 (0)編輯 收藏

          在一個Web應用中經常需要向服務器傳遞一些參數,一般通過form向服務器發送一個POST請求。在參數中有可能包含中文信息,如用戶信息登記、購物定單中的地址信息等等。參數字符串一般用本地字符集進行編碼,如中文采用GB2312或GBK字符集,英文或西歐文字采用ISO8859_1字符集,但在Java程序中一律采用Unicode處理字符串,這就需要有一個編碼轉換的過程。不幸的是,現有的大部分Java應用服務器都是在英語國家開發出來的,由于缺乏大字符集(中文、日文、韓文等)的應用環境,這些應用服務器在處理HTTP請求參數時都存在一些中文處理的問題,也是最為困擾JSP和Servlet開發者的問題。 

          產生這一問題的根本原因是在HTTP請求中缺乏足夠的信息來指明客戶端所使用的字符集。在一個JSP頁面中我們可以通過下面的偽指令來指明輸出頁面所使用的字符集: 



          JSP引擎會將上面的偽指令轉換為HTTP應答的頭部: 

          Content-Type: text/html; charset=GB2312 

          樣輸出的就是采用GB2312編碼的中文頁面,瀏覽器會正確地顯示出中文。但瀏覽器在將form的內容POST到服務器時卻沒有包含charset,而且將中文內容用%xx的形式(xx是十六進制數)進行編碼,例如漢字"中"的GB2312內碼為0xD6D0,在HTTP請求中就變成了%D6%D0,根據RFC2616的規定,如果在HTTP請求中未指明字符集,就使用ISO8859_1編碼,這樣"中"字在處理時變成了兩個字符,分別為'u00D6'和'u00D0',而返回到客戶端時變成了兩個不可顯示的字符,瀏覽器一般顯示成'??'。 

          解決這一問題的傳統做法是編寫額外的代碼來完成字符集的轉換: 

          strOut = new String(strIn.getBytes("8859_1"), "GB2312"); 

          strIn是未經過轉換的字符串,其編碼為ISO8859_1,strOut是經過轉換的字符串,其編碼為GB2312。 

          在Apusic 0.9.5版中實現了Java Servlets 2.3規范草案,其中在ServletRequest接口中新增了一個方法setCharacterEncoding(String enc),可以補上在HTTP請求中缺少的charset信息,而上面這一煩瑣的轉換過程就在Servlet引擎中自動完成了,而且Servlet引擎還對轉換過程做了優化,提高了運行效率。下面給出一個簡單的例子,大家可以做一下比較。 

          // 傳統方式 
          <%@ page contentType="text/html; charset=gb2312" %> 
          <html> 
          <body> 
          <form method=post action=test.jsp> 
          <input type=text name=your_name> 
          </form> 
          <%= new String(request.getParameter("your_name").getBytes("8859_1"), "GB2312"%> 
          </body> 
          </html> 

          // 新的方式 
          <%@ page contentType="text/html; charset=gb2312" %> 
          <% request.setCharacterEncoding("GB2312"); %> 
          <html> 
          <body> 
          <form method=post action=test.jsp> 
          <input type=text name=your_name> 
          </form> 
          <%= request.getParameter("your_name"%> 
          </body> 
          </html> 
          posted @ 2006-02-01 12:46 方佳瑋 閱讀(312) | 評論 (0)編輯 收藏

          1.   在業務層使用JDBC直接操作數據庫-最簡單,最直接的操作
           
          1)數據庫url,username,password寫死在代碼中
              Class.forName("oracle.jdbc.driver.OracleDriver").newInstance(); 
              String url="jdbc:oracle:thin:@localhost:1521:orcl"; 
              String user="scott"; 
              String password="tiger"; 
              Connection conn= DriverManager.getConnection(url,user,password); 
              Statement stmt=conn.createStatement(
          ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE); 
              String sql="select * from test"; 
              ResultSet rs=stmt.executeQuery(sql);
           
          2)采用Facade和Command模式,使用DBUtil類封裝JDBC操作;
                數據庫url,username,password可以放在配置文件中(如xml,properties,ini等)。
                這種方法在小程序中應用較多。
           
          2.DAO(Data Accessor Object)模式-松耦合的開始
          DAO = data + accessor + domain object
           
           例如User類-domain object (javabean)
          UserDAO類-accessor ,提供的方法getUser(int id),save(User user)內包含了JDBC操作
          在業務邏輯中使用這兩個類來完成數據操作。
           
          使用Factory模式可以方便不同數據庫連接之間的移植。
           
          3.數據庫資源管理模式
          3.1 數據庫連接池技術
          資源重用,避免頻繁創建,釋放連接引起大大量性能開銷;
          更快的系統響應速度;
           
          通過實現JDBC的部分資源對象接口( Connection, Statement, ResultSet ),可以使用Decorator設計模式分別產生三種邏輯資源對象: PooledConnection, PooledStatement和 PooledResultSet。
           
           
          一個最簡單地數據庫連接池實現
          public class ConnectionPool {
           
                 private static Vector pools;
                 private final int POOL_MAXSIZE = 25;
                 /**
                  * 獲取數據庫連接
                  * 如果當前池中有可用連接,則將池中最后一個返回;若沒有,則創建一個新的返回
                  */
                 public synchronized Connection getConnection() {
                        Connection conn = null;
                        if (pools == null) {
                               pools = new Vector();
                        }
           
                        if (pools.isEmpty()) {
                               conn = createConnection();
                        } else {
                               int last_idx = pools.size() - 1;
                               conn = (Connection) pools.get(last_idx);
                               pools.remove(last_idx);
                        }
           
                        return conn;
                 }
           
                 /**
                  * 將使用完畢的數據庫連接放回池中
                  * 若池中連接已經超過閾值,則關閉該連接;否則放回池中下次再使用
                  */
                 public synchronized void releaseConnection(Connection conn) {
                        if (pools.size() >= POOL_MAXSIZE)
                               try {
                                      conn.close();
                               } catch (SQLException e) {
                                      // TODO自動生成 catch
                                      e.printStackTrace();
                               } else
                               pools.add(conn);
                 }
           
                 public static Connection createConnection() {
                        Connection conn = null;
                        try {
                               Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
                               String url = "jdbc:oracle:thin:@localhost:1521:orcl";
                               String user = "scott";
                               String password = "tiger";
                               conn = DriverManager.getConnection(url, user, password);
                        } catch (InstantiationException e) {
                               // TODO自動生成 catch
                               e.printStackTrace();
                        } catch (IllegalAccessException e) {
                               // TODO自動生成 catch
                               e.printStackTrace();
                        } catch (ClassNotFoundException e) {
                               // TODO自動生成 catch
                               e.printStackTrace();
                        } catch (SQLException e) {
                               // TODO自動生成 catch
                               e.printStackTrace();
                        }
                        return conn;
                 }
          }
           
          注意:利用getConnection()方法得到的Connection,程序員很習慣地調用conn.close()方法關閉了數據庫連接,那么上述的數據庫連接機制便形同虛設。在調用conn.close()方法方法時如何調用releaseConnection()方法?這是關鍵。這里,我們使用Proxy模式和java反射機制。
           
          public synchronized Connection getConnection() {
                        Connection conn = null;
                        if (pools == null) {
                               pools = new Vector();
                        }
           
                        if (pools.isEmpty()) {
                               conn = createConnection();
                        } else {
                               int last_idx = pools.size() - 1;
                               conn = (Connection) pools.get(last_idx);
                               pools.remove(last_idx);
                        }
                 
                  ConnectionHandler handler=new ConnectionHandler(this);
                        return handler.bind(con);
                 }
           
          public class ConnectionHandler implements InvocationHandler {
               private Connection conn;
               private ConnectionPool pool;
              
               public ConnectionHandler(ConnectionPool pool){
                      this.pool=pool;
               }
              
               /**
                * 將動態代理綁定到指定Connection
                * @param conn
                * @return
                */
               public Connection bind(Connection conn){
                      this.conn=conn;
          Connection proxyConn=(Connection)Proxy.newProxyInstance(
          conn.getClass().getClassLoader(), conn.getClass().getInterfaces(),this);
                    return proxyConn;
               }
              
                 /* (非 Javadoc
                  * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
                  */
                 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // TODO自動生成方法存根
                        Object obj=null;
                        if("close".equals(method.getName())){
                               this.pool.releaseConnection(this.conn);
                        }
                        else{
                               obj=method.invoke(this.conn, args);
                        }
                       
                        return obj;
                 }
          }
           
                在實際項目中,并不需要你來從頭開始來設計數據庫連接池機制,現在成熟的開源項目,如C3P0,dbcp,Proxool等提供了良好的實現。一般推薦使用Apache dbcp,基本使用實例:
          DataSource ds = null;
             try{
               Context initCtx = new InitialContext();
               Context envCtx = (Context) initCtx.lookup("java:comp/env");
               ds = (DataSource)envCtx.lookup("jdbc/myoracle");
                  if(ds!=null){
                          out.println("Connection is OK!");
                          Connection cn=ds.getConnection();
                          if(cn!=null){
                                  out.println("cn is Ok!");
                          Statement stmt = cn.createStatement();
                           ResultSet rst = stmt.executeQuery("select * from BOOK");
                          out.println("<p>rst is Ok!" + rst.next());
                          while(rst.next()){
                                  out.println("<P>BOOK_CODE:" + rst.getString(1));
                            }
                                  cn.close();
                          }else{
                                  out.println("rst Fail!");
                          }
                  }
                  else
                          out.println("Fail!");
                     }catch(Exception ne){ out.println(ne);
                   }

          3.2 Statement Pool
          普通預編譯代碼:
          String strSQL=”select name from items where id=?”;
          PreparedStatement ps=conn.prepareStatement(strSQL);
          ps.setString(1, “2”);
          ResultSet rs=ps.executeQuery();
           
          但是PreparedStatement 是與特定的Connection關聯的,一旦Connection關閉,則相關的PreparedStatement 也會關閉。
          為了創建PreparedStatement 緩沖池,可以在invoke方法中通過sql語句判斷池中還有沒有可用實例。
           
          4. 持久層設計與O/R mapping 技術
          1) Hernate:適合對新產品的開發,進行封閉化的設計
          Hibernate 2003年被Jboss接管,通過把java pojo對象映射到數據庫的table中,采用了xml/javareflection技術等。3.0提供了對存儲過程和手寫sql的支持,本身提供了hql語言。
          開發所需要的文件:
          hibernate配置文件: hibernate.cfg.xml hibernate.properties
          hibernate 映射文件: a.hbm.xml
          pojo類源文件: a.java  
           
          導出表與表之間的關系:
          a. java對象到hbm文件:xdoclet
          b. hbm文件到java對象:hibernate extension
          c. 從數據庫到hbm文件:middlegen
          d. hbm文件到數據庫:SchemaExport
           
          2)Iatis :適合對遺留系統的改造和對既有數據庫的復用,有很強的靈活性 3) Apache OJB:優勢在于對標準的全面支持 4)EJB:適合集群服務器,其性能也不象某些人所詬病的那么差勁 5) JDO (java data object)
          設置一個Properties對象,從而獲取一個JDO的PersistenceManagerFactory(相當于JDBC連接池中的DataSource),進而獲得一個PersistenceManager對象(相當于JDBC中的Connection對象),之后,你可以用這個PersistenceManager對象來增加、更新、刪除、查詢對象。
          JDOQL是JDO的查詢語言;它有點象SQL,但卻是依照Java的語法的。
           
          5. 基于開源框架的Struts+Spring+Hibernate實現方案
          示例:這是一個3層架構的web 程序,通過一個Action 來調用業務代理,再通過它來回調 DAO類。下面的流程圖表示了MyUsers是如何工作的。數字表明了流程的先后順序,從web(UserAction)到中間層(UserManager),再到數據層(UserDAO),然后返回。
          SpringAOP, UserManagerUserDAO都是接口.
          1)       web(UserAction) :調用中間層的接口方法,將UserManager作為屬性注入。
                       采用流行的Struts框架,雖然有很多人不屑一顧,但是這項技術在業界用的比較普遍,能滿足基本的功能,可以減少培訓學習成本。
          2)       中間層(UserManager):將UserDAO作為屬性注入,其實現主要是調用數據層接口的一些方法;它處于事務控制中。
                      采用Spring框架實現,IOC與AOP是它的代名詞,功能齊全,非常棒的一個架構。
          3)       數據層(UserDAO):實現類繼承HibernateDaoSupport類,在該類中可以調用getHibernateTemplate()的一些方法執行具體的數據操作。
                      采用Hibernate做O/R mapping,從種種跡象可以看出,Hibernate就是EJB3.0的beta版。
          posted @ 2006-02-01 12:44 方佳瑋 閱讀(499) | 評論 (0)編輯 收藏

          FACTORY—人才市場:以往是要哪個人才,就找哪個人才,效率低,現在有了人才市場,我們只需
          直接去人才市場挑一個好了;

          BUILDER—生產流水線:以前是手工業作坊式的人工單個單個的生產零件然后一步一步組裝做,好
          比有了工業革命,現在都由生產流水線代替了。如要造豐田汽車,先制定汽車的構造如由車胎、方
          向盤、發動機組成。再以此構造標準生產豐田汽車的車胎、方向盤、發動機。然后進行組裝。最后
          得到豐田汽車;

          PROTOTYPE—印刷術的發明:以前只能臨貼才能保持和別人的字跡基本相同,直從印刷技術發明,
          從而保證了復制得和原物一模一樣;

          SINGLETON—唯一:以前是商標滿天飛,相同的商標難免造成侵權,直從有商標保護法后,就保證
          了不會再產生第家企業使用相同的商標;
          結構型模式

          ADAPTER—集眾人之私,成一己之公:武當派張三豐會太極拳,少林派智空大師會金剛般若掌,如
          果他們兩個都成為我的師傅,我就既會太極拳,又會金剛般若掌了;

          DECORATOR—青出于藍而勝于藍:武當派張三豐會太極拳,是我師傅,他教會了我太極拳,但我自
          己還會點蒙古式摔交,張三豐卻不會。于是我就成了DECORATOR模式的實現;

          BRIDGE—白馬非馬:馬之顏色有黑白,馬之性別有公母。我們說”這是馬”太抽象,說”這是黑色
          的公馬”又太死板,只有將顏色與性別和馬動態組合,”這是(黑色的或白色的)(公或母)馬”
          才顯得靈活而飄逸,如此bridge模式精髓得矣。

          COMPOSITE—大家族:子又生孫,孫又生子,子子孫孫,無窮盡也,將眾多紛雜的人口組織成一個
          按輩分排列的大家族即是此模式的實現;

          FACADE—求同存異:高中畢業需讀初中和高中,博士也需讀初中和高中,因此國家將初中和高中普
          及成九年制義務教育;

          FLYWEIGHT—一勞永逸:認識三千漢字,可以應付日常讀書與寫字,可見頭腦中存在這個漢字庫的
          重要;

          PROXY—垂簾聽政:猶如清朝康熙年間的四大府臣,很多權利不在皇帝手里,必須通過輔佐大臣去
          辦;


          行為模式

          CHAIN OF RESPONSIBLEITY—租房:以前為了找房到處打聽,效率低且找不到好的房源。現在有了
          房屋中介,于是向房屋中介提出租房請求,中介提供一個合適的房源,滿意則不再請求,不滿意
          繼續看房,直到滿意為止;

          COMMAND—借刀殺人:以前是想殺誰就殺,但一段時間后領悟到,長此以往必將結仇太多,于是假
          手他人,挑撥他人之間的關系從而達到自己的目的;

          INTERPRETER—文言文注釋:一段文言文,將它翻譯成白話文;

          ITERATOR—趕盡殺絕:一個一個的搜索,絕不放掉一個;

          MEDIATOR—三角債:本來千頭萬緒的債務關系,忽出來一中介,包攬其一切,于是三角關系變成了
          獨立的三方找第四方中介的關系;

          MEMENTO—有福同享:我有多少,你就有多少;

          OBSERVER—看守者:一旦被看守者有什么異常情況,定會及時做出反應;

          STATE—進出自由:如一扇門,能進能出,如果有很多人隨時進進出出必定顯得雜亂而安全,如今
          設一保安限制其進出,如此各人進出才顯得規范;

          STRATEGY—久病成良醫:如人生病可以有各種癥狀,但經過長期摸索,就可以總結出感冒、肺病、
          肝炎等幾種;


          TEMPLATE METHOD——理論不一定要實踐:教練的學生會游泳就行了,至于教練會不會則無關緊要;

          VISITOR—依法治罪:因張三殺人要被處死,李四偷竊要被罰款。由此勢必制定處罰制度,故制定
          法律寫明殺人、放火、偷竊等罪要受什么處罰,經通過后須變動要小。今后有人犯罪不管是誰,按
          共條例處罰即是,這就是訪問者模式誕生的全過程;
          posted @ 2006-02-01 12:42 方佳瑋 閱讀(429) | 評論 (2)編輯 收藏

          跳槽是一門學問,也是一種策略。“人往高處走”,這固然沒有錯。但是,說來輕巧的一句話,它卻包含了為什么“走”、什么是“高”、怎么“走”、什么時候“走”,以及“走”了以后怎么辦等一系列問題。 
            那么當面臨跳槽時,如何才能順利地完成跳槽,從而取得職業的成功呢? 
            首先,要確定你跳槽的動機是什么和自己是不是需要跳槽。大致來說,一個人跳槽的動機一般有如下兩種:一是被動的跳槽,即個人對自己目前的工作不滿意,不得不跳槽,這里又具體包括對人際關系(包括上、下級關系)、工作內容、工作崗位、工作待遇、工作環境或工作條件、發展機會的不滿意等方面。比如,如果你與上司關系不融洽,覺得得不到發展,你自己也感覺無法適應目前的環境,那么恐怕就要考慮換個環境試試了;第二,是主動的跳槽,即面對著更好的工作條件,如待遇、工作環境、發展機會,自己經不住“誘惑”而促使自己跳槽;或者尋求更高的挑戰與報酬,比如你發現自己的能力應付目前的工作綽綽有余,并且發現了自己真正感興趣的工作的時候,你就不妨考慮換個工作試試。 
            無論如何,當你具備了跳槽動機的時候,就是你跳槽行動的開始。但是,為了跳得更“高”,你在跳槽前不妨先問自己下面的問題: 
            1、是什么讓你不滿意現在的工作了? 
            2、你想跳槽經過慎重考慮了嗎?還是一時的情緒?嘗試做自我調整了嗎? 
            3、跳槽會使你失去什么,又得到什么呢? 
            4、適應新的工作或環境、建立新的人際關系需要你付出更多的精力,你有信心嗎? 
            5、你的背景和能力能適應新的工作嗎? 
            6、你是為了生活而工作,還是為了工作而生活? 
            7、你有沒有職業目標?新的工作是不是為你提供了一個清晰的職業方向? 
            8、征求過專家的意見嗎?有沒有咨詢過職業顧問? 
            如果對上面的問題回答是“是”,那么你可以接著考慮下面的問題: 
            1、你要跳過去的公司的職位是什么?如果比你現在的職位還低你能接受嗎? 
            2、新的工作要求你從頭做起,你有這個心理準備嗎? 
            3、你在目前的公司里工作有多久??一般來說,在一個公司的工作至少應該滿1年,否則它不會為你提供非常有價值的職業發展依據; 
            4、你應何時跳槽???最好的狀態是在目前工作進展順利時跳槽,那么你的職業含金量會大大提升。 
            5、你實事求是地估價自己的能力了嗎?你的優點或特長是什么?你有哪些不足???這里要求你既不要好高騖遠,也不要自甘弱小。 
            一旦決定跳槽,你就要大膽地付諸實施了。這時你需要選擇恰當的跳槽時機,以下是職業咨詢顧問提醒你跳槽時應當注意的事項和建議你的比較妥當的做法: 
            1、知己知彼:查閱與目前公司簽訂的勞動合同,明確自己是否受到違約金或競業壁止等條款影響、離職手續辦理難易程度等,做到心中有數; 
            2、盡可能收集新公司的信息以及可能要求自己提供的項目,做到有備無患; 
            3、設計簡歷:準備一份職業化的簡歷,你可以尋求職業顧問的幫助; 
            4、有時候根據自己的工作經歷和能力,使用獵頭公司應聘也不失為一種有效的策略; 
            5、遞交辭呈:向原公司遞交辭職信,做好離職過渡期的安排。記住千萬在拿到“O ffe r Le tte r”以后再遞交辭職信; 
            6、與人為善:雖然你應聘成功了,雖然你可能“痛恨”原來的公司,但是也不要在背后惡言冷語,你哪天還會“用”到原來的公司,這誰也說不準。 
            你也在準備跳槽嗎?你知道跳槽之前應該做什么嗎?當你開始認真思考這些問題的時候,說明你開始關注自己的職業發展了。但是,你必須明白:跳槽并不意味著你就能夠取得職業的成功,這個時候,尋求職業顧問的幫助才是理性的做法,因為職業顧問會告訴你什么是正確的跳槽、什么是你應該選擇的職業方向。一句話:職業顧問會幫助你取得職業生涯的成功!
          posted @ 2006-02-01 12:41 方佳瑋 閱讀(295) | 評論 (0)編輯 收藏

          JAVA學習之路:不走彎路,就是捷徑
          ChinaITLab劉曉濤原創     2005-8-22
          備注:本文選自ChinaITLab網校課程《劉曉濤Java就業直通班V2.0》之預備知識!
            0.引言
            在ChinaITLAB導師制輔導中,筆者發現問得最多的問題莫過于"如何學習編程?JAVA該如何學習?"。類似的問題回答多了,難免會感覺厭煩,就萌生了寫下本文的想法。到時候再有人問起類似的問題,我可以告訴他(她),請你去看看《JAVA學習之路》。拜讀過臺灣蔡學鏞先生的《JAVA夜未眠》,有些文章如《JAVA學習之道》等讓我們確實有共鳴,本文題目也由此而來。
            軟件開發之路是充滿荊棘與挑戰之路,也是充滿希望之路。JAVA學習也是如此,沒有捷徑可走。夢想像《天龍八部》中虛竹一樣被無崖子醍醐灌頂而輕松獲得一甲子功力,是很不現實的。每天仰天大叫"天神啊,請賜給我一本葵花寶典吧",殊不知即使你獲得了葵花寶典,除了受自宮其身之苦外,你也不一定成得了"東方不敗",倒是成"西方失敗"的幾率高一點。
            "不走彎路,就是捷徑",佛經說的不無道理。
            1.如何學習程序設計?
            JAVA是一種平臺,也是一種程序設計語言,如何學好程序設計不僅僅適用于JAVA,對C++等其他程序設計語言也一樣管用。有編程高手認為,JAVA也好C也好沒什么分別,拿來就用。為什么他們能達到如此境界?我想是因為編程語言之間有共通之處,領會了編程的精髓,自然能夠做到一通百通。如何學習程序設計理所當然也有許多共通的地方。
            1.1 培養興趣
            興趣是能夠讓你堅持下去的動力。如果只是把寫程序作為謀生的手段的話,你會活的很累,也太對不起自己了。多關心一些行業趣事,多想想蓋茨。不是提倡天天做白日夢,但人要是沒有了夢想,你覺得有味道嗎?可能像許多深圳本地農民一樣,打打麻將,喝喝功夫茶,拜拜財神爺;每個月就有幾萬十幾萬甚至更多的進帳,憑空多出個"食利階層"。你認為,這樣有味道嗎?有空多到一些程序員論壇轉轉,你會發現,他們其實很樂觀幽默,時不時會冒出智慧的火花。
            1.2 慎選程序設計語言
            男怕入錯行,女怕嫁錯郎。初學者選擇程序設計語言需要謹慎對待。軟件開發不僅僅是掌握一門編程語言了事,它還需要其他很多方面的背景知識。軟件開發也不僅僅局限于某幾個領域,而是已經滲透到了各行各業幾乎每一個角落。
            如果你對硬件比較感興趣,你可以學習C語言/匯編語言,進入硬件開發領域。如果你對電信的行業知識及網絡比較熟悉,你可以在C/C++等之上多花時間,以期進入電信軟件開發領域。如果你對操作系統比較熟悉,你可以學習C/Linux等等,為Linux內核開發/驅動程序開發/嵌入式開發打基礎。如果你想介入到應用范圍最廣泛的應用軟件開發(包括電子商務電子政務系統)的話,你可以選擇J2EE或.NET,甚至LAMP組合。每個領域要求的背景知識不一樣。做應用軟件需要對數據庫等很熟悉。總之,你需要根據自己的特點來選擇合適你的編程語言。
            1.3 要腳踏實地,快餐式的學習不可取
            先分享一個故事。
            有一個小朋友,他很喜歡研究生物學,很想知道那些蝴蝶如何從蛹殼里出來,變成蝴蝶便會飛。 有一次,他走到草原上面看見一個蛹,便取了回家,然后看著,過了幾天以后,這個蛹出了一條裂痕,看見里面的蝴蝶開始掙扎,想抓破蛹殼飛出來。 這個過程達數小時之久,蝴蝶在蛹里面很辛苦地拼命掙扎,怎么也沒法子走出來。這個小孩看著看著不忍心,就想不如讓我幫幫它吧,便隨手拿起剪刀在蛹上剪開,使蝴蝶破蛹而出。 但蝴蝶出來以后,因為翅膀不夠力,變得很臃腫,飛不起來。
            這個故事給我們的啟示是:欲速則不達。
            浮躁是現代人最普遍的心態,能怪誰?也許是貧窮落后了這么多年的緣故,就像當年的大躍進一樣,都想大步跨入共產主義社會。現在的軟件公司、客戶、政府、學校、培訓機構等等到處彌漫著浮躁之氣。就拿筆者比較熟悉的深圳IT培訓行業來說吧,居然有的打廣告宣稱"參加培訓,100%就業",居然報名的學生不少,簡直是藐視天下程序員。社會環境如是,我們不能改變,只能改變自己,鬧市中的安寧,彌足珍貴。許多初學者C++/JAVA沒開始學,立馬使用VC/JBuilder,會使用VC/JBuilder開發一個Hello World程序,就忙不迭的向世界宣告,"我會軟件開發了",簡歷上也大言不慚地寫上"精通VC/JAVA"。結果到軟件公司面試時要么被三兩下打發走了,要么被駁的體無完膚,無地自容。到處碰壁之后才知道捧起《C++編程思想》《JAVA編程思想》仔細鉆研,早知如此何必當初呀。
            "你現在講究簡單方便,你以后的路就長了",好象也是佛經中的勸戒。
            1.4 多實踐,快實踐
            彭端淑的《為學一首示子侄》中有窮和尚與富和尚的故事。
            從前,四川邊境有兩個和尚,一個貧窮,一個有錢。一天,窮和尚對富和尚說:"我打算去南海朝圣,你看怎么樣?"富和尚說:"這里離南海有幾千里遠,你靠什么去呢?"窮和尚說:"我只要一個水缽,一個飯碗就夠了。"富和尚為難地說:"幾年前我就打算買條船去南海,可至今沒去成,你還是別去吧!" 一年以后,富和尚還在為租賃船只籌錢,窮和尚卻已經從南海朝圣回來了。
            這個故事可解讀為:任何事情,一旦考慮好了,就要馬上上路,不要等到準備周全之后,再去干事情。假如事情準備考慮周全了再上路的話,別人恐怕捷足先登了。軟件開發是一門工程學科,注重的就是實踐,"君子動口不動手"對軟件開發人員來講根本就是錯誤的,他們提倡"動手至上",但別害怕,他們大多溫文爾雅,沒有暴力傾向,雖然有時候蓬頭垢面的一副"比爾蓋茨"樣。有前輩高人認為,學習編程的秘訣是:編程、編程、再編程,筆者深表贊同。不僅要多實踐,而且要快實踐。我們在看書的時候,不要等到你完全理解了才動手敲代碼,而是應該在看書的同時敲代碼,程序運行的各種情況可以讓你更快更牢固的掌握知識點。
            1.5 多參考程序代碼
            程序代碼是軟件開發最重要的成果之一,其中滲透了程序員的思想與靈魂。許多人被《仙劍奇俠傳》中凄美的愛情故事感動,悲劇的結局更有一種缺憾美。為什么要以悲劇結尾?據說是因為寫《仙劍奇俠傳》的程序員失戀而安排了這樣的結局,他把自己的感覺融入到游戲中,卻讓眾多的仙劍迷扼腕嘆息。
            多多參考代碼例子,對JAVA而言有參考文獻[4.3],有API類的源代碼(JDK安裝目錄下的src.zip文件),也可以研究一些開源的軟件或框架。
            1.6 加強英文閱讀能力
            對學習編程來說,不要求英語, 但不能一點不會,。最起碼像JAVA API文檔(參考文獻[4.4])這些東西還是要能看懂的,連猜帶懵都可以;旁邊再開啟一個"金山詞霸"。看多了就會越來越熟練。在學JAVA的同時學習英文,一箭雙雕多好。另外好多軟件需要到英文網站下載,你要能夠找到它們,這些是最基本的要求。英語好對你學習有很大的幫助。口語好的話更有機會進入管理層,進而可以成為剝削程序員的"周扒皮"。
            1.7 萬不得已才請教別人
            筆者在ChinaITLab網校的在線輔導系統中解決學生問題時發現,大部分的問題學生稍做思考就可以解決。請教別人之前,你應該先回答如下幾個問題。
            你是否在google中搜索了問題的解決辦法?
            你是否查看了JAVA API文檔?
            你是否查找過相關書籍?
            你是否寫代碼測試過?
            如果回答都是"是"的話,而且還沒有找到解決辦法,再問別人不遲。要知道獨立思考的能力對你很重要。要知道程序員的時間是很寶貴的。
            1.8 多讀好書
            書中自有顏如玉。比爾?蓋茨是一個飽讀群書的人。雖然沒有讀完大學,但九歲的時候比爾?蓋茨就已經讀完了所有的百科全書,所以他精通天文、歷史、地理等等各類學科,可以說比爾?蓋茨不僅是當今世界上金錢的首富,而且也可以稱得上是知識的巨富。
            筆者在給學生上課的時候經常會給他們推薦書籍,到后來學生實在忍無可忍開始抱怨,"天吶,這么多書到什么時候才能看完了","學軟件開發,感覺上了賊船"。這時候,我的回答一般是,"別著急,什么時候帶你們去看看我的書房,到現在每月花在技術書籍上的錢400元,這在軟件開發人員之中還只能夠算是中等的",學生當場暈倒。(注:這一部分學生是剛學軟件開發的)
            對于在JAVA開發領域的好書在筆者另外一篇文章中會專門點評。該文章可作為本文的姊妹篇。
            1.9 使用合適的工具
            工欲善其事必先利其器。軟件開發包含各種各樣的活動,需求收集分析、建立用例模型、建立分析設計模型、編程實現、調試程序、自動化測試、持續集成等等,沒有工具幫忙可以說是寸步難行。工具可以提高開發效率,使軟件的質量更高BUG更少。組合稱手的武器。到飛花摘葉皆可傷人的境界就很高了,無招勝有招,手中無劍心中有劍這樣的境界幾乎不可企及。在筆者另外一篇文章中會專門闡述如何選擇合適的工具(該文章也可作為本文的姊妹篇)。
            2.軟件開發學習路線
            兩千多年的儒家思想孔孟之道,中庸的思想透入骨髓,既不冒進也不保守并非中庸之道,而是找尋學習軟件開發的正確路線與規律。
            從軟件開發人員的生涯規劃來講,我們可以大致分為三個階段,軟件工程師→軟件設計師→架構設計師或項目管理師。不想當元帥的士兵不是好士兵,不想當架構設計師或項目管理師的程序員也不是好的程序員。我們應該努力往上走。讓我們先整理一下開發應用軟件需要學習的主要技術。
            A.基礎理論知識,如操作系統、編譯原理、數據結構與算法、計算機原理等,它們并非不重要。如不想成為計算機科學家的話,可以采取"用到的時候再來學"的原則。
            B.一門編程語言,現在基本上都是面向對象的語言,JAVA/C++/C#等等。如果做WEB開發的話還要學習HTML/JavaScript等等。
            C.一種方法學或者說思想,現在基本都是面向對象思想(OOA/OOD/設計模式)。由此而衍生的基于組件開發CBD/面向方面編程AOP等等。
            D.一種關系型數據庫,ORACLE/SqlServer/DB2/MySQL等等
            E.一種提高生產率的IDE集成開發環境JBuilder/Eclipse/VS.NET等。
            F.一種UML建模工具,用ROSE/VISIO/鋼筆進行建模。
            G.一種軟件過程,RUP/XP/CMM等等,通過軟件過程來組織軟件開發的眾多活動,使開發流程專業化規范化。當然還有其他的一些軟件工程知識。
            H.項目管理、體系結構、框架知識。
            正確的路線應該是:B→C→E→F→G→H。
            還需要補充幾點:
            1).對于A與C要補充的是,我們應該在實踐中逐步領悟編程理論與編程思想。新技術雖然不斷涌現,更新速度令人眼花燎亂霧里看花;但萬變不離其宗,編程理論與編程思想的變化卻很慢。掌握了編程理論與編程思想你就會有撥云見日之感。面向對象的思想在目前來講是相當關鍵的,是強勢技術之一,在上面需要多投入時間,給你的回報也會讓你驚喜。
            2).對于數據庫來說是獨立學習的,這個時機就由你來決定吧。
            3).編程語言作為學習軟件開發的主線,而其余的作為輔線。
            4).軟件工程師著重于B、C、E、 D;軟件設計師著重于B、C、E、 D、F;架構設計師著重于C、F、H。
            3.如何學習JAVA?
            3.1 JAVA學習路線
            3.1.1 基礎語法及JAVA原理
            基礎語法和JAVA原理是地基,地基不牢靠,猶如沙地上建摩天大廈,是相當危險的。學習JAVA也是如此,必須要有扎實的基礎,你才能在J2EE、J2ME領域游刃有余。參加SCJP(SUN公司認證的JAVA程序員)考試不失為一個好方法,原因之一是為了對得起你交的1200大洋考試費,你會更努力學習,原因之二是SCJP考試能夠讓你把基礎打得很牢靠,它要求你跟JDK一樣熟悉JAVA基礎知識;但是你千萬不要認為考過了SCJP就有多了不起,就能夠獲得軟件公司的青睞,就能夠獲取高薪,這樣的想法也是很危險的。獲得"真正"的SCJP只能證明你的基礎還過得去,但離實際開發還有很長的一段路要走。
            3.1.2 OO思想的領悟
            掌握了基礎語法和JAVA程序運行原理后,我們就可以用JAVA語言實現面向對象的思想了。面向對象,是一種方法學;是獨立于語言之外的編程思想;是CBD基于組件開發的基礎;屬于強勢技術之一。當以后因工作需要轉到別的面向對象語言的時候,你會感到特別的熟悉親切,學起來像喝涼水這么簡單。
            使用面向對象的思想進行開發的基本過程是:
            ●調查收集需求。
            ●建立用例模型。
            ●從用例模型中識別分析類及類與類之間的靜態動態關系,從而建立分析模型。
            ●細化分析模型到設計模型。
            ●用具體的技術去實現。
            ●測試、部署、總結。
            3.1.3 基本API的學習
            進行軟件開發的時候,并不是什么功能都需要我們去實現,也就是經典名言所說的"不需要重新發明輪子"。我們可以利用現成的類、組件、框架來搭建我們的應用,如SUN公司編寫好了眾多類實現一些底層功能,以及我們下載過來的JAR文件中包含的類,我們可以調用類中的方法來完成某些功能或繼承它。那么這些類中究竟提供了哪些方法給我們使用?方法的參數個數及類型是?類的構造器需不需要參數?總不可能SUN公司的工程師打國際長途甚至飄洋過海來告訴你他編寫的類該如何使用吧。他們只能提供文檔給我們查看,JAVA DOC文檔(參考文獻4.4)就是這樣的文檔,它可以說是程序員與程序員交流的文檔。
            基本API指的是實現了一些底層功能的類,通用性較強的API,如字符串處理/輸入輸出等等。我們又把它成為類庫。熟悉API的方法一是多查JAVA DOC文檔(參考文獻4.4),二是使用JBuilder/Eclipse等IDE的代碼提示功能。
            3.1.4 特定API的學習
            JAVA介入的領域很廣泛,不同的領域有不同的API,沒有人熟悉所有的API,對一般人而言只是熟悉工作中要用到的API。如果你做界面開發,那么你需要學習Swing/AWT/SWT等API;如果你進行網絡游戲開發,你需要深入了解網絡API/多媒體API/2D3D等;如果你做WEB開發,就需要熟悉Servlet等API啦。總之,需要根據工作的需要或你的興趣發展方向去選擇學習特定的API。
            3.1.5 開發工具的用法
            在學習基礎語法與基本的面向對象概念時,從鍛煉語言熟練程度的角度考慮,我們推薦使用的工具是Editplus/JCreator+JDK,這時候不要急于上手JBuilder/Eclipse等集成開發環境,以免過于關注IDE的強大功能而分散對JAVA技術本身的注意力。過了這一階段你就可以開始熟悉IDE了。
            程序員日常工作包括很多活動,編輯、編譯及構建、調試、單元測試、版本控制、維持模型與代碼同步、文檔的更新等等,幾乎每一項活動都有專門的工具,如果獨立使用這些工具的話,你將會很痛苦,你需要在堆滿工具的任務欄上不斷的切換,效率很低下,也很容易出錯。在JBuilder、Eclipse等IDE中已經自動集成編輯器、編譯器、調試器、單元測試工具JUnit、自動構建工具ANT、版本控制工具CVS、DOC文檔生成與更新等等,甚至可以把UML建模工具也集成進去,又提供了豐富的向導幫助生成框架代碼,讓我們的開發變得更輕松。應該說IDE發展的趨勢就是集成軟件開發中要用到的幾乎所有工具。
            從開發效率的角度考慮,使用IDE是必經之路,也是從一個學生到一個職業程序員轉變的里程碑。
            JAVA開發使用的IDE主要有Eclipse、JBuilder、JDeveloper、NetBeans等幾種;而Eclipse、JBuilder占有的市場份額是最大的。JBuilder在近幾年來一直是JAVA集成開發環境中的霸主,它是由備受程序員尊敬的Borland公司開發,在硝煙彌漫的JAVA IDE大戰中,以其快速的版本更新擊敗IBM的Visual Age for JAVA等而成就一番偉業。IBM在Visual Age for JAVA上已經無利可圖之下,干脆將之貢獻給開源社區,成為Eclipse的前身,真所謂"柳暗花明又一村"。浴火重生的Eclipse以其開放式的插件擴展機制、免費開源獲得廣大程序員(包括幾乎所有的骨灰級程序員)的青睞,極具發展潛力。
            3.1.6 學習軟件工程
            對小型項目而言,你可能認為軟件工程沒太大的必要。隨著項目的復雜性越來越高,軟件工程的必要性才會體現出來。參見"軟件開發學習路線"小節。
            3.2學習要點
            確立的學習路線之后,我們還需要總結一下JAVA的學習要點,這些要點在前文多多少少提到過,只是筆者覺得這些地方特別要注意才對它們進行匯總,不要嫌我婆婆媽媽啊。
            3.2.1勤查API文檔
            當程序員編寫好某些類,覺得很有成就感,想把它貢獻給各位苦難的同行。這時候你要使用"javadoc"工具(包含在JDK中)生成標準的JAVA DOC文檔,供同行使用。J2SE/J2EE/J2ME的DOC文檔是程序員與程序員交流的工具,幾乎人手一份,除了菜鳥之外。J2SE DOC文檔官方下載地址:http://java.sun.com/j2se/1.5.0/download.jsp,你可以到google搜索CHM版本下載。也可以在線查看:  3.2.3學習開源軟件的設計思想
            JAVA領域有許多源代碼開放的工具、組件、框架,JUnit、ANT、Tomcat、Struts、Spring、Jive論壇、PetStore寵物店等等多如牛毛。這些可是前輩給我們留下的瑰寶呀。入寶山而空手歸,你心甘嗎?對這些工具、框架進行分析,領會其中的設計思想,有朝一日說不定你也能寫一個XXX框架什么的,風光一把。分析開源軟件其實是你提高技術、提高實戰能力的便捷方法。
            3.2.4 規范的重要性
            沒有規矩,不成方圓。這里的規范有兩層含義。第一層含義是技術規范,多到
          http://www.jcp.org/下載JSRXXX規范,多讀規范,這是最權威準確最新的教材。第二層含義是編程規范,如果你使用了大量的獨特算法,富有個性的變量及方法的命名方式;同時,沒給程序作注釋,以顯示你的編程功底是多么的深厚。這樣的代碼別人看起來像天書,要理解談何容易,更不用說維護了,必然會被無情地掃入垃圾堆。JAVA編碼規范到此查看或下載  4.結束語
            需要強調的是,學習軟件開發確實有一定的難度,也很辛苦,需要付出很多努力,但千萬不要半途而廢。本文如果能對一直徘徊在JAVA神殿之外的朋友有所幫助的話,筆者也欣慰了。哈哈,怎么聽起來老氣橫秋呀?沒辦法,在電腦的長期輻射之下,都快變成小老頭了。最后奉勸各位程序員尤其是MM程序員,完成工作后趕快遠離電腦,據《胡播亂報》報道,電腦輻射會在白皙的皮膚上面點綴一些小黑點,看起來鮮艷無比……
          posted @ 2006-02-01 12:38 方佳瑋 閱讀(418) | 評論 (0)編輯 收藏

          六種異常處理的陋習

          你覺得自己是一個Java專家嗎?是否肯定自己已經全面掌握了Java的異常處理機制?在下面這段代碼中,你能夠迅速找出異常處理的六個問題嗎?

          1 OutputStreamWriter out =  
          2 java.sql.Connection conn =  
          3 try // ⑸ 
          4  Statement stat = conn.createStatement(); 
          5  ResultSet rs = stat.executeQuery( 
          6   "select uid, name from user"); 
          7  while (rs.next()) 
          8  
          9   out.println("ID:" + rs.getString("uid"// ⑹ 
          10    ",姓名:" + rs.getString("name")); 
          11  }
           
          12  conn.close(); // ⑶ 
          13  out.close(); 
          14 }
           
          15 catch(Exception ex) // ⑵ 
          16 
          17  ex.printStackTrace(); //⑴,⑷ 
          18 }

          作為一個Java程序員,你至少應該能夠找出兩個問題。但是,如果你不能找出全部六個問題,請繼續閱讀本文。

            本文討論的不是Java異常處理的一般性原則,因為這些原則已經被大多數人熟知。我們要做的是分析各種可稱為“反例”(anti-pattern)的違背優秀編碼規范的常見壞習慣,幫助讀者熟悉這些典型的反面例子,從而能夠在實際工作中敏銳地察覺和避免這些問題。

            反例之一:丟棄異常

            代碼:15行-18行。

            這段代碼捕獲了異常卻不作任何處理,可以算得上Java編程中的殺手。從問題出現的頻繁程度和禍害程度來看,它也許可以和C/C++程序的一個惡名遠播的問題相提并論??不檢查緩沖區是否已滿。如果你看到了這種丟棄(而不是拋出)異常的情況,可以百分之九十九地肯定代碼存在問題(在極少數情況下,這段代碼有存在的理由,但最好加上完整的注釋,以免引起別人誤解)。

            這段代碼的錯誤在于,異常(幾乎)總是意味著某些事情不對勁了,或者說至少發生了某些不尋常的事情,我們不應該對程序發出的求救信號保持沉默和無動于衷。調用一下printStackTrace算不上“處理異常”。不錯,調用printStackTrace對調試程序有幫助,但程序調試階段結束之后,printStackTrace就不應再在異常處理模塊中擔負主要責任了。

            丟棄異常的情形非常普遍。打開JDK的ThreadDeath類的文檔,可以看到下面這段說明:“特別地,雖然出現ThreadDeath是一種‘正常的情形’,但ThreadDeath類是Error而不是Exception的子類,因為許多應用會捕獲所有的Exception然后丟棄它不再理睬。”這段話的意思是,雖然ThreadDeath代表的是一種普通的問題,但鑒于許多應用會試圖捕獲所有異常然后不予以適當的處理,所以JDK把ThreadDeath定義成了Error的子類,因為Error類代表的是一般的應用不應該去捕獲的嚴重問題。可見,丟棄異常這一壞習慣是如此常見,它甚至已經影響到了Java本身的設計。

            那么,應該怎樣改正呢?主要有四個選擇:

            1、處理異常。針對該異常采取一些行動,例如修正問題、提醒某個人或進行其他一些處理,要根據具體的情形確定應該采取的動作。再次說明,調用printStackTrace算不上已經“處理好了異常”。

            2、重新拋出異常。處理異常的代碼在分析異常之后,認為自己不能處理它,重新拋出異常也不失為一種選擇。

            3、把該異常轉換成另一種異常。大多數情況下,這是指把一個低級的異常轉換成應用級的異常(其含義更容易被用戶了解的異常)。

            4、不要捕獲異常。

            結論一:既然捕獲了異常,就要對它進行適當的處理。不要捕獲異常之后又把它丟棄,不予理睬。

            反例之二:不指定具體的異常

            代碼:15行。

            許多時候人們會被這樣一種“美妙的”想法吸引:用一個catch語句捕獲所有的異常。最常見的情形就是使用catch(Exception ex)語句。但實際上,在絕大多數情況下,這種做法不值得提倡。為什么呢?

            要理解其原因,我們必須回顧一下catch語句的用途。catch語句表示我們預期會出現某種異常,而且希望能夠處理該異常。異常類的作用就是告訴Java編譯器我們想要處理的是哪一種異常。由于絕大多數異常都直接或間接從java.lang.Exception派生,catch(Exception ex)就相當于說我們想要處理幾乎所有的異常。

            再來看看前面的代碼例子。我們真正想要捕獲的異常是什么呢?最明顯的一個是SQLException,這是JDBC操作中常見的異常。另一個可能的異常是IOException,因為它要操作OutputStreamWriter。顯然,在同一個catch塊中處理這兩種截然不同的異常是不合適的。如果用兩個catch塊分別捕獲SQLException和IOException就要好多了。這就是說,catch語句應當盡量指定具體的異常類型,而不應該指定涵蓋范圍太廣的Exception類。

            另一方面,除了這兩個特定的異常,還有其他許多異常也可能出現。例如,如果由于某種原因,executeQuery返回了null,該怎么辦?答案是讓它們繼續拋出,即不必捕獲也不必處理。實際上,我們不能也不應該去捕獲可能出現的所有異常,程序的其他地方還有捕獲異常的機會??直至最后由JVM處理。

            結論二:在catch語句中盡可能指定具體的異常類型,必要時使用多個catch。不要試圖處理所有可能出現的異常。

            反例之三:占用資源不釋放

            代碼:3行-14行。

            異常改變了程序正常的執行流程。這個道理雖然簡單,卻常常被人們忽視。如果程序用到了文件、Socket、JDBC連接之類的資源,即使遇到了異常,也要正確釋放占用的資源。為此,Java提供了一個簡化這類操作的關鍵詞finally。

            finally是樣好東西:不管是否出現了異常,Finally保證在try/catch/finally塊結束之前,執行清理任務的代碼總是有機會執行。遺憾的是有些人卻不習慣使用finally。

            當然,編寫finally塊應當多加小心,特別是要注意在finally塊之內拋出的異常??這是執行清理任務的最后機會,盡量不要再有難以處理的錯誤。

            結論三:保證所有資源都被正確釋放。充分運用finally關鍵詞。

          反例之四:不說明異常的詳細信息

            代碼:3行-18行。

            仔細觀察這段代碼:如果循環內部出現了異常,會發生什么事情?我們可以得到足夠的信息判斷循環內部出錯的原因嗎?不能。我們只能知道當前正在處理的類發生了某種錯誤,但卻不能獲得任何信息判斷導致當前錯誤的原因。

            printStackTrace的堆棧跟蹤功能顯示出程序運行到當前類的執行流程,但只提供了一些最基本的信息,未能說明實際導致錯誤的原因,同時也不易解讀。

            因此,在出現異常時,最好能夠提供一些文字信息,例如當前正在執行的類、方法和其他狀態信息,包括以一種更適合閱讀的方式整理和組織printStackTrace提供的信息。

            結論四:在異常處理模塊中提供適量的錯誤原因信息,組織錯誤信息使其易于理解和閱讀。

            反例之五:過于龐大的try塊

            代碼:3行-14行。

            經常可以看到有人把大量的代碼放入單個try塊,實際上這不是好習慣。這種現象之所以常見,原因就在于有些人圖省事,不愿花時間分析一大塊代碼中哪幾行代碼會拋出異常、異常的具體類型是什么。把大量的語句裝入單個巨大的try塊就象是出門旅游時把所有日常用品塞入一個大箱子,雖然東西是帶上了,但要找出來可不容易。

            一些新手常常把大量的代碼放入單個try塊,然后再在catch語句中聲明Exception,而不是分離各個可能出現異常的段落并分別捕獲其異常。這種做法為分析程序拋出異常的原因帶來了困難,因為一大段代碼中有太多的地方可能拋出Exception。

            結論五:盡量減小try塊的體積。

            反例之六:輸出數據不完整

            代碼:7行-11行。

            不完整的數據是Java程序的隱形殺手。仔細觀察這段代碼,考慮一下如果循環的中間拋出了異常,會發生什么事情。循環的執行當然是要被打斷的,其次,catch塊會執行??就這些,再也沒有其他動作了。已經輸出的數據怎么辦?使用這些數據的人或設備將收到一份不完整的(因而也是錯誤的)數據,卻得不到任何有關這份數據是否完整的提示。對于有些系統來說,數據不完整可能比系統停止運行帶來更大的損失。

            較為理想的處置辦法是向輸出設備寫一些信息,聲明數據的不完整性;另一種可能有效的辦法是,先緩沖要輸出的數據,準備好全部數據之后再一次性輸出。

            結論六:全面考慮可能出現的異常以及這些異常對執行流程的影響。

            改寫后的代碼

            根據上面的討論,下面給出改寫后的代碼。也許有人會說它稍微有點?嗦,但是它有了比較完備的異常處理機制。

           1OutputStreamWriter out =  
           2java.sql.Connection conn =  
           3try 
           4 Statement stat = conn.createStatement(); 
           5 ResultSet rs = stat.executeQuery( 
           6  "select uid, name from user"); 
           7 while (rs.next()) 
           8 
           9  out.println("ID:" + rs.getString("uid"+ ",姓名: " + rs.getString("name")); 
          10 }
           
          11}
           
          12catch(SQLException sqlex) 
          13
          14 out.println("警告:數據不完整"); 
          15 throw new ApplicationException("讀取數據時出現SQL錯誤", sqlex); 
          16}
           
          17catch(IOException ioex) 
          18
          19 throw new ApplicationException("寫入數據時出現IO錯誤", ioex); 
          20}
           
          21finally 
          22
          23 if (conn != null
          24  try 
          25   conn.close(); 
          26  }
           
          27  catch(SQLException sqlex2) 
          28  
          29   System.err(this.getClass().getName() + ".mymethod - 不能關閉數據庫連接: " + sqlex2.toString()); 
          30  }
           
          31 }
           
          32
          33 if (out != null
          34  try 
          35   out.close(); 
          36  }
           
          37  catch(IOException ioex2) 
          38  
          39   System.err(this.getClass().getName() + ".mymethod - 不能關閉輸出文件" + ioex2.toString()); 
          40  }
           
          41 }
           
          42}
           

          本文的結論不是放之四海皆準的教條,有時常識和經驗才是最好的老師。如果你對自己的做法沒有百分之百的信心,務必加上詳細、全面的注釋。

            另一方面,不要笑話這些錯誤,不妨問問你自己是否真地徹底擺脫了這些壞習慣。即使最有經驗的程序員偶爾也會誤入歧途,原因很簡單,因為它們確確實實帶來了“方便”。所有這些反例都可以看作Java編程世界的惡魔,它們美麗動人,無孔不入,時刻誘惑著你。也許有人會認為這些都屬于雞皮蒜毛的小事,不足掛齒,但請記住:勿以惡小而為之,勿以善小而不為。

          posted @ 2006-02-01 12:36 方佳瑋 閱讀(336) | 評論 (0)編輯 收藏

               摘要: java語言已經內置了多線程支持,所有實現Runnable接口的類都可被啟動一個新線程,新線程會執行該實例的run()方法,當run()方法執行完畢后,線程就結束了。一旦一個線程執行完畢,這個實例就不能再重新啟動,只能重新生成一個新實例,再啟動一個新線程。Thread類是實現了Runnable接口的一個實例,它代表一個線程的實例,并且,啟動線程的唯一方法就是通過Thread類的start()實例方...  閱讀全文
          posted @ 2006-02-01 12:34 方佳瑋 閱讀(2689) | 評論 (1)編輯 收藏

          僅列出標題  下一頁
          主站蜘蛛池模板: 塔河县| 固安县| 平果县| 台湾省| 台山市| 蛟河市| 韶山市| 天气| 阿拉善左旗| 朔州市| 吉林省| 青田县| 商城县| 双峰县| 万州区| 越西县| 化州市| 滨州市| 南阳市| 崇礼县| 屯门区| 八宿县| 常宁市| 平阳县| 富平县| 井研县| 廉江市| 邳州市| 弥渡县| 辽中县| 巴东县| 科技| 浦北县| 甘谷县| 灌云县| 珠海市| 洞口县| 大足县| 鄄城县| 龙川县| 公安县|