this,static,执行程序的顺序等等留意点

self、static 和 $this 的区别
为了更好地理解 self、static 和 $this 的区别,先来看一个示例。

<?php
class A {

protected $name = ‘A‘;
static $alias = ‘a‘;
const HASH = ‘md5‘;

public function dd() {
echo $this->name; echo ‘--‘;
echo static::$alias; echo ‘--‘; // 后期静态绑定
echo static::HASH; echo ‘--‘; // 后期静态绑定
echo self::$alias; echo ‘--‘;
echo self::HASH; echo ‘--‘;

var_dump(new self); echo ‘--‘;
var_dump($this); echo ‘--‘;
var_dump(new static); echo ‘<br>‘; // 后期静态绑定
}

public static function who() {
echo __CLASS__;
echo ‘ [ This is A ]‘; echo ‘<br>‘;
}

public static function test() {
self::who();
}

public static function test2() {
static::who(); // 后期静态绑定
}

public static function getInstance() {
var_dump(new self); echo ‘--‘;
var_dump(new static); echo ‘<br>‘; // 后期静态绑定
}
}

class B extends A {
protected $name = ‘B‘;
static $alias = ‘b‘;
const HASH = ‘sha1‘;

public static function who() {
echo __CLASS__;
echo ‘ [ This is B ]‘; echo ‘<br>‘;
}
}

class C extends B {
public static function who() {
echo __CLASS__;
echo ‘ [ This is C]‘; echo ‘<br>‘;
}
}

(new A)->dd(); // A--a--md5--a--md5--object(A)#2 (1) { ["name":protected]=> string(1) "A" } --object(A)#1 (1) { ["name":protected]=> string(1) "A" } --object(A)#2 (1) { ["name":protected]=> string(1) "A" }
(new B)->dd(); // B--b--sha1--a--md5--object(A)#2 (1) { ["name":protected]=> string(1) "A" } --object(B)#1 (1) { ["name":protected]=> string(1) "B" } --object(B)#2 (1) { ["name":protected]=> string(1) "B" }

A::who(); // A [ This is A ]
B::who(); // B [ This is B ]

A::test(); // A [ This is A ]
B::test(); // A [ This is A ]

A::test2(); // A [ This is A ]
B::test2(); // B [ This is B ]
C::test2(); // C [ This is C]

A::getInstance(); // object(A)#1 (1) { ["name":protected]=> string(1) "A" } --object(A)#1 (1) { ["name":protected]=> string(1) "A" }
B::getInstance(); // object(A)#1 (1) { ["name":protected]=> string(1) "A" } --object(B)#1 (1) { ["name":protected]=> string(1) "B" }
总结说明:

self 和 __CLASS__,都是对当前类的静态引用,取决于定义当前方法所在的类。也就是说,self 写在哪个类里面, 它引用的就是谁。
$this 指向的是实际调用时的对象,也就是说,实际运行过程中,谁调用了类的属性或方法,$this 指向的就是哪个对象。但 $this 不能访问类的静态属性和常量,且 $this 不能存在于静态方法中。
static 关键字除了可以声明类的静态成员(属性和方法)外,还有一个非常重要的作用就是后期静态绑定。
self 可以用于访问类的静态属性、静态方法和常量,但 self 指向的是当前定义所在的类,这是 self 的限制。
$this 指向的对象所属的类和 static 指向的类相同。
static 可以用于静态或非静态方法中,也可以访问类的静态属性、静态方法、常量和非静态方法,但不能访问非静态属性。
静态调用时,static 指向的是实际调用时的类;非静态调用时,static 指向的是实际调用时的对象所属的类。
后期静态绑定
后期静态绑定(也叫延迟静态绑定),可用于在继承范围内引用静态调用的类,也就是代码运行时最初调用的类。

后期静态绑定本想通过引入一个新的关键字来表示,但最终还是沿用了 static 关键字。

工作原理
确切地说,static 后期静态绑定的工作原理是存储了上一个非转发调用(non-forwarding call)的类名。

当进行静态方法调用时,该类名(static指向的类名)为明确指定的那个(通常是 :: 运算符的左侧部分),即实际调用时的类。

如上述示例中的:

A::test2();
B::test2();
static 和 self 的区别:

self 可以用于访问类的静态属性、静态方法和常量,但 self 指向的是当前定义所在的类,这是 self 的限制。
static 也可以用于访问类的静态属性、静态方法和常量,static 指向的是实际调用时的类。
当进行非静态方法调用时,该类名(static指向的类名)为该对象所属的类,即实际调用时的对象所属的类。

