11/04/2014
3:54:00 PM 0

Java flatMap vs C# SelectMany

  • Old Java
    String[][] names = { { "John", "Mary", "Steven" }, { "Tom", "Jeff" } };
    List<String> list1 = new ArrayList<String>();  
    for (String[] s1 : names) {
        for (String s2 : s1) {
            list1.add(s2);
        }
    }
    
  • Java 8 Stream API
    String[][] names = { { "John", "Mary", "Steven" }, { "Tom", "Jeff" } };
    List<String> list2 = Arrays.stream(names)
        .flatMap(s1 -> Arrays.stream(s1))
        .collect(Collectors.toList());
    
  • C# LINQ (method syntax)
    string[][] names = { new string[] { "John", "Mary", "Steven" }, new string[] { "Tom", "Jeff" } };
    List<string> list3 = names.SelectMany(s1 => s1).ToList();
    
8/26/2014
2:56:00 PM 0

Docker

Docker 是一個開源的軟體容器(software containers), 利用 Linux kernel 中的隔離(isolation)特性, 實現了OS level virtualization
  • 目前常見虛擬化技術類型(Virtualization Types)
    1. PV(paravirtualization), 透過修改OS核心,植入Hypercall,讓原本無法被虛擬化的指令,可以經過 Hyper interface 直接向硬體提出請求
      http://en.wikipedia.org/wiki/Paravirtualization
    2. HVM(hardware virtual machine)
      http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/virtualization_types.html
    3. Application level virtualization - JVM, CLR
    4. OS level virtualization - docker
  • Hypervisor 有兩種類型
    1. Type 1 (Bare-Metal hypervisor) - 又稱為 Native VM, hypervisor 直接安裝於機器上,直接掌控硬體資源,硬碟無須事先安裝OS,例如 VMware ESXi, 在 Xen 第一個建立 domain 稱之為 Dom0 它可以管理並且針對硬體做設定,還可以建立 DomU
    2. Type 2 (Hosted hypervisor)- 又稱為 Hosted VM,須先安裝 OS 才可使用,例如 VMware Player
  • docker 優點
    1. 輕量級虛擬化:啟動 Container 速度快,Container 重新啟動後,回復原本乾淨的環境
    2. 自動建構:使用 Dockerfile 可自動建構環境
    3. 版本控制
    4. Docker hub
  • docker 安裝 - Ubuntu 14.04, 可參考 http://docs.docker.com/installation/ubuntulinux/
    $ sudo apt-get update
    $ sudo apt-get install docker.io
    $ sudo ln -sf /usr/bin/docker.io /usr/local/bin/docker
    $ sudo sed -i '$acomplete -F _docker docker' /etc/bash_completion.d/docker.io
    
  • docker 指令
    $ sudo docker pull centos (取得 image)
    $ sudo docker images (安裝的 image 列表)
    $ sudo docker run centos yum install redhat-lsb -y (安裝 redhat-lsb)
    $ sudo docker commit containerID newImgName (commit 成新的 image)
    $ sudo docker run newImgName lsb_release -d (顯示 linux 版本)
    $ sudo docker run ubuntu ifconfig (顯示 ip)
    $ sudo docker run -i -t ubuntu /bin/bash (互動模式)
    $ sudo docker ps (顯示正在 run 的 process)
    $ sudo docker ps -l  <-- last
    $ sudo docker ps -a -s 
    (-a makes sure you see already closed containers,-s gives you the container file size as well)
    $ sudo docker run -p 9999:8080 imgName command
    (port forwarding,前面是實體 linux 的 port,後面為 container 的 port)
    $ sudo docker run -d -p 9999:8080 imgName command (Daemonize)
    $ sudo docker run -d -p 27017:27017 -p 28017:28017 -v /path/to/data/db/:/data/db myhubpath/mongodb
    (mounts the current working directory into the container)
    $ sudo docker kill containerID (kill 正在 run 的 process)
    
  • Dockerfile
    $ sudo docker build –t imgName .
    
2:47:00 PM 0

PLSQL Developer & SQL Navigator password 備份

  • PLSQL Developer 儲存的 password 預設存在 C:\Users\[username]\AppData\Roaming\PLSQL Developer\Preferences\[username]\user.prefs
    或安裝目錄下的 \PLSQL Developer\Preferences\[username]\user.prefs

    自訂路徑 可修改安裝目錄下的 \PLSQL Developer\params.ini 並且定義 prefpath 參數

  • SQL Navigator
    password 儲存在 HKEY_CURRENT_USER\Software\Quest Software\SQL Navigator ?.?.?\Logon Passwords
6/17/2014
6:16:00 PM 0

Named and Optional Arguments in C#, Python, JavaScript

  • C#

    C# 在使用具名引數上非常方便, function 不需做任何修改即可使用

    public double CalculateBMI(double height, double weight, bool isPrint = true)
    {
        double heightMeter = height / 100D;//double suffix
        double bmi = weight / Math.Pow(heightMeter, 2);
        //double bmi = Math.Floor(weight / Math.Pow(heightMeter, 2) * 100) / 100;
        if (isPrint) Console.WriteLine("BMI=" + bmi);
        return bmi;
    }
    

    傳入參數的方式使用類似 JSON 的表示法, 且傳入不需有順序性

    CalculateBMI(height: 170, weight: 65);
    CalculateBMI(height: 170, weight: 65, isPrint: false);
    
  • Python

    具名引數和選擇性參數,在 Python 稱做 keyword arguments & default argument values,宣告 keyword arguments 必須在函式參數的變數前面加上兩顆星號,** 可以將引數收集在一個 dictionary 物件中, 使用上要留意傳入參數宣告的順序, 下面的例子如果寫成 def calculateBMI(**keywords, isPrint=True): 是不合法的, keyword arguments 必須放在函式參數宣告的最後面

    import math
    
    def calculateBMI(isPrint=True, **keywords):
        heightMeter = keywords['height'] / 100
        bmi = keywords['weight'] / math.pow(heightMeter, 2)
        #bmi = math.floor(keywords['weight'] / math.pow(heightMeter, 2) * 100) / 100
        if isPrint:
            print('BMI=', bmi)
        return bmi
     
    calculateBMI(height = 170, weight = 65)
    calculateBMI(False, height = 170, weight = 65)
    

    Python 星號的用途不僅止於此,在某些相反情況下,譬如說你要傳遞的參數已經是一個列表但要調用的函數卻接受分開一個個的參數值,這時需要將已有的列表分拆開來,Python 有提供簡便的方式處理這樣的情形, 詳細可參考Unpacking Argument Lists, Extended Iterable Unpacking

  • JavaScript

    將多個參數集合而成一個 JavaScript 的物件當作參入傳入 function, 此種方式稱為 options hash

    function calculateBMI(options, isPrint)
    {
        var isprt = typeof(isPrint) == 'undefined' ? true : isPrint;
        var opts = options || {};
        var heightMeter = opts['height'] / 100; 
        var bmi = opts['weight'] / Math.pow(heightMeter, 2);
        //var bmi = Math.floor(opts['weight'] / Math.pow(heightMeter, 2) * 100) / 100;
        if (isprt)
            console.log('BMI=' + bmi);
        return bmi;
    }
    
    calculateBMI({height:170, weight:65})
    calculateBMI({height:170, weight:65}, false)
    
1:54:00 PM 0

Variable-length Argument in Java, C#, Python

  • Java
    public int sum(int... nums) 
    {
        //nums.getClass().getTypeName() -> int[]
        int total = 0;
        for (int num : nums ) 
            total += num;
        return total;
    }
    
  • C#
    public int Sum(params int[] nums) 
    {
        //nums.GetType() -> System.Int32[]
        int total = 0;
        for (int i = 0; i < nums.Length; i++)
            total += nums[i];
        return total;
    }
    
  • Python
  • def sum(*nums):
        #type(nums) -> <class 'tuple'>
        total = 0
        for num in nums:
            total += num
        return total
    
5/27/2014
2:16:00 PM 0

Apache Commons Logging 整合 java.util.logging

  1. 設定 commons logging 使用 java.util.logging

    在應用程式的 classpath 中加入 commons-logging.properties 檔案
    commons-logging.properties 內容如下,作用是要 commons.logging 使用 Jdk14Logger
    org.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger
    
    若不使用 commons-logging.properties, commons logging 會先搜尋 classpath 中是否存在 log4J , 若存在 log4J 則優先使用 log4J, 若不存 log4J 才會使用 java.util.logging, 順序可參考 org.apache.commons.logging.LogSource

  2. 建立 java.util.logging 設定檔

    建立設定檔 logging.properties, 檔名可自訂
    handlers= java.util.logging.FileHandler
    .level= INFO
    java.util.logging.FileHandler.pattern = test_%g.log
    java.util.logging.FileHandler.encoding = UTF-8
    java.util.logging.FileHandler.limit = 10000000
    java.util.logging.FileHandler.count = 10
    java.util.logging.FileHandler.append = true
    java.util.logging.SimpleFormatter.format = %1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %4$s %2$s %5$s%6$s%n
    java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter
    
  3. 設定 java.util.logging 讀取設定檔
    方法一
    InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream("logging.properties");
    
    try {
        LogManager.getLogManager().readConfiguration(input);
    } 
    catch (SecurityException | IOException e) {
        e.printStackTrace();
    }
    
    方法二
    設定 system property, 或在啟動程式時加入參數
    example: java -Djava.util.logging.config.file=logging.properties
    
    在不指定設定檔的狀況下, 預設會讀取 System.getProperty("java.home")/lib/logging.properties 檔案, 可參考 LogManager 文件
使用範例
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class TestLog {

    private static final Log log = LogFactory.getLog(TestLog.class);
 
    public static void main(String[] args) {
        log.debug("Debug Message");
        log.info("Info Message");
        log.warn("Warn Message");
        log.error("Error Message");
        log.fatal("Fatal Message");
     }
}
Log LEVEL 定義比較表

Java Logging Commons Logging
SEVERE FATAL
SEVERE ERROR
WARNING WARN
INFO INFO
CONFIG
FINE DEBUG
FINER
FINEST TRACE
5/22/2014
3:19:00 PM 0

Java Type Erasure

Java 的泛型其實是在編譯器上做了手腳,Java 在編譯時期由編譯器幫我們做了型別轉換的檢查,當 compile 成 bytecode 後,我們使用工具觀察,可以發現我們在程式中所使用的泛型全都不見了,也因此程式在 run time 時是看不到泛型的影子,這種泛型的實現方式稱作 type erasure, 其優點是確保了相容性, 而 .NET 的泛型就不一樣了, .NET 的泛型是真正運行於 CLR 層上的功能, 也因此 Java 的泛型與 .NET 相比, Java 多了許多限制

舉個例子,假設我們要實做一個 List, 當索引值超出 List 長度時, 回傳傳入泛型型別的實例

C#
public class Animal
{
    public override String ToString()
    {
        return this.GetType().Name;
    }
}

public class Cat : Animal {}
public class Dog : Animal {}
public class Monkey : Animal {}

public class AnimalList<T> : List<T> where T : Animal
{
    public new T this[int index] 
    { 
        get
        {
            if (index < this.Count)
            {
                return base[index];
            }
            else
            {
                return Activator.CreateInstance<T>();
            }
        }

        set
        {
            base[index] = value;               
        }
    }
}

public static void Main(string[] args)
{
    AnimalList<Animal> a = new AnimalList<Animal>()
    {
        new Cat(),
        new Dog(),
        new Monkey()
    };

    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(a[i]);
    }
}
C# 可利用傳入的 T 型別, 利用 reflection 建立物件實例

Java
public class Animal 
{
    @Override
    public String toString()
    {
        return this.getClass().getSimpleName();
    }
}

public class Cat extends Animal {}
public class Dog extends Animal {}
public class Monkey extends Animal {}

public class AnimalList<T extends Animal> extends ArrayList<T> 
{
    private final Class<T> clazz;

    public AnimalList(Class<T> clazz) 
    {
        this.clazz = clazz;
    }

    @Override
    public T get(int index) 
    {
        T result = null;
     
        if (index < size())
        {
            result = super.get(index);
        }
        else
        {
            try 
            {
                result = this.clazz.newInstance();
            }
            catch (InstantiationException | IllegalAccessException e) 
            {
                e.printStackTrace();
            }
        }
        return result;
    }
}

public static void main(String[] args) 
{
    final AnimalList<Animal> a = new AnimalList<Animal>(Animal.class)
    {{
        add(new Cat());
        add(new Dog());
        add(new Monkey());
    }};

    for (int i = 0; i < 10; i++)
    {
        System.out.println(a.get(i));
    }
}

因 type erasure 的原因, 造成 JVM 在執行時期, 無法利用傳入的 T 型別, 建立物件實例, 所以必須在 constructor 傳入 Class 參數, 利用 Class 建立實例

參考 : http://docs.oracle.com/javase/tutorial/java/generics/restrictions.html#createObjects
4/28/2014
1:24:00 PM 0

CSS 權重計算

