面向对象介绍

介绍

面向对象是一个编程思想,编程思想有面向过程和面向对象

面向对象:编程思路集中的是在过程中

面向对象:编程思路集中在参与的对象

以去饭馆吃饭为例子:

面向过程:点菜—————上菜————吃饭————结账————收拾

面向对象:服务员、厨师、客人

面向对象的好处

  1. 多人合作方便
  2. 减少代码冗余、灵活性高
  3. 代码的可重复用性发挥到了极致
  4. 可扩展性强
多学一招:
OOP: Object-Oriented Program
OOA: Object-Oriented Analysis
OOD: Object-Oriented Designed

类和对象

  1. 对象是具体存在的事物,对象是由属性(变量)和 方法(函数)组成的
  2. 类是具有相同属性和行为的一组对象的集合(有相同变量和函数的一组集合 对象只是集合中的一个元素)

分析: 做菜动作——厨师对象——厨师类

结论: 我们在开发的时候---->先写类---->通过类创建对象---->然后调用对象的属性和方法实现功能

​ 类——对象——调用成员

注意:一个类可以创建多个对象

小结

  1. 对象是由属性和方法组成的
  2. 类是所有对象的相同属性和方法的集合
  3. 在开发的时候 先写类 通过类创建对象 然后调用对象的属性和方法实现功能
  4. 一个类可以创建多个对象

在PHP中实现类和对象

创建类

class 类名{
    // 属性
    // 方法
    // 常量
}
# 类成员有 属性 方法 常量

类名的命名规则:

  1. 以字母、下划线开头(不能用数字开头)
  2. 不能用PHP关键字
  3. 类名不区分大小写 (变量名区分 关键字 类名不区分大小写)
  4. 类名用帕斯卡命名法 (大驼峰 单词的首字母大写)(非必须)
<?php
class Student{

}
?>

对象实例化

通过new关键字来实例化对象

<?php
# 定义类
class Student{

}
# 实例化对象
$stu1 = new Student(); //括号写不写都行
$stu2 = new Student;   //偷懒写法
var_dump($stu1,$stu2);

#输出结果
#object(Student)#1 (0) {}
#object(Student)#2 (0) {}

// 这里注意虽然都是$stu1、$stu2 对象的实例化 但是他们的编号不一样 有先后顺序

对象比较

注意:对象的之间的赋值、或叫做对象之间的传递是地址的传递(简单来说 就是共用一份内存)

相等:结构和保存的值一样就相等

全等:指向同一个对象才是全等

<?php
# 定义类
class Student{

}
# 实例化对象
$stu1 = new Student();
$stu2 = new Student;
$stu3 = $stu2;                        // 对象传递默认的是地址 就是共享内存 就像一家人一样
//var_dump($stu1,$stu2,$stu3);       
// object(Student)#1 (0) {} object(Student)#2 (0) {} object(Student)#2 (0) {}
var_dump($stu2===$stu3);            // bool(true) 编号相同是同一个对象

var_dump($stu1==$stu2);      // bool(true) 比较对象的结构
var_dump($stu1===$stu2);    // bool(false) 比较对象是否是同一个 编号不相同 只是双胞胎但不是同一个人
?>

属性

属性的本质就是变量

通过->调用

对象的成员或对象名->属性名

对象名->方法名字

<?php
// 定义类
class Student{
    public $name;
    public $add = '地址不详';
}
// 实例化对象
$stu = new student(); //把这个对象赋值给$stu变量
print_r($stu);
/* Student Object
(
    [name] => 
    [add] => 地址不详
) */

// 给属性赋值
$stu->name = 'tom';
$stu->add = '北京';

// 获取属性的值
echo '姓名:'.$stu->name."\n";
echo '地址:'.$stu->add."\n";

/* 姓名:tom
   地址:北京 */

// 添加一个属性 只要添加了就是公有的
$stu->age = 20;
print_r($stu);
/*
Student Object
(
    [name] => tom
    [add] => 北京
    [age] => 20
)*/

// 删除属性
unset($stu->add);
print_r($stu);

