在展示範例之前,先介紹一些相關的概念和注意點,這是抽象類和接口的基礎預備知識。
在繼承的層次結構中,随着每個新子類的出現,類會變的越來越明确和具體。如果從一個子類追溯到父類,類就會變得更通用、更加不明确。類的設計應該確定父類包含子類的共同特征。
有時候,一個父類設計的非常抽象,以至于它沒有任何具體的執行個體。這樣的類稱為抽象類。
抽象類中不實作,隻聲明的方法稱為抽象方法。在方法頭中用abstract修飾符表示。在類頭使用abstract修飾符表示該類為抽象類。在UML類圖中,抽象類和方法的名字用斜體表示。
抽象類和正常類很像,但是不能使用new操作符建立它的執行個體。抽象方法隻有定義而沒有實作。它的實作由子類提供。一個包含抽象方法的類必須聲明為抽象類。
抽象類的構造方法定義為protected,因為它隻被子類使用。建立一個具體子類的執行個體時,它的父類構造方法被調用以初始化父類中定義的資料域。
關于抽象類的幾個注意點:
1)抽象方法不能包含在非抽象類中。如果抽象父類的子類不能實作所有的抽象方法,那麼子類必須定義為抽象的。換句話說,在抽象類擴充的非抽象類中,必須實作所有的抽象方法。并且,抽象方法是非靜态的。
2)包含抽象對象的類必須是抽象的。但是,可以定義一個不包含抽象方法的抽象類。在這種情況下,不能使用new操作符建立該類的執行個體。這種類是用來定義新子類的基類的。
3)即使子類的父類是具體的,這個子類也可以是抽象的。
4)子類可以覆寫父類的方法并将它定義為abstract。這是很少見的,但是它在當父類的方法實作在子類中變得不合法時是很有用的。在這種情況下,子類必須定義為abstract。
5)不能使用new操作符從一個抽象類建立一個執行個體,但是抽象類可以用作一種資料類型。
接口是一種與類相似的結構,隻包含常量和抽象方法。它在很多方面都與抽象類很相似,但是它的目的是指明多個對象的共同行為。如,使用正确的接口,可以指明這些對象是可以比較的、可食用的或可克隆的。
Java中,接口被看作是一種特殊的類。就像正常類一樣,每個接口都被編譯為獨立的位元組碼檔案。與抽象類相似,不能使用new操作符建立接口的執行個體,但是大多數情況下,使用接口或多或少有點像使用抽象類。如可以使用接口作為引用變量的資料類型或類型轉換的結果等等。
接口和抽象類的差別
或多或少可以使用和抽象類一樣的方式使用接口,但是定義一個接口和定義一個抽象類有所不同,如下表所示:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcJTSE90drR0TpxGSlZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39zN0gzMwgzM5EjNwEDM1EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
Java隻允許為類的擴充做單一繼承,但是允許使用接口做多重擴充,如下所示的定義是合法的。
接口可以擴充其他接口而不是類。一個類可以擴充它的父類同時實作多個接口 。
所有的類共享同一個根類Object,但是接口沒有共同的根。與類類似,接口也可以定義一種類型。一個接口類型的變量可以引用任何實作該接口的類的執行個體。如果一個類實作了一個接口,那麼這個接口就類似于該類的一個父類。可以将接口當作一種資料類型使用,将接口類型的變量轉換成他的子類,反過來也可以。
範例一:抽象類的使用範例。
運作效果如圖:
實作的源代碼如下所示:
package Blog;
public class blogTryProject {
public static void main(String[] args) {
TestChouXiang tcx = new TestChouXiang();
tcx.main(args);
}
}
//抽象類測試程式
class TestChouXiang{
public static void main(String[]args){
Rectangle rt = new Rectangle(4,5);
System.out.println(rt.toString());
System.out.println("Rectangle 的面積為:"+rt.getArea());
System.out.println("Rectangle 的周長為:"+rt.getPerimeter());
Circle c = new Circle(3);
System.out.println(c.toString());
System.out.println("Circle 的面積為:"+c.getArea());
System.out.println("Circle 的周長為:"+c.getPerimeter());
}
}
//抽象類GeometricObject
abstract class GeometricObject{
private String color = "White";
private boolean filled;
private java.util.Date dateCreated;
protected GeometricObject(){
dateCreated = new java.util.Date();
}
protected GeometricObject(String color,boolean filled){
dateCreated = new java.util.Date();
this.color = color;
this.filled = filled;
}
public String getColor(){
return color;
}
public void setColor(String color){
this.color = color;
}
public void setFilled(boolean filled){
this.filled = filled;
}
public java.util.Date getDateCreated(){
return dateCreated;
}
public String toString(){
return "created on "+dateCreated+"\ncolor: "+color+"\t\tfilled: "+filled;
}
public abstract double getArea();
public abstract double getPerimeter();
}
//矩形類
class Rectangle extends GeometricObject{
private double width;
private double height;
public Rectangle(){
}
public Rectangle(double width,double height){
this.width = width;
this.height = height;
}
public Rectangle(double width,double height,String color,boolean filled){
this.width = width;
this.height = height;
setColor(color);
setFilled(filled);
}
public double getWidth(){
return width;
}
public void setWidth(double width){
this.width = width;
}
public double getHeight(){
return height;
}
public void setHeight(double height){
this.height = height;
}
public double getArea(){
return width * height;
}
public double getPerimeter(){
return 2 * (width + height);
}
public String toString(){
return super.toString()+"\n寬為:"+getWidth()+"\t\t長為:"+getHeight();
}
}
//圓類
class Circle extends GeometricObject{
private double radius;
public Circle(){
}
public Circle(double radius){
this.radius = radius;
}
public Circle(double radius,String color,boolean filled){
this.radius = radius;
setColor(color);//這裡不能用this.color = color;因為它是父類的私有資料域,在GeometricObject
//類外隻能通過通路器和修改器對它進行操作
setFilled(filled);
}
public double getRadius(){
return radius;
}
public void setRadius(double radius){
this.radius = radius;
}
public double getArea(){
return radius * radius * Math.PI;
}
public double getPerimeter() {
return 2 * radius * Math.PI;
}
public double getDiameter(){
return 2 * radius;
}
public String toString(){
return super.toString()+"\n半徑為: "+radius;
}
}
範例二:接口的簡單範例。
運作效果如圖所示:
實作的源代碼如下所示:
package Blog;
public class blogTryProject {
public static void main(String[] args) {
TestEdible te = new TestEdible();
te.main(args);
}
}
//接口的簡單範例
interface Edible{
public abstract String howToEat();
}
class TestEdible{
public static void main(String[]args){
Object[] objects = {new Tiger(),new Chicken(),new Apple(),new Orange()};
for(int i = 0;i < objects.length;i++)
if(objects[i] instanceof Edible)
System.out.println(((Edible)objects[i]).howToEat());
}
}
class Animal{
}
class Chicken extends Animal implements Edible{
public String howToEat(){
return "Chicken: Fry it";
}
}
class Tiger extends Animal{
}
abstract class Fruit implements Edible{
}
class Apple extends Fruit{
public String howToEat(){
return "Apple: Make apple cider";
}
}
class Orange extends Fruit{
public String howToEat(){
return "Orange: Make orange juice";
}
}
範例三:監聽器接口(ActionListener),克隆接口(Cloneable),可比較接口(Comparable)。
ActionListener
運作效果如圖所示:
實作的源代碼如下所示:
package Blog;
import javax.swing.*;
public class blogTryProject {
public static void main(String[] args) {
HandleEvent he = new HandleEvent();
he.main(args);
}
}
//ActionListener監聽器接口
class HandleEvent extends JFrame{
public HandleEvent(){
JButton jbtOk = new JButton("Ok");
JButton jbtCancel = new JButton("Cancel");
JPanel p = new JPanel();
p.add(jbtOk);
p.add(jbtCancel);
add(p);
OkListenerClass listener1 = new OkListenerClass();
CancelListenerClass listener2 = new CancelListenerClass();
//條件二:使用source.addActionListener(listener)注冊源對象
jbtOk.addActionListener(listener1);
jbtCancel.addActionListener(listener2);
}
public static void main(String[]args){
JFrame frame = new HandleEvent();
frame.setTitle("Handle Event");
frame.setSize(200, 150);
frame.setLocation(200, 100);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
//條件一:使得OkListenerClass成為監聽器類的執行個體,并實作其方法
class OkListenerClass implements java.awt.event.ActionListener{
//事件actionPerformed方法的實作
public void actionPerformed(java.awt.event.ActionEvent e){
System.out.println("Ok button clicked");
}
}
class CancelListenerClass implements java.awt.event.ActionListener{
public void actionPerformed(java.awt.event.ActionEvent e){
System.out.println("Cancel button clicked");
}
}
Cloneable(注意克隆不成功會有異常抛出,可參考《Java基本功練習十八》)
運作效果如圖所示:
實作的源代碼如下所示:
package Blog;
public class blogTryProject {
public static void main(String[] args) throws CloneNotSupportedException {
House house = new House(12,89);
house.main(args);
}
}
//Cloneable接口範例
class House implements Cloneable,Comparable{
private int id;
private double area;
private java.util.Date whenBuilt;
public House(int id,double area){
this.id = id;
this.area = area;
whenBuilt = new java.util.Date();
}
public int getId(){
return id;
}
public double getArea(){
return area;
}
public java.util.Date getWhenBuilt(){
return whenBuilt;
}
//覆寫Object中保護類型的clone()方法,并增強其可見性為public
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
public int compareTo(Object o){
if(area > ((House)o).area)
return 1;
else if(area < ((House)o).area)
return -1;
else
return 0;
}
public String toString(){
return "House的資訊,ID為:"+id+"\t面積為:"+area;
}
public boolean equals(Object o){
if(id == ((House)o).id && area == ((House)o).area)
return true;
else
return false;
}
public static void main(String[]args) throws CloneNotSupportedException{
House house1 = new House(502, 123);
House house2 = new House(503, 223);
House house3 = (House)house1.clone();
System.out.println("house1的資訊為:"+house1.toString());
System.out.println("house2的資訊為:"+house2.toString());
System.out.println("house3的資訊為:"+house3.toString());
System.out.println("house1 與 house2 的面積大小比較:"+house1.compareTo(house2));
System.out.println("house1 與 house3 是否内容相同? "+house1.equals(house3));
System.out.println("house1 與 house3 是否引用變量相同? "+(house1 == house3));
}
}
Comparable
運作效果如圖所示:
實作的源代碼如下所示:
package Blog;
public class blogTryProject {
public static void main(String[] args){
TestComparableRectangle tcr = new TestComparableRectangle();
tcr.main(args);
}
}
//Comparable接口實作範例
/*
Comparable的接口定義如下
package java.lang;
public interface Comparable{
public int compareTo(Object o);
}
*/
class ComparableRectangle extends Rectangle implements Comparable{
//構造函數
public ComparableRectangle(double width,double height){
super(width,height);
}
//Comparable接口的實作
public int compareTo(Object o){
if(getArea() > ((ComparableRectangle)o).getArea())
return 1;
else if(getArea() < ((ComparableRectangle)o).getArea())
return -1;
else
return 0;
}
//利用接口新定義一個比較兩個矩形面積的靜态方法
public static Comparable max(Comparable o1,Comparable o2){
if(o1.compareTo(o2) > 0)
return o1;
else
return o2;
}
}
//測試Comparable接口程式
class TestComparableRectangle{
public static void main(String[]args){
ComparableRectangle tcr1 = new ComparableRectangle(2, 5);
ComparableRectangle tcr2 = new ComparableRectangle(3, 6);
System.out.println("\nComparable接口示範範例");
System.out.println("兩個矩形的資訊如下");
System.out.println(tcr1.toString());
System.out.println(tcr2.toString());
System.out.println("tcr1 和 tcr2 比較結果為:"+tcr1.compareTo(tcr2));
System.out.println("面積大者的資訊如下\n"+tcr1.max(tcr1, tcr2));
}
}
//矩形類
class Rectangle extends GeometricObject{
private double width;
private double height;
public Rectangle(){
}
public Rectangle(double width,double height){
this.width = width;
this.height = height;
}
public Rectangle(double width,double height,String color,boolean filled){
this.width = width;
this.height = height;
setColor(color);
setFilled(filled);
}
public double getWidth(){
return width;
}
public void setWidth(double width){
this.width = width;
}
public double getHeight(){
return height;
}
public void setHeight(double height){
this.height = height;
}
public double getArea(){
return width * height;
}
public double getPerimeter(){
return 2 * (width + height);
}
public String toString(){
return super.toString()+"\n寬為:"+getWidth()+"\t\t長為:"+getHeight();
}
}
//抽象類GeometricObject
abstract class GeometricObject{
private String color = "White";
private boolean filled;
private java.util.Date dateCreated;
protected GeometricObject(){
dateCreated = new java.util.Date();
}
protected GeometricObject(String color,boolean filled){
dateCreated = new java.util.Date();
this.color = color;
this.filled = filled;
}
public String getColor(){
return color;
}
public void setColor(String color){
this.color = color;
}
public void setFilled(boolean filled){
this.filled = filled;
}
public java.util.Date getDateCreated(){
return dateCreated;
}
public String toString(){
return "created on "+dateCreated+"\ncolor: "+color+"\t\tfilled: "+filled;
}
public abstract double getArea();
public abstract double getPerimeter();
}
範例四:包裝類範例。Java提供一個友善的辦法,将基本資料類型并入對象或包裝成對象,稱為包裝類。
運作效果如圖所示:
實作的源代碼如下所示:
package Blog;
public class blogTryProject {
public static void main(String[] args){
GenericSort gc = new GenericSort();
gc.main(args);
}
}
//對象數組排序範例
class GenericSort{
public static void main(String[]args){
System.out.println("對象數組排序範例");
Integer[] intArray = {new Integer(2),new Integer(4),new Integer(3)};
Double[] doubleArray = {new Double(3.4),new Double(1.3),new Double(-22.1)};
Character[] charArray = {new Character('a'),new Character('J'),new Character('r')};
String[] stringArray = {"Tom","John","Fred"};
sort(intArray);
sort(doubleArray);
sort(charArray);
sort(stringArray);
System.out.println("Sorted Integer objects:");
printList(intArray);
System.out.println("Sorted Double objects:");
printList(doubleArray);
System.out.println("Sorted Character objects:");
printList(charArray);
System.out.println("Sorted String objects:");
printList(stringArray);
}
public static void sort(Comparable[] list){
Comparable currentMin;
int currentMinIndex;
for(int i = 0;i < list.length - 1;i++){
currentMin = list[i];
currentMinIndex = i;
for(int j = i + 1;j < list.length;j++){
if(currentMin.compareTo(list[j]) > 0){
currentMin = list[j];
currentMinIndex = j;
}
}
if(currentMinIndex != i){
list[currentMinIndex] = list[i];
list[i] = currentMin;
}
}
}
public static void printList(Object[] list){
for(int i = 0;i < list.length;i++)
System.out.print(list[i]+" ");
System.out.println();
}
}
範例五:有理數類的加減乘除實作。
運作效果如圖所示:
實作的源代碼如下所示:
package Blog;
public class blogTryProject {
public static void main(String[] args){
TestRational tr = new TestRational();
tr.main(args);
}
}
//有理數類測試
class TestRational{
public static void main(String[]args){
Rational r1 = new Rational(4,2);
Rational r2 = new Rational(2,3);
System.out.println(r1+" + "+r2+" = "+r1.add(r2));
System.out.println(r1+" - "+r2+" = "+r1.subtract(r2));
System.out.println(r1+" * "+r2+" = "+r1.multiply(r2));
System.out.println(r1+" / "+r2+" = "+r1.divide(r2));
System.out.println(r2+" is "+r2.doubleValue());
}
}
//有理數類實作
class Rational extends Number implements Comparable{
private long numerator = 0;
private long denominator = 1;
public Rational(){
this(0,1);
}
public Rational(long numerator,long denominator){
long gcd = gcd(numerator,denominator);
this.numerator = ((denominator > 0) ? 1 : -1) * numerator / gcd;
this.denominator = Math.abs(denominator) / gcd;
}
private static long gcd(long n,long d){
long n1 = Math.abs(n);
long n2 = Math.abs(d);
int gcd = 1;
for(int k = 1;k <= n1 && k <= n2;k++){
if(n1 % k == 0 && n2 % k == 0)
gcd = k;
}
return gcd;
}
public long getNumerator(){
return numerator;
}
public long getDenominator(){
return denominator;
}
public Rational add(Rational secondRational){
long n = numerator * secondRational.getDenominator() +
denominator * secondRational.getNumerator();
long d = denominator * secondRational.getDenominator();
return new Rational(n,d);
}
public Rational subtract(Rational secondRational){
long n = numerator * secondRational.getDenominator() -
denominator * secondRational.getNumerator();
long d = denominator * secondRational.getDenominator();
return new Rational(n,d);
}
public Rational multiply(Rational secondRational){
long n = numerator * secondRational.getNumerator();
long d = denominator * secondRational.getDenominator();
return new Rational(n,d);
}
public Rational divide(Rational secondRational){
long n = numerator * secondRational.getDenominator();
long d = denominator * secondRational.numerator;
return new Rational(n,d);
}
public String toString(){
if(denominator == 1)
return numerator +"";
else
return numerator+"/"+denominator;
}
public boolean equals(Object parm1){
if((this.subtract((Rational)(parm1))).getNumerator() == 0)
return true;
else
return false;
}
public int intValue(){
return (int)doubleValue();
}
public float floatValue(){
return (float)doubleValue();
}
public double doubleValue(){
return numerator * 1.0 / denominator;
}
public long longValue(){
return (long)doubleValue();
}
public int compareTo(Object o){
if((this.subtract((Rational)o)).getNumerator() > 0)
return 1;
else if((this.subtract((Rational)o)).getNumerator() < 0)
return -1;
else
return 0;
}
}
範例六:接口和抽象類簡單執行個體的實作對比。
運作效果如圖所示:
實作的源代碼如下所示:
package Blog;
public class blogTryProject {
public static void main(String[] args){
ChouXiangAnimal cxa = new ChouXiangAnimal();
cxa.main(args);
JieKouAnimal jka = new JieKouAnimal();
jka.main(args);
}
}
//接口和抽象類的優劣比較
//抽象類實作的方式
class ChouXiangAnimal{
public static void main(String[]args){
//抽象類實作Animal
System.out.println("抽象類實作Animal");
NewAnimal animal = new NewChicken();
eat(animal);
animal = new Duck();
eat(animal);
}
public static void eat(NewAnimal animal){
System.out.println(animal.howToEat());
}
}
abstract class NewAnimal{
public abstract String howToEat();
}
class NewChicken extends NewAnimal{
public String howToEat(){
return "NewChicken: Fry it";
}
}
class Duck extends NewAnimal{
public String howToEat(){
return "Duck: Roast it";
}
}
//接口實作方式
class JieKouAnimal{
public static void main(String[]args){
//接口實作的Animal
System.out.println("接口實作的Animal");
NewEdible stuff = new NewChicken1();
eat(stuff);
stuff = new Duck1();
eat(stuff);
stuff = new Broccoli();
eat(stuff);
}
public static void eat(NewEdible stuff){
System.out.println(stuff.howToEat());
}
}
interface NewEdible{
public String howToEat();
}
class NewChicken1 implements NewEdible{
public String howToEat(){
return "NewChicken1: Fry it";
}
}
class Duck1 implements NewEdible{
public String howToEat(){
return "Duck1: Fry it";
}
}
class Broccoli implements NewEdible{
public String howToEat(){
return "Broccoli: Stir-fry it";
}
}