1. 程式人生 > 其它 >JAVA學習筆記9,抽象類和介面及內部類

JAVA學習筆記9,抽象類和介面及內部類

技術標籤:# java基礎學習筆記java抽象類多型

第九章 抽象類和介面及內部類

一 抽象類和抽象方法

1.1 抽象類

用abstract關鍵字來修飾一個類,這個類叫做抽象類。

  • 此類不能例項化
  • 抽象類中一定有構造器,便於子類例項化時呼叫
  • 開發中,都會提供抽象類的子類,讓子類物件例項化,完成相關的操作

1.2 抽象方法

用abstract來修飾一個方法,該方法叫做抽象方法。

  • 只有方法的宣告,沒有方法的實現。以分號結束
  • 含有抽象方法的類必須被宣告為抽象類。反之,抽象類中可以沒有抽象方法的。
  • 抽象類是用來被繼承的,抽象類的子類必須重寫父類的抽象方法,並提供方法體。若沒有重寫全部的抽象方法,仍為抽象類,需要用abstract修飾。

1.3 abstract使用注意事項

  • 不能用abstract修飾變數、程式碼塊、構造器;
  • 不能用abstract修飾私有方法、靜態方法、final的方法、final的類。

1.4 練習

  1. 編寫工資系統,實現不同型別員工(多型)的按月發放工資。如果當月出現某個Employee物件的生日,則將該僱員的工資增加100元。
  • 定義一個Employee類,該類包含:private成員變數name,number,birthday,其中birthday 為MyDate類的物件;abstract方法earnings();toString()方法輸出物件的name,number和birthday。
  • MyDate類包含:private成員變數year,month,day ;
    toDateString()方法返回日期對應的字串:xxxx年xx月xx日
  • 定義SalariedEmployee類繼承Employee類,實現按月計算工資的員工處理。該類包括:private成員變數monthlySalary;實現父類的抽象方法earnings(),該方法返回monthlySalary值;toString()方法輸出員工型別資訊及員工的name,number,birthday。
  • 參照SalariedEmployee類定義HourlyEmployee類,實現按小時計算工資的員工處理。該類包括:private成員變數wage和hour;實現父類的抽象方法earnings(),該方法返回wage*hour值;toString()方法輸出員工型別資訊及員工的name,number,birthday。
  • 建立Employee變數陣列並初始化,該陣列存放各類僱員物件的引用。利用迴圈結構遍歷陣列元素,輸出各個物件的型別,name,number,birthday,以及該物件生日。當鍵盤輸入本月月份值時,如果本月是某個Employee物件的生日,還要輸出增加工資資訊。
    結果圖
import java.util.Scanner;
public class Test3 {
    public static void main(String[] args) {
        Employee[] emps= new Employee[]{
            new SalariedEmployee("tom", new MyDate(1999, 2, 11), 6000),
            new SalariedEmployee("john", new MyDate(1992, 5, 28), 7000),
            new HourlyEmployee("wang", new MyDate(2000, 8, 1), 60, 31)
        };
        Scanner scan = new Scanner(System.in); 
        System.out.print("輸入月份:");
        int m=scan.nextInt();
        for (int i = 0; i < emps.length; i++) {
            System.out.println(emps[i]);
            if (m==emps[i].getBirthday().getMonth()) {
                System.out.println("今天是你的生日,增加工資100元");
            }
        }
        scan.close();
    }
}
class HourlyEmployee extends Employee {
    private double wage;
    private int hours;

    public HourlyEmployee(String name, MyDate birthday, double wage, int hours) {
        super(name, birthday);
        this.wage = wage;
        this.hours = hours;
    }

    @Override
    public double earnings() {
        return wage*hours;
    }

    @Override
    public String toString() {
        return "HourlyEmployee [名字:"+getName()+"  員工號:"+getNumber()+"  薪水:" + earnings() +"  生日:"+getBirthday() +"]";
    }
    
}
class SalariedEmployee extends Employee {
    private double monthlySalary;
    @Override
    public double earnings() {        
        return monthlySalary;
    }

    public SalariedEmployee(String name, MyDate birthday, double monthlySalary) {
        super(name,birthday);
        this.monthlySalary = monthlySalary;
    }

    @Override
    public String toString() {
        return "SalariedEmployee [名字:"+getName()+"  員工號:"+getNumber()+"  薪水:" + earnings() +"  生日:"+getBirthday() +"]";
    }
    
}

class MyDate{
    private int year;
    private int month;
    private int day;

    public MyDate(int year, int month, int day) {
        this.year = year;
        this.month = month;
        this.day = day;
    }

    @Override
    public String toString() {
        return year+"年" +  month + "月" + day + "日";
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }
    
    
}
abstract class Employee{
    private String name;
    private int number;
    private MyDate birthday;
    public abstract double earnings();
    private static int total=0;
    private static int id=1000;

    public Employee(String name,MyDate birthday) {
        this.name = name;
        this.number = (total++)+id;
        this.birthday = birthday;

    }

    public String getName() {
        return name;
    }


