JavaRush /Java Blog /Random-KO /메소드, 매개변수, 상호작용 및 오버로딩

메소드, 매개변수, 상호작용 및 오버로딩

Random-KO 그룹에 게시되었습니다
다시 한번 안녕하세요! 지난 강의에서 우리는 클래스와 생성자에 대해 알아보고, 우리만의 클래스를 만드는 방법을 배웠습니다. 메소드, 매개변수, 상호작용 및 오버로딩 - 1오늘 우리는 메소드와 같은 클래스의 필수적인 부분을 자세히 살펴볼 것입니다. 메소드는 프로그램에서 일부 작업을 수행할 수 있는 명령 집합입니다. 즉, 메소드는 함수입니다. 수업에서 할 수 있는 일. 다른 프로그래밍 언어에서는 메소드를 '함수'라고 부르는 경우가 많지만, 자바에서는 '메서드'라는 단어가 더 많이 사용되었습니다. :) 지난 강의에서 기억하시겠지만 우리는 고양이들이 야옹 울 수 있도록 Cat 클래스에 대한 간단한 메소드를 만들었습니다. 그리고 점프:
public class Cat {

    String name;
    int age;

    public void sayMeow() {
        System.out.println("Meow!");
    }

    public void jump() {
        System.out.println("Jumping gallop!");
    }

    public static void main(String[] args) {
        Cat barsik = new Cat();
        barsik.age = 3;
        barsik.name = "Barsik";

        barsik.sayMeow();
        barsik.jump();
    }
}
sayMeow()jump()우리 수업의 방법입니다 . 작업 결과는 콘솔에 출력됩니다.
Мяу!
Прыг-скок!
우리의 방법은 매우 간단합니다. 단순히 텍스트를 콘솔에 인쇄하기만 하면 됩니다. 그러나 Java에서 메소드에는 주요 작업이 있습니다. 즉, 객체의 데이터에 대해 작업을 수행해야 합니다 . 객체의 데이터 값을 변경하고, 변환하고, 콘솔에 출력하거나, 다른 작업을 수행합니다. 현재 메소드는 객체의 데이터로 아무 작업도 수행하지 않습니다 Cat. 좀 더 명확한 예를 살펴보겠습니다.
public class Truck {

    int length;
    int width;
    int height;
    int weight;

    public int getVolume() {
        int volume = length * width * height;
        return volume;
    }
}
예를 들어 트럭을 나타내는 클래스가 있습니다 Truck. 트럭 트레일러에는 길이, 너비, 높이, 무게가 있습니다(나중에 필요함). 이 방법에서는 getVolume()계산을 수행합니다. 개체의 데이터를 볼륨을 나타내는 숫자로 변환합니다(길이, 너비 및 높이를 곱함). 이것은 메소드의 결과가 될 숫자입니다. 참고 - 방법 설명에 기록되어 있습니다 public int getVolume. 이는 이 메서드의 결과가 형식의 숫자여야 함을 의미int 합니다 . 메소드의 결과를 계산했으므로 이제 메소드를 호출한 프로그램에 결과를 반환 해야 합니다. Java에서 메소드의 결과를 반환하려면 키워드가 사용됩니다 return.
return volume;

메소드 매개변수

메소드는 "메소드 매개변수"라고 불리는 값을 입력으로 받아들일 수 있습니다. 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;
        }
    }
}
이 메소드는 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 number 1! May I pass, officer?");
    boolean canFirstTruckGo = officer.checkTruck(first);
    System.out.println(canFirstTruckGo);

    System.out.println();

    System.out.println("Truck number 2! May I?");
    boolean canSecondTruckGo = officer.checkTruck(second);
    System.out.println(canSecondTruckGo);
}
우리는 10,000과 20,000의 하중을 가진 두 대의 트럭을 만들고 동시에 경찰관이 근무하는 교량의 최대 중량은 15,000입니다. 메소드라는 프로그램은 officer.checkTruck(first)모든 것을 계산하고 결과를 프로그램에 반환합니다. true, 그리고 프로그램은 그것을 변수에 저장했습니다 boolean canFirstTruckGo. 이제 그는 그것으로 원하는 것은 무엇이든 할 수 있습니다(회계사로부터 받은 돈을 사용하는 것처럼). 결국 코드는
boolean canFirstTruckGo = officer.checkTruck(first);
에 내려진다
boolean canFirstTruckGo = true;
매우 중요한 점: 연산자는 return메서드의 결과를 반환할 뿐만 아니라 해당 작업도 종료합니다 ! 반환 이후 작성된 모든 코드는 실행되지 않습니다!
public boolean checkTruck(Truck truck) {

    if (truck.weight > maxWeight) {
        return false;
        System.out.println("Turn around, overweight!");
    } else {
        return true;
        System.out.println("Alright, move on!");
    }
}
해당 메소드가 이미 결과를 반환하고 작업을 완료했기 때문에 경찰관이 말하는 문구는 콘솔에 출력되지 않습니다! 프로그램은 메소드가 호출된 지점으로 돌아갔습니다. 이에 대해 직접 걱정할 필요는 없습니다. Java 컴파일러는 . 이후에 코드를 작성하려고 하면 오류를 던질 만큼 똑똑합니다 return.