/* Student Object
(
    [name] => tom
    [age] => 20
)*/

总结:

  1. 对公有属性的赋值:$stu->name = 'tom'; $stu->add = '北京';
  2. 添加公有属性:$stu->age = 20;
  3. 删除公有的属性:unset($stu->add);
<?php
class Student{
    public $name;
}
$stu1 = new Student;
$stu2 = new Student;
$stu1->name='tom';
var_dump($stu1==$stu2);         //bool(false)

这里为啥bool值是0呢?虽然结构相同 == 指的是相等

  1. 结构相同
  2. 值相等

明显结构相同 但是值发生了变化

方法

方法的本质就是函数

<?php
class Student{
    // 定义方法 public对于方法可以省略
    public function show(){
        echo "这是一个show方法"."\r\n";
    }
     function test(){
        echo "test方法";
    }
}

// 新建一个对象
$stu = new Student;
// 用对象调用方法 也就是这个函数
$stu->show();
$stu->test();

/* 这是一个show方法
test方法 */

tips: public对于方法可以省略 属性前面的public不能省略

访问修饰符

用来控制成员的访问权限

修饰符描述
public在类的内部和外部都能访问
private只能在内部访问
protected(受保护的)在整个继承链上访问

tips:一般来说 属性都是私有的 通过公有的方法对私有的属性进行赋值和取值

==作用:保证数据的合法性== 感觉就是出于安全性的考虑

<?php
class Student{
    private $name;  //私有属性
    private $sex;   //私有属性
    // 通过公有方法对私有属性进行赋值
    public function setinfo($name,$sex){
        if ($sex!='man' && $sex!='woman') {
            echo '性别必须是男或女';
            exit;
        }
        $this->name=$name;        //this 表示当前对象
        $this->sex=$sex;
    }
    /* 上面的setinfo()函数定义了一个公有方法 对姓名和性别的赋值 
        但是对性别进行了限制 用if来判断传进来的值
        在判断之后对实例化的 当前对象的属性进行了赋值
        $this->属性 = $变量
    */
    // 定义函数进行输出
    public function getinfo(){
        echo 'name:'. $this->name."\n";
        echo 'sex:'. $this->sex."\n";
    }
}

$stu = new Student;
$stu->setinfo("tom",'man');
$stu->getinfo();        //name:tom sex:man

$stu2 = new Student;
$stu->setinfo("berry",'woman');
$stu->getinfo();        //name:berry sex:woman

我们可以实验一下 传入非法参数会怎么样?

$stu3 = new Student;
$stu3->setinfo("hacker", 'hacker');
$stu3->getinfo();  //性别必须是男或女

类和对象在内存中的分布

  1. 对象的本质是一个复杂的变量
  2. 类的本质是一个自定义的复杂数据类型
  3. 栈区:运行速度快 体积小 保存基本类型
  4. 堆区:运行速度稍慢 体积大 保存复杂类型
  5. 实例化的过程就是分配内存空间的过程
  6. 对象保存在堆区 将堆区的地址保存在栈区

image-20220522143631785.png

封装

有选择性的提供数据

通过访问修饰符来实现封装

构造方法

介绍

构造方法也叫构造函数,当实例化一个对象的时候自动执行

function __construct(){

} 
// 注意前面是两个下划线

例题

<?php
class Student{
    // 和类名同名的方法是构造方法 在php中不建议这种写法
    public function __construct(){
        echo "这是构造方法"."\n";
    }
}
new Student;
new Student;

/* 这是构造方法
这是构造方法 */

构造方法的作用:初始化成员变量

析构方法

介绍

当对象销毁的时候自动调用

function __destruct{
    
}

tips:析构函数不可以带参数

例题

<?php
class Student{
    private $name;  //私有属性
    private $sex;   //私有属性
    // 公有的构造方法对私有属性赋值
    public function __construct($name){
        $this->name=$name;
        echo "{$this->name} is born \n";
    }
    // 析构方法
    public function __destruct(){
        echo "{$this->name} has died \n";
    }
}
$stu = new Student('tom');    //实例化对象的时候传入参数
$stu1 = new Student('berry');
$stu2 = new Student('jack');

