欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  php教程

php面向对象(基础)

程序员文章站 2024-02-02 22:06:46
...
最近重新学习了php面向对象这一部分的知识,做了下笔记,分享给大家,其中一部分为自己的简介,加色部分一般为自己认为比较重点或需要注意的地方,分享给大家,还希望大家多提意见共同学习。 1. 析构函数:__destruct ( ) //这是一个析构函数,在对象销毁前调用
function __destruct()
{
echo “再见”.$this->name.”
”;
}

2. 构造函数:__construct( )
PHP子类能否继承父类的构造方法?
如果没有定义子类构造方法的,默认调用父类构造方法 如果定义了子类的构造方法,那么就直接调用自身
function __construct($name, $sex, $age)
{
//通过构造方法传进来的$name给成员属性$this->name赋初使值
$this->name=$name;
//通过构造方法传进来的$sex给成员属性$this->sex赋初使值
$this->sex=$sex;
//通过构造方法传进来的$age给成员属性$this->age赋初使值
$this->age=$age;
}

3. __set()设置属性 。 __get()取得属性 class Person
{
//下面是人的成员属性, 都是封装的私有成员
private $name; //人的名字
private $sex; //人的性别
private $age; //人的年龄
//__get()方法用来获取私有属性
private function __get($property_name)
{
echo "在直接获取私有属性值的时候,自动调用了这个__get()方法
";
if(isset($this->$property_name))
{
return($this->$property_name); //去掉后将不能正确的给成员变量赋值
}
else
{
return(NULL);
}
}
//__set()方法用来设置私有属性
private function __set($property_name, $value)
{
echo "在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值
";
$this->$property_name = $value;
}
}
$p1=new Person(); //直接为私有属性赋值的操作, 会自动调用__set()方法进行赋值 $p1->name="张三"; $p1->sex="男"; $p1->age=20; //直接获取私有属性的值, 会自动调用__get()方法,返回成员属性的值 echo "姓名:".$p1->name."
"; echo "性别:".$p1->sex."
"; echo "年龄:".$p1->age."
"; ?>
程序执行结果: 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值 在直接获取私有属性值的时候,自动调用了这个__get()方法 姓名:张三 在直接获取私有属性值的时候,自动调用了这个__get()方法 性别:男 在直接获取私有属性值的时候,自动调用了这个__get()方法 年龄:20
以上代码如果不加上__get()和__set()方法,程序就会出错,因为不能在类的外部操作私有成员, 而上面的代码是通过自动调用__get()和__set()方法来帮助我们直接存取封装的私有成员的。


4. __isset() 方法,__unset() 方法
class Person
{
//下面是人的成员属性
private $name; //人的名字
private $sex; //人的性别
private $age; //人的年龄
//__get()方法用来获取私有属性
private function __get($property_name)
{
if(isset($this->$property_name))
{
return($this->$property_name);
}else {
return(NULL);
}
}
//__set()方法用来设置私有属性
private function __set($property_name, $value)
{
$this->$property_name = $value;
}
//__isset()方法
private function __isset($nm)
{
echo "isset()函数测定私有成员时,自动调用
";
return isset($this->$nm);
}
//__unset()方法
private function __unset($nm)
{
echo "当在类外部使用unset()函数来删除私有成员时自动调用的
";
unset($this->$nm);
}
}
$p1=new Person();
$p1->name="this is a person name";
//在使用isset()函数测定私有成员时,自动调用__isset()方法帮我们完成,返回结果为true
echo var_dump(isset($p1->name))."
";
echo $p1->name."
";
//在使用unset()函数删除私有成员时,自动调用__unset()方法帮我们完成,删除name私有属性
unset($p1->name);
//已经被删除了, 所这行不会有输出
echo $p1->name;
?>
输出结果为: isset()函数测定私有成员时,自动调用 bool(true) this is a person name 当在类外部使用 unset()函数来删除私有成员时自动调用的 __set()、__get()、__isset()、__unset() 这四个方法都是我们添加到对象里面的,在需要时自动调 用的,来完成在对象外部对对象内部私有属性的操作。


