Home > Article > Backend Development > Summary of commonly used object-oriented knowledge for PHP interviews (with examples)
This article brings you a summary of object-oriented knowledge commonly used in PHP interviews (with examples). It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.
Related recommendations: "2019 PHP interview questions summary (collection)"
Following the previous article "Object-oriented php interview "Relevant knowledge introduction " will be updated today after being published.
The content modules involved in the structure of the entire object-oriented article are:
1. What is the difference between object-oriented and process-oriented?
2. What are the characteristics of object-oriented?
3. What are constructors and destructors?
4. What are the types of object-oriented scopes?
5. What are the magic methods in PHP?
6. What is object cloning?
7. What is the difference between this, self and parent?
8. What are the differences and connections between abstract classes and interfaces?
9. Explanation of PHP object-oriented common interview questions
The content about PHP object-oriented will be divided into three articles to explain the complete content. The first article mainly explains one to four points. , the second part mainly explains the content from five to eight, and the third part focuses on the ninth point.
The content of the following text comes from the "PHP Programmer Interview Written Test Guide" book. If you reprint, please keep the source:
5. PHP magic methods are Which ones?
In PHP, keep all class methods starting with __ (two underscores) as magic methods. Therefore, when defining class methods, it is not recommended to use __ as the prefix of the method. The functions of each magic method are introduced below.
1. __get, __set, __isset, __unset
These four methods are designed for properties that are not declared in the class and their parent class.
#1) When accessing a class attribute, if the attribute can be accessed, it will be returned directly; if it cannot be accessed, the __get function will be called.
The method signature is: public mixed __get (string $name)
2) When setting the properties of an object, if the property can be accessed, the value will be assigned directly; if it cannot be accessed, then Call the __set function.
The method signature is: public void __set (string $name, mixed $value)
3) When isset() or empty() is called on an inaccessible property, __isset() will be called.
The method signature is: public bool __isset (string $name)
4) When unset() is called on an inaccessible property, __unset() will be called.
The method signature is: public bool _unset (string $name)
It should be noted that the above inaccessibility includes attributes that are not defined, or the access control of the attributes is protected or private (attributes without access rights).
The following is an example of saving object variables in another array.
<?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"; ?>
The running result of the program is
1
2. __construct, __destruct
1) __construct constructor, called when instantiating an object.
2) __destruct destructor, called when the object is destroyed. Normally, PHP will only release the memory and related resources occupied by the object. Resources requested by the programmer themselves need to be released explicitly. Usually, operations that need to release resources can be placed in the destructor method, which ensures that when the object is released, the resources applied for by the programmer can also be released.
For example, you can open a file in the constructor and then close the file in the destructor.
<?php class Test { protected $file = NULL; function __construct(){ $this->file = fopen("test","r"); } function __destruct(){ fclose($this->file); } } ?>
3. __call() and __callStatic()
1) __call( $method, $arg_array ): This method will be called when an inaccessible method is called.
2) __callStatic works similarly to __call(). When the called static method does not exist or has insufficient permissions, __callStatic() will be called automatically.
Usage examples are as follows:
<?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'); ?>
The running result of the program is
Call the object method 'method1' parameter 1
Call the static method 'method2' parameter 2
4. __sleep() and __wakeup()
1) __sleep is called during serialization.
2) __wakeup is called during deserialization.
In other words, when executing serialize() and unserialize(), these two functions will be called first. For example, when serializing an object, if the object has a database connection and you want to restore the status of the connection during deserialization, you can achieve this by overloading these two methods. The sample code is as follows:
<?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)一个类可以同时实现多个接口,但是只能实现一个抽象类。
The above is the detailed content of Summary of commonly used object-oriented knowledge for PHP interviews (with examples). For more information, please follow other related articles on the PHP Chinese website!