选择何时实例化类

时间:2008-11-15 09:54:46

标签: java variables performance

我最近为一个赋值编写了一个类,我必须在ArrayList中存储名称(在java中)。我将ArrayList初始化为实例变量private ArrayList<String> names。后来当我根据解决方案检查我的工作时,我注意到他们已经用run()方法初始化了他们的ArrayList。

我考虑过这一点,我觉得这可能是一个品味问题,但总的来说,在这样的情况下如何选择?一个人占用的内存是少吗?

PS我喜欢Ruby中以@符号开头的实例变量:它们更可爱。

(元问题:对于这个问题,什么是更好的标题?)

6 个答案:

答案 0 :(得分:3)

用伟大的Knuth的话说“过早的优化是万恶之源”。

只是担心您的程序正常运行并且没有错误。这比稍后难以调试的模糊优化要重要得多。

但要回答你的问题 - 如果你在类成员中初始化,将在代码中第一次提到你的类时(即从你调用一个方法时)分配内存。如果在方法中初始化,则在调用此特定方法时,将在稍后进行内存分配。

所以这只是后来初始化的问题......这在业界被称为延迟初始化。

答案 1 :(得分:2)

初始化

根据经验,尝试在声明变量时初始化变量。

如果一个变量的值永远不会改变,那么使用final关键字将其显式化。这有助于您推断代码的正确性,虽然我不知道识别final关键字的编译器或JVM优化,但它们肯定是可能的。

当然,这条规则有例外。例如,可以在if-else或switch中分配变量。在这种情况下,“空白”声明(没有初始化的声明)比在读取虚拟值之前保证被覆盖的初始化更可取。

/* DON'T DO THIS! */
Color color = null;
switch(colorCode) {
  case RED: color = new Color("crimson"); break;
  case GREEN: color = new Color("lime"); break;
  case BLUE: color = new Color("azure"); break;
}
color.fill(widget);

如果出现无法识别的颜色代码,现在您有NullPointerException。最好不要分配无意义的null。编译器会在color.fill()调用时产生错误,因为它会检测到您可能没有初始化color

为了在这种情况下回答你的问题,我必须看到有问题的代码。如果解决方案在run()方法中初始化它,它必须用作临时存储,或者作为“返回”任务结果的方式。

如果集合用作临时存储,并且不能在方法之外访问,则应将其声明为局部变量,而不是实例变量,并且很可能应该在方法中声明它的位置进行初始化。

并发问题

对于一门初级编程课程,你的教师可能并没有试图用并发编程的复杂性来面对你 - 尽管如果是这样,我不确定你为什么使用Thread。但是,根据CPU设计的当前趋势,任何正在学习编程的人都需要牢牢掌握并发性。我会尝试在这里深入研究一下。

从线程的run方法返回结果有点棘手。这个方法是Runnable接口,没有什么能阻止多个线程执行单个实例的run方法。由此产生的并发问题是Java 5中引入的Callable接口背后的动机的一部分。它非常像Runnable,但可以以线程安全的方式返回结果,并抛出{{1}如果任务无法执行。

这是一个题外话,但如果你很好奇,请考虑以下示例:

Exception

class Oops extends Thread { /* Note that thread implements "Runnable" */ private int counter = 0; private Collection<Integer> state = ...; public void run() { state.add(counter); counter++; } public static void main(String... argv) throws Exception { Oops oops = new Oops(); oops.start(); Thread t2 = new Thread(oops); /* Now pass the same Runnable to a new Thread. */ t2.start(); /* Execute the "run" method of the same instance again. */ ... } } 方法结束时,您几乎不知道main的“状态”是什么。两个线程同时处理它,我们还没有指定集合是否可以安全地并发使用。如果我们在线程中初始化它,至少我们可以说最终,Collection将包含一个元素,但我们不能说它是0还是1。

答案 2 :(得分:0)

来自wikibooks

Java中有三种基本的变量范围:

  • 在类的方法中声明的局部变量,对方法执行的时间有效(并仅占用存储)。每次调用该方法时,都会使用该变量的新副本。

  • 实例变量,在类中声明但在任何方法之外。只要相应的对象在内存中,它就有效并占用存储空间;程序可以实例化该类的多个对象,并且每个对象都获得其自己的所有实例变量的副本。这是面向对象编程的基本数据结构规则;类被定义为保存给定系统中特定于“对象类”的数据,并且每个实例都拥有自己的数据。

  • 静态变量,在类中声明为static,在任何方法之外。无论从该类实例化多少个对象,这个变量只有一个副本。

所以是的,内存消耗是一个问题,特别是如果run()内的ArrayList是本地的。

答案 3 :(得分:0)

我并不完全理解你的完整问题。

但就我现在所理解的而言,性能/内存的好处将相当轻微。因此,我绝对赞成易用性方面。

那么最适合你的是什么。仅在需要时解决性能/内存优化问题。

答案 4 :(得分:0)

我个人的经验法则变量是初始化它们,至少是默认值:

  1. 在退火时,即

    private ArrayList<String> myStrings = new ArrayList<String>();

  2. 在构造函数

  3. 如果它确实是一个实例变量,并且表示对象的状态,那么它会在构造函数退出时完全初始化。否则,您可以尝试在变量具有值之前尝试访问该变量。当然,这不适用于您将自动获得默认值的基元。

    对于静态(类级别)变量,在声明或静态初始化程序中初始化它们。如果我做计算或其他工作来获取值,我使用静态初始化程序。如果您只是调用new Foo()或将变量设置为已知值,请在声明中初始化。

答案 5 :(得分:-1)

您必须避免延迟初始化。这会导致以后出现问题 但是如果你必须这样做,因为初始化太重了,你必须这样做:

静态字段:

// Lazy initialization holder class idiom for static fields
private static class FieldHolder {
   static final FieldType field = computeFieldValue();
}
static FieldType getField() { return FieldHolder.field; }

实例字段:

// Double-check idiom for lazy initialization of instance fields
private volatile FieldType field;
FieldType getField() {
   FieldType result = field;
   if (result == null) { // First check (no locking)
      synchronized(this) {
         result = field;
         if (result == null) // Second check (with locking)
            field = result = computeFieldValue();
      }
   }
   return result;
}

根据Joshua Bolch的书“Effective Java™” 第二版“(ISBN-13:978-0-321-35668-0):
“明智地使用延迟初始化”