- 先下載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月19日 星期日
Integration test (4) - IEDriverServer
使用IE Web Driver進行測試時需要設定:
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
在eclipse安裝步驟:
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安裝步驟:
- 至http://testng.org/doc/maven.html
- 複製maven dependency
- 可以把原本default的JUnit刪去
- 確認TestNG是否在Eclipse上有安裝:
- 至chormedriver下載driver https://sites.google.com/a/chromium.org/chromedriver/downloads
<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>
Integration test (2) - Selenium
Selenium
- Selenium webdriver是open source,可以針對編寫操作web的code
- 為了Browser automation所設計的工具集合,讓程式可以直接驅使web做網站操作
- 能夠直接獲取內容,可以和網頁互動,執行JavaScript,訪問Dom,模擬鍵盤輸入
- 實現UI測試全自動化及做回歸測試
- 由於瀏覽器有不同行為,所以需要進行維護
以下為編寫注意事項
- Selenium webdriver是open source,可以針對編寫操作web的code
- 為了Browser automation所設計的工具集合,讓程式可以直接驅使web做網站操作
- 能夠直接獲取內容,可以和網頁互動,執行JavaScript,訪問Dom,模擬鍵盤輸入
- 實現UI測試全自動化及做回歸測試
- 由於瀏覽器有不同行為,所以需要進行維護
以下為編寫注意事項
- 使用DOM的ID進行定位-> 不受element位置更動而變動例如UI改版
- 特定項目操作繼承來設計class
- 使用Page Object Pattern,將所有頁面應該要使用到的操作放在一起,並由class使用
- 替測試做重試功能,直到成功或timeout
下載流程:
- 至http://www.seleniumhq.org/download/maven.jsp
- 可以看到對maven的dependency,並加入在pom.xml內,另可把版號放入properties裡
- 可以至路徑 ~/.m2/repository 底下看到org的packages
<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>
Integration test (1) - Maven
Maven:
字意為:知識的累積者
- 標準化的建立專案,目的為一致的專案架構,統一的專案建構流程
- 不同的專案在code外有一套管理JARs的方式,能夠分享共同部分,補足差異檔案,且節省空間
- 用來建立專案的規則,包含原始檔案的相依性
- 自動測試與生成報告
- 從SVN GIT等取得原始碼
Pom:(Project Object Model)
- 專案建立標準化需要有共同的Metadata(對數據的一種說明),共同的目錄配置方式
- 舉例,像建立project物件,使用getXXX()取得資訊
- 格式為XML,能夠方便程式端的取用
- 格式內有
Repositories:
- 儲存Artifact
- 在pom聲明dependency後,就會去下載Artifact,供自己使用
- 自身開發的project也能夠使用mvn install放入Repository
目錄結構:
字意為:知識的累積者
- 標準化的建立專案,目的為一致的專案架構,統一的專案建構流程
- 不同的專案在code外有一套管理JARs的方式,能夠分享共同部分,補足差異檔案,且節省空間
- 用來建立專案的規則,包含原始檔案的相依性
- 自動測試與生成報告
- 從SVN GIT等取得原始碼
Pom:(Project Object Model)
- 專案建立標準化需要有共同的Metadata(對數據的一種說明),共同的目錄配置方式
- 舉例,像建立project物件,使用getXXX()取得資訊
- 格式為XML,能夠方便程式端的取用
- 格式內有
- <project> : 最上層的element
- <modelVersion>:哪一個model這個POM在使用,作為Maven驗證機制
- <groupId>:專案起始位置,如com.xxx
- <artifactId>:唯一可辨別的名字,這個名字是給產生的Jar檔使用的,會與版本變成一黨名 <artifactId>-<version>.<extension> (如, myapp-1.0.jar).
- <version>:專案版本,通常會加上-SNAPSHOT,表示最新的,但不保證是穩定會不會在改變(開發中,在release之前)。相反的,若不加上-SNAPSHOT,則表示此code不會再變動。也因此,版號也會
- <name> : 專案使用的名稱
- <url> : 專案的位址,通常會在Maven產生的文件中被使用到
- <description> : 專案描述,通常會在Maven產生的文件中被使用到
Artifact:
- 每個artifact都由以下三種組成唯一識別,需要使用的時候就需要放入repository
- groupId
- artifactId
- version
- 儲存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
以下為前置步驟:
以下為前置步驟:
- 至maven官網http://maven.apache.org/download.cgi下載apache-maven-3.5.2-bin.tar.gz
- 解壓縮至/usr/local/apache-maven-<version>
- 新增M2_HOME系統變數 export M2_HOME=/usr/local/apache-maven/apache-maven-<version>
- 新增M2系統變數 export M2=/usr/local/apache-maven/apache-maven-<version>
- 可選擇是否要新增MAVEN_OPTS系統變數,可以提供Maven額外的選擇 export MAVEN_OPTS="-Xms256m -Xmx512m" (ㄧInvalid maximum heap size: -Xmx512mi 的問題)
- 新增PATH export PATH=$M2:$PATH
- 確認JAVA_HOME JDK路徑設定正確 echo $JAVA_HOME /Library/Java/JavaVirtualMachines/jdk1.8.0_152.jdk/Contents/Home 且必須確認PATH是否有包含$JAVA_HOME/bin的路徑
- 輸入 mvn --version 來卻確認版本
開始執行指令:
- 建立新的Maven project:
- mvn compile 能夠下載需要的plugins,及其他dependencies中需要的jar),有時會需要到4分鐘以上。若是第二次下此指令,則只會下載新加入需要下載的jar檔,則會比較快一點。
- mvn test 能夠執行maven的test
- 產生Jar檔並且安裝在local repository
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確認
mvn compile
mvn test
mvn test-compile
mvn package
mvn install
2017年11月15日 星期三
使用JSoup Login網站取得cookie
目的:使用JSoup傳送帳號密碼取得Cookie,以便使用此cookie登入後繼續瀏覽網頁
比較需要注意的是data內的資訊,
表示是送出一串request至web認證,而前面的是key,後面的是value,依序放入connect內。
key的取得方法,以yahoo
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
- 右鍵開啟檢查(Inspect)
- 切換到Network
- 先點左上角的清空按鈕(紅按鈕旁)
- 輸入測試帳號
- 可以看到Name欄位多了一個data(有時可能多個,可從名字看出可能是哪一個)
- 拉到最下面的From Data
- 此例的key即為username/passwd
另外 Jsoup.connect("http://www.example.com/login.php") 這裡面的url也可從此視窗最上面的Request URL中取得
2017年11月12日 星期日
NumberFormat
NumberFormat:
- nf.format(d)將會四捨五入
- 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(),例:
- 若superclass implements Serializable,則所有底下的subclass都會跟著繼承,也就是subclass不需要明確的標示Serializable也能夠存取state
- 若superclass沒有implements Serializable,任何維護在superclass的state(從superclass繼承下來,且沒有override的),都不會回覆,將被重新初始化(就像是跑了一次 superclass的constructor)
- 若使用transient標示變數,代表不需要序列化此值,且該值會被恢復成default值,例:
注意:
- 除了被標示為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; }
注意:
- 最後output s2.y=0,原因是被標示為transient,不管initial值為何,都會變成該形態的default值
- 因為此例中z是static,代表memory只有存一份記憶體,且和instance無關,是class等級。所以最後取出的時候,值也會跟著變成10。
- 綜合以上兩點代表static&transient 變數所屬的object並不能夠被序列化。
Collections, hashcode, equals
Collections&Arrays
兩者都有提供method搜尋特定元素,搜尋使用的是binarySearch()
Priority Queue
排序方式:用來依照優先權進出的Queue,element優先順序代表被取出的順序
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來排序。
兩者都有提供method搜尋特定元素,搜尋使用的是binarySearch()
- 成功會回傳找到的element int index
- 不成功會回傳insertion point的int值 (維持排序的順序下,應該被插入的地方)
- 正值和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
排序方式:用來依照優先權進出的Queue,element優先順序代表被取出的順序
- 根據element的基本順序
- 根據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
Java1.6版後TreeSet有實作NavigableSet,也就是可以指定其他set來定當原set的backup set
HashSet/HashMap
使用hashcode判斷object要如何被儲存在collections中,用來協助對object的定位
Hashcode&equals
使用“怦然鐔跳整理法”一書來解釋hashcode&equals:
想像你今天要整理房間,根據近藤麻理惠傳授的整理定義,你必須先把東西做分類,比如今天要整理衣服,你就需要把房間所有的衣服拿出來放在衣服堆; 今天要整理書本,你就要把所有的書本拿出來放在書本堆。為了加速等下找到的速度,你不只把衣服拿出來,你還會把衣服簡單分類,比如依照衣服的長度及顏色分成短淺、短深、長淺、長深四種。
而依照衣服的長度及顏色分到哪一堆,這就是一種hascode的實作。
把所有衣服都丟到同一堆,這是hashcode的實作,不過要找到你想要的衣服的時候就會變得非常的慢。所以hascode的實作跟之後找到物品的效率也非常的有關聯。
當你在分類的時候,你可能會發現手上剛剛才把一件“無肩帶美背小可愛-白色”放到短淺類別中,怎麼現在手上又拿著“無肩帶美背小可愛-白色”?這時候你會事不宜遲的把剛剛放進去的那件“無肩帶美背小可愛-白色”丟棄,並把手上的那件放到分類中。因為你對衣服的心動指數在於,一樣的衣服(同樣的名字,這邊每個人可以有不同的定義,也就是equals())只能在一個類別出現一次!!
假設今天都分好類了,接著你開始要做將衣服掛在衣櫃上。當你開始要整理衣服做心動分類的時候,你的姊姊進來問你『欸,你上次跟我借的那件”粉色雪紡碎花荷葉邊短袖開釦V領上衣“是不是還沒還我!?限你三分鐘找出來!』
這時候你會慶幸好險剛剛有先使用衣服長度及深淺分四大類,好險不是只有做衣服堆。所以你開始思考那件短的淺的的“粉色雪紡碎花荷葉邊短袖開釦V領上衣”在哪
Hashcode&equals
使用“怦然鐔跳整理法”一書來解釋hashcode&equals:
想像你今天要整理房間,根據近藤麻理惠傳授的整理定義,你必須先把東西做分類,比如今天要整理衣服,你就需要把房間所有的衣服拿出來放在衣服堆; 今天要整理書本,你就要把所有的書本拿出來放在書本堆。為了加速等下找到的速度,你不只把衣服拿出來,你還會把衣服簡單分類,比如依照衣服的長度及顏色分成短淺、短深、長淺、長深四種。
而依照衣服的長度及顏色分到哪一堆,這就是一種hascode的實作。
把所有衣服都丟到同一堆,這是hashcode的實作,不過要找到你想要的衣服的時候就會變得非常的慢。所以hascode的實作跟之後找到物品的效率也非常的有關聯。
當你在分類的時候,你可能會發現手上剛剛才把一件“無肩帶美背小可愛-白色”放到短淺類別中,怎麼現在手上又拿著“無肩帶美背小可愛-白色”?這時候你會事不宜遲的把剛剛放進去的那件“無肩帶美背小可愛-白色”丟棄,並把手上的那件放到分類中。因為你對衣服的心動指數在於,一樣的衣服(同樣的名字,這邊每個人可以有不同的定義,也就是equals())只能在一個類別出現一次!!
假設今天都分好類了,接著你開始要做將衣服掛在衣櫃上。當你開始要整理衣服做心動分類的時候,你的姊姊進來問你『欸,你上次跟我借的那件”粉色雪紡碎花荷葉邊短袖開釦V領上衣“是不是還沒還我!?限你三分鐘找出來!』
這時候你會慶幸好險剛剛有先使用衣服長度及深淺分四大類,好險不是只有做衣服堆。所以你開始思考那件短的淺的的“粉色雪紡碎花荷葉邊短袖開釦V領上衣”在哪
- 你蹲到衣服短淺堆前
- 你開始一件一件翻,找那間叫做“粉色雪紡碎花荷葉邊短袖開釦V領上衣”的衣服在不在裡面
這兩件事情的翻譯其實是
- 重新做一次hashcode()(衣服的長度及顏色分類)找出是哪一堆(因此找到了短淺堆)
- 執行equals()找到自己要的衣服
而這邊的equals就會是衣服的名字。當你在實作equals的時候,裡面的程式碼可能就會是this.name == (cloth(object)).getName()。如此,你就能找到那件“粉色雪紡碎花荷葉邊短袖開釦V領上衣“。當然也有可能找不到,那麽得到就會是null。hashcode(分類)只是告訴你在哪找,沒有保證一定找得到。
以下總結存入讀取步驟:
放入hashtable:
- 執行hashtable()決定分類
- 執行equals()看是否在分類有一樣的object
讀取特定物品:
- 執行hashtable()找出分類
- 對裡面的每個object執行equals(),若==true代表找到了!
Jar命令提示指令
- 將app資料夾內的檔案歸檔包裝至MyApp.jar
- 列出MyApp.jar檔案內容
- 將MyJar.jar解開
jar -cf MyApp.jar app
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
jar -xf MyJar.jar
Generic Types 泛型
版本:
Java 5 之前沒有辦法宣告Type safe的collections。需要建立一個 String 的 ArrayList,是無法使用角括號的,因此只能寫成:
上面的範例程式碼無法運作,Gereric types左右必須相等,不論他們的關係是否是subtype或supertype都不行
以下兩種也都是錯誤範例:
正確範例:
多型只能用在base,也就是collections的type,如:
是可行的,但不要搞混了,Array是可以內部存的object是多型:
以下程式碼將無法編譯成功,原因是試圖將int及string加入TreeSet,但TreeSet會去排序,所以當他不知道如何排序String和int時,將會拋出ClassCastException
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> ();
以下兩種也都是錯誤範例:
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>();
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
以下Modifier都適用在inner class上,對待inner class就像一個outer class的memberclass 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); } } }
- final
- abstract
- public
- private
- protected
- static—但是 static 會將它轉變成static nested class,而不是inner class。
- strictfp
Method-Local Inner Class
Static Nested 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
publicprivateprotectedstaticstrictfp
根據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()
3. public final void notifyAll()
1. void run()
上述兩種synchronized寫法都可以
注意locker一定要是一個object
Thread-Safe:
當一個class使用同步化保護資料時,會說這個class是thread-safe(如Vector, Hashtable, StringBuffer..),但只是針對這個class內的method做同步化,例如以下例子:
Thread method:
1. public void start()
- 呼叫start()才會開始執行thread程序
- 讓目前執行中的thread回到Runnable的狀態,使其他priority相同的thread有機會被輪到,所 yield()可以促進相同priority的thread輪替。但不保證會讓想要的thread被選上!
- 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,因為要等待
- wait裡若有帶parameter,表示最多等待時間。如下範例,最多等兩秒或是被notify()
synchronized(anotherObject) { // 取得anotherObject的鎖 try { anotherObject.wait(); // 執行緒釋放鎖並且等待 // 為了繼續執行,執行緒需要鎖, // 所以它可能會被卡住,直到它取得鎖 } catch(InterruptedException e){} }
synchronized(a){ // 執行緒取得a的鎖 a.wait(2000); // 執行緒釋放鎖並等待通知 // 最多等待兩秒,便回到可執行的狀態 // 執行緒取得鎖 // 其他指令 }
- 必須要在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; } }
注意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:
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有兩種版本
假如欲使用Java 6的規則,可使用-source 1.6 或 -source 6 (兩者一樣)
執行時開啟assert:
預設是關閉的,若要打開可加入-ea或-enableassertions啟動:
關閉是-da或-disableassertions:
- 簡易版(必為)boolean值,範例如下
- assert(x == 1);
- assert(b);
- assert true;
- 除錯版,後者一定要有回傳值(前者為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:.,則搜尋順序如下
- 第一個使用者自行設定的路徑,從左至右第一個是path1
- 第二個是path2
- 第三個是(.)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
|
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
結論:
- 先考慮要編譯的class相關class完整名稱,Javac指令 default是目前目錄,Java指令default是$CLASSPATH
- 再考慮要編譯的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
若使用 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設計會希望能夠達成
class A對class B認識程度; class間如何互動
high cohesion的做法是將所有功能都切成一個class
不如將所有class切出來,變成只做非常專門的工作,如此擁有high cohesion。
總結:
好的設計會建議遵循loose coupling以及high cohesion(低耦合高內聚)。OO設計會希望能夠達成
- Ease of Creation
- Ease of Maintenance
- Ease of Enhancements
Coupling
- 若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(){} }
class TravelDesign{} class MapDesign{} class AcccountingRecord{} class ScheduleDesign{}
總結:
- Encapsulation,它的重點和隱藏實作細節最相關
- Polymorphism原則,它的重點是允許一個物件可以被看做多種型別
- Coulping是 OO 原則,它的重點是確定類別只透過 API 和其他的類別溝通
- Cohesion是 OO 原則,它的重點是確定類別只為單一、且定義精準的目地
訂閱:
文章 (Atom)