1. 类和对象
关于类的知识我们可以类比Java的语法去记忆,
类对象通过new
关键字进行实例化,
实例化的对象通过->
调用类成员;
demo:
<?php
//定义一个类
class Car {
var $name = '汽车';
function getName() {
return $this->name;
}
}
//实例化一个car对象
$car = new Car();
$car->name = '奥迪A6'; //设置对象的属性值
echo $car->getName(); //调用对象的方法 输出对象的名字
运行结果:
奥迪A6
2. 创建一个对象
类的定义方法,类通过关键字class
开头,然后是类名与花括号,在花括号
中定义类的属性与方法。
类名必须是字母
或下划线
开头,后面紧跟若干个字母、数字或下划线
,
类名最好能够表意
,可以采用名词或者英文单词。
注意区分,Java的命名规则:
名字必须以字母开头
,后面可以跟字母和数字的任意组合。
长度基本上没有限制。
但是不能使用 Java 保留字(例如,public 或 class)作为类名。
要创建一个类的实例,
可以使用new关键字创建一个对象,
也可以采用变量来创建:
<?php
//定义一个Car类并实例化一个对象
class Car {
//定义属性
public $name = '汽车';
//定义方法
public function getName() {
//方法内部可以使用$this伪变量调用对象的属性或者方法
return $this->name;
}
}
//使用new关键字创建一个对象
$car0 = new Car();
echo $car0->getName(). PHP_EOL;
echo $car0->name. PHP_EOL;
//采用变量来创建
$classname = 'Car';
$car1 = new $classname();
echo $car1->getName(). PHP_EOL;
echo $car1->name;
运行结果:
汽车
汽车
汽车
汽车
再上一个来自菜鸟教程的创建与调用的demo:
-
PHP_EOL
是换行符,同<br />
;
<?php
class Site {
/* 成员变量 */
var $url;
var $title;
/* 成员函数 */
function setUrl($par){
$this->url = $par;
}
function getUrl(){
echo $this->url . PHP_EOL;
}
function setTitle($par){
$this->title = $par;
}
function getTitle(){
echo $this->title . PHP_EOL;
}
}
$runoob = new Site;
$taobao = new Site;
$google = new Site;
// 调用成员函数,设置标题和URL
$runoob->setTitle( "菜鸟教程" );
$taobao->setTitle( "淘宝" );
$google->setTitle( "Google 搜索" );
$runoob->setUrl( 'www.runoob.com' );
$taobao->setUrl( 'www.taobao.com' );
$google->setUrl( 'www.google.com' );
// 调用成员函数,获取标题和URL
$runoob->getTitle();
$taobao->getTitle();
$google->getTitle();
$runoob->getUrl();
$taobao->getUrl();
$google->getUrl();
?>
运行结果:
菜鸟教程
淘宝
Google 搜索
www.runoob.com
www.taobao.com
www.google.com
3. 类的属性
在
类中定义的变量
称之为属性
,
通常属性
跟数据库中的字段
有一定的关联
,因此也可以称作“字段”
。属性声明
是由关键字 public,protected
或者private
开头,后面跟一个普通的变量声明
来组成
。属性的变量
可以设置初始化的默认值
,默认值
必须是常量
。
访问控制的关键字
代表的意义
为:
public:公开的
protected:受保护的
private:私有的
class Car {
//定义公共属性
public $name = '汽车';
//定义受保护的属性
protected $corlor = '白色';
//定义私有属性
private $price = '100000';
}
属性的声明
默认
都为public
,外部可以访问。一般通过
->
对象操作符 来 访问对象的属性或者方法,
对于静态属性
则使用:: 双冒号
进行访问。(这个下面第6节会详细说一下)
当在类成员方法内部调用的时候,可以使用
伪变量$this
调用当前对象的属性。
下面是简单的调用例子:
$car = new Car();
echo $car->name; //调用对象的属性
echo $car->color; //错误 受保护的属性不允许外部调用
echo $car->price; //错误 私有属性不允许外部调用
-
受保护的属性
与私有属性
不允许外部调用,
在类的成员方法内部
是可以调用
的。
class Car{
private $price = '1000';
public function getPrice() {
return $this->price; //内部访问私有属性
}
}
demo(类似第2节第一个例子):
- 注意,
类中声明类属性变量
的时候,变量名首部带$
;
而实例化后的对象
调用类属性
的时候,变量名首部不能带$
;
<?php
class Car{
//在这里定义一个共有属性name
public $name = '汽车';
}
$car = new Car();
//在这里输出$car对象的name属性
echo $car->name;
4. 定义类的方法
方法
就是在类中的function
,
很多时候我们分不清方法
与函数
有什么差别
,
在面向过程的程序设计
中function
叫做函数
,
在面向对象
中function
则被称之为方法
。
同属性
一样,类的方法也具有public,protected
以及 private
的访问控制。
访问控制的关键字代表的意义为:
public:公开的
protected:受保护的
private:私有的
- 我们可以这样定义方法:
class Car {
public function getName() {
return '汽车';
}
}
$car = new Car();
echo $car->getName();
- 使用关键字
static
修饰的,称之为静态方法
,
静态方法
不需要实例化对象
,
可以通过类名直接调用
,
操作符为双冒号::
。
class Car {
public static function getName() {
return '汽车';
}
}
echo Car::getName(); //结果为“汽车”
demo:
<?php
class Car {
public $speed = 0;
//增加speedUp方法,使speed加10
public function speedUp(){
$this->speed += 10;
}
public static function getName(){
return '汽车';
}
}
$car = new Car();
$car->speedUp();
echo $car->speed.'<br />';
echo Car::getName();
运行结果:
10
汽车
5. 构造函数和析构函数
PHP5可以在类中使用__construct()定义一个构造函数,具有构造函数的类,会在每次对象创建的时候调用该函数,因此常用来在对象创建的时候进行一些初始化工作。
class Car {
function __construct() {
print "构造函数被调用\n";
}
}
$car = new Car(); //实例化的时候 会自动调用构造函数__construct,这里会输出一个字符串
在子类中如果定义了__construct则不会调用父类的__construct,如果需要同时调用父类的构造函数,需要使用parent::__construct()显式的调用。
class Car {
function __construct() {
print "父类构造函数被调用\n";
}
}
class Truck extends Car {
function __construct() {
print "子类构造函数被调用\n";
parent::__construct();
}
}
$car = new Truck();
同样,PHP5支持析构函数,使用__destruct()进行定义,析构函数指的是当某个对象的所有引用被删除,或者对象被显式的销毁时会执行的函数。
class Car {
function __construct() {
print "构造函数被调用 \n";
}
function __destruct() {
print "析构函数被调用 \n";
}
}
$car = new Car(); //实例化时会调用构造函数
echo '使用后,准备销毁car对象 \n';
unset($car); //销毁时会调用析构函数
当PHP代码执行完毕以后,会自动回收与销毁对象,因此一般情况下不需要显式的去销毁对象。
- 下面是C++以及Java的构造和析构函数的规则(参考文章和概念引用)
- 在C++中子类继承和调用父类的构造函数方法
a. 如果子类没有定义构造方法,则调用父类的无参数的构造方法。
b. 如果子类定义了构造方法,不论是无参数还是带参数,
在创建子类的对象的时候,首先执行父类无参数的构造方法,然后执行自己的构造方法。
- Java|《为什么子类的构造方法一定会调用父类的某个构造方法》
附一个demo;
- 谈谈 java 中的构造函数
子类的构造函数中默认的第一行有一条隐式语句super(),
该语句会访问父类中的空参数构造函数,
除非父类中没有空参数的构造函数,
那么子类构造函数的第一行必须显式调用父类的构造函数,即super(int x,…) 。
demo1:
<?php
class Car {
//增加构造函数与析构函数6.
function __construct(){
print "父类的构造函数被调用 \n";
}
function __destruct(){
print "父类的析构函数被调用 \n";
}
}
class Truck extends Car{
function __construct(){
print "子类构造函数被调用 \n";
parent::__construct();
}
function __destruct(){
print "子类析构函数被调用 \n";
parent::__destruct();
}
}
// class Audi extends Car{
// function __construct(){
// parent::__construct();
// print "子类构造函数被调用 \n";
// }
// function __destruct(){
// parent::__destruct();
// print "子类析构函数被调用 \n";
// }
// }
$car = new Truck();
// $car1 = new Audi();
运行结果:
子类构造函数被调用
父类的构造函数被调用
子类析构函数被调用
父类的析构函数被调用
demo2:
<?php
class Car {
//增加构造函数与析构函数
function __construct(){
print "父类的构造函数被调用 \n";
}
function __destruct(){
print "父类的析构函数被调用 \n";
}
}
// class Truck extends Car{
// function __construct(){
// print "子类构造函数被调用 \n";
// parent::__construct();
// }
// function __destruct(){
// print "子类析构函数被调用 \n";
// parent::__destruct();
// }
// }
class Audi extends Car{
function __construct(){
parent::__construct();
print "子类构造函数被调用 \n";
}
function __destruct(){
parent::__destruct();
print "子类析构函数被调用 \n";
}
}
// $car = new Truck();
$car1 = new Audi();
运行结果:
父类的构造函数被调用
子类构造函数被调用
父类的析构函数被调用
子类析构函数被调用
由上述代码,再次可见PHP的构造和析构函数的规则与C++、Java各自不相同。
6. Static静态关键字(五道调用时要注意的地方)
静态属性与方法
可以在不实例化类
的情况下调用,
直接使用类名::方法名
的方式进行调用。静态属性
不允许对象使用->操作符
调用。
class Car {
private static $speed = 10;
public static function getSpeed() {
return self::$speed;
}
}
echo Car::getSpeed(); //调用静态方法
- 静态方法也可以通过
变量
来进行动态调用
;
$func = 'getSpeed';
$className = 'Car';
echo $className::$func(); //动态调用静态方法
静态方法中,
$this伪变量
不允许使用。可以使用
self,parent,static
在内部
调用静态方法与属性
。
-
self
用于本类中(静态 / 非静态)方法
对本类中静态属性
的调用; -
parent
用于子类中方法
对父类中静态属性
的调用;
这里题外话说一下。。。
某位大佬居然写错了个细节,还得在下调试半天。。。
demo:
<?php
class Car {
protected static $speed = 10;
//非静态方法调用静态属性*****************************
public function getSpeed() {
return self::$speed;
}
//静态方法调用静态属性*******************************
//在这里定义一个静态方法,实现速度累加10
public static function speedUp(){
return self::$speed+=10;
}
}
class BigCar extends Car {
//子类静态方法调用父类静态方法*********************
public static function start() {
return parent::speedUp();
}
//子类非静态方法调用父类静态方法******************
public function go(){
return parent::getSpeed();
}
//子类非静态方法调用父类静态属性
public function fly(){
return parent::$speed;
}
}
$car = new Car();
//类名::静态方法名***********************1
//调用静态方法
echo 'protected static $speed = 10;'."<br />"."<br />";
echo 'return self::$speed+=10; '.'Car::speedUp(): '.Car::speedUp()."<br />"."<br />";
//-------------------------------------------------
//变量名->普通方法名***********************2
//调用共有/非静态方法 输出当前的速度值
echo 'return self::$speed; '.'$car->getSpeed(): '.$car->getSpeed()."<br />"."<br />";
//-------------------------------------------------
//动态调用静态方法***********************3
$func = 'getSpeed';
$className = 'Car';
echo '$className::$func(): '.$className::$func()."<br />"."<br />";
//-------------------------------------------------
//子类
$bigcar = new BigCar();
//类名::静态方法名***********************4
//子类直接调用父类静态方法
echo 'BigCar::speedUp(): '.BigCar::speedUp()."<br />"."<br />";
echo 'BigCar::getSpeed(): '.BigCar::getSpeed()."<br />"."<br />";
//子类简接调用父类静态方法
echo 'BigCar::start(): '.BigCar::start()."<br />"."<br />";
//-------------------------------------------------
//调非静态方法***********************5
echo '$bigcar->go(): '.$bigcar->go()."<br />"."<br />";
//-------------------------------------------------
//调非静态方法***********************6
echo '$bigcar->fly(): '.$bigcar->fly()."<br />"."<br />";
运行结果:
protected static $speed = 10;
return self::$speed+=10; Car::speedUp(): 20
return self::$speed; $car->getSpeed(): 20
$className::$func(): 20
BigCar::speedUp(): 30
BigCar::getSpeed(): 30
BigCar::start(): 40
$bigcar->go(): 40
$bigcar->fly(): 40
由以上可见:
- 一个static变量 是会被所有父类和子类公用的;
- 它只会被初始化一次;
- 作为一个局部变量,作用域结束之后它没有被删除,
并且会保留
着前一次被函数调用时的值
;
再概念:
当一个函数完成时,它的所有变量通常都会被删除。
然而,有时候我们希望某个局部变量不要被删除,
这时候也即可以使用static
对变量 / 属性
进行声明;每次调用相关函数
结束后
,
该变量将会保留
着前一次被函数调用时的值
,
而不会马上被销毁
;注释:该变量仍然是
函数的局部变量
。<?php function myTest() { static $x=0; echo $x; $x++; echo PHP_EOL; // 换行符 } myTest(); myTest(); myTest(); ?>
7. 访问控制
访问控制通过关键字
public,protected
和private
来实现。
被定义为公有
的类成员可以在任何地方
被访问。
被定义为受保护
的类成员则可以被其自身
以及其子类和父类
访问。
被定义为私有
的类成员则只能被其定义所在的类
访问。类属性 必须
定义为公有、受保护、私有之一。-
为兼容PHP5以前的版本,如果采用
var 定义
,则被视为公有
。
类中的
方法
可以被定义为 公有、私有或受保护。
如果没有设置
这些关键字,则该方法默认为公有
。
class Car {
//默认为公有方法
function turnLeft() {
}
}
- 如果
构造函数
定义成了私有方法
,则不允许直接实例化对象
了,
这时候一般通过静态方法
进行实例化;
在设计模式
中会经常使用这样的方法来控制对象的创建
,
比如单例模式只允许有一个全局唯一的对象。
class Car {
private function __construct() {
echo 'object create';
}
private static $_object = null;
public static function getInstance() {
if (empty(self::$_object)) {
self::$_object = new Car(); //内部方法可以调用私有方法,因此这里可以创建对象
}
return self::$_object;
}
}
//$car = new Car(); //这里不允许直接实例化对象
$car = Car::getInstance(); //通过静态方法来获得一个实例
demo:
设计一个类中公有方法,其中调用被保护方法;
被保护方法操作了类中私有变量;
实例化对象,调用公有方法,间接对类中私有变量进行操作:
<?php
class Car {
private $speed = 0;
public function getSpeed() {
return $this->speed;
}
protected function speedUp() {
$this->speed += 10;
}
//增加start方法,使他能够调用受保护的方法speedUp实现加速10
public function start(){
$this -> speedUp();
}
}
$car = new Car();
$car->start();
echo $car->getSpeed();
运行结果:
10
8. 对象继承
建立一个Truck类,扩展Car类,并覆盖speedUp方法,速度累加50:
<?php
class Car {
public $speed = 0; //汽车的起始速度是0
public function speedUp() {
$this->speed += 10;
return $this->speed;
}
}
//定义继承于Car的Truck类
class Truck extends Car{
public function speedUp (){
$this -> speed = parent::speedUp();
return $this -> speed +=50;
}
}
$car = new Truck();
$car->speedUp();
echo $car->speed;
运行结果:
60
注意:
子类中使用$this -> 父类属性
直接继承调用父类属性;
(子类 调用 / 继承 过来了,就是子类的了)如上,
拓展父类的speedUp 方法
对继承而来的属性speed
进行操作,
实例化的子类对象
使用实例化对象->属性
调用所继承属性;(与调用自己定义的类属性无异)
9. 重载
PHP中的重载
指的是动态的创建属性与方法,是通过魔术方法来实现的。
精髓是:
不存在的属性
;
不存在的方法
;
转为调用魔术方法
;
更详细的关于PHP重载说明,还可参考 此文章;
区别一下 Java的重载 !
- 在一个类里面,方法名字相同,而参数列表不同。返回类型可以相同也可以不同。
- 每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
- 最常用的地方就是构造器的重载。
-
属性的重载
通过__set,__get,__isset,__unset
来分别实现对不存在属性的赋值、读取、判断属性是否设置、销毁属性
。
class Car {
private $ary = array();
public function __set($key, $val) {
$this->ary[$key] = $val;
}
public function __get($key) {
if (isset($this->ary[$key])) {
return $this->ary[$key];
}
return null;
}
public function __isset($key) {
if (isset($this->ary[$key])) {
return true;
}
return false;
}
public function __unset($key) {
unset($this->ary[$key]);
}
}
$car = new Car();
$car->name = '汽车'; //name属性动态创建并赋值
echo $car->name;
-
方法的重载
通过__call
来实现,
当调用不存在的方法
的时候,将会转为参数调用__call
方法,
当调用不存在的静态方法
时会使用__callStatic
重载。
class Car {
public $speed = 0;
public function __call($name, $args) {
if ($name == 'speedUp') {
$this->speed += 10;
}
}
}
$car = new Car();
$car->speedUp();
echo $car->speed;
注意,
强调一下第六节的内容:
静态方法中,$this伪变量不允许使用。
可以使用self,parent,static在内部调用静态方法与属性。
demo:
<?php
class Car {
public $speed = 10;
public static $ok = 'ok';
//在这里使用重载实现speedDown方法
public function __call($name, $args){
if($name == 'speedUp'){
$this->speed += 10;
}
if($name == 'speedDown'){
$this->speed -= 10;
}
}
public static function __callStatic($name, $args){
if($name == 'OK'){
echo self::$ok;
}
}
}
$car = new Car();
$car->speedDown(); //调用不存在的speedDown方法//会使用重载,转为参数调用__call方法
echo $car->speed.'<br />';
$car->speedUp(); //调用不存在的speedUp方法
echo $car->speed.'<br />';
Car::OK();//当调用不存在的静态方法时会使用__callStatic重载。
运行结果:
0
10
ok
10. 对象的高级特性
- 对象比较,
当同一个类的两个实例
的所有属性
都相等
时,可以使用比较运算符==
进行判断,
当需要判断两个变量是否为同一个对象的引用
时,可以使用全等运算符===
进行判断。
class Car {
}
$a = new Car();
$b = new Car();
if ($a == $b) echo '=='; //true
if ($a === $b) echo '==='; //false
- 对象复制,
在一些特殊情况下,可以通过关键字clone
来复制一个对象
,
这时__clone
方法会被调用,通过这个魔术方法来设置属性的值。
class Car {
public $name = 'car';
public function __clone() {
$obj = new Car();
$obj->name = $this->name;
}
}
$a = new Car();
$a->name = 'new car';
$b = clone $a;
var_dump($b);
- 对象序列化,
可以通过serialize
方法将对象序列化为字符串,
用于存储或者传递数据
,
然后在需要的时候通过unserialize
将字符串反序列化
成对象
进行使用。
class Car {
public $name = 'car';
}
$a = new Car();
$str = serialize($a); //对象序列化成字符串
echo $str.'<br>';
$b = unserialize($str); //反序列化为对象
var_dump($b);
demo:
<?php
class Car {
public $name = 'car';
public function __clone() {
$obj = new Car();
$obj->name = $this->name;
}
}
$a = new Car();
$a->name = 'new car';
$b = clone $a;
if ($a == $b) echo '=='.'<br>'.'<br>'; //true
if ($a === $b) echo '==='.'<br />'.'<br>'; //false
$str = serialize($a); //对象序列化成字符串
echo $str.'<br>'.'<br>';
$c = unserialize($str); //反序列化为对象
var_dump($c);
echo '<br>'.$c->name;
运行结果:
==
O:3:"Car":1:{s:4:"name";s:7:"new car";}
object(Car)#3 (1) {
["name"]=>
string(7) "new car"
}
new car