2015年8月16日 星期日

Java 練習(14):判斷是否為正三角形,並拋出自訂例外

Java 7 教學手冊第五版 第十三章習題

12.三角形的三個邊為 a、b、c,請判斷是否為正三角形,並利用try-catch捕捉自訂的例外,這些例外由 triangle()拋出。



public class App13_12
{
 

 public static void main(String[] args)
 {

  try
  {
   Triangle t = new Triangle();
   t.triangle(5, 2, 2);
  } catch (NotTriangle e)
  {
   System.out.println(e.getMessage());
  } catch (EquilateralTriangle e)
  {
   System.out.println(e.getMessage());
  } catch (NotEquilateralTriangle e)
  {
   System.out.println(e.getMessage());
  }

 }
}

class NotTriangle extends Exception
{

 public NotTriangle(int a, int b, int c)
 {
  super("不構成三角形");
 }
}

class EquilateralTriangle extends Exception
{

 public EquilateralTriangle(int a, int b, int c)
 {
  super("這是正三角形");
 }
}

class NotEquilateralTriangle extends Exception
{

 public NotEquilateralTriangle(int a, int b, int c)
 {
  super("這不是正三角形");
 }
}

class Triangle
{

 public void triangle(int a, int b, int c) throws NotTriangle, EquilateralTriangle, NotEquilateralTriangle
 {

  if ((a + b) < c || (a + c) < b || (b + c) < a)
  {
   throw new NotTriangle(a, b, c);
  } else if (a == b && b == c && c == a)
  {
   throw new EquilateralTriangle(a, b, c);
  } else
  {
   throw new NotEquilateralTriangle(a, b, c);
  }
 }
}

2015年8月6日 星期四

Java上課練習:抽象&介面

針對抽象的介面來寫程式 (抽象類別 與 介面 皆可)

針對下面類別圖撰寫 code


玩家可以更換武器,所以將 武器 作成 介面,和玩家組合(給玩家拿武器),後面可以隨時更換不同的武器
interface Weapon { //設計一把 武器(抽象介面)
    void attack(); //mothod
}

class player { //玩家

    Weapon w; //欄位

    public void attack() { //mothod
        w.attack(); 
    }

}

class 屠龍刀 implements Weapon { //實作武器,繼承Weapon

    @Override
    public void attack() { //覆寫攻擊方式
        System.out.println("屠龍刀攻擊");
    }
}

class 倚天劍 implements Weapon { //實作武器,繼承Weapon

    @Override
    public void attack() { //覆寫攻擊方式
        System.out.println("倚天劍攻擊");
    }
}

class 黃飛鴻 implements Weapon { //實作武器,繼承Weapon

    @Override
    public void attack() { //覆寫攻擊方式
        System.out.println("無影腳");
    }
}

public class PlayerTest {

    public static void main(String[] args) {
        player p1=new player(); //p1(遙控器)是一個玩家
        p1.w=new 黃飛鴻(); //p1的武器是黃飛鴻,可隨時更換武器
        p1.attack(); 

    }
}

撰寫程式需要進行 策略設計模式 ( Strategy Pattern )

  • Player 有一個 (HAS-A) Weapon,是一種組合的概念
  • 程式碼要能重用,可透過繼承 與 組合
  • Java 類別僅能單一繼承,組合比繼承更有彈性
  • 繼承,強調 是一種 (IS-A) 的關係(父類別有ㄧ個子類別)
  • 組合,強調 有一個 (HAS-A) 的關係(類別中有什麼類別)

方法委派
重構 Player 程式碼
interface Weapon { //設計一把 武器(抽象介面)

    void attack(); //mothod
}

class player { //玩家

    private Weapon w; //封裝欄位,有一個HAS-A

    //建構子
    public player(Weapon w) {
        this.w = w;
    }

    //getter
    public Weapon getW() {
        return w;
    }

    //setter
    public void setW(Weapon w) {
        this.w = w;
    }

    public void attack() { //mothod
//        w.attack(); //若玩家手上沒有武器會出現錯誤,所以要加入判斷
        if (w != null) { //判斷有沒有設計拿武器
            w.attack();//委派武器進行攻擊
        }

    }

}

class 屠龍刀 implements Weapon { //實作武器,繼承Weapon

    @Override
    public void attack() { //覆寫攻擊方式
        System.out.println("屠龍刀攻擊");
    }
}

class 倚天劍 implements Weapon { //實作武器,繼承Weapon

    @Override
    public void attack() { //覆寫攻擊方式
        System.out.println("倚天劍攻擊");
    }
}

