“非静态方法无法从静态上下文中引用”背后的原因是什么?

时间:2008-11-14 17:59:31

标签: java static

非常常见的初学者错误是当你尝试“静态地”使用类属性而不创建该类的实例时。它会留下您提到的错误消息:

  

您可以将非静态方法设为静态,也可以使该类的实例使用其属性。

为什么呢?我不是要求解决方案。我很高兴知道它背后的原因是什么。核心原因!

private java.util.List<String> someMethod(){
    /* Some Code */
    return someList;            
}

public static void main(String[] strArgs){          
     // The following statement causes the error. You know why..
    java.util.List<String> someList = someMethod();         
}

13 个答案:

答案 0 :(得分:326)

你不能打电话给不存在的东西。由于尚未创建对象,因此尚不存在非静态方法。静态方法(按定义)始终存在。

答案 1 :(得分:53)

您尝试调用的方法是实例级方法;你没有实例。

static方法属于该类,非static方法属于该类的实例。

答案 2 :(得分:22)

面向对象编程的本质是将逻辑与其操作的数据一起封装。

实例方法是逻辑,实例字段是数据。它们共同构成了一个对象。

public class Foo
{
    private String foo;
    public Foo(String foo){ this.foo = foo; }
    public getFoo(){ return this.foo; }

    public static void main(String[] args){
        System.out.println( getFoo() );
    }
}

运行上述程序的结果可能是什么?

没有对象,就没有实例数据,虽然实例方法作为类定义的一部分存在,但它们需要一个对象实例来为它们提供数据。

理论上,不访问任何实例数据的实例方法可以在静态上下文中工作,但实际上没有任何理由将其作为实例方法。无论如何,这是一个语言设计决定,而不是制定额外的规则来禁止它。

答案 3 :(得分:12)

我刚刚意识到,我认为人们不应该很早就接触到“静态”的概念。

静态方法应该是例外而不是常态。如果你想学习OOP,尤其是早期。 (为什么从规则的例外开始?)这是Java的反教学法,你应该学习的“第一”东西是公共的 static void主要的东西。 (很少有真正的Java应用程序有自己的主要方法。)

答案 4 :(得分:11)

我认为值得指出的是,根据Java语言的规则,Java编译器会插入相当于“this”的内容。当它注意到您在没有显式实例的情况下访问实例方法或实例字段时。当然,编译器知道它只能在一个实例方法中执行此操作,该方法具有“this”变量,而静态方法则没有。

这意味着当您使用实例方法时,以下内容是等效的:

instanceMethod();
this.instanceMethod();

这些也是等价的:

... = instanceField;
... = this.instanceField;

编译器正在有效地插入“this”。当您不提供特定实例时。

编译器的“神奇帮助”这个(双关语)可能会让初学者感到困惑:它意味着实例调用和静态调用有时看起来具有相同的语法,而实际上是不同类型和底层机制的调用。

由于支持多态的虚方法的行为,实例方法调用有时被称为方法调用或调度;无论您是编写要使用的显式对象实例还是编译器插入“this”,都会发生调度行为。

静态方法调用机制更简单,就像非OOP语言中的函数调用一样。

就个人而言,我认为错误消息具有误导性,它可能是“非静态方法无法从静态上下文引用而未指定显式对象实例”。


编译器抱怨的是它不能简单地插入标准“this”。就像在实例方法中一样,因为这段代码在静态方法中;但是,也许作者只是忘了为这个调用提供感兴趣的实例 - 例如,一个可能作为参数提供给静态方法的实例,或者在这个静态方法中创建的实例。

简而言之,您肯定可以在静态方法中调用实例方法,您只需要为调用指定一个显式实例对象。

答案 5 :(得分:6)

到目前为止,答案描述了原因,但这里有一些你可能想要考虑的事情:

您可以通过向其构造函数附加方法调用来从可实例化的类中调用方法,

Object instance = new Constuctor().methodCall();

primitive name = new Constuctor().methodCall();

这很有用,你只希望在一个范围内使用一次可实例化类的方法。如果要从单个范围内的可实例化类调用多个方法,请务必创建一个可参考的实例。

答案 6 :(得分:3)

如果我们尝试从静态上下文访问实例方法,编译器无法猜测您指的是哪个实例方法(对象是哪个对象)。但是,您始终可以使用对象引用来访问它。

答案 7 :(得分:2)

编译器实际上为非静态方法添加了一个参数。它添加了this pointer/reference. This is also the reason why a static method can not use this,因为没有对象。

答案 8 :(得分:1)

静态方法将动作与对象类型相关联,而非静态方法将动作与该类型对象的实例相关联。通常,它是一种与实例有关的方法。

例如:

类汽车可能有洗涤方法,这表示清洗特定的汽车,而静态方法适用于汽车类型。

答案 9 :(得分:1)

如果方法不是静态的,则“告诉”编译器该方法需要访问类中的实例级数据(如非静态字段)。除非已创建类的实例,否则此数据将不可用。因此,如果您尝试从静态方法调用该方法,则编译器会抛出错误。如果实际上该方法未引用该类的任何非静态成员,请将该方法设为静态。

在Resharper中,例如,只创建一个不引用该类的任何静态成员的非静态方法会生成一条警告消息“此方法可以变为静态”

答案 10 :(得分:0)

这背后的简单原因是父类的静态数据成员 可以被访问(仅当它们没有被覆盖时),但是例如(非静态的) 数据成员或方法我们需要他们的参考,所以他们只能 通过一个物体召唤。

答案 11 :(得分:0)

所以你要求一个非常核心的原因?

好吧,由于您使用Java进行开发,编译器会生成Java虚拟机可以解释的目标代码。无论如何,JVM是一个以机器语言运行的二进制程序(可能是JVM的特定于您的操作系统和硬件的版本,之前由另一种编程语言如C编译,以获得可在您的处理器中运行的机器代码)。最后,任何代码都被转换为机器代码。因此,创建一个对象(一个类的实例)相当于保留一个内存空间(当操作系统的CPU调度程序将您的程序放在队列的顶部以执行它时,将成为处理器寄存器的内存寄存器)拥有一个能够读写数据的数据存储空间。如果您没有类的实例(在静态上下文中发生),那么您没有该内存空间来读取或写入数据。事实上,就像其他人说的那样,数据不存在(因为从一开始你就没有写过,也没有预留内存空间来存储它)。

抱歉我的英文!我拉丁!

答案 12 :(得分:0)

非静态方法取决于对象。一旦创建了对象,程序就会识别它。

甚至可以在创建对象之前调用静态方法。静态方法非常适合进行不依赖于您计划使用的实际对象的比较或操作。