PHP面向对象
【一】目标
1.类声明语法------试声明student类,有score属性和study方法
2.权限封装---------知道public、protected、private各自的可见范围
3.继承---------------写A类,再写B类继承自A类,且要重写A类中的某个方法
4.静态属性与静态方法-----知道static静态方法需要用类名..方法名0调用
5.魔术方法--------了解常用魔术方法分别在什么时间被调用
6.写一个抽象类,并用2个子类分别继承实现分析这2个子类有什么共同特点?
7.写一个接口,并用2个类分别继承实现分析这2个类有什么共同点?
基本语法:声明,权限,继承,静态属性和方法,魔术方法,抽象类,接口
【二】基础
<?php //对象的声明,将函数放到对象里 class oneclass{
//方法 function one(){ echo "666"; } } //实例化对象 $class = new oneclass(); // 函数调用 $class -> one(); ?>
(1)属性和方法
将变量赋值放到类中,便成为了属性;将函数放到类里,便成了方法
属性和方法的调用:
<?php //放到对象里 class oneclass{ public $name = '我是属性'; function one(){echo "我是方法";} } //实例化对象 $class = new oneclass(); // 方法调用 $class -> one(); //属性调用 echo $class->name;//调用属性时可以不加$ ?>
注意:方法和属性加到类里时前面要加入权限声明,否则默认public
(2)类的声明
语法:class 类名{...}
注意:类名不区分大小写,但是Linux下区分,所以要保持一致。且命名采用驼峰法,一般首字母大写
<?php //类的声明 class person{ //属性声明 public $name = 'wang'; //方法声明 public function man(){ echo "1111"; } } //调用类的话,要先new一个对象,且实例化时不区分大小写,但在Linux下严格区分大小写,所以要避免不一致的情况。命名时用驼峰法 $person = new PERSON(); $person -> man(); echo $person->name; ?>
(3)属性赋值的变化
属性不能赋值表达式?
5.6版本开始支持表达式,例如public $rand = 1+1;
(4)类与对象关系
类是同类事物共同特点的抽象描述;而对象是以类作为模板,形成的具体实例
所以当new + 类名时,便成为new一个对象,即实例化对象
<?php //类的声明 class person{ //属性声明 public $name = 'tony'; //方法声明 public function man(){ echo "1111"; } } //实例化一个对象,赋值给变量 //new类名时,内存便会产生一个对象,开辟新空间存放属性和方法名 $person = new PERSON(); $person1 = new PERSON(); echo $person->name; $person1 ->name = 'tony1'; echo $person1->name; ?>
注意:
①这里改变的属性值不是类里的属性值,而是开辟出的新空间的值;
②开辟的新空间只存放属性和属性值,至于方法只存放方法名,不存放函数
(5)$this本对象、self本类、parent父类
①$this是伪变量,谁实例化对象就是谁。简单理解谁调用就是谁
<?php class Person{ public $name = 'tony'; public $goods = 'dogs'; public function buy(){ echo "拍电影吧".$this->name; } } $person = new Person();
//调用new出来的对象中的方法,所以this指的是new出来的对象。而不是类本身 $person -> buy(); ?>
②self本类,即class类
类名随意改,函数内部用self替换类名,代表调用本类
③parent父类
作用:实现某些情况下子类和父类的兼容,即在继承的子类里做的修改不会影响父类。二者可以同时共存,不会覆盖重写
先举个普通例子:
<?php class Single{ public $value = 1; } class Test extends Single{ public $value = 2;//子类继承后对变量进行覆盖重写 } $test = new Test(); echo($test->value); ?>
parent父类案例:
<?php class Single{ public function __construct(){ echo rand(0,10); } } class Test extends Single{ public function __construct(){ parent::__construct(); echo 2; } } $test = new Test(); ?>
总结:self()调用本类方法\属性;parent()调用父类方法\属性
(6)封装MySQL类
注意:原生MySQL的api自5.0版本开始逐渐废弃,在将来预计会完全移除。虽然有向下兼容特效,但开发中还是推荐使用mysqli
用封装MySQL连接数据库
<meta charset="utf-8"> <?php class Mysql{ public $link; //连接数据库 public function coon(){ $cfg = array( 'host' => 'localhost', 'user' => 'root', 'password' => 'root', 'db' => 'user', 'charset' => 'utf8' ); //连接数据库(地址,用户,密码,选库) $this ->link = mysqli_connect($cfg['host'],$cfg['user'],$cfg['password'],$cfg['db']); if(!$this ->link){ die("连接失败:".mysqli_error()); }else{ echo "连接成功"; } } //发送查询 public function query($sql){ return mysqli_query($this->link,$sql); } //查询获取所有数据,返回数据 public function getAll($sql){ $array = array(); $res = $this ->query($sql); // mysqli_fetch_assoc($res);//数组 while ($row = mysqli_fetch_assoc($res)) {//遍历数组 $data[] = $row; } return $data; } } $sql = new Mysql(); $sql -> coon(); var_dump($sql->getAll('select* from user')); ?>
注意:每连接一次(刷新页面),便会增加一次连接数Connection id
(7)构造方法和析构方法----------(简单即为构造和销毁construct和destruct)
①构造方法(construct构造)
与面向对象调用方式不同,构造方法的类,一旦被实例化,就会被调用
②析构方法(destruct销毁)
同样也是实例化后便被调用
③构造方法传参:直接在调用时传入即可
注意:析构方法对象销毁时被调用
<?php class Human{ public function __construct(){ echo "构造方法"; }
public function __destruct(){
echo"析构方法";
} } new Human();//调用执行 ?>
构造函数与析构函数调换位置后输出顺序依然不变,因为实例化对象时会先执行构造函数
案例:
<?php class Human{ public function __destruct(){ echo "调用2<br>"; } public function one(){ echo "调用One函数<br>"; } public function __construct(){ echo "调用1<br>"; } } $a = new Human(); $a -> one(); ?>
输出顺序:1,one,2
③析构函数销毁时间:变量赋其他值;被人工销毁unset();页面结束
④构造方法的旧式声明:一个和类名同名的方法,被理解为构造方法。老旧的PHP代码会遇到,遇到时认识即可(可以传参)
<?php class Human{ //方法名和类名一致时会被理解为构造方法 public function Human($a){ echo "调用"; } } $a = new Human('形参'); ?>
(8)类的封装性
封装性:类中的某些方法或属性不允许外部调用
可以通过开放部分接口来间接调用,写个简单例子
<?php class Human{ //方法名和类名一致时会被理解为构造方法 public function aa(){ echo "调用1"; } public function __construct(){//通过调用构造函数来间接调用方法aa() $this -> aa();//我是内部调用 } } $a = new Human();//我是外部调用 ?>
类似于ATM机,只能输入密码,而不能查询。因为这里只开放了输入对比的接口
不封装的缺点:可以在外部调用获取内部函数或变量,安全性差
<?php class Atm{ //返回数据库存储的密码 public function getPwd(){ return '123456'; } //对比密码 public function checkPwd($pwd){ return $this -> getPwd() == $pwd; } } $atm = new Atm(); //不封装时在外部会可以调用到内部函数,获取密码 echo '存储密码为'.$atm->getPwd()."<br>"; if($atm -> checkPwd('123456')){ echo "密码输入正确"; }else{ echo "密码输入错误"; } ?>
为了封装函数,保护重要数据。将返回数据库存储密码的行的权限改为protected(保护),则该函数便被封装了起来
通过调用公共接口即可间接调用方法
//返回数据库存储的密码 protected function getPwd(){ return '123456'; }
(9)类的继承
语法:extends继承、扩展
class ParClass{} class SubClass extends ParClass{}
接下来,首先看个问题
<?php include 'footer.php';//不存在会继续终止 require 'header.php';//不存在会终止执行 function check(){...} ?>
引入的文件里可能会有函数名与check相同,导致运行出现问题
详解:结合下面代码进行解析
继承的好处:
①子类可以继承父类的方法和属性,例如底下案例里通过实例化调用子类来调用继承自父类的函数method;
②允许覆盖和修改父类的方法属性或新增方法,例如check函数的覆盖重新;
案例:
<?php class one{ function check(){ echo "检查1"; } function method(){ echo "方法1"; } } //two相对于one 来说是子类,此时one类里的方法可用,比如通过子类two调用父类的method方法 class two extends one{ function check(){ echo "检查2"; } } $three = new two(); $three -> check();//这里会输出“检查2”,因为two对one进行了继承修改 $three -> method();//通过子类two调用父类的method方法 ?>
(11)final类和final方法
final类不能被继承,且final方法不能被子类重写
①类无法继承
<?php final class One{} class Two extends One{ public function a(){ echo "123"; } } $three = new Two();//检验后,会提示无法继承错误 ?>
②方法可以通过子类继承调用,但无法覆盖重写
<?php class One{ final public function check(){ echo('父级'); } } class Two extends One{ public function a(){ echo "子级1"; } public function check(){ echo "子级2"; } } $three = new Two(); $three -> check();//输出父级,所以可以继承 ?>
(12)三种权限的详解
类中的权限一共3中,三种权限包含public(公共的),protected(保护的),private(私有的)
①public
内外部均可调用,子类可以继承和重写
<?php class Human{ public $money = 3000; public function one(){ echo $this->money; } } class Stu extends Human{} $Stu = new Stu(); // 变量调用 echo $Stu->money; //方法调用 echo $Stu->one(); ?>
②protected
不能在外部直接调用,只能通过内部开放接口调用。但是可以被子类覆盖重写
<?php class Human{ public $money = 3000; protected $pwd = '123456'; public function one(){ echo $this->money; } } class Stu extends Human{ public function show(){ return $this->pwd; } } $Stu = new Stu(); // protected权限的变量,不能在函数外部调用。只能通过子类间接调用 echo $Stu->show(); ?>
③private(私有的)
外部不可以被调用,内部可被调用。不可以被继承
(13)静态属性和静态方法
①为什么实例化类?
为了得到类里的属性和方法,通过实例化出来的对象调用
②静态方法
类的方法和属性的调用,可以不通过实例化类来调用方法,这时我们可以通过静态方法来调用
③静态属性
<?php class Math{ //声明静态属性 static public $name = 'Tony'; //声明静态方法 static public function add($a,$b){ return $a+$b; } } $math = new Math(); echo Math::add(1,2); echo Math::$name; ?>
优势:略过实例化步骤,直接调用属性和方法
(14)类常量
先说下常量:常量类似变量,但是常量一旦被定义就无法更改或撤销定义。全局均可被调用
常量语法:define(常量名,常量值,是否对大小写敏感(默认不敏感true))
<meta charset="utf-8"> <?php define('name', 'Tony'); class Math{ public function test(){ echo name; } } $math = new Math(); $math->test(); ?>
因为常量是全局性的,可能有命名冲突的情况,造成运行问题。
为了避免这种情况的出现,可以把常量写到类里,用类常量来解决
类常量:
调用时类名::常量即可调用
<?php class Math{ const name = 'Tony'; public function test(){ echo Math::name; //类常量的调用为类名::常量名; } } $math = new Math(); $math->test(); ?>
(15)单例模式
简单理解:单例就是类只能被实例化一次,只能得到一个对象
购物商场用到单例模式
思路:按步骤分为
1. 创建普通类;2. 保护构造方法,封装起来(保护后不能调用);3. 所以要在封装后在内部调用,从而实现对外开放一个接口;
4.设为静态,移除控制权,从而不实例化调用;5. 添加判断;6. final禁止继承
单个实例对象,即只能实例化一个对象
①普通类的实例
<?php class Single{ public $rand; public function __construct(){ $this->rand = mt_rand(10,300);//mt_rand()随机数 } } var_dump(new Single());//115 var_dump(new Single());//148 ?>
这里我实例化了类两次,得到两个对象,且两次数值不一样。由此总结出一个类可以实例化出多个对象。
②单例模式案例
实例化放到类里,加上条件判断
<?php class Single{ public $rand; static public $ob; protected function __construct(){ $this->rand = mt_rand(10,300);//随机数 } static public function out(){ if (Single::$ob === null) {//判断是否实例化 Single::$ob = new Single(); } return Single::$ob; } } var_dump(Single::out());//输出222 var_dump(Single::out());//输出222 ?>
两次输出一样
上例还未完成,因为继承后的子类里再次实例化时,还是会产生多个不同结果
<?php class Single{ public $rand; static public $ob; //final不允许被子类重写 protected function __construct(){ $this->rand = mt_rand(10,300);//随机数 } static public function out(){ if (Single::$ob === null) {//判断是否实例化 Single::$ob = new Single(); } return Single::$ob; } } class Test extends Single{ public function __construct(){ echo rand(20,300);//此处被子类重写 } } new Test(); new Test(); ?>
对此我们用final来禁止重写,因为final类不能被继承,且final方法不能被子类重写
(16)魔术方法
在某些场景下可以自动调用的方法,即为魔术方法
举例:__construct、__destruct、__set、__get、__isset、__unset、__call
详解:
__construct: 构造方法,new 实例时,自动调用
__destruct: 析构方法,对象销毁时自动调用
__get(属性名): 当读取对象的一个不可见属性时,自动调用,并返回值
不可见:未定义或无权访问时
__set(属性名,属性值):当对一个不可见的属性赋值时,自动调用
__isset(属性名):当用isset,或empty判断一个不可见属性时,自动调用
__unset(属性名): 当unset一个不可见属性时,自动调用
不可见属性:未定义,不能访问(受保护或者私有),即外部不能访问,protected和private.
简单理解就是:在外部没有访问权限的都是不可见属性
案例:
__get():
<?php class Single{ public function __get($a){ echo $a; } } $single = new Single(); $single->tony;//这里调用了一个未定义的属性(不可见属性),所以自动触发了__get(),并返回值。获取不可见属性时触发,返回属性值 ?>
__set():设置不可见属性时自动触发
<?php class Single{ public function __set($a,$b){ echo $a.$b; } } $single = new Single(); $single -> name='Tony'; ?>
_isset():当用isset,或empty判断一个不可见属性时,自动调用
<?php class Single{ public function __isset($a){ echo $a; } } $single = new Single(); isset($single -> tony); ?>
__unset(属性名): 当unset一个不可见属性时,自动调用
<?php class Single{ public function __unset($a){ echo $a; } } $single = new Single(); unset($single -> tony); ?>
(17)魔术方法的意义
作用:防止对类的随意修改;运用__set()实现数据库数据添加
先来根据案例介绍
<?php class Single{ } //实例化一个对象,赋值给变量 //new类名时,内存便会产生一个对象,开辟新空间存放属性和方法名 $single = new Single(); $single -> name = 'tony'; echo $single ->name; ?>
①这里改变的属性值不是类里的属性值,而是开辟出的新空间的值;
②开辟的新空间只存放属性和属性值,至于方法只存放方法名,不存放函数
代码解析:
上面的代码里,Single类本身失去了对代码的控制权,因为继承的子类可以随意修改覆盖,这里便可以通过魔术方法来限制
<?php class Single{ public function __set($a,$b){ echo"禁止随意添加属性"; } } $single = new Single(); $single -> name = 'tony'; ?>
总结:通过魔术方法来限制对父类的添加覆盖和获取。
案例:thinkPHP里,可以通过触发魔术方法,直接将数据添加到数据库
//ORM方式,原理:设置了__set()魔术方式
<?php $this->name = 'tony'; $this->age = '23'; $this->add(); ?>
(18)自动加载
实例化某个类时,如MySQL,需要先require()。如果类比较多,目录也就比较多,require引入文件时将显得很麻烦。这时需要一个自动化的解决方案----自动加载。
<?php function myload($class){//这里$class为实例化的类名 echo $class."?"; } //注册一个函数为自动触发函数--调用/实例化一个不存在的类时便会自动触发 spl_autoload_register('myload'); new mySql(); ?>
解析:new mySql()会在本页面查找mysql类,不存在时便会触发sql_autoload_register()里注册的自动触发函数
被注册函数的形参,上例为$class,会接受实例化的类名。
<?php function myload($class){ include './'.$class.'.class.php';//这里便会引入(自动加载)mySql.class.php文件 } //注册一个函数为自动触发函数--调用/实例化一个不存在的类时便会自动触发 spl_autoload_register('myload'); new mySql(); ?>
mySql.class.php:
<?php class mySql{ public function __construct(){ echo "789"; } } ?>
总结:简单理解所谓的自动加载并不是真的自动加载,而是利用spl_autoload_register()注册自动触发函数,利用函数里面设定的include内容来加载想要加载的文件。并且new里面的一个类名。thinkPHP里经常遇到类名与文件名一致,这便用到了自动加载功能。
(19)抽象类和抽象方法
问题由来:项目组A组负责底层数据库,B组负责调用接口,实现业务逻辑。为了分开两组进度,避免相互影响,从而产生了抽象类和抽象方法模板
抽象类和抽象方法实现模板的功能,实现数据与逻辑分离。其原理类似于螺母和扳手,都采用国标进行生产。而这里的抽象类和抽象方法,功能类似于国标。为数据和逻辑规定了模板。
语法:abstract + 类名,则为抽象类;abstract + 方法名,则为抽象方法。
注意:
①抽象方法没有方法体,即代码块;
②抽象类里的方法都是抽象方法?
NO!抽象类里可以有已经实现的方法,是完整的方法,即有代码块。例如下例中的check(),但之前不能加abstract
③抽象类里只要有一个抽象方法,则类仍然是抽象的。抽象类不能被实例化。否则会报错
Fatal error: Cannot instantiate abstract class Test in C:\PHPTutorial\demo1.php on line 30
翻译为抽象类不能被实例化
<?php abstract class Test{ /* *参数:sql语句 *返回值类型:array */ abstract public function getAll($sql); /* *参数:sql语句 *返回值类型:array */ abstract public function getRow($sql); //已完成方法 public check(){ echo "我是已完成方法"; } } //逻辑方法方面,只需知道getAll()方法;而数据库方面照着以上模板写sql执行。从而实现逻辑和数据分离 //接下来数据库方面想写数据,通过继承来实现上面模板的继承。继承时必须对每个方法都进行一一实现,少一个也不行 class Mysql extends Test{ public function getAll($sql){ }; public function getAll($sql){ }; } ?>
(20)接口的概念
抽象类可以理解为”类的模板”,接口则是”方法模板”。
接口粒度更小,用于描述通用的方法。
<?php interface fly1{ public function fly($oil,$height); } interface run1{ public function run($speed,$width); } interface water1{ public function water($depth); } //声明一个类,实现其接口 class Super implements fly1,run1,water1{ //与抽象类和抽象方法相同,对于声明类里所带的接口,必须对每个接口都进行一一实现,少一个也不行 public function fly($oil,$height){ echo "飞"; } public function run($speed,$width){ echo "跑"; } public function water($depth){ echo "游泳"; } } $super = new Super(); $super -> fly(1,2);//这里因为上面接口规定了必须传参,所以不管用不用都得传入,否则会报错 ?>
(21)接口的语法
①接口本身就是抽象的,方法前不用加abstract;
②接口里的方法,只能是public;
③类可以同时实现多个接口。例如下例的fly1,run1,water1
//声明一个类,实现其接口 class Super implements fly1,run1,water1{ //与抽象类和抽象方法相同,对于声明类里所带的接口,必须对每个接口都进行一一实现,少一个也不行 public function fly($oil,$height){ echo "飞"; } public function run($speed,$width){ echo "跑"; } public function water($depth){ echo "游泳"; } }
总结:抽象类相当于一类事物的规范;接口:组成事物的零件的规范
(22)异常处理
程序运行的每个环节都可能出错,要判断程序的运行逻辑,要靠返回不同的值
主要靠两部分:①抛出异常;②捕获异常
//抛出异常
//参数(异常信息,异常错误码),两个参数共同定位了错误的位置 throw new Exception("抛出异常,错误码为1", 1);
//捕获异常,$e为错误码
try{ var_dump(t1()); }catch(Exception $e){ var_dump($e);//输出验证即可分析出$e为对象类型 }
报错:
object(Exception)#1 (7) { ["message":protected]=> string(8) "t1返回" ["string":"Exception":private]=> string(0) "" ["code":protected]=> int(1) ["file":protected]=> string(24) "C:\PHPTutorial\demo1.php" ["line":protected]=> int(7) ["trace":"Exception":private]=> array(1) { [0]=> array(4) { ["file"]=> string(24) "C:\PHPTutorial\demo1.php" ["line"]=> int(21) ["function"]=> string(2) "t1" ["args"]=> array(0) { } } } ["previous":"Exception":private]=> NULL }
作用:利用报错机制,定位到相关错误行,打印出具体信息
<?php function t1(){ if(rand(1,10)>5){ //参数(异常信息,异常错误码) //两个参数共同定位了错误的位置 throw new Exception("t1返回", 1); }else{ t2(); } } function t2(){ if(rand(1,10)>5){ throw new Exception("t2返回", 2); }else{ return true; } } try{ var_dump(t1()); }catch(Exception $e){ // var_dump($e);//输出验证即可分析出$e为对象类型 echo '文件位置为:'.$e->getFile()."<br>"; echo "行号".$e->getLine(); } ?>
结合PHP手册查阅可以知道:getFile()错误获取文件位置、getLine()获取错误所在行等错误信息。而Expection为所有异常的基类
适用场景?
不该出错的重要地方,却有可能出错,就用异常。比如连接数据库,尤其是连接远程数据库时(断网);查询用户是否存在,可能返回true/false,此时就用return;上传文件时(上传过程断网)。出错时要写到错误日志里
(23)命名空间
多个人开发项目,函数名很容易重复。用了类之后,类之间的方法名被类分开,重名也没关系。
但是当项目更大时,类名也有可能重复。此时就要用到命名空间,来避免重名。
注意:①命名空间语句必须顶行,且之前不可以有任何输出
案例:
<?php namespace name;//命名空间---相当于创建了一个文件夹 include 'two.php'; class Test1{ public function __construct(){ echo "666"; } } new Test1(); ?>
two.php:
<?php namespace name1; class Test1{ public function __construct(){ echo "333"; } } ?>
最终会输出666,也就是调用了one.php里的函数。
如果去掉命名空间语句,再次测试会发现报错提示:Fatal error: Cannot redeclare class Test1。无法重新声明Test1
这里如果想调用two.php里的同名函数怎么办呢?
①原理类似于Linux下的文件查询操作cd\...\...;②之前说过,命名空间相当于创建了一个文件夹,现在我们要通过cd来查看内部文件;③定向调用
//two.php里命名空间为name1,所以相当于在name1文件夹下查看文件
new \name1\Test();
所以,上例可以改为
<?php namespace name;//命名空间---相当于创建了一个文件夹 include 'mySql.class.php'; class Test{ public function __construct(){ echo "666"; } } new \name1\Test();//这时便会输出333 ?>
有了命名空间可以明确指出运用的类
上例继续修改
one.php:
use \name1\Test1; new Test1();
two.php:
<?php namespace name1; class Test1{//改名为Test1 public function __construct(){ echo "333"; } } ?>
但是这种写法需要修改文件,有时没有相应权限。所以这里我们可以用别名as
use \name1\Test as newName; new newName();//直接new()别名
这样便可以访问
注意:命名空间可以创建多层,调用时按照cd原理逐层读取即可。例如
one.php:
use \name1\one\two\three\Test as newName; new newName();
two.php:
<?php
namespace name1\one\two\three;
?>
注意:
①命名空间声明位置必须位于首行;②声明后,其后的函数、类都将被封锁在命名空间里;
③引入其他页面后,自身空间不受干扰;④如果想明确使用某空间下的类,可以从根目录下逐层寻找读取,原理类似Linux的cd;
⑤若频繁使用某个空间下的类,可以先用use声明,然后as别名,避免冲突;⑥自动加载函数的参数,包含“空间路径\类名”
(24)延迟绑定
先来看个例子:
<?php class One{ public function demo1(){ echo "父类"; } public function demo2(){ $this->demo1(); } } class Two extends One{ public function demo1(){//继承重写 echo "子类"; } } $test = new Two(); $test->demo2();//输出结果为"子类" ?>
以上输出结果为"子类",虽然demo2是调用demo1实现的,但是这里还是打印出子类改写的结果,为什么呢?
因为这里用$this当前对象调用,而$this伪元素是谁调用就指向谁,所以会输出“子类”
对上面代码进行修改,改为$self本类
<?php class One{ public static function demo1(){ echo "父类"; } public static function demo2(){ self::demo1(); } } class Two extends One{ public static function demo1(){//继承重写 echo "子类"; } } Two::demo2();//这里会输出父类“” ?>
上面会输出父类,再做些修改
static::demo1();//输出“子类”
此时输出“子类”,这便是延迟绑定
简单理解:所谓延迟绑定就是在定义时未做出绑定,知道后面调用时才决定出绑定哪个。完整代码
<?php class One{ public static function demo1(){ echo "父类"; } public static function demo2(){ static::demo1(); } } class Two extends One{ public static function demo1(){//继承重写 echo "子类"; } } One::demo2();//调用时才决定出绑定哪个类 Two::demo2(); ?>
(25)超载的static
静态变量static在函数执行完毕后不会被重写
static用法场景:延迟绑定,静态属性和静态方法
用在变量前构成静态变量,用在类里的属性前则构成静态属性,类里的方法前则构成静态方法,用在静态方法里调用则构成延迟绑定
(26)多态
面向对象的3大特征:封装、继承、多态
PHP数据类型:4种标量类型(整型,浮点型,字符串,布尔值),2种复合类型(array(),object()),2种特殊类型(null,resource)
①首先要了解,PHP是弱类型语言,即对变量类型的控制不严格。不做参数的类型检测,变量的数据类型通常不是由程序员设定的,准确的说,是PHP根据该变量使用的上下文在运
行时决定的,简单的说就是,我们不需要设定变量的数据类型,PHP会自动识别。所以这便容易产生一些数据类型上的BUG
②在强类型语言里(JAVA、C++等),会检测参数的类型,即声明变量时,前面必须加上变量的数据类型。
③在声明时,声明参数的父类类型为数字,具体实参可以是数字或数字的子类,这种现象便是多态
<?php class Par{//作为父类存在 } class Son1 extends Par{ public function run(){ echo "123"; } } class Son2 extends Par{ public function run(){ echo "456"; } } function test($par){//强类型语言里参数必须声明数据类型 $par->run(); } test(new Son1()); ?>
PHP是弱类型语言,不会检测数据类型,所以严格上说没有多态