一、三种访问控制
对属性或方法的访问控制,是通过在前面添加关键字 public、protected 或 private 来实现的。
1.public(公共成员)
public (公有的):类中的成员将没有访问限制,所有的外部成员都可以访问(读和写)这个类成员(包括成员属性和成员方法)。如果类的成员没有指定成员访问修饰符,将被视为 public (默认的)。
1)在子类中可以通过 self::属性名(或方法名) 调用public方法或属性,parent::方法名 调用父类方法
2)在实例中可以能过 $obj->属性名(或方法名) 来调用public类型的方法或属性
2.protected(保护成员)
protected(保护的): 所定义的类成员则可以被其所在类的子类和父类访问(当然,该成员所在的类也可以访问),其他地方则不可以调用。
1)在子类中可以通过 self::属性名(或方法名) 调用protected方法或属性, parent::属性名(或方法名) 调用父类方法。
2)在实例中不能通过 $obj->属性名(或方法名) 来调用 protected类型的方法或属性
//protected的使用
class Money { protected $mymoney=1000; //我有点钱数 //借出钱的方法 public function loan($num) { if ($this->mymoney >= $num) { $this->mymoney = $this->mymoney - $num; echo "好的,这里借给你 $num 元,可是我也不多了。<br />"; } else { echo "我无法借 $num 元给你,我没有这么多钱。<br />"; } } } class SMoney extends Money { public function getMoney() { return $this->mymoney; } } $mon = new SMoney(); $mon->loan(900); //借钱 echo "老婆,这里我还有..." . $mon->getMoney() . "元"; //其他人不允许访问,
输出:好的,这里借给你 900 元,可是我也不多了。
老婆,这里我还有...100元
3.private(私有成员)
private (私有的):被定义为 private 的成员,允许同一个类里的所有成员访问,但对于该类的外部代码和子类都不允许访问,可以通过$this->来调用。
1)该类型的属性或方法只能在该类中使用,
2)在该类的实例、子类中、子类的实例中都不能调用私有类型的属性和方法
class Book{ private $name = 'computer'; //声明私有变量$name public function setName($name){ //设置私有变量 $this -> name = $name; } public function getName(){ //读取私有变量 return $this -> name; } } class LBook extends Book{ //Book类的子类 } $lbook = new LBook(); //实例化对象 echo '正确操作私有变量的方法:'; $lbook -> setName("PHP从入门到精通"); echo $lbook -> getName();//对私有变量进行操作,正常 echo '<br>直接操作私有变量的结果:'; echo Book::$name;//对私有变量进行操作,出错,直接调用私有变量
例2:
class MyClass { public $public = 'Public'; protected $protected = 'Protected'; private $private = 'Private'; function printHello() { echo $this->public; echo $this->protected; echo $this->private; } } $obj = new MyClass(); echo $obj->public; // 这行能被正常执行 echo $obj->protected; // 这行会产生一个致命错误 echo $obj->private; // 这行也会产生一个致命错误 $obj->printHello(); // 输出 Public、Protected 和 Private /** * Define MyClass2 */ class MyClass2 extends MyClass { // 可以对 public 和 protected 进行重定义,但 private 而不能 protected $protected = 'Protected2'; function printHello() { echo $this->public; echo $this->protected; echo $this->private; } } $obj2 = new MyClass2(); echo $obj->public; // 这行能被正常执行 echo $obj2->private; // 未定义 private echo $obj2->protected; // 这行会产生一个致命错误 $obj2->printHello(); // 输出 Public、Protected2,但不会输出 Private
例3:
class Bar { public function test() { $this->testPrivate(); $this->testPublic(); } public function testPublic() { echo "Bar::testPublic "; } private function testPrivate() { echo "Bar::testPrivate "; } } class Foo extends Bar { public function testPublic() { echo "Foo::testPublic "; } private function testPrivate() { echo "Foo::testPrivate "; } } $myFoo = new foo(); $myFoo->test();
输出:// Bar::testPrivate
// Foo::testPublic
二、引用
1.“$this->” 和 “::”的使用
PHP 通过伪变量“this->”和作用域操作符“::”来实现调用自己或父类中的变量和方法。而且对于其他不相关的类成员同样可以调用。
“$this->”
PHP利用“对象名->方法名”来调用对象的成员方法。但在定义类时,无法得知对象的名称是什么,这时如果想调用类中的方法时,就需要用到伪变量$this->。this 的意思就是本身,所以 $this->只可以在类的内部使用。
· $this在构造函数中指该构造函数所创建的新对象。
· 在类中使用当前对象的属性和方法,必须使用$this->取值
· 方法内部的局部变量不属于对象,不适用$this关键字取值。
当类被实例化后,$this同时被实例化为本类对象,这时对$this使用get_class()函数,将返回本类的类名。实例代码如下:
class example{ function exam(){ //创建成员方法 if(isset($this)){ echo '$this的值为:'.get_class($this);//如果存在,输出this所属类的名字,如果不是对象,则返回false }else{ echo '$this未定义'; } } } $class_name = new example(); //实例化对象 $class_name->exam();
输出:$this的值为:example
例2:
class User { public $name; function getName() { echo $this->name;// 在类中使用当前对象的属性和方法 } } $user1 = new User(); $user1->name = '张三'; $user1->getName(); //这里就会输出张三! $user2 = new User(); $user2->name = '李四'; $user2->getName(); //这里会输出李四!
例3:调用其他方法
class myclass { function fun() { echo 'myfun'; } function fun1() { //$a->fun(); $a = new myclass();$a->fun(); $this->fun(); // this 是自己的意思。因为不知道你实例化之后的变量名。 } }
2.操作符“::”
相比伪变量$this只能在类的内部使用,操作符“::”更为强大,可以在没有声明任何实例的情况下访问类中的成员方法和变量。使用“::”操作符的格式为:
关键字::变量名/常量名/方法名
这里的关键字分3种情况。
· parent:可以调用父类中的成员变量、成员方法和常量
· self:可以调用当前类中的静态成员和常量
· 类名:可以调用本类中的变量、常量和方法
class Book{ const NAME = 'computer'; //常量NAME function __construct(){ //构造方法 echo '本月图书类冠军为:'.Book::NAME.' '; //调用本类中的静态变量 } } class l_book extends Book{ //Book类的子类 const NAME = 'foreign language'; //声明常量 static $name='jingtai'; //静态变量 function __construct(){ //子类的构造方法 parent::__construct(); //调用父类的构造方法 echo '本月图书类冠军为:'.self::NAME.' '; //输出本类中的默认值 echo '调用本类中的常量:'.l_book::NAME.' '; //调用本类中的常量 echo '本类中的静态成员变量:'.self::$name.' '; //本类中的静态成员变量 } } $obj = new l_book(); //实例化对象
3.static(静态)
前面讲过的静态变量,同样,属性和方法也能声明为静态,称为静态属性和静态方法。static关键字声明的属性或方法是和类相关的,而不是和类的某个特定的实例相关。因此,这类属性或方法也称为“类属性”或“类方法”。
调用静态成员的格式为:
关键字::静态成员
关键字可以是:
· self:在类的内部调用静态成员时
· 静态成员所在的类名:在类外调用类内部的静态成员时
1)静态属性
一个类的所有实例,共有类中的静态属性,也就是说,在内存中即使有多个实例,静态的属性也只有一份。访问该类时,可不必实例化,而直接使用属性名加两个冒号“::”直接访问类中static的属性。
//调用静态变量
class nowamagic { public static $nm = 1;//静态变量 function nmMethod() { self::$nm += 2; echo self::$nm . '<br />'; } } $nmInstance1 = new nowamagic(); $nmInstance1 -> nmMethod();
输出:3
2)静态方法
如果将方法声明为静态,则在类中不能使用this来引用该方法,可以使用下面两种方法引用。
· 在类中:self::静态成员名称
· 在类外:类名称::静态成员名称
在静态方法中,只能调用静态变量,而不能调用普通变量;而普通方法则可以调用静态变量。
使用静态成员,处理不需要实例化对象外,另一个作用就是在对象被销毁后,仍然保存被修改的静态数据,以便下次继续使用。下面例子说明:
//静态方法的使用
class Book{//Book类 static $num = 0;//声明一个静态变量$num,初值为0 public static function showMe(){//声明一个静态方法 echo '您是第'.self::$num.'位访客'; //输出静态变量,不能使用$this->num self::$num++; //将静态变量加1 } } $book1 = new Book(); //实例化对象$book1 $book1 -> showMe(); //调用showMe()方法 echo "<br>"; $book2 = new Book(); //实例化对象$book2; $book2 -> showMe(); //再次调用showMe()方法 echo "<br>"; echo '您是第'.Book::$num.'为访客';//直接使用类名调用静态变量,只能调用静态
输出:您是第0位访客
您是第1位访客
您是第2为访客
4.final关键字
带有final关键字的类不能被修饰或继承,也不能再有子类。如:
final class class_name{ ….}
带有final关键字的方法不能被重载或重写。如:
final function function_name{ ….}
例1:
//final 修饰的类不能被继承
final class SportObject{ //final修饰的类,不能被继承 function __construct(){ //构造方法 echo 'initialize object'; } } class MyBook extends SportObject{ //试图继承final 类 static function exam(){ echo "You can't see me"; } } MyBook::exam(); //调用子类方法,出错
例2:
//final 修饰的方法不能被重载
class Cart{ public function Cart(){ echo "正在调用Cart()<br />"; } static function doSomething(){ //static方法 echo "正在调用doSomethimg()<br />"; } } class Named_Cart extends Cart{ function Named_Cart(){ echo "正在调用Named_Cart()<br />"; } function doSomething(){ //方法重写,与父类具有相同的方法名,会出错,不能重写static方法 echo "正在调用Named_Cart::doSomething()<br />"; } } $myNamed_Cart=new Named_Cart(); $myNamed_Cart->doSomething(); //调用子类方法
三、抽象类(abstract)
抽象类是一种不能被实例化的类,只能作为其他类的父类来使用,抽象类使用abstract关键字来声明,格式为:
abstract class class_name{
……
}
抽象类和普通类相似,包含成员变量,成员方法,区别在于,抽象类至少要有一个抽象方法。抽象方法没有方法体,其功能的实现只能在子类中完成,只能被重写。被定义为抽象的方法只是声明了其调用方式(参数)。抽象方法也是用关键字abstract来 修饰的,格式为:
abstract function function_name();
注:在抽象方法后面要有分号“;”
abstract class CommodityObject{//定义抽象类 abstract function service($getName,$price,$num);//定义抽象方法 } class MyBook extends CommodityObject{//定义子类,继承抽象类 function service($getName,$price,$num){//定义方法 echo '您购买的商品是'.$getName.',该商品的价格是:'.$price.' 元。'; echo '您购买的数量为:'.$num.' 本。'; echo '如发现缺页,损坏请在3日内更换。'; } } class MyComputer extends CommodityObject{//定义子类继承父类 function service($getName,$price,$num){//定义方法 echo '您购买的商品是'.$getName.',该商品的价格是:'.$price.' 元。'; echo '您购买的数量为:'.$num.' 台。'; echo '如发生非人为质量问题,请在3个月内更换。'; } } $book = new MyBook();//实例化子类 $computer = new MyComputer();//实例化子类 $book -> service('《PHP从入门到精通》',85,3);//调用方法 echo '<p>'; $computer -> service('XX笔记本',8500,1);//调用方法
输出:
您购买的商品是《PHP从入门到精通》,该商品的价格是:85 元。您购买的数量为:3 本。如发现缺页,损坏请在3日内更换。 您购买的商品是XX笔记本,该商品的价格是:8500 元。您购买的数量为:1 台。如发生非人为质量问题,请在3个月内更换。
1.继承一个抽象类的时候,子类必须定义父类中的所有抽象方法;
2.另外,这些方法的访问控制必须和父类中一样(或者更为宽松)。例如某个抽象方法被声明为受保护的,那么子类中实现的方法就应该声明为受保护的或者公有的,而不能定义为私有的。
3.此外方法的调用方式必须匹配,即类型和所需参数数量必须一致。例如,子类定义了一个可选参数,而父类抽象方法的声明里没有,则两者的声明并无冲突。 这也适用于 PHP 5.4 起的构造函数。在 PHP 5.4 之前的构造函数声明可以不一样的。
//抽象类和方法使用2
abstract class AbstractClass { // 强制要求子类定义这些方法 abstract protected function getValue(); abstract protected function prefixValue($prefix); // 普通方法(非抽象方法) public function printOut() { print $this->getValue() . "\n"; } } class ConcreteClass1 extends AbstractClass { protected function getValue() { return "ConcreteClass1"; } public function prefixValue($prefix) { return "{$prefix}ConcreteClass1"; } } class ConcreteClass2 extends AbstractClass { public function getValue() { return "ConcreteClass2"; } public function prefixValue($prefix) { return "{$prefix}ConcreteClass2"; } } $class1 = new ConcreteClass1; $class1->printOut(); echo $class1->prefixValue('FOO_') ."\n"; $class2 = new ConcreteClass2; $class2->printOut(); echo $class2->prefixValue('FOO_') ."\n";
输出:ConcreteClass1
FOO_ConcreteClass1
ConcreteClass2
FOO_ConcreteClass2
四、接口(interface)的使用
继承特性简化了对象、类的创建,增加了代码的重用性。但PHP只是单继承,如果想实现多继承就要用到接口。PHP可以实现多个接口。
接口可以通过interface关键字来声明,并且类中只能包含为实现的方法和一些成员变量,格式如下:
interface interface_name{
function infterfaceName1();
function infterfaceName2();
………
}
· 不要使用public以外的关键字来修饰接口中的类成员,对于方法,不写关键字也可以。这是由接口类自身的属性决定的。
· 使用接口(interface),你可以指定某个类必须实现哪些方法,但不需要定义这些方法的具体内容。
· 我们可以通过interface来定义一个接口,就像定义一个标准的类一样,但其中定义所有的方法都是空的。
· 接口中定义的所有方法都必须是public,这是接口的特性。
Note: 实现多个接口时,接口中的方法不能有重名。
Note: 接口也可以继承,通过使用extends操作符。
常量:接口中也可以定义常量。接口常量和类常量的使用完全相同,它们都是定值,不能被子类或子接口修改。
· 子类是通过implements关键字来实现接口的,如果要实现多个接口,那么每个接口之间应使用逗号“,”隔开。而且接口类中的所有未实现的方法需要在子类中全部实现,否则PHP将会出错。格式如下:
class Subclass implements InterfaceName1,InterfaceName2{
function infterfaceName1(){ …..功能实现代码 }
function infterfaceName2(){ …..功能实现代码 }
………
}
interface MPopedom{ function popedom(); } interface MPurview{ function purview(); } class Member implements MPurview{ function purview(){ echo '会员拥有的权限。'; } } class Manager implements MPurview,MPopedom{ function purview(){ echo '管理员拥有会员的全部权限。'; } function popedom(){ echo '管理员还有会员没有的权限'; } } $member = new Member(); $manager = new Manager(); $member -> purview(); echo '<p>'; $manager -> purview(); $manager ->popedom();
输出:会员拥有的权限。
管理员拥有会员的全部权限。管理员还有会员没有的权限
五、克隆对象
PHP5中,当对象被当做普通的数据类型来使用,如果想引用对象,需要使用“&”来声明,否则会按照PHP4中默认方式来按值传递对象。
//克隆对象
class SportObject{//类SportObject private $object_type = 'book';//声明私有变量$object_type,并赋初值等于“book” public function setType($type){ //声明成员方法setType,为变量$object_type赋值 $this -> object_type = $type; } public function getType(){//声明成员方法getType,返回变量$object_type的值 return $this -> object_type; } } $book1 = new SportObject();//实例化对象$book1 $book2 = $book1;//使用普通数据类型的方法给对象$book2赋值 $book2 -> setType('computer');//改变对象$book2的值(属于对象引用) echo '对象$book1的值为:'.$book1 -> getType();//输出对象$book1的值
输出:对象$book1的值为:computer
在PHP5中,如果需要将对象复制,也就是克隆一个对象,需要用到一个关键字clone来实现。克隆对象的格式为:
$object1=new ClassName();
$object2=clone $object1;
将例14.15中的$book2=$book1修改为$book2=clone $book1,其他不变,即可返回PHP4中的效果。
1.__clone()方法
有时候除了单纯的克隆对象外,还需要克隆出来的对象可以拥有自己的属性和方法。这时就可以使用__clone()方法来实现。__clone()方法的作用是:在克隆对象的过程中,调用__clone()方法,可以使克隆出来的对象保持自己的一些方法和属性。
class SportObject{//类SportObject private $object_type = 'book'; //声明私有变量$object_type,并赋初值等于“book” public function setType($type){//声明成员方法setType,为变量$object_type赋值 $this -> object_type = $type; } public function getType(){//声明成员方法getType,返回变量$object_type的值 return $this -> object_type; } public function __clone(){//声明__clone()方法 $this ->object_type = 'computer'; //将变量$object_type的值修改为computer } } $book1 = new SportObject();//实例化对象$book1 $book2 = clone $book1;//使用普通数据类型的方法给对象$book2赋值,克隆对象 echo '对象$book1的变量值为:'.$book1 -> getType();//输出对象$book1的值 echo '<br>'; echo '对象$book2的变量值为:'.$book2 -> getType();
输出:
对象$book1的变量值为:book
对象$book2的变量值为:computer
不难看出,对象$book2克隆了对象$book1的全部行为和属性,也拥有了属于自己的成员变量值。
2.对象比较
在实际开发过程中,需要对两个对象之间的关系是克隆还是引用,这就要用到比较运算符“==”和“===”。两个等号“==”是比较两个对象的内容,3个等号“===”是比较对象引用的地址
class SportObject{ private $name; function __construct($name){ $this -> name = $name; } } $book = new SportObject('book'); //实例化一个对象$book $cloneBook = clone $book; //克隆对象$cloneBook $referBook = $book;//引用对象$referBook if($cloneBook == $book){ echo '两个对象的内容相等<br>'; //使用==比较克隆对象和原对象 } if($referBook === $book){ echo '两个对象的引用地址相等<br>'; //使用===比较引用对象和原对象 }
输出:两个对象的内容相等
两个对象的引用地址相等
3.对象类型检测(instanceof)
instanceof操作符可以检测当前对象是属于哪个类。格式为:
ObjectName instanceof ClassName
//对象类型检测 class SportObject{} //创建空类 class MyBook extends SportObject{ //创建子类 private $type; } $cBook = new MyBook(); if($cBook instanceof MyBook) //判断对象是否属于子类mybook echo '对象$cBook属于MyBook类<br>'; if($cBook instanceof SportObject)//判断对象是否属于基类sportObject echo '对象$cBook属于SportObject类<br>';
输出:对象$cBook属于MyBook类
对象$cBook属于SportObject类
积累:
得到一个对象的类型,使用gettype()函数:
<?php echo gettype(1); // 输出integer echo gettype(array()); // 输出array ?>
得到一个对象是哪个类的实例,使用get_class()函数:
<?php $o = new stdClass(); echo get_class($o); // 输出stdClass ?>
得到一个类或对象的方法和属性,要使用反射:
<?php class MyClass { public $var; public function foo() {} } $ref = new ReflectionClass('MyClass'); $ref->getProperties(); // 会返回一组对象,用法参考PHP手册 $ref->getMethods(); // 会返回一组对象,用法参考PHP手册 $obj = new MyClass(); $ref = new ReflectionObject($obj); $ref->getProperties(); $ref->getMethods(); ?>
参考网址:
http://php.net/manual/zh/language.oop5.php
转载请注明: ITTXX.CN--分享互联网 » php基础(十七)--类和对象之访问控制、引用、抽象类、接口、克隆对象、对象类型
最后更新:2019-03-14 15:34:19