详解php中的类与对象(继承)

简介

在php中,类型的继承使用extends关键字,而且最多只能继承一个父类,php不支持多继承。

class MyClass
{
 public $dat = 0;
 public function __construct($dat) {
  $this->dat = $dat;
 }
 public function getDat() {
  return "$this->dat\n";
 }
}
class MySubClass extends MyClass
{
 public function getDat() {
  return "dat: $this->dat\n";
 }
}
$a = new MyClass(3);
$b = new MySubClass(4);
echo $a->getDat();  // 3
echo $b->getDat();  // dat: 4 

方法覆盖

包括构造函数在内,子类可以重新定义同名的类方法以覆盖父类方法。覆盖时遵循以下规则:

1.除构造函数之外,其他函数在覆盖时,函数的参数列表必须相同

2.包括构造函数在内,方法被覆盖后,调用子类方法时并不会自动调用父类方法

3.如果父类要禁止方法被子类覆盖,可以使用final来声明方法,这时如果子类仍要覆盖父类方法,将会出错

class MyClass
{
 private $name = "";
 public $num = 0;
 public $str = "";
 public function __construct($name) {
  $this->name = $name;
  $this->num = 100;
  $this->str = "none";
 }
 public function getName() {
  return $this->name;
 }
}
class MySubClass extends MyClass
{
 public function __construct($name, $str) {
  parent::__construct($name);    // 调用父类方法
  $this->num = "0";
  $this->str = $str;
  echo parent::getName()."\n";    // 调用父类方法
 }
 public function getName() {
  return parent::getName()."$this->str\n"; // 调用父类方法
 }
}
$b = new MySubClass("myName", true);  // myName
echo $b->getName();          // myName1
class MyClass
{
 final public function getName() {
 }
} 

属性重定义

在子类中,可以访问父类中的public和protected属性成员,除非重定义了同名的自有属性,这时,父类中的属性将无法访问。

方法则不同,子类对方法进行覆盖后,仍然可以访问到父类方法。

class MyClass
{
 public $a = 1;
 protected $b = 2;
 private $c = 3;
 public function f1() {
  echo "MyClass f1\n";
  echo "\$a:$this->a; \$b:$this->b; \$c:$this->c;\n";
 }
 protected function f2() {
  echo "MyClass f2\n";
  echo "\$a:$this->a; \$b:$this->b; \$c:$this->c;\n";
 }
 private function f3() {
  echo "MyClass f3\n";
 }
}
class MySubClass extends MyClass
{
 public $b = 22;
 public $c = 33;
 public function f1() {
  echo "MySubClass f1\n";
  // 继承到父类中的$a属性,直接使用
  echo "\$a:$this->a; \$b:$this->b; \$c:$this->c;\n";
  // 调用父类中的同名方法
  parent::f1();
  // 继承到父类中的f2()方法,直接使用
  $this->f2();
 }
 // 父类的f3()是私有的,这里的定义与父类无关
 public function f3() {
  echo "MySubClass f3\n";
 }
}
$b = new MySubClass;
$b->f1();echo "\n";
/*
MySubClass f1
$a:1; $b:22; $c:33;
MyClass f1
$a:1; $b:22; $c:3;
MyClass f2
$a:1; $b:22; $c:3;
*/
$b->f3();echo "\n";
/*
MySubClass f3
*/ 

重定义父类(同名)属性时,属性的可访问性可以变得更开放,但不能更严格,也就是说,父类中的public属性,不能在子类中修改为private属性。

如果通过子类对象调用父类方法,那么该父类方法在访问属性时,对于重定义了的同名属性,public和protected的属性将访问到子类版本,private属性将访问到父类版本。也可以理解为,public和protected属性可以被重定义(父类的版本被重定义,从而不存在了),而private并未被重定义(父类中的属性仍然存在,通过父类方法进行访问,与子类中是否有同名属性毫不相干)。

class MyClass
{
 public $a = 1;
 protected $b = 2;
 private $c = 3;
 public function f1() {
  echo "\$a:$this->a; \$b:$this->b; \$c:$this->c;\n";
 }
}
class MySubClass extends MyClass
{
 public $a = 11;   // 必须为public
 protected $b = 22; // 必须为protected或public
 private $c = 33;
 public function f2() {
  echo "\$a:$this->a; \$b:$this->b; \$c:$this->c;\n";
 }
}
$b = new MySubClass;
$b->f1(); // $a:11; $b:22; $c:3;
$b->f2(); // $a:11; $b:22; $c:33; 

