第六章 Java的進階語法 - 人生紀錄本 - udn部落格
人生紀錄本
作家:宋坤祐
文章分類
    Top
    第六章 Java的進階語法
    2021/06/06 16:45:11
    瀏覽:1772
    迴響:0
    推薦:0
    引用0

    大家已經學過很多程式語言的觀念,這些觀念多半屬於程式語言的主要特徵,所以在很多程式語言裡頭都可以得到印證。以Java來說,由於發展得很快,而且之前已經有CC++C#等語言的基礎,所以各種語法都相當地完備。我們在本章中將以Java程式為主軸,Java來看一般程式語言的特徵。除此之外,由於程式語言也有一些比較進階的觀念與語法,例如例外處理(exception handling)、並行程式設計(concurrent programming),我們可以從Java來了解這些未來可能會派上用場的語法。

    1. Java的語法驗證程式語言的觀念。

    2. 認識Java程式語言的進階語法。

    3. 綜合應用Java的語法與程式語言的觀念。

    6-1-1 Java類別架構

    應用系統中資料之間的關係: 所謂的關聯通常是代表應用系統中資料之間的關係,應用系統的使用者對於這些關係的認知是相當敏銳的。 程式也應該很明確地描述這些關係。 可以透過Java的語法來描述兩種常見的關聯: is-ahas-a

    關聯的實例

    class A extends B { //AB的子類別

    A( ) { } ;

    }

    class C { //C中有A屬性的成員

    A ca;

    }

    class B {

    int anyone;

    }

    // class A is-a B

    // class C has-a A

    正確與錯誤的使用實例

    public class IsHas {

    public static void main(String [ ] args) {

    A ma = new A( );

    B mb = new B( );

    C mc = new C( );

    A oaref;

    B obref;

    C ocref;

    oaref = ma;

    mc.ca = new A( );

    oaref = mc.ca;

    obref = mb;

    obref = ma;

    // oaref = mb; // 這是錯誤的!

    }

    5-1-1-2內部類別

    內部類別(inner class): 類別定義裡頭還可以包含類別定義,這就是所謂的內部類別 (inner class)的觀念。 內部類別算是Java中比較進階的語法。

    TestInner.java

    class tryInner {

    classIn co;

    tryInner ( ) {

    co = new classIn( );

    co.sayHi( );

    }

    class classIn {

    void sayHi( ) {

    System.out.println("Hi from classIn!");

    }

    }

    }

    public class TestInner {

    public static void main(String [ ] args) {

    tryInner io = new tryInner( );

    }

    }

    6-1-2 參數的傳送規則

    參數傳送的兩種情況: 程式和作業系統之間傳送的程式行參數 (command-line arguments)。 主程式和副程式之間的參數傳送。

    C/C++ 中副程式的呼叫: 傳值呼叫 (call by value)。 傳址呼叫 (call by reference)。 從語法上能看出差異,不過常滋生困擾。

    Java 傳遞參數的規則: 傳送簡單的變數或基本值以傳值呼叫為參數傳送的方法,所以只是把參數的值傳過去,隨後任何改變不會反映到原呼叫程式的變數中。 傳送物件或陣列時以傳址呼叫的方式來傳送參數,所以參數值的變化會反映到原來呼叫程式對應的變數中。

    6-1-3 複雜資料型態的傳送

    用簡單的語法來傳送複雜的資料型態可以大幅簡化程式的撰寫。 陣列 (array) 可以算得上是一種複雜的資料型態。 Java中陣列的傳送是用傳址呼叫的方式,所以傳出去的陣列若是有任何數值的改變,原呼叫程式同樣會查覺。

    class array_receiver

    {

    public void compute(int ary[ ])

    {

    for (int i=0; i<ary.length; i++) {

    ary[i] *= ary[i];

    }

    }

    }

    public class PassArray {

    public static void main(String[ ] args)

    {

    int myArray[ ] = {1,3,5,7,9};

    array_receiver ar = new array_receiver();

    for (int i=0; i<myArray.length; i++) {

    System.out.println("myArray["+i+"]="+myArray[i]);

    }

    ar.compute(myArray);

    for (int i=0; i<myArray.length; i++) {

    System.out.println("now, myArray["+i+"]="+myArray[i]);

    }

    }

    }

    myArray[0]=1

    myArray[1]=3

    myArray[2]=5

    myArray[3]=7

    myArray[4]=9

    now, myArray[0]=1

    now, myArray[1]=9

    now, myArray[2]=25

    now, myArray[3]=49

    now, myArray[4]=81

    Java 裡頭的有效範圍(scope): 類別層次的範圍 (class-level scope)。 方法層次的範圍 (method-level scope)。 程式片段層次的範圍 (code block-level scope)

    6-1-4 副程式的觀念

    思考活動: Java裡頭有沒有副程式 (subroutine)的觀念呢? Java還需要有副程式的觀念嗎? Java裡頭就只有類別,副程式必須以類別方法的型式存在。

    ModifiedPassArray.java

    class array_receiver {

    public void compute(int ary[ ]) {

    for (int i=0; i<ary.length; i++) {

    ary[i] *= ary[i];

    }

    }

    public void print_array(int ary[ ]) {

    for (int i=0; i<ary.length; i++) {

    System.out.println("ary["+i+"]="+ary[i]);

    }

    }

    }

    public class ModifiedPassArray {

    public static void main(String[ ] args) {

    int myArray[ ] = {1,3,5,7,9};

    array_receiver ar = new array_receiver( );

    ar.print_array(myArray);

    ar.compute(myArray);

    ar.print_array(myArray);

    }

    }

    6-1-5 系統資源的管理

    記憶體空間的管理: 記憶體空間是電腦系統裡相當珍貴的資源。 一般程式語言都會提供管理記憶體的語法,例如C 裡頭的mallocC++newdelete,或是Java new關鍵字。 基本的原則是:不必再用到的資料可以移出記憶體。

    「循環參考」(circular reference): Java 支援自動回收記憶體空間的機制,叫做「垃圾回收」 (garbage collection)。 任何的記憶體空間若是沒有任何物件參考指向它,Java 不久就會把這塊空間歸還給系統。 在自動回收記憶體空間的機制下,必須避免「循環參考」 (circular reference)。 也就是甲物件有指向乙物件的物件參考,而乙物件同時具有指向甲物件的物件參考。 這時候即使讓任一物件的物件參考值為null,由於指向另外一個物件的參考依然存在,造成記憶體空間無法被回收。

    物件參考的意義: Java裡的物件建立以後,就會占用記憶體空間。 物件參考只是一種變數,可指向不同的記憶體空間,所以同一個記憶體空間可能被多個物件參考所指。 到底一個物件存在多久,得看這些物件參考何時變成null,否則就得等到程式結束才會全部清空。

    ReturnArray.java

    class Array_Creator {

    public int[ ] create_an_array( ) {

    int ary[ ]={1,3,5,7,9};

    return ary;

    }

    }

    public class ReturnArray {

    public static void main(String [ ] args) {

    Array_Creator ac = new Array_Creator( );

    int ary[ ]=ac.create_an_array( );

    for (int i=0; i<ary.length; i++) {

    System.out.println("ary["+i+"]="+ary[i]);

    }

    }

    }

    6-2-1 例外處理基本觀念

    程式錯誤的型態: 與系統有關的錯誤:例如記憶體不足、檔案無法開啟等問題,通常程式會終止,看不到有意義的結果。 和程式編輯有關的錯誤:此時程式可能有輸出結果,但卻不是正確的,代表程式本身的邏輯就有問題。

    例外處理 (Exception handling)基本的用法:

    6-2-2 Java例外處理的實例

    Finally的子句: Finally後面的區塊一定會被執行。 當例外發生以後,try區塊中有些程式碼不會被執行到,可能有一些必須執行的動作,例如關閉檔案等,會被略過,這時候就可以把這些程式碼放到finally的程式區塊中。 Finally必須放到最後一個catch區塊之後,這樣才能保證裡頭的程式碼一定會被執行。

    TestMyException.java

    class myException extends Exception {

    myException(String reportMsg) {

    super(reportMsg);

    }

    }

    class TestMyException {

    public static void main(String args[ ]) {

    int x=50, y=7;

    try {

    if (x/y < 10) throw new myException(x/y + "<10" + "give up!");

    }

    catch (myException e) {

    System.out.println("x/y is smaller than 10!");

    }

    finally {

    System.out.println("finally中的程式碼一定會被執行!");

    }

    }

    }

    系統內定的例外

    6-3 基本觀念

    多工(multitasking): 電腦系統常利用多工 (multitasking) 的方式來提昇效率,主要是讓電腦的資源時常保持忙碌,常用的方法是同時執行數個工作。 程式 (program) 儲存在媒體上,執行時才會載入電腦裡,執行緒可以看成是輕量級的執行程式,通常都比較簡短。 從多工的觀點來,執行緒能達到的效果比程序來得好,因為簡短的執行緒在安排執行時彈性比較大。 執行緒之間共享位址空間 (address space)、執行緒執行時的切換 (context switching) 比較不費事,而且執行緒之間的溝通很方便。

    執行緒的特性: 共享記憶體。 同時執行。

    執行緒的種類: 使用者執行緒(user threads):執行Java應用程式的時候,系統會產生一個使用者執行緒,負責執行應用程式中的main( )方法,我們也把這個執行緒稱做「主執行緒」(main thread),主執行緒還可以繼續產生所謂的「子執行緒」(child thread)常駐執行緒(daemon threads):屬於系統產生的執行緒,常駐行在應用程式執行的過程中一直都存在,等所有的使用者執行緒都結束後,常駐執行緒才會停止執行。

    Java 程式裡使用執行緒的方法: 實作Runnable介面Runnable介面裡有一個run( ) 方法,實作Runnable介面的意思就是把run( ) 方法內含的程式碼寫出來,然後在類別定義的建構子中產生Thread物件,啟動Thread物件的start( ) 方法。 這個子執行緒會自動執行run( ) 方法。 繼承Thread類別:直接定義成Thread類別的子類別,同時也提供run( ) 方法的程式碼,然後執行start( ) 方法,和實作Runnable介面的方式很類似。

    6-3-1-1 多執行緒程式設計的優點

    執行緒的組成: 執行緒的識別碼 (thread ID) 、程式計數器 (program counter)、 暫存器組 (register set) 、堆疊 (stack)

    多執行緒程式設計的優點: 資源的共用。 提升系統回應的效率。 整體效能的提升。

    6-3-1-2 執行緒狀態的變化

    傳統的處理元可以看成僅有單一的執行緒,常稱為heavyweight processsingle threaded process, 具有多執行緒的處理元(multi-threaded process)可同時進行多項工作,等於是讓各執行緒分頭進行,同屬於一個處理元的執行緒會共用程式碼、資料與一些作業系統的資源。

    1. new的狀態: 使用new關鍵字建立執行緒物件之後就進入了new的狀態。

    2. runnable的狀態: 呼叫start( )方法會促使記憶體空間分配給該執行緒,同時執行run( )方法進入runnable的狀態,正在執行的或是等待執行的執行緒都是處在runnable的狀態。

    3. dead的狀態: run( )方法執行結束或是呼叫stop( )方法會使執行緒進入dead(即結束)的狀態。

    4. blocked的狀態: 執行中的執行緒會因I/O, 呼叫sleep( )或呼叫suspend( )方法而進入blocked(即中止)的狀態,但可呼叫resume( )方法回到runnable的狀態。

    6-3-1-3 Java中建立執行緒

    Java 中建立執行緒: 定義Thread類別的子類別: JavaThread類別中定義了很多和執行緒相關的方法,只要繼承了這些方法,就可以對執行緒進行一些處理。 實作Runnable介面: 實作Runnable介面,在建構子中建立執行緒,呼叫start( )方法,然後把執行緒的真正功能放在 run( )方法中。

    Thread 子類別的方式建立執行緒

    class ThreadBySubclassing extends Thread {

    ThreadBySubclassing( ) {

    start( ); //標準的建構子啟始碼

    }

    public void run( ) // thread建立後執行的程式碼 {

    System.out.println("A thread is created!");

    }

    }

    public class MainPgm1 {

    public static void main(String args[ ]) {

    Thread t = Thread.currentThread( ); System.out.println("Current thread name is " + t.getName( )); //建立並啟始thread

    ThreadBySubclassing nt = new ThreadBySubclassing( ); System.out.println("Thread name is " + nt.getName( )); nt.setName("NewName");

    System.out.println("Thread name has been changed to " + nt.getName( ));

    }

    }

    實作Runnable 介面來建立執行緒

    class ThreadWithRunnable implements Runnable {

    Thread thread; ThreadWithRunnable( ) { //標準的建構子啟始碼 thread = new Thread(this,"ThreadName");

    thread.start( );

    }

    public void run( ) // thread建立後執行的程式碼 {

    System.out.println("A thread is created!");

    }

    }

    public class MainPgm2 {

    public static void main(String args[ ]) { //建立並啟始thread

    ThreadWithRunnable nt = new ThreadWithRunnable( );

    System.out.println("Thread name is " + nt.thread.getName( ));

    }

    }

    6-3-1-4 Java執行緒中常用的方法

    Java 執行緒中常用的方法: sleep( )方法: 強迫執行緒進入中止的狀態,裡頭的參數代表中止的時間,以千分之一秒為單位。 SetPriority( )方法: Java執行緒具有執行上的優先順序(priority),最高是10,最低為1,正常的為5。 由於作業系統才是真正的主宰,所以不見得完全如願。 Join( )方法: 等待某個執行緒結束執行。

    MainPgm3.java

    // 執行緒優先順序的設定

    class MyThread extends Thread {

    MyThread(String TName) {

    super(TName); start();

    }

    public void run() {

    try {

    System.out.println("目前的執行緒: " + (Thread.currentThread()).getName());

    Thread.sleep(1000);

    }

    catch(InterruptedException e) { } System.out.println((Thread.currentThread()).getName() +

    "執行緒結束");

    }

    }

    public class MainPgm3 {

    public static void main(String args[]) {

    MyThread t1 = new MyThread("thread-1");

    MyThread t2 = new MyThread("thread-2");

    MyThread t3 = new MyThread("thread-3");

    t1.setPriority(1);

    執行時期的行為

    6-3-2 共用程式碼與同時性控制

    同時性控制(synchronization): 由於Java 執行緒共用程式碼與位址空間,在某些不能共用的情況下,我們必須限制執行緒的行為。 例如執行緒甲預期對A帳戶加入存款100 ,假如執行緒乙恰巧也同時將A帳戶扣除100 ,則正在執行中的執行緒甲所看到的結果是帳戶餘額不增不減,這是不合理的。 解決的辦法是每次只讓一個執行緒對相同的帳戶進行處理,處理完後才可以由其他執行緒來用。這就叫做同時性的控制。

    程式方塊的同時性控制(I)

    class MainPgm4 {

    public static void main(String args[ ]) {

    CommonArea common = new CommonArea();

    MyThread thread1 = new MyThread(common,"執行緒甲");

    MyThread thread2 = new MyThread(common,"執行緒乙");

    MyThread thread3 = new MyThread(common,"執行緒丙");

    try {

    thread1.join( );

    thread2.join( );

    thread3.join( );

    }

    catch(InterruptedException e) { }

    }

    }

    程式方塊的同時性控制(II)

    class MyThread extends Thread {

    CommonArea CA;

    public MyThread(CommonArea CA, String string) {

    super(string);

    this.CA = CA;

    start( );

    }

    public void run( ) {

    synchronized(CA) {

    CA.SharedCodeBlock(Thread.currentThread( ).getName( ));

    }

    }

    }

    程式方塊的同時性控制(II)

    class CommonArea {

    void SharedCodeBlock(String string) {

    System.out.println("開始進行的執行緒 : "+string);

    try {

    Thread.sleep((long)(Math.random()*500));

    } catch (InterruptedException e) { }

    System.out.println("結束的執行緒 : "+string);

    }

    }

    6-3-3 執行緒之間的溝通

    執行緒之間的溝通(I)

    class Common {

    int signal=0;

    synchronized void SharedCodeBlock( ) {

    try {

    Thread.sleep(1000);

    } catch(InterruptedException e) { }

    signal=1;

    notify( );

    }

    synchronized int getResult() {

    try {

    wait( ); } catch(InterruptedException e) { }

    return signal;

    }

    }

    執行緒之間的溝通(II)

    class MyThread1 extends Thread {

    Common Common;

    public MyThread1(Common Common, String string) {

    super(string);

    this.Common=Common;

    start( );

    }

    public void run( ) {

    System.out.println("結果為: "+Common.getResult());

    }

    }

    執行緒之間的溝通(II)

    class MyThread2 extends Thread {

    Common Common;

    public MyThread2(Common Common, String string) {

    super(string);

    this.Common=Common;

    start( );

    }

    public void run( ) {

    Common.SharedCodeBlock( );

    }

    }

    執行緒之間的溝通(III)

    class WaitNotify {

    public static void main(String args[ ]) {

    Common Common=new Common( ); MyThread1 thread1=new MyThread1(Common,"one"); MyThread2 thread2=new MyThread2(Common,"two");

    }

    }

    執行WaitNotify.java 的結果

    重點整理: 1. Java類別架構衍生出來的語法。2. 副程式與資料的傳遞。3. 資源管理。4. 例外處理。5. Java執行緒(thread)

    回應
    發表迴響

    會員登入