網路城邦
上一篇 回創作列表 下一篇   字體:
第六章 Java的進階語法
2021/06/06 16:45:11瀏覽980|回應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)

( 知識學習隨堂筆記 )
回應 推薦文章 列印 加入我的文摘
上一篇 回創作列表 下一篇

引用
引用網址:https://classic-blog.udn.com/article/trackback.jsp?uid=ben168&aid=163566104