在Java中,让对象的成员公开可用是不是一个坏主意?

时间:2009-02-16 22:06:53

标签: java field private encapsulation public

我的应用程序中有一个数据类。我的应用程序永远不会被用作公共API,我将是唯一一个在我的项目中开发代码的人。

我正在努力保存每一盎司的处理器和内存电源。

让我的数据类中的数据成员具有公共/受保护/默认保护是不是一个坏主意,这样我就不必使用getter了?使用getter需要更多的内存和堆栈的创建......我相信这是不必要的。我在使用getter时可以看到的唯一原因是保护/隐私,但如果我是唯一的编码器而没有其他人会使用我的API,那么不使用getter是一个坏主意吗?

如果这是愚蠢的,请告诉我。

28 个答案:

答案 0 :(得分:24)

如果您要更换getter / setter以进行性能/内存优化,那么您采取了错误的方法。这几乎肯定不是你的应用程序运行缓慢或使用太多内存的原因。

优化的主要罪恶是在你知道自己需要之前做到这一点。只有当您有真实的信息显示浪费的时间/内存最多时才进行优化,然后花时间优化它。这背后的想法是,通过在占用总运行时间的80%的代码中削减5%的时间来获得更多,而不是在仅占5%的代码中削减20%的代码。总运行时间。 (同样适用于记忆)。

另外,我会按照你的建议设计应用程序,因为这意味着可以直接访问某些属性(例如:简单属性),而其他属性(更复杂的派生属性或不需要的属性)暴露底层类型)将有getter / setter。所以你最终会得到一种混合的访问风格,这些风格的可维护性会降低。

答案 1 :(得分:11)

将成员公开作为优化糟糕的主意。正如其他人所说,它几乎没有任何影响。但是,如果对象是一个行为很少的简单数据结构,它很好以这种方式编写它。只要您为了简单性和可读性而 ,而不是表现。

答案 2 :(得分:8)

无论如何都很有可能会得到琐碎的getter和setter - 但是对于公共字段,你将失去合同(API)和实现之间的区别。即使这只是将使用的API,最好还是让IMO松散耦合。

答案 3 :(得分:4)

Python人不使用getter而且他们没有在地狱中燃烧。

Getter / setter是一种将类的一部分暴露给Java的简单内省的方法。 Java Bean规范依赖于将公共getter和setter作为确定哪些属性很重要的方法。

虽然对于需要/生成/使用bean的东西至关重要,但它并不是OO编程或Java编程的基本功能。它只是Bean规范的一部分,对于任何想要参与bean类的东西都是必需的。

公共属性很好。它们简单,直接且明显。

答案 4 :(得分:4)

与任何优化一样,在之前和之后进行衡量,看看是否有任何好处可以证明其缺点。

我认为您会发现它不会对您的代码的性能产生任何明显的影响(但请亲自尝试)。 JVM将内联常用的getter和setter。

使用分析器并在应用程序中找到真正的热点。没有证据的优化只是猜测。

答案 5 :(得分:3)

这有些异议,但我同意你的看法,如果你知道没有其他人会使用你的课程,你可以跳过这些东西。我不会在可能被重用的代码中执行此操作,甚至隐藏在API后面,但在您的情况下,它似乎相当安全。

答案 6 :(得分:2)

我很确定使用的内存量可以忽略不计。如果为生产构建,JVM甚至可以优化调用以使它们内联,具体取决于运行时的实现。如果您自己开发它,它可能仍然有用,因为如果您有一个难以跟踪的错误,您可以在getter / setter中设置断点,并确切地查看更改值的时间。它不应该是任何代码编写,因为大多数现代IDE都具有自动生成代码的功能。

答案 7 :(得分:2)

我认为这个问题的反面是一个更好的问题:

让对象成员公开可用是不是一个好主意?

通过开发,从使用所需的最低可访问性级别的原则来处理事物通常是个好主意;换句话说,只显示您需要的数据。如果你通常应用这个原则,那么它就变成了一种只按需要共享的做法。

所以说 - 为什么这样做?

答案 8 :(得分:2)

我从来没有希望我有一个公共领域而不是财产,但我无法计算我想要一个房产并拥有一个公共领域的次数。最终你最终想要内部的逻辑。

答案 9 :(得分:2)

吸气剂和制定者不仅仅是为了保护/隐私。主要用途是更改公开基础数据的方式。

例如,在asp.net c#中,控件的visible属性可能包含以下内容:

private bool visible = true;

public bool Visible
{
    get
    {
        return visible && Parent.Visible;
    }
    set
    {
        visible = value;
    }
}

外部属性(accessor / mutator)与底层变量具有不同的含义。此外,如果您在开始时没有上述功能,那么当您拥有简单的获取/设置时,它将成为您可以在以后轻松添加的内容。

至于性能点,可以节省每一盎司的处理器和内存。如果这会产生明显的影响,那么你可能最好不要看另一种语言。

答案 10 :(得分:2)

所有过早的优化问题都在前面的答案中讨论过。

我只是认为值得一提的是,在Java中你可以通过定义一个只有公共成员的类来模仿C的结构:

public class DataClass {
    public int a;
    public String b;
    public char c;
}

只能通过引用这些公共成员来访问(获取和设置)。

这个习语在某些特定情况下是完全可以接受的。

答案 11 :(得分:2)

一旦你把一些东西放在公共API中就可以了。你不能改变它的名字,你不能改变它的类型,你不能改变它的存储方式。

将它置于一种方法之后会导致现代虚拟机无法实现性能(在过去10年中几乎都是如此)。

在我看来,通过获取价值的方法获得的灵活性更为重要。

如果您确定将其公开,请确保将其标记为最终版并且它们是不可变的。

public class Point
{
    public final int x;
    public final int y;

    public Point(final int xVal, final int yVal) 
    {
        x = xVal;
        y = yVal;
    }
}

编辑:

通过公共API,我指的是任何公共变量,而不是类本身将成为其他开发人员公开API的一部分。通过将其公开,您可以在以后引发自己的问题(如果它是一个“真正的”公共API,其他开发人员可以访问它,除非您喜欢在发布更新时破坏其他人的代码)。

答案 12 :(得分:1)

恕我直言,对于那些本质上只是为了被动地保存数据并且无法从API用户的角度完全改变其含义而无法做任何事情的属性,getter和setter只是不必要的样板。如果属性是,并且其性质显然总是如此,那么只是一个数据持有者,例如类似于C结构的类,只需将愚蠢的东西公之于众。作为一个更一般性的陈述,最佳实践很容易成为浪费时间甚至是最糟糕的做法,如果过度应用而不考虑为什么它们是最佳实践。哎呀,甚至goto偶尔也可以证明是合理的。

答案 13 :(得分:1)

如果您的优先级是性能,那么您应该查看您选择的Java虚拟机以及它如何执行程序。来自Sun的Desktop Java花费大量的时间分析并将java字节代码编译为机器代码,这导致程序运行速度非常快但使用大量内存。

如果您发现要使用基于HotSpot的JVM,则应该研究所有技巧来帮助它。 getter和setter通常是内联的(即直接在机器代码中替换,而不是调用子例程)并且常量被折叠。小范围的switch语句通常转换为跳转表。

通常我发现“head-under-the-arm”方法适用于编写大部分代码,然后分析正在运行的代码并找到瓶颈。我发现例如StringBuffers对很多东西都很快,但是deleteCharAt(0)不是其中之一。重写使用字符串上的简单迭代而不是在字符串缓冲区中删除,确实很奇怪。您也很可能会发现最大的好处在于算法 - 而不是小的快捷方式。

在C世界中,人类几乎不能超越编译器。在Java世界中,人类可以通过直接编写Java来帮助HotSpot。

答案 14 :(得分:1)

这不是愚蠢的,但我认为这也许不是一个好主意。

我理解诱惑......如果没有其他人使用过代码,那么你就不用担心了。但是一次又一次,事实证明这与实际发生的情况不符......你发现你将在比你想象的更多的地方使用它,或者项目变得比预期的要大,或者只是某个人得到它并认为它是有用的......从来没有意味着永久或公共的东西有办法成为这样。

