
오늘의 잔디

오늘의 공부

두 숫자를 입력 받아서 더하고 출력하는 단순한 기능을 개발해보자.
먼저 1 + 2 를 수행하고, 그 다음으로 10 + 20 을 수행할 것이다.
Method1
package method;
public class Method1 {
public static void main(String[] args) {
//계산1
int a = 1;
int b = 2;
System.out.println(a + "+" + b + " 연산 수행");
int sum1 = a + b;
System.out.println("결과1 출력:" + sum1);
//계산2
int x = 10;
int y = 20;
System.out.println(x + "+" + y + " 연산 수행"); int sum2 = x + y;
System.out.println("결과2 출력:" + sum2);
}
}
계산1
int a = 1;
int b = 2;
System.out.println(a + "+" + b + " 연산 수행");
int sum1 = a + b;
계산2
int x = 10;
int y = 20;
System.out.println(x + "+" + y + " 연산 수행");
int sum2 = x + y;
계산1, 계산2 둘 다 변수를 두 개 선언하고, 어떤 연산을 수행하는지 출력하고, 두 변수를 더해서 결과를 구한다.
만약 프로그램의 여러 곳에서 이와 같은 계산을 반복해야 한다면? 같은 코드를 여러번 반복해서 작성해야 할 것이다.
더 나아가서 어떤 연산을 수행하는지 출력하는 부분을 변경하거나 또는 제거하고 싶다면 해당 코드를 다 찾아다니면서
모두 수정해야 할 것이다.
이런 문제를 어떻게 깔끔하게 해결할 수 있을까?
잠깐 아주 간단하게 수학의 함수를 알아보자.

