2021年6月28日 星期一

JAVA 物件導向的概念-11 覆寫、隱藏父類別中的方法,過載

  本篇故事:愛未未在逃亡的過程中,見證到太多無辜的女人被汙衊為魔女而遭到殺害,這一切逼使著她投入黑暗,於是暗黑愛未未出現了! 她要復仇!

  這一篇要練習的覆寫和隱藏父類別的方法,前陣子很紅的廣告詞:開局就送魔關羽!有魔關羽就有一般的關羽,他們一定極為相似,魔關羽繼承了關羽的大部份屬性,也一定有所不同,魔關羽的技能可能更帶有邪氣!

  本篇我所做的就是使者與暗黑使者兩個類別,其中暗黑使者類別繼承了使者類別,同時修改(隱藏或覆寫)了它所繼承的方法,只要子類別內的方法和父類別內的方法同名、同參數列、同回傳值形態,就會修改!之所以要都相同,理由之一是因為 JAVA 還有一個功能叫過載,即使方法名稱相同,只要傳入的參數不同,JAVA 會自動選擇適合的方法。過戴會在本篇最下方再介紹一次。

父類別:

/* 使者類別 */
// 編寫人:Lancelot Cheng
public class Envoy {
public String name, brithday, blood;
public String profession, weapon;
public int level, exp, oID;
//private Contract envoyDemon;

// 建構子
public Envoy (int oID, String nn, String bD, String bb,
String pro, int lev, int ex, String wea) {
name = nn;
brithday = bD;
blood = bb;
this.oID = oID;
profession = pro;
level = lev;
exp = ex;
weapon = wea;
}

public static void surrender(){
System.out.println("投降");
}

public void normalattack () {
System.out.println( weapon + "一般攻擊!");
}
}

子類別:注意這裡有兩個方法的名稱與父類別相同,同內容不同,此即為修改。

/* 暗黑使者類別 */
// 編寫人:Lancelot Cheng
public class DarkEnvoy extends Envoy{

public DarkEnvoy(int oID, String nn, String bD, String bb
, String pro, int lev, int ex, String wea) {
super(oID, nn, bD, bb, pro, lev, ex, wea);
}

public static void surrender(){ // 與父類別同名:隱藏
System.out.println("自爆!");
}

public void attack () { // 與父類別同名:覆寫
System.out.println(weapon + "暗黑屬性攻擊!");
}
}

主程式:創造了一個愛未未使者和暗黑愛未未使者,都用它們的方法。

/* 主程式 */
// 編寫人:Lancelot Cheng
public class Main {
public static void main( String[] args){

// 創造一個愛未未使者物件。
Envoy ai = new Envoy( 0, "愛未未", "0712", "AB",
"魔術師", 10, 200, "撲克牌");

System.out.print("愛未未攻擊:");
ai.normalattack();
System.out.print("愛未未投降:");
ai.surrender();

System.out.println();

// 創造一個暗黑愛未未使者物件。
DarkEnvoy darkAi = new DarkEnvoy(0, "愛未未", "0712", "AB",
"魔術師", 10, 200, "撲克牌");

System.out.print("暗黑愛未未攻擊:");
darkAi.attack();
System.out.print("暗黑愛未未投降:");
darkAi.surrender();
}
}

結果:兩個物件的方法名稱相同,但結果不同,意思是子類別內的方法的確修改了。

愛未未攻擊:撲克牌一般攻擊!
愛未未投降:投降

暗黑愛未未攻擊:撲克牌暗黑屬性攻擊!
暗黑愛未未投降:自爆!

Process finished with exit code 0


  這個練習滿簡單的,但不是一個好的寫法,我認為最好的寫法是使者類別與暗黑使者類別有共同的父類別,因為最好物件都是由子類產生,原因是子類別只能繼承到父類別中公開的變數,於是裡面所有的變數都必須設為公開,造成的結果是父類別所產生的物件,其成員可以被任意讀取與更動。都是修改方法,為什麼一個叫隱藏一個叫覆寫,我不知道原因。

  關於過載,在同一個類別中,定義同名的方法,當你在使用該類別產生的物件之方法時, JAVA 可以以你輸入的參數類型、數量判斷要用哪一個方法。舉個應用的例子,我們要計算三角形面積,依照所拿到的條件是邊長、邊長、邊長或角度、邊長、角度還是邊長、角度、邊長,會用使用不同的方法,但這些方法的目的都相同,就是算出面積,對我們來說,因為方法不同而去記憶不同的方法名字很麻煩,所以名字都取一樣,讓 JAVA 依輸入參數屬性幫我們判斷使用哪個方法就好,這就是過載。其中輸入的參數也可以是我們自己定義的物件。在這邊的算三角形面積例子有點不太好,因為角度、邊長都可以是浮點數。

  之後會再介紹介面與抽象類別,期待下一次的惡魔使者愛未未。


