Interfaces and Packages Lecturer:曾學文
Outline 抽象(Abstract) 巢狀類別(Nested Classes) Your Turn 介面(Interfaces) 抽象類別(Abstract Classes) 抽象方法(Abstract Methods) 巢狀類別(Nested Classes) 靜態巢狀類別(static nested classes) 內部類別(inner classes) Your Turn 介面(Interfaces) 套件(Packages)
抽象(Abstract) 真實世界 Java 程式中 食物為例,應沒見過食物這個東西,一般見到的都是紅蘿蔔、蘋果、巧克力這種真實東西,所以食物可以代表一種抽象概念。 Java 程式中 lava.lang.Number 類別代表數字的抽象概念,所以 Number 是一個抽象類別(Abstract Class) 抽象類別只能被其他類別繼承,不能被實體化 如果企圖對 abstract class 進行實體化動作,將會在編譯時期發生錯誤
抽象類別(Abstract Classes) 定義 類別內部有尚未定義實作內容的方法,則此類別必須宣告為 abstract class,該方法需宣告為 abstract method 宣告 abstract class Number { … } 特性 宣告為 abstract 的類別無法產生實體。例如,您無法對上述類別做: Number myNumber = new Number(); // 會發生錯誤
抽象方法(Abstract Methods) 沒有實作內容的 methods (即沒有 method body) abstract classes 至少必須提供一個完整或部分程式碼的 method 若一個 abstract class 都只有宣告 abstract methods 的話,那就必須改宣告成介面(interface) 抽象類別主要是用來在繼承上,繼承的 subclass 必須將 abstract classes 中的 abstract methods 實作內容補上
抽象類別(Abstract Classes) 範例: GraphicsObject 是一個抽象類別,提供所有 subclass 必備的成員變數與 methods Point Origin; // 重心 moveTo(); // 移動 abstract draw(); // 繪出 GraphicsObject abstract class Rectangle Circle Line
抽象類別(Abstract Classes) public abstract class GraphicsObject { protected Point Origin; public GraphicsObject() { Origin = new Point(0,0); } public void moveTo(int newX, int newY) { Origin.x = newX; Origin.y = newY; public abstract void draw(); // abstract method protected void finalize() throws Throwable { Origin = null; super.finalize();
抽象類別(Abstract Classes) class Rectangle extends GraphicsObject { public int Length; public int Height; public Rectangle() { Length = 0; Height = 0; } public Rectangle(int Left, int Top, int Len, int High) { Origin.x = Left; Origin.y = Top; Length = Len; Height = High; public void draw() { // 必須填上 abstract method 的實作內容 System.out.println("Rectangle has been drawn at \n" + "(" + Origin.x + ", " + Origin.y + ") - (" + (Origin.x+Length) + ", " + Origin.y + ")\n" + "(" + Origin.x + ", " + (Origin.y+Height) + ") - (" + (Origin.x+Length) + ", " + (Origin.y+Height) + ")");
抽象類別(Abstract Classes) class Circle extends GraphicsObject { public int Radius; public Circle() { Radius = 0; } public Circle(int a, int b, int r) { Origin.x = a; Origin.y = b; Radius = r; public void draw() { // 必須填上 abstract method 的實作內容 System.out.println("Circle has been drawn at \n" + "Center: (" + Origin.x + ", " + Origin.y + ")\n" + "Radius: " + Radius);
抽象類別(Abstract Classes) class Line extends GraphicsObject { public Point End; public Line() { End = new Point(0,0); } public Line(int a, int b, int c, int d) { Origin.x = a; Origin.y = b; End = new Point(c, d); public Line(Point p1, Point p2) { Origin.x = p1.x; Origin.y = p1.y; End = new Point(p2.x, p2.y); public void draw() { // 必須填上 abstract method 的實作內容 System.out.println ("Line: (" + Origin.x + ", " + Origin.y + ") - (" + End.x + ", " + End.y + ")");
抽象類別(Abstract Classes) 測試主程式:DrawDemo.java public class DrawDemo { public static void main(String[] args){ Rectangle rectObj = new Rectangle(1, 2 ,5 ,5); Circle cirObj = new Circle(1, 1, 5); Line lineObj = new Line(1, 1, 2, 2); rectObj.draw(); cirObj.draw(); lineObj.draw(); }
巢狀類別(Nested Classes) Java 中 可以將一個類別變成另一個類別的成員 如下的程式碼稱為巢狀類別 class EnclosingClass { … class ANestedClass { }
巢狀類別(Nested Classes) 巢狀類別(Nested Classes) 強制限制住兩個類別之間的關連性 使用時機:當某一個類別在另一個類別中才具有意義的時候,如:只有在文字元件(text component)之內,文字游標才具有意義。因此文字游標(巢狀類別)定義於文字元件(類別)之內應是一個很好的選擇。 巢狀類別可以存取外層類別的任何成員,即使是宣告成 private 的成員。
巢狀類別(Nested Classes) 巢狀類別 如同其他成員一樣,巢狀類別也可以宣告成 static 宣告為 static 的巢狀類別,稱為靜態巢狀類別(static nested class) 非 static 的巢狀類別,稱為內部類別(inner class) class EnclosingClass { ... static class StaticNestedClass { } class InnerClass {
巢狀類別(Nested Classes) 靜態巢狀類別(static nested class) 與 class variables, class method 一樣都是屬於類別,而非屬於物件 與 class method 一樣,在靜態巢狀類別中也不能直接使用實體變數(instance variables) 反應的是兩個類別之間的關連性 宣告 存取範圍 類別特性 繼承宣告 public protected (空白) private static abstract final extends 父類別名稱 implements 父介面名稱 class 類別名稱
巢狀類別(Nested Classes) 靜態巢狀類別(static nested class) 使用前不用 new 即可使用。 可擁有任何 static 成員。 這個Class也是在Load進JVM的記憶體後就存在 主類別名稱.內部類別名稱.方法(參數); 如 Line2.Data.showData("Hi...");
巢狀類別(Nested Classes) 內部類別(inner class) 與 instance variables, instance methods 一樣都是屬於物件 反應的是兩個類別實體物件之間的關連性 宣告 存取範圍 類別特性 繼承宣告 public protected (空白) private abstract final extends 父類別名稱 implements 父介面名稱 class 類別名稱
巢狀類別(Nested Classes) 內部類別(inner class) InnerClass 的實體只能存活在 Enclosing Class 物件之內 InnerClass 可以直接使用 EnclosingClass 物件的實體變數與 methods(包括宣告成 private 的) class EnclosingClass { ... class InnerClass { }
巢狀類別(Nested Classes) 內部類別(inner class) new一個內部物件 使用前必須先 new 才能使用 不可以擁有 static 成員,但可有 static final 成員。 可以繼承 static 成員。 new一個內部物件 物件instance.類別名稱 = 物件instance.new 類別名稱(參數); 如 myLine2.Style = myLine2.new CStyle();
巢狀類別(Nested Classes) Example: Line2.java import java.lang.Math; public class Line2 { public Point Start; public Point End; public CStyle Style = null; public Line2() { Start = new Point(0,0); End = new Point(0,0); } public Line2(int a, int b, int c, int d) { Start = new Point(a, b); End = new Point(c, d); public Line2(Point p1, Point p2) { Start = new Point(p1.x, p1.y); End = new Point(p2.x, p2.y);
巢狀類別(Nested Classes) public class CStyle { int Thickness; int Pattern; int Color; public CStyle() { Thickness = 0; Pattern = 0; Color = 0; } public CStyle(int th, int pt, int cl) { Thickness = th; Pattern = pt; Color = cl; public String draw() { return "Thickness: " + Thickness + "\nPattern: " + Pattern + "\nColor: " + Color;
巢狀類別(Nested Classes) public void CreateStyle(int th, int pt, int cl) { Style = new CStyle(th, pt, cl); } public void draw() { System.out.println ("Line: (" + Start.x + ", " + Start.y + ") - (" + End.x + ", " + End.y + ")"); public double area() { return Math.sqrt(Math.pow(End.x - Start.x,2) + Math.pow(End.y - Start.y,2)); protected void finalize() throws Throwable { Start = null; End = null; super.finalize();
巢狀類別(Nested Classes) Line2 的測試主程式:DrawLine2.java public class DrawLine2 { public static void main(String[] args) { Line MyLine = new Line(1,1,5,5); MyLine.draw(); System.out.println("Length = " + MyLine.area()); MyLine.CreateStyle(3,3,3); System.out.println(MyLine.Style.draw()); }
Your Turn 假設給定以下類別:Parcel.java,試著在此類別中寫一個 main() 來測試其中所有巢狀類別的方法。 public class Parcel { class Contents { private int i = 11; public int value() { return i; } } class Destination { private String label; Destination(String whereTo) { label = whereTo; } String readLabel() { return label; } static class Data { static void showData(String data){ System.out.println(data); }
介面(Interface) 定義 宣告 作用 一段只有常數與函式宣告,但沒有函式實作的程式碼。 [public] interface 介面名稱 [extends 父介面名稱] 作用 讓某個功能,不論由誰實作,都能夠有相同的函式名稱,傳入值,傳出值,與存取範圍。
介面(Interface) 定義介面 public interface Timer { public void counting(); public void reset(); public long ONE_SECOND = 1000; public long ONE_MINUTE = 60000; … }
介面成員的預設存取權限 [Method] public abstract [Variable] public static final public abstract void counting(); [Variable] public static final public static final long ONE_SECOND = 1000;
介面與抽象類別的差異 介面(interface)與抽象類別(abstract class) 介面裡不能有任何實作的methods存在;不過,abstract class 可以 一個類別可以同時實作多個介面;但一個類別只能有一個 superclass(Java 不支援多重繼承) 介面不屬於類別階層架構的一份子。毫不相干的類別,也可以實作相同介面 因為多重繼承會有問題,但實際上又有多重繼承的需要,所以發明了 interface,作為折衷的替代方案。
介面(Interface) 注意事項 如果某介面繼承另一個介面,則 subclass 能從 superclass 繼承到的只有常數與函式宣告而已。 介面一旦公佈出去,開始供大家使用後,就請盡量不要更改。因為一旦更改,那些已經使用此介面的程式會因無法實作新功能,而不能執行。 如果真要更改,請宣告新介面,其它人就可以決定要實作一個介面或兩個介面都實作。 優點 快速知道如何使用此class,省去閱讀大量文件之時間
介面(Interface) 範例: GraphicsObject Rectangle Circle Line Paintable Point Origin; // 重心 moveTo(); // 移動 abstract draw(); // 繪出 GraphicsObject Rectangle Circle Line Point Origin; moveTo(); abstract draw(); Point Origin; moveTo(); abstract draw(); Point Origin; moveTo(); abstract draw(); void fillColor (int Color); double getArea(); Paintable
介面(Interface) 範例:Paintable.java public interface Paintable { public void fillColor(String Color); public double getArea(); }
介面(Interface) 範例:Rectangle2.java public class Rectangle2 extends GraphicsObject implements Paintable { public int Length; public int Height; public Rectangle2() { Length = 0; Height = 0; } public Rectangle2(int Left, int Top, int Len, int High) { Origin.x = Left; Origin.y = Top; Length = Len; Height = High;
介面(Interface) public void draw() { System.out.println("Rectangle has been drawn at \n" + "(" + Origin.x + ", " + Origin.y + ") - (" + (Origin.x+Length) + ", " + Origin.y + ")\n" + "(" + Origin.x + ", " + (Origin.y+Height) + ") - (" + (Origin.x+Length) + ", " + (Origin.y+Height) + ")"); } public void fillColor(String Color) { System.out.println("Rectangle has been filled with " + Color); public double getArea() { return Length * Height;
介面也被視為一種資料型別 未實作前即可應用interface做為溝通的介面 <Timer.java> public interface Printer{ public int CPS = 80; public int LPP = 55; public void print(String fileName) public boolean checkStatus(); public void reset(); }
實作 <MyPrinter.java> public class MyPrinter implements Printer{…} <DocuCenter.java> public class DocuCenter{ … void printDoc(Printer p, String fileName) // Printer 是一種DataType (class) { if (p. checkStatus()==false) p.reset(); p.print(fileName); }
實作 public class Main{ public static void main(String args[]) { DocuCenter dc = new DocuCenter(); HpPrinter hp = new HpPrinter(); CanonPrinter canno = new CanonPrinter(); …… dc.printDoc(hp); dc.printDoc(canon); }
實作 class DocuCenter 實作者 規格 使用者 interface printer Implements print(String fileName); checkStatus(); reset(); void printDoc (printer p,String fileName); Implements Class MyPrinter Implements Class HpPrinter Implements Class CanonPrinter 實作者 規格 使用者
介面的擴充與多重繼承 interface DataOutput {……} interface ObjectOutput extends DataOutput {……} interface House{……} interface Car{……} interface CarHouse extends House,Car{…} //某種成度的多重繼承
介面的擴充與多重繼承 class Property {String name; ……} class Car extends Property {…; void print() {…} } //繼承name field class House extends Property {…; void print() {…} } class CarHouse extends Car, House {…; void print() {…} } //在Java中不允許這樣做 結論: 造成資料成員的混淆.可能有多份的name,多份的print(),讓你不知道呼叫何者 討論: 那為什麼interface不會有這種困擾???
Example interface Property{ String getOwner(); void setOwner(String Owner); } interface Car extends Property{ String getCarID(); void setCarID(String ID); void print();
Example interface House extends Property{ String getAddr(); void setAddr(String addr); void print(); } class CarHouse implements Car, House{ String name, addr, carID, Onwer; ……// DIY
Example ActionListerner <java.awt.event.Act ActionListerner.java > public interface ActionListener extends EventListener{ public void actionPerformed(ActionEvent e); } <MyFrame.java> import java.awt.event.*; import java.awt.*; public class Frame1 extends Frame implements ActionListener { TextField tf = new TextField(); Button b = new Button("Hi"); public static void main(String[] args) { Frame1 mf = new Frame1(); mf.setBounds(10,10,150,100); mf.setVisible(true);
Example ActionListerner public Frame1(){ this.setLayout(null); tf.setBounds(30,30,80,30); b.setBounds(new Rectangle(30,80,50,30)); b.addActionListener(this); add(tf); add(b); } public void actionPerformed(ActionEvent e) { tf.setText("Hello");
Example MultiThread Interface Runnable Thread JVM OS C1 run(); C2
Example MultiThread <java.lang.Runnable> public interface Runnable { public abstract void run(); } public class TwoThread { public static void main(String args[]) { Counter c1= new Counter(“C1”); Counter c2= new Counter(“C2”); Thread t1 = new Thread(c1); Thread t2 = new Thread(c2); t1.start(); t2.start();
Example MultiThread class Counter implements Runnable { String name; public Counter(String _name) {name = _name;} public void run() { for(int i=0; i<100;i++) System.out.println(name+”:”+i); }
套件(Package) 楔子:實作Project RemoteControl 子計劃一(RcBase): Stack.java; List.java; Controller.java 子計劃二(RcApp): IrReceive.java; Driver.java; Db.java \RC \RC\RcBase \RC\RcAPP \Main.java Stack.java; List.java; Controller.java IrReceive.java; Driver.java; Db.java
使用Package指令 以檔案管理的觀點著手 我們準備在C:\Rc目錄下發展系統(並準備讓Compiler自動形成上述的檔案系統) 將Stack.java; List.java; Controller.java 統稱為package RcBase下的檔案. 需要在各檔案的第一行加上 package RcBase; 將IrReceive.java; Driver.java; Db.java統稱為package RcApp下的檔案. 需要在各檔案的第一行加上 package RcApp; 在Main.java中使用package RcBase及package RcApp
版本一 public class Main{ public static void main(String args[]) { RcBase.Stack s = new RcBase.Stack(10); RcApp.IrReceive receiver =new RcApp.IrReceive(3); … }
版本二 import RcBase.*; import RcApp.*; public class Main{ public static void main(String args[]) { Stack s = new RcBase.Stack(10); IrReceive receiver =new RcApp.IrReceive(3); … }
套件(Package) 套件目的 套件命名習慣 防止兩位程式師取了相同的類別名稱 將公司的網際網路名稱倒過來寫 範例:package tw.com.pcschool.Graphics 有時會再加上部門名稱 範例:package tw.com.pcschool.rd.Graphics
套件(Package) 在 Java 中規定,.class 檔案真正存在的地方必須與套件的命名結構相對應 tw com pcschool Point.java Point.class package tw.com.pcschool.Graphics; class Point { ….. } tw com pcschool Graphics Point.class
套件(Package) 所以,為了在編譯時能夠抓到我們自訂的套件,要用 classpath 參數告訴 javac 到哪裡去找我們要的套件 在 c:\autoexec.bat 中寫下: set classpath=.;c:\jdk1.3\lib;c:\mylib\tw \com\pcschool\Graphics 在 javac 後方直接用 –classpath 參數 javac –classpath .;c:\jdk1.3\lib;c:\mylib\tw\com \pcschool\Graphics Point.java 設環境變數
套件(Package) package 與目錄結構 使用 javac 之參數 所有屬於 packagename 類別庫的 .class 檔案都必須儲存在 packagename 資料夾下 packagename 必須唯一,類別庫名稱可使用句點分隔不同層次的資料夾名稱 例: package tw.com.pcschool.Graphics package tw.com.pcschool.rd.Graphics 使用 javac 之參數 語法:javac –classpath user-class-path –d des-path xxx.java -classpath:指定使用者自訂類別路徑,至存放類別目錄的上一層 -d:指定編譯完成之 .class 檔案產生路徑 若指定路徑中的名稱包含空白,必須使用雙引號括起來
套件(Package) Source 與 Class 檔的管理 Source 檔與 Class 不一定要放在同一個目錄下
套件(Package) 使用類別庫 編寫 batch 檔案 語法:java –classpath .;user-class-path main-class 用分號「;」區隔多個類別庫 類別庫可為目錄路徑、zip 檔案或 jar 檔案 目錄擺放結構一定要正確! 編寫 batch 檔案 參閱 compile.bat
Your Turn 製作套件練習:(產生如 my_lib.jar) 利用 compile.bat 試著將此課堂中所講解的程式碼:GraphicsObject.java、Point.java、Circle.java、Rectangle.java 以及 Line.java 全部都 compile 到 tw.com.Graphics 套件中 然後來重新 DrawDemo 程式 並可以正確執行 DrawDemo