PHP面向对象
面向过程编程:
其基本特征是:
将要完成的任务,分割为若干个步骤:
第1步:做什么。。。
第2步:做什么。。。
.......
最后,做完了,任务完成!
面向对象编程:OOP: Object Oriented Program(面向对象编程)
其基本特征是:
将要完成的任务,“分派”给不同的“对象”去做;
某对象1:会做什么。。。
某对象2:会做什么。。。
........
程序一旦启动,则各个对象“各司其职”,相互配合就完成了任务。
面向对象中的基本概念
类和对象
对象: 万物皆对象;
类: 任何对象,都可以人为“规定”为某种类型(类别);
class Person{
var $name ;
var $age;
var $edu;
}
我:
姓名:罗XX,
年龄40,
学历:大学;
王亮:
姓名:王亮
年龄:20;
学历:大学;
class Teacher{
var $name ;
var $age;
var $edu;
var $major; //专业
}
可见:
• 类是描述一类事物的一个总称,是具有相同特征特性的该类事物的一个通用名字(称呼);
• 对象是一个明确的具体的“物体”,是某个类中的一个“实物”(相对来说,类就是一种抽象的泛称)。对象离不开类,或者说,对象一定隶属于某个类——有类才有对象,先有类再有对象。
属性和方法
属性:就是原来的“变量”,只是现在它“隶属于”一个类了,即写在一个类中,就称为属性;
方法:就是原来的“函数”,只是现在它“隶属于”一个类了,即写在一个类中,就称为方法;
注意:属性和方法,已经不能“自由使用”了,而是都要通过这个类或这个类的对象去使用。
使用属性,就把它当做一个“变量”去使用就好了,只是需要该形式:对象->属性名;
使用方法,就把它当做一个“函数”去使用就好了,只是需要该形式:对象->函数名(实参列表...)
创建对象的几种形式
class C1{
var $p1 = 1; //定义一个属性;
}
形式1:
$o1 = new C1(); //通过一个类,去new出一个对象
形式2:
$o2 = new $o1(); //通过一个对象,去new出一个对象——其实是new出来的是旧对象所述类的一个新对象。
形式3:
$s1 = “C1”; //只是一个字符串变量而已;
$o3 = new $s1(); //这就是所谓的“可变类”——无非就是类的名字是一个变量而已。
形式4:
$o4 = new self(); //self表示“当前类本身”,它只能出现在一个类的方法中。
对象的传值方式:
为什么对于对象,值传递和引用传递,这个情况下,他们似乎没有区别???
这要从对象的数据的存储方式来理解:
$o1 = new C1(); //这里创建一个对象o1,其存储结果
这里,实际上,变量$o1中,存储的数据只是一个“对象编号#1”,这个对象编号,才会去指向对象数据new C1(); 该编号数据,我们不能控制,只是系统内部的分配。
在语法上,对对象变量进行的属性进行操作,其实就是通过该编号来指向对象而操作的。
$o1->p1 = 2; //此时,就是通过对象编号#1去修改了对象(new C1() )本身的内部数据.
echo $o2->p1; //此时相当于取得变量$o2所包含的编号#1中所指向的对象(new C1() )的内部数据p1
类中成员
类中成员概述
面向对象编程,是需要通过“对象”去做什么事情(以完成某种任务);
而:
对象总是来源于类;
所以:
面向对象的编程,一切都是从定义类开始;
类中成员分为3大类:
属性:
方法:
常量:
形式上,大致如下:
class 类名 {
常量定义1;
常量定义2;
.......
属性定义1;
属性定义2;
........
方法定义1;
方法定义2;
.......
}
说明:
以上各项,没有顺序问题;习惯上,其实常量放前面,然后是属性,然后是方法;
详细一点,就又分为:
属性:
普通属性;//一般属性,实例属性
静态属性;
方法:
普通方法;//一般方法,实例方法
静态方法;
构造方法;
析构方法;
常量:
类常量
当在一个类中定义一个常量时,该常量就称为“类常量”——本质其实还是常量;
定义形式:
class 类名{
const 常量名 = 常量值;
//不可以使用define()来定义!
}
使用形式:
常量的使用,是通过类名,并使用范围解析符(::)来取用的;
类名::常量名;
普通属性(实例属性):
实例的单词为:instance
实例,其实也叫做“对象”;
普通(实例)属性,就是一个可以在该类实例化出的对象上使用的属性!
定义形式:
class 类名{
var $属性名 = 初始值;
var $属性名; //这个属性没有初始值;
//上述的var 还可以使用public来代替,比如:
public $属性名 = 初始值;
public $属性名; //这个属性没有初始值;
}
使用形式:
是通过该类的对象,来使用普通属性(实例属性):
$对象->属性名;
因为,属性的本质就是变量,则其就可以当做一个变量来看待和使用,比如:
$v1 = $对象->属性名;
echo $对象->属性名;
$v2 = $对象->属性名 * 3 + 5; //进行计算
静态属性:
静态属性,本质上也是“变量”,但其有一个特点就是:该变量只隶属于“类”,即:
一个类中的一个静态属性,就只有“一份数据”;
但:
一个类中的一个实例属性,就可以有“多份数据”——每创建一个对象出来,就会有一份数据;
定义形式:
class 类名{
static $属性名 = 初始值;
static $属性名; //这个属性没有初始值;
}
使用形式:
使用类名和范围解析符(::)来对静态属性进行操作:
类名::$静态属性名; //注意:属性名带$符号
对比1:
常量的使用:类名::常量名;
对比2:
实例属性的使用:对象名->实例属性名; //注意:属性名不带$符号
1,实例属性,是每个对象都可以不一样的数据,也是每个对象都“独自拥有”的数据;
2,静态属性,他不属于任何一个对象,而只属于该类本身,也可以理解为为所有对象所共有的数据;
普通方法(实例方法)
一个类中定义的方法,可以为这个类的所有对象调用的方法。也可以理解为,这个类的所有对象,都各自有自己的一个该方法;
定义形式:
class 类名{
function 方法名(形参1,形参2,.... ){
//方法体。。。
}
}
调用形式:
$对象名->方法名(实参1,实参2,....);
静态方法
一个类中定义的方法,只隶属于这个类本身,而不是隶属于这个类的对象。
定义形式:
class 类名{
static function 方法名(形参1,形参2,.... ){
//方法体。。。
}
}
调用形式:
类名::方法名(实参1,实参2,....);
构造方法(__construct):
构造方法,是一个特殊的方法:
1,名字是固定的:_ _construct;
2,该方法通常都不要我们自己调用,而是在new一个对象的时候会自动调用。
3,该方法主要的目的是为了在new一个对象的时候,给该对象设置一些“初始值”(初始化工作);
4,构造方法的参数没有规定,通常是根据实际的需要来定义,目的是为了对象属性数据的初始化;
析构方法(__destruct):
说明:
1,析构方法是一个特殊的方法,名字为固定的词:_ _desctruct
2,析构方法是在一个对象被“销毁”的时候会自动被调用的方法——我们无法调用它;
3,析构方法不能带参数(形参),但方法中也可以使用$this这个词,代表“当前对象”;
对象在哪些情况下会被销毁?
1,如果程序结束,所有变量都会被销毁,自然,变量所代表的对象也会被销毁;
对象销毁的顺序,默认情况下,跟其创建的顺序相反;
2,当一个对象没有任何变量“指向”它的时候,即使程序还没有结束,也会被销毁;
基本概念
将一个类A中的特性信息,传递到另一个类B中,此时就称为:
B继承A
A派生出B
class A{
}
class B extends A{
}
几个基本概念
• 继承:一个类从另一个已有的类获得其特性,称为继承。
• 派生:从一个已有的类产生一个新的类,称为派生。
• 父类/子类:已有类为父类,新建类为子类。父类又可以称为“基类”,上级类,子类又称为派生类,下级类,
• 单继承:一个类只能从一个上级类继承其特性信息。PHP和大多数面向对象的语言都是单继承模式。C++是多继承。
• 扩展:在子类中再来定义自己的一些新的特有的特性信息(属性,方法和常量)。没有扩展,继承也就没有意义了。
访问控制修饰符
形式:
class 类名{
访问控制修饰符 属性或方法定义;
}
有3个访问修饰符:
• public公共的:在所有位置都可访问(使用)。
• protected受保护的:只能再该类内部和该类的子类或父类中访问(使用)。
• private私有的:只能在该类内部访问(使用)。
他们的作用是:用来“限制”其所修饰的成员的“可访问性”;
可访问性:
就是在代码中使用这样两种语法形式的“有效性”(合法性):
对象->实例属性或方法;
类::静态属性或方法;
访问控制修饰符,需要结合使用该语法形式的所在位置,才能确定是否可访问。
有3个访问位置(范围):
某个类的内部:
某个类的继承类的内部:
某个类的外部:
1,public修饰的成员,哪里都能访问;
2,类的内部,可以访问任何级别的成员;
3,public具有最宽泛的可访问性;private具有最狭小的可访问性;protecte则居中;
parent关键词
parent表示“父母”的意思,在面向对象语法中,代表“父类”
——本质上就是代表父类这个“类”,而不是父类的“对象”;
其使用方式为:
parent::属性或方法; //通常是静态属性或静态方法,但有时候可能是实例属性或实例方法;
构造方法和析构方法调用上级同类方法的问题
1,如果一个类 有 构造方法,则实例化这个类的时候,就 不会 调用父类的构造方法(如果有);
2,如果一个类没有构造方法,则实例化这个类的时候,就会自动调用父类的构造方法(如果有);
3,如果一个类 有 析构方法,则销毁这个类的时候,就 不会 调用父类的析构方法(如果有);
4,如果一个类没有析构方法,则销毁这个类的时候,就会自动调用父类的析构方法(如果有);
5,如果一个类中有构造方法或析构方法,则就可以去“手动”调用父类的同类方法(如果有);
手动调用的语法形式总是这样:
parent::构造方法或析构方法()
则,第5种情况,parent在构造方法中的一个典型代码(写法):
(在子类的构造方法中,常常需要去调用父类的构造方法,以简化对象的初始化工作。)
覆盖(override):
基本概念
覆盖,又叫“重写”:
含义:
将一个类从父类中继承过来的属性和方法“重新定义”——此时相当于子类不想用父类的该属性或方法,而是想要定义。
覆盖的现实需要:
对于一个父类,或许其属性的现有数据(值),子类觉得不合适,而需要有自己的新的描述;
或许其方法,子类觉得也不合适,需要自己来重新定义该方法中要做到事。
此时就可以使用覆盖。
重写的基本要求:
访问控制权限:
子类覆盖的属性或方法的访问控制权限,不能“低于”父类的被覆盖的属性或方法的访问控制权限:
具体来说:
父类: public 子类:只能是public
父类: protected 子类:可以说protected和public
父类: private 子类:不能覆盖!——既父类的私有成员,不存在被子类覆盖的可能。
方法的参数形式:
子类覆盖父类的同名方法的时候,参数要求跟父类保持一致;
特例:
构造方法重写的时候参数可以不一致
小注意:
虽然父类的私有属性不能被覆盖,但子类却可以定义自己的跟父类同名的属性;
虽然父类的私有方法不能被覆盖,但子类也不能定义自己的同名方法;
最终类
最终类,其实就是一种特殊要求的类:要求该类不允许往下继承下去。
形式:
final class 类名{
//类的成员定义。。。跟一般类的定义一样!
}
最终方法
最终方法,就是一个不允许下级类去覆盖的方法!!
形式:
class 类名{
final function 方法名(形参列表...){ 。。。。。 }
}
设计模式
什么叫设计模式?
简单来说,设计模式就是解决某个问题的一般性代码的经验性总结。
类比来说:
它类似之前所学的“算法”:针对某种问题,使用某种特定的语法逻辑就可以完成该任务。
工厂模式
所谓工厂模式,就是这样一个类(就是所谓的工厂类):
它可以根据“传递”给他的类名,而去生产出对应的类的对象。
class A{}
class B{}
class FactoryClass{
static function GetObject($className){
$obj = new $className();
return $obj;
}
}
$obj1 = FactoryClass::GetObject("A");
$obj2 = FactoryClass::GetObject("B");
$obj3 = FactoryClass::GetObject("A");
单例模式:
例,就是实例(Instance),其实就是对象(object)
单例:就是一个对象;
单例模式:就是设计这样一个类,这个类只能“创造”出它的一个对象(实例);
class Singleton{
private function __construct(){
}
static private $instance = null;
static function ShareInstance(){
if(!isset(self::$instance)){
$obj = new self();
self::$instance = $obj;
return $obj;
}
return self::$instance;
}
}
设计目标:
1,该类一实例化,就可以自动连接上mysql数据库;
2,该类可以单独去设定要使用的连接编码(set names XXX)
3,该类可以单独去设定要使用的数据库(use XXX);
4,可以主动关闭连接;
上述设计目录,大致上相当于如下几行代码:
$link = mysql_connect(“localhost”, “root”, “123”);
mysql_query(“set names XXX”);
mysql_query(“use XXX”);
然后,后面就可以执行各种sql语句来:
$r1 = mysql_query(“insert into ....”);
$r2 = mysql_query(“delete from .....”)
$r3 = mysql_query(“select * from ....”);
class MySQLDB{
publick $link = null;
function __construct($host,$port,$username,$password,$charset,$dbname){
$this->link = @mysql_connect("$host:$port","$username","$password") or die("connect failure!");
mysql_query("set names $charset");
mysql_query("use $dbname");
}
function setCharset($charset){
mysql_query("set names $charset")
}
function selectDB($dbname){
mysql_query("use $dbname");
}
function closeDB(){
mysql_close($this->link)
}
}
//使用
$host = "localhost";
$port = 3306;
$username = "root";
$password = "aaa"
$charset = "utf8";
$dbname = "info";
$db = new MySQLDB($host,$port,$username,$password,$charset,$dbname);
$result = mysql_query("select * from tab_int");
$db->setCharset("gbk");
$db->closeDB();
抽象类:
是一个不能实例化的类;
定义形式:
abstract class 类名{}
为什么需要抽象类:
它是为了技术管理而设计!
抽象方法:
是一个只有方法头,没有方法体的方法定义形式;
定义形式:
abstract function 方法名( 形参1,形参2,.... ); //注意,这里必须有分号;
为什么需要抽象方法:
它也是为了技术管理而设计:要求下级类需要去实现这个方法的“具体做法”;
抽象类和抽象方法的细节
1,一个抽象方法,必须在抽象类中;
2,反过来,抽象类中可以没有抽象方法——虽然不常见;
3,可见:抽象方法是为了规定下级类中“必须”要具体去完整某个工作(任务);
4,下级类中继承了上级类的抽象方法,则要么去“实现该方法的具体内容”,要么自己也作为抽象类(即其继承的抽象方法仍然是抽象的);
5,子类实现父类的抽象方法的时候,其形参也应该跟父类保持一致,其访问权限也不能更小;
——其原因其实这是“重写现象”,自然应该遵循重写的要求;
重载技术overloading
重载的基本概念
重载在“通常面向对象语言”中的含义:
是指,在一个类(对象)中,有多个名字相同但形参不同的方法的现象;
类似这样:
class C{
function f1(){。。。}
function f1($p1){。。。}
function f1($p1, $p2 ){。。。}
}
$c1 = new C();
$c1->f1();
$c1->f1(2);
$c1->f1(3,4);
重载在“php语言”中的含义:
是指,当对一个对象或类使用其未定义的属性或方法的时候,其中的一些“处理机制”;
比如:
class A{
public $p1 = 1;
}
$a1 = new A();
echo $a1->p1; //1;
echo $a1->p2; //出错,未定义的属性!
则:php中的重载技术,就是来应对上述“出错”的情况,使代码不出错,而且还能“优雅处理”;
class A{
public $p = 1;
}
$a = new A();
echo $a->p; //1
echo $a->p2; //报错
//处理
class A{
public $p = 1;
function __get($pro_name){
echo "调用了未定义字段"
}
}
$a = new A();
echo $a->p; //1
echo $a->p2; //不报错,直接调用方法->调用了未定义字段
属性重载
就是对一个对象的不存在的属性进行使用的时候,这个类中预先设定好的应对办法(处理机制);
属性,本质,就是变量,其只有4个操作:
取值:
当对一个对象的不存在的属性进行“取值”的时候,就会自动调用内部方法:__GET()
赋值:
当对一个对象的不存在的属性进行“赋值”的时候,就会自动调用内部方法:__SET()
判断(isset):
当对一个对象的不存在的属性进行isset()判断的时候,就会自动调用内部方法:__isset()
销毁(unset):
当对一个对象的不存在的属性进行unset()销毁的时候,就会自动调用内部方法:__unset()
以上,4个方法,被称为“魔术方法”;
__GET($属性名):
在对一个对象的不存储的属性进行“取值”的时候,会自动调用的方法;
我们其实是可以使用该方法来对这种“意外”情况进行某种特别的处理。
其中,该方法可以带一个形参,表示这个要对之取值的不存在的属性名(字符串);
__SET($属性名,值):
当对一个对象的不存在的属性进行“赋值”的时候,就会自动调用这个内部的魔术方法;
它有2个形参,分别代表要对不存在的属性进行赋值的时候的“属性名”和“属性值”;
这个方法,结合__GET方法,往往可以使我们定义的类,就有一种“可方便扩展属性”的特性。
即:类(或对象)的属性,可以更为方便自由,
class A{
protected $prop_list = array();
function __set($p,$v){
$this->prop_list[$p] = $v;
}
function __get($p){
return $this->prop_list[$p];
}
}
__ISSET($属性名):
当对一个对象的不存在的属性进行isset()判断的时候,就会自动调用内部方法:__isset();
用法:
$v1 = isset($对象 -> 不存在的属性); // 此时就会调用这个对象的所属类中的魔术方法:__isset()
function __isset($prop){
$v = isset($this->prop_list[$prop]);
return $v;
}
__UNSET($属性名):
当对一个对象的不存在的属性进行unset()销毁操作的时候,就会自动调用内部方法:__unset()
function __unset($prop){
unset($this->prop_list[$prop]);
}
方法重载
当对一个对象的不存在的实例方法进行“调用”的时候,会自动调用类中的__call()这个魔术方法;
当对一个类的不存在的静态方法进行“调用”的时候,会自动调用类中的__callstatic()这个静态魔术方法;
class A{
function __call($method,$args){
echo "__call被调用了!"
}
}
利用php的重载技术,实现通常的“方法重载”
什么是接口?
先看抽象类:
abstract class 类名 {
属性1;
属性2;
.....
非抽象方法1;
非抽象方法2;
......
抽象方法1;
抽象方法2;
......
}
设想,将上述抽象类中“实在的成员”,删除,即删除那些非抽象的成员。则,自然该抽象类中,就只有抽象方法;
abstract class 类名 {
抽象方法1;
抽象方法2;
......
}
由此,可以理解为:这个抽象类,“太抽象了”,几乎自己什么都没做,就光让别人做什么。
那么:
接口就是这样一个“天然不实在”的家伙:
接口,就是规定,里面只能放“抽象方法”和“常量”的一种类似类的语法结构;
——可见,接口就是“比抽象类更抽象的”一种语法结构。
接口(interface)定义形式:
interface 接口名{
常量1;
常量2;
.....
抽象方法1;
抽象方法2;
.....
}
说明:
1,可见,接口中,只有常量(接口常量)和抽象方法两种成员;
2,接口常量的使用形式为: 接口名称::常量名称;
3,接口中的抽象方法,不要使用abstract修饰,也不需要使用访问控制修饰符,因为其天然就是public
为什么需要接口?
面向对象编程思想是对“现实世界”的描述(模拟)!
现实世界往往都都是多继承的;
但:
出于降低类跟类之间关系的复杂度的考虑,就将语言设计为单继承的;
但这样,就无法表达出现实世界的多继承特性;
则:
接口就是对没有多继承的类之间关系的一个补充;
因为:接口可以实现“多继承”——但此时不称为继承而已,而是称为“实现”;
即:
接口1 -->> 类1;
就称为:类1实现了接口1;
其本质,其实就是类1中,有了接口1中“特征信息”;
使用形式:
形式为:
class 类名 implements 接口名1, 接口名2, ....{
//类的定义。
}
这里,叫做,类实现了接口。
其中,接口跟接口之间,也可以继承,跟类之间的继承:
interface 接口1 extends 接口2{
//接口的成员定义;。。。。
}
进一步完善mysqldb工具类:
完善分2个方面:
1,现有已经完成的功能,做优化处理;
2,添加更多的功能,要求继续实现如下功能:
2.1:用该类的对象可以执行任意的增删改语句,并返回布尔值;
2.2:用该类的对象可以执行返回一行数据的“查询语句”:结果是一个一维数组,类似这样:
array( ‘id’=>3, ‘name’=>’张三’, ‘age’ => 18, ‘edu’=>’大学’ );
2.3:用该类的对象可以执行返回多行数据的“查询语句”:结果是一个二维数组,类似这样:
array(
0=>array( ‘id’=>3, ‘name’=>’张三’, ‘age’ => 18, ‘edu’=>’大学’ ),
1=>array( ‘id’=>4, ‘name’=>’张四’, ‘age’ => 14, ‘edu’=>’中学’ ),
2=>array( ‘id’=>7, ‘name’=>’张七’, ‘age’ => 17, ‘edu’=>’小学’ )
)
2.4:用该类的对象可以执行返回一个数据的“查询语句”:结果是一个数据值,其sql语句常常类似这样: select name from Users where id = 1; 或: select count(*) as c from 表名;
含义:
当某行代码需要一个类的时候,php的内部机制可以做到“自动加载该类文件”,以满足该行需要一个类的这种需求。
什么时候需要一个类?
1,new一个对象的时候;
2,使用一个类的静态方法的时候;
3,定义一个类(B)并以另一个类(A)作为父类的时候;
function __autoload($name){
require_once './'.$name.'.class.php'
}
$config = array(
"host"=>"localhost",
"port"=>3306,
"username"=>"root",
"password"=>"aaa",
"charset"=>"utf8",
"dbname"=>"db123"
);
$db = MySQLDB::ShareInstance($config);
条件和要求
1, 当需要一个类的时候,就会自动调用某个函数(默认是__autoload),并传入所需要的类的名字
2, 一个类应该保存到一个独立的“类文件中”:即其中只有该类的定义,没有别的代码;
3,习惯上,类文件的命名要有一定的“规则”,通常是:类名.class.php
4,通常,我们需要将各种类,存储在一些特定的目录中,以方便确定其位置!
5,在该自动加载的函数中,“充分”使用传过来的类名,以构建一个合适的文件路径并载入;
自定义自动加载函数:
刚才,__autoload()函数,是系统内部的自动加载函数,我们只是定义其函数体。
但:
我们可以使用更多函数(自定义的),来实现更灵活的自动加载!
基本模式为:
spl_autoload_register(“函数1”); //声明“函数1”作为自动加载函数;
spl_autoload_register(“函数2”); //声明“函数2”也作为自动加载函数;
.........
然后,就去定义这些函数,跟定义__autoload()函数一样:
function 函数1( $class_name ){
//.......
}
function 函数2( $class_name ){
//.......
}
.............
这样,系统就会一次调用这些自动加载函数去加载所需要的类,直到加载成功!
spl_autoload_register("autoload1");
spl_autoload_register("autoload2");
function autoload1($class_name){
$file = './class/'.$class_name.".class.php";
if(file_exists($file)){
include_once $file;
}
}
function autoload2($class_name){
$file = './lib/'.$class_name.".class.php";
if(file_exists($file)){
include_once $file;
}
}
对象的复制(克隆)
$obj1 = new A();
$obj1->p1 = 11;
$obj2 = $obj1; //值传递
//则,现在有几个对象?——1个对象!
当然:
$obj3 = & $obj1;
结果,还是一个对象!
对象的克隆语法,就是用于将一个对象“制作”双份的语法,类似之前普通数据的“值传递”;
语法:
$obj2 = clone $obj1; //这样,就有一个跟$obj1完全一样的新的对象。
对象的遍历
对象的遍历,跟数组的遍历,一样!
其实,只能遍历出对象的“实例属性数据”
foreach( $对象名 as $key => $value){
//这里就可以处理$key和$value
//但注意:
1, $key表示的是对象 的 “属性”,$value是其对应值;
2, 这里能够遍历出来的属性,只能是在该范围中的“可访问属性”(就是要考虑访问控制权限)
}
将一个对象的所有属性都遍历出来
class A{
public $p1 = 1;
protected $p2 = 2;
private $p3 = 3;
static $p4 = 4;
function showAllProperties(){
foreach($this as $key=>$vlue){
...
}
}
}
PHP内置标准类
php语言内部,有“很多现成的类”,其中有一个,被称为“内置标准类”。
这个类“内部”可以认为什么都没有,类似这样:
class stdclass{ }
其作用,可以用于存储一些临时的简单的数据:
$obj1->pp1 = 1;
$obj1->port = ‘3306’;
也可以用于类型转换时用于存储数据,如下节所示
其他数据类型转换为对象类型
其他数据类型转换为对象类型,得到的结果是:内置标准类(stdclass)的一个对象!
语法形式为:
$obj1 = (object) 其他类型数据;
• 数组转换为对象:数组的键名当作属性名,值为对应值;
注意:数字下标的数据元素,转换为对象后的属性,无法通过对象语法获取,因此不推荐转换。
• null转换为对象:空对象;
$obj1 = (object)null;
• 其他标量数据转换为对象:属性名为固定的“scalar”,值为该变量的值:
类型约束
什么叫类型约束?
就是要求某个变量只能使用(接收,存储)某种指定的数据类型;
php属于“弱类型语言”,通常不支持类型约束;
相应的,强类型语言,类型约束却是其“基本特征”。
php中,只支持局部的部分类型约束
php中,只支持在函数(或方法)的形参上,设定类型的约束目标,形式如下:
function 方法名(【要求使用的类型】$p1 , 【要求使用的类型】$p2, ..... ){
//..........
}
说明:
1,定义一个函数(方法)时, 一个形参,可以使用类型约束,也可以不使用;
2,如果使用了类型约束,则对应的该实参数据,就必须是要求的那种类型。
3,能够使用的类型约束,其实非常少,只有以下几种可用:
数组: array,
对象:使用类的名称,表示,传递过来的实参,必须是该类的实例;
接口: 使用接口的名称,表示,传递过来的实参,必须是实现了该接口的类的实例
单例类的加强:禁止克隆
对于一个类的对象,如果使用“clone运算符”,就会克隆出一个跟当前对象完全一样的新对象出来,
并且:
此时还会自动调用该类中的魔术方法:_ _c l o n e ();只要其中有该方法;
则,要想实现单例类,就应该对这个单例类的对象“禁止克隆”,做法是:
私有化这个魔术方法:_ _c l o n e ();
与类有关的其他魔术方法
序列化与反序列化技术
含义:
序列化:
就是将一个变量所代表的“内存”数据,转换为“字符串”形式并持久保存在硬盘上的一种做法。
反序列化:
就是将序列化之后保存在硬盘上的“字符串数据”,恢复为其原来的内存形式的变量数据的一种做法。
序列化的做法:
$v1 = 123; //这是一个变量,代表任意的内存数据
$s1 = serialize( $v1 ); //将任何类型的变量数据,转换为“字符串”
file_put_contents( ‘要保存的目标文本文件’, $s1); //将该字符串,保存到一个文件里(就是硬盘数据)
反序列化的做法:
$s1 = file_get_contents( ‘保存序列化数据的目标文本文件’); //从一个文件里读出其中的所有字符
$v1 = unserialize( $s1 ); //将该字符串数据,反序列化转换为变量(数据)
__sleep():用于对象的序列化:
1,对一个对象进行序列化,只能将其属性数据“保存起来”,而方法被忽略(方法不是数据)
2,对象的序列化的时候,会自动调用该对象所属类的这个魔术方法:__sleep()(前提是有该方法)。
且,此时,该方法必须返回一个数组,数组中是“计划”要进行序列化的属性名;
__wakeup:用于对象的反序列化:
1,对一个对象进行反序列化,其实是恢复其原来保存起来的属性数据,而且,此时必然需要依赖该对象原本的所属类;
2,对象在反序列化的时候,会自动调用该对象所属类的这个魔术方法:__wakeup()
__tostring()魔术方法——比较常用!
含义:
将一个对象“当做”一个字符串来使用的时候,会自动调用该方法,并且在该方法中,可以返回一定的字符串,以表明该对象转换为字符串之后的结果。
注意:
如果没有定义该方法,则对象无法当做字符串来使用!
__invoke()魔术方法:
将对象当作函数来使用的时候,会自动调用该方法。通常不推荐这么做。
class A{
function __invoke(){
echo “
我是一个对象呀,你别当我是一个函数来随便调用!”;
}
}
$obj1 = new A();
$obj1(); //此时就会调用类中的方法:__invoke()
其他零碎:
与类有关的魔术常量:
以前学过的魔术常量:
__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 ):获得一个对象的所有属性;结果是一个数组,里面存储的是这些属性的名称
与类有关的运算符:
new,
instanceof: 判断一个“变量”(对象,数据),是否是某个类的“实例”;
示意如下:
class A {}
class B {}
class C extends A{}
$a1 = new A();
$b1 = new B();
$c1 = new C();
$v1 = $a1 instanceof A ; //结果是true
$v2 = $b1 instanceof A ; //结果是false
$v3 = $c1 instanceof C ; //结果是true
$v4 = $c1 instanceof A ; //结果是true
——推论:一个类的对象,必然也属于这个类的上级类的对象;
static关键字的新用法和总结:
static这个关键字,也可以像“self”一样,代表“当前类”,用于访问一个类的“静态属性或静态方法”;
但,
static,在应用中,更灵活,因此更常见!
因为static,它代表的是“调用”当前方法的类,而不是“其代码所在的类”:
self它就比较死板,只代表这个单词本身所在位置的所在类。
面向对象编程思想的3个特征:
封装:
无非是一个大的指向思想,目的是为了将一个类设计得更为健壮!
其基本做法是:
尽可能地将一个类的成员私有化,只开放那些必不可少的对外的属性或方法,能private的就不要protected。能protected就不要public
继承:
是面向对象的基本思想和基本做法。
继承是代码重用的一种重要机制。
多态:
多态,就是“多种形态”,其实指的是,现实世界的“丰富多彩的表现形式”,比如:
人在吃饭;
猪在吃食;
鱼在进食;
小鸡啄米;
。。。。
他们都是“吃”这个行为!
在实际代码(应用)中,多态常常有两种表现形式:
1, 不同对象,使用相同的方法,会表现为不同的结果!
2, 同一个对象,使用相同的方法,也可能会表现为不同的结果——这其实是“方法重载现象”