Java中未初始化的变量和成员

时间:2022-06-19 00:09:54

Consider this:

public class TestClass {

    private String a;
    private String b;

    public TestClass()
    {
    a = "initialized";
    }

    public void doSomething()
    {
    String c;

        a.notify(); // This is fine
    b.notify(); // This is fine - but will end in an exception
    c.notify(); // "Local variable c may not have been initialised"
    }

}

I don't get it. "b" is never initialized but will give the same run-time error as "c", which is a compile-time error. Why the difference between local variables and members?

我不明白。 “b”从未初始化,但会产生与“c”相同的运行时错误,这是一个编译时错误。为什么局部变量和成员之间存在差异?

Edit: making the members private was my initial intention, and the question still stands...

编辑:让会员私密是我最初的意图,问题仍然存在......

7 个解决方案

#1


13  

The rules for definite assignment are quite difficult (read chapter 16 of JLS 3rd Ed). It's not practical to enforce definite assignment on fields. As it stands, it's even possible to observe final fields before they are initialised.

明确赋值的规则非常困难(阅读JLS 3rd Ed的第16章)。在字段上强制执行明确的赋值是不切实际的。就目前而言,甚至可以在初始化之前观察最终字段。

#2


44  

The language defines it this way.

语言以这种方式定义它。

Instance variables of object type default to being initialized to null. Local variables of object type are not initialized by default and it's a compile time error to access an undefined variable.

对象类型的实例变量默认初始化为null。默认情况下,对象类型的局部变量未初始化,访问未定义变量是编译时错误。

See section 4.5.5 in here http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

请参阅此处的第4.5.5节http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

#3


21  

Here's the deal. When you call

这是交易。你打电话的时候

TestClass tc = new TestClass();

the new command performs four important tasks:

new命令执行四个重要任务:

  1. Allocates memory on the heap for the new object.
  2. 在堆上为新对象分配内存。

  3. Initiates the class fields to their default values (numerics to 0, boolean to false, objects to null).
  4. 将类字段启动为其默认值(数字为0,布尔值为false,对象为null)。

  5. Calls the constructor (which may re-initiate the fields, or may not).
  6. 调用构造函数(可能会重新启动字段,也可能不会)。

  7. Returns a reference to the new object.
  8. 返回对新对象的引用。

So your fields 'a' and 'b' are both initiated to null, and 'a' is re-initiated in the constructor. This process is not relevant for method calling, so local variable 'c' is never initialized.

所以你的字段'a'和'b'都被启动为null,并且在构造函数中重新启动'a'。此过程与方法调用无关,因此永远不会初始化局部变量“c”。

HTH

PS: for the gravely insomniac, read this.

PS:对于严重失眠者,请阅读此内容。

#4


3  

The compiler can figure out that c will never be set. The b variable could be set by someone else after the constructor is called, but before doSomething(). Make b private and the compiler may be able to help.

编译器可以确定c永远不会被设置。在调用构造函数之后,但在doSomething()之前,b变量可以由其他人设置。使b私有,编译器可能会提供帮助。

#5


3  

The compiler can tell from the code for doSomething() that c is declared there and never initialized. Because it is local, there is no possibility that it is initialized elsewhere.

编译器可以从代码中告诉doSomething()c在那里声明并且从不初始化。因为它是本地的,所以不可能在其他地方初始化它。

It can't tell when or where you are going to call doSomething(). b is a public member. It is entirely possible that you would initialize it in other code before calling the method.

它无法分辨你何时何地打电话给doSomething()。 b是公共会员。在调用方法之前,完全有可能在其他代码中初始化它。

#6


2  

Member-variables are initialized to null or to their default primitive values, if they are primitives.

成员变量初始化为null或默认原始值(如果它们是基元)。

Local variables are UNDEFINED and are not initialized and you are responsible for setting the initial value. The compiler prevents you from using them.

局部变量是UNDEFINED并且未初始化,您负责设置初始值。编译器阻止您使用它们。

Therefore, b is initialized when the class TestClass is instantiated while c is undefined.

因此,当实例化类TestClass而未定义c时,b被初始化。

Note: null is different from undefined.

注意:null与undefined不同。

#7


1  

