类与对象
类和对象是面向对象编程的核心概念。类是对象的模板或蓝图,而对象是类的具体实例。理解类和对象的关系是掌握OOP编程的基础。
学习目标
完成本节学习后,你将能够:
- 理解类和对象的概念和关系
- 掌握类的定义语法
- 学会创建和使用对象
- 理解对象的内存管理
- 掌握静态成员的使用
- 了解类与对象的调试技巧
类的定义
基本语法
<?php
class ClassName {
// 类的属性和方法
}
// 示例:定义一个简单的Person类
class Person {
// 属性(成员变量)
public $name;
public $age;
public $gender;
// 方法(成员函数)
public function introduce() {
return "大家好,我叫{$this->name},今年{$this->age}岁。";
}
public function setAge($age) {
$this->age = $age;
}
public function getAge() {
return $this->age;
}
}
?>
类的命名规范
<?php
// 好的类命名示例
class User {}
class Product {}
class DatabaseConnection {}
class EmailValidator {}
// 不推荐的命名
class user {} // 小写开头
class product_info {} // 使用下划线
class DBConnection {} // 缩写不清晰
// 推荐使用大驼峰命名法(PascalCase)
class ShoppingCart {}
class UserProfile {}
class ConfigurationManager {}
?>
类的组成部分
<?php
class Student {
// 1. 常量 - 使用 const 关键字定义
const MAX_AGE = 120;
const MIN_AGE = 6;
const STATUS_ACTIVE = 'active';
const STATUS_INACTIVE = 'inactive';
// 2. 静态属性 - 属于类而不是对象
private static $totalStudents = 0;
private static $schoolName = '示例学校';
// 3. 实例属性 - 属于每个对象实例
private $id;
private $name;
private $age;
private $grade;
private $status = self::STATUS_ACTIVE;
// 4. 构造方法 - 创建对象时调用
public function __construct($name, $age, $grade) {
$this->id = uniqid('student_');
$this->name = $name;
$this->age = $this->validateAge($age);
$this->grade = $grade;
self::$totalStudents++;
echo "学生 {$this->name} 已创建\n";
}
// 5. 实例方法 - 对象的行为
public function introduce() {
return "我是{$this->grade}的学生{$this->name},今年{$this->age}岁。";
}
public function promote() {
if ($this->grade === '六年级') {
return "已经毕业了!";
}
$gradeMap = [
'一年级' => '二年级',
'二年级' => '三年级',
'三年级' => '四年级',
'四年级' => '五年级',
'五年级' => '六年级'
];
$this->grade = $gradeMap[$this->grade] ?? $this->grade;
return "恭喜升入{$this->grade}!";
}
// 6. 静态方法 - 属于类而不是对象
public static function getTotalStudents() {
return self::$totalStudents;
}
public static function setSchoolName($name) {
self::$schoolName = $name;
}
public static function getSchoolName() {
return self::$schoolName;
}
// 7. 析构方法 - 对象销毁时调用
public function __destruct() {
self::$totalStudents--;
echo "学生 {$this->name} 已注销\n";
}
// 8. 私有辅助方法
private function validateAge($age) {
if ($age < self::MIN_AGE) {
throw new Exception("学生年龄不能小于" . self::MIN_AGE . "岁");
}
if ($age > self::MAX_AGE) {
throw new Exception("学生年龄不能大于" . self::MAX_AGE . "岁");
}
return $age;
}
// Getter 方法
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public function getAge() {
return $this->age;
}
public function getGrade() {
return $this->grade;
}
public function getStatus() {
return $this->status;
}
// Setter 方法
public function setName($name) {
$this->name = $name;
}
public function setAge($age) {
$this->age = $this->validateAge($age);
}
public function setStatus($status) {
if (in_array($status, [self::STATUS_ACTIVE, self::STATUS_INACTIVE])) {
$this->status = $status;
}
}
}
// 使用示例
echo "=== 类的使用示例 ===\n";
// 设置学校名称
Student::setSchoolName("阳光小学");
echo "学校名称:" . Student::getSchoolName() . "\n";
// 创建学生对象
$student1 = new Student("张小明", 8, "二年级");
$student2 = new Student("李小红", 9, "三年级");
// 访问对象方法
echo $student1->introduce() . "\n";
echo $student2->introduce() . "\n";
// 访问对象属性
echo $student1->getName() . " 当前在 " . $student1->getGrade() . "\n";
// 调用对象方法
echo $student1->promote() . "\n";
echo "现在在 " . $student1->getGrade() . "\n";
// 访问静态方法
echo "当前学生总数:" . Student::getTotalStudents() . "\n";
// 访问常量
echo "学生年龄范围:" . Student::MIN_AGE . " - " . Student::MAX_AGE . "岁\n";
?>
对象的创建和使用
创建对象
<?php
// 使用 new 关键字创建对象
$student = new Student("王大伟", 10, "四年级");
// 创建对象的其他方式
$className = 'Student';
$student2 = new $className("刘小美", 7, "一年级");
// 从变量创建对象
$studentData = [
'name' => '陈志强',
'age' => 11,
'grade' => '五年级'
];
$student3 = new Student($studentData['name'], $studentData['age'], $studentData['grade']);
// 检查对象是否为某类的实例
if ($student instanceof Student) {
echo "\$student 是 Student 类的实例\n";
}
// 使用类常量
echo "学生状态:" . Student::STATUS_ACTIVE . "\n";
?>
对象的属性访问
<?php
class Product {
// 公共属性
public $name;
public $price;
// 受保护的属性
protected $category;
// 私有属性
private $stock;
public function __construct($name, $price, $category, $stock) {
$this->name = $name;
$this->price = $price;
$this->category = $category;
$this->stock = $stock;
}
// Getter 方法
public function getCategory() {
return $this->category;
}
public function getStock() {
return $this->stock;
}
// Setter 方法
public function setStock($stock) {
if ($stock >= 0) {
$this->stock = $stock;
return true;
}
return false;
}
// 公共方法
public function getInfo() {
return "产品:{$this->name},价格:¥{$this->price},库存:{$this->stock}";
}
// 受保护的方法
protected function calculateDiscount() {
if ($this->stock > 100) {
return 0.1; // 10% 折扣
} elseif ($this->stock > 50) {
return 0.05; // 5% 折扣
}
return 0;
}
// 公共方法调用受保护方法
public function getDiscountedPrice() {
$discount = $this->calculateDiscount();
return $this->price * (1 - $discount);
}
}
// 使用示例
echo "=== 属性访问示例 ===\n";
$product = new Product("笔记本电脑", 5999.00, "电子产品", 50);
// 访问公共属性
echo "产品名称:" . $product->name . "\n";
echo "产品价格:" . $product->price . "\n";
// 通过getter访问私有/受保护属性
echo "产品类别:" . $product->getCategory() . "\n";
echo "产品库存:" . $product->getStock() . "\n";
// 通过setter修改属性
$product->setStock(120);
echo "修改后库存:" . $product->getStock() . "\n";
// 调用方法
echo "产品信息:" . $product->getInfo() . "\n";
echo "折扣价格:" . $product->getDiscountedPrice() . "\n";
?>
对象的比较
<?php
class User {
public $id;
public $name;
public function __construct($id, $name) {
$this->id = $id;
$this->name = $name;
}
public function equals($otherUser) {
if ($otherUser instanceof User) {
return $this->id === $otherUser->id && $this->name === $otherUser->name;
}
return false;
}
public function __toString() {
return "User[id={$this->id}, name={$this->name}]";
}
}
// 使用示例
echo "=== 对象比较示例 ===\n";
$user1 = new User(1, "张三");
$user2 = new User(1, "张三");
$user3 = new User(2, "李四");
// 对象同一性比较(===)- 比较的是对象是否为同一个实例
echo "user1 === user2: " . ($user1 === $user2 ? "true" : "false") . "\n";
// 对象相等性比较(==)- 比较的是属性值是否相同
echo "user1 == user2: " . ($user1 == $user2 ? "true" : "false") . "\n";
echo "user1 == user3: " . ($user1 == $user3 ? "true" : "false") . "\n";
// 自定义比较方法
echo "user1 equals user2: " . ($user1->equals($user2) ? "true" : "false") . "\n";
echo "user1 equals user3: " . ($user1->equals($user3) ? "true" : "false") . "\n";
// 对象字符串表示
echo "user1: " . $user1 . "\n";
echo "user2: " . $user2 . "\n";
echo "user3: " . $user3 . "\n";
?>
静态成员
静态属性
<?php
class Counter {
private static $count = 0;
private static $instances = [];
private $id;
private $name;
public function __construct($name) {
$this->id = ++self::$count;
$this->name = $name;
self::$instances[$this->id] = $this;
}
public static function getCount() {
return self::$count;
}
public static function getInstances() {
return self::$instances;
}
public function getId() {
return $this->id;
}
public function getName() {
return $this->name;
}
public static function reset() {
self::$count = 0;
self::$instances = [];
}
public function __toString() {
return "Counter #{$this->id}: {$this->name}";
}
}
// 使用示例
echo "=== 静态成员示例 ===\n";
// 创建多个Counter对象
$counter1 = new Counter("计数器A");
$counter2 = new Counter("计数器B");
$counter3 = new Counter("计数器C");
// 访问静态方法
echo "当前计数器数量:" . Counter::getCount() . "\n";
// 访问静态属性
$instances = Counter::getInstances();
foreach ($instances as $instance) {
echo $instance . "\n";
}
// 注意:不能这样访问静态属性
// echo Counter::$count; // 错误:私有静态属性
?>
静态方法
<?php
class MathUtils {
// 静态常量
const PI = 3.14159265359;
const E = 2.71828182846;
// 静态方法 - 不需要创建对象实例就可以调用
public static function add($a, $b) {
return $a + $b;
}
public static function subtract($a, $b) {
return $a - $b;
}
public static function multiply($a, $b) {
return $a * $b;
}
public static function divide($a, $b) {
if ($b == 0) {
throw new InvalidArgumentException("除数不能为零");
}
return $a / $b;
}
public static function power($base, $exponent) {
return pow($base, $exponent);
}
// 静态方法调用其他静态方法
public static function quadraticEquation($a, $b, $c) {
if ($a == 0) {
throw new InvalidArgumentException("二次项系数不能为零");
}
$discriminant = self::power($b, 2) - 4 * $a * $c;
if ($discriminant < 0) {
return null; // 无实数解
}
$sqrtDiscriminant = sqrt($discriminant);
$x1 = self::subtract(-$b, $sqrtDiscriminant) / (2 * $a);
$x2 = self::add(-$b, $sqrtDiscriminant) / (2 * $a);
return [$x1, $x2];
}
// 静态工厂方法
public static function createCalculator() {
return new class {
private $result = 0;
public function add($number) {
$this->result += $number;
return $this;
}
public function multiply($number) {
$this->result *= $number;
return $this;
}
public function getResult() {
return $this->result;
}
};
}
}
// 使用静态方法
echo "=== 静态方法示例 ===\n";
// 直接调用静态方法
echo "10 + 5 = " . MathUtils::add(10, 5) . "\n";
echo "10 - 3 = " . MathUtils::subtract(10, 3) . "\n";
echo "6 * 7 = " . MathUtils::multiply(6, 7) . "\n";
echo "15 / 3 = " . MathUtils::divide(15, 3) . "\n";
echo "2^8 = " . MathUtils::power(2, 8) . "\n";
// 访问静态常量
echo "π = " . MathUtils::PI . "\n";
echo "e = " . MathUtils::E . "\n";
// 求解二次方程 x^2 - 5x + 6 = 0
$solutions = MathUtils::quadraticEquation(1, -5, 6);
if ($solutions) {
echo "方程 x^2 - 5x + 6 = 0 的解:x1 = {$solutions[0]}, x2 = {$solutions[1]}\n";
}
// 使用静态工厂方法
$calculator = MathUtils::createCalculator();
$result = $calculator->add(10)->multiply(2)->add(5)->getResult();
echo "计算结果:(10 + 5) * 2 = $result\n";
?>
对象的克隆
<?php
class Document {
public $title;
public $content;
public $author;
public $version;
private $createdAt;
public function __construct($title, $content, $author) {
$this->title = $title;
$this->content = $content;
$this->author = $author;
$this->version = 1;
$this->createdAt = date('Y-m-d H:i:s');
}
// 定义克隆行为
public function __clone() {
$this->version++;
// 注意:$this->createdAt 不会被克隆,保持原值
}
public function updateContent($newContent) {
$this->content = $newContent;
$this->version++;
}
public function getInfo() {
return "文档:{$this->title},作者:{$this->author},版本:{$this->version},创建时间:{$this->createdAt}";
}
}
// 使用示例
echo "=== 对象克隆示例 ===\n";
$originalDoc = new Document("PHP教程", "这是一篇关于PHP的教程", "张老师");
echo "原文档:" . $originalDoc->getInfo() . "\n";
// 克隆对象
$clonedDoc = clone $originalDoc;
echo "克隆文档:" . $clonedDoc->getInfo() . "\n";
// 修改原文档
$originalDoc->updateContent("这是更新后的PHP教程内容");
echo "更新后原文档:" . $originalDoc->getInfo() . "\n";
echo "克隆文档(未受影响):" . $clonedDoc->getInfo() . "\n";
// 修改克隆文档
$clonedDoc->updateContent("这是克隆版本的PHP教程");
echo "修改后克隆文档:" . $clonedDoc->getInfo() . "\n";
echo "原文档(未受影响):" . $originalDoc->getInfo() . "\n";
?>
类型声明和返回类型
<?php
// PHP 7+ 支持类型声明
class Calculator {
private $result = 0;
// 参数类型声明和返回类型声明
public function add(float $number): float {
$this->result += $number;
return $this->result;
}
public function subtract(float $number): float {
$this->result -= $number;
return $this->result;
}
public function multiply(float $number): float {
$this->result *= $number;
return $this->result;
}
public function divide(float $number): float {
if ($number == 0) {
throw new Exception("除数不能为零");
}
$this->result /= $number;
return $this->result;
}
public function reset(): void {
$this->result = 0;
}
public function getResult(): float {
return $this->result;
}
// 可变参数
public function addMultiple(float ...$numbers): float {
foreach ($numbers as $number) {
$this->result += $number;
}
return $this->result;
}
// 可空返回类型
public function sqrt(float $number): ?float {
if ($number < 0) {
return null;
}
return sqrt($number);
}
// 数组类型声明
public function getHistory(): array {
return [];
}
// 对象类型声明
public function setLogger(Logger $logger): void {
$this->logger = $logger;
}
}
// 简单的Logger类
class Logger {
public function log($message) {
echo "[LOG] $message\n";
}
}
// 使用示例
echo "=== 类型声明示例 ===\n";
$calculator = new Calculator();
$result = $calculator->add(10.5);
echo "加法结果:$result\n";
$result = $calculator->multiply(2);
echo "乘法结果:$result\n";
$result = $calculator->addMultiple(1, 2, 3, 4, 5);
echo "多参数加法结果:$result\n";
$sqrtResult = $calculator->sqrt(16);
echo "平方根结果:" . ($sqrtResult ?? 'null') . "\n";
$logger = new Logger();
$calculator->setLogger($logger);
$logger->log("计算完成");
?>
实际应用示例
简单的银行账户系统
<?php
class BankAccount {
private static $totalAccounts = 0;
private static $bankName = "示例银行";
private $accountNumber;
private $accountHolder;
private $balance;
private $type; // 'checking' 或 'savings'
private $createdAt;
private $transactions = [];
public function __construct($accountHolder, $initialBalance = 0, $type = 'checking') {
if ($initialBalance < 0) {
throw new InvalidArgumentException("初始余额不能为负数");
}
if (!in_array($type, ['checking', 'savings'])) {
throw new InvalidArgumentException("账户类型必须是 checking 或 savings");
}
$this->accountNumber = $this->generateAccountNumber();
$this->accountHolder = $accountHolder;
$this->balance = $initialBalance;
$this->type = $type;
$this->createdAt = date('Y-m-d H:i:s');
self::$totalAccounts++;
if ($initialBalance > 0) {
$this->recordTransaction('deposit', $initialBalance, "初始存款");
}
echo "账户创建成功:{$this->accountNumber}\n";
}
private function generateAccountNumber() {
return date('Y') . sprintf('%04d', self::$totalAccounts + 1001);
}
public function deposit($amount) {
if ($amount <= 0) {
throw new InvalidArgumentException("存款金额必须大于0");
}
$this->balance += $amount;
$this->recordTransaction('deposit', $amount, "存款");
return true;
}
public function withdraw($amount) {
if ($amount <= 0) {
throw new InvalidArgumentException("取款金额必须大于0");
}
if ($this->balance < $amount) {
throw new Exception("余额不足");
}
if ($this->type === 'savings' && $this->getWithdrawalCount() >= 3) {
$fee = 2.0; // 储蓄账户每月最多3次免费取款
if ($this->balance < $amount + $fee) {
throw new Exception("余额不足(包含手续费)");
}
$this->balance -= ($amount + $fee);
$this->recordTransaction('withdraw', $amount, "取款");
$this->recordTransaction('fee', $fee, "取款手续费");
} else {
$this->balance -= $amount;
$this->recordTransaction('withdraw', $amount, "取款");
}
return true;
}
public function transfer($toAccount, $amount) {
if (!$toAccount instanceof BankAccount) {
throw new InvalidArgumentException("目标账户必须是BankAccount实例");
}
$this->withdraw($amount);
$toAccount->deposit($amount);
$this->recordTransaction('transfer_out', $amount, "转账给 {$toAccount->accountNumber}");
$toAccount->recordTransaction('transfer_in', $amount, "来自 {$this->accountNumber}");
return true;
}
public function getBalance() {
return $this->balance;
}
public function getAccountNumber() {
return $this->accountNumber;
}
public function getAccountHolder() {
return $this->accountHolder;
}
public function getAccountType() {
return $this->type;
}
public function getTransactions() {
return $this->transactions;
}
private function getWithdrawalCount() {
$count = 0;
foreach ($this->transactions as $transaction) {
if ($transaction['type'] === 'withdraw') {
$count++;
}
}
return $count;
}
private function recordTransaction($type, $amount, $description) {
$this->transactions[] = [
'date' => date('Y-m-d H:i:s'),
'type' => $type,
'amount' => $amount,
'description' => $description,
'balance' => $this->balance
];
}
public function printStatement() {
echo "\n=== 银行对账单 ===\n";
echo "账户号码:{$this->accountNumber}\n";
echo "账户持有人:{$this->accountHolder}\n";
echo "账户类型:" . ($this->type === 'checking' ? '支票账户' : '储蓄账户') . "\n";
echo "创建日期:{$this->createdAt}\n";
echo "当前余额:¥" . number_format($this->balance, 2) . "\n";
echo "交易记录:\n";
foreach ($this->transactions as $transaction) {
$sign = ($transaction['type'] === 'deposit' || $transaction['type'] === 'transfer_in') ? '+' : '-';
echo " {$transaction['date']} | {$sign}¥" . number_format($transaction['amount'], 2) .
" | {$transaction['description']} | 余额:¥" . number_format($transaction['balance'], 2) . "\n";
}
}
// 静态方法
public static function getTotalAccounts() {
return self::$totalAccounts;
}
public static function getBankName() {
return self::$bankName;
}
public static function setBankName($name) {
self::$bankName = $name;
}
public function __destruct() {
echo "账户 {$this->accountNumber} 已关闭\n";
}
}
// 使用示例
echo "=== 银行账户系统示例 ===\n";
// 设置银行名称
BankAccount::setBankName("阳光银行");
echo "银行名称:" . BankAccount::getBankName() . "\n";
// 创建账户
$account1 = new BankAccount("张三", 1000.00, 'checking');
$account2 = new BankAccount("李四", 500.00, 'savings');
$account3 = new BankAccount("王五", 2000.00, 'checking');
echo "总账户数:" . BankAccount::getTotalAccounts() . "\n\n";
// 存款操作
echo "=== 存款操作 ===\n";
$account1->deposit(500.00);
$account2->deposit(300.00);
echo "张三账户余额:¥" . number_format($account1->getBalance(), 2) . "\n";
echo "李四账户余额:¥" . number_format($account2->getBalance(), 2) . "\n\n";
// 取款操作
echo "=== 取款操作 ===\n";
$account1->withdraw(200.00);
$account2->withdraw(100.00);
$account2->withdraw(50.00); // 储蓄账户第二次取款
$account2->withdraw(30.00); // 储蓄账户第三次取款
$account2->withdraw(20.00); // 储蓄账户第四次取款(含手续费)
// 转账操作
echo "\n=== 转账操作 ===\n";
$account1->transfer($account3, 300.00);
echo "张三转给王五 300 元\n";
echo "转账后余额:\n";
echo "张三:¥" . number_format($account1->getBalance(), 2) . "\n";
echo "王五:¥" . number_format($account3->getBalance(), 2) . "\n";
// 打印对账单
$account2->printStatement();
?>
总结
类和对象是面向对象编程的基础,理解它们的概念和用法对于掌握OOP至关重要。
关键要点:
-
类是模板,对象是实例:
- 类定义了属性和方法的蓝图
- 对象是类的具体实例,拥有独立的状态
-
访问控制:
public:可以从任何地方访问protected:只能在类内部和子类中访问private:只能在类内部访问
-
静态成员:
- 静态属性和方法属于类而不是对象
- 通过类名直接访问,不需要创建对象
-
对象生命周期:
- 通过
new创建对象 - 通过
__destruct()清理资源 - 支持对象克隆
- 通过
最佳实践:
- 使用有意义的类和方法名称
- 合理使用访问控制,保护内部数据
- 优先使用组合而不是继承
- 为类和方法编写清晰的文档注释
- 使用类型声明提高代码安全性
通过本节学习,你应该能够熟练地定义和使用类与对象,为后续学习更高级的面向对象特性打下坚实基础。