欢迎您的光临,本博所发布之文章皆为作者亲测通过,如有错误,欢迎通过各种方式指正。

教程  php基础(十七)--类和对象之访问控制、引用、抽象类、接口、克隆对象、对象类型

PHP学习 本站 865 0评论

一、三种访问控制


对属性或方法的访问控制,是通过在前面添加关键字 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为访客

777.jpg


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(){   …..功能实现代码   }

………

}

333.jpg

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中默认方式来按值传递对象。

444.jpg

//克隆对象

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

555.jpg


在PHP5中,如果需要将对象复制,也就是克隆一个对象,需要用到一个关键字clone来实现。克隆对象的格式为:

$object1=new  ClassName();

$object2=clone  $object1;

将例14.15中的$book2=$book1修改为$book2=clone $book1,其他不变,即可返回PHP4中的效果。


1.__clone()方法


有时候除了单纯的克隆对象外,还需要克隆出来的对象可以拥有自己的属性和方法。这时就可以使用__clone()方法来实现。__clone()方法的作用是:在克隆对象的过程中,调用__clone()方法,可以使克隆出来的对象保持自己的一些方法和属性。


666.jpg

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个等号“===”是比较对象引用的地址

888.jpg

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

999.jpg

//对象类型检测
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

赞 (2) or 分享 ()
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