重读PHP文档之类与对象笔记

1、伪变量$this

在静态方法中不能使用伪变量。

2、instanceof 运算符

在PHP5中,通过方法传递变量的类型有不确定性。于是我们很难判断,一些操作是否可以运行。
使用instanceof运算符,可以判断当前实例是否可以有这样的一个形态。当前实例使用 instanceof与当前类,父类(向上无限追溯),已经实现的接口比较时,返回真。

class User{
  private $name;
  public function  getName(){
    return "UserName is ".$this->name;
  }
}

class NormalUser extends User {
  private $age = 99;
  public function getAge(){
    return "age is ".$this->age;
  }
}

class UserAdmin{ //操作.
  public static function  getUserInfo(User $_user){
    if($_user instanceof NormalUser ){
      echo $_user->getAge();
    }else{
      echo "类型不对,不能使用这个方法.";
    }
  }
}
$User = new User(); // 这里new的是User.
UserAdmin::getUserInfo($User);

3、访问控制

public(共有): 被定义为公有的类成员可以在任何地方被访问。

protected(受保护): 被定义为受保护的类成员可以被其自身和其子类和父类访问。

private(私有): 被定义为私有的类成员 只能被其所在类访问。

4、spl_autoload_register(自动加载类)

也可以使用__autoload函数来实现,不过spl_autoload_register函数更加灵活

bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )

将函数注册到SPL __autoload函数队列中。如果该队列中的函数尚未激活,则激活它们。它实际上创建了 autoload 函数的队列,按定义时的顺序逐个执行.

参数:

autoload_function:欲注册的自动装载函数。如果没有提供任何参数,则自动注册 autoload 的默认实现函数spl_autoload()。

throw:此参数设置了 autoload_function 无法成功注册时, spl_autoload_register()是否抛出异常。

perpend:如果是 true,spl_autoload_register() 会添加函数到队列之首,而不是队列尾部。

范例:

// function __autoload($class) {
//     include 'classes/' . $class . '.class.php';
// }

function my_autoloader($class) {
    include 'classes/' . $class . '.class.php';
}

spl_autoload_register('my_autoloader');

// 或者,自 PHP 5.3.0 起可以使用一个匿名函数
spl_autoload_register(function ($class) {
    include 'classes/' . $class . '.class.php';
});

5.构造函数和析构函数

构造函数

void __construct ([ mixed $args [, $... ]] )

具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作。

如果在子类中想调用父类的构造函数可以显示的调用parent::__construct(),如果子类中没有定义构造函数,则会继承父类的构造函数。

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

  • 构造函数可以接受参数,在创建对象时赋值给对象属性
  • 构造函数可以调用类方法或其他函数
  • 构造函数可以调用其他类的构造函数

析构函数

void __destruct ( void )

析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时自动执行。不能显示调用。

析构函数不能带参数。

通常以下几种情况会调用析构函数(但不绝对):

  • PHP页面加载完毕后。
  • unset()类
  • 变量引用指向别的变量或值时

范例

<?php
class test{
    function __destruct(){
        echo "当对象销毁时会调用!!!";
    }

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

$a = null;
unset($b);

echo "<hr />";

?>

201211072155156726

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

6、抽象类与接口

抽象类/abstract

抽象类不能被实例化,一个类中如果有一个方法被声明为抽象的,那么这个类必须要被声明为抽象的,被定义的抽象方法只是声明了其调用方法(参数),不能具体的功能实现。

继承一个抽象类的时候,子类必须实现所有的抽象方法,这些方法可以被定义为 私有,受保护,公有的。子类继承时访问控制应该与父类一致或者更宽松。

abstract class AbstractClass
{
    // 我们的抽象方法仅需要定义需要的参数
    abstract protected function prefixName($name);

}

class ConcreteClass extends AbstractClass
{

    // 我们的子类可以定义父类签名中不存在的可选参数
    public function prefixName($name, $separator = ".") {
        if ($name == "Pacman") {
            $prefix = "Mr";
        } elseif ($name == "Pacwoman") {
            $prefix = "Mrs";
        } else {
            $prefix = "";
        }
        return "{$prefix}{$separator} {$name}";
    }
}

$class = new ConcreteClass;
echo $class->prefixName("Pacman"), "\n";
echo $class->prefixName("Pacwoman"), "\n";

接口/interface

使用接口可以指定类必须实现哪些方法,但不需要实现这些方法的具体内容。

接口中定义的所有方法必须是公有的。

接口中可以定义常量,但不能被子类或子接口覆盖

interface a
{
    const b = 'Interface constant';
}

// 输出接口常量
echo a::b;

// 错误写法,因为常量不能被覆盖。接口常量的概念和类常量是一样的。
class b implements a
{
    const b = 'Class constant';
}

抽象类与接口

不同点

1、接口使用implements关键字实现,抽象类使用类继承关键字extends实现

2、接口没有数据成员,但抽象类有数据成员,抽象类可以实现数据封装

3、接口没有构造函数,抽象类可以有构造函数

4、接口中方法都是public,而抽象类的方法可以是private、protected或public

5、一个类可以实现多个接口,但只能实现一个抽象类

相同点

函数体内不能写任何东西,大括号也不能写,如 public function funname();

如何选择使用抽象类还是接口

如果要创建一个模型,这个模型将由一些紧密相关的对象采用,就可以使用抽象类。

如果要创建将由一些不相关对象采用的功能,就使用接口。

如果必须从多个来源继承行为,就使用接口。

如果知道所有类都会共享一个公共的行为实现,就使用抽象类,并在其中实现该行为。