[toc]
## 基本概念
1. **類** ? 定義了一件事物的抽象特點
1. **對象** ? 是類的實例
1. **成員變量** ? 定義在類內部的變量。該變量的值對外是不可見的,但是可以通過成員函數訪問,在類被實例化為對象后,該變量即可稱為對象的屬性
1. **成員函數** ? 定義在類的內部,可用于訪問對象的數據
1. **繼承** ? 繼承性是子類自動共享父類數據結構和方法的機制,這是類之間的一種關系
1. **父類** ? 一個類被其他類繼承,可將該類稱為父類,或基類,或超類。
1. **子類** ? 一個類繼承其他類稱為子類,也可稱為派生類。
1. **多態** ? 相同的函數或方法可作用于多種類型的對象上并獲得不同的結果
1. **重載** ? 函數或者方法有同樣的名稱,但是參數列表不相同的情形,這樣的`同名不同參`的函數或者方法之間,互相稱之為重載函數或者方法
1. **抽象性** ? 將具有一致的數據結構(屬性)和行為(操作)的對象抽象成類。
1. **封裝** ? 將現實世界中存在的某個客體的屬性與行為綁定在一起,并放置在一個邏輯單元內。
1. **構造函數** ? 主要用來在創建對象時初始化對象
1. **析構函數** ? 析構函數(destructor) 與構造函數相反,當對象結束其生命周期時(例如對象所在的函數已調用完畢),系統自動執行析構函數
## PHP 類定義
```php
<?php
class Site {
// 成員變量
var $url;
var $title;
// 成員函數
function setUrl($par) {
$this->url = $par;
}
function getUrl() {
echo $this->url . PHP_EOL;
}
function setTitle($par) {
$this->title = $par;
}
function getTitle() {
echo $this->title . PHP_EOL;
}
}
```
## PHP 中創建對象
```php
$baidu = new Site();
$google = new Site();
$baidu->setTitle("百度一下");
$google->setTitle("谷歌一下");
$baidu->setUrl("www.baidu.com");
$google->setUrl("www.google.com");
$baidu->getTitle();
$baidu->getUrl();
$google->getTitle();
$google->getUrl();
```
**完整代碼及運行結果**
```php
<?php
class Site {
// 成員變量
var $url;
var $title;
// 成員函數
function setUrl($par) {
$this->url = $par;
}
function getUrl() {
echo $this->url . PHP_EOL;
}
function setTitle($par) {
$this->title = $par;
}
function getTitle() {
echo $this->title . PHP_EOL;
}
}
$baidu = new Site();
$google = new Site();
$baidu->setTitle("百度一下");
$google->setTitle("谷歌一下");
$baidu->setUrl("www.baidu.com");
$google->setUrl("www.google.com");
$baidu->getTitle();
$baidu->getUrl();
$google->getTitle();
$google->getUrl();
```
```
D:\soft\php72\php.exe D:\project\php_dp\index.php
百度一下
www.baidu.com
谷歌一下
www.google.com
Process finished with exit code 0
```
## PHP 構造函數
> 構造函數是一種特殊的方法。主要用來在創建對象時初始化對象, 即為對象成員變量賦初始值
```php
<?php
class Site {
var $url;
var $title;
// 構造方法
function __construct($par1, $par2) {
$this->url = $par1;
$this->title = $par2;
}
function setUrl($par) {
$this->url = $par;
}
function getUrl() {
echo $this->url . PHP_EOL;
}
function setTitle($par) {
$this->title = $par;
}
function getTitle() {
echo $this->title . PHP_EOL;
}
}
$baidu = new Site('www.baidu.com', '百度一下');
$google = new Site('www.google.com', '谷歌一下');
// $baidu->setTitle("百度一下");
// $google->setTitle("谷歌一下");
// $baidu->setUrl("www.baidu.com");
// $google->setUrl("www.google.com");
$baidu->getTitle();
$baidu->getUrl();
$google->getTitle();
$google->getUrl();
```
```
D:\soft\php72\php.exe D:\project\php_dp\index.php
百度一下
www.baidu.com
谷歌一下
www.google.com
Process finished with exit code 0
```
## PHP 析構函數
> 析構函數(destructor) 與構造函數相反,當對象結束其生命周期時(例如對象所在的函數已調用完畢),系統自動執行析構函數。
```php
<?php
class MyDestrutableClass {
public function __construct() {
print "構造函數\n";
$this->name = "MyDestrutableClass";
}
function __destruct() {
print "銷毀" . $this->name . "\n";
}
}
$obj = new MyDestrutableClass();
```
```
D:\soft\php72\php.exe D:\project\php_dp\index.php
構造函數
銷毀MyDestrutableClass
Process finished with exit code 0
```
## 繼承
> PHP 使用關鍵字 extends 來繼承一個類,PHP 不支持多繼承
```php
<?php
class Site {
var $url;
var $title;
function __construct($par1, $par2) {
$this->url = $par1;
$this->title = $par2;
}
function setUrl($par) {
$this->url = $par;
}
function getUrl() {
echo $this->url . PHP_EOL;
}
function setTitle($par) {
$this->title = $par;
}
function getTitle() {
echo $this->title . PHP_EOL;
}
}
// 子類擴展站點類別
class ChildSite extends Site {
var $category;
function setCate($par) {
$this->category = $par;
}
function getCate() {
echo $this->category . PHP_EOL;
}
}
```
## 方法重寫
> 如果從父類繼承的方法不能滿足子類的需求,可以對其進行改寫,這個過程叫方法的覆蓋(override),也稱為方法的重寫。
```php
<?php
class Site {
var $url;
var $title;
function __construct($par1, $par2) {
$this->url = $par1;
$this->title = $par2;
}
function setUrl($par) {
$this->url = $par;
}
function getUrl() {
echo $this->url . PHP_EOL;
}
function setTitle($par) {
$this->title = $par;
}
function getTitle() {
echo $this->title . PHP_EOL;
}
}
class ChildSite extends Site {
var $category;
function setCate($par) {
$this->category = $par;
}
function getCate() {
echo $this->category . PHP_EOL;
}
// 重寫父類的方法
function getUrl() {
echo $this->url . PHP_EOL;
return $this->url;
}
// 重寫父類的方法
function getTitle() {
echo $this->title . PHP_EOL;
return $this->title;
}
}
```
## 訪問控制
> PHP 對屬性或方法的訪問控制,是通過在前面添加關鍵字 `public`(公有),`protected`(受保護)或 `private`(私有)來實現的。
1. **public(公有)**:公有的類成員可以在任何地方被訪問。
1. **protected(受保護)**:受保護的類成員則可以被其自身以及其子類和父類訪問。
1. **private(私有)**:私有的類成員則只能被其定義所在的類訪問。
### 屬性的訪問控制
> 類屬性必須定義為公有,受保護,私有之一。如果用 var 定義,則被視為公有。
```php
<?php
class MyClass {
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello() {
echo $this->public . PHP_EOL;
echo $this->protected . PHP_EOL;
echo $this->private . PHP_EOL;
}
}
$obj = new MyClass();
echo $obj->public; // 正常執行
echo "\n";
echo $obj->protected; // 致命錯誤
echo $obj->private; // 致命錯誤
$obj->printHello(); // 正常輸出
```
```php
<?php
class MyClass {
public $public = 'Public';
protected $protected = 'Protected';
private $private = 'Private';
function printHello() {
echo $this->public . PHP_EOL;
echo $this->protected . PHP_EOL;
echo $this->private . PHP_EOL;
}
}
// $obj = new MyClass();
// echo $obj->public; // 正常執行
// echo "\n";
// echo $obj->protected; // 致命錯誤
// echo $obj->private; // 致命錯誤
// $obj->printHello(); // 正常輸出
class MyClass2 extends MyClass {
// 可以對 public 和 protected 進行重定義,但 private 卻不能
protected $protected = 'Protected2';
function printHello() {
echo $this->public;
echo $this->protected;
echo $this->private;
}
}
$obj2 = new MyClass2();
echo $obj2->public; // 正常執行
echo $obj2->private; // 未定義private
echo $obj2->protected; // 致命錯誤
echo $obj2->printHello(); // 正常輸出, 除了private
```
### 方法的訪問控制
> 類中的方法可以被定義為公有,私有或受保護。如果沒有設置這些關鍵字,則該方法默認為公有。
```php
<?php
class MyClass {
// 聲明一個公有的構造函數
public function __construct() {
}
// 聲明一個公有方法
public function MyPublic() {
echo "this is a public funtion!";
}
// 聲明一個受保護的方法
protected function MyProtected() {
echo "this is a protected function!";
}
// 聲明一個私有方法
private function MyPrivate() {
echo "this is a private function!";
}
function index() {
$this->MyPublic();
$this->MyPrivate();
$this->MyProtected();
}
}
$myclass = new MyClass();
$myclass->MyPublic(); // 正常執行
$myclass->MyProtected(); // 致命錯誤
$myclass->MyPrivate(); // 致命錯誤
$myclass->index(); // 都可執行
```
```php
<?php
class MyClass {
public function __construct() {
}
public function MyPublic() {
echo "this is a public funtion!";
}
protected function MyProtected() {
echo "this is a protected function!";
}
private function MyPrivate() {
echo "this is a private function!";
}
function index() {
$this->MyPublic();
$this->MyPrivate();
$this->MyProtected();
}
}
class MyClass2 extends MyClass {
// 默認公有
function index2() {
$this->MyPublic();
$this->MyProtected();
$this->myPrivate(); // 致命錯誤
}
}
$myclass2 = new MyClass2();
$myclass2->MyPublic();
$myclass2->index2();
```
## 接口
1. 使用接口(interface),可以指定某個類必須實現哪些方法,但不需要定義這些方法的具體內容。
1. 接口是通過 interface 關鍵字來定義的,就像定義一個標準的類一樣,但其中定義所有的方法都是空的。
1. 接口中定義的所有方法都必須是公有,這是接口的特性。
1. 要實現一個接口,使用 implements 操作符。類中必須實現接口中定義的所有方法,否則會報一個致命錯誤。類可以實現多個接口,用逗號來分隔多個接口的名稱。
```php
<?php
// 聲明接口
interface iTemplate {
public function setVariable($name, $var);
public function getHtml($template);
}
// 實現接口
class Template implements iTemplate {
private $vars = array();
public function setVariable($name, $var) {
$this->vars[$name] = $var;
}
public function getHtml($template) {
foreach ($this->vars as $name => $value) {
$template = str_replace('{' . $name . '}', $value, $template);
}
return $template;
}
}
```
## 常量
> 可以把在類中始終保持不變的值定義為常量。在定義和使用常量的時候不需要使用 $ 符號。
```php
<?php
class MyClass {
const constant = "常量";
function showConstant() {
echo self::constant . PHP_EOL;
}
}
echo MyClass::constant . PHP_EOL;
$class = new MyClass();
echo $class::constant;
```
```
D:\soft\php72\php.exe D:\project\php_dp\index.php
常量
常量
Process finished with exit code 0
```
## 抽象類
1. 任何一個類,如果它里面至少有一個方法是被聲明為抽象的,那么這個類就必須被聲明為抽象的。
1. 定義為抽象的類不能被實例化。
1. 被定義為抽象的方法只是聲明了其調用方式(參數),不能定義其具體的功能實現。
1. 繼承一個抽象類的時候,子類必須定義父類中的所有抽象方法;另外,這些方法的訪問控制必須和父類中一樣(或者更為寬松)。
```php
<?php
abstract class AbstractClass {
// 強制要求子類定義這些方法
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// 普通方法(非抽象方法)
public function printOut() {
print $this->getValue() . PHP_EOL;
}
}
// 子類必須定義父類中的所有抽象方法
class ConcreteClass1 extends AbstractClass {
protected function getValue() {
return "ConcreteClass1";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}
class ConcreteClass2 extends AbstractClass {
public function getValue() {
return "ConcreteClass2";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass2";
}
}
$class1 = new ConcreteClass1();
$class1->printOut();
echo $class1->prefixValue("Index_") . PHP_EOL;
$class2 = new ConcreteClass2();
$class2->printOut();
echo $class2->prefixValue("Index_") . PHP_EOL;
```
**子類方法可以包含父類抽象方法中不存在的可選參數。例如,子類定義了一個可選參數,而父類抽象方法的聲明里沒有,則也是可以正常運行的。**
```php
<?php
abstract class AbstractClass {
// 只有一個$name
abstract protected function prefixName($name);
}
class ConcreteClass1 extends AbstractClass {
// 多加了$separator
public function prefixName($name, $separator = ".") {
if ($name == "male") {
$prefix = "Mr";
} elseif ($name == "female") {
$prefix = "Mrs";
} else {
$prefix = '';
}
return "{$prefix}{$separator} {$name}";
}
}
$class = new ConcreteClass1();
echo $class->prefixName("male") . PHP_EOL;
echo $class->prefixName('female') . PHP_EOL;
```
## Static 關鍵字
1. 聲明類屬性或方法為 static(靜態),就可以不實例化類而直接訪問。
1. 靜態屬性不可以由對象通過 -> 操作符來訪問。
```php
<?php
class Index {
public static $my_static = 'index';
public function staticValue() {
return self::$my_static;
}
}
print Index::$my_static . PHP_EOL;
$index = new Index();
print $index->staticValue() . PHP_EOL;
print $index::$my_static . PHP_EOL;
```
```
D:\soft\php72\php.exe D:\project\php_dp\index.php
index
index
index
Process finished with exit code 0
```
## final關鍵字
> 如果父類中的方法被聲明為 final,則子類無法覆蓋該方法。
> 如果一個類被聲明為 final,則不能被繼承。
```php
<?php
class BaseClass {
public function test() {
echo "BaseClass::test() called" . PHP_EOL;
}
final public function moreTesting() {
echo "BaseClass::moreTesting() called" . PHP_EOL;
}
}
class ChildClass extends BaseClass {
public function moreTesting() {
echo "ChildClass::moreTesting() called" . PHP_EOL;
}
}
```
```
D:\soft\php72\php.exe D:\project\php_dp\index.php
Fatal error: Cannot override final method BaseClass::moreTesting() in D:\project\php_dp\index.php on line 18
Process finished with exit code 255
```
## 調用父類構造方法
> PHP 不會在子類的構造方法中自動的調用父類的構造方法。要執行父類的構造方法,需要在子類的構造方法中調用 `parent::__construct()` 。
```php
<?php
class BaseClass {
function __construct() {
echo "BaseClass類中的構造方法" . PHP_EOL;
}
}
class SubClass extends BaseClass {
function __construct() {
// 子類構造方法不能自動調用父類的構造方法
parent::__construct();
echo "SubClass類中的構造方法" . PHP_EOL;
}
}
class OtherSubClass extends BaseClass {
// 繼承 BaseClass 的構造方法
}
$obj = new BaseClass(); // 調用 BaseClass 構造方法
$obj = new SubClass(); // 調用 BaseClass、SubClass 構造方法
$obj = new OtherSubClass(); // 調用 BaseClass 構造方法
```
```
D:\soft\php72\php.exe D:\project\php_dp\index.php
BaseClass類中的構造方法
BaseClass類中的構造方法
SubClass類中的構造方法
BaseClass類中的構造方法
Process finished with exit code 0
```