class 黃飛鴻 implements Weapon { //實作武器,繼承Weapon

    @Override
    public void attack() { //覆寫攻擊方式
        System.out.println("無影腳");
    }
}

public class PlayerTest {

    public static void main(String[] args) {
        player p1 = new player(new 黃飛鴻());
        p1.attack();

        System.out.println("");

        p1.setW(new 倚天劍());//將武器改為拿倚天劍
        p1.attack();
    }
}



2015年8月5日 星期三

Java作業練習:抽象&介面_收銀機

注意:
類別只能繼承一個類別,但可實作多個介面
介面可以繼承多個介面

類別圖


Code
public interface 有價格的 {

    int getPrice();
}


public interface 有名稱的 {

    String getName();
}


public interface 商品 extends 有名稱的, 有價格的 {

}

class 屠龍刀 implements 商品 {

    @Override
    public String getName() {
        return "屠龍刀";

    }

    @Override
    public int getPrice() {
        return 6000;
    }

}

class 倚天劍 implements 商品 {

    @Override
    public String getName() {
        return "倚天劍";

    }

    @Override
    public int getPrice() {
        return 4000;
    }

}

class 青龍劍 implements 商品 {

    @Override
    public String getName() {
        return "青龍劍";

    }

    @Override
    public int getPrice() {
        return 8000;
    }

}


interface 折扣活動 {
    int 計算(商品[] products);
}

class 父親節 implements 折扣活動{

    @Override
    public int 計算(商品[] products) {
        int sum=0;
        for (int i=0;i<products.length;i++){
            sum+=products[i].getPrice();
        }
        return (int)(sum*0.8);

        }
    
}

class 母親節 implements 折扣活動{

    @Override
    public int 計算(商品[] products) {
        int sum=0;
        for (int i=0;i<products.length;i++){
            sum+=products[i].getPrice();
        }
        return (int)(sum*0.7);

        }
    
}


public class 收銀機 {
    private  折扣活動 strategy;
    private  商品[] products;

    public 收銀機(折扣活動 strategy, 商品[] products) {
        this.strategy = strategy;
        this.products = products;
    }
    
    public void print(){
        String s="";
        int sum=0;
        for (int i=0;i<products.length;i++){
            s=products[i].getName()+"\t"+products[i].getPrice();
            sum+=products[i].getPrice();
            System.out.println(s);
        }
        
        System.out.println("總金額\t"+sum+"\t折扣後\t"+strategy.計算(products));
    }
    
}


public class Test {

    public static void main(String[] args) {
//        商品[] products={new 倚天劍(),new 屠龍刀()};
//        收銀機 cashier=new 收銀機(new 父親節(),products);
        商品[] products={new 倚天劍(),new 屠龍刀(),new 青龍劍()};
        收銀機 cashier=new 收銀機(new 母親節(),products);
        cashier.print();
    }

}

Java上課練習:集合_Map

集合預設使用 Object 型別,所以 key 與 value 可以是任意型別的物件



程式碼
import java.util.HashMap;
import java.util.Map;

//集合預設使用 Object 型別,所以 key 與 value 可以是任意型別的物件
public class MapDemo {

    public static void main(String[] args) {
        Map map = new HashMap(); //HashMap基於雜湊表的 Map 介面的實作,並允許使用 null 值和 null 鍵
        
        //將成對的Key、valus放入集合,每個鍵最多只能映射到一個值
        map.put(100, "中正區");
        map.put(103, "大同區");
        
        Object obj=map.get(103); //給Key取出valus
        System.out.println(obj);
        
        String s=(String)obj;//將Object轉換為String
        System.out.println("字串長度:"+s.length());//讀取字串長度
    }

}

可以使用泛型,就不需要轉換類別
import java.util.HashMap;
import java.util.Map;


public class MapDemo2 {

    public static void main(String[] args) {
        //< > 泛型,限定 Key 為Integer,valus為 String
        Map<Integer,String> map = new HashMap<>();
        
        //將成對的Key、valus放入集合
        map.put(100, "中正區");
        map.put(103, "大同區");
        
        /*
        因為當初使用泛型,指定valus為String
        所以現在不用Object宣告變數,直接使用String
        如果不使用泛型的集合,需要進行轉型,請參照MapDemo
        */
        String value=map.get(103);
        System.out.println(value);
        //valus是String,不用轉型
        System.out.println("字串長度:"+value.length());

    }

}

運用之前作過的樂透進行配對
Lotto
import java.util.ArrayList;


public class Lotto {
    
    private static ArrayList 數字陣列;
    private static ArrayList 樂透;
    private static int 索引;
    private static int 抽出號碼;
    
