2010年最新PHP类的精缩归纳
2010-04-12 17:56
375 查看
一:结构和调用
(
实例化
)
:
class className{}
,调用:
$obj = new className();
当类有构造函数时,还应传入参数。如
$obj = new className($v,$v2...);
二:构造函数和析构函数
:
1
、构造函数用于初始化:使用
__construct()
,可带参数。
2
、但析构函数不能带参数(用于在销去一个类之前执行一些操作或功能)。析构函数用
__destruct()
做名称。在脚本执行结束时,
PHP
会销掉内存中的对象,因此可不用析造函数,但有些比如
COOKIE
等,就应当要用此函数销掉。
知识点:在
PHP4
中也提供了构造函数,但使用的是与类同名的类方法,在
PHP5
仍能兼容这种做法,当一个类中没有包含
__construct
时,会查找与类同名的方法,如果找到,就认为是构造函数,如下:
class test
{ var $b;
function test() { $this->b=5; }
function addab($c) { return $this->b+$c; }
}
$a = new test(); echo $a->addab(4); //
返回
9
3
、
PHP
不会自动调用父类的构造函数
(
不支持构造函数重载
)
,必须使用
parent
关键字显式地调用。
class employee{
function __construct()....
}
class Manager extents Employee{
function __construct(){
parent::_construct();
echo '
这个子类的父类构造函数调用了!
';
}
}
当然也可以调用与该实例没有任何关系的其它类的构造函数。只需在
__construct()
前加上类名即可。如:
otherClassName::__construct();
类的主家庭成员:属性、方法、常量、静态成员
4000
三、类的属性:
有两种方法对类的属性赋值或取值。
1
、使用公共作用域
public
关键词。
2
、使用
__set()
和
__get()
来分别赋值和取值,前者称为设置方法(
setter
)或修改方法
(mutator)
,后者称为访问方法
(accessor)
或获取方法
(getter)
。建议使用这种方法:优点:
A
、可在
__set
()统一进行数据验证。
B
、便于统一管理属性。
注意:
第一:
__set()
和
__get()
只对私有属性起作用,对于用
public
定义的属性,它们两个都懒理搭理,如下:
class test{
protected $a=9,$b=2,$c;
public $d;
function __set($n,$v) { $this->$n = $v+2; }
function __get($name) { return $this->$name+2; }
}
$a = new test();
$a->b =5; echo "<br />"; echo $a->b;
实例只对
$a,$b,$c
的设置会经过
__set
和
__get
过滤与返回,对于
$d
,就不会起作用。如
$a->d=5,
再返回还是
5
。
第二:
__set($n,$v)
要带两个参数。而
__get($n)
只能有一个参数。实例:
class test{
private $a=5,$b=6,$c;
function __set($n,$v)
{
if($n=='a'&&$n>0)
$this->$n = $v;
else
$this->$n = $v+2;
}
function __get($name)
{
return $this->$name; //
如果改为
return $this->$name + $this->addab();
如调用
a
的值,实际返回的是
a+a+b
的值。默认为
5+5+6=16
。
}
function addab()
{ return $this->a + $this->b; }
}
$e=new test();
$e->a = 11; //
注意写法:类的内部用
$this->$n
即变量的写法,但外部实例要用
$e->a
的方式。
$e->b = 12; //get 14
$e->k = 22;
类的属性可自由扩展,如上例的
k
,不管是否用
__set
,当一个实例建立起来后,可以用
$e->newProperty = xx;
直接来创造一个属性,但不建议这么做。
四、类的方法:
理解成类当中的函数即可。
调用:
1
、内部调用:可使用
$this->Fanname();
或
$this->addab()
或
test::addab();
2
、实例化调用时,用
$e->addab();
即可。对于在该方法中没有使用
$this
关键字的,如上例中的:
function addab() { return $this->a+$this->b; }
改为:
function addab() { return 25; }
那在在外部实例调用该方法,也可用
“$e::addab();”
或
“test::addab();”
五、类的常量:
如果类的属性理解成类中的变量,那么类的常量和变量是不一样的,其定义方法为:
class test{
private $a;
const PI = '3.14';
.....
//
在类中调用上面的常量用两种方法,
“$this::PI”,
或
“
类名
::PI”,
这里就是
test::PI,
如下:
function getvalue(){
return $this->a * $this::PI; //
或
$this->a * test::PI,
用
this
关键字或类名均可,但都要用双冒号。
}
}
$e= new test();
$e->PI =5; //
注意,这里用
->
只是创造了一个也是名为
PI
的属性,而不是改变类中的
PI
常量的值。
echo $e::PI; //
这个才是调用类的常量。
常量只能用双冒号
::
来调用。并且不能更改其值。
在类外部实例化后调用类常量同样也有两种方法。方法为:
“$e::PI”
或
“test::PI”
,共同点是都要用冒号,不同点是外部不能用
this
关键字,只能用实例名,但类名
::PI
是通用的。
六、类的静态成员(静态属性或静态方法):
如果需要创建供所有类的实例共享的字段或方法。就得用静态成员。有两个特征:
1
、静态成员是共产主义者,它让脚本上的所有该类的实例调用,但不能借助类的特定实例名调用,而是在类的外部,统一使用
“
类名
::$
成员名
”
的方式调用。而类的内部则统一使用
“self::$
成员名
”
来调用。
2
、当每一次新创建实例时,静态成员会从上次创建的实例最后值开始重新计算,而不是类中初始的值开始计算。
3
、对于用
public
定义的静态成员,可以在外部更改它的值。
private
等则不行。
class test{
public static $v = 0;
function __construct(){ self::$v++; }
static function getV(){ return self::$v; }
}
$a = new test();
echo test::getV(); //
返回
1
$b = new test();
echo test::getV(); //
返回
2
test::$v=8; //
由于
public
定义的成员,改变静态成员的值。
$c = new test();
echo test::getV(); //
返回
9
七、关键字:
(一)
this
关键字:用于类的内部指代类的本身。来访问属性或方法或常量,如
$this->
属性名或方法名。
$this::
常量名。
this
还可以用在该类的子类中,来指代本身的属性或方法。
(二)双冒号
“::”
关键字:用于调用常量、静态成员。
(三)
self
关键字
:
在类的内部与双冒号配合调用静态成员,如
self::$staticVar.
,在类的内部,不能用
$this
来调用静态成员。
(四)
__toString()
:在类中使用
__toString()
,用于将类转成字串并打印类,用处不大:如:
class test{ public $p;
public function __toString(){ return var_export($this,TRUE); }
}
$a=new test();
echo $a; //
输出:
test::__set_state(array( 'p' => NULL, ))
,或写成:
echo $a->__toString();
(五)
__clone()
:当克隆对象时,这个关键字才会发生作用,用于更改克隆时某些值。
(六)
__call()
:方法重载,参下面示例:
class cB{
function __call($method,$n){
if($method=='showVarType'){
if(is_numeric($n[0])){ //
不能用
$n
。要用
$n[0];
$this->displayNum();
}else if (is_array($n[0])){
$this->displayArr();
}else{
$this->displayOther();
}
}
}
function displayNum() {
echo '<h3>
这是数字
!</h3>';
}
function displayArr() {
echo '<h3>
这是数组
!</h3>';
}
function displayOther() {
echo '<h3>
不是数组也不是数字
!</h3>';
}
}
$x='a';
$y=array('a','b');
$b=new cB;
$b->showVarType($x); //
不是数组也不是数字
$b->showVarType($y); //
这是数组
注意,不能在类中定义
showVarType
()方法,否则代码不能用。
(七)
extends:
继承
:
如
class a{} class b extends a{}
类
b
继承了类
a
附:记忆:以后统一在调用方法或属性时用
"-> ",
调用常量则用双冒号
“::”
,不会搞晕。
八、方法和属性的作用域:
共有
6
种:
public(
默认,可省略,也等同于
php6
的
var
声明
),private
(私有,也不能由子类使用),
protected(
私有,但可由子类使用
)
,
abstract(
抽象,参下文
)
,
final(
阻止在子类中覆盖
—
也称重载,阻止被继承,用于修饰类名及方法,如
final class test{ final function fun(){}}
,但不能用于属性
),static(
静态
)
九:抽象类和抽象方法(
abstract——
注意:没有所谓抽象属性)
:
抽象可以理解成父类为子类定义了一个模板或基类。作用域
abstract
只在父类中声明,但在子类中实现。注意事项:
1
、抽象类不能被实例化,只能被子类(具体类)继承后实现。
2
、抽象类必须在其子类中实现该抽象类的所有抽象方法。否则会出错。
3
、在抽象方法中,只是声明,但不能具体实现:如
abstract function gettow(){ return $this->p; }
是错的,只能声明这个方法:
abstract function gettow();
(连方括号
{}
都不要出现)
,
抽象方法和抽象类主要用于复杂的类层次关系中。该层次关系需要确保每一个子类都包含并重载了某些特定的方法。这也可以通过接口实现
4
、属性不能被命名为抽象属性,如
abstract $p = 5
是错的。
5
、只有声明为抽象的类可以声明抽象方法,但如果方法声明为抽象,就不能具体实现。如:
abstract class Employee
{
abstract function a(...);
abstract function b(...);
}
以后再对这个父类扩展,组成各种子类(如经理,员工,出纳)。
6
、抽象类中,如果要实现具体的方法,不能声明为抽象。这样可能实际意义更大。可以把几个类库中共同的部分提取到抽象类中,其它的类继承抽象类即可。如下:
abstract class BaseShop{
Const TAX=0.06; //
在抽象类中定义常量
public function buy($gid) { //
如果定义为抽象方法
abstract function buy
()就不能在这里实现主体。
echo('
你购买了
ID
为
:'.$gid.'
的商品
');
}
public function sell($gid) {
echo('
你卖了
ID
为
:'.$gid.'
的商品
');
}
public function view($gid) {
echo('
你查看了
ID
为
:'.$gid.'
的商品
');
}
}
class BallShop extends BaseShop{
var $itme_id = null;
public function __construct()
{
$this->itme_id = 2314;
}
public function open()
{
$this->sell($this->itme_id);
}
public function getTax()
{
echo printf('<h3>
平均税率是
%d%%
。
</h3>',$this::TAX*100);
}
}
$s = new BallShop;
$s->open(); //
你卖了
ID
为
:2314
的商品
$shop->getTax();
十:类型提示:
注意,类型提示功能只能用于参数为对象的提示,而无法用于为整数,字串,浮点等类型提示。有些类的方法需要传入的参数为所期望的对象类型,可以用下面的方法达到强制实施此替则。要达到类型提示,只要在方法的对象型参数前加一个已存在的类的名称,如:
function funname(OtherClassName $otherclassINSName,$c....)
,注意,
OtherClassName
必须是存在的类。如下:
class em{ var $k=56; }
class test{
function __construct()
{ echo $this->addab(new em(),2); }
function addab(em $j,$c) //
这个方法,即可以在内部调用,也可以在外部调用。只要作用域许可。
{ return $j->k+$c; }
}
$a = new test();
$b = new em();
echo $a->addab($b,2); //
或
$a->addab(new em(),2);
十一、类的管理:
1
、
instanceof
关键字:用于分析一个对象是否是某一个类的实例或子类或是实现了某个特定的接口:如下例,但要注意:
类名没有任何引号等定界符,否则会出错。如
test
不能用
'test'
class test2{}
class test{}
class testChilern Extends test{}
$a = new test2();
$m = new test();
$i = ($m instanceof test);
if($i)echo '$m
是类
test
的实例!
<br />'; // get this value
switch ($a instanceof test){
case true :
echo 'YES<br />';
break;
case false :
echo 'No<br />'; //get this value
break;
}
$d=new testChilern();
if($d instanceof test)echo '$d
是类
test
的子类!
<br />'; // get this value
2
、确定类是否存在:
boolean class_exists(string class_name): class_exists('test');
3
、返回类名:
string get_class(object)
,成功时返回实例的类名,失败则返回
FALSE
:
$a = new test2(); echo get_class($a); //
返回
test2
4
、了解类的公用属性:
array get_class_vars('className') ,
返回关键数组:包含所有定义的
public
属性名及其相应的值。这个函数不能用实例名做变量
5
、返回类方法:
get_class_methods('test'); //
或:
get_class_methods($a);
可用实例名做参数,返回包括构造函数在内的所有非私有方法。
6
、
print_r(get_declared_classes())
了解当前
PHP
版本中所有的类名。
PHP5
有
149
个。
7
、
get_object_vars($a)
返回实例中所有公用的属性及其值的关联数组。注意它和
get_class_vars()
的区别:
/* (1) get_object_vars($a)
是用实例名做参数,而
get_class_vars('test')
是用类名做参数。
* (2) get_object_vars($a)
获得的属性值是实例运行后的值,而
get_class_vars('test')
获得的属性值是类中的初始定义。
* (3)
两者均返回关联数组,且均对未赋值的属性返回
NULL
的值。如类
test
中有定义了
public $q;
则返回
Array ( [v] => 5 [q]=>) ,
*/
8
、返回父类的名称:
get_parent_class($b);//
或
get_parent_class('test2');
返回
test
9
、确定接口是否存在:
boolean interface_exists($string interface[,boolean autoload])
10
、确定对象类型:
boolean is_a($obj,'className')
,当
$obj
属于
CLASSNAME
类时,或属于其子类时,返回
TRUE
,如果
$obj
与
class
类型无关则返回
FALSE
。如:
is_a($a,'test')
11
、确定是否是某类的子对象:当
$b
是继承自
TEST
类时,返回
TRUE
,否则
FALSE
。
boolean is_subclass_of($b,'test');
12
、确定类或实例中,是否存在某方法。
method_exists($a,'getv') //
或用
method_exists('test','getv')
,此函数适用于非
public
定义的作用域的方法。
以上函数实例:
class test{
public $v=2;
private $c=5;
function __construct(){
$this->v=5;
}
private function getv(){
return $this->v;
}
}
class test2 extends test{}
$a=new test();
$b=new test2();
print_r( get_class_methods('test')); //
或:
print_r( get_class_methods($a));
均返回:
Array ( [0] => __construct [1] => getv )
echo '<br />';
print_r( get_class_vars('test')); //
返回:
Array ( [v] => 2 )
,和上面不一样,不能用
print_r( get_class_methods($a));
echo '<br />';
echo get_parent_class($b);//
或
get_parent_class('test2');
返回
test
echo '<br />';
echo is_a($b,'test');//
返回
1
echo '<br />';
if(is_subclass_of('test2','test'))echo '
是子类!
'; //
或
(is_subclass_of($b,'test')),
返回
1
,当参数
1
为
$a
时则返回
false,
echo '<br />';
echo method_exists($a,'getv') //
或用
method_exists('test','getv')
返回
1,
本函数也适用于用
private
等定义域的方法。
十一、自动加载类库文件:
当类多了以后,比如要在一个文件中载入
3
个类库文件:
a.class.php,b.class.php,c.class.php
要用三个
require_once('classes/a.class.php);
require_once('classes/b.class.php);
require_once('classes/c.class.php);
可以用
PHP5
自动加载的功能来处理:在全局应用配置文件中,定义一个特殊的函数
__autoload($class)
函数(
__autoload
并不是一个类的方法,只是单独的函数,和类没有关系):
function __autoload($class){
require_once("classes/$class)
}
该函数放哪没有关系,在创建类实例时,也不必去调用这个
autoload
函数。
PHP
会自动完成。但务必注意一点:
“
在调用页面上创建实例所使用的类名称
”
、和
“
被调用的文件名
”
、以及
“
该文件中的类的名称
”3
个必须是一样的。这样就不需要去调用
__autoload();
如果不一样则必须单独调用
__autoload('c');
并给它一个文件名前缀。如:
c.class.php
文件的代码是:
<?php
class c{
public $m=7;
}
?>
这里代码的类名称是
c,
而文件名也是
c
,
现在要在
index.php
调用:
<?php
function __autoload($class){
require_once "$class.class.php";
}
$m = new c(); //
创建实例调用的类也是
c
echo $m->m;
?>
此时
PHP
会自动调用根目录下的
c.class.php
中的类
C
。
但如果
c.class.php
中的代码是:
<?php
class mm{
public $m=7;
}
?>
而调用页
index.php
代码是:
<?php
function __autoload($class){
require_once "$class.class.php";
}
# __autoload('c'); //
如果不加这一行就会出错。
$m = new mm();
echo $m->m;
?>
会出错,提示找不到
mm.class.php
文件。这时可以加一行
__autoload('c');
但这样就达不到简化代码的目的。
类的家族化扩展:类的高级功能:
一、对象克隆:
当克隆一个对象的实例时,其属性初始值继承了被克隆对象的当前值。
class test
{
public $p=5;
function __clone(){ //
只在克隆发生时起作用。用于改变在克隆时某些值
$this->p=15;
}
}
$a=new test();
echo $a->p;
$a->p=8; //
如果没有
__clone()
方法影响,
$b
的
P
值将为
8
$b = clone $a;
echo $b->p; //15
二、对象继承:
没有被声明为
final
的类可以被继承,没有被
final
和
private
界定的方法也可以继承,没有被
private
界定的属性也可以继承。当子类继承了父类或超类后,可以直接使用父类或超类(祖父类以及祖父的祖父)的所有允许的方法,属性。
关键:理解构造函数和重载在继承中的特性!
(一)构造函数在继承中的特性:
1
、当父类有构造函数而子类没有:则子类会在实例化时会自动执行父类的构造函数。这时如果要创建子类的实例,需要引入父类构造函数中所需的参数,否则出错。即使是
“
子类的子类
”
如果没有构造函数,也要在创建实例时输入其父类的父类的构造函数所需参数。
PHP
会从实例所在的子类会向上搜索合造的构造函数,一旦找到就停止,使用该构造函数。而不会再向上搜索,因此:子类本身如果没有构造函数,则以其最靠近的一个超类并且有构造函数的为准。
class cA{
public $name,$age;
function __construct($n) {
$this->name = $n;
$this->age = 25;
}
function __set($n,$v) {
$this->$n = $v;
}
function __get($n) {
return $this->$n;
}
}
class cB extends cA{
function funB1() { echo '<h3>Class cB execute success!</h3>'; }
}
class cC extends cB {
function funC1() { echo '<h3>Class cC FunC1!</h3>'; }
}
$b=new cB('Jack');
$b->name='John';
echo "$b->name : $b->age";
$b->funB1();
$c=new cC(); //
这里会出错,由于
cB
也没有构造函数,因此再向上以
cA
为准,需要一个参数。改为
$c=new cC('David');
即可。
echo $c->name(); //David
2
、当子类也有构造函数时:这时,不管父类是否有构造函数,都会执行子类自己的构造函数。
如上:
class cB extends cA{
function __construct() {
echo '<h3>this is Class cB /'s __construct!</h3>';
}
function funB1() {
echo '<h3>Class cB execute success!</h3>';
}
}
现在类
CB
有自己的构造函数时,这时创建实例
$b=new cB('Jack');
参数
JACK
不会起作用,因为父类
CA
的构造函数没有得到执行。因此
$b->name
和
$->age
就不会初始化值。需要另外赋值
$b->name='Jack',$b->age=25;
如果这时要执行父类
CA
的构造函数,可以这样:
function __construct($n) {
parent::__construct($n); //
或:
cA::__construct($n);
echo '<h3>this is Class cB /'s __construct!</h3>';
}
由于
parent::__construct($n);
只会向上搜索父类的构造函数,一找到就停止且执行当前找到的构造函数,因此在上面例子中,如果
parent::__construct($n)
是用在最后一层的类
cC
中,并且类
CB,CA
都有构造函数,那么
cC
的实例只会执行
cB
的构造函数。不会执行
cA
。这时,如果
CC
的实例想都调用
CA
和
CB
的构造函数,有两种方法:
A
、在
CB
中也加入
parent::__construct($n)
B
、在
CC
中把构造函数改为:
function __construct($n) {
cA::__construct($n); //
即:类名
::
构造函数。
cB::__construct();
echo '<h3>this is Class cB /'s __construct!</h3>';
}
(二)在子类中调用父类的属性或方法:
1
、调用父类方法:在子类中调用父类的方法,有
3
种方法:
$this->ParentFunction();
或
父类名
::ParentFunction();
或
parent::parentFun();
2
、调用父类属性:只能用
$this->ParentProperty;
(三)重载:
在子类中,可以定义与父类相同属性或方法,改变父类该属性或方法的值或操作,称做重载。如:
calss ParClass{ function pfun(){ ....}}
class ChildrenClass extends ParClass{function pfun(){ ....}}} //
重载了父类的
pfun
的方法。
在子类中重载后,优先执行自己重载后的新定义的方法或属性。
也可以在子类中用
parent::parentFun();
调用父类的方法,但所得到的值是子类自己输入的参数运算值。而不是该方法在父类中运算的值。
三、接口:
接口:
interface
,可以理解成一组功能的共同规范,最大意义可能就是在多人协作时,为各自的开发规定一个共同的方法名称。
和抽象类中的抽象方法一样:
1
、不能在接口中对方法具体实现进行定义。而是由具体类来实现(而抽象类中的非抽象方法可以不必再定义,只有抽象方法和接口是一样要求要在具体类中实现)。
2
、和抽象类一样,可以在接口中定义常量,并由具体类直接继承。
3
、具体类必须实现抽象类的所有抽象方法(非抽象方法除外),同样,具体类如通过
implements
实现了接口后,必须完成接口中的所有方法。
接口实现过程:
1
、定义接口,
2
、用
..implement X,Y,...
和具体类对接。
interface Info{ //
定义接口
const N=22;
public function getage();
public function getname();
}
class age implements Info //
如要多个接口
class age
(
extends emJob
)
implements Info,interB...
{
public $age=15;
public $name='Join';
function getage() {
echo "
年级是
$this->age";
}
function getname() {
echo "
姓名是
$this->name";
}
function getN(){
echo '<h3>
在接口中定义的常量
N
的值是:
'.$this::N.' </h3>'; //
直接继承接口中的常量值。
}
}
$age=new age;
echo $age::N; //22
,直接调用接口中的常量值。
$age->getN();
关于抽象类和接口类的使用区分:何时用接口,何时用抽象?
1
、相关性:当创建的模型由一些紧密相关的对象采用时,用抽象。对于不相关对象采用的功能,用接口。
2
、多重继承:
PHP
类可以继承多个接口,但不能扩展多个抽象类。
3
、公共行为实现:抽象类可在其中实现公共的方法,但接口不行。
四、命名空间(
PHP6
)
类库脚本
A.inc.php
和脚本
B.inc.php
中都一个类的名称为
class CNAME
,并且这两个文件要在同一个文件如
index.php
中被调用。这时要用到命名空间。
步聚:
1
、打开上面的
A
和
B
两个文件,分别在上面的最前面各加一行:
namespace SPACEA;
和
namespace SPACEB;
名字自定。
2
、在
index.php
中实例化类时,在类的前面添加命名空间和双冒号做为前缀:
include 'a.inc.php';
include 'b.inc.php';
$a=new SPACEA::CNAME();
$b=new SPACEB::CNAME();
这样就不会冲突了。
但在
PHP6
正式发布前,这个功能还未定下来。
五、实现迭代器和迭代。
参《
PHP
圣经》
P142
;
六、使用
Reflection(
反射
)API
。
简易实例:
class a{ .... }
$c = new ReflectionClass('a'); //PHP
内置类。
echo '<pre>'.$c.'</pre>';
输出类
a
的结构和内容。参《
PHP
圣经》
P145;
(
实例化
)
:
class className{}
,调用:
$obj = new className();
当类有构造函数时,还应传入参数。如
$obj = new className($v,$v2...);
二:构造函数和析构函数
:
1
、构造函数用于初始化:使用
__construct()
,可带参数。
2
、但析构函数不能带参数(用于在销去一个类之前执行一些操作或功能)。析构函数用
__destruct()
做名称。在脚本执行结束时,
PHP
会销掉内存中的对象,因此可不用析造函数,但有些比如
COOKIE
等,就应当要用此函数销掉。
知识点:在
PHP4
中也提供了构造函数,但使用的是与类同名的类方法,在
PHP5
仍能兼容这种做法,当一个类中没有包含
__construct
时,会查找与类同名的方法,如果找到,就认为是构造函数,如下:
class test
{ var $b;
function test() { $this->b=5; }
function addab($c) { return $this->b+$c; }
}
$a = new test(); echo $a->addab(4); //
返回
9
3
、
PHP
不会自动调用父类的构造函数
(
不支持构造函数重载
)
,必须使用
parent
关键字显式地调用。
class employee{
function __construct()....
}
class Manager extents Employee{
function __construct(){
parent::_construct();
echo '
这个子类的父类构造函数调用了!
';
}
}
当然也可以调用与该实例没有任何关系的其它类的构造函数。只需在
__construct()
前加上类名即可。如:
otherClassName::__construct();
类的主家庭成员:属性、方法、常量、静态成员
4000
三、类的属性:
有两种方法对类的属性赋值或取值。
1
、使用公共作用域
public
关键词。
2
、使用
__set()
和
__get()
来分别赋值和取值,前者称为设置方法(
setter
)或修改方法
(mutator)
,后者称为访问方法
(accessor)
或获取方法
(getter)
。建议使用这种方法:优点:
A
、可在
__set
()统一进行数据验证。
B
、便于统一管理属性。
注意:
第一:
__set()
和
__get()
只对私有属性起作用,对于用
public
定义的属性,它们两个都懒理搭理,如下:
class test{
protected $a=9,$b=2,$c;
public $d;
function __set($n,$v) { $this->$n = $v+2; }
function __get($name) { return $this->$name+2; }
}
$a = new test();
$a->b =5; echo "<br />"; echo $a->b;
实例只对
$a,$b,$c
的设置会经过
__set
和
__get
过滤与返回,对于
$d
,就不会起作用。如
$a->d=5,
再返回还是
5
。
第二:
__set($n,$v)
要带两个参数。而
__get($n)
只能有一个参数。实例:
class test{
private $a=5,$b=6,$c;
function __set($n,$v)
{
if($n=='a'&&$n>0)
$this->$n = $v;
else
$this->$n = $v+2;
}
function __get($name)
{
return $this->$name; //
如果改为
return $this->$name + $this->addab();
如调用
a
的值,实际返回的是
a+a+b
的值。默认为
5+5+6=16
。
}
function addab()
{ return $this->a + $this->b; }
}
$e=new test();
$e->a = 11; //
注意写法:类的内部用
$this->$n
即变量的写法,但外部实例要用
$e->a
的方式。
$e->b = 12; //get 14
$e->k = 22;
类的属性可自由扩展,如上例的
k
,不管是否用
__set
,当一个实例建立起来后,可以用
$e->newProperty = xx;
直接来创造一个属性,但不建议这么做。
四、类的方法:
理解成类当中的函数即可。
调用:
1
、内部调用:可使用
$this->Fanname();
或
$this->addab()
或
test::addab();
2
、实例化调用时,用
$e->addab();
即可。对于在该方法中没有使用
$this
关键字的,如上例中的:
function addab() { return $this->a+$this->b; }
改为:
function addab() { return 25; }
那在在外部实例调用该方法,也可用
“$e::addab();”
或
“test::addab();”
五、类的常量:
如果类的属性理解成类中的变量,那么类的常量和变量是不一样的,其定义方法为:
class test{
private $a;
const PI = '3.14';
.....
//
在类中调用上面的常量用两种方法,
“$this::PI”,
或
“
类名
::PI”,
这里就是
test::PI,
如下:
function getvalue(){
return $this->a * $this::PI; //
或
$this->a * test::PI,
用
this
关键字或类名均可,但都要用双冒号。
}
}
$e= new test();
$e->PI =5; //
注意,这里用
->
只是创造了一个也是名为
PI
的属性,而不是改变类中的
PI
常量的值。
echo $e::PI; //
这个才是调用类的常量。
常量只能用双冒号
::
来调用。并且不能更改其值。
在类外部实例化后调用类常量同样也有两种方法。方法为:
“$e::PI”
或
“test::PI”
,共同点是都要用冒号,不同点是外部不能用
this
关键字,只能用实例名,但类名
::PI
是通用的。
六、类的静态成员(静态属性或静态方法):
如果需要创建供所有类的实例共享的字段或方法。就得用静态成员。有两个特征:
1
、静态成员是共产主义者,它让脚本上的所有该类的实例调用,但不能借助类的特定实例名调用,而是在类的外部,统一使用
“
类名
::$
成员名
”
的方式调用。而类的内部则统一使用
“self::$
成员名
”
来调用。
2
、当每一次新创建实例时,静态成员会从上次创建的实例最后值开始重新计算,而不是类中初始的值开始计算。
3
、对于用
public
定义的静态成员,可以在外部更改它的值。
private
等则不行。
class test{
public static $v = 0;
function __construct(){ self::$v++; }
static function getV(){ return self::$v; }
}
$a = new test();
echo test::getV(); //
返回
1
$b = new test();
echo test::getV(); //
返回
2
test::$v=8; //
由于
public
定义的成员,改变静态成员的值。
$c = new test();
echo test::getV(); //
返回
9
七、关键字:
(一)
this
关键字:用于类的内部指代类的本身。来访问属性或方法或常量,如
$this->
属性名或方法名。
$this::
常量名。
this
还可以用在该类的子类中,来指代本身的属性或方法。
(二)双冒号
“::”
关键字:用于调用常量、静态成员。
(三)
self
关键字
:
在类的内部与双冒号配合调用静态成员,如
self::$staticVar.
,在类的内部,不能用
$this
来调用静态成员。
(四)
__toString()
:在类中使用
__toString()
,用于将类转成字串并打印类,用处不大:如:
class test{ public $p;
public function __toString(){ return var_export($this,TRUE); }
}
$a=new test();
echo $a; //
输出:
test::__set_state(array( 'p' => NULL, ))
,或写成:
echo $a->__toString();
(五)
__clone()
:当克隆对象时,这个关键字才会发生作用,用于更改克隆时某些值。
(六)
__call()
:方法重载,参下面示例:
class cB{
function __call($method,$n){
if($method=='showVarType'){
if(is_numeric($n[0])){ //
不能用
$n
。要用
$n[0];
$this->displayNum();
}else if (is_array($n[0])){
$this->displayArr();
}else{
$this->displayOther();
}
}
}
function displayNum() {
echo '<h3>
这是数字
!</h3>';
}
function displayArr() {
echo '<h3>
这是数组
!</h3>';
}
function displayOther() {
echo '<h3>
不是数组也不是数字
!</h3>';
}
}
$x='a';
$y=array('a','b');
$b=new cB;
$b->showVarType($x); //
不是数组也不是数字
$b->showVarType($y); //
这是数组
注意,不能在类中定义
showVarType
()方法,否则代码不能用。
(七)
extends:
继承
:
如
class a{} class b extends a{}
类
b
继承了类
a
附:记忆:以后统一在调用方法或属性时用
"-> ",
调用常量则用双冒号
“::”
,不会搞晕。
八、方法和属性的作用域:
共有
6
种:
public(
默认,可省略,也等同于
php6
的
var
声明
),private
(私有,也不能由子类使用),
protected(
私有,但可由子类使用
)
,
abstract(
抽象,参下文
)
,
final(
阻止在子类中覆盖
—
也称重载,阻止被继承,用于修饰类名及方法,如
final class test{ final function fun(){}}
,但不能用于属性
),static(
静态
)
九:抽象类和抽象方法(
abstract——
注意:没有所谓抽象属性)
:
抽象可以理解成父类为子类定义了一个模板或基类。作用域
abstract
只在父类中声明,但在子类中实现。注意事项:
1
、抽象类不能被实例化,只能被子类(具体类)继承后实现。
2
、抽象类必须在其子类中实现该抽象类的所有抽象方法。否则会出错。
3
、在抽象方法中,只是声明,但不能具体实现:如
abstract function gettow(){ return $this->p; }
是错的,只能声明这个方法:
abstract function gettow();
(连方括号
{}
都不要出现)
,
抽象方法和抽象类主要用于复杂的类层次关系中。该层次关系需要确保每一个子类都包含并重载了某些特定的方法。这也可以通过接口实现
4
、属性不能被命名为抽象属性,如
abstract $p = 5
是错的。
5
、只有声明为抽象的类可以声明抽象方法,但如果方法声明为抽象,就不能具体实现。如:
abstract class Employee
{
abstract function a(...);
abstract function b(...);
}
以后再对这个父类扩展,组成各种子类(如经理,员工,出纳)。
6
、抽象类中,如果要实现具体的方法,不能声明为抽象。这样可能实际意义更大。可以把几个类库中共同的部分提取到抽象类中,其它的类继承抽象类即可。如下:
abstract class BaseShop{
Const TAX=0.06; //
在抽象类中定义常量
public function buy($gid) { //
如果定义为抽象方法
abstract function buy
()就不能在这里实现主体。
echo('
你购买了
ID
为
:'.$gid.'
的商品
');
}
public function sell($gid) {
echo('
你卖了
ID
为
:'.$gid.'
的商品
');
}
public function view($gid) {
echo('
你查看了
ID
为
:'.$gid.'
的商品
');
}
}
class BallShop extends BaseShop{
var $itme_id = null;
public function __construct()
{
$this->itme_id = 2314;
}
public function open()
{
$this->sell($this->itme_id);
}
public function getTax()
{
echo printf('<h3>
平均税率是
%d%%
。
</h3>',$this::TAX*100);
}
}
$s = new BallShop;
$s->open(); //
你卖了
ID
为
:2314
的商品
$shop->getTax();
十:类型提示:
注意,类型提示功能只能用于参数为对象的提示,而无法用于为整数,字串,浮点等类型提示。有些类的方法需要传入的参数为所期望的对象类型,可以用下面的方法达到强制实施此替则。要达到类型提示,只要在方法的对象型参数前加一个已存在的类的名称,如:
function funname(OtherClassName $otherclassINSName,$c....)
,注意,
OtherClassName
必须是存在的类。如下:
class em{ var $k=56; }
class test{
function __construct()
{ echo $this->addab(new em(),2); }
function addab(em $j,$c) //
这个方法,即可以在内部调用,也可以在外部调用。只要作用域许可。
{ return $j->k+$c; }
}
$a = new test();
$b = new em();
echo $a->addab($b,2); //
或
$a->addab(new em(),2);
十一、类的管理:
1
、
instanceof
关键字:用于分析一个对象是否是某一个类的实例或子类或是实现了某个特定的接口:如下例,但要注意:
类名没有任何引号等定界符,否则会出错。如
test
不能用
'test'
class test2{}
class test{}
class testChilern Extends test{}
$a = new test2();
$m = new test();
$i = ($m instanceof test);
if($i)echo '$m
是类
test
的实例!
<br />'; // get this value
switch ($a instanceof test){
case true :
echo 'YES<br />';
break;
case false :
echo 'No<br />'; //get this value
break;
}
$d=new testChilern();
if($d instanceof test)echo '$d
是类
test
的子类!
<br />'; // get this value
2
、确定类是否存在:
boolean class_exists(string class_name): class_exists('test');
3
、返回类名:
string get_class(object)
,成功时返回实例的类名,失败则返回
FALSE
:
$a = new test2(); echo get_class($a); //
返回
test2
4
、了解类的公用属性:
array get_class_vars('className') ,
返回关键数组:包含所有定义的
public
属性名及其相应的值。这个函数不能用实例名做变量
5
、返回类方法:
get_class_methods('test'); //
或:
get_class_methods($a);
可用实例名做参数,返回包括构造函数在内的所有非私有方法。
6
、
print_r(get_declared_classes())
了解当前
PHP
版本中所有的类名。
PHP5
有
149
个。
7
、
get_object_vars($a)
返回实例中所有公用的属性及其值的关联数组。注意它和
get_class_vars()
的区别:
/* (1) get_object_vars($a)
是用实例名做参数,而
get_class_vars('test')
是用类名做参数。
* (2) get_object_vars($a)
获得的属性值是实例运行后的值,而
get_class_vars('test')
获得的属性值是类中的初始定义。
* (3)
两者均返回关联数组,且均对未赋值的属性返回
NULL
的值。如类
test
中有定义了
public $q;
则返回
Array ( [v] => 5 [q]=>) ,
*/
8
、返回父类的名称:
get_parent_class($b);//
或
get_parent_class('test2');
返回
test
9
、确定接口是否存在:
boolean interface_exists($string interface[,boolean autoload])
10
、确定对象类型:
boolean is_a($obj,'className')
,当
$obj
属于
CLASSNAME
类时,或属于其子类时,返回
TRUE
,如果
$obj
与
class
类型无关则返回
FALSE
。如:
is_a($a,'test')
11
、确定是否是某类的子对象:当
$b
是继承自
TEST
类时,返回
TRUE
,否则
FALSE
。
boolean is_subclass_of($b,'test');
12
、确定类或实例中,是否存在某方法。
method_exists($a,'getv') //
或用
method_exists('test','getv')
,此函数适用于非
public
定义的作用域的方法。
以上函数实例:
class test{
public $v=2;
private $c=5;
function __construct(){
$this->v=5;
}
private function getv(){
return $this->v;
}
}
class test2 extends test{}
$a=new test();
$b=new test2();
print_r( get_class_methods('test')); //
或:
print_r( get_class_methods($a));
均返回:
Array ( [0] => __construct [1] => getv )
echo '<br />';
print_r( get_class_vars('test')); //
返回:
Array ( [v] => 2 )
,和上面不一样,不能用
print_r( get_class_methods($a));
echo '<br />';
echo get_parent_class($b);//
或
get_parent_class('test2');
返回
test
echo '<br />';
echo is_a($b,'test');//
返回
1
echo '<br />';
if(is_subclass_of('test2','test'))echo '
是子类!
'; //
或
(is_subclass_of($b,'test')),
返回
1
,当参数
1
为
$a
时则返回
false,
echo '<br />';
echo method_exists($a,'getv') //
或用
method_exists('test','getv')
返回
1,
本函数也适用于用
private
等定义域的方法。
十一、自动加载类库文件:
当类多了以后,比如要在一个文件中载入
3
个类库文件:
a.class.php,b.class.php,c.class.php
要用三个
require_once('classes/a.class.php);
require_once('classes/b.class.php);
require_once('classes/c.class.php);
可以用
PHP5
自动加载的功能来处理:在全局应用配置文件中,定义一个特殊的函数
__autoload($class)
函数(
__autoload
并不是一个类的方法,只是单独的函数,和类没有关系):
function __autoload($class){
require_once("classes/$class)
}
该函数放哪没有关系,在创建类实例时,也不必去调用这个
autoload
函数。
PHP
会自动完成。但务必注意一点:
“
在调用页面上创建实例所使用的类名称
”
、和
“
被调用的文件名
”
、以及
“
该文件中的类的名称
”3
个必须是一样的。这样就不需要去调用
__autoload();
如果不一样则必须单独调用
__autoload('c');
并给它一个文件名前缀。如:
c.class.php
文件的代码是:
<?php
class c{
public $m=7;
}
?>
这里代码的类名称是
c,
而文件名也是
c
,
现在要在
index.php
调用:
<?php
function __autoload($class){
require_once "$class.class.php";
}
$m = new c(); //
创建实例调用的类也是
c
echo $m->m;
?>
此时
PHP
会自动调用根目录下的
c.class.php
中的类
C
。
但如果
c.class.php
中的代码是:
<?php
class mm{
public $m=7;
}
?>
而调用页
index.php
代码是:
<?php
function __autoload($class){
require_once "$class.class.php";
}
# __autoload('c'); //
如果不加这一行就会出错。
$m = new mm();
echo $m->m;
?>
会出错,提示找不到
mm.class.php
文件。这时可以加一行
__autoload('c');
但这样就达不到简化代码的目的。
类的家族化扩展:类的高级功能:
一、对象克隆:
当克隆一个对象的实例时,其属性初始值继承了被克隆对象的当前值。
class test
{
public $p=5;
function __clone(){ //
只在克隆发生时起作用。用于改变在克隆时某些值
$this->p=15;
}
}
$a=new test();
echo $a->p;
$a->p=8; //
如果没有
__clone()
方法影响,
$b
的
P
值将为
8
$b = clone $a;
echo $b->p; //15
二、对象继承:
没有被声明为
final
的类可以被继承,没有被
final
和
private
界定的方法也可以继承,没有被
private
界定的属性也可以继承。当子类继承了父类或超类后,可以直接使用父类或超类(祖父类以及祖父的祖父)的所有允许的方法,属性。
关键:理解构造函数和重载在继承中的特性!
(一)构造函数在继承中的特性:
1
、当父类有构造函数而子类没有:则子类会在实例化时会自动执行父类的构造函数。这时如果要创建子类的实例,需要引入父类构造函数中所需的参数,否则出错。即使是
“
子类的子类
”
如果没有构造函数,也要在创建实例时输入其父类的父类的构造函数所需参数。
PHP
会从实例所在的子类会向上搜索合造的构造函数,一旦找到就停止,使用该构造函数。而不会再向上搜索,因此:子类本身如果没有构造函数,则以其最靠近的一个超类并且有构造函数的为准。
class cA{
public $name,$age;
function __construct($n) {
$this->name = $n;
$this->age = 25;
}
function __set($n,$v) {
$this->$n = $v;
}
function __get($n) {
return $this->$n;
}
}
class cB extends cA{
function funB1() { echo '<h3>Class cB execute success!</h3>'; }
}
class cC extends cB {
function funC1() { echo '<h3>Class cC FunC1!</h3>'; }
}
$b=new cB('Jack');
$b->name='John';
echo "$b->name : $b->age";
$b->funB1();
$c=new cC(); //
这里会出错,由于
cB
也没有构造函数,因此再向上以
cA
为准,需要一个参数。改为
$c=new cC('David');
即可。
echo $c->name(); //David
2
、当子类也有构造函数时:这时,不管父类是否有构造函数,都会执行子类自己的构造函数。
如上:
class cB extends cA{
function __construct() {
echo '<h3>this is Class cB /'s __construct!</h3>';
}
function funB1() {
echo '<h3>Class cB execute success!</h3>';
}
}
现在类
CB
有自己的构造函数时,这时创建实例
$b=new cB('Jack');
参数
JACK
不会起作用,因为父类
CA
的构造函数没有得到执行。因此
$b->name
和
$->age
就不会初始化值。需要另外赋值
$b->name='Jack',$b->age=25;
如果这时要执行父类
CA
的构造函数,可以这样:
function __construct($n) {
parent::__construct($n); //
或:
cA::__construct($n);
echo '<h3>this is Class cB /'s __construct!</h3>';
}
由于
parent::__construct($n);
只会向上搜索父类的构造函数,一找到就停止且执行当前找到的构造函数,因此在上面例子中,如果
parent::__construct($n)
是用在最后一层的类
cC
中,并且类
CB,CA
都有构造函数,那么
cC
的实例只会执行
cB
的构造函数。不会执行
cA
。这时,如果
CC
的实例想都调用
CA
和
CB
的构造函数,有两种方法:
A
、在
CB
中也加入
parent::__construct($n)
B
、在
CC
中把构造函数改为:
function __construct($n) {
cA::__construct($n); //
即:类名
::
构造函数。
cB::__construct();
echo '<h3>this is Class cB /'s __construct!</h3>';
}
(二)在子类中调用父类的属性或方法:
1
、调用父类方法:在子类中调用父类的方法,有
3
种方法:
$this->ParentFunction();
或
父类名
::ParentFunction();
或
parent::parentFun();
2
、调用父类属性:只能用
$this->ParentProperty;
(三)重载:
在子类中,可以定义与父类相同属性或方法,改变父类该属性或方法的值或操作,称做重载。如:
calss ParClass{ function pfun(){ ....}}
class ChildrenClass extends ParClass{function pfun(){ ....}}} //
重载了父类的
pfun
的方法。
在子类中重载后,优先执行自己重载后的新定义的方法或属性。
也可以在子类中用
parent::parentFun();
调用父类的方法,但所得到的值是子类自己输入的参数运算值。而不是该方法在父类中运算的值。
三、接口:
接口:
interface
,可以理解成一组功能的共同规范,最大意义可能就是在多人协作时,为各自的开发规定一个共同的方法名称。
和抽象类中的抽象方法一样:
1
、不能在接口中对方法具体实现进行定义。而是由具体类来实现(而抽象类中的非抽象方法可以不必再定义,只有抽象方法和接口是一样要求要在具体类中实现)。
2
、和抽象类一样,可以在接口中定义常量,并由具体类直接继承。
3
、具体类必须实现抽象类的所有抽象方法(非抽象方法除外),同样,具体类如通过
implements
实现了接口后,必须完成接口中的所有方法。
接口实现过程:
1
、定义接口,
2
、用
..implement X,Y,...
和具体类对接。
interface Info{ //
定义接口
const N=22;
public function getage();
public function getname();
}
class age implements Info //
如要多个接口
class age
(
extends emJob
)
implements Info,interB...
{
public $age=15;
public $name='Join';
function getage() {
echo "
年级是
$this->age";
}
function getname() {
echo "
姓名是
$this->name";
}
function getN(){
echo '<h3>
在接口中定义的常量
N
的值是:
'.$this::N.' </h3>'; //
直接继承接口中的常量值。
}
}
$age=new age;
echo $age::N; //22
,直接调用接口中的常量值。
$age->getN();
关于抽象类和接口类的使用区分:何时用接口,何时用抽象?
1
、相关性:当创建的模型由一些紧密相关的对象采用时,用抽象。对于不相关对象采用的功能,用接口。
2
、多重继承:
PHP
类可以继承多个接口,但不能扩展多个抽象类。
3
、公共行为实现:抽象类可在其中实现公共的方法,但接口不行。
四、命名空间(
PHP6
)
类库脚本
A.inc.php
和脚本
B.inc.php
中都一个类的名称为
class CNAME
,并且这两个文件要在同一个文件如
index.php
中被调用。这时要用到命名空间。
步聚:
1
、打开上面的
A
和
B
两个文件,分别在上面的最前面各加一行:
namespace SPACEA;
和
namespace SPACEB;
名字自定。
2
、在
index.php
中实例化类时,在类的前面添加命名空间和双冒号做为前缀:
include 'a.inc.php';
include 'b.inc.php';
$a=new SPACEA::CNAME();
$b=new SPACEB::CNAME();
这样就不会冲突了。
但在
PHP6
正式发布前,这个功能还未定下来。
五、实现迭代器和迭代。
参《
PHP
圣经》
P142
;
六、使用
Reflection(
反射
)API
。
简易实例:
class a{ .... }
$c = new ReflectionClass('a'); //PHP
内置类。
echo '<pre>'.$c.'</pre>';
输出类
a
的结构和内容。参《
PHP
圣经》
P145;
相关文章推荐
- 2010年最新PHP类的精缩归纳
- 2010年最新PHP类的精缩归纳
- 2010年最新PHP类的精缩归纳
- 2010年最新PHP类的精缩归纳
- 2010年最新PHP类的精缩归纳
- 2010年最新PHP类的精髓归纳第1/2页
- 2010年最新PHP类的精髓归纳第1/2页
- 【专家命题】2010年最新软考模拟题60套
- 《系统集成项目管理工程师考试考前冲刺预测卷及考点解析(2010年最新版)》
- 2010年国内外最新最全最专业的设计软件下载
- 2010年最新令人无语语录
- 2010年最新令人无语语录
- 最新2010年全国城市GDP排行前100名
- 2010年最新热门IT资源 30下载豆大礼包
- \t\t[推荐] 2010年最新的100个杯具
- 2010年计算机语言最新排名
- 2010年最新的100个免费HTML模板(附下载)
- 2010年1度全球IPv6最新变化
- PHP类的精缩归纳
- 2010年最新令人无语语录