介紹

這一篇是 Java 基本語法介紹的第三篇,我們要來繼續探討程式碼架構。

Vector 類別

Vector 是一種可以自動增減長度的「動態陣列」,屬於 java.util 套件的一部分,它的概念類似於 ArrayList,但 Vector 是執行緒安全 (synchronized) 的版本,因此在多執行緒環境中使用更安全,但效能稍慢一些 。

與 ArrayList 相比的主要差異是 Vector 所有操作方法都加上同步鎖定 (synchronized)。

常用的方法統整:

方法 功能說明
add(E e) 在尾端新增元素
add(int index, E e) 在指定位置加入元素
get(int index) 取得指定位置的元素
remove(int index) 移除指定位置的元素
size() 回傳目前元素個數
capacity() 回傳目前容量大小
clear() 清空所有內容
contains(Object o) 檢查是否包含指定元素
iterator() 回傳用於遍歷的 Iterator

範例:

1
2
3
4
5
6
7
8
9
Vector<String> fruits = new Vector<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Mango");

System.out.println(fruits); // [Apple, Banana, Mango]
System.out.println(fruits.get(1)); // Banana
fruits.remove("Apple");
System.out.println(fruits.size()); // 2

例外

例外處理

我們在執行程式的過程中,有時會有發生錯誤的時候,這時我們可以使用例外處理機制來攔截執行期間的錯誤。
以下是常見的例外 (Exception):

錯誤代碼 意思
ArrayIndexOutOfBoundsException 陣列索引值超出範圍
ArithmeticException 運算時產生的錯誤,例如除數為 0
ArrayStoreException 指定陣列內容時產生的錯誤
ClassCastException 類別轉換錯誤
ClassNotFoundException 找不到指定的類別
CloneNotSupportException 在類別中使用 clone(),但該類別尚未實作 Cloneable 介面
FileNotFoundException 找不到指定檔案
InterruptedException 另一個執行緒試圖使用 Interrupt() 來中斷已停止的執行緒
IOException 檔案、網路輸入輸出產生的錯誤
IllegalArgumentException 呼叫方法時傳遞錯誤的參數
IndexOutOfBoundsException 索引值超出範圍
NullPointerException 使用物件時,該物件的參考值為 null
NumberFormatException 字串轉數字產生的錯誤
SecurityException 違反安全性限制

如何判斷錯誤並執行例外處理:

1
2
3
4
5
6
7
try {
程式執行區塊
}catch (例外名稱){
例外處理的程式碼區塊
}finally{
最後執行的程式區塊
}

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.InputMismatchException;
import java.util.Scanner;

try (Scanner scanner = new Scanner(System.in)) {
System.out.print("請輸入分子數:");
int a = scanner.nextInt();
System.out.print("請輸入分母數:");
int b = scanner.nextInt();
System.out.println(a + "除以" + b + "等於:" + a / b);
} catch (ArithmeticException e) {
System.out.println("錯誤代碼: " + e.getClass().getSimpleName());
System.out.println("除數不能為0");
} catch (InputMismatchException e) {
System.out.println("輸入的數值必須為整數數值");
}

拋出例外 Throw & Throws

程式執行的過程中,所引發的錯誤例外,都是由 Java 的虛擬機 (JVM) 來自動產生錯誤提示的例外。
我們也可以自行產生指定的例外,來提供程式判斷與處理這些例外。

自行產生例外有兩種方式:

  • throw: 在程式中產生一個例外物件。
  • throws: 宣告一個類別的方法,並指定該方法可以產生一個例外物件。

範例:

1
2
3
4
5
6
7
8
try {
String myText = "Hello, World!";
if (myText.equals("Hello, World!")) {
throw new ArithmeticException("自己產生的錯誤例外");
}
} catch (ArithmeticException e) {
System.out.println("捕捉到 ArithmeticException: " + e.getMessage());
}

如果在方法內發生錯誤,但方法內沒有 try ... catch ... 的例外處理,就可以在方法宣告時使用 throws 將例外產生,讓呼叫該方法的那一層來處理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Main {
public int divide(int a, int b) throws ArithmeticException {
return a / b;
}

public static void main(String[] args) {
Main t = new Main();
try {
System.out.println(t.divide(10, 0));
} catch (ArithmeticException ex) {
System.out.println("發生運算錯誤");
}
}
}

自訂例外

