W3docs

Методы классов в Java

Добавляйте методы экземпляра в классы Java, чтобы задать поведение объектам и работать с их полями.

Методы класса определяют поведение его объектов. Поля описывают то, чем объект является; методы — то, что он умеет делать. До сих пор вы писали методы static — утилиты, которые принадлежат самому классу. Эта глава посвящена методам экземпляра, которые принадлежат каждому объекту и работают с его полями.

Термин «методы класса» в Java трактуется по-разному. Большинство авторов понимают под ним любые методы, объявленные внутри класса — как статические, так и нестатические. Некоторые используют его строго для обозначения static-методов (поскольку они буквально принадлежат классу). Здесь мы рассмотрим методы экземпляра, а static-методам посвящена отдельная глава — Java static.

Метод экземпляра

Добавьте метод в тело класса без ключевого слова static, и он станет частью каждого объекта:

public class Circle {
  double radius;

  double area() {
    return Math.PI * radius * radius;
  }
}

Теперь каждый созданный объект Circle может вычислить area():

Circle c = new Circle();
c.radius = 5;
System.out.println(c.area());   // 78.539...

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

this — получатель

У каждого метода экземпляра есть невидимый параметр this, указывающий на объект, на котором был вызван метод:

Circle a = new Circle();   a.radius = 2;
Circle b = new Circle();   b.radius = 5;

a.area();    // inside area(), this == a, so this.radius == 2
b.area();    // inside area(), this == b, so this.radius == 5

Один и тот же скомпилированный метод выполняется в обоих случаях; меняется лишь то, на какой объект ссылается this. В главе ключевое слово this подробно рассматривается, когда нужно явно писать this., а когда можно опустить.

Методы, изменяющие состояние

Метод также может изменять поля. Такие методы часто называют мутаторами:

public class Counter {
  int count;

  void increment() {
    count++;            // mutates this.count
  }
  void reset() {
    count = 0;
  }
  int get() {
    return count;
  }
}

Counter c = new Counter();
c.increment(); c.increment(); c.increment();
System.out.println(c.get());   // 3

Изменение через методы — это то, как объекты эволюционируют со временем, не позволяя внешнему коду напрямую трогать их поля. Методы, которые только читают поле и возвращают его значение, — это зеркальное отражение: их обычно называют аксессорами или геттерами (int get() выше — пример такого). Направление чтения и записи через методы, вместо прямого доступа к полям, — это основная идея инкапсуляции: объект сам управляет своими данными.

Вызов других методов изнутри

Методы экземпляра могут вызывать другие методы того же объекта просто по имени:

public class Rectangle {
  double width, height;

  double area()      { return width * height; }
  double perimeter() { return 2 * (width + height); }

  String describe() {
    return "area=" + area() + ", perimeter=" + perimeter();
  }
}

Компилятор превращает area() и perimeter() внутри describe в this.area() и this.perimeter(). Они вызываются на том прямоугольнике, на котором был вызван метод describe.

Static или метод экземпляра — что выбрать?

Эмпирическое правило: зависит ли метод от состояния конкретного объекта?

  • Если да — это метод экземпляра (без static).
  • Если нет — это static.
public class Math2 {
  public static int squared(int n) {       // pure calculation — static
    return n * n;
  }
}

public class Counter {
  int count;
  public void increment() {                // reads/writes this.count — instance
    count++;
  }
}

Частый признак проблемы — метод экземпляра, который полностью игнорирует this. Такой метод — это static, замаскированный под метод экземпляра. В главе Java static разбираются компромиссы.

Для вызова метода экземпляра нужен объект

Поскольку методы экземпляра читают this, вызвать такой метод без объекта невозможно:

Counter.increment();      // ERROR — increment is not static
new Counter().increment(); // fine — increment is called on a fresh Counter (which is then thrown away)

Counter c = new Counter();
c.increment();             // fine — increment is called on c

Это самая частая ошибка компиляции у начинающих: нестатический метод вызывается из статического контекста (как правило, из main, который сам является статическим).

Внимание

Если компилятор сообщает non-static method increment() cannot be referenced from a static context, вы почти наверняка вызвали метод экземпляра без объекта — например, Counter.increment() вместо c.increment(). Сначала создайте объект, затем вызывайте метод на нём.

Перегрузка и переопределение

Методы экземпляра поддерживают оба механизма:

  • Перегрузка: несколько методов с одинаковым именем, но разными списками параметров в одном классе (рассматривается в главе о перегрузке методов).
  • Переопределение: подкласс заменяет унаследованную версию метода (рассматривается в главе о переопределении методов).

С перегрузкой вы уже сталкивались в части 5. Переопределение — одно из главных преимуществ наследования, и мы вернёмся к нему совсем скоро.

Тело метода — это блок

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

Практический пример

Этот пример объединяет в одном классе аксессоры (area, perimeter), мутатор (scale) и метод, вызывающий другие методы (describe). Rectangle объявлен как static class только для того, чтобы весь демо-код уместился в один файл — это ключевое слово относится к вложенному классу, но не к его методам экземпляра, которые по-прежнему выполняются на конкретном прямоугольнике. Обратите внимание: масштабирование r не затрагивает s — каждый объект хранит собственное состояние.

java— editable, runs on the server

Что дальше

До сих пор вы создавали объекты командой new Dog(), а затем заполняли поля построчно. Это многословно, чревато ошибками — и легко что-то забыть. Решение — конструктор, специальный метод, который выполняется при создании объекта. Переходите к главе о конструкторах.

Практика

Практика
В чём разница между статическим методом и методом экземпляра?
В чём разница между статическим методом и методом экземпляра?
Was this page helpful?