今日目标
1.2 面向对象介绍
1.2.1 介绍
面向对象就是把程序里的东西都当成 "东西" 来看。就像现实中你看到的一切 —— 猫、手机、汽车,每个 "东西" 都有自己的特点(比如猫有颜色、有品种)和能做的事(比如猫会叫、会跑)。
以前的编程可能更像列清单:第一步做什么,第二步做什么。而面向对象更像在描述一个小世界,里面有各种 "东西",它们之间互相配合。
1.2.2 面向对象的好处
- 省事:写一次代码,多个地方能用,不用重复劳动
- 好改:改一个地方,不影响其他地方,不会牵一发而动全身
- 好懂:结构清晰,就像现实世界的东西一样,容易理解
- 能长大:项目从小做大时,代码不会乱成一团麻
- 多人合作方便:你负责写 "汽车",我负责写 "道路",互不干扰
1.3 类和对象
- 类:就是 "类别",比如 "狗" 就是一个类,它描述了所有狗都有的特点(有毛、有尾巴)和技能(会叫、会跑)
- 对象:就是具体的某个东西,比如你家那只叫 "旺财" 的金毛,就是 "狗" 这个类的一个对象
举个例子:"水果" 是类,你手里拿的那个苹果就是对象
1.4 在PHP中实现类和对象
1.4.1 创建类
在 PHP 里,用class关键字来定义一个类,就像写一份 "说明书"
<?php
// 定义一个"狗"类(写一份狗的说明书)
class Dog {
// 狗的特征(属性)
public $name; // 名字
public $color; // 颜色
// 狗的技能(方法)
public function bark() {
// 输出"名字叫的汪汪叫"
echo $this->name . "在汪汪叫";
}
}
?>
1.4.2 对象实例化
有了说明书,就能造个具体的狗了,用new关键字:
<?php
// 用Dog类造一只具体的狗
$wangcai = new Dog();
// 给这只狗起名字、定颜色
$wangcai->name = "旺财"; // ->就像"的",$wangcai->name就是"旺财的名字"
$wangcai->color = "黄色";
// 让这只狗叫一声
$wangcai->bark(); // 输出:旺财在汪汪叫
// 再造一只狗
$dahuang = new Dog();
$dahuang->name = "大黄";
?>
1.4.3 对象的比较
比较两个对象有两种方式,很好理解:
var_dump 可以用来比较对象是不是一样的 一样的就输出true
<?php
$dog1 = new Dog();
$dog1->name = "小白";
$dog1->color = "白色";
$dog2 = new Dog();
$dog2->name = "小白";
$dog2->color = "白色";
$dog3 = $dog1; // 让dog3和dog1是同一只狗
// 比较两只狗是不是长得一模一样
var_dump($dog1 == $dog2); // 结果:true(长得一样)
// 比较是不是同一只狗
var_dump($dog1 === $dog2); // 结果:false(不是同一只)
var_dump($dog1 === $dog3); // 结果:true(是同一只)
?>
1.5 属性
属性就是对象的 "特征"。比如手机的属性有:品牌、颜色、内存大小;人的属性有:姓名、年龄、身高
<?php
class Phone {
// 手机的属性
public $brand; // 品牌
public $color; // 颜色
public $storage; // 存储容量
}
// 造一个手机对象
$myPhone = new Phone();
$myPhone->brand = "华为";
$myPhone->color = "蓝色";
$myPhone->storage = "512G";
// 看看我的手机是什么样的
echo "我的手机是{$myPhone->color}的{$myPhone->brand},有{$myPhone->storage}内存";
?>
1.6 方法
方法就是对象能 "做什么"。比如手机能打电话、发微信;汽车能开、能停
<?php
class RemoteControl {
// 遥控器能开电视
public function turnOn() {
echo "电视打开了";
}
// 遥控器能关电视
public function turnOff() {
echo "电视关闭了";
}
// 遥控器能换台
public function changeChannel($channel) {
echo "换到{$channel}频道";
}
}
// 拿起遥控器
$tvRemote = new RemoteControl();
// 开电视
$tvRemote->turnOn(); // 输出:电视打开了
// 换台到5频道
$tvRemote->changeChannel(5); // 输出:换到5频道
// 关电视
$tvRemote->turnOff(); // 输出:电视关闭了
?>
1.7 访问修饰符
就是控制哪些属性和方法能被外面访问,就像:
- public(公共的) :像商店橱窗里的商品,谁都能看能摸
- protected(受保护的) :像家里的客厅,自己家人能进,外人不能
- private(私有的) :像自己的日记本,只有自己能看
-
<?php class Person { public $name; // 名字(公开的,谁都能知道) protected $age; // 年龄(自己和家人知道就行) private $salary; // 工资(只有自己知道) // 设置年龄(提供一个方法让外面能设置,但自己可以控制) public function setAge($age) { // 年龄不能瞎填 if ($age > 0 && $age < 150) { $this->age = $age; } } // 获取年龄 public function getAge() { return $this->age; } } $xiaoli = new Person(); $xiaoli->name = "小李"; // 可以直接设置名字 $xiaoli->setAge(28); // 通过方法设置年龄 echo $xiaoli->name; // 可以直接看名字 echo $xiaoli->getAge(); // 通过方法看年龄 // echo $xiaoli->age; // 不行!不能直接看受保护的属性 ?>
1.8 类和对象在内存中的分布
简单想象一下:
- 类就像一本菜谱,放在书架上(内存的某个地方),只需要一本
- 每个对象就像根据菜谱做出来的菜,放在盘子里(内存的另一个地方)
- 所有的菜都照着同一本菜谱做(共享类的方法),但味道可能略有不同(各自的属性值)
比如,"宫保鸡丁" 这个类是菜谱,你做的宫保鸡丁和饭店做的宫保鸡丁是两个对象,做法一样(方法相同),但辣度可能不同(属性值不同)。
1.9 封装
就是 "该露的露,该藏的藏"。就像电视机,我们只需要按遥控器(露出来的部分),不需要知道里面的电路板怎么工作(藏起来的部分)
<?php
class BankCard {
// 卡号可以让人看,但余额和密码要藏起来
public $cardNumber;
private $balance;
private $password;
// 构造方法:办卡时设置卡号和密码
public function __construct($cardNumber, $password) {
$this->cardNumber = $cardNumber;
$this->password = $password;
$this->balance = 0; // 新卡余额为0
}
// 存钱(公开方法)
public function deposit($money) {
$this->balance += $money;
echo "存了{$money}元,现在余额{$this->balance}元";
}
// 取钱(需要密码)
public function withdraw($money, $inputPwd) {
if ($inputPwd != $this->password) {
echo "密码错误";
return;
}
if ($money > $this->balance) {
echo "余额不足";
return;
}
$this->balance -= $money;
echo "取了{$money}元,现在余额{$this->balance}元";
}
}
// 办一张卡
$myCard = new BankCard("622848123456789", "888888");
// 存点钱
$myCard->deposit(1000);
// 取点钱
$myCard->withdraw(300, "888888");
// 别人能看到卡号,但看不到余额和密码
echo "卡号是:{$myCard->cardNumber}";
?>
1.10 构造方法
构造方法就是 "刚创建对象时自动执行的方法"。就像你买了新手机,第一次开机时会自动让你设置语言、连接 WiFi—— 这些就是手机的 "构造方法" 在工作。
在 PHP 里,构造方法叫__construct()(两个下划线,别写错了)
1.10.1 介绍
1.10.2 构造函数作用:初始化成员变量
<?php
class Student {
public $name;
public $studentId;
public $className;
// 构造方法:创建学生对象时自动调用
public function __construct($name, $studentId, $className) {
$this->name = $name;
$this->studentId = $studentId;
$this->className = $className;
echo "新学生{$name}加入{$className}班";
}
}
// 创建学生对象时,直接传参数给构造方法
$stu = new Student("张三", "2023001", "高三一班");
// 不用再手动设置属性了
echo $stu->name; // 输出:张三
echo $stu->className; // 输出:高三一班
?>
1.11 析构方法
析构方法和构造方法相反,是 "对象要消失时自动执行的方法"。就像你离开家时,会自动关灯、锁门 —— 这些就是 "析构方法" 在工作
PHP 里析构方法叫__destruct()(也是两个下划线)。
<?php
class Lamp {
private $isOn = false;
// 开灯(构造方法)
public function __construct() {
$this->isOn = true;
echo "灯开了";
}
// 关灯(析构方法)
public function __destruct() {
$this->isOn = false;
echo "灯关了";
}
}
// 开灯
$lamp = new Lamp(); // 输出:灯开了
// 使用灯...
// 当灯不再使用时,自动关灯
// 脚本结束时,会看到输出:灯关了
?>
1.11.1 介绍
1.11.2 计算机的内存管理
简单说,计算机内存就像你家的衣柜:
- 当你创建对象时,就像把衣服放进衣柜(分配内存)
- 当这件衣服你不再穿了,就要把它扔掉,腾出空间(释放内存)
- 析构方法就像扔衣服前,先把口袋里的东西掏出来(释放资源)
1.11.3 思考题
-
析构方法什么时候执行?
- 一般是在对象用不上的时候,或者程序结束的时候,由系统自动安排。
-
如果打开文件后不关闭会怎么样?
- 系统能同时打开的文件数量有限,一直不关闭可能导致无法再打开新文件
- 就像你打开了很多书,不合上,桌子就摆满了,没法再放新书了
1.12 继承
1.12.1 继承介绍
继承就是 "子承父业"。子类可以继承父类的所有本领,还能自己学新本领。就像儿子会继承父亲的一些技能,还能学会父亲不会的新技能。
PHP 用extends关键字实现继承:
<?php
// 父类:动物
class Animal {
protected $name;
public function __construct($name) {
$this->name = $name;
}
// 所有动物都会吃
public function eat() {
echo "{$this->name}在吃东西";
}
}
// 子类:鸟(继承自动物)
class Bird extends Animal {
// 鸟还会飞
public function fly() {
echo "{$this->name}在天上飞";
}
}
// 子类:鱼(继承自动物)
class Fish extends Animal {
// 鱼还会游
public function swim() {
echo "{$this->name}在水里游";
}
}
// 造一只鸟
$bird = new Bird("麻雀");
$bird->eat(); // 继承来的本领:麻雀在吃东西
$bird->fly(); // 自己的本领:麻雀在天上飞
// 造一条鱼
$fish = new Fish("金鱼");
$fish->eat(); // 继承来的本领:金鱼在吃东西
$fish->swim(); // 自己的本领:金鱼在水里游
?>
1.12,2 子类中调用父类成员
用parent::关键字可以在子类里调用父类的方法,就像 "先按我爸教的方法做,再加上我自己的改进"。
<?php
class Dad {
public function cook() {
echo "爸爸的红烧肉做法:放酱油、糖、八角";
}
}
class Son extends Dad {
public function cook() {
// 先按爸爸的方法做
parent::cook();
// 再加上自己的改进
echo ",再加一点啤酒味道更好";
}
}
$son = new Son();
$son->cook(); // 输出:爸爸的红烧肉做法:放酱油、糖、八角,再加一点啤酒味道更好
?>
1.12.3 protected
protected修饰的成员,就像家里的工具箱,自己家人(本类和子类)可以用,但外人不能用
<?php
class Grandpa {
protected $secretRecipe = "祖传的饺子馅配方";
protected function makeDumplings() {
echo "用{$this->secretRecipe}做饺子";
}
}
class Father extends Grandpa {
public function cookDumplings() {
// 子类可以用父类的protected成员
$this->makeDumplings();
}
}
$father = new Father();
$father->cookDumplings(); // 可以用
// $father->makeDumplings(); // 不行!外人不能直接用protected方法
?>
1.12.4 继承中的构造函数
如果子类有自己的构造函数,最好先调用一下父类的构造函数,就像 "先完成爸爸交代的任务,再做自己的事"
<?php
class Person {
protected $name;
public function __construct($name) {
$this->name = $name;
echo "我叫{$name}";
}
}
class Teacher extends Person {
private $subject; // 教的科目
public function __construct($name, $subject) {
// 先调用父类的构造函数
parent::__construct($name);
// 再处理自己的事情
$this->subject = $subject;
echo ",我教{$subject}";
}
}
$teacher = new Teacher("王老师", "数学");
// 输出:我叫王老师,我教数学
?>
1.12.5 $this详解
$this就是 "我自己" 的意思。在类的方法里用$this,指的就是当前这个对象。
<?php
class Animal {
public function move() {
// 让"自己"动起来
$this->howToMove();
}
}
class Rabbit extends Animal {
// 兔子的移动方式
public function howToMove() {
echo "兔子蹦蹦跳跳";
}
}
class Snake extends Animal {
// 蛇的移动方式
public function howToMove() {
echo "蛇蜿蜒爬行";
}
}
$rabbit = new Rabbit();
$rabbit->move(); // 输出:兔子蹦蹦跳跳(调用的是兔子自己的方法)
$snake = new Snake();
$snake->move(); // 输出:蛇蜿蜒爬行(调用的是蛇自己的方法)
?>
在继承中,$this指的是实际创建的那个对象。比如用子类创建的对象,$this就指这个子类对象。
1.12.6 多重继承
PHP 不允许一个类同时继承多个父类,就像一个人不能有两个亲爸爸一样,会乱套的。
如果需要多个类的功能,可以用 "接口"(interface)来实现,一个类可以实现多个接口,就像一个人可以同时学语文老师、数学老师、英语老师的知识。
<?php
// 会做饭的接口
interface CanCook {
public function cook();
}
// 会修东西的接口
interface CanRepair {
public function repair();
}
// 全能选手,既会做饭又会修东西
class Handyman implements CanCook, CanRepair {
public function cook() {
echo "我会做饭";
}
public function repair() {
echo "我会修东西";
}
}
$person = new Handyman();
$person->cook(); // 输出:我会做饭
$person->repair(); // 输出:我会修东西
?>这样,Handyman 类就同时有了两个接口的能力。
这样,Handyman 类就同时有了两个接口的能力。