PHP Day5:面向对象

作者: 王中阳 | 来源:发表于2016-06-21 21:34 被阅读161次

笔者回顾对面向对象的理解

  1. 封装:类是将对象进行封装,把对象的属性和操作(或服务)结合为一个独立的整体,并尽可能隐藏对象的内部实现细节。
  2. 继承:单继承,多实现。 有继承,就会有父子关系。子类拥有父类所有的方法和属性。
  3. 抽象:一切皆对象,一切都可以进行抽象。抽象就是忽略内部的方法和实现,只关心和自己需求相关的部分。抽象包括过程抽象和数据抽象。重点关注和需求相关的,忽略和需求无关的,集中注意力在需求上。
  4. 多态:经典的实现是重载和重写。笔者有一段时间理解实现接口和继承父类也是多态性的体现(可能不太准确)
  • 面向对象的思想需要和实际项目结合,绝对不是纸上谈兵。这种思想不仅适用于我们编写代码开发项目,更适用于我们采用什么样的方法和思路去敏捷开发,调优升级。

对象的三大特征

  1. 对象的行为:可以给对象添加哪些操作:比如开门,关门。就是对门这个对象的行为;
  2. 对象的形态:给对象施加方法时,对象的外形如何变化:门的颜色、尺寸、材料;
  3. 对象的表示:就是对象的唯一标识(身份证),最大的意义是区分在相同行为和形态下有什么不同。
对象的特征

面向对象的内容

  • 类:定义了一件事物的抽象特点。类的定义包含了数据的形式(常量、变量)以及对数据的操作(函数)。
  • 对象:是类的实例。
  • 成员变量:定义在类内部的变量。该变量的值对外是不可见的,可以通过成员函数访问。在类被实例化为对象之后,该变量即可称为变量的属性。
  • 成员函数:定义在类的内部,可用于访问对象的数据。
  • 父类:一个类被其他类继承,可将该类称为父类,基类或超类。
  • 子类:一个类继承其他类,可将该类称为子类或派生类。
  • 继承:继承是子类自动共享父类数据结构和方法的一种机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上进行,将这个已经存在的类所定义的内容作为自己的内容,并添加若干新内容。这就是继承非常好理解,关键字extends。
  • 多态: 多态性是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。通俗的解释一下:不同的对象,收到同一消息可以产生不同的结果,这种现象就是多态。(反思一下自己之前对接口实现和类继承的理解也是说的通的)
  • 重载:简单讲就是在同一个类中,方法名相同,但是参数列表不同(参数个数,参数属性....)。像这种同名不同参的函数或者方法之间,互相称为重载关系。
  • 抽象性:是指将具有一致数据结构(属性)和行为(操作)的对象抽象成类。一个类就是这样一种抽象,其核心是:反应了与应用(需求)密切相关的信息,忽略那些无关紧要的内容。在项目中,任何类的划分都是主观的,但是必须与具体的应用需求相关。
  • 封装:封装是指将现实世界中存在的某个客体的属性和行为绑定在一起,并放置在一个逻辑单元内。
  • 构造函数:主要用来在创建对象时初始化对象, 即为成员变量赋值初始化。构造函数在创建对象的语句中总与new关键字一起使用。
  • 析构函数(destructor)与构造函数相反, 当对象结束其生命周期时,系统自动执行析构函数。析构函数往往做的是‘清理善后’的工作。
  • 延伸之析构函数详解

构造函数与析构函数

构造函数
function __construct(){
    parent::__construct(); // 调用父类的构造函数必须显示的使用parent调用父类构造函数
    classname::__construct(); // 调用其他类的构造函数,classname是类名
    //其他操作
}

如果涉及多层继承,当调用parent::__construct()时,会沿着父类向上搜索,直到找到最合适的构造函数,例如:

// 接上例

class Parrot extends Birds{
    private $name;
    private $leg;
    private $wing;
    function __construct($name){
        parent::__construct($name); // 此时没有找到父类(Birds类)合适的构造函数,只能向上搜索,搜索到Animal类时,才找到合适的构造函数
        echo "鹦鹉类被创建!";
        $this->smackTalk();
        /*
        输出结果:
        "动物类被创建!"
        "鹦鹉说话!"
        */
    }
    function smackTalk(){
        echo "鹦鹉说话!";    
    }

}

