Java_2 接口 繼承 多態

1.接口的使用

Java語言只支持單重繼承,一個類只能有一個父類。java語言提供接口實現類的多重繼承功能。

1.1 接口的定義

[修飾符]interface  接口名  [extends  父類接口名列表]{
    [public] [static] [final] 變量;
    [public] [abstract] 方法;
}

定義接口的語法格式的參數說明

例:

public interface ICalculate{
final float PI=3.14159f; //定義用於表示圓周率的常量PI
float getArea(float r); //定義一個用於計算面積的方法getArea()
float getCircumfernce(float r); //定義一個用於計算周長的方法getCircumference()
}

1.2 接口的實現

[修飾符] class <類名> [extend 父類名] [implements 接口列表]{
}

實現接口的語法格式的參數說明

在類中實現接口時,方法名、返回值類型、參數的個數及類型必須與接口中的完全一致,並且必須實現接口中的所有方法。

例:實現上述代碼的接口

public class Cire implements ICalculate{
    //定義計算圓面積的方法
    Public float getArea(float r){
        float area = PI*r*r;
        return area;
    }
    //定義計算周長的方法
    public float getCircumference(float r){
        float circumference = 2*PI*r;
        return circumference;
    }
}

在類的繼承中,只能做單重繼承,而實現接口時,一次則可以實現多個接口,每個接口間使用逗號“,”分割,這時就可能出現變量或者方法名衝突的情況。解決該問題時,如果變量衝突,則需要明確指定變量的接口,可以通過“接口名.變量”實現。如果出現方法衝突,則只要實現一個方法即可。

1.3 例 1:圖片的不同格式保存

(1)編寫ImageSaver接口,在該接口中定義save()方法。

public interface ImageSaver{
    void save();  //定義save()方法
}

(2)在項目中創建GIFSaver類,該類實現了ImageSaver接口,在實現save()方法時將圖片保存爲GIF格式。

public class GIFSaver implements ImageSaver{
    @Override
    public void save(){
        System.out.println("將圖片保存爲GIF格式");
    }
}

例 2:爲汽車增加GPS定位功能

(1)在項目中創建Car類,在該類中首先定義兩個屬性,一個是name(表示汽車的名字),另一個是speed(表示汽車的速度),併爲其提供了getXXX()和setXXX()方法,然後通過重寫toSteing()方法來方便輸出Car對象。

public class Car{
    private String name;
    private double speed;
    //省略getXXX()和setXXX()方法
    @Override
    public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append("車名:"+name+",");
        sb.append("速度:"+speed+"千米/小時");
        return sb.toString();
    }
}

(2)在項目中編寫接口GPS,在接口中定義了getLocatiion()方法,用來確定汽車的位置。

public interface GPS{
    Point getLocation();   //提供定位功能
}

(3)在項目中編寫GPSCar類,該類繼承Car並實現GPS接口。在該類中首先實現getLocation()方法,用於實現確定汽車的位置功能,然後重寫toString()方法,用於實現確定汽車位置的功能,然後重寫toString()方法方便輸出GPSCar對象。

public class GPSCar extends Car implements GPS{
    @Override
    public Point getLocation(){
        Point point = new Point();
        point.setLocation(super.getSpeed(),super.getSpeed());
        return point;
    }
    @Override
    public String toString(){
        STringBuilder sb = new StringBuild();
        sb.append(super.toString());
        sb.append(",座標:("+getLocation().x+","+getLocation().y+")");
        return sb.toString();
    }
}

2.類的繼承

2.1 繼承的實現

在Java語言中,繼承通過extends關鍵字來實現。也就是用extends指明當前類是子類,並指明從哪個類繼承而來。即在子類的聲明中,通過extends關鍵字來顯示地指明其父類。

[修飾符] class 子類名 extends 父類名{
    類體
}

子類,父類名必選參數,子類名首字母大寫,父類用於指定要定義的子類繼承與哪個父類。
修飾符,可選參數,用於指定類的訪問權限(public abstract final)

父類Bird

public class Brid{
    String color = "白色";    //顏色
    String skin = "羽毛";     //皮毛
}

子類Pigeon

public class Pigeon extends Bird{
    public static void main(String[] args){
        Pigeon pigeon = new Pigeon();
        System.out.println(pigeon.color);  //輸出成員變量color
    }
}

2.2 繼承中的重寫

重寫是指父子類之間的關係,當子類繼承父類中所有可能被子類訪問的成員方法時,如果子類的方法名與父類的方法名相同,那麼子類就不能繼承父類的方法,此時,稱爲子類的方法重寫了父類的方法。重寫體現了子類補充或者修改父類方法的能力。通過重寫,可以使一個方法在不同的子類中表現出不同的行爲。

例:

(1)創建一個名稱爲Animal的類,在該類聲明的一個成員方法cry()。

public class Animal{
    public Animal(){
    }
    public void cry(){
        Systme.out.println("動物發出叫聲!");
    }
}

(2)創建一個Animal類的子類Dog,在該類中重寫了父類的成員方法cry()

public class Dog extends Animal{
    public Dog(){
    }
    public void cry(){
        System.out.println("狗發出‘汪汪汪’聲!");
    }
}

(3)創建一個Animal類的子類Sheep,在該類中不定義任何方法。

public class Sheep extends Animal{
}

(4)創建一個名稱爲Zoo的類,在該類的main()方法中分別創建子類Dog、Sheep的對象併爲該對象分配內存,然後分別調用各對象的cry()方法。

public class Zoo{
    public static void main(String[] args){
        Dog dog = new Dog();
        System.out.println("執行dog.cry();語句時的輸出結果:");
        dog.cry();
        Sheep sheep = new Sheep();
        System.out.println("執行sheep。cry();語句時的輸出結果:");
        sheep.cry();
    }
}

由於Dog類重寫了父類的方法cry(),所以執行的是子類中的cry()方法,但是Sheep類沒有重寫方法,所以執行的是父類中的cry()方法。

2.3 使用super關鍵字

子類可以繼承父類的非私有成員變量和成員方法(不是以private關鍵字修飾的)作爲自己的成員變量和成員方法。但是,如果子類中聲明的成員變量與父類的成員變量同名,則子類不能繼承父類的成員變量,此時稱子類的成員變量隱藏了父類的成員變量。如果子類中的聲明成員方法與父類的成員方法同名,並且方法的返回值及參數個數和類型也相同,則子類不能繼承父類的成員方法,此時稱子類的成員方法重寫了父類的成員方法。

這時,如果想在子類中訪問父類 中被子類隱藏的成員方法或變量,就可以使用super關鍵字。

super關鍵字主要的兩個用途:

1.調用父類的構造方法

子類可以調用由父類聲明的構造方法。但必須在子類的構造方法中使用super關鍵字來調用

super([參數列表]);

如果父類的構造方法中包括參數,則參數列表爲必選項,用於指定父類構造方法的入口參數。

例:

(1)在項目中創建Beast類,在類中添加一個默認的構造方法和一個帶參數的構造方法。

public class Beast{
    String skin = "";      //成員變量
    public Beast(){        //默認構造方法
    }
    public Beast(String strSkin){  //帶參數的構造方法
        skin = strSkin;
    }
    public void move(){    //成員方法
        System.out.println("跑");
    }
}

(2)如果想在子類Tiger中使用父類的帶參數的構造方法,則需要在子類Tiger的構造方法中進行調用

public class Tiger extends Beast{
    public Tiger(){
        super("條紋");     //使用父類的帶參數的構造方法
    }
}

2.操作被隱藏的成員變量和被重寫的成員方法,也可以使用super關鍵字

super.成員變量名
super.成員方法名([參數列表])

如果想在子類Tiger的方法中改變父類Beast的成員變量skin的值。
super.skin = “條紋”;

如果想在子類中Tiger的方法使用父類Beast的成員方法move()。
super.move();

2.4 例:經理與員工的差異

(1)在項目中創建Employee類,在該類中定義3個屬性,分別是name(表示員工姓名),salary(表示員工的工資)和birthday(表示員工的生日),並分別爲他們定義了getXXX()和setXXX()方法。

