PHP基础参考10-类与对象语法
目录
- final
- var
- const常量
- 类自动加载
- 构造函数与析构函数
- 范围解析操作符
- 关于抽象类和接口
- Trait
- 匿名类
- 魔术方法
- clone
- static::后期静态绑定
- 对象与引用及传址赋值与引用赋值
很多人都停留在php是过程语言,不能面向对象,php已经8了,在5就已经逐步与面向对象接轨了。
PHP 具有完整的对象模型。特性包括: 访问控制,抽象类和 final 类与方法,附加的魔术方法,接口,对象复制。
对于面向对象的基本知识,不需要我们介绍了,大家都懂。
final
被继承的方法和属性可以通过用同样的名字重新声明被覆盖。但是如果父类定义方法时使用了 final,则该方法不可被覆盖。可以通过 parent:: 来访问被覆盖的方法或属性。
注意: 属性不能被定义为 final,只有类和方法才能被定义为 final。
基本的知识,但很少人能用上,也许是场景不需要用到,但也许哪天就真香,时刻牢记。
var
虽然有些版本允许使用var定义属性,会被视为public,但我们不建议使用。
const常量
常量的值必须是一个定值,不能是变量,类属性,数学运算的结果或函数调用。
要区别于外部const/define定义的常量。
PHP 7.1.0 开始,类的常量可以定义为公有、私有或受保护。如果没有设置这些关键字,则该常量默认为公有,也就是之前的版本只能使用const关键字定义常量,默认这些常量是公开可访问的,7.1之后允许public、protect、private的访问控制关键字。
类自动加载
现代php框架都引入自动加载机制了,特别是composer第三方库使用。
spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。
不建议使用__autoload函数,后续版本会被弃用。
spl_autoload_register函数用于注册触发加载类,提供回调加载,达到按需加载。
spl_autoload_register(function ($class_name) {
require_once $class_name . '.php';
});
$obj = new MyClass1();
$obj2 = new MyClass2();
构造函数与析构函数
-
子类的构造函数不会默认或隐性的调用父类构造函数,有需要时,要手动调用父类构造函数。
-
析构函数即使在使用 exit() 终止脚本运行时也会被调用。但在析构函数中调用 exit() 将会中止其余关闭操作的运行。
-
在析构函数(在脚本终止时被调用)中抛出一个异常会导致致命错误。
范围解析操作符
范围解析操作符 ::
只是认识下这个符号叫:范围解析操作符
在访问类常量、静态属性、静态方法,我们常会用到双冒号,也就是范围解析操作符。
关于抽象类和接口
如果对抽象类和接口有不清楚的可以看官方文档介绍
Trait
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。
Trait 和 Class 相似,但仅仅旨在用细粒度和一致的方式来组合功能。 无法通过 trait 自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用的几个 Class 之间不需要继承。
-
优先顺序是当前类中的方法会覆盖 trait 方法,而 trait 方法又覆盖了基类中的方法。
class Base { public function sayHello() { echo 'Hello '; } } trait HelloWorld { public function sayHello() { echo 'Hello World!'; } } class MyHelloWorld extends Base { use HelloWorld; } $o = new MyHelloWorld(); $o->sayHello(); //输出:Hello World! //因为trait覆盖了基类的方法 class TheWorldIsNotEnough { use HelloWorld; public function sayHello() { echo 'Hello Universe!'; } } $o = new TheWorldIsNotEnough(); $o->sayHello(); //输出:Hello Universe! //因为当前类覆盖trait的方法
-
一个类中支持use多个trait,用逗号隔开
-
方法冲突解决
如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。
为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。
以上方式仅允许排除掉其它方法,as 操作符可以 为某个方法引入别名。 注意,as 操作符不会对方法进行重命名,也不会影响其方法。
use A, B { B::smallTalk insteadof A;//相当于排除A中的smallTalk A::bigTalk insteadof B; B::bigTalk as talk;//定义了talk别名 }
-
修改use trait中的方法访问控制
直接看范例:
// 修改 sayHello 的访问控制 class MyClass1 { use HelloWorld { sayHello as protected; } } // 给方法一个改变了访问控制的别名 // 原版 sayHello 的访问控制则没有发生变化 class MyClass2 { use HelloWorld { sayHello as private myPrivateHello; } }
-
trait组合
trait和class一样也可以组合其他多个trait。
-
trait支持抽象方法,用于强制实体类实现trait的抽象方法
-
trait支持静态方法
-
trait支持属性,但注意冲突,一般是不能定义同样名称的属性。我们建议避免trait和class出现相同名称的属性,即使访问可见性、初始值都一样(允许的),我也不建议,会导致混乱。
匿名类
php7开始支持匿名类,用于创建一次性简单对象。
$util->setLogger(new class {
public function log($msg)
{
echo $msg;
}
});
//也就是不需要先定义一个logger的对象,再new给setLogger方法。
魔术方法
PHP所提供的重载(overloading)是指动态地创建类属性和方法。我们是通过魔术方法(magic methods)来实现的。
PHP中的重载与其它绝大多数面向对象语言不同。传统的重载是用于提供多个同名的类方法,但各方法的参数类型和个数不同。
clone
对象复制可以通过 clone 关键字来完成(如果可能,这将调用对象的 __clone() 方法)。
当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。如果有少量及简单的引用属性,可以在魔术方法__clone中手动实现引用属性的复制。
class AA{
public $object1;
function __clone()
{
// 强制复制一份this->object, 否则仍然指向同一个对象
$this->object1 = clone $this->object1;
}
}
$a = new AA();
$a2 = clone $a;//通过__clone方法,达到对$a对象的深拷贝。
static::后期静态绑定
self、parent、static
我们都知道parent的用法,就是从当前类(子类)往上追溯基类的存在的这个方法;static则是从当前类(父类、基类)往下追溯至当前执行实例(子类)中存在的方法;
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // 后期静态绑定从这里开始,这里会执行调用test方法的实例的who方法,也就是B类实例的who方法。
self::who();//这里只会调用当前类中的who方法,如果有的话,也就是当前代码行所在类A
parent::who();
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();//输出:BA
对象与引用及传址赋值与引用赋值
在php5 的对象编程经常提到的一个关键点是“默认情况下对象是通过引用传递的”。但其实这不是完全正确的。
PHP 的引用是别名,就是两个不同的变量名字指向相同的内容。
在 PHP 5以后,一个对象变量已经不再保存整个对象的值。而是保存一个标识符来访问真正的对象内容。
当对象作为参数传递,作为结果返回,或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。
这标识符我们可以理解为存储地址,所以php的对象正常情况下的复制、传递都是通过传址的方式,而不是引用,虽然效果是一样的。
class A {
public $foo = 1;
}
$a = new A;
$b = $a; // $a ,$b都是同一个标识符的拷贝,意味着a、b指向同一个地址
// ($a) = ($b) = <id>
$b->foo = 2;
echo $a->foo."\n";//输出:2
$c = new A;
$d = &$c; // $c ,$d是引用,d是c的一个别名,其实他们还是一样的
// ($c,$d) = <id>
$d->foo = 2;
echo $c->foo."\n";//输出:2
$e = new A;
function foo($obj) {//e和obj都是同一个标识符的拷贝
// ($obj) = ($e) = <id>
$obj->foo = 2;
}
foo($e);
echo $e->foo."\n";//输出:2