PHP基础之面向对象篇

前言

前面写的都是运算符、流程控制、排序查找等,下面说一说面向对象的一些内容。这是前面写的,有兴趣能够去看一看。
PHP入门之类型与运算符
PHP入门之流程控制
PHP入门之函数
PHP入门之数组
PHP基础之排序
PHP基础之查找
接下来写一下关于面向对象的内容。php

类与对象基本概念

用一个案例入门:html

<?php
//建立一个对象
class cat {
    public $name;
    public $age;
    public $color;
 }
//建立一个猫
$cat1= new cat;
$cat1->name="小刘";
$cat1->age=18;
$cat1->color="yellow";
//再建立一个猫
$cat2= new cat;
$cat2->name="小陈";
$cat2->age=16;
$cat2->color="pink";
//输出两个猫的信息
if ($cat1->name="小刘"){
    echo $cat1->name."||".$cat1->age."||".$cat1->color.'<br/>';
}if ($cat2->name="小陈"){
    echo $cat2->name."||".$cat2->age."||".$cat2->color;
}
?>

总结几句话:web

  • ①类是抽象的,表明一类事物。
  • ②对象是具体,是类的一个具体实例。
  • ③类是对象的模板,对象是类的一个个具体实例。
    类的基本格式
    class 类名{
    成员属性(变量);

}
成员属性是从某个事物提取出来的,它能够是 基本数据类型,也能够是复合数据类型(数组,对象)
如何建立对象?
$对象名=new 类名();
$对象名=new 类名; //两种方式均可以
对象如何访问(使用)对象的属性?
$对象名->属性名;数据库

对象在内存中存在形式

对象在内存中如何存在?
用下面代码说明:数组

<?php
class Person {
    public $name;
    public $age;
}
$p1= new Person();
$p1->name="小红";
$p1->age=18;
$p2=$p1;
echo $p1->name.'<br/>';
echo $p2->age.'<br/>';

?>

如今画一下内存图:

name="小红";age=18;变量$p1->name和age时就会由栈区指向堆区。
指向得是地址函数

函数接收对象时候,究竟接收得是值,仍是地址?
看一段代码:学习

<?php
class Person {
    public $name;
    public $age;
}
$p1= new Person();
$p1->name="小红";
$p1->age=18;            #咱们发现输出结果为大红,因此,函数接收对象时候,接收得是地址。
function test ($p){
    $p->name="大红";
}
test($p1);
echo $p1->name.'<br/>';
?>

若是给函数传递的是基本数据类型(整行,浮点型,布尔型),传递的是什么?
默认状况下传递的是值。若是但愿传地址,那就加上&符。
若是给一个函数传递的是一个数组,则默认状况下是传值。
举个例子:this

<?php
$arr=array($a1,$a2);
$a1=array(3,5,8);
$a2=array(5,7,9);
var_dump($arr);
?>

能够输出结果吗?答案是没法输出结果。会报变量没有定义的错误。由于是传值,因此第一行的$a1和第二行的$a1是两码事。
若是换一下顺序,就能够了。设计

<?php
$a1=array(3,5,8);
$a2=array(5,7,9);
$arr=array($a1,$a2);
var_dump($arr);
?>

这样就能够输出了,数组有值了。code

构造函数

什么是构造函数(方法)?
想要知道什么是构造函数,咱们先看一个需求,以前咱们建立一个对象的时候,是建立好以后,再给对象的属性进行赋值,若是咱们再建立对象的时候就直接给属性赋值,这样该如何作呢?下面咱们就要引入构造函数了。
上面的问题,咱们只须要定义一个构造函数就能够了。构造函数是类的一种特殊的函数,它的主要做用是完成对新对象的初始化。
构造函数特色:
①没有返回值。
②在建立一个类的新对象时,系统会自动的调用该类的构造函数完成对新对象的初始化。
用一个小案例说明:

<?php
class Person{
    public $name;
    public $age;
    function __construct($iname,$iage)
    {
        $name=$iname;
        $age=$iage;
        echo "我是构造函数";
        echo '<br/>';
    }
}
$p1=new Person("小可爱",18);
echo $p1->name;
echo '<br/>';
echo $p1->age;
?>

若是咱们这样写,咱们认为会输出:我是构造函数小可爱18,可是,最后只会输出我是构造函数。这位为何呢?
以前咱们说过,构造函数也是函数,也会开一个新栈。这里他会把$name和$age当成一个新的变量。并不会指向对象的属性。
因此,这里引入了一个重要的概念。$this(这个很重要)!!!!
若是使用$this,它就会指向当前对象,
再理解的深一点,就是这个对象的地址。哪一个对象使用到$this,就是哪一个对象地址。$this不能再类外部使用。
咱们须要将上面的代码进行修改。

$name=$iname;
        $age=$iage;

改成:

$this->name=$iname;
    $this->age=$iage;

这样,程序就能够正常输出了。
这里须要注意的一点是,若是咱们没有定义构造函数,系统会有一个默认的构造函数。
function __construct(){}
因此以前咱们建立对象的时候都是 $p1= new person();
若是咱们自定义了构造函数,再这样建立对象的时候,系统就会报错。
类中只能有一个构造函数(不能重载)
类的构造方法小结:

  • ①再PHP4中,构造方法名和类名相同,PHP5以后能够和类名相同也能够是__construct()。
  • ②构造方法没有返回值。
  • ③主要做用是完成对新对象的初始化,并非建立对象自己。
  • ④在建立新对象后,系统自动的调用该类的构造方法。
  • ⑤一个类有且只有一个构造方法。
  • ⑥若是没有给类自动义构造方法,则该类使用系统默认的构造方法。
  • ⑦若是给类自定义了构造方法,则该类的默认构造方法被覆盖。
  • ⑧构造方法的默认访问修饰符是public。

析构函数

什么是析构函数?
析构函数会在到某个对象的全部引用都被删除或者当对象被显式销毁时执行。在PHP5中引用。
其实就是释放资源,好比(释放数据库的连接,图片资源,销毁某个变量...)等等。
用小案例入门:

<?php
 class Person{
     public $name;
     public $age;
     //构造函数
     function __construct($name,$age)
     {
         $this->name=$name;
         $this->age=$age;
     }
    //析构函数
     function __destruct()
     {
         // TODO: Implement __destruct() method.
         echo $this->name.'销毁资源'.'<br/>';
     }
 }
 $p1= new Person("小王",18);
 $p2=new Person("小张",20);
?>

运行程序,咱们发现,析构函数会自动调用。主要用于销毁资源。
析构函数调用顺序是,先建立的对象后销毁。(想象一会儿弹上膛,最后一颗子弹第一颗打出去,先进先出)。
因此上面的执行结果为:
小张销毁资源
小王销毁资源
何时系统会调用析构函数?

  • 1、程序运行完退出的时候。
  • 2、当对象没有变量指向它的时候,它会变成垃圾对象,会马上调用析构函数回收。(和Java不同)。
    还有两点须要注意:
  • 1、析构函数没有返回值。
  • 2、一个类最多只能有一个析构函数。

静态变量与静态方法

先提出一个需求:
若是如今有一群孩子在玩游戏,不停的有新得小朋友加入,统计小朋友的个数并输出。用面向对象的程序完成。
能够考虑全局变量的方式,可是不推荐,由于那就不算纯对象了。可是也能够作出来。
代码以下:

<?php
global $child_sums;
      $child_sums=0;
class Child
{
    public $name;

    function __construct($name)
    {
        $this->name = $name;
    }

    function JoinChild()
    {
        //申明使用全局变量
        global $child_sums;
        $child_sums+=1;
        echo $this->name . "加入游戏";
    }
} 
//建立三个小孩
$child1=new Child("拉拉");
$child1->JoinChild();
$child2=new Child("哈哈");
$child2->JoinChild();
$child3=new Child("哒哒");
$child3->JoinChild();
echo "<br/>"."有".$child_sums."个小朋友";
?>

虽然能够实现,但不推荐,下面咱们使用静态变量的方法。
代码以下:

<?php
class Child{
    public $name;
    public static $sums=0;
    //构造函数
    function __construct($name)
    {
        $this->name=$name;
    }
    function JoinChild(){
        self::$sums+=1;
        echo $this->name.'加入游戏';
    }
}
//建立三个小孩
$child1=new Child("拉拉");
$child1->JoinChild();
$child2=new Child("哈哈");
$child2->JoinChild();
$child3=new Child("哒哒");
$child3->JoinChild();
//看看多少人
echo '<br/>'."一共".Child::$sums."个小孩";
?>

那什么是静态变量呢,就是全部对象共享的一个变量,它不在堆区,在全局区。对象想要访问它,就指向它的地址。
如何定义呢?
访问修饰符 static 变量名;
如何访问呢?
在类外部 类名::$类变量名
在类内部有两种 类名::$类变量名或者self::$类变量名。
这里须要注意的一点是,访问静态变量和是否建立对象无关,你不建立对象,也能够访问。
访问静态变量,禁止使用$this,会报错。

静态方法

静态方法和静态变量是对应的,只能调用静态变量,若是调用非静态变量它是会报错的。反过来就能够,就是普通成员函数是能够调用静态变量的。缘由是静态变量和静态方法都属于这个类,都是公开的。
仍是上面的例子,进行一下修改。

<?php
class Child{
    public $name;
    public static $sums=0;
    //构造函数
    function __construct($name)
    {
        $this->name=$name;
    }
   static function JoinChild(){
        //self::$sums+=1;
        Child::$sums+=1;
    }
    function haizi(){
        echo $this->name;
    }
}
//建立三个小孩
$child1=new Child("拉拉");
$child1->haizi();
$child1->JoinChild();
$child2=new Child("哈哈");
$child2->haizi();
$child1->JoinChild();
$child3=new Child("哒哒");
$child3->haizi();
$child1->JoinChild();
//看看多少人
echo '<br/>'."一共".Child::$sums."个小孩";
?>

咱们只须要在普通方法前加关键字static,就能够成为静态方法,以下面这样:

static function JoinChild(){
        //self::$sums+=1;
        Child::$sums+=1;
    }

有上面两种调用方法。

面向对象三大特性之封装

提到封装,应该先说一说修饰符。
public(公开的)、protected(受保护的)、private(私有的)
正由于有了protected(受保护的)、private(私有的)这两个修饰符,才能体现封装的概念。
写一个例子:

<?php
class Person{
    public $name;
    protected $age;
    private $wage;
    public function __construct($name,$age,$wage)
    {
       $this->name=$name;
        $this->age=$age;
        $this->wage=$wage;

    }
}
$p1=new Person("小利",18,1000);
echo $p1->name;
echo $p1->age;  #报错
echo $p1->wage; #报错
?>

这样就体现了封装的概念,protected(受保护的)、private(私有的)这两个修饰符修饰的变量不让你直接调用。
若是,咱们想要调用呢,那就写一个公开的方法,调用那个方法就能够了。
把上面的例子改一下,再类里添加:

public function PersonAge($age){
        echo $this->age=$age;
    }
    public function PersonWage($wage){
        echo $this->wage=$wage;
    }

而后类外调用这两个函数就能够了。

$p1->PersonAge(20);
$p1->PersonWage(3000);

你可能会有疑问,咱们直接调就能够了,为何要多走这一步,不是没事找事嘛。确定是有缘由的,方法里,咱们能够对变量进一步控制,好比加个范围,权限再控制的细一些等等。
也能够用另一种方法,PHP为咱们提供的,叫作魔术方法:__set()、__get()
__set()对protected或是private属性,进行赋值操做。
__get()获取protected或是private属性的值。

面向对象三大特性之继承

先来看一个小问题,若是咱们作一个学生管理系统,有小学生,大学生,研究生。若是咱们建立三个类的话,那么咱们就会发现一个问题,那就是代码重复。因此咱们有了继承的概念。
写个小案例:

<?php
//父类
class Student{
    public $name;
    public $age;
    public $studentID;

    public function ShowInfo($name,$age){
        echo $this->name=$name."||".$this->age=$age;
    }
}
//子类
class universityStudent extends Student{

    public function study(){
        echo "大学生在学习";
    }
}
$u1=new universityStudent();
$u1->ShowInfo("小练习",18);
$u1->study();
?>

