面向对象之继承

一、什么是继承

对象的继承是指从一个类派生出另外的一个类的过程,就像孩子是从父母那里继承品性一样。

  • 关键字 extends
  • PHP只能有一个父类
  • 关键词 instanceof 可以用来查看一个特别的对象是不是属于一种特定的类的类型
require 'demo.class.php';
$obj = new ChildClass();
if( $obj instanceof Demo ){
$obj->test();
}


1、全部继承

子类继承(也就是具有)父类的全部属性和方法

class Person
{
public $name;
public $age;
public $info;

public function __construct($name,$age,$info)
{
$this->name = $name;
$this->age = $age;
$this->info = $info;
}

public function talk(){
return '你好面试官,我叫' . $this->name . ',今年'.$this->age . ',特长:'. $this->info;
}
}

class Man extends Person
{

}

$jack = new Man('jack',18,'帅的令人发指');
echo $jack->talk();


2、部分继承

子类还可以添加自己的成员,从而可以保持和父类有所区别

class Man extends Person
{
public function checkAge(){
return $this->age>=18 ? '拉出去当壮丁' : '还是小鲜肉';
}
}

$jack = new Man('jack',18,'帅的令人发指');
echo $jack->talk();
echo $jack->checkAge();


3、无限多子继承

class Man extends Person
{
public function checkAge(){
return $this->age>=18 ? '拉出去当壮丁' : '还是小鲜肉';
}
}

class Women extends Person {

}


二、访问控制

封装 我们为什么要使用面向对象编程呢?既然只需要使用方法就可以写出复杂且实用的网站? OOP的真正价值在于封装,它的意义在于将相互关联的一组值和函数封装在一起,组成一个编程单元

封装的概念 : 通过修饰符 改变成员属性或者成员方法的访问权限,达到保护的作用。


修饰符

public

protected

private

本类内

Y

Y

Y

子类内

Y

Y

N

外部

Y

N

N


类的属性和成员方法在哪里可以用、可以被访问

class User extends Demo
{
public $username = 'jack';
protected $truename = '老聂';
private $age = '18';

public function printInfo(){
echo $this->username;
echo $this->truename;
echo $this->age;
}

private function secret(){
echo "这是个秘密!";
}
}


  • 一个公共的(Public)成员可以从任何一个地方访问:类本身内部,派生的子类和其他类。
  • 类的受保护的(protected)成员只能在类本身以及子类中访问。
  • 私有(private)的限制是最严格的,这些成员只能在声明它们的类中进行访问。私有的类成员不能被子类或者这些类的一个对象实例访问到。

聂哥友情备注:作为一个惯例,私有变量名通常以一个下划线开始。这在很多面向对象编程语言中都是这样做的,即便它并不是必须这样做

访问修饰符的范围

子类复写父类中的方法时,子类中的 访问修饰符的范围要大于等于 父类的【 继承只能发扬光大,至少保持不变。不可以丢失东西。

在UML中的体现

  • ​+​​ 公共的
  • ​-​​ 私有的
  • ​#​​ 受保护的

三、继承中的构造方法和析构方法

在子类里父类的构造函数会不会执行,分两种情况:

  1. 如子类不定义构造函数 ​​__construct()​​,则父类的构造函数默认会被继承下来,且会自动执行。
  2. 如子类定义了构造函数 ​​__construct()​​,因为构造函数名也是​​__construct()​​,所以子类的构造函数实际上是覆盖(override)了父类的构造函数。这时执行的是该子类的构造函数
class Demo
{
function __construct()
{
echo '正在进行构造...<br/>';
}
function test(){
echo '这是什么地方<br/>';
}
function __destruct()
{
echo '正在进行析构...<br/>';
}
}
class ChildClass extends Demo
{
function __construct()
{
//如果要在子类里执行父类的构造函数
//parent::__construct();
echo '我是后代构造函数-正在进行构造...<br/>';
}
}


注意​parent::__construct()​​; 语句不一定必须放在子类的构造函数中。放在子类的构造函数中仅仅保证了其在子类被实例化时自动执行。

四、重写

