开发者学堂课程【PHP 进阶教程-由浅入深掌握面向对象开发-第一阶段:类内部对象】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/711/detail/12661
类内部对象
类内部对象
1、目标
理解内部对象的概念,掌握内部对象对成员的访问
公有和私有的属性或者方法叫做访问修饰限定符限定访问的区间。
2、概念
内部对象:$this,方法内部内置的一个对象,会自动指向来调用方法的对象
(1)$this 存在于方法内部(仅限内部使用),所以相当于在类的结构内部
可以访问任意访问修饰限定符修饰的成员
私有成员都是通过公有方法来实现访问(公有方法可以在类外部访问)(因为如果方法私有化,外边还不能调用方法,方法自然就无法实现里面的代码去运行)
(2)类内部对类成员的访问也需要通过对象才能访问,所以必须通过$this 内部对象访问类成员(函数里面必须通过对象才能访问对象里面的成员)
3、步骤
(1)声明类结构
(2)明确私有成员(不限定成员的访问修饰限定符)
(3)私有成员需要在某种情况下被访问:增加方法,在方法里使用$this 访问
4、示例
(1)尝试在类内部方法中访问属性
class saler{
# 属性
public $count = 100;
protected $discount = 0.8;
private $money=100;
public function geta11(){
echo $count,$discount,$money;;
# 全部错误:提示未定义的“变量”
}
}
$s=new saler();
$s->geta11();
已经确认私有不能访问,在里面来访问就直接输出 $count、$ discount 、$ money ,实际上走到这一步可以简单的去掉。访问的就是一个函数,函数里面要访问的变量没有定义过,所以报错。
代码 04this.php:
#内部对象$this
class Saler{
#属性
public $count = 100;
protected $discount = 0.8:
private $money = 100;
public function getall(){
echo $count,$discount,$money;
#全部错误:提示未定义的“变量”
}
}
$s = new Saler();
$s->getAll();
尝试之后看效果:
Notice: Undefined variable: count in D:\server\Web\04th is.php on line 13
Notice:Undefined variable: discourt in D:\serverWeb\04th is.php on line 13
Notice: Undefined variable: money in D:\server\Web\04th is.php on line 13
没有访问到结果。
注意:方法本质是定义在类内部的函数,因此受制干作用域的问题,在方法内部访问的变量系统认定为局部变量(必须内部定义或者参数传入),否则就会提示未定义
(2)类内部访问类成员,需要通过对象来进行访问
class saler{
# 属性
public $count = 100;
protected $discount = 0.8;
private $money = 100;
public function geta11(){
# 需要获取到对象名字:因为方法本身就是函数,访问外部全局变量可以通过q1oba1引入实现
global $s;
echo $s->count,$s->discount,$s->money;
#正确输出
}
}
$s=
new saler();
$s->geta11();
global 可以引入外边变量,引入之后可以利用对象进行访问:
#内部对象$this
class Saler{
#属性
public $count = 100;
protected $discount = 0.8;
private $money = 100;
public function getall(){
# 全部错误:提示未定义的“变量” # echo $count,$discount,$money;
# 引入对象
global $s;
#正确输出 echo $s->count,$s->discount,$s->money;
}
}
$s = new Saler();
$s->getAll();
结果:1000.8100
$s 还不是内部对象,不能知道$s,不适合使用,只是一种解决方案,不是最优的解决方案
注意:上述代码规定死了以后对象只能是$s,不能有其他对象或者其他命名,所以非常不又灯
(3)使用内置对象 $this 访问
class saler{
# 属性
public $count = 100;
protected $discount = 0.8; private $money = 100;
public function geta11(){
var_dump($this);
echo $this->count.$this->discount,$this->money;
#正确输出
}
}
$s = new saler();
$s->geta11();
注意:$this 代表的是对象,而 $this 所在环境为类内部的方法内部,所以 $this 对象是在类内部访问,因此可以访问所有的属性和方法,不受访问修饰限定符限制
内部有一个变量 $this,有类之后给函数特殊赋予的内置变量。
代码:
#内部对象$this
class Saler{
#属性
public $count = 100;
protected $discount = 0.8;
private $money = 100;
public function getall(){
# 全部错误:提示未定义的“变量” # echo $count,$discount,$money;
#引入对象
# global $s;
#正确输出# echo $s->count,$s->discount,$s->money;
#
#内置变量($this)
var_dump($this);
}
}
$s = new Saler();
$s->getAll();
结果:
objectSaler)#1 (3){["count*]=> int(100)["discount":prot ected]=>float(0.8)["money”:"Saler:private]=>int(100)}
编号为1说明与外面 $s 一样,整个脚本里面运行变量代表第几个对象,唯一说明指向的是同一个对象,编号只有一个。
#内部对象$this
class Saler{
#属性
public $count = 100;
protected $discount = 0.
8;
private $money = 100;
public function getall(){
# 全部错误:提示未定义的“变量” # echo $count,$discount,$money;
#引入对象
# global $s;
# echo $s->count,$s->discount,$s->money;
#正确输出
#
#内置变量($this)
var_dump($this);
echo $this->count,this->discount,$this->money;
正确输出
}
}
$s = new Saler();
$s->getAll();
结果:1000.8100
不管外面用什么变量都可以拿到,跟变量名字没有关系。
5、$this、class 和 new 之间的关系原理
(1)class 是定义类结构,属于非执行段代码,因此会被加载到代码段(编译阶段)
(2)new 是实例化对象,先判定类在内存(代码段)是否存在
①类不存在,报错;
②类存在,将类内部的属性部分复制一份,然后在内存(堆区)开辟一块内存空间,将属性放到里面,同时内部有一个指针指向类的内存空间(代码段)
③对象访问属性即访问的是对象空间里存储的部分
④对象访问方法是对象通过内部指针找到类空间中的方法,然后在内存(栈区)开辟运行(方法是函数也是结构在代码段里面,堆是放数据的,函数不是数据)
(3)$this 是系统在方法内詈的对象通用名字
①对象在调用方法的时候,系统会自动找到对象所保存的内存地址(堆区),然后把地址赋值给 $this
(4)方法内部的 $this就代表调用当前 $this 所在方法的外部对象
$this的本质是函数内部的一个局部变量,只是系统自动对其进行赋值,而且一定是调用方法的对象本身(用完就没)
(5)面向对象编程代码运行内存关系
内存发生的过程:
class Saler{
//属性
public $count=100;
private $money=100;
public function getAll(){
var_dump($this);
}
}
$s =new Saler();
cho $s->count;
$s->getAll();
代码不会自动执行脚本不会执行,把东西放到内存才能运行,内存里面分为四段,栈区小效率高,数据段专门放数据效率高,堆区放大数据效率低,代码段代码运行必须先加载到代码段。请求编译会将代码全部装载到代码段里面,会有优化和处理,最终编译的结果是二进制。运行发现是一个类结构,不会执行系统会跳过,跳过会访问 $s=new Saler() 发现要执行,第一件事情去代码段寻找类 Saler 是否存在,存在可以实例化。实例化的过程会在堆里面产生一个内存区域,然后会指向类把里面所有的属性全部复制,count 直接 $count=100 ,money 会带一个 private ,再有一个 $s 指向对象,此时实例化产生,代码运行结束。
对象的内存空间与类的空间不在一起,函数在类的空间代码段里面,对象的空间里面只有属性,指针指向代码段所以能访问,$s->count 去访问属性,会找到 $s 接下来会找到所在代表的对象访问里面的 $count ,发现能访问输出结束,如果访问私有就不允许访问,因为所在的位置不是类,类里面在代码段。
访问方法,找到对象找到内存发现访问的是方法,方法在对象里面不存在顺着指针找到对应的类,然后类里面有发现有执行,把方法取出来放到内存的栈区,因为要在里面开辟栈区来运行所有的代码,运行之后系统会自动做一件事情,当方法开始执行的时候,会在里面产生一个局部变量,指向顺着代码去找,通过对象来所以指向对象。此时会发现与 $s 一样都指向对象,它们代表的是同一个对象。接下来如果要访问,因为此时方法虽然在栈里面运行,但是它有一个环境属于类内部,本质属于类内部,意味着私有的东西可以拿,因为在内部换环境、换个位置来进行运行,内存的规定必须要在这。是局部变量因为它属于函数运行的过程中,两个肯定是相等的一旦运行结束之后,函数就没了意味着从内存里面要消失,$ 自然跟着函数没了之后,指针没用所以它会断掉,此时就 $s 指向对应的,这就是结果。最后脚本要执行结束,首先清空栈里面的内存,用完了之后指针就不存在,会发现对象变成了游离内存,没有用只是它指向别人,所以系统会回收,脚本执行结束也要回收,内存又清空,这就是内存的关系。
能体现出 $this 是怎么来的, $this 代表谁,为什么能够代表谁, 类与对象的关系。
6、小结
(1)类内部方法内有一个内置对象 $this,代表访问该方法的外部对象
(2)类在实例化对象的时候要保证内存中有该类
(3)一个类可以实例化多个对象,每个对象访问成员方法时,$this 就代表对应对象
内部对象可以解决私有、隐秘的类成员的访问,私有的属性、私有的方法、受保护的都可以访问。