处理器能量和内存是否真的受到限制?我不知道你的情况(也许是嵌入式应用程序?),但作为一个(过度概括的)规则,你最好在别处寻找资源...在你的数据结构,算法或结构中看到更好的改进内存或CPU。

答案 15 :(得分:1)

我创建了两个小类,并测试它们。

  • 首先,我创建了一个单独的对象作为原型。

  • 然后我创建了一个2,000,000数组来存储它们。

  • 然后我运行一个循环,在那里创建一个新实例并从原型中获取值。

比较每一个的平均结果以秒为单位:

WithGS  Without
1.1323  1.1116

Diff  = 0.0207 secs.

因此,对于这种情况,我认为使用非优化解决方案第一会好得多,一旦不需要进一步的要求,就继续进行性能分析。

以下是代码:

PersonGetSet.java


public class PersonGetSet {
    private String name;
    private boolean deceased;

    public void setName( String name ) {
        this.name = name;
    }
    public String getName() {
        return name;
    }

    public void setDeceased( boolean deceased ) {
        this.deceased = deceased;
    }
    public boolean isDeceased() {
        return this.deceased;
    }

    public static void main( String [] args )  {
        PersonGetSet pb = new PersonGetSet();
        pb.setName( "name" );
        pb.setDeceased( true ) ;

        long start = System.currentTimeMillis();
        PersonGetSet [] array = new PersonGetSet[2000000];
        for( int i = 0 ; i < array.length; i++ ) {
            PersonGetSet personGs = new PersonGetSet();
            personGs.setName( pb.getName() );
            personGs.setDeceased( pb.isDeceased() );
            array[i] =  personGs;
        }
        System.out.println( "PersonGetSet took " + ( System.currentTimeMillis() - start ) + " ms. " );
    }
}


Person.java


public class Person {
    String name;
    boolean deceased;
    public static void main( String [] args )  {
        Person pb = new Person();
        pb.name=  "name" ;
        pb.deceased = true;

        long start = System.currentTimeMillis();
        Person [] array = new Person[2000000];
        for( int i = 0 ; i < array.length; i++ ) {
            Person simplePerson = new Person();
            simplePerson.name=  pb.name;
            simplePerson.deceased = pb.deceased;
            array[i] =  simplePerson;
        }
        System.out.println( "Person took " + ( System.currentTimeMillis() - start ) + " ms. " );
    }
}

答案 16 :(得分:1)

使用

的潜在缺点
  

public final 类型 var ;

而不是

  

private final 类型 var ;

     

public 类型 get var () {return var ;}

(我个人经历过)包括:

  • 前景 - 无论现在看起来多么不可能 - 代表需要在基类或未来的子类中进行更改。

  • 混合暴露的不可变和可变类型字段的潜在尴尬。

  • 记住哪些课程使用public final以及哪些课程使用get var 的麻烦。

  • 向后来看到源代码的其他人解释这种不一致的麻烦。

我从来没有能够说服自己这是值得的,即使我已经尝试过。

答案 17 :(得分:1)

你永远不会知道,总是编码,好像读你的代码的人知道你住在哪里,并且是连环杀手。即使您只为自己开发代码,也可以尝试(imo),就像您在团队中进行开发一样。这样您就习惯于构建可读的最佳实践代码。至于性能开销,也许最好用C之类的东西来做,如果你真的真的需要你的每一点内存和CPU的每个循环。

答案 18 :(得分:0)

如果速度非常重要,那么用C或C ++写它可能是值得的。

答案 19 :(得分:0)

任何堆栈使用都将是非常短暂的,而且无论如何都不确定。您可能会发现编译器会优化任何不必要的开销,因此您可能会发现实际上没有任何收益。

我同意私人非API类使用公共成员并不一定是坏的,但你获得的是如此之小(如果有的话)我就是不会打扰。

答案 20 :(得分:0)

在功能上进行设计,你永远不需要(getter / setter)和早期优化一样糟糕。如果这只是供您使用,请不要将其公开,然后构建您认为最简单的。

只有在您真正需要时才应添加优化和/或getter。

答案 21 :(得分:0)

