諶衛(wèi)軍
(清華大學 計算機科學與技術系,北京 100084)
眾所周知,Java既是一種編程語言,又是一個跨系統(tǒng)的運行平臺,在軟件工業(yè)界得到廣泛的應用,成為眾多程序員的首選編程語言。事實上,在軟件工業(yè)歷屆的程序設計語言排名榜中,Java語言始終名列前茅,并在大部分時間內(nèi)排名第一,尤其是隨著移動開發(fā)和安卓手機的爆炸式增長,Java語言又出現(xiàn)了新的增長點,因為安卓系統(tǒng)上的應用軟件都是用Java語言開發(fā)的,因此Java語言的普及和推廣具有廣泛的市場需求。由于Java是一種面向?qū)ο蟮木幊陶Z言,很多內(nèi)容具有一定的難度,如抽象與封裝、函數(shù)重載與重寫、數(shù)據(jù)的存儲、繼承、多態(tài)、動態(tài)綁定、抽象類與接口、對象集合等,特點是單調(diào)枯燥、晦澀難懂,而且有的還與其他課程如操作系統(tǒng)的知識相融合,因此學生在學習時可能會面臨一些困難。
筆者在清華大學開設了一門全校性選修課“Java語言程序設計”,主要面向全校對Java 編程比較感興趣的本科生,以工科院系為主,既有計算機、軟件、電子、自動化等信息相關專業(yè)的學生,又有工業(yè)工程、物理、經(jīng)管、精儀、機械、化學工程等非信息專業(yè)的學生。經(jīng)過幾年的努力,這門課程逐漸受到學生的歡迎。雖然春季和秋季這兩個學期都有開課,但是仍然供不應求,學生的選課熱情很高。因此,在以往教學經(jīng)驗的基礎上[1-2],如何講好這樣一門既有需求、又有一定難度的課程,需要認真地思考、總結和實踐。
從教學目標來看,這門課程的教學目標主要有3個。
(1)編程語言的學習,即把Java看成一種編程語言,學習這種語言的使用方法。
(2)編程思想的學習,即把Java看成面向?qū)ο缶幊陶Z言的一個具體實例,通過它學習面向?qū)ο蟮木幊趟枷?包括編碼、設計與分析。
(3)實踐能力的培養(yǎng),即把Java看成一種實用的軟件開發(fā)工具,利用它解決實際的問題,編寫出一些實用的應用程序。
為了實現(xiàn)這些目標,在教學理念上應遵守如下原則。
首先,堅持“以學生為中心”的觀念。教學的根本目標是讓學生學到知識、提高能力,從而有所收獲,而不僅僅是讓教師完成一項教學任務,因此,教師要投入大量的心血研究課程的教學規(guī)律和學生的特點,有針對性地設計課程的教學內(nèi)容和教學方式,讓學生愿意學,而且能學好。
其次,正確處理編程語言與編程思想之間的關系。選修此課程的學生大多是非計算機專業(yè)的本科生,編程基礎較為薄弱,基本上只學過一門結構化的程序設計語言,如C語言,因此,要想方設法幫助他們建立面向?qū)ο蟮木幊趟枷?。從C到Java,不僅僅是編程語言的變化,還是編程思想的升級。要讓學生逐漸脫離“程序就是數(shù)據(jù)加函數(shù)”的舊觀念,形成“類、對象、抽象與封裝、繼承與多態(tài)”等新思想。
最后,正確處理理論與實踐之間的關系。理論學習固然重要,但實踐能力的培養(yǎng)更加重要。作為一門程序設計課程,它的最終目的一定不是讓學生學會多少理論知識,而是實踐能力得到提高,能夠解決實際的問題,編寫出真正有用的軟件,因此,在課程設計上,要勇于創(chuàng)新,銳意改革,突出強調(diào)學生思維能力和動手實踐能力的培養(yǎng)。在課堂講授環(huán)節(jié),通過引入一些創(chuàng)新性問題激發(fā)學生思維,培養(yǎng)學生的創(chuàng)新能力和分析、解決問題能力;在課外訓練環(huán)節(jié),要精心設計大作業(yè)的內(nèi)容,讓學生完成一個具有一定規(guī)模和實用性的軟件,通過這種方式培養(yǎng)和提高學生的動手實踐能力。
在課堂教學上,根據(jù)課程和學生的特點,精心地設計教學內(nèi)容,并在教學目標和教學理念的指導下,提出相應的教學方法。
本課程是一門3學分、48學時的課程,每周3學時,共16周。采用的教材是由清華大學出版社出版的《Java程序設計》,是為了配合本課程的講授以及便于學生學習而編寫的,其內(nèi)容與本課程的內(nèi)容一致。課程的考核方式為平時作業(yè)占30%,大作業(yè)占40%,期末考試占30%。
表1是課程的教學大綱,包括每一章節(jié)的主要內(nèi)容和相應的學時數(shù),總共有10章。
表1 課程大綱
課程的教學大綱和教學內(nèi)容是按照3條主線展開的,即Java語言、面向?qū)ο蟪绦蛟O計和Java類庫。
2.2.1 Java語言
Java語言的內(nèi)容安排在第2章,主要介紹Java的語法,包括數(shù)據(jù)類型與變量、運算符與表達式、控制結構、數(shù)組等。由于Java可以算是從C++發(fā)展而來的,因此Java與C語言的語法比較類似,有些甚至是完全相同的,如算術運算、關系運算、邏輯運算、選擇結構、循環(huán)結構等。熟悉C語言就能夠很輕松地掌握Java語言的語法,只要重點關注兩者之間的不同部分即可,如布爾型和字符型數(shù)據(jù)、數(shù)組等。
2.2.2 面向?qū)ο蟪绦蛟O計
面向?qū)ο蟪绦蛟O計的內(nèi)容主要安排在第3章,另外在第10章也有所涉及,這是本課程最核心、最重要的內(nèi)容之一,從課時數(shù)來看,占到整個課程的1/3以上。如前所述,選修本課程的學生大多是非計算機專業(yè)的本科生,編程基礎較為薄弱,一般只學過一門編程語言,如C語言,因此,如何幫助他們培養(yǎng)和建立面向?qū)ο蟮乃季S方式,是一個巨大的挑戰(zhàn)。在第3章,我們用11課時講類、對象、訪問控制、函數(shù)重載、繼承和多態(tài)。在內(nèi)容設計上,要突出難點和重點。
一個難點是引用(Reference)類型,Java中的引用類型有點像C語言中的指針類型,引用可以脫離對象而單獨存在,要善于區(qū)分引用的運算和對象的運算。例如,在比較兩個字符串時,不能直接用關系運算符“==”,因為比較的是兩個引用的值,而不是目標字符串的內(nèi)容。另外,在使用引用作為函數(shù)的參數(shù)時,傳遞的是目標對象的地址而非內(nèi)容,這一點也比較容易混淆。
另一個難點是數(shù)據(jù)的存儲,即對于一個Java程序,它的不同類型的數(shù)據(jù)分別存儲在內(nèi)存的什么地方,以及各自的作用范圍和生存期分別是什么。靜態(tài)變量存放在靜態(tài)數(shù)據(jù)區(qū),函數(shù)的形參和局部變量存放在棧(stack)中,這就需要講清楚棧和棧幀(stack frame)的原理。棧幀是隨著函數(shù)調(diào)用的開始而分配空間,隨著函數(shù)調(diào)用的結束而釋放空間。當執(zhí)行一個Java程序時,隨著一次次的函數(shù)調(diào)用,就會在棧中形成一個動態(tài)的棧幀序列。動態(tài)申請的空間都是存放在內(nèi)存的堆(heap)中,例如,如果用new新建一個類的對象,那么該對象(包括它的所有成員變量)就存放在堆中。此外,對于程序員來說,堆中的對象可以只創(chuàng)建不釋放,但這并不會導致內(nèi)存泄露,因為Java專門有一個垃圾收集器(garbage collector)回收所有失聯(lián)對象的內(nèi)存空間。
另一個難點是多態(tài),這個知識點對于學生來說是最難掌握的。所謂多態(tài),即在一個類的層次結構中,一個引用變量根據(jù)它所指向的對象類型改變其行為的能力。這允許不同子類的多個對象可被視為同一個父類的對象,卻又能根據(jù)各個對象所屬的子類自動地選擇合適的函數(shù)去執(zhí)行。這里的難點在于:類與成員函數(shù)是分離的,對象與引用也是分離的。如果定義了一個父類引用,然后調(diào)用了它的某個成員函數(shù),但最終執(zhí)行的可能并不是這個父類相應的成員函數(shù),可能是該父類的某個子類同名的成員函數(shù),而且該父類往往又會有多個子類,每個子類都會有一個同名的成員函數(shù)。在這種情形下,學生就會迷惑不解,到底執(zhí)行的是哪一個類的成員函數(shù)呢?因此,對于這樣一個難度比較大的內(nèi)容,需要仔細斟酌,精心設計課件內(nèi)容,務必讓學生能夠真正掌握。
除了上述難點,在教學內(nèi)容設計上,還要注意突出重點。本課程的一個重點,就是要幫助學生建立面向?qū)ο蟮乃季S方式,讓學生在面對一個實際的問題時,學會如何進行分析、歸納和抽象,從中抽取出類的定義,并構造類和類之間的層次關系。在課堂講授上,可以在課件中設計多個案例。例如,在第3章給出如下3個案例:斗地主游戲、Windows中的畫圖軟件以及一家模擬的律師事務所。在這些案例中,引導學生分析和總結,正確地區(qū)分類與對象以及設計合理的類的層次結構。在斗地主游戲中,有一個角色叫豬八戒,可以引導學生這個豬八戒并不能定義為一個類,因為他和其他的游戲玩家如孫悟空和沙和尚具有相同的屬性和行為,因此可以把他們抽象為一個類:游戲玩家類,然后豬八戒、孫悟空和沙和尚都只是該類的一個對象。在律師事務所案例中,有不同類型的員工,如律師、市場銷售、秘書、法律秘書等,可以引導學生建立類和類之間的層次關系。具體來說,無論是律師、銷售還是秘書,都是公司的員工,都要遵守一些相同的規(guī)章制度,如果把他們定義為完全獨立、互不相干的類,就不太合適,可以建立一個新的類:雇員類,然后律師、銷售和秘書都是該類的子類。同樣的道理,秘書和法律秘書也可以形成相應的父類和子類關系。
在第10章,進一步系統(tǒng)地討論如何進行面向?qū)ο蠓治鲞@個問題,即對于給定的問題領域,如何確定類與類之間的關系以及如何確定類的屬性和操作,可以介紹當前常用的幾種方法,包括名詞/動詞分析法、RUP構造型和CRC模型法。以名詞/動詞分析法為例,這是一種運用語言分析的簡單方法,即從文本描述中識別出有關的名詞或動詞,嘗試找出類、屬性和職責。一般來說,名詞和名詞短語暗示著類或類的屬性,動詞和動詞短語暗示著職責或者類的操作。
由于Java本身就是一種面向?qū)ο蟮木幊陶Z言,因此還可使用Java類庫中的例子。例如,在講授數(shù)據(jù)的輸入輸出這部分內(nèi)容時,并不是直接把相關的Java類圖告訴學生,而是引導學生,這個類圖是如何設計出來的。如果是我們自己設計這個類圖,需要考慮如下問題。
(1)數(shù)據(jù)流的方向:輸入還是輸出?
(2)用戶接口:需要定義哪些操作?接口函數(shù)的定義和實現(xiàn)放在什么地方?
(3)數(shù)據(jù)的來源:文件、網(wǎng)絡、鍵盤、其他線程等。
(4)讀寫延遲:從外部設備輸入輸出數(shù)據(jù)時,在時間上會有延遲。
根據(jù)數(shù)據(jù)流的方向,可以推斷出需要定義兩個方面的類,即輸入相關的類和輸出相關的類;根據(jù)用戶接口,可以推斷出需要定義一個類的層次結構,然后把函數(shù)的原型放在上層的接口中,把具體的實現(xiàn)放在下層的實體類中;根據(jù)數(shù)據(jù)的來源,可以推斷出對于每一種不同的來源,需要定義一個相應的實體類;根據(jù)讀寫延遲,可以推斷出需要定義帶有緩沖區(qū)的輸入輸出類。
2.2.3 Java類庫
本課程的第3條主線是Java類庫,主要是介紹Java系統(tǒng)中現(xiàn)有的一些類的使用方法。這部分內(nèi)容安排在第4章—第8章,包括異常處理、輸入輸出、圖形用戶界面、線程、網(wǎng)絡和對象集合。
在介紹這部分內(nèi)容時,為了讓學生的理解更深入、更透徹,需要與其他課程知識相融合。例如,在講解輸入輸出、文件、線程、網(wǎng)絡等章節(jié)時,都是先從操作系統(tǒng)的角度講清楚相關原理,然后再從Java類庫的角度講具體實現(xiàn)。
以線程為例,先從操作系統(tǒng)的角度講解相關的理論背景,如什么是進程、進程的并發(fā)運行、進程的狀態(tài)、什么是線程、線程間的數(shù)據(jù)共享、同步與互斥、調(diào)度與優(yōu)先級等;然后再討論在Java語言中如何實現(xiàn)這些內(nèi)容,如用Thread類創(chuàng)建線程,用堆對象實現(xiàn)數(shù)據(jù)共享,用互斥鎖和線程間通信方法實現(xiàn)同步與互斥以及線程的優(yōu)先級等。
在教學目標、教學理念和教學內(nèi)容明確以后,就需要精心設計教學方法,把課程講好、講精彩。
首先, 在內(nèi)容組織上,要做到結構清晰,各部分內(nèi)容之間要前后關聯(lián)、邏輯性強。以多態(tài)為例,如前所述,這部分內(nèi)容難度很大,因此在設計課件時,是按照Why,What,How這樣一個邏輯結構組織內(nèi)容:先講為什么要引入多態(tài),即希望對一組擁有相同父類的不同子類的對象進行批量化或參數(shù)化處理,如定義一個數(shù)組,能夠同時裝入這組對象;或者定義一個函數(shù),能夠使用這組對象作為參數(shù)。然后講什么是多態(tài),它是一種合而不同的機制,即在編寫代碼時將這組子類對象按照相同類型(即父類)進行處理,以簡化代碼;而在程序運行時,再根據(jù)對象的類型分別綁定到各自不同的實現(xiàn)函數(shù)。最后,再通過例子系統(tǒng)地歸納和總結多態(tài)的所有用法,具體來說,一次函數(shù)調(diào)用的形式為obj.method(),其中,obj是一個引用,本身會有一個類型(父類或子類),另外它會指向一個對象,該對象也會有一個類型(父類或子類),而method是父類或子類中的某個成員函數(shù)。在這種情形下,可以把所有可能的組合都枚舉出來,見表2。
表2 多態(tài)函數(shù)調(diào)用
在表2中,“父有子無”就是指該函數(shù)在父類中有定義,而在子類中沒有?!案笩o子有”則相反,“父子都有”即該函數(shù)在父類和子類中都有定義。該表格把所有可能的組合都枚舉了出來,這樣學生今后再碰到類似的情形,就不會再有疑惑。例如,對于子類對象、父類引用的情形,在調(diào)用一個“父有子無”的函數(shù)時,會調(diào)用父類的函數(shù);在調(diào)用一個“父無子有”的函數(shù)時,會編譯錯誤,無法執(zhí)行;在調(diào)用一個“父子都有”的函數(shù)時,會根據(jù)多態(tài)的原理,調(diào)用子類的相應函數(shù)。
其次,在內(nèi)容設計上,要善于激發(fā)學生的學習興趣,能夠以生動活潑、通俗易懂的方式講述復雜的原理概念,原因在于學生聽課是一個體力活,時間長了容易疲倦,而生動有趣的內(nèi)容容易留下深刻的印象,而且記得牢。
例如,在講解Java子類對象的存儲這部分內(nèi)容時,相關的知識點是這樣:在創(chuàng)建一個子類對象后,一方面,該子類對象本身是一個獨立、完整的對象;另一方面,在該對象內(nèi)部,又包含一個父類子對象,該子對象與正常創(chuàng)建的父類對象相同。對于這樣一段話,學生顯然不太好理解,因此可以在課件中貼一張圖片,即電影鋼鐵俠的海報。如果把人類(Man)看成父類,鋼鐵俠(IronMan)看成子類,那么顯然,在一個子類對象(即影片中的鋼鐵俠)內(nèi)部,又包含了一個父類對象(即商界大亨Tony Stark)。事實上,只要鋼鐵俠把外面這層裝甲脫掉,他就變成一個普通人。
再如,在介紹接口(interface)這部分內(nèi)容時,相關的知識點是這樣:接口主要用來將不同類中的共性方法抽取和封裝起來,它只是一個抽象的接口,只有函數(shù)的聲明,沒有具體的實現(xiàn)。顯然,這段話的確很抽象,學生不太好理解。為了加深學生的印象,可以“杜撰”一個例子。在一些國外的電影中,經(jīng)常會有英雄的形象,所謂英雄,一般會做兩件事情,即打仗和談戀愛,因此可以定義一個英雄類,類名為Hero,它本身是人類類(類名為Man)的子類,同時又實現(xiàn)兩個接口,一個是Fighter,即能打仗;另一個是Lover,即能談戀愛,這樣,學生就在輕松歡快的氛圍中記住接口的原理。
最后,在教案設計和課堂教學安排中,要以學生為中心,師生互動、課堂交流充分。在課堂教學中,一個常見的問題就是教師講得太多、太快,而學生的參與程度不夠。講課的最終目的是讓學生能夠聽懂,能夠?qū)W到知識和提高能力。如果教師講了100%的內(nèi)容,而學生只聽懂30%,那么這樣的課堂就是失敗的。在教案設計和課堂教學中,都要特別重視與學生的交流。具體來說,在教案設計中,要經(jīng)常采用啟發(fā)式教學方法,提高學生的參與程度。例如,在設計課件時經(jīng)常預留一些交互式問答,讓學生回答問題或者讓學生問問題,同時在課堂上經(jīng)常使用Java集成開發(fā)環(huán)境,現(xiàn)場與學生一起編寫和運行程序,這樣,學生的積極性和主動性就會得到很大提高。
對于一門程序設計類課程,課堂教學固然重要,但更重要的是課外實踐,是實踐能力得到提高,能夠解決實際的問題,編寫出真正有用的軟件。為了實現(xiàn)這個目標,課后作業(yè)分為兩部分,一個是每周的編程練習,主要是用來復習課堂上所講授的內(nèi)容;另一個是大作業(yè),學生要完成一個具有一定規(guī)模和實用性的軟件。大作業(yè)的選題需要遵守以下幾條原則。
(1)大作業(yè)要與課堂講授的內(nèi)容相一致。課堂上講授的內(nèi)容主要包括面向?qū)ο缶幊?、圖形用戶界面、多線程編程、網(wǎng)絡編程、異常處理、文件等,在大作業(yè)中需要體現(xiàn)這些內(nèi)容。
(2)大作業(yè)需要有一定的規(guī)模,代碼量不能太少,否則無法起到訓練的效果。
(3)大作業(yè)需要有一定的實用性,如果做好了,那么就是一個實用的應用軟件。
(4)大作業(yè)要有一定的趣味性,這樣學生的積極性就更高一些。
就目前來說,各個學期布置的大作業(yè)題目一般是一些經(jīng)典的小游戲,包括貪吃蛇、俄羅斯方塊、連連看、坦克大戰(zhàn)、黑白棋、飛機大戰(zhàn)、黃金礦工等。
從大作業(yè)的要求來看,以坦克大戰(zhàn)為例,大作業(yè)的總分為40分,其要求包括圖形用戶界面16分(包括界面功能實現(xiàn)6分、美觀程度4分、性能要求6分)、事件處理4分(包括鍵盤按鍵響應2分和碰撞檢測2分)、網(wǎng)絡編程6分(包括socket網(wǎng)絡通信3分、客戶端狀態(tài)同步策略3分)、其他游戲功能4分、聲音和音效2分、文件讀寫2分、實驗報告6分。要求只能使用Java語言編程,而且每位學生獨自完成整個作業(yè)。
從往屆學生的情況來看,絕大多數(shù)學生能順利完成作業(yè),只是完成的效果不盡相同。有部分學生的作業(yè)非常完美,界面美觀、運行流暢、網(wǎng)絡狀態(tài)同步實時、音效良好、動畫炫美,幾乎就是一個可以上線的軟件產(chǎn)品。圖1和圖2所示分別是坦克大戰(zhàn)和黑白棋的優(yōu)秀作品。
圖1 坦克大戰(zhàn)的優(yōu)秀作品
圖2 黑白棋的優(yōu)秀作品
Java是一門重要的編程語言,在軟件工業(yè)有著廣泛的應用。如何在大學中講好這門課程,切實地提高學生的分析問題、解決問題和動手實踐能力,是每一名任課教師需要認真思考的問題。本文就是在這個方面的一個嘗試,從教學目標、教學理念、教學內(nèi)容設計,到教學方法和實踐訓練,完整地描述了一門課程的所有環(huán)節(jié)。經(jīng)過多年的實踐,這門課程已經(jīng)基本成熟,也逐漸得到了學生的認可;在歷年的教學評估中,都取得了較好的成績,尤其是在2016年秋季學期的教學評估中,在全校同類、同規(guī)模的536門課程中,進入了全校前5%。
未來的工作包括進一步完善教學內(nèi)容,提出新的教學案例,根據(jù)學生的反饋改進大作業(yè)的內(nèi)容和安排。