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

What is the reason behind “non-static method cannot be referenced from a static context”?

本问题已经有最佳答案,请猛点这里访问。

初学者最常见的错误是,当您试图"静态"地使用类属性而不创建该类的实例时。它会给您留下所提到的错误消息:

You can either make the non static method static or make an instance of that class to use its properties.

为什么?我不是在寻求解决方案。我很高兴知道这背后的原因是什么。最核心的原因!

1
2
3
4
5
6
7
8
9
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();        
}

你不能说不存在的东西。由于尚未创建对象,因此非静态方法尚不存在。静态方法(根据定义)始终存在。


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

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


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

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

1
2
3
4
5
6
7
8
9
10
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() );
    }
}

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

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

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


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

静态方法可能是异常而不是规范。如果你想学OOP的话,尤其是在早期。(为什么从规则的例外开始?)这是非常反常的Java教学法,你应该学习的"第一件事"是公共静态空洞的主要内容。(几乎没有真正的Java应用程序有它们自己的主要方法。)


我认为,值得注意的是,Java编译器的规则是,当Java编译器在没有显式实例的情况下访问实例方法或实例字段时,会插入等效的"这个"。当然,编译器知道它只能从一个实例方法中完成这项工作,这个实例方法有一个"this"变量,而静态方法则没有。

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

1
2
instanceMethod();
this.instanceMethod();

这些也相当于:

1
2
... = instanceField;
... = this.instanceField;

当您不提供特定的实例时,编译器会有效地插入"this"。

编译器提供的这一点(双关语)的"魔力帮助"可能会混淆新手:这意味着实例调用和静态调用有时似乎具有相同的语法,而实际上是不同类型和底层机制的调用。

实例方法调用有时被称为方法调用或调度,因为支持多态性的虚拟方法的行为;无论您是编写显式对象实例来使用还是编译器插入了"this.",都会发生调度行为。

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

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

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

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


到目前为止,答案描述了原因,但这里还有一个你可能想考虑的问题:

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

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

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

这很有用,因为您只希望在单个范围内使用一次可实例化类的方法。如果您在一个范围内从一个可实例化类调用多个方法,那么一定要创建一个可引用的实例。


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


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


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

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


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

前任:

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


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

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


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

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

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


这背后的简单原因是父类的静态数据成员可以访问(仅当不重写时),但例如(非静态)数据成员或方法我们需要它们的引用,因此它们只能通过对象调用。