协慌网

登录 贡献 社区

public,private 和 protected 之间有什么区别?

我何时以及为什么要在类中使用publicprivateprotected函数和变量?他们之间有什么区别?

例子:

// Public
public $variable;
public function doSomething() {
  // ...
}

// Private
private $variable;
private function doSomething() {
  // ...
}

// Protected
protected $variable;
protected function doSomething() {
  // ...
}

答案

你用:

  • public范围,使该变量 / 函数可以从对象的任何地方,其他类和实例中获得。

  • 当您希望变量 / 函数仅在其自己的类中可见时, private范围。

  • 当您希望在扩展当前类(包括父类)的所有类中显示变量 / 函数时, protected范围。

更多:(如需全面信息)

dd

上市:

将方法(函数)或属性(变量)声明为public ,可以通过以下方式访问这些方法和属性:

  • 声明它的同一个类。
  • 继承上述声明的类的类。
  • 此类之外的任何外来元素也可以访问这些内容。

例:

<?php

class GrandPa
{
    public $name='Mark Henry';  // A public variable
}

class Daddy extends GrandPa // Inherited class
{
    function displayGrandPaName()
    {
        return $this->name; // The public variable will be available to the inherited class
    }

}

// Inherited class Daddy wants to know Grandpas Name
$daddy = new Daddy;
echo $daddy->displayGrandPaName(); // Prints 'Mark Henry'

// Public variables can also be accessed outside of the class!
$outsiderWantstoKnowGrandpasName = new GrandPa;
echo $outsiderWantstoKnowGrandpasName->name; // Prints 'Mark Henry'

保护:

将方法(函数)或属性(变量)声明为protected ,可以访问这些方法和属性

  • 声明它的同一个类。
  • 继承上述声明的类的类。

局外人无法访问这些变量。 “局外人” 在某种意义上说它们不是声明类本身的对象实例。

例:

<?php

class GrandPa
{
    protected $name = 'Mark Henry';
}

class Daddy extends GrandPa
{
    function displayGrandPaName()
    {
        return $this->name;
    }

}

$daddy = new Daddy;
echo $daddy->displayGrandPaName(); // Prints 'Mark Henry'

$outsiderWantstoKnowGrandpasName = new GrandPa;
echo $outsiderWantstoKnowGrandpasName->name; // Results in a Fatal Error

确切的错误是这样的:

PHP 致命错误:无法访问受保护的属性 GrandPa :: $ name


私人的:

将方法(函数)或属性(变量)声明为private ,可以通过以下方式访问这些方法和属性:

  • 声明它的同一个类。

局外人无法访问这些变量。局外人在某种意义上说它们不是声明的类本身的对象实例,甚至是继承声明的类的类。

例:

<?php

class GrandPa
{
    private $name = 'Mark Henry';
}

class Daddy extends GrandPa
{
    function displayGrandPaName()
    {
        return $this->name;
    }

}

$daddy = new Daddy;
echo $daddy->displayGrandPaName(); // Results in a Notice 

$outsiderWantstoKnowGrandpasName = new GrandPa;
echo $outsiderWantstoKnowGrandpasName->name; // Results in a Fatal Error

确切的错误消息将是:

注意:未定义的属性:Daddy :: $ name
致命错误:无法访问私有属性 GrandPa :: $ name


反射解剖爷爷班

这个主题并没有真正超出范围,我在这里添加它只是为了证明反射真的很强大。正如我在上面三个例子中所说的那样,无法在课堂外访问protectedprivate成员(属性和方法)。

但是,通过反射,你甚至可以通过访问课堂外的protectedprivate成员来做到超常

好吧,什么是反思?

Reflection 增加了对类,接口,函数,方法和扩展进行反向工程的能力。此外,它们还提供了检索函数,类和方法的文档注释的方法。

前言

我们有一个名为Grandpas的班级,并说我们有三个属性。为了便于理解,请考虑有三个名字的爷爷:

  • 马克亨利
  • 约翰克拉什
  • 威尔琼斯

让我们分别将它们(赋值修饰语)设为publicprotectedprivate 。您非常清楚,无法在课堂外访问protectedprivate成员。现在让我们反驳使用反射的陈述。

代码

