什么是"non-static method"错误,"this"如何工作?



我有几个非常基本的Java问题,我想最终一劳永逸地理解。我有以下一小段代码:

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

当我编译时,我收到可怕的"非静态方法swapMe(int,int(无法从静态上下文引用"错误。另外,我得到"a已经在swapMe(int,int(中定义"和"b已经在swapMe(int,int(中定义">

最终需要通过我厚厚的头骨,是"非静态方法"错误,它是如何(为什么(引起的,以及如何避免它。

此外,我假设您可以执行我尝试在"swapMe"方法中使用"a"和"b"变量执行的操作。我想我可以传入"a"和"b",但也可以创建新的变量"a"和"b",并使用"this"关键字引用它们。

我知道这是非常基本的,但这两个"问题"是我对Java的两个主要症结,由于某种原因,似乎无法正确学习。

谢谢大家抽出宝贵时间阅读本文。祝你有美好的一天。

这意味着方法swapMe()是一个实例方法,你需要一个VeryBasicJava类的实例来调用它,如下所示:

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

或者,您可以将swapMe()声明为 static ,这样您就不需要先创建实例:

private static void swapMe(int a, int b)

你只有几个小问题。您在评论中提到您对 C 有一些经验,所以我将尝试进行一些基本的类比。static方法(如main(的行为类似于普通的C函数。但是,非static方法采用一个隐藏参数:this,它指的是该方法要对其进行操作的对象。编写如下方法时:

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

它实际上意味着这样的事情:

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

由于 this 参数是专门处理的,因此有一种特殊的语法可用于在对象上调用非static方法:

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

而且因为swapMe没有被声明为static,所以它希望像上面一样被调用。

VeryBasicJava 类中声明main并不意味着您自动拥有 VeryBasicJava 对象。再次,因为mainstatic,所以它就像一个普通的C函数:

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

为了创建对象的实例,您可以使用new

VeryBasicJava vbj = new VeryBasicJava();

这类似于 C 中的malloc

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

使用实例,您可以调用方法:

vbj.swapMe(spam, eggs);

你的另一个问题是双重的:一个是范围问题,另一个是成员问题。您可能知道,范围是指变量存在的位置。取这个函数:

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: }

调用此方法时,将发生以下情况:

  1. 根据调用 swapMe 中指定的值,将创建ab

  2. 将创建一个新a,该swapMe 的本地 ,默认值为 0 。这个a隐藏了参数a,并且没有办法区分它们。

  3. 将创建一个新b,也是严格本地的。它也具有默认的0值,并隐藏参数b

  4. 创建tmp,并且其值设置为新声明的a的值,因此它也0

  5. [见下文]

  6. [见下文]

  7. 当地人ab不复存在,参数ab也是如此。

在第 5 行和第 6 行中,您尝试使用语法this.a来引用本地a而不是参数。虽然这种语法存在于 Java 中,但它并没有按照你的意思去做。参数的处理方式与局部变量相同,因此不是区分这两个类别,this.a而是区分局部变量和成员。现在,什么是会员?好吧,假设您的类声明包含变量声明:

class VeryBasicJava {
    private int a;
    private int b;
    // ...
}

这就像 C struct中的成员声明:

struct VeryBasicJava {
    int a;
    int b;
};

这意味着当您创建 VeryBasicJava 的实例时:

VeryBasicJava vbj = new VeryBasicJava();

该实例有自己的变量 ab ,可以在任何非static方法中使用:

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

如果具有与成员同名的局部变量,则必须使用 this 显式声明要引用该成员。这个完整的程序说明了如何声明、使用和区分成员和本地人。

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

它将产生以下输出:

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

我希望这些例子和解释对您有所帮助。

你的问题源于不理解面向对象的范式是如何工作的。

这个想法是你定义一个类,这是一个"事物类型"。从该类中,您可以创建实例,这些实例实际上是该类型事物的示例。

例如,当您创建一个类"钱包"时,它定义了您的 系统将处理钱包(即它们能做什么,以及你能做什么( 与他们一起(。您现在可以创建多个不同的实例 钱包,存储不同的价值,但仍然以相同的方式工作。 (例如"我的钱包"和"你的钱包"都是钱包,所以你可以拿 钱出来,把钱放进去等,但如果我把钱签进去 你的钱包,我得到的值是 50,而如果我检查值 我的钱包,我得到的值是20(

现在,">静态"方法属于该类。这意味着它们无法访问特定于类的成员变量。例如,如果我跟踪系统中的每个钱包,则该值不属于该类中的任何特定钱包,但它仍然与该类有关,因此我们将其设置为静态。

使用我的例子,如果你有两个"钱包"类型的对象,和一个 将其"现金"值设置为 30,另一个将其现金值设置为 对于"25",静态函数无法访问这两个值,因此它不能 访问任一。您必须给它一个特定的钱包才能访问。

与 C 的方法相比,此方法的优点是它将相关功能联系在一起。例如,如果您看到一个 C 数组的 int,您并不真正知道它们要表示什么,并且可能会对它们执行一些意外操作。但是,如果我给你一个Java钱包数组,你也会得到影响你与它们交互方式的方法。(这是对封装的一个真正精简的解释(

Java的一个主要问题是它将所有内容都绑定到一个类上。这会导致 main 方法出现问题,该方法必须启动程序,因为它还没有实例,但仍必须是类的一部分。因此,主方法必须始终是静态的。这往往会让新程序员感到困惑,因为您在 main 方法中做的第一件事总是创建它"内部"的类的实例。

在您的示例中,您没有"特定"数据。一切都在方法中,这意味着您实际上是在尝试用 Java 编写过程代码。你可以这样做(只是声明所有内容都是静态的(,但语言会和你作斗争,你会写出非常糟糕的代码。

当我最初开始使用Java时,我有效地将类的main((方法视为不是类的一部分。从概念上讲,它有所帮助,因为您不再将非静态的东西视为已经存在的东西(这是您的主要问题(。但我给你的建议是尝试通读这篇文章。但是有一些非常好的过渡课程可以学习面向对象编程,你可能想投资它们。

类是 OBJECTS 的模板。 类(即和对象(的实例具有自己的非静态变量和方法版本。

静态方法和字段不绑定到类的任何特定实例

因此,从静态方法调用类实例方法没有意义,而没有要在其上调用该方法的实例。 在您的示例中,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

这就是为什么你会遇到一个问题 - 你在参数后声明的a和b与你的参数冲突,而不是"this.a"和"this.b"。

您的第一个问题已经回答了。

由于提供了修复程序,我想补充:

静态方法

只能调用其他静态方法,并且只能直接访问静态变量(在 Java 中(。

若要调用非静态方法,必须创建该类的实例,然后调用这些方法。

swapMe 需要一个 VeryBasicJava 实例来调用它。 静态不会。

从该对象之外,你可以调用:VeryBasicJava.main(args(。

从对象外部,你需要做(new VeryBasicJava(((.swapMe(a,b(来调用该方法。

你在 swapMe 中出现错误的原因是因为你定义了这些变量两次。 签名行在该命名空间中为您声明了它们。 在该方法中,您已经有一个 int a。 然后在第一行,再次声明 int a。

你想在交换中做的是这样的:

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

当然,变量 a 和 b 只存在于该方法中,所以一旦方法结束,你的工作就会丢失。 如果你想保留它,使变量成为类变量,并为它们提供getter和setter。

相关内容

  • 没有找到相关文章

最新更新