1. 程式人生 > >java-繼承進階_抽象類_接口

java-繼承進階_抽象類_接口

完全 對象初始化 num cal ... obj ram 1.4 mce

一, 繼承的進階

1.1,成員變量

重點明確原理。
特殊情況:
子父類中定義了一模一樣的成員變量。
都存在於子類對象中。
如何在子類中直接訪問同名的父類中的變量呢?
通過關鍵字 super來完成。

super和this的用法很相似。
this:代表的是本類的對象的引用。
super:代表的是父類的內存空間。

註意:這種情況開發見不到,因為父類一旦描述完了屬性,子類直接使用就可以了。

//父類。
class Fu
{
    /*private int num1 = 3;*///父類中私有的內容子類不可以直接訪問。
    int num = 3;
}

class
Zi extends Fu { /*int num2 = 4;*/ int num = 4; void show() { // System.out.println("num1="+num1); // System.out.println("num2="+num2); System.out.println("zi num="+num); // num=4 System.out.println("fu num="+super.num);// num=3 } } class ExtendsDemo {
public static void main(String[] args) { Zi z = new Zi(); z.show(); } }

圖解

技術分享圖片

1.2 成員函數@@

子父類中成員函數的特點

特殊情況:
子父類中的定義了一模一樣的函數
運行的結果:子類的函數在運行
這種情況在子父類中,是函數的另一個特性:override(重寫,覆蓋,復寫)

【重寫什麽時候用?】
舉例:
//描述手機。

class Phone
{
int number;
//打電話。
void call(){}
//來電顯示。
void show()
{
sop(
"電話號碼.."+number); } } Phone p = new Phone(); p.show();

隨著電話的升級,只顯示號碼不爽,希望顯示姓名,大頭貼。
修改源碼,雖然費勁但是可以解決,不利於後期的維護和擴展。
為了擴展方便。新功能是不是新的電話具備呢?
單獨描述單獨封裝。新電話也是電話中的一種。繼承。直接獲取父類中的功能。
但是新電話的來顯功能已經變化了。需要重新定義。
那麽定義一個新功能合適嗎?比如newShow,不合適,因為父類已經將來顯功能定義完了,
子類完全不需要重新定義新功能。直接用就可以了。如果子類的來顯功能內容不同。
直需要保留來顯功能,定義子類的內容即可:這就是重寫的應用!

class NewPhone extends Phone
{
String name;
String picPath;//圖片路徑。
void show()
{
//sop("電話號碼");
super.show();//如果還需要父類中原有的部分功能,可以通過super調用@@@@@
sop("姓名"+name);
sop("大頭貼"+picPath);
}
}


【重寫(覆蓋)的註意事項】@@

1,子類覆蓋父類,必須保證全要大於或者等於父類的權限。
Fu:
private void show(){}

Zi:
public void show(){}
2,靜態覆蓋靜態。


寫法上稍微註意:必須一模一樣:函數的返回值類型 , 函數名 參數列表都要一樣

【重寫總結】
當一個類是另一個類中的一種時,可以通過繼承,來擴展功能。
如果從父類具備的功能內容需要子類特殊定義時,使用重寫。

*/

class Fu
{
int show()
{
System.out.println("fu show run");
return 0;
}
}
class Zi extends Fu
{
void show()
{
System.out.println("zi show run");
}
}
class ExtendsDemo2 
{
public static void main(String[] args) 
{
Zi z = new Zi();
z.show();
}
}

圖解

技術分享圖片

1.3 構造函數

【子父類中構造函數的特點】

class Fu
{
    int Fu()
    {
        System.out.println("fu show run");
        return 0;
    }
}
class Zi extends Fu
{
    void Zi()
    {
        System.out.println("zi show run");
    }
}
class ExtendsDemo2 
{
    public static void main(String[] args) 
    {
        new Zi();
    }
}


當子父類都有構造函數時,發現結果為:
fu constructor run
zi constructor run
先執行了父類的構造函數,再執行子類的構造函數。

【這是為啥呢?】
因為子類的所有的構造函數中的第一行都有一句隱式語句 super(); //默認調用的是父類中的空參數的構造函數。

技術分享圖片

【子類中的構造函數為什麽有一句隱式的super()呢?】
原因:子類會繼承父類中的內容,所以子類在初始化時,必須先到父類中去執行父類的初始化動作。
才可以更方便的使用父類中的內容。

【小結】
當父類中沒有空參數構造函數時,子類的構造函數必須同構顯示的super語句指定要訪問的父類中的構造函數。
這就是傳說中的子類實例化過程。*/

class Fu 
{

Fu()
{
//super();
//顯示初始化。
System.out.println("fu constructor run..A..");
}
Fu(int x)
{
//顯示初始化。
System.out.println("fu constructor run..B.."+x);
}
}
class Zi extends Fu
{
Zi()
{

System.out.println("zi constructor run..C..");
}
Zi(int x)
{

System.out.println("zi constructor run..D.."+x);
}
}
class ExtendsDemo3 
{
public static void main(String[] args) 
{
//    new Zi();  //  AC@@
new Zi(6);  // AD@@
}
}

【實例化過程的細節】
1,如果子類的構造函數第一行寫了this調用了本類其他構造函數,那麽super調用父類的語句還有嗎?
沒有的因為this()或者super(),只能定義在構造函數的第一行,因為初始化動作要先執行

2,父類構造函數中是否有隱式的super呢?
也是有的。記住:只要是構造函數默認第一行都是super();
父類的父類是誰呢?super調用的到底是誰的構造函數呢?
Java體系在設計,定義了一個所有對象的父類Object ,


【總結】
類中的構造函數默認第一行都有隱式的super()語句,在訪問父類中的構造函數。
所以父類的構造函數既可以給自己的對象初始化,也可以給自己的子類對象初始化。

如果默認的隱式super語句沒有對應的構造函數,必須在構造函數中通過this或者super的形式明確調用的構造函數。

【問題】@@@
1,this語句和super語句是否可以在同一個構造函數中出現呢?不行,因為必須定義在第一行。
2,為什麽要定義在第一行呢?因為初始化動作要先執行。

//子類的實例化過程的應用。也是super調用的應用
//什麽時候用super調用父類中構造函數。只要使用父類的指定初始化動作,就在子類中通過super(參數列表)格式進行調用。

class Person
{
private String name;
private int age;
public Person(String name,int age)
{
this.name = name;
this.age = age;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return age;
}
}

class Student extends Person
{
public Student(String name,int age)
{

//調用父類。使用父類的初始化動作。
super(name,age);
}
public void study()
{}
}

class Worker extends Person
{
public Worker(String name,int age)
{
//調用父類。使用父類的初始化動作。
super(name,age);
}
}

1.4 final

可以修飾 類,方法,變量  

/*
繼承的弊端:打破封裝性。
不讓其他類繼承該類,就不會有重寫。
怎麽能實現呢?通過Java中的一個關鍵字來實現,final(最終化)。

【final關鍵字】
是一個修飾符,可以修飾類,方法,變量(成員變量,局部變量,靜態變量)。
【特點】
1,final修飾的類是一個最終類,不能在派生子類。
如果類中從出現部分可以重寫,部分不可以?怎麽辦?只要讓指定的方法最終化就可以了。
2,final修飾的方法是最終方法,不可以給重寫。
3,final修飾的變量是一個常量,只能被賦值一次。
【什麽時候會在程序中定義final常量呢?】
當程序中一個數據使用時是固定不變的,這時為了增加閱讀性,可以該該數據起個名字。
這就是變量,為了保證這個變量的值不被修改,加上final修飾,這就一個閱讀性很強的常量。
書寫規範,被final修飾的常量名所有的字母都是大寫的。如果由多個單詞組成單詞間通過 _ 連接。

*/
/*final*/class Fu
{
/*final*/ void show()
{
//調用到一些系統的功能。
//功能的內容是不可以改變的。
}
}

class Zi extends Fu
{
static final int number = 9;//最終化的是顯示初始化值。
static final double PI = 3.14;
//重寫
void show()
{
final int count = 21;
//    count = 2;
System.out.println(count);
}
}


class FinalDemo 
{
public static void main(String[] args) 
{
System.out.println("Hello World!");
}
}

二 抽象類

2.1 產生

/*
描述狗,行為:吼叫。
描述狼,行為:吼叫。
發現他們之間有共性,可以進行向上抽取。
當然是抽取它們的所屬共性類型:犬科。
犬科這類事物:都具備吼叫行為,但是具體怎麽叫,是不確定的,是由具體的子類來明確的。
這時在描述犬科時,發現了有些功能不具體,這些不具體的功能,需要在類中標識出來,通過java中的關鍵字abstract(抽象)。
定義了抽象函數的類也必須被abstract關鍵字修飾,被abstract關鍵字修飾的類是抽象類。
*/
//抽象類:在描述事物時,沒有足夠的信息描述一個事物,這時該事物就是抽象事物。

2.2 抽象類的特點

1,抽象類和抽象方法都需要被abstract修飾。
抽象方法一定要定義在抽象類中。
2,抽象類不可以創建實例,原因:調用抽象方法沒有意義。
3,只有覆蓋了抽象類中所有的抽象方法後,其子類才可以實例化。
否則該子類還是一個抽象類。


之所以繼承,更多的是在思想,是面對共性類型操作會更簡單。

【細節問題】
1,抽象類一定是個父類?
是的,因為不斷抽取而來的。

2,抽象類是否有構造函數?
,雖然不能給自己的對象初始化,但是可以給自己的子類對象初始化。
抽象類和一般類的異同點:
相同:
1,它們都是用來描述事物的。
2,它們之中都可以定義屬性和行為。

不同:
1,一般類可以具體的描述事物。
抽象類描述事物的信息不具體
2,抽象類中可以多定義一個成員:抽象函數。
3,一般類可以創建對象,而抽象類不能創建對象。

3,抽象類中是否可以不定義抽象方法。
是可以的,那這個抽象類的存在到底有什麽意義呢?僅僅是不讓該類創建對象。


4,抽象關鍵字abstract不可以和哪些關鍵字共存?
1,final:
2,private:
3,static:

*/

abstract class 犬科 //extends Object
{
static abstract void 吼叫();//抽象函數。需要abstract修飾,並分號;結束
}


//代碼體現。
class Dog extends 犬科
{
void 吼叫()
{
System.out.println("汪汪汪汪");
}
}

class Wolf extends 犬科
{
void 吼叫()
{
System.out.println("嗷嗷嗷嗷");
}
}

 

class AbstractDemo
{
public static void main(String[] args) 
{
System.out.println("Hello World!");
}
}

2.2 例子

/*
需求:公司中程序員有姓名,工號,薪水,工作內容。
項目經理除了有姓名,工號,薪水,還有獎金,工作內容。
對給出需求進行數據建模。

在問題領域中先找尋其中涉及的對象。
程序員
屬性:姓名,工號,薪水
行為:工作

項目經理
屬性:姓名,工號,薪水,獎金
行為:工作

這些對象是否有關系呢?因為發現了他們之間的共性,應該存在著關系。
可以將他們的共性向上抽取到共性類型:員工。
員工:
屬性:姓名,工號,薪水
行為:工作
發現員工的工作內容本身就不具體。應該是抽象的,由具體的子類來體現的。

一定要動手!

*/

abstract class Employee
{
private String name;
private String id;
private double pay;
/**
構造一個員工對象,一初始化就具備著三個屬性。
*/
public Employee(String name,String id,double pay)
{
this.name = name;
this.id = id;
this.pay = pay;
}
/**
工作行為。
*/
public abstract void work();
}

//具體的子類:程序員。
class Programmer extends Employee
{
public Programmer(String name,String id,double pay)
{
super(name,id,pay);
}
public void work()
{
System.out.println("code....");
}
}

//具體的子類:經理。
class Manager extends Employee
{
//特有屬性。
private double bonus;
public Manager(String name,String id,double pay,double bonus)
{
super(name,id,pay);
this.bonus = bonus;
}
public void work()
{
System.out.println("manage");
}
}

class AbstractTest 
{
public static void main(String[] args) 
{
System.out.println("Hello World!");
}
}

三 接口

3.1 產生

/*
抽象類中可以定義抽象方法的 。
當一個抽象類中的方法全是抽象的。
這時,可以通過另一種特殊的形式來體現。

用接口來表示。

3.2 定義

接口該如何定義呢?

interface


abstract class Demo
{
abstract void show1();
abstract void show2();
}
*/
/*

3.3 接口中的成員

接口中的成員已經被限定為固定的幾種。
【接口的定義格式先介紹兩種:】
1,定義變量,但是變量必須有固定的修飾符修飾,public static final 所以接口中的變量也稱之為常量。
2,定義方法,方法也有固定的修飾符,public abstract

註意:如果你不寫編譯器會給你自動加上

例如當你只寫一個final的時候,編譯器會給你自動加上public static final
接口中的成員都是公共的。

3.4 接口的特點

1,接口不可以創建對象。
2,子類必須覆蓋掉接口中所有的抽象方法後,子類才可以實例化。
否則子類是一個抽象類。

*/
interface Demo//定義一個名稱為Demo的接口。
{
public static final int NUM = 3;
public abstract void show1();
public abstract void show2();
}

//定義子類去覆蓋接口中的方法。子類必須和接口產生關系,類與類的關系是繼承類與接口之間的關系是 實現。通過 關鍵字 implements 註意: 定義接口時命名 名+Impl

class DemoImpl implements Demo//子類實現Demo接口。
{
//重寫接口中的方法。
public void show1(){}
public void show2(){}
}

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


interface A
{
void show1();
}
interface B
{
void show2();
}
class C implements A,B// 多實現。同時實現多個接口。
{
public void show1(){}
public void show2(){}
}

【怎麽解決多繼承的弊端呢?】
弊端:多繼承時,當多個父類中有相同功能時,子類調用會產生不確定性。
其實核心原因就是在於多繼承父類中功能有主體,而導致調用運行時,不確定運行哪個主體內容。
為什麽多實現就解決了呢?
因為接口中的功能都沒有方法體,由子類來明確。

interface A
{
void show();
}
interface B
{
void show();
}
class C implements A,B// 多實現。同時實現多個接口。
{
public void show();
}

C c = new C();
c.show();

【基於接口的擴展。】

class Fu
{
public void show(){}
}
//子類通過繼承父類擴展功能,通過繼承擴展的功能都是子類應該具備的基礎功能。
//如果子類想要繼續擴展其他類中的功能呢?這時通過實現接口來完成。
interface Inter
{
pulbic void show1();
}
class Zi extends Fu implements Inter
{
public void show1()
{

}
}

[ 接口的好處]

接口的出現避免了單繼承的局限性。
父類中定義的事物的基本功能。
接口中定義的事物的擴展功能。

3.5 接口出現後的一些小細節

1,類與類之間是繼承(is a)關系,類與接口之間是實現(like a)關系,
接口與接口之間是繼承關系,而且可以多繼承。

interface InterA
{
void show1();
}
interface InterAA
{
void show11();
}
interface InterB extends InterA,InterAA//接口的多繼承。
{
void show2();
}

class Test implements InterB
{
public void show1(){}
public void show2(){}
public void show11(){}
}


class InterfaceDemo 
{
public static void main(String[] args) 
{
DemoImpl d = new DemoImpl();
d.show1();
d.show2();

}
}

3.6 接口的思想

舉例:筆記本電腦,USB接口的故事。

1,接口的出現對功能是實現了擴展。
2,接口的出現定義了規則。
3,接口的出現降低了耦合性(解耦)。

接口的出現,完成了解耦,說明有兩方,一方在使用這個規則,另一方在實現這個規則。
比如筆記本電腦使用這個規則,而外圍設備在實現這個規則。

3.7 接口和抽象類的區別

描述事物。
:按照功能分類: 導盲犬,緝毒犬...

犬的行為
吼叫();
吃飯();

abstract class 犬
{
public abstract void 吼叫();
public abstract void 吃飯();

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

//對於緝毒,有可能還有緝毒豬,具備者緝毒功能,應該將緝毒功能抽取。
//對緝毒進行描述。
abstract class 緝毒
{
public abstract void 緝毒();
}

緝毒犬既需要犬的功能又需要緝毒的功能。
無法直接多繼承。
是否可以多實現呢?可以的。
犬是接口,緝毒也是接口。緝毒犬多實現即可。
類負責描述的是事物的基本功能。接口負責描述事物的擴展功能。
緝毒犬是犬中一種。is a 關系,
將犬定義成類。而緝毒是犬的一個擴展功能。這時將緝毒定義接口。

這時描述就變成了這樣:

interface 緝毒able
{
public abstract void 緝毒();
}

class 緝毒犬 extends 犬 implements 緝毒able
{
public void 吼叫(){code..}
public void 吃飯(){}
public void 緝毒(){}
}

總結:
1,抽象類是描述事物的基本功能,可以定義非抽象的方法。
接口中定義只能是抽象方法,負責功能的擴展。
2,類與類之間是繼承關系 is a關系。
類與接口之間是實現關系 like a 關系。

門
open();
close();

報警門。

class 門
{
}
interface 報警
{
}

class 
{
public static void main(String[] args) 
{
System.out.println("Hello World!");
}
}

java-繼承進階_抽象類_接口