如果想要依次调用几个父类的构造函数,可以使用类名直接调用构造函数,例如:

 function __construct($name,$leg){
       Animal::__construct($name); // 调用Animal构造函数
        Birds::__construct($name,$leg); // 调用Birds构造函数
}
析构函数
  • 析构函数是在销毁对象时,自动调用,不能显示的调用。
  • 析构函数不能带参数。
  • 在以下几种情况下可能会调用析构函数(但不一定):
    • PHP页面加载完毕之后;
    • unset()类;
    • 变量引用指向别的对象或值时;
<?php
class test{
    function __destruct(){
        echo "当对象销毁时会调用!!!";
    }

}
$a = $b = $c = new test();

$a = null;
unset($b);

echo "<hr />";

?>

此例子,如下图,有三个变量引用$a,$b,$c指向test对象,test对象就有3个引用计数,当$a = null时,$a对test对象的引用丢失,计数-1,变为2,当$b被unset()时,$b对test对象的引用也丢失了,计数再-1,变为1,最后页面加载完毕,$c指向test对象的引用自动被释放,此时计数再-1,变为0,test对象已没有变量引用,就会被销毁,此时就会调用析构函数。


析构函数
PHP构造函数 __construct
void __construct ([ mixed $args [, $... ]] )
function __construct( $par1, $par2 ) {
   $this->url = $par1;
   $this->title = $par2;
}
PHP析构函数 __destruct
<?php
class MyDestructableClass {
   function __construct() {
       print "构造函数\n";
       $this->name = "MyDestructableClass";
   }

   function __destruct() {
       print "销毁 " . $this->name . "\n";
   }
}

$obj = new MyDestructableClass();
?>

执行上述代码,返回结果是:
构造函数
销毁 MyDestructableClass

PHP重写和重载

  • PHP不支持像Java那种强类型语言一样的重载。
PHP重写

如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
实例中重写了 getUrl 与 getTitle 方法:

function getUrl() {
   echo $this->url . PHP_EOL;
   return $this->url;
}
   
function getTitle(){
   echo $this->title . PHP_EOL;
   return $this->title;
}

访问控制

PHP对属性或者方法的访问控制,是通过在前面添加关键字实现的:

  • public(共有的):公有的类成员可以在任何地方被访问。
  • protected(受保护):受保护的类成员可以在自身或者子父类中被访问。
  • private(私有):私有的类成员则只能被其定义所在的类访问。
属性的访问控制

类属性必须定义为公有,受保护,私有之一。如果用 var 定义,则被视为公有。

<?php
/**
 * Define MyClass
 */
class MyClass
{
    public $public = 'Public';
    protected $protected = 'Protected';
    private $private = 'Private';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj = new MyClass();
echo $obj->public; // 这行能被正常执行
echo $obj->protected; // 这行会产生一个致命错误
echo $obj->private; // 这行也会产生一个致命错误
$obj->printHello(); // 输出 Public、Protected 和 Private


/**
 * Define MyClass2
 */
class MyClass2 extends MyClass
{
    // 可以对 public 和 protected 进行重定义,但 private 而不能
    protected $protected = 'Protected2';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj2 = new MyClass2();
echo $obj2->public; // 这行能被正常执行
echo $obj2->private; // 未定义 private
echo $obj2->protected; // 这行会产生一个致命错误
$obj2->printHello(); // 输出 Public、Protected2 和 Undefined

?>
方法的访问控制

类中的方法可以被定义为公有,私有或受保护。如果没有设置这些关键字,则该方法默认为公有。

<?php
/**
 * Define MyClass
 */
class MyClass
{
    // 声明一个公有的构造函数
    public function __construct() { }

    // 声明一个公有的方法
    public function MyPublic() { }

    // 声明一个受保护的方法
    protected function MyProtected() { }

    // 声明一个私有的方法
    private function MyPrivate() { }

