2014年8月28日星期四

高洛峰-PHP面向对象笔记

面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计);

类是具有相同属性和服务的一组对象的集合。

它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和服务两个主要部分;在面向对象的编程语言中;

类是一个独立的程序单位,它应该有一个类名并包括属性说明和服务说明两个主要部分;


对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。

类与对象的关系就如模具和铸件的关系,类的实例化结果就是对象,而对一类对象的抽象就是类。


打个比方:清单就是类,清单里面的东西是对象,把东西买回来就是实例化对象;



类的定义:

class 类名{

}




比如说,一个人就是一个对象,你怎么把一个你看好的人推荐给你们领导呢?当然是越详细越好了:

首先,你会介绍这个人姓名、性别、年龄、身高、体重、电话、家庭住址等等。

然后,你要介绍这个人能做什么,可以开车,会说英语,可以使用电脑等等。



属性,像上面我们看到的,人的姓名、性别、年龄、身高、体重、电话、家庭住址等等;


动态上也就是人的这个对象的功能,比如这个人可以开车,会说英语,可以使用电脑等等,抽象成程序时,

我们把动态的写成函数或者说是方法;




class 人{

成员属性:姓名、性别、年龄、身高、体重、电话、家庭住址

成员方法:可以开车, 会说英语, 可以使用电脑

}



通过在类定义中使用关键字" var"来声明变量


<?php

class Person

{

//下面是人的成员属性

var $name//人的名字

var $sex//人的性别

var $age//人的年龄

//下面是人的成员方法

function say() //这个人可以说话的方法

{

echo "这个人在说话";

}

function run() //这个人可以走路的方法

{

echo "这个人在走路";

}

}

$p1=new Person();

$p2=new Person();

$p3=new Person();

//下面三行是给$p1对象属性赋值

$p1->name="张三";

$p1->sex="男";

$p1->age=20;

//下面三行是访问$p1对象的属性

echo "this is p1's name".$p1->name."<br>";

echo "p1 is a ".$p1->sex."<br>";

echo "p1 ".$p1->age."'s old.<br>";

//下面两行访问$p1对象中的方法

$p1->say();

$p1->run();

?>


 

“$this”的使用


<?php

class Person

{

//下面是人的成员属性

var $name//人的名字

var $sex//人的性别

var $age//人的年龄

//下面是人的成员方法

function say() //这个人可以说话的方法

{

echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:

".$this->age."<br>";

}

function run() //这个人可以走路的方法

{

echo "这个人在走路";

}

}

$p1=new Person(); //创建实例对象$p1

$p2=new Person(); //创建实例对象$p2

$p3=new Person(); //创建实例对象$p3

//下面三行是给$p1对象属性赋值

$p1->name="张三";

$p1->sex="男";

$p1->age=20;

//下面访问$p1对象中的说话方法

$p1->say();

?>  




构造方法与析构方法

通常用 构造方法 执行一些有用的初始化任务。


比如对成属性在创建对象的时候赋初值。

通过构造方法后实例化和赋值同时完成;

析构函数允许在销毁一个类之前执行的一些操作或完成一些功能,比如说关闭文件,释放结果集等;



<?

//创建一个人类

class Person

{

//下面是人的成员属性

var $name;//人的名字

var $sex;//人的性别

var $age;//人的年龄

//定义一个构造方法参数为姓名$name、性别$sex和年龄$age

function __construct($name, $sex, $age)

{

//通过构造方法传进来的$name给成员属性$this->name赋初使值

$this->name=$name;

//通过构造方法传进来的$sex给成员属性$this->sex赋初使值

$this->sex=$sex;

//通过构造方法传进来的$age给成员属性$this->age赋初使值

$this->age=$age;

}

//这个人的说话方法

function say()

{

echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:

".$this->age."<br>";

}

//这是一个析构函数,在对象销毁前调用

function __destruct(){

    echo "bye bye ".$this->name."<br>";

}

}

//通过构造方法创建3个对象$p1、p2、$p3,分别传入三个不同的实参为姓名、性别和年龄

$p1=new Person("貂蝉""女""23");

$p2=new Person("吕布""男""26");

//下面访问$p1对象中的说话方法

$p1->say();

//下面访问$p2对象中的说话方法

$p2->say();

?>




封装性


