这是我参与更文挑战的第12天,活动详情查看: 更文挑战
对象的秘密
本文讲解了PHP类中, 关于各种对象的创建方法, 对象比较, 对象赋值的内在逻辑, 对象复制, 对象类型, 对象间的互相调用, instanceof类运算符, 其中还会介绍一些在使用中的小技巧. 使用好对象, 能让我们的程序更简洁.
创建对象的N种方式
new
$obj= new MyClass{}; //新建普通类MyClass{}的对象;
$obj1= new stdClass(); //新建内置类对象;
$ob2=new class{}; //新建匿名类对象;
以上三种new的创建方式, 可以用在类外, 也可以直接用在类内成员方法中, 这样的例子我们看过不少, 我这里举一个匿名类+链式调用的例子:
class Outer
{
private $attr = 1;
protected $attr2 = 2;
protected function func1()
{
return 3;
}
public function func2()
{ //新建匿名子类, 并将父类属性传给子类构造函数
return new class($this->attr) extends Outer {
private $attr3;
public function __construct($attr)
{
$this->attr3 = $attr;
}
public function func3()
{
return $this->attr2 + $this->attr3 + $this->func1();
}
};
}
}
echo (new Outer)->func2()->func3(); //6
复制代码
简单讲解一下这个例子的关键点:
-
链式调用是由左至右执行; 创建父类对象, 并调用func2();
-
func2()方法建立新的匿名子类对象, 并初始化了$attr3. 这时$this指代的不再是父类对象, 而是子类对象, 这一点很重要.
-
通过子类对象调用, 可以正常调用父类func3(), 完成链式调用, 并执行方法体.
new与代词搭配, 达到实例化指定类的目的, 看下面这个例子:
class ParentClass
{
function creatObjParent()
{
echo ' new self:';
return new self; //返回本类ParentClass的对象
}
}
class Son extends ParentClass
{
function creatObjSon()
{
echo ' new parent:';
return new parent; //返回父类ParentClass的对象
}
}
class Grandson extends Son
{
function creatObjGrandson()
{
echo ' new static:';
return new static; //返回调用者所在类的对象
}
}
$aaa= new Grandson();
echo get_class($aaa->creatObjGrandson()); //new static:Grandson
echo get_class($aaa->creatObjSon()); //new parent:ParentClass
echo get_class($aaa->creatObjParent()); //new self:ParentClass
复制代码
static静态方法创建对象
静态方法可以被类名和对象进行调用, 创建新的对象:
class Store {
static function creatobj()
{
return new static;
}
}
$b=Store::creatobj();
var_dump($b); //object(Store)#8 (0) { }
复制代码
new的一些其他用法
与对象搭配, 创建新的对象;
class NewClass{};
$a= new NewClass();
$b= new $a; //与对象搭配, 创建新的对象;
var_dump($a==$b); //true, 同一类的实例相等
var_dump($a===$b); //false, 指代不同实例, 不全等
复制代码
通过字符串指代类名:
class NewClass{};
$a= 'NewClass';
$b= new $a;
var_dump($b);
复制代码
这里补充一下对象间的比较原则:
当使用比较运算符(==)比较两个对象变量时,比较的原则是:如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等。
而如果使用全等运算符(===),这两个对象变量一定要指向某个类的同一个实例(即同一个对象)。
4. 对象类型转换(object)
我们还可以将其他类型的数据, 比如字符串, 数字或数组转换成对象. 只需要在被转换对象前面加(object)即可.
字符串转化为对象:
$a= 'NewClass';
$ob=(object)$a;
var_dump($ob); //object(stdClass)#6 (1) {["scalar"]=>string(8) "NewClass"}
复制代码
可以看到它创建了一个内置类stdClass, 并将字符串作为’scalar’的属性值; 这种处理方式同样应用于数字型, 大家可以在本地测试一下.
数组转化为对象:
$a= [1,2,3,[1,2,3],'one'=>123,'two'=>321];
$ob=(object)$a;
print_r($ob);
//output:
stdClass Object
(
[0] => 1
[1] => 2
[2] => 3
[3] => Array
(
[0] => 1
[1] => 2
[2] => 3
)
[one] => 123
[two] => 321
)
复制代码
可以看到, 同样创建了一个内置类stdClass, 并将key作为了属性名, value作为了属性值, 如果是多维数组, 则第二维数组会作为一个属性的值. 你还可以通过指定key值来指定属性名. 可见将数组转化为对象, 是一个很快捷的创建对象的方式;
但是在转化数组型的时候注意一点: 属性名的命名规则, 由字母或者下划线开头,后面跟上任意数量的字母,数字,或者下划线。如果是不符合规则的属性名, 会导致无法访问属性.
2. 对象的赋值和引用
我们可以通过一个对象给另一个变量赋值, 或建立两个对象间的引用关系, 来创建另一个对象:
class Myclass{};
$a=new MyClass;
$b=$a;
$c=&$a;
$d=new MyClass;
echo (int)($b===$a); //1, $b, $a两者全等, 指向相同对象
echo (int)($c===$a); //1, $c,$a两者全等, 指向相同对象
print_r($c===$b); //1, $c, $b全等;
var_dump($d==$a); //true, 两者都是同一个类的对象
var_dump($d===$a); //False, 两者指向不同对象
$a->{'test'}=5; //创建新属性并赋值
print_r($b); //Myclass Object ( [test] => 5 )
Print_r($c); //Myclass Object ( [test] => 5 )
$a=5;
var_dump($c); //int(5)
var_dump($b); //object(Myclass)#11 (1) { ["test"]=> int(5) }
复制代码
从上例可以看出关于引用和赋值的几点原理:
-
无论是直接赋值, 还是引用, 等号两边指向的都是同一个实例. 当其中一个对象属性发生变化时, 另外两个对象同步变化
-
同属于一个类的两个对象相等, 但不全等, 只有指向同一对象的两个变量才是全等的.
-
当存在引用关系的其中一个变量c也同步变化, 但不会影响到普通赋值的变量$b.
关于PHP中对象的存储方式, 类似于指针:
一个对象变量不是保存整个对象的值, 而是保存一个标识符来访问真正的对象内容。 当对象作为参数传递/作为结果返回/或者赋值给另外一个变量,另外一个变量跟原来的不是引用的关系,只是他们都保存着同一个标识符的拷贝,这个标识符指向同一个对象的真正内容。