php备忘录(四)之面向对象

  • A+
所属分类:php

主要包含以下几点:

(1)类和对象

  •  − 定义了一件事物的抽象特点。类的定义包含了数据的形式以及对数据的操作。

一个类可以包含有属于自己的常量变量(称为“属性”)以及函数(称为“方法”)。

声明一个类,使用class关键字。变量 $this 代表自身的对象。$this只能引用非静态的变量和方法。

class Site
{
	/* 成员变量 */
	var $url;
	var $title;

	/* 成员函数 */
	function setUrl($par)
	{
		$this->url = $par;
	}

	function getUrl(){
		echo $this->url . PHP_EOL;   //PHP_EOL 为换行符
	}

	function setTitle($par){
		$this->title = $par;
	}

	function getTitle(){
		echo $this->title . PHP_EOL;
	}
}

 

  • 对象 − 是类的实例。

使用 new 运算符来实例化该类的对象。要创建一个类的实例,必须使用 new 关键字。

如果该类属于一个名字空间,则必须使用其完整名称。

如针对上面的class进行实例化。

$php = new Site(); 
$taobao = new Site(); 
$google = new Site(); 

// 调用成员函数,设置标题和URL 
$php->setTitle( "php中文网" ); 
$taobao->setTitle( "淘宝" ); 
$google->setTitle( "Google 搜索" ); 

$php->setUrl( 'www.php.cn' ); 
$taobao->setUrl( 'www.taobao.com' ); 
$google->setUrl( 'www.google.com' ); 

// 调用成员函数,获取标题和URL 
$php->getTitle(); 
$taobao->getTitle(); 
$google->getTitle(); 

$php->getUrl(); 
$taobao->getUrl(); 
$google->getUrl(); 

说明:自 PHP 5.5 起,关键词 class 也可用于类名的解析。使用 ClassName::class 你可以获取一个字符串,包含了类 ClassName 的完全限定名称。这对使用了 命名空间 的类尤其有用。

echo Site::class;   //输出: 类 Site的完全名称(即包括命名空间)

(2)属性

类的变量成员叫做“属性”。属性声明是由关键字 publicprotected或者 private 开头,然后跟一个普通的变量声明来组成。属性中的变量可以初始化,但是初始化的值必须是常数,这里的常数是指 PHP 脚本在编译阶段时就可以得到其值,而不依赖于运行时的信息才能求值。

class SimpleClass
{
    // 错误的属性声明(因为必须在运行时,才能得到值)
    public $var1 = 'hello ' . 'world';
    public $var2 = 1+2;

    // 正确的属性声明(在编译时就可以得到值)
    public $var3 = array(true, false);
}

在类的成员方法里面,访问属性。

访问非静态属性:$this->property(其中 property 是该属性名)

访问静态属性:self::$property

(3)类常量

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

常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。

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

class MyClass
{
    const constant = 'constant value';

    function showConstant() {
        echo  self::constant;  //类的成员函数,访问常量
    }
}

echo MyClass::constant;  //类的外部访问常量

$classname = "MyClass";
echo $classname::constant; // 自 5.3.0 起

$class = new MyClass();
echo $class::constant."\n"; // 自 PHP 5.3.0 起

(4)类的自动加载

在一个php文件中,引用其他php文件的class,需要使用Inclue把类文件加载进来。这会带来一个烦恼:每个脚本的开头,都需要包含(include)一个长长的列表(每个类都有个文件)。在 PHP 5 中,已经不再需要这样了。 spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。

<?php
spl_autoload_register(function ($class_name) {
    require_once $class_name . '.php';
});

//当使用MyClass1 和 MyClass2时,没有定义这两个类,则会自动调用spl_autoload_register函数进行加载
$obj  = new MyClass1();
$obj2 = new MyClass2();
?>

 

(5)构造函数和析构函数

  • 构造函数 − 主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。

构造函数 ,是一种特殊的方法。主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中。PHP 5 允行开发者在一个类中定义一个方法作为构造函数。

Note如果子类中定义了构造函数则不会隐式调用其父类的构造函数。要执行父类的构造函数,需要在子类的构造函数中调用 parent::__construct()。如果子类没有定义构造函数则会如同一个普通的类方法一样从父类继承(假如没有被定义为 private 的话)

	function __construct( $par1, $par2 ) 
        {
		$this->url = $par1;
		$this->title = $par2;
	}

 

  • 析构函数 − 析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做"清理善后" 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。

和构造函数一样,父类的析构函数不会被引擎暗中调用。要执行父类的析构函数,必须在子类的析构函数体中显式调用 parent::__destruct()。此外也和构造函数一样,子类如果自己没有定义析构函数则会继承父类的。

Note:

试图在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。

PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,其语法格式如下:

void __destruct ( void )
class MyDestructableClass 
{
	function __construct() 
	{
		print "构造函数\n";
		$this->name = "MyDestructableClass";
	}

	function __destruct() 
	{
		print "销毁 " . $this->name . "\n";
	}
}

$obj = new MyDestructableClass();
//输出:
构造函数
销毁 MyDestructableClass

 

(6)访问控制

访问控制,即类中变量和函数的作用域。

PHP 对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。

  • public(公有):公有的类成员,可以在任何地方被访问。
  • protected(受保护):受保护的类成员,可以被自身类和子类、父类访问。
  • private(私有):私有的类成员,只能被自身类访问。

类属性必须定义为公有,受保护,私有之一。如果用 var 定义,则被视为公有。

类中的方法可以被定义为公有,私有或受保护。如果没有设置这些关键字,则该方法默认为公有。

其它对象的访问控制

同一个类的对象即使不是同一个实例也可以互相访问对方的私有与受保护成员。这是由于在这些对象的内部具体实现的细节都是已知的。