如上述示例中的:

(new A)->dd();
(new B)->dd();
static 和 $this 有点类似,但又有区别:

$this 指向的对象所属的类和 static 指向的类相同。
$this 不能用于静态方法中,也不能访问类的静态属性和常量。
$this 指向的是实际调用的对象。
static 可以用于静态或非静态方法中,也可以访问类的静态属性、静态方法、常量和非静态方法,但不能访问非静态属性。
static 指向的是实际调用时的对象所属的类。
转发调用(forwarding call)
所谓的转发调用(forwarding call)指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call() 。

可用 get_called_class() 函数来获取被调用的方法所在的类名。

以下四种形式的调用,都是转发调用:

self::
parent::
static::
forward_static_call()
除此之外的调用,就是非转发调用。

非转发调用(non-forwarding call)
后期静态绑定的工作原理是存储了上一个非转发调用(non-forwarding call)的类名。

通过具体的类名或具体的对象进行的调用都是非转发调用。

比如:

A::test2();
B::test2();

(new A)->dd();
(new B)->dd();
注意事项
非静态环境下的私有方法的查找顺序
在非静态环境下,在类的非静态方法中,使用 $this 和 static 调用类的私有方法时,执行方式有所不同。

$this 会优先寻找所在定义范围(父类)中的私有方法,如果存在就调用。
static 是先到它指向的类(子类)中寻找私有方法,如果找到了就会报错,因为私有方法只能在它所定义的类内部调用;如果没找到,再去所在定义范围(父类)中寻找该私有方法,如果存在就调用。
具体来说,$this 会先到所在定义范围内寻找私有方法,再到它指向的对象所属的类中寻找私有方法,然后寻找公有方法,最后到所在定义范围内寻找公共方法。只要找到了匹配的方法,就调用,并停止查找。

而 static 则是先到它指向的类中寻找私有方法,再寻找共有方法;然后到所在定义范围内寻找私有方法,再寻找共有方法。只要找到了匹配的方法,就调用,并停止查找。

下面是一个例子:

<?php
class A {
private function foo () {
var_dump($this); echo ‘--‘;
var_dump(new static); echo ‘--‘;

echo __CLASS__; echo ‘--‘;
echo get_called_class();
echo ‘<br>‘;
}

public function test () {
$this -> foo ();
static:: foo ();
echo ‘<br>‘;
}
}

class B extends A { }

class C extends A {
private function foo () {
echo ‘this is C‘;
}
}

(new B())->test();
(new C())->test();
输出结果为:

object(B)#1 (0) { } --object(B)#2 (0) { } --A--B
object(B)#1 (0) { } --object(B)#2 (0) { } --A--B

object(C)#1 (0) { } --object(C)#2 (0) { } --A--C

Fatal error: Uncaught Error: Call to private method C::foo() from context ‘A‘
关于后期静态绑定的解析
后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止。如果静态调用使用了 parent:: 或者 self:: 等转发调用的形式,将会转发调用信息。

<?php
class A {
public static function foo () {
static:: who ();
}

public static function who () {
echo __CLASS__ . "\n" ;
}
}

class B extends A {
public static function test () {
A :: foo ();
parent :: foo ();
self :: foo ();
static::foo();
forward_static_call([‘A‘, ‘foo‘]);
echo ‘<br>‘;
}

public static function who () {
echo __CLASS__ . "\n" ;
}
}

class C extends B {
public static function who () {
echo __CLASS__ . "\n" ;
}

public static function test2() {
self::test();
}
}

class D extends C {
public static function who () {
echo __CLASS__ . "\n" ;
}
}

B::foo();
B::test();

C::foo();
C::test();

D::foo();
D::test2();
以上的输出结果为:

B A B B B B
C A C C C C
D A D D D D
static 后期静态绑定的工作原理是存储了上一个非转发调用(non-forwarding call)的类名。请记住这句话。

下面的例子是非转发调用。

A::foo(); // 输出 A

B::foo(); // 输出 B

C::foo(); // 输出 C
后期静态绑定 static ,是定义在了 foo() 方法中,哪个类通过非转发调用的形式调用 foo() 方法, foo() 方法中的 static 指向的就是哪个类。

但是,如果通过转发调用的形式,调用 foo() 方法,如:

parent :: foo ();
self :: foo ();
static::foo();
forward_static_call([‘A‘, ‘foo‘]);
那么,就以转发调用代码所在的方法 test() 为准,哪个类通过非转发调用的形式调用 test() 方法, foo() 方法中的 static 指向的就是哪个类。