您是否需要getter ?也就是说,你的对象应该为你工作,而不是你的客户自己获取数据值并自己完成工作吗?

OOP的一个关键方面是告诉对象做某事,而不是把它们拉出来并自己做。 e.g

double ret = myObj.calculateReturn()

VS

int v = myObj.getValue();
int ov = myObj.getOldValue();
double ret = (v-ov)/ov * 100; // do I work about dividing by zero etc.? 

我看到了很多。通常你确实需要getter(也许是setter),但是当我写一个getter时,我总是问自己是否应该暴露这些数据,或者对象是否应该完全隐藏它。

答案 22 :(得分:0)

将非最终字段公开为始终是一个坏主意。

如果您以后想要添加事件通知怎么办?

如果您以后想要子类化并更改行为怎么办?

如果您想强制执行某些跨字段限制,该怎么办?

TofuBeer显示了一个带有最终字段的Point类 - 没关系。然而,AWT中的Point类并没有使用决赛,并且在Java早期引起了很多悲痛。

我正在编写一些3D代码,并希望将Point子类化,以便每当x&amp;我改变了我可以根据需要自动调整z。因为Point的字段是公开的,所以无法知道x或y何时发生变化!

答案 23 :(得分:0)

从谁编写代码以及谁将要使用它的角度来看,无论是你还是其他任何人,你可能仍然需要吸气剂和制定者 - 如果有人在以后使用它事实上的公共API。

但是,如果它只是数据,并且您不需要与该类关联的任何行为,那么无论如何都要将成员公开。

告诉我这个类是否有任何行为,或者你是否想要将其数据成员的名称或类型更改为真正重要的实现细节。

关于处理器功率和内存 - 您测量过吗?

答案 24 :(得分:0)

在我们的团队中,我们使用以下规则:如果由类字段表示的状态是可变的,那么总是将其设为私有并提供mutators;但是,如果它是不可变的(即在类实例化后不会被重新分配),那么它可以被声明为public final [type] [field-name] - 对于访问是安全的。

请注意,这里“不可变状态”特别意味着该字段被声明为final。不应与public [immutable-type] [field-name]混淆,其中可以执行重新分配,尽管不可变类型实例本身是不可变的。

请注意,即使对于不可变字段,也有很多很好的理由可以使用存取器。例如,在需要在读取字段时执行某些动作(例如安全检查)的情况下。

答案 25 :(得分:0)

该代码仅供您自己使用的论点是一个容易陷入的陷阱 - 大多数代码就是这种情况,公开暴露的位通常非常小。所以假设大多数代码都是供私人使用的,为什么还要使用私有的,受保护的,最终的,const(C / C ++) - 我知道的修辞问题,但我希望这一点很明确。

这里错过的另一件事是锁定。如果直接访问该成员,您将只能在整个对象或成员级别锁定此代码。并且锁定将控制使用代码,而不是对象本身。也许好吧,也许不是,因为在所有事情上它都是课程的马匹。

答案 26 :(得分:0)

如果性能至关重要,我建议如果只在同一个类中访问字段,或者如果在另一个类中访问它们,则将字段设置为本地字段。如果给内部/嵌套类私有字段,编译器将添加/使用访问器方法(因为即使Java有,VM也不允许类访问另一个类中的字段)

拥有公共可变字段对我来说是一个设计问题。将性能关键代码拆分为跨包并不意味着您需要对性能关键代码进行严格编码。

如果您有不可变字段,那么如果字段更改行为,如果您期望更高的开发成本,那么将字段设置为public是没有问题的。事实上,我建议使用公共不可变字段,如果您认为这使代码更简单/更容易理解。

然而,第一优先级应该仍然是代码可读性。最简单的代码通常也表现得最好。

答案 27 :(得分:0)

一般情况下:如果您想手动优化某些内容,则应考虑体系结构改进而不是微观优化。在许多情况下,微型激发可以通过工具自动完成。

对于Java:有ProGuard工具,可以进行许多优化。但是,如果您使用现代JVM(比如HotSpot或OpenJDK),JVM可以实时进行这些优化。如果您有一个长期运行的应用程序,ProGuard可能会对性能产生轻微影响。