为什么编译器说如果变量需要在循环中传递,变量可能未被初始化?

问题描述:

我正在编写一个程序,用于统计用户定义的整数中的平均数,可能性和零的数量。奇怪的是,编译器根本不会编译。它说“错误:可能的数字可能没有被初始化”。为什么编译器说如果变量需要在循环中传递,变量可能未被初始化?

什么是没有意义的,据我所知,这个循环将保证number总是初始化。我知道我可以通过在声明中初始化来轻松关闭编译器。我总是被告知,尽管在声明中初始化以避免逻辑错误通常是一个糟糕的主意。然而,我最大的恐惧/困惑是我不知道为什么它不喜欢它。如果有例外,它会在validInput = true曾经发生之前被捕获,不是吗?那么当循环进行检查时,它会重新启动,因为validInput仍然是false。这甚至不是一个警告或任何东西;这是一个硬性的错误。我错过了什么或没有看到?

我已阅读其他关于if-checks的案例,这与在可能通过或不通过的支票中的位置不同。这个循环总是会最终通过,除非程序以某种方式退出(在这种情况下,它不会到达for-loop)。如果这是重复的,有人能指出我找不到直接相关的答案吗?谢谢!

public static void main(String[] args) 
{ 
    long number; 
    boolean validInput = false; 
    String numStr; 
    Scanner input = new Scanner(System.in); 

    do 
    { 
     System.out.print("Please enter an integer: "); 
     numStr = input.next(); 

     try { 
      number = Long.parseLong(numStr); 
      validInput = true; 
     } catch (NumberFormatException e) { 
      System.out.print("That's not an integer. "); 
     } 
    } while (!validInput); 


    // Break each digit up by dividing by powers of 10. 
    int evens = 0, odds = 0, zeros = 0; 
    for (long temp = number; temp > 0; temp /= 10) 
    { 
     int digit = (int)(temp % 10); 

     if (digit == 0) 
     { 
      zeros++; 
      System.out.println(digit + " is a zero digit."); 
     } 
     else if (digit % 2 == 0) 
     { 
      evens++; 
      System.out.println(digit + " is an even digit."); 
     } 
     else 
     { 
      odds++; 
      System.out.println(digit + " is an odd digit."); 
     } 
    } 

    String evenStr = " even " + ((evens == 1) ? "digit" : "digits"); 
    String oddStr = " odd " + ((odds == 1) ? "digit" : "digits"); 
    String zeroStr = (zeros == 1) ? " zero" : " zeros"; 

    System.out.println(number + " has " + evens + evenStr + ", " + odds + oddStr + ", and " 
      + zeros + zeroStr + "."); 
} 

编译器试图说number不保证被初始化。例如,如果下面的语句抛出一个异常,number不会被初始化:

number = Long.parseLong(numStr); 

然后,您可以捕获该异常,但number还没有“感动”,让变量的所有进一步的用途可能潜在导致意外的行为甚至例外。

摆脱这种通常的方法是从一开始就指定变量值:

long number = 0; // just an example, assign whatever value makes sense for you 

UPDATE

Java规范可以摆脱一些关于这个论题更多的光线,尤其是部分Initial Values of VariablesDefinite Assignment

+0

如果异常被抓到,是不是会继续,那么到达'while(!validInput);',这仍然是true,因为catchIn会跳过'validInput = true',因此重复直到validInput(和因此无异常输入)被提供并分配给'number'? – BrainFRZ

+1

如果编译器允许您编译和执行代码,则会出现这种情况,但标准编译器的行为会引发错误 - 它无法分析代码太深以至于无法“发现”此代码。 –

+0

你知道,编译器不可能检查变量值,程序流等的所有可能的变体和组合。 - 更容易抛出错误并使程序员“修复”代码... –

你说得对,number将永远无法逃脱的因为validInput你的病情的环路初始化。但编译器不够聪明,无法确定整个逻辑并确定它是安全的。所有它看到的是,这条线是try-catch块可能会或可能不会引发异常内:

number = Long.parseLong(numStr); 

...等它看到潜力number变量保持未初始化,甚至尽管你可以说当你的程序的整个逻辑被考虑时,这绝不会发生。

若要解决声明number变量时只指定一些虚拟默认值。

long number = -1L; // dummy default value to reassure the compiler. 
+0

好的,谢谢。编译器在我一直认为对它来说很容易的情况下工作的非常神奇。有什么方法可以继续获得编译器或者帮助解决潜在的逻辑错误,就像我之前在初始化之后提到的那样? – BrainFRZ

Long.parseLong可能会抛出一个异常,它将被捕获到您的catch块中。在这种情况下,编号不会被初始化,这就是为什么你得到编译器错误。

声明它时初始化数字,并确保代码的其余部分能够处理数字设置为初始值的情况。

+0

虽然这不是我的问题或程序的逻辑。是的,它会抛出一个不初始化它的异常,但是如果'validInput'为true,那么抛出'while'循环来达到'for'循环的唯一方法就是。这只能在'number'被初始化为无异常之后才会发生。否则,在程序之后,它会持续循环直到发生这种情况。 – BrainFRZ

对于编译器来说太重逻辑来检测 布尔条件,循环,不抛出catch;

add“number = 0;”赶上子句

catch (NumberFormatException e) { 
    **number=0;** 
    System.out.print("That's not an integer. "); 
}