正修科技大學資訊工程系
Java 程式設計課程協同教學講義
主題 1:類別、物件、屬性、方法 主題 2:存取修飾元
主題 3:建構子 (Constructor) 主題 4:static 關鍵字
主題 5:多載 (Overload) 主題 6:繼承 (Inheritance) 主題 7:覆寫 (Override)
主題 8:抽象類別 (Abstract Class) 主題 9:介面 (Interface)
主題 10:多型 (Polymorphism) 主題 11:泛型 (Generics)
主題 12:集合 (Collection) 主題 13:例外 (Exception)
講師:黃國穎 0911735188
日期:2016/5/11、5/18、5/25
目錄
主題 1:類別、物件、屬性、方法 ... 1
主題 2:存取修飾元 ... 2
主題 3:建構子 (Constructor) ... 3
範例 03_建構子 ... 3
主題 4:static 關鍵字 ... 4
範例 04_static 關鍵字_01 ... 4
範例 04_static 關鍵字_02 ... 5
主題 5:多載 (Overload) ... 6
範例 05_多載 ... 6
主題 6:繼承 (Inheritance) ... 7
範例 06_繼承_01 ... 7
範例 06_繼承_02 ... 8
主題 7:覆寫 (Override) ... 9
範例 07_覆寫 ... 9
主題 8:抽象類別 (Abstract Class) ... 10
範例 08_抽象類別 ... 11
主題 9:介面 (Interface) ... 12
範例 09_介面_01 ... 13
範例 09_介面_02 ... 14
主題 10:多型 (Polymorphism) ... 16
範例 10_多型_01 ... 16
範例 10_多型_02 ... 17
主題 11:泛型 (Generics) ... 18
範例 11_泛型_01_泛型方法 ... 20
範例 11_泛型_02_泛型類別 ... 20
範例 11_泛型_03_比較運算測試 ... 20
範例 11_泛型_04_限定的泛型 ... 21
主題 12:集合 (Collection) ... 21
範例 12_集合_01_HashSet ... 23
範例 12_集合_02_TreeSet ... 24
範例 12_集合_03_ArrayList ... 25
範例 12_集合_04_LinkedList ... 25
範例 12_集合_05_HashMap ... 26
範例 12_集合_06_TreeMap ... 27
範例 12_集合_07_Iterator 介面... 27
範例 12_集合_08_ListIterator 介面 ... 28
範例 12_集合_09_Collections 類別 ... 28
範例 12_集合_10_其他應用 1 ... 29
範例 12_集合_11_其他應用 2 ... 29
範例 12_集合_12_其他應用 3 ... 30
主題 13:例外 (Exception) ... 31
範例 13_例外_01 ... 32
範例 13_例外_02_自訂例外類別 ... 32
範例 13_例外_03_例外處理 1 ... 33
範例 13_例外_04_例外處理 2 ... 34
主題 1:類別、物件、屬性、方法
物件導向程式設計,簡單地說,就是利用小程式建立出一個個的零件,再將零件組合起來,以建立出 大型應用程式的程式設計方式。
類別、物件、屬性,與方法:
所謂類別(Class),就是具有相同特徵(屬性)與行為(方法)的物件所組成的集合。類別是抽象的,我們可 以把類別看做是物件的設計藍圖,設計藍圖中規範了在這個類別下的物件擁有哪些屬性,哪些方法。
而物件就是根據這份設計藍圖所建立的實體。
在 java 的環境中,我們使用 new 這個關鍵字建立類別的實體。
譬如: Car bus=new Car();
這行程式敘述表示,在 Car 類別下,產生一個名為 bus 的物件實體。
主題 2:存取修飾元
在 Java 的環境,有四種存取修飾元:private、default、protected、public
透過存取修飾元,可實現物件導向程式設計的封裝性(encapsulation),依照存取權限可歸納如下:
套件(package),簡單地說就是將類別分門別類來儲存。當程式內容越來越大時,就有可能產生類別名 稱重複的問題,而套件就能避免這樣的問題。如果一個程式中沒有宣告套件,則 Java 會將這個程式 預設為“沒有名稱的套件”,放在同一路徑下的類別都會被視為屬於同一套件。
若類別未被加上任何修飾元,則表示該類別被設定為 default 等級,只能被同一套件(package)下的類別 (class)存取。若加上了 public,則該類別可被任何類別存取。
主題 3:建構子 (Constructor)
建構子,又稱建構函式或建構方法,是一種特殊的函式。
透過建構子,在自類別生成實體物件的同時,能對物件進行 "初始化"。
在本主題中,我們練習定義一 Cube 類別,包含該類別中的屬性、建構子和方法。利用建構子新增兩 實體物件,利用 showProfile()方法打印出物件的資訊,getArea()方法回傳表面積的計算結果,getVol() 方法回傳體積的計算結果。
範例 03_建構子 public class Ch01 {
public static void main(String args[]) {
Cube c1=new Cube("1號立方體",3);
Cube c2=new Cube("2號立方體",5);
c1.showProfile();
c2.showProfile();
} }
class Cube {
String name;
int side;
Cube() { }
Cube(String name,int side) {
this.name=name;
this.side=side;
}
void showProfile() {
System.out.println(name+"邊長"+side+"公分,表面積為"+getArea(side)+"平方公分,體積為"+getVol(side)+"立 方公分.");
}
int getArea(int x)
{
return x*x*6;
}
int getVol(int x) {
return x*x*x;
} }
主題 4:static 關鍵字
成員 (資料成員或方法成員) 在宣告時若使用關鍵字 static 修飾,則該成員變成屬於類別 (class) 擁有 而非物件 (object) 擁有,因此我們若要在其他地方使用該成員時,不用建立實體物件,只需透過類別 即可使用。譬如:Math.PI 即為靜態的資料成員,而 Math.pow() 即為靜態的方法成員。
範例 04_static 關鍵字_01 public class Ch01
{
public static void main(String args[]) {
Dog d1=new Dog("憨憨",2,3.8f);
Dog d2=new Dog("球球",1,2.5f);
Cat c1=new Cat("咪咪",5,3.2f);
d1.showProfile();
d2.showProfile();
c1.showProfile();
System.out.println("總共有"+Dog.sum+"隻狗,"+Cat.sum+"隻貓.");
} }
class Dog {
static int sum=0;
String name;
int age;
float w;
Dog(String name, int age, float w) {
sum++;
this.name=name;
this.age=age;
this.w=w;
}
void showProfile() {
System.out.println(name+"今年"+age+"歲,體重"+w+"公斤.");
} } class Cat {
static int sum=0;
String name;
int age;
float w;
Cat(String name, int age, float w) {
sum++;
this.name=name;
this.age=age;
this.w=w;
}
void showProfile() {
System.out.println(name+"今年"+age+"歲,體重"+w+"公斤.");
} }
在第二個練習中,我們自定一 MyMath 類別,類別中包含三個方法:pow()、minus() 與 plus() 利用這個自定類別的方法,完成指數、減法、與加法運算。
譬如當呼叫 MyMath.pow(2,5)時,能回傳 32。
範例 04_static 關鍵字_02 public class Ch02
{
public static void main(String[] args) {
int x=2, y=5;
System.out.println(x+"的"+y+"次方為"+MyMath.pow(x,y));
System.out.println(x+"減去"+y+"等於"+MyMath.minus(x,y));
System.out.println(x+"加上"+y+"等於"+MyMath.plus(x,y));
} }
class MyMath {
static int pow(int x,int y) {
int res=1;
for(int i=1; i<=y;i++) {
res*=x;
}
return res;
}
static int minus(int x,int y) {
return x-y;
}
static int plus(int x,int y) {
return x+y;
} }
主題 5:多載 (Overload)
方法多載的定義:
相同的方法名稱,卻擁有不同功能運算。
條件是引入參數的數量不同或是型態不同。
練習:輸入五個數字,分別利用四個相同名稱的方法來計算
1. 前兩個數相加 2. 前三個數相加 3. 前四個數相加 4. 五個數相加
範例 05_多載
import java.util.Scanner;
public class Ch01 {
static Scanner s=new Scanner(System.in);
public static void main(String args[]) {
int a, b, c, d, e;
System.out.print("請依序任意輸入5個數: ");
a=s.nextInt();
b=s.nextInt();
c=s.nextInt();
d=s.nextInt();
e=s.nextInt();
System.out.println("前兩個數相加: "+total(a,b));
System.out.println("前三個數相加: "+total(a,b,c));
System.out.println("前四個數相加: "+total(a,b,c,d));
System.out.println("五個數相加: "+total(a,b,c,d,e));
}
public static int total(int a, int b) {
return a+b;
}
public static int total(int a, int b, int c) {
return a+b+c;
}
public static int total(int a, int b, int c, int d) {
return a+b+c+d;
}
public static int total(int a, int b, int c, int d, int e) {
return a+b+c+d+e;
} }
主題 6:繼承 (Inheritance)
所謂繼承(Inheritance)是一種類別之間的關係,可以利用現有類別衍生出新的類別,新的類別可以 與現有類別分享共同的屬性、方法與資料結構等,在物件導向程式設計中利用繼承,可以達到程式碼 重複使用的優點。
繼承功能可以在建立新類別時,修改、添加或繼承現有類別的定義。建立一種類別後,如果須要再建 立許多大同小異的類別,就可以利用繼承特性,分別繼承這個現有的類別,並且將差異部分加以修改 或添加。
被繼承的類別稱為父類別或基礎類別(Base Class),而經由繼承產生的類別,則稱為子類別或衍生類 別(Derived Class),子類別不僅可以繼承父類別的特性(屬性與方法),也可以修改或添加特性。
C++支援多重繼承(一個類別擁有多個父類別),但 Java 不直接支援多重繼承,類似的機制則必須透 過介面來完成。
歸納上述,關於繼承的四大特性如下:
1. 子類別可以繼承父類別的特性。
2. 子類別可以添加新的特性。
3. 子類別可以修改並重新定義自父類別繼承下來的特性。
4. 子類別繼承父類別時,不需要複製父類別的程式碼,造成程式碼重複。
範例 06_繼承_01 public class Ch01 {
public static void main(String args[]) {
Dog d1=new Dog("憨憨",2,1.28);
d1.showProfile();
Dog d2=new Dog("球球",1,1.35);
d2.showProfile();
Cat c1=new Cat("咪咪",3,0.95);
c1.showProfile();
} }
class Animal
{
String name;
int age;
double w;
Animal(String name, int age, double w) {
this.name=name;
this.age=age;
this.w=w;
}
void showProfile() {
System.out.println(name+"今年"+age+"歲,體重"+w+"公斤.");
} }
class Dog extends Animal {
Dog(String name, int age, double w) {
super(name, age, w); //呼叫父類別的建構子 }
}
class Cat extends Animal {
Cat(String name, int age, double w) {
super(name, age, w);
} }
範例 06_繼承_02 public class Ch02 {
public static void main(String args[]) {
Dog d1=new Dog("憨憨",2,1.28);
d1.showProfile();
d1.makeSound(2);
Dog d2=new Dog("球球",1,1.35);
d2.showProfile();
d2.makeSound(3);
Cat c1=new Cat("咪咪",3,0.95);
c1.showProfile();
c1.makeSound(5);
} }
class Animal {
String name;
int age;
double w;
Animal(String name, int age, double w) {
this.name=name;
this.age=age;
this.w=w;
}
void showProfile()
{
System.out.println(name+"今年"+age+"歲,體重"+w+"公斤.");
} }
class Dog extends Animal {
Dog(String name, int age, double w) {
super(name, age, w);
}
void makeSound(int x) //於子類別中新增的方法 {
for(int i=0; i<x; i++)
System.out.print("汪~");
System.out.println();
} }
class Cat extends Animal {
Cat(String name, int age, double w) {
super(name, age, w);
}
void makeSound(int x) //於子類別中新增的方法 {
for(int i=0; i<x; i++)
System.out.print("喵~");
System.out.println();
} }
主題 7:覆寫 (Override)
範例 07_覆寫 public class Ch01 {
public static void main(String args[]) {
Dog d1=new Dog("憨憨",2,1.28,"棕色");
d1.showProfile();
d1.makeSound(2);
Dog d2=new Dog("球球",1,1.35,"白色");
d2.showProfile();
d2.makeSound(3);
Cat c1=new Cat("咪咪",3,0.95);
c1.showProfile();
c1.makeSound(5);
} }
class Animal {
String name;
int age;
double w;
Animal(String name, int age, double w) {
this.name=name;
this.age=age;
this.w=w;
}
void showProfile() {
System.out.println(name+"今年"+age+"歲,體重"+w+"公斤.");
} }
class Dog extends Animal {
String color; //於子類別中新增的屬性 Dog(String name, int age, double w, String color) {
super(name, age, w);
this.color=color;
}
void makeSound(int x) //於子類別中新增的方法 {
for(int i=0; i<x; i++)
System.out.print("汪~");
System.out.println();
}
void showProfile() //覆寫自父類別繼承的方法 {
System.out.println(name+"今年"+age+"歲,體重"+w+"公斤,毛色為"+color+".");
} }
class Cat extends Animal {
Cat(String name, int age, double w) {
super(name, age, w);
}
void makeSound(int x) //於子類別中新增的方法 {
for(int i=0; i<x; i++)
System.out.print("喵~");
System.out.println();
} }
主題 8:抽象類別 (Abstract Class)
在設計 Java 程式時,當程式設計者希望基礎類別只是用來被衍生類別繼承,而不希望真正產生一個 基礎類別的物件,此時就可以將基礎類別宣告成抽象類別。
所謂抽象類別(Abstract Class)是指無法建立物件,只能被衍生類別繼承的一種特殊類別,而在抽象 類別裡可以宣告抽象方法(Abstract Method),抽象方法是一個尚未完全實作的方法,表示一個方法原 型,必須在衍生類別中撰寫方法的實作內容。
關於抽象類別的幾個重點歸納如下:
1. 包含抽象方法的類別一定要宣告為抽象類別。
2. 抽象類別有建構子,但無法產生物件實體。
3. 抽象類別可同時包含抽象方法與一般方法,也可以完全沒有抽象方法。
4. 抽象類別一定要被繼承,而抽象方法一定要被改寫。
5. 若衍生類別中有任何一個抽象方法沒有被實作,則必須將該類別也宣告為抽象類別,以便強迫更 下層的類別在繼承它後,實作剩餘沒有被實作的抽象方法。
範例 08_抽象類別 public class Ch01 {
public static void main(String[] args) {
Square sq=new Square("方形1號","綠色",5);
sq.showName();
sq.showColor();
sq.calArea();
Tri tr=new Tri("三角形1號","粉紅色",7,5);
tr.showName();
tr.showColor();
tr.calArea();
} }
abstract class Shape {
String name, color;
Shape(String name, String color) //抽象類別一樣有建構子,但無法產生物件實體
{
this.name=name;
this.color=color;
}
void showName() {
System.out.println("物件名稱: "+name);
}
void showColor() {
System.out.println("顏色為: "+color);
}
abstract void calArea();
}
class Square extends Shape {
int x;
Square(String name, String color, int x) {
super(name,color);
this.x=x;
}
void calArea() //實作父類別的抽象方法 {
System.out.println("面積為: "+x*x+"平方公分");
} }
class Tri extends Shape {
double x, y;
Tri(String name, String color, double x, double y) {
super(name,color);
this.x=x;
this.y=y;
}
void calArea() //實作父類別的抽象方法 {
System.out.println("面積為: "+(x*y/2)+"平方公分");
} }
主題 9:介面 (Interface)
介面(Interface)是一種特殊的類別,在宣告時不是使用 class 而是改用 interface,但在編譯後也會產 生.class 檔。介面只有宣告而沒有實作,架構上與抽象類別有點類似,用法與限制卻大不相同。
在 Java 中,類別並不允許多重繼承,但允許實作多個介面,而在介面繼承時,也允許繼承多個父介 面,我們利用這兩種機制來模擬多重繼承。
我們可以把介面想像成物件與外界溝通的橋樑,或物件欲扮演的角色。通常我們使用介面來描述不同 類別的物件間,共通的行為或特性。譬如:有個介面叫父親,把這個介面套用在小貓、小鳥、還是人 類身上,就能使該物件開始扮演父親的角色,執行照顧子女的任務。
關於介面的幾個重點歸納如下:
1. 介面中的成員變數必須利用關鍵字 final 宣告為常數,並指定初始值。
2. 介面中的成員方法必須宣告為抽象方法,不能有方法主體。
3. 由於編譯器會自動將介面中的成員變數宣告為 public、static 與 final,因此是常數變數,同時關鍵字 可以省略。
4. 由於編譯器會自動將介面中的成員方法宣告為 public 與 abstract,因此是抽象方法,同時關鍵字可 以省略。
5. 類別可繼承一個類別但可實作多個介面。
6. 介面不能實作另一個介面但可繼承多個介面。
7. 類別可以新增成員變數與成員方法,但一定要定義介面中所有抽象方法的內容,同時因為介面中 的成員方法存取權限都是 public,所以在這邊定義成員方法時,也必須加上 public,否則會編譯錯 誤。
範例 09_介面_01 public class Ch01 {
public static void main(String[] args) { Human h1=new Human("湯尼",35,70);
h1.showProfile();
h1.eat(0.85);
h1.showProfile();
h1.swim(1500);
h1.sing("新不了情");
h1.takeCare();
} }
abstract class Animal {
int age;
double w;
Animal(int age, double w) {
this.age=age;
this.w=w;
}
abstract void eat(double x);
abstract void showProfile();
}
interface Swimmer {
String LEVEL="專業級";
void swim(double x);
}
interface Singer {
String LEVEL="專業級";
void sing(String song);
}
interface Father {
String LEVEL="新手級";
void takeCare();
}
class Human extends Animal implements Swimmer, Singer, Father {
String name;
Human(String name, int age, double w) {
super(age, w);
this.name=name;
}
void eat(double x) {
System.out.println(name+"咕嚕咕嚕吃下了"+x+"公斤的食物.");
w+=x;
}
void showProfile() {
System.out.println(name+"今年"+age+"歲,體重"+w+"公斤.");
}
public void swim(double x) {
System.out.println(name+"以"+Swimmer.LEVEL+"水準,刷刷刷快速游了"+x+"公尺.");
}
public void sing(String song) {
System.out.println(name+"以"+Singer.LEVEL+"水準,唱了一首"+song+".");
}
public void takeCare() {
System.out.println(name+"以"+Father.LEVEL+"水準,開始扮演父親的角色,照顧小孩.");
} }
依據下列設計導引,完成第二個練習:
1. 定義抽象類別 Animal,該類別包含了 兩個屬性成員:age 與 w
建構子
兩個抽象方法:eat(double x) 與 showProfile()
2. 定義一般類別 Human 繼承 Animal,在該類別中新增 一個屬性成員:name
建構子
實作自父類別繼承下來的兩個抽象方法:eat(double x) 與 showProfile() 3. 定義三個介面:Swimmer、Singer、Father
分別包含了常數變數 LEVEL="專業級" 或 "新手級" 等 以及 swim(double x)、sing(String song)、takeCare() 等抽象方法
4. 定義一個介面:Genius 多重繼承 Swimmer、Singer、Father 這三個介面 在該介面中新增一抽象方法 fly()
5. 讓類別 Human 實作介面 Genius
並實作所有抽象方法:swim(double x)、sing(String song)、takeCare()、fly() 6. 在 Human 類別下新增一物件,並利用該物件帶出所有方法,如執行畫面所示
範例 09_介面_02 public class Ch02 {
public static void main(String[] args) { Human h1=new Human("湯尼",35,70);
h1.showProfile();
h1.eat(0.85);
h1.showProfile();
h1.swim(1500);
h1.sing("新不了情");
h1.takeCare();
h1.fly();
} }
abstract class Animal {
int age;
double w;
Animal(int age, double w)
{
this.age=age;
this.w=w;
}
abstract void eat(double x);
abstract void showProfile();
}
interface Swimmer {
String LEVEL="專業級";
void swim(double x);
}
interface Singer {
String LEVEL="專業級";
void sing(String song);
}
interface Father {
String LEVEL="新手級";
void takeCare();
}
interface Genius extends Swimmer, Singer, Father {
String LEVEL="大神級";
void fly();
}
class Human extends Animal implements Genius {
String name;
Human(String name, int age, double w) {
super(age, w);
this.name=name;
}
void eat(double x) {
System.out.println(name+"咕嚕咕嚕吃下了"+x+"公斤的食物.");
w+=x*0.7;
}
void showProfile() {
System.out.println(name+"今年"+age+"歲,體重"+w+"公斤.");
}
public void swim(double x) {
System.out.println(name+"以"+Swimmer.LEVEL+"水準,刷刷刷快速游了"+x+"公尺.");
}
public void sing(String song) {
System.out.println(name+"以"+Singer.LEVEL+"水準,唱了一首"+song+".");
}
public void takeCare() {
System.out.println(name+"以"+Father.LEVEL+"水準,開始扮演父親的角色,照顧小孩.");
}
public void fly() {
System.out.println(name+"以"+Genius.LEVEL+"水準,快速揮動兩片扇子,飛了起來!");
} }
主題 10:多型 (Polymorphism)
多型 (Polymorphism) 簡單地說,就是運用類別間繼承的關係,使父類別可以當成子類別的通用型態。
多型代表能夠在執行階段,物件能夠依照不同情況變換資料型態,換句話說,多型是指一個物件參考 可以在不同環境下,扮演不同角色的特性,指向不同的物件實體,可透過實作多個繼承或介面來實現 父類別,並使用 Override 或 Overload 來達成。
透過多型的設計手法,我們可以使用同一個操作介面,操作不同的物件實例。多型操作在物件導向上 是為了降低對操作介面的依賴程度,進而增加程式架構的彈性與可維護性。
綜合上述,多型的運作模式可歸納如下:
1. 利用父類別的型態 2. 接受子類別的物件 3. 做相同的動作 4. 引發不同的行為
範例 10_多型_01 public class Ch01 {
public static void main(String args[]) {
Animal a;
a=new Dog("小狗"); //用父類別的型態接收子類別的物件,使父類別成為統一的操作介面,操作不同的
物件實例
System.out.println(a.getName()+": "+a.makeSound());
a=new Cat("小貓");
System.out.println(a.getName()+": "+a.makeSound());
a=new Pig("小豬");
System.out.println(a.getName()+": "+a.makeSound());
} }
class Animal {
String name;
Animal(String name) {
this.name=name;
}
String getName() {
return name;
}
String makeSound() {
return "";
} }
class Dog extends Animal {
Dog(String name) {
super(name);
}
String makeSound() {
return "汪汪汪";
} }
class Cat extends Animal {
Cat(String name) {
super(name);
}
String makeSound() {
return "喵喵喵";
} }
class Pig extends Animal {
Pig(String name) {
super(name);
}
String makeSound() {
return "齁齁齁";
} }
範例 10_多型_02 public class Ch02 {
public static void main(String[] args){
Dog d=new Dog("小狗");
Cat c=new Cat("小貓");
Pig p=new Pig("小豬");
show(d);
show(c);
show(p);
}
//用父類別的型態接收子類別的物件,使父類別成為統一的操作介面,操作不同的物件實例
public static void show(Animal a) {
System.out.println(a.getName()+": "+a.makeSound());
} }
class Animal {
String name;
Animal(String name)
{
this.name=name;
}
String getName() {
return name;
}
String makeSound() {
return "";
} }
class Dog extends Animal {
Dog(String name) {
super(name);
}
String makeSound() {
return "汪汪汪";
} }
class Cat extends Animal {
Cat(String name) {
super(name);
}
String makeSound() {
return "喵喵喵";
} }
class Pig extends Animal {
Pig(String name) {
super(name);
}
String makeSound() {
return "齁齁齁";
} }
主題 11:泛型 (Generics)
泛型(Generics)是現代程式語言提供的一種功能,它使得程式設計師不必撰寫功能相同但資料型態 不同的程式。
譬如,下面的幾個方法目的都只是在回傳中間的參數,邏輯其實都相同就只是當中所涉及的型態不一 樣,若沒有運用泛型語法,我們可能會使用方法多載(Overload)的特性寫了如下的三個方法。雖然
只需拷貝方法的內容,或修改變數宣告的資料型態,但這類的工作相當乏味且浪費時間。
String mid(String a, String b, String c) {
return b;
}
int mid(int a, int b, int c) {
return b;
}
float mid(float a, float b, float c) {
return b;
}
如果程式允許一種資料型態在撰寫程式時不需要考量真實的資料型態,而能夠在呼叫時才決定真實的 資料型態,那就可以省略上述的工作,而程式碼可改寫如下:
<T> T mid(T a, T b, T c) {
return b;
}
在上列程式碼中,我們使用角括號 <T> 來宣告一個型態持有者,名稱為 T(Type 的縮寫),之後就 可以用 T 這個名稱作為型態,來宣告成員、參數或返回值的型態。
由於 Java 中所有定義的類別,都以 Object 為最上層的父類別,在 J2SE 5.0 之前,程式設計師使用 Object 定義類別以解決類似的需求。為了讓定義出來的類別可以更加通用(Generic),傳入的值或傳 回的實例都是以 Object 型態為主,當要取出這些實例來使用時,必須記得將之轉換為原來的類型或 適當的介面,如此才可以操作物件上的方法。
然而使用 Object 來撰寫泛型類別(Generic class)留下了一個問題,就是必須要轉換介面,粗心的程 式設計師往往忘了作這個動作,或是在轉換介面時用錯了型態(像是該用 Boolean 卻用了 Integer), 要命的是由於語法上沒問題,所以編譯器檢查不出錯誤,而在執行時才會跳出惱人的
ClassCastException。
在 J2SE 5.0 之後,Java 提出了針對泛型(Generics)設計的解決方案,要定義一個泛型類別變得更加 容易。
泛型解決的不只是讓我們少寫幾個類別的程式碼,還在於定義「安全的」泛型類別。與單純使用 Object 宣告型態所不同的地方在於,使用泛型所定義的類別在宣告及配置物件時,可以使用角括號一併指定 泛型類別型態持有者 T 真正的型態,我們不會因為將物件置入某個容器(Container)而失去其型態,
而在使用泛型類別時多了一層安全性,可以避免惱人的 ClassCastException,讓編譯器作我們的第一層 防線。
關於泛型的幾個重點歸納如下:
1. 泛型名稱可以自行決定,將來可以使用任何的資料型態替換。
2. 若使用 extends 限定類別名,則將來只能用限定類別名的子孫類別替換。
3. 若未使用 extends 限定類別名,則相當於 extends Object,因為任何型別都是 Object 的子孫類別。
4. 可同時宣告多個型態持有者(Holder),譬如 <T1,T2 extends Number> 。其中 T1 將來可以是任何 的資料型態,但 T2 只能是 Number 類別的子孫類別型態,如 Integer、Double、Float 等。
5. 由於 Java 對於泛型採用的是擦拭法,泛型名稱經擦拭後的結果為 Object,而 Object 無法進行 +-*/
及比較運算。因此當方法內容牽涉到 +-*/ 及比較運算時,無法使用泛型語法。
範例 11_泛型_01_泛型方法 public class Ch01 {
public static void main(String[] args) {
System.out.println("中間的是: "+MyClass.mid("哈囉",123,4.5));
System.out.println("中間的是: "+MyClass.mid(1.23,"你好",123));
} }
class MyClass {
static <T> T mid(T a, T b, T c) {
return b;
} }
範例 11_泛型_02_泛型類別 public class Ch02 {
public static void main(String[] args) { MyClass<String> obj1=new MyClass<String>();
MyClass<Float> obj2=new MyClass<Float>();
System.out.println("中間的是: "+obj1.mid("哈囉", "123","4.5"));
System.out.println("中間的是: "+obj2.mid(1.23f,4.5f,2.3f));
} }
class MyClass<T>
{
T mid(T a, T b, T c) {
return b;
} }
範例 11_泛型_03_比較運算測試 public class Ch03 {
public static void main(String[] args) { //MyClass<Float> obj=new MyClass<Float>();
MyClass obj=new MyClass();
System.out.println("較大的是: "+obj.bigger(8.23f,4.5f));
} }
//class MyClass<T>
class MyClass {
//T bigger(T a, T b)
float bigger(float a, float b) {
//Java對於泛型採用擦拭法,泛型名稱經擦拭後的結果為物件,而物件無法進行比較運算 if(a>b)
return a;
else
return b;
} }
範例 11_泛型_04_限定的泛型 public class Ch04 {
public static void main(String args[]) {
//宣告與產生物件實體時,同時指定泛型的真實資料型態
MyClass<String,Integer> obj=new MyClass<String,Integer>();
System.out.println("在中間的是: "+obj.mid("哈囉","你好","!"));
System.out.println("在中間的是: "+obj.mid(1,2,3));
//System.out.println("在中間的是: "+obj.mid(1,2,"你好"));
} }
//宣告兩個泛型名稱,T1將來可以是任何的資料型態,但T2只能是Number類別的子孫類別型態,如Integer、Float等 class MyClass<T1,T2 extends Number>
{
T1 mid(T1 a, T1 b, T1 c) {
return b;
}
T2 mid(T2 a, T2 b, T2 c) {
return b;
} }
主題 12:集合 (Collection)
Collection 可以翻譯為集合,是將多個元素組織為一個單元的抽象設計方式。
Collection 有時也被稱為集合物件或容器,指一群相關聯的資料集合在一起組成一個物件。而集合物 件裡的資料,稱為元素。我們可以運用集合物件儲存、取用或操作資料(新增、刪除、排序等),或 將資料從一個方法傳遞到另一個方法。
至於要使用什麼樣的容器則依設計需求而定,可以使用循序有索引的 List 結構、不允許重複元素的 Set 結構、或是「鍵-值」(Key-Value)索引的 Map 結構來儲存資料。
Java Collections Framework 主要分為兩大繼承族譜,一類為 Collection 介面的子孫介面,另一類為 Map 介面的子孫介面。其中,Collection 介面一系代表的是單一元素系,而 Map 介面一系代表的是 Key-Value 的成對(pair)元素系。
首先介紹幾個主要介面的特性:
- Collection 介面
可以將此介面視為單一元素的集合,並未規定元素在集合內是否允許重複,也未規定是否具有順序,
進階的規定交由繼承的子介面來規範。
-Set 介面
其內不能有重複的元素,但元素的順序未加以規定,類似於數學上的集合。
-SortedSet 介面
繼承自 Set 介面,並且要求其內的元素會自動遞增排序,譬如英漢字典裡的單字排序就符合此介面 的規範。
-List 介面
其內可以有重複的元素,且元素在集合裡是有順序性的(物件加入容器的順序),每個元素都有其對 應的位置(數值的索引),可以透過位置來存取元素,譬如陣列(Array)就符合此介面的規範。
-Queue 介面
其內元素的取出具有一定的順序,即符合先進先出(first-in first-out)的原則。
-Map 介面
此介面為成對(pair)之集合,每個元素由兩個值組成,一個為 key 另一個為 value。此外,key 值是 不能重複的,且每個 key 只能映射(mapping)到一個 value,但每個 value 則不限定幾個 key 來對應。
每個元素必須同時有 key 與 value,成對表現才具意義。
-SortedMap 介面
繼承自 Map 介面,規定其內的元素必須按照 key 值自動排序。譬如成績單若依學號來排序,即符合 此介面的規範。
接下來介紹幾個常見的集合介面實作:
-HashSet 類別(Set 介面的實作)
HashSet 在實作時,採用資料結構中雜湊表(Hash Table)的方式來完成,元素的順序與存入時的順序 無關。HashSet 根據湊雜碼來確定元素於容器中儲存的位置,也根據雜湊碼來快速的找到容器中的元 素,在大多數情況下這樣的方式可以有效提升搜尋、新增、刪除元素的速度。
-TreeSet 類別(Set 介面或 SortedSet 的實作)
TreeSet 使用的是紅黑樹(Red-black tree)的資料結構來實作。每次有資料加進去,就會對資料作排序
(遞增)。
-ArrayList 類別(List 介面的實作)
ArrayList 使用陣列結構來實作,元素加入時是用索引值(index)依序儲存。陣列的特性是依據索引 來快速指定物件的位置,所以對於快速的隨機取得物件來說,使用 ArrayList 可以得到較好的效能,
但由於使用陣列實作,若要從中間作移除或插入物件的動作,會需要搬動後段的陣列元素以重新調整 索引順序,所以速度上就會慢的多。
-LinkedList 類別(List 介面與 Deque 介面的實作)
LinkedList 使用鏈結串列(Linked list)的資料結構來實作。鏈結串列是由許多節點(node)所構成的 串列(list),每個節點包含資料欄位與鏈結欄位,透過鏈結欄位指向下一個節點的位址,至於最後一 個節點的鏈結欄位則指向 null,代表串列已經結束。
鏈結串列在刪除或插入節點時,是透過改變鏈結欄位的方式達成,不必如同陣列般進行資料的大量搬 移,因此如果元素在加入之後大都是為了取出,而不會常作移除或插入的動作,建議使用 ArrayList 效 能上會比較好;但如果需要經常從容器中作移除或插入元素的動作,則使用 LinkedList 會獲得較好 的效能。
-HashMap 類別(Map 介面的實作)
HashMap 使用雜湊表結構來實作,儲存的元素分為 key 值與 value 值,形成一個 key-value pair,使 我們能依據 key 值快速查找到對應的資料。
-TreeMap 類別(Map 介面與 SortedMap 介面的實作)
TreeMap 使用紅黑樹結構來實作,儲存的元素同樣分為 key 值與 value 值,元素會依據 key 值由小 至大排序。
範例 12_集合_01_HashSet import java.util.HashSet;
public class Ch01 {
public static void main(String[] args) {
HashSet<String> color=new HashSet<String>(); //宣告一名為 color 的 HashSet 集合物件, 集合內元素 的型態為 String
String str1="green",str2="yellow";
System.out.println("color empty: "+color.isEmpty());
color.add("blue"); //新增元素 color.add("red");
color.add(str1);
color.add(str2);
System.out.println("新增重複元素: "+color.add("blue")); //若成功會回傳true System.out.println("color empty: "+color.isEmpty()); //集合是否為空
System.out.println("color size: "+color.size()); //集合大小 (元素數量) System.out.println("color 內容: "+color); //集合內容
color.remove(str1); //移除元素 System.out.println("color size: "+color.size());
System.out.println("color 內容: "+color);
System.out.println("color 中是否有 str1: "+color.contains(str1)); //是否包含元素 System.out.println("color 中是否有 blue: "+color.contains("blue"));
color.add("pink");
System.out.println("color 內容: "+color);
color.clear(); //清除集合中所有元素 System.out.println("color empty: "+color.isEmpty());
System.out.println("color 內容: "+color);
System.out.println("color size: "+color.size());
} }
範例 12_集合_02_TreeSet import java.util.TreeSet;
public class Ch02 {
public static void main(String[] args) {
TreeSet<Integer> n=new TreeSet<Integer>();
for(int i=20; i>=2; i-=2) n.add(i);
System.out.println("元素個數: "+n.size());
System.out.println("集合內容: "+n);
System.out.println("第一個元素: "+n.first());
System.out.println("最後一個元素: "+n.last());
System.out.println("介於6和14之間的集合: "+n.subSet(6, 14));
System.out.println("大於等於10的集合: "+n.tailSet(10));
System.out.println("小於8的集合: "+n.headSet(8));
} }
範例 12_集合_03_ArrayList import java.util.ArrayList;
public class Ch03 {
public static void main(String[] args) {
ArrayList<Integer> n=new ArrayList<Integer>(); //可將 ArrayList 集合視為可變更大小的動態陣列 for(int i=20; i>=2; i-=2)
n.add(i);
n.add(40);
n.add(60); //以上所加入的元素將依據加入的順序排列 n.add(0, 80); //指定索引值為 0 加入元素
n.add(1, 100);
System.out.println("元素個數: "+n.size());
System.out.println("集合內容: "+n);
n.add(1, 150); //插入點後的所有元素其索引值都會改變 System.out.println("集合內容: "+n);
n.set(2, 10); //將索引值 2 的元素以 10 取代 System.out.println("集合內容: "+n);
n.remove(3); //移除索引值為 3 的元素 System.out.println("集合內容: "+n);
System.out.println(n.indexOf(10)); //第一個元素值為 10 的索引值
System.out.println(n.lastIndexOf(10)); //最後一個元素值為 10 的索引值 (由後往前搜尋第一個)
System.out.println(n.get(1)); //集合中索引值為 1 的元素 }
}
範例 12_集合_04_LinkedList import java.util.LinkedList;
public class Ch04 {
public static void main(String[] args) {
//LinedList 和 ArrayList 其實很像,多了處理 LinkedList<Integer> n=new LinkedList<Integer>();
for(int i=20; i>=2; i-=2) //元素將以加入的順序排列 n.add(i);
n.addFirst(10); //加入一個元素在鏈結串列的最前方 n.addLast(10); //加入一個元素在鏈結串列的最後方 n.addFirst(100);
System.out.println("元素個數: "+n.size());
System.out.println("集合內容: "+n);
n.removeFirstOccurrence(10); //移除第一個值為 10 的元素 n.removeLastOccurrence(10); //移除最後一個值為 10 的元素 System.out.println("集合內容: "+n);
n.removeFirst(); //移除鏈結串列中最前方的元素
n.removeLast(); //移除鏈結串列中最後方的元素 System.out.println("集合內容: "+n);
System.out.println(n.get(1)); //鏈結串列中索引值為 1 的元素 System.out.println(n.getFirst()); //鏈結串列中最前方的元素
System.out.println(n.getLast()); //鏈結串列中最後方的元素
System.out.println(n.indexOf(18)); //第一個元素值為 18 的索引值
System.out.println(n.lastIndexOf(18)); //最後一個元素值為 18 的索引值 (由後往前搜尋第一個)
System.out.println(n.indexOf(30)); //若回傳 -1 代表找不到該元素 }
}
範例 12_集合_05_HashMap import java.util.HashMap;
import java.util.Set;
import java.util.Collection;
public class Ch05 {
public static void main(String[] args) {
HashMap<Integer,String> hm=new HashMap<Integer,String>();
hm.put(92001, "Ben");
hm.put(92002, "Peter");
hm.put(92003, "Tony");
System.out.println("HashMap 是否為空: "+hm.isEmpty());
System.out.println("元素個數: "+hm.size());
System.out.println("HashMap 的內容: "+hm);
System.out.println("HashMap 中是否有關鍵值為 92001 的元素: "+hm.containsKey(92001));
System.out.println("HashMap 中是否有對應值為 Kevin 的元素: "+hm.containsValue("Kevin"));
hm.put(92001, "John"); //覆蓋舊值
System.out.println("HashMap 的內容: "+hm);
hm.remove(92002); //移除關鍵值為 92002 的元素 System.out.println("HashMap 的內容: "+hm);
System.out.println("關鍵值 92003 的對應值: "+hm.get(92003));
Set<Integer> id=hm.keySet(); //將各元素的 K 欄位集合轉換為 Set 物件 System.out.println("ID 集合內容為: "+id);
Collection<String> name=hm.values(); //將各元素的 V 欄位集合轉換為 Collection 物件 System.out.println("姓名集合內容為: "+name);
} }
範例 12_集合_06_TreeMap import java.util.TreeMap;
public class Ch06 {
public static void main(String[] args) {
TreeMap<Integer,String> tm=new TreeMap<Integer,String>();
tm.put(92001, "Ben");
tm.put(92002, "Peter");
tm.put(92003, "Tony");
System.out.println("TreeMap 是否為空: "+tm.isEmpty());
System.out.println("元素個數: "+tm.size());
System.out.println("TreeMap 的內容: "+tm); //依 key 值遞增排序
System.out.println("TreeMap 中是否有關鍵值為 92001 的元素: "+tm.containsKey(92001));
System.out.println("TreeMap 中是否有對應值為 Kevin 的元素: "+tm.containsValue("Kevin"));
System.out.println("第一個元素為: "+tm.firstKey()+" "+tm.get(tm.firstKey()));
System.out.println("最後一個元素為: "+tm.lastKey()+" "+tm.get(tm.lastKey()));
System.out.println("關鍵值大於或等於 92002 的元素: "+tm.tailMap(92002));
System.out.println("關鍵值大於或等於 92002 且小於 92003 的元素: "+tm.subMap(92002,92003));
} }
再接著我們介紹 Iterator 與 ListIterator 介面:
Iterator 與 ListIterator 介面,可用來「走訪」或是刪除集合物件的元素。Iterator 物件的讀取是單向的,
且只能讀取一次;而 ListIterator 物件的走訪可以是雙向的(正向、反向)。
範例 12_集合_07_Iterator 介面 import java.util.TreeSet;
import java.util.Iterator;
public class Ch07 {
public static void main(String[] args) {
TreeSet<Integer> ts=new TreeSet<Integer>();
for(int i=20; i>=2; i-=2) ts.add(i);
System.out.println("元素個數: "+ts.size());
System.out.println("集合內容: "+ts);
Iterator<Integer> itr=ts.iterator(); //將 TreeSet 集合物件轉換為 Iterator 物件 System.out.print("正向走訪: "); //只能讀取一次
while(itr.hasNext()) //若仍有下個元素則回傳 true System.out.print(itr.next()+" "); //回傳下個元素 System.out.println();
itr.remove(); //移除最後讀取的元素 System.out.println("集合內容: "+ts);
} }
範例 12_集合_08_ListIterator 介面 import java.util.LinkedList;
import java.util.ListIterator;
public class Ch08 {
public static void main(String[] args) {
LinkedList<Integer> ts=new LinkedList<Integer>();
for(int i=20; i>=2; i-=2) ts.add(i);
System.out.println("元素個數: "+ts.size());
System.out.println("集合內容: "+ts);
ListIterator<Integer> itr1=ts.listIterator(); //將 LinkedList 集合物件轉換為 ListIterator 物件
ListIterator<Integer> itr2=ts.listIterator(ts.size()); //將 LinkedList 集合物件轉換為 ListIterator 物件, 並指 定指標位置(介於元素之間)
System.out.print("正向走訪: "); //只能讀取一次 while(itr1.hasNext()) //若仍有下個元素則回傳 true
System.out.print(itr1.next()+" "); //回傳下個元素 System.out.println();
System.out.print("反向走訪: "); //同樣只能讀取一次 while(itr2.hasPrevious()) //若仍有下個元素則回傳 true
System.out.print(itr2.previous()+" "); //回傳下個元素 System.out.println();
itr2.remove(); //移除最後讀取的元素 System.out.println("集合內容: "+ts);
} }
最後介紹 Collections 類別:
Collections 類別是 Java Collections Framework 中專門負責演算法的類別。該類別中提供了多個有用的 static 方法,較常用的有 binarySearch()、copy()、max()、min()、reverse()、sort()、swap() 等。
範例 12_集合_09_Collections 類別 import java.util.Collections;
import java.util.HashSet;
public class Ch09 {
public static void main(String[] args) {
HashSet<Integer> hs=new HashSet<Integer>();
hs.add(11);
hs.add(8);
hs.add(2);
hs.add(33);
hs.add(29);
System.out.println("集合內容: "+hs);
System.out.println("集合中最大的數: "+Collections.max(hs));
System.out.println("集合中最小的數: "+Collections.min(hs));
//Collections.sort(hs); //包含 sort(),reverse() 等多數 Collections 類別所提供的方法只適用 List 介面 的實作
} }
範例 12_集合_10_其他應用 1 import java.util.HashSet;
import java.util.TreeSet;
import java.util.LinkedList;
import java.util.Collections;
public class Ch10 {
public static void main(String[] args) {
HashSet<Integer> hs=new HashSet<Integer>();
//利用 HashSet 元素不得重複的特性, 產生不重複之隨機亂數 while(hs.size()<6) //當元素數量達到 6 時, 停止加入元素 {
//System.out.println(hs.add((int)(Math.random()*39+1)));
//若出現重複則會造成元素加入失敗, 並回傳 false hs.add((int)(Math.random()*39+1)); //1~39
}
System.out.println("本期樂透號碼: "+hs);
TreeSet<Integer> ts=new TreeSet<Integer>(hs); //將 HashSet 集合物件, 轉換為 TreeSet 集合物件 //TreeSet<Integer> ts=new TreeSet<Integer>();
//ts.addAll(hs);
System.out.println("本期樂透號碼: "+ts);
LinkedList<Integer> ll=new LinkedList<Integer>(hs); //將 HashSet 集合物件, 轉換為 LinkedList 集合物 件
System.out.println("本期樂透號碼: "+ll);
Collections.sort(ll);
System.out.println("本期樂透號碼: "+ll);
Collections.reverse(ll);
System.out.println("本期樂透號碼: "+ll);
} }
範例 12_集合_11_其他應用 2 //import java.util.Collections;
import java.util.LinkedList;
import java.util.ListIterator;
public class Ch11 {
public static void main(String[] args) { Stu stu1=new Stu(92001,"Tony",95);
Stu stu2=new Stu(92002,"Ben",80);
Stu stu3=new Stu(92003,"Peter",85);
Stu stu4=new Stu(92004,"May",75);
LinkedList<Stu> ll=new LinkedList<Stu>(); //元素為 Stu 物件 ll.add(stu1);
ll.add(stu2);
ll.add(stu3);
ll.add(stu4);
//System.out.println("集合內容: "+ll); //無法顯示 System.out.println("使用 for 迴圈正向走訪:");
for(Stu s:ll)
s.showProfile();
System.out.println();
ListIterator<Stu> itr=ll.listIterator();
System.out.println("使用 ListIterator 介面正向走訪:");
while(itr.hasNext()) {
itr.next().showProfile(); //itr.next() 所取出的元素為物件 }
//Collections.sort(ll); //集合內容為物件, 無法排序 }
} class Stu {
int id,score;
String name;
Stu(int id, String name, int score) {
this.id=id;
this.name=name;
this.score=score;
}
void showProfile() {
System.out.println("學號: "+id+"\t姓名: "+name+"\t成績: "+score);
} }
範例 12_集合_12_其他應用 3 import java.util.HashSet;
import java.util.HashMap;
public class Ch12 {
public static void main(String[] args) { int n[]={23,45,73,22,45};
for(int i:n) //for 迴圈針對 Collection 或 Array 的特殊寫法 System.out.print(i+" ");
System.out.println();
HashSet<String> hs=new HashSet<String>();
hs.add("藍");
hs.add("紅");
hs.add("綠");
hs.add("黃");
for(String str:hs)
System.out.print(str+" ");
System.out.println();
HashMap<Integer,String> hm1=new HashMap<Integer,String>();
hm1.put(92001, "Blue");
hm1.put(92002, "Red");
hm1.put(92003, "Green");
hm1.put(92004, "Pink");
for(String str:hm1.values()) //利用values()方法取得集合中 V 欄位的值 System.out.print(str+" ");
System.out.println();
Product p1=new Product("香雞排",45);
Product p2=new Product("珍珠奶茶",50);
Product p3=new Product("汽水",20);
HashMap<Integer,Product> hm2=new HashMap<Integer,Product>();
hm2.put(92001, p1);
hm2.put(92002, p2);
hm2.put(92003, p3);
for(Product p:hm2.values()) //走訪中每次要帶出來的為 Product 類別的物件 System.out.println(p.getName()+" "+p.getPrice());
} }
class Product {
String name;
int price;
Product(String n, int p) {
name=n;
price=p;
}
String getName() {
return name;
}
int getPrice() {
return price;
} }
主題 13:例外 (Exception)
所謂「例外」,就是當程式碼編輯完,在編譯期間沒有出現錯誤訊息,但在程式執行時卻發生錯誤,
這種錯誤又稱為執行時期錯誤(Runtime error)。
在編譯程式或執行程式時常會遇到各種不同錯誤,以致無法正常完成工作。研發軟體時最容易遇到三 種錯誤(Bug):語法錯誤、執行時期錯誤、邏輯錯誤。
1. 語法錯誤
語法錯誤是初學者最容易犯的錯誤。在編譯過程中,系統常能立即指出此種錯誤的所在,並要求程式
設計者修正後才能正式執行。這樣錯誤最容易解決,只要熟悉語法多多練習就可以減少錯誤產生。
2. 執行時期錯誤
程式在執行時,因為輸入資料不符合、在計算過程分母為 0、磁碟中無此檔案存在、陣列的索引值超 出陣列宣告範圍…等,使得程式中斷執行。這種錯誤的問題在編譯時,並不會發生,被 Java 稱為「例 外」,而 Java 也提供了例外處理的方式來解決問題。
3. 邏輯錯誤
邏輯錯誤是最難找出的,尤其在大型應用程式最為明顯。程式在執行過程並沒有出現錯誤,也會有執 行結果,甚至有時候結果是正確的。除非你仔細觀察,多人多次測試,否則不見得會發現。因此誤信 其執行結果,往往造成很大損失。有些系統提供偵錯(Debug)工具,用來協助找出錯誤之處。若沒 有偵錯工具,就只能自己設定偵測點,輸出目前主要變數內容是否如預測結果,以推測可能錯誤之處,
再仔細研讀程式,尋找邏輯上錯誤之處,加以修正。
範例 13_例外_01 import java.util.Scanner;
public class Ch01 {
static Scanner s=new Scanner(System.in);
public static void main(String[] args) {
int x,y;
System.out.print("輸入分子: ");
x=s.nextInt();
System.out.print("輸入分母: ");
y=s.nextInt();
System.out.println(x+"/"+y+"="+(x/y));
} }
除了捕捉 Java 拋出的例外物件,還可以利用關鍵字 throw 自行拋出例外物件。而若拋出的例外物件非 系統能自行捕捉到,譬如自訂類別的例外,則需在方法宣告列後面利用關鍵字 throws 註明例外類別名 稱,以便在指定的方法中拋出例外。
範例 13_例外_02_自訂例外類別 import java.util.Scanner;
public class Ch02 {
static Scanner s=new Scanner(System.in);
//若拋出的例外物件非系統能自行捕捉到,譬如自訂類別的例外物件,需在此註明 public static void main(String[] args) throws MyException
{ int x,y;
System.out.print("輸入分子: ");
x=s.nextInt();
System.out.print("輸入分母: ");
y=s.nextInt();
if(y==0)
throw new MyException("嘿嘿嘿~分母不可為零喔!");
System.out.println(x+"/"+y+"="+(x/y));
} }
class MyException extends Exception {
MyException(String str) {
super(str); //呼叫父類別的建構子 }
}
進行例外處理是不希望程式中斷。而是希望程式能捕捉錯誤並繼續執行,若錯誤是使用者輸入不正確 資料所造成的,可以要求使用者輸入正確資料再繼續執行,或者不處理使用者輸入資料繼續做其他工 作。
Java 使用 try … catch … finally 敘述來解決例外處理,它的方式是將被監視的敘述區段寫在 try 大括 號內,當程式執行到 try 內的敘述有發生錯誤時,會逐一檢查捕捉(catch)該錯誤,以便執行該 catch 內敘述。最後不管是否有符合 catch,都會執行最後的 finally 敘述區段。例外處理的格式如下:
try {
預期可能發生例外的敘述 }
catch(例外物件) {
對應的處理程序 }
finally //可有可無 {
無論例外是否發生都會處理的程序 }
範例 13_例外_03_例外處理 1 import java.util.*;
public class Ch03 {
public static void main(String args[]) {
for(int i=1; i<=3; i++) //允許三次嘗試 {
try //可能發生例外的程式區塊
{ int a, b;
Scanner s=new Scanner(System.in);
System.out.print("請輸入分子: ");
a=s.nextInt();
System.out.print("請輸入分母: ");
b=s.nextInt();
System.out.println(a+"/"+b+"="+(a/b));
break; //若運算成功,即跳出迴圈 }
catch(ArithmeticException e) //針對捕捉到的例外物件,作相對應的回應 {
System.out.println("運算錯誤! 分母不可為零!");
}
catch(InputMismatchException e) {
System.out.println("格式錯誤! 輸入須為整數!");
}
System.out.println();
if(i==3)
System.out.println("錯誤嘗試過多! 程式跳出!");
} } }
範例 13_例外_04_例外處理 2 import java.util.Scanner;
public class Ch04 {
static Scanner s=new Scanner(System.in);
//因有使用 catch 語法捕捉自訂類別的例外物件,故 throws 敘述便可省略 public static void main(String[] args) //throws MyException
{ try
{
int x,y;
System.out.print("輸入分子: ");
x=s.nextInt();
System.out.print("輸入分母: ");
y=s.nextInt();
if(y==0)
throw new MyException();
System.out.println(x+"/"+y+"="+(x/y));
}catch(MyException e) {
System.out.println("嘿嘿嘿~分母不可為零喔!");
} } }
class MyException extends Exception {
MyException() {
super(); //呼叫父類別的建構子 }
}