参考资料:PHP5 权威编程
PHP 中的类和对象
对象:万物皆对象;
类: 任何对象,都可以人为规定为某种类型(类别)。
类是描述一类事物的一个总称,是具有相同特征的该类事物的一个通用名称。
对象是一个明确的具体的物体,是某个类中的一个实物(类就是一个抽象的概念)。对象一定隶属于某个类--------有类才有对象,先有类再有对象。
属性和方法
在面相对象过程中
属性就是原来的变量,写在一个类中,原来的变量就称为属性(类的变量成员叫属性);
方法就是原来的函数,写在一个类中,原来的函数就称为方法。
对象的创建
对象的传值方式
对象的存储模式(结构):
$变量 ----> 对象标识符/编号(#1) -----> #1对象数据本身
并且:该变量中存储的实际是该对象的标识符/编号。这个标识符或编号有能力去“指向对象”;
对象中的 值传递 和 引用传递 结果一样, 但是过程却不相同
值传递过程
引用传递
类中成员
类中成员分为3大类
属性:1 普通属性 2 静态属性
方法: 1 普通方法 2 静态方法 3 构造方法 4 析构方法
常量
类常量
定义形式
class 类名{
const 常量名 ; // 不能使用define
}
使用形式
常量的使用,是通过类名,并使用范围解析符(::)来取用的;
类名::常量名;
普通属性(实例属性)
定义形式
class 类名{
var $属性名 = 初始值;
var $属性名;
// 另一种形式
public $属性名 = 初始值;
public $属性名;
}
使用形式
$对象->属性名;(没有$符号!!)
静态属性
静态属性,本质上也是“变量”,但其有一个特点就是:该变量只隶属于“类”,即:
一个类中的一个静态属性,就只有“一份数据”;
一个类中的一个实例属性,就可以有“多份数据”——每创建一个对象出来,就会有一份数据;
定义形式
class 类名{
static $属性名 = 初始值;
static $属性名;
}
使用形式
使用类名和范围解析符(::)来对静态属性进行操作:
类名::$静态属性名; //注意:属性名带$符号!!!!!!
1,实例属性,是每个对象都可以不一样的数据,也是每个对象都“独自拥有”的数据;
2,静态属性,他不属于任何一个对象,而只属于该类本身,也可以理解为为所有对象所共有的数据;
普通方法(实例方法)
定义形式
class 类名{
function 方法名($形参1,$x形参2....){
// code.......
}
}
调用形式
$对象名->方法名(实参1,实参2,....);
静态方法
一个类中定义的方法,只隶属于这个类本身,而不是隶属于这个类的对象。
定义形式
class 类名{
static function 方法名($形参1,$x形参2....){
// code.......
}
}
调用形式
类名::方法名(实参1,实参2,....);
self关键字:用在方法中,表示该方法所在的类。
static关键字:代替self关键字的位置,除了具有self作用外,还具有更灵活的作用,那就是所谓“后期静态绑定”。
构造方法(__construct)
构造方法是一个类在进行实例化(new一个对象出来)的时候,会 首先自动调用 的方法。主要的目的是为了在new一个对象的时候,给该对象设置一些“初始值”(初始化工作);构造方法的参数没有规定,通常是根据实际的需要来定义,目的是为了对象属性数据的初始化。
析构方法(__destruct)
析构方法是一个对象被销毁的时候被 自动调用 的方法,自己无法调用。
析构方法 不能带参数(形参),只能是public的。但方法中也可以使用$this这个词,代表“当前对象”。
如果一个类中定义了析构方法,则销毁对象时就会调用该方法。
如果一个类中没有定义析构方法,则销毁对象时就会调用其父类的析构方法(如果有)
对象在哪些情况下会被销毁?
1,如果程序结束,所有变量都会被销毁,自然,变量所代表的对象也会被销毁;
对象销毁的顺序,默认情况下,跟其创建的顺序相反;
2,当一个对象没有任何变量“指向”它的时候,即使程序还没有结束,也会被销毁;
此时,$obj1 断开了指向 ,但是程序还没也结束,此时,也会销毁对象。此时执行析构方法。
3,当变量赋值为标量,不能再指向对象,对象就会被销毁。
类的继承
将一个类A中的特性信息,传递到另一个类B中,此时就称为:
B继承A
A派生出B;
基本语法:extends
说明
父类/子类:已有类为父类,新建类为子类。父类又可以称为“基类”,上级类,子类又称为派生类,下级类,
单继承:一个类只能从一个上级类继承其特性信息。PHP和大多数面向对象的语言都是单继承模式。C++是多继承。
扩展:在子类中再来定义自己的一些新的特有的特性信息(属性,方法和常量)。没有扩展,继承也就没有意义了。
访问控制修饰符
public:公告的,在所有位置都可以访问;
protected:受保护的,只能在该类内部和该类的子类或者父类中访问和使用;
private:私有的,只能在该类内部访问。
形式
class 类名{
访问控制修饰符 属性或者方法
}
作用:用来 限制 其说修饰的成员的 可访问性,即用
对象->实例属性和方法 或者 类::静态属性和静态方法 的合理性。
访问控制修饰符,需要结合使用该语法形式的所在位置,才能确定是否可访问。
3个访问位置:类的内部, 继承类的内部,类的外部
总结:
1,public修饰的成员,哪里都能访问;
2,类的内部,可以访问任何级别的成员;
3,public具有最宽泛的可访问性;private具有最狭小的可访问性;protecte则居中;
parent关键词
paren在面向对象语法中,代表“父类”。本质上就是代表父类这个“类”,而不是父类的“对象”;
使用方式为:
parent::属性或方法; //通常是静态属性或静态方法,但有时候可能是实例属性或实例方法;
三个关键字对比
构造方法 和 析构方法 在父类和子类中的运用
1,如果一个类 有构造方法,则实例化这个类的时候,就不会调用父类的构造方法(如果有);
2,如果一个类没有构造方法,则实例化这个类的时候,就会自动调用父类的构造方法(如果有);
3,如果一个类 有析构方法,则销毁这个类的时候,就不会调用父类的析构方法(如果有);
4,如果一个类没有析构方法,则销毁这个类的时候,就会自动调用父类的析构方法(如果有);
5,如果一个类中有构造方法或析构方法,则就可以去“手动”调用父类的同类方法(如果有);
手动调用的语法形式总是这样:
parent::构造方法或析构方法()
覆盖(override)
覆盖,又叫“重写”,将一个类从父类中继承过来的属性和方法“重新定义”——此时相当于子类不用父类的该属性或方法,而是重新定义。
访问控制权限:
子类覆盖的属性或方法的访问控制权限,不能“低于”父类的被覆盖的属性或方法的访问控制权限:
具体来说:
父类:public 子类:只能是public
父类:protected 子类:可以是protected和public
父类:private 子类:不能覆盖!——既父类的私有成员,不存在被子类覆盖的可能。
方法的参数形式:
子类覆盖父类的同名方法的时候,参数要求跟父类保持一致;
特例:
构造方法重写的时候参数可以不一致
小注意:
虽然父类的私有属性不能被覆盖,但子类却可以定义自己的跟父类同名的属性;
虽然父类的私有方法不能被覆盖,但子类也不能定义自己的同名方法;
最终类
最终类,其实就是一种特殊要求的类:要求该类不允许往下继承下去。
形式:
final class类名{
//类的成员定义。。。跟一般类的定义一样!
}
最终方法
最终方法,就是一个不允许下级类去覆盖的方法!!
形式:
class类名{
final function方法名(形参列表...){ 。。。。。 }
}
设计模式
设计模式就是解决某个问题的一般性代码的经验性总结。
工厂模式
所谓工厂模式,就是这样一个类(就是所谓的工厂类):它可以根据“传递”给他的类名,而去生产出对应的类的对象。
工厂模式是我们最常用的实例化对象模式了,是用工厂方法代替new操作的一种模式。
单例模式
通过单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例。
抽象类和抽象方法
抽象类:一个不能实例化的类
形式:abstract class 类名{ }
抽象方法:是一个只有方法头,没有方法体的方法定义形式;
形式:abstract function 方法名(); //注意,这里必须有分号;
抽象类 和 抽象方法 注意事项
1 一个抽象方法必须在抽象类中,抽象类中不一定有抽象方法(不常见);
2 被定义为抽象的方法只是声明了其调用方式(参数),不能定义其具体的功能实现。 抽象方法是为了规定下级类中必须去完成对抽象方法的详细描述;
3子类实现父类的抽象方法的时候,其形参也应该跟父类保持一致,其访问权限也不能更小;——其原因其实这是“重写现象”,自然应该遵循重写的要求;
重载技术
重载的基本概念
PHP所提供的重载(overloading)是指动态地创建类属性和方法。简单来说,当对一个对象或类使用其未定义的属性或方法的时候,其中的一些“处理机制”。
我们是通过魔术方法(magic methods)来实现的。
属性重载
就是对一个对象的不存在的属性进行使用的时候,这个类中预先设定好的应对办法(处理机制);
属性,本质,就是变量,其只有4个操作:
取值:当对一个对象的不存在的属性进行“取值”的时候,就会自动调用内部方法:__GET()
__GET($属性名)
该方法可以带一个形参,表示这个要对之取值的不存在的属性名(字符串);
赋值:当对一个对象的不存在的属性进行“赋值”的时候,就会自动调用内部方法:__SET()
__SET($属性名,值)
它有2个形参,分别代表要对不存在的属性进行赋值的时候的“属性名”和“属性值”;
判断(isset):当对一个对象的不存在的属性进行isset()判断的时候,就会自动调用内部方法:__isset()
__isset(属性名)
销毁(unset):当对一个对象的不存在的属性进行unset()销毁的时候,就会自动调用内部方法:__unset()
__unset(属性名)
方法重载
当对一个对象的不存在的实例方法进行“调用”的时候,会自动调用类中的__call()这个魔术方法;
当对一个类的不存在的静态方法进行“调用”的时候,会自动调用类中的__callstatic()这个静态魔术方法;
__call($method_name , $argument )
$method_name 表示要调用的不存在的方法名
$argument 表示要调用的不存在的方法所使用的实参数据,是一个数组
接口
使用接口(interface),可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
接口是通过interface关键字来定义的,就像定义一个标准的类一样,但其中定义所有的方法都是空的。
接口中定义的所有方法都必须是公有,这是接口的特性。
接口里面只能放 “抽象方法” 和 “常量”。
形式:
interface 接口名{
常量1;
常量2;
.........
抽象方法1;
抽象方法2;
}
接口常量的使用形式为:接口名称::常量名称;
接口中的抽象方法,不要使用abstract修饰,也不需要使用访问控制修饰符,因为其天然就是public。
接口的作用
PHP是单继承,但是运用中往往要运用到 多继承,接口就是对没有多继承的类之间关系的一个补充。
接口可以实现“多继承”——但此时不称为继承而已,而是称为“实现”;
即: 接口1 -->>类1; 称为:类1实现了接口1;
其本质,其实就是类1中,有了接口1中“特征信息”;
使用形式:
class 类名 implements接口1,接口2.....{
}
类的自动加载
当某行代码需要一个类的时候,php的内部机制可以做到“自动加载该类文件”,以满足该行需要一个类的这种需求。
什么时候需要一个类
1 new一个对象的时候;
2 使用一个类的静态方法的时候;
3 定义一个类(B)并以另外一个类(A)作为父类的时候。
使用条件
1, 当需要一个类的时候,就会自动调用某个函数(默认是__autoload),并传入所需要的类的名字;
2,一个类应该保存到一个独立的“类文件中”:即其中只有该类的定义,没有别的代码;
3,习惯上,类文件的命名要有一定的“规则”,通常是:类名.class.php;
4,在自动加载函数中,形参为需要加载的类名,所以要拼凑好正确的路径。
自定义自动加载函数
__autoload 函数是系统定义的自动加载函数,在需要时,我们可以自定义 自动加载函数。
形式
spl_autoload_register(“函数1”); //声明“函数1”作为自动加载函数;
spl_autoload_register(“函数2”); //声明“函数2”也作为自动加载函数;
.........
定义(与__autoload一样)
function 函数1( $class_name ){
//.......
}
function函数2( $class_name ){
//.......
}
对象的复制(克隆)
对象的克隆,就是用于将一个对象“制作”双份,类似之前普通数据的“值传递”;
定义
$obj2 = clone $obj1; //这样,就有一个跟$obj1完全一样的新的对象。
__clone 魔术方法
使用 clone 会克隆出一个跟当前对象一样的新的对象,并且会调用该类中的魔术方法:__clone ,只要 该类中有该方法。
在单例模式中,应该禁止对该单例的克隆,所有要在该类中禁止__clone 魔术方法,做法就是私有化__clone
第4步私有化这个克隆的魔术方法
private function __clone {//不用写代码}
对象的遍历
对象的遍历,跟数组的遍历,一样!只能遍历出对象的“实例属性数据”。
foreach($对象名 as $key=>$value){
//处理$key $value
}
说明:1 $key 是对象的属性名,$value 是其对应的值;
2 能变量出来的属性,根据访问控制权限,只能是在该范围中的“可访问属性”;
PHP内置标准类
PHP中有很多 定义好的类,其中有一个叫 内置标准类。该类的内部什么都没有定义,可以理解为:
class stdclass{ }
作用:1 可以临时存储数据:$obj2 -> p1 = 100;
2 当其他数据类型转换为对象类型的时候,得到就就是一个内置标准类(stdclass)的一个对象实例;
形式: $obj = (object)其他数据类型;
①数组转为对象:数组的键名作为属性名,键值作为对应的值;数字下标的数组,不推荐转换,因为无法通过对象语法获取。
②null 转为对象 为空对象
③其他 标量数据 (布尔(boolean),整型(interger),浮点型(float/double), 字符串(string))转为对象,键名为 固定的“scalar”,值为 该变量的值;
类型约束
就是要求某个变量只能使用(接收,存储)某种指定的数据类型;
PHP只支持在函数(或方法)的形参上,设定类型的约束目标,形式:
function 函数名 (【要求使用的类型】形参1,【要求使用的类型】形参2.......){ }
说明:1 定义一个函数的时候,可以设定约束类型也可以不设定;
2 如果设定了约束类型,该实参就必须是该类型,否则就会报错;
3 能够使用约束类型的就只有一下几种:数组(array),对象(使用类的名称,表示该实参必须是该类的一个实例),接口(使用接口的名称,表示该实参必须是该接口的类的一个实例)
类相关的魔术方法
序列化与反序列化技术
序列化:就是将一个变量所代表的“内存”数据,转换为“字符串”形式并持久保存在硬盘上的一种做法。
$v1 = 100;
$s1 = serialize($v1); //将任何类型的变量数据,转化为字符串
file_put_contents('要保存的目标文件名',$s1);//保存到文件中去
反序列化:就是将序列化之后保存在硬盘上的“字符串数据”,恢复为其原来的内存形式的变量数据的一种做法。
$s1 = file_get_contents( ‘保存序列化数据的目标文本文件’); //从一个文件里读出其中的所有字符
$v1 = unserialize( $s1 ); //将该字符串数据,反序列化转换为变量(数据)
对象的序列化和反序列化
对象序列化只能保存其属性,而方法被忽略;而且会自动调用该对象所属类的魔术方法:__sleep()(前提是有该方法),且该方法必须返回一个数组,数组中是“计划”要进行序列化的属性名;
对象反序列化其实是恢复其原来保存起来的属性数据,而且,此时必然需要依赖该对象原本的所属类;对象在反序列化的时候,会自动调用该对象所属类的这个魔术方法:__wakeup()(前提是有该方法)。
__tostring()魔术方法(常用)
将一个对象“当做”一个字符串来使用的时候,会自动调用该方法,并且在该方法中,可以返回一定的字符串,以表明该对象转换为字符串之后的结果。
注意:如果没有定义该方法,则对象无法当做字符串来使用。
与类相关的方法属性
魔术常量:以前的笔记中有的 __FILE__ __DIR__ __LINE__
__CLASS__: 代表当前其所在的类的类名;
__METHOD__:代表其当前所在的方法名;
与类有关的系统函数:
class_exists(“类名”), 判断一个类是否存在(是否定义过)
interface_exists(“接口名”), 判断一个接口是否存在(是否定义过)
get_class( $obj ), 获得某个对象$obj的所属类
get_parent_class($obj ), 获得某个对象$obj的所属类的父类
get_class_methods(), 获得一个类的所有方法名,结果是一个数组,里面存储的是这些方法的名称
get_class_vars(), 获得一个类的所有属性名。结果是一个数组,里面存储的是这些属性的名称get_declared_classes() 获得“整个系统”所定义的所有类名;
与对象有关的系统函数:
is_object( $obj ): 判断某个变量是否是一个对象;
get_object_vars( $obj ):获得一个对象的所有属性;结果是一个数组,里面存储的是这些属性的名称
与类有关的运算符:
instanceof: 判断一个“变量”(对象,数据),是否是某个类的“实例”;
static关键字的新用法和总结(重要)
static这个关键字,也可以像“self”一样,代表“当前类”,用于访问一个类的“静态属性或静态方法”;但,static,在应用中,更灵活,因此更常见。因为static,它代表的是“调用”当前方法的类,而不是“其代码所在的类”;self它就比较死板,只代表这个单词本身所在位置的所在类。