2017年8月12日 星期六

Chapter 13: 字串

StringBuilder
  • String可以用 "+" 來串連起來,但每次compiler都會產生一個新的StringBuilder物件來處理,效率較差。
  • 最好是直接用StringBuilder的append( )跟toString( )來串連字串,效率較高
public class WhitherStringBuilder {
     public String implicit(String[] fields) {
         String result = "";
         for(int i = 0; i < fields.length; i++)
             result += fields[i];
         return result;
     }
     public String explicit(String[] fields) {
         StringBuilder result = new StringBuilder();
         for(int i = 0; i < fields.length; i++)
             result.append(fields[i]);
         return result.toString();
     }
}

Chapter 11: 持有你的物件

Container的列印
  • Java容器庫分成兩類,Collection和Map,差別在於每個位置的元素個數。
  • Collection:每個位置只能持有一個元素
    • List
      • ArrayList
      • LinkedList (提供的功能較多,random access較慢,在中央位置做插入/刪除較快)
    • Set(元素不得重複)
      • HashSet:使用較複雜方法儲存元素,為取得元素最快速的方法,但元素內沒有順序。
      • TreeSet:元素會照比較結果排序。
      • LinkedHashSet:元素會照加入的順序來排序。
    • Queue
  • Map:每個位置持有兩物件,key及value。(下列三種Map的特性與Set類似名稱的特性相同)
    • HashMap
    • TreeMap
    • LinkedHashMap
  • 若希望可以不用管是用哪個container,而使用通用的存取方式,則可用iterator來達成。

Chapter 10: 內隱類別

撰寫inner classes
  • 若想要在outer class的non-static method之外產生一個inner class物件,須以OuterClassName.InnerClassName的形式指定該物件的型別。
public class Parcel2 {
     class Contents {
         private int i = 11;
         public int value() { return i; }
     }

     // 一般會在outer class中建立method來產生inner class物件
     public Contents contents() {
         return new Contents();
     }

     public static void main(String[] args) {
         Parcel2 q = new Parcel2();
         // Defining references to inner classes:
         Parcel2.Contents c = q.contents();
     }

  • outer class又可稱為enclosing class
  • inner class可以存取enclosing class的所有成員,就好像inner class自己擁有這些成員一樣。
  • non-static的inner class會自動記錄一個reference指向enclosing class的某個物件,後者就是此inner class物件的製造者。也就是一定會先有enclosing class物件才有辦法產生inner class物件。

使用.this及.new
  • 若想在inner class中產生指向outer class物件的reference,可以使用OuterClassName.this
public class DotThis {
     void f() { System.out.println("DotThis.f()"); }
     public class Inner {
         public DotThis outer() {
             return DotThis.this;
         }
     }
     public Inner inner() { return new Inner(); }
     public static void main(String[] args) {
         DotThis dt = new DotThis();
         DotThis.Inner dti = dt.inner();
         dti.outer().f();
     }
}

  • 若是想用outer class物件來產生inner class物件,可使用.new
public class DotNew {
     public class Inner {}
     public static void main(String[] args) {
         DotNew dn = new DotNew();
         DotNew.Inner dni = dn.new Inner();
     }
}

位於methods和scopes內的inner class
  • 基於兩個理由可能會想這樣做
    • 想實作某個interface,使你可產生並回傳其reference。
    • 希望在解決方案中設計某個class,又不希望此class給外界使用。
public class Parcel5 {
     public Destination destination(String s) {
         class PDestination implements Destination {
             private String label;
             private PDestination(String whereTo) {
                 label = whereTo;
             }
             public String readLabel() { return label; }
         }
         return new PDestination(s);
     }
     public static void main(String[] args) {
         Parcel5 p = new Parcel5();
         Destination d = p.destination("Tasmania");
     }
}

匿名的inner class
public class Parcel7 {
     public Contents contents() {
         return new Contents() { // Insert a class definition
             private int i = 11;
             public int value() { return i; }
         }; // Semicolon required in this case
     }
     public static void main(String[] args) {
         Parcel7 p = new Parcel7();
         Contents c = p.contents();
     }
}
  • 產生某個匿名class的物件,此匿名class是繼承自Contents。
  • new回傳的reference會被自動向上轉型為Contents reference。

巢狀類別
  • static的inner class被稱為nested class
  • nested class意味著:
    • 生成nested class的物件時不需要outer class的物件了。
    • 無法在nested class的物件中存取outer class的non-static物件。
    • 可擁有static data及nested class。

為什麼需要inner class
  • 若你想同時繼承多個抽象或具象的class,必須用inner class來做到。

繼承inner class
  • 繼承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);
     }
}

