首頁 後端開發 php教程 類別與物件 - PHP手冊筆記

類別與物件 - PHP手冊筆記

Aug 08, 2016 am 09:29 AM
class function public static

基本概念

PHP對待物件的方式與引用和句柄相同,即每個變數都持有物件的引用,而不是整個物件的拷貝。

當建立新物件時,該物件總是被賦值,除非該物件定義了建構函式並且在出錯時拋出了一個異常。類別應在被實例化之前定義。

建立物件時,如果該類別屬於一個名字空間,則必須使用其完整名稱。

在類別定義內部,可以用new selfnew parent建立物件。

<code><?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);</code>
登入後複製

這段程式碼的輸出如下,這是為什麼呢?

<code>null
null
object(stdClass)[1]
  public 'var' => string '$assigned will have this value.' (length=31)</code>
登入後複製

PHP 5.3引進了兩個新方法來建立一個物件的實例,可以使用下面的方法建立實例。

<code><?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);  // true
var_dump($obj1 == $obj2);  // true</code>
登入後複製

PHP不支援多重繼承,被繼承的方法和屬性可以透過同樣的名字重新宣告被覆寫,注意參數必須保持一致,當然建構子除外。但是如果父類別定義方法時使用了final,則該方法不可被覆寫。可以透過parent::來存取被覆蓋的方法和屬性,parent::只能存取父類別中的常數const,不能存取變數。

<code><?php 
class A {
	private $name = &#39;A&#39;;
	const conname = &#39;A&#39;;
	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());  // B
var_dump($b->getParent());  // A
$c = new C;
var_dump($c->getName());  // C
var_dump($c->getParent());  // B</code>
登入後複製

自PHP 5.5起,關鍵字class也可用於類別名稱的解析。使用ClassName::class你可以取得一個字串,包含了類別ClassName的完全限定名稱。

<code><?php 
namespace NS {
	class ClassName {}
	echo ClassName::class;  // NS\ClassName
}</code>
登入後複製

屬性

屬性,也就是類別的變數成員。屬性中的變數可以初始化,但是初始化的值必須是常數。這裡的常數是指 PHP 腳本在編譯階段時就可以得到其值,而不依賴執行時的資訊才能求值。

在類別的成員方法裡,存取非靜態屬性使用$this->property,存取靜態屬性使用self::$property。靜態屬性聲明時使用static關鍵字。

類常數

在定義常數時不需要$符號和存取控製關鍵字。

介面(interface)中也可以定義常數。

自動載入類別

寫物件導向的應用程式時,通常對每個類別的定義履歷表一個PHP來源檔案。當某個檔案需要呼叫這些類別時,需要在檔案開頭寫一個長長的包含檔案清單。其實,並不需要這樣,可以定義一個__autoload()函數,它會在試圖使用尚未被定義的類別時自動呼叫。

手冊Tip說,spl_autoload_register()提供了一種更靈活的方式來實現類別的自動加載,這個後面再看。

自動載入不可用於PHP的CLI互動模式,也就是命令列模式。

用戶輸入中可能存在危險字符,起碼要在__autoload()時驗證下輸入。

可以透過下面的方式自動載入類別。

<code><?php 
function __autoload($class_name) {
	require_once $class_name.&#39;.php&#39;;
}
$obj1 = new MyClass1();
$obj2 = new MyClass2();</code>
登入後複製

對於異常處理,後面再看。

建構子和析構函式

PHP 5允許開發者在一個類別中定義一個方法作為建構函數,建構函數也不支援重載。

如果子類別中定義了建構函數,則不會隱式呼叫父類別的建構函數,否則會如同一個普通類別方法那樣從父類別繼承(前提是未被定義為private)。要執行父類別的建構函數,需要在子類別建構子中呼叫parent::__construct()

與其它方法不同,當__construct()與父類__construct()具有不同參數時,可以覆蓋。

自PHP 5.3.3起,在命名空間中,與類別名稱同名的方法不再作為建構子。

