6.1. 类,对象

未匹配的标注

持之以恒,方得始终!

类,对象是什么

什么是类?
物以类聚,具有相同特性和行为的实体,我们可以归为一类。类是一种虚拟的称呼,是对一类事物的一种描述性。

什么是对象?
对象是类的具体实例,比如一只麻雀,它是鸟类下的一个实体单位。

面向对象的三大特性

封装

使用访问控制修饰符 public protected private, 避免对象的属性,方法被外部直接使用,其实思想就是隐藏对象的实现细节,只提供几个必要的对外接口。

打个比方,内置的函数,我们一般也不清楚它的实现细节,但是有手册告诉我们怎么给参数,参数的含义,我们就可以用它为我们做事情了。

继承

子类可以继承父类的属性和方法,比方说,自行车类,和汽车类,它们有些共同地方,我们可以再来个交通工具类,自行车类和汽车类就可以继承交通工具类了。这样可以省掉一部分的代码。

多态(方法的重写)

子类继承父类时,可以重写父类的方法,也就是说子类的行为细节,可能和父类有点不一样。也就是多种行为状态。

我们也可以看下这篇文章 zhuanlan.zhihu.com/p/238129674

写一个简单的类

我们来写一个读取文件内容的类,并且和面向过程对比。
面向过程的做法:

  1. fopen() 打开一个文件。
  2. fgets() 逐行读取一行内容。
  3. 读完后,fclose() 关闭。

可以看到面向过程,做事情比较直接,上手就开始做,其实更符合人类的思考方式。

面向对象的做法:

  1. 创建一个文件读取类。
  2. 定义一个读取文件的方法。
  3. 类封装好后,我们要依托类,实例化一个文件读取对象,然后调用该对象的读取方法。

可以看到,面向对象的方式其实更麻烦,需要先封装好类,而且执行效率是比过程慢的。不过面向对象更体现了软件工程,而非小脚本。

class FileReadClass { // 一般是一个文件代表一个类,类名和文件名相同
    protected $file = null;

    // 构造方法,new一个对象时,会执行
    public function __CONSTRUCT($file) {
        $this->file = $file;
    }

    public function read() {
        if (empty($this->file) || !is_file($this->file)) {
            return false;
        }

        $data = '';
        $fp   = fopen($this->file, 'rb');
        while(!feof($fp)) {
            $data .= fgets($fp);
        }
        fclose($fp);
        return $data;
    }

    public  function __destruct() {
        echo "对象被销毁了";
    }
}

$file_oob     = new FileReadClass('./page.html');
$file_content = $file_oob->read();
echo $file_content;
unset($file_oob);

构造方法

当 new 一个对象时,会自动调用构造方法,所以它可以帮助执行一些初始化任务,比如设置属性的初始值,生成该对象需要的其它对象。
可以看上面的例子。

析构方法

在销毁一个类之前,执行一些操作。
析构方法 __destruct() 不能带有任何参数。
可以看上面的例子。

使用类的属性

$this 可以访问,它指代的其实是一个对象。
$this->attribute 访问属性

class classname {
    public $attribute;
    function operation($param) {
        $this->attribute = $param;
        echo $this->attribute;
    }
}

是否可以在类的外部访问属性,是由访问修饰符决定的(public,protected,private)。上面的例子属性是public,可以在外部访问。

$obj = new classname();
$obj->attribute = 'value';
echo $obj->attribute;

一般来说,从类的外部直接访问属性是不好的做法,好的做法是,我们应该隐藏类的细节,只暴露一些获取,设置的方法接口。
我们可以用 __get(), __set() 来访问属性。

  • 在给不可访问(protected 或 private)或不存在的属性赋值时,__set() 会被调用。

  • 读取不可访问(protected 或 private)或不存在的属性的值时,__get() 会被调用。

class classname {
    protected $attribute;

    function __get($name) {
        echo "调用了 __get()";
        return $this->$name;    
    }

    function __set($name, $value) {
        echo "调用了 __set()";
        if ($name == 'attribute' && $value >=0 && $value <= 100) {
            $this->attribute = $value;
        }
    }

}

$a = new classname();

$a->attribute = 5;

echo $a->attribute; // 调用了 __set()调用了 __get()5

这种好处是提供了一个单一的访问入口来设定,获取属性,并且是固定用法,对项目的维护友好。

访问修饰符

它们可以控制属性,方法的可见性。

  • public,默认的,公有的,可以在类的内外部可见。
  • protected,受保护的,类和子类中可见。
  • private,私有的,仅类里面可见,子类中也不可见。

protected和private,如果想外部访问,我们可以使用 __get(), __set() 魔术方法。

调用方法

class cacl {
    public function add($n1, $n2) {
        return $n1 + $n2;
    }
}
$obj = new cacl();
$res = $obj->add(10, 5);

实现继承 extends

class B extends A {    // B类继承A类,从而也可以使用A类中的属性,方法

}

注意:继承是单向的,子类可以继承父类,但父类不能从子类中继承。
private 不能被继承,protected,public可以。

重载

就是重写父类中的属性,方法。

class A {
    public $attribute = "default value";
    public function operation() {
        echo "attribute values is " . $this->attribute;
    }
}

class B extends A {
    public $attibute = "diff value";
    public function operation() {
        echo "new value is".$this->attribute;
    }
}

重写并不会影响父类的属性,方法。

如果说,我们使用了重写,但是还想用一下父类中的属性,方法,怎么办? 我们可以使用 parent 关键字,它表示父类。

parent::operation(); // 但是要注意,父类operation()里面的调用,还是当前类中的。比如里面的$this->attribute, 它还是用的B类对象的值。

继承是可以有子子类的,比如还可以创建一个类C,来继承类B,因此类C拥有类A,类B的所有属性,方法。 C=>B=>A

final, 禁止类被继承,方法被重写

禁止一个方法被重写:
类,对象

禁止一个类被继承:
类,对象

一个类同时继承两个父类

php并不支持有多个父类,php中,每个类都只能有一个父类。
当然,一个父类可以有多个子类,这样是没毛病。

类,对象

当然后面 php 引入了 Trait ,就可以实现第三种了。

接口类

实现多继承

  • 接口,一个类可以继承父类,还可以实现多个接口。
  • trait(推荐)

什么是接口?
类的范本,需要去实现它所规划的功能。

怎么做这种范本?

  1. 用继承的方式,父类写好空方法,子类去重写来实现。
class FileCurdBase {
    protected $file = null;
    protected function read() {}
    protected function write() {}
    protected function del() {}
    protected function add() {}
}

class FileCurd extends FileCurdBase {
    protected $file = null;

    public function __construct($file) {
        $this->file = $file;
        if (!file_exists($this->file)) {
            $this->add();
        }
    }

    public function read() {
        return file_get_contents($this->file);
    }

    public function write($data) {
        file_put_contents($this->file, $data);
    }

    public function del() {
        unlink($this->file);
    }

    public function add() {
        touch($this->file);
    }
}

$filecurd = new FileCurd("./log.txt");
  1. 用接口类 interface
interface Displayabe {
    function display();
}

class webpage implements Displayable { // 如果没有实现接口的方法,是会报错的。
    function display() {
        // code
    }
}

如有任何侵权行为,请通知我删除,谢谢大家!
个人邮箱:865460609@qq.com

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
Junwind
讨论数量: 0
发起讨论 只看当前版本


暂无话题~