2021年6月26日 星期六

JAVA 物件導向的概念-10 結合類別

  本篇故事:在中古世紀,表演魔術為生的愛未未被當成魔女而遭受教會組織的追殺,逃亡的過程中進入了一間詭異的圖書館,不小心招喚出詐欺惡魔伊莉莎白,並與之締結了契約,愛未未以生命為代價,借得了惡魔的力量,成為了惡魔使者,終於有了與教會組織對抗的能力。

  簡單的說,兩個類別間的結合是透過第三個類別完成,則第三個類別稱為結合類別。僅僅只是第三個類別分別與第一和第二個類別有結合關係,概念上很簡單,只是[類別間的關係之結合關係]那篇文中的事情做兩次而已,如果您還沒看過,也許能去看一下[JAVA 物件導向的概念-06],以方便理解本篇中的範例。

  在本範例中,我會先創造愛未未使者、伊莉莎白惡魔兩個物件,然後再創造c_01契約物件,建立時同時結合前面兩個物件。結合後,愛未未可以使用伊莉莎的的方法。

使者的類別:
/* 使者類別 */
// 編寫人:Lancelot Cheng
public class Envoy {
private String name, brithday, blood;
private String profession, weapon;
private int level, exp, oID;
private Contract envoyDemon;

// 建構子
public Envoy (int oID, String nn, String bD, String bb,
String pro, int lev, int ex, String wea) {
name = nn;
brithday = bD;
blood = bb;
this.oID = oID;
profession = pro;
level = lev;
exp = ex;
weapon = wea;
}

public void toDemonContrant (Contract eD) {
envoyDemon = eD;
}
// *** 上一行重點,接契約物件。

public void powerOfDemon(){
if(envoyDemon != null) {
System.out.print(name + " 以自己的生命為代價,使用惡魔的絕招——");
envoyDemon.dem.useStunt(); // 透過契約使用惡魔的絕招。
}
else{
System.out.println("愛未未 沒有和任何惡魔締結契約,只是嚇嚇對方!");
}
}

}

惡魔的類別:
/* 惡魔類別 */
// 編寫人:Lancelot Cheng
public class Demon {
private String name;
private String title, stunt;
private int level, exp, oID;
private Contract envoyDemon;

// 建構子
public Demon (int oID, String nn, String tit, int lev, int ex, String stu) {
this.oID = oID;
name = nn;
title = tit;
level = lev;
exp = ex;
stunt = stu;
}

public void useStunt(){
System.out.println(stunt);
}

public void toEnvoyContrant (Contract eD) {
envoyDemon = eD;
}

}

契約的類別:在這裡扮演結合類別的角色。
/* 契約類別 */
// 結合類別:結合使者與惡魔。
// 透過契約,使者可以借得惡魔的力量,但也必須付出相應的代價。
// 編寫人:Lancelot Cheng
public class Contract {
int iD;
public Envoy env;
public Demon dem;

// 建構子。
public Contract( int iD, Envoy env, Demon dem) {
this.iD = iD;
this.env = env;
this.dem = dem;
env.toDemonContrant(this); // 把自己丟給使者物件。
dem.toEnvoyContrant(this); // 把自己丟給惡魔物件
}

// 解約的方法。
public void deContract(){
iD = 0;
env.toDemonContrant(null); // 把 null 丟給使者物件。
dem.toEnvoyContrant(null); // 把 null 丟給惡魔物件
env = null;
dem = null;
} // 注意上面的順序和建構子的不同,可以思考一下為什麼。

}

