浅谈PHP类与对象

面向对象编程(OOP)是一种计算机编程架构,PHP是一种弱类型的编程语言,可以使用OOP,也可以使用过程化编程。
类:是具有相同属性和服务的一组对象的集合。
对象:用来描述客观事物的一个实体,是类的实例化。

访问控制

类中的属性和服务是具有访问控制权限的,通过添加关键字public(公有)、protected(受保护的)、private(私有)来实现的。
被定义为公有的成员可以在任何地方被访问;被定义为受保护的成员可以被其自身以及其子类和父类访问;被定义为私有的成员则只能被其定义所在的类访问。

构造函数与析构函数

类中的构造函数在每次创建新对象时自动调用,所以非常适合做一些初始化的工作。构造函数能够接收用户传递的参数,即在新建对象时传递给对象的参数。
析构函数则相反,它在对象被销毁之前调用,且不接受任何参数。
构造函数与析构函数都是public属性。

<?php

class A {
    public function __construct($name){
        echo "Hello,".$name.".\n";
    }

    public function __destruct(){
        echo "Goodbye\n";
    }
}

$a = new A("Roshan");

上例代码展示了一个类中的构造与析构函数,运行后会输出两行信息:

[root@localhost class]# php A.php 
Hello,Roshan.
Goodbye

在新建对象时传递了参数,构造函数接收参数并输出相关信息;在对象销毁前调用了析构函数,输出‘Goodbye’信息。

Static关键字

Static用来声明类的属性或方法为静态,可以不实例化类而直接访问。
正常声明的属性或方法在类中调用时,使用‘$this’;而静态属性或方法在类中调用时使用‘self::’。

<?php

class A {
    static $city;

    public function __construct($name){
        echo "Hello,".$name.".\n";
    }

    public static function showCity(){
        self::$city = 'BeiJing';
        echo "Welcome to ".self::$city."\n";
    }

    public function __destruct(){
        echo "Goodbye\n";
    }
}

A::showCity();

调用类中的static类型属性或方法,不需要新建对象,使用“类名::方法名”来进行调用。如果使用新建对象的方法调用static类型,会报一个Notice级的错误。

__toString

__toString方法用于一个类被当成字符串时的回应,此方法必须返回一个字符串,否则会发生致命错误。

<?php

class A {
    static $city;

    public function __construct(){
        echo "Hello.\n";
    }

    public function __toString(){
        return "Don't echo me!\n";
    }

    public static function showCity(){
        self::$city = 'BeiJing';
        echo "Welcome to ".self::$city."\n";
    }

    public function __destruct(){
        echo "Goodbye\n";
    }
}

$a = new A();
echo $a;

输出:

[root@localhost class]# php A.php 
Hello.
Don't echo me!
Goodbye

自动加载类

在脚本开关写一个长长的包含文件的列表,是不是很烦?没关系,可以使用自动加载类的方法来解决这个问题。
定义一个__autoload()函数,它会在试图使用尚未被定义的类时自动加载该类。通过调用此函数,脚本引擎在PHP出错失败前有了最后一个机会加载所需的类。

<?php

function __autoload($className){
    require_once $className.".php";
}

A::showCity();

在脚本B.php中有如上代码,当调用A类中的showCity()方法时,PHP发现并没有加载A类,此时会调用__autoload()方法来进行自动加载后再运行。
注意:使用__autoload()方法需要类名与文件名相匹配才能自动加载。

操作private属性

private属性为私有的属性,按权限控制来讲,只允许定义它的类可以对它进行操作,对外是不可见的。但是如果想要在类的外部操作类中的private属性,也不是没有办法。
设置与获取
通过__set()和__get()方法可以设置和获取类中的private属性。
在对private属性时,__set()方法会被调用。__set()方法接收两个参数,第一个参数表示属性的名称,第二个参数表示属性的值。
当读取private属性时,__get()方法会被调用。__get()方法接收一个参数,表示属性的名称。

<?php

class A {
    private $car_type;
    private $car_age;

    public function __set($name,$value){
        $this->$name = $value;
    }
    public function __get($name){
        return $this->$name;
    }
}

$a = new A();
$a->car_type = "BMW";
echo $a->car_type;

检测与重置
__isset()方法用来检测private属性是否设置,以及是否为空,当对private属性使用isset()或empty()时会被调用。它接收一个参数,为属性的名称。
__unset()方法用来重置private属性,当对private属性使用unset()时会被调用,它接收一个参数,为属性的名称。

<?php

class A {
    private $car_type;
    private $car_age;

    public function __set($name,$value){
        $this->$name = $value;
    }
    public function __get($name){
        return $this->$name;
    }

    public function __isset($name){
        return isset($this->$name);
    }
    public function __unset($name){
        unset($this->$name);
    }
}

$a = new A();
$a->car_type = "BMW";
echo isset($a->car_type)?"car_type is set\n":"car_type not set\n";
echo $a->car_type;

方法重载

