顯示具有 Java 標籤的文章。 顯示所有文章
顯示具有 Java 標籤的文章。 顯示所有文章

2015年4月12日 星期日

在Raspberry Pi上架設Tomcat

Image(1)

Context
  • 這份筆記紀錄如何在樹莓派上架設Tomcat Server
  • 樹莓派為RPi 2 model B
  • 作業系統為Raspbian

2014年1月15日 星期三

OCPJP 證照考試重點技巧

java program


考題概念:


這份筆記主要是針對OCPJP SE6.0作的重點整理,由於考證照跟實務上的程式撰寫需要掌握的概念有不少差異,所以既使對java程式撰寫相當熟悉,也有必要針對證照考試做準備,以免白白浪費錢去報名,而差別有哪些呢?比方說寫程式時,不用特別注意語法錯誤,因為你的IDE通常已經幫你標記出有問題的地方了,而函數與語法在實務上我們並不會硬性的背下它們,而是參考相關技術文件。
另外OCPJP SE6.0會慢慢淘汰,新版的證照考試會加入實務程式撰寫,所以趁SE6.0還能考的時候把這份筆記分享給大家吧。
首先考題就大概三類

  • 語法:了解甚麼樣的程式碼會導致編譯錯誤
  • 例外:了解甚麼樣的程式碼會導致執行錯誤
  • 結果:執行正確,會輸出甚麼樣的結果

從網路上可以找到各類題型的標準答案,所以我想這篇筆記的重點不是提供標準答案,而是提供解題技巧和重點整理

2013年9月21日 星期六

泛型(Generics)

泛型
Image(28)[4]

泛型(generics)
  • 撰寫程式時,往往有一種情況是,我們無法再編譯期間確定程式碼的撰寫方式,也因此衍生出許多技巧,來克服這個問題,比方說利用動態記憶體配置,來解決無法在編譯期間確定使用者輸入資料量的問題,或者利用晚期鏈結(late binding)的技巧,使得呼叫的方法可以依照執行期間的狀況而決定。 
  • 泛型是物件導向的三個特性之一的多型(polymorphism)的一種技巧,多型的本意是「一種介面,多種使用方法」,然而在Java中多型通常指處理未知的資料型態,對不同的型別都給予相同的操作方式,也就是一種函式(function),多種方法,或者一種指令,多種方法。
  • 而在Java中,泛型則是針對特定指定型別產生對應型態的程式碼以處理不同型別,泛型將型別抽象化、參數化,型別參數化意味著我們可以更彈性的決定類別、介面與方法的資料型別,將主要的核心演算法與邏輯獨立出來,不再牽制於資料型別的限制,比方說,堆疊結構需要Pop()、Push()等方法,但堆入的元素型別可能是String、double、float...等,與其為每個型別都撰寫一種堆疊類別來處理來實現多型,不如用另外一種方式:泛型。泛型可以讓類別的型別參數化,也就是說我們可以定義完堆疊後,再告訴編譯器我們的堆疊會使用甚麼樣的資料型別。如此一來同樣達成了「一種介面,多種方法」的技巧,省下的重複撰寫的成本。
  • 在Java中,多型是針對函式的,而泛型是針對參數的。
  • 泛型可以讓你流暢的建立、型別安全,與可重複使用的程式碼。

簡單的範例
  • 接著我們舉例一個簡單的泛型示例:
    Image(29)[6]
  • Image(30)[8]
  • 輸出結果:
    Image(31)[6]

泛型的型別取決於型別引數
  • 一個版本的泛型型別參考與另一個版本的泛型型別參考,在型別上市不相容的,例如上述的程式中:
    iOb=strOb;  //錯誤

泛型如何提升型別安全
  • 或許你會發現,只要指定Object作為資料型別,再加上適當的轉型即可實現泛型機制,但泛型可以自動地保護型別安全,在撰寫程式中,你不需要輸入型別轉型的程式碼,也不須手動檢查程式中的型別轉換是否正確。
  • 舉例以Object來取代泛型
    Image(32)[5]
  • Image(33)[5]
  • 此程式相較於泛型有兩個缺點
    • 一、你必須明確使用強制轉型取得儲存在物件的資料
    • 二、許多型別不符的錯誤都是在執行時期才發現,使用Object建立泛型機制無法確保型別安全,泛型將執行期間的錯誤變成編譯期間的錯誤,這使得我們比較容易避免潛在問題

