7

我有几个非常基本的 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 的两个主要症结,并且由于某种原因,似乎无法正确学习。

谢谢大家,花时间阅读本文。祝你有美好的一天。

4

7 回答 7

11

这意味着该方法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)
于 2012-03-19T17:46:12.417 回答
5

你只有几个小问题。您在评论中提到您对 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,它期望像上面那样被调用。

main在类中声明的事实VeryBasicJava并不意味着您自动拥有一个VeryBasicJava 对象。同样,因为mainis static,它就像一个普通的 C 函数:

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

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

VeryBasicJava vbj = new VeryBasicJava();

这类似于malloc在 C 中:

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

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

vbj.swapMe(spam, eggs);

您的另一个问题是双重的:一个scope和 of members。您可能知道,作用域是指变量存在的位置。采取这个功能:

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. ab被创建,给定在调用中指定的值swapMe

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

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

  4. tmp被创建,并且它的值被设置为新声明的值a,所以它也是0

  5. [见下文]

  6. [见下文]

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

在第 5 行和第 6 行中,您尝试使用语法this.a来引用本地a而不是参数。尽管这种语法存在于 Java 中,但它并不符合您的意思。参数被视为与局部变量相同,因此不是区分这两个类别,而是this.a区分 locals 和members。现在,什么是会员?好吧,假设您的类声明包含变量声明:

class VeryBasicJava {

    private int a;
    private int b;

    // ...

}

这就像 C 中的成员声明struct

struct VeryBasicJava {
    int a;
    int b;
};

这意味着当您创建以下实例时VeryBasicJava

VeryBasicJava vbj = new VeryBasicJava();

该实例有自己的变量aand b,可以在任何非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

我希望这些例子和解释是有帮助的。

于 2012-03-19T20:17:43.920 回答
2

您的问题源于不了解面向对象范式的工作原理。

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

例如,当您创建一个类“Wallet”时,它定义了您的系统将如何处理钱包(即它们可以做什么,以及您可以用它们做什么)。您现在可以创建多个不同的钱包实例,它们存储不同的值,但仍以相同的方式工作。(例如“myWallet”和“yourWallet”都是Wallets,所以你可以取钱和存钱等等,但是如果我检查yourWallet中的钱,我会得到50的价值,而如果我检查myWallet中的价值,我得到的值为20)

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

使用我的示例,如果您有两个“钱包”类型的对象,一个将其“现金”值设置为 30,另一个将其现金值设置为“25”,则静态函数无法访问这两个值,所以它也无法访问。你必须给它一个特定的钱包才能访问。

这种方法相对于 C 方法的优势在于它将相关的功能联系在一起。例如,如果您看到一个 C 整数数组,您并不真正知道它们打算表示什么,并且可能会对它们做一些意想不到的事情。但是,如果我给你一个 Java 钱包数组,你也会得到影响你如何与它们交互的方法。(这是对封装的真正精简解释)

Java 的主要问题之一是它把所有东西都与一个类联系在一起。这会导致 main 方法出现问题,它必须启动程序,因为它还没有实例,但仍然必须是类的一部分。因此,主要方法必须始终是静态的。这往往会使新程序员感到困惑,因为您在 main 方法中所做的第一件事总是创建它“内部”的类的实例。

在您的示例中,您没有“特定”数据。一切都在方法中,这意味着您正在有效地尝试用 Java 编写过程代码。您可以这样做(只需声明所有内容都是静态的),但是语言会与您抗衡,并且您会编写非常糟糕的代码。

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

于 2012-03-19T21:13:11.363 回答
1

由于提供了修复程序,我想添加:

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

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

于 2012-03-19T18:08:46.807 回答
1

对于第二个问题,当您创建一个函数时,它会为其所有变量创建自己的小堆栈。它的参数包含在该堆栈中 - 因此,您的堆栈如下所示:

    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”。

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

于 2012-03-19T17:49:46.013 回答
1

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

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

因此,如果没有要调用该方法的实例,从静态方法调用类实例方法是没有意义的。在您的示例中,this是对实例的引用,但您没有实例....

于 2012-03-19T17:48:08.273 回答
0

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。

于 2012-03-19T17:51:24.810 回答