PHP的類及其實例:
class Myclass{
public $prop = 123;
}
$obj = new Myclass();
?>
類的成員屬性(屬性的稱呼相對於‘方法’而言)包括類常量和類變量,其中類常量在定義時不可為空,類的屬性在定義時如果被賦值,只能使用變量和數組,並且不能是表達式,因為類屬性在編譯期被初始化,PHP在編譯器不執行表達式。
1、成員的訪問控制
public:可以繼承,可以在類的方法之外被訪問,如$obj->prop;
protected:可以繼承,不可以在類的方法之外被訪問;
private:不可以繼承,不可以在類的方法之外訪問;
PHP4使用var來聲明類的屬性,在PHP5之后不再使用,PHP5.3之前使用被警告,在PHP5.3之后可以在public之前或單獨使用作為public的別名。
這三個訪問控制關鍵字也可以修飾構造函數,當private和protected修飾類的構造函數時,只能通過一個public static的靜態方法調用構造函數以實例化對象,因為該構造函數無法在類之外被訪問了,比如,單例類的實現:
class Singleton{
private static $instance = null;
public $k = 88;
private function __construct(){
}
public static function getInstance(){
if(self:: $instance == null){
// self::$instance = new Singleton();
self:: $instance = new self();
}
return self:: $instance;
}
public function __clone(){
throw new Exception('Singleton class can not be cloned');
return self:: $instance;
}
}
// $in = new Singleton();
$in = Singleton::getInstance();
var_dump( $in);
?>
2、繼承禁止:final關鍵字,僅用於修飾類或類的方法
如果一個類被final修飾,這個類不能被繼承,如果一個方法被final修飾,則這個方法不能被子類重寫(override)。
class Myclass{
public $prop = 123;
final public static function methodA(){
return 'this is a final method';
}
}
?>
3、抽象類和抽象方法,abstract僅用於類和方法,抽象類不能直接用於實例化對象,只能用於產生子類
abstract class Myclass{
public $prop = 123;
abstract public function methodA(); // 抽象方法沒有實現函數體
}
?>
4、類的常量及其訪問:類的常量不能使用訪問控制修飾符,他是public的,可繼承,可以被子類重寫。訪問類的常量必須使用雙冒號::,可以使用類名或類的實例(對象)來訪問,因為是常量,所以名稱不能使用表示變量的符號$。
class Myclass{
public $prop = 123;
const X = 999;
final public static function methodA(){
return 'this is a final method';
}
public function getConst(){
return self::X; // 或者$this::X
}
}
$in = new Myclass();
echo $in->getConst();
echo Myclass::X;
?>
類的常量是一個值,在代碼編譯器常量名被替換為相應的值,在運行期不可修改,因此類的常量與類本身無關,在類實例化對象之前就已經存在,因此類的常量可以直接使用類名訪問。
class P{
const M = 100;
const N = self::M;
public static $k = 99;
public function getStatic(){
return self:: $k;
}
public function getStatic2(){
return static:: $k;
}
}
class S extends P{
const M = 200;
public static $k = 88;
public function getPConst(){
return parent::N;
}
}
$p = new P();
$s = new S();
echo $p::N.'<br>'; // 100
echo $s::N.'<br>'; // 200 該常量名繼承自父類,在編譯期就已經直接取 self::M 的值替換了 ,注意區別類的方法中使用 self::M
echo $s->getPConst().'<br>'; // 100
echo $s->getStatic2().'<br>'; // 88
?>
5、類的靜態成員訪問
static可以修飾類的屬性和方法,被static修飾的成員歸屬於類而非類的實例。靜態成員必須使用類名加雙冒號::來訪問,因此在實例化對象之前靜態成員就存在了,因此,在靜態方法內禁止使用指向實例本身的偽變量$this(或習慣上稱為$this指針),可以使用關鍵字self代替類名(相當於類的魔術常量__CLASS__)。
static不能修飾類的構造函數,也不能修飾接口聲明的方法。
class Myclass{
public static $x = 99;
public function getX(){
return self:: $x;
}
public static function getX2(){
return self:: $x;
}
}
echo Myclass:: $x."<br>";
echo Myclass::getX2()."<br>";
$MC = new Myclass();
echo $MC->getX2()."<br>";
?>
靜態成員可以使用訪問控制關鍵字修飾,可以被繼承和重寫,需要注意的是,如果一個子類繼承了父類的靜態方法(沒有重寫該方法),那么子類調用的實際是父類的靜態方法。因為靜態成員持有者是類不是對象,所以類的多個實例是共享一個靜態屬性,在一個實例中修改靜態屬性會影響到另一個實例的靜態屬性:
class A{
public static $a1 = 11;
public $a2 = 22;
public static function showStatic(){
return self:: $a1;
}
public function getStatic(){
return self:: $a1;
}
public function getClassStatic(){
$className = get_called_class();
return $className:: $a1;
}
public function getProp(){
return $this->a2;
}
}
class B extends A{
public static $a1 = 88;
public $a2 = 99;
}
$obj1 = new A();
$obj2 = new B();
echo $obj1->getStatic()."<br>"; // 11
echo $obj1->getClassStatic()."<br>"; // 11
echo $obj1->getProp()."<br>"; // 22
echo B::showStatic()."<br>"; // 11調用的是父類的方法,訪問父類的靜態成員
echo $obj2->getStatic()."<br>"; // 調用的是父類的方法,方法中的slef指向持有該靜態方法的類
echo $obj2->getClassStatic()."<br>"; // 88
echo $obj2->getProp()."<br>"; // 99
?>
后期靜態綁定:為了避免子類重寫靜態屬性后,使用繼承來的方法仍然方法父類的靜態屬性,PHP5.3增加了一個新的語法,后期靜態綁定,使用static關鍵詞替代self關鍵字,使得static指向與get_called_class()返回的相同類,即當前調用該靜態方法的對象所屬的類,該關鍵字對於靜態方法的訪問同樣有效。
public function getClassStatic(){
$className = get_called_class();
return $className::$a1;
}
//可以寫成
public function getClassStatic(){
return static::$a1;
}
6、類的方法中幾個指向類或實例的關鍵字
$this->propName $this指向類的實例
parent::xxx parent指向父類,可以訪問父類的靜態常量、靜態屬性,不能訪問父類的非靜態屬性,可以調用父類的方法(不能是private方法,無論是否靜態)
self::xxx self指向定義了當前被調用的方法的類,用於訪問靜態成員和類的常量
static::xxx 訪問實例化了調用當前方法的實例的那個類,用於訪問靜態成員和類的常量,它跟self的差別是訪問靜態成員是“后期靜態綁定”
7、類的繼承中的重寫問題
重寫的成員的訪問控制程度不能被縮小,例如,public的成員不能重寫為protected
非靜態成員不能重寫為靜態成員,靜態成員也不能重寫為非靜態成員
8、接口中定義的方法必須是public
類在實現接口的方法時,這些方法也必須是public的。
接口也可以定義接口常量,用法和類的常量一致,但是接口不可以定義非函數成員。
接口與接口之間可以繼承,接口的繼承可以是多繼承,用逗號隔開(子類與父類的繼承是單繼承)。
一個口可以實現多個接口,用逗號隔開。
interface Ix extends Iy,Iz{
public function a();
}
class A implements Iy,Iz{
// .......
}
?>
9、類型約束
PHP的函數(或類的方法)可以在聲明是限定參數的類型,但只能限定array或object(class/interface),如果限定為string,PHP會認為是限定為一個string類的object參數。
如果類型被限定為某一個接口,則傳入的參數必須是實現該接口的類的實例。
在接口實現、子類重寫父類方法時,不能修改已經限定的參數類型。
在方法、函數調用時,如果傳入了與限定的參數類型不同的數據將會報錯,但是可以接收null參數。
interface Im{
public function a( classm $m);
}
class A implements Im{
public function a( $x){ // error ,參數$x必須限定為 classm 類型以匹配接口的定義
var_dump( $x);
}
}
?>