公共,私有和受保护的有什么区别?


988

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

例子:

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

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

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

59
我认为这个问题也可以从使用每个示例的实际示例中得到答案,而不是提供每个关键字的作用的字面定义。
马修

70
我真的认为这个问题应该是公开的,而不是受保护的。
dotancohen

Answers:


1260

你用:

  • public 范围,以使该属性/方法可从任何位置,对象的其他类和实例使用。

  • private 当您希望您的属性/方法仅在其自己的类中可见时。

  • protected 要使属性/方法在扩展当前类的所有类(包括父类)中可见时显示范围。

更多:(有关全面信息)


79
protected当您想使变量/函数在扩展当前类及其父类的所有类中可见时,请使用范围。
沙希德

4
@Shahid-我不明白你的意思。任何扩展A类的类也扩展A的父类,不是吗?
JDelage 2012年

4
@JDelage-请查看链接http://www.php.net/manual/en/language.oop5.visibility.php#109324
Shahid

4
@Growler为什么那么麻烦使用对象呢?
J.Steve 2014年

27
@Growler,一个更有用的答案是,尽可能多地隐藏对象的内部工作是很好的。这样就不太可能破裂。如果将所有内容公开,那么另一个程序员可能会更改一个变量,除了对象的内部工作方式之外,您不希望更改该变量。
在塞浦路斯放松2014年

1174

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类的成员外!

好吧,反射是什么?

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

前言

我们有一个名为的类,Grandpas并说我们有三个属性。为了便于理解,请考虑以下三个具有名称的爷爷:

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

让我们让他们(分配调节剂)publicprotectedprivate分别。您非常了解,protected并且private无法在课堂之外访问成员。现在,让我们使用反射与该语句矛盾。

编码

<?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

常见的误解:

请不要与以下示例混淆。如您所见,如果不使用反射,则不能在类外部访问privateprotected成员

<?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属性。静态类成员将不会显示。


更多资源:



对于后来加入该会议表示歉意。你能告诉我为什么有人会用这些吗?您已经完美地解释了它们的工作原理等。我只想知道使用它们的好处。谢谢
JamesG

@JamesG在上面的其他评论中有所解释。stackoverflow.com/questions/4361553/…–
cjmling

我不知道为什么可能没有这个问题,但是没有人提到在PHP中还有另外两个访问修饰符:abstract和final这个关键字只能用于PHP类,但它仍然可以访问修饰符
bxN5

1
我建议您在这里阅读Dhairya Lakhera提供的有关抽象的解释:stackoverflow.com/questions/2558559/…。它是Shankar Damodaran解释的完美补充。
Julio Marchi

83

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

如果您“编码到接口而不是实现”,那么通常就很容易做出可视性决策。通常,除非您有充分的理由公开变量,否则变量应为私有变量或受保护的变量。改用公共访问器(获取者/设置者)来限制和规范对类内部的访问。

以汽车为例,速度,档位和方向等都是私有实例变量。您不希望驾驶员直接操纵空燃比。而是将有限数量的操作公开为公共方法。到汽车的界面可能包括诸如accelerate()deccelerate()/ brake()setGear()turnLeft()turnRight(),等。

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

这种方法还允许您更改和改进类中公共方法的实现,而不会破坏与客户端代码的接口约定。例如,您可以改进该accelerate()方法以提高燃油效率,但该方法的使用将保持不变;客户端代码无需进行任何更改,但仍可从效率提高中获得收益。

编辑:由于似乎您仍在学习面向对象的概念(比任何一种语言的语法都难于掌握),所以我强烈建议您阅读Matt Zandstra 编写的《 PHP对象,模式和实践》。这是第一本书,教我如何有效地使用OOP,而不仅仅是教我语法。我之前已经学过语法,但是如果不了解OOP的“原因”,那将毫无用处。


3
在这篇文章的编辑中推荐的书确实非常出色。到目前为止,我证明的那部分是很有启发性的。前几章回答了我大部分与课堂有关的问题。
乔西亚2014年

本书使我能够真正理解对象,而又不会像不必要的细节那样将我的思想排挤出去,例如Smalltalk中的示例,这些书是David A Taylor写的,他是面向对象技术:对象指南的管理者指南业务工程。两者都只有100页,每页都足够轻松阅读一个下午。当然,有Gamma等人的“ 设计模式”,尽管基本方法可以简单地通过“将要更改的内容子类化”来描述。
Patanjali

一个非常好的类比。您有一个受保护的还是私有的?
贾尼斯·埃默里斯(JānisElmeris)

79

private -只能从WITHIN类中访问

protected -可以从WITHIN类和INHERITING类访问

public -也可以从类外的代码访问

这适用于函数以及变量。


不确定受保护的定义是否正确,从实际选择的答案来看,受保护的-仅可以从继承的类开始访问,而不能从原始/父类访问。说“ WITHIN the class”可能有点令人困惑。
pal4life 2012年

7
我不这么认为,实际上似乎选择的答案是令人困惑的答案。请参阅Shahids评论。恕我直言,可以从原始类中很好地访问受保护的方法。
奥拉夫(Olaf)2012年

一个班级可以访问另一个班级的公众吗?
Serjas 2012年

1
@Serjas:不,只有另一个对象的,除非它们是静态方法/字段。
DanMan

我不知道这是否适用于所有编程语言,但是在PHP中,可以在声明了该属性/方法的类中或从定义该属性/方法的类继承的类中访问“受保护”的属性/方法。
John Slegers 2015年

25

区别如下:

Public ::类的任何用户都可以直接访问公共变量或方法。

Protected ::受保护的变量或方法不能由该类的用户访问,但可以在从该类继承的子类内部访问。

Private ::只能从定义它的类内部访问私有变量或方法,这意味着不能从扩展该类的子级调用私有变量或方法。


17

具有抽象示例的可见性范围 :: 易于理解

属性或方法的这种可见性是通过预先声明三个关键字(公共,保护和私有)之一来声明的

Public:如果将属性或方法定义为public,则意味着可以由任何引用对象的对象访问和操纵它。

  • 摘要例如 将公众可见度范围视为任何人都可以参加的“公众野餐”

受保护的:将属性或方法的可见性设置为受保护的成员时,只能在类本身内以及通过继承和继承的类访问。(继承:-一个类可以具有另一个类的所有属性和方法)。

  • 可以将“公司野餐”视为“公司野餐”,其中不允许公司成员及其家人入场。这是最常见的范围限制。

私有:将属性或方法的可见性设置为私有时,只有具有私有成员的类才能访问这些方法和属性(在类内部),尽管可能存在任何类关系。

  • 与野餐类似,可以将其视为“只允许公司成员参加的公司野餐”。没有家庭,也没有公众。

15
/**
 * Define MyClass
 */
class MyClass
{
    public $public = 'Public';
    protected $protected = 'Protected';
    private $private = 'Private';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj = new MyClass();
echo $obj->public; // Works
echo $obj->protected; // Fatal Error
echo $obj->private; // Fatal Error
$obj->printHello(); // Shows Public, Protected and Private


/**
 * Define MyClass2
 */
class MyClass2 extends MyClass
{
    // We can redeclare the public and protected method, but not private
    protected $protected = 'Protected2';

    function printHello()
    {
        echo $this->public;
        echo $this->protected;
        echo $this->private;
    }
}

$obj2 = new MyClass2();
echo $obj2->public; // Works
echo $obj2->private; // Undefined
echo $obj2->protected; // Fatal Error
$obj2->printHello(); // Shows Public, Protected2, Undefined

摘自 :

http://php.net/manual/zh/language.oop5.visibility.php


12

⚡️下面是一个简单的方法来记住的范围publicprotectedprivate

PUBLIC

  • public 作用域:一个公共变量/函数可用于对象和其他类。

PROTECTED

  • protected 作用域:所有扩展当前类的类都可以使用受保护的变量/函数。
  • 没有!对象无法访问此范围

PRIVATE

