面向对象(对象是复合数据类型 )
[toc] //md 内容表
1. 封装
- 面向对象就是把生活中要解决的问题都用对象的方式进行存储—把所有的数据用属性、方法表现出来。对象之间的互动是通过方法的调用完成互动
- 封装是隐藏对象的属性或方法(实现细节),目的是为了控制在程序中属性的读或者修改的访问级别,使用者只需要通过外部接口以特定的权限来使用类成员
- 把文件类名称与类文件名称保持一致可以实现类的自动加载
- 加载类文件,类的自动加载器,在哪里调用就在哪里加载类文件
require 'autoload.php';
类的定义
- 类的定义以关键字 class 开始,后面跟着这个类的名称。类的命名通常每个单词的第一个字母大写,以中括号开始和结束
- 类的实例化为对象时使用关键字 new,new 之后紧跟类的名称和一对圆括号
- 对象中得成员属性和方法可以通过->符号来访问
- oop 具有单继承的诟病->带来程序的高耦合性: 如果程序通过深层次继承绑定到某个具体类,即使对父类做一些简单的修改,也会对子类带来严重的破坏
访问权限
- 类成员属性 前一定要有 访问修饰符 public(公有) protected(子类和本类) private(私有)
- public 是公有的类成员,可以在任何地方被访问,可以被类以及子类或者对象都可以访问;
- protected 受保护的类成员,可以被其自身以及继承的子类访问,可以被子类继承,但是不能被对象访问,只能通过封装的方式让对象访问;
- private 私有的类成员,只能被自身访问.不能被子类继承,也不能被对象访问,只能在自身通过封装让外界访问(例如在类里面定义一个公开方法来调用私有属性);
- 对象成员的访问 对象引用->属性名称/方法名称()
- 类的实例化 new $j 对象引用
class Player{
// 成员属性 前一定要有 访问修饰符 public protected private
public $name;//抽象属性 null
public $height;
public $team;
// protected 只能被本类和子类访问
protected $playNum = 23;
// 只能被本类访问
private $weight;
// 如何给protected private属性赋值
// __construct()魔术方法 构造函数 构造器 类实例化一次就被自动调用一次 __get __set __call __callStatic
public function __construct($name,$height,$team,$playNum,$weight){
// 1. 初始化类成员 让类/实例化的状态稳定下来
// 2. 给对象属性进行初始化赋值
// 3.可以给私有成员,受保护的成员初始化赋值
$this->name = $name;
$this->height = $height;
$this->team = $team;
$this->playNum = $playNum;
$this->weight = $weight;
}
public function jog()
{
// $this 特殊的对象引用,这里是指本对象,它会变
return "$this->name is jogging,whose playNum is $this->playNum<br>";
}
public function shoot()
{
return "$this->name is shooting,weighing $this->weight<br>";
}
}
2. 继承
父类:拥有部分相同的属性和方法
继承的好处:
- 父类里面定义的类成员可以不用在子类中重复定义,节约了编程的时间和代价;
- 同一个父类的子类拥有相同的父类定义的类成员,因此外部代码调用他们的时候可以一视同仁;
- 子类可以修改和调用父类定义的类成员我们称为重写(Overwrite), 一旦子类修改了,就按照子类修改之后的功能执行;
子类
:
- 子类可以通过$this 访问父类的属性
- 子类的对象可以直接调用父类的方法和属性
PHP 的单继承特性:类不允许同时继承多个父类(extends后面只能跟一个父类名称)
类的继承(扩展)是通过 extends 关键字实现
class Son extends Product
就是 son 类继承了 Product 类- 通过类的继承可以对类属性/方法进行扩展,重写
- php oop 具有单继承的诟病->带来程序的高耦合性: 如果程序通过深层次继承绑定到某个具体类,即使对父类做一些简单的修改,也会对子类带来严重的破坏
- trait Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制
类的静态成员 static 关键字 标志静态属性/方法 静态成员由本类来调用 为所有实例共享
父类
class Product
{
public $name;
public $price;
protected $discount = 5;
public function __construct($name,$price)
{
$this->name = $name;
$this->price = $price;
}
public function show()
{
return "{$this->name}的单价为{$this->price}元";
}
final public function display()
{
return "现在为您展示的商品是{$this->name},它目前的市场价为{$this->price}元。";
}
}
子类
class Son extends Product
{
// 属性扩展
private $num;
// 重写 父类构造器
public function __construct($name,$price,$num)
{
// parent关键字 调用父类中的成员方法
parent::__construct($name,$price);
$this->num = $num;
}
// 重写父类中的普通方法
public function show() :string
{
return parent::show() . ", 数量{$this->num}个,双十一期间打{$this->discount}折。";
}
// 方法扩展
public function total()
{
return "商品{$this->name}的总价为".($this->price * $this->num) . '元';
}
// 子类不能重写父类中带有final关键字修饰的普通方法 可继承
}
Static(静态)关键字
- 静态成员:定义时在访问控制关键字后添加 static 关键字即可(访问控制关键字:public. protected. private)
- 静态属性用于保存类的公有数据,可以在不同对象间共享
- 静态方法里面只能访问静态属性
- 静态成员不需要实例化对象就可以访问
- 类的内部可以通过 self:: 或 static:: 关键字访问自身静态成员,self::$属性 self::方法()
- 通过 parent:: 关键字访问父类的静态成员,也可以通过子类::父类静态成员
- 通过 类名:: 的方式在类的外部访问静态成员
- 类的静态成员 static 关键字,属于类级别的存在,标志静态属性/方法,静态成员由本类来调用,为所有实例共享
class User
{
public static $name = '胡歌';
protected $_config = [
'auth_on'=>'true',
'auth_type'=>1//认证方式 1实时认证 2位登录认证
];
public static $nation = 'China';
private $salary;
static $count = 0;//记录已登录用户的总数
// public $count = 0;//记录已登录用户的总数
public function __construct($name,$salary)
{
// 静态成员与类的实例无关 不能用$this访问, 用self:: 类的引用 访问静态成员
self::$name = $name;
$this->salary = $salary;
self::$count ++;
// $this->count ++;
}
public static function getCount(){
return sprintf('目前该网站的在线人数为%d<br>',self::$count);
}
// 对象被销毁时自动调用
public function __destruct()
{
self::$count --;
}
// 在静态上下文中访问不到普通属性
// public static function getConfig()
// {
// return sprintf('认证开关:%s<br>,认证类型%d',$this->_config['auth_on'],$this->_config['auth_type']);
// }
public function getConfig()
{
return sprintf('认证开关:%s<br>,认证类型%d',$this->_config['auth_on'],$this->_config['auth_type']);
}
}
- 优点:
- 在内存中即使存在多个实例,静态成员在内存中只占一份,为所有实例共享,普通成员以实例的方式会创建多个内存
- 静态成员的执行效率比实例化高,不用创建对象就可以直接调用
- 缺点: 静态成员不自动进行销毁,而实例化的则可以销毁
- 静态方法不能访问类中的普通属性(此时还不存在对象)
- 普通成员是对象级别的存在,只能通过对象访问 默认都提供了$this 指针
- 静态成员是类级别的存在 随着类的加载而加载 没有提供$this 指针, 优先于对象存在 只能用类引用 self::访问
- 每 new 一次得到不同实例 就会产生一个新的对象 id 单例模式 类只能被实例化一次 只能产生唯一的对象 id
- 后期静态绑定
class Father
{
protected static $_instance = null;
static public function getInstance()
{
// 单例模式 保证实例化只发生一次
// 后期静态绑定开始
if(static::$_instance === null)
{
static::$_instance = new static(); //new static 生成的实例 是由调用对象决定的
}
return static::$_instance;
//if(self::$_instance === null)
//{
// self::$_instance = new self(); //返回的实例是不变的 无论谁去调用 返回的是同一个类的实例
//}
//return self::$_instance;
}
}
子类 1
class Son1 extends Father
{
protected static $_instance = null;
}
子类 2
class Son2 extends Father
{
protected static $_instance = null;
}
var_dump(Father::getInstance());
var_dump(Son1::getInstance());
var_dump(Son2::getInstance());
重写和 Final 关键字
- 子类中编写跟父类完全一致的方法可以完成对父类方法的重写,方法参数最好有默认参数
- 对于不想被任何类继承的类可以在 class 之前添加 final 关键字
- 对于不想被子类重写(overwrite, 修改)的方法,可以在方法定义前面添加 final 关键字
数据访问
- parent 关键字可以可用于调用父类中被子类重写了的方法
- self 关键字可以用于访问类自身的成员方法,静态成员和类常量;不能用于访问类自身的属性!!!使用常量的时候不需要在常量 const 名称前面添加$符号
- static::关键字用于访问类自身定义的静态成员,访问静态属性时需要在属性前面添加$符号。
- 常量属性 const 不能使用对象访问,仅能使用类访问,在类本体内可以使用“self::常量名”,在类本体外可以使用“类名::常量名”
3. 多态
- php 中多态指的是方法的重写
- 多态实现了动态绑定的功能
1. 抽象类/具体类
- 当接口中的某些方法对于所有的实现类都是一样的实现方法,只有部分方法需要用到多态的特性
- 大的方向定义,细节不用
- abstract 关键字用于定义抽象类
- 在抽象方法前面添加 abstract 关键字可以标明这个方法是抽象方法不需要具体实现{}
- 抽象类中可以包含普通的方法,有方法的具体实现
- 继承抽象类的关键字是 extends
- 继承抽象类的子类需要实现抽象类中定义的抽象方法
- 抽象类不能被实例化,当子类继承抽象类的时候,所有的抽象的方法都必须定义
- 抽象类 abstract 声明抽象类 不能被实例化
- 抽象类经常用于描述一些事物所具有的特性和行为,但是没有具体的实现
- 抽象类: 设计与实现分离 抽象类可以为子类提供一些公共的接口以作为子类重写的模板来使用
- 具体类是根据自己的需求去实现抽象类
- 具体类继承抽象类
2. 接口
- 接口就是把不同类的共同行为进行定义,然后在不同的类里面实现不同的功能
interface
定义接口,implements
用于表示类实现某个接口- 接口里面的方法没有具体的实现,无
{}
- 实现了某个接口的类必须提供接口中定义的方法的具体实现
- 不能实例化接口,但是能够判断某个对象是否实现了某个接口。i
nstanceof
关键字判断某个对象是否实现了某个接口 $object instanceof interface - 接口可以继承接口(
interface extends interface
) 接口中定义的所有方法都必须是公有,这是接口的特性
接口是定义,类是实现 trait 结构,解决 oop 的高耦合性 声明接口用
interface
interface USB {// 接口中只允许声明两类成员 1. 接口常量 2. 公共的抽象方法 function run(); }
- 接口都是静态,没有具体的方法,只有公共的抽象方法,接口的实现用
implements
class Ukey implements USB { function run() { return '运行USB键盘设备<br>'; } }
- 接口属于先继承再实现,工作类 extends 抽象类(普通类)implements interface1,interface2,…..
- 接口的实例化
class Computer { // 计算机类中的一个方法 要可以应用任意一种USB设备 function useUSB($usb)//参数是一个对象 { return $usb->run(); } }
总结
- 变量是实现数据的复用,函数是实现代码块的复用,类是具有相同属性和方法的对象的集合
- class: 类声明
- 使用对象引用也可以访问静态成员,不推荐
echo $user3->getCount();
- parent::方法名() 可以调用父类成员
parent::__construct($name,$price);
- class: 类声明
- new: 类实例化
- public: 公开成员,随处可见
- protected: 受保护成员,仅在当前类或子类中可见
- private: 私有成员, 仅在当前类可见
- spl_autoload_register(): 自动加载器
- extends: 类的扩展
- static: 声明类的静态成员 new static 生成的实例 是由调用对象决定的
- self: 类的引用,静态成员返回的实例是不变的 无论谁去调用 返回的是同一个类的实例(父类)
- $this: 实例引用,对象成员之间的内部访问
- trait: 类功能横向扩展