Exception 是一個類別。裡面有各種例外物件的類別型態。如果想要建立自己的例外類別,來處理特殊的情況,可以使用繼承 Exception 類別,產生自訂例外類別。
產生自訂例外類別的方法如下:

1
2
3
4
5
// 繼承 Exception 類別
class MyException extends Exception {...}

// 繼承 RuntimeException 類別
class MyException_2 extends RuntimeException {...}

Exception 可用的 Throwable 方法:

方法 功能 回傳值資料型態
fillnStackTrace() 回傳包含完整堆疊追蹤的 Throwable 物件 Throwable
getCause() 回傳造成例外原因的 Throwable 物件 Throwable
getMessage() / getLocalizedMessage() 回傳例外訊息說明 String
getStackTrace() 回傳包含堆疊追蹤的陣列 StackTraceTlement[ ]
initCause(Throwable cause) 將 cause 當作是例外發生的原因 Throwable
printStackTrace() 顯示堆疊追蹤 void
printStackTrace(PrintStream ps) 將堆疊追蹤顯示在 PrintStream 類別串流物件上 void
printStackTrace(PrintWriter pw) 將堆疊追蹤顯示在 PrintWriter 類別串流物件上 void
setStackTrace(StackTraceElement[] ste) 設定堆疊追蹤元素由 getStackTrace 的方法回傳,由 printStackTrace 的方法顯示 void
toString() 回傳簡短的例外描述字串 String
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class UserException extends Exception {
private String errCode; // 自訂例外的錯誤碼
private String errMessage; // 自訂例外的錯誤訊息
// 自訂例外的構造方法,需要傳入錯誤資訊和錯誤碼
public UserException(String errCode) {
this.errCode = errCode;
errMessage="這是一個自訂例外!!";
}
public String getErrCode() { // 獲取自訂例外的錯誤碼
return errCode;
}
public String getMessage() { // 獲取自訂例外的錯誤訊息
return errMessage;
}
}
public class Main {
// 拋出自訂例外
public void throwUserException() throws UserException {
throw new UserException("001");
}
public static void main(String[] args) {
Main test = new Main();
try {
test.throwUserException();
} catch (UserException e) {
System.out.println("錯誤碼:" + e.getErrCode());
System.out.println("錯誤訊息:" + e.getMessage());
}
}
}

Math

Java 將較常使用的數學函式,製作成方法,封裝於 Java.lang 套件中的 Math 類別。屬於 Java 的預設套件,所以 Java 會自動引入該套件。

常數

Math 類別定義了兩個數學常數,分別是自然對數 (E) 與圓周率的 (PI)。

常數名稱 資料類型
E double 2.71828182…
PI double 3.141592653…

方法

方法 功能
random() 隨機數
pow(a, b) 次方
sqrt(a) 平方根
min(a, b) 回傳最小值
max(a, b) 回傳最大值
abs(a) 回傳絕對值
toDegrees(angrad) 回傳弧度的角度
toRadians(angdeg) 回傳角度的弧度
sin(a) 正弦函數
cos(a) 餘弦函數
tan(a) 正切函數
asin(a) 反正弦函數
acos(a) 反餘弦函數
atan(a) 反正切函數
exp(a) 回傳指數
log(a) log
log10(a) log10

使用方式

使用 Math 類別底下的函式,Math.<方法>

1
2
3
double value = Math.pow( 2, 10 ); //計算 2 的 10 次方
System.out.println( "2 的 10 次方 = " + value);
System.out.println( value + "開根號的結果 = "+Math.sqrt(value));

Object

物件的建構

物件是一個參考類型,所以在宣告物件名稱的時候,不會配置實際的記憶體空間。因此,必須要用 new 來建立記憶體空間並將物件指向記憶體位址。

格式如下:

1
類別名稱 物件名稱 = new 建構子();

建構子

建構子是類別內的方法,建構子有兩種型態,分別是有參數與無參數型態。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Car {
private String brand;
private String model;

// 無參數建構子
public Car() {
this.brand = "Toyota";
this.model = "Corolla";
}

// 有參數建構子
public Car(String brand, String model) {
this.brand = brand;
this.model = model;
}

public void displayInfo() {
System.out.println("Brand: " + brand + ", Model: " + model);
}

public static void main(String[] args) {
// 呼叫無參數建構子
Car car1 = new Car();
car1.displayInfo();

// 呼叫有參數建構子
Car car2 = new Car("Honda", "Civic");
car2.displayInfo();
}
}

