PHP Notes 05

■ PHP 的面向对象
 
关键词:类、对象、属性、方法、继承、访问修饰符、构造方法
 
面向对象的目的:分工明确、高复用性。
 
面向对象的思路:尽量将每个“功能”独立包装(分工越细越好),然后“功能”间相互调用。
 
☆面向对象的三大特性:封装、继承、多态
 
 
● 类 与 对象:
 
Class/Object : 类(Class)和 对象(Object)是面向对象方法的核心概念。
 
类 是对一类事物共性的总结、描述,是抽象的、概念上的定义;
 
对象 是实际存在的,是该类事物的具体个体。因此也称为实例(instance)。
 
(比喻:类 是一个“模子”;对象 是根据这个“模子”造出来的具体的东西。)
 
 
●创建类:
 
class 类名 {
 
访问修饰符 $成员属性名 = 属性值;
 
访问修饰符 function 方法名(){
 
}
}
 
 
注意:
① 类名首字母要大写;
② PHP中创建类时,属性不能是表达式或函数的返回值!
③ 在类中只能定义属性和方法 ,不能直接调用方法。
(★即 在类中除了定义属性和方法,不能够有任何其他的操作语句!)
 
 
★ $this 关键字:
$this 关键字 是用来访问当前类中(对象中)的属性或方法。
(即 用来访问自身的属性或方法。)
 
 
●创建对象(实例):
 
$对象名 = new 类名();
 
 
●访问对象属性:
 
$对象名->属性名;
 
注意:访问对象的属性时,属性名前面不需要加 $ 。
 
 
●如果一个文件,专门用于定义类,则命名规范应为:
 
类名.class.php
 
 
●类的静态属性:
 
例如:
class Human {
public static $leg = 2; //在属性名前面加 static 关键字,定义类的静态属性。
}
 
注意:类的静态属性 属于全类,不属于某一个对象,在内存中仅有一份。
 
 
★构造方法(构造函数):
 
构造方法 __construct 是【PHP魔术方法】之一,它是类的一种特殊的方法,它的主要作用是完成对新对象实例的初始化。
 
构造方法(构造函数)有两个特点:
① 构造方法没有返回值;
② 创建新对象(实例)时,系统会自动调用该构造函数来完成对象实例的初始化。
 
一般使用形式为:
 
class 类名 {
访问修饰符 function __construct(形参列表){
……初始化操作……
}
}
 
实例化对象时:
 
$对象名 = new 类名(实参列表);
 
常用处:
①通过 构造方法(构造函数)可以实现在实例化对象时给类(其实也就是构造方法在起作用)传入参数;
②创建类时 属性不能是表达式或函数的返回值,但通过构造方法就可以实现。
例如:
 
class Human {
public $name;
public function __construct($a){
$this->name = $a; //根据“类的参数”改变 Human类的成员属性 name 的值。
}
}
 
$zhangSan = new Human('张三');
 
echo $zhangSan->name;// 输出:张三
 
 
★析构方法(析构函数):
 
构造方法 __destruct 也是【PHP魔术方法】之一,它也是类的一种特殊的方法,它会在对象(实例)的所有引用都被删除或当对象(实例)被显式销毁时执行。
(即 当对象内部的操作执行完毕时 __destruct() 会被自动调用。“析构≈善后”)
 
一般使用形式为:
 
class 类名 {
function __destruct(){
 
}
}
 
析构方法(析构函数)有5个特点:
① 析构方法没有返回值,也没有参数;
② 析构方法会自动调用;
③ 析构方法主要用于销毁资源;
④ 析构方法调用顺序是:后创建的对象(实例),先被销毁;(栈 —— 后进先出)???
⑤ 析构方法在程序结束或对象成为垃圾对象时被调用。
 
 
■ 面向对象的三大特性:封装、继承、多态
 
● 一、封装特性:
 
q:什么是封装?
a:封装是把一些相关的属性和行为隐藏起来,从而得到保护和安全。
 
 
q:什么是 外部访问 和 内部访问?
a:
“外部访问”的意思是 对 属性或方法 的访问 发生在 类的外部。
(即 访问属性或方法的代码 不是在 创建类 的代码之内,而是在其它地方。)
例如,在创建类的代码之外 通过 -> 访问对象的属性或方法。代码如:$对象名->属性名
 
“内部访问”的意思是 对 属性或方法 的访问 发生在 类的内部。
(即 访问属性或方法的代码 位于 创建类 的代码之内。)
例如,在类的内部通过 $this-> 访问自身的属性或方法。代码如:$this->属性名
 
 
☆我们是通过 访问修饰符 体现 封装特性 的。
 
★访问修饰符(封装关键字):
 
public (无封装性)表示公开的,本类的对象 内部、外部 和 子类的对象 都可以访问。
protected (有封装性)表示受保护的,仅有 本类的对象 或 子类的对象 内部可以访问。
private (有封装性)表示私有的,只有 本类的对象 内部可以使用。
 
注意:默认是 public
 
 
★如果我们想从外部访问 对象中的 protected 或 private 属性/方法,我们通常的做法是 在对象内部创建一个 public 方法,然后通过该方法去访问这些“被保护的”或“私有的”属性/方法。(一般我们都在这个public方法中添加一些检测权限的设定)
例如:
 
class A{
private $money = 10000; // 私有的属性
 
// 创建一个公共方法,用于重置私有属性的值:
public function resetMoney($rmb){
$this->money = $rmb;
}
 
// 创建一个公共方法,用于获得私有属性的值:
public function showMoney(){
return $this->money;
}
}
 
$a = new A();
$a->resetMoney(10);
echo '我有 ¥',$a->showMoney(),'.00'; // 输出:我有 ¥10.00
 
 
 
● 二、继承特性:
 
被继承的类称之为:基类 或 父类 。
继承的类称之为:派生类 或 子类 。
 
类的继承通过 extends 关键字 来实现。一般形式如下:
 
//父类
class Parents {
 
}
 
//子类 Children 继承 父类 Parents :
class Children extends Parents {
 
}
 
继承的细节:
① 父类中 public 与 protected 的属性和方法 可以在子类中使用,而 private 的属性和方法 不能在子类中使用(无法访问)。
② 一个子类只能继承一个父类。
③ 子类的对象实例也会继承父类的构造方法。
④ 如果我们想去调用父类中原有的方法(如:父类的构造方法或已被覆盖的方法),可以使用以下方式实现:
父类名::方法名();
或:
parent::方法名();
⑤如果子类中的方法和父类中的方法重名,且参数个数相同,则父类中的方法将被重写(或称 方法覆盖)。
注意:在实现方法覆盖时,新方法的“访问范围”要大于或等于被覆盖的方法。(public 的“访问范围”比 protected 的大。)
 
 
 
 
/************************************
??????谜 ??????
************************************/
 
class Parents{
public $a = 'P_a';
protected $b = 'P_b';
private $c = 'P_c';
 
public function f(){
echo $this->a,'<br />';
echo $this->b,'<br />';
echo $this->c,'<br />';
}
}
 
class Children extends Parents{
public $a = 'C_a';
protected $b = 'C_b';
private $c = 'C_c';
 
public function f_c(){
echo 'C~',$this->a,'<br />';
echo 'C~',$this->b,'<br />';
echo 'C~',$this->c,'<br />';
}
}
 
$xiaoMing = new Children();
 
var_dump($xiaoMing);
echo '<hr />';
$xiaoMing->f(); // 调用 从父类 继承来的 f() 方法。
/*
输出:
C_a
C_b
P_c   <——居然输出了父类的 private 的属性。
*/
echo '<hr />';
$xiaoMing->f_c();
/*
输出:
C~C_a
C~C_b
C~C_c
*/
 
 
 
● 三、多态特性:
 
在 Java 中 多态是指:一个接口,多个形态。
 
Java 中的多态 是由 一个父类 和 继承它的多个子类 通过 重写方法 实现的。(即 继承 + 重写)
 
在 PHP 中 无法实现 Java 中的这种多态特性,但是可以通过 动态类名 模拟类似的效果,即“伪多态”。
(详细,请看057讲)
 
 
 
★方法重载:
 
q:什么是 方法重载 ?
a:方法重载 就是:函数名一样,通过函数的参数个数或者是参数类型不同,达到调用同一个函数名,但执行不同函数的效果。
 
(PHP5中 默认不支持像 JAVA 那样的方法重载。)
 