import java.util.Date;
public class Employee{
    private String name;
    private double salary;
    private Date birthday;

    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
    }
    public double getSalary(){
        return salary;
    }
    public void setSalary(double salary){
        this.salary = salary;
    }
    public Date getBirthday(){
        return birthday;
    }
    public void setBirthday(Date birthday){
        this.birthday = birthday;
    }
}

(2)在項目中創建一個名稱爲Manager的類,該類繼承自Employee。在該類中,定義了一個bonu域,表示經理的獎金,併爲其設置了getXXX()和setXXX()方法

public class Manager extends Employee{
    private double bonus;
    public double getBonus(){
        return bonus;
    }
    public void setBonus(double bonus){
        this.bonus = bonus;
    }
}

(3)在項目中在創建一個名稱爲Test的類,用於測試。在該類中分別創建Empliyee和Manager對象,併爲其賦值,然後輸出屬性。

import java.util.Date;
public class Test{
    public static main(String[] args){
        Employee empioyee = new Employee();
        employee.setName("張三");
        employee.setSalay(1000);
        employee.setBirthday(new Date());
        Manager manager = new Manager();
        manager.setName("李四");
        manager.setSalary(3000);
        manager.setBirthday(new Date());
        manager.setBonus(2000);

        System.out.println("員工的姓名:"+employee.getName());
        System.oout.println("員工的工資:"+employee.getSalary());
        System.out.println("員工的生日:"+employee.getBirthday());
        System.out.println("經理的姓名:"+manager.getName());
        System.out.println("經理的工資:"+manager.getSalary());
        System.out.println("經理的生日:"+manager.getBithday());
        System.out.println("經理的獎金:"+manager.getBonus());
    }
}

2.5 例:重寫父類中的方法

在繼承一個類後,就可以使用父類中定義的方法,然而父類中的方法可能並不完全適用於子類。此時,如果不想定義新的方法,則可以重寫父類中的方法。

(1)在項目中創建Employee類,在該類中添加getInfo()方法,返回值爲字符串“父類:我是這裏的員工!”。

public class Employee{
    public String getInfo(){
        return"父類:我是這裏的員工!";
    }
}

(2)在包中在創建一個名稱爲Manager的類,該類繼承自Employee。在該類中,重寫getInfo方法。

public class Manager extends Employee{
    @Override
    public String getInfo(){
        return"子類:我是這裏的經理!";
    }
}

3.多態

在Java語言中,通常使用方法的重載和重寫實現類的多態性

重寫之所以具有多態性,是因爲父類的方法在子類中被重寫,子類和父類的方法名稱相同,但完成的功能卻不一樣,所以說,重寫也具有多樣性。

方法的重載是值在一個類中出現多個方法名相同,但參數個數或參數類型不同的方法,則稱爲方法的重載。Java語言在執行具有重載關係的方法時,將根據調用參數的個數和類型區分具體執行的是哪個方法。

例:

public class Calculate{
    final float PI = 3.14159f;

    public float getArea(float r){
        float area = PI*r*r;
        return area;
    }
    public float getArea(float l,float w){
        float area = l*w;
        return area;
    }

    public void draw(int num){
        System.out.println("畫"+num+"個任意形狀的圖形");
    }
    public void draw(String shape){
    System.out.println("畫一個"+shape);
    }
    public static void main(String[] args){
    Calculate calculate = new Calculate();
    float l = 20;
    float w = 30;
    float areaRectangle = calculate.getArea(l,w);
    System.out.println("求長爲"+l+" 寬爲"+w+"的矩形的面積是:"+areaRectangle);
    float r = 7;
    float areaCirc = calculate.getArea(r);
    System.out.println("求半徑爲"+r+"的圓的面積是:"+areaRectangle);
    int num = 7;
    calculate.draw(num);
    calculate.draw("三角形");
    }
}

重載的方法之間並不一定必須有聯繫,但是爲了提高程序的可讀性,一般只重載功能相似的方法。在進行方法的重載時,方法的返回值的類型不能作爲區分方法的標誌。

3.3 簡單的汽車銷售商城

(1)在項目中創建一個抽象類,名稱爲Car,在該類中定義一個抽象方法getInfo()。

public abstract class Car{
    public abstract String getInfo();
}

(2)在項目中創建一個名稱爲BMW的類,該類繼承自Car並實現了其getInfo()方法。

public class BMW extends Car{
    @Override
    public String getInfo(){
        return "BMW";
    }
}

在項目中創建一個名稱爲Benz的類,該類繼承自Car並實現了其getInfo()方法

public class Benz extends Car{
    @Ocerride
    public String getInfo(){
    if(name.equalslgnoreCase("Bmw")){
        return new BMW();
    }else if(name.equalslgnoreCase("Benz")){
        return new Benz();
    }else{
        return null;
    }
    }
}

(5)在項目中創建一個名稱爲Customer的類,用來進行測試,在main()方法中,根據用戶的需求提取不同的汽車。

public class Customer{
    public static void main(String[] aargs){
        System.out.println("顧客要購買BMW:");
        Car bmw = CarFactory.getCar("BMW");
        System.out.println("要提取汽車:"+bmw.getInfo());
        System.out.println("顧客需要購買Benz:");
        Car benz = CarFactory.getCar("Benz");
        System.out.println("提取汽車:"+benz.getInfo());     
    }
}

4.經典範例

4.1 使用Comparable接口自定義排序

在項目中創建Employee類,在該類中首先定義3個屬性,分別爲id(員工的編號)、name(員工的姓名)和age(員工的年齡),然後在構造方法中初始化這3個屬性,最後在實現接口中定義的compareTo()方法,經對象按照編號進行升序排列。

public class Employee inplements Comparable<Employee>{
    private int id;
    private String name;
    prinate int age;
    public Employee(int id,String name,int age){
        this.id = id;
        this.name = name;
        this.age = age;
    }
    @Override
    public int compareTo(Employee o){
        if(id>o.id){
            return 1;
        }else if(id<o.id){
            return -1;
        }
        return 0;
    }
    @Ocerride
    public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append("員工的編號:"+id+",");
        sb.append("員工的姓名:"+name+",");
        sb.append("員工的姓名:"+age);
        return sb.toString();
    }
}

4.2 動態設置類的私有域

爲了保證面向對象的封裝特性,通常會將域設置爲私有的,然後提供對應的getXXX()和setXXX()方法來操作該域。然而利用反射機制,就可以在運行是修改類的私有域。

(1)在項目中創建Student類,在該類中定義4個域及對應的getXXX()和setXXX()方法。這四個域分別對應是id(學生的序號)、name(學生的姓名)、male(學生是否爲男性)和account(學生的賬戶餘額)。

public class Student{
    private int id;
    private String name;
    private boolean male;
    private double account;
    //getXXX()   setXXX()
}

(2)在包中編寫Test類進行測試。在main()方法中,分別爲不同的域設置不同的值,並輸入初始值和信值作爲對比。

public class Test{
    public static void main(String[] args){
        Student student = new Student();
        Class<?>class = student.getClass();
        System.out.println("類的標準名稱:"+clazz.getCanonicalName());
        try{
            Field id = clazz.getDeclaredField("id");
            System.out.println("設置前的id:"+student.getId());
            id.setAccessible(true);
            id.setInt(student,10);
            System.out.println("設置後的id:"+student.getId());

            Field name = clazz.getDeclaredField("name");
            System.out.println("設置前的name:"+student.getName());
            name.setAccessible(true);
            name.set(student,"Java");
            System.out.println("設置後的name:"+student.getName);

            Field male = clazz.getDeclaredField("made");
            System.out.println("設置前的male:"+student.isMale());
            male.setAccessible(true);
            male.setBoolean(student,true);
            System.out.println("設置後的male:"+student.isMale());

            Field account = clazz.getDeclareField("account");
            System.out.println("設置前的account:"+student.getAccount());
        }catch(SecurityException e){
            e.printStackTraace();
        }catch(NoSuchFieldException e){
            e.printStackTRACE();
        }catch(IllegalArgumentException e){
            e.printStackTrace();
        }catch(IllgalAccessException e){
            e.printStackTrace();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章