【PHP7 面向对象 笔记整理一】构造方法和析构方法
本节主要讲解 PHP面向对象中的 魔术方法 中的其中两种,构造方法 和 析构方法,主要从以下三部分总结:
一、__construct 构造方法
二、__destruct 析构方法
三、构造方法 和 析构方法 【综合练习】
一、 __construct 构造方法
1) 格式:
修饰符 function __construct()
{
方法体
}
实例1:构造方法执行
class cc
{
public $name = '串串';
public $age = 18;
public function __construct()
{
echo "预备工作,准备干掉串串<br>";
}
}
// 实例化
$a = new cc;
运行结果:
2) 特性
① 在实例化的瞬间,自动触发;
② 无返回值;
③ public 可以省略;
④ 构造方式 不允许重复。
3) 调用构造方法的两种方式:
① 自动调用
// 实例化
$a = new cc;
② 手动调用
$a->__construct();
4) 作用:
常用于做 赋初始值 (完成一些对象的初始化工作)
实例2:构造方法 赋初始值
class cc
{
public $name;
public $age;
// 此处的 $name是形参,与属性$name无关
public function __construct($name, $age)
{
echo "预备工作,准备干掉串串";
// 将 形参 给了 属性
$this->name = $name;
var_dump($this->name);
echo "<hr>";
}
public function test1()
{
echo '<br>测试1<br>';
echo $this->name;
echo "<hr>";
return $this;
}
}
// 实例化
$a = new cc('串串', 18);
echo $a->name;
echo "<hr>";
$a->test1();
运行结果:
如何 将两个变量中的方法通用?
答案: 将 变量 变成 属性
class cc
{
public $name;
public $age;
// 此处的 $name 与属性 $name无关
public function __construct($name, $age)
{
// 将 形参 给了 属性
$this->name = $name;
$this->age = $age;
}
public function test1()
{
// 如何 将两个方法中的 变量通用?
// 答案:将 变量 变成 属性
echo $this->name;
echo $this->age;
}
}
实例3:如何 将两个方法中的 变量通用?
class cc
{
public $name;
public $age;
// 此处的 $name是形参,与属性 $name 无关
public function __construct($name, $age)
{
echo "预备工作,准备干掉串串<br>";
// 将 形参 给了 属性
$this->name = $name;
var_dump($this->name); echo "<br>";
$this->age = $age;
var_dump($age);
echo "<hr>";
}
public function test1()
{
echo "<br>测试1<br>";
echo $this->name;
echo "<br>";
// 如何 将两个方法中的变量 通用?
// 答案:将变量 变成 属性
echo $this->age;
echo "<br>";
return $this;
}
}
// 实例化
$a = new cc('串串', 18);
echo $a->name;
echo "<hr>";
$a->test1();
运行结果:
注意事项:
没有构造方式时,如果方法名 和 类名相同,自动调用方法;有构造方法时,方法名不会自动调用。【建议:尽量方法名和类名不要相同】
实例4: 无构造方法时
class cc
{
public $name;
public $age;
public function cc()
{
echo "测试1<br>";
}
}
$a = new cc();
echo 222;
运行结果:可以看到,都没有构造方法的时候,类名 和 方法名 相同会报错。
实例5:有构造方法时
class cc
{
public $name;
public $age;
public function __construct()
{
echo "预备工作,准备干掉串串<br>";
}
public function cc()
{
echo "测试1<br>";
}
}
$a = new cc();
echo 222;
运行结果:有构造方法时,方法名不会被调用,但是尽量不要让方法名和类名同名,否则没有构造方法的时候,是会报错的。
不成文规定:
构造方法写在属性的最后面,方法的最前面。
class cc
{
// 属性
public $name;
public $age;
// 构造方法
public function __construct()
{
echo "构造方法";
}
// 方法
public function test()
{
echo "测试1<br>";
}
}
二、__destruct 析构方法
相当于 遗嘱 die()
在整个程序结束之前执行
1) 格式:
public function __destruct()
{
方法体
}
2) 特性:
① 当对象被销毁时,自动调用;
- 程序自然结束时,对象会自动消失;
- 对象变量 被 unset;
- 对象变量 被覆盖
② 不能有参数;
实例1:程序自然结束时
class cc
{
public $name;
public $age;
public function test1()
{
echo "测试1<br>";
}
public function __destruct()
{
echo "老母鸡";
}
}
$a = new cc;
$a->test1();
echo "小母鸡<br>";
运行结果:
实例2:对象变量 被 unset时
class cc
{
public $name;
public $age;
public function test1()
{
echo "测试1<br>";
}
public function __destruct()
{
echo "老母鸡<br>";
}
}
$a = new cc;
$a->test1();
unset($a);
echo "小母鸡<br>";
运行结果:
实例3:对象变量 被覆盖
class cc
{
public $name;
public $age;
public function test1()
{
echo "测试1<br>";
}
public function __destruct()
{
echo "老母鸡<br>";
}
}
$a = new cc;
$a->name;
$a = 10;
echo "小母鸡<br>";
运行结果:
3) 作用:
给对象写临时前的遗嘱。(即对象在销毁之前的清理工作)
不成文规定:
析构方法 一般写在方法的最后面
class cc
{
public $name;
public $age;
// 方法
public function test1()
{
echo "测试1<br>";
}
// 析构方法
public function __destruct()
{
echo "老母鸡<br>";
}
}
三、构造方法 和 析构方法 【综合练习】
实例1: 构造方法 和 析构方法 (综合练习1)
class test
{
// 构造方法
public function __construct()
{
static $x = 0;
$x++;
echo $x."<br>";
}
// 方法
public function hello()
{
echo "母鸡<br>";
}
// 析构方法
public function __destruct()
{
static $y = 10;
$y++;
echo $y."<br>";
}
}
$a = new test; // 1
$a->hello(); // 母鸡
$a = new test; // 2 11 12
运行结果:
分析结果:
① $a = new test; 先 执行构造方法,输出 1,由于后面还有代码,因此不会 执行析构方法;
② $a->hello(); 输出 母鸡;
③ $a = new test; 等号的优先级是先做右边,因此先执行右边的 new test,new 的一瞬间,再次执行 构造方法,由于构造方法中,已经输出1,再次执行 输出 2;
④ 再执行左边,此时第一个 $a 被覆盖,因此触发 第一次 new test 的析构方法,输出 11;(执行 第一个 new test的析构)
⑤ 由于下面再没有代码执行,因此触发第二次析构方法(此时已经输出为11),再次执行 输出 12 (执行第二个 new test的析构)
执行步骤归纳:
为了方便归纳总结:现将代码继续排序:
a. $a = new test;
b. $a->hello();
c. $a = new test;
1) a 中的 构造方法,输出 1;
2) b 中的 方法,输出 母鸡;
3) c 中的 构造方法, 输出 2;(执行的是 c中的右边)
4) a 中的 析构方法, 输出 11;(由于c 中的 $a覆盖了 a中的$a,因此触发a 中的析构方法)
5) c 中的 析构方法,输出12.
实例2:构造方法 和 析构方法 (综合练习2)
class test
{
// 构造方法
public function __construct()
{
static $x = 0;
$x++;
echo $x."<br>";
}
// 方法
public function hello()
{
echo "母鸡<br>";
}
// 析构方法
public function __destruct()
{
static $y = 10;
$y++;
echo $y."<br>";
}
}
$a = new test;
$a->hello();
$b = new test;
运行结果:
执行步骤归纳:
为了方便归纳总结:现将代码继续排序:
a. $a = new test;
b. $a->hello();
c. $b = new test;
1)a 中的 构造方法,输出1;
2) b 中的 方法, 输出母鸡;
3) c 中的 构造方法,输出2;(等号的优先级,先执行右边,实例化的同时,触发 构造方法)
4) c 中的 析构方法,输出11;(等号的左边,由于以下没有代码,触发 c中的 析构方法,根据 数据结构中的 “后进先出” 执行 c中的析构方法)
5)a 中的 析构方法, 输出12.
注:虽然和先前的执行结果是一样的,但是 原理不同,遵循的是 数据结构中的堆栈思想(先进后出).
实例3:构造方法 和 析构方法 (综合练习3)
class test
{
// 构造方法
public function __construct()
{
static $x = 0;
$x++;
echo $x."<br>";
}
// 方法
public function hello()
{
echo "母鸡<br>";
}
// 析构方法
public function __destruct()
{
static $y = 10;
$y++;
echo $y."<br>";
}
}
$a = new test;
$a->hello();
$b = new test;
$a->hello();
$b = new test;
运行结果:
分析结果:
① $a = new test; (实例化的时候,执行 构造方法,输出 1),由于下面还有代码,则继续执行;
② $a->hello(); 输出 母鸡;
③ $b = new test; (根据等号的优先级,先执行左边,实例化的时候,再次执行 构造方法 ,先前输出1,此时输出 2),由于下面还有代码,则继续执行;
④ $a->hello(); 输出 母鸡;
⑤ $b = new test; (根据等号的优先级,先执行左边,实例化的时候,再次执行 构造方法,先前输出3, 此时输出 3);
⑥ 再执行右边,由于 此处的 $b,覆盖了 ③ 中的 $b,因此触发了 ③中的析构方法,输出 ③中的 $b, 输出为 11;
⑦ 由于⑤ 以下已经没有代码,因此触发了⑤中的 析构方法,由于先前输出 11,此处输出 12;
⑧ 最后触发① 中$a的 析构方法,先前输出12, 此处 输出 13.
执行步骤归纳:
为了方便归纳总结:现将代码继续排序:
a. $a = new test;
b. $a->hello();
c. $b = new test;
d. $a->hello();
e. $b = new test;
1) a 中的 构造方法,输出1;
2) b 中的 方法,输出 母鸡;
3) c 中的 构造方法,输出 2;
4) d 中的 方法, 输出 母鸡;
5) e 中的 构造方法,输出 3;
6) c 中的 析构方法, 输出 11;(由于e中的 $b覆盖了 c中的$b,触发了 c中的析构方法)
7) e 中的 析构方法, 输出 12;(由于e下面没有代码执行了)
8) a 中的 析构方法,输出 13;
实例4:构造方法 和 析构方法(综合练习4)
class test
{
// 构造方法
public function __construct()
{
static $x = 0;
$x++;
echo $x."<br>";
}
// 方法
public function hello()
{
echo "母鸡<br>";
}
// 析构方法
public function __destruct()
{
static $y = 10;
$y++;
echo $y."<br>";
}
}
$a = new test;
$a->hello();
$a = new test;
$a->hello();
$a = new test;
运行结果:
分析结果:
① $a = new test;(实例化的时候,触发 构造方法,输出1)
② $a->hello(); (输出母鸡)
③ $a = new test;(第二个 new test),根据等号的优先级,先执行右边;当实例化的时候,会触发 构造方法,先前输出1,此处输出2;
④ 执行 第二个 new test的左边(即第二个$a),由于会把 第一个\$a覆盖,会触发 第一个\$a的 析构方法,输出11;
⑤ $a = hello(); (输出母鸡)
⑥ $a = new test(第三个 new test),根据等号的优先级,先执行右边;当实例化的时候,会触发 构造方法,先前输出2,此处输出3;
⑦ 执行 第三个new test的左边(即第三个$a),由于会把 第二个\$a覆盖,会触发 第二个\$a的 析构方法,先前输出11,此处输出12;
⑧ 由于 第三个 new test下面再无代码,因此会触发 第三个$a的 析构方法,先前输出12,此处输出13.
执行步骤归纳:
为了方便归纳总结:现将代码继续排序:
a. $a = new test;
b. $a->hello();
c. $a = new test;
d. $a->hello();
e. $a = new test;
1)a 中的 构造方法,输出1;
2) b 中的 方法,输出母鸡;
3) c 中的 构造方法, 输出2;(等号的优先级,先执行右边,会触发 构造方法)
4) a 中的 析构方法,输出11;(执行c中的左边,左边的$a 会覆盖 a中的 $a,因此触发 a中的析构方法)
5) d 中的 方法, 输出母鸡;
6) e 中的 构造方法,输出3;(等号的优先级,先执行右边,会触发 构造方法)
7) c 中的 析构方法, 输出12;(执行e中的左边,左边的$a 会覆盖 c中的 $a,因此触发 c中的析构方法)
8) e 中的 析构方法,输出13;(e下面再无代码,会触发 e中的析构方法)
以上只是我学习过程中涉及到的,有缺漏处,之后补充。