PHP學習筆記(四)--面向物件
1、基本內容
注意事項:
- 無論是使用“$this->”還是使用“物件名->”格式,後面的變數是沒有$符號的,如$this->value;
- 通過“類名::常量”方式類訪問類常量的,如bastBall::Type;
- 在PHP中使用“垃圾回收”機制,不需要手動建立解構函式。不再使用的物件會自動清楚,釋放記憶體;
- 對於成員方法,如果沒有指定關鍵字,則預設是public,這一點與C++不同;
- 被protected修飾的類成員,可以在本類和子類中被呼叫,其它地方不可以,這也與C++不同;
- 在靜態方法中,只能呼叫靜態變數,而不能呼叫普通變數,而普通方法中可以呼叫靜態變數;
- 在類內部訪問靜態成員使用“self::靜態變數”,在類外部使用“類名::靜態變數”;
- 建構函式__construct及解構函式__destruct不能宣告為私有型別;
- finale修飾的類不可被再繼承,也不能再有子類。finale修飾的方法不可以進行重寫,也不可以被覆蓋;
- 抽象類是一種不能被例項化的類,只能作為其它的父類來使用。抽象類包含變數、成員方法,至少要包含一個抽象方法。抽象類和抽象方法主要用於複雜的層次關係中。
- PHP只支援單繼承,要想實現多繼承就要使用介面。介面類中只能包含未實現的方法和一些成員變數。介面中的類成員必須使用public來修飾。介面類中的所有未實現的方法必須要在子類中實現;
- 類常量前面不加$符號。
【例1】類的定義及使用
<?php
class SportObject{
const TYPE='Sports';
static $counter=0;
private $name;
private
private $sex;
public function __construct($name,$age,$sex){
$this->name=$name;
$this->age=$age;
$this->sex=$sex;
self::$counter++;//計數
}
public function __destruct(){
echo self::TYPE.'呼叫了解構函式';//類中訪問常量使用self
}
public function PrintInfo(){
echo $this->name.',';
echo $this->age.',';
echo $this->sex.'<p>';
}
public function IsBoy(){
return $this->sex='男' ? true : false;
}
public function ShowCounter(){
echo '總共建立了:'.self::$counter.'個例項';
}
}
$so=new SportObject('張三',22,'男');
$so->PrintInfo();//呼叫成員方法
echo $so->IsBoy();
echo SportObject::TYPE;//訪問類的常量
$sp2=new SportObject('張琴',25, '女');
$sp2->ShowCounter();
?>
2、繼承
【例2】繼承
<?php
class SportObject{
protected $name;
private $age;
private $sex;
public function __construct($name,$age,$sex){
$this->name=$name;
$this->age=$age;
$this->sex=$sex;
self::$counter++;//計數
}
public function PrintInfo(){
echo '姓名'.$this->name.'<p>';
}
}
class BasketBallSports extends SportObject{
private $height;
public function __construct($name, $height){
$this->name=$name;
$this->height=$height;
}
public function PrintInfo(){//覆蓋父類的方法
echo '身高:'.$this->height.'<p>';
}
}
$sp1=new BasketBallSports('鄭鈞', 188);
$sp1->PrintInfo();
?>
3、多型
【例3】多型
<?php
class Overloader
{
private $properties = array();
function __get($property_name)
{
if(isset($this->properties[$property_name]))
{
return($this->properties[$property_name]);
}
else
{
return(NULL);
}
}
function __set($property_name, $value)
{
$this->properties[$property_name] = $value;
}
public function __call($method, $p)
{
if($method == 'display')
{
if(is_object($p[0]))
$this->displayObject($p[0]);
else
if(is_array($p[0]))
$this->displayArray($p[0]);
else
$this->displayScalar($p[0]);
}
}
public function displayObject($p)
{
echo ("你傳入的是個物件,內容如下:<br>");
print_r($p);
echo "<hr>";
}
public function displayArray($p)
{
echo ("你傳入的是個陣列,內容如下:<br>");
print_r($p);
echo "<hr>";
}
public function displayScalar($p)
{
echo ("你傳入的是個單獨變數,內容如下:<br>" . $p);
echo "<hr>";
}
}
$o = new Overloader();
//呼叫 __set() 給一個不存在的屬性變數賦值
$o->dynaProp = "Dynamic Content";
$o->display('Cat');
?>
4、抽象類
【例4】抽象類
<?php
abstract class CommodityObject{
protected $type;
const CNAME='CommodityObject';
static public function Show(){
}
public function __construct(){
$this->type='通用類';
}
public function Display(){
echo $this->type;
}
abstract function service($getName,$price,$num);
}
class MyBooks extends CommodityObject{
function service($getName, $price, $num){
echo '書名:'.$getName.'<p>';
echo '價格:'.$price.'<p>';
echo '數量:'.$num.'<p>';
}
}
$book=new MyBooks();$book->service('PHP從入門到精通', 60, 3);
$book->Display();
?>
5、介面
【例5】介面
<?php
interface MPopedom{
function popedom();
}
interface MPurview{
function purview($name);
}
class Manager implements MPopedom,MPurview{
function popedom(){
echo '擁有會員的許可權';
}
function purview($name){
echo '擁有管理員的許可權';
}
}
$manager=new Manager();
$manager->popedom();
$manager->purview('張三');
?>
6、克隆物件
使用close關鍵字進行物件的複製,直接將一個物件賦值給另一個物件$object2=$object1,則object2是object1的引用。有時候單純的克隆物件外,還需要克隆出來的物件可以擁有自己的屬性和方法,這時可以使用__clone()方法來實現,在物件的克隆過程中呼叫該方法。
<?php
class SpoerObject{
private $object_type='book';
public function setType($type){
$this->object_type=$type;
}
public function getType(){
return $this->object_type;
}
}
$book1=new SpoerObject();
$book2=$book1;
$book2->setType('computer');
echo $book1->getType();//輸出computer
$book3=clone $book1;
$book3->setType('mouse');
echo $book1->getType();//還是輸出computer
?>
<?php
class SpoerObject{
private $object_type='book';
public function setType($type){
$this->object_type=$type;
}
public function getType(){
return $this->object_type;
}
public function __clone(){
$this->object_type='computer';
}
}
$book1=new SpoerObject();
$book2=clone $book1;
echo $book2->getType();//輸出computer
?>
7、物件比較及型別檢測
使用“==”可以比較兩個物件的內容是否相同,“===”可以比較兩個物件的地址是否相同。
instanceof操作符可以檢測當前物件是屬於哪個類。一般格式為:
ObjectName instanceof ClassName
【例1】物件型別的比較
<?php
class SpoerObject{
private $object_type='book';
public function setType($type){
$this->object_type=$type;
}
public function getType(){
return $this->object_type;
}
}
$book1=new SpoerObject();
$book2=$book1;
$book3=clone $book1;
echo ($book2==$book1 ? "true" : "false").'<p>';//true
echo ($book2===$book1 ? "true" : "false").'<p>';//true
echo ($book3==$book1 ? "true" : "false").'<p>';//true
echo ($book3===$book1 ? "true" : "false").'<p>';//false
?>
【例2】
<?php
class SportObject{ }//建立空類
class BasketBallSports extends SportObject{
private $height;
public function __construct($height){
$this->height=$height;
}
public function PrintInfo(){//覆蓋父類的方法
echo '身高:'.$this->height.'<p>';
}
}
$sp1=new BasketBallSports( 188);
echo ($sp1 instanceof BasketBallSports ? 'true' : 'false');//true
echo ($sp1 instanceof SportObject ? 'true' : 'false');//true
?>
8、魔術方法
PHP中以兩個下劃線“__”開頭的方法,例如__destruct、__construct、__clone等都稱為魔術方法。如果要呼叫這些魔術方法必須在類中進行定義。
- __set()方法
當程式試圖寫入一個不存在或不可見的成員變數時,PHP會呼叫__set()方法。__set()包含兩個引數,分別是變數名稱和變數值,引數都不可省略。
- __get()方法
當程式呼叫一個未定義或不可見的成員變數時,可以通過__get()方法來讀取變數值。__get()犯法有一個引數,表示要呼叫的變數名。
【例1】
<?php
class SportObject{
private $type='';
public function __get($name){
if(isset($this->$name)){
echo "<p>變數".$name."的值為:".$this->$name;
}else{//如果變數未定義
echo "<p>變數".$name."未定義,初始化為0";
$this->$name=0;
}
}
public function __set($name,$value){
if(isset($this->$name)){
$this->$name=$value;
echo '<p>變數'.$name.'已定義';
}else{
echo '<p>變數'.$name.'未定義,被初始化為'.$value;
$this->$name;
}
}
}
$myComputer=new SportObject();
$myComputer->type='DIY';
echo $myComputer->type;
$myComputer->name;
?>
執行結果為:
- __call()方法
當程式檢視呼叫不存在或不可見的成員方法時,PHP會先呼叫__call()方法類儲存方法名及其引數。__call()方法包含兩個引數,即方法名和方法引數,其中方法引數是以陣列的形式存在的。
<?php
class SportObject{
public function myDream(){
echo '不存在要 呼叫的方法時呼叫此方法<p>';
}
public function __call($method,$parameter){
echo '方法'.$method.'不存在';
echo '引數資訊:';
var_dump($parameter);
$this->myDream();
}
}
$exam=new SportObject();
$exam->dream('張三',22);
?>
- __sleep()方法和__wakeup()方法
PHP使用serialize()函式可以序列化物件。就是將類中的變數全部儲存下來,物件中的類則只儲存類名。當一個物件被序列化,PHP會呼叫__sleep方法(如果存在的話). 在反序列化一個物件後,PHP 會呼叫__wakeup方法. 這兩個方法都不接受引數. __sleep方法必須返回一個數組,包含需要序列化的屬性. PHP會拋棄其它屬性的值. 如果沒有__sleep方法,PHP將儲存所有屬性. 在程式執行前,serialize() 函式會首先檢查是否存在一個魔術方法 __sleep.如果存在,__sleep()方法會先被呼叫,然後才執行序列化(序列化)操作。這個功能可以用於清理物件,並返回一個包含物件中所有變數名稱的陣列。如果該方法不返回任何內容,則NULL被序列化,導致一個E_NOTICE錯誤。與之相反,unserialize()會檢查是否存在一個__wakeup方法。如果存在,則會先呼叫 __wakeup方法,預先準備物件資料。
<?php
class Connection {
protected $link;
private $server, $username, $password, $db;
public function __construct($server, $username, $password, $db)
{
$this->server = $server;
$this->username = $username;
$this->password = $password;
$this->db = $db;
$this->connect();
}
private function connect()
{
$this->link = mysql_connect($this->server, $this->username, $this->password);
mysql_select_db($this->db, $this->link);
echo '連線資料庫成功!<p>';
}
public function __sleep()
{
return array('server', 'username', 'password', 'db');
}
public function __wakeup()
{
$this->connect();
}
}
$cnn = new Connection('localhost', 'root', null, 'students');
$str=serialize($cnn);
echo '序列化後的字串'.$str.'<p>';
$recnn=unserialize($str);
?>
輸出結果:
- __toString()方法
當使用echo或print輸出物件時,將物件轉化為字串。如果沒有__toString()方法,直接輸出物件會導致致命錯誤。注意,使用echo或print後面直接跟輸出的物件,不可加入其它的字元。如echo ‘字元’.$myComputer不可以。
<?php
class Connection {
protected $link;
private $server, $username, $password, $db;
public function __construct($server, $username, $password, $db)
{
$this->server = $server;
$this->username = $username;
$this->password = $password;
$this->db = $db;
$this->connect();
}
private function connect()
{
$this->link = mysql_connect($this->server, $this->username, $this->password);
mysql_select_db($this->db, $this->link);
echo '連線資料庫成功!<p>';
}
public function __toString(){
$res='伺服器名:'.$this->server.'<p>';
$res.='使用者名稱:'.$this->username.'<p>';
$res.='密碼:'.$this->password.'<p>';
$res.='資料庫:'.$this->db.'<p>';
return $res; }
}
$cnn = new Connection('localhost', 'root', '', 'students');
echo $cnn;
?>
- __autoload()方法
__autoload()方法可以自動例項化所需要使用的類。__autoload()方法在指定的路勁下自動查詢和該類同名稱的檔案,如果找到程式繼續執行,否則報錯。注意,類名必須與儲存的檔名一致。
現在將上面5)中的類connection儲存在當前工程目錄下的connection.php中,index.php訪問程式碼如下:
<?php
function __autoload($class_name){
$cname=getcwd().'\\'.$class_name.'.php';
if(file_exists($cname)){
include_once($cname);//動態引入類檔案
}else{
echo '類路徑'.$cname.'錯誤';
}
}
$cnn = new Connection('localhost', 'root', '', 'students');
echo $cnn;
?>