5. 调用父类接口
header("Content-Type: text/html; charset=utf-8");
class Person
{
//下面是人的成员属性, 都是封装的私有成员
private $name; //人的名字
private $sex; //人的性别
public $age; //人的年龄


function __construct($name, $sex, $age)
{
$this->name=$name;
$this->sex=$sex;
$this->age=$age;
}

function say(){
echo "我的名字叫:".$this->name."性别:".$this->sex."年龄:".$this->age;
}


}


class Student extends Person
{
var $school; //学生所在学校的属性
function __construct($name, $sex, $age,$school)
{
parent::__construct($name,$sex,$age);
$this->school=$school;
}


//这个学生学习的方法
function study()
{
echo "我的名字叫:".$this->name." 我正在”.$this->school.”学习
";
}
//这个学性可以说话的方法, 说自己所有的属性,覆盖了父类的同名方法
function say()
{
//使用父类的“类名::“来调用父类中被覆盖的方法;
// Person::say();
//或者使用“parent::”的方试来调用父类中被覆盖的方法;


parent::say();
//加上一点自己的功能
echo "我的年龄是:".$this->age."我在".$this->school."上学.
";
}
}
$p1 = new Student("wangyaofeng","男","22","河南科技学院");
$p1->say();

?> 结果: 我的名字叫:wangyaofeng性别:男年龄:22我的年龄是:22我在河南科技学院上学


6. final 关键字的应用
这个关键字只能用来定义类和定义方法,不能使用 final 这个关键字来定义成员属性,因为 final 是常量的意思,我们在 PHP 里定义常量使用的是 define()函数,所以不能使用 final 来定义 成员属性。 使用 final 关键标记的类不能被继承; 代码片断 final class Person { ...... } class Student extends Person { } 会出现下面错误: Fatal error: Class Student may not inherit from final class (Person) 使用 final 关键标记的方法不能被子类覆盖,是最终版本; 代码片断 class Person {
final function say()
{
}
} class Student extends Person {
function say()
{
}
} 会出现下面错误: Fatal error: Cannot override final method Person::say()

7.static 和 const 关键字的使用
Static 关键字是在类中描述成员属性和成员方法是静态的;静态的成员好处在那里呢?前面我们声明了“Person”的人类,在“Person”这个类里如果我们加上一个“人所属国家”的属性,这样用“Person”这个类实例化出几百个或者更多个实例对象,每个对象里面就都有“所属国家”的属性了,如果开发的项目就是为中国人而开发的,那么每个对象里面就都有一个国家的属性是“中国”其它的属性是不同的,如果我们把“国家”的属性做成静态的成员,这样国家的属性在内存中就只有一个,而让这几百个或更多的对象共用这一个属性,static 成员能够限制外部的访问,因为static 的成员是属于类的,是不属于任何对象实例,是在类第一次被加载的时候分配的空间,其他类是无法访问的,只对类的实例共享,能一定程度对类该成员形成保护。 从内存的角度我们来分析一下,内存从逻辑上被分为四段,其中对象是放在“堆内存”里面,对象的引用被放到了“栈内存”里,而静态成员则放到了“初始化静态段”,在类第一次被加载的时候放入的,可以让堆内存里面的每个对象所共享,如下图;类的静态变量,非常类似全局变量,能够被所有类的实例共享,类的静态方法也是一样的,类似于全局函数。
class Person
{
//下面是人的静态成员属性
public static $myCountry="中国";
// var $name; //人的名字
//这是人的静态成员方法
public static function say()
{
echo "我是中国人
";
}
}
//输出静态属性
echo Person::$myCountry;
//访问静态方法
Person::say();
//重新给静态属性赋值
Person::$myCountry="美国";
echo Person::$myCountry;
?> 因为静态成员是在类第一次加载的时候就创建的,所以在类的外部不需要对象而使用类名就可
以访问的到静态的成员;上面说过,静态成员被这个类的每个实例对象所共享,那么我们使用对象
可不可以访问类中的静态成员呢?从上图中我们可以看到,静态的成员不是在每个对象内部存在
的,但是每个对象都可以共享,所以我们如果使用对象访问成员的话就会出现没有这个属性定义,
使用对象访问不到静态成员的,在其它的面向对象的语言中,比如 Java 是可以使用对象的方式访
问静态成员的,如果 PHP 中可以使用对象访问静态成员的话,我们也尽量不要去使用,因为静态的
成员我们在做项目的时候目的就是使用类名去访问(Person::$myCountry="美国";)。
类里面的静态方法只能访问类的静态的属性,在类里面的静态方法是不能访问类的非静态成员
的,原因很简单,我们要想在本类的方法中访问本类的其它成员,我们需要使用$this 这个引用,
而$this 这个引用指针是代表调用此方法的对象,我们说了静态的方法是不用对象调用的,而是使
用类名来访问,所以根本就没有对象存在,也就没有$this 这个引用了,没有了$this 这个引用就
不能访问类里面的非静态成员,又因为类里面的静态成员是可以不用对象来访问的,所以类里面的
静态方法只能访问类的静态的属性,既然$this 不存在,在静态方法中访其它静态成员我们使用的
是一个特殊的类“self”;self 和$this 相似,只不过 self 是代表这个静态方法所在的类。所以
在静态方法里,可以使用这个方法所在的类的“类名”
,也可以使用“self”来访问其它静态成员,
如果没有特殊情况的话,我们通常使用后者,即“self::成员属性”的方式。
代码片断

class Person
{
//下面是人的静态成员属性
public static $myCountry="中国";
//这是人的静态成员方法, 通过self访问其它静态成员
public static function say()
{
echo "我是".self::$myCountry."
";
}
}
//访问静态方法
Person::say();
?>
在非静态方法里可不可以访问静态成员呢,当然也是可以的了,但是也不能使用“$this”引用
也要使用类名或是“self::成员属性的形式”。

const 是一个定义常量的关键字,在 PHP 中定义常量使用的是“define()”这个函数, 但是在类里面定义常量使用的是“const”这个关键字, 类似于 C 中的#define 如果在程序中改变了它的值,
那么会出现错误,用“const”修饰的成员属性的访问方式和“static”修饰的成员访问的方式差
不多,也是使用“类名”,在方法里面使用“self”关键字。但是不用使用“$”符号,也不能使
用对象来访问。
代码片断
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; 是不允许的
?>

8.__toString()方法

我们前面说过在类里面声明“--”开始的方法名的方法(PHP 给我们提供的),都是在某一时刻
不同情况下自动调用执行的方法,“__toString()”方法也是一样自动被调用的,是在直接输出对
象引用时自动调用的, 前面我们讲过对象引用是一个指针,比如说:“$p=new Person()”中,$p
就是一个引用,我们不能使用 echo 直接输出$p,这样会输出“Catchable fatal error: Object of
class Person could not be converted to string ” 这 样 的 错 误 , 如 果 你 在 类 里 面 定 义 了
“ __toString()” 方 法 , 在 直 接 输 出 对 象 引 用 的 时 候 , 就 不 会 产 生 错 误 , 而 是 自 动 调 用 了
“__toString()”方法,输出“__toString()”方法中返回的字符,所以“__toString()”方法一
定要有个返回值(return 语句). 代码片断
// 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;
?>
上例输出:Hello
9.克隆对象
有的时候我们需要在一个项目里面,使用两个或多个一样的对象,如果你使用“new”关键字
重新创建对象的话,再赋值上相同的属性,这样做比较烦琐而且也容易出错,所以要根据一个对象
完全克隆出一个一模一样的对象,是非常有必要的,而且克隆以后,两个对象互不干扰。
在 PHP5 中我们使用“clone”这个关键字克隆对象;
代码片断

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."
";
}
}
$p1=new Person("张三", "男", 20);
//使用“clone”克隆新对象 p2,和 p1 对象具有相同的属性和方法。
$p2=clone $p1;
$p2->say();
?>
PHP5 定义了一个特殊的方法名“__clone()”方法,是在对象克隆时自动调用的方法,用
“__clone()”方法将建立一个与原对象拥有相同属性和方法的对象,如果想在克隆后改变原对象
的内容,需要在__clone()中重写原本的属性和方法,
“__clone()”方法可以没有参数,它自动包
$this $that 两个指针,$this 指向复本,而$that 指向原本;
代码片断
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."
";
}
//对象克隆时自动调用的方法, 如果想在克隆后改变原对象的内容,需要在__clone()中重写原本的属性和方法
function __clone()
{
//$this 指的复本 p2, 而$that 是指向原本 p1,这样就在本方法里,改变了复本的属性。
$this->name="我是假的$that->name";
$this->age=30;
}

}
$p1=new Person("张三", "男", 20);
$p2=clone $p1;
$p1->say();
$p2->say();
?>
上例输出:
执行结果
我的名字叫:张三 性别:男 我的年龄是:20
我的名字叫:我是假的张三 性别:男 我的年龄是:30