수학 용어가 나왔다고 전혀 어렵게 생각할 것이 없다! 숫자를 2개 입력하면 해당 숫자를 더한 다음에 그 결과를 출력하
는 아주 단순한 함수이다. 이 함수의 이름은 add 이다.
add(a, b) = a + b
add 이고 a , b 라는 두 값을 받는 함수이다. 그리고 이 함수는 a + b 연산을 수행한다.add(1,2) -> 결과:3
add(5,6) -> 결과:11
add(3,5) -> 결과:8
a+b 라는 연산을 수행
만약 두 수의 평균을 구해야 한다면 매번 (a + b) / 2라는 공식을 사용해야 할 것이다.
이것을 함수로 만들어두면 다음과 같이 사용하면 된다.
avg(a, b) = (a + b) / 2
avg(4,6) -> 결과:5
avg(10,20) -> 결과:15
avg(100,200) -> 결과:150
수학의 함수의 개념을 프로그래밍에 가지고 온다면 어떨까? 필요한 기능을 미리 정의해두고 필요할 때 마다 호출해서
사용할 수 있기 때문에 앞서 고민한 문제들을 해결할 수 있을 것 같다.
프로그램 언어들은 오래 전 부터 이런 문제를 해결하기 위해 수학의 함수라는 개념을 차용해서 사용한다.
자바에서는 함수를 메서드(Method)라 한다.
메서드도 함수의 한 종류라고 생각하면 된다. 지금은 둘을 구분하지 않고, 이정도만 알아두자.
메서드를 사용하면 앞서 고민한 문제를 한번에 해결할 수 있다.
메서드에 대한 자세한 설명보다는 우선 메서드를 사용해서 코드를 작성해보자. 참고로 앞에서 작성한 코드와 완전히 동
일하게 작동하는 코드이다.
Method1Ref
package method;
public class Method1Ref {
public static void main(String[] args) {
int sum1 = add(5, 10);
System.out.println("결과1 출력:" + sum1);
int sum2 = add(15, 20);
System.out.println("결과2 출력:" + sum2);
}
//add 메서드
public static int add(int a, int b) {
System.out.println(a + "+" + b + " 연산 수행");
int sum = a + b;
return sum;
}}
실행 결과
5+10 연산 수행
결과1 출력:15
15+20 연산 수행
결과2 출력:35
중복이 제거되고, 코드가 상당히 깔끔해진 것을 느낄 수 있을 것이다.
메서드 정의
public static int add(int a, int b) {
System.out.println(a + "+" + b + " 연산 수행");
int sum = a + b;
return sum;
}
이 부분이 바로 메서드이다. 이것을 함수를 정의하는 것과 같이, 메서드를 정의한다고 표현한다.
메서드는 수학의 함수와 유사하게 생겼다. 함수에 값을 입력하면, 어떤 연산을 처리한 다음에 결과를 반환한다.
(수학에 너무 집중하지는 말자, 단순히 무언가 정의해두고 필요할 때 불러서 사용한다는 개념으로 이해하면 충분하다)
메서드는 크게 메서드 선언과 메서드 본문으로 나눌 수 있다.
public static int add(int a, int b)
메서드의 선언 부분으로, 메서드 이름, 반환 타입, 매개변수(파라미터) 목록을 포함한다.
이름 그대로 이런 메서드가 있다고 선언하는 것이다. 메서드 선언 정보를 통해 다른 곳에서 해당 메서드를 호출할 수 있
다.
public staticpublic : 다른 클래스에서 호출할 수 있는 메서드라는 뜻이다. 접근 제어에서 학습한다.static : 객체를 생성하지 않고 호출할 수 있는 정적 메서드라는 뜻이다. 자세한 내용은 뒤에서 다룬다.int add(int a, int b)int : 반환 타입을 정의한다. 메서드의 실행 결과를 반환할 때 사용할 반환 타입을 지정한다.add : 메서드에 이름을 부여한다. 이 이름으로 메서드를 호출할 수 있다.(int a, int b) : 메서드를 호출할 때 전달하는 입력 값을 정의한다. 이 변수들은 해당 메서드 안에서만 사용된다. 이렇게 메서드 선언에 사용되는 변수를 영어로 파라미터(parameter), 한글로 매개변수라 한다.
System.out.println(a + "+" + b + " 연산 수행");
int sum = a + b;
return sum;
}
return 문을 사용해야 한다. return 문 다음에 반환할 결과를 적어주면 된return sum : sum 변수에 들어있는 값을 반환한다.앞서 정의한 메서드를 호출해서 실행하려면 메서드 이름에 입력 값을 전달하면 된다. 보통 메서드를 호출한다고 표현한
다.
int sum1 = add(5, 10);
int sum2 = add(15, 20);
int sum1 = add(5, 10); //add라는 메서드를 숫자 5,10을 전달하면서 호출한다.
int sum1 = 15; //add(5, 10)이 실행된다. 실행 결과는 반환 값은 15이다.
//sum1에 15 값이 저장된다.
메서드를 호출하면 메서드는 계산을 끝내고 결과를 반환한다. 쉽게 이야기하자면, 메서드 호출이 끝나면 해당 메서드가 반환한 결과 값으로 치환된다.
조금 더 자세히 알아보자. 메서드의 코드는 일부 축약했다.
//1: 메서드 호출
int sum1 = add(5, 10);
//2: 파라미터 변수 a=5, b=10이 전달되면서 메서드가 수행된다.
public static int add(int a=5, int b=10) {
int sum = a + b;
return sum;
}
//3: 메서드가 수행된다.public static int add(int a=5, int b=10) {
int sum = a(5) + b(10);
return sum;
}
//4: return을 사용해서 메서드 실행의 결과인 sum을 반환한다. sum에는 값 15가 들어있으므로 값 15가
반환된다.
public static int add(int a=5, int b=10) {
int sum = 15;
return sum(15);
}
//5: 메서드 호출 결과로 메서드에서 반환한 값 15가 나온다. 이 값을 sum1에 대입했다.
int sum1 = 15;
메서드 호출이 끝나면 더 이상 해당 메서드가 사용한 메모리를 낭비할 이유가 없다. 메서드 호출이 끝나면 메서드 정의
에 사용한 파라미터 변수인 int a , int b 는 물론이고, 그 안에서 정의한 int sum 도 모두 제거된다.
메서드를 호출할 때는 다음과 같이 메서드에 넘기는 값과 매개변수(파라미터)의 타입이 맞아야 한다. 물론 넘기는 값과
매개변수(파라미터)의 순서와 갯수도 맞아야 한다.
호출: call("hello", 20)
메서드 정의: int call(String str, int age)
여기서 "hello" , 20 처럼 넘기는 값을 영어로 Argument(아규먼트), 한글로 인수 또는 인자라 한다.
실무에서는 아규먼트, 인수, 인자라는 용어를 모두 사용한다.
메서드를 정의할 때 선언한 변수인 String str , int age 를 매개변수, 파라미터라 한다.
메서드를 호출할 때 인수를 넘기면, 그 인수가 매개변수에 대입된다.
실무에서는 매개변수, 파라미터 용어를 모두 사용한다.
메서드는 다음과 같이 정의한다.
public static int add(int a, int b) {
//메서드 본문, 실행 코드
}
제어자 반환타입 메서드이름(매개변수 목록) {
메서드 본문
}
public , static 과 같은 부분이다. 제어자는 뒤에서 설명한다. 지금은 항상 public static 키워드를 입력하자.void 를 사용해야 한다. 예) void print(String str)add(){} 사이에 코드를 작성한다.매개변수가 없고, 반환 타입도 없는 메서드를 확인해보자
package method;
public class Method2 {
public static void main(String[] args) {
printHeader();
System.out.println("프로그램이 동작합니다.");
printFooter();
}
public static void printHeader() { System.out.println("= 프로그램을 시작합니다 =");
return; //void의 경우 생략 가능
}
public static void printFooter() {
System.out.println("= 프로그램을 종료합니다 =");
}
}
실행 결과
= 프로그램을 시작합니다 =
프로그램이 동작합니다.
= 프로그램을 종료합니다 =
printHeader() , printFooter() 메서드는 매개변수가 없고, 반환 타입도 없다.
public static void printHeader() 와 같이 매개변수를 비워두고 정의하면 된다.printHeader(); 와 같이 인수를 비워두고 호출하면 된다.public static void printHeader() 와 같이 반환 타입을 void 로 정의하면 된다.printHeader(); 와 같이 반환 타입이 없으므로 메서드만 호출하고 반환 값을 받지 않으면 된다.String str = printHeader(); 반환 타입이 void 이기 때문에 이렇게 반환 값을 받으면 컴모든 메서드는 항상 return 을 호출해야 한다. 그런데 반환 타입 void 의 경우에는 예외로 printFooter() 와 같이 생략해도 된다. 자바가 반환 타입이 없는 경우에는 return 을 마지막줄에 넣어준다. 참고로 return 을 만나면 해당 메서드는 종료된다.
반환 타입이 있는 메서드는 반드시 return 을 사용해서 값을 반환해야 한다.
이 부분은 특히 조건문과 함께 사용할 때 주의해야 한다.
MethodReturn1
package method;
public class MethodReturn1 {
public static void main(String[] args) {
boolean result = odd(2);
System.out.println(result);
}
public static boolean odd(int i) {
if (i % 2 == 1) {
return true;
}
}
}
이 코드에서 if 조건이 만족할 때는 true 가 반환되지만, 조건을 만족하지 않으면 어떻게 될까? 조건을 만족하지 않은 경우에는 return 문이 실행되지 않는다. 따라서 이 코드를 실행하면 return 문을 누락했다는 다음과 같은 컴파일 오류가 발생한다.
컴파일 오류
java: missing return statement
MethodReturn1 - 수정 코드
package method;
public class MethodReturn1 {
public static void main(String[] args) {
boolean result = odd(2);
System.out.println(result);
}
public static boolean odd(int i) {
if (i % 2 == 1) {
return true;
} else {
return false;
} }
}
이렇게 수정하면 if 조건을 만족하지 않아도 else 를 통해 return 문이 실행된다.
return 문을 만나면 그 즉시 해당 메서드를 빠져나간다.
다음 로직을 수행하는 메서드를 만들어보자.
MethodReturn2
package method;
public class MethodReturn2 {
public static void main(String[] args) {
checkAge(10);
checkAge(20);
}
public static void checkAge(int age) {
if (age < 18) {
System.out.println(age + "살, 미성년자는 출입이 불가능합니다.");
return;
}
System.out.println(age + "살, 입장하세요.");
}
}
return 문이 수행된다. 따라서 다음 로직void 형이기 때문에return 은 생략할 수 있다.반환 타입이 있는 메서드를 호출했는데 만약 반환 값이 필요없다면 사용하지 않아도 된다.
예시1: int sum = add(1,2) //반환된 값을 받아서 sum 에 저장했다.
예시2: add(1,2) //반환된 값을 사용하지 않고 버린다. 여기서는 예시1과 같이 호출 결과를 변수에 담지 않았다. 단
순히 메서드만 호출했다.
다음 코드를 보고 어떤 결과가 나올지 먼저 생각해보자.
MethodValue0
package method;
public class MethodValue0 {
public static void main(String[] args) {
int num1 = 5;
int num2 = num1;
num2 = 10;
System.out.println("num1=" + num1);
System.out.println("num2=" + num2);
}
}
실행 결과
num1=5
num2=10
실행 과정
int num2 = num1; //num1의 값은 5이다. num1(5)
int num2 = 5; //num2 변수에 대입하기 전에 num1의 값 5를 읽는다. 결과: num1(5), num2(5)
num2 = 10; // num2에 10을 대입한다. 결과: num1(5), num2(10)
여기서 값을 복사해서 대입한다는 부분이 바로 이 부분이다.
int num2 = num1;
num1 에 있는 값 5 를 복사해서 num2 에 넣는 것이다.num1 의 값을 읽어도 num1 에 있는 기존 값이 유지되고, 새로운 값이 num2 에 들어가기 때문이다. 마치 num1 의 값이 num2 에 복사가 된 것 같다.num1 이라는 변수 자체가 num2 에 들어가는 것이 아니다. num1 에 들어있는 값을 읽고 복사해서 num2num1 에 있는 값을 num2 에 대입한다고 표현한다. 하지만 실제로는 그 값을 복사해서 대입하는너무 당연한 이야기를 왜 이렇게 장황하게 풀어서 하지? 라고 생각한다면 이제 진짜 문제를 만나보자.
다음은 숫자를 2배 곱하는 메서드이다. 다음 코드를 보고 어떤 결과가 나올지 먼저 생각해보자.
MethodValue1
package method;
public class MethodValue1 {
public static void main(String[] args) {
int num1 = 5;
System.out.println("1. changeNumber 호출 전, num1: " + num1);
changeNumber(num1);
System.out.println("4. changeNumber 호출 후, num1: " + num1);
}
public static void changeNumber(int num2) {
System.out.println("2. changeNumber 변경 전, num2: " + num2);
num2 = num2 * 2;
System.out.println("3. changeNumber 변경 후, num2: " + num2);
}
}
실행 결과를 먼저 예측해보고 그 다음에 실행 결과를 확인해보자.
혹시라도 실행 결과의 마지막이 10이라고 생각했다면 대원칙을 떠올려보자
실행 결과
1. changeNumber 호출 전, num1: 5
2. changeNumber 변경 전, num2: 5
3. changeNumber 변경 후, num2: 10
4. changeNumber 호출 후, num1: 5
다음 대원칙을 따라간다면 문제를 정확하게 풀 수 있다.

