类是对象的抽象,对象是类的实例
提到面向对象,就会想起来这句话,印象中似乎大学考过不止一次,连我这个学渣都印象深刻
类的定义
使用class
关键字对类进行声明1
2
3
4
5
6
7
8
9
10
11
12//声明定义类
class Task{
public $color = 'red';
public function say(){
echo 'hello';
}
}
//创建类的实例对象
$task = new Task();
echo $task->$color; //red
$task->say(); //hello
构造函数与析构函数
- 构造函数
void __construct ([ mixed $args [, $... ]] )
PHP 5 允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类在实例化对象时会调用这个方法。1
2
3
4
5
6
7
8
9
10class Task{
public $color;
public function __construct($color){
$this->color = $color;
}
}
//创建类的实例对象
$task = new Task('red');
var_dump($task->$color); //red
- 析构函数
void __destruct ( void )
PHP 5 引入了析构函数的概念,析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。
getter/setter
getter/setter提供了一些属性读取的封装,可以让代码更便捷,使用方法限制对数据的随意赋值1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class Task{
public $age;
public function setAge($age){
if($age < 18){
throw new Exception('not old enough');
}
$this->age = $age;
}
public function getAge(){
return $this->age;
}
}
$task = new Task();
$task->setAge(30);
var_dump($task->getAge()); //30
这里对age赋值小于18时就会报错,但是这样的封装还不够彻底,使用$task->$age
可以不通过setter直接赋值,所以就需要用到类的封装
类的封装
- public 公有,可以直接读取,修改和继承
- private 私有,只能在类的内部访问到,直接读取会出错,不可以继承
- protected 被保护,只能在类的内部访问到,直接读取会出错,可以继承
1 | class Task{ |
只能通过类内部的方法获取private和protected的属性
类的继承
使用extends
关键字,子类可以继承到所有的public和protected的属性和方法1
2
3
4
5
6
7
8
9
10
11
12
13
14class Mother{
protected function getEyesCount(){
return 2;
}
}
class Child extends Mother{
public function getEyes(){
return $this->getEyesCount();
}
}
$child = new Child();
var_dump($child->getEyes()); //2
抽象类
- 抽象类只能用来继承,不能直接调用
- 抽象类中的抽象方法,所有继承它的子类都必须定义,否则会报错
1 | abstract class Shape{ |
对象接口
- 使用接口,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容
- 接口是通过
interface
关键字来定义的,类通过implements
关键字调用接口 - 接口中定义的所有方法都必须是公有,这是接口的特性
- 接口也可以继承,通过使用
extends
操作符
1 | interface Logger{ |
接口中的定义的方法都是空的,跟抽象类一样,调用接口的类中必须定义这个方法,不然就会报错
这里需要补充一个php函数依赖注入的概念
1 | class C {} |
参数可以通过加类名来限制,只有这个类的以及继承了这个类的才有效,接口名同理,厉害了我的php
命名空间
在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:
- 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
- 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
1 | //文件1 |
静态属性和静态方法
使用static
关键字定义静态属性和静态方法1
2
3
4
5
6
7class Math
{
public static function add(...$num){
return array_sum($num);
}
}
echo Math::add(1,2,3);
调用静态方法时,可以不将类实例化为对象,直接调用方法1
2
3
4
5
6
7
8
9
10
11
12
13class Person{
public static $age = 1;
public function run(){
self::$age ++ ;
}
}
$jack = new Person();
$jack->run();
echo Person::$age; //2
$jane = new Person();
$jane->run();
echo Person::$age; //3
定义静态属性后,在类中使用self
关键字调用
重新实例化person类并调用方法后,静态属性值并没有重置,说明静态属性的值与实例化对象无关,而是跟类相关,使用时需要注意
常量
使用const
关键字声明,在任何地方都不能改变,调用方式和静态属性相同1
2
3
4class Task{
const num = 10;
}
echo Task::num;
Trait
php是单继承的语言,在PHP 5.4 Traits出现之前,PHP的类无法同时从两个基类继承属性或方法。php的Traits通过在类中使用use关键字声明要组合的Trait名称,而具体某个Trait的声明使用trait关键词,Trait不能直接实例化1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21trait Drive {
public $carName = 'trait';
public function driving() {
echo "driving {$this->carName}\n";
}
}
class Person {
public function eat() {
echo "eat\n";
}
}
class Student extends Person {
use Drive;
public function study() {
echo "study\n";
}
}
$student = new Student();
$student->study(); //study
$student->eat(); //eat
$student->driving(); //driving trait
Student类通过继承Person,有了eat方法,通过组合Drive,有了driving方法和属性,实现了多继承
trait还有以下特点
- 当方法或属性同名时,当前类中的方法会覆盖 trait的 方法,而 trait 的方法又覆盖了基类中的方法
- Trait 也能组合Trait,Trait中支持抽象方法、静态属性及静态方法