10.__call 处理调用错误
在程序开发中,如果在使用对象调用对象内部方法时候,调用的这个方法不存在那么程序就会
出错,然后程序退出不能继续执行。那么可不可以在程序调用对象内部不存在的方法时,提示我们
调用的方法及使用的参数不存在,但程序还可以继续执行,这个时候我们就要使用在调用不存在的
方法时自动调用的方法“__call()”。 代码片断
//这是一个测试的类,里面没有属性和方法
class Test
{
}
//产生一个 Test 类的对象
$test=new Test();
//调用对象里不存在的方法
$test->demo("one", "two", "three");//此方法是没有的
//程序不会执行到这里
echo "this is a test
";
?>
上例出现如下错误,程序通出不能继续执行;
Fatal error: Call to undefined method Test::demo()
下面我们加上”__call()”方法,这个方法有 2 个参数,第一个参数为调用不存在的方法过程中,
自动调用__call()方法时,把这个不存在的方法名传给第一个参数,第二个参数则是把这个方法的多
个参数以数组的形式传进来。
//这是一个测试的类,里面没有属性和方法
class Test
{
//调用不存的方法时自动调用的方法,第一个参数为方法名,第二个参数是数组参数
function __call($function_name, $args)
{
print "你所调用的函数:$function_name(参数:";
print_r($args);
print ")不存在!
\n";
}
}
//产生一个Test类的对象
$test=new Test();
//调用对象里不存在的方法
$test->demo("one", "two", "three");
//程序不会退出可以执行到这里
echo "this is a test
";
?>
上例输出结果为:
执行结果
你所调用的函数:demo(参数:Array ( [0] => one [1] => two [2] => three ) )不存在!
this is a test.
11.抽象方法和抽象类 在 OOP 语言中,一个类可以有一个或多个子类,而每个类都有至少一个公有方法做为外部代码
访问其的接口。而抽象方法就是为了方便继承而引入的,我们先来看一下抽象类和抽象方法的定义
再说明它的用途。
什么是抽象方法?我们在类里面定义的没有方法体的方法就是抽象方法,
所谓的没有方法体指的是,
在方法声明的时候没有大括号以及其中的内容,
而是直接在声明时在方法名后加上分号结束,
另外在声明抽象方法时还要加一个关键字“abstract”来修饰;
例如:
abstract function fun1();
abstract function fun2();
上例是就是“abstract”修饰的没有方法体的抽象方法“fun1()”和“fun2()”
,不要忘记抽象方法后面还要有一个分号;那么什么是抽象类呢?只要一个类里面有一个方法是抽象方法,那么这
个类就要定义为抽象类,抽象类也要使用“abstract”关键字来修饰;在抽象类里面可以有不是抽
象的方法和成员属性,但只要有一个方法是抽象的方法,这个类就必须声明为抽象类,使用
“abstract”来修饰。
例如:
代码片断
abstract class Demo
{
var $test;
abstract function fun1();
abstract function fun2();
function fun3()
{
....
}
}

上例中定义了一个抽象类“Demo”使用了“abstract”来修饰, 在这个类里面定义了一个成员
属性“$test”,和两个抽象方法“fun1”和“fun2”还有一个非抽象的方法 fun3();那么抽象类我
们怎么使用呢?最重要的一点就是抽象类不能产生实例对象,所以也不能直接使用,前面我们多次
提到过类不能直接使用,我们使用的是通过类实例化出来的对象,那么抽象类不能产生实例对象我
们声明抽象类有什么用呢?我们是将抽象方法是作为子类重载的模板使用的,定义抽象类就相当于
定义了一种规范,这种规范要求子类去遵守,子类继承抽象类之后,把抽象类里面的抽象方法按照
子类的需要实现。子类必须把父类中的抽象方法全部都实现,否则子类中还存在抽象方法,那么子
类还是抽象类,还是不能实例化对;为什么我们非要从抽象类中继承呢?因为有的时候我们要实现
一些功能就必须从抽象类中继承,否则这些功能你就实现不了,如果继承了抽象类,就要实现类其
中的抽象方法;
代码片断