输出结果:
tom is born 
berry is born 
jack is born 
jack has died 
berry has died 
tom has died 

计算机的内存管理


计算机内存的管理方式有:先进先出,先进后出

先进先出的内存管理方式一般用在业务逻辑中,比如秒杀、购票等等

管道里面排队的例子----》在一个管道里面 先进来的人先出去

先进后出是计算机的默认内存管理方式

茶杯排队的例子-----》在一个茶杯里面 先进来的人被压在最底下 动弹不得 得前面的人走光了才能出去


思考题1

<?php
class Student{
    private $name;  //私有属性
    private $sex;   //私有属性
    // 公有的构造方法对私有属性赋值
    public function __construct($name){
        $this->name=$name;
        echo "{$name} is born \n";
    }
    // 析构方法
    public function __destruct(){
        echo "{$this->name} has died \n";
    }
}
$stu = new Student('tom');
$stu1 = new Student('berry');
$stu2 = new Student('jack');
unset($stu1);

输出结果:
tom is born 
berry is born 
jack is born 
berry has died 
jack has died 
tom has died 

分析:

  1. 实例化了三个对象 依次是tom berry jack
  2. 按照茶杯排队的原则 最先实例化的对象是tom最后一个死亡
  3. 又因为手动销毁了berry 所以死亡顺序是 berry jack tom

思考题2

<?php
class Student{
    private $name;  //私有属性
    private $sex;   //私有属性
    // 构造方法
    public function __construct($name){
        $this->name=$name;
        echo "{$name} is born \n";
    }
    // 析构方法
    public function __destruct(){
        echo "{$this->name} has died \n";
    }
}
new Student('tom');
new Student('berry');
new Student('jack');
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
tom is born 
tom has died 
berry is born 
berry has died 
jack is born 
jack has died 

分析:

  1. 因为实例化对象没有赋值给变量?在内存中实例化就销毁了 好旧之前学的忘记了
  2. 一实例化马上就死掉了

思考题3

<?php
class Student{
    private $name;  //私有属性
    private $sex;   //私有属性
    // 构造方法
    public function __construct($name){
        $this->name=$name;
        echo "{$name} is born \n";
    }
    // 析构方法
    public function __destruct(){
        echo "{$this->name} has died \n";
    }
}
$stu = new Student('tom');
$stu = new Student('berry');
$stu = new Student('jack');
----------------------------------------------------------------------------------------------------------
tom is born 
berry is born 
tom has died 
jack is born 
berry has died 
jack has died 

分析:

  1. 实例化tom 然后实例化berry
  2. tom被销毁 然后实例化jack
  3. berry被销毁 最后销毁jack

继承

继承介绍

  1. 继承使得代码具有层次结构
  2. 子类继承了父类的属性和方法,实现了代码的可重用性(实际感觉就是一个类一个文件 这样方便找错 结构也更清楚)
  3. 使用extends关键字实现继承
  4. 父类和子类是相对的

语法:

<?php
// 定义一个父类
class Person{
    public function show(){
        echo "这是一个人类";
    }
}
// 定义一个子类继承父类
class Student extends Person{

}

$stu = new student;
$stu->show();    //子类继承父类的方法

//这是一个人类

执行过程:

第一步:在Student类中去找show() 如果找不到就调用 找不到就到父类中去查找

第二步:在Person类中查询show()

子类中调用父类成员

<?php
// 定义一个父类
class Person{
    public function show(){
        echo "这是一个人类";
    }
}
// 定义一个子类继承父类
class Student extends Person{
    public function test(){
        // 方法一
        /* $person = new Person();
        $person->show(); */

        // 方法二
        $this->show();
    }
}

$stu = new student;
$stu->test();

//这是一个人类

小结:

  1. 通过实例化父类调用父类的成员
  2. 通过$this关键字调用父类成员

Protected

protected:受保护的 在整个继承链上都能访问 也就是说在子类和父类中都能够使用

<?php
class Person{
    public function getNum(){
        echo $this->num;
    }
}
class Student extends Person{
    protected $num = 10;
}
$obj = new Student(); //整个继承链上有A和B
$obj -> getNum();

