面向对象编程的确缺点:由于面向对象的代码在执行的过程中比面向过程的代码繁琐,所以面向对象的执行效率略低于面向过程,不过这种低是几乎可以忽略的。
封装,继承,多态。
class className{
public$a;
publicfunction abc(){
...
}
}
无论是类属性还是类方法,前面都是要有关键字修饰的,private、protect、public。
类属性与普通属性的一个很大的区别是,类属性在声明时赋予初始值,且这个初始值要么为空要么为常数,不能是一个表达式,public $a = 1+2;这样写就是不对的。
除了声明类属性之外的其它代码都必须放在类方法中。
我们声明的类不能跟php系统的类重名。
unset($object);删除一个类。
为了编程的规范和方便,类最好单独分开,放在以类名为文件名的文件里,用的时候require一下。
类名不区分大小写,对象名区分大小写,顺便说一句,数组里面的键名是区分大小写的。
函数名不区分大小写。
class className{
public$a = 0;
publicfunction abc(){
$this->a= 1;
}
}
方法中$this->a = 1可以修改类的属性$a。
类方法之间是可以互相调用的。
构造函数__construct是在实例化对象时最先调用的函数。
析构函数__destruct是在销毁对象时调用,不能接收任何参数。
魔术函数__autoload():
PHP在魔术函数__autoload()方法出现以前,如果你要在一个程序文件中实例化100个对象,那么你必须用include或者require包含进来100个类文件,或者你把这100个类定义在同一个类文件中——相信这个文件一定会非常大。
但是__autoload()方法出来了,以后就不必为此大伤脑筋了,这个类会在你实例化对象之前自动加载制定的文件。
下边我们通过一个例子来看一下,具体的使用方法,并在稍后说明使用PHP魔术函数__autoload应该注意些什么。
代码如下 | 复制代码 |
//定义一个类ClassA,文件名为ClassA.php //定义一个类ClassB,文件名为ClassB.php,ClassB继承ClassA |
定义两个测试用的类之后,我们来编写一个含有__autoload()方法的PHP运行程序文件如下:
代码如下 | 复制代码 |
function __autoload($classname){ |
这个文件的运行是一点问题都没有的,可见autoload是多么的好用啊。
__autoload函数是在需要引用其它类的那个文件里面编写的单独的函数,不是放在类中编写的,注意不要混淆,而且还要注意,类名和文件名必须一致。
instanceof关键词可以用来查看一个特别的对象是不是继承自某个类,也可以用来查看接口的继承关系。
if ($obj instanceof $father) …
注意,类名不放在引号里。
当子类与父类都有构造函数时,php优先调用子类的构造函数,假如调用到了子类的构造函数,就不再调用父类的构造函数。(其实这样也可以理解为子类对父类构造函数的重写)
final修饰的方法不可以被重写。
final修饰的类不可以被继承。
重写和重载是不同的:函数中的变量个数相同叫重写(变量个数相同,方法体不同),变量个数不同叫重载。
Private:只能在本类内部使用。
Protect:可以在子类中使用。
Public:到处都能用。
PHP中调用类方法时->和::的区别:
在类里面的时候,$this->func()和self::func()没什么区别。
在外部的时候,->必须是实例化后的对象使用;而::可以是未实例化的类名直接调用。
举个例子:
class Mytest{
function ccc($str){
echo $str;
}
}
Mytest::ccc("123456");
$object = new Mytest();
$object->ccc("123456");
静态属性:类里的静态属性相当于函数中的静态变量。也就是说,一个静态属性可以在每次被方法调用之后记住其新值,也就是说,静态属性的值是可以被方法改变的。
static $n;
静态属性和标准属性的区别是,我们不能在类里用$this访问他,而是应该使用self后跟范围操作符(::) self::$counter++
类常数:类常数可以被类及其子类的全部实例访问,他的值是不可改变的。(不用$符号)
const PI = 3.14;
PHP的静态方法:
静态方法的规则和静态变量是相同的。使用ststic关键字可以将方法标识为静态方法,通过类的名称和作用域限定操作符::可以访问静态方法。
静态方法和非静态方法之间有一个很重要的区别,就是在调用静态方法时,我们不需要创建类的实例。
静态方法可以不实例化,直接调用,可以被重写。
Program List:用类名作为参数
用类名作为参数可以解决非继承的静态问题。
01 | <?php | |
02 | class Fruit { |
03 | public static $category = "I'm fruit"; | |
04 |
|
05 | static function find($class) | |
06 | { |
07 | $vars = get_class_vars($class) ; | |
08 | echo $vars['category'] ; |
09 | } | |
10 | } |
11 |
| |
12 | class Apple extends Fruit { |
13 | public static $category = "I'm Apple"; | |
14 | } |
15 |
| |
16 | Apple::find("Apple"); |
17 | ?> |
程序运行结果:
1 | I'm Apple |
Program List:重写基类方法
在派生类重写基类的方法。
01 | <?php | |
02 | class Fruit |
03 | { | |
04 | static function Foo ( $class = __CLASS__ ) |
05 | { | |
06 | call_user_func(array($class, 'Color')); |
07 | } | |
08 | } |
09 |
| |
10 | class Apple extends Fruit |
11 | { | |
12 | static function Foo ( $class = __CLASS__ ) |
13 | { | |
14 | parent::Foo($class); |
15 | } | |
16 |
|
17 | static function Color() | |
18 | { |
19 | echo "Apple's color is red"; | |
20 | } |
21 | } | |
22 |
|
23 | Apple::Foo(); // This time it works. | |
24 | ?> |
程序运行结果:
1 | Apple's color is red |
Program List:静态数组的使用
静态和const作用域都可以用::操作符访问,如果你想使用::操作符访问数组,你需要事先将数组声明为静态。
01 | <?php | |
02 | class Fruit |
03 | { | |
04 | static $color = array('color1' => 'red', 'color2' => 'yellow'); |
05 | } | |
06 |
|
07 | class Apple | |
08 | { |
09 | public function __construct() | |
10 | { |
11 | var_dump(Fruit::$color); | |
12 | } |
13 | } | |
14 |
|
15 | class Banana | |
16 | { |
17 | public function __construct() | |
18 | { |
19 | Fruit::$color = FALSE; | |
20 | } |
21 | } | |
22 |
|
23 | new Apple(); // prints array(2) { ["color1"]=> string(3) "red" ["color2"]=> string(6) "yellow" } | |
24 | echo '<br />'; |
25 | new Banana(); | |
26 | new Apple(); // prints bool(false) |
27 | ?> |
程序运行结果:
1 | array(2) { ["color1"]=> string(3) "red" ["color2"]=> string(6) "yellow" } | |
2 | bool(false) |
Program List:再来一个单例模式
Static真的很酷,下面的程序演示了如何获得一个已经存在的实例。
01 | <?php | |
02 | class Singleton { |
03 |
| |
04 | private static $instance=null; |
05 | private $value=null; | |
06 |
|
07 | private function __construct($value) { | |
08 | $this->value = $value; |
09 | } | |
10 |
|
11 | public static function getInstance() { | |
12 | if ( self::$instance == null ) { |
13 | echo "<br>new<br>"; | |
14 | self::$instance = new Singleton("values"); |
15 | } | |
16 | else { |
17 | echo "<br>old<br>"; | |
18 | } |
19 | return self::$instance; | |
20 | } |
21 |
| |
22 | } |
23 |
| |
24 | $x = Singleton::getInstance(); |
25 | var_dump($x); // returns the new object | |
26 | $y = Singleton::getInstance(); |
27 | var_dump($y); // returns the existing object | |
28 | ?> |
程序运行结果:
1 | new | |
2 | object(Singleton)#1 (1) { ["value:private"]=> string(6) "values" } |
3 | old | |
4 | object(Singleton)#1 (1) { ["value:private"]=> string(6) "values" } |
抽象类
抽象类不能被实例化,只能被继承。
abstract class className{}
抽象类中通常也会有抽象方法,abstract public function name()
抽象方法只有声明,没有方法体。
哪怕一个类中只有一个抽象方法,那么这个类也必须定义成抽象类。
抽象类中可以有非抽象方法。
__toString();当一个类被当做string类型的时候,就会自动调用__toString()方法,比如:
$a = new someClass();
echo $a;
__toString()可以作为一个简单的调试工具。
要是没有__toString()方法的话,直接echo这个类,是要报错的。
接口
接口中的所有方法都必须是public的,且子类必须实现接口中的所有方法,接口中的方法有声明、有参数,但是没有方法体。
定义接口:interface isomething
作为惯例,接口的名字往往以i开头
子类继承接口:class className implements isomething
复制和克隆
由于php是使用引用来传递参数,而不是使用值,所以,当我们创建一个对象的副本的时候,php其实只是创建了这个对象的一个引用,而不是复制整个新对象。换句话说,两个变量会指向相同的东西,通过一个对象对其值的修改会影响到另外一个对象。
如果我们真的需要两个分开的、完全独立的对象,就需要创建一个对象的克隆。
$b = clone $a;
这个时候会出发class中的__clone()方法(假如类中定义了__clone()方法的话)。
traits(PHP 5.4版本)
以一种迂回的方式解决php不能多继承的问题。
定义一个trait:
trait tSomeTrait{
publicfunction someName(){
…
}
}
使用trait:
Class someClass{
UsetSomeTrait;
}
可以use多个trait,相当于require代码的效果。
trait来的方法、继承来的方法、类自身方法优先级问题:
当上述三种情况出现同名方法时,优先级为:类自身方法> trait来的方法>继承来的方法
命名空间namespace(php 5.3版本)
可以解决多个类名相同而导致的冲突问题。