PHP對(duì)待對(duì)象的方式與引用和句柄相同,即每個(gè)變量都持有對(duì)象的引用,而不是整個(gè)對(duì)象的拷貝。
當(dāng)創(chuàng)建新對(duì)象時(shí),該對(duì)象總是被賦值,除非該對(duì)象定義了構(gòu)造函數(shù)并且在出錯(cuò)時(shí)拋出了一個(gè)異常。類應(yīng)在被實(shí)例化之前定義。
創(chuàng)建對(duì)象時(shí),如果該類屬于一個(gè)名字空間,則必須使用其完整名稱。
在類定義內(nèi)部,可以用new self
和new parent
創(chuàng)建對(duì)象。
<?php $instance = new stdClass();$assigned = $instance;$reference = & $instance;$instance->var = '$assigned will have this value.';$instance = null;var_dump($instance);var_dump($reference);var_dump($assigned);
這段代碼的輸出如下,這是為什么呢?
nullnullobject(stdClass)[1] public 'var' => string '$assigned will have this value.' (length=31)
PHP 5.3引進(jìn)了兩個(gè)新方法來創(chuàng)建一個(gè)對(duì)象的實(shí)例,可以使用下面的方法創(chuàng)建實(shí)例。
<?php class Test {static public function getNew() {return new static;}}class Child extends Test {}$obj1 = new Test();$obj2 = new $obj1;var_dump($obj1 !== $obj2); // true$obj3 = Test::getNew();var_dump($obj3 instanceof Test); // true$obj4 = Child::getNew();var_dump($obj4 instanceof Child); // truevar_dump($obj1 == $obj2); // true
PHP不支持多重繼承,被繼承的方法和屬性可以通過同樣的名字重新聲明被覆蓋,注意參數(shù)必須保持一致,當(dāng)然構(gòu)造函數(shù)除外。但是如果父類定義方法時(shí)使用了final
,則該方法不可被覆蓋??梢酝ㄟ^parent::
來訪問被覆蓋的方法和屬性,parent::
只能訪問父類中的常量const
,不能訪問變量。
<?php class A {PRivate $name = 'A';const conname = 'A';public function getName() {return $this->name;}}class B extends A {private $name = 'B';const conname = 'B';public function getName() {return $this->name;}public function getParent() {return parent::conname;}}class C extends B {private $name = 'C';const conname = 'C';public function getName() {return $this->name;}public function getParent() {return parent::conname;}}$a = new A;var_dump($a->getName()); // A$b = new B;var_dump($b->getName()); // Bvar_dump($b->getParent()); // A$c = new C;var_dump($c->getName()); // Cvar_dump($c->getParent()); // B
自PHP 5.5起,關(guān)鍵詞class
也可用于類名的解析。使用ClassName::class
你可以獲取一個(gè)字符串,包含了類ClassName
的完全限定名稱。
<?php namespace NS {class ClassName {}echo ClassName::class; // NS/ClassName}
屬性屬性,也就是類的變量成員。屬性中的變量可以初始化,但是初始化的值必須是常數(shù)。這里的常數(shù)是指 PHP 腳本在編譯階段時(shí)就可以得到其值,而不依賴于運(yùn)行時(shí)的信息才能求值。
在類的成員方法里,訪問非靜態(tài)屬性使用$this->property
,訪問靜態(tài)屬性使用self::$property
。靜態(tài)屬性聲明時(shí)使用static
關(guān)鍵字。
在定義常量時(shí)不需要$
符號(hào)和訪問控制關(guān)鍵字。
接口(interface)中也可以定義常量。
自動(dòng)加載類寫面向?qū)ο蟮膽?yīng)用程序時(shí),通常對(duì)每個(gè)類的定義簡(jiǎn)歷一個(gè)PHP源文件。當(dāng)某個(gè)文件需要調(diào)用這些類時(shí),需要在文件開頭寫一個(gè)長(zhǎng)長(zhǎng)的包含文件列表。其實(shí),并不需要這樣,可以定義一個(gè)__autoload()
函數(shù),它會(huì)在試圖使用尚未被定義的類時(shí)自動(dòng)調(diào)用。
手冊(cè)Tip說,spl_autoload_register()
提供了一種更加靈活的方式來實(shí)現(xiàn)類的自動(dòng)加載,這個(gè)后面再看。
自動(dòng)加載不可用于PHP的CLI交互模式,也就是命令行模式。
用戶輸入中可能存在危險(xiǎn)字符,起碼要在__autoload()
時(shí)驗(yàn)證下輸入。
可以通過下面的方式自動(dòng)加載類。
<?php function __autoload($class_name) {require_once $class_name.'.php';}$obj1 = new MyClass1();$obj2 = new MyClass2();
對(duì)于異常處理,后面再看。
構(gòu)造函數(shù)和析構(gòu)函數(shù)PHP 5允許開發(fā)者在一個(gè)類中定義一個(gè)方法作為構(gòu)造函數(shù),構(gòu)造函數(shù)也不支持重載。
如果子類中定義了構(gòu)造函數(shù),則不會(huì)隱式調(diào)用父類的構(gòu)造函數(shù),否則會(huì)如同一個(gè)普通類方法那樣從父類繼承(前提是未被定義為private
)。要執(zhí)行父類的構(gòu)造函數(shù),需要在子類構(gòu)造函數(shù)中調(diào)用parent::__construct()
。
與其它方法不同,當(dāng)__construct()
與父類__construct()
具有不同參數(shù)時(shí),可以覆蓋。
自PHP 5.3.3起,在命名空間中,與類名同名的方法不再作為構(gòu)造函數(shù)。
析構(gòu)函數(shù)會(huì)在某個(gè)對(duì)象的所有引用都被刪除或者對(duì)象被顯示銷毀時(shí)執(zhí)行。析構(gòu)函數(shù)即使在使用exit()
終止腳本運(yùn)行時(shí)也會(huì)被調(diào)用。
試圖在析構(gòu)函數(shù)中拋出異常,將會(huì)導(dǎo)致致命錯(cuò)誤。
訪問控制類屬性必須定義為公有、受保護(hù)、私有之一,不能省略關(guān)鍵字。如果類中方法沒有設(shè)置訪問控制的關(guān)鍵字,則該方法默認(rèn)為公有。
同一個(gè)類的對(duì)象,即使不是同一個(gè)實(shí)例,也可以互相訪問對(duì)方的私有與保護(hù)成員。示例程序如下。
<?php Class Test {private $foo;public function __construct($foo) {$this->foo = $foo;}private function bar() {echo 'accessed the private method.';}public function baz(Test $other) {$other->foo = 'hello';var_dump($other->foo);$other->bar();}}$test = new Test('test');$test->baz(new Test('other'));
對(duì)象繼承如果一個(gè)類擴(kuò)展了另一個(gè),則父類必須在子類前被聲明。
范圍解析操作符范圍解析操作符,簡(jiǎn)單地說就是一對(duì)冒號(hào),可以用于訪問靜態(tài)成員、類常量,還可以用于調(diào)用父類中的屬性和方法。
當(dāng)在類定義之外引用這些項(xiàng)目時(shí),要使用類名。
static使用static
關(guān)鍵字可以用來定義靜態(tài)方法和屬性,也可用于定義靜態(tài)變量以及后期靜態(tài)綁定。聲明類屬性或方法為靜態(tài),就可以不實(shí)例化類而直接訪問。
靜態(tài)屬性不能通過一個(gè)類已實(shí)例化的對(duì)象來訪問,但靜態(tài)方法可以。
如果沒有指定訪問控制,屬性和方法默認(rèn)為公有。
用靜態(tài)方法調(diào)用一個(gè)非靜態(tài)方法會(huì)導(dǎo)致一個(gè)E_STRICT
級(jí)別的錯(cuò)誤。
PHP 5支持抽象類和抽象方法。類中如果有一個(gè)抽象方法,那這個(gè)類必須被聲明為抽象的。
抽象類不能被實(shí)例化。抽象方法只是聲明了其調(diào)用方式(參數(shù)),不能定義其具體的功能實(shí)現(xiàn)。繼承抽象類時(shí),子類必須定義父類中的所有抽象方法,且這些方法的訪問控制必須和父類一樣活更寬松。
方法的調(diào)用方式必須匹配。但是,子類定義了一個(gè)可選參數(shù),而父類抽象方法的聲明里沒有,則兩者的聲明并無沖突。這也試用與PHP 5.4起的構(gòu)造函數(shù)。可以在子類中定義父類簽名中不存在的可選參數(shù)。
<?php abstract class AbstractClass {abstract protected function prefixName($name);}class ConcreteClass extends AbstractClass {public function prefixName($name, $separator = ', ') {if($name === "Pacman") {$prefix = 'Mr';} elseif($name === 'Pacwoman') {$prefix = "Mrs";} else {$prefix = '';}return "$prefix $separator $name ";}}$class = new ConcreteClass;echo $class->prefixName('Pacman');echo $class->prefixName('Pacwoman');
對(duì)象接口聽說過接口,一直沒用過。使用接口,可以指定某個(gè)類必須實(shí)現(xiàn)哪些方法,但不需要定義這些方法的具體內(nèi)容,也就是說接口中定義的所有方法都是空的。接口中定義的所有方法都必須是公有的,這是接口的特性。
接口也可以繼承多個(gè)接口,用逗號(hào)分隔,使用extends
操作符。類中必須實(shí)現(xiàn)接口中定義的所有方法,否則會(huì)報(bào)錯(cuò)。要實(shí)現(xiàn)一個(gè)接口,使用implements
操作符。類可以實(shí)現(xiàn)多個(gè)接口,用逗號(hào)分隔。實(shí)現(xiàn)多個(gè)接口時(shí),接口中的方法不能有重名。類要實(shí)現(xiàn)接口,必須使用和接口中所定義的方法完全一致的方式。
接口中也可定義常量。接口常量和類常量的使用完全相同,但是不能被子類或子接口覆蓋。
traits從PHP 5.4.0開始,可以使用traits
實(shí)現(xiàn)代碼復(fù)用。Traits 是一種為類似 PHP 的單繼承語言而準(zhǔn)備的代碼復(fù)用機(jī)制。Trait 不能通過它自身來實(shí)例化。它為傳統(tǒng)繼承增加了水平特性的組合。
優(yōu)先順序是來自當(dāng)前類的成員覆蓋了 trait 的方法,而 trait 則覆蓋了被繼承的方法。
通過逗號(hào)分隔,在 use 聲明列出多個(gè) trait,可以都插入到一個(gè)類中。如果兩個(gè) trait 都插入了一個(gè)同名的方法,如果沒有明確解決沖突將會(huì)產(chǎn)生一個(gè)致命錯(cuò)誤,為解決沖突,需使用insteadof
操作符來指明使用沖突方法中的哪一個(gè),這種方法僅允許排除掉其它方法。as
操作符可以將其中一個(gè)沖突的方法以另一個(gè)名稱(別名)來引入。
<?php trait A {public function smallTalk() {echo 'a';}public function bigTalk() {echo 'A';}}trait B {public function smallTalk() {echo 'b';}public function bigTalk() {echo 'B';}}class Talker {use A, B {B::smallTalk insteadof A;A::bigTalk insteadof B;B::bigTalk as talk;}}$t = new Talker();$t->smallTalk(); // b$t->bigTalk(); // A$t->talk(); // B
使用as
操作符還可以用來調(diào)整方法的訪問控制,或者給方法一個(gè)改變了訪問控制的別名,原版方法的訪問控制規(guī)則沒有改變。
<?php trait HelloWorld {public function sayHello() {echo 'Hello World.';}}class MyClass1 {use HelloWorld {sayHello as protected;}}class MyClass2 {use HelloWorld {sayHello as private myPrivateHello;}}
就像類能夠使用trait
那樣,多個(gè)trait
能夠組合為一個(gè)trait
。
為了對(duì)使用的類施加強(qiáng)制要求,trait 支持抽象方法的使用。
<?php trait Hello {public function sayHelloWorld() {echo 'Hello ' . $this->getWorld();}abstract public function getWorld();}class MyHelloWorld {private $world;use Hello;public function getWorld() {return $this->world;}public function setWorld($val) {$this->world = $val;}}$c = new MyHelloWorld;$c->setWorld('world');$c->sayHelloWorld();
如果trait
定義了一個(gè)屬性,那類將不能定義同樣名稱的屬性,否則會(huì)產(chǎn)生錯(cuò)誤。
PHP提供的重載是指動(dòng)態(tài)地創(chuàng)建類屬性和方法,與其它絕大多數(shù)面向?qū)ο笳Z言不同。通過魔術(shù)方法來實(shí)現(xiàn)。當(dāng)使用不可訪問的屬性或方法時(shí),重載方法會(huì)被調(diào)用。所有的重載方法都必須被聲明為public
。
使用__get()
,__set()
,__isset()
,__unset()
進(jìn)行屬性重載,示例如下。
<?php class PropertyTest {private $data = array();public $declared = 1;private $hidden = 2;public function __set($name, $value) {echo "Setting $name to $value. " . '<br>';$this->data[$name] = $value;}public function __get($name) {echo "Getting $name. <br>";if(array_key_exists($name, $this->data)) {return $this->data[$name];}return null;}public function __isset($name) {echo "Is $name set? <br>";return isset($this->data[$name]);}public function __unset($name) {echo "Unsetting $name. <br>";unset($this->data[$name]);}}$obj = new PropertyTest;$obj->a = 1;var_dump($obj->a);var_dump(isset($obj->a));unset($obj->a);var_dump(isset($
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注