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

初学者最常犯的错误是试图“静态”地使用类属性。而不需要创建该类的实例。它会给你留下上面提到的错误信息:

您可以将非静态方法设置为静态,也可以创建该类的实例来使用它的属性。

背后的原因是什么?我关心的不是解决方法,而是原因。

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


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

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

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

你不能给不存在的东西打电话。因为还没有创建对象,所以非静态方法还不存在。静态方法(根据定义)总是存在的。

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

例:

Car类可能有一个wash方法,用于指示清洗特定的汽车,而静态方法应用于Car类型。

如果一个方法不是静态的,这就“告诉”编译器该方法需要访问类中的实例级数据(就像一个非静态字段)。除非创建了类的实例,否则此数据将不可用。因此,如果你试图从静态方法调用该方法,编译器会抛出一个错误。如果实际上该方法没有引用类的任何非静态成员,则将该方法设置为静态。

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

到目前为止,答案描述了原因,但还有一件事你可能需要考虑:

你可以通过将方法调用附加到可实例化类的构造函数来调用方法,

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

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

如果您只希望在单个作用域内使用一次可实例化类的方法,这是非常有用的。如果在一个作用域中从一个可实例化类调用多个方法,一定要创建一个可引用的实例。

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

静态方法应该是例外,而不是规范。尤其是在你想学习OOP的时候。(为什么要从规则的例外开始呢?)这是非常反教学法的Java,你应该学习的“第一”件事是公共静态 void主要内容。(很少有真正的Java应用程序有自己的主方法。)

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

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

实例方法是逻辑,实例字段是数据。它们一起构成了一个物体。

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() );
}
}

运行上述程序可能会得到什么结果?

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

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

这背后的简单原因是父类的Static数据成员 可以访问(仅当它们未被覆盖时),但例如(非静态) 数据成员或方法需要它们的引用,因此它们只能是

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

这意味着当你在一个实例方法中,以下是等价的:

instanceMethod();
this.instanceMethod();

这些也是等价的:

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

当您没有提供特定实例时,编译器会有效地插入“this.”。

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

实例方法调用有时被称为方法调用或分派,因为虚拟方法的行为支持多态;不管你是写了一个显式的对象实例来使用,还是编译器插入了一个"this.",分派行为都会发生。

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

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


编译器抱怨的是它不能像在实例方法中那样简单地插入标准的"this.",因为这段代码是在静态方法中;然而,可能作者只是忘记为这个调用提供感兴趣的实例—例如,一个实例可能作为参数提供给静态方法,或者在此静态方法中创建。

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

如果我们试图从静态上下文访问一个实例方法,编译器无法猜测你引用的是哪个实例方法(变量对应哪个对象)。不过,您总是可以使用对象引用访问它。

所以你问的是一个非常核心的原因?

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

对不起我的英语!我是拉丁!

非静态方法依赖于对象。一旦对象被创建,程序就会识别它。

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