【问题标题】:What is the difference between public, private, and protected?公共,私有和受保护之间有什么区别?
【发布时间】:2011-05-20 16:18:48
【问题描述】:

何时以及为什么应该在类中使用publicprivateprotected 函数和变量?它们有什么区别?

例子:

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

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

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

【问题讨论】:

  • 我认为这个问题也将受益于每个使用实际示例的答案,而不是提供每个关键字的字面定义。

标签: php oop private public protected


【解决方案1】:

PHP 手册很好地阅读了问题here

属性或方法的可见性可以通过在声明前加上关键字public、protected或private来定义。声明为 public 的类成员可以在任何地方访问。声明为 protected 的成员只能在类本身内以及被继承类和父类访问。声明为私有的成员只能由定义该成员的类访问。

【讨论】:

    【解决方案2】:

    private - 只能在类内访问

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

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

    这适用于函数和变量。

    【讨论】:

    • 不确定受保护的定义是否正确,从实际选择的答案看来,受保护 - 只能从继承的类开始访问,不能从原始/父类访问。说“在课堂上”可能有点令人困惑。
    • 我不这么认为,实际上似乎选择的答案是这里令人困惑的答案。见沙希德评论。恕我直言,可以从原始类中很好地访问受保护的方法。
    • 一个类可以访问另一个类的public吗?
    • @Serjas:不,只是另一个对象的,除非它们是静态方法/字段。
    • 我不知道这是否适用于所有编程语言,但在 PHP 中,“受保护”属性/方法可以在声明它的类或从该类继承的类中访问定义属性/方法。
    【解决方案3】:

    你使用:

    • public 作用域使该属性/方法可从任何地方、其他类和对象实例使用。

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

    • protected 作用域,当您想让您的属性/方法在所有扩展当前类(包括父类)的类中可见时。

    如果不使用任何可见性修饰符,则属性/方法将是公共的。

    更多:(获取全面信息)

    【讨论】:

    • protected 作用域,当你想让你的变量/函数在所有扩展当前类及其父类的类中可见时。
    • @Shahid - 我不明白你的意思。任何扩展类 A 的类也扩展了 A 的父类,不是吗?
    • @JDelage - 请查看链接http://www.php.net/manual/en/language.oop5.visibility.php#109324
    • @Growler 那为什么还要使用对象呢?
    • @Growler,一个更有帮助的答案是尽可能多地隐藏对象的内部工作。这样它就不太可能破裂。如果您将所有内容都公开,那么另一个程序员可能会更改一个您不希望被对象内部工作以外的任何东西更改的变量。
    【解决方案4】:

    他们在那里允许不同级别的encapsulation

    【讨论】:

    • 你可以稍微充实一下。
    【解决方案5】:

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

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

    private 方法的一个很好的理由是实现该对象固有的东西,即使是扩展类也不应该改变(事实上的原因,除了封装,如内部状态管理)。最终,追踪protected 方法通常在哪里使用仍然很容易,所以我现在默认使用protected。我承认,也许不是 100% 客观的“战壕”体验。

    【讨论】:

    • 随着您的更新:您能否更清楚地说明“足够好”和“充分的理由”是如何结合在一起的?例如,使用private 仍然“足够好” 可以使用,但您不建议再使用,尽管您之前给出的理由听起来仍然像一个“好理由”:封装。
    • @hakre:我们应该努力封装的原因是避免将状态泄漏到外部范围。 protected 已经这样做了,但是您可以灵活地进行扩展/继承。同样,除非您有充分的理由这样做private
    • 嗯,这可能是我们不同意的一点:protected 实际上会泄漏到外部范围,并且通常会妨碍您,因为它支持错误的设计决策,例如隐式支持继承,而支持组合更好。这就是为什么除非您有实际要求,否则坚持使用私有通常是开始编写代码的更好方法。这也将防止在实际上还不需要时过早做出设计决策。
    • 我不会与你的整体观点争论,因为它很公平,但protected 不会泄漏到 outer 范围内(调用/访问方法的代码/字段),但仅限于内部范围(扩展类)。有区别,对你来说可能很小。跟踪 protected 字段的使用情况比跟踪 public 字段的使用要容易得多。
    【解决方案6】:

    区别如下:

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

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

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

    【讨论】:

      【解决方案7】:

      默认为所需的最低可见性通常被认为是一种良好的做法,因为这促进了数据封装和良好的界面设计。在考虑成员变量和方法可见性时,请考虑成员在与其他对象交互中所起的作用。

      如果您“编写接口而不是实现”,那么做出可见性决定通常非常简单。一般来说,变量应该是私有的或受保护的,除非您有充分的理由公开它们。改用公共访问器(getter/setter)来限制和规范对类内部的访问。

      以汽车为例,速度、档位和方向等内容将是私有实例变量。您不希望驾驶员直接操纵空气/燃料比之类的东西。相反,您将有限数量的操作公开为公共方法。汽车的接口可能包括accelerate()deccelerate()/brake()setGear()turnLeft()turnRight()等方法。

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

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

      编辑:由于您似乎仍在学习面向对象的概念(这比任何语言的语法都更难掌握),我强烈推荐拿起一份由 Matt Zandstra 撰写的PHP 对象、模式和实践。这本书首先教我如何有效地使用 OOP,而不仅仅是教我语法。我早在几年前就学会了语法,但如果不了解 OOP 的“原因”,那将毫无用处。

      【讨论】:

      • 本帖编辑推荐的书真的很优秀。到目前为止,我已经证明了这一块非常有启发性。前几章回答了我大部分与课堂相关的问题。
      • David A Taylor 所著的面向对象技术:经理指南 和 具有对象技术的业务工程。两者都只有 100 页,每一个都足够简单,可以在一个下午阅读。当然,还有 Gamma 等人的 Design Patterns,不过基本方法可以简单地用“子类化你想要改变的东西”来描述。
      • 一个很好的类比。你有一个受保护的还是私有的?
      【解决方案8】:

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

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

      Private:此类变量的值仅适用于它所属的类。 private $classRoomComputers;

      受保护:该类的值仅在以继承形式或其子类授予访问权限时可用。一般使用::来授予父类访问权限

      protected $familyWealth;

      【讨论】:

        【解决方案9】:
        /**
         * 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/en/language.oop5.visibility.php

        【讨论】:

          【解决方案10】:

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

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

          公共:如果一个属性或方法被定义为公共的,这意味着它可以被任何可以引用对象的东西访问和操作。

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

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

          • 将受保护的可见范围视为“公司野餐”,其中允许公司成员及其家庭成员而不是公众。这是最常见的范围限制。

          私有:当属性或方法的可见性设置为私有时,只有具有私有成员的类才能访问这些方法和属性(在类内部),不管那里的类关系如何也许吧。

          • 与野餐类比,将野餐视为“只允许公司成员参加的公司野餐”。不是家庭,也不是公众。

          【讨论】:

            【解决方案11】:

            公开:

            当您将方法(函数)或属性(变量)声明为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


            使用Reflection剖析爷爷班

            这个主题并没有超出范围,我在这里添加它只是为了证明反射真的很强大。正如我在上述三个示例中所述,protectedprivate 成员(属性和方法)不能在类之外访问。

            但是,通过反射,您甚至可以通过访问类外的 protectedprivate 成员来实现非凡

            那么,反射是什么?

            反射增加了对类、接口进行逆向工程的能力, 函数、方法和扩展。此外,它们还提供了方法 检索函数、类和方法的 doc cmets。

            序言

            我们有一个名为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
            

            常见误解:

            请不要与以下示例混淆。如您所见,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_dumpdebugger functions。它们以人类可读的形式呈现有关变量的信息。这三个函数将显示 PHP 5 对象的 protectedprivate 属性。静态类成员将显示。


            更多资源:


            【讨论】:

            • 为迟到的加入这个车队道歉。你能告诉我为什么有人会使用这些吗?你已经完美地解释了它们是如何工作的等等。我只是想知道使用它们的好处。谢谢
            • @JamesG 在上面的其他评论中有所解释。 stackoverflow.com/questions/4361553/…
            • 我不知道为什么这可能有点超出这个问题但没有人提到在 PHP 中还有另外两个访问修饰符:abstract 和 final this 关键字只能用于 PHP 类,但它仍然访问修饰符
            • 我建议你在这里阅读 Dhairya Lakhera 提供的关于抽象的解释:stackoverflow.com/questions/2558559/…。这是对 Shankar Damodaran 解释的完美补充。
            【解决方案12】:

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

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

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

            【讨论】:

              【解决方案13】:

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

              Public:如果您可以从代码中的任何位置直接访问和更改此变量,请使用此选项。

              类外的示例用法:

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

              受保护:当你想强制其他程序员(和你自己)在访问和设置变量时使用类之外的 getter/setter 时使用它(但你应该保持一致并使用 getter 和类内的二传手)。这或private 往往是您应该设置所有类属性的默认方式。

              为什么?因为如果您在未来的某个时间点(甚至可能在 5 分钟内)决定要操作为该属性返回的值或在获取/设置之前对其执行某些操作,您可以在不重构任何地方的情况下做到这一点。在你的项目中使用它。

              类外的示例用法:

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

              私有private 属性与protected 属性非常相似。但显着的特征/区别在于,如果不使用父类的 getter 或 setter,将其设为 private 也会使子类无法访问它。

              所以基本上,如果您对属性使用 getter 和 setter(或者如果它仅由父类在内部使用,并且不能在其他任何地方访问),您最好将其设为 private,只是为了防止任何人尝试直接使用它并引入错误

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

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

              【讨论】:

                【解决方案14】:

                ⚡️ 这是记住publicprotectedprivate 范围的简单方法。

                PUBLIC

                • public 范围:对象和其他类都可以使用公共变量/函数。

                PROTECTED

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

                PRIVATE

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

                阅读 PHP 手册中方法或变量的Visibility

                【讨论】:

                  【解决方案15】:

                  重提一个老问题,但我认为考虑这个问题的一个非常好的方法是根据您定义的 API。

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

                  • protected - 别被骗了,这也是 API 的一部分!人们可以继承、扩展您的代码并使用任何标记为受保护的东西。

                  • private - 私有属性和方法可以随意更改。没有其他人可以使用这些。这些是您可以在不进行重大更改的情况下进行更改的唯一内容。

                  或者用Semver 的话:

                  • publicprotected 的任何更改都应视为重大更改。

                  • 任何新的publicprotected 都应该(至少)是次要的

                  • 只有 private 的任何新内容/更改才能成为 PATCH

                  因此,在维护代码方面,最好小心你所做的事情publicprotected,因为这些是你向用户承诺的事情。

                  【讨论】:

                    【解决方案16】:

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

                    1. 公开
                    2. 私人
                    3. 受保护

                    现在,让我们看看下图来了解访问修饰符访问级别

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

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

                    private :- 带有这个关键字的类成员将在类本身中被访问。我们无法从子类访问私有数据。它保护成员免受外部类访问。

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

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

                    【讨论】:

                      【解决方案17】:

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

                      private - 只有当前类才能访问该字段或方法。

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

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

                      【讨论】:

                        猜你喜欢
                        • 2012-04-10
                        • 2017-12-19
                        • 2012-09-28
                        • 1970-01-01
                        • 2012-04-03
                        • 2010-10-26
                        相关资源
                        最近更新 更多