9.PHP 类与对象(一)

PHP 中用 class 来定义类,用 new 来实例化对象,用 extends 继承类,不过只能单继承。属性和方法有 publicprivateprotected 做访问控制,默认为 public,在类里定义常量不需要 $,用(::)范围解析符可以调用父类的方法,访问类的静态变量、静态方法和常量。

基本概念

定义

  • 每个类的定义都以关键字 class 开头,后面跟着类名(非保留字)。
  • 类名后跟着一对花括号,里面包含有类属性和方法的定义。
<?php
class A
{
    //属性
    public $a;
    private $b;

    //方法
    public function actionA()
    {

    }
}

类成员默认值

在定义类属性的时候,可以使用默认值

<?php
class A
{
    //默认值
    public $a = 'Hi';
    private $b = 'Hello';

    //方法
    public function actionA()
    {

    }
}

创建实例

要创建一个对象的实例,使用关键字 new

<?php
$a = new A();//创建类 A 的实例

对象赋值

当把一个对象已经创建的实例赋给一个新变量时,新变量会访问同一个实例,就和用该对象赋值一样。此行为和给函数传递入实例时一样。可以用克隆给一个已创建的对象建立一个新实例。

<?php

class A
{
}

$a = new A();
$b = $a;
$c = &$a;
$d = clone $a;

$a = null;

var_dump($a,$b,$c,$d);

$this

伪变量 $this 可以在当一个方法在对象内部调用时使用。$this 是一个到调用对象(通常是方法所属于的对象,但也可以是另一个对象,如果该方法是从第二个对象内静态调用的话)的引用。

<?php
class A
{
    function actionA()
    {
        if (isset($this)) {
            echo '$this is defined (';
            echo get_class($this);
            echo ")\n";
        } else {
            echo '$this is not defined.'.PHP_EOL;
        }
    }
}

class B
{
    function actionB()
    {
        A::actionA();
    }
}

$a = new A();
$a->actionA();
A::actionA();
$b = new B();
$b->actionB();
B::actionB();

从结果可以看出,$this 只能在对象中使用,不能在静态方法中调用。但是如果在另一个对象(类 B)中调用静态方法,则 $this 指向该类( B )。

对象继承

一个类可以在声明中用 extends 关键字继承另一个类的方法和成员。PHP 不支持多重继承,一个类只能继承一个类。

class A
{

}
class B extends A
{

}

被继承的方法和成员可以通过用同样的名字重新声明被覆盖,除非父类定义方法时使用了 final 关键字。可以通过 parent:: 来访问被覆盖的方法或成员。

<?php
class A
{
    public function sayHi()
    {
        echo "Hi".PHP_EOL;
    }
    final public function sayBye()
    {
        echo "Bye".PHP_EOL;
    }
}

class B extends A
{
    public function sayHi()
    {
        parent::sayHi();
        echo "Hello".PHP_EOL;
        parent::sayBye();
    }
    //不能被覆盖,报错,练习的时候注意删除该方法
    public function sayBye()
    {
        echo "See you";
    }
}

$b = new B();
$b->sayHi();

从结果可以看到

  • 使用 final 修饰的方法不能被覆盖
  • 使用 parent:: 可以调用父类方法或属性

属性

定义

类的变量成员叫做属性。属性声明是由访问控制关键字 publicprotectedprivate 和一个变量来组成,同时可以加上默认值。

<?php

class A
{
    //只能在类本身使用
    private $a = "Hello";

    //可以在子类和类本身使用
    protected $b = <<<EOT
This is variable b;
EOT;

    //除了子类,类本身,外部也可以访问
    public $c;
}

访问属性

在类的成员方法里面,可以通过 $this-> 加变量名来访问类的属性和方法,但是要访问类的静态属性或者在静态方法要使用 self:: 加变量名。

注意 self:: 这种方式后的变量名需要加 $ 符号,而 $this-> 后的变量名不需要加

<?php 

class A
{
    private $a = "Hello";

    protected $b = <<<EOT
This is property b
EOT;

    public static $c = 'This is a'.' static property';

    public function talk()
    {
        echo $this->a.PHP_EOL;
        echo $this->b.PHP_EOL;
        echo self::$c;
    }
}

(new A())->talk();

类常量

