【问题标题】:Why does javac complain about not initialized variable?为什么 javac 抱怨没有初始化变量?
【发布时间】:2010-02-02 20:55:09
【问题描述】:

对于这个 Java 代码:

String var;
clazz.doSomething(var);

为什么编译器会报这个错误:

变量 'var' 可能尚未初始化

我认为所有变量或引用都初始化为null。为什么需要这样做:

String var = null;

??

【问题讨论】:

    标签: java initialization


    【解决方案1】:

    实例和类变量被初始化为 null(或 0),但局部变量不是。

    请参阅JLS 中的§4.12.5 以获得非常详细的解释,其内容基本相同:

    程序中的每个变量在使用它的值之前都必须有一个值:

    • 每个类变量、实例变量或数组组件在创建时都使用默认值进行初始化:
      • [截取所有默认值的列表]
    • 每个方法参数都被初始化为方法调用者提供的相应参数值。
    • 每个构造函数参数都初始化为类实例创建表达式或显式构造函数调用提供的相应参数值。
    • 异常处理程序参数被初始化为表示异常的抛出对象。
    • 在使用局部变量之前,必须通过初始化或赋值显式地赋予一个值,编译器可以使用明确赋值的规则来验证该值。

    【讨论】:

    • 为什么会做出这个决定?为什么不像类变量一样将它们初始化为null
    • 有很多关于为什么局部变量没有默认值的 stackoverflow 问题。例如stackoverflow.com/questions/415687/…
    • @Marcus @matt b:是的,这可能是因为不初始化局部变量几乎总是存在错误的迹象。
    【解决方案2】:

    这是因为 Java 非常有用(尽可能)。

    它将使用相同的逻辑来捕捉您可能错过的一些非常有趣的边缘情况。例如:

    int x;
    
    if(cond2)
       x=2;
    else if(cond3)
       x=3;
    
    System.out.println("X was:"+x);
    

    这将失败,因为存在未指定的 else 情况。事实是,这里绝对应该指定一个 else 情况,即使它只是一个错误(默认情况也是如此:switch 语句中的条件)。

    有趣的是,您应该从中得到的好处是,在您确定确实必须这样做之前,永远不要初始化您的局部变量。如果你习惯总是说“int x=0;”您将阻止这个奇妙的“错误逻辑”检测器运行。这个错误不止一次为我节省了时间。

    【讨论】:

      【解决方案3】:

      Bill K 同上。我补充说:

      Java 编译器可以保护您免受在函数中使用变量之前未能设置变量的伤害。因此,它没有像 Bill K 所描述的那样明确设置默认值。

      但是当涉及到类变量时,编译器很难为你做这件事。类变量可以由类中的任何函数设置。编译器很难确定调用函数的所有可能顺序。至少它必须分析系统中调用此类中任何函数的所有类。它可能必须检查任何数据文件或数据库的内容,并以某种方式预测用户将输入什么内容。最好的情况是任务极其复杂,最坏的情况是不可能的。所以对于类变量,提供一个可靠的默认值是有意义的。该默认值基本上是用零位填充字段,因此引用为 null,整数为 0,布尔值为 false,等等。

      正如比尔所说,你绝对不应该养成在声明变量时自动初始化变量的习惯。如果这在您的程序上下文中确实有意义,则仅在声明时初始化变量。比如,如果 99% 的时间你希望 x 为 42,但在某些 IF 条件下,你可能会发现这是一个特殊情况,x 应该是 666,那么很好,从“int x=42;”开始并在 IF 内部覆盖它。但在更正常的情况下,根据任何条件计算出值,不要初始化为任意数字。只需用计算值填充它。然后,如果您在某些条件组合下出现逻辑错误并且无法设置值,编译器会告诉您是您搞砸了而不是用户搞砸了。

      PS 我见过很多蹩脚的程序,它们会说:

      HashMap myMap=new HashMap();
      myMap=getBunchOfData();
      

      当你知道一毫秒后你会立即扔掉这个对象时,为什么还要创建一个对象来初始化变量呢?那只是浪费时间。

      编辑

      举个简单的例子,假设你是这样写的:

      int foo;
      if (bar<0)
        foo=1;
      else if (bar>0)
        foo=2;
      processSomething(foo);
      

      这会在编译时抛出错误,因为编译器会注意到当 bar==0 时,您从未设置 foo,但随后您尝试使用它。

      但是如果你将 foo 初始化为一个虚拟值,比如

      int foo=0;
      if (bar<0)
        foo=1;
      else if (bar>0)
        foo=2;
      processSomething(foo);
      

      然后编译器将看到无论 bar 的值是什么,foo 都被设置为某个值,因此它不会产生错误。如果您真正想要的是当 bar 为 0 时 foo 为 0,那么这很好。但是,如果真正发生的事情是您的意思是其中一个测试是 =,或者您的意思是在 bar==0 时包含一个最终的 else,那么您已经欺骗了编译器,使其无法检测到您的错误。顺便说一句,我认为这样的构造是糟糕的编码风格:不仅编译器无法确定您的意图,而且未来的维护程序员也无法确定。

      【讨论】:

      • 为什么不把它初始化为null呢?我能想到的唯一真正有问题的情况是,您应该声明一个必须在 try 块中设置值的最终变量。但是你可以在这里定义一个临时变量,并在 try 块之后将其值分配给 final 。
      • @sibidiba:你回复我的哪一部分?如果您的意思是笼统的,那么您将失去编译时检查的优势。请参阅比尔 K 的回答。如果您的意思是我对 myMap 示例的结束评论,那么简单的解决方案是编写“HashMap myMap=getBunchOfData()”并跳过无意义的额外对象。
      【解决方案4】:

      我喜欢 Bill K 关于让编译器为你工作的观点——我陷入了对每个自动变量的初始化,因为它“看起来像是 Java 要做的事情”。我无法理解类变量(即构造函数担心的持久性事物)和自动变量(一些计数器等)是不同的,即使一切都是 Java 中的类。

      所以我返回并删除了我将要使用的初始化,例如

      List <Thing> somethings = new List<Thing>(); 
      somethings.add(somethingElse); // <--- this is completely unnecessary
      

      很好。我收到了编译器警告

      List<Thing> somethings = new List(); 
      

      我认为问题在于缺乏初始化。错误的。问题是我没有理解规则,我需要在“新”中标识的&lt;Thing&gt;,而不是创建的任何&lt;Thing&gt; 类型的实际项目。

      (接下来我需要学习如何将文字小于和大于符号放入 HTML!)

      【讨论】:

      • @Bill IV 我为你整理了报价;您需要做的就是将代码部分指定为代码(编辑器上方的 101/010 图标将执行此操作)。
      【解决方案5】:

      我不知道它背后的逻辑,但是局部变量没有初始化为null。我想让你的生活变得轻松。如果可能的话,他们本可以使用类变量来完成。这并不意味着您必须在一开始就对其进行初始化。这很好:

      MyClass cls;
      if (condition) {
          cls = something;
      else 
          cls = something_else;
      

      【讨论】:

        【解决方案6】:

        当然,如果您在显示时确实有两行重叠 - 声明它,填充它,不需要默认构造函数。但是,例如,如果您想声明一次并多次使用它,则默认构造函数或 null 声明是相关的。或者指向对象的指针是否如此轻量级以至于最好在循环内一遍又一遍地分配它,因为指针的分配比对象的实例化少得多? (大概在循环的每一步都有一个新对象的正当理由)。

        第四号法案

        【讨论】:

        • 我并不是说,不要创造价值并重复使用它。我是说,在找到实际值之前,请避免填写虚拟值作为占位符。如果您只是声明变量但未填充它,那么稍后您会遍历所有 IF 和 BUT 来填充它,然后您尝试使用它,编译器会告诉您是否有一些可能的路径通过您从未填充过的地方.但是如果你填写一个虚拟值,编译器无法知道这是一个虚拟值而不是真实值,因此你失去了编译时检查的优势。
        猜你喜欢
        • 2011-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-29
        • 1970-01-01
        • 2019-01-01
        • 2014-08-23
        • 2012-02-23
        相关资源
        最近更新 更多