PHP与MySQL程序设计 学习笔记 第六章 面向对象的PHP
封装将接口与内部实现分离。
继承使派生出来的类拥有基类的所有属性和方法。
多态使继承出来的类可重写基类的某些方法,使得可以根据类的上下文选择特定的属性和方法。
创建类:
class Employee {
private $name;
private $title;
protected $wage;
protected function clockIn() {
echo "Member $this->name clocked in at " . date("h:i:s");
}
protected function clockOut() {
echo "Member $this->name clocked out at " . date("h:i:s");
}
}
根据预定义的类创建对象常被称为类的实例化:
$employee = new Employee();
类的属性可以不声明:
class a { }
$aObj = new a();
$aObj->member1 = 5;
var_dump($aObj);
运行它:
但最好在类中声明属性。
获取对象中属性:
$employee->name; // 获取name成员的值
在类中调用属性:
// 为employee类定义新方法
function setName($name) {
$this->name = $name; // 引用当前类中属性时要使用$this
}
属性作用域:
1.public:可由相应的对象直接操作和访问。
2.private:只能在定义该属性的类中被访问。
3.protected:可在定义该属性的类及其派生类中被访问。
4.final:派生类中不能覆盖此属性。也可用于方法。但我用php 7测试时会报错final不能用于属性。
class a {
public $v = 8;
}
class b extends a {
public $v = 5;
}
$bObj = new b();
var_dump($bObj);
运行它:
class a {
private $v = 8;
public function basefunc() {
$this->v = 2;
}
}
class b extends a {
public $v = 5;
public function func() {
$this->v = 3;
}
}
$bObj = new b();
print_r($bObj);
$bObj->func();
print_r($bObj);
$bObj->basefunc();
print_r($bObj);
运行它:
由上图,如果派生类中的public属性覆盖了基类中的public属性,则基类中的public属性不再存在;如果覆盖的是基类中的private属性,则会有两个同名属性,一个是public的,一个是private的,在派生类中访问的是派生类的public属性,在基类中访问的是基类中的private属性。如果派生类中的同名属性是protected的,则结果相同,也会出现两个同名属性,且派生类中访问的是派生类中的protected属性,基类中访问的是基类的private属性。
派生类在覆盖基类中的同名属性时,访问权限必须更大,不能缩小:
class a {
public $v = 8;
}
class b extends a {
private $v = 5;
}
运行它:
当基类中的属性v是protected的时,也会报类似的错误,要求派生类中的同名属性的访问权限要与基类中的同名属性的访问权限相同。
当对类中不存在的属性赋值时,会调用:
它接受一个属性名和其值作为输入。
class a {
public $v = 2;
public function __set($propName, $propValue) {
print("Nonexistent variable $propName." . PHP_EOL);
}
}
$aObj = new a();
$aObj->v = 4;
$aObj->v1 = 3;
print_r($aObj);
运行它:
也可通过此方法扩展类:
class a {
public $v = 2;
public function __set($propName, $propValue) {
$this->$propName = $propValue;
}
}
$aObj = new a();
$aObj->v = 3;
$aObj->v1 = 4;
var_dump($aObj);
运行它:
当__set方法未定义时对不存在的属性赋值时,效果同上。
当访问一个不存在的属性时会调用:
class a {
public $v = 2;
public function __get($propName) {
print("in __get");
print("Nonexistant variable $propName.");
}
}
$aObj = new a();
print($aObj->v . PHP_EOL);
print($aObj->v1);
运行它:
当__get方法未定义时访问不存在的属性会报错找不到该属性:
class a {
public $v = 2;
}
$aObj = new a();
print($aObj->v . PHP_EOL);
print($aObj->v1);
运行它:
类中可定义常量,常量在对象的整个生命周期中都保持不变。
常量的定义和使用:
class a {
const PI = 3.14159;
}
$aObj = new a();
print($aObj::PI);
声明方法:
scope function funcName(parameters) { }
public方法不必显式声明作用域。
调用方法:
$obj->funcName(arguments);
方法作用域:
1.public:可在任何时间地点访问。书上演示:
但我用php 7测试时发现非static的方法必须通过对象调用,否则报错这是被弃用的特性,报错后是正常的输出,可通过修改error_reporting的错误报告等级消除警告。
2.private:只能在类的内部使用。
3.protected:只能在类中或该类的派生类中使用。
4.abstract:在父类中声明,在子类中实现。只有声明为abstruct的类可声明抽象方法:
abstract class a { // 定义抽象方法的类必须也是抽象类
abstract public function func();
}
class b extends a {
private function func() { } // 错误,实现时访问级别也必须是public
}
5.final:可防止被子类覆盖:
class a {
final public function func() { }
}
class b extends a {
public function func() { } // 报错,无法重写方法
}
类型提示是php 5新特性,可确保传递给方法的对象类型是期望类型。php 5的类型提示只用于类类型和数组类型,无法为整数、浮点数、字符串提供类型提示。而php 7的类型提示可扩展至标量数据类型:
class a { }
function func(a $aObj) { }
func(1); // 报错
构造函数名为__constructor
,但在php 4时,构造函数名字与类名相同,为了兼容,在现在的php版本中如果找不到名为__constructor
的构造函数,则会接着寻找与类同名的函数,如果找到了,会将其认为是构造函数:
class a {
public function a() {
print("in constructor");
}
}
$aObj = new a();
运行它:
php不会自动调用父类的构造函数,必须在子类中显式调用:
class a {
public function __construct() {
print("in parent's constructor\n");
}
}
class b extends a {
public function __construct() {
parent::__construct(); // 也可以改为a::__construct()
print("in child's constructor");
}
}
$bObj = new b();
运行它:
如果没有显式调用父类的构造函数,则第一句输出不会被输出。
书上说即使两个类没有继承层次关系,也能在一个类的构造函数中使用另一个类的构造函数,但我用php 7测试:
class a {
public function __construct() {
print("in parent's constructor\n");
}
public $va = 1;
}
class b {
public function __construct() {
a::__construct(); // 会报错
print("in child's constructor\n");
}
public $vb = 2;
}
$bObj = new b();
脚本执行结束时,php会撤销内存中所有对象,此时会调用对象的析构函数:
class a {
public function __construct() {
print("in constructor" . PHP_EOL);
}
public function __destruct() {
print("in destructor" . PHP_EOL);
}
}
$aObj = new a();
运行它:
静态成员不属于对象,属于该对象所属的类:
class Vistor {
public static $Visitors = 0;
public function __construct() {
++self::$Visitors;
}
public static function getVisitors() {
return self::$Visitors;
}
}
$aVistor = new Vistor();
print(Vistor::getVisitors() . "\n"); // 通过类名访问静态方法
print($aVistor::getVisitors()); // 通过对象名访问静态方法
print(Vistor->getVisitors() . "\n"); // 错误,类名没有->运算符
print($aVistor->getVisitors() . "\n"); // 正确,可通过对象->访问静态方法
print(Vistor::$Visitors . "\n"); // 通过类名访问静态属性
print($aVistor::$Visitors . "\n"); // 通过对象名访问静态属性
print(Vistor->$Visitors . "\n"); // 错误,类名没有->运算符
print($aVistor->$Visitors); // 错误,报错该对象不存在$Visitors属性
总结:
1.静态属性和静态方法都可以通过::
来访问;
2.类名只能通过::
访问静态对象;
3.静态方法可通过类名或对象名访问,而静态属性只能通过类名访问;
4.静态方法也可通过对象名->静态方法名
访问。
最好在编写代码时在类外使用静态成员都通过类名::静态成员
来访问,在类内最好使用self::静态成员
来访问,这样比较清晰。
php 5引入了instanceof关键字,可确定一个对象是否是一个类的实例:
class a { }
$aObj = new a();
if ($aObj instanceof a) {
print("1"); // 会被输出
}
派生类对象是基类的实例:
class a { }
class b extends a { }
$aObj = new a();
$bObj = new b();
if ($bObj instanceof a) {
print("1"); // 会输出
}
if ($aObj instanceof b) {
print("2"); // 不会输出
}
php 5.3新增了创建类别名的函数:
class a { }
class_alias("a", "b");
$bObj = new b();
if ($bObj instanceof a) {
print("1"); // 会被输出
}
确定一个类是否存在:
class a { }
if (class_exists("a")) {
print("1"); // 会被输出
}
返回一个对象所属的类名,如果它不是类的对象则返回false:
class a { }
$aObj = new a();
$aInt = 1;
$objName1 = get_class($aObj);
$objName2 = get_class($aInt);
if ($objName1) {
print($objName1 . "\n");
} else {
print("not a obj\n");
}
if ($objName2) {
print($objName2);
} else {
print("not a obj");
}
运行它:
返回类中包含的所有public方法名:
class a {
public $v = 4;
public function pubFunc() { }
protected function proFunc() { }
private function priFunc() { }
}
print_r(get_class_methods("a"));
运行它:
返回类中定义的public属性:
class a {
public $v = 4;
public $v1;
protected $v2;
private $v3;
public function func() { }
}
print_r(get_class_vars("a"));
运行它:
返回当前脚本中定义的所有类名,返回值根据php发行包不同而不同:
class a { }
print_r(get_declared_classes());
运行它:
自定义的类名在最后输出。
返回对象的所有public属性:
class a {
public $v = 4;
public $v1;
protected $v2;
private $v3;
}
$aObj = new a();
$aObj->v4 = 5;
print_r(get_object_vars($aObj));
运行它:
确定对象所属的父类,如果参数为字符串,则返回该字符串表示的类的父类:
class a { }
class b extends a { }
$bObj = new b();
print(get_parent_class("b") . "\n");
print(get_parent_class($bObj));
运行它:
确定一个接口是否存在:
interface a { }
if (interface_exists("a")) {
print("1\n");
}
if (!interface_exists("b")) {
print("2");
}
运行它:
如果一个对象属于一个类或该类的子类时,返回true,否则返回false的函数:
class a { }
class b extends a { }
$bObj = new b();
if (is_a($bObj, "b")) {
print("1\n");
}
if (is_a($bObj, "a")) {
print("2");
}
运行它:
php 5.0.0到5.3.0中删除了此函数,使得遇到此函数时显示一个E_STRICT警告。
当对象是一个类的子类对象时,以下函数返回true:
class a { }
class b extends a { }
$bObj = new b();
if (is_subclass_of($bObj, "a")) {
print("1\n"); // 会被输出
}
if (is_subclass_of($bObj, "b")) {
print("2");
} else {
print("3"); // 会被输出
}
确定一个对象中是否有某方法;
class a {
public function func() { }
}
$aObj = new a();
if (method_exists($aObj, "func")) {
print("1"); // 会输出
}
if (method_exists($aObj, "__construct")) {
print("1"); // 不会输出
}
php 5引入了自动加载对象,允许通过定义__autoload函数自动加载类,当引用未在脚本中定义的类时会自动调用该函数:
./myClass.php
<?php
class myClass {
public function __construct() {
echo "myClass init'ed successfuly!!!";
}
}
?>
./index.php
<?php
// we have writen this code where we need
function __autoload($classname) {
$filename = "./". $classname .".php";
include_once($filename);
}
// we've called a class ***
$obj = new myClass();
该方法已在php 7.2.0中废除,不要使用它。
final类不能作为基类:
final class a { }
class b extends a{ }
运行它:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)