// 输出结果为10

tips:子类继承父类的非私有属性和非私有方法

<?php
class Person{
    public function getNum(){
        echo $this->num;
    }
}
class Student extends Person{
    protected $num = 10;
}
$obj = new Person(); // 整个继承链上只有A
$obj -> getNum();

// Notice: Undefined property: Person::$num 

继承中的构造函数

规则:

1、如果子类有构造函数就调用子类的,如果子类没有就调用父类的构造函数
2、子类的构造函数调用后,默认就不再调用父类的构造函数

通过类名调用父类的构造函数

类名::__construct()

例题

<?php
class Persons{
    public function __construct(){
        echo "这是父类方法\n";
    }
}
class Student extends Persons{
     public function __construct(){
        //Person::__construct();     // 调用父类的构造函数
        parent::__construct();      // parent表示父类的名字
        echo "这是子类方法\n";
    } 
}

new Student();  //  耦合性高 联系紧密
注意:parent关键字表示父类的名字 可以降低程序的耦合性 耦合性越低越好

例题:给父类传递参数

<?php
class Persons{
    protected $name; //受保护的属性 在整个继承链上使用
    protected $sex;    //受保护的属性 在整个继承链上使用
    // 父类的公有构造方法对父类的属性进行赋值
    public function __construct($name,$sex){
        $this->name = $name;
        $this->sex = $sex;        
    }
}
class Student extends Persons{
    private $score; //私有的属性 只能在类的内部使用
    public function __construct($name,$sex,$score){
        parent::__construct($name,$sex);      //调用父类构造函数并传递参数
        $this->score = $score;
    } 
    public function getinfo(){
        echo "name:{$this->name}\n";
        echo "sex:{$this->sex}\n";
        echo "score:{$this->score}\n";
    }
    
}

$stu = new Student('tom','man',88);  //  耦合性高 联系紧密
$stu -> getinfo();

/* name:tom
sex:man
score:88 */

$this详解

$this表示当前对象的地址,也就是$this保存的当前对象的地址

<?php
class A{
    public function __construct(){
        var_dump($this);
    }
}
class B extends A{

}

new A();      //object(A)#1 (0) {}
new B();     //object(B)#1 (0) {}

多重继承

PHP不允许多重继承 因为多重继承容易产生二义性

A--->B--->C

多态

多态:多种形态

多态分为两种:方法重写和方法重载

方法重写

简单理解就是子类把父类的同名函数重新命名一遍

子类重写了父类的同名方法(函数名相同 形参相同)

<?php
class Person{
    public function show(){
        echo 'parent';
    }
}
class Student extends Person{
    public function show(){
        echo 'son';
    }
}
// 子类重写了父类的同名方法
$stu = new Student;
$stu -> show();            // 输出结果:son

注意事项:

  1. 子类的方法必须和父类的方法名相同
  2. 参数个数要一致
  3. 子类修饰的不能比父类更加严格

方法重载

简单理解 就是同名函数用不同的参数来重新加载

在一个类中 有多个同名的函数 通过参数的个数不同来区分不同的方法 称为方法重载

<?php
class Student{
    public function show(){
        
    } 
    public function show($num){
        
    } 
    public function show($num1,$num2){

    } 
}

注意:PHP不支持方法重载 但是可以通过其他方法来模拟方法重载

面向对象的三大特性

  1. 封装(通过访问修饰符去封装)
  2. 继承(通过extends去继承)
  3. 多态(多种形态)

私有属性继承重写

私有属性可以被继承不可以重写

先来看看公有属性

<?php
class A{
    public $name = 'PHP';
}
class B extends A{
    // B类继承A类后面再重写掉$name
    public $name = 'JAVA';
}
$obj = new B;
var_dump($obj);         //object(B)#1 (1) {["name"]=>string(4) "JAVA"}
// echo $obj->name;
<?php
class A{
    // 私有属性只能在类的内部访问 那是否能够被继承呢?
    private $name = 'PHP';
}
class B extends A{
    private $name = 'JAVA';
}
$obj = new B;
var_dump($obj);   