어벤져스: 옵션 워

우리 프로그램이 메소드 작동 방식에 대해 몇 가지 옵션을 요구하는 상황이 있습니다. 우리만의 인공지능을 만들어 보는 것은 어떨까요? Amazon에는 Alexa가 있고 Yandex에는 Alice가 있는데 왜 우리는 더 나쁠까요? :) Iron Man에 관한 영화에서 Tony Stark는 자신의 뛰어난 인공 지능을 만들었습니다. JARVIS 멋진 캐릭터에 경의를 표하고 그를 기리기 위해 AI의 이름을 지정합시다 :) 먼저 Jarvis에게 가르쳐야 할 것은 방에 들어오는 사람들에게 인사하는 것입니다(그런 위대한 지성이 무례한 것으로 판명된다면 이상할 것입니다).
public class Jarvis {

    public void sayHi(String name) {
        System.out.println("Good evening, " + name + ", How are you doing?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
    }
}
콘솔 출력:
Добрый вечер, Тони Старк, How ваши дела?
엄청난! 자비스는 들어오는 사람에게 인사하는 방법을 알고 있습니다. 물론 대부분의 경우 소유자는 Tony Stark입니다. 하지만 그는 혼자 오지 않을 수도 있습니다! 그리고 우리의 방법은 sayHi()단 하나의 인수만 입력으로 사용합니다. 따라서 그는 오는 사람들 중 한 사람에게만 인사할 수 있고 다른 사람은 무시할 것입니다. 그다지 예의바르지 않은데요, 동의하시나요? :/ 이 경우 문제를 해결하려면 클래스에 이름은 같지만 매개변수는 다른 2개의 메서드를 간단히 작성할 수 있습니다.
public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Good evening, " + firstGuest + ", How are you doing?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + ", " + secondGuest + ", How are you doing?");
    }
}
이를 메소드 오버로딩 이라고 합니다 . 과부하를 사용하면 프로그램이 더욱 유연해지고 다양한 작업 옵션을 수용할 수 있습니다. 어떻게 작동하는지 확인해 봅시다:
public class Jarvis {

    public void sayHi(String firstGuest) {
        System.out.println("Good evening, " + firstGuest + ", How are you doing?");
    }

    public void sayHi(String firstGuest, String secondGuest) {
        System.out.println("Good evening, " + firstGuest + ", " + secondGuest + ", How are you doing?");
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark");
        jarvis.sayHi("Tony Stark", "Captain America");
    }
}
콘솔 출력:
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Тони Старк, Капитан Америка, How ваши дела?
좋습니다. 두 가지 옵션 모두 효과가 있었습니다. :) 그러나 문제는 해결되지 않았습니다! 손님이 세 명이라면 어떨까요? 물론 sayHi()세 명의 손님 이름을 받아들이도록 메서드를 다시 오버로드할 수 있습니다. 하지만 그 중 4~5개가 있을 수 있으며, 무한정 계속됩니다. 백만 개의 메서드 오버로드 없이 Jarvis에게 원하는 수의 이름을 사용하도록 가르치는 또 다른 방법이 있습니까 sayHi()? :/ 물론이죠! 그렇지 않다면 Java가 세계에서 가장 인기 있는 프로그래밍 언어가 될까요? ;)
public void sayHi(String...names) {

    for (String name: names) {
        System.out.println("Good evening, " + name + ", How are you doing?");
    }
}
매개변수로 전달된 레코드( String...names)를 통해 특정 수의 문자열이 메소드에 전달되었음을 나타낼 수 있습니다. 우리는 얼마나 많은 것이 있어야 하는지 미리 지정하지 않았으므로 이제 우리 방법의 작동이 훨씬 더 유연해졌습니다.
public class Jarvis {

