文档章节

七天精通PHP面向对象

胡明哲
 胡明哲
发布于 2017/08/04 22:12
字数 11653
阅读 77
收藏 0

面向对象第一天

一、学习目标

       1.面向对象技术的语法

       2.面向对象的编程思想

二、面向对象的概念(OOP)

        OOP(Object-Programming, 面向对象的编程)  技术为编程人员敞开了一扇大门,使其编程的代码更简洁、更易于维护,并且具有更强的可重用性。

三、OOP达到了软件工程的三个目标

        1.重用性,2.灵活性,3.扩展性。

四、OOP面向对象编程的特点

        1.封装,2.继承,3.多态。

五、面向过程和面向对象的对比

        1.面向过程(单身屌丝)

            饿了,自己做饭
            渴了,自己买水
            衣服脏了,自己洗衣服
            空虚寂寞了,自己解决
        所有的需求都需要自己解决,这就是单身屌丝的现状。

        2.面向对象(有对象了,长的像某某某)

            饿了,女友帮你去做饭
            渴了,女友帮你去买水
            衣服脏了,女友帮你洗衣服
            空虚寂寞了,女友帮你解决。
        所有的需求都可以让女友去完成,这就是有对象的好处。(只是举例,不要有这样的幻想,不然一辈子也找不到对象!~)。

六、什么是对象?

        对象是事物的统称:具有一定功能和特征的事物就是对象;
        那么,哪些是功能哪些是对象呢?
        上述举例中,做饭、买水、洗衣服,这些是功能;女友长得像谁是特征。
        换言之,这些东西就是帮你解决问题,能满足你某种需求的事物都是对象。
        那这样说,所有东西都是对象了。没错,比如写这篇文章的我,很帅、很高,这是我的一些属性特征;我能写文章、能说话、能吃饭、还能打Kiss,这是我的一些功能。我是一个对象。
        我能不能满足你的需求呢?每个人的需求不一样,有人会觉得这篇文章能帮助你,有人觉得这篇文章写得真菜。所以,在某种程度上还是可以满足一定的需求的。
        综上所述,可以理解为万事万物皆对象。
        如空调,现在夏天来临,外面很热,都想在家、在办公室不出去吹凉风,能满足你一定的需求,空调就是一个对象。
        如电脑,夏天很热啊,不想出去,在家躺着也很无聊,玩玩电脑,比如看看我这篇文章,你通过电脑看到了我这篇文章,细细的读完了,觉得对自己很有帮助,满足了你一定的需求,电脑就是一个对象。
        那么,回到编程中,对象是如何来的呢?对象是通过类实例化出来的。

七、什么是类?

        上述说到,对象是有一定功能和特征的事物,或者能够满足我们某种需求的事物,他就是一个对象。
        具有相同功能和特征的抽象就称之为类。如人是一个类,有男人、女人,当然在泰国还有其他人。只拿一个人来定义他们,他们就统称为一个类。
        抽象是什么?抽象是没有具体实例化出来,是一个泛指,一个统称。
        类这个东西是思想上的。
        也可以这样理解:小明今天去采购了十台服务器,这是一个类。小明今天去采购了十台戴尔的服务器,这是一个对象。上述说到,对象是某一特定的事物。而类只是一个泛指。当我指明服务器的品牌为戴尔的时,就是一个对象。但是当我没有指明服务器品牌的时候,他就是一个类,因为没有说明是不是戴尔,也有可能是IBM、华为等众多品牌的服务器,所以为类。


八、类的比较

        马良        画的画        从画里走出了事物
        程序员    类                对象
        设计师    图纸            按照图纸造出来的事物
        总结:要有对象先有类。

        *类的实例化(实体化)称之为对象。
        *对象的抽象就是类

九、类的声明(先有类,再有对象)

        1.格式:class+空格+类名{},示例代码如下所示。     

<?php

    /*
     * 类的声明
     * 类名推荐使用驼峰命名法
     * 代码编写格式请严格遵循PSR-4规范
     * */
    class MeiNv
    {

    }

    // 实例化对象

    $a = new MeiNv();
    var_dump($a);

       现在思考一个问题,类里面都有哪些东西?上述讲到类要有功能和特征,特征在这里指的是成员属性,功能指的是成员方法。

        如果造一个美女的类,那么美女应该有哪些功能和特征呢?

        特征:身高、年龄、三围。

        功能: 唱歌、跳舞、约会。

        类里面要有修饰符,修饰符有三个,把他们称为3P修饰符。分别为:public(公共的)、protected(受保护的)、private(私有的)。

        成员属性必须要加修饰符,否则会报语法错误,代码示例如下所示。

public $name = '小红';
public $age = 20;

不加修饰符则会报如图1-1、1-2所示的错误。

                                            图1-1

                                                              图1-2   

成员属性的初始值20去掉可以不可以?也是可以的,成员属性的初始值可有可无。

上述是=20,那么,改成10+10可以吗?改成10+10后虽然没有报错,但是,在PHP5.4时,不能使用运算。现在我所使用的环境是PHP5.6,所以可以使用。

除了直接给值、不给值、运算之外,还可以使用变量。示例代码如下所示。

    $a = 10+20;
    $b = $a;
    echo $b;

那么,在public $age 中使用变量可不可以呢?结果如图1-3、1-4所示。

                                        图1-3

                                                        图1-4

这个错误什么意思呢?在赋值的时候不能使用(不允许使用)变量,及函数调用。

什么叫函数调用呢?

    function getage()
    {
        return 20;
    }

    $b = getage();
    echo $b;

我一调用他,他就把值返回来。返回之后把值赋值给$b,在输出$b结果等于20,结果如图1-5所示。

                            图1-5

刚才说不能在类里面使用,结果如图1-6、1-7所示,报语法错误,确实不能使用。

                                        图1-6

                                                        图1-7