PHP中通过 魔术方法 __call() 模拟Java中的方法重载:
 
魔术方法 __call() 的作用是:当调用一个对象的某个方法,但该方法并不存在时,系统就会自动调用 __call() 。
 
注意:系统还会自动传给 __call() 两个参数:
第一个参数是 被调用但不存在的方法名;
第二个参数是 调用这个不存在的方法时 使用的参数 所组成的数组。
 
例如:
 
function __call($存放方法名的变量,$参数数组){
if($存放方法名的变量 == "方法名"){
$cnt = count($参数数组);
if($cnt == 1){
……如果只有一个参数时,执行……
}else if($cnt == 2){
……如果有两个参数时,执行……
}
}
}
 
 
 
■ PHP 的 静态变量:
 
★ 无论什么语言,静态变量 的特性都是:常驻内存!!
 
<?php
 
function f(){
static $a = 0; //静态变量
return $a++;
}
 
echo f(),'<br />';
echo f(),'<br />';
echo f(),'<br />';
echo f(),'<br />';
echo f(),'<br />';
echo f(),'<br />';
echo f(),'<br />';
echo f(),'<br />';
echo f(),'<br />';
echo f(),'<br />';
 
/* 输出:
0
1
2
3
4
5
6
7
8
9
*/
?>
 
 
■ 静态属性 与 静态方法:
 
★ 静态属性 和 静态方法 位于内存的 静态数据区 不随对象实例放在堆区。
 
个人理解 {
静态属性 和 静态方法 被定义在创建类的代码中。
静态属性 和 静态方法 从定义之时起 就位于 内存的 静态数据区 且 常驻内存。
所有我们不需要对该类进行实例化,也可以通过类名直接对 静态属性 和 静态方法 进行访问。
 
静态属性 是不是主要用于 被多个实例 公用 ??
}
 
● 一、静态属性:
 
静态属性的一般定义形式为:static 访问修饰符 $静态属性名;
 
静态属性一般有两种访问形式:
① 在类外部:类名::$静态属性名
② 在类内部:self::$静态属性名 或 类名::$静态属性名
 
★注意:就算没有给某个类创建对象(实例),也可以通过类名直接调用它的静态属性。因为静态属性位于内存的 静态数据区 不随对象实例放在堆区。
 
 
● 二、静态方法:
 
静态方法的一般定义形式为:static 访问修饰符 function 静态方法名(){……}
 
★注意:静态方法中不能访问非静态属性。
 
静态方法一般也有两种访问形式:
① 在类外部:类名::静态方法名() 或 对象名->静态方法名()
② 在类内部:self::静态方法名() 或 类名::静态方法名()
 
 
★静态属性 和 静态方法 只属于类所有。同一个类在一个页面中,只能声明一个次。因此,一个类的静态属性 在内存中只有一份。我们可以利用这个特性,实现PHP的 单例模式 。
 
 
★ self 关键字:
 
用于访问当前类中的内容,类似 $this 关键字,但 $this 需要类被实例化以后才可以使用,self 可以直接访问当前类中的内部成员。
 
self关键字的一般使用形式如下:
 
self::类内的成员属性或方法
 
等价于:
 
类名::类内的成员属性或方法
 
提示:self 关键字一般用来访问类中的静态属性、静态方法 或 常量。
 
 
★ final 关键字:
 
当我们希望 某个类不能被继承 或 某个方法不能被重写(覆盖)时,我们可以使用 final 关键字。
 
final关键字的一般使用形式如下:
 
final class 类名 {
final 访问修饰符 function 方法名(){
 
}
}
 
/*
上例只用作演示语法,实际上 类 与 该类中的方法 同时用 final 修饰是毫无意义的。
既然不能被继承,又何来重写呢。
*/
 
 
★ const 关键字:
 
当我们不希望某个成员属性被修改时,可以使用 const 关键字 将其定义为常量。
 
一般定义形式:const 常量名 = 值;
 
注意:
① 常量名 前面没有 $ ;
② 常量名 用大写字母。
 
一般访问形式为:
在类外部访问:类名::常量名  或  接口名::常量名
在类内部访问:self::常量名
 
 
★ instanceof 运算符:
 
instanceof ——作用:判断一个变量是不是某一个类的对象(实例)。
 