    public void sayHi(String...names) {
        for (String name: names) {
            System.out.println("Good evening, " + name + ", How are you doing?");
        }
    }

    public static void main(String[] args) {
        Jarvis jarvis = new Jarvis();
        jarvis.sayHi("Tony Stark", "Captain America", "Black Widow", "Hulk");
    }
}
콘솔 출력:
Добрый вечер, Тони Старк, How ваши дела?
Добрый вечер, Капитан Америка, How ваши дела?
Добрый вечер, Черная Вдова, How ваши дела?
Добрый вечер, Халк, How ваши дела?
여기에 있는 코드 중 일부는 익숙하지 않지만 신경쓰지 마세요. 그 본질은 간단합니다. 이 방법은 모든 이름을 차례로 살펴보고 각 손님에게 인사합니다! 또한, 전송된 회선 수에 관계없이 작동합니다! 2명, 10명, 심지어 1000명 - 이 방법은 손님 수에 관계없이 안정적으로 작동합니다. 가능한 모든 옵션에 대해 오버로드를 수행하는 것보다 훨씬 편리하다는 점에 동의하지 않나요? :) 또 다른 중요한 점: 인수의 순서가 중요합니다! 우리의 메소드가 문자열과 숫자를 입력으로 사용한다고 가정해 보겠습니다.
public class Man {

    public static void sayYourAge(String greeting, int age) {
        System.out.println(greeting + " " + age);
    }

    public static void main(String[] args) {
        sayYourAge("My age - ", 33);
        sayYourAge(33, "My age - "); //error!
    }
}
sayYourAge클래스 메소드가 문자열과 숫자를 입력으로 사용하는 경우 Man프로그램에서 이러한 순서를 전달해야 합니다! 다른 순서로 전달하면 컴파일러에서 오류가 발생하고 그 사람은 자신의 나이를 알 수 없게 됩니다. 그런데 지난 강의에서 다룬 생성자도 메소드입니다! 또한 오버로드될 수 있으며(다양한 인수 집합을 사용하여 여러 생성자를 생성) 인수 전달 순서도 근본적으로 중요합니다. 실제 방법! :)

그리고 다시 매개 변수에 대해

예, 예, 아직 끝나지 않았습니다. :) 지금 고려할 주제는 매우 중요합니다. 앞으로 면접 때마다 이 질문을 할 확률은 90%입니다! 매개변수를 메소드에 전달하는 방법에 대해 이야기하겠습니다. 간단한 예를 살펴보겠습니다.
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 = 2020;

        System.out.println("What is the year now?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("And now?");
        System.out.println(currentYear);
    }
}
타임머신에는 두 가지 방법이 있습니다. 둘 다 현재 연도를 나타내는 숫자를 입력으로 사용하고 값을 늘리거나 줄입니다(시간을 거슬러 올라가기를 원하는지 아니면 미래로 가고 싶은지에 따라). 그러나 콘솔 출력에서 ​​볼 수 있듯이 이 방법은 작동하지 않았습니다! 콘솔 출력:
Какой сейчас год?
2020
А сейчас?
2020
currentYear메소드에 변수를 전달했지만 goToPast()해당 값은 변경되지 않았습니다. 2020년에도 그랬고, 여전히 그렇습니다. 그런데 왜? :/ Java의 프리미티브는 값으로 메소드에 전달되기 때문입니다. 무슨 뜻이에요? 메소드를 호출 goToPast()하고 거기에 변수를 전달할 때 int currentYear = 2020메소드에 들어가는 것은 변수 자체가 아니라 currentYear변수 의 복사본 입니다 . 물론 이 복사본의 값도 2020과 동일하지만 복사본에 발생하는 모든 변경 사항은 어떤 식으로든 원래 변수에 영향을 미치지 않습니다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!");
        System.out.println("The currentYear value inside the goToPast method (at the beginning) = " + currentYear);
        currentYear = currentYear-10;
        System.out.println("The currentYear value inside the goToPast method (at the end) = " + currentYear);
    }

    public static void main(String[] args) {
        TimeMachine timeMachine = new TimeMachine();
        int currentYear = 2020;

        System.out.println("What is the year at the very beginning of the program?");
        System.out.println(currentYear);

        timeMachine.goToPast(currentYear);
        System.out.println("What year is it now?");
        System.out.println(currentYear);
    }
}
콘솔 출력:
Какой год в самом начале работы программы?
2020
Метод goToPast начал работу!
Значение currentYear внутри метода goToPast (в начале) = 2020
Значение currentYear внутри метода goToPast (в конце) = 2010
А сейчас Howой год?
2020
이는 메소드에 전달된 변수가 goToPast()복사본일 뿐임을 분명히 보여줍니다 currentYear. 그리고 사본을 변경해도 "원본"의 의미에는 영향을 미치지 않습니다. " 참조로 전달 "은 정확히 반대 의미를 갖습니다. 고양이를 대상으로 연습해보자! 고양이를 예로 들어 링크를 전달하는 것이 어떻게 보이는지 살펴보겠습니다. :)
public class Cat {

