为什么私有成员可以通过静态方法访问?


25

以下是伪代码,我在Java和PHP中进行了尝试,并且都可以使用:

class Test { 

    private int a = 5;

    public static function do_test(){
        var t = new Test();
        t.a = 1;
        print t.a // 1
    }

}

Test::do_test();

为什么要在OOP范式中做到这一点,它的用途是什么?


6
为什么不呢?在Java中,私有成员不是实例专有的,而是私有源文件的。首先想到的是equals必须检查另一个实例的私有字段。(由于简短,因此发表评论,而这种方法的面向对象
操作性

2
请注意,静态方法没有this,因此它们可以访问的自己类的唯一对象是它们自己创建的对象(或作为参数传递的对象)。因此,如果您认为这违反了封装或存在安全漏洞,那么就好像它不是一个很大的漏洞,可能不值得插入。
Kilian Foth,2014年

4
不知道为什么这被否决了。这个问题可能是微不足道的,但是OP在询问之前遇到了用两种语言测试行为的麻烦。这比我们通常从新来者身上看到的努力更多。
yannis 2014年

1
@YannisRizos同意,实际上我认为这个问题并不重要。它对于遵循“最小特权原则”具有影响。这意味着不需要访问实例内部的辅助函数应该在单独的类中定义,反之,当遵循此约定时,您将知道,只要静态方法存在于同一类中,便会访问内部状态。
2014年

1
实际上,当我问我的同事时,所有人都说这是不可能的。这就是为什么我不认为这是微不足道

Answers:


17

在Java中,私有变量对整个类都是可见的。可以从静态方法和同一类的其他实例访问它们。

例如,这在工厂方法中很有用。工厂方法通常对一个对象进行初始化,因为它是如此复杂,以至于您不想将它们留给应用程序代码。为了进行初始化,factory方法通常需要访问您不想公开的类内部。能够直接访问私有变量使您的生活更加轻松。

但是,当您甚至想从静态方法或该类的其他实例中隐藏类的实现细节时,可以遵循私有类数据模式。将类的所有私有变量放入私有内部类中,并将任何获取器或设置器委派给该内部类的获取器和设置器。

另一个选择是为类定义一个接口,该接口声明该类的所有公共方法,然后仅在可能的情况下在该接口下引用该类。对interface-type的引用无论在何处都不能用于直接访问未在接口中声明的任何内容(当然,除了反射之外)。当您使用没有接口的面向对象的编程语言(例如C ++)时,可以使用由实际类继承的抽象基类来模拟它们。

interface ITest {
     public int getA();
}

class Test implements ITest { 

    private int a = 5;

    public int getA() { return a; } // implementation of method declared in interface

    public static void main(){
        ITest t = new Test();
        t.a = 1; // syntax error: Interface ITest has no "a"
        System.out.println(t.getA()); // calls Test.getA, visible because ITest declares it
    }

}

您能想到私有类数据模式有用的情况吗?我个人仅在GUI(例如设置(Swing等))中使用内部类,或者在编码练习中使用静态内部类,因为我不希望练习跨越多个源文件。
通知2014年

1
将类的内部与其他实例隐藏在一起,可以消除类在接口上的优势之一,而无需交换任何东西。一个更简单,更灵活的解决方案是只使用一个接口。
2014年

3

某些语言和运行时框架(例如Java,.NET)假设,正在为任何特定类编译代码的任何人都可以信任,不要以有害于其正确性的方式使用该类任何实例的任何私有成员。操作。在这方面,其他语言和框架更具限制性,并且不允许访问实例的私有成员,除非通过在该实例上运行的代码来访问。两种设计都有优点和缺点。

允许类中的任何代码访问任何实例的私有成员的最大优点是,在某些情况下,该级别的访问是适当的,并且通过private这种方式进行工作,无需为此目的使用不同的访问限定符,或者否则,迫使代码暴露成员的范围超出了理想情况。

禁止这种访问的一个优点(例如Microsoft通用对象模型(COM)中的情况)是它允许外部代码将类视为接口。如果一个类ImmutableMatrix包含一个私有或受保护的double[][]支持字段,并且如果类中代码检查其它情况下的背衬阵列,那么它会是不可能的,以限定一个非阵列支持类(例如ZeroMatrixIdentityMatrix),其以外的代码可以作为使用一个Immutable2dMatrix,而该类不必包括背景字段。如果内部没有Immutable2dMatrix使用以外的任何实例的私有成员this,则可以将该类重命名为ImmutableArrayBackedMatrix,并定义一个新的抽象ImmutableMatrix类,该抽象类可能具有ImmutableArrayBackedMatrix上述非数组支持的类作为子类型。

请注意,除非使用语言“允许” ImmutableMatrix检查this除之外的其他实例的后备数组,否则不会阻止这种重构,除非该语言利用了该功能并确实检查了外部实例。语言限制了这种用法的主要作用是,在任何尝试编写不适合这种重构的代码的尝试时,编译器都会立即发出嘎嘎叫声。


2

Java严格来说不是一种面向对象的语言,而是一种基于类的语言 -该类确定操作和行为的访问权限,而不是实例。

因此,不要惊讶于它可以让您执行严格不面向对象的操作。

由于该方法与实例在同一类范围内,因此它对私有成员具有完全访问权限。类似的规则控制内部类的实例从外部类的实例访问数据-内部类的实例可以访问外部类的私有成员。

这是从C ++继承的,在C ++中它对于创建复制和移动构造函数很有用。对于比较或合并两个对象的值取决于无法以有效方式公开访问的成员的对象(例如,Java中的数组的getter应该复制该数组,以便客户端代码无法对其进行修改,更改),这也很有用。对象的内部状态,但必须复制数组以比较对象的相等性并不高效()

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.