Chapter 09: 介面

抽象類別與抽象函式
  • 為了建立讓子型別都可用的共同介面,可以讓base class成為一個abstract class
  • 只有宣告而無本體的method稱為abstract method。(相當於C++的pure virtual function)
abstract void f();
  • 含有一個或多個abstract method的class需以關鍵字abstract做為此class的飾詞。
  • 不能產生abstract class的物件,因為他只是用來表示介面。
  • 繼承abstract class的話必須實做所有abstract method才能產生物件,若沒有實作所有abstract method,此class仍然要加abstract做為abstract class。
  • 若只是單純不希望某個class被產生出物件,也可以將沒有abstract method的class變為abstract class。

介面
  • interface可以想像成是"純粹"的abstract class。
  • interface內的data member都預設為public final static。
  • interface內的method都只能是宣告,而且預設為public。
  • 可以透過繼承來擴充interface,但繼承它的也要是一個interface。
  • 只有對interface允許extends後面接一個以上的interface。

interface vs. abstract class
  • interface的好處
    • 能被向上轉型到多個base class (多重繼承時)
    • 和abstract class一樣使其無法產生物件
  • 若不帶任何函式定義及成員變數,應該優先選擇使用interface

Chapter 08: 多型

多型 (Polymorphism)
  • 又稱為dynamic/late/run-time binding
  • 用base class的reference指向其derived class的物件,執行期間才依據物件是哪個class執行其所override的method。

陷阱: fields及static methods
  • 多型只用在一般的成員method對於成員變數及static method則是在compile time根據reference的class做存取

Polymorphic methods在建構式中的行為


  • 建構式中盡量不要呼叫method,除非其為final或是非override的method。呼叫會override的method可能會因為late binding造成問題且難以debug。

Chapter 07: 重複運用Classes

base class的初始化
  • Java編譯器會自動在derived class建構式中插入base class建構式的呼叫動作。
  • class A{
        A() {print("new A");}
    }

    class B extends A{
        B() {print("new B");};
    }

    public class C extends B{
        public C() {print("new C");}
        public static void main(String[] args){
              C instance = new C();
        }
    }
    Output =>
    new A
    new B
    new C
  • 如果沒有在class中定義預設constructor,編譯器會自動產生一個。
  • 如果定義了一個(不論是否有帶引數的)constructor,編譯器就不會產生預設constructor。
Name hiding
  • 若在derived class中新增overload的method,C++會把base class中同名的method遮蔽掉,但JAVA不會。
複合 vs. 繼承
  • 面對複合跟繼承的選擇時,考慮是否需要做"向上轉型",有需要才選繼承。
關鍵字final
  • final data
    • 不能改變的常數
  • final method
    • 確保此method在繼承過程中不能被override
    • 允許compiler將此method轉化為inline呼叫
  • final class
    • 此class不能被繼承
補充: 重要範例 1 !!
父類別
public class Super {
    static String s = "Super=====";
    static {  //類別的初始化
         System.out.println("instance super class static");
    }
    {  //類別物件的建立
         System.out.println("instance super class");
    }
    public Super(){  //建構子
         System.out.println("into Super constructor");
    }
}

子類別
public class Sub extends Super{
    static String s1 = "Sub1-----";
    static String s2 = "Sub2-----";
    static {  //類別的初始化
         System.out.println("instance sub class static");
    }
    {  //類別物件的建立
         System.out.println("instance sub class");
    }
    public Sub(){  //建構子
         System.out.println("into Sub constructor");
    }
}