咱们发现,子类可使用父类的方法,这就解决了刚才的问题,解决了代码的重复性。若是想要使用继承,关键字extends不能少。
其实所谓继承,就是子类经过extends关键字,把父类的(public、protected)属性和(public、protected)方法继承下来。
咱们还要注意,只能继承(public、protected)属性和(public、protected)方法,private的属性和方法只能本类使用。
注意:
子类最多只能继承一个父类(指直接继承)
在建立某个子类对象时,默认状况不会自动调用其父类的构造函数。(和Java不同)。
举个例子:将上面的代码修改

<?php
class Student{
    public $name;
    public $age;
    public $studentID;
    function __construct()
    {
        echo "我是父类的构造函数"."<br/>";
    }
    public function ShowInfo($name,$age){
        echo $this->name=$name."||".$this->age=$age;
    }
}
class universityStudent extends Student{
    public function __construct()
    {
        echo "我是子类的构造函数"."<br/>";
    }

    public function study(){
        echo "大学生在学习";
    }
}
$u1=new universityStudent();
$u1->ShowInfo("小练习",18);
$u1->study();
?>

上面的代码会输出:

我是子类的构造函数
小练习||18大学生在学习

父类的构造函数不会自动调用。那若是想调用父类的构造函数呢。只须要在子类的代码中加入:父类名::构造函数名或者parent::构造函数名两种方法均可以。

public function __construct()
    {
        Student::__construct();
        echo "我是子类的构造函数"."<br/>";
    }

这样的话,会输出:

我是父类的构造函数
我是子类的构造函数
小练习||18大学生在学习

若是子类的方法名和父类的方法名相同,这叫作方法的重写(覆盖),这就是多态了,后面再详细说多态。

面向对象三大特性之多态

多态是一种概念,下面说两个知识点。

函数重载

“重载”是类的多态的一种实现,是指的是一个标识符被用做多个函数名,而且可以经过参数个数或者参数类型将这些同名的函数区分开,调用不发生混淆。
PHP虽然支持重载,但重载在具体实现上,和其余语言有较大的差异。举个例子:

class A{
    public $name;
    public $age;
    public function test(){
        echo "hello,123";
    }
    public function test($a){       #若是咱们这么写,PHP会报错!!!!其余的语言能够,Java这么写的话没问题。
        echo "hello,456";
    }
}
$a=new A();
$a->test();
$a->test($a);

上面的是错误的写法。PHP有本身的方法,这里PHP引进了魔术方法。魔术方法:__call()
这个方法比较神奇。下面看代码:

class A{
    public $name;
    public $age;
    public function test1($a){
        echo "hello,123";
    }
    public function test2($a){
        echo "hello,456";
    }
    public function __call($name, $arguments)
    {
        var_dump($arguments);
        if($name=="test"){
            if(count($arguments)==1){
                $this->test1($arguments);
            }elseif (count($arguments)==2){
                $this->test2($arguments);
            }
        }
        // TODO: Implement __call() method.
    }
}
$a=new A();
$a->test(1);
$a->test(2,6);
/*执行结果为:
array(1) { [0]=> int(1) } hello,123array(2) { [0]=> int(2) [1]=> int(6) } hello,456
咱们发现执行成功了,实现了函数重载。这是多态的一种体现。*/

咱们须要知道一些魔术常量:

echo "<br/>".__LINE__;
echo "<br/>".__DIR__;
echo "<br/>".__FILE__;
echo "<br/>".__CLASS__;
echo "<br/>".__TRAIT__;
echo "<br/>".__FUNCTION__;
echo "<br/>".__METHOD__;
echo "<br/>".__NAMESPACE__;
输出结果为:
150
D:\phpstudy_pro\WWW\PHP
D:\phpstudy_pro\WWW\PHP\object02.php
A
test1
A::test1
array(2) { [0]=> int(2) [1]=> int(6) } hello,456

方法重写(覆盖)

