20155303 2016-2017-2 《Java程序設計》第六周學習總結
課堂筆記
- 高效學習法推薦
看視頻學習(2h)→ 以代碼為中心看課本,思考運行結果並驗證(3h)→ 課后作業驗證(5h)→ 教材指導
【充分利用教材指導,積極思考遇到的問題,在實踐中學習,不要拘泥於記憶教材講解的知識點和概念。】
教材學習中的問題和解決過程
- 『問題一』:以課本P306為例,
args[0]、args[1]代表什么?
『問題一解決』:java的主方法為:public static void main(String [] args),args[0]表示命令行輸入時傳的第一個參數,args[1]同理。例如執行以下程序:

運行java WhatIsArgs No1 No2命令可得到結果:No1和No2。
- 『問題二』:
hasNextLine()與nextLine()的用法?
『問題二解決』:查詢API文檔可知,hasNextLine()與nextLine()均繼承自java.util.Scanner。它們的用法是,hasNextLine()用來判斷下一行是否存在,常用在while語句中,當且僅當下一行有輸入時返回true;而nextLine()返回值是當前行的剩余內容。


兩者具體使用方法可參考以下代碼:

- 『問題三』:課本P316提到“如果在做對象串行化時,對象中某些數據成員不希望被寫出,則可以標上
transient關鍵字”一句該如何理解?
『問題三解決』:一個對象只要實現了Serilizable接口,這個對象就可以被序列化,不過有些時候,一個類的有些屬性需要序列化,而其他屬性不需要被序列化。java的transient關鍵字為我們提供了便利,你只需要實現Serilizable接口,將不需要序列化的屬性前添加關鍵字transient,序列化對象的時候,這個屬性就不會序列化到指定的目的地中。
比如以下程序:

在上面的例子中,將屬性b前添加關鍵字transient,雖然我們序列化的對象b的屬性值為“Transient or not”,但是當我們反序列化之后發現這個屬性為空,說明這個屬性沒有進行序列化。

【關於什么是序列化,參考博客深入理解Java對象序列化】
- 『問題四』:守護線程與非守護線程
『問題四解決』:簡單來說,java可以創建兩種線程,即守護線程與非守護線程。非守護線程(User Thread用戶線程)就是平時創建的一般線程,而守護線程(Daemon Thread守護線程)是用來服務用戶線程的。
區分守護線程與非守護線程:當線程只剩下守護線程的時候,JVM就會退出.但是如果還有其他的任意一個用戶線程還在,JVM就不會退出。
setDaemon()與isDaemon():setDaemon方法用來設定一個線程。如果setDaemon(true)表示設定一個線程為Daemon線程。isDaemon則用來測試一個線程是否為守護線程。如果是,返回true。

- 『問題五』:Thread常用方法以及狀態圖
『問題五解決』:
常用方法:

start(); //啟動線程
getId(); //獲得線程ID
getName(); //獲得線程名字
getPriority(); //獲得優先權
isAlive(); //判斷線程是否活動
isDaemon(); //判斷是否守護線程
getState(); //獲得線程狀態
sleep(long mill); //休眠線程
join(); //等待線程結束
yield(); //放棄cpu使用權利
interrupt(); //中斷線程
currentThread(); //獲得正在執行的線程對象
Thread狀態圖:

- 『問題六』:對於課本P334
join()的介紹產生疑問:Thread B 什么時候加入主線程呢?是不是從join()方法出現的位置開始呢?
『問題六解決』:在不同位置調用join()方法,程序如下:

“嘗試一”運行結果:

“嘗試二”運行結果:

可以看出,程序啟動后主線程就開始了,調用join()之后把Thread B加入主線程流程中,執行完畢后再繼續執行原本的線程。
注:可以在join()指定時間,如join(1000)表示讓加入的線程執行1000毫秒,也就是1秒。1秒結束后線程可以繼續執行原本流程。
代碼調試中的問題和解決過程
本周跟視頻學習的過程中思考一個問題:字節流和字符流的優勢各在哪里呢?使用哪一個比較好呢?
答案是字節流。首先因為硬盤上的所有文件都是以字節的形式進行傳輸和保存的,包括圖片,mp3等等。但是字符只是在內存中才會形成,所以在開發過程中,字節流使用更加廣泛。
下面通過幾個例子總結了字節流的應用情況。
- 『問題一』:向文件中寫入字符串
運行以下程序:

查看hello.txt會看到“你好”:

也可以向文件中一個字節一個字節地寫入字符串,運行結果同上:

- 『問題二』:向文件中追加新內容
運行以下程序:

查看hello.txt會看到“你好DiWeijia”:

- 『問題三』:讀取文件內容
運行以下程序,可以讀出hello.txt里的內容:

知識拓展
謝濤老師在之前的一篇博客中提出問題:如何把一個Java源文件里的注釋去掉(處理結果可以輸出到新的文件里),保證修改后源文件仍然能正常編譯,正確運行。得到婁老師的指點之后,明白這個問題的解決需要用到正則表達式。
正則表通常被用來檢索、替換那些符合某個模式(規則)的文本。我們經常使用Windows/Dos下用於文件查找的通配符(wildcard),也就是* 和?。如果想查找某個目錄下所有的word文檔,我們會搜索 * .doc。在這里,* 會被解釋成任意的字符串。和通配符類似,正則表達式也是用來進行文本匹配的工具,只不過比起通配符,它能更精確地描述你的需求。
比如要求填寫5-12位的QQ號,就可以使用正則表達式:\d{5,12}$。表示匹配字符串的開始,$表示匹配字符串的結束,\d表示匹配數字,{5,12}表示數字為5-12位。這樣一來,如果用戶輸入能匹配這個表達式的話,就符合要求了。
像以上的“^”、“$”等都是正則表達式的元字符。元字符以及常用的正則表達式如下:(參考婁老師博客正則表達式入門)

“去注釋”問題可以利用正則表達式的相關知識,結合之前學到的“轉義符”解決。基本思路是:對待分析的帶注釋段的字符串進行遍歷,聲明一個緩沖字符串變量來記錄非注釋的部分,最后返回這個緩沖字符串變量作為結果。這樣就能把去除注釋之后的文件保存下來了。可以從以下四個方面考慮:
1.考慮雙引號:雙引號中的注釋部分是不能去掉的,比如print("//Hello"World"/ * comment * /")。以下幾條都應在沒有雙引號的前提下。如果發現了開始雙引號,在匹配結束雙引號的時候要注意可能會遇到轉義雙引號,需要跳過以\開始的雙引號,從而匹配到正確的結束雙引號;
2.考慮 / * comment * / 形式的注釋 :當遇到 / * 部分便停止記錄,繼續往后遍歷到 * / 部分,實現跳過 / * * / 段;
3.考慮/ * comment / * inside * / out * /形式的嵌套注釋:聲明一個數字變量來記錄 / * 的開始的次數,遇到一個 / * 就+1,遇到一個 * / 就-1,實現嵌套匹配;
【注意】:注釋不能嵌套:/ * / * inside * / * /,所以這種情況不予考慮。感謝謝濤老師的指正:)
4.考慮雙斜杠注釋 發現 // 形式的字符串的時候表明遇到了雙斜杠注釋,這時候使用while循環繼續向后遍歷,直到發現一個換行符,從而跳過整個這一行;
正則表達式的應用領域非常廣,以上提到的這些只是一點皮毛。要想熟練掌握正則表達式的用法,還需要多動手多實踐。
代碼托管