Main類別
public class Main {
    public static void main(String[] arg){
         Sub sub = new Sub();
    }
}

執行結果
instance super class static
instance sub class static
instance super class
into Super constructor
instance sub class
into Sub constructor

補充: 重要範例 2 !!
Main類別
public class Main {
    public static void main(String[] arg){
         System.out.println(Sub.s);
         System.out.println(Sub.s1);
    }
}

執行結果
instance super class static
Super=====
//此時Sub類別並不會進行類別的初始化
instance sub class static
Sub1-----
//此時Sub類別才有進行類別的初始化動作

Chapter 05: 初始化和清理

Cleanup
  • 當garbage collector打算開始釋放物件所占的空間時,會先呼叫其finalize(),並且在下一次的garbage collect動作發生時回收該物件所占的空間。
  • 觀念:
    • 物件有可能不被garbage collector回收(程式執行到結束中間沒有發生memory不夠)
    • garbage collection並非deconstruction
    • garbage collection只回收memory
  • finalize()存在的意義:
    • free以new物件之外的方式配置某個儲存空間(例如呼叫C/C++的native function)
    • termination condition的檢查(協助debug)

Garbage collector的運作方式
  • Java從heap配置物件的速度逼近其他語言從stack挖掘空間的速度。
    • C++之類的heap在配置時需要時間去做"搜尋可用空間"之類的大動作。
    • Java heap像是輸送帶,配置物件時,heap指標只是單純的前往還沒配置的下一個區域。
    • 這是因為garbage collection會重新安排heap內的物件,讓它們緊密排列,避免page fault發生。

物件生成的過程(以名為Dog的class為例)
  • constructor實際上是static method,所以當Dog物件首次被產生,或是Dog的static member/method首次被存取,JAVA interpreter會找出Dog.class。
  • 當Dog.class首次被載入後,它所有的static初始化動作會被執行。
  • 從heap上配置Dog物件的空間。
  • 把這塊空間清為0,將物件的基本型別成為設成預設值,reference成員設成null。
  • 執行各成員在定義處的初始化動作。
  • 執行constructor。

static明確初始化
  • JAVA允許將多個static初始化動作置於static block中。
public class Spoon {
     static int i;
     static {
         i = 47;
     }
}

