目录
对于面向对象开发,抽象类与接口这两个东西是比较难理解的。也是面试经常问到的一个问题。
抽象类
在说抽象类之前,先了解一下抽象方法。
抽象方法:没有方法实体的方法即为抽象方法,用abstract来修饰 abstract function abc();
抽象类:任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。(抽象类可以没有抽象方法,但是抽象类依然不能被实例化)被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。用abstract来修饰 abstract class A {};
抽象类不能被实例化。在抽象类中,即便全是具体方法,也不能够实例化。
理解一下:首先知道什么是抽象方法,即没有方法体的方法,然后理解抽象类,一个类中只要包含了抽象方法就是抽象类,但是抽象类中可以没有抽象方法。
总结一下抽象类:
1.抽象类是通过 abstract 关键字来定义的,用关键字 extends 实现继承;
2.抽象类可以有属性、普通方法、抽象方法;
3.抽象类内不一定有抽象方法,但有抽象方法的类,则必是抽象类;
4.抽象类不能被实例化,只有非抽象类继承后才可实例化;
5.非抽象子类继承必须定义父类中的所有抽象方法;
6.非抽象子类中继承方法的访问权限必须和父类一样或更宽松。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的;
7.抽象类可以继承抽象类,且不能重写抽象父类的抽象方法,否则报错。这样的用法,可以理解为对抽象类的扩展;
8.此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。例如,子类定义了一个可选参数(类似function eat(a,a,b=1)中的$b就是可选参数),而父类抽象方法的声明里没有,则两者的声明并无冲突。这也适用于 PHP 5.4 起的构造函数。在 PHP 5.4 之前的构造函数声明可以不一样的。
代码理解一下:
/** * 定义一个抽象类,可以有属性、普通方法、抽象方法 */ abstract class A { //属性 public $name; //抽象方法,强制要求非抽象子类继承这些方法,注意没有有{} abstract public function name(); abstract protected function age($age); //普通方法 public function say() { echo 'I am from A;'; } } /** * 抽象类可以继承抽象类,且不能重写抽象父类的抽象方法 */ abstract class B extends A{ //抽象方法 abstract protected function height(); //普通方法 public function hello() { echo 'I am from B;'; } } /** * 定义非抽象类继承抽象类,必须定义父类中的所有抽象方法,且访问权限要与父类一致或宽松 */ class C extends B { //定义父类中的所有抽象方法,且访问权限要与父类一致或宽松 public function name() { echo 'I am from C, my name is '. $this->name; } //父类该方法是protected,子类可以是public 或 protected public function age($age) { echo 'I am from C,my age is ' . $age; } //父类没有参数,子类可以定义一个可选参数 protected function height($height = 180) { echo 'I am from C,my height is ' . $height; } public function info($height = 180) { $this->height($height); } } $obj = new C(); $obj->name = 'jon'; $obj->name(); //I am from C, my name is jon $obj->age(20); //I am from C,my age is 20 $obj->info(170); //I am from C,my age is 170 $obj->info(); //I am from C,my age is 180 $obj->say(); //I am from A $obj->height();//会报致命错误。被定义为受保护的类成员则可以被其自身以及其子类和父类访问
接口
接口:没有主体代码的方法定义组成的集合体。可以被其他类实现或被其他接口继承。
接口可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
总结一下接口:
1.接口通过 interface 来定义;
2.接口中定义的所有方法都必须是公有,这是接口的特性,protected和private会报错(Fatal error: Access type for interface method),非抽象类实现接口方法必须是public;
3.要实现一个接口,使用 implements 操作符。非抽象类中必须实现接口中定义的所有方法,否则会报一个致命错误;
4.接口可以继承另一个或多个接口,使用 extends 关键字,多个用 ‘,’ 隔开,但是不能实现另一个接口,当然更不能继承抽象类;
5.可以同时继承抽象类和实现接口,extends 要写在前面;
6.抽象类可以实现接口,且可以不实现其中的方法;
7.接口中可以定义常量。接口常量和类常量的使用完全相同,但是不能被子类或子接口所覆盖。(不建议这样用,实在想不到有什么意义,也容易产生和抽象类的混淆)。
代码理解一下:
/** * 定义接口, */ interface A { //可以定义常量 const PI = 3.14; //定义方法,没有方法体,必须是共有的 public function say(); public function show(); } /** * 定义接口B */ interface B { public function hello(); } /** * 定义抽象类 */ abstract class H { abstract public function info(); } /** * 非抽象类实现接口,必须定义接口中的所有方法,可以实现多个接口用','隔开 */ class X implements A,B { public function say() { echo 'I am from calss X method say'; } public function show() { echo 'I am from calss X method say'; } public function hello() { echo 'I am from calss X method hello'; } } $obj = new X(); $obj->say(); //I am from calss X method say /** * 可以同时继承抽象类和实现接口,extends 要写在前面; */ class Y extends H implements A,B { public function info(){ echo 'I am from calss Y method info'; } public function say() { echo 'I am from calss Y method say'; } public function show() { echo 'I am from calss Y method say'; } public function hello() { echo 'I am from calss Y method hello'; } } $obj1 = new Y(); $obj1->info(); //I am from calss Y method info /** * 抽象类实现接口,可以不实现其中的方法 */ abstract class Z implements A { public function say() { echo 'I am from calss Z method say'; } } /** * 非抽象类继承抽象类,要实现抽象类和接口中的方法 */ class M extends Z { public function show() { echo 'I am from calss M method show'; } } $obj2 = new M(); $obj2->say(); //I am from calss Y method info
抽象类与接口的区别:
抽象类与接口的相同点:
1.都是用于声明某一种事物,规范名称、参数,形成模块,未有详细的实现细节;
2.都是通过类来实现相关的细节工作;
3.语法上,抽象类的抽象方法与接口一样,不能有方法体,即{}符号;
4.都可以用继承,接口可以继承接口形成新的接口,抽象类可以继承抽象类从而形成新的抽象类。
抽象类与接口的不同点:
1.抽象类可以有属性、普通方法、抽象方法,但接口不能有属性、普通方法,可以有常量;
2.抽象类内未必有抽象方法,但接口内一定会有“抽象”方法;
3.语法上有不同,抽象类用abstract关键字在类前声明,且有class声明为类,接口是用interface来声明,但不能用class来声明,因为接口不是类;
4.抽象类的抽象方法一定要用abstract来声明,而接口则不需要;
5.抽象类是用extends关键字让子类继承父类后,在子类实现详细的抽象方法。而接口则是用implements让普通类在类里实现接口的详细方法,且接口可以一次性实现多个方法,用逗号分开各个接口即可;
6.抽象类的抽象方法的访问权限可以是public、protected,接口的方法必须是public。
总结一句话:
抽象类一般用来定义一类实体是什么,它包含了属性、抽象方法和非抽象方法。接口用来定义一类实体能做什么,一般认为它只有抽象方法,常量极少用到。
当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
以上总结如有错误,欢迎指出更正。另外,本篇文章不做任何商业用途,仅方便自己日后查阅。同时也希望能够帮助其他小伙伴~