为什么静态方法只能使用静态数据?


38

我不明白为什么静态方法不能使用非静态数据。谁能解释什么是问题,为什么我们不能解决?


11
因为从静态方法的角度来看,仅存在静态数据。
mouviciel 2013年

4
分享您的研究成果对所有人都有帮助。告诉我们您尝试过的内容以及为什么它不能满足您的需求。这表明您已花时间尝试自我帮助,这使我们免于重复显而易见的答案,并且最重要的是,它可以帮助您获得更具体和相关的答案。另请参阅“ 如何提问
2013年

19
在这种情况下,@ gnat OP试图了解设计决策背后的原因。您希望他在这种情况下尝试什么?
极客

2
@Geek-静态方法的存在,静态数据是一种语言设计问题。假定标准含义,则静态方法无法访问实例数据的事实并非如此。这些定义暗示了这种局限性,它是可能的并且有意义,而不是某些语言设计者的常识。
Steve314 2013年

6
用格特鲁德·斯坦(Gertrude Stein)来解释:“那里没有这个。”
hippo-dancer

Answers:


73

在大多数OO语言中,当您在类中定义方法时,它便成为Instance Method。当您通过关键字创建该类的新实例new,将初始化该实例唯一的一组新数据。然后,属于该实例的方法可以使用您在其上定义的数据。

相比之下,静态方法对单个类实例一无所知。静态方法类似于C或C ++中的自由函数。它与类的特定实例无关。这就是为什么他们无法访问实例值的原因。没有任何实例可以借鉴!

静态数据类似于静态方法。声明的值static没有关联的实例。它在每个实例中存在,并且仅在内存中的单个位置声明。如果更改,则该类的每个实例都会更改。

一个静态方法可以访问静态数据,因为它们都独立存在的一类特定的实例。

与实例方法相比,看一下如何调用静态方法可能会有所帮助。假设我们有以下类(使用类似Java的伪代码):

class Foo {
    // This static value belongs to the class Foo
    public static final string name = "Foo";

    // This non-static value will be unique for every instance
    private int value;

    public Foo(int value) {
         this.value = value;
    }

    public void sayValue() {
        println("Instance Value: " + value);
    }

    public static void sayName() {
        println("Static Value: " + name);
    }
}

Foo foo1 = new Foo(10);
Foo foo2 = new Foo(20);

foo1.sayValue(); // Prints "Instance Value: 10" - called on foo1
foo2.sayValue(); // Prints "Instance Value: 20" - called on foo2

Foo.sayName(); // Prints "Static Value: Foo" - called on Foo (not foo1 or foo2)

更新资料

由于前来指出的评论,静态方法能够与非静态数据的工作,但必须明确地传递。假设Foo该类具有另一个方法:

public static Foo Add(Foo foo1, Foo foo2) {
    return new Foo(foo1.value + foo2.value);
}

Add仍然是静态的,没有value自己的实例,但作为类Foo的成员,它可以访问私有value传入的领域foo1foo2实例。在这种情况下,我们使用它来返回带有两个传入值的添加值的 Foo值。

Foo foo3 = Foo.Add(foo1, foo2); // creates a new Foo with a value of 30

30
扩展“没有实例可以从中获取值”-即使存在实例,静态方法也无法知道从哪个实例中获取值。
Steve314 2013年

9
用默认情况下不会强迫所有内容成为对象一部分的语言来解释,这要简单得多。
Mason Wheeler 2013年

3
@梅森真实的话。诸如Java之类的语言提出了一个错误的观念,即功能必须属于某个类。
KChaloux

5
这是一个很好的答案,但仍然无法说明全部事实:静态方法可以访问非静态数据。它们只是没有隐式对象或this-reference可用。我认为了解这一点至关重要。

2
@COMEFROM您的意思是显式传递?如果我对您的理解正确,我可以记录下来。我假设这意味着静态方法可以访问显式传递的非静态数据,因为任何函数都可以处理显式传递给它的数据。
KChaloux 2013年

22

让我们用一个假设的样本来解释它。

想象一个简单的类:

class User
{
User(string n) { name = n; };
string name;
}

现在,我们创建该类的2个实例:

User Bones = new User("Bones");
User Jim = new User("Jim");

现在,考虑-如果我们向User添加新的静态方法,例如:

static string GetName();

然后您将其称为:

string x = User::GetName()

x将包含什么?“吉姆”,“骨头”还是其他?

问题在于静态方法是在类上定义的单个方法,而不是对象。结果,您不知道它可能适用于哪个对象。这就是为什么它很特别的原因。最好将静态方法视为独立的事物,例如C中的函数。Java之类的语言将它们包含在类中主要是Java不允许在类外部存在任何内容的问题,因此必须以某种方式将此类函数强制在类内部(有点像main()被强制为在所有意义上说它应该是一个单一的独立函数时,也可以在类中使用)。


2