上周考試錯題總結
- 『問題一』:
現有:
- list是一個合法的集合引用
- getCollection()返回一個合法集合的引用
哪個是合法的?
A.or(Object o ; list)
√B.for(Object o : getCollection())
C.for(Object o : list.iterator())
D.for(lterator i ; list.iterator() ; i.hasNext () )
√E.for(lterator i=list.iterator(); i.hasNext (); )
- 『考點』:
B選項是增強式for循環。增強式for循環能對數組和集合進行遍歷,使用上更加簡潔。D選項是普通循環,i操作了iterator()接口,如果沒有拋出異常,則i.hasNext()返回值為true。
- 『問題二』:
Which of the following methods will not compile? :
A.
private void method1(String name) {
if (name.equals("star"))
throw new IllegalArgumentException(name);
}
√B.
private void method2(int age) {
if (age > 30)
throw Exception();
}
C.
public double method5() throws Exception {
return 0.7;
}
√D.
protected double method4() throws Exception {
throw new Throwable();
}
- 『考點』:
B選項無法編譯,因為Exception是受檢異常,必須使用throws聲明此方法會拋出的異常類型或父類型。D選項無法編譯,因為子類不能拋出比父類更一般的異常。
- 『問題三』:
What is the output of the following code?
class EJava {
void method() {
try {
guru();
return;
} finally {
System.out.println("finally 1");
}
}
void guru() {
System.out.println("guru");
throw new StackOverflowError();
}
public static void main(String args[]) {
EJava var = new EJava();
var.method();
}
}
A.guru
finally 1
√B.guru
finally 1
Exception in thread "main" java.lang.StackOverflowError
C.guru
Exception in thread "main" java.lang.StackOverflowError
D.guru
E.The code fails to compile.
- 『考點』:
首先程序可以通過編譯。其次,StackOverflowError()是非受檢異常,方法guru()在try-catch塊中,異常會被捕捉。由於guru()本身沒有處理堆棧溢出錯誤,但method()定義了finally區塊,所以程序在執行完畢finally區塊之后將錯誤傳播至JVM,中斷程序。
結對及互評
本周結對學習情況
-
結對同學學號20145202馬超
-
結對學習內容:查看對方代碼,並對學習中遇到的疑問進行交流。解答對方博客中未解決的問題。
第六周博客互評情況
其他(感悟、思考等,可選)
-
1、最近幾周的學習內容系統性和連貫性很強,所以要經常查詢API文檔,了解常用的類和方法以及其繼承架構。
-
2、學習過程中應該把思考作為重中之重,這一點之前就領悟到了,不過在實踐之中經常被忽略。所以我們不應該單純地把代碼行數看做衡量自己學習情況的標准,理解得透徹了才能達到舉一反三的效果。按照這種方法學習不僅可以深刻理解所學知識,也提高了學習效率,減輕了學習負擔。
-
3、不學則已,一學即專。心不能靜下來的時候寧可不看書。萬萬不可一邊打着學習的名義捧書研讀,一邊還在為其他事困擾,這樣只能欺騙自己“我真的學習了”,卻僅僅是耗費時間,而達不到理想的效果。
學習進度條
| 代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
|---|---|---|---|---|
| 目標 | 5000行 | 30篇 | 400小時 | |
| 第一周 | 16/16 | 1/1 | 18/18 | 初步認識了Java |
| 第二周 | 219/235 | 1/2 | 28/46 | 學習了Java的基本語法知識 |
| 第三周 | 766/1001 | 1/3 | 23/69 | 了解對象與參考的關系,以及封裝的概念與實現 |
| 第四周 | 984/1985 | 1/4 | 18/87 | 學習了繼承與多態的關系,以及接口的多態操作 |
| 第五周 | 866/2851 | 1/5 | 12/99 | 學習了異常處理,學會使用Collection收集對象 |
| 第六周 | 664/3515 | 1/6 | 15/114 | 認識字節流和字符流的繼承架構,學習線程與並行API |
嘗試一下記錄「計划學習時間」和「實際學習時間」,到期末看看能不能改進自己的計划能力。這個工作學習中很重要,也很有用。
耗時估計的公式
:Y=X+X/N ,Y=X-X/N,訓練次數多了,X、Y就接近了。
-
計划學習時間:20小時
-
實際學習時間:15小時
-
改進情況:這周的效率跟之前比有了很大的提高,我想應該歸功於婁老師上節課提到的學習方法。以后的學習應該抓住重點,多思考,不要把時間浪費在照搬書上的代碼之類的無用功上。
(有空多看看現代軟件工程 課件
軟件工程師能力自我評價表)