有时写代码的过程中可能会将调用的某个类的方法名写错,这时就会出现致命错误,如果不想报PHP的错误,那应该怎么办呢?使用__call()方法或__callStatic()方法。
__call()方法用于在对象中调用一个不可用的方法时被调用。
__callStatic方法,用静态方式调用一个不可用的方法时被调用,需要定义为static类型。
__call()方法和__callStatic()方法都是接收两个参数,分别为方法名和参数数组。

<?php

class A {
    public function __call($name,$argu){
        echo "Call object method:$name,arguments(".implode(',',$argu).")\n";
    }

    public static function __callStatic($name,$argu){
        echo "Call object method:$name,arguments(".implode(',',$argu).")\n";
    }
}

$a = new A();
$a->b('Roshan','Happy new Year');
A::c('qwrd',10);

final关键字

final关键字用于类和方法的声明,不能用于属性的声明。
如果父类中的方法被声明为final,则子类不可以重写该方法。
如果一个类被声明为final,则不能被继承。

<?php

class A {
    final public function funB(){
        echo "Class A function funB";
    }
}

class B extends A {
    public function funB(){
        echo "Class B function funB";
    }
}

运行结果:

[root@localhost class]# php A.php 
PHP Fatal error:  Cannot override final method A::funB()

以final声明的类:

<?php

final class A {
    public function funB(){
        echo "Class A function funB";
    }
}

class B extends A {
    public function funB(){
        echo "Class B function funB";
    }
}

运行结果:

[root@localhost class]# php A.php 
PHP Fatal error:  Class B may not inherit from final class (A)

抽象类

抽象类使用abstract关键字来声明,抽象类不能被实例化。一个类中至少有一个方法被声明为抽象的,那这个类就必须声明为抽象的。
继承一个抽象类时,子类必须定义父类中的所有抽象方法,同时,这些方法的访问控制权限必须与父类中的相同或更加宽松。

<?php

abstract class A {
    abstract protected function funB($arg1,$arg2);

    public function sayHi(){
        echo "HI";
    }   
}

class B extends A {
    public function funB($arg1,$arg2){
        echo "ARG1:$arg1,ARG2:$arg2";
    }
}

对象接口

对象接口使用interface关键字定义,接口中定义的方法都是空的,它只是指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
接口中定义的所有方法都必须是公有的,这是接口的特性。接口也可以继承,使用extends操作符。
要实现接口,使用implements操作符,类可以实现多个接口,使用逗号分隔。在实现多个接口时,接口中的方法不能重名。类实现接口中的方法,必须使用和接口中所定义的方法完全一致的方式。

<?php

interface A {
    public function funA($name,$age);
    public function funB($city);
}

class B implements A {
    public function funA($name,$age){

    }

    public function funB($city){

    }
}

序列化

PHP中的值都可以使用serialize()函数来返回一个包含字节流的字符串来表示。而unserialize()函数则是重新把字符串变回PHP原来的值。
序列化对象保存的是类的名字。

<?php

class A {
    public function sayHi(){
        echo "HI\n";
    }
}

$a = new A();
$s = serialize($a);
$b = unserialize($s);
$b->sayHi();

克隆clone

使用clone关键字可以复制对象,当对象被复制后,PHP对对象的所有属性执行一个浅复制(shallow copy),所有的引用属性仍然会是一个指向原来变量的引用。

<?php

class A {
    public function sayHi(){
        echo "HI\n";
    }
}

$a = new A();
$b = clone $a;
$b->sayHi();

如果clone的目标对象带有变量,那clone后的新对象也带有同样的变量。
例如:

<?php

class A {
    var $name,$age;
    public function __construct($arg1='',$arg2=''){
        $this->name = $arg1;
        $this->age = $arg2;
    }
}

$a = new A("Roshan",18);
$b = clone $a;
echo $b->name;

__sleep

__sleep这个魔术方法有2个用处,一个是对工程师而言用来保存序列化后的数据;另一个是相对于安全而言,用来隐藏类中属性的具体信息,以免被外人获取。
在正常的情况下,如果序列化一个类对象后,是如下的场景:

<?php
class t {
    public $val;
    public $count;
    public function __construct($w){
        $this->val = $w; 
    }
}

$a = new t('ss');
echo serialize($a);

输出如下:

O:1:"t":2:{s:3:"val";s:2:"ss";s:5:"count";N;}

可以看到,类中的属性在序列化后都被输出了。下面来看看__sleep的作用:

<?php
class t {
    public $val;
    public $count;
    public function __construct($w){
        $this->val = $w; 
    }

    public function __sleep(){
        $this->count = 1;
        return array('count');
    }

}

$a = new t('ss');
echo serialize($a);

输出是这样的:

O:1:"t":1:{s:5:"count";i:1;}

可以看到,__sleep可以控制序列化类对象时的类属性信息,只序列化想要看到的属性而隐藏不想被看到的属性。

__wakeup

__wakeup用来控制反序列化后的动作,即unserialize之后的操作:

<?php
class t {
    public $val;
    public $count;
    public function __construct($w){
        $this->val = $w; 
    }

    public function __sleep(){
        $this->count = 1;
        return array('count');
    }

    public function __wakeup(){
        echo '__wakeup()';
    }

}

$a = new t('ss');
unserialize(serialize($a));

输出如下:

__wakeup()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值