封装性就是把对象的属性和服务结合成一个独立的相同单位,并尽可能隐蔽对象的内部细节

包含两个含义:1.把对象的全部属性和全部服务结合在一起,形成一个不可分割的独立单位(即对象)。

2.信息隐蔽,即尽可能隐蔽对象的内部细节,对外形成一个边界〔或者说形成一道屏障〕,只保留有限的对外接口使之与外部发生联系。



比如某个人的对象中有年龄和工资等属性,像这样个人隐私的属性是不想让其它人随意就能获得到的;


再比如说,个人电脑都有一个密码,不想让其它人随意的登陆,在你的电脑里面拷贝和粘贴。

还有就是像人这个对象,身高和年龄的属性,只能是自己来增长,不可以让别人随意的赋值等等。

使用 private 这个关键字来对属性和方法进行封装:




<?php 

class Person

{

//下面是人的成员属性

private $name//人的名字,被private封装上了

private $sex//人的性别, 被private封装上了

private $age//人的年龄, 被private封装上了

//这个人可以说话的方法

function say()

{

echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:

".$this->age."<br>";

}

//这个人可以走路的方法, 被private封装上了

private function run()

{

echo "这个人在走路";

}

}

//实例化一个人的实例对象

$p1=new Person();

//以下给private的属性或方法 赋值,调用的任何操作都会报错;  

//试图去给私有的属性赋值, 结果会发生错误

$p1->name="张三";

$p1->sex="男";

$p1->age=20;

//试图去打印私有的属性, 结果会发生错误

echo $p1->name."<br>";

//试图去打印私有的成员方法, 结果会发生错误

$p1->run();

?>


1


 

这个时候就只能访问公用的这个say()方法,而不能调用private的$name,$age,$sex属性了;


如果强行加上 echo $p1->name; 就会出现如下fatal error; 访问private function

2 3

所以私有成员只能在本对象内部自己访问

构造方法是默认的公有方法,所以可以用构造方法给私有的属性赋初值。

 


<?php 

class Person

{

//下面是人的成员属性

private $name//人的名字,被private封装上了

private $sex//人的性别, 被private封装上了

private $age//人的年龄, 被private封装上了

function __construct($name,$sex,$age){

    $this->name=$name; 

    //通过构造方法传进来的$name给私有成员属性$this->name赋初使值;

    $this->sex=$sex;

    $this->age=$age;    

}

//这个人可以说话的方法

function say()

{

echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:

".$this->age."<br>";

}

//这个人可以走路的方法, 被private封装上了

private function run()

{

echo "这个人在走路";

}

}

//实例化一个人的实例对象

$p1=new Person("feng""male""32");

echo $p1->say();

//可以通过构造方法的参数传递来访问私有数据;但还是不能直接访问private;

//就是私有的成员只能在类的内部使用

?>


接口,可以为私有属性在类外部提供设置方法和获取方法,来操作私有属性;

 

__set() __get() __isset() __unset()四个方法的应用

 


“__set()”和“__get()”这两个方法,这两个方法不是默认存在的,而是我们手
工添加到类里面去的,像构造方法(__construct())一样, 类里面添加了才会存在;




__get()方法:这个方法用来获取私有成员属性值的;

__get()只有一个参数,参数传入你要获取的成员属性的名称,返回获取的属性值。

__set()方法:这个方法用来为私有成员属性设置值的;

__set()有两个参数,第一个参数为你要为设置值的属性名,第二个参数是要给属性设置的值,没有返回值。



<?php

class Person

{

//下面是人的成员属性, 都是封装的私有成员

private $name//人的名字

private $sex//人的性别

private $age//人的年龄

//__get()方法用来获取私有属性

private function __get($property_name)

{

echo "在直接获取私有属性值的时候,自动调用了这个__get()方法<br>";

if(isset($this->$property_name))

{

return($this->$property_name);

}

else

{

    return(NULL);

}

}

//__set()方法用来设置私有属性

private function __set($property_name, $value)

{

echo "在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值<br>";

$this->$property_name = $value;

}

}

$p1=new Person();

//直接为私有属性赋值的操作, 会自动调用__set()方法进行赋值

$p1->name="张三";

$p1->sex="男";

$p1->age=20;

//直接获取私有属性的值, 会自动调用__get()方法,返回成员属性的值

