学习目标

ü OOP中内存的分配情况

ü 类的封装性

ü 类的继承性

ü 类的多态性

ü 最终类和最终方法

静态属性和静态方法

1、概述

ü static关键字修饰的属性,就是静态属性;

ü static关键字修饰的方法,就是静态方法;

ü 静态属性:就是类的属性,与类相关,与对象无关;

ü 静态方法:就是类的方法,与类相关,与对象无关;

ü 类的东西(类常量、静态属性、静态方法),通过类名::来调用;

ü 静态属性或静态方法,在内存中只有一份,被所有对象去共享;

ü 静态属性或静态方法的好处:就是为了节省内存。例如:创建了100个对象,而静态属性只有一份。

ü 静态属性和类常量的区别:类常量在一次HTTP请求过程值永远不变;但是静态属性可以改变。

ü 静态属性和静态方法,都可以加权限控制符,而类常量没有权限。

2、举例说明

img
img

4、self关键字使用

ü $this是指向当前对象的指针,而self是指向当前类的指针;

ü $this关键字用来调用对象的属性和方法;

ü self用来调用类常量、静态属性、静态方法;

ü $this关键字只能在成员方法中使用;

ü self关键字可以在成员方法和静态方法中使用;

提示:对象可以调用:成员属性、成员方法、静态方法

提示:self可以调用:类常量、静态属性、静态方法、成员方法

img
img

综合案例

设计一个“学生类”,要求如下:

​ 定义一个“学生类”,并由此类实例化两个“学生对象”。该类包括姓名,性别,年龄等基本信息,并至少包括一个静态属性(表示总学生数)和一个常量,以及包括构造方法和析构方法。该对象还可以调用一个方法来进行“自我介绍”(显示其中的所有属性)。构造方法可以自动初始化一个学生的基本信息,并显示“xx加入传智,当前有xx个学生”。

<?php
class Student
{
    const TITLE = '313班学生类对象';

    private static $stu_num = 0;

    private $name;
    private $age;
    private $gender;

    public function __construct($name, $age, $gender)
    {
        $this->name = $name;
        $this->age = $age;
        $this->gender = $gender;
        self::$stu_num++; // 创建对象时总人数加一
        $this->show_info();
    }

    private function show_info()
    {
        echo self::TITLE . "<br>";
        echo "我是学生{$this->name},我今年{$this->age}岁了,我是{$this->gender}生。" . "<br>";
        echo "该班级当前有" . self::$stu_num . "名学生。" . "<br>"; // 静态属性不能在字符串中输出
    }

    public function __destruct()
    {
        echo "{$this->name}学生被开除辣...";
        self::$stu_num--; // 销毁对象时总人数减一
        echo "该班级当前有" . self::$stu_num . "名学生。" . "<br>"; // 静态属性不能在字符串中输出
    }
}

$stu_01 = new Student('喜羊羊', 20, '男');
// $stu_01.show_info();

$stu_02 = new Student('美羊羊', 18, '女');
// $stu_01.show_info();

// unset($stu_02);

OOP中内存的分配情况

1、为什么使用var_dump打印对象时,只能看到成员属性呢?

img
img

2、OOP中内存的分配情况

img
img

3、什么是值传递?

ü 所谓“值传递”,就是将一个变量的“数据”或“值”,复制一份,传递给另一个变量;

ü 这两个变量之间没有任何关系,修改其中一个变量的值,另一个变量的值不受影响;

ü 默认情况下,PHP值传递的数据类型有:字符串型、整型、浮点型、布尔型、数组型、NULL。

img
img

值传递在内存中如何表现?

img
img

4、什么是引用传递?

ü 所谓“引用传递”,就是将一个变量的“数据地址”,复制一份,传递给另一个变量;

ü 这两个变量指向“同一个地址”,修改其中一个变量的值,另一个变量的值也会受影响;

ü 默认情况下,PHP引用传递的数据类型有:对象和资源。

img
img
img
img

对于海量数据,使用“引用传地址”,要比“传值”速度快。

