简体   繁体   English

什么是“非静态方法”错误以及“this”如何工作?

[英]What is the “non-static method” error and how does “this” work?

I have a couple of EXTREMELY basic Java questions that I would like to finally understand, once and for all. 我有一些非常基本的Java问题,我想一劳永逸地理解这些问题。 I have the following short piece of code: 我有以下简短的代码:

public class VeryBasicJava{
    public static void main(String[] args){
        int x = 3;
        int y = 4;
        swapMe(x, y);
    }
    private void swapMe(int a, int b){
        int a;
        int b;
        int tmp = a;
        this.a = b;
        this.b = a;
    }
}

When I compile, I get the dreaded "non-static method swapMe(int,int) cannot be referenced from a static context" error. 当我编译时,我得到了可怕的“非静态方法swapMe(int,int)无法从静态上下文引用”错误。 Also, I get "a is already defined in swapMe(int,int)" and "b is already defined in swapMe(int,int)" 另外,我得到“a已在swapMe(int,int)中定义”,“b已经在swapMe(int,int)中定义”

What I need to finally get through my thick skull, is the "non-static method" error, how (why) it is caused, and how to avoid it. 我需要最终通过我的厚厚的头骨,是“非静态方法”错误,它是如何(为什么)引起的,以及如何避免它。

Further, I was under the assumption that you could do what I am attempting to do with my 'a' and 'b' variables in the "swapMe" method. 此外,我假设你可以做我试图用'swapMe'方法中的'a'和'b'变量做的事情。 I thought I could pass in an 'a' and 'b', but also create new variables 'a' and 'b', and reference them with "this" keyword. 我以为我可以传入'a'和'b',但也创建新变量'a'和'b',并用“this”关键字引用它们。

I know this is extremely basic, but these two "issues" are two of the main sticking points I have with Java, and for some reason, cannot seem to properly learn. 我知道这是非常基本的,但这两个“问题”是我使用Java的两个主要问题,并且出于某种原因,似乎无法正确学习。

Thank you all, for taking the time to read this. 谢谢大家,花时间阅读本文。 Have a great day. 祝你有美好的一天。

That means that the method swapMe() is an instance method and you need an instance of the VeryBasicJava class to invoke it, like this: 这意味着方法swapMe()是一个实例方法,你需要一个VeryBasicJava类的实例来调用它,如下所示:

VeryBasicJava instance = new VeryBasicJava();
int x = 3; int y = 4;
instance.swapMe(x, y);

Alternatively, you could declare swapMe() as static , in that way you won't need to create an instance first: 或者,您可以将swapMe()声明为static ,这样您就不需要先创建实例:

private static void swapMe(int a, int b)

You have just a few minor problems. 你只有一些小问题。 You mentioned in a comment that you have some experience with C, so I'll try to draw some basic analogies. 您在评论中提到您对C有一些经验,因此我将尝试绘制一些基本的类比。 A static method (such as main ) behaves like an ordinary C function. static方法(例如main )的行为类似于普通的C函数。 A non- static method, however, takes a hidden parameter: this , which refers to an object on which that method is to operate. 但是,非static方法采用隐藏参数: this ,它指的是该方法要在其上运行的对象 When you write a method such as this: 当你编写这样的方法时:

private void swapMe(int a, int b) {
    // ...

It really means something like this: 它真的意味着这样的事情:

private void swapMe(VeryBasicJava this, int a, int b){
    //              ^^^^^^^^^^^^^^^^^^^^
    // ...

Because the this parameter is treated specially, there is a special syntax for calling non- static methods on objects: 由于this参数是专门处理的,因此在对象上调用非static方法有一种特殊的语法:

    myInstance.swapMe(someA, someB);
//  ^^^^^^^^^^        ^^^^^  ^^^^^
//     this             a      b

And because swapMe is not declared static , it expects to be called like the above. 并且因为swapMe未声明为static ,所以它希望像上面那样被调用。

The fact that main is declared inside the VeryBasicJava class does not mean that you automatically have a VeryBasicJava object . mainVeryBasicJava类中声明的事实并不意味着您自动拥有一个VeryBasicJava 对象 Again, because main is static , it is just like an ordinary C function: 同样,因为mainstatic ,它就像一个普通的C函数:

void VeryBasicJava_main(...) {
    // ...

In order to create an instance of an object, you use new : 创建对象的实例,请使用new

VeryBasicJava vbj = new VeryBasicJava();

This is analogous to malloc in C: 这类似于C中的malloc

VeryBasicJava *vbj = malloc(sizeof(VeryBasicJava));
VeryBasicJava_construct(vbj);

With an instance, you can invoke a method: 使用实例,您可以调用方法:

vbj.swapMe(spam, eggs);

Your other issue is twofold: one of scope , and of members . 您的另一个问题有两个: 范围成员之一 Scope, as you may know, refers to where a variable exists. 范围,因为你可能知道,指的一个变量存在。 Take this function: 采取这个功能:

1: private void swapMe(int a, int b) {
2:     int a;
3:     int b;
4:     int tmp = a;
5:     this.a = b;
6:     this.b = a;
7: }

When this method is called, the following things happen: 调用此方法时,会发生以下情况:

  1. a and b are created, given the values specified in the call to swapMe . 在给swapMe调用中指定的值的情况下创建ab

  2. A new a is created, local to swapMe , with the default value 0 . 创建一个新的a ,本地为swapMe ,默认值为0 This a hides the parameter a , and there is no way to differentiate them. a 隐藏的参数a ,有没有办法来区分它们。

  3. A new b is created, also strictly local. 创建一个新的b ,也是严格本地的。 It too has the default 0 value, and hides the parameter b . 它也具有默认的0值,并隐藏参数b

  4. tmp is created, and its value is set to that of the newly declared a , so it too is 0 . 创建了tmp ,其值设置为新声明的a ,因此它也是0

  5. [ see below ] [ 见下文 ]

  6. [ see below ] [ 见下文 ]

  7. The locals a and b cease to exist, as do the parameters a and b . 本地ab不再存在,参数ab

In lines 5 and 6, you attempt to use the syntax this.a to refer to the local a rather than the parameter. 在第5行和第6行中,您尝试使用语法this.a来引用本地a而不是参数。 Though this syntax exists in Java, it does not do what you mean. 虽然这种语法存在于Java中,但它并不是你的意思。 Parameters are treated just the same as local variables, so rather than differentiating between those two categories, this.a differentiates between locals and members . 参数都被视为一样的局部变量,因此不是这两类之间的区别, this.a当地人和成员之间的区别。 Now, what are members? 现在,会员是什么? Well, say your class declaration contains variable declarations: 好吧,说你的类声明包含变量声明:

class VeryBasicJava {

    private int a;
    private int b;

    // ...

}

That's just like member declarations in a C struct : 这就像C struct成员声明一样:

struct VeryBasicJava {
    int a;
    int b;
};

What this means is that when you create an instance of VeryBasicJava : 这意味着当您创建VeryBasicJava的实例时:

VeryBasicJava vbj = new VeryBasicJava();

That instance has its own variables a and b , which can be used in any non- static method: 该实例有自己的变量ab ,可以在任何非static方法中使用:

public void print() {
    System.out.println("a is " + a);
    System.out.println("b is " + b);
}

If you have a local variable with the same name as a member, then you must use this to explicitly state that you want to refer to the member. 如果您有一个与成员同名的局部变量,那么您必须使用this来明确声明您要引用该成员。 This complete program illustrates how to declare, use, and differentiate between members and locals. 这个完整的程序说明了如何声明,使用和区分成员和本地人。

class VeryBasicJava {

    private int a;
    private int b;
    private int c;

    public static void main(String[] args) {
        VeryBasicJava vbj = new VeryBasicJava();
        vbj.a = 3;
        vbj.b = 4;
        vbj.c = 5;
        vbj.print(1);
    }

    public void print(int a) {
        int b = 2;
        System.out.println("a is " + a);
        System.out.println("b is " + b);
        System.out.println("c is " + c);
        System.out.println("this.a is " + this.a);
        System.out.println("this.b is " + this.b);
        System.out.println("this.c is " + this.c);
    }

}

It will produce this output: 它会产生这个输出:

a is 1
b is 2
c is 5
this.a is 3
this.b is 4
this.c is 5

I hope these examples and explanations are helpful. 我希望这些例子和解释都有帮助。

Your problems stem from not understanding how the object-orientated paradigm works. 你的问题源于不理解面向对象范式的运作方式。

The idea is that you define a class, which is a "type of thing". 这个想法是你定义一个类,它是一种“类型的东西”。 From that class, you create instances, which are effectively examples of that type of thing. 从该类中,您可以创建实例,这些实例就是这类事物的有效示例。

For example, when you create a class "Wallet", that defines how your system will treat wallets (ie what they can do, and what you can do with them). 例如,当您创建一个“钱包”类时,它定义了您的系统如何处理钱包(即他们可以做什么,以及您可以用它们做什么)。 You can now create multiple different instances of wallets, that store different values, but still work the same way. 您现在可以创建多个不同的钱包实例 ,这些实例存储不同的值,但仍然以相同的方式工作。 (eg "myWallet" and "yourWallet" are both Wallets, so you can take money out and put money in etc, but if I check the money in yourWallet, I get a value of 50, whereas if I check the value in myWallet, I get a value of 20) (例如“myWallet”和“yourWallet”都是钱包,所以你可以把钱拿出来放钱等等,但是如果我在你的钱包中检查钱,我得到50的值,而如果我检查myWallet中的值,我得到的值为20)

Now, " static " methods belong to the class . 现在,“ 静态 ”方法属于该类 This means that they can't access members variables specific to the class. 这意味着他们无法访问特定于该类的成员变量。 For example, if I'm keeping track of every wallet in my system, that value doesn't belong to any particular wallet in the class, but it's still involved with that class, so we make it static. 例如,如果我正在跟踪系统中的每个钱包,那么该值不属于该类中的任何特定钱包,但它仍然涉及该类,因此我们将其设置为静态。

To use my example, if you have two objects of type "Wallet", and one has its "cash" value set to 30, and the other has its cash value set to "25", a static function can't access both values, so it can't access either. 要使用我的示例,如果您有两个类型为“Wallet”的对象,一个将其“cash”值设置为30,另一个将其现金值设置为“25”,则静态函数无法访问这两个值,所以它也无法访问。 You have to give it a particular wallet to access. 你必须给它一个特定的钱包才能访问。

The advantage of this method over C's approach is that it ties related functionality together. 这种方法优于C的方法的优点是它将相关的功能联系在一起。 For example, if you see a C array of ints, you don't really know what they are intended to represent and may do something unexpected with them. 例如,如果您看到一个C数组的int,您实际上并不知道它们要表示的内容,并且可能会对它们执行意外操作。 But if I give you a Java array of Wallets, you also get the methods that influence how you interact with them. 但是如果我给你一个Java数组的钱包,你也会得到影响你如何与它们互动的方法。 (This is a really stripped down explanation of encapsulation ) (这是封装的真正细分解释)

One of the principle issues with Java is that it ties everything to a class . Java的一个主要问题是它将所有东西都绑定到了一个类 This causes a problem for the main method, which has to start the program, because it has no instances yet, but must still be part of the class. 这会导致main方法出现问题,该方法必须启动程序,因为它还没有实例,但仍必须是类的一部分。 Therefore the main method must always be static. 因此,主要方法必须始终是静态的。 This tends to confuse new programmers, as invariably the first thing you do within the main method is create an instance of the class it's "inside". 这往往会让新程序员感到困惑,因为你在main方法中做的第一件事就是创建一个“内部”类的实例。

In your example, you've got no "specific" data. 在您的示例中,您没有“特定”数据。 Everything's inside the methods, which means you're effectively trying to write procedural code in Java. 方法中的所有内容,这意味着您正在有效地尝试用Java编写过程代码。 You can do this (just declare everything static), but the language will fight you, and you'll write very bad code. 你可以这样做(只是声明一切都是静态的),但语言会对你不利,你会编写非常糟糕的代码。

When I was originally starting out with Java, I effectively treated the main() method of a class as if it wasn't part of the class. 当我最初使用Java时, 我有效地处理了类的main()方法,就好像它不是类的一部分一样。 Conceptually, it helped, because you stop thinking of the non-static stuff as something that's already there (which is your main issue). 从概念上讲,它有所帮助,因为你不再把非静态的东西想象成已经存在的东西(这是你的主要问题)。 But my recommendation to you is try reading through this . 但我给你的建议是试着读一读 But there are some very good transition courses out there for learning object-orientated programming, and you may want to invest in them. 但是有一些非常好的过渡课程用于学习面向对象的编程,你可能想投资它们。

Classes are a template for OBJECTS. 类是OBJECTS的模板。 An instance of a class (ie and object) has its own version of variables and methods that are non-static. 类(即对象)的实例具有自己的变量和非静态方法。

static methods and fields are not tied to any specific instance of a class . 静态方法和字段不依赖于类的任何特定实例

So it makes no sense to invoke a class instance method from a static method, without the instance on which you want to invoke the method. 因此,在没有要调用方法的实例的情况下,从静态方法调用类实例方法是没有意义的。 In your example, this is a reference to an instance, but you have no instance.... 在您的示例中, this是对实例的引用,但您没有实例....

For your second question, when you create a function, it creates its own little stack for all of its variables. 对于第二个问题,当您创建一个函数时,它会为其所有变量创建自己的小堆栈。 Its arguments are included in that stack - so, your stack looks like this: 它的参数包含在该堆栈中 - 因此,您的堆栈如下所示:

    int a; //this is the first argument
    int b; //this is the second argument
    int a; //this is the first line of your function
    int b; //this is the second line of your function

That's why you're getting an issue - the a and b you declare after your arguments are conflicting with your arguments, not "this.a" and "this.b". 这就是为什么你得到一个问题 - 你的论点与你的论点冲突后声明的a和b,而不是“this.a”和“this.b”。

Your first question has already been answered. 您的第一个问题已经得到解答。

As the fix's been provided, I would like to add: 提供修复后,我想补充一下:

static methods can only call other static methods and access only static variables directly (in java). 静态方法只能调用其他静态方法,并且只能直接访问静态变量 (在java中)。

To call non-static methods, you have create an instance of that class and call the methods then. 要调用非静态方法,您必须创建该类的实例,然后调用方法。

swapMe needs an instance of VeryBasicJava to invoke it. swapMe需要一个VeryBasicJava实例来调用它。 Static does not. 静态没有。

From outside that object, you could call: VeryBasicJava.main(args). 从该对象外部,您可以调用:VeryBasicJava.main(args)。

From outside the object, you would need to do (new VeryBasicJava()).swapMe(a,b) to invoke that method. 从对象外部,您需要执行(new VeryBasicJava())。swapMe(a,b)来调用该方法。

The reason you are getting errors inside of swapMe is because you are defining those variables twice. 您在swapMe中获得错误的原因是因为您要定义这些变量两次。 The signature line declared them in that namespace for you. 签名行为您在该命名空间中声明它们。 In that method, you already have an int a. 在那个方法中,你已经有一个int a。 Then on the first line, you declare int a again. 然后在第一行,再次声明int。

What you want to do in your swap is something like this: 你想在交换中做什么是这样的:

 private void swapMe(int a, int b){
    int tmp = a;
    a = b;
    b = tmp;
 }

Of course the variables a and b only exist in that method so as soon as the method ends your work is lost. 当然,变量a和b只存在于该方法中,因此只要方法结​​束,您的工作就会丢失。 If you wanted to keep it, make the variables class variables and provide getters and setters for them. 如果要保留它,请创建变量类变量并为它们提供getter和setter。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM