2011年6月16日 星期四

Java Inner Class 記事

inner class的作用並不只是隱藏名稱或程式碼
他最大的效果是能有效及完整的達到"多重繼承"-實際上繼承多個non-interface
以下做深入的筆記

inner class的個用途是當建立一個inner class時
inner class可以存取外圍物件的所有元素而無需添加任何飾詞,如下範例:

interface Selector {
  boolean end();
  Object current();
  void next();
}   

public class Sequence {
  private Object[] items;
  private int next = 0;
  public Sequence(int size) { items = new Object[size]; }
  public void add(Object x) {
    if(next < items.length)
      items[next++] = x;
  }
  private class SequenceSelector implements Selector {
    private int i = 0;
    public boolean end() { return i == items.length; }
    public Object current() { return items[i]; }
    public void next() { if(i < items.length) i++; }
  }
  public Selector selector() {
    return new SequenceSelector();
  }   
  public static void main(String[] args) {
    Sequence sequence = new Sequence(10);
    for(int i = 0; i < 10; i++)
      sequence.add(Integer.toString(i));
    Selector selector = sequence.selector();
    while(!selector.end()) {
      System.out.print(selector.current() + " ");
      selector.next();
    }
  }
} /* Output:
0 1 2 3 4 5 6 7 8 9
*/

 範例的inner class的SequenceSelector可以存取其外部的Sequence的類別成員
不論inner class內放置的層次有多深
所有的outer classes的成員,即使是private修飾也都可以被他存取

inner class必須記錄一個reference指向enclosing class(外圍類別)
當取用外部成員時,編譯器會自動用那個隱藏的reference取得
也因為inner class物件產生時必須要關聯至其enclosing class的某個物件
建構inner class物件的同時必須要有enclosing class的物件

inner class可以寫在程式的任意位置
這不是說他們只會在執行那個段落時產生
他們一樣會跟其他class一起被編譯出來,只是在該段落以外無法被使用

假如不需要inner class物件和enclosing class物件之間的連結關係
可以將inner class宣告為static,而這種class被稱為nested class
nested class在生成物件時不需要外圍class的物件,也無法存取外圍class的非靜態物件
一般的inner class不能擁有static data、static fields或nested class,但nested class都可以有

一般而言,interface內不能加入任何程式碼,但nested class卻可以是interface的一部分
任何加到interface的class會自動成為public和static
甚至可以在inner class中時做外圍的interface
如果想寫出讓某interface的不同實作都可以共用的程式碼時可以這麼做,如下:

public interface ClassInInterface {
  void howdy();
  class Test implements ClassInInterface {
    public void howdy() {
      System.out.println("Howdy!");
    }
    public static void main(String[] args) {
      new Test().howdy();
    }
  }
} /* Output:
Howdy!
*/

繼承inner class則必須注意要先建立外圍類別
指向outer class的reference也必須在建構式由我們建立,如下:

class WithInner {
  class Inner {}
}

public class InheritInner extends WithInner.Inner {
  //! InheritInner() {} // Won't compile
  InheritInner(WithInner wi) {
    wi.super();
  }
  public static void main(String[] args) {
    WithInner wi = new WithInner();
    InheritInner ii = new InheritInner(wi);
  }
}

Java Interface 記事

Interface存在的原因有二:
1.能夠向上轉型為多個型別
2.讓客戶端無法產生物件,確保"介面"的設計意義

如果使用的base class不帶任何函式定義式或任何成員變數
應優先選用Interface,其次才考慮abstract class
基本上在確定某些東西會成為base class,優先考量就是使他成為Interface
過度使用Interfaces會帶來額外的複雜度,必須確定有需求再使用

在類別實作多個Interface時
有不同的Interface間採用相同的method命名會導致出錯,應避免此情形

將Interfaces置於某個class中會形成巢狀的Interfaces
巢狀的Interfaces可以為private,意義是實作他的類別在外部不能向上轉型
而巢狀位於另一個Interface內時,那些Interfaces會自動成為public

2011年6月14日 星期二

Java使用多形時可能犯的錯誤

1.覆寫 private method

import static net.mindview.util.Print.*;

public class PrivateOverride{
    private void f(){  print("private f()"); }
    public static void main(String[] args){
        privateOverride po = new Derived();
        po.f();
    }
}

class Derived extends PrivateOverride{
    public void f(){  print("public f()"); }
}
//output:private f()

private method會自動成為final,也會被derived class遮蔽
例子中的Derived的f()是新的method

2.fields及static methods

物件只有一般的method可以是多形
假如base class 與 derived class 有相同的data member
derived class實際上包含了兩個同名的field,其中一個是繼承而來的
只是要呼叫base class的field就必須使用 super.field 的形式來取得
不過在實務上幾乎不會發生,將fields都宣告為private而不直接存取才是避免混淆的方法

static methos是伴隨著物件,而非個別的objects

Java關鍵字-final

final這個關鍵字依照使用的地方不同會有不同的效果及意義
以3個可以使用final的地方來分析:data、method、classes

final data-

告訴編譯器某塊資料是固定不變
但是在用來修飾object reference時須注意
final 會讓reference固定指向一個物件,但該物件的內容卻還是可以更動,array也是相同情況
Java允許產生留白的finals(blank final)
能宣告資料成員為final但不給予初始值,以保持彈性
例如:

public class BlankFinal{
    private final int i = 0;  //Initialized final
    private final int j;         //Blank final
  
    public BlankFianl(){
         j = 1;    //Initialized blank final
    }
    public BlankFianl(int x){
         j = x;    //Initialized blank final

    }
}

編譯器會強迫必須對所有的finals賦值
如果不是在定義處,就得在建構式中以運算式設定其值
此外,Java也允許將引述宣告為final,意指無法在函式中另該引數改指他處
如:  void doSth(final objectA a){...}

final methods-

使用final methods的原因是鎖住這個函式,使繼承者無法進行override
class中所有的private函式自然就會是final
所以將final用在private函式上沒有意義
假使嘗試要override private method,編譯器只會將其視為該class新建立的函式而不會override

final classes-

將class宣告為final表示此class不允許被繼承

2011年6月10日 星期五

Eclipse上安裝Spring IDE套件

Eclipse版本:Eclipse Java EE IDE 3.6.2  Helios

在Eclipse中輸入Spring的updatesite安裝Spring IDE
updatesite:http://springide.org/updatesite
安裝選擇 Core / Spring IDE 、Extensions(Incubation) / Spring IDE、Resources / Spring IDE
以上三項應該就可以了

遇到無法安裝的情形時
可以嘗試先安裝AJDT套件,簡單查一下資料發現這似乎是AOP技術的一種
套件位置:http://www.eclipse.org/ajdt/
挑最新的版本安裝即可

Java基本型別資訊

Java在運作上還是會將基本型別轉換成物件
透過自動封箱(autoboxing)將基本型別轉換成外覆類別
Java在class的data member為基本型別時會給予預設值
但在區域變數則不成立並會引發錯誤
所以在建立基本型別的物件時最好還是給訂初始值

基本型別              大小           外覆類型            預設值

boolean                  -               Boolean              false
char                    16-bit           Character           '\u0000'(null)
byte                      8-bit           Byte                   (byte)0
short                   16-bit           Short                  (short)0
int                       32-bit           Integer                0
long                    64-bit           Long                   0L
float                    32-bit           Float                   0.0f
double                64-bit           Double                0.0d

高精度數值
BigInteger       -  提供的整數支援任意精度,可以很精確的表達任意長度的整數數值
BigDecimal    -  提供任意精度的定點數,在需要精確小數點後計算的場合中適用

高精度的以上兩個類別雖然可以被視為外覆類別,但沒有對應的基本型別
在int或float型別能做到的在這兩個型別也能做到
只是必須以函式叫用方式取代基本型別的運算子,且速度比較慢
屬於以速度換取精度

2011年5月4日 星期三

[轉載] FreeMarker IDE

環境說明

作業環境:Windows XP SP3
軟體版本:
  • Eclipse v3.4.2
說明:FreeMarker 是一套 Java 的樣板引擎,然而 Eclipse 沒有支援 FreeMarker 所以要透過外掛,安裝FreeMarker IDE 可以將 FreeMarker 的語法加上色彩標記,這樣撰寫 FreeMarker 可以比較清楚。
Update Site: http://download.jboss.org/jbosstools/updates/stable
官方網站http://freemarker.sourceforge.net/
版本更新

安裝步驟

  1. 請參考 Subclipse 這篇文章使用 Eclipse 內建的「Software Updates...」來安裝來安裝
  2. 因為 FreeMarker IDE 是由 JBoss 所維護的,所以有許多的 JBoss 的外掛,基本上只要勾選「FreeMarkerIDE Feature」並按下「Install...」即可
    fmide-001.png
  3. 安裝完後,附檔名為「ftl」的 FreeMarker 樣板檔就會改變圖示。
    fmide-002.png
  4. 開啟編輯就會有色彩標示了。
    fmide-003.png

2011年3月18日 星期五

[轉載] 詳細解讀Tomcat 7的七大新特性

Tomcat的7引入了許多新功能,並對現有功能進行了增強。很多文章列出了Tomcat 7的新功能,但大多數並沒有詳細解釋它們,或指出它們的不足,或提供代碼示例。本文將明確描述TOMCAT 7中七個最顯著的特徵和新增的功能,並對其作出評論,而不是僅僅列出新的功能。本文還提供了代碼例子以方便你可以對其有更好的理解。

本文分為兩個部分,分別是「TOMCAT 7的新特性」和「TOMCAT 7」增強的功能。

TOMCAT 7新特性

1 使用隨機數去防止跨站腳本攻擊。
2 改變了安全認證中的jessionid的機制,防止session攻擊。
3 內存洩露的偵測和防止
4 在war文件外使用別名去存儲靜態內容。

TOMCAT 7的增強功能

5 對Servlet 3.0,JSP 2.2和JSP-EL 2。2的支持
6 更容易將Tomcat內嵌到應用去中去,比如JBoss
7 異步日誌記錄
根據Mark Thomas,Tomcat 7委員會的經理的說法,Tomcat 7最顯著的三個特徵是Servlet 3.0,內存檢測洩露和增強的安全特性。
Tomcat 7的例子程序中,包含了Eclipse的工程文件和Ant的構建文件,以方便去構建war文件。其中Eclipse工程文件有例子代碼描述了Tomcat 7的一些新特性。
下面逐一開始介紹。

TOMCAT 7新特性

1使用隨機數去防止跨站請求偽造攻擊。

Wikipedia將跨站請求偽造攻擊(Cross Site Request forgery,CSRF)定義為:「一種影響Web應用的惡意攻擊。CSRF讓用戶當進入一個可信任的網頁時,被強行執行惡意代碼。
經典的防止CSRF攻擊的方法是使用隨機數的方式,Wikipedia中定義為「利用隨機或偽隨機數嵌入到認證協議中,以確保舊的不能在以後的重放攻擊中被利用。」

Tomcat 7中有一個servlet過濾器,用於將隨機數存儲在用戶每次請求處理後的seesion會話中。這個隨機數,必須作為每次請求中的一個參數。 Servlet過濾器然後檢查在請求中的這個隨機數是否與存儲在用戶session中的隨機數是一樣的。如果它們是相同的,該請求是判斷來自指定的網站。 如果它們是不同的,該請求被認為是從其他網站發出並且會被拒絕。
這個servlet過濾器是十分簡單的,下面是從TOMCAT 源代碼CsrfPreventionFilter文檔中摘錄的片段:

public class CsrfPreventionFilter extends FilterBase {

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {

String previousNonce = req.getParameter(Constants.CSRF_NONCE_REQUEST_PARAM);
String expectedNonce = (String) req.getSession(true).getAttribute(Constants.CSRF_NONCE_SESSION_ATTR_NAME);

if (expectedNonce != null && !expectedNonce.equals(previousNonce)) {
res.sendError(HttpServletResponse.SC_FORBIDDEN);
return;
}

String newNonce = generateNonce();
req.getSession(
true).setAttribute(Constants.CSRF_NONCE_SESSION_ATTR_NAME, newNonce);

所以每個URL地址中都有一個從用戶session中提取的隨機數,下面是使用的JSTL例子:
在以前,JSTL中構造鏈接可以這樣:


< c:url var="url" value="/show" >
< c:param name="id" value="0" / >
< /c:url >
< a href="${show}" >Show< /a >

而現在可以這樣:


< c:url var="url" value="/show" >
< c:param name="id" value="0" / >
< c:param name="org.apache.catalina.filters.CSRF_NONCE" value="${session.org.apache.catalina.filters.CSRF_NONCE}" / >
< /c:url >

具體的例子可以參考TOMCAT 7自帶例子中的演示,這個過濾器可以在web.xml中進行配置,配置後,所有訪問如http://localhost:8080/tomcat7demo/csrf/的都必須帶上參數,不帶上參數的話會出現403禁止訪問錯誤。
當然這種方法的缺點就是所有的鏈接都必須帶上這個隨機數。

2 改變了安全認證中的jessionid的機制,防止session攻擊。

Session劫持攻擊通常是以下的情況:
1 惡意攻擊者先訪問一個網頁,由於cookie是以jsession id的方式存儲在瀏覽器中的,即使攻擊者不登陸,他可以偽造一個帶有jsession id的地址,把它發給受害者,比如
http://example.com/login?JESSIONID=qwerty)

2 受害者點這個帶有jsessionid的鏈接,提示輸入驗證信息之後就登陸系統。

3 攻擊者現在使用這個帶jsessionid的鏈接,以受害者的身份登陸進系統了。
對於攻擊者來說,將jsessionid加在url中以及通過一個惡意表單發送出去是很容易的事,對於session劫持攻擊的更詳細描述,請 參考Acros Security組織的白皮書「Session Fixation Vulnerability in Web-based Applications」。

TOMCAT 7對此的解決方案是一個補丁,它在驗證後改變了jsessionid。這個補丁主要是應用在TOMCAT 7中,當然在TOMCAT 5和6中也可以使用但只是有些不同。
根據Mark Thomas說的,應用了Tomcat 7的這個補丁後:
• TOMCAT默認情況下安全性不再變得脆弱,因為驗證後會話發生了變化
• 如果用戶改變了默認設置(比如應用程序不能處理變化了的session id),風險也會降到最小,因為在Servlet 3中,可以禁止在url中進行會話跟蹤。
而在TOMCAT 5和TOMCAT 6中,應用了補丁後:
• 能阻止session劫持攻擊,因為能讓TOMCAT在驗證後改變session id。
• 如果應用程序不能處理變化了的session id,可以通過寫自定義的過濾器去檢查request.isRequestedSessionIdFromURL()和其返回的結果,以降低風險。
以上這些改變都是TOMCAT在幕後所做的,開發者根本不用去理會。

3 內存洩露的偵測和防止

開發者在部署他們寫的程序到生產環境上時,經常會遇到Pemgen錯誤:OutOfMemoryError。這是由於內存洩露而引起的。通常開發者是通過增大permgen內存的大小去解決或者就是重新啟動tomcat。

TOMCAT 7包含了一個新的特性,它通過把不能垃圾回收的引用對象移走的方法,能解決一些Permgen內存洩露的問題。這個特性對程序員部署應用程序在他們的開發 環境中是十分方便的,因為程序員在開發環境中為了節省時間一般不重新啟動Tomcat就能部署新的war文件。在生產環境中,最好的建議還是停掉 TOMCAT,然後清除work下面的目錄文件並且重新部署應用。

當然,內存洩露檢測和防止這個特性現在還不是很完善,還是有的情況TOMCAT不能檢測內存洩露和修復之的,所以對於生產環境,最好的的辦法還是停掉TOMCAT,然後清除work下面的目錄文件並且重新部署應用。

Mark Thomas解析應用程序或者庫程序在如下情況下會觸發內存洩露:
• JDBC驅動的註冊
• 一些日誌框架
• 在ThreadLocals中保存了對象但沒有刪除它們
• 啟動了線程但沒停止
而 Java API 存在內存洩漏的地方包括:
1.使用 javax.imageio API ( Google Web Toolkit會用到)
2.使用 java.beans.Introspector.flushCaches()
3.使用 XML 解析器
4.使用 RMI 遠程方法調用
5.從 Jar 文件中讀取資源

4 在war文件外使用別名去存儲靜態內容

Web應用程序需要靜態資源文件,比如象CSS,Javascript和視頻文件、圖片文件等。通常都把它們打包放在war文件中,這將增加了 WAR文件的大小並且導致很多重複的加載靜態資源。一個比較好的解決方法是使用Apache HTTP服務器去管理這些靜態文件資源,下面是一個apache httpd.conf文件的配置摘錄:

< Directory "/home/avneet/temp/static" >
Order allow,deny
Allow from all
< /Directory >
Alias
/static "/home/avneet/temp/static"

以上的設置,使得訪問http://localhost/static時,能訪問到放在/home/avneet/temp/static下的資源。

允許使用新的aliases屬性,指出靜態文件資源的位置,可以通過使用 Classloader.getResourceAsStream('/static/...')或者在鏈接中嵌入的方法讓TOMCAT去解析絕對路徑, 下面是一個在context.xml中配置的例子:


< ?xml version="1.0" encoding="UTF-8"? >
< Context path="/tomcat7demo" aliases="/static=/home/avneet/temp/static" >
< /Context >

假設/home/avneet/temp/static這個文件夾存放有一張圖片bg.png,如果war文件以tomcat7demo的名字部署,那麼可以通過以下三個方式去訪問這張圖片
1 直接訪問
http://localhost:8080/tomcat7demo/static/bg.png
2 在HTML鏈接中訪問:< img src="/tomcat7demo/static/bg.png" / >
3 通過JAVA代碼訪問: ByteArrayInputStream bais = (ByteArrayInputStream)getServletContext().getResourceAsStream("/static/bg.png");
使用aliases的好處是可以代替Apache的httpd.conf的設置,並且可以在servlet容器範圍內訪問,並且不需要Apache。

TOMCAT 7的增強特性

5對Servlet 3.0,JSP 2.2和JSP-EL 2。2的支持

Servlet 3的增強特性有:
• 可以在POJO或者過濾器filters中使用annotations註釋(在web.xml中不再需要再進行設置了)
• 可以將web.xml分塊進行管理了。也就是說,用戶可以編寫多個xml文件,而最終在web.xml中組裝它們,這將大大降低web.xml的複雜性增 強可讀性。比如, struts.jar和spring-mvc.jar每一個都可以有一個web-fragment.xml。開發者不再需要在web.xml中去配置它們 了,在web-fragment.xml中的jar文件會自動加載,並且struts/spring-mvc servlets和filters也會自動裝配設置。
• 異步處理web的請求----這個特性在tomcat 6 中已經有了,現在在tomcat 7中以Servlet 3標準規範化了,能讓使用異步I/O的web應用程序可以移植到不同的web容器中。異步處理使用非阻塞I/O,每次的HTTP連接都不需要對應一個線 程。更少的線程可以為更多的連接提供服務。這對於需要長時間計算處理才能返回結果的情景來說是很有用的,比如產生報表,Web Servce調用等。
• 安全的增強---Servlet 3.0現在使用SSL 去加強了會話session的跟蹤,代替了原來的cookie和URL重寫。

6 更容易將Tomcat內嵌到應用去中去

Tomcat 7現在可以嵌入到應用程序中去,並可以通過程序去動態設置和啟動。像在CATALINA_HOME/conf/server.xml中的很多配置,現在都 可以用程序動態去設置了。在Tomcat 7前,Tomcat 6提供了一個嵌入類,它能方便地去配置Tomcat。但在TOMCAT 7中,這個類已被廢棄了。這個新的Tomcat 7的類,使用了幾個默認的配置元素,並提供了一個更容易和簡單的方法去嵌入Tomcat。

下面是CATALINA_HOME/conf/server.xml中的一些相關屬性和配置:

< Server >
< Service >
< Connector port="8080 >
< Engine >
< Host appBase="/home/avneet/work/tomcat7demo/dist" / >
< /Engine >
< /Connector >
< /Service >
< /Server >

我們可以通過程序去進行動態設置了:


final String CATALINA_HOME = "/home/avneet/work/temp/tomcat7demo/";
Tomcat tomcat
= new Tomcat();
tomcat.setBaseDir( CATALINA_HOME );
tomcat.setPort(
8080 );
tomcat.addWebapp(
"/tomcat7demo", CATALINA_HOME + "/webapps/tomcat7demo.war");
tomcat.start();
System.out.println(
"Started tomcat");
tomcat.getServer().await();
//Keeps Tomcat running until it is shut down
//Webapp tomcat7demo accessible at http://localhost:8080/tomcat7demo/

7 異步日誌記錄

TOMCAT 7現在包括了一個異步日誌記錄器(AsyncFileHandler)。AsyncFileHandler繼承了FileHandler類並能代替 FileHandler。使用AsyncFileHandler,時,只需要在CATALINA_HOME/conf /logging.properties中把FileHandler全部替換為AsyncFileHandler就可以了。要注意的是異步日誌不能跟 log4一起工作。

當有日誌發向AsyncFileHandler時,日誌被加入到隊列中 (java.util.concurrent.LinkedBlockingDeque)並且方法調用的信息會馬上返回不需要等待I/O寫到磁盤中。當類 加載器加載AsyncFileHandler時,會有一個單獨的線程啟動,這個線程會從隊列中讀取日誌信息並且寫到磁盤中去

這種方法的好處是如果I/O速度很慢(比如日誌要保存在遠端的設備上)時,記錄日誌的請求和處理過程不會顯得很慢。

AsyncFileHandler使用生產者和消費者的關係原理,在隊列中存儲日誌信息。隊列默認大小為10000。為了預防隊列溢出,默認是丟棄最後的信息。默認的隊列大小和溢出的設置都可以通過啟動參數進行設置。

關於TOMCAT 7的示例程序

TOMCAT 7的自帶程序例子有兩個servlets,一個是演示了如何採用隨機數的辦法防止CSRF攻擊,另外一個是描述了使用aliases。更新一下web/META-INF/context.xml,指出圖片的絕對路徑即可順利運行。

通過ant運行build.xml去將它們部署到tomcat 7中,使用如下兩個地址訪問:
• http://localhost:8080/tomcat7demo/csrf/
• http://localhost:8080/tomcat7demo/alias/