  • private 作用域:私有变量/函数仅在定义它的当前类中可见。
  • 没有!扩展当前类的类无法访问此范围。
  • 没有!对象无法访问此范围。

阅读PHP手册中方法或变量的可见性


9

考虑“ when ”:
如果我不确定,我一开始倾向于将所有内容声明为私有。原因是,公开私有方法通常比其他方法容易得多。那是因为您至少可以确保私有方法没有在任何地方使用,而是在类本身中使用。公共方法可能已在各处使用,可能需要大量重写。

更新:我protected现在使用默认值,因为我发现它对于封装已经足够好了,并且在扩展类时也不会造成任何阻碍(无论如何我都尽量避免这样做)。只有当我有充分的理由使用其他两个时,我才会这样做。

使用private方法的一个很好的理由是实现该对象固有的某些东西,即使扩展类也不应更改(事实原因,除了封装之外,例如内部状态管理)。最终,仍然很容易找到protected通常在哪里使用一种方法,因此我默认为protected如今。我承认,也许不是100%客观的“在战trench中”的经历。


3
随着您的更新:您能否更清楚地说明“足够好”和“充分理由”在这里如何结合?例如,使用private仍然可以“足够好”地使用,但是您不建议再使用,尽管先前的理由听起来像是“好的理由”:封装。
13年

@hakre:我们应该努力进行封装的原因是为了避免将状态泄漏到外部范围中。protected已经做到了,但是您可以灵活地扩展/继承。同样,除非您有充分的理由做到这一点private
DanMan '17

好吧,这可能是我们不同意的观点:protected实际上会泄漏到外部范围中,并且通常会以您的方式出现,因为它支持不良的设计决策,例如隐式地偏爱继承而更好地偏向于组成。这就是为什么除非您有实际要求,否则坚持私有通常是开始编写代码的更好方法。这也可以防止在实际上不需要设计决策时就过早做出决策。
hakre

我不会对您的整体观点争论不休,因为它足够公平,但protected不会泄漏到外部范围(调用/访问方法/字段的代码)中,而只会泄漏到内部范围(扩展类)中。有一个差异,对您来说可能很小。跟踪一个protected字段的使用比查找一个字段要容易得多public
DanMan '17

6

PHP手册对这里的问题有很好的阅读。

属性或方法的可见性可以通过在声明的前面加上关键字public,protected或private来定义。宣告为公开的班级成员可以在任何地方访问。声明为protected的成员只能在该类内部以及继承的和父类访问。声明为私有的成员只能由定义该成员的类访问。


6

对我来说,这是了解三种属性类型的最有用的方法:

Public:如果可以,可以在代码中的任何位置直接访问和更改此变量,则可以使用它。

类外部的用法示例:

$myObject = new MyObject()
$myObject->publicVar = 'newvalue';
$pubVar = $myObject->publicVar;

受保护的:当您要强制其他程序员(和您自己)在访问和设置变量时使用类之外的getter / setter时使用此方法(但您应保持一致,并在类内也使用getter和setter)。这private是您设置所有类属性的默认方法。

为什么?因为如果您在将来的某个时候(甚至可能在5分钟之内)决定要对该属性返回的值进行操作,或者想要在获取/设置之前对其进行某些操作,那么您就可以做到这一点而无需重构任何地方在您的项目中使用了它。

类外部的用法示例:

$myObject = new MyObject()
$myObject->setProtectedVar('newvalue');
$protectedVar = $myObject->getProtectedVar();

私有private属性与protected属性非常相似。但是与众不同的特征/差异在于,它private也使得子类无法访问,而无需使用父类的getter或setter。

因此,基本上,如果您对属性使用getter和setter(或者如果它仅由父类在内部使用并且在其他任何地方都不能访问),那么最好也这样做private,以防止任何人尝试直接使用它并引入错误

子类(扩展MyObject)内部的用法示例:

$this->setPrivateVar('newvalue');
$privateVar = $this->getPrivateVar();


4

PHP中的变量以三种不同的类型转换:

Public:此变量类型的值在所有范围内可用,并在您执行代码时调用。声明为:public $examTimeTable;

专用:此类变量的值仅在其所属的类上可用。 private $classRoomComputers;

受保护的:仅此类的值,并且仅当以继承形式或其子类授予访问权限时才可用。通常用于::授予父类的访问权限

protected $familyWealth;


3

重提一个古老的问题,但是我认为考虑这个问题的一种好方法是使用您定义的API。

  • public -标记为public的所有内容都是API的一部分,任何使用您的类/接口/其他人的人都将使用并依赖该API。

  • protected-不要上当,这也是API的一部分!人们可以继承,扩展您的代码并使用任何标记为受保护的内容。

  • private-可以随意更改私有属性和方法。没有其他人可以使用这些。这些是您可以进行的唯一更改,无需进行重大更改。

或用Semver术语:

  • 更改任何内容publicprotected应视为重大更改。

  • 任何新事物publicprotected应该(至少)是不重要的

  • 只有新内容/更改内容private才能进行PATCH

因此,在维护代码方面,最好注意所做的事情,public或者protected因为这些是您对用户有希望的事情。


1

当我们在项目中遵循面向对象的php时,我们应遵循一些规则以在php中使用访问修饰符。今天,我们将清楚地了解什么是访问修饰符以及如何使用它。PHP访问修饰符用于设置PHP类及其成员(这些成员是在类范围内定义的函数和变量)的访问权限。在php中,类成员有三个作用域。

  1. 上市
  2. 私人的
  3. 受保护的

现在,让我们看下面的图片以了解访问修饰符的访问级别 在此处输入图片说明

现在,让我们看一下以下列表,以了解可能用作访问修饰符的PHP关键字。

public:-类或使用此访问修饰符定义的成员可以从任何地方公开访问,甚至可以从该类范围之外访问。

private:-使用此关键字的类成员将在类本身内访问。我们无法从子类访问私有数据。它保护成员免受外部类访问。

protected:-与private相同,除了允许子类访问受保护的超类成员。

现在查看表以了解访问修饰符阅读全文php access modifire


1

Public:是声明变量或方法时的默认状态,可以由任何对象直接访问该对象。

Protected:只能在对象和子类中访问。

Private:只能在对象中引用,不能在子类中引用。


0

提到的关键字是访问修饰符,可帮助我们实现封装(或信息隐藏)。它们告诉编译器哪些其他类应有权访问所定义的字段或方法。

private-仅当前类有权访问该字段或方法。

protected-只有此类的当前类和子类(有时也包括相同包装的类)才能访问该字段或方法。

public-任何类都可以引用该字段或调用该方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.