假如调用 test() 方法时,也采用了转发调用的形式,如:

public static function test2() {
self::test();
}
那么,就以 test2() 方法为准 ... 依次类推。

也就是说,在使用了后期静态绑定的基类中,后期静态绑定所在的方法如果被转发调用,则 static 的指向,会一直向上追溯,直到遇到非转发调用的形式。
---------------------
作者:lamp_yang_3533
来源:CSDN
原文:https://blog.csdn.net/lamp_yang_3533/article/details/79912453
版权声明:本文为博主原创文章,转载请附上博文链接!

原文地址:https://www.cnblogs.com/gaogch/p/10662335.html

时间: 2024-10-09 08:43:22

this,static,执行程序的顺序等等留意点的相关文章

调整static变量初始化顺序的一个办法

// wrap the LaunchDir variable in a function to work around static/global initialization order static FString& GetWrappedLaunchDir() { static FString LaunchDir; return LaunchDir; } 在ue4中看到这么一段代码,注释有点意思 不同cpp文件里的全局static变量初始化顺序是不可控的 FString显然会依赖很多内存分配

Java中static字段赋值顺序的问题

看两个例子 public class Client { public static int i = 2; static { i = 100; } public static void main(String[] args) { System.out.println("i=" + i); } } i=100 public class Client { static { i = 100; } public static int i = 2; public static void main(

Java static 执行顺序

package com.rainy.main; import java.util.ArrayList; import java.util.List; public class Test extends A { static List<Object> p = new ArrayList<Object>(); static { p.add(new Object()); } public static void main(String[] args) { System.out.print

【春招】 java static 执行顺序

1 package static类型.执行顺序; 2 3 public class Test { 4 Person person = new Person("Test"); 5 static{ 6 System.out.println("test static");//1 7 } 8 9 public Test() { 10 System.out.println("test constructor");//5 11 } 12 13 public

(转)Java中的static关键字解析

转载: http://www.cnblogs.com/dolphin0520/p/3799052.html 一.static关键字的用途 在<Java编程思想>P86页有这样一段话: "static方法就是没有this的方法.在static方法内部不能调用非静态方法,反过来是可以的.而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法.这实际上正是static方法的主要用途." 这段话虽然只是说明了static方法的特殊之处,但是可以看出static关键

java static块详解

1. java static块执行时机 java static块在类被初始化的时候被执行. 参考<深入Java虚拟机>中的描述,一个java class的生命周期: 装载 通过类的全限定名,产生一个代表该类型的二进制数据流: 解析这个二进制数据流为方法区内的数据结构: 创建一个表示该类型的java.lang.Class的实例. 如果一个类装载器在预先装载的时遇到缺失或错误的class文件,它需要等到程序首次主动使用该类时才报告错误. 连接 验证,确认类型符合Java语言的语义,检查各个类之间的

Java中的static关键字解析

http://www.cnblogs.com/dolphin0520/p/3799052.html 一.static关键字的用途 在<Java编程思想>P86页有这样一段话: “static方法就是没有this的方法.在static方法内部不能调用非静态方法,反过来是可以的.而且可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法.这实际上正是static方法的主要用途.” 这段话虽然只是说明了static方法的特殊之处,但是可以看出static关键字的基本作用,简而言之,一句

java中static关键字解析

static关键字是很多朋友在编写代码和阅读代码时碰到的比较难以理解的一个关键字,也是各大公司的面试官喜欢在面试时问到的知识点之一.下面就先讲述一下static关键字的用法和平常容易误解的地方,最后列举了一些面试笔试中常见的关于static的考题.以下是本文的目录大纲: 一.static关键字的用途 二.static关键字的误区 三.常见的笔试面试题 若有不正之处,希望谅解并欢迎批评指正. 请尊重作者劳动成果,转载请标明原文链接: http://www.cnblogs.com/dolphin05

java中的static详解

如果一个类成员被声明为static,它就能够在类的任何对象创建之前被访问,而不必引用任何对象.static 成员的最常见的例子是main( ) .因为在程序开始执行时必须调用main() ,所以它被声明为static. 声明为static的变量实质上就是全局变量.当声明一个对象时,并不产生static变量的拷贝,而是该类所有的实例变量共用同一个static变量,例如:声明一个static的变量count作为new一个类实例的计数.声明为static的方法有以下几条限制: 1.它们仅能调用其他的s