面向对象的三个特点:继承、封装和多态
类 是对 对象 的 抽象,在 类中 可以定义 对象的 属性和方法 的描述;
对象 是 类 的实例,类 只有被实例化后才能被使用。
instanceof (1)判断一个对象是否是某个类的实例,(2)判断一个对象是否实现了某个接口。
// 用于检测当前对象属于哪个类
$obj = new A();
if ($obj instanceof A) {
echo 'A';
}
一般在调用类的属性和方法的时候会使用:$this->name 或 $this->name()来完成。下面通过一个简单的例子来说明一下$this->$name的这种用法。
class Test{
public $name = "abc";
public $abc = "test";
public function Test(){
$name1 = "name";
echo $this->name; // 输出 abc
echo $this->$name1; // 输出 abc,因为 $name1 的值是name,相当与这里替换成 echo $this->name;
$name2 = $this->$name1; // $name2 的值是 abc
echo $this->$name2; // 输出 test,同上,相当与是 echo $this->abc;
}
}
new Test();
构造方法 __construct 和 析构函数 __destruct(如果类名和方法名相同,则会当做构造函数来执行)
class BaseClass {
function __construct() {
echo "In BaseClass constructor";
}
}
class SubClass extends BaseClass {
function __construct() {
parent::__construct(); // 要执行父类的析构函数,必须在子类的析构函数体中显式调用parent::__construct()
echo "In SubClass constructor"; // 如果不引用父类的析构函数的话,则会自动调用本类的析构函数。
}
}
class OtherSubClass extends BaseClass {
// inherits BaseClass's constructor // 子类如果自己没有定义析构函数,则会继承父类的。
}
$obj = new BaseClass(); // In BaseClass constructor
$obj = new SubClass(); // In BaseClass constructor // In SubClass constructor
$obj = new OtherSubClass(); // In BaseClass constructor
成员常量 (不会改变的量,是一个恒值)
class ClassName{
const PI = 3.14159;
public function aaa(){
echo self::PI; // 类内推荐使用这种方法调用类成员属性
}
}
echo ClassName::PI; //3.14159 类外直接调用常量
$a = new ClassName();
$a->aaa(); //3.14159
私有成员 private 将数据完全隐藏起来,只能在类中被访问,类外与子类不能访问。
保护成员 protected 可以在本类和子类中被访问,其他地方则不能被调用。
class ClassName{
protected $carName = '奥迪';
}
class car extends ClassName{
public function say(){
echo '父类:'.$this->carName; //$this只能在类的内部使用,"::"可以在没有声明任何实例的情况下访问类中的成员 (如下)
}
}
$a = new car();
$a->say(); // 父类:奥迪
$b = new ClassName();
$b->carName // 会报错 因为在类外部是无法访问protected修饰的常量,得写一个方法,要用public修饰。
-
“::”操作符
关键字::变量名/常量名/方法名
parent::关键字:可以调用父类中的公共的成员变量、公共的成员方法和公共的常量。
self::关键字:可以调用当前类中的静态成员和静态常量。
类名::关键字:可以调用本类中的公共的变量、公共的常量和公共的方法。
class ClassName{
const PI = 3.14159;
public function aaa(){
return self::PI;
}
public function bbb(){
return ClassName::aaa();
}
public function ccc(){
return $this->carName;
}
protected $carName = '奥迪';
private $carNames = '宝马';
}
class car extends ClassName{
public function say(){
echo '父类:'.parent::ccc();
echo '父类:'.parent::bbb();
}
}
$a = new car();
echo $a->say(); // 父类:奥迪父类:3.14159
echo $a->bbb(); // 3.14159
echo $a->carName; // 会报错 因为在类外部是无法访问protected修饰的常量,得写一个方法,要用public修饰。
-
static关键字
关键字static修饰的成员属性称为 静态属性,成员方法称为 静态方法,它属于类本身而不属于类的任何实例。相当于存在类中的全局变量,可以在任何位置通过类名访问。如下:Web::change()
“::”称为范围解析操作符,用于访问静态成员、静态方法和常量,还可以用于覆盖类中的成员和方法。
如果要在类内部的成员方法中的访问 静态属性 或者 要在类内部的成员方法中引用 静态方法 ,则只需要“self::静态属性/静态方法”即可。如下:self::$num,self::change()
class Web{
static $num = 1;
static function change(){
echo "第".self::$num."位访客。";
self::$num++;
}
public function fun(){
echo self::change().'fun.';
}
public function webfun(){
return self::$num.'&'.Web::$num;
}
}
$web = new Web();
$web->change(); // 第1位访客。
$web->change(); // 第2位访客。
Web::change(); // 第3位访客。fun.
Web::change(); // 第4位访客。
echo Web::$num."\n"; // 第5位访客。6
echo $web->webfun(); // 6&6
-
抽象类和接口
抽象类(abstract)和接口(Interface)都不能被实例化的特殊类。注:它们都是配合面向对象的多态性一起使用的。
- 抽象类:
- 抽象类 是不能被实例化的类,只能作为其它类的父类使用。使用abstract关键字来声明。
- 抽象类 要只要包含一个抽象方法,且抽象方法中没有方法体,必须要在子类中来调用完成。注:抽象方法也是用abstract来修饰,且后面跟“;”号。
- 当子类继承抽象类以后,子类必须把父类中的抽象方法全部都实现,否则子类中还存在抽象方法,所以还是抽象类,也不能实例化对象。
- 抽象类:
abstract class Person{
protected $name;
protected $country;
public function __construct($name="",$country="china"){
$this->name = $name;
$this->country = $country;
}
abstract function say();
abstract function eat();
}
Class ChineseMan extends Person{
public function say(){
echo $this->name."是".$this->country."人";
}
public function eat(){
echo $this->name."使用筷子吃饭";
}
}
$chinaeseMan = new ChineseMan("知青"); // ChineseMan类中少Person类中两个抽象方法任何一个都会报错,必须全部实现,才能new
$chinaeseMan->say(); // 知青是china人
$chinaeseMan->eat(); // 使用筷子吃饭
- 接口:
- PHP只支持单继承,如果想要多继承,就要使用接口,PHP可以实现多个接口。
- 接口中声明的方法必须是抽象方法,接口中不能声明变量,只能使用const关键字声明为常量的成员属性,且接口中的所有成员都必须具备public的访问权限,所以public 、abstract 可不写。
- 接口中 成员只能是常量 成员方法只能是抽象方法,且在子类必须把父类中抽象方法全部实现,否则子类中还存在接口,就不能实例化对象。
- 抽象类:
interface Person1{
const SITENAME = '我在学习PHP';
function show();
function code();
}
interface Person2{ // 多个接口继承可使用extends 如: interface Person2 extends Person1
function hello();
}
class Worker implements Person1,Person2{ // 继承接口使用implements关键字,多个接口用","拼接
public function show(){
return self::SITENAME;
}
public function code(){
return '我在写代码';
}
public function hello(){
return 'PHP是非常好的一种语言';
}
}
$worker = new Worker(); // Worker类中少Person1或者Person2两个接口中任何一个方法都会报错,必须全部实现,才能new实例化对象
echo $worker->show(); // 我在学习PHP
echo $worker->code(); // 我在写代码
echo $worker->hello(); // PHP是非常好的一种语言
echo Person1::SITENAME; // 我在学习PHP
面向对象的多态
不同的对象,收到同一消息将可以产生不同的结果,这种现象称为多态性。
多态性的一般定义为:同一个操作作用于不同的类的实例,将产生不同的执行结果。即不同类的对象收到相同的消息时,将得到不同的结果。
多态即多种表现方式:,也作一个对外接口,多个内部实现方法。
实现多态有两种实现方法:
通过继承实现多态
abstract class parent1{
abstract function travel();
}
// 通过继承实现多态
class car extends parent1{
public function travel(){
echo "开着小车去旅游";
}
}
class bus extends parent1{
public function travel(){
echo "坐着大巴去旅游";
}
}
function check($obj){
if($obj instanceof car){
$obj->travel();
}elseif($obj instanceof bus){
$obj->travel();
}else{
echo "Error: 对象错误!";
}
}
check(new car()); // 开着小车去旅游
check(new bus()); // 坐着大巴去旅游
通过接口实现多态
interface parent2{
function travel();
}
class cars implements parent2{
public function travel(){
echo "开着小车去旅游";
}
}
class buss implements parent2{
public function travel(){
echo "坐着大巴去旅游";
}
}
function checkInterface($obj){
if($obj instanceof cars){
$obj->travel();
}elseif($obj instanceof buss){
$obj->travel();
}else{
echo "Error: 对象错误!";
}
}
checkInterface(new cars()); // 开着小车去旅游
checkInterface(new buss()); // 坐着大巴去旅游
面向对象的其他关键字
-
final关键字 (最终的、最后的)
两种格式:
class parent1{
final function travel(){
echo '该方法在子类中不可被重写,也不可在被覆盖';
}
}
final class parent1{
// 该类不能再被继承,也不能在有子类
}
-
clone关键字 (克隆对象)
使用clone克隆的对象与原对象没有任何关系,它是将原对象从当前位置重新复制了一份,也就是相当于在内存中新开辟了一块空间。
对象克隆成功后,它们中的成员方法、属性以及值是完全相同的。如果要为克隆后的副本对象在克隆时候重新为成员赋初始值,就要使用魔术方法 __clone
class book{
public function __construct($param){
$this->param = $param;
}
public function type(){
return $this->param;
}
public function __clone(){
return $this->param = '克隆';
}
}
$book1 = new book('原本');
echo $book1->type(); // 原本
$book2 = clone $book1;
echo $book2->type(); // 克隆
-
__set() 和 __get()方法
__set() 和 __get()方法 用于对私有成员进行 赋值 或者 获取值 的操作。
__set() 为私有的成员属性设置值,它不需要任何返回值。
__get() 在对象的外部获取私有成员属性的值,它返回一个允许对象在外部使用的值。
class Person{
private $name;
private $sex;
private $age;
public function __get($property_name){
echo "在直接获取私有属性值的时候,自动调用了这个__get()方法";
return $this->$property_name;
}
public function __set($property_name, $value){
echo "在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值";
$this->$property_name = $value;
}
}
$p1 = new Person();
$p1->name = "张三"; // 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值
$p1->age = 20; // 在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值
echo "姓名:".$p1->name; // 在直接获取私有属性值的时候,自动调用了这个__get()方法 姓名:张三
echo "性别:".$p1->sex; // 在直接获取私有属性值的时候,自动调用了这个__get()方法 性别:
echo "年龄:".$p1->age; // 在直接获取私有属性值的时候,自动调用了这个__get()方法 年龄:20
-
__isset() 和 __unset()方法
__isset() 可以对共有的成员属性进行检测,对于私有的成员属性无法检测。
如果类中存在 ”__isset()“方法,当在类外使用isset()函数检测对象中的私有成员属性时,就会自动调用“__isset()”方法完成对私有成员属性的检测。
在面向对象中,通过”unset()”函数可以对共有的成员进行 删除 操作,但是对于私有的成员属性,就必须有“__unset()”方法才能完成删除。
class Person {
private $name;
private $sex;
private $age;
public function __get($property_name) {
if(isset($this->$property_name)){
return $this->$property_name;
}else{
return '该参数不存在';
}
}
public function __set($property_name, $value) {
$this->$property_name = $value;
}
public function __isset($nm) {
echo "isset()函数测定私有成员时,自动调用";
return isset($this->$nm);
}
public function __unset($nm) {
echo "当在类外部使用unset()函数来删除私有成员时自动调用的";
unset($this->$nm);
}
}
$p1 = new Person();
$p1->name = "this is a person name";
var_dump(isset($p1->name)); // isset()函数测定私有成员时,自动调用 bool(true)
echo $p1->name; // this is a person name
unset($p1->name); // 当在类外部使用unset()函数来删除私有成员时自动调用的
echo $p1->name; // isset()函数测定私有成员时,自动调用 该参数不存在
-
__call()方法
当程序试图调用不存在或不可见的成员方法时,PHP会先调用__call()方法来存储方法名及其参数。其中,方法参数是以数组形式来存在的。
class Fir {
public function exist(){
echo '调用方法存在';
}
public function __call($funName, $param) {
echo '当方法不存在时候会自动调用此方法';
echo '不存在的方法名为'.$funName;
var_dump($param);
}
}
$p1 = new Fir();
$p1->exist(); // 调用方法存在
$p1->noexist('noExist','this is name'); // 当方法不存在时候会自动调用此方法 不存在的方法名为noexist array(2) { [0]=> string(7) "noExist" [1]=> string(12) "this is name" }
-
__toString()方法
作用:当使用echo 或print输出对象时,将对象转化为字符串。另外,__toString()需要return一个返回值。
输出对象时,应该注意echo或print函数后要直接跟要输出的对象,中间不要加多余的字符。否则__toString()方法不会被执行。
class Fir {
public function __toString(){
return '直接打印 new对象的时候 自动调用执行';
}
}
$p1 = new Fir();
echo $p1; // 直接打印 new对象的时候 自动调用执行
-
__autoload()方法
在指定路径下自动查找和该类名称相同的文件,如果找到,继续执行,否则报错。
<?php
// 文件名为Fir.php
class Fir {
public function __toString(){
return '直接打印 new对象的时候 自动调用执行';
}
}
function __autoload($className){ // 当new的类不存在时候 自动调用 __autoload()函数 并且 把new的类名作为参数传进去
$classPath = $className.'.php';
if (file_exists($classPath)) {
include_once($classPath); // $className = 'Fir' 相当于引入 Fir.php文件
}else{
echo '类路径错误';
}
}
echo new Fir(); // 当直接打印对象的时候 则调用自动__toString()函数 输出结果 直接打印 new对象的时候 自动调用执行。