2017年11月19日 星期日

Integration test (4) - IEDriverServer

使用IE Web Driver進行測試時需要設定:

  • 先下載selenium-server-standalone-version.jar
  • 再下載IE driver,需要下載32bits的,因為現在64bits的driver會有打字非常慢的問題。http://forumsqa.com/question/typing-too-slow-in-text-fields-while-replaying-tests/
  • 在project內加入jar檔(Properties -> Java Build Path -> Add external jar)
  • 開啟IE,選擇“網際網路選項”->“安全性”->針對所有的安全性區域,都要勾選“啟用保護模式”
  • “進階”->設定攔裡”安全性“內的”啟用加強的受保護模式“不能勾選
  • 設定zoom level為100%,這樣mouse events才能夠滑到到正確的位置
  • 針對IE11,需要設定registry entry,這樣的話driver才能夠持續擁有連線的IE instance。

    • 在搜尋列輸入regedit
    • 32 bits: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BFCACHE
    • 64 bits: HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BFCACHE
    • FEATURE_BFCACHE不存在,則需要新增(右鍵FeatureControl,新增機碼(k))
    • 在FEATURE_BFCACHE內新增DWORD(32 bits),且命名為iexplore.exe,數值為0





2017年11月17日 星期五

Integration test (3) - TestNG

TestNG & JUnit 比較
https://www.guru99.com/junit-vs-testng.html

TestNG:
 - 測試open source framework,靈感來自JUnit及NUnit,但引入新功能
 - 有註解的特性
 - 依賴TDD
 - 支援multi-thread
 - 可以加入testng.xml文件添加想要測試的class
以下為TestNG註解意義
  • @BeforeSuite : 所有測試執行前
  • @AfterSuite : 所有測試執行後
  • @BeforeClass : 當下的Class前
  • @AfterClass : 當下的Class後
  • @BeforeTest : 內部class的<test>前
  • @AfterTest : 內部class的<test>後
  • @BeforeGroup : 
  • @AfterGroup : 
  • @BeforeMethod : 每個測試method之前
  • @AfterMethod :  每個測試method之後
  • @DataProbider : 
  • @Factory:
  • @Listeners: 定義一個測試的listener
  • @Parameters:如何將參數傳給@Test
  • @Test : 為一個class或test的方法
使用注釋的好處

以下是一些使用注釋的好處:
  • TestNG的標識的方法關心尋找注解。因此,方法名並不限於任何模式或格式。
  • 我們可以通過額外的參數注解。
  • 注釋是強類型的,所以編譯器將標記任何錯誤。
  • 測試類不再需要任何東西(如測試案例,在JUnit3)擴展。







在eclipse安裝步驟:
  1. 至http://testng.org/doc/maven.html
  2. 複製maven dependency
  3. 可以把原本default的JUnit刪去
  4.   <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <testng.version>6.8</testng.version>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>org.testng</groupId>
          <artifactId>testng</artifactId>
          <version>${testng.version}</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    </project>
    
  5. 確認TestNG是否在Eclipse上有安裝:

    • Help> EclipseMarketplace>Search上輸入TestNG
    • 若不是出現Installed則必須安裝
  6. 至chormedriver下載driver https://sites.google.com/a/chromium.org/chromedriver/downloads


Integration test (2) - Selenium

Selenium
 - Selenium webdriver是open source,可以針對編寫操作web的code
 - 為了Browser automation所設計的工具集合,讓程式可以直接驅使web做網站操作
  - 能夠直接獲取內容,可以和網頁互動,執行JavaScript,訪問Dom,模擬鍵盤輸入
 - 實現UI測試全自動化及做回歸測試
 - 由於瀏覽器有不同行為,所以需要進行維護

以下為編寫注意事項
  1. 使用DOM的ID進行定位-> 不受element位置更動而變動例如UI改版
  2. 特定項目操作繼承來設計class
  3. 使用Page Object Pattern,將所有頁面應該要使用到的操作放在一起,並由class使用
  4. 替測試做重試功能,直到成功或timeout
下載流程:
  1. 至http://www.seleniumhq.org/download/maven.jsp
  2. 可以看到對maven的dependency,並加入在pom.xml內,另可把版號放入properties裡
  3.   <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <selenium.version>3.7.1</selenium.version>
      </properties>
    
      <dependencies>
       <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>${selenium.version}</version>
        </dependency>  
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>3.8.1</version>
          <scope>test</scope>
        </dependency>
      </dependencies>
    </project>
    
  4. 可以至路徑 ~/.m2/repository 底下看到org的packages




Integration test (1) - Maven

Maven:
字意為:知識的累積者
- 標準化的建立專案,目的為一致的專案架構,統一的專案建構流程
- 不同的專案在code外有一套管理JARs的方式,能夠分享共同部分,補足差異檔案,且節省空間
- 用來建立專案的規則,包含原始檔案的相依性
- 自動測試與生成報告
- 從SVN GIT等取得原始碼

Pom:(Project Object Model)
- 專案建立標準化需要有共同的Metadata(對數據的一種說明),共同的目錄配置方式
- 舉例,像建立project物件,使用getXXX()取得資訊
- 格式為XML,能夠方便程式端的取用
- 格式內有

  1. <project> : 最上層的element
  2. <modelVersion>:哪一個model這個POM在使用,作為Maven驗證機制
  3. <groupId>:專案起始位置,如com.xxx
  4. <artifactId>:唯一可辨別的名字,這個名字是給產生的Jar檔使用的,會與版本變成一黨名 <artifactId>-<version>.<extension> (如, myapp-1.0.jar).
  5. <version>:專案版本,通常會加上-SNAPSHOT,表示最新的,但不保證是穩定會不會在改變(開發中,在release之前)。相反的,若不加上-SNAPSHOT,則表示此code不會再變動。也因此,版號也會
  6. <name> : 專案使用的名稱
  7. <url> : 專案的位址,通常會在Maven產生的文件中被使用到
  8. <description> : 專案描述,通常會在Maven產生的文件中被使用到
Artifact:
- 每個artifact都由以下三種組成唯一識別,需要使用的時候就需要放入repository
  1. groupId
  2. artifactId
  3. version
Repositories:
- 儲存Artifact
- 在pom聲明dependency後,就會去下載Artifact,供自己使用
- 自身開發的project也能夠使用mvn install放入Repository

目錄結構:
  • src/main/java - application/library sources
  • src/main/resources - application/library配置文件
  • src/main/filters - resource filter files
  • src/main/webapp - Web程式
  • src/test/java - 測試資源
  • src/test/resources - 測試配置文件
  • src/test/filters - 測試resource filter files
  • src/it - 使用integration test的plugins
  • src/assembly - 組合descriptors
  • src/stie - site
  • LiCENSE.txt - 專案license
  • NOTICE.txt - libraries使用的attributions即notice
  • README.txt - 專案readme
  • target - 所有build產生的output
  • src/main : 主要build artiface
  • src/test: unit test code或resources
Maven Command
以下為前置步驟:
  1. 至maven官網http://maven.apache.org/download.cgi下載apache-maven-3.5.2-bin.tar.gz
  2. 解壓縮至/usr/local/apache-maven-<version>
  3. 新增M2_HOME系統變數 
  4. export M2_HOME=/usr/local/apache-maven/apache-maven-<version>
  5. 新增M2系統變數 
  6. export M2=/usr/local/apache-maven/apache-maven-<version> 
  7. 可選擇是否要新增MAVEN_OPTS系統變數,可以提供Maven額外的選擇
  8. export MAVEN_OPTS="-Xms256m -Xmx512m" (ㄧInvalid maximum heap size: -Xmx512mi 的問題)
  9. 新增PATH
  10. export PATH=$M2:$PATH
  11. 確認JAVA_HOME JDK路徑設定正確
  12. echo $JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_152.jdk/Contents/Home 且必須確認PATH是否有包含$JAVA_HOME/bin的路徑
  13. 輸入 mvn --version 來卻確認版本
開始執行指令:
  • 建立新的Maven project: 
  • mvn archetype:generate
    Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 16: //可回上面的地16行看新建的type為何,可以選擇行數變更archtype
    Choose org.apache.maven.archetypes:maven-archetype-quickstart version:
    1: 1.0
    2: 1.1
    Choose a number: 2: 2 //選擇版本
    Define value for property 'groupId': com.test //groupId
    Define value for property 'artifactId': test //artifactId
    Define value for property 'version' 1.0-SNAPSHOT: : //snapshot版本
    Define value for property 'package' com.test: : //package name,直接按enter就是預設
    Confirm properties configuration: 
    groupId: com.test
    artifactId: test
    version: 1.0-SNAPSHOT
    package: com.test
     Y: : //直接按enter確認
    
    完成以上指令可以看到目錄下多了test folder,進入還能看到pom.xml
  • mvn compile 能夠下載需要的plugins,及其他dependencies中需要的jar),有時會需要到4分鐘以上。若是第二次下此指令,則只會下載新加入需要下載的jar檔,則會比較快一點。
  • mvn compile
    
    完成以上指令可以在${basedir}/target/classes看到檔案。好處是maven知道所有東西的位置。
  • mvn test 能夠執行maven的test
  • mvn test
    
    Maven此次會下載其他的dependencies。這次是下載跑test所需要用到的dependencies和pugins。如果已經下載過了,則只會做compile,不會再下載一次。若只想要compile而不想要執行test,則可以下以下指令:
    mvn test-compile
    
  • 產生Jar檔並且安裝在local repository
  • mvn package
    
    在POM檔案內有一段<packaging>jar</packaging>,這也就是告訴Maven如果執行以上指令則需要產生jar檔。如此將在${basedir}/target下產生Jar檔。 若想在${user.home}/.m2/repository安裝檔案,則需要下以下指令
    mvn install
    


2017年11月15日 星期三

使用JSoup Login網站取得cookie

目的:使用JSoup傳送帳號密碼取得Cookie,以便使用此cookie登入後繼續瀏覽網頁

Connection.Response res = Jsoup.connect("http://www.example.com/login.php")
    .data("username", "myUsername", "password", "myPassword")
    .method(Method.POST)
    .execute();

Document doc = res.parse();
String sessionId = res.cookie("SESSIONID"); 


比較需要注意的是data內的資訊,
根據Connection api 指出

Add a number of request data parameters. Multiple parameters may be set at once, e.g.: .data("name", "jsoup", "language", "Java", "language", "English"); creates a query string like: ?name=jsoup&language=Java&language=English

表示是送出一串request至web認證,而前面的是key,後面的是value,依序放入connect內。
key的取得方法,以yahoo
  1. 右鍵開啟檢查(Inspect)
  2. 切換到Network
  3. 先點左上角的清空按鈕(紅按鈕旁)
  4. 輸入測試帳號
  5. 可以看到Name欄位多了一個data(有時可能多個,可從名字看出可能是哪一個)
  6. 拉到最下面的From Data
  7. 此例的key即為username/passwd
另外 Jsoup.connect("http://www.example.com/login.php") 這裡面的url也可從此視窗最上面的Request URL中取得


2017年11月12日 星期日

NumberFormat

NumberFormat:
  1. nf.format(d)將會四捨五入
  2. format的setMaximumFractionDigits無法使用在parse()中

class Slice{
    public static void main(String[] args){
        String s = "987.123456";
        double d = 987.123456d;
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(5);
        System.out.println(nf.format(d) + " ");//987.12346
        try{
            System.out.println(nf.parse(s));//987.123456
        }catch (Exception e){
            System.out.println("got exc");
        }
    }
}


Serialization 序列化

用途:當想要儲存一個或多個object state(instance variable)的時候,就需要使用序列化;若沒有的話,實作就要特別小心傳送過去的值和收到的值有對應成功,非常麻煩。
- 除了被標示為transient的variable不會儲存外,其他都會被儲存
- 必須藉由implements Serializable interface來明確選擇哪些object可以被serialize
- implements Serializable不需要override任何methods
- 若有沒辦法implements Serializable interface(比如class設定成final無法修改)的情況下且需要儲存狀態,需要自行實作writeObject()或 readObject(),例:

class Dog implements Serializable {
 transient private Collar theCollar; // 不能序列化它 private int dogSize,自行實作
 public Dog(Collar collar, int size) {
  theCollar = collar;
  dogSize = size;
 }
 public Collar getCollar() {
  return theCollar;
 }
 private void writeObject(ObjectOutputStream os) { // override
  // throws IOException {
  try {
   os.defaultWriteObject();
   os.writeInt(theCollar.getCollarSize()); // 自行包裝dog size
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 private void readObject(ObjectInputStream is) { // override

  // throws IOException, ClassNotFoundException { try {
  is.defaultReadObject();
  theCollar = new Collar(is.readInt());// 自行取出dog size
 } catch (Exception e) {
  e.printStackTrace();
 }
}
}

- 若superclass implements Serializable,則所有底下的subclass都會跟著繼承,也就是subclass不需要明確的標示Serializable也能夠存取state
- 若superclass沒有implements Serializable,任何維護在superclass的state(從superclass繼承下來,且沒有override的),都不會回覆,將被重新初始化(就像是跑了一次 superclass的constructor)
- 若使用transient標示變數,代表不需要序列化此值,且該值會被恢復成default值,例:

class TestSer{
    public static void main(String[] args){
        SpecialSerial s = new SpecialSerial();
        try{
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("myFile"));
            os.writeObject(s); //s1.y ==7, s1.z == 9
            os.close();
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("myFile"));
            SpecialSerial s2 = (SpecialSerial)is.readObject();
            is.close();
            System.out.println(s2.y + " " + s2.z); // s2.y == 0, s2.z == 10
        }catch (Exception e){
            System.out.println("exc");
        }
    }
}
class SpecialSerial implements Serializable{
    transient int y = 7;
    static int z = 9;
}

注意:

  1. 最後output s2.y=0,原因是被標示為transient,不管initial值為何,都會變成該形態的default值
  2. 因為此例中z是static,代表memory只有存一份記憶體,且和instance無關,是class等級。所以最後取出的時候,值也會跟著變成10。
  • 綜合以上兩點代表static&transient 變數所屬的object並不能夠被序列化。




Collections, hashcode, equals

Collections&Arrays
兩者都有提供method搜尋特定元素,搜尋使用的是binarySearch()
  1. 成功會回傳找到的element int index
  2. 不成功會回傳insertion point的int值 (維持排序的順序下,應該被插入的地方)
  3. 正值和0都表示成功搜尋,第一個可以用的插入點為-1,實際的插入點為(-(insertion point) -1)
如:

key map marble pen 
Search apple: -1 //插入點為0,所以是(-(0)-1)=-1
key map marble pen 
Search map: 1 //第一個index找到
key map marble pen 
Search zibra: -5 //插入點為4,所以是(-(4)-1)=-5

Priority Queue
排序方式:用來依照優先權進出的Queue,element優先順序代表被取出的順序
  1. 根據element的基本順序
  2. 根據Comparator override決定
- offer method和add method是一樣的,都是加資料到queue:
boolean offer(E e)
Inserts the specified element into this priority queue.

boolean add(E e)
Inserts the specified element into this priority queue.

- pool是取queue優先權最高的,且移除,若queue是空的則會回傳null:
Element poll()
Retrieves and removes the head of this queue, or returns null if this queue is empty.


TreeSet/TreeMap (分別實作SortedSet/SortedMap (interface))
使用自然順序(natural order)排序
新增element的時候就會使用紅黑樹的吃料結構,保證所有元素都能夠根據自然順序,以升冪排序。也可以藉由Comparable或Comparator來排序。

public class Test3 {
    public static void main(String[] args){
        Set s = new TreeSet(); //Cannot add string and int to the same TreeSet, but HashSet is ok.
        s.add("2");
        s.add(3); //TreeSet is trying do sort, so ClassCastException shows
        s.add("1");
        Iterator it = s.iterator();
        while(it.hasNext()){
            System.out.print(it.next() + " ");
        }
    }
}

以上的範例當新增節點的時候就會有ClassCastException,原因是新增節點的時候就會試著排序。當新增一個String時,TreeSet就會預設這是一顆只會有String的樹,並且以String default的排序法排序。(若今天s是HashSet或LinkedHashSet則不會有任何Exception)
Java1.6版後TreeSet有實作NavigableSet,也就是可以指定其他set來定當原set的backup set

HashSet/HashMap
使用hashcode判斷object要如何被儲存在collections中,用來協助對object的定位



Hashcode&equals
使用“怦然鐔跳整理法”一書來解釋hashcode&equals:
想像你今天要整理房間,根據近藤麻理惠傳授的整理定義,你必須先把東西做分類,比如今天要整理衣服,你就需要把房間所有的衣服拿出來放在衣服堆; 今天要整理書本,你就要把所有的書本拿出來放在書本堆。為了加速等下找到的速度,你不只把衣服拿出來,你還會把衣服簡單分類,比如依照衣服的長度及顏色分成短淺、短深、長淺、長深四種。

而依照衣服的長度及顏色分到哪一堆,這就是一種hascode的實作。
把所有衣服都丟到同一堆,這是hashcode的實作,不過要找到你想要的衣服的時候就會變得非常的慢。所以hascode的實作跟之後找到物品的效率也非常的有關聯。

當你在分類的時候,你可能會發現手上剛剛才把一件“無肩帶美背小可愛-白色”放到短淺類別中,怎麼現在手上又拿著“無肩帶美背小可愛-白色”?這時候你會事不宜遲的把剛剛放進去的那件“無肩帶美背小可愛-白色”丟棄,並把手上的那件放到分類中。因為你對衣服的心動指數在於,一樣的衣服(同樣的名字,這邊每個人可以有不同的定義,也就是equals())只能在一個類別出現一次!!
假設今天都分好類了,接著你開始要做將衣服掛在衣櫃上。當你開始要整理衣服做心動分類的時候,你的姊姊進來問你『欸,你上次跟我借的那件”粉色雪紡碎花荷葉邊短袖開釦V領上衣“是不是還沒還我!?限你三分鐘找出來!』
這時候你會慶幸好險剛剛有先使用衣服長度及深淺分四大類,好險不是只有做衣服堆。所以你開始思考那件短的淺的的“粉色雪紡碎花荷葉邊短袖開釦V領上衣”在哪

  1. 你蹲到衣服短淺堆前
  2. 你開始一件一件翻,找那間叫做“粉色雪紡碎花荷葉邊短袖開釦V領上衣”的衣服在不在裡面
這兩件事情的翻譯其實是
  1. 重新做一次hashcode()(衣服的長度及顏色分類)找出是哪一堆(因此找到了短淺堆)
  2. 執行equals()找到自己要的衣服
而這邊的equals就會是衣服的名字。當你在實作equals的時候,裡面的程式碼可能就會是this.name == (cloth(object)).getName()。如此,你就能找到那件“粉色雪紡碎花荷葉邊短袖開釦V領上衣“。當然也有可能找不到,那麽得到就會是null。hashcode(分類)只是告訴你在哪找,沒有保證一定找得到。

以下總結存入讀取步驟:
放入hashtable:
  1. 執行hashtable()決定分類
  2. 執行equals()看是否在分類有一樣的object
讀取特定物品:
  1. 執行hashtable()找出分類
  2. 對裡面的每個object執行equals(),若==true代表找到了!

Jar命令提示指令


  • 將app資料夾內的檔案歸檔包裝至MyApp.jar 
  • jar -cf MyApp.jar app 
    
  • 列出MyApp.jar檔案內容
  • jar -tf MyApp.jar 
    
    執行後如下範例
    » jar -tf MyJar.jar
    META-INF/
    META-INF/MANIFEST.MF
    myApp/
    myApp/Baz.class
    myApp/Baz.java
    myApp/Foo.java
    myApp/myAppSub/
    myApp/myAppSub/Bar.java
    
  • 將MyJar.jar解開 
  • jar -xf MyJar.jar

Generic Types 泛型

版本:
Java 5 之前沒有辦法宣告Type safe的collections。需要建立一個 String 的 ArrayList,是無法使用角括號的,因此只能寫成:

ArrayList myList = new ArrayList();
List myList = new ArrayList();

class Parent {}
class Child extends Parent {}
List <Parent> myList = new ArrayList <Child> ();
上面的範例程式碼無法運作,Gereric types左右必須相等,不論他們的關係是否是subtype或supertype都不行

以下兩種也都是錯誤範例:

List<Object>myList=newArrayList<JButton>(); 
List<Number> numbers = new ArrayList<Integer>();

正確範例:

List<JButton> myList = new ArrayList<JButton>(); 
List<Object> myList = new ArrayList<Object>(); 
List<Integer> myList = new ArrayList<Integer>();

多型只能用在base,也就是collections的type,如:
List<JButton> myList = new ArrayList<JButton>();
是可行的,但不要搞混了,Array是可以內部存的object是多型:

Object[] myArray = new JButton[3];

以下程式碼將無法編譯成功,原因是試圖將int及string加入TreeSet,但TreeSet會去排序,所以當他不知道如何排序String和int時,將會拋出ClassCastException

public class Test3 {
    public static void main(String[] args){
        Set set = new HashSet(); //Cannot add string and int to the same TreeSet, but HashSet is ok.
        set.add("2");
        set.add(3);
        set.add("1");
        Iterator it = set.iterator();
        while(it.hasNext()){
            System.out.print(it.next() + " "); //TreeSet is trying do sort, so ClassCastException shows
        }
    }
}



2017年11月11日 星期六

Inner Class

Inner Class 
設計目的:假設事件處理的程式碼都在一個class,但是為了要讓這個事件處理的class在執行的時候可以很容易存取某個class的members(例如聊天室的文字方塊,介面..)。這時候就可以使用Outer class和Innter class有特殊連結關係的Innter class
  • Inner class就像是Outer class的member,所以可以存取Outer class的private instance variable
以下範例顯示如何在Outer class呼叫Innter class

class MyOuter {
 private int x = 1;
 public void testInner() {
  MyInner in = new MyInner(); // 建立內部類別的實體
  in.innerGo();
 }
 class MyInner {
  public void innerGo() {
   System.out.println("Outer x is " + x); // 可以使用private variable
  }
 }
}

若在其他的class要呼叫MyInner class的話,記住class type是[OuterClass].[InnerClass]

MyOuter.MyInner inner = new MyOuter().new MyInner();

就像是member,若要使用inner class,外部的static method也不能使用非static的inner class

class pockets {
    public static void main(String[] args){
        String[] sa = {"nickel", "button", "key", "lint"};
        Sorter s = new Sorter();// Compile error!!!!!不能在static method內使用非static的Sorter class
        
    }
    class Sorter implements Comparator<String>{
        @Override
        public int compare(String a, String b) {
            return b.compareTo(a);
        }
    }
}

以下Modifier都適用在inner class上,對待inner class就像一個outer class的member
  • final
  • abstract
  • public
  • private
  • protected
  • static—但是 static 會將它轉變成static nested class,而不是inner class。
  • strictfp 
Method-Local Inner Class 
  • 只有在method內才能使用該inner class
  • 和Inner class一樣,就像是Outer class的member,所以可以存取Outer class的private instance variable
  • Java 1.8版後可以使用原method的variable(原本不能的原因是若method-local inner class 實體化後傳到外部還存在,但method內的variable卻會因為執行完畢而被剔除在stack中,如果不是final會存取不到這個variable)
  • 若外部method是static,則內部的class也不能夠存取外部非static的instance
以下Modifier可用在method-locla inner class上,不適用的剔除(規則和local variable相同)
  • final
  • abstract
  • public
  • private
  • protected
  • static
  • strictfp 

Static Nested Class 
根據Inner class的定義,static nested class不是inner class的一種
  • inner class和outer class的instance間有特殊的關聯,但static nested class沒有
  • 主要為name-space的解決方案,而非和outer class間的隱含關係
  • 單純位於outer class的非inner的class
  • 屬於outer class的一個static member
  • 並沒有所謂的static的class,使用static nested class只是為了表示是outer class的一個static member
  • 就像 static method無法存取instance variable和class中的非 static method一樣,static nested class也無法存取outer class的instance variable和class中的非 static method。
  • 表示可以像其他static member一樣被存取,outer class不需要new實體就可以取得,但內部的class還是需要new才能產生
  • new MyOuter.MyInner();
    
範例:

class BigOuter {
 static class Nest {
  void go() {
   System.out.println("hi");
  }
 }
}
class Broom {
 static class B2 {
  void goB2() {
   System.out.println("hi 2");
  }
 }
 public static void main(String[] args) {
  BigOuter.Nest n = new BigOuter.Nest(); // 兩個類別的名字都用到了 n.go();
  B2 b2 = new B2(); // 存取被包圍的類別
  b2.goB2();
 }
}

Thread

Thread() 的constructor可以接受Runnable的instance,但是因為Thread有繼承Runnable,所以也可以放入Thread的constructor內

Thread method:
1. public void start()
  • 呼叫start()才會開始執行thread程序
2. public static void yield()


  • 讓目前執行中的thread回到Runnable的狀態,使其他priority相同的thread有機會被輪到,所 yield()可以促進相同priority的thread輪替。但不保證會讓想要的thread被選上!
3. public static void sleep(long millis) throws InterruptedException
  • sleep是一個static method,是一個class level的method,意即沒有一個thread可以叫另外一個thread sleep。假設在a thread執行時呼叫b.sleep(),此時其實還是a sleep(static method只影響目前的thread),雖然讓人誤解但是合法的
4. public final void join() throws InterruptedException
  • 功能:呼叫Join()的Thread需要等到被呼叫的完成才可繼續。Ex: main Thread呼叫t1.join(),意指t1需結束main Thread才能繼續執行。呼叫時必須處理 InterruptedException,因為要等
Object method:
1. public final void wait() throws InterruptedException
  • 必須要在synchronized block呼叫,且要拿到locker才有辦法呼叫wait()
  • 呼叫時必須處理 InterruptedException,因為要等
  • synchronized(anotherObject) { // 取得anotherObject的鎖 
    try {
      anotherObject.wait();
    // 執行緒釋放鎖並且等待
    // 為了繼續執行,執行緒需要鎖,
    // 所以它可能會被卡住,直到它取得鎖
    } catch(InterruptedException e){} }
    
    範例如上,表示一開始若拿到anotherObject的locker,就能夠進入try區塊,而anotherObject.wait()將會釋放anotherObject locker,直到等到有人呼叫notify()。若寫的是其他object.wait()則會有exception: IllegalMonitorStateExceptionObject 
  • wait裡若有帶parameter,表示最多等待時間。如下範例,最多等兩秒或是被notify()
  • synchronized(a){ // 執行緒取得a的鎖 
      a.wait(2000); 
    // 執行緒釋放鎖並等待通知 
    // 最多等待兩秒,便回到可執行的狀態
    // 執行緒取得鎖
    // 其他指令
    }

2. public final void notify()
  • 必須要在synchronized block呼叫,且要拿到locker才有辦法呼叫notify()
  • 呼叫此函式後,Thread鑰直到離開synchronized block才會釋放所持有的lock 
  • 只會隨機通知一個等到此locker的thread

3. public final void notifyAll()
  • 必須要在synchronized block呼叫,且要拿到locker才有辦法呼叫notifyAll()
  • 呼叫此函式後,Thread鑰直到離開synchronized block才會釋放所持有的lock 
  • 和notify()的差別是,會通知所有等待的此locker的thread
    Runnable method:
    1. void run()
    • 呼叫start()才會開始執行thread程序
    Synchronized:

    • 當有兩個執行指令不能被分割,稱為atomic operation,這時可用synchronized保證不會被打斷
    public class Test4 {
        private int a;
        private int b;
    
        public void set(int a, int b) { 
            synchronized (this){ // synchronized here is ok. Note that parameter must be an object
                this.a = a;
                this.b = b;
            }
        }
    
        public synchronized int read() { // synchronized here is ok
            return a + b;
        }
    
    }
    
    上述兩種synchronized寫法都可以
    注意locker一定要是一個object

    Thread-Safe:
    當一個class使用同步化保護資料時,會說這個class是thread-safe(如Vector, Hashtable, StringBuffer..),但只是針對這個class內的method做同步化,例如以下例子:

    if (names.size() > 0)
      return (String) names.remove(0);
    else
      return null;
    
    則假設此時有t1, t2兩個thread執行,t1和t2輪流檢查size都>0,也輪流移除element,第二個thread還是會遭遇錯誤的發生(null point)。因此使用thread-safe的class並不能保證讓自己寫的class變成thread-safe
    Static synchronized:
    • 如果是不同的instance,非static synchronized method並不會彼此卡住,因為這時候的locker是this
    • 如果是static synchronized method,同一個class的instance會卡住,因為這時候的locker是MyClass.class,也可以寫成Class.forName("MyClass")JVM會找出代表 MyClass的Class instance。
    • static synchronized method和非static synchronized method並不會彼此卡住。前者的locker是class instance,後者是this

    Assert 斷言

    assert有兩種版本
    1. 簡易版(必為)boolean值,範例如下
      • assert(x == 1);
      • assert(b);
      • assert true;
    2. 除錯版,後者一定要有回傳值(前者為boolean值(+冒號+)後者會幫忙轉成String後印出,就像是呼叫System.out.pritnln(),然後 顯示在stackTrace內,會多一點除錯的線索
      • assert(x == 1) : x;
      • assert(x == 1) : aReturn(); assert(x == 1) : new ValidAssert();
    版本:
    • Java 1.4 前,assert可以拿來當identifier。假如使用Java1.3 編譯,則assert當作identifier則會出現警告 ; 但如果使用assert當作keyword,則編譯會失敗!
    • 1.4 後,只能是key保留字。若當作identifier編譯會失敗。

    若檔案內已經把assert當作identifier使用,則可以使用較低版本編譯:
    javac -source 1.4 CH10/Test7.java
    

    假如欲使用Java 6的規則,可使用-source 1.6 或 -source 6 (兩者一樣)
    javac -source 1.6 CH10/Test7.java
    javac -source 6 CH10/Test7.java
    

    執行時開啟assert:
    預設是關閉的,若要打開可加入-ea或-enableassertions啟動:
    java -ea com.geeksanonymous.TestClass
    java -enableassertions com.geeksanonymous.TestClass

    關閉是-da或-disableassertions:
    java -da com.geeksanonymous.TestClass
    java -disableassertions com.geeksanonymous.TestClass

    Javac編譯搜尋class

    當cmd出現錯誤:找不到或無法載入主要類別時,可能是指令輸入錯誤,以下為觀念解說:

    • 注意編譯的檔案是否有使用到其他class,若有,則需要在classpath加上class完整名稱(full qualified name),一旦一個class被加入套件,class名稱就會是package+class名稱,他們是不可分割的,指令中也不能夠切開。(ex: 假設MyClass是com.foo的member,則class全名為com.foo.Myclass,不可分開 )
    • javac若要編譯.java檔,預設是搜尋目前目錄搜尋該.java檔案。
    • javac指令是用來呼叫編譯器編譯java檔案,而java為執行java檔案
    • javac及java搜尋class時,若在提示字元輸入javac -classpath path1:path2:.,則搜尋順序如下
      1. 第一個使用者自行設定的路徑,從左至右第一個是path1
      2. 第二個是path2 
      3. 第三個是(.)dot,此為當前目錄
      • 註:在環境設定內的$CLASSPATH路徑會因為使用者有自訂而被替換掉。
      • 註2: 以上的目錄+ class完整名稱(package+class))就是要找的class位置
    • classpath正名應為”class搜尋的路徑,且其路徑為class的目錄清單“
    有了上述的了解,以下例子加以說明:
    假設有兩個檔案(A.class, B.java),皆為foo package,其檔案位置如下
    test
        |
        foo
            |
            A.class
            B.java

    且foo/B.java繼承foo/A.java,因此在編譯B.java時需要使用到foo.A class
    呼叫方法為:javac  -classpath  .  foo/B.java
    • 必須在在test檔案夾中 (必須為foo目錄的上一層,才能找得到foo.A.class)
    • class搜尋的路徑寫了(.)dot,代表在當前目錄(test)下能夠找到foo.A.class
    • 編譯的檔案名需為(foo/B.java),因為是在test中的相對路徑下才找得到B.java
    結論:
    1. 先考慮要編譯的class相關class完整名稱,Javac指令 default是目前目錄,Java指令default是$CLASSPATH
    2. 再考慮要編譯的java檔案位置是否在default目錄,否則需要加再-classpath options中

    Static import 靜態匯入

    一種Java5後加強版import用法,能夠簡化呼叫static時需要打的字

    若使用 import static java.lang.System.out; (結尾是指定靜態的參數(out))
    - 則可以直接使用System.out(靜態member out)
    若使用 import static java.lang.Integer.*; (結尾是Integer靜態的所有參數)
    - 代表在Integer class底下的所有static member都可以使用

    呼叫方式 Ex:
    out.println(MAX_VALUE);
    - out : 直接使用宣告的out static variable
    MAX_VALUE: 直接使用Integer的MAX_VALUE static variable


    2017年11月5日 星期日

    Day2-10 Coupling and Cohesion 耦合和內聚力

    5.1 Develop code that implements tight encapsulation, loose coupling, and high cohesion in classes, and describe the benefits. 

    好的設計會建議遵循loose coupling以及high cohesion(低耦合高內聚)。OO設計會希望能夠達成
    • Ease of Creation
    • Ease of Maintenance 
    • Ease of Enhancements
    Coupling
    class A對class B認識程度; class間如何互動
    • 若A只知道B暴露的interface,則為loose coupling (Good!)
    • 若A知道除了B暴露的interface以外的東西(包含有哪些instance variable),則為tight coupling (Bad!)
    • 若A依賴B class內的某個部份,則為tight coupling (Bad!)
    • 若A知道B如何implementation,則為tight coupling (Bad!)
    • 若A修改了和interface沒有關係的程式碼,卻影響到B,則為不好的設計(A可能根本不知道B用了A內部的東西)。如此的修改會造成B程式碼的破壞。
    • 若B封裝內的get, set method依賴A內的method,則為tight coupling (Bad!)
    • loose coupling將會有良好的encapsulation
    理想上,OO系統內object的互動都應該要透過type的API(contract),而一個良好的API應該要有良好的encapsulation。

    Cohesion
    一個class擁有單一目標的程度
    • 一個class擁有越專精的目標,則為high cohesion (Good!)
    • 擁有high cohesion的class,越少被更動,越常被使用
    範例:若今天有一旅程規劃系統,裡面需要有地圖功能,消費功能,行程功能
    以下是將所有程式碼都寫在一個class

    class TravelDesign{
        void MapDesign(){}
        void AcccountingRecord(){}
        void ScheduleDesign(){}
    }
    high cohesion的做法是將所有功能都切成一個class
    class TravelDesign{}
    class MapDesign{}
    class AcccountingRecord{}
    class ScheduleDesign{}
    
    不如將所有class切出來,變成只做非常專門的工作,如此擁有high cohesion。

    總結:
    • Encapsulation,它的重點和隱藏實作細節最相關
    • Polymorphism原則,它的重點是允許一個物件可以被看做多種型別
    • Coulping是 OO 原則,它的重點是確定類別只透過 API 和其他的類別溝通
    • Cohesion是 OO 原則,它的重點是確定類別只為單一、且定義精準的目地