您的位置:首页 > 编程语言 > PHP开发

PHP基础知识系列:面向对象基础

2012-09-08 13:43 866 查看
面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)是一种计算机编程架构,OOP的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成,OOP达到了软件工程的三个目标:重用性、灵活性和扩展性。为了实现整体运算,每个对象都能够接收信息、处理数据和向其它对象发送信息。首先,面向对象符合人类看待事物的一般规律。其次,采用面向对象方法可以使系统各部分各司其职、各尽所能。为编程人员敞开了一扇大门,使其编程的代码 更简洁、更易于维护,并且具有更强的可重用性。

1.类与对象类是生成对象的模板,对象是根据类中定义的模板所 构造的数据,即对象是类的实例。

  类:是具有相同属性和方法的一组对象的集合。它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和方法两个主要部分。在面向对象的编程语言中,类是一个独立的程序单元,它应该有一个类名并包括属性说明和方法说明两个主要部分。

  对象:是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。一个对象由一组属性和对这组属性进行操作的一组服务组成。

class ShopProduct{
  //类体
}


生成ShopProduct对象:$product1是类的实例。

$product1 = new ShopProduct();
$product2 = new ShopProduct();


  

  代码就是通过类产生实例对象的过程,$product1就是我们实例出来的对象名称, 同理,$product2也是我们实例出来的对象名称,一个类可以实例出多个对象,每个对象都是独立的,实例化出来的对象里面就包含了这些属性和方法。
  对象在PHP里面和整型、浮点型一样,也是一种数据类,都是存储不同类型数据用的,在运行的时候都要加载到内存中去用, 那么对象在内存里面是怎么体现的呢?内存从罗辑上说大体上是分为4段, 栈空间段堆空间段代码段 初使化静态段
程序里面不同的声明放在不同的内存段里面,栈空间段是存储占用相同空间长度并且占用空间小的数据类型的地方,比如说整型1, 10, 100,
1000, 10000, 100000等等,在内存里面占用空间是等长的,都是64位4个字节。
那么数据长度不定长,而且占有空间很大的数据类型的数据放在那内存的那个段里面呢?这样的数据是放在堆内存里面的。栈内存是可以直接存取的,而堆内存是不
可以直接存取的内存。对于我们的对象来说就是一种大的数据类型而且是占用空间不定长的类型,所以说对象是放在堆里面的,但对象名称是放在栈里面的,这样通
过对象名称就可以使用对象了。

$product1 = new ShopProduct();

对于这个条代码, $product1是对象名称在栈内存里面,new ShopProduct();是真正的对象是在堆内存里面的,所以$product1是存储对象首地址的变量,$product1放在栈内存里边,$product1相当于一个指针指向堆里面的对象, 所以我们可以通过$product1这个引用变量来操作对象。

2.对象属性和方法的使用:对象里面的成员使用对象->属性对象->方法 形式访问,没有第二种方法来访问对象中的成员。

3.$this:现在我们知道了如何访问对象中的成员,是通过”对象->成员”的方式访问的,这是在对象的外部去访问对象中成员的形式,那么如果我想在对象的内部,让对象里的方法访问本对象的属性。因为对象里面的所有的成员都要用对象来调用,包括对象的内部成员之间的调用,PHP提供了一个本对象的引用$this, 每个对象里面都有一个对象的引用$this来代表这个对象,完成对象内部成员的调用, this的本意就是“这个”的意思, 上面的实例里面,我们实例化三个实例对象$product1、 $product2,这二个对象里面各自存在一个$this分别代表对象。


4.__construct()与__destruct:

构造函数:在创建对象时,构造方法会被自动调用。构造方法可以用来确保必要的属性被设置。

BookProduct 子类

class BookProduct extends ShopProduct{
  private $numPages= 0;
  public function __construct($title,$firstName,$mainName,$price,$numPages){
  //获取父类的初始化参数
    parent::__construct($title,$firstName,$mainName,$price);
    //私有的初始化参数
    $this->numPages= $numPages;
  }

  public function getNumbersOfPages(){
    return $this->numPages= $numPages;
  }

  public function getSummaryLine(){
    $base = parent::getSummaryLine();
    $base .= ":page count -{$this->numPages}";
    return $base;
  }
}


