326

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

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

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

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

13 回答 13

443

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

于 2008-11-14T18:10:50.620 回答
86

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

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

于 2008-11-14T18:04:17.810 回答
32

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

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

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

运行上述程序可能会产生什么结果?

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

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

于 2009-07-16T21:30:10.103 回答
15

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

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

于 2008-11-15T07:28:36.610 回答
15

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

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

instanceMethod();
this.instanceMethod();

这些也是等价的:

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

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

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

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

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

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


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

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

于 2012-07-15T19:20:31.090 回答
8

到目前为止的答案描述了原因,但您可能还需要考虑其他一些问题:

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

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

或者

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

如果您只想在单个范围内使用可实例化类的方法一次,这很有用。如果您在单个范围内从可实例化类调用多个方法,请务必创建一个可引用实例。

于 2008-11-15T07:18:41.420 回答
5

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

于 2014-06-05T20:46:19.207 回答
3

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

前任:

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

于 2008-11-14T18:11:16.470 回答
3

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

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

于 2008-11-14T18:11:50.290 回答
3

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

于 2009-04-07T14:09:24.790 回答
2

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

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

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

于 2014-08-20T21:48:24.430 回答
1

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

于 2011-07-05T14:23:05.647 回答
1

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

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

于 2014-09-13T15:10:07.890 回答