abstract class Demo
{
var $test;
abstract function fun1();
abstract function fun2();
function fun3()
{
....
}
}
$demo=new Demo(); //抽象类为能产生实例对象,所以这样做是错的,实例化对象交给子类
class Test extends Demo
{
function fun1()
{
...
}
function fun2()
{
...
}
}
$test=new Test(); //子类可以实例化对象,因为实现了父类中所有抽象方法
?> fun3可以不用再重新实现即可应用
12.php5 接口技术 PHP 与大多数面向对象编程语言一样,不支持多重继承.也就是说每个类只能继承一个父类.为
了解决这个问题,PHP 引入了接口,接口的思想是指定了一个实现了该接口的类必须实现的一系列
方法。接口是一种特殊的抽象类,抽象类又是一种特殊的类,所以接口也是一种特殊的类,为什么
说接口是一种特殊的抽象类呢?如果一个抽象类里面的所有的方法都是抽象方法,那么我们就换一
种声明方法使用“接口”;也就是说接口里面所有的方法必须都是声明为抽象方法,另外接口里面
不能声明变量,而且接口里面所有的成员都是 public 权限的。所以子类在实现的时候也一定要使
用 public 权限实限。 声明一个类的时候我们使用的关键字是“class”,而接口一种特殊的类,使用的关键字是
“interface”;
类的定义:class 类名{ ... },接口的声明:interface 接口名{ ... } 代码片断
//定义一个接口使用 interface 关键字,“One”为接口名称
interface One
{
//定义一个常量
const constant = 'constant value';
//定义了一个抽象方法”fun1”
public function fun1();
//定义了抽象方法”fun2”
public function fun2();
}
?> 上例中定义了一个接口“one”,里面声明了两个抽象方法“fun1”和“fun2”,因为接口里
面所有的方法都是抽象方法,所以在声明抽象方法的时候就不用像抽象类那样使用“abstract”
个关键字了,默认的已经加上这个关键字,另外在接口里边的“public”这个访问权限也可以去掉,
因为默认就是 public 的,因为接口里所有成员都要是公有的,所在对于接口里面的成员我们就不
能使用“private”的和“protected”的权限了,都要用 public 或是默认的。另外在接口里面我
们也声明了一个常量“constant”
,因为在接口里面不能用变量成员,所以我们要使用 const 这个
关键字声明。
因为接口是一种特殊的抽象类,里面所有的方法都是抽象方法,所以接口也不能产生实例对象;
它也作为一种规范,所有抽象方法需要子类去实现。
我们可以使用“extends”关键字让一个接口去继承另一个接口;
代码片断
//使用”extends”继承另外一个接口
interface Two extends One
{
function fun3();
function fun4();
}
?> 而我们定义一个接口的子类去实现接口中全部抽象方法使用的关键字是“implements”,而不
是我们前面所说的“extends”;
代码片断
//使用“implements”这个关键字去实现接口中的抽象方法
class Three implements One
{
function fun1()
{
....
}
function fun2()
{
....
}
}
//实现了全部方法,我们去可以使用子类去实例化对象了
$three=new Three();
?>
我们也可以使用抽象类,去实现接口中的部分抽象方法,但要想实例化对象,这个抽象类还要
有子类把它所有的抽象方法都实现才行;
在前面我们说过,PHP 是单继承的,一个类只能有一父类,但是一个类可以实现多个接口,就
相当于一个类要遵守多个规范,就像我们不仅要遵守国家的法律,如果是在学校的话,还要遵守学
校的校规一样;
代码片断
//使用implements实现多个接口
class Four implemtns 接口一, 接口二, ... .
{
//必须把所有接口中的方法都要实现才可以实例化对象。
}
?>
PHP 中不仅一个类可以实现多个接口,也可以在继承一个类的同时实现多个接口, 一定要先
继承类再去实现接口;
//使用 extends 继承一个类,使用 implements 实现多个接口
class Four extends 类名一 implemtns 接口一, 接口二, ... .
{
//所有接口中的方法都要实现才可以实例化对象
... ... ... ..
}
?> 抽象类中允许抽象方法的存在,同时也允许非抽象方法的存在。而接口只允许抽象方法和常量存在。所以抽象方法是可以继承与接口实现的,抽象方法中的非抽象方法通过self::方法名进行调用。 例: //抽象类继承接口类并实现接口类的方法
abstract class Demo implements one{
var $test;
abstract function fun1();
abstract function fun2();
function fun3(){
echo "这是demo类中的fun3
";
//抽象类中方法之间的相互调用要使用self
self::fun4();
}
//将接口中的方法在抽象类中实现,也可要在具体的继承抽象类的子类中实现
function fun4(){
echo "这是demo类中的fun4
";
}
}
//接口
interface one{
public function fun4();
}
//继承抽象类
class Test extends Demo{
//重写抽象类中的方法
function fun1(){
echo "这是Test类中的fun1
";
}
function fun2(){
echo "这是Test类中的fun2";
}
//fun3若不重写则在调用时自动调用父类中的fun3
function fun3(){
echo "hello word
";
//调用父类中的方法
parent::fun3();
}
function fun4(){
echo "hi,helllo !";


}
}
$p1 = new Test();
$p1->fun1();
$p1->fun2();
$p1->fun3();


?> 运行结果: 这是Test类中的fun1
这是Test类中的fun2
hello word
这是demo类中的fun3
这是demo类中的fun4
hi,helllo !
13.多态的应用 多态是除封装和继承之外的另一个面向对象的三大特性之一,我个人看来 PHP 中虽然可以实现
多态,但和 C++还有 Java 这些面向对象的语言相比,多态性并不是那么突出,因为 PHP 本身就是一
种弱类型的语言,不存在父类对象转化为子类对象或者是子类对象转化为父类对象的问题,所以多
态的应用并不是那么的明显;所谓多态性是指一段程序能够处理多种类型对象的能力,比如说在公
司上班,每个月财务发放工资,同一个发工资的方法,在公司内不同的员工或是不同职位的员工,
都是通过这个方法发放的,但是所发的工资都是不相同的。所以同一个发工资的方法就出现了多种
形态。对于面向对象的程序来说,多态就是把子类对象赋值给父类引用,然后调用父类的方法,去
执行子类覆盖父类的那个方法(基类:class A{} 寄)
,但在 PHP 里是弱类型的,对象引用都是一样的不分父类引用,还是
子类引用。
我们现在来看一个例子,首先还是要使用多态就要有父类对象和子类对象的关系。做一个形状
的接口或是抽象类作为父类,里面有两个抽象方法,一个求周长的方法,另一个是求面积的方法;
这接口的子类是多种不同的形状,每个形状又都有周长和面积,又因为父类是一个接口,所以子类
里面就必须要实现父类的这两个周长和面积的抽象方法,这样做的目的是每种不同形状的子类都遵
守父类接口的规范,都要有求周长和求面积的方法。 代码片断

//定义了一个形状的接口,里面有两个抽象方法让子类去实现
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()."
";
echo $shape->perimeter()."
";
//把子类圆形对象赋给形状的一个引用
$shape=new Circular(10);
echo $shape->area()."
";
echo $shape->perimeter()."
";
?>
上例执行结果:
执行结果
矩形的面积是:50
矩形的周长是:30
圆形的面积是:314
圆形的周长是:62.8
通过上例我们看到,把矩形对象和圆形对象分别赋给了变量$shape,调用$shape 引用中的面积
和周长的方法,出现了不同的结果,这就是一种多态的应用,其实在我们 PHP 这种弱类形的面向对
象的语言里面,多态的特性并不是特别的明显,其实就是对象类型变量的变项应用。

