1. 程式人生 > >Java基礎語法——面向對象(3)

Java基礎語法——面向對象(3)

屬於 eth 其他 cas bool ani 出現 student 就是

第一章 接口

1.1 接口概念

接口是功能的集合,同樣可看做是一種數據類型,是比抽象類更為抽象的接口只描述所應該具備的方法,並沒有具體實現,具體的實現由接口的實現類(相當於接口的子類)來完成。這樣將功能的定義與實現分離,優化了程序設計請記住:一切事物均有功能,即一切事物均有接口。

1.2 接口的定義

與定義類的class不同,接口定義時需要使用interface關鍵字。定義接口所在的仍為.java文件,雖然聲明時使用的為interface關鍵字的編譯後仍然會產生.class文件。這點可以讓我們將接口看做是一種只包含了功能聲明的特殊類。

定義格式:

public interface 接口名

{

抽象方法1;

抽象方法2;

抽象方法3;

}

使用interface代替了原來的class,其他步驟與定義類相同:

接口中的方法均為公共訪問的抽象方法

接口中無法定義普通的成員變量

1.3 類實現接口

類與接口的關系為實現關系,即類實現接口。實現的動作類似繼承,只是關鍵字不同,實現使用implements其他類(實現類)實現接口後,就相當於聲明:我應該具備這個接口中的功能。實現類仍然需要重寫方法以實現具體的功能。

格式:

class implements 接口 {

重寫接口中方法

}

在類實現接口後,該類就會將接口中的抽象方法繼承過來,此時該類需要重寫該抽象方法,完成具體的邏輯

接口中定義功能,當需要具有該功能時,可以讓類實現該接口,只聲明了應該具備該方法,是功能的聲明。

在具體實現類中重寫方法,實現功能,是方法的具體實現

1.4 接口中成員的特點

接口中可以定義變量,但是變量必須有固定的修飾符修飾,public static final 所以接口中的變量也稱之為常量,其值不能改變.接口中可以定義方法,方法也有固定的修飾符,public abstract。接口不可以創建對象。子類必須覆蓋掉接口中所有的抽象方法後,子類才可以實例化。否則子類是一個抽象類。

interface Demo { ///定義一個名稱為Demo的接口。
    public static
final int NUM = 3;// NUM的值不能改變 public abstract void show1(); public abstract void show2(); } //定義子類去覆蓋接口中的方法。類與接口之間的關系是 實現。通過 關鍵字 implements class DemoImpl implements Demo { //子類實現Demo接口。 //重寫接口中的方法。 public void show1(){} public void show2(){} }

接口最重要的體現:解決多繼承的弊端。將多繼承這種機制在java中通過多實現完成了。

怎麽解決多繼承的弊端呢?

弊端:多繼承時,當多個父類中有相同功能時,子類調用會產生不確定性。

其實核心原因就是在於多繼承父類中功能有主體,而導致調用運行時,不確定運行哪個主體內容。

為什麽多實現能解決了呢?

因為接口中的功能都沒有方法體,由子類來明確。

多個接口之間可以使用extends進行繼承。

interface Fu1{
    void show();
}
interface Fu2{
    void show1();
}
interface Fu3{
    void show2();
}
interface Zi extends Fu1,Fu2,Fu3{
    void show3();
}

接口在開發中的它好處:

1、接口的出現擴展了功能。

2、接口其實就是暴漏出來的規則。

3、接口的出現降低了耦合性,即設備與設備之間實現了解耦。

1.4 接口和抽象的區別

通過實例進行分析和代碼演示抽象類和接口的用法。

1、舉例:

犬:

  行為:

     吼叫;

     吃飯;

緝毒犬:

  行為:

     吼叫;

     吃飯;

     緝毒;

2、思考:由於犬分為很多種類,他們吼叫和吃飯的方式不一樣,在描述的時候不能具體化,也就是吼叫和吃飯的行為不能明確。當描述行為時,行為的具體動作不能明確,這時,可以將這個行為寫為抽象行為,那麽這個類也就是抽象類。可是當緝毒犬有其他額外功能時,而這個功能並不在這個事物的體系中。這時可以讓緝毒犬具備犬科自身特點的同時也有其他額外功能,可以將這個額外功能定義接口中。

如下代碼演示:

interface 緝毒{
    public abstract void 緝毒();
}
//定義犬科的這個提醒的共性功能
abstract class 犬科{
public abstract void 吃飯();
public abstract void 吼叫();
}
// 緝毒犬屬於犬科一種,讓其繼承犬科,獲取的犬科的特性,
//由於緝毒犬具有緝毒功能,那麽它只要實現緝毒接口即可,這樣即保證緝毒犬具備犬科的特性,也擁有了緝毒的功能
class 緝毒犬 extends 犬科 implements 緝毒{

    public void 緝毒() {
    }
    void 吃飯() {
    }
    void 吼叫() {
    }
}
class 緝毒犬 implements 緝毒{
    public void 緝毒() {
    }
}

通過上面的例子總結接口和抽象類的區別:

相同點:

都位於繼承的頂端,用於被其他類實現或繼承;

都不能直接實例化對象;

都包含抽象方法,其子類都必須覆寫這些抽象方法;

區別:

抽象類為部分方法提供實現,避免子類重復實現這些方法,提高代碼重用性;接口只能包含抽象方法;

一個類只能繼承一個直接父類(可能是抽象類),卻可以實現多個接口;(接口彌補了Java的單繼承);

抽象類是這個事物中應該具備的裏內容, 繼承體系是一種 is..a關系;

接口是這個事物中的額外內容,繼承體系是一種 like..a關系。

第二章 多態

2.1 多態概述

多態是繼封裝、繼承之後,面向對象的第三大特性。

現實事物經常會體現出多種形態,如學生,學生是人的一種,則一個具體的同學張三既是學生也是人,即出現兩種形態。

Java作為面向對象的語言,同樣可以描述一個事物的多種形態。如Student類繼承了Person類,一個Student的對象便既是Student,又是Person

Java中多態的代碼體現在一個子類對象(實現類對象)既可以給這個子類(實現類對象)引用變量賦值,又可以給這個子類(實現類對象)的父類(接口)變量賦值。

Student類可以為Person類的子類。那麽一個Student對象既可以賦值給一個Student類型的引用,也可以賦值給一個Person類型的引用。

最終多態體現為父類引用變量可以指向子類對象多態的前提是必須有子父類關系或者類實現接口關系,否則無法完成多態在使用多態後的父類引用變量調用方法時,會調用子類重寫後的方法

多態的定義格式:就是父類的引用變量指向子類對象

類類型 變量名 = new 子類類型();

變量名.方法名();

普通類多態定義的格式:

父類 變量名 = new 子類();
如:    class Fu {}
    class Zi extends Fu {}
    //類的多態使用
Fu f = new Zi();

抽象類多態定義的格式

抽象類 變量名 = new 抽象類子類();

如:abstract class Fu {

      public abstract void method();

        }
class Zi extends Fu {
        public void method(){
              System.out.println(“重寫父類抽象方法”);
        }
  }
//類的多態使用
  Fu fu= new Zi();

接口多態定義的格式:

接口 變量名 = new 接口實現類();
如: interface Fu {
             public abstract void method();
}
class Zi implements Fu {
             public void method(){
              System.out.println(“重寫接口抽象方法”);
}
}
//接口的多態使用
Fu fu = new Zi();

同一個父類的方法會被不同的子類重寫。在調用方法時,調用的為各個子類重寫後的方法。

如 Person p1 = new Student();
   Person p2 = new Teacher();
   p1.work(); //p1會調用Student類中重寫的work方法
   p2.work(); //p2會調用Teacher類中重寫的work方法

class Fu {
    int num = 4;
}
class Zi extends Fu {
    int num = 5;
}
class Demo {
    public static void main(String[] args)     {
        Fu f = new Zi();
        System.out.println(f.num);
        Zi z = new Zi();
        System.out.println(z.num);
    }
}

技術分享圖片

多態成員變量

當子父類中出現同名的成員變量時,多態調用該變量時:

編譯時期:參考的是引用型變量所屬的類中是否有被調用的成員變量。沒有,編譯失敗。

運行時期:也是調用引用型變量所屬的類中的成員變量。

簡單記:編譯和運行都參考等號的左邊。編譯運行看左邊。

多態出現後會導致子父類中的成員方法有微弱的變化。看如下代碼:

class Fu {
    int num = 4;
    void show()    {
        System.out.println("Fu show num");
    }
}
class Zi extends Fu {
    int num = 5;
    void show()    {
        System.out.println("Zi show num");
    }
}
class Demo {
    public static void main(String[] args)     {
        Fu f = new Zi();
        f.show();
    }
}

技術分享圖片

多態成員方法

編譯時期:參考引用變量所屬的類,如果沒有類中沒有調用的方法,編譯失敗。

運行時期:參考引用變量所指的對象所屬的類,並運行對象所屬類中的成員方法。

簡而言之:編譯看左邊,運行看右邊。

instanceof關鍵字

我們可以通過instanceof關鍵字來判斷某個對象是否屬於某種數據類型。如學生的對象屬於學生類,學生的對象也屬於人類。

使用格式:

boolean b = 對象 instanceof 數據類型;

Person p1 = new Student(); // 前提條件,學生類已經繼承了人類
boolean flag = p1 instanceof Student; //flag結果為true
boolean flag2 = p2 instanceof Teacher; //flag結果為false

2.2多態-轉型

多態的轉型分為向上轉型與向下轉型兩種:

向上轉型:當有子類對象賦值給一個父類引用時,便是向上轉型,多態本身就是向上轉型的過程。

使用格式:

父類類型 變量名 = new 子類類型();

如:Person p = new Student();

向下轉型:一個已經向上轉型的子類對象可以使用強制類型轉換的格式,將父類引用轉為子類引用,這個過程是向下轉型。如果是直接創建父類對象,是無法向下轉型的!

使用格式:

子類類型 變量名 = (子類類型) 父類類型的變量;

:Student stu = (Student) p; //變量p 實際上指向Student對象

當父類的引用指向子類對象時,就發生了向上轉型,即把子類類型對象轉成了父類類型。向上轉型的好處是隱藏了子類類型,提高了代碼的擴展性。

但向上轉型也有弊端,只能使用父類共性的內容,而無法使用子類特有功能,功能有限制。看如下代碼

技術分享圖片
 1 //描述動物類,並抽取共性eat方法
 2 abstract class Animal {
 3     abstract void eat();
 4 }
 5  
 6 // 描述狗類,繼承動物類,重寫eat方法,增加lookHome方法
 7 class Dog extends Animal {
 8     void eat() {
 9         System.out.println("啃骨頭");
10     }
11 
12     void lookHome() {
13         System.out.println("看家");
14     }
15 }
16 
17 // 描述貓類,繼承動物類,重寫eat方法,增加catchMouse方法
18 class Cat extends Animal {
19     void eat() {
20         System.out.println("吃魚");
21     }
22 
23     void catchMouse() {
24         System.out.println("抓老鼠");
25     }
26 }
27 
28 public class Test {
29     public static void main(String[] args) {
30         Animal a = new Dog(); //多態形式,創建一個狗對象
31         a.eat(); // 調用對象中的方法,會執行狗類中的eat方法
32         // a.lookHome();//使用Dog類特有的方法,需要向下轉型,不能直接使用
33         
34         // 為了使用狗類的lookHome方法,需要向下轉型
35 // 向下轉型過程中,可能會發生類型轉換的錯誤,即ClassCastException異常
36         // 那麽,在轉之前需要做健壯性判斷 
37         if( !a instanceof Dog){ // 判斷當前對象是否是Dog類型
38                  System.out.println("類型不匹配,不能轉換"); 
39                  return; 
40         } 
41         Dog d = (Dog) a; //向下轉型
42         d.lookHome();//調用狗類的lookHome方法
43     }
44 }
View Code

技術分享圖片

我們來總結一下:

什麽時候使用向上轉型:

當不需要面對子類類型時,通過提高擴展性,或者使用父類的功能就能完成相應的操作,這時就可以使用向上轉型。

如:Animal a = new Dog();

a.eat();

什麽時候使用向下轉型

當要使用子類特有功能時,就需要使用向下轉型。

如:Dog d = (Dog) a; //向下轉型

d.lookHome();//調用狗類的lookHome方法

向下轉型的好處:可以使用子類特有功能。

弊端是:需要面對具體的子類對象;在向下轉型時容易發生ClassCastException類型轉換異常。在轉換之前必須做類型判斷。

如:if( !a instanceof Dog){…}

Java基礎語法——面向對象(3)