范围解析操作符 ::

又冒号常用于访问类常量、类静态变量,也用于在方法覆盖时调用父类版本。与其搭配的还包括parent、self、static等关键字。

class MyClass
{
 const Name0 = "MyClass";  // 类常量
 public static $id0 = 0;  // 类变量
 public function put() {  // 将被子类覆盖的方法
  echo "MyClass put()\n";
 }
}
class MySubClass extends MyClass
{
 const Name1 = "MySubClass";
 public static $id1 = 1;
 public function put() {
  parent::put();        // 调用父类版本的对象方法
  echo parent::Name0 . "\n";  // 父类常量
  echo parent::$id0 . "\n";   // 父类变量
  echo self::Name1."\n";    // 子类常量
  echo self::$id1 . "\n";    // 子类变量
  echo static::Name1 . "\n";  // 子类常理
  echo static::$id1 . "\n";   // 子类变量
 }
}
$a = "MyClass";
$ca = new MyClass;
$cb = new MySubClass;
$cb->put();
echo MyClass::Name0 . "\n";
echo MyClass::$id0 . "\n";
echo $a::Name0 . "\n";
echo $a::$id0 . "\n";
echo $ca::Name0 . "\n";
echo $ca::$id0 . "\n"; 

在子类中访问父类中的成员时,应避免直接使用父类类名,而应使用parent::,以免破坏父类的封装性。

final

声明为final的方法不能被子类覆盖,如果类声明为final,则此类不能被继承。

// 声明为final的类不能被继承
final class MyClass
{
 private $dat;
 public function __construct($dat) {
  $this->dat = $dat;
 }
 // final方法不能被覆盖,不过此类已经是final类,方法无必要在声明为final了
 final public function getDat() {
  return $this->dat;
 }
}

总结