上述所说,不可以使用变量,函数调用,在PHP5.4时不能使用运算。那么,可以使用常量吗?

    // 常量定义
    define('AGE',20);
    echo  AGE;
    class MeiNv
    {
            public $age = AGE;
    }

刷新浏览器页面,发现没有报错。所以,可以使用常量赋值。

说到常量,回顾下常量的值有几种。常量的值一共有五种:四种标量,一种null。

    1.boolean(布尔型)
    2.integer(整型)
    3.float(浮点型)
    4.string(字符串)
    5.null

回到刚才所说的,每个女人共有的特征是:身高、年龄、三围。示例代码如下所示。

    class MeiNv
    {
            public $height = 168;
            public $age = 24;
            public $sanwei = '80 65 85';
    }

当前定义的类里面他有三个属性,分别是身高、年龄、三围。在这个例子后,考虑这样一个问题,这些是属性,那么,他的功能和方法有哪些呢?刚才说美女可以唱歌,跳舞,约会。示例代码如下所示。

class MeiNv
    {
        // 功能 ==> 成员方法
        // 约会
        public function date()
        {
            echo '美女正在约会呢,思密达。。。';
        }
    }

上述代码所提到的function date(){}这是一个函数,前面加上public修饰符放在类里面就表示成员方法,或者也可以称之为成员函数。那么,不加前面的修饰符可以吗?试一下,示例代码如下所示。

    // 如果不加修饰符,默认为public
    function sing()
        {
            echo '美女正在唱歌';
        }

刷新浏览器页面,发现也没有报错。所以,成员方法的修饰符可有可无。如果不加修饰符,默认为public(共有的)。

函数在定义时,可以return返回,示例代码如下所示。

    public function dnace()
        {
            return '美女正在跳舞呢';
        }

刷新浏览器页面发现也没有报错,在成员方法定义时,返回值可有可无。之前在调用函数时,可以加参数,这里也一样可以加参数。示例代码如下所示。

    public function date($someone)
        {
            
        }

到底,就把类定义完成了,类的定义里面有属性和方法。那么,来看看能不能实例化成为一个对象呢?

如果在函数中没有使用return手动去返回一个值的话,默认返回null(空)。示例代码如下所示。

<?php

    function love()
    {

    }

    $a = love();
    var_dump($a);

    function hate()
    {
        return;
    }

    $b = hate();
    var_dump($b);

综上所述,总结如下。

    特征 ==> 成员属性
    1.成员属性变量 必须要加修饰符 3P
    2.成员属性的初始值可有可无
    3.成员属性在赋值时不能使用变量及函数调用
    4.成员属性赋值可以使用常量

    功能 ==> 成员方法
    1.成员方法修饰符可有可无
    2.参数可有可无
    3.返回值可有可无

十、实例化对象

上述类定义完,接下来该实例化对象了,示例代码如下所示。

class MeiNv
    {
        // 特征 ==> 成员属性
        public $height = 168;
        public $age = 24;
        public $sanwei = '80 65 85';

        // 功能 ==> 成员方法
        public function date($someone)
        {
            echo '美女正在和$aomeone约会呢,思密达。。。';
        }
        function sing()
        {
            echo '美女正在唱歌';
        }

        public function dnace()
        {
            return '美女正在跳舞呢';
        }
    }

    // 实例化对象
    $a = new MeiNv();
    var_dump($a);

刷新浏览器页面,结果如图1-8所示。

                            图1-8

注意:在打印的时候只能看到它的属性,看不到他的方法。

上述代码段中,实例化对象时加了(),如果不加也是可以的。代码如下所示。

    // 实例化对象
    //$a = new MeiNv();
    $a = new MeiNv;
    var_dump($a);

在这个例子中可以加可以不加,但是什么时候加什么时候不加呢?在后面的构造方法中必须加,如果没有构建方法可以直接new,但是建议大家一定要加上小括号。

根据上面的写法,对象的实例化要先定义类。那么,这个实例化的对象能不能放到类的定义之前呢?也是可以的,也就是说,实例化的对象可以再类的声明之前,也可以在类的声明之后。如图1-9所示。

                                                        图1-9

object(MeiNv)[1]
  public 'height' => int 168
  public 'age' => int 24
  public 'sanwei' => string '80 65 85' (length=8)
释义:
object ==> 数据类型(对象)
MeiNv ==> 类名(当前对象通过哪个类实例化出来的)
[1] ==> 代表当前对象在脚本中所有对象的数字索引,从1开始。
public ==> 成员属性的修饰符
'height/age/sanwei' ==> 属性名
int 168/ int 24/ string '80 65 85' ==> 成员属性的值,什么类型,多长

对象拿出来后,我们是要对他进行操作的。比如看一下他的名字,怎么称呼;他三围多少,这是他的属性。对对象进行操作使用->符,称为成员属性操作符。

重新定义一个类,声明其属性和方法,示例代码如下所示。

// 定义类
    class MeiNv
    {
        // 成员属性
        public $name = '玲玲';
        public $age = 20;
        public $sanwei = '80 65 80';

        // 成员方法
        public function date($someone)
        {
            echo "美女正在和{$someone}约会";
        }
    }

实例化MeiNv该对象,示例代码如下所示。

// 实例化对象

    $a = new MeiNv();
    var_dump($a);

结果如图1-10所示。

                                                        图1-10

获取MeiNv类中的姓名,年龄及三围,示例代码如下所示。

// 成员属性的操作 在类的外部 ->成员访问符
    // 获取 对象 -> 属性名;
        echo $a -> name;
        echo $a -> age;
        echo $a -> sanwei;

如果访问的属性不存在,则提示Notice,如图1-11所示。

echo $a -> height;

                                                    图1-11

