最近在准备面试,复习了一下基础,包括PHP的类与对象。

1.类是一种抽象定义,对象是一种实现。

#定义类
class object
{
}
#实现类(对象)
new object

2.类中包括属性与方法

访问控制(可见性)
class object
{
  #可在内部调用,可被子类调用,可被外部(对象)调用
  public $a;
  #可在内部调用,可被子类调用
  protected $b;
  #可在内部调用
  private $c;

}

3.类的自动加载

类的自动加载,之前是使用__autoload(),现在,倾向于使用 spl_autoload_register() 注册器,实现懒加载

‘__’开头的为php中的语法糖

spl_autoload_register(function ($class){
    echo $class;exit;
    include $class . '.php';
});

new $a;

#输入
#a

4.对象继承

继承一个类,就会继承父类的属性与方法(private可见性除外),如果不覆盖父类的属性和方法,则会全部继承。

class parent_class
{
    public $a;
    public function func_a($arg)
    {
        echo 'hello' . $arg;
    }
}
class son extends parent_class
{
    
}

$model = new son();
$model->func_a('liuser');
#输出
#hello liuser



# 重写子类
class son extends parent_class
{

    public function func_a($arg)
    {
        echo '你好' . $arg;
    }
}
$model = new son();
$model->func_a('liuser');

#输出
#你好 liuser



#调用子类自己的属性
abstract class dbObject
{
    const TABLE_NAME='undefined';

    public static function GetAll()
    {
        # 这里使用$c或者static都可以
        $c = get_called_class();
        
        return "SELECT * FROM `".static::TABLE_NAME."`";
    }
}

class dbPerson extends dbObject
{
    const TABLE_NAME='persons';
}

class dbAdmin extends dbPerson
{
    const TABLE_NAME='admins';

}

echo dbPerson::GetAll()."<br>";//output: "SELECT * FROM `persons`"
echo dbAdmin::GetAll()."<br>";//output: "SELECT * FROM `admins`"

5.抽象类

抽象类用abstract表示,当一个类中有一个方法为抽象,则类必须被定义为抽象类,抽象类不能被实例化,只能被继承
抽象类中的方法只是定义其形式,不实现具体方法。

abstract class absclass
{
    #只能写方法名和参数,不能写具体实现(没有{}方法体)
    abstract protected function say($name);
    abstract public function run();
}

class class_1 extends absclass
{
    #这里的可见性级别只能比父类更开发,protected或者public,不能使用private
    protected function say($name)
    {
        // TODO: Implement say() method.
    }
    public function run()
    {
        // TODO: Implement run() method.
    }
}

6.对象接口

接口(interface),规则类必须实现的方法,但不需要定义内容,同抽象类相似。
接口中定义的所有方法都必须是公有,这是接口的特性。
接口中的常亮不能被子类覆盖。

抽象中,只有抽象类中的抽象方法必须实现,接口中,只要定义为接口,就必须全部实现。

定义接口:interface
实现接口:implements

interface  template
{
    public function setVariable($name, $var);
    public function getHtml($template);
}

class template1 implements template
{
    public function setVariable($name, $var)
    {
        // TODO: Implement setVariable() method.
    }
    public function getHtml($template)
    {
        // TODO: Implement getHtml() method.
    }
}

多重继承

interface a
{
    public function foo();
}

interface b
{
    public function bar();
}

interface c extends a, b
{
    public function baz();
}

class d implements c
{
    public function baz()
    {
        // TODO: Implement baz() method.
    }
    public function bar()
    {
        // TODO: Implement bar() method.
    }
    public function foo()
    {
        // TODO: Implement foo() method.
    }
}

7.trait(特性)

自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait,说白了就是定义一个trait,然后在需要用到的类中使用use调用其。
特点:
1.实现代码复用,解决多重继承问题。
2.无法被实例化

想象一下,如果你需要在一个类中加上日志的相关方法,会怎么做呢?继承?如果有多个类都需要实现日志的方法呢?
如果需要继承,则违背了继承的原则,除非你的类是和日志相关。可以考虑使用特性解决。

trait log
{
    #记录日志
    public function start()
    {
        echo time();
    }
    public function end()
    {
        echo time();
    }
}


class Car
{
    #使用特性log
    use log;
}
$model = new Car();
$model->start();
$model->end();

使用use就是当trait中的方法复制到当前的类对象中,可能会产生问题。
比如log中定义了start,而car中也定义了start,则会产生一个优先级的问题,官方给出的优先级如下:

优先顺序是当前类中的方法会覆盖 trait 方法,而 trait 方法又覆盖了基类中的方法。

9.重载

动态的创建类方法或属性,使用魔法方法__set __get,注意一下用法。
1.魔术方法的可见性必须是public.
2.如果类方法可类变量存在,则无法使用.


class test
{
    private $data = [];

    public function __set($name, $value)
    {
        $this->data[$name] = $value;
    }
    public function __get($name)
    {
       if($this->data[$name]){
           return $this->data[$name];
       }
       echo '变量' . $name . '不存在';
    }
}

$model = new test();
$model->name = 'liuser';
echo $model->name;

10. final

定义:如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。
这个很好了解,就不展开了,具体可以点开下面的链接

http://php.net/manual/zh/language.oop5.final.php

11.clone(克隆)

使用clone克隆一个对象,会把其进行浅复制。有一些特点
1.因为是浅复制,所以对象的引用还是会指向对象之前的引用。
2.如果对象定义了__clone魔术方法,则在复制完成后会执行__clone
3.如果类需要定义为单例模式时,则需要防止被克隆,在__clone 方法写入exit。

    class c1
{
    public $instance = 0;

    public function __clone()
    {
        $this->instance  = $this->instance +1 ;
    }
    public function say()
    {
        echo 'hello';
    }
}

$m1 = new c1();
$m2 = clone $m1;


echo $m1->instance;
echo $m2->instance;
输出: 0 1 

12.类型约束

在函数的参数执行参数所属的类型,包括对象、数组、接口

class c1
{

    public function say(){}
}

class c2
{
    #指定必须为c1的一个对象
    public function f1(c1 $object)
    {
        $object->say();
    }
    #指定必须为一个数组
    public function f2(array $list)
    {
        return $list['k1'];
    }
    #指定必须为一个回调函数
    public function call(callable $call,$data)
    {
        call_user_func($call,$data);
    }
}

13.对象与引用

PHP 的引用是别名,就是两个不同的变量名字指向相同的内容。在 PHP 5,一个对象变量已经不再保存整个对象的值。只是保存一个标识符来访问真正的对象内容。 当对象作为参数传递,作为结果返回,或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容

class A {
    public $foo = 1;
}

$a = new A;
# $a ,$b都是同一个标识符的拷贝
$b = $a;    
$b->foo = 2;
echo $a->foo."\n";

#结果等于2

14.序列化对象

所有php里面的值都可以使用函数serialize()来返回一个包含字节流的字符串来表示。unserialize()函数能够重新把字符串变回php原来的值。 序列化一个对象将会保存对象的所有变量,但是不会保存对象的方法,只会保存类的名字。

注意:如果反序列化,然后调用对象中的方法,则在之前,必须引入该类.
虽然在序列化的时候,不会保存对象的方法,但是在反序列化后,依然可以使用对象中的方法。

class A {
    public $foo = 1;
    public function say()
    {
        echo 'hello';
    }
}

$a = new A();
$s_a = serialize($a);
#结果 : O:1:"A":1:{s:3:"foo";i:1;}

$b = unserialize($s_a);
var_dump($b);
#object(A)#2 (1) {
#["foo"]=>
#  int(1)
#}
$b->say();
#结果 hello
echo $b->foo;
#结果  1