本節初步介紹類的細節。
1.2.1 類體結構
首先,按照我的對象優先教學政策,先介紹類的知識而非先講控制結構。是以,早在第一章第二節,就直接給出一個完整的Dog例程例程1-1。而1.2.1類體結構圍繞例程,總體介紹狀态、行為和身份辨別(引用)。很多東西,不友善現在解釋的,就不解釋。同學們先在BlueJ中操作它。
★Learning by doing.
package zoo;
/**
* class Dog 描述了人類忠實的朋友狗。類聲明。
* @author yqj2065
* @ version 0.1
*/
public class Dog { // 類頭、類聲明
private String name = "baby" ; //字元串 狗名,預設時叫baby。private表示私有的通路權限
private boolean sex; //布爾/邏輯資料 性别。性别隻有兩種,必居其一。true代表雄性。
private int age = 0; //整數(integer) 年齡,預設為0歲。
/* setter and getter*/
/**
* 設定狗的名字.
* @param n 狗的名字(字元串)。
* @throws 以後再考慮參數檢查的事情,現在搞不定。不得用總統的名字。
*/
public void setName(String n) {
name = n;
}//設定狗的名字。void表示傳回值是空的、不需要的。
String getName() {return name;} // return/傳回狗的名字
/**@param isMale true代表雄性 */
void setSex(boolean isMale) { sex = isMale; }
int getAge() {return age;}
/**搖尾巴行為。輸出字元串Hello World */
void wagTail() { System.out.println("Hello World"); }
/**狗會做簡單的加法*/
int add(int i ,int j){return i+j;}
}
1.對象的狀态(state)是對象的所有屬性值的集合。每一個對象擁有自己的記憶體空間儲存自己的狀态,即儲存名字、年齡、性别等等屬性值。
2.客觀對象的“行為”是一種概括的說法。如Dog有搖尾巴、咆哮等動作、有(被人)設定其年齡或詢問其年齡的活動;抽象的對象如圓,有計算其面積和周長的公式、計算過程或算法。源代碼将客體行為模組化為可被執行的代碼塊,Java中稱為方法(method) ,針對某個屬性進行讀寫的各個方法,俗稱setter和getter。
3.對象的身份辨別(或同一性),在本教程的讨論範圍内,完全按照它在記憶體中占據的位置(通過引用)判斷。即兩個對象占據相同的記憶體位置,它們就是“同一個”對象;否則,即使它們的狀态完全一樣,僅僅是“相等的”。
練習1-11.使用BlueJ IDE,掌握附錄A.1的内容:編輯和編譯源代碼。 |
練習1-12.:熟練掌握附錄A.2與類、對象互動:在BlueJ中建立一個Dog對象,并調用其wagTail()、getName() 和setName(String)方法。說明如何檢視上面的3個方法調用後的效果。 提醒:字元串文字作為參數時注意用雙引号包圍它。 練習1-17:什麼是對象的同一性? |
4.類的結構綜述
類作為對象的模闆,最基本的組成部分是對象的狀态和行為,還包括内部類、内部接口(請參見[9.4嵌套類型])。Java中,它們被稱為類的成員。
類還有一個重要工作,就是為更友善地建立自己的對象做好準備。因而在類定義時,源代碼中經常安放了這種目的的代碼塊—— 構造器、初始化塊。這些代碼塊不用于描述對象特征,故它們不被視為類的成員。就像旁聽生一樣,雖然在class中,卻不是班級的一分子。有時稱其為 類的非成員。
是以,普通類的結構通常為:
//package xxx;
//import xxx;
/**類簡介 */
public class Xxxx{
<域聲明>
<構造器,初始化塊>
<方法定義>
<内部類、内部接口>
}
1.2.2 空白與注釋
下面圍繞例程1-1 zoo.Dog.java,說明源代碼的構成——注釋、空白和五種Java詞法元素:辨別符、關鍵字、文字、操作符和分隔符。同時請做一個遊戲,把Dog代碼複制到zoo. Dog4Fun中,逐一删除已經介紹的部分。
空白包括空格、制表符(Tab)和換行符。
*單詞和單詞之間如private String name,需要使用一個空格進行間隔(它是一個分隔符)
*制表符——有規律的縮進,主要的作用是使代碼的結構清晰。
*源代碼是純文字檔案,每一行以換行符結束。換行符有ASCII碼中的CR (Carriage return, '\r', 0x0D, 13)和LF (Line feed, '\n', 0x0A, 10)。(讀取一個檔案時,要注意換行符的處理。)
注釋,在書中占用的篇幅接近“類的結構”,特别是列舉了常用的JavaDoc的塊标記。不管如何強調都不過分,
★程式必須是寫給人讀的,僅僅偶爾讓機器執行——Abelson and Sussman
★Any fool can write code that a computer can understand. Good programmers write code that humans can understand——Martin Fowler
1.2.3 五種Java元素
Java詞法元素(token、語言符号)是Java編譯器能夠識别的最小單元,包括如下五種:辨別符、關鍵字、文字、操作符和分隔符。
例程 1-2 不同種類的辨別符互相獨立
package semantics;
public class YQJ {
YQJ YQJ(YQJ YQJ){
YQJ:
for(; ;){
if(YQJ.YQJ(YQJ)==YQJ)
break YQJ;
}
return YQJ;
}
}
辨別符的文法簡單列舉了一下。Java中的 辨別符、注釋、字元文字、字元串文字可以使用Unicode,而關鍵字、操作符等其他Java元素則使用ASCII字元。
練習1-25. 定義一個方法,判斷一個字元是否為辨別符可用字元。請用各種字元(包括漢字)替換下面代碼中的符号"★"。并證明2mail、room#是或不是合法的辨別符。
chard='★';
booleanb1=Character.isJavaIdentifierStart(d);//是否合法的辨別符首字元
booleanb2=Character.isJavaIdentifierPart(d);
System.out.println(b1 + " "+ b2);
可用字元的詳細資料參考JDK Documentation中Character.isJavaIdentifierStart()等方法的文檔。
(這是我的嘗試,在這個階段,學生能夠完成練習1-25嗎?)
練習1-30. 數值0、字母A、a的ASCII嗎分别為____。(1)0、65、66 (2)48、65、97 (2)48、96、97。
2.關鍵字
abstract | assert | boolean | break | byte | case |
catch | char | class | const | continue | default |
do | double | else | enum | extends | final |
finally | float | for | goto | if | implements |
import | instanceof | int | interface | long | native |
new | package | private | protected | public | return |
short | static | strictfp | super | switch | synchronized |
this | throw | throws | transient | try | void |
volatile | while |
false、 true和null理論上是文字,沒有填入表1-1中。
3.文字
文字也稱為常量值、字面值。
整數文字通常使用十進制數,也可以使用2、8或16進制數,數字之間可以加_以增強可讀性;以零(0)開頭的數為八進制數;以零b(0b或0B)開頭的數是二進制數,以零x(0x或0X)開頭的數是十六進制數。String文字是String對象的引用。
4.[3.2.2 操作符]将全面介紹操作符。
5.分隔符(separators)将程式不同部分分隔開來。如一個空格分割單詞,逗号分割方法的參數,分号表示語句的結束(因為可以把多個語句放在一行)。剩下的就是{} ()。
練習1-35:編寫程式typeSystem.primitive.Literal.java,驗證各種文字的文法。(我發現我有極端輕視文法教學的傾向。)
1.2.4 文法、語意和約定
文法(syntax、句法;syntactic grammar、文法規則) 嚴格規定了一系列詞法元素如何組成句法正确的語句。它是編寫程式時 必須遵循的規則。編譯器能夠按照程式設計語言的token和一套文法規則,判定程式的諸多元素是否正确的組合在一起。
對新程式員而言,文法錯誤非常容易出現。
語意(或語義,semantics)通常指語言結構所表達的含義,或者說一個語句執行時,計算機将做些什麼。絕大多數情況下,進階語言采用自然語言和數學的習慣用法來表達語意,因而可以不太關注語意,通常将文法和語意合為一體來讨論。如果借助直覺能夠了解的語言結構越多,說明該語言越友好。
通常語言結構的語意是明确的,即每一條語句有且隻有一種解釋。然而,有時候一門語言對某種結構沒有定義其語意,如[3.2.2 操作符]中表達式的求值順序,在C語言中沒有定義其語意。同一個合法的語句,不同的程式員表達的意思可能不同,不同的執行環境運作後得到的結果可能不同。另外,語言結構所表達的不同于人們想當然的語意,就要注意語意的學習。如
for(int x : list){
x++;
}
别指望它能夠對int[]list = {1,3,5,7,9}的各元素加一,使數組元素變成{2,4,6,8,10}。
約定(convention)。文法是程式員“必須”遵守的規定,否則編譯不通過。約定是“應該”遵守的,因為它是程式員社群的要求。約定如同現實生活中的公德限制——應該給老人、孕婦等讓座。需要約定的基本原因,是為了程式員能夠容易地讀懂(他人的)代碼。程式設計約定能夠有效地增強代碼的可讀性、更好的維護性。如命名約定、排版約定等等。
1.2.5 案例:分數
程式并非都是Dog這樣的語言教學例子,先看看一些實用性的而且簡單的代碼。
Fraction類中僅列舉了加法功能,其他功能代碼如減法、乘除在學完[3.2實作]後請自行實作。為了得到規格化形式的分數,需要一個計算a,b的最大公約數的功能子產品gcd(int,int),其實作采用了歐幾裡德(Euclid)算法/輾轉相除法,參見[10.1.1 算法]。
package tips;
public class Fraction{
private int num; //分子numerator
private int den = 1; //分母denominator
public int getNum(){return num;}
public int getDen(){return den;}
public Fraction(){this(0);}
/** N/1 形式的分數*/
public Fraction(int N){
num = N;
}
/**建立一個分數等于 N/D
* @param N 分子numerator,
* @param D 分母denominator,非0的int值。 */
public Fraction(int N , int D){
int n = N >= 0 ? N: -N ;
int d = D >= 0? D: -D ;
int g = gcd (n ,d); // 規格化
this.num = (( N>= 0) == ( D>= 0))? (n/g):( - n/g);
this.den = d/g;
}
/**将一個分數相加到本對象身上*/
public void add(Fraction f){
num= num*f.den +den*f.num;
den = den *f.den;
int g = gcd(num,den);
num /= g;
den /= g;
}
/**計算a,b的最大公約數greatest common divisor */
public final int gcd(int a,int b){
while(b != 0){
int tep=a % b;
a = b;
b = tep ;
}
return a>=0?a:-a;
}
/**改寫Object的toString() */
@Override public String toString(){
return (den == 1 )? (""+getNum()):(getNum()+"/"+getDen());
}
}
本節共10頁,建議教學課時1h,課下自學4h——做練習為主。