echo "姓名:".$p1->name."<br>";

echo "性别:".$p1->sex."<br>";

echo "年龄:".$p1->age."<br>";

?>



类的继承

 


<?php

class person{

    var $name;

    var $sex;

    var $age;


    function __construct($name,$sex,$age){

        $this->age=$age;

        $this->name=$name;

        $this->sex=$sex;        

    }

    function say(){

        echo "my name is ".$this->name." ".$this->age."years old "."i am a ".$this->sex."<br>";

    }    

}

//定义一个子类“学生类“使用”extends”关键字来继承”人”类

class student extends person{

    var $school;

    function study(){

        echo "my name is ".$this->name."i am learning at ".$this->school."<br>";

    }

}


?>  



 

重载新的方法

 

重载新的方法就是子类覆盖父类的已有的方法,那为什么要这么做呢?


因为继承过来的方法需要扩展,然后使用;继承过来的属性、方法不能被删除;


 PHP 里面不能定义同名的方法, 但是在父子关系的两个类中,我们可以在子类中定义和父类同名的方法,这样就把父类中继承过来的方法覆盖掉了。


<?php

class person{

    var $name;

    var $sex;

    var $age;


    function __construct($name,$sex,$age){

        $this->age=$age;

        $this->name=$name;

        $this->sex=$sex;        

    }

    function say(){

        echo "my name is ".$this->name." ".$this->age."years old "."i am a ".$this->sex."<br>";

    }

    

}

//定义一个子类“学生类“使用”extends”关键字来继承”人”类

class student extends person{

    var $school;

//重载say方法;

function say(){

    parent::say();


    //使用父类的“类名::“来调用父类中被覆盖的方法;

    //或者使用“parent::”的方试来调用父类中被覆盖的方法;  



    //尽可能的使用parent方式来调用,以便以后的修改;

    echo "通过parent扩展这个继承的方法,";

}

    function study(){

        echo "my name is ".$this->name."i am learning at ".$this->school."<br>";

    }

}

$p1=new student("张三","male","23","xiaoxue");

echo $p1->age."<br>";

echo $p1->name."<br>";

echo $p1->sex."<br>";

echo $p1->say();

?>



访问类型


PHP5 支持如下 3 种访问修饰符:

public(公有的、默认的),

private(私有的)

protected(受保护的)


4


final 关键字的应用

 

使用 final 关键标记的方法不能被子类覆盖,是最终版本;

这个关键字只能用来定义类和定义方法,不能使用 final 这个关键字来定义成员属性


static 和 const 关键字的使用



static 成员能够限制外部的访问,因为static 的成员是属于类的,是不属于任何对象实例,是在类第一次被加载的时候分配的空间,其他

类是无法访问的,只对类的实例共享,能一定程度对类该成员形成保护。


类的静态变量,非常类似全局变量,能够被所有类的实例共享,类的静态方法也是一样的,类似于全局函数。


<?php 

class person{

    public static $mycountry='usa '//静态属性

    public static function say(){

        //静态方法

        echo "i love usa ,i have a american dram!";

    }

public static function say2(){


//在静态方法里,可以使用这个方法所在的类的“类名”,也可以使用“self”来访问其它静态成员,

//如果没有特殊情况的话,我们通常使用后者,即“self::成员属性”的方式。


    echo "i love ".self::$mycountry;

}


}

//输出静态属性和方法;

//静态成员是在类第一次加载的时候就创建的,所以在类的外部不需要对象而使用类名就可以访问的到静态的成员;

//使用对象访问不到静态成员的; 就是说实化后不能通过$p1->mycontury这种形式访问这个静态成员;

echo person::$mycountry;

person::say();

//给静态属性重新赋值;

person::$mycountry="china";

echo person::$mycountry;

echo person::say2();

?>



const 是一个定义常量的关键字

 

在 PHP 中定义常量使用的是“define()”这个函数,但是在类里面定义常量使用的是“const”这个关键字;


<?php

class MyClass

{

//定义一个常量 constant

const constant = 'constant value';

function showConstant() {

echo self::constant . "\n"//使用 self 访问,不要加”$”

}

}

echo MyClass::constant . "\n"//使用类名来访问,也不加”$”

$class = new MyClass();

$class->showConstant();

// echo $class::constant; 是不允许的

?>  



__toString()方法

 


在类里面声明“__”开始的方法名的方法(PHP 给我们提供的),都是在某一时刻不同情况下自动调用执行的方法;

“__toString()”方法也是一样自动被调用的,是在直接输出对象引用时自动调用的,


 


<?php

// Declare a simple class

class TestClass

{

public $foo;

public function __construct($foo) {

$this->foo = $foo;

}

//定义一个__toString方法,返加一个成员属性$foo

public function __toString() {

return $this->foo;

}

}

$class = new TestClass('Hello');

//直接输出对象

echo $class;

?>  



 

克隆对象

 


需要在一个项目里面,使用两个或多个一样的对象,如果你使用“new”关键字重新创建对象的话,再赋值上相同的属性,这样做比较烦琐而且也容易出错,所以要根据一个对象完全克隆出一个一模一样的对象,是非常有必要的,而且克隆以后,两个对象互不干扰。




<?php

class Person

{

//下面是人的成员属性

var $name//人的名字

var $sex;

//人的性别

var $age;

//人的年龄

//定义一个构造方法参数为属性姓名$name、性别$sex 和年龄$age 进行赋值


function __construct($name="", $sex="", $age="")

{

$this->name=$name;

$this->sex=$sex;

$this->age=$age;

}

//这个人可以说话的方法, 说出自己的属性

function say()

{

echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";

}

//对象克隆时自动调用的方法, 如果想在克隆后改变原对象的内容,需要在__clone()中重写原本的属性和方法

function __clone()

{

//$this 指的复本 p2, 而$that 是指向原本 p1,这样就在本方法里,改变了复本的属性。

$this->name="我是假的";

$this->age=30;

}

}

$p1=new Person("张三","男", 20);

$p2=clone $p1;

$p1->say();

$p2->say();

?>  




__call 处理调用错误


如果调用对象不存在的内部方法时候,程序会退出不能继续执行。

__call可以在程序调用对象内部不存在的方法时,提示我们调用的方法及使用的参数不存在,且程序还可以继续执行;


 


<?php

//这是一个测试的类,里面没有属性和方法

class Test

{

//调用不存的方法时自动调用的方法,第一个参数为方法名,第二个参数是数组参数;

function __call($function_name, $args)

{

print "你所调用的函数:$function_name(参数:";

print_r($args);

print ")不存在!<br>\n";

}

}

//产生一个Test类的对象

$test=new Test();

//调用对象里不存在的方法

$test->demo("one""two""three");

//程序不会退出可以执行到这里

echo "this is a test<br>";

?>



抽象方法和抽象类


抽象方法就是为了方便继承而引入的;

只要一个类里面有一个方法是抽象方法;

抽象类不能产生实例对象,所以也不能直接使用;


我们是将抽象方法是作为子类重载的模板使用的,定义抽象类就相当于定义了一种规范,这种规范要求子类去遵守,子类继承抽象类之后,把抽象类里面的抽象方法按照子类的需要实现。


子类必须把父类中的抽象方法全部都实现,

否则子类中还存在抽象方法,那么子类还是抽象类,还是不能实例化;


 

php5 接口技术

 

PHP 引入接口是为了解决不支持多重继承;

接口的思想是指定了一个实现了该接口的类必须实现的一系列方法。


接口里面所有的方法必须都是声明为抽象方法,另外接口里面不能声明变量,

而且接口里面所有的成员都是 public 权限的。所以子类在实现的时候也一定要使用 public 权限实限。


我们定义一个接口的子类去实现接口中全部抽象方法使用的关键字是“implements”,而不是我们前面所说的“extends”;


接口就是全都是公用的 public的 ,拿来调用的,可不可以这样说?

父类是一个接口,所以子类就必须要实现父类的抽象方法?

5 6 7





多态的应用

 

所谓多态性是指一段程序能够处理多种类型对象的能力;

<?


//定义了一个形状的接口,里面有两个抽象方法让子类去实现

interface Shape

{

function area();

function perimeter();

}

//定义了一个矩形子类实现了形状接口中的周长和面积

class Rect implements Shape

{

private $width;

private $height;

function __construct($width, $height)

{

$this->width=$width;

$this->height=$height;

}

function area()

{

return "矩形的面积是:".($this->width*$this->height);

}

function perimeter()

{

return "矩形的周长是:".(2*($this->width+$this->height));

}

}

//定义了一个圆形子类实现了形状接口中的周长和面积

class Circular implements Shape

{

private $radius;

function __construct($radius)

{

$this->radius=$radius;

}

function area()

{

return "圆形的面积是:".(3.14*$this->radius*$this->radius);

}

function perimeter()

{

return "圆形的周长是:".(2*3.14*$this->radius);

}

}

//把子类矩形对象赋给形状的一个引用

$shape=new Rect(5, 10);

echo $shape->area()."<br>";

echo $shape->perimeter()."<br>";

//把子类圆形对象赋给形状的一个引用


$shape=new Circular(10);

echo $shape->area()."<br>";

echo $shape->perimeter()."<br>";

?>  



通过上例我们看到,把矩形对象和圆形对象分别赋给了变量$shape,调用$shape 引用中的面积和周长的方法,出现了不同的结果,这就是一种多态的应用;

 

把对象串行化


把一个对象在网络上传输,为了方便传输,可以把整个对象转化为二进制串,等到达另一端时,再还原为原来的对象,这个过程称之为串行化


串行化有两个过程,一个是串行化,就是把对象转化为二进制的字符串,我们使用 serialize()函数;

另一个是反串行化,就是把对象转化的二进制字符串再转化为对象, 我们使用 unserialize()函数;






<?

class Person

{

//下面是人的成员属性

var $name//人的名字

var $sex;

//人的性别

var $age;

//人的年龄

//定义一个构造方法参数为属性姓名$name、性别$sex 和年龄$age 进行赋值

function __construct($name="",$sex="", $age="")

{

$this->name=$name;

$this->sex=$sex;

$this->age=$age;

}

//这个人可以说话的方法, 说出自己的属性


function say()

{

echo "我的名字叫:".$this->name." 性别:".$this->sex." 我的年龄是:".$this->age."<br>";

}

/*在 php5 中有两个魔术方法__sleep()方法和__wakeup()方法,在对象串行化的时候,会调用一

个__sleep()方法来完成一些睡前的事情;而在重新醒来,即由二进制串重新组成一个对象的时候,

则会自动调用 PHP 的另一个函数__wakeup(),做一些对象醒来就要做的动作。*/

//末被包含的属性将在串行化时被忽略,如果没有__sleep()方法,PHP 将保存所有属性。

//指定串行化时把返回的数组中$name 和$age 值串行化,忽略没在数组中的属性$sex

function __sleep(){

    $arr=array("name","age");

    return $arr;

}

//重新生成对象时,并重新赋值$age为40

function __wakeup(){

    $this->age=40;

}



}

$p1=new Person("张三""男", 20);

$p1_string=serialize($p1); //把一个对象串行化,返一个字符串

echo $p1_string."<br>"//串行化的字符串我们通常不去解析

$p2=unserialize($p1_string); //把一个串行化的字符串反串行化形成对象$p2

$p2->say();

?>




自动加载类 


 

 可以定义一个__autoload()函数,它会在试图使用尚未被定义的类时自动调用;


本例尝试分别从 MyClass1.php 和 MyClass2.php 文件中加载 MyClass1 和 MyClass2 类;



<?php

function __autoload($classname)

{

require_once $classname . '.php';

}

//MyClass1 类不存在自动调用__autoload()函数,传入参数”MyClass1”

$obj = new MyClass1();

//MyClass2 类不存在自动调用__autoload()函数,传入参数”MyClass2”

$obj2 = new MyClass2();

?>  




1、如果类存在继承关系(例如:ClassB extends ClassA),并且ClassA不在ClassB所在目录利用__autoload魔术函数实例化ClassB的时候就会受到一个致命错误:Fatal error: Class ‘Classd’ not found in ……ClassB.php on line 2,

2、另外一个需要注意的是,类名和类的文件名必须一致,才能更方便的使用魔术函数__autoload;

3、在CLI模式下运行PHP脚本的话这个方法无效;(PHP CLI模式开发不需要任何一种Web服务器(包括Apache或MS IIS等));

4、如果你的类名称和用户的输入有关——或者依赖于用户的输入,一定要注意检查输入的文件名,例如:.././这样的文件名是非常危险的。

没有评论:

发表评论