    public static ArrayList 產生一組樂透號碼(){
        建立數字與樂透陣列();
        42個號碼放進數字陣列();
        for (int j=1;j<=6;j++){
            用亂數產生一個陣列索引();
            根據索引從陣列取出號碼();
            將抽出的號碼放進樂透陣列();
        }return 樂透;
    }
    
    
    private static void 建立數字與樂透陣列(){
          數字陣列=new ArrayList();
          樂透=new ArrayList();
    }
    
    private static void 42個號碼放進數字陣列(){
        for (int i=1;i<=42;i++){
            數字陣列.add(i);
        }
    }
    
    private static void 用亂數產生一個陣列索引(){
        索引=(int)(Math.random()*數字陣列.size());
    }
    
    private static void 根據索引從陣列取出號碼(){
        Object obj=數字陣列.remove(索引);
        抽出號碼=(int)obj;
    }
    
    private static void 將抽出的號碼放進樂透陣列(){
        樂透.add(抽出號碼);
    }
    
}

使用Map
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JOptionPane;

public class MapDemo3 {

    public static void main(String[] args) {
        ArrayList a = Lotto.產生一組樂透號碼();
        ArrayList b = Lotto.產生一組樂透號碼();
        System.out.println("a=" + a);
        System.out.println("b=" + b);

        //泛型,指定Key為String,value為ArrayList
        Map<String, ArrayList> map = new HashMap<>();
        //成對的Key,valus放入
        map.put("張無忌", a);
        map.put("周芷若", b);
        /*
         因為當初使用泛型,指定valus為String
         所以現在不用Object宣告變數,直接使用String
         */
        String name = JOptionPane.showInputDialog("請輸入名字:");
        ArrayList value = map.get(name); //將Key帶入取value
        JOptionPane.showMessageDialog(null, value);
    }

}



Java上課練習:集合

集合可以裝集合

類別圖


程式碼如下
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.TreeSet;

//集合可以裝集合(addall)
public class CollectionDemo {

    public static void main(String[] args) {

        //參考類別圖
        ArrayList list = new ArrayList(); //類別 ArraryList 已實作的介面有list、Collection
        list.add(1);
        list.add(2);
        list.add(3);

        HashSet set = new HashSet();//類別 HashSet 已實作的介面有Set、Collection
        set.add(100);
        set.add(90);

        LinkedList list2 = new LinkedList();//類別 LinkedList 已實作的介面有list、Collection
        /*
         LinkedList也是一種Collection,所以可以把另一個Collection(ArraryList or HashSet)直接加入
         */
        list2.addAll(list);//將Collection(list)加入
        list2.addAll(set);
        list2.add(80);
        list2.add(60);
        System.out.println(list2);

        //排序,不可以把不同種類的類別放入,因為要compareTo,會無法比大小
        TreeSet set2 = new TreeSet(list2);//類別 TreeSet 已實作的介面有Set、Collection
        /*
         建構子直接可以裝Collection,不用使用addAll
         */
        System.out.println(set2);

    }

}

集合也可以放物件,但是是參考物件,非複製
import java.util.ArrayList;

//集合裡放的是物件參考
public class CollectionDemo2 {

    public static void main(String[] args) {
        Animal dog=new Dog(); //dog是一個遙控器
        dog.setName("小白");
        
        ArrayList list= new ArrayList(); //ArrayList裡面也有一支list遙控器
        list.add(dog); //實際上是複製參考(共用),並非複製物件
        /*
        把dog這支遙控器複製到List中,所以當dog遙控器改名字,list這支遙控器的名字也會改變
        */
        System.out.println(list);
        
        dog.setName("阿花");//所以dog名字改為阿花,list的dog名字也會變成阿花
        System.out.println(list);
                

    }

}

2015年8月4日 星期二

Java上課練習:多執行緒_同步化(synchronized)

同步化方法 ( synchronized )
多執行緒共享物件問題

進行多執行緒要注意同步的問題,當同時有多個執行緒進行時,若該程式需要某一執行緒執行完畢後,才能執行下一個執行緒,此時就需要進行同步化

製作一程式ATM,同時有三個執行緒進行提款
類別圖


程式碼
class Account {

    private int balance;//餘額

    //建構子
    public Account(int balance) {
        this.balance = balance;
    }

    //getter
    public int getBalance() {
        return balance;
    }

    //setter
    public void setBalance(int balance) {
        this.balance = balance;
    }

