一、什么面向对象
面向对象是一种编程的思维,最主要的是它是一种思想。举个简单的例子,你想组装台电脑,但是不懂电脑方面的知识,对电脑的市场行情不了解。你就需要先去网上调查电脑硬件知识以及目前的市场行情,再去网上商城下单或者去实体商城看看,最后将买回来的各个硬件组合起来。但是如果你有个朋友知道怎么买电脑,怎么组装,你就可以将任务委托给他,让他帮你完成。
面向对象就是最后一种的懒人思想,你不需要干什么,只需要将需求告诉有这种功能的对象就可以了。
二、如何抽象一个类
(1)类的写法格式
class 类名{
成员属性(需要修饰符)
成员方法(修饰符可以不写,默认为public)
}
类的写法示例(以Person类为例)
class Person{
public $name='张三';
public $age=‘18’;
function speak(){
echo 'hello';
}
}
(2)类的实例化(以person为例)
$p = new Person()
echo $p->name;
echo $p->speak();
(3)修饰符
成员属性:常用的修饰符 public protected private static
成员方法:常用修饰符 public protected private static abstract(抽象) final(最终)
- 常见修饰符
成员属性、成员方法都可以用修饰符修饰,最常见的修饰符有public公有、protected受保护的、private私有。
注意:除了公有的成员属性和成员方法可以在外部访问,私有和受保护的都不能在外部访问。
关于修饰符的访问权限,有个很好的记忆表格如下:
private | protected | public | |
---|---|---|---|
类内部 | yes | yes | yes |
子类 | no | yes | yes |
类外部 | no | no | yes |
形象的说,类就像社会上的人一样,我可以用自己的私人物品、家里的工具、社会上的基础设施;我的后代也可以家里工具,我的私人用品(牙刷什么的)我不想让他用,他也可以用社会上的基础设施;但是随便来个陌生人,我能将我家里的工具给他吗?
- 有特殊用途的修饰符
1)final 最终
什么时候用final?
1、如果类不希望被别人继承 在类名前加修饰词 final;
2、如果方法不希望被重写,在方法名前加修饰词 final。
注意:final不能用来修饰成员属性。
使用final目的 1、出于安全性 ,指的是被继承后可能发生我不希望的改变;2、没有必要时,指的是方法已完善,没有必要重写。
2)static 静态
静态的成员属性只初始化一次,静态的成员方法也只能初始化一次。
class A{
static $count=0;
public function __construct()
{
//访问类中的静态属性 或静态方法 self::
//访问类中的普通属性 或普通方法 $this->
self::$count++;
}
public function getCount(){
echo self::$count;
}
}
$a=new A();
$a->getCount();
$a1=new A();
$a1->getCount();
- 静态方法应用-单例模式
单例模式又名单态模式。指的是一个类只能有一个实例化对象存在。
为了能让类只能实例化一次 前提是让类不能实例化,确切说是不能以new 的方式去实例化。
class Db{
private static $obj=null;
private function __construct()
//1、构造方法私有化
{
echo '数据库连接成功
';
}
public function a(){
echo 'aaa';
}
public static function getInstance(){
//通过此静态方法来实例化 产生对象
if(is_null(self::$obj)){
//如果self::$obj===null
//类内部去实例化
self::$obj=new Db();
}
return self::$obj;
}
}
//类外访问静态方法 类名::静态方法
$db=DB::getInstance();
$db->a();
3)abstract 抽象
- 应用实例-抽象类 抽象方法
1、abstract 修饰没有方法体的方法称为抽象方法;
2、类中有抽象方法的类, 称为抽象类;
有抽象方法的类必须是抽象类;
抽象类不一定必须有抽象方法。
abstract class A{
//普通类 普通方法
public function test(){
echo 'i am a test!';
}
//1、abstract 修饰的方法称为抽象方法 ,没有花括号 没有方法体
//抽象方法
public abstract function test1();
public abstract function test2();
}
//$a =new A(); 抽象类不能实例化 也就是new成对象
//抽象类可以继承
abstract class B extends A{
//继承抽象类的同时要重写抽象类中的抽象方法。
public function test1(){
echo 'i am test1!';
}
public abstract function test3();
}
//当一个抽象类继承一个抽象类的时候,可以不必须实现所有的抽象方法。
//当一个普通类继承一个抽象类的时候就必须全部实现抽象类里的所有抽象方法。
class C extends B{
public function test2(){
echo 'i am test2!';
}
public function test3(){
echo 'i am test3';
}
}
$c = new C();
$c ->test1();
$c ->test2();
$c ->test3();
三、类的三大特性(封装、继承、多态)
(1)封装
封装就是根据类外部不能访问类中私有及受保护的属性或方法进行的。在封装的类中至少应该有一个公有的方法存在,以便我们通过此方法来访问类中某个受保护的/私有的属性或方法。就像你要盖个房子总得留个门吧,要是各方向都堵住,不能进、不能出,没有什么用处。
举个简单封装的例子:
class Person{
public $name=‘张三’;
protected $age=‘18’;
private $money=‘200’;
function speak(){
echo 'hello';
}
protected function job(){
echo '我的工作比较保密';
}
private function addr(){
echo '我住在那,我自己知道';
}
}
这样我们就没法在外部访问受保护/私有的属性或方法,就需要用公有方法调用那些不能再外部访问的。
写法可以是这样的,
function speak(){
echo 'hello'.$this->age;
}
但这样访问很不方便,我们有了更简便的方法。
(2)继承
子类继承父类 就拥有了父类的一切属性和方法。
子类可以重写父类方法:
如果子类中出现了与父类的重名的方法 ,重写会覆盖父类的方法。
class Person{
public $name='张三';//公有
protected $age='19';//受保护,可以继承给子类
private $money='200';//私有,子类不可以访问
public function speak(){
echo 'i can speak'.'我的年龄是:'.$this->age.'我有'.$this->money.'元钱';
}
protected function b(){
echo 'Person/b';
}
private function c(){
echo 'Person/c';
}
}
//extends 继承 关键字
class Teacher extends Person {
public $sex ='男';
public function run(){
echo 'i can run'.'我的年龄是:'.$this->age.'我有'.$this->money.'元钱';
}
public function b(){
//子类方法中调用父类的方法
parent::b();
echo 'Student/b';
}
public function c(){
// parent::c();私有方法 子类继承也不能调用
echo 'Student/c';
}
}
//子类继承父类 就拥有了父类的一切属性和方法
$t=new Teacher();
echo $t->name;
echo $t->sex;
echo $t->speak();
$t->b();
//echo $t->run();
重写方法的注意点:如果子类重写父类,子类只能比父类宽松,不能比父类严格。
private严格 | protected中间 | public宽松 | |
---|---|---|---|
父类 | private | protected | public |
子类 | private protected public | protected public | public |
(3)多态
常量一经定义 不能修改;
多态指的是通过传入参数的不同,获得不同的响应结果。
常量const, 用const关键字来修饰的成员属性叫做常量.
//定义一个USB接口 让每个USB设备都必须遵守这个规范
interface Usb1{
//在类中 接口中 定义常量
const WIDTH='20mm';
const HEIGHT='5mm';
function run();//抽象方法 等待被实现的类来重写此方法。
}
class Computer{
function useUsb($usb){
$usb->run();
}
}
//鼠标来实现接口(鼠标插入接口)
class Mouse implements Usb1 {
public function run()
{
echo '鼠标插入成功可以正常运行';
}
}
//u盘来实现接口(U盘插入接口)
class Udisk implements Usb1{
public function run(){
echo 'u盘插入就可以正常运行';
}
}
$c= new Computer();
$mouse= new Mouse();
$u=new Udisk();
//$mouse->run();
$c->useUsb($mouse);
$c->useUsb($u);
四、魔术方法
PHP 独有的,其他语言没有的,封装好的一些具有固定功能的方法。
特点1:php面向对象中,所有的魔术方法 __下滑线开头。
特点2:php魔术方法在满足条件的下,将会自动调用
这里介绍常用的几种方法,
- __construct(构造方法)
- __destruct(析构方法)
- __set(设值方法)
- __get(取值方法)
- __isset(判断变量是否存在)
- __unset (销毁一个变量)
- __call (处理错误调用)
- __autoload (自动加载)
1)__construct(构造方法)和__destruct(析构方法)
class Person{
//构造方法(函数) 在类被实例化的时候,在对象创建的时候
//满足一些初始化的操作
public $name;
function __construct($name){
$this->name = $name;
}
//析构方法,对象销毁前被自动调用。
public function __destruct()
{
echo $this->name;
}
/*public function person(){
echo '被调用';
}*/
//跟类名同名的方法,当对象被创建的时候 也会被自动调用
}
$p =new Person('张三');//类的实例化
$p =null;
echo $p->name;
$p1 =new Person('李四');
在没有构造方法的时候,我们用跟类名同名的方法。当对象被创建的时候,其同名方法也会被自动调用。
构造方法是用来初始化成员属性的。
析构方法是用来销毁对象的,一般在代码执行结束前执行。但是当对象的实例化设值为null时,会调用析构方法销毁对象。
2)__set(设值方法)和__get(取值方法)
class Person{
public $name;
protected $age;
protected $sex;
private $money;
//在类外对类中的受保护属性赋值的时候会自动调用。
function __set($name, $value)
{
$this->$name=$value;
}
//在类外对类中的受保护属性获取的时候会自动调用
function __get($name)
{
return $this->$name;
}
}
$p =new Person();
$p->age=12;
echo $p->age;
在类外对类中的受保护的属性不能直接赋值,可以通过一个公有的方法达到在类外对类中的受保护的属性进行赋值。
public function setAge($age){
$this->age = $age;
return '我的年龄是:'.$this->age;
}
public function setSex($sex){
$this->sex =$sex;
return '我的性别是:'.$this->sex;
}
echo $p->setAge(18);
echo $p->setSex('男');
但是这样的做法很麻烦,远不如用设值方法和取值方法方便。
3)__isset(判断变量是否存在)和 __unset (销毁一个变量)
class Person{
public $name='小明';
public $abc ='abc';
protected $age='18';
//类外 用isset方法判断类中私有/受保护的属性是否存在的时候
public function __isset($name)
{
return isset($this->$name);
}
//类外 用unset方法销毁类中私有/或受保护属性的时候
//会自动执行__unset 魔术方法
public function __unset($name)
{
unset($this->$name);
}
}
//在类的外部判断类的内部属性是否存在
$p=new Person();
var_dump(isset($p->name));
var_dump(isset($p->abc));
echo '
';
var_dump(isset($p->age));
unset($p->age);
var_dump(isset($p->age));
4)__call (处理错误调用)
当试图调用一个类中不存在的方法,就会报错;
如果此时有__call 方法则会自动调用此方法。
class A{
public function test(){
echo 'test';
}
public function __call($name, $arguments)
{
echo '你访问的'.$name.'方法不存在,参数列表如下:';
foreach ($arguments as $v){
echo $v.'
';
}
}
}
$a =new A();
$a->test('hehe');
$a->test1('hello','world');
5)__autoload (自动加载)
作用:当new 一个不存在的类的时候 会自动调用
唯一一个写在类外的魔术方法。
One类:
class One{
function a(){
echo 'one/a';
}
}
示例代码:
function __autoload($name){
if(file_exists($name.'.class.php')){
include_once $name.'.class.php';
}else{
die($name.'.class.php文件不存在');
}
}
$one =new One();
$one ->a();
五、PHP其他常用的函数及对数据的处理
(1)对象串行化与对象反串行化
serialization 串行化,串行化又名序列化。
- serialize()串行化
- unserialize()反串行化
1)对象什么时候需要串行化
1、对象需要在网络中传输 ,将对象串行化二进制串;
2、对象需要持久化保存 ,将对象串行化后写入文件或数据库(以字符串)。
串行化示例:
class Person{
public $name='张三';
public $age= '19';
function speak(){
echo 'Hello, my name is '.$this->name.'. My age is '.$this->age;
}
}
$p= new Person();
//对象串行化
$str=serialize($p);
//file_put_contents 向文件中写入内容
file_put_contents('file.txt',$str);
//var_dump($str);
$p->speak();
反串行化示例:
include_once '08-oop-serialize.php';
//反串行化 反序列化
//不用在重新创建对象
//获取file.txt文本内容
$str =file_get_contents('file.txt');
$obj=unserialize($str);
//var_dump($obj);
$obj->speak();
注意:需要将要串行化对象的实例化代码注释掉,才能看出结果。
(2)约束类型
class Person{
//类型约束 只运行约束的类型对象及其子类
public function test(Teacher $obj){
echo $obj->a();
}
}
class Student extends Teacher {
public function a(){
return 'Student/a';
}
}
class Teacher{
public function a(){
return 'Teacher/a';
}
}
$s = new Student();
$t = new Teacher();
//echo $s->a();
$p = new Person();
$p->test($s);
$p->test($t);
约束类型只是一种写法,能够约束传入成员方法的参数。
(3)instanceof
instanceof 是检测当前对象实例是否属于某一个类的类型;
instanceof的示例:
class A extends B{
}
class B{
}
$a=new A();
$b =new B();
//$a对象是否属于A类;
var_dump($a instanceof A);//true
//$b对象是否属于B类;
var_dump($b instanceof B);//true
//$a对象是否属于B类;
var_dump($a instanceof B);//true
//一个类一定属于自己类本身,
//一个对象还属于本类的父类;
//$b对象是否属于A类;
var_dump($b instanceof A);//false
echo '
';
//__toString 快速获取对象字符串
//当我们直接输出一个对象的时候 会自动调用
class C{
//__toString 方法的返回值 一定是 字符串
public function __toString()
{
//解释说明类的用法
return '此类是作为方便大家来调用使用的,用来解释说明此类的作用,编写文档。';
}
}
$c = new C();
//类外直接输出对象
echo $c;
六、PHP interface 接口
(1)接口的写法
php是单继承的,不支持多继承 为了解决这个问题,引入接口。
接口的思想是 ,实现该接口的类 必须实现的一些列的方法。
接口更多是为了定义规范。
定义接口,接口内部的方法必须全部为抽象方法;
因为接口中的所有方法均为抽象方法,所以省去 abstract 关键字。
interface Usb{
function test1();
function test2();
}
//implements 实现(继承的含义)
//普通类实现接口 需要实现接口中的所有抽象方法
class Machine implements Usb{
function test1()
{
// TODO: Implement test1() method.
}
function test2()
{
// TODO: Implement test2() method.
}
function test3(){
echo '我是名牌产品!';
}
}
七、PHP对数据库的操作-pdo类
(1)如何使用pdo 操作数据库
1.用pdo 连接数据库:
$dsn = 'mysql:dbname=myphp;host=127.0.0.1;';
$user='root';
$pass='';
$pdo =new PDO($dsn,$user,$pass);//实例化pdo 类 得到pdo 对象
$pdo->query("set names utf8");
//执行query 执行sql
$stmt = $pdo->query("select * from users");
//如何用pdo来获取所有记录
$rows =$stmt->fetchAll();
print_r($rows);
2.连接数据的封装类。
这里是连接数据的封装类,是由后面的知识总结写出的。
try{
$dsn = 'mysql:dbname=myphp;host=localhost';
$user= 'root';
$pwd='';
$pdo =new PDO($dsn,$user,$pwd);
//设置属性
//PDO::ATTR_DEFAULT_FETCH_MODE 设置提取模式
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT,1);//关闭自动提交
$pdo->query('set names utf8');//设置字符集 utf8
}catch(PDOException $e){
echo '数据连接失败'.$e->getMessage();
}
(2)异常处理机制与数据报错处理
Exception 异常
try...catch 异常处理,处理用于在指定的错误发生时改变脚本的正常流程。
try{
//使用try 去包含可能发生异常的代码
//一旦出现异常 try 进行捕获异常
//catch 异常对象参数
$error = '一直会抛出的这个异常';
throw new Exception($error);//throw 对象(异常)
echo '一旦异常被抛出,此处代码不执行';
}catch(Exception $e){
//获取异常后,相应处理
echo $e->getMessage();
}
两数相除的示例:
try{
$a =10;
$b = 2;//$b ?= 0
if($b!=0){
$c = $a/$b;
}else{
$error = '分母不能为0';
throw new Exception($error);
}
echo $c;
}catch(Exception $e){
echo $e->getMessage();
}
(3)关于fetchAll与fetch
include_once 'database.php';
$sql= "select * from users WHERE id=1";
//执行SQL语句
$stmt=$pdo->query($sql);
$rows=$stmt->fetchAll();//从结果集中取出所有记录 二维数组
$rows=$stmt->fetch();//从结果中获取一条记录,一维数组存储
$rows2=$stmt->fetch();
$rows3=$stmt->fetch();
echo '';//格式化结果
print_r($rows);
print_r($rows1);
print_r($rows2);
(4)pdo对数据库的操作-增删改查
try{
$dsn = 'mysql:dbname=myphp;host=localhost';
$user= 'root';
$pwd='';
$pdo =new PDO($dsn,$user,$pwd);
//设置属性
//PDO::ATTR_DEFAULT_FETCH_MODE 设置提取模式
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
}catch(PDOException $e){
echo '数据连接失败'.$e->getMessage();
}
//insert 增 update 改 delete 删 exec 来执行
//select 查 query 来执行
$pdo->query('set names utf8');//设置字符集 utf8
$stmt=$pdo->query("select * from users");//查
$rows=$stmt->fetchAll();
print_r($rows);
$stmt=$pdo->exec("insert into users VALUES (null,'小王','123123')");//增
$stmt=$pdo->exec("update users set name = '小李' WHERE id=2");//改
$stmt=$pdo->exec("delete from users WHERE id = 3");//删
var_dump($stmt);//返回受影响的行数。
(5) pdo对数据库的操作-事务
pdo 事物处理 一件事正在发生 ,要么全部成功 ,要么全部失败;
pdo 来做事务处理需要注意的事项:
1.MySQL表引擎 必须是 innodb 类型 myisam
2.关闭当前的自动提交改为手动提交
3.改为手动提交
4.回滚
5.提交
注意: 如果没有提交,在提交之前的任何步骤都可以回滚,提交后就不能回滚了。
小知识:定点数据(DECIMAL)例, 5,2 指的是最大长度5,小数位2,如 999.99。
//1.连接数据库
try{
$dsn = 'mysql:dbname=myphp;host=localhost';
$user= 'root';
$pwd='';
$pdo =new PDO($dsn,$user,$pwd);
//设置属性
//PDO::ATTR_DEFAULT_FETCH_MODE 设置提取模式
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC);
$pdo->setAttribute(PDO::ATTR_AUTOCOMMIT,0);//关闭自动提交
$pdo->query('set names utf8');//设置字符集 utf8
}catch(PDOException $e){
echo '数据连接失败'.$e->getMessage();
}
try{
//2.开启一个事务
$pdo->beginTransaction();
//3.准备sql 语句 转账开始 把张三的钱减少100
$sql ="update salary set money = money - 100 where id = 1";
$n = $pdo->exec($sql);
//$n如果0 true执行成功 否则失败
if(!$n){
//转账失败
throw new PDOException('张三转账失败');
}
//4.准备sql语句 转账开始 把李四的钱加100
$sql ="update salary set money = money + 100 where id = 2";
$n = $pdo->exec($sql);
//$n如果0 true执行成功 否则失败
if(!$n){
//转账失败
throw new PDOException('李四接收失败');
}
//最终提交
$pdo->commit();
echo '转账成功';
}catch (PDOException $e){
//回滚 回到最初
//代码能够执行到此处 说明在交易的过程中有任何一个环节出现问题,就要回滚
$pdo->rollBack();
echo $e->getMessage();
}
(6)pdo对数据库的操作-预处理批量插入数据
使用预处理语句来批量插入, 操作数据库。
1)?表示占位 问号式
include_once 'database.php';
$sql = "insert into users(name,pass) VALUES (?,?)";
$stmt = $pdo->prepare($sql);
$stmt->execute(['小白','123132']);
$stmt->execute(['小黑','123132']);
2)参数绑定 别名式
include_once 'database.php';
$sql = "insert into users(name,pass) VALUES (:name,:pass)";
$stmt = $pdo->prepare($sql);
$stmt->bindParam(':name',$name);//用于绑定参数
$stmt->bindParam(':pass',$pass);
$name='大白';
$pass='123321';
$stmt->execute();
注意:如果插入的字段少的话,问号式交较好,较多的话别名式较好。