PHP基础教程十二之抽象、接口

x33g5p2x  于2022-03-06 转载在 其他  
字(4.3k)|赞(0)|评价(0)|浏览(331)

本节讲解的内容

  • 抽象
  • 接口
  • final的使用
  • 类常量

前言

在PHP中的面向对象是通过定义类,来完成对象的示例化,而PHP的抽象类和接口类可以说是类的一种规范,通过定义这两种类来对类进行强制约束,虽然这两种都是对类的约束,但本质上他们还是有区别的。

抽象类

抽象类的概念我们可以用动物的继承关系来说明问题,当我们写父类Animal类时,其中有两个方法sleep(),eat(),因为不知道具体是什么动物而无法确定方法中写什么内容。这是我们就可以用抽象类进行实现。

<?php
//通过关键字abstract来声明抽象类
abstract class Animal{
    protected $name;
    protected $age;
    //声明该方法是抽象方法
    abstract public function sleep();
    abstract public function eat();
}

当父类的一些方法不能确定的时候,可以用abstract关键字来修饰该方法,称为抽象方法,而用abstract修饰的类称为抽象类。

基本语法:

abstract  class  类名{
    abstract  访问修饰符   函数名(参数列表);
}

在开发中当我们想让继承该类的所有子类都重写该类的方法,就可以用抽象方法来实现。抽象类就好比某个东西的架子也可以说是一个模板,有了模板就可以可以根据模板实现具体的功能。,而模板和具体事物的这种关系是通过继承类传递的。就好比一台电脑,通过模板来制作一台台电脑。而抽象类定义出来就是需要被继承的。

<?php

abstract class Animal{
    protected $name;
    protected $age;

    abstract public function sleep();
    abstract public function eat();
}
class Dog extends Animal{

}
......结果........
Fatal error: Class Dog contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Animal::sleep, Animal::eat) in D:\mywamp\Apache24\htdocs\zendstudio\yunsuanfu\chouxiang.php on line 13

当一个类继承了抽象类后,在子类中就需要实现父类的所有抽象方法上面报的错误就是说在父类中包含2个抽象方法,子类必须实现该抽象方法。而关于抽象类的特点可以总结如下:

  • 抽象类是不能被示例化的,不能通过抽象类来new一个对象,会报一个Cannot instantiate abstract class错误。
  • 抽象类中可以没有抽象方法的,在抽象类中都是普通方法,但是类名是用abstract修饰的。
<?php
abstract class Animal{
    protected $name;
    protected $age;
    public function sleep(){

    }
    public function eat(){

    }
}
  • 抽象类可以有普通的成员方法,属性等
<?php
abstract class Animal{
    protected $name;
    protected $age;

    abstract public function sleep();
    abstract public function eat();

    public function cry(){

    }
}
  • 如果一个类中有抽象方法,那么这个类必须是抽象类。
  • 抽象方法是没有方法体的
abstract public function eat();

没有{},也就是说没有方法体。

  • 一个类继承了抽象类,则子类必须实现抽象类的所有抽象方法,或者子类自己也声明为抽象类

接口

接口的初衷和抽象类是一样的,不知道方法里面怎么实现的时候可以用接口来实现。而接口的定义是:给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来,接口的出现体现了高内聚低耦合的特点。

<?php

interface iTechnical{
    public function fly();
}

基本语法:

interface 接口名{
    //方法, 在接口中,所有的方法都不能有方法体, 即都是抽象方法
}

接口类和抽象类大致一样,那接口具体是什么呢?上面有说到抽象类就好比一台笔记本的架子、模板,然后根据模板创建具体的笔记本,而没有笔记本都有几个usb接口,而接口类就好比这些笔记本上的接口,是一个扩展的实现。就像动物一样都继承了动物特有的特性吃,睡等,但是突然一只动物在别的地方实现了写字的本领,这种本领就是通过接口进行扩充的。

接口的命名一般是在类名的第一个字母是小写的i开头。在**接口类中所有的方法都默认是抽象方法。**所以并不需要写abstract来声明。

接口的实现

我们定义一个接口当然是让别的类去实现的,这里说的是实现,而不是继承,接口和别的类之间是实现的关系,也就是类实现了某一接口,用implements关键字实现。

interface iTechnical{
    public function fly();
}

class Dog implements iTechnical{
    public function fly(){
        echo '<br>飞';
    }
}

当然在子类中必须实现接口中所有的方法。

