PHP面向对象的程序设计:
**关于面向对象程序设计的定义:**这是一种计算机的变成架构,他的一条基本原则是:计算机程序是由单个能够起到子程序作用的单元或是对象组合而成的,为了实现整体的运算,每个对象都能够接受,信息,处理数据和向其他对象发送信息。
这方面一般是被分为两个:
1、面向对象的技术的语法
2、面向对象的编程思想
类与对象之间的关系:
类描写了一组具有相同特性和相同行为方法的对象。(这一部分其实就是和HTML里面的类是一样的)
.(之前说的是错的,正确的类比应该是C语言或是C++里面的结构体,我之前是把这个当成CSS里面的类来理解的,但是发现根本就理解不同,不是一回事,毕竟在CSS里面,类是用来模块化代码的,将各种美化效果加上去的,本质上其实还是一种标记类语言,是不会用到变量的。)
在开发时一般时先抽象类,在去用该类去创建对象。
类的声明:
关于类的声明格式:
[一些修饰类的关键字]class 类名{
类中成员;
}
成员属性:
在类中直接声明变量,就称为成员属性,可以在类中声明多个变量,即是说对象中具有多个成员属性,每个变量都是存储在对象中的不同的属性信息。
由于成员属性可以使用PHP中的标量类型和复合类型,所以也可以是其他类实例化的对象,但是在类中使用资源和空类型是没什么意义的。
值得注意的一点是说,在类中声明变量的时候是可以给变量赋值的,但是因为类变量使用来抽象一个或是多个对象的,所以说,其实赋值对类的成员属性来说是没什么意思的。一个是有的时候本身就不需要赋值,有的时候是赋值了之后反而会造成不必要的效果。
值得注意的一点是:
在类中进行对变量的申明时,是必须要在前面加上前缀的,其中的前缀有多种类型,且带有各不相同的特性。而其中最为常见,且不带特性的就是var前缀。
eg:
class Person{
var $name; //第一个成员属性,用来存储人的名字
var $age; //第二个成员属性,用来存储人的年龄
public $sex; //第三个成员属性,用来存储人的性别,同时将该成员属性声明为公有的权限。
}
类似这样的前缀,或是说关键字有很多的种类,一旦要使用别的关键字来修饰,就必须要去除掉var。
成员方法:
简单来说,类中申明的函数,就是成员方法,一般是用来操作本对象中的成员属性,来完成对象的一些行为的方法。
如果一次性申明多个函数,在成员中就有多个成员方法。
成员方法的申明和一般的函数声明是完全一样的,都是要使用function()关键字来进行函数的定义。而且在定义成员方法的时候,也可以使用在定义成员属性时使用的关键字,注意,这个地方是**“可以使用”**而不是必须使用,之前的成员属性是必须要使用一个关键字来进行申明。
eg:
class Person{
function say(){
方法体; //声明第一个成员方法,也就是一个特别的函数
}
function eat($food){
方法体; //声明第二个成员方法,其中调用了一个名为$food的参数
}
private function run(){
方法体; //声明了第三个成员方法,其中在方法的前面加上了一个关键字,修饰控制了访问的权限
}
}
注:在一个类里面可以同时具有成员属性以及成员方法,这两个并不会相互冲突,基本上是属于一个平级的关系。
通过类实例化对象:
很好理解,类是一个抽象化出来的对象,或者说类其实就是很多对象所具有的共同特点抽象出来聚合的一个集合。因此,要使得类发挥出它的效果,就必须要一个对象,也就是来实例化对象。
实例化对象的方法:
*需要使用到一个关键字:*new
如果在实例化对象的时候不需要为对象传递参数,就直接在new关键字的后面直接使用类的名称,不需要加上括号。
eg:
1、需要传递参数的时候:
$变量名 = new 类名称([参数列表]);
2、不需要传递参数的时候:
$变量名 = new 类名称; //此时就没有加上像上面一样的括号来放入参数。
在其中,$变量名是通过类所创建的一个对象的引用名称,将来通过这个引用来访问对象中的成员,而关键字new表明这是要创建一个新的对象,类名称表示新对象的类型,而参数指定了类的构造方法用于初始化对象的值。如果类中没有定义一个构造函数,那么PHP会自动的创建一个不带参数的默认构造函数。
(这一部分差不多可以当作C语言中的结构体来理解,所谓的变量名其实就是一个新创建的结构体变量的名称,其本身就是对象,而对象中的成员其实就是C语言中结构体里面的各种变量或是组成部分,因此,在PHP中可以使用变量名来对其中的成员进行访问。)
其中,通过同一个类化出的每个变量都是独立的,不相互影响,在内存中都各自占有自己的一个空间,这一点其实也很好理解,说白了就是几个名称不同的结构体变量,只是类型一样而已,根本就不是一个东西。
关于对象类型在内存中的分配:
(这部分很好,让我补充了一部分关于内存的概念)
对象类型也是一种数据类型(这一点就和之前想的是一样的)
关于内存结构:在逻辑上内存结构大体被分为四段
1、栈:特点是空间小,但是可以以极快的速度被CPU访问,一般是用来存放程序中临时创建的变量,栈区有一个特点,叫做后进先出,因为这个特点,栈用来保存和恢复调用现场非常的方便。(我一般会把栈想象成一个弹夹,先压入的子弹最后打出),因此,可以将栈看成一个临时的数据存储,交换的内存区,用于存储占用空间长度不变并且占用空间小的数据类型的内存段。例如,整型1、100、10000,等在内存中占用的空间是等长的,都是32位4字节。此外,double、boolean等类型的数据都可以存储在栈里面。
2、堆:堆是用于存放进程运行中被动态分配的内存段,大小宾补固定,可以动态的扩张或是缩减,一般用于存储数据长度可变或是占用内存比较大的数据,比如说字符串、数组、对象都存储在这里面。
3、初始化数据段:用来存放可执行文件中的已经被初始化的全局变量,即是用来存储程序静态分配的变量。
4、代码段:用来存放可执行文件的操作指令的地方,也就是说这基本上就是可执行程序在内存中的镜像。代码段需要防止被非法修改,所以是一个只读的部分,要举例来说的话,程序中的函数的部分一般就存放在这段内存里面。
而其中,对象类型的数据就是一种占用空间比较大的数据类型,并且是占用的空间不定长的数据类型,所以对象创建完成以后就是直接被存放在堆内存里面的,但是对象的引用名称是存放在栈里面的。
*这两点都是和之前所写的堆、栈的特性有关*
在程序运行中,栈内存中的数据是可以直接存取的,而堆内存中的是不可以直接存取的内存,但可以通过对象的引用名称访问对象中的成员。
这一部分就有点像是数组里面的关系,使用一个键值对来对数组中的内容进行存取删改,只不过现在换成了类而已。实际的原理其实更相像于指针。
一个类的名称作为可存取数据存放在栈中,而类的内容作为一个不可存取的数据放在堆中。
在PHP中 ,只要使用了一次new关键字,就会直接的实例化出一个对象,并在堆中开辟一块自己的空间。这其实就是相当于创建了一个全新的对象,因为每个对象之间都是使用独立的空间,所以会直接的开辟一个新的空间,并在这个空间中存放自己的成员。
而类的名和成员的关联方法其实比较类似于指针,每个类的存放在栈中的名字,都是和存放在堆中的成员的首地址一一对应的,而当使用赋值符号=的时候,就相当于是在两者之间创造了一个指针。
例如:
$person1 = new Persona();
这就是相当于一个指针的操作,在变量$personal里面存放的是一个十六进制整数,也就是Person()这个类在堆区域中的首地址。
对象中成员的访问:
对象中包含成员属性和成员方法,访问对象中的成员则包括成员属性的访问和成员方法的访问。
而对成员方法的访问有包括赋值操作和获取成员属性值的操作。
访问对象中的成员和访问数组中的元素相似,只能通过对象的引用来访问对象中的每个成员,在访问的同时还要使用一个特殊的运算符号"->"来完成对成员的访问。
eg:
$引用名 = new 类名称([参数列表]); //对象实例化格式,简单来说就是将这个类赋予一个对象
$引用名 -> 成员属性 = 值; //对成员属性赋值的操作,例如$personal -> name = "张三"
echo $引用名 -> 成员属性; //获取成员属性的值,例如echo $personal -> name;
$引用名 -> 成员方法; //访问对象中的成员方法,例如$personal -> say();
基本上来说,把这个新的运算符号理解成一个箭头就是了,使用引用名然后再使用这个箭头符号就可以访问对应的成员属性的值了,同样的做法也可以用来访问对应的成员方法的使用。
如果在对象中的成员不是静态的,这就是唯一的访问方式。
特殊的对象引用"$this":
好吧,我理解错了,这一节还不算是俄罗斯套娃。
这一部分的主要效果其实就是,要考虑到对象中的一些成员方法可能会引用对象自身内部的别的成员方法,或是对象中的一些成员属性的情况,当出现了这种情况之后,就需要用到$this这个关键字来进行重复引用。
只要对象一旦被创建,在对象中的每个成员方法里都会存在一个特殊的对象的引用" t h i s " 。 成 员 方 法 属 于 哪 个 对 象 , this"。成员方法属于哪个对象, this"。成员方法属于哪个对象,this引用就代表哪个对象。专门用来完成对象内部成员之间的访问。
也就是说,这种特殊的对象引用 t h i s 就 是 在 对 象 内 部 的 成 员 方 法 中 , 代 表 ∗ ∗ “ 本 对 象 ” ∗ ∗ 的 一 个 引 用 , 但 是 只 能 在 对 象 的 成 员 方 法 中 使 用 。 不 管 是 在 对 象 的 内 部 使 用 this就是在对象内部的成员方法中,代表**“本对象”**的一个引用,但是只能在对象的成员方法中使用。不管是在对象的内部使用 this就是在对象内部的成员方法中,代表∗∗“本对象”∗∗的一个引用,但是只能在对象的成员方法中使用。不管是在对象的内部使用this访问自己对象内部的成员,还是在对象外部通过对象的引用名称访问对象中的成员,都需要使用特殊的运算符号"->"来完成访问。
在这里我来总结一下这一部分的意思~~(说说人话)~~:
其实$this就是一个特殊的指针,其代表的是一个对象本身,这是每个对象一创建的时候就自带的,而且只能在对象的成员方法中使用这个特殊引用,因此,在不同的对象中使用这个引用,对应的对象也是不一样的。
在书上的170页有个案例,不记得了就去看一眼。
构造方法与析构方法:
构造方法与析构方法是对象中的两个特殊方法,它们都与对象的生命周期有关。
构造方法是对象创建完成后第一个被对象自动调用的方法,这是我们在对象中使用构造方法的原因**(进行初始化用)**。
析构方法是对象在被销毁之前使用的最后一个被对象自动调用的方法,这也是我们在对象中使用析构方法的原因**(进行销毁用)**。
通常来说,会使用构造方法来完成一些对象的初始化工作,然后使用析构方法来完成一些对象在销毁前的清理工作。
1、构造方法:
在每个声明的类中都有一个称为构造方法的特殊成员方法,如果没有显式声明它,那么类中就会默认的存在一个没有参数列表,且内存为空的构造方法。相对的,如果显式地声明它,则类中地默认构造方法将不会存在(就理解为不设置就会自动生成一个空地构造方法就可以了)。
当创建一个对象地时候,构造方法就会被自动调用一次,即是每次使用关键字new来实例化对象的时候,都会自动的调用这个构造方法,不能主动通过对象的引用调用构造方法。所以通常使用构造方法执行一些有用的初始化任务,比如对成员属性在创建对象时的初赋值等。
在类中对构造方法进行声明:
在类中对构造方法进行声明的时候,构造方法的方法名称必须是以下划线开始的__construct(),这是PHP5中的变化。在PHP5以前的版本中,构造方法的方法名称必须和类名称相同,这种方法在PHP5中同样可以使用。
(注:出于这个原因在创建对象的时候,如果在一个类里面没有名为__construct()的构造方法,那么PHP就会搜索与类名相同的构造方法去执行。)
格式:
function __construct([参数列表]){ //构造方法名称是以两个下划线开始的__construct()
//方法体,通常用来对成员属性进行初始化赋值
}
在一个类中只能声明一个构造方法,在PHP中没有构造方法重载。
但是可以在声明构造方法的时候使用默认函数,实现其他面向对象的编程语言中构造方法重载的功能,也就是说如果在创建对象的时候,没有传入参数进入构造方法中,就可以使用默认参数为成员属性进行初始化。
加一点,构造方法也是可以设置为公有或是私有的,这样就可以完成在封装之后对成员属性的赋值,但是这不是唯一的手段,也可以使用成员方法进行联系
什么叫重载:
指不同的函数使用相同的函数名,但是函数的参数个数或者类型不同。调用的时候根据参数来区别不同函数。
构造函数的重载应该就是说根据传入的参数不同,来对构造方法的使用加以区别,但是在PHP中,是不能够使用构造方法重载的,但是可以通过PHP代码使得其他的语言中的重载发生。
eg:
<?php
class Person(){ //创建一个类,类名为Person
var $name;
var $sex;
var $age; //声明三个成员属性
function __construct($name="",$sex="男",$age=1){//创建一个构造方法为将来创建对象时为成员属性赋予初值,设定了默认参数
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}
function say(){
echo "我的名字:".$this->name."性别:".$this->sex."年龄:".$this->age."</br>";
}
}
/*如果在将来创建对象的时候没有传入参数的话,就会直接使用创建方法中的默认参数,如果传入了参数的话,就会按照传入参数的顺序将之前设置的默认参数顶掉,传多少顶多少,没顶掉的话还是照旧*/
构造方法不会回传任何数据。
2、析构方法:
PHP会在对象被销毁之前调用析构方法。
这是在PHP5中新加入的内容,在PHP4里面并没有提供这个效果。
析构方法的主要效果是允许在销毁一个对象之前做一些别的操作,例如关闭文件按,释放结果集等等。
一旦堆中的对象失去访问用的引用的时候,就无法被访问,即为垃圾对象。通常当对象不能够被访问的时候,PHP就会自动启动垃圾回收的程序,收回对象在堆中占用的内存空间。
而在这之前就会调用析构方法,以达成一些特殊的操作 。
关于析构方法的声明格式:
function__destruct(){ //析构方法名称是以两个下划线开始的__destruct()
//方法体,通常用来完成一些在对象销毁之前的清理任务
}
在PHP中,一般来说,析构方法不是很常用,这个是属于类中可选的一部分,只有需要时才在类中声明。
比如说可以用来在销毁销毁对象之前输出一条语句,这种输出的方法是在PHP自带的垃圾回收功能之前就会调用的。
注意:
很有趣的一点,虽然之前在反复强调成为垃圾内存的条件是失去引用的名称,但是我却完全没有注意到这个特点。因为引用名称是存放在栈空间中的,所以最后创建的对象反而是最先被消除的,这就符合栈的后进先出的“弹夹”式的做法,这个很有意思。
PHP的封装性:
封装性是面向对象编程的三大特性之一,就是把对象的成员属性和成员方法结合成一个独立的相同单位,并尽可能地隐蔽对象的内部细节,其包含如下两个含义:
1、把对象的全部成员属性和成员方法结合在一起,形成一个不可分割的独立单位(即对象)
2、信息隐蔽,即尽可能隐蔽对象的内部细节,对外形成一个边界(在安全里面的边界理论),只保留有限的对外接口使之与外部发生联系。
封装性主要还是体现在安全这个方面上,因为在创建对象之后如果不封装的话,就相当于是告诉别人随便来访问这个对象,里面的成员属性或是方法都可以被任意的查看更改。但是在封装之后,就是相当于在冰箱外面加上了一层壳,让别人无法去查看和修改内部结构。
真要说起来,PHP这类服务器语言的一个优势就是是在后端的服务器里面进行运行,让外界的人无法知道具体的细节,只能对这个的运行方式进行猜测,这一点在我打CTF的时候就有感觉了,虽然有很多东西是套路化的,但是确实是有很多题目是需a要代码审计的,其中很多的漏洞,如果是不知道后端代码是怎么写的,根本就相当于是无从下手,这种做法还是非常安全的(虽然不是绝对的安全吧)
封装的原则就是要求对象以外的部分不能随意存取对象的内部数据(成员属性和成员方法),从而有效地避免了外部错误对它的“交叉感染”,使得软件的错误能够局部化,大大减小差错和排错的难度
设置私有成员:
只要在声明成员属性或是成员方法的时候,使用关键字"private"(也就是私密的意思)进行修饰,就可以实现对成员的封装。
这一点在我之前写成员属性的时候就有提到过,利用不同的关键字可以起到不同的效果,var是一般的,没有任何别的意思的关键字,而private则是代表的私有,public则是代表的公有,也就是公有成员。
只要使用了这个关键字,那么在对象的外部就不能够访问,但是在对象内部的成员方法中还是可以访问到的,这就相当于是加上了一层壳
关于这个的报错格式可以查看一下书上的174页。
私有成员的访问:
这个是关于一些特殊的情况的时候应对方法。
比如说就是要求用户在对象外部去访问一个内部的成员属性,又或是需要用户对对象内部的成员属性赋值的时候,怎么样既保证安全性,又可以达成以上的操作。
做法就是在对象的内部声明一些操作私有属性的公有方法,这里需要使用到"public"关键字
这个地方相当于就是通过设计的一些公有的操作方式对其中私有的部分进行间接的访问,也就相当于是设置了一个公有的接口。
对于这种公有成员方法的设置,也就是在function这个关键字之前加上了一个"public"关键字,也就是相当于之前对于成员属性的设置,当然也可以通过if等语句来对传入的数据进行判定,就相当于是一个白名单了。