面向对象基础
Posted superzwb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象基础相关的知识,希望对你有一定的参考价值。
面向对象编程(OOP)是我们编程的一项基本技能,php5对OOP提供了良好的支持.
如何使用OOP的思想来进行PHP的高级编程,对于提高 PHP编程能力和规划好Web开发构架都是非常有意义的
面向对象的三大特性:封装性 ,继承性, 多态性
1.什么是面向对象?
面向对象编程(Object Oriented Programming, OOP, 面向对象程序设计)是一种计算机编程架构.
OOP的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成
OOP达到了软件工程的三个目标:重用性、灵活性和扩展性
每个对象都能够接收信息、处理数据和向其它对象发送信息
优势:
面向对象符合人类看待事物的一般规律。其次,采用面向对象方法可以使系统各部分各司其职、各尽所能。为编程人员敞开了一扇大门,使其编程的代码 更简洁、更易于维护,并且具有更强的可重用性。
类的概念:
物以类聚,人以群分
类是具有相同属性和服务的一组对象的集合。它为属于该类的所有对象提供了统一的抽象描述,其内部包括属性和服务两个主要部分
一个类,由属性和方法组成两个主要部分构成
对象是系统中用来描述客观事物的一个实体,它是构成系统的一个基本单位。
客观世界是由对象和对象之间的联系组成的。
道理是一样的,对于一个网站应用来说,它牵涉的对象也非常多
对象是问题域或实现域中某些事物的一个抽象,它反映该事物在系统中需要保存的信息和发挥的作用。
举例:配置一台组装电脑
类的实例化结果就是对象
★ 有句话打死也要记住:对象抽象成类,类实例成对象(调用)
举个栗子:
如果你想建立一个电脑教室,首先要有一个房间,房间里面要有N台电脑,有N个桌子, N个椅子, 白板, 投影机等等,这些是什么,刚才咱们说了,这就是对象,能看到的一个个的实体,可以说这个电脑教室的单位就是这一个个的实体对象,它们共同组成了这个电脑教室,那么我们是做程序,这和面向对象有什么关系呢?开发一个系统程序和建一个电脑教室类似,你把每个独立的功能模块抽象成类形成对象,由多个对象组成这个系统,这些对象之间都能够接收信息、处理数据和向其它对象发送信息等等相互作用。(例如人能够上网买东西,加入购物车,下订单,支付等)就构成了面向对象的程序。
实现一个网站应用,需要分析牵扯的对象有哪些,有哪些属性, 对象和对象之间有什么关系。
面向对象程序的单位就是对象,但对象又是通过类的实例化出来的,所以我们首先要做的就是如何来声明类, 做出来一个类很容易,只要掌握基本的程序语法定义规则就可以做的出来,那么难点在那里呢? 一个项目要用到多少个类,用多少个对象, 在那要定义类,定义一个什么样的类,这个类实例化出多少个对象, 类里面有多少个属性, 有多少个方法等等,这就需要读者通过在实际的开发中就实际问题分析设计和总结了。
类的定义:
class 类名
使用一个关键字class和后面加上一个你想要的类名以及加上一对大括号, 这样一个类的结构就定义出来了,
class 人
成员属性:姓名、性别、年龄、身高、体重、电话、家庭住址
成员方法:可以开车, 会说英语, 可以使用电脑
像上面我们看到的,人的姓名、性别、年龄、身高、体重、电话、家庭住址都是属性等等。
动态上这个人可以开车, 会说英语, 可以使用电脑都是方法等等,所以,所有类都是从属性和方法这两方面去写, 属性又叫做这个类的成员属性,方法叫做这个类的成员方法。
属性:
通过在类定义中使用关键字" var "来声明变量,即创建了类的属性,虽然在声明成员属性的时候可以给定初值, 但是在声明类的时候给成员属性
初始值是没有必要的,比如说要是把人的姓名赋上“张三”,那么用这个类实例出几十个人,这几十个人都叫张三了,所以没有必 要, 我们在实例出对象后给成员属性初始值就可以了。
通过在类定义中声明函数,即创建了类的方法
<?php
class Person
// 下面是人的成员属性
var $name; //人的名子
var $sex; //人的性别
var $age; //人的年龄
// 下面是人的成员方法
function say() // 这个人可以说话的方法
echo "这个人在说话";
function run() // 这个人可以走路的方法
echo "这个人在走路";
?>
上面就是一个类的声明,从属性和方法上声明出来的一个类,但是成员属性最好在声明的时候不要给初使的值,因为我们做的人这个类是一个描述信息, 将来用它实例化对象, 比如实例化出来10个人对象。那么这10个人, 每一个人的名子, 性别,年龄都是不一样的,所以最好不要在这个地方给成员属性赋初值, 而是对每个对象分别赋值的。用同样的办法可以做出你想要的类了,只要你能用属性和方法能描述出来的实体都可以定义成类,去实例化对象
面向对象程序的单位就是对象,但对象又是通过类的实例化出来的,既然我们类会声明了,下一步就是实例化对象
★
当定义好类后,我们使用new关键字来生成一个对象。
$对象名称 = new 类名称();
<?php
class Person
//下面是人的成员属性
var $name; //人的名子
var $sex; //人的性别
var $age; //人的年龄
//下面是人的成员方法
function say() //这个人可以说话的方法
echo "这个人在说话";
function run() //这个人可以走路的方法
echo "这个人在走路";
$p1=new Person();
$p2=new Person();
$p3=new Person();
?>
这条代码就是通过类产生实例对象的过程,$p1就是我们实例出来的对象名称, 同理,$p2, $p3也是我们实例出来的对象名称,一个类可以实例出
多个对象,每个对象都是独立的,上面的代码相当于实例出来3个人来, 每个人之间是没有联系的, 只能说明他们都是人类, 每个人都有自己的
姓名, 性别和年龄的属性,每个人都有说话和走路的方法,只要是类里面体现出来的成员属性和成员方法,实例化出来的对象里面就包含了这些属性和方法
PHP对象中的成员有两种一种是成员属性, 一种是成员方法
要想访问对象中的成员就要使用一个特殊的操作符”->”来完成对象成员的访问:
<?php
class Person
// 下面是人的成员属性
var $name; // 人的名子
var $sex; // 人的性别
var $age; // 人的年龄
// 下面是人的成员方法
function say() // 这个人可以说话的方法
echo "这个人在说话";
function run() // 这个人可以走路的方法
echo "这个人在走路";
$p1 = new Person(); //创建实例对象$p1
$p2 = new Person(); //创建实例对象$p2
$p3 = new Person(); //创建实例对象$p3
// 下面三行是给$p1对象属性赋值
$p1->name = "张三";
$p1->sex = "男";
$p1->age = 20;
// 下面三行是访问$p1对象的属性
echo "p1对象的名子是:" . $p1->name;
echo "p1对象的性别是:" . $p1->sex;
echo "p1对象的年龄是:" . $p1->age;
// 下面两行访问$p1对象中的方法
$p1->say();
$p1->run();
// 下面三行是给$p2对象属性赋值
$p2->name = "李四";
$p2->sex = "女";
$p2->age = 30;
// 下面三行是访问$p2对象的属性
echo "p2对象的名子是:" . $p2->name;
echo "p2对象的性别是:" . $p2->sex;
echo "p2对象的年龄是:" . $p2->age;
// 下面两行访问$p2对象中的方法
$p2->say();
$p2->run();
// 下面三行是给$p3对象属性赋值
$p3->name="王五";
$p3->sex="男";
$p3->age=40;
// 下面三行是访问$p3对象的属性
echo "p3对象的名子是:" . $p3->name;
echo "p3对象的性别是:" . $p3->sex;
echo "p3对象的年龄是:" . $p3->age;
// 下面两行访问$p3对象中的方法
$p3->say();
$p3->run();
?>
现在我们知道了如何访问对象中的成员,是通过”对象->成员”的方式访问的,这是在对象的外部去访问对象中成员的形式, 那么如果我想在对象的内部,让对象里的方法访问本对象的属性, 或是对象中的方法去调用本对象的其它方法这时我们怎么办?因为对象里面的所有的成员都要用对象来调用,包括对象的内部成员之间的调用,所以在PHP里面给 我提供了一个本对象的引用$this, 每个对象里面都有一个对象的引用$this来代表这个对象,完成对象内部成员的调用, this的本意就是“这个”的意思, 上面的实例里面,我们实例化三个实例对象$P1、 $P2、 $P3,这三个对象里面各自存在一个$this分别代表对象$p1、$p2、$p3 。
<?php
class Person
// 下面是人的成员属性
var $name; //人的名子
var $sex; //人的性别
var $age; //人的年龄
// 下面是人的成员方法
function say() // 这个人可以说话的方法
echo "我的名子叫:" . $this->name . " 性别:" . $this->sex . " 我的年龄是:" . $this->age;
function run() // 这个人可以走路的方法
echo "这个人在走路";
$p1 = new Person(); // 创建实例对象$p1
$p2 = new Person(); // 创建实例对象$p2
$p3 = new Person(); // 创建实例对象$p3
// 下面三行是给$p1对象属性赋值
$p1->name = "张三";
$p1->sex = "男";
$p1->age = 20;
// 下面访问$p1对象中的说话方法
$p1->say();
// 下面三行是给$p2对象属性赋值
$p2->name = "李四";
$p2->sex = "女";
$p2->age = 30;
// 下面访问$p2对象中的说话方法
$p2->say();
// 下面三行是给$p3对象属性赋值
$p3->name = "王五";
$p3->sex = "男";
$p3->age = 40;
// 下面访问$p3对象中的说话方法
$p3->say();
?>
在$p1、$p2和$p3这三个对象中都有say()这个方法,$this分别代表这三个对象, 调用相应的属性,打印出属性的值,这就是在对象内部访问对象
属性的方式, 如果相在say()这个方里调用run()这个方法也是可以的,在say()这个方法中使用$this->run()的方式来完成调用。
8.构造方法__construct()与析构方法__destruct()
构造方法:刚出生时婴儿的啼哭
析构方法:老人临终时的遗言
格式:function __construct ( [参数] ) ... ...
在一个类中只能声明一个构造方法,而是只有在每次创建对象的时候都会去调用一次构造方法,不能主动的调用这个方法,所以通常用它执行一些有用的初始化任务。比如对成属性在创建对象的时候赋初值
<?
// 创建一个人类
class Person
// 下面是人的成员属性
var $name; // 人的名子
var $sex; // 人的性别
var $age; // 人的年龄
// 定义一个构造方法参数为姓名$name、性别$sex和年龄$age
function __construct($name, $sex, $age)
// 通过构造方法传进来的$name给成员属性$this->name赋初使值
$this->name = $name;
// 通过构造方法传进来的$sex给成员属性$this->sex赋初使值
$this->sex = $sex;
// 通过构造方法传进来的$age给成员属性$this->age赋初使值
$this->age = $age;
// 这个人的说话方法
function say()
echo "我的名子叫:" . $this->name . " 性别:" . $this->sex . " 我的年龄是:" . $this->age;
// 通过构造方法创建3个对象$p1、p2、$p3,分别传入三个不同的实参为姓名、性别和年龄
$p1 = new Person("张三","男", 20);
$p2 = new Person("李四","女", 30);
$p3 = new Person("王五","男", 40);
// 下面访问$p1对象中的说话方法
$p1->say();
// 下面访问$p2对象中的说话方法
$p2->say();
// 下面访问$p3对象中的说话方法
$p3->say();
?>
当创建一个对象时,它将自动调用构造函数,也就是使用new这个关键字来实例化对象的时候自动调用构造方法
格式:function __destruct ( ) ... ...
与构造函数相对的就是析构函数。
析构函数允许在销毁一个类之前执行的一些操作或完成一些功能,比如说关闭文件, 释放结果集等,析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行,也就是对象在内存中被销毁前调用析构函数。与构造函数的名称类似, 一个类的析构函数名称必须是__destruct( )。析构函数不能带有任何参数。
<?
// 创建一个人类
class Person
// 下面是人的成员属性
var $name; // 人的名子
var $sex; // 人的性别
var $age; // 人的年龄
// 定义一个构造方法参数为姓名$name、性别$sex和年龄$age
function __construct($name, $sex, $age)
// 通过构造方法传进来的$name给成员属性$this->name赋初使值
$this->name = $name;
// 通过构造方法传进来的$sex给成员属性$this->sex赋初使值
$this->sex = $sex;
// 通过构造方法传进来的$age给成员属性$this->age赋初使值
$this->age = $age;
// 这个人的说话方法
function say()
echo "我的名子叫:" . $this->name . " 性别:" . $this->sex . " 我的年龄是:" . $this->age;
// 这是一个析构函数,在对象销毁前调用
function __destruct()
echo "再见" . $this->name;
// 通过构造方法创建3个对象$p1、p2、$p3,分别传入三个不同的实参为姓名、性别和年龄
$p1 = new Person("张三", "男", 20);
$p2 = new Person("李四", "女", 30);
$p3 = new Person("王五", "男", 40);
// 下面访问$p1对象中的说话方法
$p1->say();
// 下面访问$p2对象中的说话方法
$p2->say();
// 下面访问$p3对象中的说话方法
$p3->say();
?>
9.封装性(public,protected,private的关系)
说点简单一点,就是属性和方法前面可以加一些限定词,
分别是:pulic(公开的) private(私有的) protected(被保护的)
没有加任何访问的,默认是public,任何地方都可以访问
|
private |
protected |
public |
同一个类中 |
√ |
√ |
√ |
类的子类中 |
|
√ |
√ |
所有的外部成员 |
|
|
√ |
只要是成员属性前面有其它的关键字就要去掉原有的关键字”var”
因为构造方法是默认的公有方法(构造方法不要设置成私有的)
子类覆盖父类的方法时也要注意一点,子类中方法的访问权限一定不能低于父类被覆盖方法的访问权限,也就是一定要高于或等于父类方法的访问权限。
例如,如果父类方法的访问权限是protected,那么子类中要覆盖的权限就要是protected和public,如果父类的方法是public那么子类中要覆盖的方法只能也是public,总之子类中的方法总是要高于或等于父类被覆盖方法的访问权限
封装性:将一些敏感的属性或者方法进行封装,不让外界进行访问,设置屏障,起到保护作用
我们称以存在的用来派生新类的类为基类,又称做父类,超类
由已存在的类派生出的新类称为派生类,又称为子类
从一个基类派生的继承称单继承,从多个基类派生的继承称为多继承
也就是说:一个类只能直接从一个类中继承数据
<?php
final class Person
function say()
class Student extends Person
function say()
?>
这个关键字只能用来定义类和定义方法, 不能使用final这个关键字来定义成员属性,因为final是常量的意思,我们在PHP里定义常量使用的是define()函数,所以不能使用final来定义成员属性
使用final关键标记的方法不能被子类覆盖,是最终版本;
10.__set(),__get(),__isset(),__unset()四个方法的应用
<?php
//__get()方法用来获取私有属性
function __get($property_name)
if (isset($this->$property_name))
return ($this->$property_name);
else
return (NULL);
//__set()方法用来设置私有属性
function __set($property_name, $value)
$this->$property_name = $value;
<?php
class Person
//下面是人的成员属性
var $name; //人的名子
var $sex; //人的性别
var $age; //人的年龄
//下面是人的成员方法
function say() //这个人可以说话的方法
echo "这个人在说话";
function run() //这个人可以走路的方法
echo "这个人在走路";
$p1=new Person();
$p2=new Person();
$p3=new Person();
?>
这是一个实例化过程
$p1 $p2 $p3 就是我们实例出来的对象名称
在PHP里面,不管什么数据类型,在运行的时候都要加载到内存中,不同的数据类型在内存中的分配也是不一样的
内存从逻辑上说大体分为4段:
栈 堆 代码段 初始化静态段
程序里面不同的声明放在不同的内存段里面,栈空间段是存储占用相同空间长度并且占用空间小的数据类型的地方,比如说整型1, 10, 100, 1000, 10000, 100000等等,在内存里面占用空间是等长的,都是64位4个字节。 那么数据长度不定长,而且占有空间很大的数据类型的数据放在那内存的那个段里面呢?这样的数据是放在堆内存里面的。栈内存是可以直接存取的,而堆内存是不可以直接存取的内存。对于我们的对象来说就是一种大的数据类型而且是占用空间不定长的类型,所以说对象是放在堆里面的,但对象名称是放在栈里面的,这样通过对象名称就可以使用对象了
$p1 = new Person();
$p2 = new Person();
$p3 = new Person();
从上图可以看出$p1=new Person();等号右边是真正的对象实例, 在堆内存里面的实体,上图一共有3次new Person(),所以会在堆里面开辟3个空间,产生3个实例对象,每个对象之间都是相互独立的,使用自己的空间,在PHP里面,只要有一个new这个关键字出现就会实例化出来一个对象,在堆里面开辟一块自己的空间。
每个在堆里面的实例对象是存储属性的,比如说,现在堆里面的实例对象里面都存有姓名、性别和年龄。每个属性又都有一个地址。
$p1=new Person();等号的左边$p1是一个引用变量,通过赋值运算符“=”把对象的首地址赋给“$p1“这个引用变量, 所以$p1是存储对象首地址的变量,$p1放在栈内存里边,$p1相当于一个指针指向堆里面的对象, 所以我们可以通过$p1这个引用变量来操作对象, 通常我们也称对象引用为对象。
重点来了!!!
static关键字是在类中描述成员属性和成员方法是静态的;静态的成员好处在哪里呢?前面我们声明了“Person”的人类,在“Person”这个类里如果我们加上一个“人所属国家”的属性,这样用“Person”这个类实例化出几百个或者更多个实例对象,每个对象里面就都有“所属国家”的属性了,如果开发的项目就是为中国人而开发的,那么每个对象里面就都有一个国家的属性是 “中国“其它的属性是不同的,如果我们把“国家”的属性做成静态的成员,这样国家的属性在内存中就只有一个,而让这几百个或更多的对象共用这一个属性,static成员能够限制外部的访问,因为static的成员是属于类的,是不属于任何对象实例,是在类第一次被加载的时候分配的空间,其他类是无法访问的,只对类的实例共享,能一定程度对类该成员形成保护;
从内存的角度我们来分析一下,内存从逻辑上被分为四段,其中对象是放在“堆内存”里面,对象的引用被放到了“栈内存“里,而静态成员则放到了“初始化静态段”,在类第一次被加载的时候放入的,可以让堆内存里面的每个对象所共享,如下图:
类的静态变量,非常类似全局变量,能够被所有类的实例共享,类的静态方法也是一样的,类似于全局函数。
<?
class Person
// 下面是人的静态成员属性
public static $myCountry = "中国";
// var $name; //人的名子
// 这是人的静态成员方法
public static function say()
echo "我是中国人";
// 输出静态属性
echo Person::$myCountry;
// 访问静态方法
Person::say();
// 重新给静态属性赋值
Person::$myCountry = "美国";
echo Person::$myCountry;
?>
因为静态成员是在类第一次加载的时候就创建的,所以在类的外部不需要对象而使用类名就可以访问的到静态的成员;上面说过,静态成员被这个类的每个实例对象所共享,
那么我们使用对象可不可以访问类中的静态成员呢?从上图中我们可以看到,静态的成员不是在每个对象内部存在的,但是每个对象都可以共享,所以我们如果使用对象访问成员的话就会出现没有这个属性定义,使用对象访问不到静态成员的,
类里面的静态方法只能访问类的静态的属性,在类里面的静态方法是不能访问类的非静态成员的,原因很简单,我们要想在本类的方法中访问本类的其它成员,我们需要使用$this这个引用,而$this这个引用指针是代表调用此方法的对象,我们说了静态的方法是不用对象调用的,而是使用类名来访问, 所以根本就没有对象存在,也就没有$this这个引用了,没有了$this这个引用就不能访问类里面的非静态成员,又因为类里面的静态成员是可以不用对象 来访问的,所以类里面的静态方法只能访问类的静态的属性,即然$this不存在,在静态方法中访其它静态成员我们使用的是一个特殊的类“self”; self和$this相似,只不过self是代表这个静态方法所在的类。所以在静态方法里,可以使用这个方法所在的类的“类名“,也可以使用“self”来访问其它静态成员,如果没有特殊情况的话,我们通常使用后者,即“self::成员属性”的方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php class Person // 下面是人的静态成员属性 public static $myCountry = "中国";
// 这是人的静态成员方法, 通过self访问其它静态成员 public static function say() echo "我是" . self::$myCountry;
// 访问静态方法 Person::say(); ?>
|
在非静态方法里可不可以访问静态成员呢,当然也是可以的了,但是也不能使用“$this”引用,也要使用类名或是”self::成员属性的形式”。
const是一个定义常量的关键字,在PHP中定义常量使用的是“define()”这个函数,但是在类里面定义常量使用的是“const”这个关键字,类似于C中的#define如果在程序中改变了它的值,那么会出现错误,用“const”修饰的成员属性的访问方式和“static”修饰的成员访问的方式差不多,也是使用“类名”,在方法里面使用“self”关键字。但是不用使用“$”符号,也不能使用对象来访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php class MyClass // 定义一个常量constant const constant = ‘constant value‘;
function showConstant() echo self::constant . " "; // 使用self访问,不要加“$”
echo MyClass::constant . " "; // 使用类名来访问,也不加“$”
$class = new MyClass(); $class->showConstant(); // echo $class::constant; // 是不允许的 ?> |
以上是关于面向对象基础的主要内容,如果未能解决你的问题,请参考以下文章