一、类的声明--基本语法
<?php
//声明类
[修饰符] class 类名 [extends 父类名称] [implements 接口1名称,接口2名称]{
//声明成员变量
[public][var][private][protected][static][final] $变量名;
//声明成员方法
[public][private][protected][static][final][abstract] function 方法名(参数1,参数2){
}
}
//实例化对象
$引用变量名 = new 类名; //PHP允许不加括号
$引用变量名 = new 类名(实参1,实参2...);
//访问对象的成员变量(不需要$符号)
$引用变量名 -> 成员变量名称
//给对象的成员变量赋值
$引用变量名 -> 成员变量名称 = [值];
//访问对象的成员方法
$引用变量名 -> 成员方法名();
$引用变量名 -> 成员方法名(实参1,实参2...);
二、对象的存储原理(开辟内存空间)
- 栈内存的特点:空间小,与CPU的数据交换快,适合存定长的数据类型(定长不是指固定长度,而是该数据类型是固定长度,不会变化),先进后出
- 堆内存的特点:空间大,与CPU的数据交换慢,存变长的数据类型(数组,字符串等)
所以一般通过栈内存里的引用访问堆内存的内容
三、$this关键字
对象中的成员方法访问对象内部的成员属性时,使用$this。
$this指代“本对象”
四、构造方法和析构方法
作用:初始化成员属性。
构造方法是实例化对象后,第一个自动调用的方法。
构造方法名称和类名一致。
如果以下两种构造方法同时使用,则优先使用魔术方法(php5版开始)
- 和类名相同的方法为构造方法(不限大小写)
- 或魔术方法__construct(),不能加private修饰词
在java中,同名构造方法或者方法可以有多个,根据参数类型和参数个数自动判断调用哪个(java的重载)。但php不支持重载,不能有多个同名方法(包括构造方法)。
析构方法是对象销毁前自动调用的方法
当对象失去引用,php的垃圾回收机制自动回收,在回收前自动调用析构方法。
如果声明了多个对象,根据栈的特点,后声明的对象先执行析构方法先释放
- 魔术方法__destruct() //php5特性,不能加private修饰词,不能有参数
五、封装性://PHP5特性
1、意义:
- 封装就是给对象的属性和方法添加访问修饰符,隐藏对象的内部细节,以达到对成员的访问控制。
2、语法:
- 修饰词public,private,protected
3、成员方法的封装:
- private:加上后对象不能直接调用该成员方法,但对象的其他成员可以调用它。
4、成员属性的封装:
- private:加上后对象不能直接调用或者修改成员属性
- 可以通过方法getXXX去获取,通过方法setXXX()去有限制的修改
- 一般有多少个成员属性,就要写多少个set和get方法。为了更加方便,使用__get(形参)和__set(形参)魔术方法来简化。
- __get(形参):在直接访问私有成员属性时自动调用(其他修饰词的成员不自动调用),可做到通用的get方法。
例如:当访问私有成员属性$sex时,
echo $obj->sex
此时自动调用__get(形参)方法,其中形参为访问的该私有成员属性的变量名称,那么
function __get($pro){
return $this->$pro;
}
- __set(形参1,形参2):在直接设置私有成员属性(其他修饰词的成员不自动调用)时自动调用,可做到通用的set方法。
例如:当设置私有成员属性$sex时,
$obj->sex = “男”
此时自动调用__set(形参1,形参2),其中形参1为 设置的该私有成员属性的变量名称,形参2为 设置的该私有成员属性的变量值,那么
function __set($key,$value){
this->$key = $value;
}
- 成员属性设置为私有,并使用__get和__set魔术方法,从外部看起来可以随意的得到和设置私有成员属性,这种情况和成员属性设置为公有,不写get、set方法有何区别?前者可以在方法里对成员属性进行规范和限制,后者不能。
- 魔术方法__set()和__get()不能封装,即,不能加修饰词private。要么不写修饰词,要么写public。
- isset()、unset()、__isset()、__unset()的区别和联系
isset()和unset()不仅可以作用于数组,同样可作用于对象。
例如当某成员属性为var(已过时)或者public时,用isset()可以判断对象中是否存在该成员属性;当先用unset()删除对象中的该成员属性,再用isset()判断,其是不存在的、
当某成员属性为private时,用isset()判断时,其是不存在的;
- 当使用isset()判断一个对象的私有成员属性是否存在时,自动调用__isset(参数)魔术方法,参数是属性名称
- 当使用unset()删除对象的成员属性时,自动调用__unset($参数)魔术方法,参数是属性名称
<?php
$p = new Person("zjs",18,"male");
isset($p->name);
unset($p->name);
class person{
private $name;
private $age;
private $sex;
function __construct($name="",$age=0,$sex="male"){
$this -> name = $name;
$this -> age = $age;
$this -> sex = $sex;
}
function __isset($key){
if($key=="age")
return false;
return isset($this->$key);
}
function __unset($key){
if($key!="age")
unset($this->$key);
}
}
六、继承性:
1、意义&介绍:
- 子类使用extends关键字继承父类,子类可以使用父类属性和方法。
- 开发原则:当多个类出现重复方法和属性时,抽象共性为父类。在扩展时以继承父类的方式实现,不会引发连锁反应。
- 父类=基类,子类=派生类
- PHP和JAVA一样是单继承,一个类只能有一个父类,但一个类可以有多个子类。
2、继承语法&权限控制:
当成员方法和成员属性都是公有时:
class Person{
public $name;
public $age;
public $sex;
function __construct($name,$age,$sex){
$this -> name = $name;
$this -> age = $age;
$this -> sex = $sex;
}
function say(){
echo $this -> name."正在说话...";
}
function eat(){
echo $this -> name."正在吃饭...";
}
}
class Student extends Person{
public $student_id;
function study(){
echo $this -> name."正在学习...";
}
}
class Teachere extends Person{
public $teacher_id;
function teach(){
echo $this -> name."正在教学...";
}
}
$student1 = new Student("zjs",18,"male");
echo $student1 -> name;
//输出 zjs
$student1 -> say();
//输出 zjs正在说话...
$student1 -> study();
//输出 zjs正在学习...
当成员属性为私有,成员方法为公有时:
class Person{
private $name;
private $age;
private $sex;
function __construct($name,$age,$sex){
$this -> name = $name;
$this -> age = $age;
$this -> sex = $sex;
}
function say(){
echo $this -> name."正在说话...";
}
function eat(){
echo $this -> name."正在吃饭...";
}
}
class Student extends Person{
public $student_id;
function study(){
echo $this -> name."正在学习...";
}
}
class Teachere extends Person{
public $teacher_id;
function teach(){
echo $this -> name."正在教学...";
}
}
$student1 = new Student("zjs",18,"male");
echo $student1 -> name;
//报错:PHP Notice: Undefined property: Students::$name in /usercode/file.php on line 39
//不能访问父类的私有成员属性
$student1 -> say();
//输出 zjs正在说话...
//可以访问父类的公有成员方法,通过父类的成员方法可以使用父类的私有成员属性name
$student1 -> study();
//报错,输出以下:
//正在学习...
//PHP Notice: Undefined property: Students::$name in /usercode/file.php on line 26
//可以访问自己的成员方法,但成员方法不能使用父类的私有成员属性
由此可知,当父类的成员属性为私有时,子类中不能直接访问该成员属性
当父类的成员属性为公有时,子类中可以直接访问该成员属性
当父类的成员方法为私有时,子类中不能直接使用该成员方法
当父类的成员方法为公有时,子类中可以直接使用该成员方法
则:
当父类的成员属性和成员方法为公有时,这个类没有受到保护,外部可以随意访问
当父类的成员属性和成员方法为私有时,子类不能直接使用
如何能让类受到保护,并且能够让子类直接访问,使用呢?
- 答案是使用关键词protected,只能自己内部和自己子类内部可以直接使用成员,而不能在外部使用。
- 不加修饰词,或者使用public关键词,类内部,类外部,子类内部都能使用
3、继承中的重写(不是重载):
- 在编程语言中,重载指的是在同一个类中有多个名称相同的方法,根据参数个数和类型的不同自动选择调用的方法。
- 因php是弱类型语言,和java不同,PHP不能重载,只能重写。
- 重写是指:在子类中可以写和父类中同名的方法,主要是为了扩展。
- 补充:同一类——>重载(overload),继承类——>重写(override)
- 如果子类的某个方法不是重写的,那么在这个方法中可以使用$this->方法名()来调用从父类继承过来的所有公有方法。如果子类的某个方法是重写的,那么怎样调用父类中被重写的方法呢?不能使用$this->方法名(),$this指代的是“本类”,如果这样使用相当于是一个递归方法。正确的方法是:使用 类名::方法名调用被重写的父类方法,或者使用parent::方法名。
<?php
class Person{
protected $name;
protected $sex;
protected $age;
function __construct($name="",$sex="男",$age=1){
$this ->name = $name;
$this ->sex = $sex;
$this ->age = $age;
}
//在人类中声明一个通用的说话方法,介绍一下自己
function say(){
echo "我的名字:".$this-> name.",性别:".$this ->sex.",年龄:".$this ->age."。";
}
}
class Student extends Person {
private $school;
//覆盖父类中的构造方法,并在参数列表中添加一个学校属性
//子类重写的构造方法建议调用一下父类被重写的构造方法,这样才能随父类而改变成员变量
function __construct($name="",$sex="男",$age=1,$school=""){
parent::__construct($name,$sex,$age);
$this ->school = $school;
}
function study(){
echo $this ->name."正在".$this ->school."学习<br>";
}
//定义一个和父类中同名的方法,将父类中的说话方法覆盖并重写,多说出所在的学校名称
function say(){
//parent::say();
//或者
Person::say();
echo "在".$this ->school."学校上学";
}
}
$student= new Student("张三","男",20,"edu");
$student -> say();
七、关键字介绍
1、instanceof操作符
- 用于检测当前对象实例是否属于某一个类
<?php
class person{····}
class student extends person{····}
$p = new person();
$s = new student();
echo $p instanceof student;//false
echo $s instanceof student;//true
echo $s instanceof person;//true
2、final
- 在php5中新增了final关键字,它只能用来修饰类和成员方法,不能定义常量,也不能修饰成员属性。
- 在java中,final是定义常量的,PHP里定义常量是用define()函数和const关键字
- final特性:
使用final标识的类不能有子类,不能被继承(该类不能扩展)
使用final标识的方法不能被子类重写(该方法不能扩展)
3、static(静态)
- 使用static修饰的成员属性在内存中被存储在初始化静态段内(见上面内存图),类的所有对象可以共用static修饰的成员属性,不再为每个对象的该成员属性单独开辟内存空间。
- 第一次加载类的时候,就将static修饰的成员属性加载到了内存中。
- static可以修饰成员属性和成员方法,不能修饰类
- 静态的成员(属性和方法都是)一定要使用类来访问,不能使用对象来访问。类外使用类名::成员名访问,类内使用self::成员名访问。
- 在静态方法中不能访问非静态成员。原因:静态方法只能使用类来使用,如果其他成员非静态,那么这些成员没有开辟内存空间,即没有被创建,所以无法访问。
- 静态成员一旦被加载,只有脚本结束后才会被释放
- 优点:创建对象是消耗资源的,使用静态的方式可以在不创建对象的情况下直接使用,效率高,建议使用。
- 缺点:静态成员是在类加载时开辟的内存空间,直到脚本结束才被销毁。而使用对象调用成员在脚本结束自然被销毁,另外删除对象的引用也能销毁,例如unset($obj);
<?php
class person{
public $name = "ZJS";
public $sex;
public $age;
public static $country = "中国";
function __construct($name,$sex,$age){
$this -> name = $name;
$this -> sex = $sex;
$this -> age = $age;
}
public function say(){
echo "我的名字是:{$this->name},我的性别是:{$this->sex},我的年龄是:{$this->age}";
//只能通过类去访问static修饰的$country
echo "我的国家是:".self::$country;
//或者
echo "我的国家是:".person::$country;
}
}
echo person::$country;//输出:中国 ,因为static修饰的成员属性$country已经被加载到内存中了
echo person::$name; //报错,$name成员属性没有被加载到内存,只有实例化对象后才能通过对象访问
$p = new person("zjs","male",18);
echo $p->country; //报错,只能通过类访问,不能通过对象访问
$p -> say(); //输出:我的名字是:ZJS,我的性别是:male,我的年龄是:18,我的国家是:中国
4、单态设计模式(单件、单例)
- 目的:节约系统资源。
- 如果想让一个类只能有一个对象,就要先让这个类不能创建对象,将构造方法私有化
- 可以在类中使用一个静态方法来创建对象
<?php
class person{
static $obj = null;
private __construct(){}
static function getObj(){
//没有对象则创建,已有对象则直接使用上一次的创建。
//因为static修饰的$obj是在类加载时创建的,如果脚本没有结束,保存的是上一次的值。
if(is_null(self::$obj))
self::$obj = new person;
//或者
//self::$obj = new self;
return self::$obj;
}
function say(){
echo "aaaaa";
}
function __destruct(){
echo "#####";
}
}
$p = person::getObj();
$p -> say();
5、const
- JAVA使用final定义常量
- PHP声明常量:define("常量名","常量值")
- PHP,在类中声明常量使用const关键字,不能使用define(会报错)
- 常量特点:值是标量类型;常量只有脚本结束才能被释放;常量值不能改变;常量存储在初始化静态段中;
- const修饰的成员属性为常量,只能修饰成员属性。
- 常量名建议使用大写字母,不能使用$;常量必须在声明时赋初值
- 类中的常量的访问方式和static关键字相同。类外使用:类名::常量名;类内使用:self::常量名
八、魔术方法
1、__toString()
- 在直接使用echo、print、printf输出一个对象引用时,自动调用这个方法。
- 将对象的基本信息放在__toString()方法内部,形成字符串返回。
- __toString()方法中,不能有参数,必须返回一个字符串
<?php
class person{
public $name;
public $sex;
public $age;
function __construct($name,$sex,$age){
$this -> name = $name;
$this -> sex = $sex;
$this -> age = $age;
}
function say(){
}
function __toString(){
return "####";
}
}
$p = new Person("ZJS",18,"male");
echo $p;
2、__clone()克隆方法
- 使用clone关键字克隆对象时自动调用的方法
- __clone()的作用:类似构造方法,对克隆的对象进行初始化
- 在这个方法中$this代表的是克隆副本
<?php
class person{
public $name;
public $sex;
public $age;
function __construct($name,$sex,$age){
$this -> name = $name;
$this -> sex = $sex;
$this -> age = $age;
}
function say(){
echo "我的名字是:{$this->name},我的性别是:{$this->sex},我的年龄是:{$this->age}";
}
function __destruct(){
echo "{$this->name}####";
}
function __clone(){
$this->name = "克隆的ZJS";
$this->age = 0;
$this->sex = "female";
}
}
$p = new Person("ZJS",18,"male");
$p->say();
//输出:我的名字是:ZJS,我的性别是:male,我的年龄是:18
// ZJS####
$p2 = clone $p;
$p2->say();
//输出:我的名字是:克隆的ZJS,我的性别是:female,我的年龄是:0
// 克隆的####