【问题标题】:Java optimization: declaring class variables VS using temporary variablesJava优化:声明类变量VS使用临时变量
【发布时间】:2014-05-09 11:25:41
【问题描述】:

首先,如果我的英语不完美,请原谅我不是来自说英语的国家(西班牙),所以...

好吧,问题来了。创建类时,尽可能使用临时变量是一种好习惯,还是最好将变量声明为类变量,以保持清晰?

我会给你一个例子,使用一个简单的 SpriteSheet 类。是一个非常简短且流行的类,几乎用于 Java 中的所有 2D 游戏。

这是我正在观看的教程的创建者最初计划的代码:

public class SpriteSheet {

private String path;
private final int SIZE;
public int[] spriteSheetPixels;

public SpriteSheet(String path, int size) {
this.path = path;
SIZE = size;

spriteSheetPixels = new int[SIZE * SIZE];

load();
}

private final void load() {
try {
    BufferedImage image = ImageIO.read(SpriteSheet.class
        .getResource(path));
    int w = image.getWidth();
    int h = image.getHeight();
    image.getRGB(0, 0, w, h, spriteSheetPixels, 0, w);
} catch (IOException e) {
    e.printStackTrace();
}
}

}

关键是,据我所知,他只是在做一个普通的课程,遵循所有 Java 约定。看了之后觉得可以稍微改进一下。这是我的同一类的版本:

public final class SpriteSheet {

public final int[] spriteSheetPixels;

public SpriteSheet(final String path, final int width, final int height) {
spriteSheetPixels = new int[width * height];

load(path, width, height);
}

private final void load(final String path, final int width, final int height) {
try {
    BufferedImage image = ImageIO.read(SpriteSheet.class
        .getResource(path));

    final int w = image.getWidth();
    final int h = image.getHeight();
    final byte ZERO = 0;

    image.getRGB(ZERO, ZERO, w, h, spriteSheetPixels, ZERO, w);
} catch (IOException e) {
    e.printStackTrace();
}
}

}

以防万一,如果您不想过多关注,我将尝试恢复我已更改的内容以及原因: - 在类声明中添加了“final”,因为我认为我不需要实例化它。 - 删除了除数组之外的所有类变量,因为它是我最终会从这个类中使用的唯一东西。我觉得将其余变量声明为类变量只是浪费内存。如果它们是临时的,如果我没记错的话,它们会被使用,然后 GC 迟早会处理它们,释放内存。 - 将数组标记为最终数组,因为它将在运行时的其余部分保持不变。 - 将 SIZE 常量拆分为宽度和高度,以防我决定使用一些非方形精灵表。 - 声明 w 和 h 实际上是一个好主意,因为在参数中调用方法通常不利于执行速度(或者这是我在某些地方读到的)。 - 由于0被多次使用,我相信将其声明为变量将有助于提高执行速度(只是一点点,反正可能不会被注意到)。

这基本上就是全部了。请注意,我是一名学生,可能会犯一些非常严重的错误,这就是我想在这里问的原因,因为我相信周围有很多经验丰富的程序员。

请记住,我并不真正关心 SpriteSheet 类,我对我的优化质量更好奇。

¿我是改进了事情还是让事情变得更糟(实际上让事情变得更慢、可读性降低、将来更难维护、做编译器无论如何都会做的事情......)?

对不起,如果我的问题太长太模糊,我的第一个问题是这样吗,对我来说很容易;)

提前致谢。

编辑:

我只是在稍作休息后阅读它,它没有任何意义(您是否看到我正在将宽度和高度参数解析为 load() 而从不使用它们?)

我认为应该是这样的:

public final class SpriteSheet {

public final int[] spriteSheetPixels;

public SpriteSheet(final String path, final int width, final int height) {
final byte ZERO = 0;
spriteSheetPixels = new int[width * height];

try {
    BufferedImage image = ImageIO.read(SpriteSheet.class
        .getResource(path));

    image.getRGB(ZERO, ZERO, width, height, spriteSheetPixels, ZERO,
        width);
} catch (IOException e) {
    e.printStackTrace();
}
}

}

刚刚意识到我真的不需要这个方法。一切都可以在构造函数中完成。

【问题讨论】:

  • 类变量定义对象的属性。如果您认为某个变量对该对象的属性没有贡献,那么最好将其保留为临时变量。这一切都取决于你想如何设计你的课程。
  • 是的,有时,最难的是知道你是否真的需要这些属性(例如,将它们用于从另一个类访问的东西)。

标签: java optimization class-variables code-readability temporary-objects


【解决方案1】:

或者最好将你的变量声明为类变量,只是为了保持清楚?

我认为您的意思是“属性”(实例变量),而不是静态(类)变量。将所有内容声明为实例变量会使事情变得非常不清楚。

简短回答:仅在严格必要时才使用局部变量并创建属性,用于在不同方法之间共享的数据。此外,局部变量的访问速度比属性要快一些。

【讨论】:

  • Stack Overflow 中的标准语言是英语,但如果它有助于使事情更清楚,请知道... aquí también se habla español :)
  • 是的,这就是我的意思,只是无法正确翻译。谢谢(gracias jeje)。顺便说一句,我将稍微修改一下代码,因为我复制了错误的实现。
  • “属性”不是 Java 的正确术语。 JLS 使用术语“字段”。
  • 这太令人沮丧了@MikeSamuel,所有这些困惑都是因为用西班牙语学习 Java,现在试图用英语解释自己。谢谢你的链接。
  • 不要道歉。你的英语很好。我们这些以英语为母语的人应该为从未修正过我们的正字法而道歉,这是由于罗马语和日耳曼语之间长达一千年的火车撞车事故,而凯尔特语则在一旁诘问。
【解决方案2】:

假设您的示例是指实例变量,而不是类/static 变量。 (如果你真的是说static 变量,那么你还有很多其他问题要处理......)


让我们从这个问题的“优化”方面开始。

首先要说的是,这两种方法之间的区别可能微不足道。很有可能它不会对程序的性能产生明显的影响。在您的情况下,我想您在任何时候最多有几十个该类的实例,因此内存使用量的差异最多为几千字节。

话虽如此,但还是有区别的。当您将字段声明为实例字段时,它们将在对象的生命周期内存在(并占用堆内存)。相反,当封闭方法调用结束时,局部变量将不复存在。所以从长远来看,使用局部变量可能会使用 less 内存。


但这里的重要问题是可读性、可维护性和正确性。

如果您将本地“临时”变量转换为实例变量,那么不同方法的范围......或对同一方法的不同调用......通过使用实例变量相互干扰。请注意,在某些用例中,干扰是不可避免的。例如,当两个不同的线程同时在同一个对象上调用同一个方法时,或者当一个方法直接或间接调用自身时;即递归。

而且这种事情可能发生的事实使代码更难阅读和更难维护。 (除非您真的熟悉代码,并且正在跟踪 任何人 对其所做的所有更改...您无法确定某些事情已经破坏了您的假设...并且您必须检查.)

相比之下,局部变量没有这些问题。保证它们对当前线程或其他线程上的任何其他方法调用不可见

简而言之,将变量声明为实例变量“只是为了保持清晰”实际上会产生相反的效果。这会让事情变得明显不太清楚


最后,程序员网站上有一个与此相关的有趣问题:

我在回答中的结论是,这并不真正符合“反模式”的条件,因为它不是设计模式。但还是很糟糕。

【讨论】:

  • 所以,你的意思是,可能,以这种方式编写类在运行时更安全、更快,但很难理解(比如我花了几个星期没有看在代码中,然后,当我尝试更改某些内容或扩展程序时,我将无法理解我自己的代码)。也许添加 javadoc 或 cmets 将有助于代码的未来更改?
  • 没有。我并不是说它在运行时会更安全、更快。它肯定不会更安全,并且我怀疑它会更快地被测量。
  • 抱歉,只是为了澄清一下,我们是在谈论关于反模式的示例还是我的示例(我只是迷路了)?拜托,你能解释一下我的例子带来的不便吗?我问是因为在我的示例中,我不是从另一个方法调用一个方法,而是从构造函数调用一个方法。最后,该示例的重点是填充像素阵列。我没有看到(记住,我是新手)现在有任何危险。
  • 1) 我说的是你的例子,你把局部变量变成了实例变量。 2) 问题是“可读性、可维护性和正确性”……而不是“方便性”。 3)在像您这样简单的示例中,代码无论如何都很容易理解。但对于更复杂的代码,情况并非如此。除非您必须自己阅读并理解一个庞大而复杂的程序,否则我可能无法解释它。
【解决方案3】:

在创建类时,尽可能使用临时变量是一种好习惯,还是最好将变量声明为类变量,以保持清晰?

如果信息仅与当前运行的方法相关,则将其存储在尽可能小的范围内的局部变量中。

如果有关对象的信息需要比任何一个方法调用更持久,并且无法从其他来源派生,则将其存储在字段中。

如果可以从其他来源获取有关对象的信息,但继续获取它效率低下或不方便,则记录其与其他数据的关系,可能将其标记为transient 并将其存储在一个字段中。

如果您发现一个包含许多字段的类,那么也许是时候将其分解为更小的类了。 同样,如果一个方法有很多局部变量,那么也许是时候尝试将其分解为更小的方法了。

【讨论】:

  • 参考示例,现在,我认为我只需要数组,因此将该数组作为类中唯一的字段是有意义的。但很明显,我无法预见将来是否需要该类中的其他东西(也许我需要一些东西,然后我必须将一些局部变量转换为字段。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-23
  • 1970-01-01
  • 2020-08-07
  • 2014-09-04
相关资源
最近更新 更多