例如:var_dump(变量名 instanceof 类名);
 
 
 
☆ PHP的【魔术方法】都是 当某种情况发生时会 自动触发 的方法。
 
 
★魔术方法 __get() :【注意:要在类的内部声明才有效!】
当运行的语句,请求了一个当前环境不可见的属性时(比如:未定义的属性、受保护的属性、私有的属性),将会触发 __get() 魔术方法。
 
当 __get() 被触发时,系统为其传了一个参数,该参数的值为请求的属性名。
 
 
★魔术方法 __set() :【注意:要在类的内部声明才有效!】
 
当运行的语句,试图设置一个当前环境不可见的属性时(比如:未定义的属性、受保护的属性、私有的属性),将会触发 __set() 魔术方法。
 
当 __set() 被触发时,系统为其传了两个参数,分别是:要求设置的属性名 和 属性值。
 
 
★魔术方法 __autoload() :【注意:在类的外部声明。】
 
当试图实例化一个未声明的类时,自动触发 __autoload() 魔术方法。
 
当 __autoload() 被触发时,系统为其传入了一个参数,该参数的值是被实例化的类的类名。
 
 
 
■ 对象克隆:
 
什么是对象克隆?先看以下代码:
 
$obj = new A();
 
$obj_02 = $obj;
 
上述 语句中 $obj_02 = $obj 是 “引用计数赋值” 。它们传递的是某个数据的地址(指针)。即 它们都是指向同一块内存空间。
 
而对象克隆就不同,对象克隆能真正地‘复制’出第二个对象。一般形式如下:
 
$obj_02 = clone $obj;
 
 
魔术方法 __clone() :【注意:要在类的内部声明才有效!】
 
当对象被克隆时,自动触发 __clone() 魔术方法。
 
 
 
■ 抽象类:(特点:不能被实例化,只能被继承的类)
 
★抽象类不能被实例化,只用作继承。
 
创建 抽象类 及 抽象方法 的一般形式如下:
 
abstract class 抽象类名 {
 
abstract function 抽象方法名(); // 没有方法体 “{}” 。
}
 
注意事项:
① 抽象类不能被实例化;
② 抽象类不一定要包含抽象方法,抽象类也可以包含一般方法;
③ 一旦类中包含了抽象方法,则该类也必须声明为抽象类;
④ 若一个类继承了某个抽象类,则它必须重写抽象类的所有抽象方法,且参数必须相同。
 
★抽象类的作用:
因为 一个类继承了某个抽象类,则它必须重写抽象类的所有抽象方法,且参数必须相同。
所有 抽象类可以作为一个模板、一种规范、一套计划方针。其严格规定其子类必须统一遵守其声明的内容结构。
 
 
■ 接口:(特点:“组合”)
 
创建 接口 的一般形式如下:
 
interface 接口名{
 
public function 方法名();
}
 
实现 接口 的一般形式如下:(“实现”??★ implements n.[机] 工具)
 
class 类名 implements 接口名1,接口名2,接口名3,……{
 
}
 
注意事项:
① 接口不能直接实例化;
② 接口中的方法都不能有主体 “{}” ;
③ 一个类可以实现多个接口;
④ 接口中可以有属性,但必须是常量,并且是 public ,访问接口中的常量用:接口名::常量名
⑤ 接口中的方法必须是 public (默认就是 public ,并且默认为 抽象方法)。
⑥ 接口可以继承接口,但接口不能继承类。
⑦ 若一个类实现了某个接口,则必须实现(重写)该接口中的所有方法。
 
接口 与 抽象类 的区别:
一个类 只能 继承一个 抽象类;
一个类 可以 实现多个 接口。
 
★接口的作用:
与 抽象类 的作用类似 一个接口 可以作为 一种规范、一个模板。
与 抽象类 不同的是,一个类 只能继承自 一个抽象类,但 一个类 可以实现 多个接口,即 一个类可以受到多个规范(规定)的约束。
 
这样通过把各种 接口 进行不同的组合 就可以创建出不同的规范(规定)。
 
接口体现了“高内聚、低耦合”的编程思想。

 

posted @ 2017-11-10 20:16  Uncle_Jay  阅读(92)  评论(0编辑  收藏  举报