面向对象
1、魔术方法
| 方法名 | 描述 |
| __construct() | 类的构造函数 |
| __destruct() | 类的析构函数 |
| __call($funName, $arguments) | 当调用一个未定义或不可达方法时, __call() 方法将被调用。 |
| __callStatic($funName, $arguments) | 当调用一个未定义或不可达的静态方法时, __callStatic() 方法将被调用。 |
| __get($propertyName) | 当获取一个类的成员变量时, __get() 方法将被调用。 |
| __set($property, $value) | 当赋值一个类的成员变量时, __set() 方法将被调用。 |
| __isset($content) | 当调用 isset() 或 empty() 对一个未定义或不可达的成员赋值时, __isset() 方法将被调用。 |
| __unset($content) | 当调用 reset() 对一个未定义或不可达的成员更新时, __unset() 方法将被调用。 |
| __sleep() | 当执行序列化 serialize() 时,__sleep() 方法将首先被调用。 |
| __wakeup() | 当执行反序列化 deserialization() 时, __wakeup() 方法将首先被调用。 |
| __toString() | 当使用 echo 方法直接输出显示对象时,__toString() 方法首先被调用。 |
| __invoke() | 使用调用函数(function)访问一个对象时, __invoke() 方法将首先被调用。 |
| __set_state($an_array) | 当调用 var_export() 方法时,__set_state() 方法将被调用。 |
| __clone() | 当对象被复制赋值时,__clone() 方法将被调用。 |
| __autoload($className) | 试图载入一个未定义的类时调用。 |
| __debugInfo() | 输出 debug 信息。 |
__call(funame,funvalue)
执行条件 : 为了避免当调用的方法不存在时产生错误,可以使用 call() 方法来避免。该方法在调用的 方法不存在时会自动调用,程序仍会继续执行下去。 格式:call(参数1,参数2) 注意:__call()函数里面必须写两个参数
class Animal
{
// 创建一个类外不可访问的方法
protected function eat($name)
{
echo "偷吃食物".'<br>';
}
// 在对象中调用一个不可访问方法时,__call() 会被调用
public function __call($name, $value)
{
var_dump($name);// 结果是被调用的方法的名称
var_dump($value);// 结果是传递过来参数的个数 , 其类型是数组
}
}
$xb = new Animal();
$xb->eat();// 调用类外不可直接访问的方法
get(funame) set(funame,funvalue)
执行条件 : 读取不可访问属性的值时,get() 与set会被调用。 格式:set(参数1,参数2), get(参数) 注意:set()函数里面必须写两个参数 ,get函数里面必须写一个参数
class Animal
{
// 设置两个类外面不能访问的属性
protected $age;
private $money;
// 1.读取不可访问属性的值时,__get() 会被调用
public function __get($name)
{
var_dump($name);// 结果得到的是对应的属性的名称
}
// 2.在给不可访问属性赋值时,__set() 会被调用。
public function __set($name, $value)
{
var_dump($name);// 结果得到的是对应的属性名称
var_dump($value);// 结果得到的是对应的修改的值
}
}
$xb = new Animal(0, 0);
// 1.调用类中不可访问的属性
$xb->age;
$xb->money;
// 2.给类中的属性赋值
$xb->age = 1;
$xb->money = 10000;
__isset(funame)
执行条件 : 当对不可访问属性调用 isset() 或 empty() 时, isset() 会被调用。 格式:isset([参数]) 注意:__isset()函数里面必须写一个参数
class Animal
{
// 创建一个类外不可访问的方法
protected function eat()
{
echo "偷吃食物".'<br>';
}
// 当对不可访问属性调用 isset() 或 empty() 时, __isset() 会被调用。
public function __isset($name)
{
var_dump($name);// 结果是被调用的方法的名称
}
}
$xb = new Animal();
isset($xb->age);
__unset(funame)
执行条件 : 当对不可访问属性调用 unset() 时,unset() 会被调用。 格式:unset([参数]) 注意:__unset()函数里面必须写一个参数
class Animal
{
// 创建不可访问的属性
protected $age = 1;
// 当对不可访问属性调用 unset() 时,__unset() 会被调用。
public function __unset($name)
{
// var_dump($name);// 结果是被调用的方法的名称
unset($this->$name);
echo $this->age;// 无法再访问到 age 属性
}
}
$xb = new Animal();
unset($xb->age);// 销毁属性 age
__toString
执行条件 : 该方法用于一个类被当成字符串时应怎样回应。
class Animal
{
// 方法用于一个类被当成字符串时应怎样回应。
public function __toString()
{
return "对象又被当成字符串了".'<br>';// 注意这里一定要返回一个字符串值
}
}
$xb = new Animal();
echo $xb;// echo输出的是字符串
2、静态,关键字是static
静态成员 : 指的是在类中声明成员时可以加上static关键字 , 这样声明的成员就叫做静态成员。即声明为 static的类成员便能在类的范围内同享。 也就是这个静态成员对于这个类来说是唯一的,也就是不管有多少个对象,只要它引用了一个静态成 员,那么这些对象引用出来的肯定是同一个。
定义一个名为 'Animal' 的类 , 在其中存在一个静态属性 '$age' , 静态方法 'test':
class Animal
{
// 1.定义静态属性
public static $age = 16;// 静态属性
// 2.定义静态方法
public static function test()
{
echo "我是静态的方法 test".'<br>';
}
}
在类外面静态成员的访问
Animal::$age;// 调用静态属性
Animal::test();// 调用静态方法
在类里面静态成员的访问
self::$age;// 调用静态属性
self::test();// 调用静态方法
3.静态与非静态的调用
在普通方法中调用静态成员:
class Animal
{
public $color = '普通属性 color'.'<br>';// 普通属性
public static $age = '静态属性 age'.'<br>';// 静态属性
// 定义普通方法
public function common()
{
echo "我是普通方法 common".'<br>';
echo self::$age;
self::test();
}
// 定义静态方法
public static function test()
{
echo "我是静态的方法 test".'<br>';
}
}
$xb = new Animal();
$xb->common();
/*
结果:
我是普通方法 common
静态属性 age
我是静态的方法 test
*/
在静态方法中调用普通成员:
class Animal
{
public $color = '普通属性 color'.'<br>';// 普通属性
public static $age = '静态属性 age'.'<br>';// 静态属性
// 定义普通方法
public function common()
{
echo "我是普通方法 common".'<br>';
}
// 定义静态方法
public static function test()
{
echo "我是静态的方法 test".'<br>';
echo $this->color;
$this->common;
}
}
Animal::test();
// 结果报错
静态成员的特点总结:
静态数据成员和普通数据成员区别较大,体现在下面几点:
1. 普通数据成员属于类的一个具体的对象,只有对象被创建了,普通数据成员才会被分配内存。而静
态数据成员属于整个类,即使没有任何对象创建,类的静态数据成员变量也存在。
2. 因为类的静态数据成员的存在不依赖与于任何类对象的存在,类的静态数据成员应该在代码中被显
式地初始化,一般要在类内进行。
3. 外部访问类的静态成员能直接通过类名来访问。
4. 类实例化对象可以访问静态方法,但不能访问静态属性
5. 类的静态成员函数无法直接访问普通数据成员(可以通过对象名间接的访问),而类的任何成员函
数都可以访问类的静态数据成员。
6. 静态成员和类的普通成员一样,也具有public、protected、private3种访问级别。
类常量
以前定义常量使用 'define' 函数定义 , 这种常量是全局常量 , 在任何地方都可以被访问到 , 而类常量则是
专属于该类的常量。
例:
定义一个类常量 'NAME' 并且分别在类外和类中调用:
class Person
{
const NAME = 'jack';// 定义一个类常量
public static function get()
{
// 静态方法调用类常量
echo self::NAME;
}
}
// 类外调用类常量
echo Person::NAME;
Person::get();
// 结果 jackjack
2.类常量与全局常量的区别
define('NAME', 'alice');// 定义一个全局常量 NAME
class Person
{
// 定义一个类常量 NAME
const NAME = 'jack';
}
class Animal
{
// 定义一个类常量 NAME
const NAME = '小白';
public static function get()
{
echo 'Animal 类的 NAME '.self::NAME.'<br>';// 调用类常量 NAME
echo 'Person 类的 NAME '.Person::NAME.'<br>';// 调用类常量 NAME
echo '全局的 NAME '.NAME.'<br>';// 调用全局常量 NAME
echo "<br>";
}
}
echo Animal::get();
echo NAME.'<br>';// 调用全局的常量 NAME
echo Person::NAME.'<br>';// 调用 Person 类的 NAME
echo Animal::NAME.'<br>';// 调用 Animal 类的 NAME
总结 : 全局常量可以不用使用任何标识符直接在任何地方调用到 , 而类常量必须使用对应的类名称或代
表类名称的关键字来使用。
继承
例:
创建两个类 , 让其中的 Xh 继承 Ha 类 , 继承之后子类 Xh 中自动可以拥有父类 Ha 中的成员:
// 父类 Ha
class Ha
{
// 父类的两个 公有 属性
public $color = '黑白'.'<br>';
public $ear = '竖着'.'<br>';
// 父类的一个 公有 方法
public function skill()
{
echo "拆家".'<br>';
}
}
// 创建父类对象
$dh = new Ha();
// 父类调用父类成员
echo $dh->color;
echo $dh->skill();
// 子类 Xh
class Xh extends Ha
{
}
// 创建子类对象 子类中没有创建任何成员
$xh = new Xh();
// 子类调用成员
echo $xh->ear;
echo $xh->skill();
/*
结果:
黑白
拆家
竖着
拆家
*/
继承的优点 : 在软件开发中,类的继承性使所建立的软件 具有开放性、可扩充性,这是信息组织与分类的行之有效的 方法,它简化了对象、类的创建工作量,增加了代码的可重性。
2.多重继承
多重继承就类似于 " 小明爷爷的东西会传给小明的父亲 , 小明父亲的东西又会传给小明 , 这些东西中就 存在小明爷爷传给小明父亲的东西"。
// 爷爷类
class GrandFather
{
public $name = '爷爷类'.'<br>';
}
// 父亲类
class Father extends GrandFather
{
public $age = 0 .'<br>';
}
// 小明类
class XiaoMing extends Father
{
public $sex = 'man'.'<br>';
}
// 创建小明类的对象
$xh = new XiaoMing();
// 通过小明类来调用父亲类,爷爷类和小明类的成员
echo $xh->name;
echo $xh->age;
echo $xh->sex;
/*
结果:
爷爷类
0
man
*/
小明类可以调用到父亲类和爷爷类的非私有成员 , 小明类除了继承了父亲类的非私有成员外还继承了爷 爷类的非私有成员 , 这样的继承自多个类的就称之为 多重继承。
重写
重写:就是当子类继承父类的一些方法后,子类又在其内部定义了相同的方法,则这个新定义的方法会 覆盖继承而来的父类的方法,子类只能调用其内部定义的方法。 重写的要求 : 1.当一个父类和子类有一个方法,参数和方法名字完全一致,那么子类方法会覆盖父类的方法。 2.必须参数一致,才会实现方法覆盖。当参数个数不一致,则会报错。当方法名字不一致,就不会 覆盖,只是子类新定义的方法。 3.要求参数相同,具体就是要求参数的个数与父类相同,而并不是参数名称一致。即传递的参数名 字可以为任意,只要保证传递的个数相同即可。 4.在实行方法覆盖的时候,访问修饰符可以是不一样的,但是子类的访问范围必须大于等于父类的 访问范围。
例: 创建一个父类 Animal 类 , 创建一个子类 Dog 类继承自 Animal 类 , 在 Dog 类中创建与父类 Animal 类中同名的 move 方法 , 创建子类的对象调用其中同名的方 法:
// 创建一个 Animal 类
class Animal
{
// Animal 类中创建 move 方法
public function move()
{
echo "动物能动".'<br>';
}
}
class Dog extends Animal
{
// 创建一个和父类同名的方法 注意方法名和参数个数相同,另外子类的访问权限修饰符要大于父类的访问
权限修饰符
public function move()
{
echo "狗能跑,能游泳".'<br>';
}
}
// 创建子类对象
$xh = new Dog();
// 通过子类调用类中同名的方法
$xh->move();
/*
结果: 狗能跑,能游泳 (调用到的是子类的方法)
*/
最后结果调用的是子类的 move 方法。
思考 : 子类重写父类的方法后能否在获取到父类中同名的方法呢? 该如何调用?
parent : parent关键字表示父类 , 可以通过 parent 关键字调用被重写的父类的方法。
// 创建一个 Animal 类
class Animal
{
// Animal 类中创建 move 方法
public function move()
{
echo "动物能动".'<br>';
}
}
class Dog extends Animal
{
// 创建一个和父类同名的方法
public function move()
{
echo "狗能跑,能游泳".'<br>';
}
// 调用当前类和父类方法的测试方法
public function test()
{
parent::move();// 调用的是父类的 move 方法
self::move();// 调用的是当前类的 move 方法
}
}
$xh = new Dog();
$xh->test();
/*
结果 :
动物能动
狗能跑,能游泳
*/
封装
1.什么是封装 封装性(信息隐藏): 封装性是保证软件部件具有优良的模块性的基础。 面向对象的类是封装良好的模块,类定义将其说明(用户可见的外部接口)与实现(用户不可见的内部 实现)显式地分开,其内部实现按其具体定义的作用域提供保护。 2.封装的特点
对象是封装的最基本单位 , 封装防止了程序相互依赖性而带来的变动影响。面向对象的封装比传统语言 的封装更为清晰、更为有力。 例: 创建一个银行类 , 并实现 存钱 , 取钱 , 查询金额的操作:
/**
* 创建一个银行类
*/
class Bank
{
public $money;// 这个金额不能在外部随意设置
/**
* 初始化金额
*/
public function __construct($money)
{
$this->money = $money;
}
/**
* 存钱操作
*/
public function saveMoney($money)
{
$this->money += $money;
}
/**
* 取钱操作
*/
public function getMoney($money)
{
$this->money -= $money;
}
/**
* 查询金额操作
*/
public function showMoney()
{
echo $this->money;
}
}
$jack = new Bank(100);// 给 jack 开户,并存100元
$jack->showMoney();// 查看金额
$jack->saveMoney(100);// 存钱
$jack->showMoney();
$jack->getMoney(100);// 取钱
$jack->showMoney();
$jack->money = 10000;// 随意修改金额
$jack->showMoney();
/*
结果:
100
200
100
10000
*/