析構函數會在某個物件的所有參考都被刪除或物件被顯示銷毀時執行。析構函數即使在使用exit()終止腳本執行時也會被呼叫。

試圖在析構函數中拋出異常,將會導致致命錯誤。

門禁控制

類別屬性必須定義為公有、受保護、私有之一,且不能省略關鍵字。如果類別中方法沒有設定存取控制的關鍵字,則該方法預設為公有。

同一個類別的對象,即使不是同一個實例,也可以互相存取對方的私有與保護成員。範例程式如下。

<code><?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'));</code>
登入後複製

對象繼承

如果一個類別擴展了另一個,則父類別必須在子類別前被聲明。

範圍解析操作符

範圍解析運算符,簡單來說就是一對冒號,可以用來存取靜態成員、類別常數,也可以用來呼叫父類別中的屬性和方法。

當在類別定義之外引用這些項目時,要使用類別名稱。

static

使用static關鍵字可以用來定義靜態方法和屬性,也可用於定義靜態變數以及後期靜態綁定。聲明類別屬性或方法為靜態,就可以不實例化類別而直接存取。

靜態屬性不能透過一個類別已實例化的物件來訪問,但靜態方法可以。

如果沒有指定存取控制,屬性和方法預設為公有。

用靜態方法呼叫一個非靜態方法會導致一個E_STRICT等級的錯誤。

抽象类

PHP 5支持抽象类和抽象方法。类中如果有一个抽象方法,那这个类必须被声明为抽象的。

抽象类不能被实例化。抽象方法只是声明了其调用方式(参数),不能定义其具体的功能实现。继承抽象类时,子类必须定义父类中的所有抽象方法,且这些方法的访问控制必须和父类一样活更宽松。

方法的调用方式必须匹配。但是,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。这也试用与PHP 5.4起的构造函数。可以在子类中定义父类签名中不存在的可选参数。

<code><?php 
abstract class AbstractClass {
	abstract protected function prefixName($name);
}
class ConcreteClass extends AbstractClass {
	public function prefixName($name, $separator = &#39;, &#39;) {
		if($name === "Pacman") {
			$prefix = &#39;Mr&#39;;
		} elseif($name === &#39;Pacwoman&#39;) {
			$prefix = "Mrs";
		} else {
			$prefix = &#39;&#39;;
		}
		return "$prefix $separator $name  ";
	}
}
$class = new ConcreteClass;
echo $class->prefixName('Pacman');
echo $class->prefixName('Pacwoman');</code>
登入後複製

对象接口

听说过接口,一直没用过。使用接口,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容,也就是说接口中定义的所有方法都是空的。接口中定义的所有方法都必须是公有的,这是接口的特性。

接口也可以继承多个接口,用逗号分隔,使用extends操作符。类中必须实现接口中定义的所有方法,否则会报错。要实现一个接口,使用implements操作符。类可以实现多个接口,用逗号分隔。实现多个接口时,接口中的方法不能有重名。类要实现接口,必须使用和接口中所定义的方法完全一致的方式。

接口中也可定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口覆盖。

traits

从PHP 5.4.0开始,可以使用traits实现代码复用。Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 不能通过它自身来实例化。它为传统继承增加了水平特性的组合。

优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。

通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误,为解决冲突,需使用insteadof操作符来指明使用冲突方法中的哪一个,这种方法仅允许排除掉其它方法。as操作符可以将其中一个冲突的方法以另一个名称(别名)来引入。

<code><?php 
trait A {
	public function smallTalk() {
		echo &#39;a&#39;;
	}
	public function bigTalk() {
		echo &#39;A&#39;;
	}
}
trait B {
	public function smallTalk() {
		echo &#39;b&#39;;
	}
	public function bigTalk() {
		echo &#39;B&#39;;
	}
}
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</code>
登入後複製

使用as操作符还可以用来调整方法的访问控制,或者给方法一个改变了访问控制的别名,原版方法的访问控制规则没有改变。

