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()

小結

面向對象基本語法就上面那些,適合入門,但願對你們有所幫助。

相關文章
相關標籤/搜索