CSS 的權重是一個重要觀念,初學者常常遇到新增了 CSS 去改變某個元件的樣式表,但卻沒有作用,很可能是所設定的樣式表被其他權重高的樣式表所覆蓋,以下說明一些權重的計算方式
  1. Style sheets 來源有三種
    • user agent – 瀏覽器預設樣式, ex: 在 IE 中 float 屬性(property)在瀏覽器預設值是 none, 可參考 http://msdn.microsoft.com/en-us/library/ie/ms530751(v=vs.85).aspx
    • author – 開發人員定義的樣式, 即網頁撰寫人員在網頁上所定義的樣式表
    • user – 用戶在瀏覽器中定義的 CSS,不同的瀏覽器有不同的設定方式, 以 IE8 為例, 在工具 / 網際網路選項 可自定樣式表

  2. 重要性(importance)規則
    • normal
    • important 在CSS規則後添加 !important 權重提高
    若沒有使用 important 就屬於 normal, important 就好比是古代的尚方寶劍,使用它可讓權重加到最大
    example:
    <html>
    <head>
    <style>
    #myid { color: green; }
    .myclass { color: gold; }
    p { color: blue !important; }
    </style>
    </head>
    <body>
    <p id="myid" class="myclass" style="color: red">important</p>
    </body>
    </html>
    
    output:
    important

  3. 階層順序(Cascading order),由低至高排列,越底下權重越高,因此會覆蓋上面所定義的樣式
    • user agent declarations
    • user normal declarations
    • author normal declarations
    • author important declarations
    • user important declarations
  4. CSS 選擇器權重計算方式 (Calculating a selector's specificity)
    • 第一級 inline > ID > class > element
      ID 和 class 功能看起來似乎有點相同,在一般狀況下 ID 是唯一不可重覆的,而Class是可被拿來被重覆使用
      example:
      <html>
      <head>
      <style>
      #myid { color: green; }
      .myclass { color: gold; }
      p { color: blue; }
      </style>
      </head>
      <body>
      <p id="myid" class="myclass" style="color: red">inline > ID > class > element</p>
      <p class="myclass">class > element</p>
      </body>
      </html>
      
      output:
      inline > ID > class > element
      class > element
    • 第二級 詳細度高,權重越高(若第一級權重相同,才需比較第二級)
      example:
      <html>
      <head>
      <style>
      p.myclass { color: blue; }
      .myclass { color: red; }
      </style>
      </head>
      <body>
      <p class="myclass">h1.myclass > .myclass</p>
      </body>
      </html>
      
      output:
      h1.myclass > .myclass
    • 第三級 若前兩級權重相同,依照CSS在文件中定義的順序,後定義的樣式會覆蓋前面定義的樣式
      <html>
      <head>
      <style>
      p.myclass { color: blue; }
      body .myclass { color: red; }
      .myclass2 { color: yellow; }
      .myclass1 { color: green; }
      </style>
      </head>
      <body>
      <p class="myclass">後蓋前</p>
      <p class="myclass1 myclass2">double class 後蓋前</p>
      </body>
      </html>
      
      output:
      後蓋前
      double class 後蓋前
      *注意, p.myclass & body .myclass 有無使用空格作用是不同的

    權重計算式


    將 inline, ID, class, element 轉成 a, b, c, d 的方式做比較, 若使用一個 inline 的 style 則 a = 1, 若使用兩個 class 的 style 則 c = 2, 以此類推, a 的權重最大, 因此 a 的數字大則優先權高, 若 a 相同則繼續比較 b 的數字, 以此類推, 假使 abcd 全都相同,則後定義的樣式優先

  5. 以下為 W3C 計算權重的參考範例
     *             {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
     li            {}  /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
     li:first-line {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
     ul li         {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
     ul ol+li      {}  /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
     h1 + *[rel=up]{}  /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
     ul ol li.red  {}  /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
     li.red.level  {}  /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
     #x34y         {}  /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
     style=""          /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */
     
3/27/2014
4:11:00 PM 0

JavaScript this 用法

JavaScript 是個 prototype based 的語言,有些觀念與物件導向不太相同,this 的用法就是其中一例,在 JavaScript 中, this 代表指向呼叫該函式的物件, 也就是函數的擁有者,而且在程式執行過程中 this 不可被重新指派
  1. global context 中的 this
  2. global context 中的 this 指向 global object, 若在瀏覽器中執行 this 指向 window 物件
    console.log(this === window);//true
    
  3. 一般函式中的 this
  4. 當我們使用 function func1(){} 的方式宣告函數時, 函數的擁有者是 global object, 所以 this 指向 global object
    function func1() { return this; }
    var obj = { func1 : func1 };//建立物件與函式的連結
    obj.func1 === this.func1;//true
    obj.func1() === obj;//函式內的 this 指向呼叫該函式的物件
    func1() === this;//指向 global object
    func1() === window;//true
    
  5. 巢狀函式(nested function) 中的 this
  6. function func2() {
        function func3() {
            return this;
        }
        return func3();
    }
    var obj = { func2 : func2 };
    func2() === this;//true
    obj.func2() === obj;//false
    obj.func2() === this;//true
    
    使用巢狀函式被呼叫時,this 是指向 global object, 這是要特別留意的地方,如果 func3 想取得 func2 的 this,可利用參數傳遞或 closure 的方式解決
    • 參數傳遞
    • var obj = {
          func2: function() {
              function func3(that) {
                  return that;
              }
              return func3(this);
          }
      }
      obj.func2() === obj;//true
      
    • closure
    • var obj = {
          func2: function() {
              var that = this;
              function func3() {
                  return that;
              }
              return func3();
          }
      }
      obj.func2() === obj;//true
      
  7. call & apply 中的 this
  8. 在JavaScript中,函式是Function的實例,Function 中有 call & apply function,可以讓使用者決定this的參考對象
    var o1 = {};
    var o2 = {};
    function func4() { return this; }
    func4.call(o1) === o1; //true
    func4.apply(o2) == o2;//true
    
    複習一下 call 和 apply 的差別,兩者的差別在於傳遞參數的方法不同
    call是一個一個指定參數, examlple :
    func.call(obj,"1","2","3")
    apply的第二個參數是陣列, examlple :
    func.apply(obj,["1", "2", "3"])
  9. constructor 中的 this
  10. 若將函式當作建構式來用,內部的 this 則指向 new 所產生的新物件
    function Employee(name, department) {
        this.name = name;
        this.department = department;
    }
    var e1 = new Employee('Albert', 'accounting');
    console.log(e1.name);
    
    利用 new 運算子建立物件,類似於下面的寫法
    var e1 = (function(){
        var _new = { constructor: Employee, __proto__: Employee.prototype };
        _new.constructor();
        return _new;
    })();
    
    如果對 prototype 的用法不熟悉可參考 Javascript Prototype Chain
參考 : The “this” Keyword
3/26/2014

Eclipse 正式支援 Java 8

Java 8 已於 2014/3月18 發布 , Eclipse 4.4 從 I20140318-0830 這個版本開始正式支援 Java 8

Eclipse Project 4.4.0 頁面 : http://projects.eclipse.org/projects/eclipse/releases/4.4.0

Eclipse Project 下載頁面 : http://download.eclipse.org/eclipse/downloads/

Eclipse 4.3 的使用者也可透過安裝 feature patch 的方式來支援 Java 8 新功能
https://wiki.eclipse.org/JDT/Eclipse_Java_8_Support_For_Kepler
3/19/2014
1:24:00 PM 0

Fibonacci numbers (斐波那契數列)

義大利數學家 Fibonacci 所發明,他描述兔子生長的數目時用上了這數列,假設兔子每個月都會生產小兔子,如此不停的繁衍,且兔子永不死去 此數列常在程式演算法的教科書中出現,數列的解法有很多種,以下列出幾個簡單的 Java 程式解法

簡單表達式
F0 = 0
F1 = 1
Fn = Fn-1 + Fn-2

遞迴解法
public long fib(int n) {
    if (n < 2) return n;   
    else return fib(n - 1) + fib(n - 2);
}
迴圈解法
public long fib(int n) {
    if (n < 2)
        return n;

    long x = 0L;
    long y = 1L;
    long temp = 0L;

    for (int i = 2; i <= n; i++) {
        temp = y;
        y = x + y;
        x = temp;
    }
    return y;
}
Fork / Join 解法
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

public class Fibonacci extends RecursiveTask<Long> {

    private final int n;

    Fibonacci(int n) {
        this.n = n;
    }

    @Override
    public Long compute() {
        if (n < 2) return (long) n;

        final Fibonacci f1 = new Fibonacci(n - 1);
        final Fibonacci f2 = new Fibonacci(n - 2);
        f1.fork();
        return f2.compute() + f1.join();
    }
 
    public static void main(String[] args) {
        final ForkJoinTask<Long> fjt = new Fibonacci(Integer.parseInt(args[0]));
        new ForkJoinPool().execute(fjt);
        try {
            System.out.println(fjt.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
Memoization 解法
記憶前幾次計算過的結果,避免重覆的計算,藉此來提高運算速度
import java.util.HashMap;
import java.util.Map;

public class Fibonacci {

    private static Map<Integer, Long> cache = new HashMap<>();

    public static long fib(int n) {
        if (n < 2) return n;
        return cache.computeIfAbsent(n, k -> Math.addExact(fib(k - 1), fib(k - 2)));
    }

    public static void main(String args[]) {
        fib(Integer.parseInt(args[0]));
    }
}
參考 :
http://en.wikipedia.org/wiki/Fibonacci_number
http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-11.html#%_sec_1.2.2
3/12/2014
6:04:00 PM 0

GoDaddy 設定將 domain.com 轉址到 www.domain.com

  1. 首先使用個人帳號登入
  2. 在 Products 管理畫面上, 展開 DOMAINS 的下拉表格, 按下 Launch 按鈕
  3. 修改 Domain Settings 內的 Forwarding 設定
  4. 按下 Add one now
  5. 輸入 Forwarding to 的網址
  6. 按下 Save 設定完成
  7. 完成後的畫面如下, 稍後片刻設定才會生效
3:48:00 PM 0

Java 8 Functional Interface

Java 8 新增了 lambda expression 的新概念, 如果要將 method 當作參數傳遞, 就要先定義所需的函式介面(Functional Interface)

什麼是函式介面呢? 簡而言之就是 interface 中, 只定義了一個可被 implement 的抽象 method

Java 內建的函式介面有很多,像是 java.lang.Runnable, java.util.concurrent.Callable, java.util.Comparator,這些 interface 的共同點就是只定義了一個抽象 method, 因為只定義了一個抽象 method, 當在程式中使用 lamdba expression 時, 編譯器自然知道是 implement interface 中的哪個 method

Java8 新增了一個 annotation : @FunctionalInterface, 如果在一個不是 functional interface 的 interface 用這個 annotation, compiler 就會顯示警告,使用方法如下
@FunctionalInterface
public interface TestFunctionalInterface {
    void test1();
}
若加入 void test2(); 則會發生錯誤

Java 8 的 API 文件上說明了建立 functional interface 的原則 => "a functional interface has exactly one abstract method"
只要符合上述原則,下面的例子使用了 default method 仍然是一個有效的 functional interface
@FunctionalInterface
public interface SubFunctionalInterface extends TestFunctionalInterface {
    default public void test2() {
        System.out.println("test2");
    }
}
Java 8 定義了許多通用函式介面, 置放於 java.util.function package 中, 開發者可視狀況需求使用所需的 interface
3/05/2014
5:46:00 PM 0

Java ThreadLocal

ThreadLocal 的用途 : ThreadLocal 是在多執行緒中儲存變數值的方式,當不同 thread 共用 ThreadLocal 實例,可設定和讀取屬於自身 Thread 的變數值而不相互干擾,用 thread local variable 來稱呼它或許更為恰當,其概念正好與 singleton 相反

ThreadLocal 提供 3 個 public method 與 1 個 protected method
T get()
protected  T initialValue()
void remove()
void set(T value)
ThreadLocal 內部儲存值的方式, 其實是利用到 thread 實例中的 threadLocals 實例變數(instance variable),threadLocals 是個類似於 HashMap 的物件, ThreadLocal 將自身實例當作 key, 將 set 的值設定到 threadLocals 物件參考中

ThreadLocal set method source code
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);//reference to t.threadLocals
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);//t.threadLocals = new ThreadLocalMap(this, value);
}
ThreadLocal set 的資料實際上是儲存於 thread instance 中, 這樣做的好處是當該 thread instance 被 GC 後 ThreadLocal 儲存的參考也跟著消失了,不會有記憶體洩漏(memory leak)的問題

ThreadLocal example:
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestThreadLocal {

    private final static ThreadLocal<Date> endDate = new ThreadLocal<Date>() {
        @Override
        protected Date initialValue() {
            return new Date();
        }
    };

    public static void main(String[] args) {
        final ExecutorService exec = Executors.newCachedThreadPool();
        final Runnable task = () -> { //Java lambda expression
            try {
                TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
            } catch (final InterruptedException e) {

                e.printStackTrace();
            }

            System.out.printf("Thread Completed: %1$d : %2$tF %2$tT%n", Thread
                    .currentThread().getId(), TestThreadLocal.endDate.get());
        };

        for (int i = 0; i < 10; i++) {
            exec.execute(task);
        }

        exec.shutdown();
    }
}
3/04/2014

解決更新 Java 版本後 Applet 無法執行的問題

在過去的 Web 上使用 Java Applet 是相當受歡迎的的技術之一,但 Applet 與一般的 Java 應用程式不同,瀏覽器必需加入 Java Plug-In 才可在瀏覽器上正確執行
如果最近你在 Windows 上做了 Java Update,升級完後卻發現原本可正常瀏覽的 Applet 卻無法執行,可檢查以下設定
  1. 設定 Applet 的 Java Runtime 版本
    • 開啟 Java 控制面板
    • 開始 > 設定 > 控制台 > Java, 點擊 Java icon 就可以開啟 Java 控制面板, 如果在 Windows 控制台中找不到可以到 JRE 的安裝目錄下尋找 javacpl.exe 並執行 (ex: Java\jre7\bin\javacpl.exe)
    • 指定 Java Runtime 版本
      Java 控制面板 > Java 頁籤 > 檢視 按鈕 > 依需求勾選已啟用欄位
      Applet 執行時可從 console 觀察 Applet 執行時所使用的 JRE 版本
  2. 設定 Java 安全性
    在 Java 7 之後, Java 控制面板在安全性設定方面做了增加
    Java 6 安全頁籤 Java 7 安全頁籤
    • 檢查在瀏覽器中啟用 Java 內容,此選項是否勾選
    • 使用新增例外網站功能,新版本的安全設定更加嚴謹,執行 Applet 時,如果出現 "您的安全設定值已封鎖自行簽署的應用程式,使其無法執行" 的訊息,可加入例外清單,降低安全性限制 (加入例外清單前請確認是否為信任網站)
  3. 啟用瀏覽器 Plug-in
    Java 控制面板 > 進階 > 瀏覽器的預設 Java

  4. 使用 x86 版本的 JDK 或 JRE
    部分舊版程式只支援 x86 的版本,因此在安裝 JDK 或 JRE 時儘量選擇 x86 的版本
2/27/2014
2:31:00 PM 0

Java 8 雙冒號 Method Reference

Java 8 加入了 lambda expression 賦予了 Java 新的能力, 在使用 lambda 的同時常發現神奇的 :: 雙冒號 (double colon) 穿插其中, :: 代表什麼意義呢?

舉例來說, 在過去我們要做個由小到大的數列排序,當然使用 java.util.Arrays.sort(Object[] a) 是最快的方式, 如果要自訂排序方式使用 anonymous inner class 是最簡便的方式

例 1 : 使用 anonymous inner class
Integer[] ary = new Integer[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

// anonymous inner class
Arrays.sort(ary, new Comparator() {
    @Override
    public int compare(Integer a, Integer b) {
        return Integer.compare(a, b);
    }
});

System.out.println(Arrays.toString(ary));
Java 8 之後你可以這樣寫

例 2 : 使用 lambda expression
Integer[] ary = new Integer[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

// lambda expression
Arrays.sort(ary, (a, b) -> Integer.compare(a, b));

System.out.println(Arrays.toString(ary));
是不是簡潔了很多呢? 但是我們發現 (a, b) -> Integer.compare(a, b)), 傳入兩個 Integer parameter 再 return Integer 不就跟 Integer.compare 一樣嗎? (a, b) -> 感覺好像是多餘的,因此有更精簡的做法, 就是使用 lambda expression 的 method reference

例 3 : 使用 method reference
Integer[] ary = new Integer[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

// method reference
Arrays.sort(ary, Integer::compare);

System.out.println(Arrays.toString(ary));

:: 雙冒號 (double colon) 是 Java 8 加入的新 operator, 稱作 Method Reference, Method Reference 提供式四個種類的用法

1. Reference to a static method
前面例3 Arrays.sort(ary, Integer::compare) 就屬此類, Integer.compare 是 static method

2. Reference to an instance method of a particular object
使用 instance method, 傳入參數與回傳型別必須與 interface 的 compare method 定義一致
class MyCompare {
    public int compareInt(int a, int b) {
        return a - b;
    }
}

Integer[] ary = new Integer[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

MyCompare my = new MyCompare();
Arrays.sort(ary, my::compareInt);

System.out.println(Arrays.toString(ary));
3. Reference to an instance method of an arbitrary object of a particular type
使用 instance method, 與前例比較原本是傳入兩個參數 (int a, int b) 回傳一個 int, 轉換為 a.compareTo(b) 的形式, 同樣都回傳 int
Integer[] ary = new Integer[] { 9, 8, 7, 6, 5, 4, 3, 2, 1 };

Arrays.sort(ary, Integer::compareTo);

System.out.println(Arrays.toString(ary));
4. Reference to a Constructor
Reference to a Constructor 相較其他三個比較複雜些,用下面的例子來說明,首先創建 ICarFactory interface 和 CarBean class
interface ICarFactory {
    CarBean getCar(String color, int year);
}

class CarBean {
    private String name;
    private int maxSpeed;

    CarBean(String name, int maxSpeed) {
        this.name = name;
        this.maxSpeed = maxSpeed;
    }

    String getName() {
        return name;
    }

    int getMaxSpeed() {
        return maxSpeed;
    }
}
要實作 ICarFactory, 傳統的方法是使用 "implements" 關鍵字來實作介面

Java 8 可使用 lambda expression, 實作可改寫成如下的方式,簡潔許多
ICarFactory f = (String name, int maxSpeed) -> { return new CarBean(name, maxSpeed); };
仔細觀察可發現,Interface 所定義的 getCar(String color, int year) 和 CarBean(String name, int maxSpeed) 傳入參數型別正好一樣,因此可使用 method reference => Reference to a Constructor 的方式,再把寫法簡化
ICarFactory f = CarBean::new;
測試 Reference to a Constructor
//ICarFactory f = (String name, int maxSpeed) -> { return new CarBean(name, maxSpeed); };
ICarFactory f = CarBean::new;
CarBean c = f.getCar("GTR", 300);
System.out.println("Car Name:" + c.getName() + " / Max Speed:" + c.getMaxSpeed());
使用 lambda 或 method reference 可有效的讓程式碼更簡短,也提供程式更大的彈性,不管用以上哪一種寫法都是由編譯器來做推斷和解釋完成,這是使用上需要留意的部分
2/21/2014

Race Condition

Race Condition 在多執行緒的開發中是一個重要的概念,當兩個 thread 同一時間點存取同一個共用變數,因為同步執行造成運算結果相互覆蓋的情形就是 race condition

wikipedia 上有一個很棒的表格,非常清楚的說明 race condition 的概念
以下表格引用自 wikipedia : http://en.wikipedia.org/wiki/Race_condition

狀況一 : Thread 1 & Thread 2 依序執行, 得到一個正確的運算結果
Thread 1 Thread 2 Integer value
0
read value 0
increase value 0
write back 1
read value 1
increase value 1
write back 2

狀況二 : Thread 1 & Thread 2 同時執行, Thread 2 的運算結果覆蓋掉 Thread 1 的運算結果, 最終得到一個錯誤的值
Thread 1 Thread 2 Integer value
0
read value 0
read value 0
increase value 0
increase value 0
write back 1
write back 1

因此在多執行緒的程式中使用 lock 來避免產生 race condition

在 Java 中使用 lock 的兩個主要作用
  1. mutex : 建立 critical section, 避免 race condition
  2. visibility : 維持記憶體一致(memory consistency)
2/20/2014
4:48:00 PM 0

Java Volatile

在 Java 多執行緒的環境中 synchronized 是相當常用的關鍵字之一,使用 synchronized 有兩個效果 mutex & visibility
mutex 主要目的是建立 critical section, 而 visibility 的作用是保持記憶體的一致性(memory consistency)

什麼是記憶體一致性呢? 我們知道一般物件是擺在記憶體內的,如果要對物件資料做運算,CPU 通常會將要做運算的資料複製一份到 CPU 的 register 加快處理速度,而運算完的資料有時並不會立刻寫回主記憶體,而是留在 register 內,在多執行緒的環境下,如果此時另一個 CPU 來讀取同一份資料就會產生記憶體不一致的問題,因此 Java 定義了 volatile 關鍵字來處理記憶體資料一致性的問題

Java Language Specification 對 volatile 的定義

Java Specification 裡只定義規範, 也就是使用 volatile 之後可保證所有的 threads 看到變數的值是一致的,並沒有硬性規定如何實作,給了實作 JVM 彈性,因此實作的方式可能有很多種,譬如說如果使用 volatile 關鍵字的變數就不可對變數使用 cache memory,直接對主記憶體資料作讀寫,這也是一種方式,當然實際實作可能沒那麼簡單

引用 Java Concurrency in Practice 的範例
public class NoVisibility {
    private static boolean ready;
    private static int number;

    private static class ReaderThread extends Thread {
        public void run() {
            while (!ready)
                Thread.yield();
            System.out.println(number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}
這個例子沒有使用 synchronized,有可能會因為記憶體不一致而造成無法預期的結果, 變數 ready 可能一直是 false, 也可能印出 0 的值,但在我的 PC 上怎麼測都測不出印出 0 的結果,正確的寫法是使用 synchronized 或使用 volatile 關鍵字, 將 ready, number 都標示為 volatile

至於在什麼狀況下可使用 volatile 呢? 可參考這篇文章 Java theory and practice: Managing volatility
  1. status flags
  2. one-time safe publication
  3. independent observations
  4. volatile bean
  5. The cheap read-write lock trick
使用 volatile 可獲得比使用鎖更高的執行效率,例如在讀取動作遠遠大於寫入動作的狀況下,就非常適合使用 volatile,但如果對於 volatile 用法不是很熟悉,使用鎖是比較不容易出錯的方法

參考 : http://www.ibm.com/developerworks/library/j-jtp06197
2/14/2014
1:31:00 PM 0

Mutex

Mutex 是 mutual exclusion 的縮寫, 中文譯為 "互斥",在多執行緒環境下,防止兩條以上的執行緒對一共用資源做同時存取的動作,一般做法是透過程式代碼 lock 的方式, 建立 critical section, 達到 mutex 的目的

下圖簡單表示 mutual exclusion 的意義
在 Java 1.4 可使用 synchronized 關鍵字來達到 mutex 的目的,但使用 synchronized 有些功能不易做到,例如
  1. 如果發現 lock 已被佔用,就只能等待,無法去處理其他的工作
  2. 在等待獲取鎖的狀況下,無法被 interrupt
  3. 自訂取得鎖的規則(公平鎖,非公平鎖)
Java 1.5 後新增 java.util.concurrent.locks package 補強 synchronized 的不足之處
  • ReentrantLock中文翻譯為"重入鎖",就字面上的意義代表當某個 thread 獲取某個鎖後,在未釋放鎖的情況下,可以獲得同一把鎖多次,因為 ReentrantLock 內部存在著一個計數器,計算取得鎖的次數,如果取得 lock 兩次,就必須 unlock 兩次才可真正的釋放鎖,這樣的設計可方便使用遞迴的方式呼叫(recursive call) lock()

    基本使用方式
    final Lock lock = new ReentrantLock();
    lock.lock();
    try { 
      // do something ...
    }
    finally {
      lock.unlock(); 
    }
    
    遞迴呼叫
    private final ReentrantLock lock = new ReentrantLock();
    
    public void execute() {
        try {
            lock.lock();//This lock supports a maximum of 2147483647 recursive locks by the same thread.
    
            System.out.println("lock|HoldCount=" + lock.getHoldCount());
    
            if (lock.getHoldCount() < 10)
                execute();//recursive locking by a single thread
    
        } finally {
            lock.unlock();
            System.out.println("unlock|HoldCount=" + lock.getHoldCount()
                + "|isHeldByCurrentThread=" + lock.isHeldByCurrentThread());
        }
    }
    
    從 Java 的 API Specification 我們可以看到 ReentrantLock 的 constructor 在不傳值的狀況下的狀況下,預設是個非公平鎖,如果我們要讓獲得鎖公平,就必需維持 thread 的排隊順序,這需要額外開銷,對執行效率是不利的,雖然獲取鎖的原則是不公平的,但 JVM 保證所有 thread 最終會獲得它們等待的鎖,避免 thread starvation

    ReentrantLock 有 lock, tryLock, lockInterruptibly 幾個獲取 lock 的 method
    • lock() : 如果拿不到 lock 就一直等待,直到拿到 lock 為止
    • tryLock() : 如果拿到鎖就 return true, 否則就 return false
    • tryLock(long timeout, TimeUnit unit) : 如果拿不到 lock, 則等待一段時間
    • lockInterruptibly() : 如果拿不到 lock 就一直等待, 在等待的過程中 thread 可以被 interrupted
    public void testLock() throws Exception {
        private final Lock lock = new ReentrantLock();
        lock.lock();
    
        Thread t = new Thread(new Runnable() {
    
            public void run() {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + " acquires the lock");
            }
        });
    
        t.start();
        Thread.sleep(3000);
        t.interrupt();//無作用,因 thread 並非在 wait,join,sleep 狀態
        Thread.sleep(Long.MAX_VALUE);
    }
    
    public void testLockInterruptibly() throws Exception {
        private final Lock lock = new ReentrantLock();
        lock.lock();
    
        Thread t = new Thread(new Runnable() {
    
            public void run() {
                try {
                    lock.lockInterruptibly();
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName() + " interrupted.");
                }
            }
        });
    
        t.start();
        Thread.sleep(3000);
        t.interrupt();//thread 在等待獲得 lock 的過程中被中斷
        Thread.sleep(Long.MAX_VALUE);
    }
    
  • ReentrantReadWriteLock 讀寫鎖內部維護一對鎖, 一把用於讀取的操作, 另一把用於寫入操作, 讀取鎖可同時被多個讀取者擁有, 而此時鎖所保護的資料處於唯讀狀態, 讀寫鎖是互斥的, 因此當沒有任何讀取或寫入鎖定時, 才可以取得寫入鎖定, 但是對於讀取來說沒有互斥性,可參考 Java Doc 對於 Interface ReadWriteLock 的說明 , 另外使用 ReentrantReadWriteLock 時, 在讀取 thread 很多, 寫入 thread 很少的狀況下, 可能會使得寫入 thread 一直處於等待狀態, 因此必需留意 thread starvation 的問題
2/12/2014
2:18:00 PM 0

Semaphore

提供平行運算環境中,控制多個 thread 存取共享資源的一種策略模式,semaphore 是一個計數器的概念,用於保護共享的資源

譬如說有多個 thread 想要對同一塊記憶體變數進行讀寫時,在讀寫這塊記憶體之前,先獲得一個保護變數的 semaphore,當獲得這個 semaphore 時,其他人都不能對這塊記憶體進行讀寫,直到 semaphore 持有者釋放 semaphore 為止,在這種狀況下 semaphore 的值只有二進位的 0 或 1,稱為 binary semaphores,當受保護的資源數量大於 1 的任意數量時,稱作 counting semaphores,semaphore 常用於實作 publisher subscriber model & resource pool 中

Java Semaphore example
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class TestSemaphore {
    public static void main(String[] args) {

        final int CONSUMER_COUNT = 20;
        final int RESOURCE_COUNT = 10;
        final ExecutorService exec = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(RESOURCE_COUNT);

        for (int i = 0; i < CONSUMER_COUNT; i++) {   
            final int INDEX = i;
            Runnable run = new Runnable() {
                public void run() {
                    try {
                        if (!semaphore.tryAcquire(2, TimeUnit.SECONDS))
                        {
                            throw new RuntimeException("timeout");
                        }

                        System.out.println("Acquire:" + INDEX + " / AvailablePermits:" + semaphore.availablePermits());

                        Thread.sleep(1000);
 
                        System.out.println("Release:" + INDEX + " / AvailablePermits:" + semaphore.availablePermits());
                        semaphore.release();
                   } 
                   catch (InterruptedException e) {
                   }
               }
           };

           exec.execute(run);
        }

        exec.shutdown();
    }
}
1/29/2014

CAPTCHA

CAPTCHA 是 Completely Automated Public Turing test to tell Computers and Humans Apart 的縮寫,主要功能防止有人使用惡意程式大量送出網頁表單

網頁 CAPTCHA
最基本的 CAPTCHA 是利用程式產生一組數字或文字圖像,讓使用者辨識,並輸入圖像中看到的文字,連同網頁表單一起送出,Server 判斷使用者輸入的答案是否正確,後來發展出解計算題,選擇題,西洋棋遊戲等等,各式各樣不同的作法,在此我們就基本文字 CAPTCHA 的實作方式做討論,因 HTTP 協定是 Stateless,所以網頁在實作 CAPTCHA 面臨如何記憶 CAPTCHA 答案的問題,常見的實作方式有三種

將答案記憶在
  1. web session
  2. 實作方便,但如果使用的人多,當 session 量一大,會有拖慢 web server 速度的風險
  3. database
  4. 使用資料庫相較其他兩種方法,實作較為複雜,建置資料庫也需要額外的開銷
    實作資料庫 CAPTCHA 可參考 CodeIgniter
    首先建立 Table
    CREATE TABLE captcha (
        captcha_id bigint(13) unsigned NOT NULL auto_increment,
        captcha_time int(10) unsigned NOT NULL,
        ip_address varchar(16) default '0' NOT NULL,
        word varchar(20) NOT NULL,
        PRIMARY KEY `captcha_id` (`captcha_id`),
        KEY `word` (`word`)
    );
    
    當使用者送出表單時,先刪除舊有驗證碼資料,然後使用 ip_address 和 captcha_time 作條件檢查輸入的驗證碼資料是否正確
  5. cookie 或隱藏欄位中
  6. 此法網路上也有許多實作的範例可參考,大致上的做法是將答案加密後放在隱藏欄位或 cookie 中,當表單送出的同時將加密後的解答一併送出到 Server 端,Server 端將使用者的答案與解密後的解答做比對,這個作法安全性是三種裡頭最差的,因為只要人工辨識一次後,將辨識後的解答與加密後的密碼記憶下來,未來送出表單時只要填入這一組解答和加密後的密碼即可輕易破解

實作 CAPTCHA 小技巧
  • 使用 Data URI scheme,圖檔使用內嵌資料寫法,增加網頁內容檢驗的難度
  • 檢查使用者送出的 CAPTCHA Code 不可為空值,避免成為漏洞
  • 若使用者輸入錯誤,則重新產生新的 CAPTCHA 圖形
  • 隨機產生不同的圖形格式(JPEG,GIF,PNG),或使用 GIF 動畫也是增加破解難度的方式之一

網路上有提供一些免費的 CAPTCHA 服務,若不想自己寫,可考慮使用

實際上現今 CAPTCHA 阻擋惡意程式的效果相當有限,一些 OCR 可輕易的破解(PWNtcha - captcha decoder),只能說防君子不防小人了
1/21/2014
3:25:00 PM 0

Java Atomic Operation

定義
代表一種不可再分割的操作,在 multithreading 的環境中 Atomic Operation 是達成 thread-safe 的方法之一
至於為什麼會取名 Atomic 呢? 可能是過去原子被認定為物質的最小單位,無法再分割有關

舉例來說,當我們在 Java 中執行宣告 int i = 12 會配置 32 bits 的記憶體空間並將 12 這個值寫到記憶體區塊中,將整數 12 寫入記憶體這個操作是一個 Atomic Operation,不會只做一半就被其他操作中斷,而影響指派(assignment)值的正確性

使用硬體裝置支援的 Atomic Operation 可有效地提高程式效率, Intel Software Developer’s Manual 有說明哪些操作 Guaranteed Atomic Operations

Java 除了 double 和 long 這兩個資料型別以外,所有的 primitive types & reference types 讀取和寫入操作都是 Atomic Operation, 宣告為 volatile 的 double 和 long 讀取和寫入操作也視為 Atomic Operation

從 Java 1.5 開始,增加了package java.util.concurrent.atomic,可以在不使用 lock 的狀況下,達到 thread-safe 的目的

舉個例子
我們如果要寫個 thread-safe 的計數器,Java 1.4 的作法
public class Counter {
    private int count = 0;

    public synchronized int incrementCount() {
        return ++count;
    }
}
在 Java 中 ++count 這個操作,不是 atomic operation 因此是 non-thread-safe,做個細部分解,++count至少包含三個以上的動作
  1. 讀取count的值
  2. 將步驟1讀取出的值加1
  3. 將步驟2計算完成的值寫回變數count
從 x86 CPU 的角度來看,會使用三條指令來處理
  1. 從 memory 讀取 count 值到 register
  2. 對 register 的值加 1
  3. 把新值寫回 count 所在的 memory address
在多執行緒的環境下為了 thread-safe,所以必須使用 synchronized 關鍵字,synchronized 可達到 mutex & visibility 的目的

Java 1.5 後就可以使用 AtomicInteger 來替代 lock,相當於使用了 CPU 的 compare and swap 指令,在 x86 的架構中稱作 compare and exchange,使用 AtomicInteger 的另一項優點是 visibility,所以記憶體的值是一致的(memory consistency)
public class Counter {
    private final AtomicInteger count = new AtomicInteger(0);

    public int incrementCount() {
        return count.incrementAndGet();
    }
}
所有 java.util.concurrent.atomic package classes 都使用 Atomic 開頭, 包含以下 classes
  • AtomicBoolean
  • AtomicInteger
  • AtomicIntegerArray
  • AtomicIntegerFieldUpdater
  • AtomicLong
  • AtomicLongArray
  • AtomicLongFieldUpdater
  • AtomicMarkableReference
  • AtomicReference
  • AtomicReferenceArray
  • AtomicReferenceFieldUpdater
  • AtomicStampedReference
1/16/2014
7:58:00 PM 0

Java time zone data

今天發現 Java 的 MIT Time Zone 會因為版本的不同而有不同的結果
System.out.println("java version=" + System.getProperty("java.version"));
Date now = new Date();  
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ");  
sdf.setTimeZone(TimeZone.getTimeZone("MIT"));  
System.out.println("MIT time=" + sdf.format(now));
以下為測試各版本JRE跑出來的結果
output
------------------------------------------------------------
java version=1.4.2
MIT time=2014-01-15 14:01:06.937-1100
------------------------------------------------------------
java version=1.5.0_22
MIT time=2014-01-15 14:01:26.025-1100
------------------------------------------------------------
java version=1.6.0_45
MIT time=2014-01-16 15:02:03.133+1400
------------------------------------------------------------
java version=1.7.0
MIT time=2014-01-15 14:02:14.400-1100
------------------------------------------------------------
java version=1.7.0_51
MIT time=2014-01-16 15:02:47.562+1400

在 1.4.2 版時 MIT 時區的 Raw Offset 是 GMT-1100,到了1.6.0_45 版時 MIT 時區的 Raw Offset 改成了 GMT+1400,而 1.7.0 版卻又變成了 GMT-1100, 看起來 Java 版本和時區是並沒有直接相關, 其實 Java 的時區資訊跟使用的 tz database 版本有關,關於 tz database 可參考 http://en.wikipedia.org/wiki/Tz_database

JRE 內部有一份屬於自己的 tz database,在 JRE 安裝目錄下的 lib/zi 目錄內記載著時區相關資料,因此時區資料不會因為OS的更新而改變,JRE 使用的 tzdata 版本資訊可參考 http://www.oracle.com/technetwork/java/javase/tzdata-versions-138805.html
至於如何得知JRE所用的 tzdata 版本,簡易的方法是使用文字編輯器打開JRE安裝目錄下的 lib/zi/ZoneInfoMappings 檔案,如下圖標註紅色的部分即是目前 tzdata 的版本
另外一個方法就是使用 TZupdater 來檢視和更新JRE的時區資訊
關於 TZupdater 用法可參考
http://www.oracle.com/technetwork/java/javase/timezones-137583.html
http://www.oracle.com/technetwork/java/javase/dst-faq-138158.html
http://www.symantec.com/business/support/index?page=content&id=TECH58669

印出所有時區 RawOffset 資訊
for (String id : TimeZone.getAvailableIDs())
{
    TimeZone tz = TimeZone.getTimeZone(id);
    System.out.println("ID:" + tz.getID() + "|Name:"+ tz.getDisplayName(Locale.ENGLISH) + "|RawOffset:" + tz.getRawOffset());
}
TimeZone class 也提供 inDaylightTime 來測試是否使用日光節約時間

MIT 是 Midway Islands Time 的縮寫, 屬於 West Samoa Time 時區,至於 Raw Offset 為什麼會改變,可能是政治或經濟因素而改變的吧
1/10/2014
3:06:00 PM 0

C# Class and Struct

1.Memory Allocation

Class => Reference Type
Struct => Value Type

new operator on a class => heap
instantiate a struct => stack

以上的差異造成傳遞資料到 method 中會有不同的結果

Class => a reference is passed
Struct => a copy of the struct is passed

以下引用 MSDN 的範例程式 http://msdn.microsoft.com/en-us/library/aa288471(v=vs.71).aspx
// struct2.cs
using System;

class TheClass
{
    public int x;
}

struct TheStruct
{
    public int x;
}

class TestClass
{
    public static void structtaker(TheStruct s)
    {
        s.x = 5;
    }
    public static void classtaker(TheClass c)
    {
        c.x = 5;
    }
    public static void Main()
    {
        TheStruct a = new TheStruct();
        TheClass b = new TheClass();
        a.x = 1;
        b.x = 1;
        structtaker(a);
        classtaker(b);
        Console.WriteLine("a.x = {0}", a.x);
        Console.WriteLine("b.x = {0}", b.x);
    }
}
Struct傳遞的是複本, 所以結果 a.x = 1, 而 b.x = 5

2.Constructors

Struct 不可使用無參數的constructor ,結構成員也不可設定初始值, 也不可使用 destructor

錯誤
public struct TheStruct1
{
    public TheStruct1()
    {      
    }
    public int x = 0;
}
正確
public struct TheStruct1
{
    public TheStruct1(int c)
    {
        x = c;
    }
    public int x;
}
TheStruct1 a = new TheStruct1();
Console.WriteLine("a.x = {0}", a.x);
TheStruct1 b = new TheStruct1(2);
Console.WriteLine("b.x = {0}", b.x);
TheStruct1 c = default(TheStruct1);//use default keyword
Console.WriteLine("c.x = {0}", c.x);
TheStruct1 d;
d.x = 99;//Initialize
Console.WriteLine("d.x = {0}", d.x);
當執行個體化結構且建構子未傳入值時,結構成員會自動初始化

執行結果
a.x = 0
b.x = 2
c.x = 0
d.x = 99

3.Inheritance

結構沒有繼承,但可實作介面

4.自訂結構在記憶體中的安排方式

使用 StructLayout(LayoutKind.Union) 和 FieldOffset

5.使用時機

struct被定義為輕量化的物件,與物件相比較 struct GC 的成本小,微軟建議在下面的狀況下可考慮使用struct

√ CONSIDER defining a struct instead of a class if instances of the type are small and commonly short-lived or are commonly embedded in other objects.
X AVOID defining a struct unless the type has all of the following characteristics:
  • It logically represents a single value, similar to primitive types (int, double, etc.).
  • It has an instance size under 16 bytes.
  • It is immutable.
  • It will not have to be boxed frequently.
引用自 http://msdn.microsoft.com/en-us/library/ms229017.aspx

參考
Structs  http://msdn.microsoft.com/en-us/library/saxz13w4.aspx
Classes and Structs  http://msdn.microsoft.com/en-us/library/ms173109.aspx
Class and struct differences  http://msdn.microsoft.com/en-us/library/aa664471(v=vs.71).aspx