Maison > Article > développement back-end > Résumé des connaissances orientées objet couramment utilisées pour les entretiens PHP (avec exemples)
Cet article vous apporte un résumé des connaissances orientées objet couramment utilisées pour les entretiens PHP (avec des exemples). Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.
Recommandations associées : "Résumé des questions d'entretien PHP en 2019 (collection) "
Suite de l'article précédent "Orienté objet pour les entretiens PHP Après la publication de « Relevant Knowledge Introduction », nous continuerons à le mettre à jour aujourd'hui.
Les modules de contenu impliqués dans la structure de l'ensemble de l'article orienté objet sont :
1 Quelle est la différence entre orienté objet et orienté processus ?
2. Quelles sont les caractéristiques de l’orienté objet ?
3. Que sont les constructeurs et les destructeurs ?
4. Quelles sont les portées orientées objet ?
5. Quelles sont les méthodes magiques en PHP ?
6. Qu'est-ce que le clonage d'objets ?
7. Quelle est la différence entre cela, soi et parent ?
8. Quelles sont les différences et les connexions entre les classes abstraites et les interfaces ?
9. Explication des questions d'entretien PHP courantes orientées objet
Le contenu orienté objet de PHP sera divisé en trois articles pour expliquer le contenu complet. Le premier article explique principalement un à quatre points. la deuxième partie explique principalement le contenu de cinq à huit, et la troisième partie se concentre sur le neuvième point.
Le contenu du texte suivant provient du livre "PHP Programmer Interview Written Test Guide" Si vous réimprimez, veuillez conserver la source :
5. . Les méthodes magiques PHP sont lesquelles ?
En PHP, conservez toutes les méthodes de classe commençant par __ (deux traits de soulignement) comme méthodes magiques. Par conséquent, lors de la définition des méthodes de classe, il n'est pas recommandé d'utiliser __ comme préfixe de la méthode. Les fonctions de chaque méthode magique sont présentées ci-dessous.
1. __get, __set, __isset, __unset
Ces quatre méthodes sont conçues pour les propriétés qui ne sont pas déclarées dans les classes et leurs classes parents.
1) Lors de l'accès à un attribut de classe, si l'attribut est accessible, il sera renvoyé directement s'il n'est pas accessible, la fonction __get sera renvoyée ; appelé.
La signature de la méthode est : public Mixed __get (string $name)
2) Lors de la définition des attributs d'un objet, si l'attribut est accessible, la valeur sera attribuée directement si ; il n'est pas accessible, puis appelez la fonction __set.
La signature de la méthode est : public void __set (string $name, Mixed $value)
3) Lorsque isset() ou empty() est appelé sur une propriété inaccessible, __isset() sera appelé.
La signature de la méthode est : public bool __isset (string $name)
4) Lorsque unset() est appelé sur une propriété inaccessible, __unset() sera appelé.
La signature de la méthode est : public bool _unset (string $name)
Il est à noter que l'inaccessibilité ci-dessus inclut des attributs qui ne sont pas définis, ou que le contrôle d'accès des attributs est protégé ou privé (attributs sans droits d'accès ).
Ce qui suit utilise un exemple pour enregistrer les variables d'objet dans un autre tableau.
<?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"; ?>
Le résultat d'exécution du programme est
1
2. __construct, __destruct
1) Constructeur __construct, appelé lors de l'instanciation d'un objet.
2) Destructeur __destruct, appelé lorsque l'objet est détruit. Normalement, PHP ne libérera que la mémoire et les ressources associées occupées par l'objet. Les ressources demandées par le programmeur lui-même doivent être libérées explicitement. Habituellement, les opérations qui nécessitent de libérer des ressources peuvent être placées dans la méthode destructeur, ce qui garantit que lorsque l'objet est libéré, les ressources demandées par le programmeur peuvent également être libérées.
Par exemple, vous pouvez ouvrir un fichier dans le constructeur puis fermer le fichier dans le destructeur.
<?php class Test { protected $file = NULL; function __construct(){ $this->file = fopen("test","r"); } function __destruct(){ fclose($this->file); } } ?>
3. __call() et __callStatic()
1) __call( $method, $arg_array ) : Cette méthode sera appelée lorsqu'une méthode inaccessible est appelée.
2) __callStatic fonctionne de manière similaire à __call() Lorsque la méthode statique appelée n'existe pas ou dispose d'autorisations insuffisantes, __callStatic() sera appelée automatiquement.
Les exemples d'utilisation sont les suivants :
<?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'); ?>
Le résultat de l'exécution du programme est
Appeler la méthode objet 'method1', paramètre 1
Appeler la méthode statique 'method2', paramètre 2
4. __sleep() et __wakeup()
1) __sleep est appelé lors de la sérialisation.
2) __wakeup est appelé pendant la désérialisation.
En d'autres termes, lors de l'exécution de serialize() et unserialize(), ces deux fonctions seront appelées en premier. Par exemple, lors de la sérialisation d'un objet, si l'objet dispose d'une connexion à la base de données et que vous souhaitez restaurer l'état de la connexion lors de la désérialisation, vous pouvez y parvenir en surchargeant ces deux méthodes. L'exemple de code est le suivant :
<?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)一个类可以同时实现多个接口,但是只能实现一个抽象类。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!