为什么java中静态成员的顺序很重要?

时间:2013-08-19 22:12:24

标签: java static initialization

为什么java中静态成员的顺序很重要?

E.g。

public class A {
  static int i= 1;
  static int c = i;
  int a = c; <<------ ok
}

VS

public class B {
  int a = c; <<--- compile error
  static int c = 1;
  static int i = c;
}

为什么Java的设计使得这种排序有所不同? (我根据ykaganovich的回答编辑了我的问题)

编辑:谢谢大家的帮助! 我用非静态变量测试了我的例子。它具有完全相同的行为,因此静态不起任何作用。这个问题具有误导性(至少对我而言)。我会尝试总结你的答案。

修改 2

我会尝试总结答案。欲了解更多信息,请阅读以下答案:)

a)Java中的直接前向引用:

static int i = c;
static int c = 1;

非常混乱。所以在Java中不允许这样做。   主要原因是初始化顺序。

b)Java中允许间接前向引用

public class Test {
int i = c();
int c() { return c; }
  int c = 1;
}

c)您必须准确定义变量声明(或初始化)的执行顺序,  它的唯一定义是如何在java中完成的。  在java中,这种排序从上到下。

d)明确定义的顺序提供了一种可预测结果的方法。

e)如果你很好地设计你的程序,你将不会遇到这个问题。

7 个答案:

答案 0 :(得分:5)

如果你实际上是为变量赋值,那就很重要了。

public class A {
  static int i = 0;
  static int c = i; //fine
}

VS

public class B {
  static int c = i; // compilation error
  static int i = 0;
}

**更新问题**

啊,我看到你明白这是不允许的,但你想知道原因。

让我们更有趣一点:

  public class A {
    static int c = boom();
    static int i = bam();

    private static int bam() {
      return c + 2;
    }

    private static int boom() {
      return i + 1;
    }

    public static void main(String[] args) throws Exception {
      System.out.println("i: " + i);
      System.out.println("c: " + c);
    }
  }

输出:

i: 3 
c: 1

如果你移动i和c:

static int i = bam();
static int c = boom();

你得到:

i: 2
c: 3

这只是为了说明订单很重要。

至于为什么不允许在变量赋值中使用前向引用,您希望这段代码能做什么?

static i = c;
static c = i++;

答案实际上是明确的,因为Java必须按照定义的特定顺序处理它。所以,这应该相当于:

static i = 0;
static c = 0;
static {
  i = c;
  c = i++;
}

但第一种形式非常混乱,因此容易出错。我的猜测就是它被禁止的原因。

答案 1 :(得分:2)

Java类中静态成员的顺序无关紧要。代码以完全相同的方式编译。此外,您的示例基本上没有意义,因为您只是为两个相同的类更改变量名。

编辑:既然你改变了问题,我将指出ykaganovich关于将预先声明的参考变量分配给对方的答案。您的原始问题在我上面的段落中是回答。

答案 2 :(得分:2)

静态变量init和静态块执行的顺序还提供了一种方法来执行可预测的操作,以后变量可以依赖于已经初始化/已处理的变量。

答案 3 :(得分:1)

这只是 java语言的语法。静态块都在程序运行时初始化。举个例子:

public class A {

    public static void main(String[] args) {
        // some code
    }

    public void play() {
        // some code
    }
}

JVM如何知道启动此程序?它是否创建了A类的实例以了解如何运行它?否 - 因为main方法声明为static,它在运行时初始化,检测到主方法并且程序可以运行。

这与C#和C ++相同。

这适用于任何静态变量或方法。它们都是在程序运行时初始化的。如果你想创建一个类并且你需要确保它有一些可用的变量 - 类变量的变量怎么办?让它们变得静止。同样,您可以创建静态块,如下所示:

static {
    // whatever code is needed for initialization goes here
}

这允许在启动时运行除简单变量之外的更复杂的代码。它还允许更好的内存分配和使用。有关详细信息,请参阅here

编辑:(根据更新的问题)

我不确定如何回答你的问题。

静态变量/块都在运行时初始化。尽管如此,JVM仍然必须单独处理每个事物,这意味着它需要某种顺序。如果在两行静态声明之后放置包含int a = c的静态块,那很好。

如果您之前放置静态声明,JVM不知道您所指的是什么并抱怨。

至于为什么会这样?这只是语言的语法。没有更好或更详细的理由。您可以说它应该将所有内容加载到内存中,然后在执行任何操作之前检查其他所有内容,但这会耗费大量资源并浪费内存。

我认为我不能给你一个更好或更详细的理由!

答案 4 :(得分:1)

  

为什么Java的设计使得这种排序有所不同?

您的程序的设计使得这种排序有所不同。您声明的值是相互依赖的变量,因此必须定义一些排序,从上到下是Java中定义的内容,实际上是大多数语言。您编写的程序在从上到下排序中没有意义。所以要修好它。

答案 5 :(得分:1)

我尝试尝试总结答案。欲了解更多信息,请阅读下面的回答:)

a)java中的前向引用:

static int i = c;
static int c = i++;

非常混乱。所以在java中不允许这样做。

b)您必须准确定义变量声明的执行顺序,  它的唯一定义是如何在java中完成的。  在java中,这种排序从上到下。

c)明确定义的顺序提供了一种可预测结果的方法。

d)如果你很好地设计你的程序,你将不会遇到这个问题。

答案 6 :(得分:1)

即使我能猜出你在这里问的是什么,你的示例代码也没有证明它,我真的很想知道没有人注意到这一点。我甚至检查了你的问题的整个修订历史,并且找不到一个实际产生编译器错误的程序的单个实例。

但是,让我向您展示 产生编译器错误的示例,您可能会想到:

public class Test {
  int i = c;
  int c = 1;
}

这里发生的是您在c的初始值设定项中引用i。因此,您正在尝试读取尚未初始化的变量。请注意,这与声明顺序(编译时)无关,只与初始化顺序(运行时)有关。在Java中,与所有其他类似语言一样,成员声明的顺序不起作用,除非暗示成员初始化程序的执行顺序。

Java有一个检查阻止一个初始化程序读取其初始化程序尚未运行的变量。 Java有许多其他类似的检查,因为它旨在努力保护程序员免于犯愚蠢的错误。用James Gosling的话来说,Java本来就是一个蓝领&#34;语言。这应该回答你的原因&#34;问题

请注意,我们在这里讨论的特定编译器检查非常弱,仅适用于最明显的情况。考虑上述程序的这个简单变​​化:

public class Test {
  int i = c();
  int c() { return c; }
  int c = 1;
}

此处未产生任何错误。 Java并没有检查你从初始化程序调用的任何方法中实际执行的操作。