PHP面向对象编程入门:类与对象的实战应用
面向对象编程(OOP)是现代PHP开发的核心概念。作为一名从过程式编程转向面向对象编程的开发者,我深知这个转变的重要性和挑战性。今天我想分享一些关于PHP面向对象编程的基础知识和实用技巧。
什么是面向对象编程
面向对象编程是一种编程范式,它将现实世界的事物抽象为程序中的对象。每个对象都有自己的属性(数据)和方法(行为)。
面向对象的三大特性
- 封装:将数据和操作数据的方法封装在一起
- 继承:子类可以继承父类的属性和方法
- 多态:同一个接口可以有不同的实现
类的基本定义
1. 简单的类定义
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
| <?php class User { public $name; public $email; public $age; public function introduce() { return "我是 {$this->name},今年 {$this->age} 岁"; } public function setEmail($email) { $this->email = $email; } public function getEmail() { return $this->email; } }
$user = new User(); $user->name = "张三"; $user->age = 25; $user->setEmail("zhangsan@example.com");
echo $user->introduce() . "\n"; echo "邮箱:" . $user->getEmail() . "\n"; ?>
|
2. 构造函数和析构函数
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <?php class Product { private $name; private $price; private $category; public function __construct($name, $price, $category = "未分类") { $this->name = $name; $this->price = $price; $this->category = $category; echo "产品 '{$this->name}' 已创建\n"; } public function __destruct() { echo "产品 '{$this->name}' 已销毁\n"; } public function getInfo() { return "产品:{$this->name},价格:¥{$this->price},分类:{$this->category}"; } public function setPrice($price) { if ($price > 0) { $this->price = $price; } else { throw new InvalidArgumentException("价格必须大于0"); } } public function getPrice() { return $this->price; } }
$product1 = new Product("iPhone 14", 6999, "手机"); echo $product1->getInfo() . "\n";
$product2 = new Product("MacBook Pro", 12999); echo $product2->getInfo() . "\n";
?>
|
访问修饰符
PHP提供了三种访问修饰符来控制属性和方法的可见性:
1. public(公有)
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php class PublicExample { public $publicProperty = "我是公有属性"; public function publicMethod() { return "我是公有方法"; } }
$obj = new PublicExample(); echo $obj->publicProperty . "\n"; echo $obj->publicMethod() . "\n"; ?>
|
2. private(私有)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php class PrivateExample { private $privateProperty = "我是私有属性"; private function privateMethod() { return "我是私有方法"; } public function accessPrivate() { return $this->privateProperty . " - " . $this->privateMethod(); } }
$obj = new PrivateExample(); echo $obj->accessPrivate() . "\n";
?>
|
3. protected(受保护)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <?php class ParentClass { protected $protectedProperty = "我是受保护的属性"; protected function protectedMethod() { return "我是受保护的方法"; } }
class ChildClass extends ParentClass { public function accessProtected() { return $this->protectedProperty . " - " . $this->protectedMethod(); } }
$child = new ChildClass(); echo $child->accessProtected() . "\n";
?>
|
实际应用示例:银行账户类
让我们通过一个银行账户的例子来理解面向对象编程的实际应用:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
| <?php class BankAccount { private $accountNumber; private $balance; private $ownerName; private $transactions = []; public function __construct($accountNumber, $ownerName, $initialBalance = 0) { $this->accountNumber = $accountNumber; $this->ownerName = $ownerName; $this->balance = $initialBalance; $this->addTransaction("开户", $initialBalance); } public function deposit($amount) { if ($amount <= 0) { throw new InvalidArgumentException("存款金额必须大于0"); } $this->balance += $amount; $this->addTransaction("存款", $amount); return $this->balance; } public function withdraw($amount) { if ($amount <= 0) { throw new InvalidArgumentException("取款金额必须大于0"); } if ($amount > $this->balance) { throw new Exception("余额不足"); } $this->balance -= $amount; $this->addTransaction("取款", -$amount); return $this->balance; } public function transfer(BankAccount $targetAccount, $amount) { if ($amount <= 0) { throw new InvalidArgumentException("转账金额必须大于0"); } if ($amount > $this->balance) { throw new Exception("余额不足"); } $this->withdraw($amount); $targetAccount->deposit($amount); $this->addTransaction("转出到账户" . $targetAccount->getAccountNumber(), -$amount); $targetAccount->addTransaction("从账户" . $this->accountNumber . "转入", $amount); } public function getBalance() { return $this->balance; } public function getAccountInfo() { return [ 'accountNumber' => $this->accountNumber, 'ownerName' => $this->ownerName, 'balance' => $this->balance ]; } public function getAccountNumber() { return $this->accountNumber; } public function getTransactions() { return $this->transactions; } private function addTransaction($type, $amount) { $this->transactions[] = [ 'date' => date('Y-m-d H:i:s'), 'type' => $type, 'amount' => $amount, 'balance' => $this->balance ]; } public function printAccountInfo() { $info = $this->getAccountInfo(); echo "账户号:{$info['accountNumber']}\n"; echo "户主:{$info['ownerName']}\n"; echo "余额:¥{$info['balance']}\n"; echo "交易记录:\n"; foreach ($this->transactions as $transaction) { echo " {$transaction['date']} - {$transaction['type']}: ¥{$transaction['amount']} (余额: ¥{$transaction['balance']})\n"; } } }
try { $account1 = new BankAccount("001", "张三", 1000); $account2 = new BankAccount("002", "李四", 500); echo "=== 初始状态 ===\n"; $account1->printAccountInfo(); echo "\n"; $account2->printAccountInfo(); echo "\n=== 张三存款500元 ===\n"; $account1->deposit(500); echo "张三账户余额:¥" . $account1->getBalance() . "\n"; echo "\n=== 张三取款200元 ===\n"; $account1->withdraw(200); echo "张三账户余额:¥" . $account1->getBalance() . "\n"; echo "\n=== 张三向李四转账300元 ===\n"; $account1->transfer($account2, 300); echo "张三账户余额:¥" . $account1->getBalance() . "\n"; echo "李四账户余额:¥" . $account2->getBalance() . "\n"; echo "\n=== 最终状态 ===\n"; $account1->printAccountInfo(); echo "\n"; $account2->printAccountInfo(); } catch (Exception $e) { echo "错误:" . $e->getMessage() . "\n"; } ?>
|
静态属性和方法
静态成员属于类本身,而不是类的实例:
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 32 33 34 35 36 37 38
| <?php class Counter { private static $count = 0; private $instanceId; public function __construct() { self::$count++; $this->instanceId = self::$count; echo "创建了第 {$this->instanceId} 个实例\n"; } public static function getCount() { return self::$count; } public function getInstanceId() { return $this->instanceId; } public static function reset() { self::$count = 0; echo "计数器已重置\n"; } }
echo "当前实例数量:" . Counter::getCount() . "\n";
$obj1 = new Counter(); $obj2 = new Counter(); $obj3 = new Counter();
echo "当前实例数量:" . Counter::getCount() . "\n"; echo "obj2的实例ID:" . $obj2->getInstanceId() . "\n";
Counter::reset(); echo "重置后实例数量:" . Counter::getCount() . "\n"; ?>
|
常量
类常量在类定义后不能被修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php class MathConstants { const PI = 3.14159; const E = 2.71828; public static function getCircleArea($radius) { return self::PI * $radius * $radius; } public static function getCircleCircumference($radius) { return 2 * self::PI * $radius; } }
echo "圆周率:" . MathConstants::PI . "\n"; echo "半径为5的圆的面积:" . MathConstants::getCircleArea(5) . "\n"; echo "半径为5的圆的周长:" . MathConstants::getCircleCircumference(5) . "\n"; ?>
|
魔术方法
PHP提供了一些特殊的魔术方法,以双下划线开头:
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| <?php class MagicExample { private $data = []; public function __get($name) { echo "尝试访问属性:$name\n"; return $this->data[$name] ?? null; } public function __set($name, $value) { echo "设置属性 $name = $value\n"; $this->data[$name] = $value; } public function __isset($name) { return isset($this->data[$name]); } public function __unset($name) { unset($this->data[$name]); } public function __toString() { return "MagicExample对象,包含数据:" . json_encode($this->data); } public function __call($name, $arguments) { echo "尝试调用方法:$name,参数:" . json_encode($arguments) . "\n"; return "方法 $name 不存在"; } public function __clone() { echo "对象被克隆了\n"; $this->data = array_map(function($item) { return is_object($item) ? clone $item : $item; }, $this->data); } }
$obj = new MagicExample();
$obj->name = "张三"; echo "姓名:" . $obj->name . "\n";
if (isset($obj->name)) { echo "name属性存在\n"; }
echo $obj . "\n";
$result = $obj->nonExistentMethod("参数1", "参数2"); echo "返回值:$result\n";
$obj2 = clone $obj; ?>
|
对象比较
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
| <?php class Person { public $name; public $age; public function __construct($name, $age) { $this->name = $name; $this->age = $age; } }
$person1 = new Person("张三", 25); $person2 = new Person("张三", 25); $person3 = $person1;
if ($person1 == $person2) { echo "person1 == person2:相等\n"; }
if ($person1 === $person2) { echo "person1 === person2:相等\n"; } else { echo "person1 === person2:不相等\n"; }
if ($person1 === $person3) { echo "person1 === person3:相等(同一个对象)\n"; } ?>
|
实用技巧和最佳实践
1. 使用getter和setter方法
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
| <?php class User { private $email; private $age; public function setEmail($email) { if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { throw new InvalidArgumentException("无效的邮箱地址"); } $this->email = $email; } public function getEmail() { return $this->email; } public function setAge($age) { if ($age < 0 || $age > 150) { throw new InvalidArgumentException("年龄必须在0-150之间"); } $this->age = $age; } public function getAge() { return $this->age; } } ?>
|
2. 方法链式调用
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 32 33 34 35 36 37 38 39
| <?php class QueryBuilder { private $query = ""; public function select($fields) { $this->query .= "SELECT $fields "; return $this; } public function from($table) { $this->query .= "FROM $table "; return $this; } public function where($condition) { $this->query .= "WHERE $condition "; return $this; } public function orderBy($field, $direction = "ASC") { $this->query .= "ORDER BY $field $direction "; return $this; } public function getQuery() { return trim($this->query); } }
$query = (new QueryBuilder()) ->select("name, email") ->from("users") ->where("age > 18") ->orderBy("name") ->getQuery();
echo $query . "\n"; ?>
|
3. 单例模式
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 32 33 34 35
| <?php class Database { private static $instance = null; private $connection; private function __construct() { $this->connection = "数据库连接"; echo "数据库连接已建立\n"; } private function __clone() {} private function __wakeup() {} public static function getInstance() { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } public function getConnection() { return $this->connection; } }
$db1 = Database::getInstance(); $db2 = Database::getInstance();
var_dump($db1 === $db2); ?>
|
常见错误和注意事项
1. 忘记使用$this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php class Example { private $value = 10; public function wrongMethod() { } public function correctMethod() { return $this->value; } } ?>
|
2. 访问修饰符的误用
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php class Parent { private $privateVar = "私有变量"; protected $protectedVar = "受保护变量"; }
class Child extends Parent { public function accessVars() { echo $this->protectedVar; } } ?>
|
3. 静态方法中使用$this
1 2 3 4 5 6 7 8 9 10 11
| <?php class Example { private $instanceVar = "实例变量"; private static $staticVar = "静态变量"; public static function staticMethod() { echo self::$staticVar; } } ?>
|
总结
PHP面向对象编程的关键要点:
- 理解类和对象的关系:类是模板,对象是实例
- 合理使用访问修饰符:封装数据,控制访问权限
- 掌握构造函数和析构函数:对象的生命周期管理
- 善用静态成员:类级别的属性和方法
- 了解魔术方法:PHP提供的特殊功能
- 遵循最佳实践:使用getter/setter、方法链等
- 避免常见错误:正确使用$this、访问修饰符等
面向对象编程不仅仅是语法的改变,更是思维方式的转变。它让我们能够更好地组织代码,提高代码的可维护性和可扩展性。
通过不断练习和实际项目应用,你会发现面向对象编程的强大之处。记住,好的面向对象设计需要时间和经验的积累。
希望这篇文章能帮助你更好地理解PHP面向对象编程的基础概念!