// 说明私有属性也是可以被继承的 但是会有两个属性 一个是A类的 一个是B类的
/* object(B)#1 (2) {
    ["name":"B":private]=>
    string(4) "JAVA"
    ["name":"A":private]=>
    string(3) "PHP"
  } */
<?php
class A{
    private $name = 'PHP';
    public function showa(){
        var_dump($this);
        // echo "$this->name\n";
    }
}
class B extends A{
    private $name = 'JAVA';
    public function showb(){
        var_dump($this);
        // echo "$this->name\n";
    }
}
$obj = new B;
$obj -> showa();
$obj -> showb();

输出结果:
object(B)#1 (2) {
  ["name":"B":private]=>
  string(4) "JAVA"
  ["name":"A":private]=>
  string(3) "PHP"
}
object(B)#1 (2) {
  ["name":"B":private]=>
  string(4) "JAVA"
  ["name":"A":private]=>
  string(3) "PHP"
}
<?php
class A{
    private $name = 'PHP';
    public function showa(){
        // var_dump($this);
         echo "$this->name\n";
    }
}
class B extends A{
    private $name = 'JAVA';
    public function showb(){
        //var_dump($this);
        echo "$this->name\n";
    }
}
$obj = new B;
$obj -> showa();
$obj -> showb();

PHP
JAVA

分析:

  1. $obj中有两个name 一个是公有的 一个是私有的 在showa()中既能访问私有的
  2. $name也能访问公有的$name 但是私有的比公有的权限高 所以输出的是tom 在showb()中只能访问公有的$name 所以输出berry

方法修饰符

方法修饰符有:static final abstract

Static 静态修饰符

  1. static修饰的属性叫静态属性、static修饰的方法叫静态方法
  2. 静态成员加载类的时候分配空间,程序执行完毕后销毁
  3. 静态成员在内存中就一份
  4. 调用语法 类名::属性 类名::方法名()
<?php
class Person{
    public static $add = "北京\n";
    public static function show(){
        echo 'this is a static function';
    }
}
echo Person::$add;
Person::show();

输出结果:
北京
this is a static function

统计在线人数:

<?php
class Student{
    public static $num = 0;
    function __construct(){
        self::$num++;
    }
    function __destruct(){
        self::$num--;
    }
    function new(){
        echo '当前在线人数为:'.self::$num."\n";
    }
}

$stu1 = new Student;
$stu2 = new Student;
$stu3 = new Student;
$stu2->new();
unset($stu2);
$stu3->new();

当前在线人数为:3
当前在线人数为:2

注意:self表示所在类的类名就是自己,使用self降低耦合性

静态延时绑定

static表示当前对象所属的类

<?php 
class Person{
    public static $add = 'China'."\n";
    public static function show(){
        echo 'this is people'."\n";
    }
}
// 继承
class Student extends Person{

}
$stu = new Student;
echo Student::$add;
Student::show();

输出结果:
China
this is people
<?php 
class Person{
    public static $type = 'People'."\n";
    public  function show(){
        echo static::$type."\n";
    }
}
// 继承
class Student extends Person{

}
$obj = new Person;
$obj->show();

输出结果:
People
<?php 
class Person{
    public static $type = 'People'."\n";
    public  function show1(){
        echo static::$type."\n";
    }
}
// 继承
class Student extends Person{
    public static $type = 'Student'."\n";
    public  function show2(){
        echo static::$type."\n";
    }
}
$obj = new Student;
$obj->show1();
$obj->show2();

输出结果:
Student
Student
    
分析:
    可以看到student类继承了person类
    static表示调用当前对象所属的类 
    所以输出的结果都是student

小结:

  1. static在内存中就一份,在类加载的时候就分配空间
  2. 如果有多个修饰符号、修饰符之间是没有顺序的
  3. self表示所在类的类名
  4. static表示调用当前对象所属的类 self表示当前类
  5. static有两个作用 第一表示静态的 第二表示类名

Final [最终的]

final修饰的方法不能被重写

