因?yàn)镴ava中繼承具有單一性锤岸,只能繼承一個(gè)父類,所以為了處理這種局限性,Java又提供了接口击敌。
接口interface
是一個(gè)比抽象類還要抽象的類,因?yàn)槠鋬?nèi)部的所有方法全部都是抽象方法拴事。
同時(shí)接口和類的關(guān)系也不再是繼承(extends)而是實(shí)現(xiàn)(implements)
接口的特點(diǎn):
- 只能有抽象方法
- 只能有常量沃斤,默認(rèn)使用
public static final
修飾 - 方法默認(rèn)就被
public abstract
修飾,并且也只能被public abstract
修飾 - 一樣的,接口也不能實(shí)例化
- 類和接口的關(guān)系不再是extends刃宵,而是implements衡瓶,并且也要實(shí)現(xiàn)它所有的方法(@Override)
public class InterfaceDemo {
public static void main(String[] args) {
Cat c = new Cat();
c.eat(); // 貓吃魚
System.out.println(Animal.num); // 3 可以直接訪問接口內(nèi)部的常量
}
}
interface Animal { // 聲明接口和聲明類很相似
public static final int num = 3; // 接口內(nèi)部只支持使用常量,并且默認(rèn)使用public static final來修飾 當(dāng)然可以直接寫出int num = 3;
public abstract void eat(); // 內(nèi)部的方法默認(rèn)使用public abstract修飾牲证,同時(shí)也只允許使用public abstract修飾哮针,也可以直接寫成void eat();
}
class Cat implements Animal { // 類和接口的關(guān)系是implements
@Override
public void eat() { // 必須要重寫接口中的所有方法
System.out.println("貓吃魚");
}
}
梳理一下接口和類之間的各種關(guān)系:
- 類和類:
extends
, 單一繼承,多層繼承 - 類和接口:
implements
, 多實(shí)現(xiàn)(可以實(shí)現(xiàn)多個(gè)接口) - 接口和接口:
extends
坦袍,接口之間不可以實(shí)現(xiàn)只可以繼承十厢,同時(shí)也是單一繼承,多層繼承
接口的優(yōu)點(diǎn):
- 一個(gè)類可以實(shí)現(xiàn)多個(gè)接口捂齐,解決了extends的單一性問題
- 接口的所有成員都是public蛮放,可以對(duì)外提供一套規(guī)范
- 降低了程序的耦合性
接口和抽象類的比較:
- 相似點(diǎn):
- 都是不斷的抽取出抽象的概念
- 不同點(diǎn):
- 抽象類也是類,只能單一繼承奠宜,而類可以實(shí)現(xiàn)多個(gè)接口
- 成員方面包颁,抽象類可以有常量和成員變量,但是接口只能有變量压真。抽象類還可以有非抽象方法娩嚼,接口必須都是抽象方法并且修飾符默認(rèn)為
public abstract
- 構(gòu)造方法方面,抽象類是有構(gòu)造函數(shù)的榴都,但是接口沒有構(gòu)造函數(shù)
匿名對(duì)象
沒有變量引用的對(duì)象
public class AnooymousObjDemo {
public static void main(String[] args) {
new Test("DeeJay").show(); // name: DeeJay
}
}
class Test {
String name;
public Test(String name) {
this.name = name;
}
public void show() {
System.out.println("name: "+ name);
}
}
一般當(dāng)方法只調(diào)用一次的時(shí)候可以使用匿名對(duì)象待锈。
final關(guān)鍵字
修飾符,可以修飾類嘴高,成員方法竿音,以及成員變量
- final修飾的類不可以被繼承和屎。
- final修飾的成員方法不可以被重寫
- final修飾的成員變量不可以被賦值,哪怕賦的是原值春瞬。即是常量
另外自定義常量必須初始化柴信,可以直接賦值,或者在構(gòu)造方法中初始化都可宽气。
public class FinalDemo {
public static void main(String[] args) {
Animal3 a3 = new Animal3();
// a3.num = 10; // the final fields cannot be assigned final修飾的成員變量不可以被賦值随常,哪怕賦的是原值。即是常量,一般命名使用大寫萄涯。
}
}
final class Animal {
public void eat(){
System.out.println("eating");
}
}
// class Dog extends Animal {} // cannot subclass the final class final修飾的類不可以被繼承
class Animal2 {
final public void eat(){ // final修飾成員方法
System.out.println("eating");
}
}
class Dog2 extends Animal2 {
// public void eat() {} // cannot Override the final method final修飾的成員方法不可以被重寫
}
class Animal3 {
final int num = 3;
}
多態(tài)
多態(tài)的前提:
- 子父類的繼承關(guān)系
- 方法的重寫
- 父類引用指向子類對(duì)象
Dad d = new Child();
多態(tài)的成員特點(diǎn):
- 成員變量:成員變量在繼承時(shí)沒有重寫的概念绪氛,也沒有動(dòng)態(tài)綁定的概念,所以運(yùn)行時(shí)指向的是父類的成員變量涝影。
- 成員方法: 成員方法運(yùn)行時(shí)枣察,指向的是子類中被重寫的方法
- 靜態(tài)方法: 調(diào)用的靜態(tài)方法是父類的靜態(tài)方法,因?yàn)閟tatic是跟著類型走的燃逻。
public class PolymorphismDemo {
public static void main(String[] args) {
Dad d = new Child();
System.out.println(d.num); // 20 成員變量在繼承時(shí)沒有重寫的概念序目,也沒有動(dòng)態(tài)綁定的概念。 所以這邊的是父類的成員變量伯襟。
d.show(); // child 成員方法運(yùn)行時(shí)猿涨,指向的是子類中被重寫的方法
d.show2(); // dad static d變量類型還是Dad型,所以調(diào)用的靜態(tài)方法是Dad類的靜態(tài)方法
}
}
class Dad {
int num = 20;
public void show() {
System.out.println("dad");
}
public static void show2() {
System.out.println("dad static");
}
}
class Child extends Dad {
int num = 10;
public void show() {
System.out.println("child");
}
public static void show2() {
System.out.println("child static");
}
}
多態(tài)中的類型轉(zhuǎn)換:
public class PolymorphismDemo {
public static void main(String[] args) {
//向下轉(zhuǎn)型
Dad2 d2 = new Child2();
Child2 c2 = (Child2)d2; // 強(qiáng)制的進(jìn)行了類型轉(zhuǎn)換 從Dad2類型到了Child2類型姆怪。 向下轉(zhuǎn)型
c2.show(); // child 向下轉(zhuǎn)型了之后就可以調(diào)用子類的方法
}
}
// 關(guān)于多態(tài)中引用類型的轉(zhuǎn)換 向上轉(zhuǎn)換Dad d = new Child(); 已經(jīng)隱式的進(jìn)行了轉(zhuǎn)換叛赚,從小到大向上轉(zhuǎn)換即從子類型到父類型
// 對(duì)于向下轉(zhuǎn)換即從父類轉(zhuǎn)到子類,一般是由于想訪問子類中特有的成員才執(zhí)行的稽揭,需要強(qiáng)制進(jìn)行類型轉(zhuǎn)換
class Dad2 {}
class Child2 extends Dad2{
public void show() {
System.out.println("child");
}
}
多態(tài)的優(yōu)缺點(diǎn):
缺點(diǎn):
- 無法直接訪問子類特有的成員红伦,如果非要訪問,需要向下轉(zhuǎn)型
優(yōu)點(diǎn): - 提高可維護(hù)性和可擴(kuò)展性
對(duì)于提高可擴(kuò)展性淀衣,來寫一個(gè)例子:
package com.polymorphism;
public class PolymorphismDemo {
public static void main(String[] args) {
Factory f = new Factory();
f.createPhone(new MiPhone());
f.createPhone(new MeiZuPhone());
}
}
class Factory {
public void createPhone(MiPhone mp) {
System.out.println("create phone");
mp.call();
}
public void createPhone(MeiZuPhone mzp) { // 隨著手機(jī)類越來越多 Factory類中要一直新增createPhone的重載方法
System.out.println("create phone");
mzp.call();
}
}
class MiPhone {
public void call() {
System.out.println("MI phone is calling");
}
}
class MeiZuPhone {
public void call () {
System.out.println("MeiZuPhone is calling");
}
}
上面這個(gè)例子中昙读,F(xiàn)actory類中的方法隨著手機(jī)類的增多,被迫要一次次的新增一個(gè)重載的方法膨桥。
我們可以使用多態(tài)來改寫這個(gè)例子:
package com.polymorphism;
public class PolymorphismDemo2 {
public static void main(String[] args) {
Factory2 f2 = new Factory2();
f2.createPhone(new MiPhone2());
f2.createPhone(new MeiZuPhone2());
}
}
class Factory2 {
public void createPhone(Phone p) { // 不需要每新增一個(gè)手機(jī)類就實(shí)現(xiàn)一次重載
System.out.println("create phone");
p.call();
}
}
interface Phone { // 創(chuàng)建一個(gè)公共的接口 讓其他的手機(jī)類來實(shí)現(xiàn)這個(gè)接口 在Factory2的方法中就可以只傳人實(shí)現(xiàn)了這個(gè)Phone 類型的變量
public abstract void call();
}
class MiPhone2 implements Phone { // 實(shí)現(xiàn)公共接口 具體的方法在類內(nèi)部進(jìn)行定義
public void call() {
System.out.println("MI phone is calling");
}
}
class MeiZuPhone2 implements Phone { // 實(shí)現(xiàn)公共接口 具體的方法在類內(nèi)部進(jìn)行定義
public void call () {
System.out.println("MeiZuPhone is calling");
}
}
在改進(jìn)的例子中蛮浑,我們寫了一個(gè)公共的接口Phone,所有的手機(jī)類都來實(shí)現(xiàn)這個(gè)接口只嚣,最后在Factory2中的方法中沮稚,我們只需要傳入Phone p
的參數(shù)就可以了,不用關(guān)心它具體是什么類型册舞,從而就避免了一直重載方法的尷尬蕴掏。