6.1. 类,对象
持之以恒,方得始终!
类,对象是什么
什么是类?
物以类聚,具有相同特性和行为的实体,我们可以归为一类。类是一种虚拟的称呼,是对一类事物的一种描述性。
什么是对象?
对象是类的具体实例,比如一只麻雀,它是鸟类下的一个实体单位。
面向对象的三大特性
封装
使用访问控制修饰符 public protected private
, 避免对象的属性,方法被外部直接使用,其实思想就是隐藏对象的实现细节,只提供几个必要的对外接口。
打个比方,内置的函数,我们一般也不清楚它的实现细节,但是有手册告诉我们怎么给参数,参数的含义,我们就可以用它为我们做事情了。
继承
子类可以继承父类的属性和方法,比方说,自行车类,和汽车类,它们有些共同地方,我们可以再来个交通工具类,自行车类和汽车类就可以继承交通工具类了。这样可以省掉一部分的代码。
多态(方法的重写)
子类继承父类时,可以重写父类的方法,也就是说子类的行为细节,可能和父类有点不一样。也就是多种行为状态。
我们也可以看下这篇文章 zhuanlan.zhihu.com/p/238129674
写一个简单的类
我们来写一个读取文件内容的类,并且和面向过程对比。
面向过程的做法:
- fopen() 打开一个文件。
- fgets() 逐行读取一行内容。
- 读完后,fclose() 关闭。
可以看到面向过程,做事情比较直接,上手就开始做,其实更符合人类的思考方式。
面向对象的做法:
- 创建一个文件读取类。
- 定义一个读取文件的方法。
- 类封装好后,我们要依托类,实例化一个文件读取对象,然后调用该对象的读取方法。
可以看到,面向对象的方式其实更麻烦,需要先封装好类,而且执行效率是比过程慢的。不过面向对象更体现了软件工程,而非小脚本。
class FileReadClass { // 一般是一个文件代表一个类,类名和文件名相同
protected $file = null;
// 构造方法,new一个对象时,会执行
public function __CONSTRUCT($file) {
$this->file = $file;
}
public function read() {
if (empty($this->file) || !is_file($this->file)) {
return false;
}
$data = '';
$fp = fopen($this->file, 'rb');
while(!feof($fp)) {
$data .= fgets($fp);
}
fclose($fp);
return $data;
}
public function __destruct() {
echo "对象被销毁了";
}
}
$file_oob = new FileReadClass('./page.html');
$file_content = $file_oob->read();
echo $file_content;
unset($file_oob);
构造方法
当 new 一个对象时,会自动调用构造方法,所以它可以帮助执行一些初始化任务,比如设置属性的初始值,生成该对象需要的其它对象。
可以看上面的例子。
析构方法
在销毁一个类之前,执行一些操作。
析构方法 __destruct()
不能带有任何参数。
可以看上面的例子。
使用类的属性
$this
可以访问,它指代的其实是一个对象。$this->attribute
访问属性
class classname {
public $attribute;
function operation($param) {
$this->attribute = $param;
echo $this->attribute;
}
}
是否可以在类的外部访问属性,是由访问修饰符决定的(public,protected,private)。上面的例子属性是public,可以在外部访问。
$obj = new classname();
$obj->attribute = 'value';
echo $obj->attribute;
一般来说,从类的外部直接访问属性是不好的做法,好的做法是,我们应该隐藏类的细节,只暴露一些获取,设置的方法接口。
我们可以用 __get(), __set()
来访问属性。
在给不可访问(protected 或 private)或不存在的属性赋值时,
__set()
会被调用。读取不可访问(protected 或 private)或不存在的属性的值时,
__get()
会被调用。
class classname {
protected $attribute;
function __get($name) {
echo "调用了 __get()";
return $this->$name;
}
function __set($name, $value) {
echo "调用了 __set()";
if ($name == 'attribute' && $value >=0 && $value <= 100) {
$this->attribute = $value;
}
}
}
$a = new classname();
$a->attribute = 5;
echo $a->attribute; // 调用了 __set()调用了 __get()5
这种好处是提供了一个单一的访问入口来设定,获取属性,并且是固定用法,对项目的维护友好。
访问修饰符
它们可以控制属性,方法的可见性。
- public,默认的,公有的,可以在类的内外部可见。
- protected,受保护的,类和子类中可见。
- private,私有的,仅类里面可见,子类中也不可见。
protected和private,如果想外部访问,我们可以使用 __get(), __set()
魔术方法。
调用方法
class cacl {
public function add($n1, $n2) {
return $n1 + $n2;
}
}
$obj = new cacl();
$res = $obj->add(10, 5);
实现继承 extends
class B extends A { // B类继承A类,从而也可以使用A类中的属性,方法
}
注意:继承是单向的,子类可以继承父类,但父类不能从子类中继承。
private 不能被继承,protected,public可以。
重载
就是重写父类中的属性,方法。
class A {
public $attribute = "default value";
public function operation() {
echo "attribute values is " . $this->attribute;
}
}
class B extends A {
public $attibute = "diff value";
public function operation() {
echo "new value is".$this->attribute;
}
}
重写并不会影响父类的属性,方法。
如果说,我们使用了重写,但是还想用一下父类中的属性,方法,怎么办? 我们可以使用 parent
关键字,它表示父类。
parent::operation(); // 但是要注意,父类operation()里面的调用,还是当前类中的。比如里面的$this->attribute, 它还是用的B类对象的值。
继承是可以有子子类的,比如还可以创建一个类C,来继承类B,因此类C拥有类A,类B的所有属性,方法。 C=>B=>A
final, 禁止类被继承,方法被重写
禁止一个方法被重写:
禁止一个类被继承:
一个类同时继承两个父类
php并不支持有多个父类,php中,每个类都只能有一个父类。
当然,一个父类可以有多个子类,这样是没毛病。
当然后面 php 引入了 Trait
,就可以实现第三种了。
接口类
实现多继承
- 接口,一个类可以继承父类,还可以实现多个接口。
- trait(推荐)
什么是接口?
类的范本,需要去实现它所规划的功能。
怎么做这种范本?
- 用继承的方式,父类写好空方法,子类去重写来实现。
class FileCurdBase {
protected $file = null;
protected function read() {}
protected function write() {}
protected function del() {}
protected function add() {}
}
class FileCurd extends FileCurdBase {
protected $file = null;
public function __construct($file) {
$this->file = $file;
if (!file_exists($this->file)) {
$this->add();
}
}
public function read() {
return file_get_contents($this->file);
}
public function write($data) {
file_put_contents($this->file, $data);
}
public function del() {
unlink($this->file);
}
public function add() {
touch($this->file);
}
}
$filecurd = new FileCurd("./log.txt");
- 用接口类 interface
interface Displayabe {
function display();
}
class webpage implements Displayable { // 如果没有实现接口的方法,是会报错的。
function display() {
// code
}
}
如有任何侵权行为,请通知我删除,谢谢大家!
个人邮箱:865460609@qq.com