[Java入門]抽象類和接口
- 1. 抽象類
- 1.1 抽象類的概念
- 1.2 抽象類語法
- 1.3 抽象類特性
- 1.4 抽象類的作用
- 2. 接口
- 2.1 接口的概念
- 2.2 語法規則
- 2.3 接口使用
- 2.4 接口特性
- 2.5 實現多個接口
- 2.6 接口間的繼承
- 2.7 抽象類和接口的區別
- 3. Object類
- 3.1 獲取對象信息
- 3.2 對象比較equals方法
1. 抽象類
1.1 抽象類的概念
在面向對象的概念中,所有的對象都是通過類來描繪的,但是反過來,并不是所有的類都是用來描繪對象的,如果一個類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類就是抽象類。 比如:
【說明】
- 從圖中可知,矩形類、圓形類、三角形類都屬于圖形類,所以三者都與Shape類是繼承關系
- 由于Shape不是具體的圖形,該類雖然存在draw方法,但無法畫出具體圖形,即draw方法無法具體實現
- 因此Shape類中沒有足夠的信息來描繪自己的對象,所以Shape類就能被設計為抽象類
【說明】
- 再來看這個圖,很明顯可以看出Student類、Teacher類、Waiter類都與Person類是繼承關系
- 由于Person不是具體的人,所以無法確定職業,即使它存在career方法,那也無法具體的實現該方法
- 因此Person類中沒有足夠的信息來描繪自己的對象,career方法也無法具體的實現,所以該類就可以被設計為抽象類
從上面兩個例子中,兩個父類Shape類和Person類 的 draw方法和career方法是無法具體實現的,只有它們的子類才會有具體的實現,像這種沒有具體實現的方法,通常設計為抽象方法(abstract method),而包含抽象方法的類稱為 抽象類(abstract class)。
1.2 抽象類語法
在Java中,如果一個類被 abstract 修飾,那么這個類被稱為抽象類,而在抽象類中,存在被 abstract 修飾的方法,那么這個方法就被稱為 抽象方法。
//抽象類: 在普通類前用 abstract 修飾
//抽象類也是類 也可以定義成員變量 和 方法
public abstract class Person {public String name;public int age;public void chat(){System.out.println("在聊天!");}//抽象方法:在普通方法前用 abstract 修飾//抽象方法 可以沒有方法體public abstract void career();
}
注意:抽象類也是類,內部可以包含普通方法和屬性,甚至構造方法
1.3 抽象類特性
- 抽象類被 abstract 修飾
- 抽象類跟普通類一樣,也可以包含普通方法和屬性,還有構造方法
- 抽象方法被 abstract 修飾,且必須在抽象類中
- 抽象方法不能被 final 和 static 修飾,因為要被重寫
- private 和 abstract 不能同時存在
看報錯提示:private abstract void career();//Illegal combination of modifiers: 'abstract' and 'private'public static abstract void career();//Illegal combination of modifiers: 'abstract' and 'static'public final abstract void career();//Illegal combination of modifiers: 'final' and 'abstract'
- 抽象類被設計出來就是為了被繼承
- 普通類繼承抽象類 必須重寫抽象類中所有的抽象方法,否則就將普通類改為抽象類
abstract class Person {public abstract void career();
}class Student extends Person{//@Overridepublic void career() {System.out.println("我的職業是學生!");}
}
- 當普通類B不想重寫時,就將普通類B變成抽象類B,那么當普通類C想繼承抽象類B時,必須要重寫抽象類B 和 抽象類A 的所有抽象方法
abstract class Person {public abstract void career();
}public abstract class Student extends Person{@Overridepublic abstract void career();public abstract void homework();
}class StudentOfPrimary extends Student{@Overridepublic void career() {System.out.println("我是小學生!");}@Overridepublic void homework() {System.out.println("我在寫小學生作業!");}
}
- 抽象類中不一定包含抽象方法,但是有抽象方法的類一定是抽象類
- 抽象類中可以有構造方法,供子類創建對象時,初始化父類的成員變量
1.4 抽象類的作用
抽象類本身不能被實例化, 要想使用, 只能創建該抽象類的子類. 然后讓子類重寫抽象類中的抽象方法.
有些同學可能會說了, 普通的類也可以被繼承呀, 普通的方法也可以被重寫呀, 為啥非得用抽象類和抽象方法呢?
確實如此. 但是使用抽象類相當于多了一重編譯器的校驗.
使用抽象類的場景就如上面的代碼, 實際工作不應該由父類完成, 而應由子類完成. 那么此時如果不小心誤用成父類了, 使用普通類編譯器是不會報錯的. 但是父類是抽象類就會在實例化的時候提示錯誤, 讓我們盡早發現問題.
很多語法存在的意義都是為了 “預防出錯”, 例如我們曾經用過的 final 也是類似. 創建的變量用戶不去修改, 不就相當于常量嘛? 但是加上 final 能夠在不小心誤修改的時候, 讓編譯器及時提醒我們.
充分利用編譯器的校驗, 在實際開發中是非常有意義的.
2. 接口
2.1 接口的概念
接口就是公共的行為規范標準,大家在實現時,只要符合規范標準,就可以通用。在Java中,接口可以看成是:多個類的公共規范,是一種引用數據類型。
2.2 語法規則
接口的定義格式與定義類的格式基本相同,將class關鍵字換成 interface 關鍵字,就定義了一個接口。
public interface Drawable {public static final int a = 0;//接口中定義變量時 public static final 是固定存在的 可以不寫public abstract void draw();//接口中定義方法時 public abstract 是固定存在的 可以不寫
}
注意:接口中的方法都是抽象方法
提示:
- 創建接口時, 接口的命名一般以大寫字母 I 開頭.
- 接口的命名一般使用 “形容詞” 詞性的單詞.
- 阿里編碼規范中約定, 接口中的方法和屬性不要加任何修飾符號, 保持代碼的簡潔性.
2.3 接口使用
接口不能直接使用,必須要有一個"實現類"來"實現"該接口,實現接口中的所有抽象方法。 模板:
public class 類名 implements 接口名{
}
比如:
public interface Drawable {public abstract void draw();
}public abstract class Shape {public abstract void getArea();
}public class Circle extends Shape implements Drawable{@Overridepublic void getArea() {System.out.println("圓的面積");}@Overridepublic void draw() {System.out.println("圓");}
}public class Main {public static void main(String[] args) {Circle circle = new Circle();circle.getArea();circle.draw();}
}
2.4 接口特性
- 接口中定義變量時 public static final 是固定存在的 可以不寫
- 接口中定義方法時 public abstract 是固定存在的 可以不寫
- 接口中的方法都是抽象方法
- 接口中的方法不能在接口中實現,只能在實現該接口的類中實現
- 若接口中的方法被static修飾,那么可以在接口中有具體的實現
public interface Drawable {public static final int a = 0;public abstract void draw();static void draw1(){System.out.println("畫畫");}
}
//Circle類 實現 Drawable接口
public class Circle implements Drawable{@Overridepublic void draw() {System.out.println("圓");}
}
- 接口是引用類型,但不能通過 new 來實例化
Drawable drawable = new Drawable();
報錯:'Drawable' is abstract; cannot be instantiated
- 在重寫接口中的方法時,必須加上public
- 接口中不能有構造方法和代碼塊
2.5 實現多個接口
在Java中,類和類之間是單繼承的,一個類只能有一個父類,即Java中不支持多繼承,但是一個類可以實現多個接口。舉例說明:
一個動物類
public abstract class Animal {private String name;private int age;public Animal(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}public abstract void makeSound();
}兩個接口
public interface Swimming {public void swim();
}public interface Trainbale {public void train();
}兩個子類:
貓
public class Cat extends Animal implements Trainbale{public Cat(String name, int age) {super(name, age);}@Overridepublic void makeSound() {System.out.println(getName() + "喵喵喵!");}@Overridepublic void train() {System.out.println(getName() + "可被訓練!");}
}狗(實現了兩個接口)
public class Dog extends Animal implements Trainbale,Swimming{public Dog(String name, int age) {super(name, age);}@Overridepublic void makeSound() {System.out.println(getName() + "旺旺旺");}@Overridepublic void train() {System.out.println(getName() + "可被訓練!");}@Overridepublic void swim() {System.out.println(getName() + "會游泳!");}
}
從代碼可以看出,子類狗繼承了Animal類實現了兩個接口,這是根據它的特性實現的。這是Java中最常見的的用法:一個類繼承一個父類實現多個接口
繼承表達的含義是 is - a 語義, 而接口表達的含義是 具有 xxx 特性 .
- 貓是一種動物,具有可被訓練的特性.
- 狗是一種動物,具有會游泳、可被訓練的特性.
這樣設計有什么好處呢? 時刻牢記多態的好處, 讓程序猿忘記類型. 有了接口之后, 類的使用者就不必關注具體類型,而只關注某個類是否具備某種能力.
2.6 接口間的繼承
在Java中,類與類之間可以繼承,類與接口之間可以實現,那接口與接口之間呢?是可以繼承的,且可以多繼承,可以借助接口達到多繼承的目的!
public interface Flying {public void fly();
}
public interface Trainbale {public void train();
}
//接口與接口之間也是通過 extends 繼承,并非用 implements
//若繼承的接口中已寫有方法,那么本接口可以不用添加特有方法
public interface Merge extends Flying,Trainbale{
}//Bird類 實現了 Merge接口 那么就要重寫train、fly方法
public class Bird implements Merge{@Overridepublic void train() {System.out.println("可被訓練!");}@Overridepublic void fly() {System.out.println("會飛!");}
}
接口之間的繼承相當于把多個接口合并起來。
2.7 抽象類和接口的區別
抽象類和接口都是 Java 中多態的常見使用方式. 都需要重點掌握. 同時又要認清兩者的區別(重要!!! 常見面試題).
核心區別: 抽象類中可以包含普通方法和普通字段, 這樣的普通方法和字段可以被子類直接使用(不必重寫), 而接口中不能包含普通方法, 子類必須重寫所有的抽象方法.
抽象類存在的意義是為了讓編譯器更好的校驗, 像 前面提到的Animal、Shape等 這樣的類我們并不會直接使用, 而是使用它的子類.萬一不小心創建了 Animal 或 Shape的實例, 編譯器會及時提醒我們.
3. Object類
Object是Java默認提供的一個類。Java里面除了Object類,所有的類都是存在繼承關系的。默認會繼承Object父類。即所有類的對象(無論是Java自帶的還是自己設計創建的)都可以使用Object的引用進行接收。
public class A {
}public class Test {public static void main(String[] args) {A a = new A(); //自己設計的類String str = new String("123");//Java中已存在的類func(a);func(str);}
public static void func(Object o){System.out.println(o);}
}//打印結果
demo5.A@119d7047
123
所以在開發之中,Object類是參數的最高統一類型。
但是Object類也存在一些定義好的方法。如下:
對于 Object 類中已存在的所有方法都需要熟悉掌握。
我們先來熟悉 toString()、 equals() 兩個方法
3.1 獲取對象信息
如果要打印對象中的內容,可以直接重寫Object類中的toString()方法,內容可以根據自己的要求修改。
這是Object類中的toString()方法具體內容,可以看出是默認打印地址
3.2 對象比較equals方法
在Java中,== 進行比較時:
a.如果 == 左右兩側是基本類型變量,比較的是變量中值是否相同
b.如果 == 左右兩側是引用類型變量,比較的是引用變量地址是否相同
c.如果要比較對象中內容,必須重寫Object中的equals方法,因為equals方法默認也是按照地址比較的:
class Person{private String name ;private int age ;public Person(String name, int age) {this.age = age ;this.name = name ;}
}public class Test {public static void main(String[] args) {Person p1 = new Person("gaobo", 20) ;Person p2 = new Person("gaobo", 20) ;int a = 10;int b = 10;System.out.println(a == b); // 輸出trueSystem.out.println(p1 == p2); // 輸出false 因為是兩個對象,是兩個不同的地址System.out.println(p1.equals(p2)); // 輸出false}
}
未重寫的equals() 方法的內容就是 比較兩對象地址是否相同。
把Person類的equals方法重寫后,然后比較:
class Person{private String name ;private int age ;public Person(String name, int age) {this.age = age ;this.name = name ;}public boolean equals(Object obj) {if (obj == null) {return false ;}if(this == obj) {return true ;}// 若不是Person類對象 返回 falseif (!(obj instanceof Person)) {return false ;}Person person = (Person) obj ; // 向下轉型,比較屬性值return this.name.equals(person.name) && this.age==person.age ;}
}//結果
System.out.println(a == b); // 輸出true
System.out.println(p1 == p2); // 輸出false
System.out.println(p1.equals(p2)); // 輸出true
結論:比較對象中內容是否相同的時候,一定要重寫equals方法。