7

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

我正在努力节省每一盎司的处理器和内存容量。

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

如果这很愚蠢,请告诉我。

4

28 回答 28

24

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

优化的主要罪过是在你知道你需要做之前就去做。仅当您有真实信息向您显示最浪费时间/内存的位置时才进行优化,然后再花时间进行优化。这背后的想法是,通过在占用总运行时间 80% 的代码中节省 5% 的时间,比在仅占总运行时间 5% 的代码中节省 20% 的时间,您将获得更多收益。总运行时间。(同样适用于内存)。

另外,我会按照您的建议小心设计应用程序,因为这意味着某些属性(例如:简单属性)将可以直接访问,而其他属性(更复杂的派生属性或您不想公开的属性)基础类型)将具有 getter/setter。所以你最终会得到混合的访问样式,这将是不太可维护的。

于 2009-02-16T22:21:04.687 回答
11

将成员公开作为优化是一个坏主意。正如其他人所说,它几乎不会产生任何影响。但是,如果对象是一个行为很少的简单数据结构,那么这样写就可以了。只要您这样做是为了简单性和可读性,而不是性能。

于 2009-02-16T22:23:11.717 回答
8

无论如何,微不足道的 getter 和 setter 很有可能会被内联 - 但是对于公共字段,您将失去合同 (API) 和实现之间的区别。即使这只是将使用的 API,保持 IMO 松散耦合也是很好的。

于 2009-02-16T22:15:05.403 回答
4

Python 的人不使用 getter,他们也不会在地狱中燃烧。

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

虽然对于需要/生产/使用 bean 的东西来说是必不可少的,但它不是 OO 编程或 Java 编程的基本特征。它只是 Bean 规范的一部分,对于任何想要参与类似 bean 的事情的类都是必需的。

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

于 2009-02-16T22:15:14.793 回答
4

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

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

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

于 2009-02-16T22:22:54.870 回答
3

这有点异端,但我同意你的观点,如果你知道没有其他人会使用你的课程,你可以跳过那些东西。我不会在可能被重用的代码中这样做,甚至隐藏在 API 后面,但在你的情况下,它似乎相当安全。

于 2009-02-16T22:11:38.530 回答
3

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

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

在开发过程中,从使用所需的最低可访问性级别的原则来处理事情通常是一个好主意;换句话说,只公开你需要的尽可能多的数据。如果你普遍应用这个原则,那么它就变成了一种只在需要时共享的做法。

话虽如此 - 为什么要这样做?

于 2009-02-17T00:19:12.623 回答
2

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

于 2009-02-16T22:13:09.400 回答
2

考虑一下,一旦您将某些内容放入公共 API 中,它就会一成不变。你不能改变它的名字,你不能改变它的类型,你不能改变它的存储方式。

把它放在一个方法后面不会导致现代虚拟机的性能下降(过去 10 年几乎没有)。

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

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

公共课点
{
    公共最终整数 x;
    public final int y;

    公共点(最终 int xVal,最终 int yVal)
    {
        x = xVal;
        y = yVal;
    }
}

编辑:

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

于 2009-02-16T22:24:31.057 回答
2

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

于 2009-02-16T23:04:08.763 回答
2

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

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

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

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

这个成语在某些特定场景下是完全可以接受的。

于 2009-02-16T22:40:18.290 回答
2

getter 和 setter 不仅仅是为了保护/隐私。主要用途是改变你暴露底层数据的方式。

例如,在 asp.net c# 中,控件的可见属性可能具有以下内容:

private bool visible = true;

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

外部属性(访问器/修改器)与底层变量具有不同的含义。此外,如果您在开始时没有上述功能,那么当您有简单的获取/设置时,它就会成为您以后可以轻松添加的东西。

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

于 2009-02-16T22:45:56.577 回答
1

这并不愚蠢,但我认为这可能也不是一个好主意。

我理解这种诱惑......如果没有其他人使用过该代码,那么您无需担心。但一次又一次,事实证明这与实际发生的情况不符......您发现您将在比您想象的更多地方使用它,或者项目变得比预期更大,或者某人只是掌握了它并认为它很有用……本来不应该永久或公开的事物有办法变成那样。

处理器能力和内存真的有那么严格的限制吗?我不知道你的情况(也许是嵌入式应用程序?),但作为一个(过度概括的)规则,你最好在其他地方寻找资源......在你的数据结构、算法或架构中看到更好的改进内存或 CPU。

于 2009-02-16T22:13:18.073 回答
1

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

于 2009-02-16T22:16:37.583 回答
1

恕我直言,对于本质上只是被动地保存数据并且不能做任何更复杂的事情而不从 API 用户的角度完全改变其含义的属性,getter 和 setter 只是不必要的样板。如果该属性是,并且就其性质而言,显然永远都是,只是一个数据持有者,例如与 C 结构类似使用的类,那么只需将愚蠢的事情公开。作为一个更笼统的说法,如果过度热心地应用最佳实践,而不考虑为什么它们是最佳实践,则很容易成为浪费时间甚至是最糟糕的实践。哎呀,即使 goto 有时也可以说是合理的。

于 2009-02-16T22:28:50.330 回答
1

使用的潜在缺点

public final 输入 变量;

代替

private final 输入 变量;

public 输入 getvar () {return var ;}

(我亲身经历过)包括:

  • 前景——不管现在看起来多么不可能——表示将需要在基类或未来的某个子类中改变。

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

  • 记住哪些类使用public final和哪些使用getvar的麻烦。

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

即使我尝试过,我也无法说服自己这是值得的。

于 2009-02-16T22:52:59.180 回答
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. " );
    }
}
于 2009-02-16T23:20:54.697 回答
1

如果您的首要任务是性能,您应该研究您选择的 Java 虚拟机以及它如何执行程序。Sun 的 Desktop Java 花费大量时间分析和编译 Java 字节码为机器码,这导致程序运行速度可能非常快,但会占用大量内存。

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

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

在 C 世界中,人类几乎无法再聪明地超越编译器了。在 Java 世界中,人类可以通过直接编写 Java 来帮助 HotSpot。

于 2009-02-17T22:57:06.273 回答
0

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

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

请注意,即使对于不可变字段,也有很多充分的理由拥有访问器。例如,在读取字段时需要执行某些操作(例如安全检查)的情况。

于 2009-02-20T18:46:44.403 回答
0

任何堆栈的使用都将是高度暂时的,无论如何都不是确定性的。您可能会发现编译器无论如何都会优化任何不必要的开销,因此您可能会发现实际上没有任何收获。

我同意私有非 API 类使用公共成员不一定是坏事,但你获得的东西太少(如果有的话)我不会打扰。

于 2009-02-16T22:16:34.773 回答
0

从谁编写代码以及谁将使用它的角度来看,无论是您还是其他任何人都没有关系,您可能仍然需要 getter 和 setter - 如果以后有人在事实上的公众中使用它API。

但是,如果它真的只是数据并且您不需要与该类相关的任何行为,那么一定要让成员公开。

对我来说,类是否有任何行为,或者您是否可能想要更改其数据成员的名称或类型作为真正重要的实现细节。

至于处理器能力和内存——你测量过吗?

于 2009-02-16T22:17:46.783 回答
0

你需要吸气剂?也就是说,您的对象是否应该为您工作,而不是您的客户获取数据值并自己执行工作?

OOP 的一个关键方面是告诉一个对象做某事,而不是把这些部分拿出来自己做。例如

double ret = myObj.calculateReturn()

对比

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时,我总是问自己是否应该公开这些数据,或者对象是否应该为我完全隐藏它。

于 2009-02-17T12:50:35.047 回答
0

该代码仅供您自己使用的论点是一个非常容易陷入的陷阱 - 大多数代码都是这种情况,公开暴露的位通常非常小。因此,假设大多数代码都是供私人使用的,为什么还要使用 private、protected、final、const (C/C++) - 我知道的反驳问题,但我希望这一点很清楚。

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

于 2009-02-22T02:43:21.177 回答
0

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

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

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

然而,首要任务仍然应该是代码的可读性。最简单的代码通常也表现最好。

于 2009-02-22T03:20:37.717 回答
0

如果速度如此关键,那么用 C 或 C++ 编写它可能是值得的。

于 2009-02-16T23:01:47.193 回答
0

公开非最终字段总是一个坏主意。

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

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

如果您想强制执行一些跨领域约束怎么办?

TofuBeer 显示了一个带有 final 字段的 Point 类——没关系。但是,AWT 中的 Point 类不使用 final,并且在 Java 早期让我很伤心。

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

于 2009-02-17T13:13:43.980 回答
0

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

只有当您确实需要优化和/或吸气剂时,才应添加它们。

于 2009-02-17T07:15:50.470 回答
0

一般来说:如果你想手动优化某些东西,你应该考虑架构改进而不是这种微优化。在许多情况下,微优化可以由工具自动完成。

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

于 2011-03-26T11:20:51.027 回答