changeNumber(num1) 호출 시점
num1 의 값 5를 읽고 복사해서 num2 에 전달 -> 이 부분이 핵심
num2 의 변경은 num1 에 영향을 주지 않는다. 왜냐하면 앞서 값을 복사해서 전달했기 때문이다.
실행 과정 코드
changeNumber(num1); //changeNumber를 호출한다. num1(5)
changeNumber(5); //num1의 값을 읽는다.
void changeNumber(int num2=5) //num1의 값 5가 num2에 복사된다. 결과: num1(5), num2(5)
num2 = num2 * 2; //num2에 2를 곱한다. 결과: num1(5), num2(5)
num2 = 5 * 2; //num2의 값을 읽어서 2를 곱한다. 결과: num1(5), num2(5)
num2 = 10; //num2에 계산 결과인 값 10을 대입한다. 결과: num1(5), num2(10)
num2를 출력한다: num2의 값인 10이 출력된다.
num1을 출력한다: num1의 값인 5가 출력된다.
결과적으로 매개변수 num2 의 값만 10으로 변경되고 num1 의 값은 변경되지 않고 기존 값인 5로 유지된다. 자바는 항상 값을 복사해서 전달하기 때문에 num2 의 값을 바꾸더라도 num1 에는 영향을 주지 않는다.
같은 문제를 호출자의 변수 이름과 매개변수의 이름을 같게 해서 한번 더 풀어보자
MethodValue2
package method;
public class MethodValue2 {
public static void main(String[] args) {
int number = 5;
System.out.println("1. changeNumber 호출 전, number: " + number); // 출력: 5
changeNumber(number);
System.out.println("4. changeNumber 호출 후, number: " + number); // 출력:
5
}
public static void changeNumber(int number) {
System.out.println("2. changeNumber 변경 전, number: " + number); // 출력:
5
number = number * 2;
System.out.println("3. changeNumber 변경 후, number: " + number); // 출력:
10
}
}
이번에는 main() 에 정의한 변수와 메서드의 매개변수(파라미터)의 이름이 둘다 number 로 같다.
실행 결과
1. changeNumber 호출 전, number: 5
2. changeNumber 변경 전, number: 5
3. changeNumber 변경 후, number: 10
4. changeNumber 호출 후, number: 5
main() 도 사실은 메서드이다. 각각의 메서드 안에서 사용하는 변수는 서로 완전히 분리된 다른 변수이다. 물론 이름
이 같아도 완전히 다른 변수다. 따라서 main() 의 number 와 changeNumber() 의 number 는 서로 다른 변수이다.
실행 과정
changeNumber(number); //changeNumber를 호출한다. main의number(5)
changeNumber(5); //number의 값을 읽는다.
//main의 number값 5가 changeNumber의 number에 복사된다.
//결과: main의 number(5), changeNumber의 number(5)
void changeNumber(int number=5)
//changeNumber의 number에 값 10을 대입한다.
//결과: main의 number(5), changeNumber의 number(10)
number = number * 2;
main의 number을 출력한다: main의 number의 값인 5가 출력된다.
그렇다면 메서드를 사용해서 값을 변경하려면 어떻게 해야할까?
메서드의 호출 결과를 반환 받아서 사용하면 된다.
MethodValue3
package method;
public class MethodValue3 {
public static void main(String[] args) {
int num1 = 5;
System.out.println("changeNumber 호출 전, num1: " + num1); // 출력: 5
num1 = changeNumber(num1);
System.out.println("changeNumber 호출 후, num1: " + num1); // 출력: 10
}
public static int changeNumber(int num2) {
num2 = num2 * 2;
return num2;
}
}
실행 결과
changeNumber 호출 전, num1: 5
changeNumber 호출 후, num1: 10
실행 과정
num1 = changeNumber(num1); //num1(5)
num1 = changeNumber(5);
//호출 시작:changeNumber()
//num1의 값 5가 num2에 대입된다. num1의 값을 num2에 복사한다. num1(5), num2(5)
int changeNumber(int num2=5)
num2 = num2 * 2; //계산 결과: num1(5), num2(10)
return num2; // num2의 값은 10이다.
return 10;
//호출 끝: changeNumber()
num1 = changeNumber(5);//반환 결과가 10이다.
num1 = 10;//결과: num1(10)
꼭 기억하자!
자바는 항상 변수의 값을 복사해서 대입한다.
(참고로 뒤에서 참조형이라는 것을 학습하는데, 이때도 똑같다. 결국 참조형 변수에 있는 값인 참조값을 복사하는 것이다! 이것은 나중에 알아본다.)