1.2 面向對象介紹
面向對象是一個編程思想。編程思想有面向過程和面向對象
面向過程:編程思路集中的是過程上
面向對象:編程思路集中在參與的對象
以去飯館吃飯為例:
? 面向過程:點菜——做菜——上菜——吃飯——結賬——收拾
? 面向對象:服務員,廚師,客人
- 多人合作方便
- 減少代碼冗余,靈活性高
- 代碼的可重用性發揮到極致
- 可擴展性強
多學一招:
OOP:面向對象編程(Object Oriented Programming,面向對象編程)
OOA: 面向對象分析(Object-Oriented Analysis,OOA)
OOD: 面向對象設計(Object-Oriented Design,OOD)
1.3 類和對象
1、對象是具體存在的事物,對象是由屬性(變量)和方法(函數)組成的
2、類是具有相同屬性和行為的一組對象的集合
分析:做菜動作——廚師對象——廚師類
結論:我們在開發的時候,先寫類,通過類創建對象,然后調用對象的屬性和方法實現功能。 類——對象——調用成員
注意:一個類可以創建多個對象
小結:
1、對象是由屬性和方法組成的
2、類是所有對象的相同屬性和方法的集合
3、在開發的時候先寫類,通過類創建對象,通過對象調用方法和屬性
4、一個類可以創建多個對象
?
1.4 在PHP中實現類和對象
語法:
class 類名{//屬性//方法//常量
}
類是由屬性、方法、常量組成的,也可以說
類成員有:屬性、方法、常量
類名的命名規則:
- 以字母、下劃線開頭,后面跟的是字母、數字、下劃線
- 不能用PHP關鍵字做類名
- 類名不區分大小寫(變量名區分,關鍵字、類名不區分大小寫)
- 類名用帕斯卡命名法(大駝峰 單詞的首字母大寫)
<?php
class Student {
}
通過new關鍵字來實例化對象。
<?php
//定義類
class Student {}
//實例化對象
$stu1=new Student();
$stu2=new Student; //小括號可以省略
var_dump($stu1,$stu2); //object(Student)#1 (0) { } object(Student)#2 (0) { }
注意:對象的傳遞是地址傳遞
相等:結構和保存的值一樣就相等
全等:指向同一個對象才是全等。
<?php
//定義類
class Student {}
//實例化對象
$stu1=new Student();
$stu2=new Student;
$stu3=$stu2; //對象傳遞的是地址
//var_dump($stu1,$stu2,$stu3); //object(Student)#1 (0) { } object(Student)#2 (0) { } object(Student)#2 (0) { }
//對象比較
var_dump($stu1==$stu2); //bool(true) ,比較對象的結構
echo '<br>';
var_dump($stu1===$stu2); //bool(false) $stu1和$stu2是否是同一個對象
echo '<br>';
var_dump($stu2===$stu3); //bool(true) $stu2和$stu3是同一個對象
1.5 屬性
屬性本質就是變量
通過->
調用對象的成員 對象名->屬性名 對象名->方法名()
<?php
//定義類
class Student {public $name; //屬性public $add='地址不詳'; //屬性
}
//實例化對象
$stu=new Student();
//print_r($stu); //Student Object ( [name] => [add] => 地址不詳 )
//操作屬性
//1、給屬性賦值
$stu->name='tom';
$stu->add='北京';//2、獲取屬性的值
echo '姓名:'.$stu->name,'<br>'; //姓名:tom
echo '地址:'.$stu->add,'<br>'; //地址:北京//3、添加屬性
$stu->age=20;
print_r($stu); //Student Object ( [name] => tom [add] => 北京 [age] => 20 )
echo '<br>';
//4、刪除屬性
unset($stu->add);
print_r($stu); //Student Object ( [name] => tom [age] => 20 )
1.6 方法
方法的本質就是函數
<?php
class Student {//定義方法public function show() {echo '這是show方法<br>';}//public可以省略,如果省略,默認就是publicfunction test() {echo '這是test方法<br>';}
}
$stu=new Student;
$stu->show(); //調用方法
$stu->test();
多學一招:
1、方法前面public是可以省略的,如果省略,默認就是public的。
2、屬性前面的public不能省略
1.7 訪問修飾符
用來控制成員的訪問權限
修飾符 | 描述 |
---|---|
public(公有的) | 在類的內部和外部都能訪問 |
private(私有的) | 只能在類的內部訪問 |
protected(受保護的) | 在整個繼承鏈上訪問 |
**多學一招:**一般來說,屬性都用私有的,通過公有的方法對私有的屬性進行賦值和取值。
作用:保證數據的合法性
<?php
//訪問修飾符
class Student {private $name; //私有屬性private $sex; //私有屬性//通過公有的方法對私有的屬性進行賦值public function setInfo($name,$sex) {if($sex!='男' && $sex!='女'){echo '性別必須是男或女';exit;}$this->name=$name; //$this表示當前對象$this->sex=$sex;}//顯示信息public function getInfo() {echo '姓名:'.$this->name,'<br>';echo '性別:'.$this->sex,'<br>';}
}
//實例化
$stu=new Student;
$stu->setInfo('tom','男');
$stu->getInfo();
echo '<hr>';
$stu2=new Student;
$stu2->setInfo('berry','女');
$stu2->getInfo();
提示:$this表示調用當前方法的對象
運行結果
1.8 類和對象在內存中的分布
- 對象的本質是一個復雜的變量
- 類的本質是一個自定義的復雜數據類型
- 棧區:運行速度快,體積小,保存基本類型
- 堆區:運行速度稍慢,體積大,保存復雜類型
- 實例化的過程就是分配內存空間的過程
- 對象保存在堆區,將堆區的地址保存到棧區。
分析如下代碼的結構
<?php
class Student {public $name;public $sex;public function show() {}
}$stu1=new Student;
$stu2=new Student;$stu1->show();
示意圖
1.9 封裝
封裝就是有選擇性的提供數據
通過訪問修飾符來實現封裝
1.10 構造方法
構造方法也叫構造函數,當實例化對象的時候自動執行。
語法:
function __construct(){
}
注意:前面是兩個下劃線
例題
<?php
class Student {public function __construct() {echo '這是構造方法<br>';}
}
new Student(); //這是構造方法
new Student(); //這是構造方法
注意:在其他語言里,與類名同名的函數是構造函數,在PHP中不允許這種寫法。
class Student {//和類名同名的方法是構造方法,PHP中不建議使用public function Student() {echo '這是構造方法<br>';}
}
/*
Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP; Student has a deprecated constructor in F:\wamp\www\6-demo.php on line 2
這是構造方法
*/
<?php
class Student {private $name;private $sex;//構造函數初始化成員變量public function __construct($name,$sex) {$this->name=$name;$this->sex=$sex;}//顯示信息public function show() {echo "姓名:{$this->name}<br>";echo "性別:{$this->sex}<br>";}
}
//實例化
$stu=new Student('tom','男');
$stu->show();
//運行結果
/*
姓名:tom
性別:男
*/
注意:構造函數可以帶參數,但不能有return。
.11 析構方法
當對象銷毀的時候自動調用
語法
function __destruct(){
}
腳下留心:析構函數不可以帶參數
例題
<?php
class Student {private $name;//構造方法public function __construct($name) {$this->name=$name;echo "{$name}出生了<br>";}//析構方法public function __destruct() {echo "{$this->name}銷毀了<br>";}
}
//測試
$stu1=new Student('tom');
$stu2=new Student('berry');
$stu3=new Student('ketty');
echo '<hr>';
運行結果
計算機內存管理方式:先進先出,先進后出
先進先出的內存管理方式一般用在業務邏輯中,比如秒殺、購票等等
先進后出是計算機的默認內存管理方式
思考題1
<?php
class Student {private $name;//構造方法public function __construct($name) {$this->name=$name;echo "{$name}出生了<br>";}//析構方法public function __destruct() {echo "{$this->name}銷毀了<br>";}
}
//測試
$stu1=new Student('tom');
$stu2=new Student('berry');
$stu3=new Student('ketty');
unset($stu2);
echo '<hr>';
/*
tom出生了
berry出生了
ketty出生了
berry銷毀了ketty銷毀了
tom銷毀了
*/
思考題2
<?php
class Student {private $name;//構造方法public function __construct($name) {$this->name=$name;echo "{$name}出生了<br>";}//析構方法public function __destruct() {echo "{$this->name}銷毀了<br>";}
}
//測試
new Student('tom');
new Student('berry');
new Student('ketty');
/*
tom出生了
tom銷毀了
berry出生了
berry銷毀了
ketty出生了
ketty銷毀了
*/
思考題3
<?php
class Student {private $name;//構造方法public function __construct($name) {$this->name=$name;echo "{$name}出生了<br>";}//析構方法public function __destruct() {echo "{$this->name}銷毀了<br>";}
}
//測試
$stu=new Student('tom');
$stu=new Student('berry');
$stu=new Student('ketty');
/*
tom出生了
berry出生了
tom銷毀了
ketty出生了
berry銷毀了
ketty銷毀了
*/
1.12 繼承
- 繼承使得代碼具有層次結構
- 子類繼承了父類的屬性和方法,實現了代碼的可重用性。
- 使用extends關鍵字實現繼承
- 父類和子類是相對的
語法
class 子類 extends 父類{
}
例題
<?php
//父類
class Person {public function show() {echo '這是人類<br>';}
}
//子類繼承父類
class Student extends Person {
}
//測試
$stu=new Student;
$stu->show(); //這是人類
執行過程:
第一步:在Student類中查找show(),如果找到就調用,找不到就到父類中查找
第二步:在Person類中查詢show()
<?php
//父類
class Person {public function show() {echo '這是人類<br>';}
}
//子類
class Student extends Person {public function test() {//方法一;/*$person=new Person();$person->show(); //這是人類*///方法二$this->show(); //這是人類}
}
//測試
$stu=new Student;
$stu->test();
小結:
1、方法一:通過實例化父類調用父類的成員
2、方法二:通過$this關鍵字調用父類的成員
protected:受保護的,在整個繼承鏈上使用
例題:
//例題一:
<?php
class A {protected $num=10; //在整個繼承鏈上訪問
}
class B extends A { public function getNum() {echo $this->num;}
}
//測試
$obj=new B(); //整個繼承鏈上有A和B
$obj->getNum(); //10//例題二:
<?php
class A {public function getNum() {echo $this->num;}
}
class B extends A {protected $num=10;
}
//測試
$obj=new B(); //整個繼承鏈上有A和B
$obj->getNum(); //10//例題三:
<?php
class A {public function getNum() {echo $this->num;}
}
class B extends A {protected $num=10;
}
//測試
$obj=new A(); //整個繼承鏈上只有A
$obj->getNum(); //Notice: Undefined property: A::$num
規則:
1、如果子類有構造函數就調用子類的,如果子類沒有就調用父類的構造函數。2、子類的構造函數調用后,默認不再調用父類的構造函數
通過類名調用父類的構造函數
類名::__construct()
例題
<?php
class Person {//父類的構造函數public function __construct() {echo '這是父類<br>';}
}
class Student extends Person {//子類的構造函數public function __construct() {Person::__construct(); //通過父類的名字調用父類的構造函數parent::__construct(); //parent表示父類的名字echo '這是子類<br>';}
}
//測試
new Student();
注意:parent關鍵字表示父類的名字,可以降低程序的耦合性
例題:給父類傳遞參數
<?php
class Person {protected $name;protected $sex;//父類的構造函數public function __construct($name,$sex) {$this->name=$name;$this->sex=$sex;}
}
class Student extends Person {private $score;//子類的構造函數public function __construct($name,$sex,$score) {parent::__construct($name,$sex); //調用父類構造函數并傳遞參數$this->score=$score;}//顯示信息public function getInfo() {echo "姓名:{$this->name}<br>";echo "性別:{$this->sex}<br>";echo "成績:{$this->score}";}
}
//測試
$stu=new Student('tom','男',88);
$stu->getInfo();
/*
姓名:tom
性別:男
成績:88
*/
t h i s 表 示 當 前 對 象 的 引 用 , 也 就 是 是 或 this表示當前對象的引用,也就是是或this表示當前對象的引用,也就是是或this保存的當前對象的地址
<?php
class A {public function __construct() {var_dump($this);}
}
class B extends A {}
new A(); //object(A)#1 (0) { }
echo '<br>';
new B(); //object(B)#1 (0) { }
PHP不允許多重繼承,因為多重繼承容易產生二義性
如何實現C繼承A和B,使用繼承鏈
1.13?多態
多態:多種形態。
多態分為兩種:方法重寫和方法重載
子類重寫了父類的同名的方法
<?php
//父類
class Person {public function show() {echo '這是父類<br>';}
}
//子類
class Student extends Person {//子類重寫了父類的同名方法public function show() {echo '這是子類<br>';}
}
//測試
$stu=new Student;
$stu->show(); //這是子類
注意事項:
- 子類的方法必須和父類的方法同名
- 參數個數要一致
- 子類修飾的不能比父類更加嚴格
在同一個類中,有多個同名的函數,通過參數的不同來區分不同的方法,稱為方法重載
注意:PHP不支持方法重載,但是PHP可以通過其他方法來模擬方法重載。
1.14 私有屬性繼承和重寫
私有屬性可以繼承但不能重寫。
<?php
class A {private $name='PHP';public function showA() {//var_dump($this); //object(B)#1 (2) { ["name":"B":private]=> string(4) "Java" ["name":"A":private]=> string(3) "PHP" } echo $this->name,'<br>'; //PHP}
}
class B extends A {private $name='Java';public function showB() {//var_dump($this); //object(B)#1 (2) { ["name":"B":private]=> string(4) "Java" ["name":"A":private]=> string(3) "PHP" } echo $this->name,'<br>'; //Java}
}
$obj=new B();
$obj->showA();
$obj->showB();
/*分析:
showA()和showB()中的$this都表示B的對象,B中繼承了A的私有屬性,所以B中有兩個$name.
在showA()中只能訪問A中的$name,不能訪問B中的$name
在showB()中只能訪問B中的$name,不能訪問A中的$name
*/
練習一
<?php
class A {protected $name='tom'; public function showA() {echo $this->name,'<br>';}
}
class B extends A {public $name='berry';public function showB() {echo $this->name,'<br>';}
}
//測試
$obj=new B();
$obj->showA(); //berry
$obj->showB(); //berry/*
分析:B中將A的$name重寫,所以$obj中只有一個$name,($name='berry'),不管$this在哪個方法中訪問,就只能訪問這個$name
*/
練習二
<?php
class A {private $name='tom'; public function showA() {echo $this->name,'<br>';}
}
class B extends A {public $name='berry';public function showB() {echo $this->name,'<br>';}
}
//測試
$obj=new B();
$obj->showA(); //tom
$obj->showB(); //berry
/*
分析:
$obj中有兩個$name,一個是私有的,一個是公有的
在showA()中既能訪問私有的$name,也能訪問公有的$name,但是私有的比公有的權限高,所以輸出tom
在showB()中不能訪問私有的$name,只能訪問公有的$name,所以輸出berry
*/
1.15 方法修飾符
方法修飾符有:static、final、abstract
- static修飾的屬性叫靜態屬性、static修飾的方法叫靜態方法
- 靜態成員加載類的時候分配空間,程序執行完畢后銷毀
- 靜態成員在內存中就一份。
- 調用語法 類名::屬性 類名::方法名()
<?php
class Person {public static $add='北京'; // 修飾符之間沒有順序static public function show() {echo '這是一個靜態的方法<br>';}
}
echo Person::$add,'<br>'; //北京
Person::show(); //這是一個靜態的方法
練習:統計在線人數
<?php
class Student {private static $num=0; //靜態變量,在內存中就一份public function __construct() {self::$num++; //self表示所在類的類名}public function __destruct() {self::$num--;}public function show() {echo '總人數是:'.self::$num,'<br>';}
}
//測試
$stu1=new Student;
$stu2=new Student;
$stu3=new Student;
$stu2->show(); //總人數是:3
unset($stu2);
$stu3->show(); //總人數是:2
**注意:**self表示所在類的類名,使用self降低耦合性
靜態成員也可以被繼承
<?php
class Person {public static $add='中國';public static function show() {echo '這是人類<br>';}
}
//繼承
class Student extends Person {
}
//測試
echo Student::$add,'<br>'; //中國 通過子類名稱訪問父類的靜態成員
Student::show(); //這是人類
靜態延時綁定
static表示當前對象所屬的類
<?php
class Person {public static $type='人類';public function show1() {//var_dump($this); //object(Student)#1 (0) { } //echo self::$type,'<br>'; //人類echo static::$type,'<br>'; //學生 延時綁定}
}
class Student extends Person {public static $type='學生';public function show2() {//var_dump($this); //object(Student)#1 (0) { } //echo self::$type,'<br>'; //學生echo static::$type,'<br>'; //學生}
}
//測試
$obj=new Student();
$obj->show1();
$obj->show2();
小結:
1、static在內存中就一份,在類加載的時候分配空間
2、如果有多個修飾符,修飾符之間是沒有順序的
3、self表示所在類的類名
4、static表示當前對象所屬的類
5、static有兩個作用,第一表示靜態的,第二表示類名
1.15.2 final【最終的】
final修飾的方法不能被重寫
final修飾的類不能被繼承
作用
1、如果一個類確定不被繼承,一個方法確定不會被重寫,用final修飾可以提高執行效率。
2、如果一個方法不允許被其他類重寫,可以用final修飾。
1.15.3 abstract【抽象的】
- abstract修飾的方法是抽象方法,修飾的類是抽象類
- 只有方法的聲明沒有方法的實現稱為抽象方法
- 一個類中只要有一個方法是抽象方法,這個類必須是抽象類。
- 抽象類的特點是不能被實例化
- 子類繼承了抽象類,就必須重新實現父類的所有的抽象方法,否則不允許實例化
- 類中沒有抽象方法也可以聲明成抽象類,用來阻止類的實例化
例題
<?php
//抽象類
abstract class Person {public abstract function setInfo(); //抽象方法public function getInfo() {echo '獲取信息<br>';}
}
//繼承
class Student extends Person {//重寫實現父類的抽象方法public function setInfo() {echo '重新實現父類的抽象方法<br>';}
}
//測試
$stu=new Student;
$stu->setInfo(); //重新實現父類的抽象方法
$stu->getInfo(); //獲取信息
抽象類的作用:
1定義命名規范
2、阻止實例化,如果一個類中所有的方法都是靜態方法,這時候沒有必要去實例化,可以通過abstract來阻止來的實例化。
1.16 類常量
類常量是const常量
<?php
class Student {//public const ADD; //7.1以后才支持訪問修飾符const ADD='地址不詳';
}
echo Student::ADD;
問題:define常量和const常量的區別?
答:const常量可以做類成員,define常量不可以做類成員。
問題:常量和靜態的屬性的區別?
答:相同點:都在加載類的時候分配空間
? 不同點:常量的值不可以更改,靜態屬性的值可以更改
1.17 接口(interface)
- 如果一個類中所有的方法是都是抽象方法,那么這個抽象類可以聲明成接口
- 接口是一個特殊的抽象類,接口中只能有抽象方法和常量
- 接口中的抽象方法只能是public,可以省略,默認也是public的
- 通過implements關鍵字來實現接口
- 不能使用abstract和final來修飾接口中的抽象方法。
<?php
//聲明接口
interface IPerson {const ADD='中國';function fun1();function fun2();
}
//接口實現
class Student implements IPerson {public function fun1() {}public function fun2() {}
}
//訪問接口中的常量
echo IPerson::ADD;
類不允許多重繼承,但是接口允許多重實現。
<?php
interface IPic1 {function fun1();
}
interface IPic2 {function fun2();
}
//接口允許多重實現
class Student implements IPic1,IPic2 {public function fun1() {}public function fun2() {}
}
注意:
1、在接口的多重實現中,如果有同名的方法,只要實現一次即可
2、類可以繼承的同時實現接口
class Student extends Person implements IPIc1,IPic1{}
1.18 匿名類
這是了解的內容,PHP7.0支持
<?php
$stu=new class {public $name='tom';public function __construct() {echo '構造函數<br>';}
};
echo $stu->name;
/*運行結果;
構造函數
tom
*/
小結:
1、如果類只被實例化一次就可以使用匿名類
2、好處,在執行的過程中,類不占用空間
1.19 方法綁定
這是了解的內容,PHP7.0支持
作用:將方法綁定到對象上,并調用
語法:
閉包->call(對象):將閉包綁定到對象上,并調用
在PHP中匿名函數稱為閉包
例題
<?php
$lang='en';
//類
class Student{
}
//匿名函數
if($lang=='ch'){$fun=function(){echo '我是一名學生';};
}else{$fun=function(){echo 'i am a studnet';};
}
//綁定
$stu=new Student;
$fun->call($stu); //i am a studnet
1.20 自動加載類
在項目開發中,因為一個文件中只能寫一個類,并且在執行過程中會有很多的類參與,如果一個一個的加載很麻煩,所以,就需要一個機制實現在PHP執行過程中自動加載需要的類。
- 一個文件中只能放一個類(必須)
- 文件名和類名同名(必須)
- 類文件以.class.php結尾(不是必須)
1、創建Goods.class.php
頁面
<?php
//商品類
abstract class Goods {protected $name;final public function setName($name) {$this->name=$name; }public abstract function getName();
}
2、創建Book.class.php頁面
<?php
//圖書類
class Book extends Goods {public function getName() {echo "《{$this->name}》<br>";}
}
3、創建Phone.class.php頁面
<?php
//電話類
class Phone extends Goods {public function getName() {echo $this->name,'<br>';}
}
4、在PHP頁面上加載類文件
<?php
require './Goods.class.php'; //手動加載類文件
require './Book.class.php'; //手動加載類文件
require './Phone.class.php'; //手動加載類文件
//測試
$book=new Book();
$book->setName('面向對象編程');
$phone=new Phone();
$phone->setName('蘋果6s');
$book->getName();
$phone->getName();
運行結果
當缺少類的時候自動的調用__autoload()
函數,并且將缺少的類名作為參數傳遞給__autoload()
。
<?php
/*
*作用:自動加載類
*@param $class_name string 缺少的類名
*/
function __autoload($class_name) {require "./{$class_name}.class.php";
}
//測試
$book=new Book();
$book->setName('面向對象編程');
$phone=new Phone();
$phone->setName('蘋果6s');
$book->getName();
$phone->getName();
注意:__autoload()函數在PHP7.2以后就不支持了。
通過spl_autoload_register()注冊__autoload()函數
<?php
//方法一:
/*
//加載類函數
function loadClass($class_name) {require "./{$class_name}.class.php";
}
//注冊加載類函數
spl_autoload_register('loadClass');
*///方法二:
spl_autoload_register(function($class_name){require "./{$class_name}.class.php";
});//測試
$book=new Book();
$book->setName('面向對象編程');
$phone=new Phone();
$phone->setName('蘋果6s');
$book->getName();
$phone->getName();
1、spl_autoload_register()可以注冊多個自動加載函數
<?php
function load1($class) {require "./{$class}.class.php";
}
function load2($class) {require "./{$class}.php";
}
function load3($class) {require "./{$class}.fun.php";
}
spl_autoload_register('load1');
spl_autoload_register('load2');
spl_autoload_register('load3');
2、PHP5.1以后就開始支持此函數。
將類名和文件地址做一個映射,組成一個關聯數組。
$map=array(//類名 => 類文件地址'Goods' => './aa/Goods.class.php','Book' => './bb/Book.class.php','Phone' => './cc/Phone.class.php'
);
代碼如下
<?php
spl_autoload_register(function($class_name){//類名和文件地址映射成一個關聯數組$map=array('Goods' => './aa/Goods.class.php','Book' => './bb/Book.class.php','Phone' => './cc/Phone.class.php');//在映射數組中找到就包含if(isset($map[$class_name]))require $map[$class_name];
});
//測試
$book=new Book();
$book->setName('面向對象編程');
$phone=new Phone();
$phone->setName('蘋果6s');
$book->getName();
$phone->getName();
在項目中,絕大部分都是規則存儲的,不規則的比較少。
?
?
?
?
?
?
?
?
?
?
?
?
?
?