抹除
  • 泛型新增至Java的主要限制在於必須與舊版本相容,任何語言語法的變更,或是JVM的修改,都必須避免破壞到舊的程式碼,Java透過抹除(erasure)方式以符合這個限制,藉以實作泛型
  • 當編輯你的Java原始碼後,所有的泛型型別資訊會被抹除:Java會以限制條件的型別(如果沒有明確指定限制條件,則預設是Object)取代型別參數,然後再以適當的轉型動作(由型別引數決定),以確保能與型別引述指定的型別相容。編譯器會堅持型別一定要相容。
  • 泛型這種處理方式代表在執行時期型別參數是不存在的,他們只存在於原始碼。執行時期所有的泛型型別只以原始型別的形式存在
  • 舉例來說,以下兩段程式編譯的結果會是相同的:
    • Gen<Integer>iOb=new Gen<Integer>(99);
      int x=iOb.getob();
    • Gen iOb=new Gen(99);
      in tx=(Integer)iOb.getob();

橋梁方法
  • 有時候,子類別中的覆寫方法,其型別抹除的動作部會發生在父類別中的相同方法上,此時編譯器需要新增一個橋梁方法(bridge method)至類別中,以便處理這樣的情形
  • 橋梁方法只發生在位元碼的層級,我們看不到,也無法使用此方法