9.final:关键字可以终止类的继承。final类不能有子类,final方法不能被覆盖。这个关键字只能用来定义和定义方法, 不能使用final这个关键字来定义成员属性,因为final是常量的意思,我们在PHP里定义常量使用的是define()函数,所以不能使用final来定义成员属性。

使用final关键标记的类不能被继承:

final class Checkout{

}


这是一个final类,下面尝试继承:

class IllegalCheckout extends Checkout{

}


会产生致命错误.

final方法不能被继承:

class Checkout{
  final function totalize(){

  }
}


继承:

class IllegalCheckout extends Checkout{
  final function totalize(){
  //重载这个方法
  }
}


也会导致错误。

10.__toString()

  如果你打印一个对象,PHP就会把对象解析成一个字符串来输出。“__toString()”方法也是一样自动被调用的,是在直接输出对象引用时自动调用的, 前面我们讲过对象引用是一个指针,比如 说:“$p=new StringThing()“中,$p就是一个引用,我们不能使用echo 直接输出$p, 这样会输 出”Catchable fatal error: Object of class StringThing could not be converted to string “这样的错误。

class StringThing{}

$st = new StringThing();
print $st;


这样毁产生上面的错误,但是通过实现自己的__toString()方法,可以控制输出字符串的格式,__toString()方法返回字符串。

class Person{
  function getName(){return "Bob";}
  function getAge(){return 44;}

  function __toString(){
    $desc = $this->getName();
    $desc .= " (age ".$this->getAge().")";
    return $desc;
  }
}


此时,打印返回出自定义的内容

$person = new Person();
print $person;//Bob (age 44)


11.__clone():

  复杂对象只是简单地将一个变量赋值给另一个变量。

class CopyMe{}

$first = new CopyMe();
$second = $first;


在PHP4中,$second和$first是两个完全不同的对象。我们无法检查两个变量是否指向相同的对象。

在PHP5中,$second和$first指向同一个对象。运行PHP5的代码时,两个变量指向同一个引用,没有各自保留一份相同的副本,有时候是需要的。

PHP5中提供了clone关键字:

class CopyMe{}

$first = new CopyMe();
$second = clone $first;//现在的$second和$first是不同的两个对象了


但是此时还有问题,每一个Person对象都有一个标示$id,如果这个对象$id对应的是数据库中的一条信息,可能出现的情况是,两个完全不同的对象指向数据库中的一条记录,此时,对一个对象的操作就会印象到另一个对象了。如果我们可以控制对象复制的内容,那么久可以避免上面的问题了。这就是__clone()的用途:

浅复制:

Person{
  private $name;
  private $age;
  private $id;

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

  function setId($id){
    $this->id = $id;
  }
  //这个函数在赋值的对象上运行
  function __clone(){//将复制的对象id设置为0
    $this->id = 0;
  }
}


当在一个Person对象上调用clone时,产生一个新的副本,并且新副本的__clone()方法会被调用:

$person = new Person("bob",44);
$person->setId(343);
$person2 = clone $person;//此时的person2:name:bob age:44 id:0


深复制:以上的浅复制,可以保证所有基本数据类型的属性被完全赋值。在复制对象属性时只复制引用,并不复制引用的对象。如果对象有一个$balance属性,可以将该属性也复制给新副本,那么此时就有两个Person对象同时引用一个$balance。对一个操作,就以为着另一个也改变了。

class Account{//账单
  public $balance;//余额
  function __construct($balance){
    $this->balance = $balance;
  }
}


下面是Person对象,且引用了Account对象

Person{
  private $name;
  private $age;
  private $id;

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

  function setId($id){
    $this->id = $id;
  }
  //这个函数在赋值的对象上运行
  function __clone(){//将复制的对象id设置为0
    $this->id = 0;
  }
}


当复制这个对象是:

$person = new Person("bob",44,new Accoun(200));
$person->setId(343);
$person2 = clone $person;
//给person充一些钱
$person->account->balance +=20;
//结果是$person2也被充了钱
print $person2->account->balance;//220


解决方法:

  //这个函数在赋值的对象上运行
  function __clone(){//将复制的对象id设置为0
    $this->id = 0;
    $this->account = clone $account;//深度clone
  }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: