PHP学习笔记:面向对象

    类是对象的抽象,对象是类的实例
    提到面向对象,就会想起来这句话,印象中似乎大学考过不止一次,连我这个学渣都印象深刻

类的定义

使用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
10
class 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
15
class 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
2
3
4
5
6
7
8
9
10
class Task{
private $age = 20;

public function getAge(){
return $this->age;
}
}
$task = new Task();
var_dump($task->$age); //error
var_dump($task->getAge()); //20

只能通过类内部的方法获取private和protected的属性

类的继承

使用extends关键字,子类可以继承到所有的public和protected的属性和方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
abstract class Shape{
public $color = 'red';
abstract public function getArea();
}

class Square extends Shape{
protected $length = 5;
public function getArea(){
return pow($this->length,2);
}
}

class Circle extends Shape{ //报错,没有定义getArea方法

}

$shape = new Shape();
var_dump($shape->color); //error
$square = new Square();
var_dump($square->color); //red
var_dump($square->getArea()); //25

对象接口

  • 使用接口,可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容
  • 接口是通过interface关键字来定义的,类通过implements关键字调用接口
  • 接口中定义的所有方法都必须是公有,这是接口的特性
  • 接口也可以继承,通过使用extends操作符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
interface Logger{
public function save($message);
}

class FileLogger implements Logger{
public function save($message)
{
var_dump('login in file '.$message);
}
}

class DatabaseLogger implements Logger{
public function save($message)
{
var_dump('login in database '.$message);
}
}

class user{
protected $logger;
public function __construct(Logger $logger) //使用Logger代替类名
{
$this->logger = $logger;
}
public function register(){
$this->logger->save('jelly');
}
}

$user = new user(new FileLogger()); //更改这里的类的实例对象,就可以调用不同的save方法
$user->register();

接口中的定义的方法都是空的,跟抽象类一样,调用接口的类中必须定义这个方法,不然就会报错

这里需要补充一个php函数依赖注入的概念

1
2
3
4
5
6
7
8
9
10
11
class C {}
class D extends C {}
class E {}

function f(C $c) {
echo get_class($c)."\n"; //get_class 获取所在的类名
}

f(new C); //C
f(new D); //D
f(new E); //error

参数可以通过加类名来限制,只有这个类的以及继承了这个类的才有效,接口名同理,厉害了我的php

命名空间

在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题:

  1. 用户编写的代码与PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲突。
  2. 为很长的标识符名称(通常是为了缓解第一类问题而定义的)创建一个别名(或简短)的名称,提高源代码的可读性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//文件1
namespace app/test1

class Task{
public function __construct()
{
return 'hello';
}
}
//文件2
$task = new app/test1/Task();
# or
use app/test1
$task = new Task();

静态属性和静态方法

使用static关键字定义静态属性和静态方法

1
2
3
4
5
6
7
class 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
13
class 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
4
class 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
21
trait 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中支持抽象方法、静态属性及静态方法
文章目录
  1. 1. 类的定义
  2. 2. 构造函数与析构函数
  3. 3. getter/setter
  4. 4. 类的封装
  5. 5. 类的继承
  6. 6. 抽象类
  7. 7. 对象接口
  8. 8. 命名空间
  9. 9. 静态属性和静态方法
  10. 10. 常量
  11. 11. Trait
|