<?php
class Test
{
    private $foo;

    public function __construct($foo)
    {
        $this->foo = $foo;
    }

    private function bar()
    {
        echo '这是个私有方法bar';
    }

    public function baz(Test $other)
    {
        //这里可以为$other的私有变量赋值。因为,虽然在$other外部,但在本对象的内部。
        $other->foo = 'hello';
        echo $other->foo;

        //这里可以访问$other的内部函数,原因同上。
        $other->bar();
    }
}

$test = new Test('test');  //输出: hello
$test->baz(new Test('other')); //输出:这是个私有方法bar
?>

 

(7)继承、父类、子类

  • 继承 − 继承性是子类自动共享父类数据结构和方法的机制,这是类之间的一种关系。在定义和实现一个类的时候,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,并加入若干新的内容。

PHP 使用关键字 extends 来继承一个类,PHP不支持多重继承,一个类只能继承一个基类。格式如下:

class Child extends Parent 
{
   // 代码部分
}
// 子类扩展站点类别
class Child_Site extends Site 
{
	var $category;

	function setCate($par)
	{
		$this->category = $par;
	}

	function getCate()
	{
		echo $this->category . PHP_EOL;
	}
}
  • 父类 − 一个类被其他类继承,可将该类称为父类,或基类,或超类。
  • 子类 − 一个类继承其他类称为子类,也可称为派生类。

在这里Site 是父类,Child_Site是子类。

(8)

范围解析操作符 (::)

可以用于访问静态成员,类常量,还可以用于覆盖类中的属性和方法。

如下,访问类常量:

<?php
class MyClass {
    const CONST_VALUE = 'A constant value';
}

$classname = 'MyClass';
echo $classname::CONST_VALUE; // 自 PHP 5.3.0 起

echo MyClass::CONST_VALUE;
?>

selfparent 和 static 这三个特殊的关键字是用于在类定义的内部对其属性或方法进行访问的。

<?php
class OtherClass extends MyClass
{
    public static $my_static = 'static var';

    public static function doubleColon() {
        echo parent::CONST_VALUE . "\n";
        echo self::$my_static . "\n";
    }
}

$classname = 'OtherClass';
echo $classname::doubleColon(); // 自 PHP 5.3.0 起
OtherClass::doubleColon();
?>

(9)static关键字

声明类属性或方法为静态,就可以不实例化类而直接访问。静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)。

由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。

静态属性不可以由对象通过 -> 操作符来访问。

就像其它所有的 PHP 静态变量一样,静态属性只能被初始化为文字或常量,不能使用表达式。所以可以把静态属性初始化为整数或数组,但不能初始化为另一个变量或函数返回值,也不能指向一个对象。

(10)抽象类

定义为抽象的类不能被实例化。任何一个类,如果它里面至少有一个方法是被声明为抽象的,那么这个类就必须被声明为抽象的。

继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。例如,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。

(11)接口

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

接口是通过 interface 关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。

接口中定义的所有方法都必须是公有,这是接口的特性。

要实现一个接口,使用 implements 操作符。类中必须实现接口中定义的所有方法,否则会报一个致命错误。类可以实现多个接口,用逗号来分隔多个接口的名称。(实现多个接口时,接口中的方法不能有重名。)

(12)对类属性的遍历

方法一,使用foreach 对当前可见的属性进行遍历。如:

class MyClass
{
    public $var1 = 'value 1';
    public $var2 = 'value 2';
    public $var3 = 'value 3';

    protected $protected = 'protected var';
    private   $private   = 'private var';

    function iterateVisible() {
       echo "内部的方法:";
       foreach($this as $key => $value) 
       {
           print "$key => $value";
       }
    }
}

$class = new MyClass();

//这里在外部环境,会打印所有public属性:
//var1 => value 1 
//var2 => value 2 
//var3 => value 3
foreach($class as $key => $value) 
{
    print "$key => $value\n";
}

//这里会调用内部的foreach,会打印所有属性()
//var1 => value 1
//var2 => value 2
//var3 => value 3
//protected => protected var
//private => private var
$class->iterateVisible();

 

(13)Final关键字

PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承。

属性不能被定义为 final,只有类和方法才能被定义为 final。

(14)对象比较

使用比较运算符(==)比较两个对象变量时,比较的原则是:如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。

而如果使用全等运算符(===),这两个对象变量一定要指向某个类的同一个实例(即同一个对象)。

(15) 方法的重写

子类会继承父类的方法和属性(public和protected)。被继承的方法和属性可以通过用同样的名字重新声明被覆盖。但是如果父类定义方法时使用了 final,则该方法不可被覆盖。可以通过 parent:: 来访问被覆盖的方法或属性。

当覆盖方法时,参数必须保持一致否则 PHP 将发出 E_STRICT 级别的错误信息。但构造函数例外,构造函数可在被覆盖时使用不同的参数。

重写即 父类中已经有了该函数,但是不满足子类的使用。在子类中又重写了该函数(函数名和参数必须全部一样),覆盖了父类的函数。

class Site
{
    public function show_class_info()
    {
        echo "This is Site class.";
    }
}

class Taobao extends Site
{
    //覆盖了父类中的该函数
    public function show_class_info()
    {
        echo "This is Taobao class.";
    }
}

$tb = new Taobao();
$tb->show_class_info(); //输出:This is Taobao class.

 

 

(后续了解)trait / 匿名类 /重载(与java不同) /魔方方法 / 对象复制 /类型约束/后期静态绑定/对象和引用/对象序列化

PHP 将所有以 __(两个下划线)开头的类方法保留为魔术方法。所以在定义类方法时,除了上述魔术方法,建议不要以 __ 为前缀。



  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: