【问题标题】:What is the reason behind "non-static method cannot be referenced from a static context"? [duplicate]“无法从静态上下文引用非静态方法”背后的原因是什么? [复制]
【发布时间】:2010-09-22 09:31:56
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: java static


    【解决方案1】:

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

    【讨论】:

    • "由于你还没有创建对象,所以非静态方法还不存在。" - 非常感谢。我早该想到的。
    • 方法本身确实存在。在加载的类定义中的某处。所以答案是错误的:)
    • @Vladimir,如果你想挑剔的话,可以。 :) “在当前上下文中不存在” :)
    • "你不能调用不存在的东西。" - 暴徒:p
    • static 方法无法判断非静态成员属于哪个特定对象。由于不存在现有对象,因此非静态方法不属于任何对象。因此,无法从static 上下文中引用非静态方法。
    【解决方案2】:

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

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

    【讨论】:

    • “非静态方法属于类的实例”——答案。但是为什么它属于类的实例呢?谢谢。
    • @ZiG:因为你告诉它不要将其标记为静态
    • 对于初学者开发人员,我创建了一个示例来理解上面提到的@StevenA.Lowe。 repl.it/repls/WavyNeighboringSpotteddolphin
    【解决方案3】:

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

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

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

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

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

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 我遇到了上述错误的问题,但必须学习如何使用静态才能在不同的活动中使用类的实例,所以我一直在苦苦挣扎。 :( 尝试了很多东西,但没有奏效。
      【解决方案5】:

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

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

      instanceMethod();
      this.instanceMethod();
      

      这些也是等价的:

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

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

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

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

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

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


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

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

      【讨论】:

        【解决方案6】:

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

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

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

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

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            【解决方案8】:

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

            例如:

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

            【讨论】:

            • 并非所有方法都有副作用!不一定是to的动作,它也可以是对象告诉你的东西。
            【解决方案9】:

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

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

            【讨论】:

              【解决方案10】:

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

              【讨论】:

                【解决方案11】:

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

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

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

                【讨论】:

                  【解决方案12】:

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

                  【讨论】:

                    【解决方案13】:

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

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

                    【讨论】:

                      猜你喜欢
                      • 2022-12-12
                      • 2011-05-17
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多