final修饰的方法不能被继承

<?php
class Person{
    // final修饰的方法不能被重写
    public final function show(){

    }
}
class Student extends Person{
    public function show(){

    }
}
/*Fatal error: 
Cannot override final method Person::show() 
in D:\phpStudy\PHPTutorial\WWW\f.com\PHP\Object\Final.php on line 11*/
<?php
final class Person{
    // final修饰的方法不能被重写
    public  function show(){

    }
}
class Student extends Person{
    public function show(){

    }
}
/* Fatal error: Class Student may not inherit from final class (Person) in
 D:\phpStudy\PHPTutorial\WWW\f.com\PHP\Object\Final.php on line 12 */

作用

  1. 如果一个类确定不被继承,一个方法确定不会被重写,用final修饰可以提高执行效率
  2. 如果一个方法不允许被其他类重写 可以用final修饰

abstract【抽象的】

  1. abstract 修饰的方法是抽象方法,修饰的类是抽象类
  2. 只有方法的声明没有方法的实现称为抽象方法
  3. 一个类中只要有一个方法是抽象方法,这个类必须是抽象类
  4. 抽象类的特点是不能实例化
  5. 如果有子类继承了抽象类 那么必须将抽象方法重新实现 如果不重新实现 不允许实例化
  6. 类中没有抽象方法也可以声明成抽象类,用来阻止类的实例化

例题

<?php 
// 抽象类
abstract class Person{
    //只有方法的声明没有方法的实现
    public abstract function setinfo();
    public function getinfo(){
            echo "获取信息\n";
    }
}
// 如果有类继承了抽象类 那么必须将抽象方法重新实现
// 如果不重新实现 不允许实例化
class Student extends Person{
    // 重写实现父类的抽象方法
    public function setinfo(){
        echo "重新实现父类的抽象方法\n";
    }
}
// 抽象类不允许被实例化
$person = new Person;
// Fatal error: Uncaught Error: Cannot instantiate abstract class Person

// 测试
$stu = new Student;
$stu->setinfo();
$stu->getinfo();

输出信息:
重新实现父类的抽象方法
获取信息

抽象类的作用

  1. 定义命名规范
  2. 阻止实例化,如果一个类中的所有方法都是静态方法,那么这时候没有必要去实例化,可以通过abstract来阻止实例化

类常量

类常量是const常量

<?php
class Student{
    public const ADDR = "地址不详";
}
echo Student::ADDR;
//地址不详

问题:define常量和const常量的区别?

答:const常量可以做类成员,define常量不可以做类成员

问题:常量和静态属性的区别在哪里?

答:相同点:在加载类的时候分配空间

​ 不同点:常量的值不可以更改 静态属性的值可以更改

接口(interface)

  1. 如果一个类中所有的方法都是抽象方法 那么这个抽象类就可以声明成接口
  2. 接口是一个特殊的抽象类 接口中只能有抽象方法和常量(function const)
  3. 接口中的抽象方法只能是public,可以省略,默认也是public的
  4. 通过implements关键字来实现接口
  5. 不能使用abstract和final来修饰接口中的抽象方法
<?php 
// 用interface来声明接口
interface Student{
    const ADDR = 'BJ';
    function fun1();
    function fun2();
}
// 接口实现
class Teacher implements Student{
    public function fun1(){

    }
    public function fun2(){
        
    }
}
echo Student::ADDR;

Implements 接口的多重实现

类不允许多重继承,但是接口允许多重实现(举个例子 )

<?php
interface Ipic1{
    function fun1();
}
interface Ipic2{
    function fun2();
}
class Student implements Ipic1,Ipic2{
    public function fun1(){

    }
    public function fun2(){

    }
}

注意:

  1. 在接口的多重实现中 如果有同名的方法 只要实现一次即可
  2. 类可以继承的同时实现接口

    class Students extends Person implements Ipic1,Ipic2{
    
    }

匿名类

(实际上就是把一个没有命名的类赋给一个对象)

了解内容 PHP7.0支持

<?php
$stu = new class {
    public $name = 'TOM';
    function __construct(){
        echo "这是一个构造方法";
    }
};
echo $stu->name;

