這篇文章帶給大家的內容是關於PHP面試之常用的物件導向知識總結(附範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。
相關推薦:《2019年PHP面試題大匯總(收藏)》
#繼上一篇「php面試之面向對象的相關知識介紹」發表後,今天繼續更新。
整個物件導向文章的結構涉及的內容模組有:
一、物件導向與物件導向有什麼差別?
二、物件導向有什麼特徵?
三、什麼是建構子和析構函式?
四、物件導向的作用域範圍有哪幾種?
五、PHP 中魔術方法有哪些?
六、什麼是物件克隆?
七、this、self和parent的差別是什麼?
八、抽象類別與介面有什麼差別與連結?
九、PHP物件導向的常考面試題講解
關於PHP物件導向的內容將會被分成三篇文章進行講解完整區塊內容,第一篇主要講解一到四點內容,第二篇主要講解五到八的內容,第三篇圍繞第九點講解。
以下正文的內容都來自《PHP程式設計師面試筆試寶典》書籍,如果轉載請保留出處:
五、PHP種魔術方法有哪些?
在PHP中,把所有以__(兩條下畫線)開頭的類別方法保留為魔術方法。所以在定義類別方法時,不建議使用 __ 作為方法的前綴。以下分別介紹每個魔術方法的作用。
1. __get、__set、__isset、__unset
這四個方法是為在類別和它們的父類別中沒有宣告的屬性而設計的。
1)在存取類別屬性的時候,若屬性可以訪問,則直接回傳;若不可以被訪問,則呼叫__get 函數。
方法簽章為:public mixed __get ( string $name )
2)設定一個物件的屬性時,若屬性可以訪問,則直接賦值;若不可被訪問,則呼叫__set 函數。
方法簽章為:public void __set ( string $name , mixed $value )
3)當無法存取的屬性呼叫 isset() 或 empty() 時,__isset() 則會被呼叫。
方法簽章為:public bool __isset ( string $name )
4)當對無法存取屬性呼叫 unset() 時,__unset() 就會被呼叫。
方法簽章為:public bool _unset ( string $name )
需要注意的是,上述存在的無法存取包含屬性沒有定義,或屬性的存取控制為proteced或private(沒有存取權限的屬性)。
下面透過一個例子把物件變數保存在另外一個陣列中。
<?php class Test { /* 保存未定义的对象变量 */ private $data = array(); public function __set($name, $value){ $this->data[$name] = $value; } public function __get($name){ if(array_key_exists($name, $this->data)) return $this->data[$name]; return NULL; } public function __isset($name){ return isset($this->data[$name]); } public function __unset($name){ unset($this->data[$name]); } } $obj = new Test; $obj->a = 1; echo $obj->a . "\n"; ?>
程式的運行結果為
1
2. __construct、__destruct
1)__construct 建構函數,實例化物件時被呼叫。
2)__destruct 析構函數,當物件被銷毀時呼叫。通常情況下,PHP只會釋放物件所佔有的記憶體和相關的資源,對於程式設計師自己申請的資源,需要明確地去釋放。通常可以把需要釋放資源的操作放在析構方法中,這樣可以確保在物件被釋放的時候,程式設計師自己申請的資源也能被釋放。
例如,可以在建構函式中開啟一個文件,然後在析構函式中關閉文件。
<?php class Test { protected $file = NULL; function __construct(){ $this->file = fopen("test","r"); } function __destruct(){ fclose($this->file); } } ?>
3. __call()和__callStatic()
1)__call( $method, $arg_array ):當呼叫一個不可存取的方法時會呼叫這個方法。
2)__callStatic的工作方式與 __call() 類似,當呼叫的靜態方法不存在或權限不足時,會自動呼叫__callStatic()。
使用範例如下:
<?php class Test { public function __call ($name, $arguments) { echo "调用对象方法 '$name' ". implode(', ', $arguments). "\n"; } public static function __callStatic ($name, $arguments) { echo "调用静态方法 '$name' ". implode(', ', $arguments). "\n"; } } $obj = new Test; $obj->method1('参数1'); Test::method2('参数2'); ?>
程式的運行結果為
呼叫物件方法'method1' 參數1
呼叫靜態方法'method2' 參數2
############################# ###4. __sleep()和__wakeup()#########1)__sleep 串列化的時候呼叫。 ###2)__wakeup 反串列化的時候呼叫。 ###也就是說,執行serialize()和unserialize()時,會先呼叫這兩個函數。例如,在序列化一個物件時,如果這個物件有一個資料庫連接,想要在反序列化中恢復這個連接的狀態,那麼就可以透過重載這兩個方法來實現。範例程式碼如下:###
<?php class Test { public $conn; private $server, $user, $pwd, $db; public function __construct($server, $user, $pwd, $db) { $this->server = $server; $this->user = $user; $this->pwd = $pwd; $this->db = $db; $this->connect(); } private function connect() { $this->conn = mysql_connect($this->server, $this->user, $this->pwd); mysql_select_db($this->db, $this->conn); } public function __sleep() { return array('server', 'user', 'pwd', 'db'); } public function __wakeup() { $this->connect(); } public function __destruct(){ mysql_close($conn); } } ?>######5. __toString()######
__toString 在打印一个对象时被调用,可以在这个方法中实现想要打印的对象的信息,使用示例如下:
<?php class Test { public $age; public function __toString() { return "age:$this->age"; } } $obj = new Test(); $obj->age=20; echo $obj; ?>
程序的运行结果为
age:20
6.__invoke()
在引入这个魔术方法后,可以把对象名当作方法直接调用,它会间接调用这个方法,使用示例如下:
<?php class Test { public function __invoke() { print "hello world"; } } $obj = new Test; $obj(); ?>
程序的运行结果为
hello world
7.__set_state()
调用 var_export 时被调用,用__set_state的返回值作为var_export 的返回值。使用示例如下:
<?php class People { public $name; public $age; public static function __set_state ($arr) { $obj = new People; $obj->name = $arr['name']; $obj->age = $arr['aage']; return $obj; } } $p = new People; $p->age = 20; $p->name = 'James'; var_dump(var_export($p)); ?>
程序的运行结果为
People::__set_state(array( 'name' => 'James', 'age' => 20, )) NULL
8.__clone()
这个方法在对象克隆的时候被调用,php提供的__clone()方法对一个对象实例进行浅拷贝,也就是说,对对象内的基本数值类型通过值传递完成拷贝,当对象内部有对象成员变量的时候,最好重写__clone方法来实现对这个对象变量的深拷贝。使用示例如下:
<?php class People { public $age; public function __toString() { return "age:$this->age \n"; } } class MyCloneable { public $people; function __clone() { $this->people = clone $this->people; //实现对象的深拷贝 } } $obj1 = new MyCloneable(); $obj1->people = new People(); $obj1->people->age=20; $obj2 = clone $obj1; $obj2->people->age=30; echo $obj1->people; echo $obj2->people; ?>
程序的运行结果为
age:20 age:30
由此可见,通过对象拷贝后,对其中一个对象值的修改不影响另外一个对象。
9.__autoload()
当实例化一个对象时,如果对应的类不存在,则该方法被调用。这个方法经常的使用方法为:在方法体中根据类名,找出类文件,然后require_one 导入这个文件。由此,就可以成功地创建对象了,使用示例如下:
Test.php:
<?php class Test { function hello() { echo 'Hello world'; } } ?>
index.php:
<?php function __autoload( $class ) { $file = $class . '.php'; if ( is_file($file) ) { require_once($file); //导入文件 } } $obj = new Test(); $obj->hello(); ?>
程序的运行结果为
Hello world
在index.php中,由于没有包含Test.php,在实例化Test对象的时候会自动调用__autoload方法,参数$class的值即为类名Test,这个函数中会把Test.php引进来,由此Test对象可以被正确地实例化。
这种方法的缺点是需要在代码中文件路径做硬编码,当修改文件结构的时候,代码也要跟着修改。另一方面,当多个项目之间需要相互引用代码的时候,每个项目中可能都有自己的__autoload,这样会导致两个__autoload冲突。当然可以把__autoload修改成一个。这会导致代码的可扩展性和可维护性降低。由此从PHP5.1开始引入了spl_autoload,可以通过spl_autoload_register注册多个自定义的autoload方法,使用示例如下:
index.php
<?php function loadprint( $class ) { $file = $class . '.php'; if (is_file($file)) { require_once($file); } } spl_autoload_register( 'loadprint' ); //注册自定义的autoload方法从而避免冲突 $obj = new Test(); $obj->hello(); ?>
spl_autoload是_autoload()的默认实现,它会去include_path中寻找$class_name(.php/.inc) 。除了常用的spl_autoload_register外,还有如下几个方法:
1)spl_autoload:_autoload()的默认实现。
2)spl_autoload_call:这个方法会尝试调用所有已经注册的__autoload方法来加载请求的类。
3)spl_autoload_functions:获取所有被注册的__autoload方法。
4)spl_autoload_register:注册__autoload方法。
5)spl_autoload_unregister:注销已经注册的__autoload方法。
6)spl_autoload_extensions:注册并且返回spl_autoload方法使用的默认文件的扩展名。
引申:PHP有哪些魔术常量?
除了魔术变量外,PHP还定义了如下几个常用的魔术常量。
1)__LINE__:返回文件中当前的行号。
2)__FILE__:返回当前文件的完整路径。
3)__FUNCTION__:返回所在函数名字。
4)__CLASS__:返回所在类的名字。
5)__METHOD__:返回所在类方法的名称。与__FUNCTION__不同的是,__METHOD__返回的是“class::function”的形式,而__FUNCTION__返回“function”的形式。
6)__DIR__:返回文件所在的目录。如果用在被包括文件中,则返回被包括的文件所在的目录(PHP 5.3.0中新增)。
7)__NAMESPACE__:返回当前命名空间的名称(区分大小写)。此常量是在编译时定义的(PHP 5.3.0 新增)。
8)__TRAIT__:返回 Trait 被定义时的名字。Trait 名包括其被声明的作用区域(PHP 5.4.0 新增)。
六、什么是对象克隆?
对于对象而言,PHP用的是引用传递,也就是说,对象间的赋值操作只是赋值了一个引用的值,而不是整个对象的内容,下面通过一个例子来说明引用传递存在的问题:
<?php class My_Class { public $color; } $obj1 = new My_Class (); $obj1->color = "Red"; $obj2 = $obj1; $obj2->color ="Blue"; //$obj1->color的值也会变成"Blue" ?>
因为PHP使用的是引用传递,所以在执行$obj2 = $obj1后,$obj1和$obj2都是指向同一个内存区(它们在内存中的关系如下图所示),任何一个对象属性的修改对另外一个对象也是可见的。
在很多情况下,希望通过一个对象复制出一个一样的但是独立的对象。PHP提供了clone关键字来实现对象的复制。如下例所示:
<?php class My_Class { public $color; } $obj1 = new My_Class (); $obj1->color = "Red"; $obj2 = clone $obj1; $obj2->color ="Blue"; //此时$obj1->color的值仍然为"Red" ?>
$obj2 = clone $obj1把obj1的整个内存空间复制了一份存放到新的内存空间,并且让obj2指向这个新的内存空间,通过clone克隆后,它们在内存中的关系如下图所示。
此时对obj2的修改对obj1是不可见的,因为它们是两个独立的对象。
在学习C++的时候有深拷贝和浅拷贝的概念,显然PHP也存在相同的问题,通过clone关键字克隆出来的对象只是对象的一个浅拷贝,当对象中没有引用变量的时候这种方法是可以正常工作的,但是当对象中也存在引用变量的时候,这种拷贝方式就会有问题,下面通过一个例子来进行说明:
<?php class My_Class { public $color; } $c ="Red"; $obj1 = new My_Class (); $obj1->color =&$c; //这里用的是引用传递 $obj2 = clone $obj1; //克隆一个新的对象 $obj2->color="Blue"; //这时,$obj1->color的值也变成了"Blue" ?>
在这种情况下,这两个对象在内存中的关系如下图所示。
从上图中可以看出,虽然obj1与obj2指向的对象占用了独立的内存空间,但是对象的属性color仍然指向一个相同的存储空间,因此当修改了obj2->color的值后,意味着c的值被修改,显然这个修改对obj1也是可见的。这就是一个非常典型的浅拷贝的例子。为了使两个对象完全独立,就需要对对象进行深拷贝。那么如何实现呢,PHP提供了类似于__clone方法(类似于C++的拷贝构造函数)。把需要深拷贝的属性,在这个方法中进行拷贝:使用示例如下:
<?php class My_Class { public $color; public function __clone() { $this->color = clone $this->color; } } $c ="Red"; $obj1 = new My_Class (); $obj1->color =&$c; $obj2 = clone $obj1; $obj2->color="Blue"; //这时,$obj1->color的值仍然为"Red" ?>
通过深拷贝后,它们在内存中的关系如图1-4所示。
通过在__clone方法中对对象的引用变量color进行拷贝,使obj1与obj2完全占用两块独立的存储空间,对obj2的修改对obj1也不可见。
七、this、self和parent的区别是什么?
this、self、parent三个关键字从字面上比较好理解,分别是指这、自己、父亲。其中,this指的是指向当前对象的指针(暂用C语言里面的指针来描述),self指的是指向当前类的指针,parent指的是指向父类的指针。
以下将具体对这三个关键字进行分析。
##1.this关键字## 1 <?php 2 class UserName { 3 private $name; // 定义成员属性 4 function __construct($name) { 5 $this->name = $name; // 这里已经使用了this指针 6 } 7 // 析构函数 8 function __destruct() { 9 } 10 // 打印用户名成员函数 11 function printName() { 12 print ($this->name."\n") ; // 又使用了this指针 13 } 14 } 15 // 实例化对象 16 $nameObject = new UserName ( "heiyeluren" ); 17 // 执行打印 18 $nameObject->printName (); // 输出: heiyeluren 19 // 第二次实例化对象 20 $nameObject2 = new UserName ( "PHP5" ); 21 // 执行打印 22 $nameObject2->printName (); // 输出:PHP5 23 ?>
上例中,分别在5行和12行使用了this指针,那么this到底是指向谁呢?其实,this是在实例化的时候来确定指向谁,例如,第一次实例化对象的时候(16行),当时this就是指向$nameObject 对象,那么执行第12行打印的时候就把print($this->name)变成了print ($nameObject->name),输出"heiyeluren"。
对于第二个实例化对象,print( $this- >name )变成了print( $nameObject2->name ),于是就输出了"PHP5"。
所以,this就是指向当前对象实例的指针,不指向任何其他对象或类。
2.self关键字
先要明确一点,self是指向类本身,也就是self是不指向任何已经实例化的对象,一般self用来访问类中的静态变量。
1 <?php 2 class Counter { 3 // 定义属性,包括一个静态变量 4 private static $firstCount = 0; 5 private $lastCount; 6 // 构造函数 7 function __construct() { 8 // 使用self来调用静态变量,使用self调用必须使用::(域运算符号) 9 $this->lastCount = ++ selft::$firstCount; 10 } 11 // 打印lastCount数值 12 function printLastCount() { 13 print ($this->lastCount) ; 14 } 15 } 16 // 实例化对象 17 $countObject = new Counter (); 18 $countObject->printLastCount (); // 输出 1 19 ?>
上述示例中,在第4行定义了一个静态变量$firstCount,并且初始值为0,那么在第9行的时候调用了这个值,使用的是self来调用,中间使用域运算符“::”来连接,这时候调用的就是类自己定义的静态变量$firstCount,它与下面对象的实例无关,只是与类有关,无法使用this来引用,只能使用 self来引用,因为self是指向类本身,与任何对象实例无关。
3.parent关键字
parent是指向父类的指针,一般使用parent来调用父类的构造函数。
1 <?php 2 // 基类 3 class Animal { 4 // 基类的属性 5 public $name; // 名字 6 // 基类的构造函数 7 public function __construct($name) { 8 $this->name = $name; 9 } 10 } 11 // 派生类 12 class Person extends Animal // Person类继承了Animal类 13 { 14 public $personSex; // 性别 15 public $personAge; // 年龄 16 // 继承类的构造函数 17 function __construct($personSex, $personAge) { 18 parent::__construct ( "heiyeluren" ); // 使用parent调用了父类的构造函数 19 $this->personSex = $personSex; 20 $this->personAge = $personAge; 21 } 22 function printPerson() { 23 print ($this->name . " is " . $this->personSex . ",this year " . $this->personAge) ; 24 } 25 } 26 // 实例化Person对象 27 $personObject = new Person ( "male", "21" ); 28 // 执行打印 29 $personObject->printPerson (); // 输出:heiyeluren is male,this year 21 30 ?>
上例中,成员属性都是public的,特别是父类的,是为了供继承类通过this来访问。第18行: parent::__construct( "heiyeluren" ),使用了parent来调用父类的构造函数进行对父类的初始化,因为父类的成员都是public的,于是就能够在继承类中直接使用 this来访问从父类继承的属性。
八、抽象类与接口有什么区别与联系?
抽象类应用的定义如下:
abstract class ClassName{ }
抽象类具有以下特点:
1)定义一些方法,子类必须实现父类所有的抽象方法,只有这样,子类才能被实例化,否则子类还是一个抽象类。
2)抽象类不能被实例化,它的意义在于被扩展。
3)抽象方法不必实现具体的功能,由子类来完成。
4)当子类实现抽象类的方法时,这些方法的访问控制可以和父类中的一样,也可以有更高的可见性,但是不能有更低的可见性。例如,某个抽象方法被声明为protected的,那么子类中实现的方法就应该声明为protected或者public的,而不能声明为private。
5)如果抽象方法有参数,那么子类的实现也必须有相同的参数个数,必须匹配。但有一个例外:子类可以定义一个可选参数(这个可选参数必须要有默认值),即使父类抽象方法的声明里没有这个参数,两者的声明也无冲突。下面通过一个例子来加深理解:
<?php abstract class A{ abstract protected function greet($name); } class B extends A { public function greet($name, $how="Hello ") { echo $how.$name."\n"; } } $b = new B; $b->greet("James"); $b->greet("James","Good morning "); ?>
程序的运行结果为
Hello James Good morning James
定义抽象类时,通常需要遵循以下规则:
1)一个类只要含有至少一个抽象方法,就必须声明为抽象类。
2)抽象方法不能够含有方法体。
接口可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。在PHP中,接口是通过interface关键字来实现的,与定义一个类类似,唯一不同的是接口中定义的方法都是公有的而且方法都没有方法体。接口中所有的方法都是公有的,此外接口中还可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所覆盖。要实现一个接口,可以通过关键字implements来完成。实现接口的类中必须实现接口中定义的所有方法。虽然PHP不支持多重继承,但是一个类可以实现多个接口,用逗号来分隔多个接口的名称。下面给出一个接口使用的示例:
<?php interface Fruit { const MAX_WEIGHT = 3; //静态常量 function setName($name); function getName(); } class Banana implements Fruit { private $name; function getName() { return $this->name; } function setName($_name) { $this->name = $_name; } } $b = new Banana(); //创建对象 $b->setName("香蕉"); echo $b->getName(); echo "<br>"; echo Banana::MAX_WEIGHT; //静态常量 ?>
程序的运行结果为
香蕉
3
接口和抽象类主要有以下区别:
抽象类:PHP5支持抽象类和抽象方法。被定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。被定义为抽象的方法只是声明了其调用方法和参数,不能定义其具体的功能实现。抽象类通过关键字abstract来声明。
接口:可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。在这种情况下,可以通过interface关键字来定义一个接口,在接口中声明的方法都不能有方法体。
二者虽然都是定义了抽象的方法,但是事实上两者区别还是很大的,主要区别如下:
1)对接口的实现是通过关键字implements来实现的,而抽象类继承则是使用类继承的关键字extends实现的。
2)接口没有数据成员(可以有常量),但是抽象类有数据成员(各种类型的成员变量),抽象类可以实现数据的封装。
3)接口没有构造函数,抽象类可以有构造函数。
4)接口中的方法都是public类型,而抽象类中的方法可以使用private、protected或public来修饰。
5)一个类可以同时实现多个接口,但是只能实现一个抽象类。
以上是PHP面試之常用的物件導向知識總結(附範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

使用數據庫存儲會話的主要優勢包括持久性、可擴展性和安全性。 1.持久性:即使服務器重啟,會話數據也能保持不變。 2.可擴展性:適用於分佈式系統,確保會話數據在多服務器間同步。 3.安全性:數據庫提供加密存儲,保護敏感信息。

在PHP中實現自定義會話處理可以通過實現SessionHandlerInterface接口來完成。具體步驟包括:1)創建實現SessionHandlerInterface的類,如CustomSessionHandler;2)重寫接口中的方法(如open,close,read,write,destroy,gc)來定義會話數據的生命週期和存儲方式;3)在PHP腳本中註冊自定義會話處理器並啟動會話。這樣可以將數據存儲在MySQL、Redis等介質中,提升性能、安全性和可擴展性。

SessionID是網絡應用程序中用來跟踪用戶會話狀態的機制。 1.它是一個隨機生成的字符串,用於在用戶與服務器之間的多次交互中保持用戶的身份信息。 2.服務器生成並通過cookie或URL參數發送給客戶端,幫助在用戶的多次請求中識別和關聯這些請求。 3.生成通常使用隨機算法保證唯一性和不可預測性。 4.在實際開發中,可以使用內存數據庫如Redis來存儲session數據,提升性能和安全性。

在無狀態環境如API中管理會話可以通過使用JWT或cookies來實現。 1.JWT適合無狀態和可擴展性,但大數據時體積大。 2.Cookies更傳統且易實現,但需謹慎配置以確保安全性。

要保護應用免受與會話相關的XSS攻擊,需採取以下措施:1.設置HttpOnly和Secure標誌保護會話cookie。 2.對所有用戶輸入進行輸出編碼。 3.實施內容安全策略(CSP)限制腳本來源。通過這些策略,可以有效防護會話相關的XSS攻擊,確保用戶數據安全。

优化PHP会话性能的方法包括:1.延迟会话启动,2.使用数据库存储会话,3.压缩会话数据,4.管理会话生命周期,5.实现会话共享。这些策略能显著提升应用在高并发环境下的效率。

theSession.gc_maxlifetimesettinginphpdeterminesthelifespanofsessiondata,setInSeconds.1)它'sconfiguredinphp.iniorviaini_set().2)abalanceisesneededeededeedeedeededto toavoidperformance andunununununexpectedLogOgouts.3)

在PHP中,可以使用session_name()函數配置會話名稱。具體步驟如下:1.使用session_name()函數設置會話名稱,例如session_name("my_session")。 2.在設置會話名稱後,調用session_start()啟動會話。配置會話名稱可以避免多應用間的會話數據衝突,並增強安全性,但需注意會話名稱的唯一性、安全性、長度和設置時機。


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

Atom編輯器mac版下載
最受歡迎的的開源編輯器

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!