🔥 🚀 Важно для всех, кто работает с Java! 🔥
На JavaRocks ты найдешь уникальные туториалы, практические задачи и редкие книги, которых не найти в свободном доступе. Присоединяйся к нашему Telegram-каналу JavaRocks — стань частью профессионального сообщества!
В этой статье вы подробнее узнаете о методах Java, которые являются неотъемлемой частью классов. Метод в Java — это набор команд, которые позволяют выполнить определенную операцию в программе. Проще говоря, метод — это функция, то, что может делать ваш класс.
В других языках программирования методы часто называют «функциями», но в Java чаще встречается слово «метод».
К примеру, вот простые методы для класса Cat
, чтобы кошки могли мяукать и прыгать:
public class Cat {
String name;
int age;
public void sayMeow() {
System.out.println("Meow!");
}
public void jump() {
System.out.println("Pounce!");
}
public static void main(String[] args) {
Cat smudge = new Cat();
smudge.age = 3;
smudge.name = "Smudge";
smudge.sayMeow();
smudge.jump();
}
}
Run CodesayMeow()
и jump()
– это методы класса Cat
.
При вызове этих методов в консоли отобразится следующее:
Meow!
Pounce!
Эти методы довольно простые: они просто выводят текст в консоль.
Но в Java методы также выполняют важную задачу: они работают с данными объекта. Они могут изменять данные объекта, преобразовывать и отображать их, а также выполнять другие операции.
Методы sayMeow()
и jump()
ничего не делают с данными объекта Cat
. Рассмотрим более наглядный пример:
public class Truck {
int length;
int width;
int height;
int weight;
public int getVolume() {
int volume = length * width * height;
return volume;
}
}
Run CodeНапример, вот класс, представляющий грузовик.
У грузовика есть длина, ширина, высота и вес (который нам понадобится позже). В методе getVolume()
мы выполняем вычисления, преобразуя данные нашего объекта в число, которое представляет его объем (перемножаем длину, ширину и высоту).
Это число и будет результатом работы метода.
Обратите внимание, что в объявлении метода указано public int getVolume
. Это говорит о том, что метод должен возвращать int
.
Мы вычислили результат метода, и теперь нам нужно вернуть его в программу, которая вызвала этот метод.
Чтобы вернуть результат метода в Java, используется ключевое слово return.
return volume;
Параметры методов в Java
При вызове метода можно передавать ему значения, называемые «аргументами». Объявление метода включает в себя список переменных, который указывает тип и порядок переменных, которые метод может принимать. Этот список называется «параметры метода».
Метод getVolume()
класса Truck
пока не имеет параметров, поэтому попробуем расширить пример с грузовиком.
Создадим новый класс BridgeOfficer
. Это полицейский, который дежурит на мосту и проверяет проезжающие грузовики на предмет превышения допустимого веса.
public class BridgeOfficer {
int maxWeight;
public BridgeOfficer(int normalWeight) {
this.maxWeight = normalWeight;
}
public boolean checkTruck(Truck truck) {
if (truck.weight > maxWeight) {
return false;
} else {
return true;
}
}
}
Run CodeМетод checkTruck
принимает один аргумент, объект Truck
, и определяет, пропустит ли офицер грузовик на мост. Внутри метода логика достаточно проста: если вес грузовика превышает максимально допустимый, то метод возвращает false
. Грузовик должен найти другой путь.
Если же вес меньше или равен допустимому, то он может проехать, и метод возвращает true.
Если всё ещё не до конца понятны фразы «return» или «метод возвращает значение», рассмотрим это на простом примере из жизни.
Представьте: вы приболели и пару дней не ходили на работу. Потом вы приносите в бухгалтерию свой больничный — ведь за него должны заплатить.
Если сравнить это с работой методов, то у бухгалтера есть метод paySickLeave()
. Вы передаёте ему больниный — это аргумент метода (без больничного не будет никаких выплат). Бухгалтер внутри метода делает расчёты, используя принесенную справку, и возвращает результат — деньги.
Программа работает похожим образом: вызывает метод, передаёт ему данные, а потом получает результат. Вот как это выглядит в методе main()
программы BridgeOfficer
:
public static void main(String[] args) {
Truck first = new Truck();
first.weight = 10000;
Truck second = new Truck();
second.weight = 20000;
BridgeOfficer officer = new BridgeOfficer(15000);
System.out.println("Truck 1! Can I go, officer?");
boolean canFirstTruckGo = officer.checkTruck(first);
System.out.println(canFirstTruckGo);
System.out.println();
System.out.println("Truck 2! And can I?");
boolean canSecondTruckGo = officer.checkTruck(second);
System.out.println(canSecondTruckGo);
}
Run CodeСоздаём два грузовика — один с весом 10 000, другой с 20 000. А мост, на котором дежурит офицер, выдерживает максимум 15 000.
Программа вызывает метод officer.checkTruck(first)
. Метод выполняет все необходимые вычисления и возвращает true
. Это значение программа затем сохраняет в булевую переменную canFirstTruckGo
. Теперь можно использовать это значение как угодно (точно так же, как тратить деньги, полученные от бухгалтера).
В конечном итоге, код
boolean canFirstTruckGo = officer.checkTruck(first);
Run Codeсводится к
boolean canFirstTruckGo = true;
Run CodeВажно помнить: оператор return
не просто возвращает результат — он тут же завершает работу метода! Любой код, написанный после оператора return
, не будет выполнен!
public boolean checkTruck(Truck truck) {
if (truck.weight > maxWeight) {
return false;
System.out.println("Turn around, you're overweight!");
} else {
return true;
System.out.println("Everything looks good, go ahead!");
}
}
Run CodeКомментарии офицера не появятся — метод уже отработал и завершился. Программа просто возвращается туда, откуда этот метод вызвали.
Но не нужно следить за этим самостоятельно: компилятор Java предупредит, если вы напишете код после оператора return
.
Мстители: Война параметров
Иногда нужно, чтобы метод вызывался несколькими способами.
Чтобы разобраться, создадим собственный искусственный интеллект, который назовем Джарвисом, в честь знаменитого помощника Тони Старка из фильма “Железный человек”.
Первое, что нам нужно сделать, — научить Джарвиса здороваться с людьми, которые входят в комнату (было бы странно, если бы такой выдающийся гений оказался невежливым).
public class Jarvis {
public void sayHi(String name) {
System.out.println("Good evening, " + name + ". How are you?");
}
public static void main(String[] args) {
Jarvis jarvis = new Jarvis();
jarvis.sayHi("Tony Stark");
}
}
Run CodeЧто увидим в консоли:
Good evening, Tony Stark. How are you?
Отлично! Теперь Джарвис умеет приветствовать гостей. Конечно, чаще всего это будет его хозяин — Тони Старк.
Но что если он придет не один! Наш метод sayHi()
принимает только один аргумент. Поэтому он сможет поприветствовать только одного человека, входящего в комнату, и проигнорирует остальных. Не очень-то вежливо, правда?
Перегрузка методов в Java
Здесь всё просто: мы можем просто написать два метода с одинаковым именем, но разными параметрами — и проблема решена!
public class Jarvis {
public void sayHi(String firstGuest) {
System.out.println("Good evening, " + firstGuest + ". How are you?");
}
public void sayHi(String firstGuest, String secondGuest) {
System.out.println("Good evening, " + firstGuest + " and " + secondGuest + ". How are you?");
}
}
Run CodeЭто и есть перегрузка методов. Она делает программу более гибкой и позволяет использовать разные способы работы. Давайте разберёмся, как это работает:
public class Jarvis {
public void sayHi(String firstGuest) {
System.out.println("Good evening, " + firstGuest + ". How are you?");
}
public void sayHi(String firstGuest, String secondGuest) {
System.out.println("Good evening, " + firstGuest + " and " + secondGuest + ". How are you?");
}
public static void main(String[] args) {
Jarvis jarvis = new Jarvis();
jarvis.sayHi("Tony Stark");
jarvis.sayHi("Tony Stark", "Captain America");
}
}
Run CodeЧто увидим в консоли:
Good evening, Tony Stark. How are you?
Good evening, Tony Stark and Captain America. How are you?
Отлично, оба варианта сработали!
Но проблема по-прежнему не решена. Что, если гостей будет трое? Конечно, мы можем снова перегрузить метод sayHi()
, чтобы он принимал три имени. Но что, если имён будет 4, 5… или бесконечно много?
Есть ли способ сделать метод sayHi()
универсальным, чтобы он работал с любым числом аргументов, без бесконечной перегрузки?
Конечно, есть! Иначе Java вряд ли стала бы самым популярным языком программирования в мире:
public void sayHi(String...names) {
for (String name: names) {
System.out.println("Good evening, " + name + ". How are you?");
}
}
Run CodeИспользование (String… names
) в параметрах метода указывает на то, что он может принимать любое количество строк. Теперь не нужно указывать их точное число, что значительно повышает гибкость метода:
public class Jarvis {
public void sayHi(String...names) {
for (String name: names) {
System.out.println("Good evening, " + name + ". How are you?");
}
}
public static void main(String[] args) {
Jarvis jarvis = new Jarvis();
jarvis.sayHi("Tony Stark", "Captain America", "Black Widow", "Hulk");
}
}
Run CodeВывод в консоль:
Good evening, Tony Stark. How are you?
Good evening, Captain America. How are you?
Good evening, Black Widow. How are you?
Good evening, Hulk. How are you?
Суть метода проста: он перебирает переданные строки и приветствует каждого гостя. Причём количество переданных строк может быть любым — две, десять, тысяча. Это намного удобнее, чем перегружать метод под каждый вариант.
Стоит обратить внимание на важный момент: порядок аргументов имеет значение!
Допустим, метод принимает строку и число:
public class Person {
public static void sayYourAge(String greeting, int age) {
System.out.println(greeting + " " + age);
}
public static void main(String[] args) {
sayYourAge("My age is ", 33);
sayYourAge(33, "My age is ");
}
}
Run CodeЕсли метод sayYourAge
класса Person
принимает строку и число в качестве входных параметров, то передавать их в программу нужно именно в таком порядке! Если поменять порядок аргументов, компилятор выдаст ошибку, и выполнение программы будет прервано.
Кстати, конструкторы тоже являются методами. Их можно перегружать (создавать несколько вариантов с разными параметрами), и для них, как и для обычных методов, порядок аргументов имеет критическое значение.
Ещё раз о параметрах
Давай разберёмся, как передавать аргументы в методы.
Вот простой пример:
public class TimeMachine {
public void goToFuture(int currentYear) {
currentYear = currentYear+10;
}
public void goToPast(int currentYear) {
currentYear = currentYear-10;
}
public static void main(String[] args) {
TimeMachine timeMachine = new TimeMachine();
int currentYear = 2018;
System.out.println("What year is it?");
System.out.println(currentYear);
timeMachine.goToPast(currentYear);
System.out.println("How about now?");
System.out.println(currentYear);
}
}
Run CodeУ машины времени есть два метода. Оба принимают число, которое представляет текущий год, и либо увеличивают его, либо уменьшают (в зависимости от того, хотите ли вы попасть в прошлое или будущее).
Но, как видно из вывода в консоли, метод не работает.
Что увидим в консоли:
What year is it?
2018
How about now?
2018
Мы передали переменную currentYear
в метод goToPast()
, но ее значение не изменилось.
Мы как были в 2018 году, так здесь и остались.
Проблема в том, что примитивные типы в Java передаются по значению.
Что это значит?
Когда мы вызываем метод goToPast()
и передаем ему переменную int
currentYear (=2018)
, метод получает не саму переменную currentYear
, а ее копию.
Её значение тоже 2018, но любые изменения копии не повлияют на переменную currentYear.
Уточним код и посмотрим, что произойдёт с переменной currentYear
:
public class TimeMachine {
public void goToFuture(int currentYear) {
currentYear = currentYear+10;
}
public void goToPast(int currentYear) {
System.out.println("The goToPast method has started running!");
System.out.println("currentYear inside the goToPast method (at the beginning) = " + currentYear);
currentYear = currentYear-10;
System.out.println("currentYear inside the goToPast method (at the end) = " + currentYear);
}
public static void main(String[] args) {
TimeMachine timeMachine = new TimeMachine();
int currentYear = 2018;
System.out.println("What was the year when the program started?");
System.out.println(currentYear);
timeMachine.goToPast(currentYear);
System.out.println("And what year is it now?");
System.out.println(currentYear);
}
}
Run CodeЧто отобразится в консоли:
What was the year when the program started?
2018
The goToPast method has started running!
currentYear inside the goToPast method (at the beginning) = 2018
currentYear inside the goToPast method (at the end) = 2008
And what year is it now?
2018
Это наглядно показывает, что переменная, переданная в метод goToPast()
, — это всего лишь копия currentYear
. Изменение копии не влияет на «оригинальное» значение. “Передача по ссылке” означает прямо противоположное.
Посмотрим, как выглядит передача по ссылке на примере кошки.
public class Cat {
int age;
public Cat(int age) {
this.age = age;
}
}
Run CodeС помощью машины времени отправим Смаджа, первую в мире кошку, способную путешествовать во времени, в прошлое и будущее! Давайте модифицируем класс TimeMachine
, чтобы он мог работать с объектами типа Cat
;
public class TimeMachine {
public void goToFuture(Cat cat) {
cat.age += 10;
}
public void goToPast(Cat cat) {
cat.age -= 10;
}
}
Run CodeТеперь методы не просто изменяют переданное число. Они изменяют поле возраста конкретной кошки.
Это не сработало с примитивами, потому что исходное число не менялось. Давайте посмотрим, что произойдет на этот раз:
public static void main(String[] args) {
TimeMachine timeMachine = new TimeMachine();
Cat smudge = new Cat(5);
System.out.println("How old was Smudge when the program started?");
System.out.println(smudge.age);
timeMachine.goToFuture(smudge);
System.out.println("How about now?");
System.out.println(smudge.age);
System.out.println("Holy smokes! Smudge has aged 10 years! Back up quickly!");
timeMachine.goToPast(smudge);
System.out.println("Did it work? Have we returned the cat to its original age?");
System.out.println(smudge.age);
}
Run CodeВывод в консоли:
How old was Smudge when the program started running?
5
How about now?
15
Holy smokes! Smudge has aged 10 years! Back up quickly!
Did it work? Have we returned the cat to its original age?
5
Теперь метод сделал что-то необычное: кошка сильно постарела, а потом снова помолодела.
Давайте попробуем разобраться, почему так произошло.
В отличие от примера с примитивами, когда объекты передаются в метод, они передаются по ссылке. В метод changeAge()
была передана ссылка на исходный объект smudge
.
Когда мы изменяем smudge.age
внутри метода, мы обращаемся к тому же участку памяти, где хранится наш объект. Это та же самая кошка Smudge
, которая была создана изначально.
Это называется “передачей по ссылке”. Но не всё так просто. Давайте попробуем изменить наш пример:
public class TimeMachine {
public void goToFuture(Cat cat) {
cat = new Cat(cat.age);
cat.age += 10;
}
public void goToPast(Cat cat) {
cat = new Cat(cat.age);
cat.age -= 10;
}
public static void main(String[] args) {
TimeMachine timeMachine = new TimeMachine();
Cat smudge = new Cat(5);
System.out.println("How old was Smudge when the program started?");
System.out.println(smudge.age);
timeMachine.goToFuture(smudge);
System.out.println ("Smudge went to the future! Has his age changed?");
System.out.println(smudge.age);
System.out.println ("And if you try going back?");
timeMachine.goToPast(smudge);
System.out.println(smudge.age);
}
}
Run CodeВывод в консоли:
How old was Smudge when the program started running?
5
Smudge went to the future! Has his age changed?
5
And if you try going back?
5
Разберемся, почему снова ничего не работает.
Это связано с методами goToPast/goToFuture
и тем, как работают ссылки.
Внимание, это самая важная часть для понимания того, как работают ссылки и методы.
Дело в том, что когда мы вызываем метод goToFuture(Cat cat)
, передается не сама ссылка, а её копия.
Таким образом, когда мы передаем объект в метод, получается две ссылки на один и тот же объект.
Это очень важно для того, чтобы понять, что происходит.
Именно поэтому в предыдущем примере возраст кошки не изменился.
Когда мы меняли возраст, мы просто использовали ссылку, переданную в метод goToFuture()
, и меняли возраст кошки (cat.age += 10
).
Но теперь внутри метода goToFuture()
мы создали новый объект (cat = new Cat(cat.age)
), и эта переменная теперь содержит копию ссылки, переданную в метод.
В результате:
Первая ссылка (Cat smudge = new Cat (5)
) указывает на исходный объект (кошку в возрасте 5 лет).
После этого, когда мы передали переменную cat в метод goToPast()
и создали новый объект, ссылка была скопирована.
И вот итог: теперь у нас есть две ссылки, каждая указывает на разные объекты. Однако мы изменили возраст только одного из них (того, который был создан внутри метода).
cat.age += 10;
И конечно, в методе main()
в консоли видно, что возраст кошки, smudge.age
, не изменился. Ведь smudge
— это просто ссылка, которая всё ещё указывает на старый объект с возрастом 5, и мы ничего с этим объектом не делали. Все изменения происходили с новым объектом.
Получается, что объекты передаются в методы по ссылке. Копии объектов не создаются автоматически. Если вы передадите объект кошки в метод и измените его возраст, то возраст действительно изменится.
Но при присваивании значений и/или вызове методов переменные-ссылки тоже копируются.
Вспомним ещё раз о передаче примитивов:
«Когда мы вызываем метод changeInt()
и передаем переменную int x (=15)
, метод получает не саму переменную x
, а её копию. Поэтому любые изменения, сделанные с копией, никак не влияют на исходную переменную x».
При копировании ссылок всё работает точно так же!
Если вы изменяете сам объект (то есть его данные в памяти), все изменения успешно применятся, потому что объект останется один. Но если вы создаете новый объект внутри метода и присваиваете его переменной-ссылке, переданной в метод в качестве аргумента, вы просто присваиваете новый объект копии этой переменной-ссылки. С этого момента будет два объекта и две переменные-ссылки.
Чтобы лучше понять тему, предлагаем вам посмотреть видеоурок по Java (на английском языке):
Перевод статьи «Methods in Java».