// 输出结果:这是一个构造方法TOM

好处:如果类只被实例化一次就可以使用匿名类 好处是实例化完毕后就回收了类的空间

方法绑定

了解内容 PHP7.0支持

作用:将方法绑定到对象上

语法:闭包->call(对象)

在PHP中匿名函数称为闭包

<?php
$lang = 'ch';
//类
class Student{}
// 匿名函数
if($lang == 'ch'){
    $fun = function(){
        echo '我是一名学生';
    };
}else{  
    $fun = function(){
        echo 'I am a student';
    };
}
# 绑定方法
$stu = new Student;
$fun->call($stu);
// 我是一名学生

异常处理

集中处理在代码块中发生的异常

在代码块中发生了异常直接抛出,代码块中不处理异常,将异常集中起来一起处理

使用的关键字

try:检测代码块
catch:捕获异常
throw:抛出异常
finally:无论有无异常都会执行,可以省略
Exception:异常类

语法结构

try{
#检测代码
}catch(Exception $ex){
#捕获异常
}
finally{
#不论是否有异常 都要执行 finally可以省略
}

例题:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <?php 
    if (isset($_POST['button'])) {
        try{    
            $age = $_POST['age'];
            if ($age=="")
                # 抛出异常
                throw new Exception("年龄不能为空");    
            if(!is_numeric($age))
                # 抛出异常
                throw new Exception("年龄必须为数字");
            if (!($age>=10 && $age<=30))
                # 抛出异常
                throw new Exception("年龄必须在10~30之间");     
            echo "您的年龄合适<br>";    
        }catch(Exception $ex){
            #捕获异常
            echo '错误信息:'.$ex->getMessage().'<br>';
            echo '错误码:'.$ex->getCode().'<br>';
            echo '文件地址:'.$ex->getFile().'<br>';
            echo '错误信息:'.$ex->getLine().'<br>';
        }finally{
            #不管是否有异常,finally表示最终都要执行
            echo "关闭数据库连接";
        }
    }
    ?>
    <form action="" method="post">    
        年龄:<input type="text" name="age" id=""><br>
        <input type="submit" name="button" value="提交">
    </form>
</body>
</html>

注意:抛出异常后 try块终止执行 权限交给了catch块

自定义异常

场景:如果有实现异常的分类处理?比如异常有三个级别异常对应三种处理方式

自定义三种异常即可

所有异常类的父类是Expection,Exception中的方法不允许重写

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <link rel="stylesheet" href="">
</head>
<body>
    <form action="" method="post" accept-charset="utf-8">
        姓名:<input type="text" name="name" value="" placeholder=""><br>
        年龄:<input type="text" name="age" value="" placeholder=""><br>
        <input type="submit" name="submit" value="提交">
    </form>
    <?php 
    #自定义空异常类
    class MyNullException extends Exception{
    }    
    #自定义类型异常类
    class MyTypeException extends Exception{
    }    
    #自定义范围异常类
    class MyRangeException extends Exception{
    }
    #逻辑代码
    if (isset($_POST['submit'])) {
        try{
            $name = $_POST['name'];
            $age = $_POST['age'];
            #$age+=0;
            var_dump($age);echo "<br>"; 
            if ($name=="") 
                throw new MyNullException("姓名不能为空", 1);
            if ($age=="") 
                throw new MyNullException("年龄不能为空", 2);            
            if (!is_numeric($age)) 
                throw new MyTypeException("年龄不是数字", 3);
            if ($age<10 || $age>30) 
                throw new MyRangeException("年龄必须在10~30之间", 4);
            echo "姓名:".$name.'<br>';
            echo "年龄:".$age.'<br>';
        }catch(MyNullException $ex){
            echo $ex->getMessage().'<br>';
            echo "错误记录在日志中";
        }catch(MyTypeException $ex){
            echo $ex->getMessage();
            echo "发送电子邮件";
        }catch(MyRangeException $ex){
            echo $ex->getMessage();
            echo "给管理员打电话";
        }
    }
    ?>
</body>
</html>