<code><?php 
trait HelloWorld {
	public function sayHello() {
		echo &#39;Hello World.&#39;;
	}
}
class MyClass1 {
	use HelloWorld {
		sayHello as protected;
	}
}
class MyClass2 {
	use HelloWorld {
		sayHello as private myPrivateHello;
	}
}</code>
登入後複製

就像类能够使用trait那样,多个trait能够组合为一个trait

为了对使用的类施加强制要求,trait 支持抽象方法的使用。

<code><?php 
trait Hello {
	public function sayHelloWorld() {
		echo &#39;Hello &#39; . $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();</code>
登入後複製

如果trait定义了一个属性,那类将不能定义同样名称的属性,否则会产生错误。

重载

PHP提供的重载是指动态地创建类属性和方法,与其它绝大多数面向对象语言不同。通过魔术方法来实现。当使用不可访问的属性或方法时,重载方法会被调用。所有的重载方法都必须被声明为public

使用__get()__set()__isset()__unset()进行属性重载,示例如下。

<code><?php 
class PropertyTest {
	private $data = array();
	public $declared = 1;
	private $hidden = 2;
	public function __set($name, $value) {
		echo "Setting $name to $value. " . &#39;<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($obj->a));
var_dump($obj->declared);
var_dump($obj->hidden);</code>
登入後複製

输出结果如下:

<code>Setting a to 1. 
Getting a. 
int 1
Is a set? 
boolean true
Unsetting a. 
Is a set? 
boolean false
int 1
Getting hidden. 
null</code>
登入後複製

在对象中调用一个不可访问方法时,__call()会被调用。用静态方式中调用一个不可访问方法时,__callStatic()会被调用。参数为调用方法的名称和一个枚举数组,注意区分大小写。

使用__call()__callStatic()对方法重载,示例如下。

<code><?php 
class MethodTest {
	public function __call($name, $arguments) {
		echo "Calling object method $name " . 
			implode(', ', $arguments) . '<br>';
	}
	public static function __callStatic($name, $arguments) {
		echo "Calling static method $name " . 
			implode(', ', $arguments) . '<br>';
	}
}
$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context');</code>
登入後複製

遍历对象

对象可以用过单元列表来遍历,例如用foreach语句。默认所有可见属性都将被用于遍历。

<code><?php 
class MyClass {
	public $var1 = &#39;value 1&#39;;
	public $var2 = &#39;value 2&#39;;
	public $var3 = &#39;value 3&#39;;
	private $var4 = &#39;value 4&#39;;
	protected $var5 = &#39;value 5&#39;;
}
$obj = new MyClass;
foreach($obj as $key => $value) {
	echo "$key => $value <br>";
}</code>
登入後複製

示例程序2实现了Iterator接口的对象遍历,示例程序3通过实现IteratorAggregate来遍历对象。

魔术方法

PHP 将所有以__(两个下划线)开头的类方法保留为魔术方法。定义类方法时,除魔术方法外,建议不要以__为前缀。

前面遇到过的魔术方法有:__construct()__destruct()__call()__callStatic()__get()__set()__isset()__unset()。后面将会介绍:__sleep()__wakeup()__toString()__invoke()__set_state()__clone()__debugInfo()

__sleep__wakeup不清楚具体做什么用的,示例程序中给出了个数据库连接的例子。

__toString方法用于一个类被当成字符串时应怎样回应。此方法必须返回一个字符串,且不能再方法中抛出异常。如果将一个未定义__toString()方法的对象转换为字符串,将产生错误。

当尝试以调用函数的方式调用一个对象时,__invoke()方法会被调用。

当调用var_export()导出类时,__set_state()会被调用。

当调用var_dump()时,__debugInfo会被调用。PHP 5.6新加入,没合适的环境无法测试。

final

果父类中的方法被声明为final,则子类无法覆盖该方法。如果一个类被声明为final,则不能被继承。属性不能被定义为final,只有类和方法才能被定义为final

对象复制

多数情况,我们不需要完全复制一个对象,但有时确实需要。对象复制可以通过clone关键字来完成。这种复制是通过调用对象的__clone()方法实现的,但是对象中的__clone()方法不能被直接调用。

对象比较

比较运算符==为真的条件是:两个对象的属性和属性值都相等,而且两个对象是同一个类的实例。

继承与统一个基类的两个子类的对象不会相等==

<code><?php 
class Base {}
class A extends Base {}
class B extends Base {}
$a = new A;
$b = new B;
var_dump($a == $b);  // false</code>
登入後複製

全等运算符===为真的条件是:两个对象变量一定要指向某个类的同一个实例(即同一个对象)。

类型约束

类型约束是指函数的参数可以指定必须为对象、接口、数组或者callable类型。但是类型约束不能用于标量类型如intstringtraits也不允许。类型约束允许NULL值。

后期静态绑定

后期静态绑定,用于在继承范围内引用静态调用的类。

转发调用,指的是通过以下几种方式进行的静态调用:self::parent::static::以及forward_static_call()

后期静态绑定的工作原理是,存储了上一个非转发调用的类名。

当进行静态方法调用时,该类名即为明确指定的那个;当进行非静态方法调用时,即为该对象所属的类。

使用self::或者__CLASS__对当前类的静态引用,取决于定义当前方法所在的类。

<code><?php 
class A {
	public static function who() {
		echo __CLASS__;
	}
	public static function test() {
		self::who();
	}
}
class B extends A {
	public static function who() {
		echo __CLASS__;
	}
}
B::test();  // A
B::who();  // B</code>
登入後複製

static::关键字表示运行时最初调用的类,后期静态绑定就是这样使用。如下面程序所示,也就是说调用test()时引用的类是B而不是A

<code><?php 
class A {
	public static function who() {
		echo __CLASS__;
	}
	public static function test() {
		static::who();
	}
}
class B extends A {
	public static function who() {
		echo __CLASS__;
	}
}
B::test();  // B
B::who();  // B</code>
登入後複製

示例2给出的是非静态环境下使用static::

后期静态绑定的解析,会一直到取得一个完全解析了的静态调用为止。另外,如果静态调用使用parent::self::将转发调用信息。

<code><?php 
class A {
	public static function foo() {
		static::who();
	}
	public static function who() {
		echo __CLASS__;
	}
}
class B extends A {
	public static function test() {
		A::foo();
		parent::foo();
		self::foo();
	}
	public static function who() {
		echo __CLASS__;
	}
}
class C extends B {
	public static function who() {
		echo __CLASS__;
	}
}
C::test();  // ACC</code>
登入後複製

那么问题来了,结果为什么是这样的呢?

对象和引用

默认情况下,对象时通过引用传递的。但这种说法不完全正确,其实两个对象变量不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。

对象序列化

所有PHP里面的值,都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变为原来的值。

序列化一个对象,将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。为了能够unserialize()一个对象,这个对象的类必须已经定义过。在应用程序中序列化对象以便在之后使用,强烈推荐在整个应用程序都包含对象的类的定义。

(全文完)

以上就介绍了类与对象 - PHP手册笔记,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它們
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Python中的class類別和method方法的使用方法 Python中的class類別和method方法的使用方法 Apr 21, 2023 pm 02:28 PM

類別和方法的概念和實例類別(Class):用來描述具有相同的屬性和方法的物件的集合。它定義了該集合中每個物件所共有的屬性和方法。物件是類別的實例。方法:類別中定義的函數。類別的建構方法__init__():類別有一個名為init()的特殊方法(建構方法),該方法在類別實例化時會自動呼叫。實例變數:在類別的宣告中,屬性是用變數來表示的,這種變數就稱為實例變量,實例變數就是用self修飾的變數。實例化:建立一個類別的實例,類別的具體物件。繼承:即一個派生類別(derivedclass)繼承基底類別(baseclass)的

function是什麼意思 function是什麼意思 Aug 04, 2023 am 10:33 AM

function是函數的意思,是一段具有特定功能的可重複使用的程式碼區塊,是程式的基本組成單元之一,可以接受輸入參數,執行特定的操作,並傳回結果,其目的是封裝一段可重複使用的程式碼,提高程式碼的可重複使用性和可維護性。

iOS的developer版和public版有什麼不同? iOS的developer版和public版有什麼不同? Mar 01, 2024 pm 12:55 PM

每年Apple發布新的iOS和macOS大版本之前,用戶都可以提前幾個月下載測試版搶先體驗一番。由於大眾和開發人員都使用該軟體,蘋果公司為兩者推出了developer和public版即開發者測試版的公共測試版。 iOS的developer版和public版有什麼差別呢?從字面上的意思來說,developer版是開發者測試版,public版是公共測試版。 developer版和public版面向的物件不同。 developer版是蘋果公司給開發者測試使用的,需要蘋果開發者帳號才能收到下載併升級,是

python中class是什麼意思 python中class是什麼意思 May 21, 2019 pm 05:10 PM

class是python中的一個關鍵字,用來定義一個類,定義類別的方法:class後面加一個空格然後加類名;類名規則:首字母大寫,如果多個單字用駝峰命名法,如【class Dog()】。

使用jQuery替換元素的class名稱 使用jQuery替換元素的class名稱 Feb 24, 2024 pm 11:03 PM

jQuery是一種經典的JavaScript庫,被廣泛應用於網頁開發中,它簡化了在網頁上處理事件、操作DOM元素和執行動畫等操作。在使用jQuery時,常會遇到需要取代元素的class名稱的情況,本文將介紹一些實用的方法,以及具體的程式碼範例。 1.使用removeClass()和addClass()方法jQuery提供了removeClass()方法來刪除

PHP Class用法詳解:讓你的程式碼更清楚易讀 PHP Class用法詳解:讓你的程式碼更清楚易讀 Mar 10, 2024 pm 12:03 PM

在編寫PHP程式碼時,使用類別(Class)是一個非常常見的做法。透過使用類,我們可以將相關的功能和資料封裝在一個單獨的單元中,使程式碼更加清晰、易於閱讀和易於維護。本文將詳細介紹PHPClass的用法,並提供具體的程式碼範例,幫助讀者更好地理解如何在實際專案中應用類別來優化程式碼。 1.建立和使用類別在PHP中,可以使用關鍵字class來定義一個類,並在類別中定義屬性和方法。

Vue報錯:無法正確使用v-bind綁定class和style,怎麼解決? Vue報錯:無法正確使用v-bind綁定class和style,怎麼解決? Aug 26, 2023 pm 10:58 PM

Vue報錯:無法正確使用v-bind綁定class和style,怎麼解決?在Vue開發中,我們常常會用到v-bind指令來動態綁定class和style,但是有時候我們可能會遇到一些問題,如無法正確使用v-bind綁定class和style。在本篇文章中,我將為你解釋這個問題的原因,並提供解決方案。首先,讓我們先來了解v-bind指令。 v-bind用於將V

SpringBoot怎麼透過自訂classloader加密保護class文件 SpringBoot怎麼透過自訂classloader加密保護class文件 May 11, 2023 pm 09:07 PM

背景最近針對公司框架進行關鍵業務代碼進行加密處理,防止透過jd-gui等反編譯工具能夠輕鬆還原工程代碼,相關混淆方案配置使用比較複雜且針對springboot項目問題較多,所以針對class文件加密再通過自訂的classloder進行解密加載,此方案並不是絕對安全,只是加大反編譯的困難程度,防君子不防小人,整體加密保護流程圖如下圖所示maven插件加密使用自訂maven插件對編譯後指定的class檔案進行加密,加密後的class檔案拷貝到指定路徑,這裡是儲存到resource/corecla

See all articles