    //提款
    public void withdraw(int amount) { 
        this.balance = getBalance();//先取得Account帳戶還有多少餘額
        交易進行中();//模擬交易處理時間
        balance -= amount;//提款後餘額
        this.setBalance(balance);
    }

    //模擬交易處理時間
    private void 交易進行中() {
        int delay = (int) (Math.random() * 3000);
        try {
            Thread.sleep(delay);//讓執行緒暫停,模擬交易時間
        } catch (InterruptedException ex) {
            System.out.println(ex);
        }
    }

}

//執行緒
class ATM extends Thread {

    private Account account; //宣告

    //建構子
    public ATM(Account account) { //設定帳戶
        this.account = account;
    }

    public void run() {
        String atmName = Thread.currentThread().getName();//取得執行緒名稱

        System.out.println(atmName + "提款中...");
        account.withdraw(1);//對帳戶提款一元
        System.out.println(atmName + "完成提款,餘額:" + account.getBalance());

    }
}

public class ATMTTest {

    public static void main(String[] args) {
        Account acc = new Account(100); //設立帳戶,餘額100
        System.out.println("帳戶餘額:" + acc.getBalance());

        //建立三個執行緒,共同使用一個帳戶
        ATM atm1 = new ATM(acc);
        ATM atm2 = new ATM(acc);
        ATM atm3 = new ATM(acc);
        //設定執行緒名稱
        atm1.setName("ATM1");
        atm2.setName("ATM2");
        atm3.setName("ATM3");
        //啟動        
        atm1.start();
        atm2.start();
        atm3.start();

    }

}

顯示結果


因為沒有進行同步化,所以3台ATM同時提款,這邊要修改為當有ㄧ台ATM在執行,其他兩台ATM要先暫停

同步化方法只允許一個執行緒在其中執行,只要有執行緒在同步化方法 中,其他執行緒必須等候
class Account {

    private int balance;//餘額

    //建構子
    public Account(int balance) {
        this.balance = balance;
    }

    //getter
    public int getBalance() {
        return balance;
    }

    //setter
    public void setBalance(int balance) {
        this.balance = balance;
    }  
    /*
     同步化方法 ( synchronized )
     同步化方法只允許一個執行緒在其中執行,只要有執行緒在同步化方法 中,其他執行緒必須等候
     */
    //提款
    public void withdraw(int amount) { //這邊可以在最前方加入同步化(synchronized)或是在執行緒內容加入
        this.balance = getBalance();//先取得Account帳戶還有多少餘額
        交易進行中();//模擬交易處理時間
        balance -= amount;//提款後餘額
        this.setBalance(balance);
    }

    //模擬交易處理時間
    private void 交易進行中() {
        int delay = (int) (Math.random() * 3000);
        try {
            Thread.sleep(delay);//讓執行緒暫停,模擬交易時間
        } catch (InterruptedException ex) {
            System.out.println(ex);
        }
    }
}

//執行緒
class ATM extends Thread {

    private Account account; //宣告

    //建構子
    public ATM(Account account) { //設定帳戶
        this.account = account;
    }

    public void run() {
        String atmName = Thread.currentThread().getName();//取得執行緒名稱
        //使用同步化synchronized
        //因為多個ATM對同一個帳戶提款需要前一個動作完成才能給下一個提款,所以需要做同步化獨佔
        synchronized(account){ //鎖定account物件(獨佔)
        System.out.println(atmName + "提款中...");
        account.withdraw(1);//對帳戶提款一元
        System.out.println(atmName + "完成提款,餘額:" + account.getBalance());
        }
        //未同步化
//        System.out.println(atmName + "提款中...");
//        account.withdraw(1);//對帳戶提款一元
//        System.out.println(atmName + "完成提款,餘額:" + account.getBalance());

    }
}

public class ATMTTest {

    public static void main(String[] args) {
        Account acc = new Account(100); //設立帳戶,餘額100
        System.out.println("帳戶餘額:" + acc.getBalance());

        //建立三個執行緒,共同使用一個帳戶
        ATM atm1 = new ATM(acc);
        ATM atm2 = new ATM(acc);
        ATM atm3 = new ATM(acc);
        //設定執行緒名稱
        atm1.setName("ATM1");
        atm2.setName("ATM2");
        atm3.setName("ATM3");
        //啟動        
        atm1.start();
        atm2.start();
        atm3.start();

    }
}

顯示結果


這邊有兩種方式,ㄧ種是直接在Account的提款mothod最前方加入synchronized,另一種是採用同步化區塊 ( synchronized blocks ),撰寫程式的彈性較大,執行緒可指定要獨佔哪個物件,並設定同步化程式的範圍