Что такое ошибка «нестатического метода» и как работает «это»? - PullRequest
7 голосов
/ 19 марта 2012

У меня есть пара ЧРЕЗВЫЧАЙНЫХ основных вопросов по 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)»

Что мне, наконец, нужно пройти через мой толстый череп, так это ошибка «нестатического метода», как (почему) она возникла и как ее избежать.

Кроме того, я предполагал, что вы можете делать то, что я пытаюсь сделать, с моими переменными 'a' и 'b' в методе "swapMe". Я думал, что смогу передать «a» и «b», но также создать новые переменные «a» и «b» и ссылаться на них с ключевым словом «this».

Я знаю, что это чрезвычайно просто, но эти две "проблемы" являются двумя основными препятствиями, с которыми я сталкиваюсь в Java, и по некоторым причинам, похоже, не могут правильно учиться.

Спасибо всем, что нашли время прочитать это. Хорошего дня.

Ответы [ 7 ]

11 голосов
/ 19 марта 2012

Это означает, что метод swapMe() является методом instance , и вам нужен экземпляр класса 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)
5 голосов
/ 20 марта 2012

У вас всего несколько мелких проблем.Вы упомянули в комментарии, что у вас есть некоторый опыт работы с 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 объект .Опять же, поскольку main равно 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 и 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. a и b создаются с учетом указанных значенийпри вызове swapMe.

  2. Создается новый a, локальный для swapMe, со значением по умолчанию 0.Этот a скрывает параметр a, и их невозможно дифференцировать.

  3. Создается новый b, также строго локальный.Он также имеет значение по умолчанию 0 и скрывает параметр b.

  4. tmp, а его значение равно значению недавно объявленного aтак что это тоже 0.

  5. [ см. ниже ]

  6. [ см. ниже ]

  7. Местные жители 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

Я надеюсь, что эти примеры и пояснения полезны.

2 голосов
/ 20 марта 2012

Ваши проблемы проистекают из непонимания того, как работает объектно-ориентированная парадигма.

Идея состоит в том, что вы определяете класс, который является "типом вещи". Из этого класса вы создаете экземпляры, которые фактически являются примерами вещей такого типа.

Например, когда вы создаете класс "Кошелек", который определяет, как ваш система будет обрабатывать кошельки (то есть что они могут делать и что вы можете делать с ними). Теперь вы можете создать несколько различных экземпляров из кошельки, которые хранят разные значения, но работают одинаково. (например, «myWallet» и «yourWallet» оба являются кошельками, так что вы можете взять деньги и положить деньги в и т. д., но если я проверю деньги в yourWallet, я получаю значение 50, тогда как если я проверю значение в myWallet, я получаю значение 20)

Теперь методы " static " относятся к классу . Это означает, что они не могут получить доступ к переменным, относящимся к классу. Например, если я отслеживаю каждый кошелек в моей системе, это значение не принадлежит ни одному конкретному кошельку в классе, но оно все еще связано с этим классом, поэтому мы делаем его статичным.

Если использовать мой пример, если у вас есть два объекта типа "Кошелек" и один имеет «денежное» значение, установленное на 30, а другое имеет денежное значение до «25», статическая функция не может получить доступ к обоим значениям, поэтому она не может доступ либо. Вы должны дать ему определенный кошелек для доступа.

Преимущество этого метода перед подходом C состоит в том, что он связывает связанные функциональные возможности вместе. Например, если вы видите массив целых чисел C, вы действительно не знаете, что они должны представлять, и можете сделать с ними что-то неожиданное. Но если я дам вам массив Java-кошельков, вы также получите методы, которые влияют на то, как вы взаимодействуете с ними. (Это действительно упрощенное объяснение инкапсуляции )

Одна из основных проблем с Java заключается в том, что он все связывает с классом . Это вызывает проблему для метода main, который должен запустить программу, потому что у него еще нет экземпляров, но он все еще должен быть частью класса. Поэтому основной метод всегда должен быть статическим. Это может сбить с толку новых программистов, поскольку первое, что вы делаете в методе main, - это создание экземпляра класса, который находится «внутри».

В вашем примере у вас нет "конкретных" данных. Все внутри методов, что означает, что вы фактически пытаетесь написать процедурный код на Java. Вы можете сделать это (просто объявите все статично), но язык будет бороться с вами, и вы будете писать очень плохой код.

Когда я только начинал с Java, я фактически рассматривал метод main () класса, как если бы он не был частью класса. Концептуально это помогло, потому что вы перестали думать о Нестатичные вещи, как то, что уже есть (это ваша основная проблема). Но я рекомендую вам , попробуйте прочитать это . Но есть несколько очень хороших переходных курсов для изучения объектно-ориентированного программирования, и вы можете захотеть в них инвестировать.

1 голос
/ 19 марта 2012

Поскольку исправление было предоставлено, я хотел бы добавить:

статические методы могут вызывать только другие статические методы и напрямую обращаться только к статическим переменным (в java).

Чтобы вызвать нестатические методы, вы должны создать экземпляр этого класса и затем вызвать методы.

1 голос
/ 19 марта 2012

Ваш второй вопрос: когда вы создаете функцию, она создает свой собственный небольшой стек для всех своих переменных.Его аргументы включены в этот стек, поэтому ваш стек выглядит следующим образом:

    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".

На ваш первый вопрос уже дан ответ.

1 голос
/ 19 марта 2012

Классы - это шаблон для ОБЪЕКТОВ.Экземпляр класса (то есть и объект) имеет свою собственную версию переменных и методов, которые не являются статичными.

статические методы и поля не привязаны ни к какому конкретному экземпляру класса .

Так что нет смысла вызывать метод экземпляра класса из статического метода, безэкземпляр, в котором вы хотите вызвать метод.В вашем примере this является ссылкой на экземпляр, но у вас нет экземпляра ....

0 голосов
/ 19 марта 2012

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 существуют только в этом методе, поэтому, как только метод завершится, ваша работа будет потеряна. Если вы хотите сохранить его, сделайте переменные класса переменных и предоставьте для них методы получения и установки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...