面向對象之繼承
一、什么是繼承
對象的繼承是指從一個類派生出另外的一個類的過程,就像孩子是從父母那里繼承品性一樣。
- 關鍵字 extends
- PHP只能有一個父類
- 關鍵詞 instanceof 可以用來查看一個特別的對象是不是屬於一種特定的類的類型
require 'demo.class.php';
$obj = new ChildClass();
if( $obj instanceof Demo ){
$obj->test();
}
1、全部繼承
子類繼承(也就是具有)父類的全部屬性和方法
class Person
{
public $name;
public $age;
public $info;
public function __construct($name,$age,$info)
{
$this->name = $name;
$this->age = $age;
$this->info = $info;
}
public function talk(){
return '你好面試官,我叫' . $this->name . ',今年'.$this->age . ',特長:'. $this->info;
}
}
class Man extends Person
{
}
$jack = new Man('jack',18,'帥的令人發指');
echo $jack->talk();
2、部分繼承
子類還可以添加自己的成員,從而可以保持和父類有所區別
class Man extends Person
{
public function checkAge(){
return $this->age>=18 ? '拉出去當壯丁' : '還是小鮮肉';
}
}
$jack = new Man('jack',18,'帥的令人發指');
echo $jack->talk();
echo $jack->checkAge();
3、無限多子繼承
class Man extends Person
{
public function checkAge(){
return $this->age>=18 ? '拉出去當壯丁' : '還是小鮮肉';
}
}
class Women extends Person {
}
二、訪問控制
封裝 我們為什么要使用面向對象編程呢?既然只需要使用方法就可以寫出復雜且實用的網站? OOP的真正價值在於封裝,它的意義在於將相互關聯的一組值和函數封裝在一起,組成一個編程單元
封裝的概念 : 通過修飾符 改變成員屬性或者成員方法的訪問權限,達到保護的作用。
修飾符 | public | protected | private |
---|---|---|---|
本類內 | Y | Y | Y |
子類內 | Y | Y | N |
外部 | Y | N | N |
類的屬性和成員方法在哪里可以用、可以被訪問
class User extends Demo
{
public $username = 'jack';
protected $truename = '老聶';
private $age = '18';
public function printInfo(){
echo $this->username;
echo $this->truename;
echo $this->age;
}
private function secret(){
echo "這是個秘密!";
}
}
- 一個公共的(Public)成員可以從任何一個地方訪問:類本身內部,派生的子類和其他類。
- 類的受保護的(protected)成員只能在類本身以及子類中訪問。
- 私有(private)的限制是最嚴格的,這些成員只能在聲明它們的類中進行訪問。私有的類成員不能被子類或者這些類的一個對象實例訪問到。
聶哥友情備注:作為一個慣例,私有變量名通常以一個下划線開始。這在很多面向對象編程語言中都是這樣做的,即便它並不是必須這樣做
訪問修飾符的范圍
子類復寫父類中的方法時,子類中的 訪問修飾符的范圍要大於等於 父類的【 繼承只能發揚光大,至少保持不變。不可以丟失東西。
在UML中的體現
+
公共的-
私有的#
受保護的
三、繼承中的構造方法和析構方法
在子類里父類的構造函數會不會執行,分兩種情況:
- 如子類不定義構造函數
__construct()
,則父類的構造函數默認會被繼承下來,且會自動執行。 - 如子類定義了構造函數
__construct()
,因為構造函數名也是__construct()
,所以子類的構造函數實際上是覆蓋(override)了父類的構造函數。這時執行的是該子類的構造函數
class Demo
{
function __construct()
{
echo '正在進行構造...<br/>';
}
function test(){
echo '這是什么地方<br/>';
}
function __destruct()
{
echo '正在進行析構...<br/>';
}
}
class ChildClass extends Demo
{
function __construct()
{
//如果要在子類里執行父類的構造函數
//parent::__construct();
echo '我是后代構造函數-正在進行構造...<br/>';
}
}
注意
parent::__construct()
; 語句不一定必須放在子類的構造函數中。放在子類的構造函數中僅僅保證了其在子類被實例化時自動執行。
四、重寫
重載就是函數或者方法有相同的名稱,但是參數列表不相同的情形,這樣的同名不同參數的函數或者方法之間,互相稱之為重載函數或者方法。PHP不支持,但可用構造方法實現。
重寫又叫覆蓋,就是將父類繼承下來的屬性或方法重新定義,只有保護的或公共的屬性或方法能夠被重寫。
1、重寫一個方法,子類定義的方法必須和父類的方法具有完全相同的名稱和參數數量。
class Person
{
public $name;
public $age;
public $info;
public function __construct($name,$age,$info)
{
$this->name = $name;
$this->age = $age;
$this->info = $info;
}
public function talk($fuhao){
return '你好面試官,我叫' . $this->name . ',今年'.$this->age . ',特長:'. $this->info;
}
}
class Man extends Person
{
public function checkAge(){
return $this->age>=18 ? '拉出去當壯丁' : '還是小鮮肉';
}
public function talk(){
}
}
2、PHP5.3+
方法重寫必須要相同個數的參數,如果非多個參數,這時要給其他參數設置默認值
class Person
{
public $name;
public $age;
public $info;
public function __construct($name,$age,$info)
{
$this->name = $name;
$this->age = $age;
$this->info = $info;
}
private function talk($fuhao){
return '你好面試官,我叫' . $this->name . ',今年'.$this->age . ',特長:'. $this->info;
}
}
class Man extends Person
{
public function checkAge(){
return $this->age>=18 ? '拉出去當壯丁' : '還是小鮮肉';
}
public function talk($fuhao='沒辦法,我是來充數的'){
}
}
3、私有屬性或私有方法的重寫問題
- 私有屬性和方法不能覆蓋,但其實子類可以定義跟父類私有的同名屬性或方法,只是當做一個自身的新屬性或方法來看待而已。
參數必須保持一致(PHP5.4前)
class A
{
private $name = '山炮';
public function getName()
{
return $this->name;
}
}
class B extends A
{
private $name = '二貨';
public function getTest()
{
return $this->getName().$this->name;
}
}
$b = new B;
echo $b->getTest();//山炮二貨
3.2 訪問私有屬性 類中的私有屬性或者方法是不能被繼承的,但是當一個父類A中的方法FunA,調用了父類A中的Private私有屬性或着方法的時,這個FunA在被繼承以后,將能繼續通過$this
訪問父類A中的Private私有屬性或者方法,無論繼承子類中對父類A中的私有屬性或方法如何重新實現,直到FunA在繼承子類中被重寫
class A
{
private $name = '山炮';
public function getName()
{
return $this->name;
}
}
class B extends A
{
public $name = '二貨';
}
$b = new B;
echo $b->name; //二貨
echo $b->getName(); //山炮
4、構造方法重寫問題,比較寬松,重寫的時候參數可以不一致。
class Person
{
public $name;
public $age;
public $info;
public function __construct($name,$age,$info)
{
$this->name = $name;
$this->age = $age;
$this->info = $info;
}
private function talk($fuhao,$cans=''){
return '你好面試官,我叫' . $this->name . ',今年'.$this->age . ',特長:'. $this->info;
}
}
class Man extends Person
{
public function __construct()
{
echo '我的方法沒有參數';
}
}
$jack = new Man();
5、非完全重寫
class Person
{
public $name;
public $age;
public function __construct($name,$age)
{
$this->name = $name;
$this->age = $age;
}
public function talk(){
return '你好面試官,我叫' . $this->name . ',今年'.$this->age;
}
}
class Man extends Person
{
public $info;
public function __construct($name,$age,$info)
{
parent::__construct($name,$age);
$this->info = $info;
}
public function talk(){
return '我先敲門!'.parent::talk().',特長:'.$this->info;
}
}
$jack = new Man('jack',18,"帥的令人發指");
echo $jack->talk();
五、不允許繼承或重寫 final
1.類里絕大部分方法,都是可以重寫的。唯一的例外就是被定義了FINAL的方法:
final function myFunc(){
}
- 屬性不能被定義為 final,只有類和方法才能被定義為 final。
- final關鍵字應該放在其他修飾符protect/public/private/static之前。
- 定義為final的方法不能被任何子類所重寫。
- 類也可以聲明為final,這意味着它不能被擴展。
2. 不能繼承的類
final A{
}