    public int getNumber() {
        return number;
    }

    public MyDate getBirthday() {
        return birthday;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setBirthday(MyDate birthday) {
        this.birthday = birthday;
    }    
}

二 介面interface

介面(interface)是抽象方法和常量值定義的集合。

2.1 介面的特點:

  1. 用interface來定義。
  2. 介面中的所有成員變數都預設是由public static final修飾的。
  3. 介面中的所有抽象方法都預設是由public abstract修飾的。
  4. 介面中沒有構造器。
  5. 介面採用多繼承機制。

2.2 介面語法格式

先寫extends,後寫implements

class SubClass extends SuperClass implements InterfaceA{ } 
interface Person{
    String name="人";//省略public final static
    public final static String con="中國";
    void eat();//省略public abstract
    public abstract void walk();
}
interface Student extends Person{
    void study();
}
class Pupil implements Student{

    @Override
    public void eat() {
    }

    @Override
    public void walk() {
     }

    @Override
    public void study() {
    }

}

2.3 注意事項

  • 一個類可以實現多個介面,介面也可以繼承其它介面。
  • 實現介面的類中必須提供介面中所有方法的具體實現內容,方可例項化。否則,仍為抽象類。
  • 介面的主要用途就是被實現類實現。(面向介面程式設計)
  • 與繼承關係類似,介面與實現類之間存在多型性
  • 介面和類是並列關係,或者可以理解為一種特殊的類。從本質上講,介面是一種特殊的抽象類,這種抽象類中只包含常量和方法的定義(JDK7.0及之前),而沒有變數和方法的實現。

2.4 介面和抽象類之間的對比

  1. 抽象類
  • 包含抽象方法的類
  • 由構造方法、抽象方法、普通方法、常量、變數組成
  • 子類繼承抽象類(extends)
  • 抽象類可以實現多個介面
  • 抽象類有單繼承的侷限
  • 常見的設計模式有模板方法
  1. 介面
  • 主要是抽象方法和全域性常量的集合
  • 由常量、抽象方法、(jdk8.0:預設方法、靜態方法)構成
  • 子類實現介面(implements)
  • 介面不能繼承抽象類,但允許繼承多個介面
  • 常見的設計模式有簡單工廠、工廠方法、代理模式
  1. 相同點
  • 都通過物件的多型性產生例項化物件
  1. 使用建議
    • 如果抽象類和介面都可以使用的話,優先使用介面,避免單繼承的侷限

在開發中,常看到一個類不是去繼承一個已經實現好的類,而是要麼繼承抽象類,
要麼實現介面。

2.5 interface Java 8新特性

  • 靜態方法:使用 static 關鍵字修飾。可以通過介面直接呼叫靜態方法,並執行其方法體。可以在標準庫中找到像Collection/Collections或者Path/Paths這樣成對的介面和類。
  • 預設方法:預設方法使用 default 關鍵字修飾。可以通過實現類物件來呼叫。在已有的介面中提供新方法的同時,還保持了與舊版本程式碼的相容性。比如:Java 8 API中對Collection、List、Comparator等介面提供了豐富的預設方法。
    • 若一個介面中定義了一個預設方法,而另外一個介面中也定義了一個同名同參數的方法(不管此方法是否是預設方法),在實現類同時實現了這兩個介面時,會出現介面衝突。
      • 解決辦法:實現類必須覆蓋介面中同名同參數的方法,來解決衝突。
    • 若一個介面中定義了一個預設方法,而父類中也定義了一個同名同參數的非抽象方法,則不會出現衝突問題。此時遵守:類優先原則。介面中具有相同名稱和引數的預設方法會被忽略。

2.6 練習

  1. 定義一個介面CompareObject用來實現兩個物件的比較。
  • 若返回值是 0 , 代表相等; 若為正數,代表當
    前物件大;負數代表當前物件小
  • 定義一個Circle類,宣告redius屬性,提供getter和setter方法
  • 定義一個ComparableCircle類,繼承Circle類並且實現CompareObject介面。在ComparableCircle類中給出介面中方法compareTo的實現體,用來比較兩個圓的半徑大小。
  • 定義一個測試類InterfaceTest,建立兩個ComparableCircle物件,呼叫compareTo
    方法比較兩個類的半徑大小。
  • 思考 :參照上述做法定義矩形類 Rectangle 和 ComparableRectangle類,在ComparableRectangle類中給出compareTo方法的實現,比較兩個矩形的面積大小。
public class Test4 {
    public static void main(String[] args) {
        ComparableCircle cc1 = new ComparableCircle(1.5);
        ComparableCircle cc2 = new ComparableCircle(1.5);
        System.out.println(cc1.compareTo(cc2));//0
    }
}

class ComparableCircle extends Circle implements CompareObject {
    
    @Override
    public int compareTo(Object o) {
        if(o == null)
            throw new RuntimeException();
        if(this == o)
            return 0;
        if(o instanceof ComparableCircle){
            ComparableCircle cc=(ComparableCircle) o;
            if (cc.getRedius()==this.getRedius()){
                return 0;
            }else if(this.getRedius()<cc.getRedius()){
                return -1;
            }else{
                return 1;
            }
        }
        throw new RuntimeException();
    }

    public ComparableCircle(double redius) {
        super(redius);
    }
    
}
class Circle{
    private double redius;

    public Circle() {
    }

    public Circle(double redius) {
        this.redius = redius;
    }

    public double getRedius() {
        return redius;
    }

    public void setRedius(double redius) {
        this.redius = redius;
    }
    
}
interface CompareObject{
    int compareTo(Object o);
}

三 內部類

3.1 內部類定義

  • 一個類A的定義位於另一個類B的內部,前者A稱為內部類,後者B稱為外部類。
    • Inner class一般用在定義它的類或語句塊之內,在外部引用它時必須給出完整的名稱。
    • Inner class的名字不能與包含它的外部類類名相同;

3.2 分類

  1. 成員內部類(static成員內部類和非static成員內部類)
    • 作為外部類的成員
      • 和外部類不同,Inner class還可以宣告為private或protected;
      • 可以呼叫外部類的結構
      • Inner class 可以宣告為static的,但此時就不能再使用外層類的非static的成員變數;
    • 作為一個類
      • 可以在內部定義屬性、方法、構造器等結構
      • 可以宣告為abstract類 ,因此可以被其它的內部類繼承
      • 可以宣告為final的
      • 編譯以後生成OuterClass$InnerClass.class位元組碼檔案(也適用於區域性內部類)
    • 注意
      1. 非static的成員內部類中的成員不能宣告為static的,只有在外部類或static的成員內部類中才可宣告static成員。
      2. 外部類訪問成員內部類的成員,需要“內部類.成員”或“內部類物件.成員”的方式
      3. 成員內部類可以直接使用外部類的所有成員,包括私有的資料
      4. 當想要在外部類的靜態成員部分使用內部類時,可以考慮內部類宣告為靜態的
        結果圖
public class Test5 {
    private int num = 123;
    public class Inner {
        private int num = 456;
        public void mb(int num) {
            System.out.println(num); // 區域性變數
            System.out.println(this.num); // 內部類物件的屬性
            System.out.println(Test5.this.num); // 外部類物件屬性s
        } 
    }
    public static void main(String args[]) {
        Test5 out = new Test5();
        Test5.Inner inner =  out.new Inner();
        inner.mb(110);
    } 
}
  1. 區域性內部類(不談修飾符)、匿名內部類
  • 區域性內部類的申明
class 外部類{
    方法(){//方法內
        class 區域性內部類{ } 
        }
    {//程式碼塊內
        class 區域性內部類{ } 
    } 
}
  • 區域性內部類使用
    • 只能在宣告它的方法或程式碼塊中使用,而且是先聲明後使用。除此之外的任何地方都不能使用該類
    • 但是它的物件可以通過外部方法的返回值返回使用,返回值型別只能是區域性內部類的父類或父介面型別
  • 區域性內部類的特點
    • 內部類仍然是一個獨立的類,在編譯之後內部類會被編譯成獨立的.class檔案,但是前面冠以外部類的類名和$符號,以及數字編號。
    • 只能在宣告它的方法或程式碼塊中使用,而且是先聲明後使用。除此之外的任何地方都不能使用該類。
    • 區域性內部類可以使用外部類的成員,包括私有的。
    • 區域性內部類可以使用外部方法的區域性變數,但是必須是final的。由區域性內部類和區域性變數的宣告週期不同所致。
    • 區域性內部類和區域性變數地位類似,不能使用public,protected,預設,private
    • 區域性內部類不能使用static修飾,因此也不能包含靜態成員
  • 匿名內部類
    • 不能定義任何靜態成員、方法和類,只能建立匿名內部類的一個例項。一個匿名內部類一定是在new的後面,用其隱含實現一個介面或實現一個類。
    • 格式:
    new 父類構造器(實參列表)|實現介面(){
    //匿名內部類的類體部分
    }
    
    • 匿名內部類的特點
      • 匿名內部類必須繼承父類或實現介面
      • 匿名內部類只能有一個物件
      • 匿名內部類物件只能使用多型形式引用
public class Test5 {
    public static void main(String args[]) {
        Test5 test = new Test5();
        test.getRunnable1().run();//我是區域性內部類
        test.gRunnable2().run();//我是匿名區域性內部類
    }
    
    public Runnable getRunnable1() {
        class Runn implements Runnable{
            @Override
            public void run() {
                System.out.println("我是區域性內部類");
            }
        }
        return new Runn();        
    }
    public Runnable gRunnable2() {
        return new Runnable(){
            @Override
            public void run() {
                System.out.println("我是匿名區域性內部類");
            }
        };
    }
}
Runnable{
            @Override
            public void run() {
                System.out.println("我是區域性內部類");
            }
        }
        return new Runn();        
    }
    public Runnable gRunnable2() {
        return new Runnable(){
            @Override
            public void run() {
                System.out.println("我是匿名區域性內部類");
            }
        };
    }
}