意義不明的錯誤
  • 新增泛型同時也產生了新型的錯誤:意義不明(ambiguity)的錯誤。
  • 抹除動作導致的問題是,當朗各表面上是不同的泛型宣告,進行抹除後卻解析成相同的型別,此時就會產生意義不明的錯誤並導致衝突:
    Image(34)[5]
  • 此範例的其中一個錯誤提示是:
    • Method set(V) has the same erasure set(Object) as another method in type MyGenClass<T,V>
    • 大意是說:在MyGenClass<T,V>類別中,set(V)方法在抹除(erasure)後,會成為set(Object),而使得它與另一個方法(此指set(T)相同,導致意義不明的錯誤(類別中不能有兩個相同的方法)
  • 解決的方法可以將類別定義改成class MyGenClass<T,V extends String>{//...
    • 不過要注意的是當你使用這樣的敘述:
      MyGenClass<String,String> x = new MyGenClass<String,String>();
    • 同樣會產生意義不明的錯誤
  • 意義不明的錯誤通常代表你的設計概念上有錯誤,要留意處理方式

泛型的限制
  • 泛型只運用在物件上:當宣告一個泛型類別的實體時,傳入的型別參數必須是類別型別,你不能使用基本型別,像是int或char:
    Gen<int> strOb=new Gen<int>(53); //錯誤,不能使用基本型別
  • 型別參數不能實體化:
    Image(35)
    錯誤提示為:Cannot instantiate the type T
    無法將型別T實體化,在執行時期T會被其他型別取代(型別參數會被抹除),而編譯時期T也只是佔位符號而已,編譯器無法知道該建立哪種型別的物件
  • 靜態成員的限制:
    Image(36)
    錯誤提示為依序為:
    Cannot make a static reference to the non-static type T
    Cannot make a static reference to the non-static type T
    ob cannot be resolved to a variable
    ob cannot be resolved to a variable
    意思是說非靜態型別的T是無法產生靜態成員參考的
    而沒辦法產生參考的ob也不能被解析成變數提供程式其他地方存取
    雖然你不可以使用有型別參數的static成員,但是可以宣告static泛型方法
  • 泛型陣列的限制
    Image(37)
    錯誤提示為:
    Cannot create a generic array of T
    Cannot create a generic array of GenericsArray<Integer>
    一、宣告型別T的陣列是合法的,但無法實體化泛型陣列,原因是編譯器並沒辦法知道實際上要建立陣列的物件型別為何,你可以用參考指派的方式(第9行)來將物件指派給泛型陣列,因為傳入Gen的陣列是已知型別的,T會與建立陣列時的型別相同。
    二、指定泛型型別的陣列是不被允許的(第14行),因為他們可能破壞型別安全,然而,如果你使用萬用字元,就可以建立泛型型別的陣列參考(第15行),者種方式比使用原始型別的陣列要來得好,因為至少它會執行一些型別檢查
  • 泛型例外的限制
    泛型的類別不能繼承Throwable。這意味著你不能建立泛型的例外類別。

延伸探討
  • 本文希望解釋泛型的核心價值,其餘的探討讀者若有興趣可以參考其他書籍或文章,以下延伸的主題僅做簡單的摘要,往後有必要可能在做章節補充,一般包括
    • 使用兩個型別參數的泛型類別:class class-name <T,V>{//...
      • 泛型類別的一般形
      • class class-name <type-param-list>{//...
      • class class-name<type-arg-list> var-name=new class-name<type-arg-list>(cons-arg-list);
    • 受限的型別:<T extends superclass>
    • 萬用字元引數與受限的萬用字元引數
      • 上限(extend)
      • 下限(super):<? super subclass>,下限條件的類別(subclass)不包含在限制條件內
    • 泛型方法:<type-param-list> return-type method-name((param-list){//...
    • 泛型建構式
    • 泛型介面:
      • 定義:interface interface-name<type-param-list>{//...
      • 實作:class class-name<type-param-list> implements interface-name<type-arg-list>{//...
    • 原始型別與舊的程式碼:泛型是新的功能,因此Java需要一些轉變的方式將舊的、泛型之前的程式碼轉換過來。
    • 泛型類別階層體系
      • 繼承泛型父類別
      • 執行時期犯行階層內的型別比較
    • 轉型:(Gen<Integer>)iOb
    • 覆寫泛型類別中的方法



2013年7月14日 星期日

多執行緒程式設計

多執行緒程式設計

多執行緒程式設計

已更新的 今日

多執行緒程式設計

Java提供許多執行緒程式設計(multithreaded programming),Multithreaded program包含兩個或多個可同步執行的部分,它的每一個部分都稱為一個執行緒(thread),因此,它是一種多工(multitasking)的特殊形式。

多工的工作方式:
  1. 以程序為基礎(process-based)
    • 本質上,process就是指正在執行的程式
    • 通常程式就是排程器所能分派(dispatch)的最小單位
    • 通常用以做重量級(heavyweight)工作的處理,需要有自己的位址空間
    • 程序間溝通消耗許多資源
  2. 以執行緒為基礎(thread-based)
    • thread為可分派的最小單位
    • 單一程式可以同時執行兩個以上的工作
    • 多處理輕量級(lightweight)工作
    • 消耗資源較程序處理小,共用相同位址空間
    • 一起使用相同的重量級程序,彼此溝通所耗資源較小,執行緒間切畫布用消耗太多資源

Java的執行緒模型
  • Java的所有類別函式庫都將multithread列入考量。
  • Java利用thread將整個環境變成非同步(asynchronous)。避免CPU週期所造成的浪費,有助於提升整體效率。

同步
  • 若兩個執行緒需要互相溝通,並且共用某個複雜的資料結構,就必須確保執行緒使用的資料是一致的。
  • Java利用monitor來達到這個目的,monitor只能容納一個執行緒,一旦一個執行緒進入monitor,其他的執行緒必須等待,直到這個執行緒離開。
  • monitor可用以保護共用資源,避免同一時間被多個執行緒操縱

建立執行緒
  • Java的執行緒是建立於Thread類別、他的方法,以及Runnable介面的基礎上
  • Thread將執行緒封裝
  • 無法直接參考正在執行的執行緒之狀態,必須透過產生該執行緒的Thread實體來處理
  • 若要建立執行緒,你需要繼承Thread類別或者實作Runnable介面。
  • 實作Runnable Interface
    • Runnable將一組可執行的程式碼抽象化
    • 可以在任何時做Runnable的物件上建構執行緒
    • 若要實作Runnable,執行要實作一個稱為run()的方法
      • public void run()
      • 在run()裡,你將定義構成新執行緒的程式碼
      • run()為另一個同時在程式中執行的thread建立進入點
      • 當run()回傳時,此執行緒將會結束
    • 在你建立一個實作Runnable的類別後,你將利用此類別建立Thread型別的物件實體
    • Thread定義許多建構式,其中一個如下
      • Thread(Runnable threadOb, String threadName)
        • threadOb:實作Runnable介面的類別實體。它定義了thread的進入點
        • threadName:指定新執行緒的名稱
      • 新執行緒建立後,必須呼叫Thread定義的start()方法,新的執行緒才會開始執行。
        • 本質上Start()會呼叫run()方法:若直接呼叫run()方法,則只是單純的方法呼叫,而不會啟動新的執行緒
        • start方法如下所示:void start()
    • 以下範例示範如何建立新的執行緒,並使之開始執行:
      • public class NewThread implements Runnable {
        Thread t ;
        public NewThread() {
        // TODO Auto-generated constructor stub
        t= new Thread (this, "Demo Thread");
        System. out. println("Child thread: " +t );
        t. start();
        }
        @Override
        public void run() {
        // TODO Auto-generated method stub
        try {
        for(int i=5;i>0;i--){
        System. out. println("Child Thread" +i );
        Thread. sleep(500 );
        }
        } catch (InterruptedException e) {
        // TODO: handle exception
        System. out. println("Exiting child thread." );
        }

        }

        }
      • //主程式
      • public class ThreadDemo {
        public static void main(String args[]) {
        new NewThread ();
        try {
        for(int i=5;i>0;i--){
        System. out. println("Main Thread: " +i );
        Thread. sleep(1000 );
        }
        } catch (InterruptedException e) {
        // TODO: handle exception
        System. out. println("Main thread interrupted." );
        }
        System. out. println("Main thread exiting." );
        }
        }
      • 在NewThread的建構式中
        • t=new Thread (this,"Demo Thread");
        • 將this當作第一個引數,表示你希望新的thread呼叫this物件上的run()

主執行緒(main thread)
  • 當Java程式開始執行時,有一個執行緒會立即開始執行,此執行緒稱之程式的主執行緒
    • 子執行緒都是由主執行緒產生
    • 通常它必須是最後一個完成的執行緒,因為它會進行許多關閉的動作
  • 透過currentTread()方法可以獲得主執行緒的參考,其為Class Thread的public static member
    static Thread currentThread()
  • 執行緒範例程式:
    public class CurrentThreadDemo {
    public static void main(String args[]) {
    Thread t =Thread .currentThread();
    System. out. println("Current Thread:" +t );
    t. setName("Main Thread" );
    System. out. println("After name change:" +t );
    try {
    for ( int n = 5; n > 0; n--) {
    System. out. println(n );
    Thread. sleep(1000 );
    }
    } catch (InterruptedException e) {
    // TODO: handle exception
    System. out. println("Main thread interrupted" );
    }
    }
    }
    • 將t透過println()印出,依次會顯示
      • 執行緒名稱:主執行緒名稱預設為main
      • 優先權:主執行緒優先權預設為5
      • 所屬群組名稱:執行緒群組(thread group)是一種資料結構,可把一群執行緒當成一個整體來控制
    • static void sleep(long milliseconds) throws InsterruptedException
      • milliseconds:執行緒暫停的毫秒數
      • InsterruptedException :當其他執行緒試圖中斷(interrupt)處於睡眠狀態的執行緒時,會發生此意外
      • sleep()的第二種形式,可以毫秒以及十億分之秒的方式指定暫停時間
        • static void sleep(long milliseconds,int nanoseconds) throws InsterruptedException
        • 只在計時週期小於十億分之秒的環境有效
    • final void setName(String threadName):設定執行緒名稱
    • final String getName():取得執行緒名稱
儲存至 Evernote