1

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

例如

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

对比

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) 如果你设计好你的程序,你就不会有这个问题。

4

7 回答 7

5

如果您实际上为变量赋值,这很重要。

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

对比

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++;
}

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

于 2013-08-19T22:20:49.033 回答
2

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

于 2013-08-19T22:22:40.043 回答
2

尽管我可以猜到你在这里问什么,但你的示例代码并没有演示它,我真的很想知道怎么没人注意到这一点。我什至检查了您问题的整个修订历史,但没有找到一个实际会产生编译器错误的程序实例。

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

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

这里发生的是您ci. 因此,您正在尝试读取尚未初始化的变量。请注意,这与声明顺序(编译时)无关,仅与初始化顺序(运行时)有关。在 Java 中,就像在所有其他类似的语言中一样,成员声明的顺序除了暗示成员初始化程序的执行顺序之外没有任何作用。

Java 有一项检查,可以防止一个初始化程序读取初始化程序尚未运行的变量。Java 还有许多其他此类检查,因为它旨在努力保护程序员不犯愚蠢的错误。用 James Gosling 的话来说,Java 本来就是一种“蓝领”语言。这应该回答您的“为什么”问题。

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

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

这里没有产生错误。Java 并没有检查您在从初始化程序调用的任何方法中实际执行的操作。

于 2013-08-22T08:17:38.363 回答
2

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

编辑:由于您更改了问题,我将向您指出 ykaganovich 关于预先声明的参考变量相互分配的答案。您最初的问题是我上面段落中的答案。

于 2013-08-19T22:15:09.903 回答
1

我试着总结答案。有关更多信息,请阅读下面的答案:)

a)Java中的前向引用为:

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

非常混乱。所以在java中是不允许的。

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

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

d) 如果你设计好你的程序,你就不会有这个问题。

于 2013-08-20T21:08:10.000 回答
1

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

public class A {

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

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

JVM 是如何知道启动这个程序的?它是否创建了一个类 A 的实例来知道如何运行它?否 - 因为 main 方法被声明为静态的,所以它在运行时被初始化,检测到 main 方法并且程序可以运行。

这与 C# 和 C++ 中的相同。

这适用于任何静态变量或方法。它们都在程序运行时初始化。如果您想创建一个类并且需要确保它具有立即可用的某些变量——类范围的变量,该怎么办?使它们成为静态的。同样,您可以像这样创建静态块:

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

这允许在启动时运行比简单变量更复杂的代码。它还允许更好的内存分配和使用。请参阅此处了解更多信息。

编辑:(基于更新的问题)

我不知道如何回答你的问题。

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

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

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

我想我不能给你比这更好或更详细的理由了!

于 2013-08-19T22:44:40.937 回答
1

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

您的程序的设计使得这种排序有所不同。您正在声明其值相互依赖的变量,因此必须定义一些顺序,并且从上到下是 Java 中定义的,实际上是大多数语言。您编写的程序在自上而下的顺序中没有意义。所以修复它,这样它就可以了。

于 2013-08-20T10:12:43.950 回答