面向对象编程(三)
回顾
- 多态:多种形态
-
多态分为:方法重载和方法重写
-
方法重写:之类重写了父类的同名方法
-
规则:
- 子类重写的方法必须和父类名字一样
- 可以具有不同个数的参数,但是严格标准必须是个数一样
- 子类中覆盖的方法不能比父类更加严格
-
规则:
- 方法重载:在同一个类中有多个同名的方法,通过不同的参数个数和参数类型区分不同方法。PHP不支持方法重载。
-
方法重写:之类重写了父类的同名方法
- 私有属性可以继承,但不能被重写
-
方法修饰符
-
Static(静态的)
- Static可以修饰属性和方法
- 静态成员在加载类的分配空间
- 静态成员在内存中只有一份,属于类,不属于某个对象
- 通过类名来调用静态成员 类名::静态成员
- 静态成员也是可以被继承的
- Static也可以表示类名,表示调用当前方法对象的所属的类
-
Final(最终的)
- Final修饰的方法不能被重写
- Final修饰的类不能被继承
-
Abstract(抽象的)
- 只有方法的声明,没有方法的实现成为抽象方法
- 一个类中只要有一个方法是抽象方法这个类必须是抽象类
- 没有抽象方法的类也可以声明成抽象类
- 抽象类不可以被实例化,抽象方法必须在子类中重新实现
- 抽象方法是用来定义方法的命名规范的
-
Static(静态的)
- Self:总是表示当前类的类名
-
接口
- 如果一个类中所有的方法都是抽象类,这个类可以声明成接口
- 使用interface关键字
- 通过implements关键字实现接口
- 接口中所有的方法都是抽象的,公有的
- 不能使用abstract和final来修饰接口中的方法
- 类不允许多重继承,接口可以多种实现
-
类常量
- 使用const关键字声明
- Const常量可以放在类和接口中
- 参数约束:只能约束对象
- 父类可以指向子类的引用。
思考:如何实现C可以调用A和B中的方法
类的自动加载
在项目层面上类文件规则
- 一个文件中只能有一个类
- 文件名和类名同名
- 文件名以.class.php结尾
场景(一个文件中需要用到3个类)
- 创建Goods.class.php页面
<?php
//商品类
abstract class Goods {
protected $name;
final public function setName($name) {
$this->name=$name;
}
abstract function getName();
}
- 创建Book.class.php页面
<?php
//图书类
class Book extends Goods {
public function getName() { //重写了父类的方法
echo “《{$this->name}》<br>”;
}
}
- 创建Mobile.class.php
<?php
//手机类
class Mobile extends Goods {
public function getName() {
echo $this->name,'<br>’;
}
}
- 在PHP页面中使用上面的3个类
问题:在代码之前要引入需要的类。如果页面中需要用到很多的类,这样引入就很麻烦了。
类的自动加载
当页面执行的时候,通过PHP核心程序(zend engine)判断用户需要用到那个类,如果没有找到该类,就会自动调用__autoload()函数,并且以缺少的类名做为参数传递到__autoload()函数中,我们只要在__autoload()函数中加载需要的类即可。
通过缺少的类名加载类文件
在类文件保存地址规则的情况下使用
把类的地址映射到数组中然后加载
适合在类文件保存不规则的时候使用
clone和__clone()
clone:用来创建一个对象
__clone():当调用clone指令的时候自动执行__clone()函数
<?php
class Student {
//执行clone指令的时候自动调用__clone()方法
public function __clone() {
echo ‘你调用了clone指令<br>’;
}
}
header(‘Content-Type:text/html;charset=utf-8’);
$stu1=new Student;
$stu2=clone $stu1; //创建一个新的对象
var_dump($stu1);echo ‘<br>’;
var_dump($stu2);
总结:创建对象的方式:
- 实例化
- clone
单例模式
一个类只能创建一个对象
阻止类实例化的方法
- 抽象方法(可以阻止在类的外部和内部实例化)
- 私有的构造函数(阻止在类的外部实例化)
判断对象是否属于某个类
通过三私一公来实现
<?php
/**
*单例模式,三私一公
*/
class MySQL {
//保存MySQL的单例
private static $instance;
//阻止在类的外部实例化对象
private function __construct() {
}
//阻止在类的外部clone对象
private function __clone() {
}
//获取MySQL的单例
public static function getInstance() {
if(!self::$instance instanceof self) //静态属性中保存的对象是否属于MySQL类
self::$instance= new self(); //实例化对象对象
return self::$instance;
}
}
//测试
$db1=MySQL::getInstance();
$db2=MySQL::getInstance();
var_dump($db1); // object(MySQL)#1 (0) { }
echo ‘<br>’;
var_dump($db2); // object(MySQL)#1 (0) { }
项目层面的单例模式
多个对象的单例
工厂模式
传递不同的参数,返回不同的对象
<?php
class ProductA {
public function show() {
echo ‘获取A商品<br>’;
}
}
class ProductB {
public function show() {
echo ‘获取B商品<br>’;
}
}
//工厂模式(传递不同的参数获取不同的对象)
class Factory {
public static function create($num) {
switch($num%2) {
case 1:
return new ProductA();
case 0:
return new ProductB;
}
}
}
//测试
header(‘Content-Type:text/html;charset=utf-8’);
$objA=Factory::create(10);
$objB=Factory::create(15);
$objA->show(); //获取B商品
$objB->show(); //获取A商品
策略模式
传递不同的参数调用不同的策略(方法)
<?php
class Walk {
public function method() {
echo ‘走着去学校<br>’;
}
}
class Bus {
public function method() {
echo ‘坐车去学校<br>’;
}
}
class Bike {
public function method() {
echo ‘骑车去学校<br>’;
}
}
//策略模式,传递不同的对象,调用不同的方法
class Strategy {
public function go($obj) {
$obj->method();
}
}
//测试
header(‘Content-Type:text/html;charset=utf-8’);
$strategy=new Strategy();
$strategy->go(new Walk()); //走着去学校
$strategy->go(new Bus()); //坐车去学校
$strategy->go(new Bike()); //骑车去学校
魔术方法
已经学过的魔术方法
__construct():构造函数,当实例化对象的时候自动调用
__destruct():析构函数,当对象销毁的时候自动调用
__clone():当调用clone指令的时候自动调用
__toString()和__invoke()
__tostring():当对象作为字符串使用的时候自动执行
__invoke():当将对象作为函数使用的时候自动执行
__set()、__get()、__isset()
1、__set($key,$value):当给无法访问的属性赋值的时候自动调用
2、__get($key):当给无法访问的属性取值的时候自动调用
3、__isset($key):当判断无法访问的属性是否存在
4、__unset($key):当销毁无法访问的属性的时候自动调用
总结:给私有属性赋值的方法
- 通过公有的方法给私有属性赋值
- 通过构造函数给私有属性赋值
- 通过魔术方法__set()赋值,__get()取值
<?php
class Student {
private $name;
private $age;
/**
*当给无法访问的属性赋值的时候自动调用
*@param $key string 属性名
*@param $value string 属性值
*/
public function __set($key,$value) {
if($key==’age’ && !($value>=20 && $value<=30)){
echo ‘年龄必须在20~30之间<br>’;
return;
}
$this->$key=$value;
}
/**
*当给无法访问的属性取值的时候自动调用
*@param $key string 属性名
*/
public function __get($key) {
return $this->$key;
}
/**
*当判断无法访问的属性是否存在
*/
public function __isset($key) {
return isset($this->$key);
}
/**
*当销毁无法访问的属性的时候自动调用
*/
public function __unset($key) {
unset($this->$key);
}
}
header(‘Content-Type:text/html;charset=utf-8’);
$stu=new Student;
$stu->name=’tom’; //给私有属性赋值
$stu->age=25;
echo ‘姓名:’.$stu->name,'<br>’; //获取私有属性的值
echo ‘年龄:’.$stu->age,'<br>’;
var_dump(isset($stu->name)); //判断私有属性是否存在
echo ‘<br>’;
unset($stu->name); //销毁私有属性
var_dump($stu);
__call()、 __callStatic()
<?php
class Student {
/**
*当调用无法访问的属性的时候自动调用
*@param $name string 方法名
*@param $args array 参数数组
*/
public function __call($name,$args) {
echo “对象中没有'{$name}’的方法<br>”;
echo ‘你传递”‘.implode(‘,’,$args).'”参数给我也没用<hr>’;
}
/**
*当调用无法访问的静态方法的时候自动调用
*/
public static function __callstatic($name,$args) {
echo “对象中没有'{$name}’这个静态方法<br>”;
echo ‘你传递”‘.implode(‘,’,$args).'”参数给我也没用<hr>’;
}
}
header(‘Content-Type:text/html;charset=utf-8’);
$stu=new Student;
$stu->show(10,20); //调用无法访问的方法
Student::connect(); //调用无法访问的静态方法
__sleep()和 __wakeup()
__sleep():在序列化的时候自动调用
__wakeup();在反序列化的时候自动调用
<?php
class Student {
private $name;
private $sex;
private $add;
/**
*在调用serialize时候自动调用
*作用:只保留需要序列化的属性
*/
public function __sleep() {
return array(‘name’,’sex’); //返回需要序列化的属性
}
/**
*在调用unserialize时候自动调用
*作用:初始化对象的值
*/
public function __wakeup() {
$this->name=’tom’;
$this->sex=’男’;
$this->add=’中国’;
}
}
header(‘Content-Type:text/html;charset=utf-8’);
$stu=new Student;
$str=serialize($stu);
echo $str,'<hr>’;
$stu=unserialize($str);
print_r($stu);
序列化(serialize)和反序列化(unserialize)
思考:能否将数组和对象永久保存?
答:可以,但是必须先序列化
序列化就是将数组或对象变成一个字符串序列
数组序列化和反序列化
序列化
反序列化
对象序列化和反序列化
序列化
反序列化
发现可以反序列化,但是类名没有反编译出来,解决方法在反序列化之前引入对应的类就可以了
foreach遍历对象
foreach遍历只能遍历到可以访问到的属性
迭代器(Iterator)
场景:一个班级有60个学生,如何遍历这个班?
解决:
Iterator是PHP内置的接口,定义了5个抽象方法,只要实现了这5个抽象方法,迭代器就可以工作了。
例题
<?php
//班级类
class MyClass implements Iterator {
private $list=array(‘tom’,’berry’,’ketty’,’rose’); //学生数组
//复位数组
public function rewind() {
reset($this->list);
}
//判断当前指针是否有效
public function valid() {
return key($this->list)!==null;
}
//返回当前键
public function key() {
return key($this->list);
}
//返回当前值
public function current() {
return current($this->list);
}
//指针下移
public function next() {
next($this->list);
}
}
//测试
$class=new MyClass();
foreach($class as $key=>$val) {
echo $key,’-‘,$val,'<br>’;
}
通过单例模式封装MySQL数据库操作类
实现功能
- 连接数据库
- 查询数据
需要注意的点:
- 一个方法只实现一个功能,方法的功能越单一越好,这样可以最大限度的实现代码的可重用性。
代码
<?php
/**
*MySQL数据库操作类(单例模式实现)
*/
class MySQLDB {
private $host; //主机地址
private $port; //端口号
private $user; //用户名
private $pwd; //密码
private $dbname; //数据库名称
private $charset; //支付编码
private static $instance; //保存MySQLDB的实例
//阻止在类的外部实例化
private function __construct($config) {
$this->initParam($config);
$this->connect();
$this->selectDb();
$this->setCharSet();
}
//阻止在类的外部调用clone指令
private function __clone() {
}
//获取MySQLDB类的单例
public static function getInstrance($config=array()) {
if(!self::$instance instanceof self)
self::$instance=new self($config);
return self::$instance;
}
//初始化数据
private function initParam($config) {
$this->host=isset($config[‘host’])?$config[‘host’]:’127.0.0.1′;
$this->port=isset($config[‘port’])?$config[‘port’]:’3306′;
$this->user=isset($config[‘user’])?$config[‘user’]:”;
$this->pwd=isset($config[‘pwd’])?$config[‘pwd’]:”;
$this->dbname=isset($config[‘dbname’])?$config[‘dbname’]:’data’;
$this->charset=isset($config[‘charset’])?$config[‘charset’]:’utf8′;
}
//连接数据库
private function connect() {
if([email protected]_connect(“{$this->host}:{$this->port}”,$this->user,$this->pwd))
$this->showMessage();
}
//选择数据库
private function selectDb() {
if(!mysql_select_db(“$this->dbname”))
$this->showMessage();
}
//设置字符编码
private function setCharSet() {
if([email protected]_query(“set names {$this->charset}”)){
$this->showMessage();
}
}
//显示错误的方法
private function showMessage($sql=”) {
if($sql!=”)
echo ‘错误SQL语句:’.$sql,'<br>’;
echo ‘错误信息:’.mysql_error(),'<br>’;
echo ‘错误编号:’.mysql_errno(),'<br>’;
exit;
}
//执行SQL语句的方法
private function query($sql) {
if($rs=mysql_query($sql))
return $rs;
$this->showMessage($sql);
}
//拼接函数名
private function createFunction($type) {
$allow=array(‘assoc’,’row’,’array’);
if(!in_array($type,$allow))
$type=’assoc’;
return ‘mysql_fetch_’.$type;
}
//获取二维表的数据
public function getAll($sql,$type=’assoc’) {
$fn=$this->createFunction($type);
$rs=$this->query($sql);
$array=array();
while($rows=$fn($rs)) {
$array[]=$rows;
}
return $array;
}
}
header(‘Content-Type:text/html;charset=utf-8’);
//测试
$config=array(
‘user’ => ‘root’,
‘pwd’ => ‘root’,
‘dbname’=> ‘jokedb’,
);
$mysql=MySQLDB::getInstrance($config);
$rs=$mysql->getAll(‘select * from title’,’aa’);
echo ‘<pre>’;
var_dump($rs);