我们可以在类中定义常量。常量的值将始终保持不变。在定义和使用常量的时候不需要使用 $ 符号。

<?php

class A
{
    const ENV = 'env';
    const HELLO = 'Hello';
}

接口(interface)中也可以定义常量

<?php

interface B
{
    const ENV = 'ENV';
    public function sayHi();
}

自动加载对象

要进行一个类操作时,需要先将该类加载进来,例如 includerequire 等。

如果要执行的类很多,则需要大量 include 操作,会导致重复加载,管理苦难等一系列问题。

在 PHP 5 中,不用这样做了,可以使用 spl_autoload_register() 函数来注册任意数量的自动加载器。

<?php

spl_autoload_register(function ($class_name) {
    include $class_name . '.php';
});

new A();
new B();

本例尝试分别从 A.php 和 B.php 文件中加载 A 和 B 类,相当于

<?php

include 'A.php';
include 'B.php';

new A();
new B();

构造和析构函数

构造函数

void __constuct()
创建一个对象时( new 操作),构造函数会自动调用

class A
{
    public function __construct()
    {
        echo 'init...'.PHP_EOL;
    }

    public function sayHi()
    {
        echo "hi";
    }
}

(new A())->sayHi();

实例化 A 的时候执行构造函数。
注意: 如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用parent::__construct()。

<?php
class A 
{
   public function __construct() 
   {
       echo "A";
   }
}

class B extends A 
{
   public function __construct() 
   {
       parent::__construct();
       echo "B";
   }
}

new A(); // A
new B(); // AB

析构函数

void __destruct( void )
析构函数会在当某个对象的所有引用都被删除或者当对象被显式销毁时执行。

<?php
class A {
   public function __construct() {
       echo 'Start...';
   } 

   public function sayHi()
   {
       echo "Hi...";    
   }

   public function __destruct() {
       echo "Finish";
   }
}

(new A())->sayHi(); // Start...Hi...Finish

和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()

析构函数即使在使用 exit() 终止脚本运行时也会被调用。在析构函数中 调用 exit() 将会中止其余关闭操作的运行。

访问控制

对属性或方法的访问控制,是通过在前面添加关键字 publicprotectedprivate 来实现的。如果未添加,则默认为 public

  • public 所定义的类成员可以在任何地方被访问
  • protected 所定义的类成员则可以被其所在类的子类和父类访问(当然,该成员所在的类也可以访问)
  • private 定义的类成员则只能被其所在类访问
<?php

class A
{
    private $hi = 'Hi'.PHP_EOL;
    protected $hello = 'Hello'.PHP_EOL;
    public $bye = 'Bye'.PHP_EOL;

    private function sayHi()
    {
        echo $this->hi;
    }

    protected function sayHello()
    {
        echo $this->hello;
    }

    public function sayBye()
    {
        echo $this->bye;
    }

}

class B extends A
{
    public function talk()
    {
        parent::sayHello();
    }
}

$a = new A();
$a->sayBye();//报错,无法调用


$b = new B();
$b->sayHello();//报错,无法调用
$b->talk();
$b->sayBye();

从结果可知,声明为 private 的方法或属性无法在类外部调用,同时子类也无法调用该方法。

范围解析操作符(::)

范围解析操作符,可以简单地说是一对冒号,可以用于访问静态成员、方法和常量,还可以用于覆盖类中的成员和方法。
当在类的外部访问这些静态成员、方法和常量时,必须使用类的名字。

<?php

class A 
{
    const CONST_A = 'A constant value';

    public static function sayHello()
    {
        echo 'Hello';
    }
}

class B extends A
{
    public static $b = 'static var b';

    /**
     * 覆盖父类方法
     *
     */
    public static function sayHello()
    {
        echo parent::sayHello().' World'.PHP_EOL;

    }

    public static function actionB() 
    {
        self::sayHello();
        echo parent::CONST_A.PHP_EOL;
        echo self::$b;
    }
}

B::actionB();

从结果可知

  • 使用 parentself 可以调用父类和自身的方法属性
  • :: 可以调用静态方法,静态属性和常量
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,331评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,372评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,755评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,528评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,526评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,166评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,768评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,664评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,205评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,290评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,435评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,126评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,804评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,276评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,393评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,818评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,442评论 2 359

推荐阅读更多精彩内容