    int age;

    public Cat(int age) {
        this.age = age;
    }
}
이제 타임머신의 도움으로 세계 최초의 고양이 시간여행자 바르식을 과거와 미래로 보내겠습니다! TimeMachine기계가 객체와 작업할 수 있도록 클래스를 변경해 보겠습니다 Cat.
public class TimeMachine {

    public void goToFuture(Cat cat) {
        cat.age += 10;
    }

    public void goToPast(Cat cat) {
        cat.age -= 10;
    }
}
이제 메소드는 전달된 숫자뿐만 아니라 age특정 객체의 필드도 변경합니다 Cat. 기억하시는 것처럼 기본 요소의 경우에는 성공하지 못했습니다. 원래 숫자는 변경되지 않았습니다. 여기서 무슨 일이 일어나는지 봅시다!
public static void main(String[] args) {

    TimeMachine timeMachine = new TimeMachine();
    Cat barsik = new Cat(5);

    System.out.println("How old is Barsik at the very beginning of the program?");
    System.out.println(barsik.age);

    timeMachine.goToFuture(barsik);
    System.out.println("And now?");
    System.out.println(barsik.age);

    System.out.println("Firs-sticks! Barsik has aged 10 years! Drive back quickly!");
    timeMachine.goToPast(barsik);
    System.out.println("Did it work? Have we returned the cat to its original age?");
    System.out.println(barsik.age);
}
콘솔 출력:
Сколько лет Барсику в самом начале работы программы?
5
А теперь?
15
Елки-палки! Барсик постарел на 10 лет! Живо гони назад!
Получилось? Мы вернули коту его изначальный возраст?
5
우와! 이제 방법이 다르게 작동했습니다. 우리 고양이가 갑자기 늙었다가 다시 젊어 보였습니다! :) 이유를 알아보도록 하겠습니다. 기본 요소의 예와 달리 객체의 경우 객체에 대한 참조가 메서드에 전달됩니다. 원래 객체에 대한 참조가 메소드 goToFuture(barsik)에 전달되었습니다 . 따라서 메소드 내부를 변경할 때 객체가 저장된 바로 그 메모리 영역에 액세스합니다. 이것은 우리가 맨 처음에 만든 것과 동일한 Barsik에 대한 링크입니다. 이것을 "참조에 의한 전달"이라고 합니다! 그러나 이러한 링크를 사용하면 모든 것이 그렇게 간단하지 않습니다. :) 예제를 변경해 보겠습니다. goToPast(barsik)barsikbarsik.age
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 barsik = new Cat(5);

        System.out.println("How old is Barsik at the very beginning of the program?");
        System.out.println(barsik.age);

        timeMachine.goToFuture(barsik);
        System.out.println("Barsik went to the future! Has his age changed?");
        System.out.println(barsik.age);

        System.out.println("And if you try in the past?");
        timeMachine.goToPast(barsik);
        System.out.println(barsik.age);
    }
}
콘솔 출력:
Сколько лет Барсику в самом начале работы программы?
5
Барсик отправился в будущее! Его возраст изменился?
5
А если попробовать в прошлое?
5
다시 작동하지 않습니다! O_O 무슨 일이 일어났는지 알아봅시다 :) 링크가 작동하는 방식과 메커니즘에 관한 모든 것 goToPast입니다 goToFuture. 이제 주목!이 점은 링크와 메소드의 작동 방식을 이해하는 데 가장 중요합니다 . 실제로 메소드를 호출할 때 goToFuture(Cat cat)메소드에 전달되는 것은 객체 참조 자체가 아니라 cat이 참조의 복사본입니다. 즉, 객체를 메소드에 전달할 때 이 객체에 대한 두 개의 참조가 있습니다 . 이것은 무슨 일이 일어나고 있는지 이해하는 데 매우 중요합니다. 결국, 이것이 우리의 마지막 예가 고양이의 나이를 바꾸지 않은 이유입니다. 나이를 변경하는 이전 예에서는 단순히 메소드 내부에 전달된 참조를 가져와 goToFuture()이를 사용하여 메모리에서 객체를 찾은 다음 나이를 변경했습니다( cat.age += 10). 이제 메소드 내부에서 goToFuture()새로운 객체를 생성합니다.
(cat = new Cat(cat.age)),
메소드에 전달된 동일한 복사 링크에 이 객체가 할당됩니다. 결과적으로:
  • 첫 번째 링크( Cat barsik = new Cat(5))는 원래 고양이(5세)를 가리킵니다.
  • cat변수를 메소드에 전달 goToPast(Cat cat)하고 이를 새 객체에 할당한 후 참조가 복사되었습니다.