非静态数据与该类的实例相关联。静态方法(和数据)不与该类的特定实例关联。不需要类的实例即可在其上使用静态方法。即使有实例,Java也无法保证您在调用静态方法时对期望的实例进行操作。因此,静态方法不能访问非静态数据。


2

它可以使用现场数据;考虑下面的java代码:

class MyBean {
    private String myString;

    static void myStaticMethod() {
        myString = "tada";/*not allowed; if this was possible how would 
                           be different from a field without static?*/

        MyBean myBean = new MyBean();//allowed if associated with an instance
        myBean.myString = "tada";
    }
}

尽管从技术上讲这可能是使用非静态数据的静态方法,但它没有抓住重点。当然,您可以创建一个新实例并对其进行访问。但这与内在毫无关系static
Bobson 2013年

2
实际上,我认为这是对这一点的很好补充。它强调了以下一点:静态方法需要类的实例才能访问非静态数据,同时提供直观的理由。
本·霍金

@Bobson您还应该阅读代码和注释。
m3th0dman

@BenHocking“是”,即使我认为告诉“实例变量始终与对象相关联”也很重要
JAVA

2

我认为这里的问题是一种理解。

从技术角度来看,从对象内部调用的静态方法将非常有能力查看实例字段。我强烈怀疑这是首先导致问题的原因。

问题是方法可以从对象外部调用。那时没有实例数据可以提供它们,因此编译器无法解析代码。由于允许实例数据引起了矛盾,因此我们绝对不能允许实例数据。


我不同意。静态方法无法访问实例数据,因为必须通过对象的实例访问实例数据,并且该静态方法不与任何给定实例关联(但与类定义关联)。
Phill W.

你想念我的意思。 如果从类内部调用它,则编译器可以像非静态类一样传递实例指针。问题是,如果从其他地方调用它,则意味着私有静态方法可以访问实例数据(尽管内部基本忽略了静态数据。)
Loren Pechtel

是的,编译器/ could /但是为什么呢?传递此类指针实际上会将其减少为实例方法。您规定只有私有方法才能做到这一点是没有意义的-反射技术使/ all /方法可以访问-私有与否-使其更具风险。我们在雷德蒙德的朋友们朝另一个方向前进;如果您尝试针对对象实例(而不是类本身)调用静态方法,则它们的语言会发出警告。
Phill W.

1

可以将其视为存在于非面向对象维度中的静态方法。

在“面向对象的维度”中,一个类可以产生多个自我(实例),每个自我通过其状态拥有自己的良心。

在平坦的,非OO维度中,一类人忽略了他们生活在OO维度中的自负。他们的世界是平坦且程序化的,几乎就像尚未发明OOP一样,好像该类是一个小的程序程序,而静态数据只是全局变量。


1

我认为解释此问题的最简单方法是先查看一些代码,然后考虑我们期望代码产生的结果。

// Create three new cars.  Cars have a name attribute.  
Car car1 = new Car("Mazda3");
Car car2 = new Car("FordFocus");
Car car3 = new Car("HondaFit");

// Now we would like to print the names of some cars: 
// First off why don't we try this: 

Car.printCarName();

// Expected behaviour: 
// If we think about what we are trying to do here it doesn't
// really make sense.  What instance of car name should this 
// print?  Should it print Mazda3?  FordFoucs?
// What is the expected behaviour?  If we are going to have a
// static call on car call printCarName it should probably do
// something like print all car names or a random car name or
// throw an error.  


//Now lets try this instead: 

Car.printCarName(car1);

// Expected Behaviour: 
// Luckily the expected behaviour is very clear here.  This
// should print Mazda3.  This works as expected.  


// Finally lets try this: 

car1.printMyName();

// Expected Behaviour:
// Same as previous example, however this is the *right* way
// to do it.  

为了完整起见,这里是汽车类:

public class Car{

    public String name;

    public Car(String name){
        this.name = name;
    }

    public static printCarName(){
        print "Not sure what to do here...  Don't know which car you are talking about.";
    }

    public static printCarName(Car c){
        print c.name;
    }

    public /*NOT static*/ printMyName(){
        print this.name;
    }

}

这如何回答所提问题?
gnat 2013年

1
@gnat已更新,以澄清注释。
sixtyfootersdude 2013年

1

其他答案几乎说明了一切,但是,我想补充一些“细节”。

静态方法(例如Java中的静态方法)只是没有与之关联的隐式对象(可通过访问this),通常可以通过名称直接访问其成员。

这并不意味着他们无法访问非静态数据。

class MyClass {
  public static void foo(MyOtherClass object) {
    System.out.println(object.member);
  }
}
class MyOtherClass { public int member = 10; }

我知道这只是一个细节,但是当我阅读它时,发现您的问题很奇怪。“只能使用静态数据”限制太多。

顺便说一句,我没有测试代码,我只是在这里写它,以例证我在说什么。

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.