5、其它类型变量使用引用传递

如果其它类型变量使用引用传递,只需要在引用的变量前加“&”符号即可。

img
img

提示:$obj2 = $obj1 和$obj2 = &$obj1效果是一样的(数组可以使用)。

<?php
$arr_01 = [1, 2, 3];

$arr_02 = &$arr_01;


$arr_01[0] = 10;

print_r($arr_02); // Array ( [0] => 10 [1] => 2 [2] => 3 )

类的封装性

1、什么是类的封装性

ü 类的三大特性:封装性、继承性、多态性;

ü 封装性:将敏感的数据保护起来,不被外界访问;还可以理解为,将一个功能的方方面面,封装成一个整体,即类;

ü 类的封装性,是通过访问权限修饰符来实现的;

ü 提示:在项目中,属性基本都是私有的。通过公有的方法,对私有的属性进行赋值和取值。

2、访问权限修饰符介绍

ü public(公共的权限):在任何地方都可以被访问,主要是类内、类外、子类中都可以被访问。

ü private(私有的权限):只能在本类中被访问,类外和子类中无权访问。

ü protected(受保护的权限):只能在本类中和子类中被访问,在类外不能访问。

3、实例:封装一个简单的数据库工具类

<?php
// 定义一个数据库链接工具类
class ConnectDatabase
{
    // 定义私有属性
    private $db_host;
    private $db_username;
    private $db_password;
    private $sheet_name;
    private $charset;
    private $sql_link;

    // 数据库对象的初始化
    public function __construct($info_arr = array())
    {
        // 传递变量
        $this->db_host = $info_arr['host'];
        $this->db_username = $info_arr['username'];
        $this->db_password = $info_arr['password'];
        $this->sheet_name = $info_arr['sheet_name'];
        $this->charset = $info_arr['charset'];

        // 启动各个服务
        $this->connect_sql();
        $this->select_sheet();
        $this->set_charset();
    }

    // 链接数据库的方法
    private function connect_sql()
    {
        if (!($this->sql_link = @mysqli_connect($this->db_host, $this->db_username, $this->db_password))) {
            echo "链接数据库{$this->db_host}失败!!!";
            die();
        }
    }

    // 设置字符集的方法
    private function select_sheet()
    {
        if (!mysqli_select_db($this->sql_link, $this->sheet_name)) {
            echo "链接数据表" . $this->sheet_name . "失败!!!";
            die();
        }
    }

    // 选择数据表
    private function set_charset()
    {
        mysqli_set_charset($this->sql_link, $this->charset);
    }

    // 执行sql语句
    public function do_sql_query($sql_query)
    {
        $result = mysqli_query($this->sql_link, $sql_query);

        return $result;
        // 对返回结果集的类型做判断
        // if (gettype($result) == 'object') { 
        //     return $result;
        // }elseif(gettype($result) == 'boolean') {
        //     return $result;
        // } else {
        //     echo "数据库返回集非'object'也非'boolean',在程序未考虑到这种情况,请修改源代码。";
        //     die();
        // }
    }

    // 公共构析
    public function __destruct()
    {
        mysqli_close($this->sql_link);
    }
}

$info_arr = array(
    'host' => 'localhost',
    'username' => 'imageSystem',
    'password' => 'imageSystem',
    'sheet_name' => 'imageSystem',
    'charset' => 'utf8'
);

$my_database = new ConnectDatabase($info_arr);
$result = $my_database->do_sql_query("SELECT * FROM photos");
// $my_database->do_sql_query("UPDATE photos SET viewsNum=viewsNum+1 WHERE id=1");

$arrs = mysqli_fetch_all($result, MYSQLI_ASSOC);

print_r($arrs);

类的继承性

1、什么是类的继承性?

ü CSS样式继承:父标签定义的样式,可以在子标签中直接使用。相同的样式只写一遍,减少代码量。

ü 如果一个B类拥有了A类的所有特征信息,我们就说B类继承了A类。

ü A类可以称为:基础类(最顶层的类)、父类、上层类。

ü B类可以称为:最终类(最终的类)、子类、下层类。

ü 提示:如果多个子类拥有相同的属性和方法,可以将相同的属性和方法提取出来,放到“父类”中,然后,再创建子类并继承父类;这样一样,重复的代码只写一遍,减少代码量,提高工作效率。

ü 为什么要使用类的继承?是为了实现功能的升级和扩展。

ü 功能的升级:原来有的功能,在子类进行完善。

ü 功能的扩展:原来没有的功能,在子类增加新功能。

继承举例
继承举例

2、继承的语法格式

<?php
// 继承的语法特征
class SubClass extends ParentsClass
{
    // 子类的内容定义
}

// SubClass:要创建的子类的名称
// extends:继承的关键字
// ParentsClass:SubClass要继承的父类的名称

3、单继承和多继承

ü 单继承:只能从一个父类来继承功能。如:Java、PHP、C#等。

ü 多继承:同时从多个父类来继承功能。如:C++

单继承和多继承
单继承和多继承

4、实例:单继承的简单演示

<?php
// 定义一个学生类
class Student
{
    const TITLE = "给排水一班学生"; // 定义常量
    protected static $stu_num = 60; // 定义静态变量
    protected $class_name = '给排水';

    protected static function read_my_function()
    {
        return __METHOD__;
    }

    // private function a() // 私有方法只能在本类中使用,儿子都用不了
    protected function a()
    {
        return __METHOD__; // 返回方法的名称
    }
}

// 定义一个小团体类
class FreeTeam extends Student
{
    // protected $class_name = '自由社'; // 子类中重新赋值会覆盖父类中的属性或者方法

    public function print_info()
    {
        echo self::TITLE . "<br>"; // 给排水一班学生
        echo self::$stu_num . "<br>"; // 60
        echo $this->class_name . "<br>"; // 给排水
        echo self::read_my_function() . "<br>"; // Student::read_my_function
        echo $this->a() . "<br>"; // Student::a
    }
}

$demo = new FreeTeam();
$demo->print_info();

php中conststatic的区别和联系:

  1. const是类中的常量,类外用define来定义常量;
  2. const只可以修饰类的属性,不能修饰类的方法,static可以修饰属性,也可以修饰方法;
  3. conststatic都属于类本身,而不属于new后的类实例.类内访问使用self::,类外使用类名::来访问;类内的$this指代类实例本身,conststatic属于类本身,所以无法用$this访问;
  4. self::NAME指代constself::$name指代staticconst是常量,是不需要变量符号的.static修饰的变量,需要变量符;
  5. const一旦被定义不可更改,static修饰的变量是可以被更改的;
  6. static方法中,不能使用类的普通成员和方法;这很好理解,调用静态方法的时候,不能肯定类有没有被实例化.类的普通方法中可以使用static属性和static方法。

5、parent关键字

ü self代表当前类,parent代表父类。

ü self和parent可以存在于所有方法(成员方法和静态方法)中。

ü self用来调用本类的内容,包括:类常量、静态属性、静态方法、成员方法;

ü parent用来调用父类的内容,包括:类常量、静态属性、静态方法、成员方法;

ü self和parent都使用范围解析符"::"来调用其它内容。

ü 语法:parent::类常量 | 静态属性 | 静态方法 | 成员方法

<?php
// 定义一个学生类
class Student
{
    const TITLE = "给排水一班学生"; // 定义常量
    protected static $class_name = '给排水';
}

// 定义一个小团体类
class FreeTeam extends Student
{
    const TITLE = "自由社牛逼"; // 定义常量
    protected static $class_name = '嘿嘿嘿';
    protected $demo_num = 1;

    public function print_info()
    {
        echo self::TITLE . "<br>"; // 自由社牛逼
        echo self::$class_name . "<br>"; // 嘿嘿嘿
        echo parent::TITLE . "<br>"; // 给排水一班学生
        echo parent::$class_name . "<br>"; // 给排水
        echo $this->demo_num;
    }
}

$demo = new FreeTeam();
$demo->print_info();

类的多态

1、什么是类的多态?

ü 类的多态,就是类的多种形态。

ü 类的多态主要指方法重载或方法重写。

ü 方法重载:在同一个类中定义两个同名方法,PHP不支持。

ü 方法重写:在子类中定义一个与父类同名的方法,就是“方法重写”。

ü 为什么要重写方法?主要是实现功能的升级。父类中有的方法,子类中再用同样的名字再定义一次,一般来说,子类中方法的功能比父类更完善、更详尽。

2、方法重写的要求

  1. ü 子类中重写的方法,要与父类中的方法同名;
  2. ü 子类中重写的方法形参个数,要与父类中的同名方法形参个数一致;
  3. ü 子类中重写的方法类型,要与父类中同名方法类型一致;
  4. ü 子类中重写的方法的访问权限,不能低于父类中同名方法的访问权限

    • þ 父类方法权限为public,子类同名方法权限只能是public;
    • þ 父类方法权限为protected,子类同名方法权限可以是protected和public;
    • þ 父类方法权限为private,子类无法继承,也无法重写。
  5. ü 注意:对于重写构造方法(__construct),就比较特殊,就没有形参个数的要求。

3、实例:方法重写的要求

方法重写的四点要求:

  • 权限只能高,不能低;
  • 静态对静态;
  • 函数名称一致;
  • 参数个数一致(?)。
<?php
// 定义一个学生类
class Student
{
    public function demo($num_01, $num_02)
    {
        return $num_01 + $num_02;
    }
}

// 定义一个小团体类
class FreeTeam extends Student
{
    //   public function demo($num_01, $num_02,  $num_03) // 报错
    //   public function demo($num_01, $num_02,  $arr = array()) // 不会报错
    public function demo($num_01, $arr = array()) // 不会报错
    {
        //...      
    }
}

4、实例:商品子类重写商品基础类中的方法

<?php
// 创建商品父类
class Good
{
    // 商品的私有属性
    private $name;
    private $price;

    // 受保护的构造方法
    protected function __construct($name, $price)
    {
        // 对属性赋值
        $this->name = $name;
        $this->price = $price;
    }

    // 构造商品基本信息
    protected function print_info()
    {
        return "该商品{$this->name}的价格是{$this->price}";
    }
}

// 创建手机商品的类,并且继承商品类
class Phone extends Good
{
    // 创建手的的私有属性
    private $brand;
    private $origin_place;

    // 重写父类商品的构造方法
    public function __construct($name, $price, $brand, $origin_place)
    {
        //传递父类的属性
        parent::__construct($name, $price);
        // 对私有属性赋值
        $this->brand = $brand;
        $this->origin_place = $origin_place;
    }

    // 重写父类的商品基本信息函数
    public function print_info()
    {
        $result_str = parent::print_info();
        $result_str .= "<br>" . "手机的品牌为:{$this->brand},产地为{$this->origin_place}。";
        return $result_str;
    }
}

// 创建图书商品的类,并且继承商品类
class Book extends Good
{
    // 创建手的的私有属性
    private $author;
    private $publish;

    // 重写父类商品的构造方法
    public function __construct($name, $price, $author, $publish)
    {
        //传递父类的属性
        parent::__construct($name, $price);
        // 对私有属性赋值
        $this->author = $author;
        $this->publish = $publish;
    }

    // 重写父类的商品基本信息函数
    public function print_info()
    {
        $result_str = parent::print_info();
        $result_str .= "<br>" . "图书的作者为:{$this->author},出版社为{$this->publish}。";
        return $result_str;
    }
}

$xiaomi = new Phone('小米MAX', '1999', '小米', '中国');
echo $xiaomi->print_info(); // 该商品xiaomi4的价格是1999 \n 手机的品牌为:小米,产地为中国。

$new_book = new Book('PHP从入门到放弃', '50', '武大郎', '北京出版社');
echo $new_book->print_info(); //该商品PHP从入门到放弃的价格是50 \n 图书的作者为:武大郎,出版社为北京出版社。