关于接口的特点有以下几点:

  • 接口类和抽象类一样是不能被实例化的。
  • 接口中所有的方法都不能有主体,因为都是抽象方法。
  • 一个类可以实现多个接口,逗号隔开(继承只能是一个)
class Dog implements 接口1,接口2{

}
  • 接口中可以有属性,但只能是常量 ,默认是public, 但不能用public 显式修饰
  • 接口中的方法都必须是public的,接口就是用来继承的所以用public,如果没有写修饰符,默认是public
  • 一个接口不能继承其它的类,但是可以继承别的接口

抽象和接口的区别

在PHP中继承是单继承的,也就是一个类最多只能有一个父类,这种单继承的机制可以保证类的纯洁性。但是这种机制对子类功能扩展有一定的影响。

接口的出现可以说是对继承的一种补充,继承是层级的,不太灵活,而接口却没有它比抽象要灵活的多。并且实现接口在不打破继承关系的前提下,对子类的功能进行扩充。
它们两个的关系图可以理解为这样:

final的使用

在上面的介绍中每个类都是可以被继承的,如果我们有一个类,我们不想让子类去重写里面的某个方法,或者不想让别的类去继承该类,就可以用到关键字final。final中文意思:最后的,最终的,可以用来修饰类或者方法。

final可以修饰方法或者类,如果修饰方法,则表示该方法不可以被继承类去重写,如果final 修饰了一个类,则表示该类不可以被继承。

基本语法:

final  class   类名{
}
class 类名{
    final 访问修饰符 function 函数名(形参){}
}

修饰类和修饰方法。

<?php

    final class A{

    }

    class B extends A{

    }
    .....结果.....
    Class B may not inherit from final class (A)

不能继承用final修饰的类。

<?php

    class A{
        final public function sum($num1,$num2){
            return $num1 + $num2;
        }

    }

    class B extends A{
        public function sum($num1,$num2){
            return $num1 + $num2;
        }
    }
    .....结果.....
    Cannot override final method A::sum()

从结果中可以看出来不能重写用final定义的方法。

在使用final时,只能用来修饰类或者方法,不能修饰属性。当一个类被final修饰后,类中的方法就不需要被final修饰,因为类都不能继承了,方法就不能重写。同时final修饰的类是可以被实例化的。

如果一个方法被final修饰,是可以被继承的,但是不能被重写。

<?php

    class A{
        final public function sum($num1,$num2){
            return $num1 + $num2;
        }

    }

    class B extends A{

    }

    $b = new B();
    echo $b -> sum(1,2);
    .......结果.......
    3

在我们写单例模式时,说过当时的单例模式是不完善的,可以通过继承来得到不同的对象,在这里我们使用final关键字修饰单例类,防止继承,这样就不能通过继承的到别的对象。

类常量

类常量和普通的常量是一个概念,当不希望一个成员变量被修改,希望该变量的值是固定不变的。这时可以用const去修饰该成员变量,这样这个变量就自动成为常量。在类中的常量称为类常量。前面我们讲过定义常量有两种方式define()和关键字const,在类中必须使用const这种方式,使用define()是会报错的。

<?php

    class A{
        const PI = 3.1415926;

        public function getPI(){
            return A::PI;
        }
    }

    $a = new A();
    echo $a -> getPI();
    echo '<br>';
    echo A::PI;
    ......结果......
    3.1415926
    3.1415926

类常量的命名一般是全部用大写字母,中间用下划线隔开,同时常量前面没有$符号。常量必须在定义的时候就赋值。同时常量的前面不能有修饰符,默认是public。

常量的访问形式

常量的访问形式分为两种,一种是在类的内部进行访问,一种是在类的外部进行访问。

内部访问

通过 类名::常量名进行访问。

class A{
    const PI = 3.1415926;

    public function getPI(){
        return A::PI;//通过类名进行访问
    }
}

通过 self::常量名进行访问

class A{
    const PI = 3.1415926;

    public function getPI(){
        return self::PI;//通过类名进行访问
    }
}

可以通过self进行访问说明常量是属于类的,并不属于对象的。

外部访问

通过类名::常量名访问。

echo A::PI;

通过对象名::常量名访问

$a = new A();
echo $a::PI;

不过推荐使用第一种,通过类名进行访问。

如果一个类中有常量,则在继承的时候常量也是可以被继承的。同时常量的数据类型不能是对象。

总结

PHP中抽象和接口应用让我们的更加清楚的把握需求的骨架,更好的搭建我们的代码体系。同时利用抽象和接口降低代码的耦合度。利于代码的扩展。

相关文章