You've actually identified one of the bigger holes in Java's system of generally attempting to find errors at edit/compile time rather than run time because--as the accepted answer said--it's difficult to tell if b is initialized or not.

你实际上已经确定了Java系统中一个较大的漏洞,通常是在编辑/编译时而不是运行时发现错误,因为 - 正如接受的答案所说 - 很难判断b是否被初始化。

There are a few patterns to work around this flaw. First is "Final by default". If your members were final, you would have to fill them in with the constructor--and it would use path-analysis to ensure that every possible path fills in the finals (You could still assign it "Null" which would defeat the purpose but at least you would be forced to recognize that you were doing it intentionally).

有一些模式可以解决这个缺陷。首先是“默认为最终”。如果你的成员是最终的,你将不得不用构造函数填充它们 - 它将使用路径分析来确保每个可能的路径填充决赛(你仍然可以指定它“Null”,这将失败目的但至少你会*承认你是故意这样做的)。

A second approach is strict null checking. You can turn it on in eclipse settings either by project or in default properties. I believe it would force you to null-check your b.notify() before you call it. This can quickly get out of hand so it tends to go with a set of annotations to make things simpler:

第二种方法是严格的空检查。您可以通过项目或默认属性在eclipse设置中打开它。我相信它会强制你在调用之前对你的b.notify()进行空值检查。这可能会很快失控,因此它倾向于使用一组注释来简化操作:

The annotations might have different names but in concept once you turn on strict null checking and the annotations the types of variables are "nullable" and "NotNull". If you try to place a Nullable into a not-null variable you must check it for null first. Parameters and return types are also annotated so you don't have to check for null every single time you assign to a not-null variable.

注释可能有不同的名称,但在概念中,一旦打开严格的空检查和注释,变量的类型是“可空”和“非空”。如果您尝试将Nullable放入非null变量,则必须先将其检查为null。参数和返回类型也会被注释,因此您不必在每次分配给非空变量时检查null。

There is also a "NotNullByDefault" package level annotation that will make the editor assume that no variable can ever have a null value unless you tag it Nullable.

还有一个“NotNullByDefault”包级别注释,它将使编辑器认为除非将其标记为Nullable,否则任何变量都不能具有空值。

These annotations mostly apply at the editor level--You can turn them on within eclipse and probably other editors--which is why they aren't necessarily standardized. (At least last time I check, Java 8 might have some annotations I haven't found yet)

这些注释主要适用于编辑器级别 - 您可以在eclipse和其他编辑器中打开它们 - 这就是为什么它们不一定是标准化的。 (至少我上次检查时,Java 8可能有一些我还没有找到的注释)

#1


13  

The rules for definite assignment are quite difficult (read chapter 16 of JLS 3rd Ed). It's not practical to enforce definite assignment on fields. As it stands, it's even possible to observe final fields before they are initialised.

明确赋值的规则非常困难(阅读JLS 3rd Ed的第16章)。在字段上强制执行明确的赋值是不切实际的。就目前而言,甚至可以在初始化之前观察最终字段。

#2


44  

The language defines it this way.

语言以这种方式定义它。

Instance variables of object type default to being initialized to null. Local variables of object type are not initialized by default and it's a compile time error to access an undefined variable.

对象类型的实例变量默认初始化为null。默认情况下,对象类型的局部变量未初始化,访问未定义变量是编译时错误。

See section 4.5.5 in here http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

请参阅此处的第4.5.5节http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

#3


21  

Here's the deal. When you call

这是交易。你打电话的时候

TestClass tc = new TestClass();

the new command performs four important tasks:

new命令执行四个重要任务:

  1. Allocates memory on the heap for the new object.
  2. 在堆上为新对象分配内存。

  3. Initiates the class fields to their default values (numerics to 0, boolean to false, objects to null).
  4. 将类字段启动为其默认值(数字为0,布尔值为false,对象为null)。

  5. Calls the constructor (which may re-initiate the fields, or may not).
  6. 调用构造函数(可能会重新启动字段,也可能不会)。

  7. Returns a reference to the new object.
  8. 返回对新对象的引用。

So your fields 'a' and 'b' are both initiated to null, and 'a' is re-initiated in the constructor. This process is not relevant for method calling, so local variable 'c' is never initialized.

所以你的字段'a'和'b'都被启动为null,并且在构造函数中重新启动'a'。此过程与方法调用无关,因此永远不会初始化局部变量“c”。

