我有几个非常基本的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
对象。再次,因为main
是static
,所以它就像一个普通的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: }
调用此方法时,将发生以下情况:
根据调用
swapMe
中指定的值,将创建a
和b
。将创建一个新
a
,该swapMe
的本地 ,默认值为0
。这个a
隐藏了参数a
,并且没有办法区分它们。将创建一个新
b
,也是严格本地的。它也具有默认的0
值,并隐藏参数b
。创建
tmp
,并且其值设置为新声明的a
的值,因此它也0
。[见下文]
[见下文]
当地人
a
,b
不复存在,参数a
和b
也是如此。
在第 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();
该实例有自己的变量 a
和 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
我希望这些例子和解释对您有所帮助。
你的问题源于不理解面向对象的范式是如何工作的。
这个想法是你定义一个类,这是一个"事物类型"。从该类中,您可以创建实例,这些实例实际上是该类型事物的示例。
例如,当您创建一个类"钱包"时,它定义了您的 系统将处理钱包(即它们能做什么,以及你能做什么( 与他们一起(。您现在可以创建多个不同的实例 钱包,存储不同的价值,但仍然以相同的方式工作。 (例如"我的钱包"和"你的钱包"都是钱包,所以你可以拿 钱出来,把钱放进去等,但如果我把钱签进去 你的钱包,我得到的值是 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。