java入門經驗分享——記面向對象先導課程學習感想
選擇在暑期學習面向對象先導課程的初衷是為大二下學期面向對象課程做一些鋪墊,接觸入門java語言。在接觸java的過程中,就編程語言的學習方法而言,我從剛入學時的手慌腳亂四處尋求幫助到現在慢慢養成了自己不斷尋找困難解決方法的習慣,感覺自己的另一種自學能力——一種計算機工科的實踐能力得到了培養,這種自學能力跟學習基礎課程理論知識的感覺完全不同,這種需要在實踐和動手中得到經驗和知識的自學方法與以前理解現成抽象理論概念的自學方法可以說是完全不同了。作為一個偏好理論研究的理科女生,動手實踐能力在此之前幾乎為零,是一個完完全全的編程小白,大學對我而言是一個重新開始、從零開始的起點,能在先導課程中進一步培養短期內學習到大量內容掌握大量技能的能力,是我覺得收獲很大的地方。我有時候也會覺得困難,但這只會讓我回想起以前的學習過程中,我總會默默告訴自己,“
在先導課的第一節課上,我了解到了java的一些基本編程思想。Java是一門面向對象的高級編程語言,在第一節課,我就感受到了面向對象的思路,在後來的課程中,對java的高級有了更深刻的印象,其中之一表現在java豐富的類庫以及友好的編程環境。Java的面向對象編程的特點之一體現在類的概念。這和之前一直熟悉的數據結構所使用的C語言有很大的區別。在之前的學習過程中,使用C語言的時候大多是面向過程進行編程,更側重於思考怎麽解決一個問題的過程。而使用java時,需要思考抽象出來一個類,這個
1 public class WordCount { 2 private String word; 3 private int count; 4 5 public int getCount() { 6 return count; 7 } 8 9 public void setCount(int count) { 10 this.count = count; 11 } 12 13 public String getWord() { 14 return word; 15 } 16 17 public void setWord(String word) { 18 this.word = word; 19 } 20 }
還有筆者在第一次接觸java的時候曾經犯過兩個印象深刻的錯誤,一個是在類裏面寫了除變量聲明和函數之外的語句(實際上這些語句應該在函數體裏面),另一個是沒有實例化對象直接調用了方法,筆者認為這兩個錯誤對於寫慣C語言的java初學者來說都是值得註意的小地方。在第一節課,老師帶我們以box類為例學習了一些java的基本語法要求,重要的內容是學習到了java中的構造方法以及繼承。構造方法相當於C語言裏面的初始化,在沒有構造函數的時候,java會對變量進行特定的默認初始化賦值。構造函數無需返回值類型,作用域類型是public,this表示當前對象(用.取對象的變量或者方法,類似於C語言的指針用法,但java裏面沒有指針),然後用傳進來的參數對當前對象(使用的時候需要實例化)進行賦值即可。構造函數中所使用的變量聲明不需要構造函數之前,因為在編譯的時候會搜索類中所有內容來找到要復制的變量。還有最重要的一個特征是,構造函數的名字要和類的名字相同。在new一個類即實例化一個對象的時候,相當於同時調用了類的構造函數,即進行了初始化賦值。下面是構造函數的使用demo:
1 public class Box1 { 2 public Box1(double width, double height, double depth){ 3 this.width = width; 4 this.height = height; 5 this.depth = depth; 6 } 7 private double vol; 8 double width; 9 double height; 10 double depth; 11 public double volume(){ 12 vol = width*height*depth; 13 return vol; 14 } 15 }
還有一個很重要的特性就是繼承。繼承可以從已有的類中派生出新的類,新的類能吸收已有類的數據屬性和行為,並能擴展新的能力。但是不能有選擇性的繼承。子類可以繼承父類非private型的所有成員。繼承的關鍵字是extends。下面是一個繼承使用方法的例子:
1 public class ScaleBox extends Box1{ 2 private double volume; //註意養成寫作用域的好習慣 3 public ScaleBox(double width, double height, double depth, double volume){ 4 super(width, height, depth); //用super調用父類方法,必須是第一行防止後面值被改變(否則編譯器會報錯) 5 volume = 0; 6 } 7 public double setScale(double scale){ 8 volume = height * scale * width * scale * depth * scale; 9 return volume; 10 } 11 }
此外,java的語法與C語言有很多的相似之處,但是有些細節還是不盡相同。在java是根據布爾值來確定if條件的真假,如像“boolean flag = false;”這樣聲明變量,同樣,函數的返回值也可以是布爾值。還有值得註意的是,在java中回車是由回車符(\r)與換行符(\n)兩個字符組成的。
第二堂課上主要介紹了封裝以及java接口的概念。Java接口(Interface)是一系列方法的聲明,是一些方法特征的集合,一個接口只有方法的特征沒有方法的實現,因此這些方法可以在不同的地方被不同的類實現,而這些實現可以具有不同的行為或功能。Java語言不支持一個類有多個直接的父類(多繼承),但可以實現(implements)多個接口,間接的實現了多繼承。Java接口中的成員變量默認都是public,static,final類型的(都可省略),必須被顯示初始化,即接口中的成員變量為常量(大寫,單詞之間用"_"分隔)。Java接口中的方法默認都是public,abstract類型的(都可省略),不能是靜態方法,沒有方法體,不能被實例化,接口中只能包含抽象方法而不能直接實現函數體的內容和功能。Java接口中只能包含public,static,final類型的成員變量和public,abstract類型的成員方法。接口中沒有構造方法,不能被實例化,接口只是抽象的。一個接口不能實現另一個接口,但它可以繼承多個其它的接口,同樣使用extends方法進行繼承,新定義的接口被稱為復合接口。Java接口必須通過類來實現它的抽象方法,如“public class Cylinder implements Geometry”。此外,值得註意的是,當類實現了某個Java接口時,它必須實現接口中的所有抽象方法,否則這個類必須聲明為抽象的。不允許創建接口的實例(實例化),但允許定義接口類型的引用變量,該引用變量引用實現了這個接口的類的實例 ,如:
public class B implements A{} A a = new B(); //引用變量a被定義為A接口類型,引用了B實例 A a = new A(); //錯誤,接口不允許實例化
在第三和第四節課裏,老師和助教又帶領我們學習了java的調試功能以及如何進行性能分析,進一步對程序進行性能提高練習,兩節課裏我們以詞頻統計為練習樣例,同時練習了一些字符串處理的小技巧。字符串的分割(split方法)對於格式化處理字符串有著很大的幫助作用。其中分割時借助正則表達式可以大大化簡處理方法,如對字符串"ab-c,de..f,gAB, CDE,FGH,CDE?f"進行分割取出字母串的時候,可以使用按位或從正面進行分割,也可以用非字母的正則表達式進行分割,用法如下:
String str1[]=s1.split("[\\?]+|[\\.]+|[\\-]+|[,| ]+"); //[^A-z]+ “+”表示不含空串
筆者為了提高程序的性能,使用了哈希方法的變種對文本文檔進行了詞頻統計,寫法類似於字典樹。不僅提高了查找效率,還使得單詞本身有序,用深度優先搜索即可完成單詞或者短語的有序輸出。筆者在完成第四次作業的時候遇到了幾個難纏的bug,第一個是對大容量文檔測試的時候,總是容易出現數組溢出的報錯,筆者將所有報錯點的字符輸出,發現都是亂碼的字符,經很多次的資料查找後發現,可能是是因為原文文檔出現了中文之類的字符,java中的中文字符是兩個字符進行表示,在讀入的時候沒有進行特殊的讀入處理的時候是會出現亂碼的情況的,於是筆者更換了測試文檔。在測試了大量不同的測試文檔之後,筆者增加了分隔符種類以保證短語定義的嚴密性。還有一個是對空串的處理,要是想實現在讀入字符串的時候能夠讀入在首位的空白字符而不是過濾掉空白字符進行字符串讀入,需要使用BufferedReader而不是scanner,因為scanner會自動過濾掉首位空白字符進行字符串讀取。另外要是想判斷空白字符及空白字符組合形成的空串,需要使用字符串的trim方法。具體實現的部分代碼如下:
1 BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in)); 2 String s = buffer.readLine(); 3 if (s.trim().isEmpty()) { 4 System.out.println("The string is empty!"); 5 } 6 else {... 7 }
經過短短兩周的學習過程,筆者覺得自己對java有了初步的了解,可以嘗試多在以後的代碼編寫中利用java寫程序。在課程學習中,個人對吳老師富有啟發式和引導式的教學方法印象深刻,這讓筆者對java學習產生了濃厚的興趣,無論是課上還是課下,一個接著一個的緊密銜接的任務讓筆者對java有了不同的感受。還有對筆者的學習過程中有即大幫助的是助教在課上和課後的細心解答,有困難和問題的時候能得到及時的解決,加深了對java的理解。這些幫助是我覺得選擇先導課的幸運之處。從不安與擔心到享受解決問題,筆者對coding有了不同的看法。還有一個重大的收獲是,以前寫代碼都是力求每個函數每個細節都親力親為,比如排序之類的函數,但是現在終於認識到java類庫的強大,這樣寫出的代碼質量更高而且看起來很漂亮。以及筆者在逐漸鍛煉自己很匱乏的一項能力——自己查找資料,百度也好Google也好,利用現有的工具解決問題,遠比自己一個人戰鬥要高效的多,要學會善於利用資源,即所謂的工欲善其事必先利其器。以前在數理化學習過程中喜歡自己思考自學,很少問別人也很少尋求幫助,只要自己思考能解決的問題絕對不會問別人,現在才體會到借助互聯網學習是很強大的資源。在學習過程中,能得到老師和助教面對面的幫助以及對java某些概念的引導來幫助理解陌生內容並且在課堂上一起完成代碼是我覺得本課程最有幫助的地方。因為是初次接觸課上的代碼測試,而不是類似於在oj上直接課後提交看到測試結果,會在課上測試的時候顯得手慌腳亂而拖延課程的進度,希望在剛開始的時候可以提前發一些測試點讓大家適應一下課上測試。以及發布課後作業要求的時候更清楚一些。
面向對象這門課程很重要,不僅僅體現在學科知識掌握方面。從宏觀人類社會角度來看,面向對象的研究方法是符合人類社會規律的。科幻電影《降臨》介紹了因果論和目的論,其原著《你一生的故事》中打翻了人類不可預知未來的前提,間接說明了如果知道未來的結果,人類就會向著這個結果來實現中間的過程,不再存在因果與選擇。筆者在寒假看完《降臨》的一個午後,突然意識到,面向過程編程是目的論的產物,而面向對象編程才是符合目前人類社會因果論的解決問題的方法,這種選擇與不確定性以及類和對象的概念,是用來模擬人類社會最好的選擇,在那一瞬間,忽然體會到計算機科學的奇妙之處,感覺到自己所知甚少,是多麽渺小。就像曾經初學物理時,感到有很多問題無處可問,有焦躁和不安,現在想想,初學計算機的感受竟和小時候的自己如此相似,忽然感覺到一種妙不可言的聯系。那就借用物理書的編者曾寫的一首打油詩來結尾吧
“昔年曾見此湖圖,不信人間有此湖。
今日打從湖上過,畫工還欠費功夫。”
願以後的自己看到現在的自己,永遠能感嘆一聲畫工還欠費功夫。
java入門經驗分享——記面向對象先導課程學習感想