【问题标题】:Why does the order of static members in java matter?为什么java中静态成员的顺序很重要?
【发布时间】:2013-08-19 22:12:24
【问题描述】:

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

【问题讨论】:

  • 我不明白。你想知道什么?
  • 如果您有更深入的问题或特定问题,请随时编辑您的问题并提出。
  • 谢谢!我的问题是为什么 java 是这样设计的。

标签: java static initialization


【解决方案1】:

重要的是你是否真的为变量赋值。

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

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

【讨论】:

  • 谢谢!但它没有回答问题。
  • 好点!我不确定是否也允许使用非静态变量。
  • 很好的解释
【解决方案2】:

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

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

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

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

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

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

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

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 您能解释一下可预测操作是什么意思吗?
    【解决方案4】:

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

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

    【讨论】:

    • 好的,也许我应该重新提出我的问题。静态初始化器顺序是相关的。
    • @JohnN 这不是一个坏问题,很抱歉,如果我的回答很草率。只是尝试提供帮助。
    【解决方案5】:

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

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

    【讨论】:

    • 感谢您的回答。当然,我的程序是这种 Java 行为的原因。但问题是:为什么 Java 在我的程序中表现得这样。
    【解决方案6】:

    这只是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
    }
    

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

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

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

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

    如果你把静态声明放在前面,JVM不知道你指的是什么并抱怨。

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

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

    【讨论】:

    • 我不确定这只是巧合,就像您使用 Word 和 String 的示例一样。 JAVA/C#的设计者这样做是有原因的。
    • @JohnN:为了更简洁,我删除了那个位。它允许更好地使用内存等。查看我编辑的链接。这就是静态代码块的意义所在。此外,静态变量是类范围的,而不是特定于对象的。无论您创建多少对象,您都只需要一个类变量。明白了吗?
    • 我了解什么是静态块或变量。但问题是为什么静态声明的顺序很重要。
    • 出于同样的原因,实例变量的顺序很重要。如果您执行问题中的代码,那很好,没有区别。如果您尝试在分配变量的行之前执行诸如增加变量之类的操作,则会引发抱怨。这只是系统的逻辑。不管它是否在运行时加载所有内容,它仍然会一一处理所有内容,因此必须以某种顺序获取信息。
    • 我不能投票,因为我已经投票了你的答案。你读过 ykaganovich 或 Ashish 的答案吗?
    【解决方案7】:

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

    a) java 中的前向引用为:

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

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

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

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

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

    【讨论】:

    • 前向引用在 Java 中是允许的
    • 你确定吗? Java 1.7 编译器出现“前向引用”错误。
    • 这里 Java 只是阻止您读取初始化程序尚未运行的变量。前向引用的概念比这更笼统:例如,您当然可以在调用它的地方调用定义在下面的方法。
    • 要使这与您的答案完全相关,请声明 static int c() { return c; } 并将直接读取替换为方法调用:static i = c();
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 2013-05-18
    • 1970-01-01
    • 2015-12-01
    • 1970-01-01
    相关资源
    最近更新 更多