为什么javac抱怨没有初始化变量?

时间:2010-02-02 20:55:10

标签: java initialization

对于这个Java代码:

String var;
clazz.doSomething(var);

为什么编译器会报告此错误:

  

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

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

String var = null;

...

6 个答案:

答案 0 :(得分:23)

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

请参阅§4.12.5JLS了解非常详细的说明,其中说的基本相同:

  

程序中的每个变量在使用其值之前必须具有值:

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

答案 1 :(得分:7)

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

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

int x;

if(cond2)
   x=2;
else if(cond3)
   x=3;

System.out.println("X was:"+x);

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

有趣的是,你应该从中获取的内容是,在你弄清楚实际必须这样做之前,不要初始化你的局部变量。如果你习惯总是说“int x = 0;”你会阻止这个梦幻般的“坏逻辑”探测器运行。这个错误不止一次为我节省了时间。

答案 2 :(得分:4)

与比尔K同上。我补充道:

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

但是当谈到类变量时,编译器很难为你做这个。类变量可以由类中的任何函数设置。编译器很难确定可能调用函数的所有可能的顺序。至少它必须分析系统中调用此类中任何函数的所有类。它可能必须检查任何数据文件或数据库的内容,并以某种方式预测用户将做出什么输入。充其量任务将极其复杂,最糟糕的是不可能。因此对于类变量,提供可靠的默认值是有意义的。这个默认值基本上是用零位填充字段,因此引用为null,整数为零,布尔值为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,那么这很好。但是如果真正发生的事情是你认为其中一个测试是&lt; =或&gt; =或者你打算在bar == 0时包含一个最终的else,那么你就欺骗了编译器没有检测到你的错误。顺便说一句,我认为这样的结构编码风格很差:编译器不仅不能确定你的意图,而且未来的维护程序员也不能。

答案 3 :(得分:1)

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

所以我回去删除了我正在使用的初始化,例如

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

尼斯。我一直在为

收到编译器警告
List<Thing> somethings = new List(); 

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

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

答案 4 :(得分:0)

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

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

答案 5 :(得分:0)

当然,如果您在显示声明时确实有两条线在彼此之上,请填充它,不需要默认构造函数。但是,例如,如果您想要声明一次并使用它几次或多次,则默认构造函数或null声明是相关的。或者是指向一个如此轻量级的对象的指针,最好在循环中一遍又一遍地分配它,因为指针的分配比对象的实例化要少得多? (据推测,在循环的每一步都有一个新对象的正当理由)。

第四条法案