Variable Argument List
public class NewVarArgs {
     static void printArray(Object... args) {
         for(Object obj : args)
             System.out.print(obj + " ");
         System.out.println();
     }
     public static void main(String[] args) {
         // Can take individual elements:
         printArray(47, 3.14F, 11.11);
         printArray("one", "two", "three");
         printArray(new A(), new A(), new A());
         // Or an array:
         printArray((Object[])new Integer[]{ 1, 2, 3, 4 });
         printArray(); // Empty list is OK
     }
} /* Output: (75% match)
47 3.14 11.11
one two three
A@1bab50a A@c3c749 A@150bd4d
1 2 3 4
  • 可使用省略符號(...)來定義可變引數列,也可以用foreach來做iteration。

Chapter 04: 控制執行

Label
  • 可以在iteration述句(for, while..)前放label,可以在巢狀迴圈中讓break跟continue越過一個以上的巢狀level。
     label1:
     outer-iteration {
         inner-iteration {
             //...
             break; // (1)
             //...
             continue; // (2)
             //...
             continue label1; // (3)
             //...
              break label1; // (4)
         }
     }

  • (3) 是跳到label1的地方並重新進入外層iteration
  • (4) 是跳到label1的地方但不再進入iteration

Chapter 02: 萬事萬物皆物件

  • 除了基本型別外,Java把所有東西都視為"物件",但用來操控物件的識別字,實際上是其reference而已。
  • 你可以產生一個String reference:
    String s:

  • 但這麼寫只會產生一個reference,而不是實際的物件。


 基本型別
  • 基本型別有Wrapper class,如果想在heap內產生代表該型別的物件則可用其wrapper class。例如:
    Character ch = new Character(‘x’);
  • 所以這類變數直接存放資料值,並置於stack。
  • 這一類極小、極簡單的變數若透過new物件的方式置於heap上會顯得效率不彰。
  • Java SE5的autoboxing會自動將基礎型別轉成wrapper type:
     Character ch = ‘x’;
  • 然後將其取回:
    char c = ch;

Java中的Array

  • Java保證array一定會被初始化,放物件的array每個元素會初始化為null,放基本型別的array則會將memory都清為0。
  • Java對array的存取不會超過範圍,但每個array會額外多出一點點的空間,並得在執行期對index做檢查。

Class內基本成員的預設值
  • 當class內的成員是基本型別時,Java會保證它有預設值。
    •      boolean => false
    •      char => '\u0000' (null)
    •      byte/short/int/long/float/double => 0




2017年8月11日 星期五

Chapter 01: 物件導論

  • Java除了public, private, protected之外,還有一個預設的存取權限,當沒有使用上述的關鍵字時便是這種權限,又稱為package存取權限class可以存取相同package中其它class的的成員

  • Composition通常被視為has-a的關係,像是車子擁有引擎,如下圖。

2017年8月4日 星期五

Combining Reducers with combineReducers in Redux Tutorial


  • 當Application越來越複雜,我們可能會想要創造出不只一個的Reducer以管理state的不同部分。下面是一個state的範例,我們可能想要一個Reply Reducer只處理state中replies的部分,而User Reducer只處理state中users的部分。
{
  users: {},
  modal: {},
  posts: {},
  replies: {},
  listeners: {}
}


  • 想要達成的話,我們在createStore的時候就不會只帶入一個Reducer,而是把所有的Reducer import成一個物件,然後使用combineReducers()將這個物件轉成一個reducing function,最後再將這個reducing function帶入createStore。
import combineReducers from 'redux'
import * as reducers from '../reducers
...
const store = createStore(combineReducers(reducers))



  • 從combineReducers回傳的main reducer會呼叫每一個child reducer,並且收集它們的結果組合成一個state物件。







2017年8月3日 星期四

Route Protection


  • 假如我們想要保護一個route不讓它隨意進入,一個基本的做法是在那個route設定一個onEnter( )的callback當作prop,當要進入這個route之前呼叫它。它接收nextState以及一個replace參數,這個replace參數可以幫助我們取得想要進入的route,或是當使用者未經授權時取得取得轉到其他地方的route。

  • 來看一個例子,這裡有兩個主要的route,若你想進入feed但沒有得到授權,checkAuth應該會執行,你將被帶到' / '或是main index page。
<Router history={hashHistory}>
  <Route path='/' component={MainContainer}>
    <Route path='feed' component={FeedContainer} onEnter={checkAuth} />
    <Route path='logout' component={LogoutContainer} />
    <IndexRoute component={HomeContainer}/>
  </Route>
</Router>


  • 接著看看checkAuth會是怎樣,它會接收nextState跟replace參數,nextState用來取得想要進入的route路徑名稱,replace則是用來做redirect。在這個例子裡面,若使用者未經授權則會被轉到home view。
function checkAuth (nextState, replace) {
  if (nextState.location.pathname === '/feed') {
    if (isAuthed() !== true) {
      replace('/')
    }
  }
}






2017年8月2日 星期三

CopyOnWriteArrayList的使用


  • 最近在Project上遇到一個問題,有一個ArrayList在讀取時偶發出現ConcurrentModificationException,這是因為不同Thread間剛好一個在寫入一個在讀取ArrayList,當然這可以用Mutex或Synchronize來解決,只是想估狗看看有沒有更好的方法,後來查到了原來有CopyOnWriteArrayList這個東西!


  • 它和ArrayList很像,但是有以下特性:


  1. 它是Thread-Safe
  2. 在做寫入時(add()/set()/remove())會複製一份出來改,overhead較大
  3. Iterator支持hasNext()/next()等的讀取動作,但不支持remove()等的寫入操作
  4. 使用Iterator掃過一遍List的速度很快
  • 它的內部實作是透過volatile的資料以及Mutex來達到Thread-Safe,並且寫入時複製一份做修改,改完再設回給volatile的資料。

  • 適用在List項目不會太多,讀取多,寫入少,會有Iterator掃過一遍且需要Thread-Safe的情況。