php面向对象2
单例模式
避免多人开发过程,实例化过多导致资源浪费, 限制只实例化一次的模式
构造方法不能执行则实例化失败
思路 :
1、 保护或私有构造函数,房子外部实例化
2、 内部开发一个公共的静态方法,负责实例化
3、 类有一个静态属性存放对象
<?php class Single { //设置私有,保存实例状态 static protected $ins = NULL; //设置为私有,限制类外实例化,若没有子类可去掉final final protected function __construct() { echo '实例化成功!'; } //设置为静态方法,类外能调用,实例化 static public function getinstance() { //self代表当前类,判断是否实例化 if (self::$ins instanceof self) { return self::$ins; } self::$ins = new self(); return self::$ins; } }
$s1 = Single::getinstance(); $s2 = Single::getinstance();
//子类继承父类若还要单例,要用final修饰父类构造方法, //阻止子类重写构造方法自己去new的问题 class Single2 extends Single { }
$s11 = Single2::getInstance(); $s12 = Single2::getInstance();
if ($s11 === $s12) { echo "相等"; } ?>
final
final 不能修饰属性
final 修饰方法,此方法能继承,不能被重写
final 修饰类,则此类 不能够被继承
魔术方法
是指某些情况下,会自动调用的方法,称为魔术方法 ; 感觉一般都是那些权限不允许调用或者是不存在的属性 才会触发魔术方法
PHP面向对象中,提供了这几个魔术方法,
他们的特点 都是以双下划线__开头的
__construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sleep(), __wakeup(), __toString(), __invoke(), __set_state() 和 __clone()
__clone() :克隆方法,当对象被克隆时,将会自动调用
__get() :当我们调用一个权限上不允许调用的属性,和不存在的属性时,__get魔术方法会自动调用,并且自动传参,参数值是属性名。酱紫就能避免系统会直接报错,甚至fatal error,通过__get()我们就能自定义用户访问时的处理行为。注意如果是数组形式的话那么这里的$p相当于键值(即是下标)
流程:
$lily->age--无权-->__get(age);
$lily->friend--没有此属性-->__get('friend');
__set() :当为无权操作的属性赋值时,或不存在的属性赋值时,__set()自动调用,且自动传2个参数 属性 属性值
__isset() :当用isset()判断对象不可见的属性时(protected/private/不存在的属性)
会引发 __isset()来执行
isset($obj->xyz) 属性为真,能说明 类声明了一个xyz属性吗?
答:不能,因为isset($hua->tail)----没有tail属性---->__isset('tail');-à 如果__isset返回1则不能
__unset() : 当 用unset 销毁对象的不可见属性时,会引发 __unset();
__call($a,$b) : 调用不可见(不存在或无权限)的方法时,自动调用;前参数是方法名,后是传参
__callStatic($a,$b) : 是调用不可见的静态方法时,自动调用.
__call是调用不可见(不存在或无权限)的方法时,自动调用
$lisi->say(1,2,3);-----没有say()方法----> __call('say',array(1,2,3))运行
__autoload() : 如果调用某个不存在的类,在报错之前,系统会调用__autoload($n)函数,并把"类名"自动传给__autoload函数我们自然可以在__autoload里 加载需要的类!
魔术方法的应用
TP中设置用户注册的做法: 把表单中接受到的信息,直接付给一个对象的属性,然后
对象 add() 用sql语句写到数据库中
具体的思路
在一个类中设置一个空数组,利用__set()把传进来的值放到数组中,然后利用 add方法 与数据库交互 ; 酱紫避免了与其他同名属性的冲突
都是使用魔法方法来进行操作
$date = array() 设置一个空数组
implode() :将数组按值进行分割
array_keys(array,value) :获得数组中键名并以数组的形式 ; 前者数组名必选 , 后者数组值可选 ; 只选前者则返回所有键名 ; 两者选返回对应值得键名
2015/7/23
重载与重写
重写/覆盖 override:子类重写了父类的同名方法。只要子类有该方法只调用子类的无论参数是否一致
<?php //模仿重载的功能 class Circle { public function area() { $a = func_get_args(); $num = count($a); if ($num == 0) { echo '传入参数' . "\n"; } else if ($num == 1) { echo 3.14 * $a[0] . "\n"; //学会利用已有的条件进行分析 } else if ($num == 2) { echo $a[0] * $a[1] . "\n"; } } }
$c = new Circle(); $c->area(); $c->area(3);
class P { public function a() { echo "nihao"; } }
$b = new P(); $b->a(2123, 'sdf'); ?>
重载 overload: 指存在多个同名方法,但参数类型/个数不同,传不同的参数,调用不同的方法。但是在PHP中,不允许存在多个同名方法.因此,不能够完成java,c++中的这种重载
但是,PHP的灵活,能达到类似的效果
<?php function __autoload($n){ require('./' . $n . '.php'); echo "加载成功!"; } $test = new autoload_class(); $test->say(); ?>
常量 (常量名全大写,不带$)
普通常量 :define('常量名',常量值); 全局有效.无论是页面内,函数内,类内,都可以访问 ;
类内常量 : 类常量 在类内用 const 声明即可;前面不用加修饰符,;且权限是public的,即外部也可以访问
作用域在类内,类似于静态属性 ;又是常量,则不可改.
其实就是"不可改变的静态属性",即是能够 类名::常量名 访问
魔术常量
1:无法手动修改他的值,所以叫常量
2:但是值又是随环境变动的,所以叫魔术
__FILE__ 返回当前文件的路径+文件名
在框架开发或者是网站初始化脚本中,用来计算网站的根目录
__DIR__ 返回当前的文件路径
__LINE__ 返回当前的行号
在框架中,可以用来在debug时,记录错误信息
__CLASS__ 返回当前的类名
__METHOD__ 返回当前的方法名
延迟绑定
注意 :static::方法名 表示调用 对象类中的方法 或者属性
注意 一般书写的时候public等权限修饰符在static前,增加规范可读性
抽象
抽象类 :类前加 abstract 是抽象类,是抽象方法抽象类不能 new 来实例化,有抽象方法,则此类必是抽象类;抽象类,内未必有抽象方法
抽象方法 :方法前加 abstract,抽象方法 不能有方法体,所以没有{} ; 而是直接();
子类 继承 抽象的父类 必须 重写父类的抽象方法!并且参数个数要一样
//利用面向对象思想实现不同语言首页欢迎!
//抽象类就是个模板,你们子类继承我的类和方法自己搞自己想要弄的东西
//比如我想要开发英语语言,只要增加一个子类,不用修改父类的东西
//所以面向对象是可插拔的
<?php //利用面向对象思想实现不同语言首页欢迎! //抽象类就是个模板,你们子类继承我的类和方法自己搞自己想要弄的东西 //比如我想要开发英语语言,只要增加一个子类,不用修改父类的东西 //所以面向对象是可插拔的 abstract class Language { //抽象方法 public abstract function wel(); } class China extends Language { public function wel() { echo "欢迎!"; } } class English extends Language { public function wel($a) { echo "Welcome!"; } } $language = 'China'; $w = new $language(); //666 $w->wel(); ?>
说明 : 抽象类就是一个模板,我不用担心同类的类不会做,反正你只要根据我的模板做就不会错。从而达到兼容多种不同的情况 和 避免代码大量的修改 和 代码的规范(方法一致)
比如 :
公司网站要上线了,要选择什么数据库? 先弄个mysql开发着,到时候有问题再换也行。那么换数据库,会不会以前的代码又得重写? 其实不用担心,用抽象类!开发者,开发时,就以db抽象类来开发。不管上线时,真正用什么数据库,我只需要再写一份如下类(右图)即可。所以业务逻辑层不用改,因为都实现的db抽象类。
接口
(没有括号) interface 接口名 { }
类如果是一种事物/动物的抽象,那么 接口则是事物/动物的功能的抽象,即再把它们的功能各拆成小块自由组合成新的物种。
以人类为例, class Human 是人的草图而接口 是零件可以用多种零件组合出一种新特种来.
1、接口本身即是抽象的,内部声明的方法 默认也是抽象的.不用加 abstract
2、一个类可以一次性实现多个接口.语法用 implements 实现 (把我这几个功能实现了)
3、接口可继承另一个接口, 用extends ;注意实现时须把继承的接口的方
4、接口是一堆抽象方法的说明,不能加属性
5、接口就是供组装成类用的,封闭起来没有意义,因此方法只能是public
<?php /* 接口 就更加抽象了,比如一个社交网站,关于用户的处理是核心应用。 登陆 退出 写信 看信 招呼 更换心情 吃饭 骂人 捣乱 示爱 撩骚 这么多的方法,都是用户的方法,可写一个user类,全包装起来 但是,分析用户一次性使不了这么方法。于是分开多个类 用户信息类:{登陆,写信,看信,招呼,更换心情,退出} 用户娱乐类:{登陆,骂人,捣乱,示爱,撩骚,退出} */ interface UserBase { //注意没括号 public function login($u, $p); public function logout(); //注意是抽象方法 } interface UserMsg { public function wirteMsg($to, $title, $content); public function readMsg($from, $title); } interface UserFun { public function spit($to); public function showLove($to); } /* 作为调用者, 我不需要了解你的用户信息类,用户娱乐类, 我就可以知道如何调用这两个类 因为: 这两个类 都要实现 上述接口. 通过这个接口,就可以规范开发. */ class User implements UserBase { public function login($u, $p) { echo "用户登录"; } public function logout() { echo "用户注销"; } } ?>
包含类进来
include/require能够包含某个php文件,但是不知道是否调用过;改进使用魔术方法__aotoload($)
__autoload() : 如果调用某个不存在的类,在报错之前,系统会调用__autoload($n)函数,并把"类名"自动传给__autoload函数我们自然可以在__autoload里 加载需要的类!
★自定义 自动加载方法
通知系统,让系统知道--我自己写了一个自动加载方法,用这个spl_auto_register($n)
★方法中能定义一个类,但是想要执行类里面的东西,必须先动态调用方法(直接方法名),有return也不关事
异常
讨论:如何判断mysql类连接是否成功? 利用 在方法里面return?
实例化后返回一个对象,无法确定是否连接成功 。 return无法干扰,仍然是返回对象
解决这种问题,用异常类 Exception
注意 @mysql_connect(); //@表示忽略此处输出的错误
关闭所有的错误报告 : Error_reporting(0);
{ protected $conn = NULL; public function __construct() { $this->conn = mysql_connect('localhost','root','111'); if (!$this->conn) //如果连接失败了,抛出错误 { $e = new Exception('失败了!',9); // throw $e; //抛出异常 } } } //若抛出异常,没有接受处理则会报错 try // 可能出现错误的代码并尝试捕捉错误信息 { $my = new mysql(); }catch(Exception $e) // 注意括号,处理错误 { echo $e->getMessage(); echo '错误代码',$e->getCode(); echo '错误文件',$e->getFile(); echo '错误行',$e->getLine();//抛出错误的行 } ?>