以上所述是小编给大家介绍的php中的类与对象(继承),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • PHP入门教程之面向对象的特性分析(继承,多态,接口,抽象类,抽象方法等)

    本文实例讲述了PHP面向对象的特性.分享给大家供大家参考,具体如下: Demo1.php <?php header('Content-Type:text/html; charset=utf-8;'); //创建一个电脑类 class Computer { //什么叫做类内,就是创建类的花括号内的范围叫做类内,其他地方则类外. //public 是对字段的公有化,这个字段类外即可访问,赋值和取值 public $_name = '联想'; } $computer = new Computer();

  • PHP面向对象程序设计高级特性详解(接口,继承,抽象类,析构,克隆等)

    本文实例讲述了PHP面向对象程序设计高级特性.分享给大家供大家参考,具体如下: 静态属性 <?php class StaticExample { static public $aNum = 0; // 静态共有属性 static public function sayHello() { // 静态共有方法 print "hello"; } } print StaticExample::$aNum; StaticExample::sayHello(); ?> 输出:0   

  • 详解php中的类与对象(继承)

    简介 在php中,类型的继承使用extends关键字,而且最多只能继承一个父类,php不支持多继承. class MyClass { public $dat = 0; public function __construct($dat) { $this->dat = $dat; } public function getDat() { return "$this->dat\n"; } } class MySubClass extends MyClass { public fu

  • 详解C++中String类模拟实现以及深拷贝浅拷贝

    详解C++中String类模拟实现以及深拷贝浅拷贝 在C语言中/C++中,字符串是一个应用很广泛的类型,也是很基础的类型,C语言并没有直接处理字符串的操作而是采用字符指针和字符串数组进行操作,而在C++中标准库为我们封装了一个字符串的类供我们使用,使用需要#inlcude <string>头文件.我们也可以自己模拟实现一个简单的String类. 在模拟实现String类的过程中,不可避免的会遇到深拷贝浅拷贝的问题,下面就深拷贝浅拷贝做一个简介.所谓深拷贝浅拷贝,简单来说就是浅拷贝只是简单的将值

  • 详解Java中的不可变对象

    不可变对象想必大部分朋友都不陌生,大家在平时写代码的过程中100%会使用到不可变对象,比如最常见的String对象.包装器对象等,那么到底为何Java语言要这么设计,真正意图和考虑点是什么?可能一些朋友没有细想过这些问题,今天我们就来聊聊跟不可变对象有关的话题. 一.什么是不可变对象 下面是<Effective Java>这本书对于不可变对象的定义: 不可变对象(Immutable Object):对象一旦被创建后,对象所有的状态及属性在其生命周期内不会发生任何变化. 从不可变对象的定义来看,

  • 详解Java中String类的各种用法

    目录 一.创建字符串 二.字符.字节与字符串的转换 1.字符与字符串的转换 2.字节与字符串的转换 三.字符串的比较 1.字符串常量池 2.字符串内容比较 四.字符串查找 五.字符串替换 六.字符串拆分 七.字符串截取 八.String类中其它的常用方法 九.StringBuffer 和 StringBuilder 1.StringBuilder与StringBuffer的区别 2.StringBuilder与StringBuffer常用的方法 十.对字符串引用的理解 一.创建字符串 创建字符串

  • 详解Java中Optional类的使用方法

    目录 一.Optional类的来源 二.Optional类是什么 三.Optional类用法 四.代码示例 1.创建Optional类 2.判断Optional容器中是否包含对象 3.获取Optional容器的对象 4.过滤 5.映射 五.什么场景用Optional 1.场景一 2.场景二 3.场景三 4.场景四 一.Optional类的来源 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因.以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optiona

  • 详解C++中存储类的使用

    目录 auto 存储类 register 存储类 static 存储类 extern 存储类 mutable 存储类 存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期.这些说明符放置在它们所修饰的类型之前.下面列出 C++ 程序中可用的存储类: auto register static extern mutable auto 存储类 在C++11 中, auto 关键字不再是C++存储类说明符.从C++11开始,auto 关键字声明一个变量,该变量的类型是从其声明中的初始化表达式推

  • 详解C#中Helper类的使用

    目录 使用背景 使用方法 1.引用CSRedisCore 2.增加helper类代码 3.使用 4.说明 结语 使用背景 项目中用户频繁访问数据库会导致程序的卡顿,甚至堵塞.使用缓存可以有效的降低用户访问数据库的频次,有效的减少并发的压力.保护后端真实的服务器. 对于开发人员需要方便调用,所以本文提供了helper类对缓存有了封装.分了三个Cache,SystemCache,RedisCache(默认缓存,系统缓存,Redis缓存).话不多说,开撸! 使用方法 1.引用CSRedisCore 可

  • 详解Java中Period类的使用方法

    目录 简介 Duration和Period 创建方法 通过时间单位创建 通过LocalDate创建 解析方法 比较方法 增减方法 转换单位 取值方法 简介 本文用示例介绍java的Period的用法. Duration和Period 说明 Duration类通过秒和纳秒相结合来描述一个时间量,最高精度是纳秒.时间量可以为正也可以为负,比如1天(86400秒0纳秒).-1天(-86400秒0纳秒).1年(31556952秒0纳秒).1毫秒(0秒1000000纳秒)等. Period类通过年.月.日

  • 详解Java中Duration类的使用方法

    目录 简介 Duration和Period 创建方法 通过时间单位创建 通过LocalDateTime或LocalTime 通过已有的Duration 解析方法 用法说明 详解 比较方法 增减方法 转换单位 取值方法 简介 本文用示例介绍java的Duration的用法. Duration和Period 说明 Duration类通过秒和纳秒相结合来描述一个时间量,最高精度是纳秒.时间量可以为正也可以为负,比如1天(86400秒0纳秒).-1天(-86400秒0纳秒).1年(31556952秒0纳

  • 详解C++中基类与派生类的转换以及虚基类

    C++基类与派生类的转换 在公用继承.私有继承和保护继承中,只有公用继承能较好地保留基类的特征,它保留了除构造函数和析构函数以外的基类所有成员,基类的公用或保护成员的访问权限在派生类中全部都按原样保留下来了,在派生类外可以调用基类的公用成员函数访问基类的私有成员.因此,公用派生类具有基类的全部功能,所有基类能够实现的功能, 公用派生类都能实现.而非公用派生类(私有或保护派生类)不能实现基类的全部功能(例如在派生类外不能调用基类的公用成员函数访问基类的私有成员).因此,只有公用派生类才是基类真正的

随机推荐