方法的多載 Overload

在同一個類別中,可以定義多個相同名稱的方法但傳入的參數不同。讓我們可以使用不同的引數組合來呼叫同一功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.raylon;

public class Main {
public void sayHello() {
System.out.println("Hello, World!");
}

public void sayHello(String name) {
System.out.println("Hello, " + name + "!");
}

public void sayHello(String name, int times) {
for (int i = 0; i < times; i++) {
System.out.println("Hello, " + name + "!");
}
}

public void sayHello(int times) {
for (int i = 0; i < times; i++) {
System.out.println("Hello");
}
}

public static void main(String[] args) {
Main obj = new Main();
obj.sayHello();
obj.sayHello("Alice");
obj.sayHello("Bob", 3);
obj.sayHello(2);
}
}

參數的「型態」、「數量」或「順序」必須要有一項不同。

This 指標

this 是自己,代表當前的類別或物件,在方法當中可以使用 this.<屬性名稱/方法名稱> 來呼叫當前類別或物件中的屬性或方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Subject {
int subjectNo; // 課程編號
String chiName,engName; //課程名稱
int credit = 2; //學分數,預設 2 學分
Subject(int subjectNo,String chiName,String engName){
this.subjectNo = subjectNo;
this.chiName = chiName;
this.engName = engName;
}
Subject(int subjectNo,String chiName,String engName, int credit){
this(subjectNo, chiName, engName); // 重用第 5~9 行的建構子
this.credit = credit;
}
void display( ){
System.out.printf( "課程: %d-%s (%s), 學分數:%d \n",
subjectNo, chiName, engName, credit );
}
}
class Main {
public static void main(String args[]){
Subject s1=new Subject(112, "運算思維", "Python");
Subject s2=new Subject(111, "程式設計", "java", 3);
s1.display();
s2.display();
}
}

繼承

透過繼承,子類別建構的物件會有父類別的特性。

繼承的規則

  • 父類別宣告為 final 的屬性或方法,子類別不能覆寫。
  • 父類別宣告為 private 的屬性或方法,子類別無法繼承使用。
  • 父類別宣告為 abstract 的方法,必須要在子類別內實作。

使用方式

語法:

1
2
3
class 子類別名稱 extends 父類別名稱 {
子類別的方法
}

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Compute {
public void times (int x, int y){
System.out.println(x+"*"+y+"="+(x*y));
}
public void divided (int x, int y){
System.out.println(x+"/"+y+"="+(float)x/y);
}
}
class Accounting extends Compute{
public void plus (int x, int y){
System.out.println(x+"+"+y+"="+(x+y));
}
public void minus (int x, int y){
System.out.println(x+"-"+y+"="+(x-y));
}
}
public class Main {
public static void main(String[] args){
Accounting myObj = new Accounting();
myObj.plus(200,30);
myObj.minus(200,30);
myObj.times(200,30);
myObj.divided(200,30);
}
}

建構子的執行順序

會先把父類別建構完畢才會建構子類別。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class TestA{
TestA( ){ System.out.println("這是類別 A");
}
}
class TestB extends TestA{
TestB( ){
System.out.println("這是繼承類別 A 的類別 B");
}
}
class TestC extends TestB{
TestC( ){
System.out.println("這是繼承類別 B 的類別 C");
}
}
public class Main {
public static void main(String[] args){
System.out.println("【單一繼承的建構子執行順序示範:】");
TestB b = new TestB( );
System.out.println("【多重繼承的建構子執行順序示範:】");
TestC c = new TestC( );
}
}

覆寫

如果子類別中的方法名稱、傳遞參數和回傳值類型都與父類別中的方法相同時,就會進行覆寫 (Override)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Main {
public static void main(String[] args){
Plane747 CAL=new Plane747();
CAL.setData(4000.5,"藍色");
CAL.setLane(5);
CAL.display();
}
}
class AirPlane{
double fuel;
String color;
public void setData(double fuel, String color){
this.fuel=fuel;
this.color=color;
}
public void display(){
System.out.println("飛機可裝載燃料數量:"+fuel);
System.out.println("飛機顏色:"+color);
}
}
// 子類別: Plane747
class Plane747 extends AirPlane{
private int airLane;
public void setLane(int airLane){
this.airLane=airLane;
}
// 覆寫父類別的 display() 方法
public void display(){
System.out.println("747飛機可裝載油料 " + fuel + " 公升");
System.out.println("747飛機的顏色是 " + color);
System.out.println("747飛機起飛的跑道是 " + airLane);
}
}