<?php

class GrandPas   // The Grandfather's class
{
    public     $name1 = 'Mark Henry';  // This grandpa is mapped to a public modifier
    protected  $name2 = 'John Clash';  // This grandpa is mapped to a protected  modifier
    private    $name3 = 'Will Jones';  // This grandpa is mapped to a private modifier
}


# Scenario 1: without reflection
$granpaWithoutReflection = new GrandPas;

# Normal looping to print all the members of this class
echo "#Scenario 1: Without reflection<br>";
echo "Printing members the usual way.. (without reflection)<br>";
foreach($granpaWithoutReflection as $k=>$v)
{
    echo "The name of grandpa is $v and he resides in the variable $k<br>";
}

echo "<br>";

#Scenario 2: Using reflection

$granpa = new ReflectionClass('GrandPas'); // Pass the Grandpas class as the input for the Reflection class
$granpaNames=$granpa->getDefaultProperties(); // Gets all the properties of the Grandpas class (Even though it is a protected or private)


echo "#Scenario 2: With reflection<br>";
echo "Printing members the 'reflect' way..<br>";

foreach($granpaNames as $k=>$v)
{
    echo "The name of grandpa is $v and he resides in the variable $k<br>";
}

输出:

#Scenario 1: Without reflection
Printing members the usual way.. (Without reflection)
The name of grandpa is Mark Henry and he resides in the variable name1

#Scenario 2: With reflection
Printing members the 'reflect' way..
The name of grandpa is Mark Henry and he resides in the variable name1
The name of grandpa is John Clash and he resides in the variable name2
The name of grandpa is Will Jones and he resides in the variable name3

常见的误解:

请不要与下面的例子混淆。您仍然可以看到,如果不使用反射,则无法在类外部访问private成员和protected成员

<?php

class GrandPas   // The Grandfather's class
{
    public     $name1 = 'Mark Henry';  // This grandpa is mapped to a public modifier
    protected  $name2 = 'John Clash';  // This grandpa is mapped to a protected modifier
    private    $name3 = 'Will Jones';  // This grandpa is mapped to a private modifier
}

$granpaWithoutReflections = new GrandPas;
print_r($granpaWithoutReflections);

输出:

GrandPas Object
(
    [name1] => Mark Henry
    [name2:protected] => John Clash
    [name3:GrandPas:private] => Will Jones
)

调试功能

print_rvar_exportvar_dump调试器函数 。它们以人类可读的形式呈现有关变量的信息。这三个函数将使用 PHP 5 显示对象的protectedprivate属性。将显示静态类成员。


更多资源:


默认情况下,默认为所需的最低可见性通常被视为良好做法,因为这会促进数据封装和良好的界面设计。在考虑成员变量和方法可见性时,请考虑成员在与其他对象的交互中扮演的角色。

如果您 “编写接口而不是实现”,则通常可以非常直接地做出可见性决策。通常,变量应该是私有的或受保护的,除非您有充分的理由公开它们。使用公共访问者(getters / setter)来限制和规范对类内部的访问。

使用汽车作为类比,速度,齿轮和方向等事件将是私有实例变量。您不希望驾驶员直接操纵空气 / 燃料比等事物。相反,您将有限数量的操作公开为公共方法。汽车的接口可能包括诸如accelerate()deccelerate() / brake()setGear()turnLeft()turnRight()等方法。

司机不知道也不应该关心这些行动是如何由汽车的内部实施的,并且暴露该功能可能对驾驶员和其他人在路上造成危险。因此,设计公共接口并将数据封装在该接口后面的良好实践。

此方法还允许您在不破坏接口与客户端代码的契约的情况下更改和改进类中公共方法的实现。例如,您可以改进accelerate()方法以提高燃油效率,但该方法的使用将保持不变; 客户端代码不需要更改,但仍然可以获得效率提升的好处。

编辑:由于你似乎还在学习面向对象的概念(比任何语言的语法都难以掌握),我强烈建议你选择 Matt Zandstra 的PHP 对象,模式和实践的副本。这本书首先教我如何有效地使用 OOP,而不仅仅是教我语法。我事先已经学会了语法,但如果不理解 OOP 的 “为什么”,那就没用了。