PHP面试专栏正式起更,每周一、三、五更新,提供最好最优质的PHP面试内容。
继上一篇“PHP面试常考内容之面向对象(2)”发表后,今天更新面向对象的最后一篇(3)。需要(1),(2)的可以直接点文字跳转获取。
PHP面试常考内容之面向对象(1)
整个面向对象文章的结构涉及的内容模块有:
一、面向对象与面向过程有什么区别?
二、面向对象有什么特征?
三、什么是构造函数和析构函数?
四、面向对象的作用域范围有哪几种?
五、PHP 中魔术方法有哪些?
六、什么是对象克隆?
七、this、self和parent的区别是什么?
八、抽象类与接口有什么区别与联系?
九、PHP面向对象的常考面试题讲解
关于PHP面向对象的内容将会被分为三篇文章进行讲解完整块内容,第一篇主要讲解一到四点内容,第二篇主要讲解五到八的内容,第三篇围绕第九点进行讲解。
以下正文的内容都来自《PHP程序员面试笔试真题解析》书籍,如果转载请保留出处:
九、PHP面向对象的常考面试题讲解
【真题1】 什么是面向对象?其主要特征是什么?
答案:面向对象是程序的一种设计方式,它是一种对现实世界的理解和抽象的方法,它可以提高程序的重用性,让程序结构更加清晰。
面向对象的主要特征为:封装、继承、多态。
【真题2】 可以获得对象的类名的函数是( )。
A.get_class_name B.get_class C.class_exists D.get_class_vars
答案:B。
PHP中获取对象的类名函数是get_class()。所以,选项B正确。
对于选项A,不存在该方法。所以,选项A错误。
对于选项C,class_exists()函数可以检查类是否存在。所以,选项C错误。
对于选项D,get_class_vars()函数可以获取类的默认属性。所以,选项D错误。
所以,本题的答案是B。
【真题3】 请简单说明PHP的垃圾收集机制。
答案:在PHP中,当没有任何变量指向该对象时,该对象变为垃圾将会在内存中被销毁,可以防止内存溢出。内存中对变量有引用计数,当计数到0时变量被销毁。
【真题4】多态与方法重写有什么关系?
答案:多态是指一个类可以被多个类继承,每个子类都可以对父类方法进行重写,每个类里的同名方法可以实现不同的功能从而表现出多种形态,它增强了软件的灵活性和重用性。
重写是子类对父类中的方法进行改写。它们的关系是重写让类具备多态性。
【真题5】面向对象几大原则是什么?
答案:面向对象存在五大基本原则,分别是:单一职责原则、开放封闭原则、替换原则、依赖原则、接口分离原则等。
(1)单一职责原则
所谓单一职责原则,即一个类最好只做一件事。为了提高内聚性减少引起变化,单一原则是低耦合、高内聚的面向原则上的引申。
(2)开放封闭原则
软件的功能应该是可扩展的,尽可能减少修改,因为修改程序,可能会对原来的程序造成影响。虽然建议尽可能不修改程序,但是允许通过添加功能来减少修改。
(3)替换原则
只有子类能够替换基类,在继承机制的约束规范中,子类替换基类时,可以保证运行期内识别子类,保证继承复用。
(4)依赖倒置原则
高层模块不依赖底层模块,二者都依赖于抽象,抽象不依赖于实体,而实体依赖于抽象。模块间的依赖是通过抽象方法发生的,实现类中不发生直接的依赖关系,而依赖关系是通过接口或抽象类产生的。即接口或抽象类不依赖于实现类,而实现类依赖于接口和抽象类。这种依赖倒置原则可以有效地减少类之间的耦合性,提高系统的稳定性,减少并发引起的风险,提高代码的可读性和可维护性。
(5)接口隔离原则
建议开发使用多个小的、专门的接口,避免使用一个大的总接口。即每一个功能有一个专门的功能接口,需要用到才调用,不需要全部功能汇总到一个接口,这样可以提高代码的灵活性,降低类之间的耦合性,提高稳定性。
【真题6】以下有关PHP高级特性的说法中,正确的是( )。
A.可以定义一个类去实现预定义接口Iterator,然后就能像访问数组一样访问这个类创建的对象
B.spl_autoload_register()提供了一种更加灵活的方式来实现类的自动加载,不再建议使用_autoload()函数
C.PHP在对象中调用一个不可访问方法时,invoke()方法会被自动调用
D.匿名函数也叫闭包函数,常用作回调函数参数的值,但是不能作为变量的值来使用
答案:B。
对于选项A,只有ArrayAccess能够提供像访问数组一样访问这个对象的接口,不能定义一个类或预定义接口Iterator去实现这个功能。所以,选项A错误。
对于选项B,因为可以通过spl_autoload_register()函数创建autoload函数的队列,按定义顺序逐个执行,比_autoload()函数只可以定义一次使用更方便,所以不建议使用_autoload()函数。所以,选项B正确。
对于选项C,_call方法是在创建一个类实例化后就可以直接调用对象使用,当调用的方法不可访问或没有权限访问时,会自动调用_call方法。所以,选项C错误。
对于选项D,匿名函数是可以赋值给变量的。所以,选项D错误。
所以,本题的答案是B。
【真题7】__autoload()函数的工作原理是什么?
答案:使用这个魔术函数的基本条件是,类文件的文件名要和类的名字保持一致。
当程序执行到实例化某个类时,如果在实例化前没有引入这个类文件,那么就自动执行__autoload()函数。这个函数根据实例化的类名去查找这个类的路径,一旦找到这个类后就会通过执行include或require载入该类,从而保证程序能够继续执行。如果没有找到,那么报错。
【真题8】以下关于PHP命名空间的说法中,不正确的是( )。
A.访问任意全局类、函数或常量,都可以使用完全限定名称,例如strlen()或Exception或INI_ALL
B.关键字 namespace可用来显式访问当前命名空间或子命名空间中的元素,它等价于类中的 this 操作符
C.任意合法的PHP代码都可以包含在命名空间中,但只有三种类型的代码受命名空间的影响,它们是类、函数和常量
D.常量__NAMESPACE__的值是当前命名空间名称的字符串。如果是在全局中,那么它不包括任何命名空间中的代码,本身是一个空字符串
答案:B。
namespace关键字是用来声明命名空间用的,它并不能等价于this操作符的功能。所以,选项B说法不对。
所以,本题的答案是B。
【真题9】以下代码的运行结果是( )。
<?php class Person{ var $name; } $a = new Person(); $a->name = "张三"; $b = $a; $b->name = "李四"; echo $a->name; ?>
A.张三 B.李四 C.Null D.什么都没有
答案:B。
首先$a实例化Person类,把张三赋值给类内的变量name,把对象张三的值给了$b,通过$b去修改类内name的值为李四,所以最后输出Person类内的name,输出得到结果李四。所以,选项B正确,选项A、选项C、选项D错误。
所以,本题的答案是B。
【真题10】下面说法错误的是( )。
A.如果一个类的成员前面有访问修饰符private,那么这些成员不能被继承,在类的外部不可见。但如果成员被指定为protected和public,那么可以被继承,在类的外部也是可见的
B.PHP5中,final关键字可以禁止继承和重载
C.PHP5中,析构函数的名称是__destruct(),并且不能有任何参数
D.继承接口的类必须实现接口中声明的所有方法,在PHP中,如果继承接口的类没有实现接口中的方法,那么将会产生一个致命错误
答案:A。
对于选项A,private修饰的成员是不可以被继承的,protected的成员是可以被继承的,但是在外部不可见,选项A说法错误,所以,选项A正确。
对于选项B,final关键字的方法是禁止被继承和重载的,选项B说法正确,所以选项B错误。
对于选项C,析构函数不能有参数,选项C说法正确,所以,选项C错误。
对于选项D,继承接口的类没有实现接口中的方法是会产生错误的,选项D说法正确,所以,选项D错误。
所以,本题的答案是A。
【真题11】 定义类成员的访问权限控制符有哪些?默认修饰符是什么?
答案:类成员的访问修饰符有public、private、protected,主要用来修饰类中的成员属性和方法。public是公共类型,允许在类的内部或子类中使用,也可以在类外部被访问。private是私有类型,只能在类的内部被使用,不能被继承使用。protected是保护类型,只能在类的内部或子类中使用。如果不使用public、private、protected等关键字修饰方法或属性,那么可以使用var关键字,它的功能等同于public,可以在类内或类外被调用,也可以被继承使用。
其中,PHP默认的修饰符是public,即公有类型。
类前面只能加final、abstract关键字,被final修饰的属性或方法是不能被继承的,只能在当前类中使用,abstract定义的类或方法,叫作抽象类或抽象方法。
属性前面:必须有访问修饰符(private,protected,public,var)。
【真题12】 PHP的魔术方法包含哪些(越多越好)?在什么情况下被自动调用?
答案:PHP可用的魔术方法会在特定情况下被自动调用,但是前提是特定的条件被触发,并且这些魔术方法可以在类中作为方法。
PHP的魔术方法有:
1)_construct():构造函数,创建对象时自动被调用。
2)_destruct():析构函数,对象的所有引用都被删除或者当对象被显式销毁时执行。
3)__clone():克隆函数,调用clone方法时自动调用。
4)__set():当程序试图写入一个不存在或不可见的成员变量时自动调用。该函数在类中定义时必须有两个参数:变量名和变量值。
5)__get():当程序调用一个未定义或不可见的成员变量时自动调用__get()来读取变量值。定义时必有有一个参数:变量名。
6)__call():当程序试图调用不存在或不可见的成员方法时,自动调用__call()。__call()方法一般用于监视错误的方法调用。为了避免当调用的方法不存在时产生错误,可以使用__call()方法来避免。该方法包含两个参数:方法名和方法参数。其中,方法参数以数组形式存在。
7)__sleep():使用serialize()实现序列化对象时,先调用该方法,可以用来清除对象并返回一个该对象中所有变量的数组。
8)__wakeup():使用unserialize()还原一个被序列化的对象时,先执行该方法,恢复在序列化中可能丢失的数据库连接及相关工作。
9)__toString():当使用echo或print输出对象时,将对象转化为字符串。
10)__autoload():调用未被实例化的类时,自动调用,在指定路径下查找和该类名称相同的文件。
【真题13】 $this、self和parent这三个关键词分别代表什么?在哪些场合下使用?
答案:$this表示当前对象,在当前类中可以通过->符调用类内的属性和方法。
self表示当前类,只能通过self的形式(“self::方法或属性”)调用类内的方法。
parent表示当前类的父类,调用父类内的方法只能使用“parent::”形式调用。
【真题14】下面关于面向对象的描述中,错误的是( )。
A.父类的构造函数与析构函数不会自动被调用
B.成员变量需要用public、protected、private修饰,在定义变量时不再需要var关键字
C.父类中定义的静态成员,不可以在子类中直接调用
D.包含抽象方法的类必须为抽象类,抽象类不能被实例化
答案:A。
对于选项A,子类继承父类,如果子类没有构造函数和析构函数,那么实例化子类时会自动调用父类的构造函数和析构函数;但如果子类只有构造函数没有析构函数时,那么实例化子类时,自动调用的是子类的构造函数,销毁对象时调用父类的析构函数;如果子类没有构造函数只有析构函数,那么实例化子类时会自动调用父类的构造函数,销毁对象时调用子类的析构函数,选项A说法不完全。所以,选项A正确。
对于选项B,成员变量使用了public、protected、private修饰定义变量时是不需要var关键字的,选项B说法正确。所以,选项B错误。
对于选项C,父类中的静态成员,子类中是不可以直接访问的,选项B说法正确。所以,选项C错误。
对于选项D,一个包含抽象方法的类必须是抽象类,并且抽象类不能被实例化。选项D说法正确。所以,选项D错误。
所以,本题的答案是A。
【真题15】 在PHP中,如果派生类与父类有相同名字的函数,那么派生类的函数会替换父类的函数,有如下程序代码:
<?php class A { function disName(){ echo "Picachu"; } } class B extends A { var $tmp=''; function disName(){ echo "Doraemon"; } } $cartoon = new B; $cartoon->disName(); ?>
上述代码的运行结果为( )。
A.tmp B.Picachu C.disName D.Doraemon E.无输出
答案:D。
当派生类继承父类时,如果通过实例化一个派生类的对象来访问对象的方法时,派生类不存在父类中的方法,那么执行父类中的方法。如果派生类和父类存在相同名字的方法,那么派生类的方法会覆盖父类方法,执行派生类的方法。所以,本题中可以执行派生类的disName()方法。所以,选项D正确,选项A、选项B、选项C、选项E错误。
所以,本题的答案是D。
【真题16】什么是抽象类和接口?抽象类和接口有什么不同和相似的地方?
答案:被关键字abstract修饰的类叫作抽象类,抽象类是不能被实例化的。被abstract修饰的方法为抽象方法,一个类只要有一个抽象方法,这个类一定是抽象类。
接口是通过关键字interface来定义的,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体实现。PHP类只支持是单重继承的,但通过接口可以实现PHP类的多重继承。
抽象类和接口的不同和相似的地方如下所示。
1)抽象类是一种不能被实例化的类,只能作为其他类的父类来使用。
2)抽象类是通过关键字abstract来声明的。
3)抽象类与普通类相似,都包含成员变量和成员方法,两者的区别在于,抽象类中至少要包含一个抽象方法。
4)抽象方法没有方法体,该方法就是要被子类重写的。
5)抽象方法的格式为:abstract function abstractMethod()。
6)因为PHP中只支持单重继承,所以如果想实现多重继承,那么就要使用接口。也就是说,子类可以实现多个接口。
7)接口类是通过interface关键字来声明的,接口类中的成员变量和方法都是public的,可以不用显式地使用public来修饰。
8)接口中的方法没有方法体。接口中的方法就是要被子类继承实现的。
9)子类继承抽象类使用extends关键字,子类实现接口使用implements关键字。
【真题17】用类编程实现:Stu类中有两个私有属性name和sex,有两个公有方法,setName()和setSex()参数自定,方法可实现对两个私有属性进行修改。在实例化类时要求对私有属性能初始化。
答案:实现代码如下:
<?php Class Stu{ private $name; private $sex; public function setName($name){ $this->name = $name; } public function setSex($sex){ $this->sex = $sex; } } ?>
【真题18】 假如有一个类Person,实例化(new)一个对象$p,那么以下使用对象$p调用Person类中的getInfo方法的写法中,正确的是( )。
A.$p=>getInfo(); B.$this->getInfo();
C.$p->getInfo(); D.$p::getInfo();
参考答案:C。
分析:“::”主要用于访问类中的静态成员,“->”主要用于访问类中的变量和方法,“=>”主要应用在数组中的key和value映射时使用。所以,选项A、选项B、选项D错误,选项C正确。
【真题19】php中public、protected、private三种访问控制模式的区别是什么?
参考答案:php中public、protected、private三种访问控制模式的区别如下:
访 问 模 式 描 述
public 共有,任何地方都可以访问
protected 继承,只能在本类或子类中访问,在其他地方不能使用
private 私有,只能在本类中访问,在其他地方不能使用
【真题20】 在PHP面向对象中,下面关于final修饰符的描述中,错误的是( )。
A.使用final标识的类不能被继承
B.在类中使用final标识的成员方法,在子类中不能被覆盖
C.不能使用final标识成员属性
D.使用final标识的成员属性,不能在子类中再次定义
参考答案:D。
分析:因为final只能修饰类与方法,不能修饰类的属性。所以,选项D错误。
PS:由于真题较多,仅罗列PHP面向对象笔试中经常遇到的20道考题!
至此本周(2019-2-11 至 2019-2-15 的面向对象专题已更新完毕,以上的内容只是摘取了PHP面向对象中最常考的内容,个别内容没有罗列可以从原书中获取。)感谢大家的支持!
预告:下周(2019-2.18 —— 2.22)更新“PHP面试常考内容之Memcache和Redis缓存的”专题,敬请期待。