提一个问题,若是咱们设计一个类,提取一些相同的特征,设计成父类,并有一些函数。若是子类中想要完善父类的方法,只须要在子类中方法的命名和父类相同,参数彻底相同就能够。咱们把它叫作方法的重写(覆盖)。若是子类想要调用父类的方法,可使用parent::方法名()就能够。
子类方法不能缩小父类方法的访问权限,能够扩大。
上面的内容体现了面向对象的多态性。

抽象类

提一个问题,为何设计抽象类。
为了快速开发,咱们可能有这样的类,是其余类的父类,但它自己并不须要实例化,主要用途是用于子类去继承。这样能够达到代码复用,而且利于项目设计者设计类。
设计成抽象类二点格式:
abstract class 类名{
abstract 修饰符 function 函数名(参数列表);//这里要注意,没有方法体。
}
注意事项

  • 抽象类不能被实例化。
  • 抽象类不必定要包含abstract方法。也就是说,抽象类能够没有abstract方法。
  • 一旦类包含了abstract方法,则这个类必须声明为abstract。
  • 抽象方法不能有函数体。
  • 若是一个类继承了某个抽象类,则它必须实现该抽象类的全部抽象方法(除非本身也声明为抽象类)。

接口

为何有接口,确定是为了方便,也是为了规范,由于只要你要实现我这个接口,就好比实现里面的全部方法。
小案例入门:

<?php
interface iTest{
    public function start();
    public function stop();
}
class camera implements iTest{
    public function start(){
        echo "相机开始工做";
    }
    public function stop(){
        echo "相机中止工做";
    }
}
class phone implements iTest{
    public function start(){
        echo "手机开始工做";
    }
    public function stop(){
        echo "手机中止工做";
    }
}
$c1=new camera();
$c1->start();
$c1->stop();
echo "<br/>";
$p1=new phone();
$p1->start();
$p1->stop();
?>

输出结果:
相机开始工做相机中止工做
手机开始工做手机中止工做
接口细节讨论:
接口比抽象类更抽象,因此,接口更不能被实例化了。
接口中全部的方法都不能有主体。
一个类能够实现多个接口,逗号隔开,变相的完善了类继承(直接继承)的不足。
语法:
public class A implements 接口1,接口2{
}
接口中能够有属性,但必须是常量,默认是public。
接口中的方法必须是public,默认就是public,你想一想,你接口就是给别人用的,你不公开那不是闲的没事嘛。
一个接口不能继承其余的类,可是能够继承别的接口。
语法:
interface 接口名 extends 接口1,接口2{
}

final关键字

若是咱们但愿某个类不被其余类继承,咱们可使用final关键字来修饰这个类。
若是咱们用final来修饰某个类中的方法,则这个方法没法被重写。
final不能用来修饰成员属性。

const概念

当不但愿一个成员变量被修改,但愿该变量的值是固定不变的,这时候能够用const来修饰该成员变量。
基本用法:
const 常量名=值;
访问:
类名::常量名或者接口名::常量名
常量名应该所有大写,而且前面不要有$

PHP如何对错误进行处理

若是咱们尝试打开一个文件:

<?php
$fp=fopen("123.txt","r");
echo '<br/>继续执行';
?>

上面这个代码,打开文件没有作任何验证,这是不对的。
系统会给一个默认警告:
Warning: fopen(123.txt): failed to open stream: No such file or directory in D:\phpstudy_pro\WWW\PHP\error.php on line 2
由于你不知道文件到底在不在,应该先判断。因此将上面的代码进行修改。

<?php
/*$fp=fopen("123.txt","r");
echo '<br/>继续执行';*/
if (!file_exists("abc.txt")){
    echo "文件不存在!!";
    exit();
}else{
    $fp=fopen("abc.txt","r");
        echo "文件打开成功";
        fclose($fp);  //这个必须有!!!

}
?>

输出结果:
文件不存在!!
还有一种简单得处理错误得方式

<?php
if (!file_exists("abc.txt")){
    die("文件不存在!");
}else{
    //文件处理。。。。
}
?>

或者直接:

file_exists("abc.txt") or die("文件不存在!!!!");
#文件存在向下执行,不存在得话执行die()

小结

面向对象基本语法就上面那些,适合入门,但愿对你们有所帮助。

相关文章
相关标签/搜索