    // 此方法为公有
    function Foo()
    {
        $this->MyPublic();
        $this->MyProtected();
        $this->MyPrivate();
    }
}

$myclass = new MyClass;
$myclass->MyPublic(); // 这行能被正常执行
$myclass->MyProtected(); // 这行会产生一个致命错误
$myclass->MyPrivate(); // 这行会产生一个致命错误
$myclass->Foo(); // 公有,受保护,私有都可以执行


/**
 * Define MyClass2
 */
class MyClass2 extends MyClass
{
    // 此方法为公有
    function Foo2()
    {
        $this->MyPublic();
        $this->MyProtected();
        $this->MyPrivate(); // 这行会产生一个致命错误
    }
}

$myclass2 = new MyClass2;
$myclass2->MyPublic(); // 这行能被正常执行
$myclass2->Foo2(); // 公有的和受保护的都可执行,但私有的不行

class Bar 
{
    public function test() {
        $this->testPrivate();
        $this->testPublic();
    }

    public function testPublic() {
        echo "Bar::testPublic\n";
    }
    
    private function testPrivate() {
        echo "Bar::testPrivate\n";
    }
}

class Foo extends Bar 
{
    public function testPublic() {
        echo "Foo::testPublic\n";
    }
    
    private function testPrivate() {
        echo "Foo::testPrivate\n";
    }
}

$myFoo = new foo();
$myFoo->test(); // Bar::testPrivate 
                // Foo::testPublic
?>

接口

  • 使用接口,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。(即只声明,不实现)
  • 接口是通过interface关键字来定义的,就像定义一个标准的类一样, 但其中定义的所有的方法都是空的。
  • 接口的特性是:接口中定义的所有方法必须都是公共的。
  • 要实现一个接口,使用implements操作符。类中必须实现接口中定义的所有方法,否则会报错。类可以实现多个接口(中间用逗号隔开),但是只能继承一个父类。(这点和Java是一样的)
<?php

// 声明一个'iTemplate'接口
interface iTemplate
{
    public function setVariable($name, $var);
    public function getHtml($template);
}


// 实现接口
class Template implements iTemplate
{
    private $vars = array();
  
    public function setVariable($name, $var)
    {
        $this->vars[$name] = $var;
    }
  
    public function getHtml($template)
    {
        foreach($this->vars as $name => $value) {
            $template = str_replace('{' . $name . '}', $value, $template);
        }
 
        return $template;
    }
}

常量

  • 把在类中始终保持不变的值定义为常量。注意:在定义和使用常量的时候不需要加$字符。
  • 常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。
  • 自php5.3开始,可以用一个变量来动态的调用类。但该变量的值不能为关键字(self,parent,static)
<?php
class MyClass
{
    const constant = '常量值';

    function showConstant() {
        echo  self::constant . PHP_EOL;
    }
}

echo MyClass::constant . PHP_EOL;

$classname = "MyClass";
echo $classname::constant . PHP_EOL; // 自 5.3.0 起

$class = new MyClass();
$class->showConstant();

echo $class::constant . PHP_EOL; // 自 PHP 5.3.0 起
?>

抽象类

  • 任何一个类,如果他里面至少有一个方法被声明为抽象的,那么这个类就必须是抽象类。
  • 抽象类不能被实例化。(实例化由其实现类来做)。
  • 抽象方法只声明了其调用方式(参数),不能定义其具体的功能实现。(是不是想到了接口)
  • 继承抽象类时,子类必须定义父类所有的抽象方法。这些方法的访问控制(权限)必须和父类一致,或者更为宽松。
    • 例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。
  • 此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。
    • 例如,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。
<?php
abstract class AbstractClass
{
 // 强制要求子类定义这些方法
    abstract protected function getValue();
    abstract protected function prefixValue($prefix);

    // 普通方法(非抽象方法)
    public function printOut() {
        print $this->getValue() . PHP_EOL;
    }
}

class ConcreteClass1 extends AbstractClass
{
    protected function getValue() {
        return "ConcreteClass1";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass1";
    }
}

class ConcreteClass2 extends AbstractClass
{
    public function getValue() {
        return "ConcreteClass2";
    }

    public function prefixValue($prefix) {
        return "{$prefix}ConcreteClass2";
    }
}

$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') . PHP_EOL;

$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') . PHP_EOL;
?>

以上代码的执行结果:

ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2

Static关键字

