非静态变量不能从静态上下文中引用


288

我已经编写了以下测试代码:

class MyProgram
{
    int count = 0;
    public static void main(String[] args)
    {
        System.out.println(count);
    }
}

但是它给出了以下错误:

Main.java:6: error: non-static variable count cannot be referenced from a static context
        System.out.println(count);
                           ^

如何获得识别类变量的方法?


尽量避免使用static。您可以编写完整的静态程序,就像一样C。但这不是一个很好的选择。尝试使用Java作为一种面向对象的语言来使用它。
Erick G. Hagstrom

Answers:


294

您必须了解一个类与该类实例之间的区别。如果您在街上看到一辆汽车,即使您看不到哪种型号或类型,也会立即知道这是一辆汽车。这是因为您将看到的内容与“汽车” 进行了比较。该类包含与所有汽车相似的类。将其视为模板或想法。

同时,您看到的汽车是“汽车”类的一个实例,因为它具有您期望的所有属性:有人驾驶它,它有引擎,车轮。

因此,该班级说“所有汽车都有颜色”,实例说“这辆特定的汽车是红色的”。

在OO世界中,您定义类,并在类内部定义type字段Color。当实例化该类时(创建特定实例时),将为该颜色保留内存,并且可以为该特定实例指定颜色。由于这些属性是特定的,因此它们是非静态的。

静态字段和方法与所有实例共享。它们用于特定于类而不是特定实例的值。对于方法,这通常是全局辅助方法(如Integer.parseInt())。对于字段,通常是常量(例如汽车类型,即您的集合有限且不经常更改的东西)。

为了解决您的问题,您需要实例化类的实例(创建对象),以便运行时可以为该实例保留内存(否则,不同的实例会相互覆盖您不想要的实例)。

在您的情况下,请尝试以下代码作为起点:

public static void main (String[] args)
{
    try
    {
        MyProgram7 obj = new MyProgram7 ();
        obj.run (args);
    }
    catch (Exception e)
    {
        e.printStackTrace ();
    }
}

// instance variables here

public void run (String[] args) throws Exception
{
    // put your code here
}

main()方法创建了它所包含的类的一个实例(听起来很奇怪,但是由于main()是用该类而不是用该实例创建的,所以可以做到这一点),然后调用一个实例方法(run())。


我现在正在向我们的新同事解释这一点-感谢您的出色解释。这应该由接受者回答。
Supahupe

83

静态字段和方法连接到类本身而不是其实例。如果您有一个class A,一个“ normal”方法b和一个static方法c,并且创建了a该类的实例A,则对A.c()和的调用a.b()是有效的。方法c()不知道连接了哪个实例,因此它不能使用非静态字段。

为您提供的解决方案是使字段为静态,或者使方法为非静态。然后,您的主体可能看起来像这样:

class Programm {

    public static void main(String[] args) {
        Programm programm = new Programm();
        programm.start();
    }

    public void start() {
        // can now access non-static fields
    }
}

54

static关键字修改类内的方法或可变的生命周期。static在加载类时会创建一个方法或变量。static仅当将类实例化为对象(例如,通过使用new运算符)时,才会创建未声明为的方法或变量。

从广义上讲,类的生命周期为:

  1. 编写该类的源代码,创建一个模板,图案或图章,然后将其用于
  2. 使用new运算符使用该类创建一个对象,以将该类的实例作为实际对象,然后对该对象进行处理
  3. 销毁对象,以回收其持有的资源,例如垃圾回收期间的内存。

为了有一个应用程序的初始入口点,Java采用了以下约定:Java程序必须具有一个类,该类包含具有约定名称或特殊名称的方法。这种特殊方法称为main()。由于无论是否已实例化包含main方法的类都必须存在该方法,因此main()必须使用static修饰符声明该方法,以便在加载该类后即可使用该main()方法。

结果是,当您通过命令行启动Java应用程序时,java helloworld会发生一系列操作。首先,将启动并初始化Java虚拟机。接下来,将包含编译后的Java代码的helloworld.class文件加载到Java虚拟机中。然后,Java虚拟机将在helloworld名为的类中寻找一个方法main(String [] args)static即使实际上尚未将类实例化为对象,此方法也必须存在。Java虚拟机不会通过从类创建对象来创建该类的实例。它只是加载类并从该main()方法开始执行。

因此,您需要创建一个类的实例作为一个对象,然后可以访问该类的方法和变量(尚未用static修饰符声明)。一旦Java程序开始使用该main()函数,您就可以使用具有修饰符的任何变量或方法,static因为它们作为要加载的类的一部分存在。

但是,在main()方法之外的,没有static修饰符的类的变量和方法在将类的实例创建为main()方法内的对象之前不能使用。创建对象后,您可以使用对象的变量和方法。staticJava编译器会在编译时尝试尝试使用不带修饰符的类的变量和方法,而这些变量和方法不经过类的对象,并且在编译时会被标记为错误。

import java.io.*;

class HelloWorld {
    int myInt;      // this is a class variable that is unique to each object
    static int myInt2;  // this is a class variable shared by all objects of this class

    static void main (String [] args) {
        // this is the main entry point for this Java application
        System.out.println ("Hello, World\n");
        myInt2 = 14;    // able to access the static int
        HelloWorld myWorld = new HelloWorld();
        myWorld.myInt = 32;   // able to access non-static through an object
    }
}

11

让我们首先分析您的程序。在您的程序中,第一个方法是main(),并记住它是静态方法...然后声明该方法的局部变量(compareCount,low,high等。)。此变量的范围仅是声明的方法,无论它是静态方法还是非静态方法。因此,您不能在该方法之外使用这些变量。这是您犯的基本错误。

然后我们到下一点。你告诉静静杀死了你。(这可能会杀死您,但是只会使您的程序生死!!)首先,您必须了解基本知识。*静态方法仅调用静态方法,而仅使用静态变量。*静态变量或静态方法不依赖于该类的任何实例。(即,如果更改静态变量的任何状态,它将反映在该类的所有对象中)*因此,您将其称为类变量或类方法。“ static”关键字还有很多。我希望你现在明白了。首先更改变量的范围,并将其声明为静态变量(以便能够在静态方法中使用它)。

对您的建议是:您误解了变量和静态功能范围的概念。对此有个清晰的想法。


11

最基本的是静态变量或静态方法在类级别。类级变量或方法先于实例级方法或变量被加载。显然,未加载的东西不能使用。因此,java编译器不让运行时要处理的事情在编译时解决。这就是为什么它给您带来错误,无法从静态上下文引用非静态内容的原因。您只需要阅读有关类级别范围,实例级别范围和本地范围的信息。


8

为了能够从您的静态方法访问它们,它们必须是静态成员变量,如下所示:

public class MyProgram7 {
  static Scanner scan = new Scanner(System.in);
  static int compareCount = 0;
  static int low = 0;
  static int high = 0;
  static int mid = 0;  
  static int key = 0;  
  static Scanner temp;  
  static int[]list;  
  static String menu, outputString;  
  static int option = 1;  
  static boolean found = false;

  public static void main (String[]args) throws IOException {
  ...

7

现在,您可以在方法中添加/使用实例

public class Myprogram7 {

  Scanner scan;
  int compareCount = 0;
  int low = 0;
  int high = 0;
  int mid = 0;  
  int key = 0;  
  Scanner temp;  
  int[]list;  
  String menu, outputString;  
  int option = 1;  
  boolean found = false;  

  private void readLine() {

  }

  private void findkey() {

  }

  private void printCount() {

  }
  public static void main(String[] args){

    Myprogram7 myprg=new Myprogram7();
    myprg.readLine();
    myprg.findkey();
    myprg.printCount();
  }
}

我以非常可靠的示例为模板,将复杂的src文件修改为适当的结构。
XMAN

3

我将尝试向您解释静态内容。首先,静态变量不属于该类的任何特定实例。它们以班级名称识别。静态方法也不再次属于任何特定实例。他们只能访问静态变量。假设您调用MyClass.myMethod(),而myMethod是静态方法。如果在方法内部使用非静态变量,那么到底该如何使用哪个变量呢?这就是为什么您只能从静态方法中使用静态变量的原因。我再说一遍,它们不属于任何特定实例。


2
  • 首先是要了解类实例与类本身之间的区别。一个类对某些属性进行建模,并在这些属性的上下文中模拟整个行为。实例将为这些属性定义特定的值。

  • 在类的上下文中,而不是在类的实例的上下文中,任何与static关键字绑定的内容都可用

  • 作为上述必然结果

    1. 方法中的变量不能是静态的
    2. 静态字段和方法必须使用类名来调用,例如MyProgram7.main(...)
  • 静态字段/方法的生命周期等于应用程序的生命周期

例如,汽车具有属性颜色,并且表现出“运动”行为。汽车的一个实例是一辆以25kmph的速度行驶的红色大众甲壳虫。

现在,汽车的静态属性将是道路上的车轮数量(4),这将适用于所有汽车。

高温超导


1

由ClassLoader负责加载类文件。让我们看看在编写自己的类时会发生什么。

范例1:

class StaticTest {

      static int a;
      int b;
      int c;
}

现在我们可以看到类“ StaticTest”具有3个字段。但是实际上不存在b,c成员变量。但是为什么要使用???。好吧,别担心。这里b,c是实例变量。因为实例变量在创建对象时获取内存。因此,此处b,c尚未获得任何内存。这就是为什么不存在b,c的原因。所以只有一个存在。对于ClassLoader,它只有一个有关a的信息。ClassLoader尚未识别b,c,因为它的对象尚未实例化。

让我们看另一个示例:示例2:

class StaticTest {

      public void display() {
          System.out.println("Static Test");
      }


      public static void main(String []cmd) {

             display();       
      }

}

现在,如果我们尝试编译此代码,编译器将给出CE错误。CE:非静态方法display()不能从静态上下文中引用。

现在,对于ClassLoader来说,它看起来像:

class StaticTest {

      public static void main(String []cmd) {

             display();       
      }

}

在示例2中,CE错误是因为我们从静态上下文调用非静态方法。因此ClassLoader在编译时无法识别方法display(),因此发生了编译时错误。


在您完成答案之前,可能是偶然提交了答案吗?请对其进行编辑并添加缺少的内容,谢谢!
plamut 2015年

1

在调用实例方法或实例变量之前,它需要一个对象(实例)。从静态方法调用实例变量时,编译器不知道该变量属于哪个对象。因为静态方法没有对象(总是只有一个副本)。当您从实例方法调用实例变量或实例方法时,它将引用该this对象。这意味着变量属于创建的任何对象,并且每个对象都有其自己的实例方法和变量的副本。

静态变量标记为static,实例变量没有特定的关键字。


0

这是为所有初学者介绍静态关键字的一个比较。
当您更多地使用类和对象时,您将清楚地了解它。

| * | 静态:可以使用类名调用静态项。
如果您在代码中观察到,则某些函数将直接使用类名进行调用,例如

NamCls.NamFnc();

System.out.println();

这是因为NamFnc和println将在它们之前使用关键字static声明。

| * | 非静态:非静态项目可以使用类变量来调用。
如果它不是静态的,则需要
该类的变量,在类变量之后放置点号,
然后调用函数。

NamCls NamObjVar = new NamCls();
NamObjVar.NamFnc();


下面的代码巧妙地解释了您

| * | 类中的静态和非静态函数:

public class NamCls
{
    public static void main(String[] args)
    {
        PlsPrnFnc("Tst Txt");

        NamCls NamObjVar = new NamCls();
        NamObjVar.PrnFnc("Tst Txt");
    }

    static void PlsPrnFnc(String SrgPsgVal)
    {
        System.out.println(SrgPsgVal);
    }

    void PrnFnc(String SrgPsgVal)
    {
        System.out.println(SrgPsgVal);
    }
}


| * | 类内的静态和非静态类:

public class NamCls
{
    public static void main(String[] args)
    {
        NamTicCls NamTicVaj = new NamTicCls();
        NamTicVaj.PrnFnc("Tst Txt");

        NamCls NamObjVar = new NamCls();
        NamNicCls NamNicVar = NamObjVar.new NamNicCls();
        NamNicVar.PrnFnc("Tst Txt");
    }

    static class NamTicCls
    {
        void PrnFnc(String SrgPsgVal)
        {
            System.out.println(SrgPsgVal);
        }
    }

    class NamNicCls
    {
        void PrnFnc(String SrgPsgVal)
        {
            System.out.println(SrgPsgVal);
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.