Super 指標

在 Java 中,可以使用 this 代表當前的類別;如果想要使用父類別的方法,就可以使用 super 代表上一層的類別。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Main extends Human{  // 繼承 Human 類別
public static void main(String[] args){
Main crew = new Main( );
crew.setName("張三");
crew.setAge(21);
crew.print();
}
public void print(){
System.out.print("【職員】");
super.print();
}
}
class Human{
private String name;
private int age;
protected void setName(String name){
this.name=name;
}
protected void setAge(int age){
this.age=age;
}
protected void print(){
System.out.printf("姓名:%s, 年齡:%n ", name, age);
}
}

多型

多型 (Polymorphism) 是指同一個物件,在不同的情況下,可以表現出不同的行為。目的是讓程式更有彈性、可擴充性、可維護性。主要是透過抽象的類別繼承與介面的實作,讓不同類型的物件可以實作同一個介面,並根據不同的情況呼叫對應的方法。

抽象類別

抽象類別是指含有抽象方法的類別,抽象方法只是事前先宣告方法名稱、參數與回傳的資料型態,還沒有實作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
abstract class Animal {
String name;
int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
abstract void makeSound();
public void printInfo() {
System.out.println("名字: " + name);
System.out.println("年齡: " + age);
}
}
class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
void makeSound() {
System.out.println("汪汪!");
}
}
class Cat extends Animal {
public Cat(String name, int age) {
super(name, age);
}
void makeSound() {
System.out.println("喵喵!");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog("來福", 3);
Animal myCat = new Cat("來喜", 2);

myDog.makeSound();
myDog.printInfo();

myCat.makeSound();
myCat.printInfo();
}
}

介面

介面 (interface) 是一個完全沒有任何方法被實作的抽象類別。介面是一個類別,裡面所有宣告的方法都必須是抽象的。因此,在繼承類別時必須實作介面中的所有方法。

語法:

1
2
3
4
5
6
7
8
9
10
interface 介面名稱 {
[ 修飾語 ] 資料類型 常數名稱 = 值;
......
[ 修飾語 ] 回傳值類型 介面方法名稱(參數, ...);
......
}

class 類別名稱 implements 介面名稱1, 介面名稱2, ... {
實作介面的方法;
}

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface IPet {
public String attr = "可愛"; // 必須給予初值
void skill( ); // 不能實作程式
void action( ); // 不能實作程式
}
class Puppy implements IPet {
public void skill( ) {
System.out.println( attr+",撒嬌");
}
public void action( ) {
System.out.println("追趕跑跳碰");
}
}
public class Main {
public static void main(String[] args){
Puppy myPet = new Puppy( );
myPet.skill( );
myPet.action( );
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
interface IRequest {
void execute();
}
class HelloRequest implements IRequest {
private String name;
public HelloRequest(String name) {
this.name = name;
}
public void execute() {
System.out.println("您好," + name);
}
}
class WelcomeRequest implements IRequest {
private String place;
public WelcomeRequest(String place) {
this.place = place;
}
public void execute() {
System.out.println("歡迎光臨" + place);
}
}
public class Main {
public static void main(String[] args) {
int n;
for(int i = 0; i < 6; i++) {
n = (int) (Math.random() * 2) +1;
if (n == 1)
doRequest( new HelloRequest("張三"));
else
doRequest(new WelcomeRequest("某大學"));
}
}
public static void doRequest(IRequest request) {
request.execute();
}
}

繼承類別與介面

繼承類別使用 extends;繼承介面使用 implements,同時進行繼承類別與介面時要先宣告 extends 再宣告 implements

語法:

1
2
3
class 類別名稱 extends 父類別名稱 implements 介面名稱1, 介面名稱2, ... {
實作介面的方法;
}

範例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
interface Engine {
public void start( );
public void stop( );
}
class Gearbox {
public void shiftUp( ) {
System.out.println("進檔");
}
public void shiftDown( ) {
System.out.println("退檔");
}
}
class Car extends Gearbox implements Engine {
public void start( ) {
System.out.println("發動引擎");
}
public void stop( ) {
System.out.println("停止引擎");
}
}
public class Main {
public static void main(String[] args) {
Car myCar = new Car( );
myCar.start( );
myCar.shiftUp( );
myCar.shiftDown( );
myCar.stop( );
}
}