  • 声明类属性或方法为static,就可以不实例化而直接访问了。(使用要有技巧,否则会内存溢出)
  • 静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)
  • 由于静态方法不需要通过对象就可以调用,所以伪对象$this在静态方法中不可用。
  • 静态属性不可以由对象通过->操作符来访问。
  • 自 PHP 5.3.0 起,可以用一个变量来动态调用类。但该变量的值不能为关键字 self,parent 或 static。
<?php
class Foo {
  public static $my_static = 'foo';
  
  public function staticValue() {
     return self::$my_static;
  }
}

print Foo::$my_static . PHP_EOL;
$foo = new Foo();

print $foo->staticValue() . PHP_EOL;
?>  

执行以上程序,输出结果为:

foo
foo

Final关键字

PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。

<?php
class BaseClass {
   public function test() {
       echo "BaseClass::test() called" . PHP_EOL;
   }
   
   final public function moreTesting() {
       echo "BaseClass::moreTesting() called"  . PHP_EOL;
   }
}

class ChildClass extends BaseClass {
   public function moreTesting() {
       echo "ChildClass::moreTesting() called"  . PHP_EOL;
   }
}
// 报错信息 Fatal error: Cannot override final method BaseClass::moreTesting()
?>

调用父类构造方法

PHP 不会在子类的构造方法中自动的调用父类的构造方法。要执行父类的构造方法,需要在子类的构造方法中调用 parent::__construct() 。

<?php
class BaseClass {
   function __construct() {
       print "BaseClass 类中构造方法" . PHP_EOL;
   }
}
class SubClass extends BaseClass {
   function __construct() {
       parent::__construct();  // 子类构造方法不能自动调用父类的构造方法
       print "SubClass 类中构造方法" . PHP_EOL;
   }
}
class OtherSubClass extends BaseClass {
    // 继承 BaseClass 的构造方法
}

// 调用 BaseClass 构造方法
$obj = new BaseClass();

// 调用 BaseClass、SubClass 构造方法
$obj = new SubClass();

// 调用 BaseClass 构造方法
$obj = new OtherSubClass();
?>

执行以上程序,输出结果为:

BaseClass 类中构造方法
BaseClass 类中构造方法
SubClass 类中构造方法
BaseClass 类中构造方法

一个敲代码,爱分享的人,我在这里!

来玩啊

相关文章

  • PHP Day5:面向对象

    笔者回顾对面向对象的理解 封装:类是将对象进行封装,把对象的属性和操作(或服务)结合为一个独立的整体,并尽可能隐藏...

  • 2019-07-28

    在PHP语言的使用中,可以分别使用面向过程和面向对象, 而且可以将PHP面向过程和面向对象两者一起混用。

  • 2019-07-18

    在PHP语言的使用中,可以分别使用面向过程和面向对象, 而且可以将PHP面向过程和面向对象两者一起混用,这是其它很...

  • 类的声明和语法

    1.达标和检测 学习PHP的面向对象,你需要具备PHP面向过程的相关基础,如不会,请先学习PHP基础部分. 面向对...

  • PHP技术梳理

    PHP语言: PHP基础, 数据类型, 导入文件 string array 面向对象技术, 类, 对象, 继...

  • php 面向对象

    给自己看的 简介 PHP 对待对象的方式与引用 (references) 和句柄 (handles) 相同,即每个...

  • php面向对象

    面向对象在很多语言,如c,java,c++,c#等语言中都有,它与面向过程的区别在于:面向过程实现功能时使用几十个...

  • php面向对象

    一、类 类是一种具有统一的特征和行为的声明特征:是属性行为:方法或者函数 属性有三个标识,1、public:公共的...

  • PHP面向对象

    1 写出 php 的 public、protected、private 三种访问控制模式的区别(新浪网技术部) ...

  • PHP 面向对象

    最近博主的网路出现问题,导致简书发布不了新文章,更新慢了。只能在CSDN博客上更新,大家见谅,欢迎关注我的 CSD...

网友评论

本文标题:PHP Day5:面向对象

本文链接:https://www.haomeiwen.com/subject/xdvgdttx.html