主程式:
/* 主程式 */
// 這個程式主要示範結合類別。
// 編寫人:Lancelot Cheng
public class Main {
public static void main(String[] args) {

// 產生 愛未未使者 物件。
Envoy ai = new Envoy(271828,"愛未未", "0912","O",
"魔術師", 99, 10000,"樸克牌");

// 產生 伊莉莎白惡魔 物件。
Demon elizabeth = new Demon(416, "伊莉莎白", "詐欺惡魔",
71, 8000, "虛幻的攻擊");

// 產生契約,同時將 愛未未使者 與 伊莉沙白惡魔 兩個物件丟給契約,結合兩個物件。
Contract c_01 = new Contract( 1, ai, elizabeth);


// 此時已經結合完成,所以可以使用愛未未使者物件中與惡魔有關的方法。
ai.powerOfDemon();

// 使用合約中,解除契約的方法。
c_01.deContract();

// 令契約容為 null,此動作或許可以讓電腦知道後面用不到它了,進而釋放記憶體。
c_01 = null;

// 再次使用與惡魔用關的方法,看結果可以知道用不出來了。
ai.powerOfDemon();

}
}

結果:
愛未未 以自己的生命為代價,使用惡魔的絕招——虛幻的攻擊
愛未未 沒有和任何惡魔締結契約,只是嚇嚇對方!

Process finished with exit code 0


  我手邊的書說結合類別使用在在多對一或多對多的結合關係,另外上網查,也有人表示非必要少用結合類別。所以我也不確定它的重要性,不過我還是寫了這個範例。我的範例仍然是一對一的結合,如果想要多對多,或多對一,在使者與惡魔類別中的契約物件宣告要宣告成陣列。

  再補充,範例中,我用契約結合了愛未未使者和伊莉莎白惡魔,但其實也是可以解約的,解除結合的方法我沒有寫在這邊,如果真的有人感興趣,請留言,我會補上。

  雖然使者和惡魔都有名字、等級和經驗,可以提出去創造一個共同的父類別,但這裡為了讓程式看起來不要太複雜,所以還是各別寫了。

  預告一下,我覺得惡魔使者愛未未的故事還不錯,之後會以這個故事為主題來寫 JAVA 程式,目標之一是寫出戰鬥的程式,可能是回合制或是時間軸制的。不過目前我還沒有使用圖的能力,所以只能是文字顯示了。

2021/6/29 :我新增了解除結合的方法於契約類別中,同時為了程式碼比較簡便,刪除了使者類別與惡魔類別的資料顯示方法。

2021年6月24日 星期四

JAVA 物件導向的概念 09- 類別間的相依關係(dependency)

  這個也滿簡單,一個類別的值或分法都與其他類別無關,具有獨立性,就稱為獨立類別,如下的例子:

薪水類別:

/* 薪水 */
// 編寫人:Lancelot Cheng
public class Salery {
public double basic = 10000.0; // 基本薪資
public double extraUnit = 999.0; // 獎金基本單位

}

上面是薪水規定的類別,基本薪水 10000 元,額外獎金單位是 999 元。

  那麼一個類別的值或方法與其他類別相關,就稱為相依類別。前面說過繼承的概念,下面三個類別各別是人類、公務人員、圓桌武士,設計邏輯是公務人員屬於人類,所以繼承人類;而圓桌武士屬於公務人員,所以繼承公務人員。

人類類別:

/* 人類 */
// 編寫人:Lancelot Cheng
public class Human {
public String name, brithday, blood;

// 建構子
public Human(String name, String brithday, String blood){
this.name = name;
this.brithday = brithday;
this. blood =blood;
}
}

公務人員類別:

/* 公務人員 */
// 編寫人:Lancelot Cheng
public class GoverEmployee extends Human {

public GoverEmployee(String name, String brithday, String blood) {
super(name, brithday, blood);
}

public double getBasic(Salery sa){
return sa.basic;
}

public double getExtraUnit(Salery sa, double score) {
return sa.extraUnit*score;
}
}

大中土 在公務人員類別中,設定了接收薪水物件的方法與計算額外獎金的方法,它的方法結果和薪水類別的成員變數有關,這便是相依。


圓桌武士類別:

/* 圓桌武士 */
// 編寫人:Lancelot Cheng
public class KOTRT extends GoverEmployee {
private String title, weapon;
private double score;

public KOTRT(String name, String brithday, String blood, String tt, String ww, double sco) {
super(name, brithday, blood);
title = tt;
weapon = ww;
score = sco;
}

public double getScore() {
return score;
}

public void setScore(double score) {
this.score = score;
}
}

接下來是主程式,新增政府給薪物件和藍斯洛特圓桌武士物件,並顯示出他的薪水:

/* 主程式 */
// 編寫人:Lancelot Cheng
public class Main {

public static void main(String[] args) {

Salery goverSal = new Salery(); // 創造政府薪水物件 gover

KOTRT lancelot = new KOTRT("藍斯洛特", "1001", "B",
"騎士之花", "阿隆黛特", 120);

System.out.println( "藍斯洛特的基本薪資與額外加給獎金:" + lancelot.getBasic(goverSal) +
";" + lancelot.getExtraUnit(goverSal, lancelot.getScore()));
}

}

結果:

藍斯洛特的基本薪資與額外加給獎金:10000.0;119880.0

Process finished with exit code 0

說明:因為藍斯洛特圓桌武士是公務人員,所以薪水按照公務人員的方法給定。

  寫這些文章也許可以賺一些廣告費,但目前為止不知道我不合規定的地方是什麼,所以只好各位面都試看看了。未來會減少曾寫過的程式碼,然後程式碼會試著更精簡,讓讀者更容易看懂。

  忽然間發現自己學到的東西己經可以重現以前文字遊戲的回合制戰鬥了,如果能搭配圖形遊戲性會更好,雖然能做出來的東西會很粗糙,不過就當個練習吧,之後會寫出來。

JAVA 實用程式分享-01 計算台灣電費的程式

   這邊難得練習了一個比較實用的程式,目的是輸入度數與是否夏季,得到電費。下面有兩個類別,一個是營業用電,一個是分營業用電。 

類別:家用,即非營業用電費。

// 編寫人:Lancelot Cheng
public class HomeUseBill{
public double degree;
private double moneyPD, bill, aa;
private String season;

public HomeUseBill(String season){
//super(degree);
this.season = season;
}

public double notSummer(double degree){
aa = 0;
if (degree <= 120*2){
moneyPD = 1.63;
aa = degree*moneyPD;
}
if (degree > 120*2 && degree <= 330*2){
moneyPD = 2.1;
aa = 195.6*2 + ( degree - 120*2 )*moneyPD;
}
if (degree > 330*2 && degree <= 500*2){
moneyPD = 2.89;
aa = 636.6*2 + ( degree - 330*2 )*moneyPD;
}
if (degree > 500*2 && degree <= 700*2){
moneyPD = 3.94;
aa = 1127.9*2 + ( degree - 500*2 )*moneyPD;
}
if (degree > 700*2 && degree <= 1000*2){
moneyPD = 4.6;
aa = 1915.9*2 + ( degree - 700*2 )*moneyPD;
}
if (degree > 1000*2){
moneyPD = 5.03;
aa = 3295.9*2 + ( degree - 1000*2 )*moneyPD;
}
return aa;
}

public double summer(double degree){
aa = 0;
if (degree <= 120*2){
moneyPD = 1.63;
aa = degree*moneyPD;
}
if (degree > 120*2 && degree <= 330*2){
moneyPD = 2.38;
aa = 195.6*2 + ( degree - 120*2 )*moneyPD;
}
if (degree > 330*2 && degree <= 500*2){
moneyPD = 3.52;
aa = 695.4*2 + ( degree - 330*2 )*moneyPD;
}
if (degree > 500*2 && degree <= 700*2){
moneyPD = 4.8;
aa = 1293.8*2 + ( degree - 500*2 )*moneyPD;
}
if (degree > 700*2 && degree <= 1000*2){
moneyPD = 5.66;
aa = 2253.8*2 + ( degree - 700*2 )*moneyPD;
}
if (degree > 1000*2){
moneyPD = 6.41;
aa = 3951.8*2 + ( degree - 1000*2 )*moneyPD;
}
return aa;
}

public double fee(double degree){
switch (season) {
case "非夏":
//System.out.println("非夏季用電");
bill = notSummer(degree);
break;
case "夏":
//System.out.println("夏季用電");
bill = summer(degree);
break;
default:
System.out.println("不合規定的輸入文字");
}
return bill;
//System.out.println(bill);
}
}


類別:商用,即營業用電費。