그 후에는 최종 상황이 발생합니다. 두 개의 링크가 두 개의 서로 다른 개체를 가리킵니다. 그러나 우리는 그 중 단 하나(메서드 내에서 생성한 것)의 나이를 변경했습니다.
cat.age += 10;
main()그리고 당연히 메소드에서 콘솔에 출력하면 barsik.age나이가 변하지 않은 것을 볼 수 있습니다. 결국 barsik이것은 아무 일도 일어나지 않은 5년차의 오래된 원본 개체를 여전히 가리키는 참조 변수입니다. 연령에 따른 모든 조작은 새로운 개체에서 수행되었습니다. 따라서 객체는 참조를 통해 메소드에 전달되는 것으로 나타났습니다 . 객체의 복사본은 자동으로 생성되지 않습니다. cat 개체를 메서드에 전달하고 해당 개체의 수명을 변경하면 성공적으로 변경됩니다. 하지만 메소드를 할당하거나 호출할 때 참조 변수의 값이 복사됩니다 ! 여기에서 프리미티브 전달에 대한 단락을 반복해 보겠습니다. "메서드를 호출 changeInt()하고 거기에 변수를 전달할 때 int x = 15메소드에 들어가는 것은 변수 자체가 아니라 x해당 복사본 입니다 . 결국 복사본에 발생하는 모든 변경 사항은 어떤 식으로든 원래 변수에 영향을 미칩니다 x.” 링크를 복사하면 모든 것이 동일하게 작동합니다! cat 객체를 메소드에 전달합니다. 고양이 자체에 대해(즉, 메모리에 있는 개체를 사용하여) 작업을 수행하면 모든 변경 사항이 성공적으로 진행될 것입니다. 우리에게는 개체가 하나만 있었고 여전히 남아 있습니다. 그러나 메소드 내에서 새 객체를 생성하고 이를 메소드의 매개변수인 참조 변수에 저장하면 이제부터 두 개의 객체와 두 개의 참조 변수가 있게 됩니다. 그게 다야! 여러 번 강의를 해야 할 수도 있을 정도로 쉽지 않았습니다. 하지만 가장 중요한 것은 여러분이 이 매우 중요한 주제를 배웠다는 것입니다. Java에서 인수가 전달되는 방식에 대한 논쟁(숙련된 개발자 사이에서도)을 자주 접하게 됩니다. 이제 당신은 그것이 어떻게 작동하는지 정확히 알았습니다. 계속하세요! :)
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION