PHP5相对于PHP4一个非常大的改变就是OOP,甚至可以说几乎所有OOP的内容在PHP5中都被改变了。
【基本概念】
1.问:OOP与面向过程编程最大的区别?
答:在OOP中,数据和代码都是被绑定到一个实体当中(常量及静态常量变量除外,因为它们是被绑定到类上的),这个实体就叫做对象。OOP把一个问题切割成很多的对象,每个对象都是一个问题的实体,它们之间相互独立,拥有各自的属性和方法。对其他类来说,那些公共方法可以看成这个类的一个接口。
2.对象的创建--new关键字
注意点:1.与C++等语言不同,PHP在创建子对象的时候并不会先创建父对象,即不会调用父类的构造函数
2.与PHP4不同,PHP5中用new关键字创建对象返回的是一个句柄(可以理解成表示该对象的ID),对属性的修改即为直接对内存地址中的值进行操作。(PHP4中new创建对象的返回值为对象的一份拷贝)
1 class Person{ 2 public $name; 3 function __construct($name=""){ 4 $this->name = $name; 5 print "成功创建一个Person对象"; 6 } 7 } 8 class Student extends Person{ 9 public $id; 10 function __construct($id="",$name=""){ 11 $this->id = $id; 12 $this->name = $name; 13 print "成功创建一个Student对象"; 14 } 15 } 16 17 $Yao = new Student("","Yao"); 18 var_dump($Yao);
输出如下:
成功创建一个Student对象 object(Student)#1 (2) { ["id"]=> string(0) "" ["name"]=> string(3) "Yao" }
3.静态和常量
① 和常规属性不同,静态属性是属于类本身而不属于任何类的实例。在类的内部,你可以通过特殊的类名self(表示一个方法所属类的缩写)访问它,在类的外面,你可以通过类名::method()直接访问;静态方法亦是如此。常量用const定义,访问方法同静态属性。看下面这个例子:
1 class Person{ 2 static function sayHello(){ 3 print "Hello "; 4 self::sayName(); 5 } 6 static function sayName(){ 7 print "Jim"; 8 } 9 } 10 Person::sayHello();
结果如下:
Hello Jim
因为静态方法不被限制到任何特定的对象,所以你可以不创建对象实例就可以通过class_name::method()语法调用它。同时,你也可以在任何一个实例中通过$this->method()访问这些静态方法,但是在这里$this是没有定义的,如果把self::sayName()改成$this->sayName(),则会提示
Hello Fatal error: Using $this when not in object context in...
②静态变量还有个特点就是:可以被初始化,但是初始化只能在第一次static申明的时候发生,而且只能在申明它的函数中访问到!
4.多态
同一个操作用于不同类的实例,将产生不同的执行结果。或者说不同的对象收到相同的消息时,将收到不同的结果。(一大优点是能省去很多的条件控制语句--如if,从而编写出可扩展的健壮的代码)。看下面的例子:
1 class Cat{ 2 function miao(){ 3 print "miao"; 4 } 5 } 6 class Dog{ 7 function wuff(){ 8 print "wuff"; 9 } 10 } 11 function printTheRightSound($obj){ 12 if( $obj instanceof Dog ){ 13 $obj->wuff(); 14 }else if( $obj instanceof Cat ){ 15 $obj->miao(); 16 }else{ 17 print "Error: unknown kind of object"; 18 } 19 print "\n"; 20 } 21 22 printTheRightSound(new Dog()); 23 printTheRightSound(new Cat());
很容易看出这个例子是不能扩展的。假如要再增加3种动物,你不得不在printTheRightSound()中增加3个else if模块以便检查是哪一种动物。
而多态可以用继承来解决这个问题,代码如下:
1 abstract class Animal{ 2 abstract function makeSound(); 3 } 4 class Dog extends Animal{ 5 function makeSound(){ 6 print "wuff"; 7 } 8 } 9 class Cat extends Animal{ 10 function makeSound(){ 11 print "miao"; 12 } 13 } 14 function printTheRightSound($obj){ 15 if($obj instanceof Animal){ 16 $obj->makeSound(); 17 }else{ 18 print "Error: unknown kind of object"; 19 } 20 print "\n"; 21 } 22 23 printTheRightSound(new Dog()); 24 printTheRightSound(new Cat());
5. 抽象类和接口
上段代码使用abstract关键字定义父类能使你不需要在Animal的makesound()方法中提供没有意义的函数执行体。
区别:抽象类不是一个具备完整功能的类而只是为了被继承。抽象类中可以包含成员变量及具体的方法,只要有一个方法被定义成abstract,整个类就需要被申明成abstract。而接口是用来弥补PHP中类之间无法实现多重继承这一问题的,接口默认是public的,不能添加访问修饰符,里面只能包含常量以及函数申明(不需要申明成abstract,默认就是这货),任何一个"implements"该接口的类必须实现接口中的所有方法(除非是抽象类)--可以用instanceof进行判断,接口与接口之间允许多重继承,但是接口A与B之间不允许有同名的方法及常量。
6.instanceof
上面的instanceof是一个二元逻辑运算符,代替PHP4中的is_a()内置函数,同时,这个函数还可以用来检查类是否所继承的接口。代码如下:
1 interface drink{ 2 function heshui(); 3 } 4 interface sleep{ 5 function shuijiao(); 6 } 7 8 abstract class Animal{ 9 abstract function makeSound(); 10 } 11 class Dog extends Animal implements drink{ 12 public $name = "Dog"; 13 function makeSound(){ 14 print "wuff"; 15 } 16 function heshui(){ 17 print $this->name." drink"; 18 } 19 } 20 class Cat extends Animal implements sleep{ 21 static $name = "Cat"; 22 function makeSound(){ 23 print "miao"; 24 } 25 function shuijiao(){ 26 print self::$name." sleep"; 27 } 28 } 29 function printTheRightSound($obj){ 30 if($obj instanceof sleep){ 31 $obj->shuijiao(); 32 }else{ 33 print $obj->name . " has not implements the interface"; 34 } 35 print "\n"; 36 } 37 38 printTheRightSound(new Dog()); 39 printTheRightSound(new Cat());
结果:
Dog has not implements the interface Cat sleep
注:这里可以发现,用$this->访问属性的时候,不需要$符号,而用self::访问的时候,还是需要$符号的。。。我的理解是,只有属于对象的属性是不需要加上$符号的,这个不知道对不对。。。
7.final关键字
写在函数前面,该函数在继承时不能被重载;写在类前面,该类不能被继承。
8.异常处理(try-throw-catch)
处理过程:当try{}结构中抛出一个异常(异常只能是一个继承自Exception的一个object,不能是字符串或者整形等),首先达到第一个catch()并执行instanceof比较,如果返回true,PHP进入catch块,如果返回false,PHP检查下一个catch语句,一旦进入一个catch语句,下面的catch语句将不再进入。抛出异常后,try块中之后的语句将不再执行。代码如下:
1 class NullHandleException extends Exception{ 2 function __construct($message){ 3 parent::__construct($message); 4 } 5 } 6 function printObject($obj){ 7 if($obj == NULL){ 8 throw new NullHandleException("printObject received NULL object"); 9 } 10 print $obj."\n"; 11 } 12 class MyName{ 13 function __construct($name){ 14 $this->name = $name; 15 } 16 function __toString(){ 17 return $this->name; 18 } 19 private $name; 20 } 21 try{ 22 printObject(new MyName("Bill")); 23 printObject(NULL); 24 printObject(new MyName("Tom")); 25 }catch(NullHandleException $e){ 26 print "first_level:" . $e->getMessage(); 27 }catch(Exception $e){ 28 print "second_level" . $e->getMessage(); 29 }
输出:
Bill first_level:printObject received NULL object
__toString()用来定义new关键字的返回值(未定义的话,返回object(Student)#1),但现在只能被print和echo调用