// 編寫人:Lancelot Cheng
public class BusiUseBill{
public double degree;
private double moneyPD, bill, aa;
private String season;

public BusiUseBill(String season){
//super(degree);
this.season = season;
}

public double notSummer(double degree){
aa=0;
if (degree <= 330*2){
moneyPD = 2.12;
aa = degree*moneyPD;
}
if (degree > 330*2 && degree <= 700*2){
moneyPD = 2.91;
aa = 699.6*2 + ( degree - 330*2 )*moneyPD;
}
if (degree > 700*2 && degree <= 1500*2){
moneyPD = 3.44;
aa = 1776.3*2 + ( degree - 700*2 )*moneyPD;
}
if (degree > 1500*2 ){
moneyPD = 5.05;
aa = 4528.3*2 + ( degree - 1500*2 )*moneyPD;
}
return aa;
}

public double summer(double degree){
aa=0;
if (degree <= 330*2){
moneyPD = 2.53;
aa = degree*moneyPD;
}
if (degree > 330*2 && degree <= 700*2){
moneyPD = 3.55;
aa = 834.9*2 + ( degree - 330*2 )*moneyPD;
}
if (degree > 700*2 && degree <= 1500*2){
moneyPD = 4.25;
aa = 2148.4*2 + ( degree - 700*2 )*moneyPD;
}
if (degree > 1500*2 ){
moneyPD = 6.43;
aa = 5548.4*2 + (degree - 1500*2 )*moneyPD;
}
return aa;
}

public double fee(double degree){
//System.out.println(degree);

switch (season) {
case "非夏":
//System.out.println("非夏季用電");
bill = notSummer(degree);
break;
case "夏":
//System.out.println("夏季用電");
bill = summer(degree);
break;
default:
System.out.println("不合規定的輸入文字");
}

return bill;
//System.out.println("電費 = " + degree + " * " + moneyPD + " = " + bill);
}
}


主程式:宣告你要的物件,並實作輸入"夏"或是"非夏"的字串,使用物件的 fee 方法,輸入度數。

// 編寫人:Lancelot Cheng
public class Main {
public static void main(String[] args) {

// 非營業用
BusiUseBill lancelotHome = new BusiUseBill( "非夏");
// 營業用
HomeUseBill lancelotStore = new HomeUseBill( "非夏");

System.out.println("非營業用電費:" + lancelotHome.fee(330));
System.out.println("營業用電費:" + lancelotStore.fee(170));
}
}

結果:

非營業用電費:636.6
營業用電費:360.40000000000003

  我寫完後發現有更好的寫法,不過也懶得改了,下一次寫程式之前,會多想一想。



JAVA 物件導向的概念-08 類別間的關係之一對多的關係(multiplicity)

   研究後我發現它基本上就是結合關係,在本篇我的範例是單向的,身為皇室的桂妮薇兒公主可以擁有兩個僕人,公主可以知道僕人的資訊,但僕人不能知道公主的資訊,所以是單向;一個公主兩個僕人,所以是一對多的關係(multiplicity)。皇室的類別中,可以有複數個僕人,所以我不是各別宣告不同名稱的僕人物件,而是直接宣告僕人物件陣列。


父類別:人類。
/* 父類別:人類 */
// 編寫人:Lancelot Cheng
public class Human {
public String name, brithday, blood;
// 我發現父類別的變數要設 public 子類別才能繼承,不然就是要設 getter and setter 吧。

// 父類別的建構子
public Human(String name, String brithday, String blood){
this.name = name;
this.brithday = brithday;
this. blood =blood;
}

void showBasical() {
System.out.println( "名字:" + name + ";生日:" + brithday + ";血型:" + blood);
}
}

子類別:僕人,由於它不用去承接任何物件,所程式碼很簡單。
// 編寫人:Lancelot Cheng
public class Servant extends Human {
private String mainjob;
private double salary;

public Servant (String nn, String bD, String bb, String mJ, double ss) {
super(nn, bD, bb); // 使用父類別的建構子。
mainjob = mJ;
salary = ss;
}

public void showJobSalary(){
showBasical();
System.out.println("主要工作:" + mainjob + ";月薪:" + salary);
}
}

子類別:皇室,由於要承接複數個僕人物件,所以有宣告僕人物件陣列與承接方法。
/* 子類別:皇室(royal family ) */
// 編寫人:Lancelot Cheng
public class RoyalFamily extends Human {
private String title, hat;
private int i = 0;
private Servant[] ser = new Servant[2];

// 建構子
public RoyalFamily (String nn, String bD, String bb, String tt, String hh) {
super(nn, bD, bb); // 使用父類別的建構子。
title = tt;
hat = hh;
}

public void haveServet ( Servant ser){
if(i < 2){
this.ser[i] = ser;
System.out.println("得到僕人!");
i++;
}
else{
System.out.println("已達僕人數上限!");
}
}

// 展現皇室成員檔案
public void showRoyalFamily() {
showBasical(); // 因為有繼承父類別,所以可以直接使用其方法,父類別和子類別的方法名稱必須不同。
System.out.println("頭銜:" + title + ";頭冠:" + hat);
System.out.println("----擁有以下僕人----");
if(i == 0) {
System.out.println("沒有僕人");
}
else {
for (int j = 0; j < 2 ; j++){
System.out.println("--------------------");
this.ser[j].showJobSalary();
}
}
System.out.println();
}
}

主程式:實作皇室物件桂妮薇兒與三個各有所長的僕人,將僕人指派給桂妮薇兒。
/* 主程式 */
// 編寫人:Lancelot Cheng
public class Main {
public static void main(String[] args) {

// 產生桂妮薇兒皇室成員物件,並給值與展現其值。
RoyalFamily guinevere = new RoyalFamily("桂妮薇兒",
"0614", "A", "公主", "鏡影神冠");

// 產生僕人
Servant al = new Servant("艾爾", "0702", "A", "幫主人洗澡", 10000.0);
Servant barty = new Servant("巴蒂", "0529", "B" , "說笑話", 30000.0);
Servant Olivia = new Servant("奧莉微亞", "0229", "O", "掃廁所", 100000.0);

System.out.println("締結契約前");
guinevere.showRoyalFamily();

// 指定僕人給桂妮薇兒
System.out.println("締結契約");
guinevere.haveServet(al);
guinevere.haveServet(barty);
guinevere.haveServet(Olivia);
System.out.println("締結契約完成");
System.out.println();

System.out.println("締結契約後");
guinevere.showRoyalFamily();

}
}

結果:
締結契約前
名字:桂妮薇兒;生日:0614;血型:A
頭銜:公主;頭冠:鏡影神冠
----擁有以下僕人----
沒有僕人

締結契約
得到僕人!
得到僕人!
已達僕人數上限!
締結契約完成

締結契約後
名字:桂妮薇兒;生日:0614;血型:A
頭銜:公主;頭冠:鏡影神冠
----擁有以下僕人----
--------------------
名字:艾爾;生日:0702;血型:A
主要工作:幫主人洗澡;月薪:10000.0
--------------------
名字:巴蒂;生日:0529;血型:B
主要工作:說笑話;月薪:30000.0


Process finished with exit code 0

  雖然主程式中指派了三個樸人給桂妮薇兒,但超出僕人數上限,所以最後一個僕人的契約締結不成功,桂妮薇兒只好自己掃廁所了。

  我自己私下又用這個程式做了其他嚐試,但沒有成功。我希望桂妮薇兒的僕人可以不只有僕人,也可以有用圓桌武士或其他人類,但我不想為此多寫幾行指令,所以我自己試了模糊化的寫法,但沒有成功,或許模糊化的寫法無法用在物件陣列上吧。

  我好奇的部份,如果我讓桂妮薇兒與每個僕人間的結合都是雙向的,那是否每個僕人都可以透過桂妮薇兒得到其他僕人的資料?有空再試看看吧。

JAVA 物件導向的概念-07 結合關係的練習

    這一篇是純練習,上一篇是把不同的物件結合,這一次我想試試同類物件的結合是否也可以,結論是可行的。以下程式碼改寫自之前的程式碼,內容是讓同為圓桌武士的藍斯洛特和伊萊恩結婚(即結合)。

父類別:

/* 父類別:人類 */
// 編寫人:Lancelot Cheng
public class Human {
public String name, brithday, blood;
// 我發現父類別的變數要設 public 子類別才能繼承,不然就是要設 getter and setter 吧。

// 父類別的建構子
public Human(String name, String brithday, String blood){
this.name = name;
this.brithday = brithday;
this. blood =blood;
}

void showBasical() {
System.out.println( "名字:" + name + ";生日:" + brithday + ";血型:" + blood);
}
}

子類別:

/* 子類別:圓桌武士(kinghts of the round table) */
// 編寫人:Lancelot Cheng
public class KinghtsRoundTable extends Human {
private String title, weapon;
private KinghtsRoundTable spous; // *** 宣告一個圓桌士的配偶

// 建構子
public KinghtsRoundTable (String nn, String bD, String bb, String tt, String ww) {
super(nn, bD, bb); // 使用父類別的建構子。
title = tt;
weapon = ww;
}

public void merried (KinghtsRoundTable spo){ // *** 承接主程式中指定的物件
spous = spo;
}

// 展現圓桌武士的檔案
public void showKinght() {
System.out.println("==圓桌武士==");
showBasical(); // 因為有繼承父類別,所以可以直接使用其方法,父類別和子類別的方法名稱必須不同。
System.out.println("稱號:" + title + ";武器:" + weapon);
if(spous == null) {
System.out.println("配偶:" + "     (為了你而空著)");
}
else{
System.out.println("配偶:" + spous.name);
}
System.out.println();
}
}

主程式:

/* 主程式 */
// 編寫人:Lancelot Cheng
public class Main {
public static void main(String[] args) {

System.out.println();

// 產生藍斯洛特圓桌武士物件,並給值與展現其值。
KinghtsRoundTable lancelot = new KinghtsRoundTable("藍斯洛特", "1228","AB", "騎士之花", "樹枝");

// 產生伊萊恩圓桌武士物件,並給值與展現其值。
KinghtsRoundTable elaine = new KinghtsRoundTable("伊萊恩", "0906","B", "聖杯少女", "音爆長鞭");

System.out.println("結婚前資料");
System.out.println();
lancelot.showKinght();
elaine.showKinght();

System.out.println("執行結婚~"); // ***
lancelot.merried(elaine); // 指定配偶是伊萊恩
elaine.merried(lancelot); // 指定配偶是藍斯洛特
System.out.println("結婚完畢!");

System.out.println();

System.out.println("結婚後資料");
System.out.println();

lancelot.showKinght();
elaine.showKinght();

}
}

結果:

結婚前資料

==圓桌武士==
名字:藍斯洛特;生日:1228;血型:AB
稱號:騎士之花;武器:樹枝
配偶:     (為了你而空著)

==圓桌武士==
名字:伊萊恩;生日:0906;血型:B
稱號:聖杯少女;武器:音爆長鞭
配偶:     (為了你而空著)

執行結婚~
結婚完畢!

結婚後資料

==圓桌武士==
名字:藍斯洛特;生日:1228;血型:AB
稱號:騎士之花;武器:樹枝
配偶:伊萊恩

==圓桌武士==
名字:伊萊恩;生日:0906;血型:B
稱號:聖杯少女;武器:音爆長鞭
配偶:藍斯洛特


Process finished with exit code 0


  不過圓桌武士裡面好像沒有女性,所以我就選了喜歡藍斯洛特,又曾女扮男裝參加騎馬槍擊比賽的伊萊恩當圓桌武士。在故事裡是悲戀,那就讓她在程式裡是美好結局吧。

  除了同類型的物件可以結合,我也試了物件是否可以自己跟自己結合,答案也是肯定的,只是這完全沒有意義就是了。

  這邊介紹一下模糊化的寫法,在這篇中的子類別,我宣告的配偶是圓桌武士物件,也就是說,圓桌武士只能跟同為圓桌武士的人結婚,如果想跟其他的人類型物件結婚,就必須再多宣告其他物件與相對應的承接方法。如果我的人物類別又很多,就會寫死我了。而模糊化的寫法就可以解決這個問題。方法就是宣告配偶為人類物件,而人類別為所有人型物件的父類別,由它去承接就可以了,任何它的子類別人型物件都可以由它來承接。這個模糊化寫法以後會再寫一篇介紹。

  我這裡的寫法是只有圓桌武士的物件可以結婚,如果想要讓其他的人類的物件也可以結婚,就必須再各別寫上方法,這樣不是很麻煩嗎?有方便一點的方式嗎?我的答案是把結婚的方法寫到父類別裡面,其子類別自然就會繼承結婚的方法了。

  這裡的結婚其實是只是相互指派物件給對方承接,事實上也可以另外寫方法來做這件事,不過對於想要學習的人來說,應該會比較不方便理解我在做什麼。





2021年6月23日 星期三

