2013年12月1日 星期日

String equals( ) 與 compareTo( ) 方法的不同

     Java 中要比對字串是否相等,我們可以使用 equals 或者 compareTo 方法來實現,但是它們之間有甚麼不同呢 ? equals 與 compareTo 之間主要的不同有下面兩點:

  1. 如果傳入的參數是 null 時, equals  總是回傳 false,而 compareTo 則是丟出 java.lang.NullPointerException。
    範例如下:
     "foo".equals((String)null)  // 回傳 false
     "foo".compareTo((String)null) == 0 // 丟出 NullPointerException
    

  2. compareTo 除了可以比對字串是否相等外,還可以得知哪一個字串比較大或者小,equals 只能比對是否相等。
     另外好奇的是,為何將 null 傳入 equals 裡面會沒事,看了一下原始碼你會發現類似下面的程式碼:
   if (anObject instanceof String) {
      ...  
   }
    而 null 是物件的參考常數值,它可以被任何物件所參考,但它不屬於任何物件。也就是說 null instanceof object 總是回傳 false,故在將它傳入 equals method 並不會丟出任何例外。

    下面是從 Oracle 上查詢到的資料,解釋有關 null type。有興趣的人可以連結過去查看。
4.1. The Kinds of Types and Values
There is also a special null type, the type of the expression null (§3.10.7, §15.8.1), which has no name.
Because the null type has no name, it is impossible to declare a variable of the null type or to cast to the null type.
The null reference is the only possible value of an expression of null type.
The null reference can always undergo a widening reference conversion to any reference type.
In practice, the programmer can ignore the null type and just pretend that null is merely a special literal that can be of any reference type.   

2013年11月24日 星期日

Eclipse 設定編碼方法

      當你載入其他人開發的專案或檔案時,有時會發現奇怪的字元時,就表示你的 Eclipse 預設的編碼方式與專案不同。 Eclipse IDE 提供三種設定編碼的方式:

  1. 設定單一檔案的編碼:只有該檔案被設定此編碼。
  2. 設定單一專案的編碼:只有所選的專案設定為此編碼。
  3. 預設所有專案的編碼:所有你新開的專案都以此設定為主,在 Windows 平台建議選擇 UTF-8 (繁體 Windows上 Eclipse 預設使用 MS-950 編碼 ),這樣切換到 Linux 上才不會要再設定一次。
      這三種設定的方式請參考下面的圖示:

  • 設定單一檔案的編碼


    • 設定專案的編碼
    • 預設所有專案的編碼

    [延伸閱讀]
         

    2013年11月17日 星期日

    解決 HTC One SV 更新後 "開發者選項" 不見的問題

        昨天更新我的 HTC One SV 後,今天發現開發者選項竟然不見了,上網查到的解決方式如下:
              設定>關於>軟體資訊>更多>狂按 建置號碼


    參考資料:

    旋轉 Windows 畫面

        今天電腦讓小朋友亂按,結果開始列跑到右邊去,上網查了一下如何旋轉畫面,終於把畫面給轉回來了~: )

        如果你想要旋轉 Windows 畫面可以透過下面的 Shortcut Key:
    1. Ctrl + Alt + Up Arrow  (這是我們一般操作的畫面 開始列在下方)
    2. Ctrl + Alt + Down Arrow (開始列在上面,畫面上下顛倒)
    3. Ctrl + Alt + Right Arrow (開始列在右邊)
    4. Ctrl + Alt + Left Arrow (開始列在左邊)
    參考資料:

    透過 getenv 與 getProperties 取得系統環境變數與JVM屬性設定

          在 Java 中我們可以透過 System.getProperties() 取得 JVM 參數設定,System.getenv() 來取得作業系統的環境變數設定。你可以在 The Java Tutorials 裡面參考 System Properties 與 Environment Variables 教學文章看到其他用法。

         如果你使用 Eclipse 你可以透過 -D<Key>=<Value> 的方式在 Run > Run Configurations... > (x)= Arguments > VM Arguments 來設定額外的 JVM 屬性設定。例如你可以填入 -DColor=blue 來設定 Color 屬性為 blue。如下圖所示:


    簡易的範例程式如下:
        // 列出目前所有的 JVM 屬性設定
        System.getProperties().list(System.out); 
        
        // 透過 getProperty 單獨取出特定的某個屬性設定值    
        System.out.println("Color = " + System.getProperty("file.separator"));    
    
        // 取出我們設定的屬性值,注意屬性名稱是 case sensitive
        // 如果你寫成 color 得到的會是 null
        System.out.println("Color = " + System.getProperty("Color"));
    
        // 你也可以使用下列方式,當所指定的屬性沒有鍵值時,回傳你所設定的預設值
        // 下面的範例當沒有設定 Color 屬性鍵值時會回傳預設的 Pink 值
        System.out.println("Color = " + System.getProperty("Color", "Pink")); 
    
        // 列出目前作業系統所有的環境變數設定
        Set<Map.Entry<String, String>> envSet = System.getenv().entrySet();
        for (Map.Entry<String, String> set: envSet) {
            System.out.println(set.getKey() + " = " + set.getValue());
        }
        // 透過 System.getenv(key) 的方式單獨取出你想要的屬性設定
        System.out.println("SystemDrive = " + System.getenv("SystemDrive"));
        System.out.println("TEMP = " + System.getenv("TEMP"));
    


    參考文章:



    2013年11月13日 星期三

    保護回傳參考型態的物件

          今天在 "最新 Java 7 程式語言 施威銘" 這本書中看到了一篇內容,大概的內容是講述當我有一個類別 Circle, 該類別裡面包含了另一個類別 Point 物件紀錄 Circle 的中心點,為了保護 Point 物件,所以在 Circle 裡面使用 private 來修飾 Point 物件;但當外部需要取得中心點資料時,便在 Circle 類別寫了一個傳回 Point 的參考物件 method,此時因為外部可以取得 Point 的物件參考,所以外部就可透過 所得到的 Point 物件參考來設定修改資料,這樣就違背了當初我們不想讓外部的使用者來隨便設定 Circle 的中心點的用意,也失去了把 Point 設成 private 作用。
         所以為了避免這種情況發生,可以使用下列方式:
    1. new 一個新的 Point 物件,拷貝目前 Circle 物件內的 Point 物件資料。
    2. 將 Point 變成 immutable 的類別,也就是不提供設定(setXX ) method。
    3. Point 類別 implements Cloneable 類別,然後回傳 clone 物件。
        書本使用第一個方式,在回傳 Point 物件參考的 method 中,可以先用 new 的方式將目前在 Circle 類別內的 Point 物件拷貝一份,然後傳回去。這樣外部修改 Point 物件時就不會影響原本的 Circle 物件裡 Point 物件資料了。

    範例程式:

    class Point implements Cloneable {
        private int x;
        private int y;
        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
        // 2. 把 setPoint 移除,讓 Point 變成 immutable 的類別
        public void setPoint(int x, int y) {
            this.x = x;
            this.y = y;
        }
        public int getX() {
            return x;
        }
        public int getY() {
            return y;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    class Circle {
        private Point point;
        private int radius;
        public Circle(int x, int y, int r) {
            point = new Point(x, y);
            radius = r;
        }
        public Point getPoint() {
            return point;
        }
        public Point getReadOnlyPoint() {
            try {
                // 3. 讓 Point 類別實作 Cloneable,然後回傳 clone 物件 
                return (Point)point.clone();
            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }
            return null;
        }
        public Point getReadOnlyPoint2() {
            //1. 使用 new 的方式,將目前的Point 資料拷貝一份
            return new Point(point.getX(), point.getY());
        }
        
        @Override
        public String toString() {
            return String.format("(%d, %d) ,radius = %d", point.getX(), point.getY(), radius);
        }
    }
    
    public class ProtectReferenceObj {
        public static void main(String[] args) {
            Circle circle = new Circle(1,1,2);
            Point pt = circle.getPoint();
            System.out.println("Before: " + circle);
            pt.setPoint(10, 20);
            System.out.println("After: " + circle);
            System.out.println("================================");
            System.out.println("Before: " + circle);
            Point pt2 = circle.getReadOnlyPoint();
            pt2.setPoint(11, 22);
            System.out.println("After: " + circle);
            System.out.println("pt2.x =  " + pt2.getX() + " pt2.y = " + pt2.getY());
            System.out.println("================================");
            System.out.println("Before: " + circle);
            Point pt3 = circle.getReadOnlyPoint2();
            pt3.setPoint(55, 99);
            System.out.println("After: " + circle);
            System.out.println("pt3.x =  " + pt3.getX() + " pt3.y = " + pt3.getY());
        }
    }
    


    輸出結果:
    Before: (1, 1) ,radius = 2
    After: (10, 20) ,radius = 2
    ================================
    Before: (10, 20) ,radius = 2
    After: (10, 20) ,radius = 2
    pt2.x =  11 pt2.y = 22
    ================================
    Before: (10, 20) ,radius = 2
    After: (10, 20) ,radius = 2
    pt3.x =  55 pt3.y = 99
    


    2013年11月5日 星期二

    ArrayList 使用 toarray 轉換成物件陣列

         在某些情況下,我們希望能夠將 ArrayList 物件所存的內容轉換成陣列的形式,在 ArrayList 中提供了 toarray 方法來達到我們的需求。下面是 Java 所提供的 toarray 方法的原始碼:

        public Object[] toArray() {
            return Arrays.copyOf(elementData, size);
        }
    
        public T[] toArray(T[] a) {
            if (a.length < size)
                // Make a new array of a's runtime type, but my contents:
                return (T[]) Arrays.copyOf(elementData, size, a.getClass());
            System.arraycopy(elementData, 0, a, 0, size);
            if (a.length > size)
                a[size] = null;
            return a;
        }
    
    
         有上面可知,我們有兩個方式可以使用 toarray 方法,一個是不帶任何參數 toarray 方法回傳 Object 陣列,另外一個是回傳你所傳入的型態陣列。
         我們先來看 public T[] toArray(T[] a) 的方法,從原始碼中,我們發現此方法會使用參數的 length 的屬性,代表該參數必須要先配置過記憶體才行,如果直接傳 null 給它會引發 "java.lang.NullPointerException" 的例外事件。再回頭看一下原始碼,會發現當你所配置的陣列元素小於目前 ArrayList 所儲存的元素時,toarray 方法會幫你配置足夠的元素來儲存目前 ArrayList 物件中所含的所有元素。而當你的所配置的陣列元素個數大於目前會將你所配置的第 size 元素設定為null。基本上你可以想成超出的元素都會被設置成 null 就可以。(PS: 要使用 ArrayList 其型態一定是參考型態,如果使用基本型態會出現編譯失敗 ,而物件陣列的預設值為 null)。

        另外一個 toArray法是回傳 Object 陣列,由於兩個陣列基底型別不同時,不能互相參照,所以你無法直接將回傳的 Object 陣列轉成你要的型態陣列。但是你可以針對每個 Object 元素做轉型。

    範例程式如下:

         static void toArrayDemo() {
            
            ArrayList<integer> list = new ArrayList<integer>(){{
                add(1);
                add(2);
                add(3);
                add(4);
            }};
    
            //"main" java.lang.NullPointerException
            //Integer[] test = null; 
            //test = list.toArray(test); 
            // or Integer[] test = list.toArray(null);
            
            // 配置的元素小於 ArrayList 所存的元素各數少時,
            // toArray 會幫你重新配置足夠的陣列個數來儲存。
            Integer[] test = list.toArray(new Integer[0]);
            System.out.print("<1>[ ");
            for (Integer val : test) {
                System.out.print(val + " ");
            }
            System.out.println("]");
            // 配置大於 ArrayList 所存的 元素個數,多餘的陣列元素都是 null
            Integer[] test2 = list.toArray(new Integer[10]);
            System.out.print("<2>[ ");
            for (Integer val : test2) {
                System.out.print(val + " ");
            }
            
            System.out.println("]");
            
            // 配置符合目前 ArrayList 所含的元素個數
            Integer[] test3 = list.toArray(new Integer[list.size()]);
            System.out.print("<3>[ ");
            for (Integer val : test3) {
                System.out.print(val + " ");
            }
            System.out.println("]");
            
            //無法將 Object 陣列轉成其他型態的陣列
            //[Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
            //Integer[] test4 = (Integer[] )list.toArray();
            
            Object [] obj = list.toArray();
            
            System.out.print("<4>[ ");
            for (Object val : obj) {
                // 你可以對每一個 Object 中的元素轉型成 Integer 型態的物件
                // 因為我們宣告的 ArrayList 是儲存 Integer 物件,所以每一個
                // Object 物件都是參考到  Integer 物件
                
                System.out.print( ((Integer)val).intValue() + " ");
                // 下面方式與上面轉型所列印出來的結果相同,因為每一個 Object 元素
                // 都是參考到 Integer 物件,而 Integer 的 toString method 就是列印其值。
                //System.out.print( val + " ");
            }
            System.out.println("]");
        }
    

    輸出結果:
    <1>[ 1 2 3 4 ]
    <2>[ 1 2 3 4 null null null null null null ]
    <3>[ 1 2 3 4 ]
    <4>[ 1 2 3 4 ]

    補充:
        Object[] obj = new Object[size] 與 Object[] intObj = new Integer[2]  的不同是,obj 陣列可以指定
    任何型態的物件參考,每一個元素值不一定要儲存相同的物件。而 intObj 是參考到 Integer 陣列所以其元素只能儲存 Integer 物件,儲存其他型態物件會引發 "java.lang.ArrayStoreException" 例外事件。另外 intObj 是參考到 Integer 陣列,所以你可以將 intObj 指定給其他 Integer 陣列來參考。

    範例程式如下:

        static void arrayDemo() {
            Object [] intobj = new Integer[2];
            System.out.println("obj instance Integer[] = " + (intobj instanceof Integer[]));
            obj[0] = new Integer(10);
            //Exception in thread "main" java.lang.ArrayStoreException: java.lang.Float
            //obj[1] = new Float(10.f);
            
            Object [] obj2 = new Object[2];
            System.out.println("obj2 instance Integer[] = " + (obj2 instanceof Integer[]));
            obj2[0] = new Integer(10);
            obj2[1] = new Float(10.f);
            System.out.println("obj2[0] = " + obj2[0] + " , obj2[1] = " + obj2[1]);
        }