大概1年前我總結過,但是那時候太菜,現在我又來總結了。 (本文是總結,不是入門,剛學oop的不適合看,基礎寫的比較省略。)
0.思路
其實oop程式設計發展到現在,知識點很多,而且很雜,比如static就很亂,很容易混淆,我自己這麼分類:
- 1.類和物件(建立類和物件的基本概念)
- 2.層級組織及其衍生(完善和增強類,建立層級,便於組織和複用程式碼)
- 3.其他工具和特性(各個語言都有的攔截器,序列化,php有的魔術方法等雜項)
1.類和物件
1.1類和物件的概念
類和物件是模擬現實中,一群相似的物體,以及他們的抽象模板的關係,在程式設計中便於將方法(功能)和屬性(資料)組織到一起,做到封裝,解耦,提高程式碼的健壯性和複用性。 是一個非常重要的概念發明。
類和物件的基本概念一言難盡,總結:
- 類的由屬性和方法組成
- 屬性和方法可以呼叫
- 類可以例項化(new)出物件
1.2可見性
屬性和方法,都有三種可見性,分別用三個單詞去修飾
在類的外部可訪問 | 子類可訪問 | |
---|---|---|
public | Y | Y |
protected | N | Y |
private | N | N |
1.3static作用域
類由屬性和方法組成,屬性和方法可以呼叫,一個類例項化成多個物件後,每個物件都有自己的屬性和方法,而且呼叫同樣的方法後結果不一定一樣,這叫多型。
然而屬性和方法不一定要例項化後才能呼叫,定義為static的屬性和方法就是直接可以呼叫,無需例項化。
我這麼理解,類是一個特殊的作用域,就當成一個特殊的物件,他持有靜態屬性和靜態方法,物件也可以呼叫靜態方法,但是類不能呼叫非靜態方法。
物件改變靜態屬性的值後,當物件銷燬了,靜態屬性的值還在,因為他屬於類。
1.4自動載入
使用類前必須定義類,當前檔案沒有這個類,就要include,當應用變大,就會很難搞。 就要用到自動載入。
自動載入就是依靠spl_autoload_register()
實現,引數是一個函式,當找不到使用的類的時候,就會將類名作為引數傳入註冊的函式。
<?php
spl_autoload_register(function($class_name){
require_once 'www/app/lib/'.$calss_name.'.php';
})
複製程式碼
1.5建構函式
建構函式是最重要的一個魔術方法,所以提前單說。
2.層級設計及其衍生
發明類和物件的關係已經很厲害了,但是還不夠,為了處理更加複雜的關係,出現了物件層級。
一個物件可以繼承另一個物件,這樣可以更好得組織程式碼和複用程式碼。然後還有抽象類,介面和trait都是oop中組織和複用程式碼的工具。
2.1繼承和過載
- 一個物件可以繼承另一個物件,這樣他就具有了父物件的屬性和方法
- 子物件可以覆蓋父物件的方法
- 呼叫時優先在子物件尋找屬性和方法,沒有就去父類尋找
- 被final修飾的屬性和方法無法過載
2.2抽象類 介面 trait
- 抽象類的程式碼是側重規範性而非實現功能,跟虛擬碼有點像,他定義要實現哪些函式,但是不寫具體實現。
- 介面跟抽象類很像,但是抽象類只能單繼承,介面可以多繼承。
- triat則是程式碼片段的複用。
一言難盡,直接說重點
是否多繼承 | 程式碼是有功能性的還是規範性的 | |
---|---|---|
類 | N | 功能 |
抽象類 | N | 規範(主要)和功能 |
介面 | Y | 規範 |
trait | Y | 功能(主要)和規範 |
注意
- 1.抽象類
- 只要類中有抽象方法,就必須定義為抽象類,同時裡面可以有非抽象方法
- 2.介面
- 介面的的方法都是public的,並且是沒有內容的, 介面可以有常量
- 介面還可以繼承介面,擴充套件
- 3.trait
- 可以有屬性、方法、可以是靜態的,
- 甚至可以寫抽象方法,
- trait還可以包含trait,
- 匯入trait的時候還能更改裡面方法的可見性
2.3各種組合下的呼叫屬性和方法
靜態非靜態*內外部+常量
class ClassA{
//屬性
public $var1 = 1;
//靜態屬性 靜態就是歸類管,不歸物件管
public static $var2 = 2;
//常量 常量不可更改 訪問方式類似靜態
const VAR3 = 3;
//非靜態方法
public function m1(){
echo 'm1 working ......';
echo PHP_EOL;
//引入$this和self
//類內訪問正常屬性 不用加$
echo $this->var1;
echo PHP_EOL;
//類內訪問靜態屬性 要加$ 用類名也行
echo self::$var2;
echo ClassA::$var2;
echo PHP_EOL;
//類內訪問常量 就用類名 self也行
echo ClassA::VAR3;
echo self::VAR3;
echo PHP_EOL;
}
//靜態方法
public static function m2(){
echo 'm2 working ......';
echo PHP_EOL;
//靜態方法不能訪問非靜態屬性或方法,因為沒有例項,$this沒意義
// echo $this->var1; //Fatal error: Uncaught Error: Using $this when not in object context
// echo PHP_EOL;
//類內訪問靜態屬性 要加$ 用類名也行
echo self::$var2;
echo ClassA::$var2;
echo PHP_EOL;
//類內訪問常量 就用類名 self也行
echo ClassA::VAR3;
echo self::VAR3;
echo PHP_EOL;
}
}
//呼叫非靜態方法
(new ClassA())->m1();
//呼叫靜態方法
ClassA::m2();
//測試在類外,呼叫屬性
echo '----------';
echo PHP_EOL;
//非靜態屬性
echo (new ClassA())->var1;
echo PHP_EOL;
//靜態屬性
echo ClassA::$var2;
echo PHP_EOL;
//常量
echo ClassA::VAR3;
echo PHP_EOL;
複製程式碼
非靜態 訪問控制 過載和訪問父級
/*非靜態 訪問控制 過載和訪問父級*/
class ClassA{
//屬性
public $var1 = 1;
protected $var2 = 2;
private $var3 = 3;
//方法
public function m1(){
echo 'm1 working ......';
echo $this->var1;
echo $this->var2;
echo $this->var3;
echo PHP_EOL;
}
protected function m2(){
echo 'm2 working ......';
echo $this->var1;//發現還是4 過載就就覆蓋了 沒有方法那樣的保持層級
echo PHP_EOL;
}
private function m3(){
echo 'm3 working ......';
echo PHP_EOL;
}
}
$obj1 = new ClassA();
$obj1->m1();
//$obj1->m2();//Fatal error: Uncaught Error: Call to protected method ClassA::m2()
//$obj1->m3();//Fatal error: Uncaught Error: Call to private method ClassA::m3()
echo '--------------------';
echo PHP_EOL;
class ClassB extends ClassA{
//屬性 這裡叫過載
public $var1 = 4;
private $var3 = 6;
//方法 過載m1
public function m1(){
echo 'm1 working ......';
echo $this->var1;
//echo parent::var1;//這是錯誤的 只有靜態才能這樣呼叫
echo $this->var2;//沒有過載就呼叫父類的屬性
echo $this->var3;
echo PHP_EOL;
}
//過載m2
public function m2(){
echo 'class B m2 working ......';
// echo parent::$var1; 屬性沒有這種呼叫,如果沒有
echo PHP_EOL;
parent::m2();
}
public function m4(){
echo 'm4 working ......';
// $this->m3();//Fatal error: Uncaught Error: Call to private method ClassA::m3()
}
}
$obj2 = new ClassB();
$obj2->m1();
$obj2->m2();
$obj2->m4();
複製程式碼
靜態 訪問控制 過載和訪問父級
/*靜態 訪問控制 過載和訪問父級*/
class ClassA{
//屬性
public static $var1 = 1;
protected static $var2 = 2;
private static $var3 = 3;
//方法
public static function m1(){
echo 'm1 working ......';
echo self::$var1;
echo self::$var2;
echo self::$var3;
echo PHP_EOL;
self::m2();
}
protected static function m2(){
echo 'm2@A working ......';
echo PHP_EOL;
}
private static function m3(){
echo 'm3 working ......';
echo PHP_EOL;
}
}
echo ClassA::$var1;
echo PHP_EOL;
// echo ClassA::$var2;//Fatal error: Uncaught Error: Cannot access protected property ClassA::$var2
ClassA::m1();
// ClassA::m2();//Fatal error: Uncaught Error: Call to protected method ClassA::m2()
//$obj1->m2();//Fatal error: Uncaught Error: Call to protected method ClassA::m2()
echo '--------------------';
echo PHP_EOL;
class ClassB extends ClassA{
//屬性 過載
public static $var1 = 4;
//方法 過載m1
public static function m1(){
echo 'm1@B working ......';
echo self::$var1;
echo parent::$var1;
echo self::$var2;
// echo self::$var3;//Fatal error: Uncaught Error: Cannot access property ClassB::$var3
echo PHP_EOL;
self::m2();
parent::m2();
}
protected static function m2(){
echo 'm2@B working ......';
echo PHP_EOL;
}
}
ClassB::m1();
複製程式碼
3.其他
- 魔術方法
- 常用8
- __construct() __destruct(),
- __call() __callStatic(),
- __get() __set() __isset() __unset(),
- 不常用4
- __sleep() __wakeup(),
- __toString(),
- __invoke(),
- 我不會3
- __set_state(),
- __clone()
- __debugInfo()
- 常用8
- 相關函式
- get_class()
- instanceof ()
- ::class
- 物件操作
- 物件序列化
- 物件比較
- 物件遍歷
- 物件複製
- 其他
- 型別約束
- 應用的理解
- 匿名類
- static延遲繫結
魔術方法
1 | __construct() __destruct() | 構建和析構方法,在建立和銷燬物件時呼叫 |
2 | __call() __callStatic() | 呼叫不存在的方法時呼叫 |
3 | __set(),__get(),__isset(),__unset() | 攔截器系列 |
4 | __sleep() __wakeup() | 在serialize()unserialize()時呼叫,可以額外加一些邏輯 |
5 | __toString() | 在echo時呼叫,不定義就echo會error |
6 | __invoke() | 當以函式方式呼叫物件時啟動 |
7 | __set_state() __debugInfo() | 我也不知道有啥用 |
8 | __clone() | 自定義如何複製物件 |
/*魔術方法2*/
/*sleep wakeup invoke toString*/
class B{
function __construct($name,$age){
$this->name = $name;
$this->age = $age;
}
function __sleep(){
echo $this->name.'is sleep';
echo PHP_EOL;
return array('name','age');
}
function __wakeup(){
echo $this->name.'is wakeup';
echo PHP_EOL;
}
function __toString(){
return 'i am '.$this->name;
}
function invoke($arg){
echo 'what are u doing';
echo PHP_EOL;
}
}
//sleep
$obj = new B('hahaha','19');
$data = serialize($obj);
var_dump($data);
$test = unserialize($data);
//tostring
echo $test;
//invoke
$test('111');
/*魔術方法1*/
/*構建 析構 call get*/
class A{
public $name = 'default';
public $data = [];
//構建方法
public function __construct($name=NULL){
if($name){
$this->name = $name;
}
echo '__construct runing.....';
echo PHP_EOL;
}
//析構方法
public function __destruct(){
echo '__destruct runing.....';
echo PHP_EOL;
}
//攔截器
public function __call($name,$arguments){
echo 'you are try to run method'.$name.'but its not exists';
echo PHP_EOL;
}
public static function __callStatic($name,$arguments){
echo 'you are try to run static method'.$name.'but its not exists';
echo PHP_EOL;
}
//獲取器設定器
public function __set($name,$value){
$this->data[$name] = $value;
echo 'setter runing ';
echo PHP_EOL;
}
public function __get($name){
if(isset($this->data[$name])){
return $this->data[$name];
}else{
return 'empty';
}
}
public function __isset($name){
if(isset($this->data[$name])){
echo 'Y';
}else{
echo 'N';
}
echo PHP_EOL;
}
public function __unset($name){
if(isset($this->data[$name])){
unset($this->data[$name]);
}
echo 'done ';
echo PHP_EOL;
}
}
$obj = new A('xyz');
echo $obj->name;
echo PHP_EOL;
$obj->tag = 'mmmm';
echo $obj->tag;
echo PHP_EOL;
echo $obj->bag;
echo PHP_EOL;
echo $obj->run();
echo PHP_EOL;
echo A::fly();
echo PHP_EOL;
複製程式碼
static有兩種意思
- 1.區分作用域在類還是物件,靜態變數是類作用域
- 2.有“最初呼叫類”的意思,主要2個用法,
- new static,
- 和延遲靜態繫結static::funct()