📖 자바의 정석 Chapter 06 참고
4. 오버로딩(overloading)
4.1 오버로딩이란?
- 오버로딩(overloading): 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것. (=메서드 오버로딩(method overloading))
- 오버로딩(overloading)의 사전적 의미는 '과적하다'. 즉, 많이 싣는 것을 뜻한다.
4.2 오버로딩의 조건
1. 메서드 이름이 같아야 한다.
2. 매개변수의 개수 또는 타입이 달라야 한다.
- 오버로딩된 메서드들은 매개변수에 의해서만 구별될 수 있다.
- 반환 타입은 오버로딩을 구현하는데 아무런 영향을 주지 못한다.
4.3 오버로딩의 예
// 오버로딩 X
// 두 메서드는 매개변수의 이름만 다를 뿐 매개변수의 타입이 같기 때문에 오버로딩이 성립하지 않는다.
int add(int a, int b) { return a+b; }
int add(int x, int y) { return x+y; }
// 오버로딩 X
// 리턴타입만 다른 경우. 매개변수의 타입과 개수가 일치하기 때문에 오버로딩으로 간주되지 않는다.
int add(int a, int b) { return a+b; }
long add(int a, int b) { return (long)(a + b); }
// 오버로딩 O
// 두 메서드 모두 int형과 long형 매개변수가 하나씩 선언되어 있지만, 서로 순서가 다른 경우.
// 매개변수의 값에 의해 호출될 메서드를 구분할 수 있으므로 오버로딩으로 간주한다.
// 매개변수의 순서만을 다르게 하여 오버로딩을 구현하면 오히려 단점이 될 수도 있다.
long add(int a, long b) { return a+b; }
long add(long a, int b) { return a+b; }
// 오버로딩 O
// 위 메서드들은 모두 바르게 오버로딩 되었다.
// 같은 일을 하지만 매개변수를 달리해야하는 경우. 이와 같이 이름은 같고 매개변수를 다르게 하여 오버로딩을 구현한다.
int add(int a, int b) { return a+b; }
long add(long a, long b) { return a+b; }
long add(int[] a) {
long result = 0;
for(int i = 0; i < a.length; i++) {
result += a[i];
}
return result;
}
4.4 오버로딩의 장점
오버로딩의 예시
e.g. PrintStream 클래스에는 10개의 오버로딩된 println 메서드를 정의해놓고 있다.
println 메서드를 호출할 때 매개변수로 지정하는 값의 타입에 따라서 호출되는 println메서드가 달라진다.
오버로딩의 장점
오버로딩을 통해 여러 메서드들이 println이라는 하나의 이름으로 정의될 수 있다면,
1. 이름을 기억하기 쉽고 이름을 짧게 쓸 수 있어서 오류의 가능성을 많이 줄일 수 있다.
그리고 같은 이름의 메서드들을 보고 같은 기능을 할 것이라 쉽게 예측할 수 있다.
2. 메서드 이름을 절약할 수 있다. 메서드 이름을 짓는데 고민을 덜 수 있고, 사용되었어야 할 메서드 이름을 다른 메서드 이름으로 사용할 수 있다.
4.5 가변인자(varargs)와 오버로딩
// Object... args <-가변인자는 항상 마지막 매개변수이어야 한다.
// 가변인자 외에도 매개변수가 더 있다면, 가변인자를 매개변수 중에서 제일 마지막에 선언해야 한다.
// 그렇지 않으면 컴파일 에러가 발생한다.
public PrintStream printf(String format, Object... args) {
...
}
- 가변인자(variable arguments): 메서드의 매개변수 개수를 동적으로 지정하는 기능.
- 가변인자는 '타입... 변수명'과 같은 형식으로 선언하며, PrintStream클래스의 printf()가 대표적인 예이다.
- 가변인자를 선언한 메서드를 오버로딩하면, 메서드를 호출했을 때 구별되지 못하는 경우가 발생하기 쉽기 때문에 가능하면 가변인자를 사용한 메서드는 오버로딩 하지 않는 것이 좋다.
// 여러 문자열을 하나로 결합하여 반환하는 메서드. (오버로딩)
// 매개변수 개수를 다르게 해서 여러 개의 메서드 작성해야 한다.
String concatenate(String s1, String s2) { ... }
String concatenate(String s1, String s2, String s3) { ... }
String concatenate(String s1, String s2, String s3, String s4) { ... }
// 이 때, 가변인자를 사용하면 메서드 하나로 간단히 대체할 수 있다.
// 가변인자로 매개변수를 선언하면 문자열을 개수의 제약없이 매개변수로 지정할 수 있다.
String concatenate(String... str) { ... }
// 이 메서드를 호출할 때는 인자의 개수를 가변적으로 할 수 있다.
// 가변인자는 내부적으로 배열을 이용한다. 선언된 메서드를 호출할 때미다 배열이 새로 생성된다.
System.out.println(concatenate()); // 인자가 없음
System.out.println(concatenate("a")); // 인자가 하나
System.out.println(concatenate("a", "b")); // 인자가 둘
System.out.println(concatenate(new String[]{"A", "B"})); // 배열도 가능
// 매개변수 타입이 배열
String concatenate(String[] str) { ... }
// 매개변수의 타입을 배열로 하면, 반드시 인자를 지정해줘야 하기 때문에 가변인자처럼 인자를 생략할 수 없다.
// 그래서 null이나 길이가 0인 배열을 인자로 지정해줘야 하는 불편함이 있다.
String result = concatenate(new String[0]); // 인자로 배열을 지정
String result = concatenate(null); // 인자로 null을 지정
String result = concatenate(); // 에러. 인자가 필요함.
5. 생성자(Constructor)
5.1 생성자란?
- 생성자: 인스턴스가 생성될 때 호출되는 '인스턴스 초기화 메서드'.
- 인스턴스변수의 초기화 작업에 주로 사용되며, 인스턴스 생성 시에 실행되어야 할 작업을 위해서도 사용된다.
- 인스턴스 초기화: 인스턴스변수들을 초기화하는 것을 뜻한다.
- 메서드처럼 클래스 내에 선언되며, 메서드와 유사하지만 리턴값이 없다.
- 생성자 앞에 리턴값이 없음을 뜻하는 키워드 void를 사용하지는 않고, 단지 아무 것도 적지 않는다.
- 생성자도 오버라이딩이 가능하므로 하나의 클래스에 여러 개의 생성자가 존재할 수 있다.
생성자의 조건
1. 생성자의 이름은 클래스의 이름과 같아야 한다.
2. 생성자는 리턴 값이 없다.
생성자의 정의
클래스이름(타입 변수명, 타입 변수명, ... ) {
// 인스턴스 생성시 수행될 코드
// 주로 인스턴스 변수의 초기화 코드를 적는다.
}
// Card클래스의 인스턴스를 생성하는 코드
Card c = new Card();
1. 연산자 new에 의해서 메모리(heap)에 Card클래스의 인스턴스가 생성된다.
2. 생성자 Card()가 호출되어 수행된다.
3. 연산자 new의 결과로, 생성된 Card인스턴스의 주소가 반환되어 참조변수 c에 저장된다.
- 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것이 아니다.
- new 다음 오는 '클래스이름()'이 바로 생성자이다.
- 인스턴스를 생성할 때는 반드시 클래스 내에 정의된 생성자 중 하나를 선택하여 지정해주어야 한다.
5.2 기본 생성자(default constructor)
- 클래스이름() { } 의 형태를 가진다.
- 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다..
- 컴파일러가 제공하는 '기본 생성자(default constructor)' 덕분에 클래스에 생성자를 정의하지 않고도 인스턴스를 생성할 수 있었다.
- 기본 생성자가 컴파일러에 의해서 자동적으로 추가되는 경우는 클래스에 정의된 생성자가 하나도 없을 때 뿐이다.
5.3 매개변수가 있는 생성자
class Car {
String color;
String gearType;
int door;
Car() {} // 기본 생성자
Car(String c, String g, int d) { // 매개변수 있는 생성자
color = c;
gearType = g;
door = d;
}
}
class CarTest {
public static void main(String[] args) {
// Car인스턴스를 생성할 때, 생성자 Car()를 사용한다면,
// 인스턴스를 생성한 다음에 인스턴스변수들을 따로 초기화해줘야 한다.
Car c1 = new Car();
c1.color = "white";
c1.gearType = "auto";
c1.door = 4;
// 매개변수가 있는 생성자를 사용한다면
// 인스턴스를 생성하는 동시에 원하는 값으로 초기화를 할 수 있다.
// 이 코드가 더 간결하다. 더 바람직한 코드.
Car c2 = new Car("white", "auto", 4);
}
}
5.4 생성자에서 다른 생성자 호출하기 - this(), this
생성자를 작성할 때 지켜야하는 두 조건 (생성자 간에 서로 호출하기 위해)
- 생성자의 이름으로 클래스이름 대신 this를 사용한다.
- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능하다.
class Car {
String color;
String gearType;
int door;
Car() {
// 생성자 Car()에서 또 다른 생성자 Car(String color, String gearType, int door)를 호출
// 생성자 간의 호출에는 생성자 이름 대신 this를 사용해야 한다.
// 생성자 Car()의 첫째 줄에서 호출했다.
this("white", "auto", 4);
}
Car(String color) {
this(color, "auto", 4);
}
// color은 생성자의 매개변수로 정의된 지역변수
Car(String color, String gearType, int door) {
// this.color는 인스턴스변수
// this는 참조변수로 인스턴스 자신을 가리킨다.'this'로 인스턴스 변수에 접근할 수 있다.
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
class CarTest2 {
public static void main(String[] args) {
Car c1 = new Car();
Car c2 = new Car("red");
}
}
- 'this'를 사용할 수 있는 것은 인스턴스멤버뿐이다. static메서드는 인스턴스를 생성하지 않고도 호출될 수 있으므로 static메서드가 호출된 시점에 인스턴스가 존재하지 않을 수도 있다.
- 생성자를 포함한 모든 인스턴스메서드에는 자신이 관련된 인스턴스를 가리키는 참조변수 'this'가 지역변수로 숨겨진 채로 존재한다.
this: 인스턴스 자신을 가리키는 참조변수. 인스턴스의 주소가 저장되어 있다.
this(), this(매개변수): 생성자. 같은 클래스의 다른 생성자를 호출할 때 사용한다.
5.5 생성자를 이용한 인스턴스의 복사
class Car {
String color;
String gearType;
int door;
Car() {
this("white", "auto", 4);
}
Car(Car c) { // 인스턴스의 복사를 위한 생성자.
// color = c.color;
// gearTeyp = c.gearType;
// door = c.door;
// Car(String color, String gearType, int door)
// 생성자 'Car(Car c)'는 다른 생성자인
// 'Car(String color, String gearType, int door)'를 호출하는것이 바람직하다.
this(c.color, c.gearType, c.door);
}
Car(String color, String gearType, int door) {
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
class CarTest3 {
public static void main(String[] args) {
Car c1 = new Car();
Car c2 = new Car(c1); // c1의 복사본 c2를 생성했다.
}
}
- 인스턴스 c2는 c1을 복사하여 생성된 것이므로 서로 같은 상태를 갖지만, 서로 독립적으로 메모리공간에 존재하는 별도의 인스턴스이므로 c1의 값들이 변경되어도 c2는 영향을 받지 않는다.
6. 변수의 초기화
6.1 변수의 초기화
- 변수의 초기화: 변수를 선언하고 처음으로 값을 저장하는 것
- 멤버변수는 초기화 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어진다.
- 지역변수는 사용하기 전에 반드시 초기화해야 한다.
멤버변수(클래스변수와 인스턴스변수)와 배열의 초기화는 선택적이지만,
지역변수의 초기화는 필수적이다.
멤버변수의 초기화 방법
1. 명시적 초기화(explicit initialization)
2. 생성자(constructor)
3. 초기화 블럭(initialization block)
- 인스턴스 초기화 블럭: 인스턴스변수를 초기화 하는데 사용
- 클래스 초기화 블럭: 클래스변수를 초기화 하는데 사용
6.2 명시적 초기화(explicit initialization)
- 명시적 초기화: 변수를 선언과 동시에 초기화하는 것. 여러 초기화 방법 중에서 가장 우선적으로 고려되어야 한다.
- 보다 복잡한 초기화 작업이 필요할 때는 '초기화 블럭(initialization block)' 또는 생성자를 사용해야 한다.
class Car {
int door = 4; // 기본형(primitive type) 변수의 초기화
Engine e = new Engine(); // 참조형(reference type) 변수의 초기화
//...
}
6.3 초기화 블럭(initialization block)
- 클래스 초기화 블럭: 클래스변수의 복잡한 초기화에 사용된다.
- 인스턴스 초기화 블럭: 인스턴스변수의 복잡한 초기화에 사용된다.
class InitBlock {
static { /* 클래스 초기화블럭입니다 */ }
{ /* 인스턴스 초기화블럭입니다. */ }
// ...
}
- 초기화 작업이 복잡하여 명시적 초기화만으로는 부족한 경우 초기화 블럭을 사용한다.
- 클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때 한번만 수행되며, 인스턴스 초기화 블럭은 생성자와 같이 인스턴스를 생성할 때마다 수행된다.
- 생성자보다 인스턴스 초기화 블럭이 먼저 수행된다.
- 실행순서: 클래스 초기화 블럭 -> main메서드 -> 인스턴스 초기화 블럭 -> 생성자
- 인스턴스 변수의 초기화는 주로 생성자를 사용하고, 인스턴스 초기화 블럭은 모든 생성자에서 공통으로 수행돼야 하는 코드를 넣는데 사용한다.
- 클래스의 모든 생성자에 공통으로 수행돼야 하는 문장들이 있을 때, 이 문장들을 각 생성자마다 써주기보다는 인스턴스 블럭에 넣어 코드를 간결하게 만들 수 있다.
- 배열이나 예외처리가 필요한 초기화에서는 추가적으로 클래스 초기화 블럭을 사용하도록 한다.
- 인스턴스 변수의 복잡한 초기화는 생성자 또는 인스턴스 초기화 블럭을 사용한다.
6.4 멤버변수의 초기화 시기와 순서
클래스변수의 초기화시점: 클래스가 처음 로딩될 때 단 한번 초기화 된다.
인스턴스변수의 초기화시점: 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어진다.
클래스변수의 초기화순서: 기본값 -> 명시적초기화 -> 클래스 초기화 블럭
인스턴스변수의 초기화순서: 기본값 -> 명시적초기화 -> 인스턴스 초기화 블럭 -> 생성자
'Backend > Java' 카테고리의 다른 글
[Java] 객체지향 프로그래밍 2(Object-oriented Programming 2) - TIL 221220 (0) | 2022.12.20 |
---|---|
[Java] 객체지향 프로그래밍 2(Object-oriented Programming 2) - TIL 221219 (0) | 2022.12.19 |
[Java] 객체지향 프로그래밍 1(Object-oriented Programming 1) - TIL 221214 (0) | 2022.12.14 |
[Java] 객체지향 프로그래밍 1(Object-oriented Programming 1) - TIL 221213 (0) | 2022.12.13 |
[Java] 변수(Variable) - TIL 221209 (0) | 2022.12.10 |