14.把对象串行化
有时候需要把一个对象在网络上传输,为了方便传输,可以把整个对象转化为二进制串,等到
达另一端时,再还原为原来的对象,这个过程称之为串行化, 就像我们现在想把一辆汽车通过轮
船运到美国去,因为汽车的体积比较大,我们可以把汽车拆开成小的部件,然后我们把这些部件通
过轮船运到美国去,到了美国再把这些部件组装回汽车。
有两种情况我们必须把对象串行化,第一种情况就是把一个对象在网络中传输的时候要将对象
串行化,第二种情况就是把对象写入文件或是数据库的时候用到串行化。
串行化有两个过程,一个是串行化,就是把对象转化为二进制的字符串,我们使用 serialize()
函数来串行化一个对象,另一个是反串行化,就是把对象转化的二进制字符串再转化为对象, 我
们使用 unserialize()函数来反串行化一个对象.
PHP 中 serialize()函数的参数为对象名,返回值为一个字符串,Serialize()返回的字符串含
义模糊,一般我们不会解析这个串来得到对象的信息,我们只要把返回来的这个字符串传到网络另
一端或是保存到方件中即可。
PHP 中 unserialize()函数来反串行化对象,这个函数的参数即为 serialize()函数的返回值,
输出当然是重新组织好的对象.
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."
";
}
}
$p1=new Person("张三", "男", 20);
$p1_string=serialize($p1); //把一个对象串行化,返一个字符串
echo $p1_string."
"; //串行化的字符串我们通常不去解析
$p2=unserialize($p1_string); //把一个串行化的字符串反串行化形成对象$p2
$p2->say();
?> 执行结果
O:6:"Person":3:{s:4:"name";s:4:"张三";s:3:"sex";s:2:"男";s:3:"age";i:20;}
我的名字叫:张三 性别:男 我的年龄是:20
在 php5 中有两个魔术方法__sleep()方法和__wakeup()方法,在对象串行化的时候,会调用一
个__sleep()方法来完成一些睡前的事情;而在重新醒来,即由二进制串重新组成一个对象的时候,
则会自动调用 PHP 的另一个函数__wakeup(),做一些对象醒来就要做的动作。
__sleep()函数不接受任何参数, 但返回一个数组,其中包含需要串行化的属性。末被包含的
属性将在串行化时被忽略,如果没有__sleep()方法,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."
";
}
//指定串行化时把返回的数组中$name 和$age 值串行化,忽略没在数组中的属性$sex
function __sleep()
{
$arr=array("name", "age");
return($arr);
}

//重新生成对象时,并重新赋值$age为40
function __wakeup() {
$this->age = 40;
}
}
$p1=new Person("张三", "男", 20);
//把一个对象串行化,返一个字符串,调用了__sleep()方法,忽略没在数组中的属性$sex
$p1_string=serialize($p1);
echo $p1_string."
"; //串行化的字符串我们通常不去解析
$p2=unserialize($p1_string); //反串行化形成对象$p2重新赋值$age为40
$p2->say();
?>
上例输出值为:
执行结果
O:6:"Person":2:{s:4:"name";s:4:"张三";s:3:"age";i:20;} 我的名字叫:张三 性别: 我的年龄是:40


15.自动加载类
很多开发者写面向对象的应用程序时,对每个类的定义建立一个 PHP 源文件。一个很大的烦
恼是不得不在每个脚本(每个类一个文件)开头写一个长长的包含文件的列表。
在软件开发的系统中,不可能把所有的类都写在一个 PHP 文件中,当在一个 PHP 文件中需要调
用另一个文件中声明的类时,就需要通过 include 把这个文件引入。不过有的时候,在文件众多的
项目中,要一一将所需类的文件都 include 进来,是一个很让人头疼的事,所以我们能不能在用到
什么类的时候,再把这个类所在的 PHP 文件导入呢?这就是我们这里我们要讲的自动加载类。
在 PHP 5 中,可以定义一个__autoload()函数,它会在试图使用尚未被定义的类时自动调用,
通过调用此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类,__autoload()函
数接收的一个参数,就是你想加载的类的类名,所以你做项目时,在组织定义类的文件名时,需要
按照一定的规则,最好以类名为中心,也可以加上统一的前缀或后缀形成文件名,比如
xxx_classname.php、classname_xxx.php 以及就是 classname.php 等等。
本例尝试分别从 MyClass1.php 和 MyClass2.php 文件中加载 MyClass1 和 MyClass2 类
代码片断
function __autoload($classname)
{
require_once $classname . '.php';
}
//MyClass1 类不存在自动调用__autoload()函数,传入参数”MyClass1”
$obj = new MyClass1();
//MyClass2 类不存在自动调用__autoload()函数,传入参数”MyClass2”
$obj2 = new MyClass2();
?>