PHP构造方法
1、默认构造方法
我们实例化一个对象
$obj = new A();
$obj = new A(1,2,3);
可以理解了分两步走,一、new,分配内存,二、A(),调用构造方法,分配内存后马上调用。
这个构造方法对应的是类中方法声明的,分配完内存就调用它。
public function __construct() {
//todo
}
如果类中没有声明__construct()方法,但我们在实例化实例的时候也是$obj = new A();按理来说,应该是调用public function __construct(){}这个方法啊,但类中没声明,该怎么办?尽管放心,系统已经构建好了一个默认的构造方法,如果类中没有声明构造方法时,它就出来了。
这个默认的构造方法,空参数,方法体内不干任何东西。也就是说,可以跟A()匹配上了。
如果类中声明了构造方法,那么,系统不再分配一个默认的构造方法给他了。就算JAVA也不是继续保留,以前理解错了。
看下例子:
<?php
class A {
}
$obja = new A(); //类中没有构造方法,空参默认构造方法出来,能匹配得上。
class A {
}
$obja = new A(); //类中没有构造方法,空参默认构造方法出来,能匹配得上。
$obja = new A(1, 2, 4); //虽然所以这样也行。但不鼓励,虽然不报错,但还是把默认的构造方法当做空参数吧,PHP还是缺乏严谨!
?>
================================================================================================
注意:要说一点,这跟java很不一样。
java有系统默认的构造方法,而且构造方法可以重载。什么意思呢?
在java中,如果没有定义构造方法,java真的会生成一个无参数空的构造方法,跟PHP一样。而且,构造方法可以重载,怎样重载?方法名相同,参数类型或者参数个数不一样就当做两个方法。那就是说,重载了之后,就可以有两个构造方法了。
具体例子:
class A {
private int i;
public A(int i) {
this.i = i;
}
prublic A(int i, int j) {
this.i = i + j;
}
//因为声明了构造方法,所以系统不再保留默认的构造方法了。
}
A aobj = new A(); //不可以,系统默认的构造方法不存在。
A aobj = new A(2); //可以,调用A(int i);
A aobj = new A(2,3); //可以,调用 A(int i, int j)
可见,java因为允许重载,所以可以有多个构造方法,随你怎么调用。
但PHP就不是这样了。
PHP不支持重载,一旦方法名相同,就当你同一个方法,管你参数个数和参数类型不一样呢。就是说,只能有一个构造方法。
而且,你一旦定义了自己的构造方法,那么系统自带的构造方法就消失了,不再保留。这点跟JAVA一样。
我们来看例子:
<?php
class A {
public function __construct($i) {
echo 'hello';
}
}
$obja = new A();
class A {
public function __construct($i) {
echo 'hello';
}
}
$obja = new A();
由于是声明了构造方法,系统默认的构造方法消失,不再分配,所以没有空参的构造的方法可匹配,就算java也没办法了,PHP也没办法。
PHP会报一个warning,说你少了1个参数。
如果我们这样呢
<?php
class A {
public function __construct() {
echo 'hehe';
}
public function __construct($i) {
echo 'hello';
}
}
$obja = new A();
class A {
public function __construct() {
echo 'hehe';
}
public function __construct($i) {
echo 'hello';
}
}
$obja = new A();
更惨,会爆出一个fatal error,说你不能重新声明。
这就说明:PHP不支持重载,只要方法名相同就当你同一个方法,而一个类中有两个相同的方法,会fatal error!
================================================================================================
2、继承问题
上一篇《继承与调用(重新绑定)》已经说了,子类继承,把一个父类嵌入在里面,在子类中调用父类继承下来的方法还是可以调用到父类的private资源,因为父类的资源空间也是分配到子类那里。之前理解错了,应该是不会生成两个对象的,只会生成一个。因为如果继承了抽象类,抽象类是没办法生成对象的,所以这说法不通。如何论证,我们看看这个例程:
<?php
class A {
public $x = 5;
public $y = 6;
public $z = 7;
}
class B extends A {
public $m = 1;
public $n = 2;
public $z = 10;
}
class C {
public $x = 5;
public $y = 6;
public $z = 7;
public $m = 1;
public $n = 2;
}
$start = memory_get_usage();
echo "Starting allocation $start<br>";
$a = new A();
$state_a = memory_get_usage();
echo "Allocated memory for new A() " . ($state_a - $start) . '<br>';
$b = new B();
$state_b = memory_get_usage();
echo "Allocated memory for new B() " . ($state_b - $state_a) . '<br>';
$c = new C();
$state_c = memory_get_usage();
echo "Allocated memory for new C() " . ($state_c - $state_b) . '<br>';
class A {
public $x = 5;
public $y = 6;
public $z = 7;
}
class B extends A {
public $m = 1;
public $n = 2;
public $z = 10;
}
class C {
public $x = 5;
public $y = 6;
public $z = 7;
public $m = 1;
public $n = 2;
}
$start = memory_get_usage();
echo "Starting allocation $start<br>";
$a = new A();
$state_a = memory_get_usage();
echo "Allocated memory for new A() " . ($state_a - $start) . '<br>';
$b = new B();
$state_b = memory_get_usage();
echo "Allocated memory for new B() " . ($state_b - $state_a) . '<br>';
$c = new C();
$state_c = memory_get_usage();
echo "Allocated memory for new C() " . ($state_c - $state_b) . '<br>';
输出:
Starting allocation 338072
Allocated memory for new A() 424
Allocated memory for new B() 520
Allocated memory for new C() 520
Allocated memory for new A() 424
Allocated memory for new B() 520
Allocated memory for new C() 520
可以看到,分配给B的空间是跟分配给C的空间是一样的。如果是生成了两个对象,那么父对象中有$z,子对象也有$z,加起来肯定比C大。但是由于继承重写,他们在B中是占同一个空间的(覆盖了),所以,应该是只有一个对象B。
但是要注意的是多态性,也就是动态绑定,上一篇已经说得很清楚,这里不多说了。
================================================================================================
现在来说构造方法的继承。
PHP是比较简单的,构造方法肯定只有一个。
要不就是没有声明,系统默认的空参不干活构造方法出来。
要不就是声明了,系统默认的消失。因为不支持类内重载,所以肯定只有一个了。
注意:java的构造方法不能继承,因为在java中,构造方法不属于成员
A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.
但PHP是能够把构造方法也继承下来。
Note: Parent constructors are not called implicitly if the child class defines a constructor. In order to run a parent constructor, a call to parent::__construct() within the child constructor is required. If the child does not define a constructor then it may be inherited from the parent class just like a normal class method (if it was not declared as private).
翻译:如果子类定义了构造方法,父类的构造方法是不会被隐式调用的。为了使用父类的构造方法,当定义子类构造方法的时候,你可以调用parent::__construct。
如果一个子类没有定义构造方法,父类的构造方法会像普通的方法一样被继承下来(声明为private除外)。(这样的话,一般$this都是针对父类做初始化了,子类没管)。
这里有一点感受:我觉得把构造方法都继承了下来,是不太合理的。因为,A有一个构造方法,B继承了下来,没写,C又继承了B,构造方法又继承了下来.....等你到了第N个的时候,你都不知道上一个有没有构造方法了。那么当你在写第N子类的构造方法是,你怎么构造上一个的父类?用默认的空构造方法吗?万一不匹配就完了。
所以,定一个规则(父类不是抽象类,不是接口):
1、如果继承,子类写构造方法的时候,第一句一定要parent::__construct()。
2、如果父类写了构造方法,子类一定要写。不要继承。
3、如果父类没写构造方法,子类可以根据自己本身的需要写不写构造方法。