继承概念
- 继承不能简单的理解成子类定义时,会把父类的属性声明,方法定义拷贝一份,而是建立了继承查找关系
- 以一个类为父类,另一个类作为其子类,子类在继承了父类属性/方法的基础上还可以进一步增添或修改,提高类代码复用性
- 继承的根本作用是解决代码的复用性,减少冗余度,同时利于类的维护和功能的扩展
- 继承时的关键字是 extends 不能修改
权限修饰符
个人理解其实继承不用想那麽多, extends之后,子类和父类建立继承查找关系,子类对象访问一个属性/方法,先到子类找,子类有优先用子类,子类没有用父类,至于有没有权限用,根据权限修饰符判断就完事了!
非继承时
- public 在类{ }内外都可以访问
- protected和private只能在类{ }内访问,可以在类{ }添加公共方法在类外间接访问
继承时
- public 父类和子类{ }内以及类{ }外都能访问
- protected 在父类和子类{ }内可以访问,在类{ }外不能访问
- private 只能在本类{ }内访问
继承示例
代码
class Person{
public $hobby = '画画';
protected $money = 1000;
private $friend = '紫霞';
public function getFriend(){
// private属性/方法只能在本类{}内访问
echo $this->friend,'<br>';
}
}
class Stu extends Person{
public $school = '蓝翔';
public function getMoney(){
// proteced属性/方法可以在本类{}和子类{}内访问
echo $this->money,'<br>';
}
}
$s = new Stu();
echo $s->hobby,'<br>';
// protect和privat属性/方法无法在类{}外访问
//echo $s->money;
//echo $s->friend;
$s->getFriend();
内存
继承的细节
子类不能直接继承多个父类,但可以多继承
- 多继承就可以实现直接继承多个父类的效果
- 这里仅限php,有些语言可以直接继承多个父类
class A{
public $name = 'zy';
}
class B extends A{
public $age = 18;
}
class c extends B{}
//class c extends A,B{} 这是错的
构造方法的继承(子类未重写构造函数,创建子类)
- 构造函数的继承与普通函数的继承一样
- 子类未重写构造函数,创建子类对象时会默认直接调用父类
- 子类定义了构造函数,就是重写了构造函数
- 如果需要调用父类的构造函数,可使用
parent::__construct()
class Mysql{
protected $conn;
public function __construct(){
$this->conn = mysqli_connect('localhost','root','');
}
}
class Mydb extends Mysql{
public function __construct(){
// 如果子类继承时重写了构造函数,保险一点
parent::__construct();
// 然后再加自己的业务逻辑
echo '重写了构造函数';
}
}
$mysql = new Mydb();
子类调用父类方法的三种方式
class A{
public function say(){
echo 'hello';
}
}
class B extends A{
public function test(){
// 第1种:parent绑定的是父类类名,静态访问,不管子类有没有重写say()方法,只能调到父类的
parent::say();
// 第2种:this绑定的是调用该方法的对象,如果子类中没写say(),可以调到父类的say(),如果子类重写say(),那这种方法只能调到子类的say()
$this->say();
// 第3种:静态访问,相比于第1种不够灵活,若改了类名,这里就g了
A::say();
}
}
$b = new B();
$b->test();
重写 (override)
- 方法的重写就是子类有一个方法,和父类(基类)中的某个方法的名称、参数个数都一样,那么我们就说子类的这个方法重写了父类的那个方法
- 属性也是可以重写的,子类中的属性名和父类一样,就是子类重写了父类的该属性
- 子类重写父类属性/方法后,子类对象优先调用子类的属性/方法
- 父类的私有属性/方法不能被子类重写
- override,有人也叫覆盖,但官方文档一般称为重写,后面统一称为重写
方法重写细节
在子类内通过 parent::方法名
访问父类被重写的方法
- 这其实就是静态访问,用
parent
代替父类名 - 静态访问的规则这里不罗嗦,能不能访问到依据权限修饰符判定
class A{
public function say(){
echo 'hello';
}
}
class B extends A{
public function say(){
echo 'shit';
parent::say();
}
}
$b = new B();
$b->say();
方法重写时名称和参数都要一致,如果参数不一致会有警告
- 若方法名一样,参数不一致,系统会警告,但依旧重写了父类方法
- 如果父类的参数使用了类型约束,还必须保证数据类型一致
class A{
public function say($num){
echo 'hello';
}
}
class B extends A{
public function say(){
echo 'shit';
}
}
$b = new B();
// Warning: Declaration of B::say() should be compatible with A::say($num)
$b->say();
构造方法的重写
- 构造方法重写规则与普通方法一样
- 父类的构造函数会做很多初始化工作,重写构造函数会导致初始化工作完成不了,如操作数据库
- 所以,如果子类继承时需要重写构造方法,保险一点,调用 parent::__construct()
class Mysql{
protected $conn;
public function __construct(){
$this->conn = mysqli_connect('localhost','root','');
}
}
class Mydb extends Mysql{
public function __construct(){
// 如果子类继承时重写了构造函数,保险一点
parent::__construct();
// 然后再加自己的业务逻辑
echo '重写了构造函数';
}
}
$mysql = new Mydb();
重写时,权限不能比父类严格
- 对属性和方法都适用
class A{
public function say($num){
echo 'hello';
}
}
class B extends A{
// Fatal error: Access level to B::say() must be public (as in class A)
protected function say(){
echo 'shit';
}
}
父类的私有方法/属性不能被重写
- 私有属性和方法会有其所在类的类名标志 [wife:B:private]=>凤姐 [wife:A:private]=>紫霞,其实并没有重写
- 如果say() 方法是重写,子类和父类参数不一致,是会报错的!
class A{
private $wife = '紫霞';
private function say($num){
echo 'hello';
}
}
class B extends A{
private $wife = '凤姐';
public function say(){
echo 'shit';
}
}
$b = new B();
$b->say(); // shit
print_r($b); // Object ([wife:B:private]=>凤姐 [wife:A:private]=>紫霞)
验证继承并不是复制
使用 __CLASS__返回当前的类名 验证
1.子类没对foo()方法重写,foo() 方法运行环境在A类中
// 若是复制,运行环境应该发生在B
class A{
public function foo(){
echo '你正在运行',__CLASS__,'下的',__METHOD__,'方法',"\n";
}
}
class B extends A{
}
$b = new B();
$b->foo(); // 你正在运行A下的A::foo方法
2.子类重写foo()方法,此时才真正将foo()方法写入B内存
class A{
public function foo(){
echo '你正在运行',__CLASS__,'下的',__METHOD__,'方法',"\n";
}
}
class B extends A{
public function foo(){
echo '你正在运行',__CLASS__,'下的',__METHOD__,'方法',"\n";
}
}
$b = new B();
$b->foo(); // 你正在运行B下的B::foo方法
私有属性继承后,子类添加同名私有属性,访问谁的问题?
案例一
父类和子类有同名私有wife属性,其实标签不同,不是重写
class Human{
private $wife = '小仙女';
public function show(){
echo $this->wife,'<br>'; // 小仙女
}
}
class Stu extends Human{
private $wife = '凤姐';
public function test(){
echo $this->wife,'<br>'; // 凤姐
$this->show();
}
}
$lisi = new Stu();
$lisi->test();
print_r($lisi); // Stu Object([wife:Stu:private]=>凤姐 [wife:Human:private]=>小仙女)
终极解决解决办法,遇到private就给他加标签
案例二
将案例一中 子类中wife属性改为protected修饰
class Human{
private $wife = '小仙女';
public function show(){
echo $this->wife,'<br>'; // 小仙女
}
}
class Stu extends Human{
protected $wife = '凤姐';
public function test(){
echo $this->wife,'<br>'; // 凤姐
$this->show();
}
}
$lisi = new Stu();
$lisi->test();
print_r($lisi); // Stu Object([wife:protected]=>凤姐 [wife:Human:private]=>小仙女)
终极解决解决办法,遇到private就给他加标签
案例三
去掉案例一子类中的wife属性
class Human{
private $wife = '小仙女';
public function show(){
echo $this->wife,'<br>';
}
}
class Stu extends Human{
public function test(){
echo $this->wife,'<br>'; // Notice: Undefined property: Stu::$wife
$this->show(); // 小仙女
}
}
$lisi = new Stu();
$lisi->test();
print_r($lisi); // Stu Object([wife:Human:private]=>小仙女)
终极解决解决办法,遇到private就给他加标签
案例四
去掉案例一父类中的wife属性
class Human{
public function show(){
echo $this->wife,'<br>'; // Fatal error: Uncaught Error: Cannot access private property Stu::$wife
}
}
class Stu extends Human{
private $wife = '凤姐';
public function test(){
echo $this->wife,'<br>'; // 凤姐
$this->show();
}
}
$lisi = new Stu();
$lisi->test();
print_r($lisi); // Stu Object([wife:Stu:private]=>凤姐)
案例五
父类的属性被子类重写覆盖
class Human{
protected $wife = '小仙女';
public function show(){
echo $this->wife,'<br>';
}
}
class Stu extends Human{
protected $wife = '凤姐';
public function test(){
echo $this->wife,'<br>'; // 凤姐
$this->show(); // 凤姐
}
}
$lisi = new Stu();
$lisi->test();
print_r($lisi); // Stu Object([wife:protected] => 凤姐)
关于静态属性的继承
静态属性具有独立空间,为所有对象共有,因此一改全改,个人猜测!
// 没精力研究 留个代码观摩
class Base
{
public static $var = 'var';
public static function testStaticFun()
{
echo 'func';
}
}
class A extends Base
{
public function testSelf()
{
echo self::$var,'<br>';
}
public function testParent()
{
echo parent::$var,'<br>';
}
public function setSelf()
{
self::$var = 'self';
}
public function setParent()
{
parent::$var = 'parent';
}
public static function testStaticFun()
{
parent::testStaticFun();
echo 'over','<br>';
}
}
$objA = new A();
$objA->testSelf(); // var
$objA->testParent(); // var
$objA->setSelf();
$objA->testSelf(); // self
$objA->testParent(); // self
echo Base::$var,'<br>'; // self
$objA->setParent();
$objA->testSelf(); // parent
$objA->testParent(); // parent
echo Base::$var,'<br>'; // parent
Base::testStaticFun(); // func
A::testStaticFun(); // func over