20145221《Java程序設計》第四章-認識對象 總結
教材學習內容總結
類與對象
- 定義:對象是Java語言中重要的組成部分,之前學過的C語言是面向過程的,而Java主要是面向對象的。Java中變量有2種類型,一個是基本類型,另一個則是類類型。使用Java撰寫程序幾乎都是在使用對象,要產生對象必須先定義類,類是對象的設計圖,對象是類的實例。
- 特點:
- 有別於C語言的程序編寫,在用Java編寫中,如果需要什么功能,我們就可去找一個對象,而這個對象就包含這個功能,然后通過new建立對象,通過“.”來調用該類的一些功能。
- 其中要能理解對象的含義,例如
Clothes c1 = new Clothes();
,其中c1是在棧內存中產生,而對象c1則是產生在堆內存中,c1可以近似看成指向堆內存中的指針。 - 書上提供了一個很好的方法正確理解其中的本質,那就是畫圖,形象又直觀,所以在涉及對對象的理解時,一是可以畫圖,二是可以把相關代碼貼在電腦上運行一下。還有一些常用的標准類Scanner,BigDecimal等都大大方便了我們編程過程。
- 注意:對象相等性,首先要明白對於類類型的變量來說==和基本類型中的==有本質上的區別,因為是類類型,所以==表示的是這個類產生的2個對象是否是同一個對象,如果是同一個對象,那么==的返回值才是true,否則為false;如果想要比較2個對象的內含值,應該要用
a.equals(b);
。其實理解其最好的方法就是畫圖,例如課本P89頁中的代碼,以及后面相關知識的介紹,都是通過畫圖來理解的。
基本類型打包器
- 概述:在上一章已經學習了基本類型的變量,但是對於Java程序語言來說,基本類型的變量效率往往不高效,Java的特點在於面向對象,所以我們也可以把這些基本類型的變量打包成對象之中,這樣我們就可以像操作對象那樣操作這些原本是“基本類型”的變量了。
- 特點:
- 開始在學習這一節內容的時候,不能理解,明明是基本類型了,為什么還要把大費周章地又打包成類類型。在最后編寫本章的操作題時,我就明白了,編寫Java時一定要把固有的“面向過程”的思想轉化為“面向對象”,對象可以提供我們許多功能,簡化我們的編程,這在后續學習中會更加明白。
- 除了打包,J2SE 5.0之后,還能自動裝箱、自動拆箱,在我的理解看來,就是不需要嚴格的像一般建立對象那樣,可以簡便一些(自動裝箱與拆箱的功能事實是編譯程序蜜糖),例如:
Integer wrapper = 10; // 自動裝箱
int foo = wrapper; // 自動拆箱
- 注意:既然打包為了對象,當然也要滿足對象的特點,尤其是判斷“相等”。作為編程者,一定要弄明白我們的目的。在建立對象之后,如果是想比較這2個對象的內含值,則一定一定要使用
a.equals(b);
這種形式的比較方式,只要把握了這一點,就可以避免課本P97這樣的錯誤。
數組對象
- 概述:數組在Java中就是對象,牢牢把握這個概念。
- 特點:對象的一些特點性質都可以在數組中使用。定義數組的方法,如果知道是哪些具體的數,則可以如課本P95一樣,不知道具體的數可以像P98一樣。在定義二維數組時,也可以仿照一維數組進行定義,總之,數組就是對象,這是數組最大的特點。
- 注意:
- 既然數組是對象,那么對象需要注意的性質,數組都要注意。對於數組本身來說,不能超過其索引范圍,不然會報錯:ArrayIndexOutOfBoundsException(編譯時不會報,運行時會報錯)。
- 注意各種類型的數組初值情況(課本P98)。
- 再就是要理解二維數組的本質,二維數組,其實是在數組的基礎上對於每個元素,再建立一個數組(對象),只是在很多編程實例中體現出了“二維”、“矩陣”等形式,本質還是在數組的每個元素上再建立數組。認識到這一點,加上課本的圖4.5、4.6、4.7、4.8理解起來就容易多了。
- 數組的復制,首先由2個方法可以用System.arraycopy()與Arrays.copyof(),可以通過這些類快速復制一個數組,不過在調用時要注意括號中參變量的含義,類型內容都要一致。在使用了上述方法后,要明白一點我們進行的都是“淺層復制”,是沒有連同對象一起復制的。如果想要深層復制,則需自己操作,敲代碼完成自行復制元素,如課本P106。
字符串對象
- 概述:字符串本質是打包字符數組的對象,是java.lang.String類的實例。
- 特點:既然是對象,當然也會有很多功能,length(),charAt(),toUpperCase()等。通過一些方法Byte.parseByte(number)等還可以將字符串剖析為基本類型。
- 注意:
- 字符串池:如果直接將一串字符指定給2個字符串變量,則這2個字符串變量會參考到同一對象。因為在Java中,為了效率考慮,只要""包括的字符串內容相同,無論在程序代碼中出現多少次,JVM都只會建立一個String實例,並在字符串池中維護。
- 不可變動字符串:必須知道的是:在Java中,字符串對象一旦建立,就無法更改對象中的任何內容,對象上沒有任何一個方法可以更改字符串內容。使用+字符可以達到這樣的效果,不過根據反編譯的過程,可以發現,實際上是產生了新的字符串對象。而大量的產生新對象又是不希望看到的,所以我們可以用StringBuilder來改善,它的特點在於每次調用完后都會返回原有的StringBuilder對象,這樣可以避免產生多個對象。
查詢Java API文件
- 概述:從本章的實際操作題和課本上的范例程序中,都會發現每個程序幾乎都使用了不同的類,如java.util.Scanner、java.math.BigDecimal、基本類型打包器等等。在以后的編程中,如果我們想用某一個類的某一功能,但不知道如何調用,或者想了解一下某一個類具體有哪種功能等內容,就必須要通過查詢Java API文件了。
- 方法:
- 通過Java官方網站;
- 直接通過搜索引擎搜索相應的類,就會顯示對應的文件說明了;
- 以上2種方法都是在線查詢Java API文件,還可以離線查詢。在Windows下可以下載CHM格式的Java API。如圖,這樣會更加的方便。
教材學習中的問題和解決過程
-
其實這一章內容開始不是很好理解,因為之前對對象沒有一個概念。所以學的有點慢,但是按照老師說的方法,一個是認真看課本,另一個就是學編程必須要養成的習慣,勤敲代碼。可能開始對書上給的一些代碼,還不能理解,面對書上畫的圖,也不能透徹明白。但是好記性不如爛筆頭,編程也一樣,只要把這些代碼敲一遍,編譯運行一編,看看結果,這樣印象可能會深一點,對於代碼的領悟可能更也更好一些。
-
在最開始接觸對象時,有一種感覺就是感覺“類”有點似曾相識,感覺和C語言中的結構體很像。不過我知道C語言是面向過程的,Java是面向對象的,所以我認為結構體和類還是應該有本質區別的。翻開原來的C語言書,發現結構體好像只是把不同類型的變量打包在了一個“新的類型變量中”,並不能在結構中定義功能,而類中還可以構造函數。類的功能應該更強大更靈活。
-
按照老師的指導,對書中P112的效率進行了測試:
(1). 代碼(只展示第一種,后兩種只是將“測試代碼段”用課本上的填充進去):public class TestJavaClass{ public static void main(String[] args){ //獲取開始時間 long startTime = System.currentTimeMillis(); //測試代碼段 for(int i=1; i<101; i++){ System.out.print(i); if(i != 100){ System.out.print('+'); } } System.out.println(); //獲取結束時間 long endTime = System.currentTimeMillis(); System.out.println("程序運行時間: "+(endTime-startTime)+"ms"); } }
(2). 運行結果:
第一種:
第二種:
第三種:
(3). 結論:通過上述操作,確實可以發現第三種的效率最高。
代碼調試中的問題和解決過程
課后操作題
Fibonacci數列:
-
代碼:
import java.util.Scanner; public class Fibonacci{ public static void main(String[] args){ System.out.printf("求幾個費氏數?"); Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); if(n == 1) System.out.print(0); else{ int[] fiboNums = new int[n]; fiboNums[1] = 1; System.out.print(0 + " " + 1); for(int i=2; i<n; i++){ fiboNums[i] = fiboNums[i-2] + fiboNums[i-1]; System.out.print(" " + fiboNums[i]); } } System.out.println(); /* fiboNums[1] = 1; for(int i=2; i<n; i++){ fiboNums[i] = fiboNums[i-2] + fiboNums[i-1]; } for(int fiboNum : fiboNums){ System.out.print(fiboNum + " "); } System.out.println(); */ } }
-
運行結果:
-
結論:第一次編譯該程序的時候出現了亂碼,可能跟我這個代碼要打印漢字有關吧。經過查找了相關資料,只要在編譯時輸入
-encoding utf-8
即可解決。斐波那契數列不算難,不斷迭代就可解決。程序中被注釋的代碼是最開始編的,最后想到也可以用一個for循環解決,不過感覺2個for循環看起來結構更清楚一些,可讀性更強一些。
洗牌:
-
代碼:
public class Shuffle{ public static void main(String[] args){ String[] pokers = { "梅1","梅2","梅3","梅4","梅5","梅6","梅7","梅8","梅9","梅10","梅J","梅Q","梅K", "磚1","磚2","磚3","磚4","磚5","磚6","磚7","磚8","磚9","磚10","磚J","磚Q","磚K", "桃1","桃2","桃3","桃4","桃5","桃6","桃7","桃8","桃9","桃10","桃J","桃Q","桃K", "心1","心2","心3","心4","心5","心6","心7","心8","心9","心10","心J","心Q","心K" }; //定義一個0-51的隨機數(不重復)數組。 int[] numbers = new int[pokers.length]; //賦初值,簡化判斷是否有重復隨機數的循環次數。 for(int i=0; i<pokers.length; i++){ numbers[i] = -1; } int num; boolean flag; for(int i=0; i<pokers.length; i++){ //產生隨機數。 while(true){ flag = true; num = (int) (Math.random() * pokers.length); //凡是搜索到了-1就表示已經搜索完畢。 for(int j=0; numbers[j]!=-1 ;j++){ if(numbers[j] == num){ flag = false; break; } } //不重復,就向隨機數數組中賦值。 if(flag){ numbers[i] = num; break; } } } /* 調代碼時用到,用以判斷是否成功生成了0-51的不重復隨機數。 for(int number : numbers){ System.out.print(number + " "); } System.out.println(); */ for(int i=0; i<pokers.length; i++){ System.out.printf("%-4s",pokers[numbers[i]]); if((i+1)%13 == 0) System.out.println(); } } }
-
運行結果:
-
結論:拿到這個題目,想了一會,考慮怎樣才能讓52張撲克牌隨機輸出。最后想到了可以用隨機數產生的方法,0-51個隨機數對應的其實就是“撲克牌數組”的角標,因為角標的隨機,實現了出牌的隨機。當然因為編寫不熟悉,輸出的撲克牌有重復的,為了能更加看清楚隨機數(不重復)是否成功產生,打印了52個隨機數(代碼中被注釋的片段)。最后發現過然是隨機數產生有問題,逆推回去,發現了第二個for循環中判斷的條件原來寫的是“numbers[j]!=0”,當時的想法是如果碰到0了,說明已經搜索完了(int型數組默認賦初值為0),不用往后搜了,但是忽略了一點,產生的隨機數也含有0,如果采取這樣的方法判斷,就會出現重復的情況。所以在前面加了一句,將數組中的元素賦初值為1。最后打印時發現,“梅10”“心10”等這4張牌多占一個字符的寬度,所以為了打印美觀,將輸出格式控制為%-4s。
排序:
-
代碼:
import java.util.Arrays; public class BubbleSort{ public static void main(String[] args){ int[] number = {70, 80, 31, 37, 10, 1, 48, 60, 33, 80}; /* 冒泡排序代碼: int temp; for(int j=0; j < number.length-1; j++){ for(int i=0; i < number.length-1-j; i++){ if(number[i] > number[i+1]){ temp = number[i]; number[i] = number[i+1]; number[i+1] = temp; } } } */ Arrays.sort(number); for(int num : number){ System.out.printf("%3d",num); } System.out.println(); } }
-
運行結果:
-
結論:排序題其實在C語言中也接觸了不少,代碼中被注釋的片段用到的就是冒泡排序,可以說比較簡單高效。但因為Java面向對象的特性,可以用
Arrays.sort(number);
,一句代碼,直接將原來的數組從小到大排列,更加簡單。既然再學Java,就要多使用對象,這樣可以提高編程技能和效率。
查詢:
-
代碼:
(1)產品代碼:import java.util.Scanner; public class Search{ public static void main(String[] args){ int[] number = {1, 10, 31, 33, 37, 48, 60, 70, 80}; Scanner scanner = new Scanner(System.in); int num = scanner.nextInt(); System.out.println(binary(number,num)); } public static int binary(int[] array, int value){ int low = 0; int high = array.length - 1; int middle; while(low <= high){ middle = (low + high) / 2; if(value == array[middle]) return middle; if(value > array[middle]) low = middle + 1; if(value < array[middle]) high = middle - 1; // System.out.println(middle); } return -1; } }
(2)測試代碼:
public class SearchTest{ public static void main(String[] args){ int[] number = {1, 10, 31, 33, 37, 48, 60, 70, 80}; if(Search.binary(number,1) != 0) System.out.println("test failed 1!"); else if(Search.binary(number,10) != 1) System.out.println("test failed 2!"); else if(Search.binary(number,31) != 2) System.out.println("test failed 3!"); else if(Search.binary(number,33) != 3) System.out.println("test failed 4!"); else if(Search.binary(number,37) != 4) System.out.println("test failed 5!"); else if(Search.binary(number,48) != 5) System.out.println("test failed 6!"); else if(Search.binary(number,60) != 6) System.out.println("test failed 7!"); else if(Search.binary(number,70) != 7) System.out.println("test failed 8!"); else if(Search.binary(number,80) != 8) System.out.println("test failed 9!"); else if(Search.binary(number,0) != -1) System.out.println("test failed 10!"); else if(Search.binary(number,40) != -1) System.out.println("test failed 11!"); else if(Search.binary(number,100) != -1) System.out.println("test failed 12!"); else System.out.println("test passed!"); } }
-
運行(測試)結果:
-
結論:二分法之前也接觸過,這次將其運用到了Java程序中。根據畢老師的視頻,學到了自定義函數的一些皮毛,就嘗試着用函數的功能編寫了一個可以查找數組中某個數的方法。其實這一點跟C語言中的知識比較類似,同一個類中,最多只允許一個main函數,它是代碼的入口,執行代碼先找main函數,先執行main函數;自定義的函數也和原來學的差不多,注意形參的類型個數,注意函數類型、有無返回值等情況。
其他
- 之前就聽說過面向過程、面向對象,但不知道具體指的是什么。經過這一章的學習,要把面向對象這個觀念牢記心中,這是區別C語言等其它面向過程語言的不二法寶。
- 對於對象的理解還要更加加深理解,要在平時的編程練習中鞏固加強。熟能生出百巧來,只有熟練了,才能提高自己的編程技術,理清自己的編程思路,升華自己的編程思想。