重载就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。PHP不支持,但可用构造方法实现。

重写又叫覆盖,就是将父类继承下来的属性或方法重新定义,只有保护的或公共的属性或方法能够被重写

1、重写一个方法,子类定义的方法必须和父类的方法具有完全相同的名称和参数数量

class Person
{
public $name;
public $age;
public $info;

public function __construct($name,$age,$info)
{
$this->name = $name;
$this->age = $age;
$this->info = $info;
}

public function talk($fuhao){
return '你好面试官,我叫' . $this->name . ',今年'.$this->age . ',特长:'. $this->info;
}
}

class Man extends Person
{
public function checkAge(){
return $this->age>=18 ? '拉出去当壮丁' : '还是小鲜肉';
}

public function talk(){

}
}


2、​​PHP5.3+​​ 方法重写必须要相同个数的参数,如果非多个参数,这时要给其他参数设置默认值

class Person
{
public $name;
public $age;
public $info;

public function __construct($name,$age,$info)
{
$this->name = $name;
$this->age = $age;
$this->info = $info;
}

private function talk($fuhao){
return '你好面试官,我叫' . $this->name . ',今年'.$this->age . ',特长:'. $this->info;
}
}
class Man extends Person
{
public function checkAge(){
return $this->age>=18 ? '拉出去当壮丁' : '还是小鲜肉';
}

public function talk($fuhao='没办法,我是来充数的'){

}
}


3、私有属性或私有方法的重写问题

  1. 私有属性和方法不能覆盖,但其实子类可以定义跟父类私有的同名属性或方法,只是当做一个自身的新属性或方法来看待而已

参数必须保持一致(PHP5.4前)

class A
{
private $name = '山炮';
public function getName()
{
return $this->name;
}
}
class B extends A
{
private $name = '二货';

public function getTest()
{
return $this->getName().$this->name;
}
}
$b = new B;
echo $b->getTest();//山炮二货


3.2 访问私有属性 类中的私有属性或者方法是不能被继承的,但是当一个父类A中的方法FunA,调用了父类A中的Private私有属性或着方法的时,这个FunA在被继承以后,将能继续通过​​$this​​访问父类A中的Private私有属性或者方法,无论继承子类中对父类A中的私有属性或方法如何重新实现,直到FunA在继承子类中被重写

class A
{
private $name = '山炮';
public function getName()
{
return $this->name;
}
}
class B extends A
{
public $name = '二货';

}
$b = new B;
echo $b->name; //二货
echo $b->getName(); //山炮


4、构造方法重写问题,比较宽松,重写的时候参数可以不一致。

class Person
{
public $name;
public $age;
public $info;

public function __construct($name,$age,$info)
{
$this->name = $name;
$this->age = $age;
$this->info = $info;
}

private function talk($fuhao,$cans=''){
return '你好面试官,我叫' . $this->name . ',今年'.$this->age . ',特长:'. $this->info;
}
}

class Man extends Person
{
public function __construct()
{
echo '我的方法没有参数';
}
}

$jack = new Man();


5、非完全重写

class Person
{
public $name;
public $age;

public function __construct($name,$age)
{
$this->name = $name;
$this->age = $age;
}

public function talk(){
return '你好面试官,我叫' . $this->name . ',今年'.$this->age;
}
}

class Man extends Person
{
public $info;
public function __construct($name,$age,$info)
{
parent::__construct($name,$age);
$this->info = $info;
}

public function talk(){
return '我先敲门!'.parent::talk().',特长:'.$this->info;
}
}

$jack = new Man('jack',18,"帅的令人发指");

echo $jack->talk();


五、不允许继承或重写 final

1.类里绝大部分方法,都是可以重写的。唯一的例外就是被定义了FINAL的方法:
final function myFunc(){

}


  • 属性不能被定义为 final,只有类和方法才能被定义为 final。
  • final关键字应该放在其他修饰符protect/public/private/static之前。
  • 定义为final的方法不能被任何子类所重写。
  • 类也可以声明为final,这意味着它不能被扩展。
2. 不能继承的类
final A{

}







​ ​