JAVA 物件導向的概念-05 繼承,老鼠的兒子會打洞

   繼承是物件導向的核心之一,方式是把類別分為父類別和子類別,子類別繼承父類別,就會擁有父類別的屬性。你可能會問為什麼不寫成一個類別就好,理由是當你要寫很多不同的類別時,各自去寫會很累,而如果你要寫的類別有共同的屬性,你可以把這些共同屬性抽出來,寫成父類別,再只針對不同的屬性,各自寫成比較簡略的子類別去繼承父類別。這樣可以少寫很多程式碼!

  這一篇範例我要寫的是一般人、圓桌武士、皇室成員,這三種我可以各自寫出他們對應的類別,但也可以抽出其共同屬性,寫成父類別,而不同屬性部份再各自寫成子類別。他們的共同性就是都是人類,也都擁有他們的名字、生日、血型。於是我寫出以下父類別,其中還有展現基本資料的方法。

父類別:人類
/* 父類別:人類 */
// 編寫人:Lancelot Cheng
public class Human {
public String name, brithday, blood;
// 我發現父類別的變數要設 public 子類別才能繼承,不然就是要設 getter and setter 吧。

// 父類別的建構子
public Human(String name, String brithday, String blood){
this.name = name;
this.brithday = brithday;
this. blood =blood;
}

void showBasicallFile() {
System.out.println( "名字:" + name + ";生日:" + brithday + ";血型:" + blood);
}
}

子類別:圓桌武士,KinghtsRoundTable 後面的 extends Human 就是繼承 Human 類別的意思,可以視作除了父類別的建構子,父類別裡的程式碼這裡都有一份。使用父類別的建構子,要使用 super 指令。
/* 子類別:圓桌武士(kinghts of the round table) */
// 編寫人:Lancelot Cheng
public class KinghtsRoundTable extends Human {
private String title, weapon;

// 建構子
public KinghtsRoundTable (String nn, String bD, String bb, String tt, String ww) {
super(nn, bD, bb); // 使用父類別的建構子。
title = tt;
weapon = ww;
}

// 展現圓桌武士的檔案
public void showKinghtFile() {
showBasicallFile(); // 因為有繼承父類別,所以可以直接使用其方法,父類別和子類別的方法名稱必須不同。
System.out.println("稱號:" + title + ";武器:" + weapon);
}
}

子類別:皇室,同上。
/* 子類別:皇室(royal family ) */
// 編寫人:Lancelot Cheng
public class RoyalFamily extends Human {
private String title, hat;

// 建構子
public RoyalFamily (String nn, String bD, String bb, String tt, String hh) {
super(nn, bD, bb); // 使用父類別的建構子。
title = tt;
hat = hh;
}

// 展現皇室成員檔案
public void showRoyalFamilyFile() {
showBasicallFile(); // 因為有繼承父類別,所以可以直接使用其方法,父類別和子類別的方法名稱必須不同。
System.out.println("頭銜:" + title + ";頭冠:" + hat);
}
}

主程式:產生平凡人我是直接用父類別 Human 產生的。
/* 主程式 */
// 這個程式主要示範繼承。
// 編寫人:Lancelot Cheng
public class Main {
public static void main(String[] args) {

// 產生理查平凡人物件,並給值與展現其值。
Human richard = new Human( "理查", "0307", "O");
richard.showBasicallFile();

System.out.println();

// 產生藍斯洛特圓桌武士物件,並給值與展現其值。
KinghtsRoundTable lancelot = new KinghtsRoundTable( "藍斯洛特", "1228", "AB", "騎士之花", "樹枝");
lancelot.showKinghtFile();

System.out.println();

// 產生桂妮薇兒皇室成員物件,並給值與展現其值。
RoyalFamily guinevere = new RoyalFamily( "桂妮薇兒", "0614", "A", "公主", "鏡影神冠");
guinevere.showRoyalFamilyFile();
}
}

結果:
名字:理查;生日:0307;血型:O

名字:藍斯洛特;生日:1228;血型:AB
稱號:騎士之花;武器:樹枝

名字:桂妮薇兒;生日:0614;血型:A
頭銜:公主;頭冠:鏡影神冠

Process finished with exit code 0

  Elton 補充:只要不是抽象類別,皆可產生物件,一般情況大多還是用子類別來產生物件,但還是可以視情況用父類別來產生物件。

  本來是要寫物件之間關係的範例,發現我還沒有寫繼承,所以先寫了這一篇,之後會修改上述的程式碼,成為其他篇的範例。