[지금 무료] 김영한의 자바 입문 - 코드로 시작하는 자바 첫걸음 강의 | 김영한 - 인프런
김영한 | 프로그래밍에 처음 입문하는 분들을 위한 자바 강의입니다. 코드를 따라하면서 손쉽게 자바를 배울 수 있습니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 자바 입문[사
www.inflearn.com
메서드 시작
계산 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`라는 연산을 출력하낟.
- 여러 번 같은 계산을 해야 한다면 지금처럼 함수를 만들어두고(정의), 필요한 입력 값을 넣어서 해당 함수를 호출하면 된다. 그러면 계산된 결과가 나온다.
- 함수를 호출할 때는 외부에서 필요한 값만 입력하면 된다.
- 같은 함수를 다른 입력 값으로 여러 번 호출할 수 있다.
- 함수를 한 번 정의해두면 계속해서 재사용할 수 있다.
수학의 함수의 개념을 프로그래밍에서 가지고 오면 된다. 필요한 기능을 미리 정의해두고 필요할 때마다 호출해서 사용할 수 있기 때문에 앞서 고민한 문제들을 해결할 수 있다.
메서드 사용
자바에서는 함수를 메서드(Method)라 한다. 메서드도 함수의 한 종류라고 생각하면 된다.
package method;
public class Method1Ref {
public static void main(String[] args) {
//계산1
int sum1 = add(5, 10);
System.out.println("결과1 출력: " + sum1);
//계산2
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;
}
}
중복이 제거되고, 코드가 상당히 깔끔해진 것을 느낄 수 있다.
메서드 정의
public static int add(int a, int b) {
System.out.println(a + "+" + b + " 연산 수행");
int sum = a + b;
return sum;
}
이 부분이 바로 메서드이다. 이것을 함수를 정의하는 것과 같이 메서드를 정의한다고 표현하다.
메서드는 수학의 함수와 유사하게 생겼다. 함수에 값을 입력하면, 어떤 연산을 처리한 다음에 결과를 반환한다.
메서드는 크게 메서드 선언과 메서드 본문으로 나눌 수 있다.
메서드 선언(Method Declaration)
public static int add(int a, int b)
메서드의 선언 부분으로, 메서드 이름, 반환 타입, 매개변수(파라미터) 목록을 포함한다. 메서드 선언 정보를 통해 다른 곳에서 해당 메서드를 호출할 수 있다.
`public static`
- `public`: 다른 클래스에서 호출할 수 있는 메서드라는 뜻
- `static`: 객체를 생성하지 않고 호출할 수 있는 정적 메서드
`int add(int a, int b)`
- `int`: 반환 타입을 정의한다. 메서드의 실행 결화를 반환할 때 사용할 반환 타입을 지정한다.
- `add`: 메서드에 이름을 부여한다. 이 이름으로 메서드를 호출할 수 있다.
- `(int a, int b)`: 메서드를 호출할 때 전달하는 입력 값을 정의한다. 이 변수들은 해당 메서드 안에서만 사용된다. 이렇게 메서드 선언에 사용되는 변수를 영어로 파라미터(parameter), 한글로 매개변수라 한다.
메서드 본문(Method Body)
{
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 a`, `int b`는 물론이고, 그 안에서 정의한 `int sum`도 모두 제거된다.
메서드 호출과 용어정리
메서드를 호출할 때는 다음과 같이 메서드에 넘기는 값과 매개변수(파라미터)의 타입이 맞아야 한다. 물론 넘기는 값과 매개변수(파라미터)의 순서와 갯수도 맞아야 한다.
호출: call("hello", 20)
메서드 정의: int call(String str, int age)
인수(Argument)
여기서 `"hello"`, `20`처럼 넘기는 값을 영어로 Argument, 한글로 인수 또는 인자라고 한다.
매개변수(Parameter)
메서드를 정의할 때 선언한 변수인 `String str`, `int age`를 매개변수, 파라미터라 한다.
메서드를 호출할 때 인수를 넘기면 그 인수가 매개변수에 대입된다.
메서드 정의
public static int add(int a, int b) {
//메서드 본문, 실행 코드
}
제어자 반환타입 메서드이름(매개변수 목록) {
메서드 본문
}
- 제어자(Modifier): `public`, `static`과 같은 부분
- 반환 타입(Return Type): 메서드가 실행된 후 반환하는 데이터 타입을 지정한다. 메서드가 값을 반환하지 않는 경우, 없다는 뜻의 `void`를 사용해야 한다.
- 메서드 이름(Method Name): 메서드의 이름이다. 이 이름은 메서드를 호출하는 데 사용된다.
- 매개변수(Parameter): 입력 값으로, 메서드 내부에서 사용할 수 있는 변수이다. 매개변수는 옵션이다. 입력 값이 필요없는 메서드는 매개변수를 지정하지 않아도 된다.
- 메서드 본문(Method Body): 실제 메서드의 코드가 위치한다. 중괄호 `{}` 사이에 코드를 작성한다.
매개변수가 없거나 반환 타입이 없는 경우
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("= 프로그램을 종료합니다 =");
}
}
- 매개변수 없는 경우
- 선언: `public static void printHeader()`와 같이 매개변수를 비워두고 정의하면 된다.
- 호출: `printHeader();`와 같이 인수를 비워두고 호출하면 된다.
- 반환 타입이 없는 경우
- 선언: `public static void printHeader()`와 같이 반환 타입을 `void`로 정의하면 된다.
- 호출: `printHeader();`와 같이 반환 타입이 없으므로 메서드만 호출하고 반환 값을 받지 않으면 된다.
- `String str = printHeader();`: 반환 타입이 `void`이기 때무넹 이렇게 반환 값을 받으면 컴파일 오류가 발생한다.
void와 return 생략
모든 메서드는 항상 `return`을 호출해야 한다. 그런데 반환 타입이 `void`의 경우에는 예외로 `printFooter()`와 같이 생략해도 된다. 자바가 반환 타입이 없는 경우에는 `return`을 마지막 줄에 넣어준다. 참고로 `return`을 만나면 해당 메서드는 종료된다.
반환 타입
반환 타입이 있으면 반드시 값을 반환해야 한다.
이 부분은 특히 조건문과 함께 사용할 때 주의해야 한다.
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
public static boolean odd(int i) {
if (i % 2 == 1) {
return true;
} else {
return false;
}
}
이렇게 수정하면 `if` 조건을 만족하지 않아도 `else`를 통해 `return`문이 실행된다.
return 문을 만나면 그 즉시 메서드를 빠져나간다.
`return`문을 만나면 그 즉시 해당 메서드를 빠져나간다.
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 + "살, 입장하세요.");
}
}
- 18세 미만의 경우 "미성년자는 출입이 불가능합니다"를 출력하고 바로 `return`문이 수행된다. 따라서 다음 로직을 수행하지 않고, 해당 메서드를 빠져나온다.
- 18세 이상의 경우 "입장하세요"를 출력하고, 메서드가 종료된다. 참고로 반환 타입이 없는 `void`형이기 때문에 마지막 줄의 `return`을 생략할 수 있다.
반환 값 무시
반환 타입이 있는 메서드를 호출했는데 만약 반환 값이 필요없다면 사용하지 않아도 된다.
add(1,2) // 반환된 값을 사용하지 않고 버린다. 단순히 메서드만 호출했다.
메서드 호출과 값 전달
자바는 항상 변수의 값을 복사해서 대입한다.
자바에서 아주 중요한 대원칙으로 반드시 이해해야 한다.
변수와 값 복사
package method;
public class MethodValue0 {
public static void main(String[] args) {
int num1 = 5;
int num2 = num1;
num2 = 10;
System.out.println("num1=" + num1); // 결과: num1=5
System.out.println("num2=" + num2); // 결과: num2=10
}
}
여기에서 값을 복사해서 대입한다는 부분인 바로 이 부분이다.
int num2 = num1;
이 부분은 생각해보면 `num1`에 있는 값 `5`를 복사해서 `num2`에 넣는 것이다.
- 복사한다고 표현한 이유는 `num1`의 값을 읽어도 `num1`에 있는 기존 값이 유지되고, 새로운 값이 `num2`에 들어가기 때문이다. 마치 `num1의 값이 `num2`에 복사가 된 것 같다.
- `num1`이라는 변수 자체가 `num2`에 들어간 것이 아니다. `num1`에 들어있는 값을 읽고 복사해서 `num2`에 넣는 것이다.
메서드 호출과 값 복사
package method;
public class MethodValue1 {
public static void main(String[] args) {
int num1 = 5;
System.out.println("1. changeNumber 호출 전, num1: " + num1); // 결과: 5
changeNumber(num1);
System.out.println("4. changeNumber 호출 후, num1: " + num1); // 결과: 5
}
public static void changeNumber(int num2) {
System.out.println("2. changeNumber 변경 전, num2: " + num2); // 결과: 5
num2 = num2 * 2;
System.out.println("3. changeNumber 변경 후, num2: " + num2); // 결과: 10
}
}
다음 대원칙을 따라간다면 문제를 정확하게 풀 수 있다.
자바는 항상 변수의 값을 복사해서 대입한다.
실행 과정 코드
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`에는 영향을 주지 않는다.
메서드 호출과 이름이 같은 변수
같은 문제를 호출자의 변수 이름과 매개변수의 이름을 같게 해보면 아래와 같다.
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`로 같다.
`main()`도 사실은 메서드이다. 각각의 메서드 안에서 사용하는 변수는 서로 완전히 분리된 변수이다. 물론 이름이 같아도 완전히 다른 변수다. 따라서 `main()`의 `number`와 `changeNumber()`의 `number`는 서로 다른 변수이다.
메서드 호출과 값 반환받기
메서드를 사용해서 값을 변경하려면 메서드의 호출 결과를 반환받아서 사용하면 된다.
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;
}
}
실행 과정
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)
메서드와 형변환
메서드를 호출할 때도 형변환이 적용된다.
명시적 형변환
package method;
public class MethodCasting1 {
public static void main(String[] args) {
double number = 1.5;
//printNumber(number); //double을 int에 대입하므로 컴파일 오류
printNumber((int) number); //명시적 형변환을 사용해 double을 int로 변환
}
public static void printNumber(int n) {
System.out.println("숫자: " + n);
}
}
자동 형변환
`int < long < double`
메서드를 호출할 때 매개변수에 값을 전달하는 것도 결국 변수에 값을 대입하는 것이다. 따라서 자동 형변환이 그대로 적용된다.
package method;
public class MethodCasting2 {
public static void main(String[] args) {
int number = 100;
printNumber(number); // 100.0
}
public static void printNumber(double n) {
System.out.println("숫자: " + n);
}
}
- `double`형 매개변수(파라미터)에 `int`형 인수를 전달하는데 문제없이 잘 작동한다.
메서드를 호출할 때는 전달하는 인수의 타입과 매개변수의 타입이 맞아야 한다. 단 타입이 달라도 자동 형변환이 가능한 경우에는 호출할 수 있다.
메서드 오버로딩
자바는 메서드의 이름 뿐만 아니라 매개변수 정보를 함께 사용하여 메서드를 구분한다. 따라서 다음과 같이 이름이 같고, 매개변수가 다른 메서드를 정의할 수 있다.
add(int a, int b)
add(int a, int b, int c)
add(double a, double b)
이렇게 이름이 같고 매개변수가 다른 메서드를 여러 개 정의하는 것을 메서드 오버로딩(Overloading)이라 한다. 오버로딩은 번역하면 과적인데, 과하게 물건을 담았다는 뜻이다. 따라서 같은 이름의 메서드를 여러 개 정의했다고 이해하면 된다.
오버로딩 규칙
메서드의 이름이 같아도 매개변수의 타입 및 순서가 다르면 오버로딩을 할 수 있다. 참고로 반환 타입은 인정하지 않는다.
다음 케이스는 메서드 이름과 매개변수의 타입이 같으므로 컴파일 오류가 발생한다. 반환 타입은 인정하지 않는다.
int add(int a, int b)
double add(int a, int b)
메서드 시그니처(method signature)
메서드 시그니처 = 메서드 이름 + 매개변수 타입(순서)
메서드 시그니처는 자바에서 메서드를 구분할 수 있는 고유한 식별자나 서명을 뜻한다. 메서드 시그니처는 메서드의 이름과 매개변수 타입(순서 포함)으로 구성되어 있다. 쉽게 이야기해서 메서드를 구분할 수 있는 기준이다. 자바 입장에서는 각각의 메서드를 고유하게 구분할 수 있어야한다. 그래야 어떤 메서드를 호출 할 지 결정할 수 있다.
따라서 메서드 오버로딩에서 설명한 것 처럼 메서드 이름이 같아도 메서드 시그니처가 다르면 다른 메서드로 간주한다. 반환 타입은 시그니처에 포함되지 않는다.
위의 두 메서드는 `add(int a, int b)`로 메서드 시그니처가 같다. 따라서 메서드의 구분이 불가능하므로 컴파일 오류가 발생한다.
메서드 사용 장점
- 코드 재사용: 메서드는 특정 기능을 캡슐화하므로, 필요할 때마다 그 기능을 다시 작성할 필요 없이 해당 메서드를 호출함으로써 코드를 재사용할 수 있다.
- 코드의 가독성: 이름이 부여된 메서드는 코드가 수행하는 작업을 명확하게 나타내므로, 코드를 읽는 사람에게 추가적인 문맥을 제공한다.
- 모듈성: 큰 프로그램을 작은, 관리 가능한 부분으로 나눌 수 있다. 이는 코드의 가독성을 향상시키고 디버깅을 쉽게 만든다.
- 코드 유지 관리: 메서드를 사용하면, 코드의 특정 부분에서 문제가 발생하거나 업데이트가 필요한 경우 해당 메서 드만 수정하면 된다. 이렇게 하면 전체 코드 베이스에 영향을 주지 않고 변경 사항을 적용할 수 있다.
- 재사용성과 확장성: 잘 설계된 메서드는 다른 프로그램이나 프로젝트에서도 재사용할 수 있으며, 새로운 기능을 추가하거나 기존 기능을 확장하는 데 유용하다.
- 추상화: 메서드를 사용하는 곳에서는 메서드의 구현을 몰라도 된다. 프로그램의 다른 부분에서는 복잡한 내부 작 업에 대해 알 필요 없이 메서드를 사용할 수 있다.
- 테스트와 디버깅 용이성: 개별 메서드는 독립적으로 테스트하고 디버그할 수 있다. 이는 코드의 문제를 신속하게 찾고 수정하는 데 도움이 된다.
따라서 메서드는 효율적이고 유지 보수가 가능한 코드를 작성하는 데 매우 중요한 도구이다
'Java' 카테고리의 다른 글
[Java/김영한] 기본형과 참조형 (1) | 2024.09.07 |
---|---|
[Java/김영한] 클래스와 데이터 (0) | 2024.09.06 |
[Java/김영한] 배열 (0) | 2024.08.14 |
[Java/김영한] 스코프, 형변환 (0) | 2024.08.13 |
[Java/김영한] 반복문 (0) | 2024.08.12 |