HTH

PS: for the gravely insomniac, read this.

PS:对于严重失眠者,请阅读此内容。

#4


3  

The compiler can figure out that c will never be set. The b variable could be set by someone else after the constructor is called, but before doSomething(). Make b private and the compiler may be able to help.

编译器可以确定c永远不会被设置。在调用构造函数之后,但在doSomething()之前,b变量可以由其他人设置。使b私有,编译器可能会提供帮助。

#5


3  

The compiler can tell from the code for doSomething() that c is declared there and never initialized. Because it is local, there is no possibility that it is initialized elsewhere.

编译器可以从代码中告诉doSomething()c在那里声明并且从不初始化。因为它是本地的,所以不可能在其他地方初始化它。

It can't tell when or where you are going to call doSomething(). b is a public member. It is entirely possible that you would initialize it in other code before calling the method.

它无法分辨你何时何地打电话给doSomething()。 b是公共会员。在调用方法之前,完全有可能在其他代码中初始化它。

#6


2  

Member-variables are initialized to null or to their default primitive values, if they are primitives.

成员变量初始化为null或默认原始值(如果它们是基元)。

Local variables are UNDEFINED and are not initialized and you are responsible for setting the initial value. The compiler prevents you from using them.

局部变量是UNDEFINED并且未初始化,您负责设置初始值。编译器阻止您使用它们。

Therefore, b is initialized when the class TestClass is instantiated while c is undefined.

因此,当实例化类TestClass而未定义c时,b被初始化。

Note: null is different from undefined.

注意:null与undefined不同。

#7


1  

You've actually identified one of the bigger holes in Java's system of generally attempting to find errors at edit/compile time rather than run time because--as the accepted answer said--it's difficult to tell if b is initialized or not.

你实际上已经确定了Java系统中一个较大的漏洞,通常是在编辑/编译时而不是运行时发现错误,因为 - 正如接受的答案所说 - 很难判断b是否被初始化。

There are a few patterns to work around this flaw. First is "Final by default". If your members were final, you would have to fill them in with the constructor--and it would use path-analysis to ensure that every possible path fills in the finals (You could still assign it "Null" which would defeat the purpose but at least you would be forced to recognize that you were doing it intentionally).

有一些模式可以解决这个缺陷。首先是“默认为最终”。如果你的成员是最终的,你将不得不用构造函数填充它们 - 它将使用路径分析来确保每个可能的路径填充决赛(你仍然可以指定它“Null”,这将失败目的但至少你会*承认你是故意这样做的)。

A second approach is strict null checking. You can turn it on in eclipse settings either by project or in default properties. I believe it would force you to null-check your b.notify() before you call it. This can quickly get out of hand so it tends to go with a set of annotations to make things simpler:

第二种方法是严格的空检查。您可以通过项目或默认属性在eclipse设置中打开它。我相信它会强制你在调用之前对你的b.notify()进行空值检查。这可能会很快失控,因此它倾向于使用一组注释来简化操作:

The annotations might have different names but in concept once you turn on strict null checking and the annotations the types of variables are "nullable" and "NotNull". If you try to place a Nullable into a not-null variable you must check it for null first. Parameters and return types are also annotated so you don't have to check for null every single time you assign to a not-null variable.

注释可能有不同的名称,但在概念中,一旦打开严格的空检查和注释,变量的类型是“可空”和“非空”。如果您尝试将Nullable放入非null变量,则必须先将其检查为null。参数和返回类型也会被注释,因此您不必在每次分配给非空变量时检查null。

There is also a "NotNullByDefault" package level annotation that will make the editor assume that no variable can ever have a null value unless you tag it Nullable.

还有一个“NotNullByDefault”包级别注释,它将使编辑器认为除非将其标记为Nullable,否则任何变量都不能具有空值。

These annotations mostly apply at the editor level--You can turn them on within eclipse and probably other editors--which is why they aren't necessarily standardized. (At least last time I check, Java 8 might have some annotations I haven't found yet)

这些注释主要适用于编辑器级别 - 您可以在eclipse和其他编辑器中打开它们 - 这就是为什么它们不一定是标准化的。 (至少我上次检查时,Java 8可能有一些我还没有找到的注释)