天天看點

《程式設計導論(Java)·1.2類》

本節初步介紹類的細節。

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——做練習為主。

《程式設計導論(Java)·1.2類》