既然能获取,自然也就可以修改。比如对年龄不满意,加一个=号就表示重新赋值。示例代码如下所示。

// 修改
    $a -> age = 18;
    var_dump($a);

防止混淆,把上面代码段中的var_dump($a);也打开,下面在打印一次重新赋值后的$a,结果如图1-12所示发生了改变。

                                                图1-12

刚才我访问了一个不存在的值,刷新浏览器页面提示报错,既然不存在,就给他添加下,示例代码如下所示。

// 添加
    $a -> height = '168cm';
    var_dump($a);

添加后,打印并刷新浏览器页面,显示如图1-13所示的结果。

                                            图1-13

因为每个人的身高不一样,所以就没再成员属性中加身高,没加这个身高可以单独给对象进行添加。我刚才给对象添加了一个身高,如果我在实例化一个对象呢?跟他是没有任何关系的,结果如图1-14所示。

$a = new MeiNv();
    var_dump($a);

                                                图1-14

因为,只是对当前对象进行的操作,对于其他的对象没有任何关系。

既然上述有增,改,肯定少不了删除的事,那么如何删除呢?示例代码如下所示。

// 删除
    unset($a->sanwei); // unset没有返回值
    var_dump($a);

打印结果如图1-15所示,已经不存在了。

                                        图1-15

上述代码段是在类的外部对成员属性做的一些操作,其实就是增删改查。那么,针对成员里面的方法如何操作呢?示例代码如下所示。

// 成员方法的操作
    $a -> date();

虽然上述代码段中没有传参数,报错了,但是也执行了。如图1-16所示。

                                                                图1-16

报错错误提示:你当前调用类里面的成员方法时少了一个参数。

那么,现在来传一个参数,美女正在和谁约会?美女正在和明哲约会,示例代码如下所示。

// 成员方法的操作,在类的外部
    $a->date('明哲');

刷新浏览器页面,结果如图1-17所示。

                                    图1-17

上述代码段中成员方法函数中的$someone不加花括号也是可以的,结果如图1-18所示。

                                                图1-18

报错提示:他说这个变量不是someone,他把约会也当成一个变量了。如果把代码段中的约会去掉则会输出如图1-19所示的结果。输出美女正在和明哲,具体干啥,我们无从得知。

                    图1-19

如果在变量$someone的前后加一个空格可以吗?其实是可以的。,结果如图1-20所示。哦,原来美女正在和明哲约会。

// 成员方法
    public function date($someone)
    {
        echo "美女正在和 $someone 约会";
    }

                                图1-20

但是这样,双引号会非常慢,因为他空能多,功能的东西,只能说相对慢一下,注意是相对。其实我们可以使用单引号加连接符的方式。示例代码如下所示。

// 成员方法
    public function date($someone)
    {
        echo '美女正在和'.$someone.'约会';
    }

尽量使用单引号,因为单引号解析速度比较快。刷新浏览器页面,如图1-21所示。

                            图1-21

上述代码段中的例子没有返回值,之前我们说过,没有手动返回值,会返回null,我们赋值一个变量打印看看是不是会返回null,确实返回了null。示例代码如下所示。

// 成员方法的操作
    $res = $a->date('明哲');
    var_dump($res);

上面的例子就是当前对象的一些操作、属性的访问、方法的一些访问都完成了。

注意:对象成员的操作访问都需要 -> 成员访问符。

上面的例子都是在类的外部进行访问,如果在类的内部如何进行访问?

举例,如图1-22所示,实例化出来了一个对象。

                                        图1-22

实例化出来一个对象,叫$a,这个例子我们想来一个自我介绍,让他自己介绍一下他的身高,他多大了,他的三围。这时候需要有一个字符串,我叫什么,我今年多大了,我的三围是多少,定义成代码示例如下所示。

// 自我介绍
    $str = '我叫'.$a->name.',我今年'.$a->age.',我的三围是'.$a->sanwei;

    echo $str;

echo 后得到的结果如图1-23所示。

                                                            图1-23

如果说又来了一个对象,只需要重新定义即可。如他的名字叫小红,其他的都不变,示例代码如下所示。

    $m = new MeiNv();
    $m -> name = '小红';
    $str = '我叫'.$m->name.',我今年'.$m->age.',我的三围是'.$m->sanwei;
    echo $str;

刷新浏览器页面,结果如图1-24所示。

                                                            图1-24

因为我们是在类的外部操作的,在类的外部操作有一个特点,我们要拿对象去访问他的属性才可以获取到。上面对象叫做$a,下面的对象就不叫$a,叫小红,所以要换一个变量,然后同样的事情再多一次,变量要换。那么,现在有一个问题,如果要不是这三个值是十个值,我岂不是要多写7次变量才可以。代码明显就冗余了,我想在类里面给他增加一个方法,这个方法就是自我介绍的方法。

// 自我介绍方法
    public function intro()
    {
        $str = '我叫'.$a->name.',我今年'.$a->age.',我的三围是'.$a->sanwei;
        echo $str;
    }

然后在外部实例化对象进行访问,示例代码如下所示。

// 实例化对象
    $a = new MeiNv();
    $a->intro();

刷新浏览器页面,发现报错。提示我们没有变量a和找不到对象,如图1-25所示。

十一、$this关键字

也就是说,不能这样使用。那么,我想在类的内部访问他的属性,可以使用关键字$this。示例代码如下所示。

// 自我介绍方法
    public function intro()
    {
        /*
         * this 关键字
         * this 本身就是 '这个的' 意思
         * $this 在成员方法中代表当前这个对象
         * 什么意思呢?也就是说谁调用了当前成员方法是谁,$this就代表谁,我当前这个对象是$a,$this就是$a。
         * */
        $str = '我叫'.$this->name.',我今年'.$this->age.',我的三围是'.$this->sanwei;
        echo $str;
    }
// 实例化对象
    $a = new MeiNv();
    $a->intro();

刷新浏览器页面,已经出来了,结果如图1-26所示。

                                                            图1-26

在类的内部,也可以做增删改查操作,示例代码如下所示。

 // 测试 $this 可以做哪些操作
    public function test()
    {
        // 在类的内部进行操作
        echo $this->name; // 获取属性
        echo $this->name = '小红'; //修改
        echo $this->height = '168cm'; // 添加
        unset($this->hetght); // 删除
    }
// 实例化对象

    $a = new MeiNv();
    $a->test();
    var_dump($a);

既然可以对属性进行增删改查的操作,那么,对方法自然也是可以的。示例代码如下所示。

// 成员方法
public function date($someone)
{
    echo '美女正在和'.$someone.'约会';
}
public function test()
{
    // 调用方法
    $this->date('小明');
}
// 实例化对象
    $a = new MeiNv();
    $a->test();
    var_dump($a);

刷新浏览器页面,结果如图1-27所示,美女正在和小明约会。

                                                        图1-27

十二、特殊的成员方法/魔术方法

    构造方法

        构造方法是一个特殊的成员方法,特殊在不需要成员对象来调用,他有自己的自动执行场景。当使用new关键字来实例化对象的时候会自动触发,他的方法名叫:__construct(推荐使用),他还有另外一个方法名叫:与类名同名的成员方法也可以理解为构造方法。

首先先来定义一个类,并且实例化打印,示例代码如下所示。

class Person
    {
        // 成员属性

        public $name;
        public $age;
        public $height;
        
        // 成员方法
        public function chi()
        {
            echo '吃';
        }

        public function he()
        {
            echo '喝';
        }

        public function piao()
        {
            echo '嫖';
        }

        public function du()
        {
            echo '赌';
        }

        public function chou()
        {
            echo '抽';
        }
    }

    // 实例化对象

    $ll = new Person();
    var_dump($ll);

打印结果,如图1-28所示,因为没有传任何值,所以打印出来的都是null。

                        图1-28

成员属性和成员方法他们两个书写的位置放在哪里都可以,只要格式正确就没有任何问题。一般来讲就是上面成员属性,下面成员方法。析构方法默认写在成员属性和成员方法的中间。析构方法代码格式如下所示。

修饰符+函数+__construct(){}

    // 构造方法
        public function __construct()
        {
            echo '我自动触发了<br>';
        }

可以理解为 new Person这个类的时候,他会先去找构造方法,如果有构造方法直接帮你执行,如果没有构造方法就不执行了。构造方法的作用就是初始化当前这个对象。

上述代码段中,我并没有给值,但人应该有名字,我要求实例化出来之后要有名字,示例代码如下所示。

// 构造方法
    public function __construct()
    {
        // 初始化当前对象
        $this->name = '张三';
    }

打印后,刷新浏览器页面,得到如图1-29所示的结果。刚开始name没有给值时,显示为空,现在输出的是我们定义的张三。

                            图1-29

但是,每个名字都应该叫张三吗?不应该。因为每个人的名字不同,应该传一个参数进来。示例代码如下所示。

// 构造方法
	public function __construct($name)
	{
		// 初始化当前对象
		$this->name = $name;
	}

直接刷新浏览器页面,如图1-30所示。发现报错了,错误提示:在实例化的时候缺少一个参数,在哪里实例化?当然是在下面的 $ll = new Person();中少一个参数。所以我们要传参(传一个参数进去)。示例代码如下所示。

// 实例化对象
    $ll = new Person('张三');
    var_dump($ll);

在刷新浏览器页面,如图1-31所示,已经打印出来了。

                                    图1-31

参数肯定不只一个,肯定会是多个,上述代码段中我写了三个,所以我们要传三个参数进去。示例代码如下所示。

// 构造方法
	public function __construct($name,$age,$height)
	{
		// 初始化当前对象
		$this->name = $name;
		$this->age = $age;
		$this->height = $height;
	}
// 实例化对象
	$ll = new Person('张三','20','170cm');
	var_dump($ll);

刷新浏览器,我们在实例化中的参数已经打印的参数已经出来了,如图1-32所示。

                                        图1-32

这样有什么好处呢?好处就是通过这个类实例化出来的对象每一个都可以是不同的,示例代码如下所示。

// 实例化对象
    $ll = new Person('张三','20','170cm');
    $a = new Person('李四','22','175cm');
    $m = new Person('王五','23','180cm');
    var_dump($ll,$a,$m);

刷新浏览器,如图1-33所示。

                                    图1-33

刚刚说过,除了__construct之外,还有和类名同名的构造方法,示例代码如下所示。

// 与类名同名的方法也叫构造方法
	public function Person()
	{
		echo '我和类名同名';
	}

刷新浏览器,如图1-34所示。

        图1-34

他也可以执行,但是我上面的注释了,如果把上面的打开,他会优先执行哪个?肯定会是优先执行上面的__construct,示例代码如下所示。

// 构造方法
	public function __construct($name,$age,$height)
	{
		// 初始化当前对象
		$this->name = $name;
		$this->age = $age;
		$this->height = $height;
	}

	// 与类名同名的方法也叫构造方法
    // 如果类中已经存在了构造方法,那么与类名同名的方法就作为普通方法。
	public function Person()
	{
		echo '我和类名同名';
	}
	
// 实例化对象
    $ll = new Person('张三','20','170cm');
    $a = new Person('李四','22','175cm');
    $m = new Person('王五','23','180cm');
    var_dump($ll,$a,$m);

刷新浏览器,如图1-35所示,优先执行上面的__construct。

                                图1-35

注意:推荐使用__construct,如果类中已经存在了构造方法,那么与类名同名的方法就作为普通方法。

    析构方法

       当对象在销毁时,自动触发。方法名:__destruct。对象销毁的几种情况如下。

        1.当脚本执行脚本自动销毁。

        把之前定义的Person类的代码复制粘贴到新建的.php文件中,并写一个析构函数,输出对象自动化销毁了,示例代码如下所示。

<?php

class Person
{
    // 成员属性

    public $name;
    public $age;
    public $height;

    // 成员方法
    public function chi()
    {
        echo '吃';
    }

    public function he()
    {
        echo '喝';
    }

    public function piao()
    {
        echo '嫖';
    }

    public function du()
    {
        echo '赌';
    }

    public function chou()
    {
        echo '抽';
    }

    // 析构方法 注意:析构方法中不能加任何参数
    // 当对象在销毁时,自动执行。
    public function __destruct()
    {
        echo '对象销毁了';
    }
}

 然后实例化对象,示例代码如下所示。

// 实例化对象
    $a = new Person();
    var_dump($a);

刷新浏览器页面,如图1-36所示。

            图1-36

    2.使用unset销毁。

    3.重新赋值,覆盖原来变量中的值(对象和变量就没有了引用关系)。

面向对象第二天

    在第一天中的开头,我说到OOP(面向对象编程,后面就不在括号声明OOP是面向对象了,直接以OOP简写)有三大特性,分别是封装、继承和多态。

一、什么是封装?

    举例:想必大家都经常购物,新购的商品都是包装好在出售的,这个包装就是封装。那么,为什么要包装呢?第一点可以保护隐私,第二点可以保护商品的安全性。比如你在淘宝上买了个娃娃,不想让他人发现,你是不是要告诉卖家包装要严密啊(不要想的太邪恶了,我这里说的是芭比娃娃)。

    其实任何编程语言的设计模式,包括思想都来自于生活。封装在当前怎么理解呢?其实封装就是使用修饰符来修饰成员方法和成员属性。最大限度的隐藏对象的内部细节,保证对象的完整性和安全性。封装是进行访问控制的,但不是拒绝访问的。

    比如我是一个对象,我有身高、肤色,鼻子、眼,这是我的一些特征和一些功能,那我也有心肝肺,心肝肺就是我的一些属性,也是我对象里面的一部分。这个时候,你说,我玩玩你的心,玩玩你的肺能行吗?肯定是不行的。让你拿着玩玩,我还活不活了~。这个时候就是拒绝访问,一定要封装。那么,我是把它隐藏在我身体内部,别人看不到也摸不着也碰不到,但实际上它存不存在呢?他存在,并且一直都在为了提供各种能量,否则,我早就该挂了。这是一个比较深远的例子。

    再举一个浅一点的例子,我做在这里,我有身高,这是我的一些属性,比如说有没有打kiss的功能?肯定是有的,这个想都不用想。这也是我的功能,那么,现在有个人不管男的女的过来让我吻她,可以吗?肯定不可以啊,这事还是要讲究你情我愿。我下班回到家了,今天累了一天了,跟老婆说:滚床单,可以不可以啊?肯定是可以的。那么,为什么同样的一些方法,有些人能用,有些人不能用呢?这些我们可以理解为是私有的,或者受保护的东西,不是所有的,随便什么人都可以调用的功能。

    再举一个例子,比如在咖啡厅,旁边坐了一个美女,你上去直接问美女你三围多少,可以吗?肯定不可以,还会扇你,顺便说你一句流氓。而且三围就算是一般的朋友她能告诉你嘛?肯定是不可以的。这一点我是亲身经历的,在OOP第一天的时候,笔者我写到美女那个类的时候,其中有一个三围的属性,(笔者当时应该是头脑发热吧,前去问自己的闺蜜,当然是妹子。问她的三围是多少,虽然告诉我了,但是事后感觉很尴尬,上去直接问确实很不礼貌,如果是不认识的人,QQ、微信上面问会拉黑你,在你面前估计真会扇你。所以,还是不要没事去问女性的三围,毕竟碍于一些没有那个啥的女人面前,这个话题很敏感)。扯远了,回归话题,那么,他会告诉谁啊?她老公肯定会告诉的,刚才说打kiss是他的一些功能,那么三围是她的一些属性。这个时候,他是有一部分可以,有一部分不可以。这个时候也是进行了封装,进行了访问控制。但是不是拒绝访问,如果是拒绝访问,就没法用了。如果调用不了,那就没有任何意义了。

二、封装

    下面通过例子来具体看一下在代码当中如何实现封装,示例代码如下所示。

<?php

    // 定义一个类
    class Meinv
    {
        // 成员属性
        public $name; //名字
        public $age;  //年龄
        public $sanwei; // 三围
        
        // 构造方法
        public function __construct($n,$a,$s)
        {
            // 初始化
            $this->name = $n;
            $this->age = $a;
            $this->sanwei = $s;
        }

        // 成员方法
        public function yangyan()
        {
            echo '美女看起来很养眼';
        }

        public function date()
        {
            echo '美女正在约会呢';
        }

        public function kiss()
        {
            echo '美女正在打Kiss';
        }
    }

    // 实例化对象
    $zly = new Meinv('赵丽颖','30','85 60 80');
    var_dump($zly);

    刷新浏览器,如图2-1所示。已经实例化成功了。

                                    图2-1

    目前对象已经创建出来了,对象创建出来之后要看一个东西,成员属性所有的都是public,成员方法所有的也都是public,也就是说都是公有的。刚才说过封装,刚才的封装意思理解了,如何来做呢?用3P修饰符。public共有的,protected受保护的,private私有的。但是上述代码段中的三个成员属性都是public没有任何意义,第一个保持不变,第二个改成protected,因为是年龄,一般女性比较在意年龄方面,你问他,他不会告诉你。第三个改成private,上面的年龄还好,也许有的会告诉你,也许有的不会告诉你。但是这个是三围,一般人是不会告诉的,除非是特别好的关系,如闺蜜、老公等。

    类的外部封装访问

    接下来我们在外部进行访问,因为第一个是name,是公有的所有人都可以访问,没有任何问题。那么,来看一下受保护的,示例代码如下所示。

<?php

    class Meinv
    {
        // 成员属性
        public $name; //名字
        protected $age;  //年龄
        private $sanwei; // 三围
        
        
        // 成员方法
        public function yangyan()
        {
            echo '美女看起来很养眼';
        }

        protected function date()
        {
            echo '美女正在约会呢';
        }

        private function kiss()
        {
            echo '美女正在打Kiss';
        }
    }

    // 实例化对象
    $zly = new Meinv('赵丽颖','30','85 60 80');
    //var_dump($zly);

    // 在类的外部访问成员属性
        // 成员属性获取
        // echo $zly->name;    // √
        echo $zly->age;

    刷新页面,提示语法错误,意思是美女这个类里面的age是受保护的,外部访问不了。如图2-2所示。

                                                                图2-2

    也就是说受保护的在类的外部是不能使用的。再来看私有的,代码如下所示。

echo $zly->sanwei;

     如图2-3所示,三围也是不可以的,提示错误信息内容为在类的外部不能调用私有的$sanwei。

                                                            图2-3       

    目前,我们达到了一个目的,就是封装。但是你说,哎呀,封装起来,在外部不能使用了还有什么意义呢?目前,只有公有的是允许外部来访问,私有的和受保护的在类的外部都访问不了。也就是说,你有哪些东西不想让他在类的外部进行访问的话,就可以把它定义为私有的或者受保护的。这样就达成了目的,这就是封装。

    上述代码是成员属性获取不能访问,那么,能不能在外部进行修改呢?如把年龄改成20试试,示例代码如下所示。

// 成员属性修改
    $zly->age = 20;

    刷新浏览器页面,如图2-4所示,也是不可以的。因为你根本就没有访问权限,所以你用不了,也修改不了。

                                                          图2-4

    那么,删除可以吗?试一下,示例代码如下所示。

// 删除
    unset($zly->sanwei);

    刷新浏览器,如图2-5所示。也是不可以的,因为私有和受保护的在外部根本用不了,更别提修改和删除了。

                                                            图2-5

    追加是否可以吗?追加是可以的,因为,不论在类的内部还是在类的外部进行添加都是对成员属性进行一个补充,这时,添加默认就是一个公有的。示例代码如下所示。

// 追加
    $zly->height = '170cm';
    var_dump($zly);

    刷新浏览器页面,如图2-6所示已经追加上了。在打印的时候会显示公有的,私有的,受保护的。

                                    图2-6

    下面在来测试一下成员方法,在类的外部访问成员方法。成员方法也是分公有的,私有的和受保护的。养眼这一功能是公有的,来看一下,示例代码如下所示。

// 在类的外部访问成员方法
    $zly->yangyan();

    刷新浏览器页面,如图2-7所示,可以显示,因为他是公有的。

                    图2-7

    那么,在试下受保护的,其实到这里大家应该都很清楚到底能不能访问了。但是,我还是要都给大家演示一遍,虽然我写的这一篇文章不能算是最好的,最细的。但是,还是要按照自己的原则去详细的写完每一个步骤。受保护的示例代码如下所示。

$zly->date();

    刷新浏览器页面,如图2-8所示,提示不能调用MeiNv这个类中受保护的方法。

                                                    图2-8

    在测试一下私有的,示例代码如下所示。

$zly->kiss();

    刷新浏览器页面,如图2-9所示。提示不能调用MeiNv这个类中私有的方法kiss。

                                                图2-9

    得出一个结果,只有修饰符是公有的,在类的外部才可以访问和调用。如果用其他的修饰符修饰的成员属性和成员方法就进行访问控制了,这就完成了一个封装。虽然达到了这个效果,但是当前只是针对在类的外部做一个测试。

    类的内部封装访问

    测试成员属性

    现在来看下在类的内部进行封装测试,先测试公有的,示例代码如下所示。

// 测试在类的内部对成员进行访问测试
    public function test()
    {
        // 在类的内部访问成员属性
        echo $this->name;
    }

    但是刷新浏览器页面是空白的什么都没有,为什么没有?因为我下面都注释了,在OOP第一天的时候,我说过,在类的内部访问是不是要先调用test这个方法才可以呢?所以,要先调用test方法,示例代码如下所示。

// 实例化对象
    $zly = new Meinv('赵丽颖','30','85 60 80');

    $zly->test();

    在去刷新浏览器页面,如图2-10所示。显示了公有的名字为赵丽颖。

图2-10

再来看下受保护的在类的内部是否可以访问呢?示例代码如下所示。

// 测试在类的内部对成员进行访问测试
	public function test()
	{
		// 在类的内部访问成员属性
		// echo $this->name;
		echo $this->age;
	}

刷新浏览器页面,如图2-11所示可以正常访问。

        图2-11

    再来测试下私有的,示例代码如下所示。

// 测试在类的内部对成员进行访问测试
	public function test()
	{
		// 在类的内部访问成员属性
		// echo $this->name;
		// echo $this->age;
		echo $this->sanwei;
	}

    刷新浏览器,如图2-12所示,也是可以正常访问的。

        图2-11

    总结:在类的外部是有访问权限的,但是在类的内部,自己私有的,公有的还是受保护的都是可以正常访问的。这就相当于,别人没有任何理由知道我的姓名,年龄及三围,但是我自己必须清楚的知道我的姓名、年龄及三围。

    在类的内部进行删除,示例代码如下所示。

// 删除
	unset($this->sanwei);

// 实例化下面进行打印
    var_dump($zly);

刷新浏览器页面,如图2-12所示。三围已经被删除了。

                                    图2-12

    在类的内部进行修改,示例代码如下所示。

// 修改
	echo $this->age = 25;

    刷新浏览器,如图2-13所示年龄已变成25。

                                    图2-13

    综上所述:有访问权限就可以进行操作,没有访问权限,访问都不能,更别提删除和修改等操作了。

    成员方法

    上面测试的是成员属性,下面再来看一下成员方法。示例代码如下所示。

// 在类的内部调用成员方法
    $this->yangyan();

    刷新浏览器页面,如图2-14所示。

    

    图2-14

上面测试的是公有的,接下来测试受保护的,示例代码如下所示。

$this->date();

刷新浏览器页面,如图2-15所示。

        图2-15

测试私有的,示例代码如下所示。

$this->kiss();

刷新浏览器页面,如图2-16所示。

    图2-16

    目前,通过上述代码测试得出一个结果:我们的封装目前就达成了。封装完成之后,考虑一个问题。下面代码段中的年龄初始化的时候给一个20的值,我想对他的年龄做一些修改的时候该如何操作呢?要知道私有的和受保护的在类的外部是不可以修改的。但是我就想访问他,这时,需要提供一个方法对某一个属性进行获取或者进行修改。

<?php

    // 定义类
    class Person
    {
        // 成员属性
        public $name;
        protected $age;
        private $sex;

        // 构造方法
        public function __construct($n,$a,$s)
        {
            $this->name = $n;
            $this->age = $a;
            $this->sex = $s;
        }

        // 成员方法
        public function eat()
        {
            echo '吃饭了吗?';
        }

        protected function drink()
        {
            echo '你喝水了吗?';
        }

        private function smoke()
        {
            echo '你抽烟了吗?';
        }
    }

    // 实例化对象
    $res = new Person('张三','20','男');

    // 如果你直接调用受保护的age会报错,因为不能直接对受保护的属性进行修改
    // $res->age = 21;
    // 但是可以用$res去调用setAge()方法,并传参21即可
    // 通过提供的方法进行年龄的赋值操作
    $res->setAge(21);
    // 参数就到了$a那个位置了,你在外部没有权限,但是在内部是有权限的.
    var_dump($res);

刷新浏览器页面,如图2-17所示。

                                图2-17

    既然可以修改了,那么,我直接在外部进行获取可以吗?肯定是不可以的。如图2-18所示。

echo $res->age;

                                            图2-18

    但是我就想让他获取,可以再内部在提供一个方法,示例代码如下所示。

// 提供一个方法进行获取
	public function getAge()
	{
		return $this->age;
	}
// 在实例化下面进行echo 输出
// 通过提供的方法进行年龄的获取操作
	echo $res->getAge();

    现在有一个问题,如果有一个要求,所有的私有和受保护的都要至少提供一个get和一个set你要写过少个呢?目前有两个是在外部不可以进行操作的。一个就要提供两个方法,目前我这里有两个。所以,要提供四个方法。如果我再有呢,当然,每一个类属性不只这一点,有可能十几个甚至更多。此时,想象一下,如果我有100个不允许你访问的属性,这时,需要提供的方法就是200个。这样在操作就相当麻烦了。封装是进行访问控制,但是有些时候还想对某一些方法、某一些属性做一些处理这时候该怎么办呢?这就是接下来要学习的知识点。

    魔术方法

    music他是一个单词,把首字母m改成g就转换成了gusic。什么意思呢?之所以用music转换举例只是为了方便大家去记忆。这里面每一个单词的字母都代表着一个方法,一个什么方法呢?一个魔术方法。

    魔术方法 有自动的触发的执行场景,就好比之前讲的构造方法和析构方法。刚才说到每个单词的字母都代表着一个方法,代表的方法如下所示。

 *  g == __get
 *  u == __unset
 *  s == __set
 *  i == __isset
 *  c == __call

    接下来定义一个类来看一下这些方法是如何使用的,示例代码如下所示。定义的类我这里copy的前面的代码。

<?php

    // 定义类
    class Person
    {
        // 成员属性
        public $name;
        protected $age;
        private $sanwei;

        // 构造方法
        public function __construct($n,$a,$s)
        {
            $this->name = $n;
            $this->age = $a;
            $this->sanwei = $s;
        }

        // 成员方法
        public function eat()
        {
            echo '吃饭了吗?';
        }

        protected function drink()
        {
            echo '你喝水了吗?';
        }

        private function smoke()
        {
            echo '你抽烟了吗?';
        }
    }

    // 实例化对象
    $c = new Person('李四','22','50 50 50');
    var_dump($c);

刷新浏览器页面,如图2-19所示。

图2-19

    __get魔术方法

在类的外部访问成员属性,访问name是没有问题的,那么,访问age就会报错。这个时候,__get就派上用场了。

// 在类中定义
// __get魔术方法
	/*
	 *  触发场景:当对对象中非公有(私有和受保护的)或者不存在的成员属性进行获取时自动触发
	 *  并且将属性名作为第一个参数
	 * */
	public function __get($name)
	{
		echo $name;
	}
// 在类的外部访问成员属性
    echo $c->age;

刷新浏览器页面,如图2-20所示。不报错了,而且输出了age。

        图2-20

同样,我们在获取下私有的三围。示例代码如下所示。

echo $c->sanwei;

刷新浏览器页面,如图2-21所示。

            图2-21

以上就是关于__get魔术方法的使用。这个时候他具体做什么呢?你可以选择告诉他或者不告诉他。不让他报错给他一个提醒也可以。示例代码如下所示。

public function __get($name)
	{
		//echo $name;
		echo '你尝试的获取不存在或非公有的成员属性:'.$name;
	}

刷新浏览器页面,如图2-22所示。

                                                         图2-22

如果我们访问一个不存在的呢?如height,示例代码如下所示。

echo $c->height;

刷新浏览器页面,如图2-23所示。

                                                           图2-23

    __unset魔术方法

    接下来看下__unset魔术方法,示例代码如下所示。

// 在类中定义
	/*
	 *  __unset魔术方法
	 *  触发场景:当使用unset来销毁对象中不存在或非公有的成员属性时自动触发
	 *  并且将属性名作为一个参数
	 * */
	public function __unset($destruction)
	{
		echo '你尝试销毁一个不存在或非公有的成员属性:'.$destruction;
	}
// 实例化对象下面定义
// 销毁
    unset($c->age);
    unset($c->sanwei);

刷新浏览器页面,如图2-24、2-25所示。

                                                        图2-24

                                                        图2-25

同上,不存在的值也会提示,这里不再演示。

    __set魔术方法

再来看下__set魔术方法,示例代码如下所示。

// 在类中定义
	/*
	 *  __set魔术方法
	 *  触发场景:当对对象中非公有或不存在的成员属性进行赋值时,自动触发
	 *  属性名作为第一个参数,属性值作为第二个参数
	 * */
	public function __set($n,$s)
	{
		// var_dump($n);
		// var_dump($s);
		$this->$n = $s;
	}
// 实例化对象下面定义
// 赋值
    $c->age = 28;
    var_dump($c);

刷新浏览器页面,如图2-26所示。

                                            图2-26

    __isset魔术方法

再来看下__isset魔术方法,示例代码如下所示。

// 在类中定义
	/*
	 *  __isset魔术方法
	 *  触发场景:当对对象中非公有或者不存在的成员属性进行使用isset empty进行检测时,自动触发
	 *  属性名作为第一个参数
	 * */

	public function __isset($name)
	{
		echo '你尝试检测不存在或非公有的成员属性:'.$name;
	}
// 实例化对象下面定义
// 检测
    $res = isset($c->sanwei);
    var_dump($res);

刷新浏览器页面,如图2-27所示。

                    图2-27

如果想让他返回一个正确的值,如何返回?其实可以手动检测,示例代码如下所示。

public function __isset($name)
	{
		echo '你尝试检测不存在或非公有的成员属性:'.$name;
		return isset($this->$name);
	}

刷新浏览器页面,如图2-28所示,下面提示true。

                                        图2-28

如果输入一个不存在的值呢?示例代码如下所示。

$res = isset($c->aaa);
var_dump($res);

刷新浏览器页面,如图2-29所示。

                                         图2-29

    __call魔术方法

上面也演示过,在类的外部进行访问私有的成员方法时会报错。如果不想报错怎么办呢?这时,__call就派上用场了。

// 在类中定义
	/*
	 *  __call魔术方法 针对的是成员方法
	 *  触发场景:党对对象中不存在或非公有的成语方法进行调用时,自动触发。
	 *  1.调用的方法名
	 *  2.调用方法时的参数列表
	 * */
	public function __call($name,$values)
	{
		var_dump($name);
		var_dump($values);
	}
// 实例化对象下面定义
// 在类的外部调用方法
    $c->smoke('1','2','3');

刷新浏览器页面,如图2-30所示。

                图2-30

但是,想象一下,是怎么获取到的呢?图2-30中的数组0、1、2是函数调用时的参数列表,函数调用时的参数列表是怎么获取的呢?其实就是因为拥有func_get_args这么一个方法。

    以上就是关于五个魔术方法的讲解,加之面向对象第一天中讲解的两个魔术方法,一共是七个魔术方法。希望大家牢牢记住,因为在企业面试题中出现的概率非常大。

    

面向对象第三天

一、什么是继承?

 

 

二、继承

 

面向对象第四天

一、const

 

面向对象第五天

一、抽象类

 

面向对象第六天

 

一、类和对象的常用函数

 

面向对象第七天

一、PDO是什么?

未完,待续......................

此文章为原创文章,转载请注明出处:http://www.huzhen.org;如有侵权,违者必究!

                                                                                                                                    —    胡明哲        2017-8-4 

© 著作权归作者所有

胡明哲
粉丝 0
博文 15
码字总数 26908
作品 0
香港
CTO(技术副总裁)
私信 提问

暂无文章

Qt程序打包发布方法(使用官方提供的windeployqt工具)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/toTheUnknown/article/details/81748179 如果使用到了Qt ...

shzwork
24分钟前
4
0
MainThreadSupport

MainThreadSupport EventBus 3.0 中的代码片段. org.greenrobot.eventbus.MainThreadSupport 定义一个接口,并给出默认实现类. 调用者可以在EventBus的构建者中替换该实现. public interface ...

马湖村第九后羿
44分钟前
3
0
指定要使用的形状来代替文字的显示

控制手机键盘弹出的功能只能在ios上实现,安卓是实现不了的,所以安卓只能使用type类型来控制键盘类型,例如你要弹出数字键盘就使用type="number",如果要弹出电话键盘就使用type="tel",但这...

前端老手
54分钟前
6
0
总结:Raft协议

一、Raft协议是什么? 分布式一致性算法。即解决分布式系统中各个副本数据一致性问题。 二、Raft的日志广播过程 发送日志到所有Followers(Raft中将非Leader节点称为Follower)。 Followers收...

浮躁的码农
今天
7
0
Flask-admin Model View字段介绍

Model View字段介绍 can_create = True 是否可以创建can_edit = True 是否可以编辑can_delete = True 是否可以删除list_template = 'admin/model/list.html' 修改显......

dillonxiao
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部