본문 바로가기
Book/Java의 정석

[Chapter 6] 객체지향 프로그래밍 1_2

by slchoi 2022. 4. 1.
728x90
SMALL

'자바의 정석 3rd Edition'을 공부하며 정리한 내용입니다.

4. 오버로딩(overloading)


4.1 오버로딩이란?

  • 메서드도 같은 클래스 내에서 서로 구별될 수 있어야 하기 때문에 각기 다른 이름을 가져야 함
  • 자바에서는 한 클래스 내에 이미 사용하려는 이름과 같은 이름을 가진 메서드가 있더라도 매개변수의 개수 또는 타입이 다르면, 같은 이름을 사용해서 메서드를 정의할 수 있음
  • 메서드 오버로딩(method overloading): 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것

4.2 오버로딩의 조건

  1. 메서드 이름이 같아야 함
  2. 매개변수의 개수 또는 타입이 달라야 함
  • 메서드의 이름이 같다 하더라도 매개변수가 다르면 서로 구별될 수 있기 때문에 오버로딩이 가능
  • 위의 조건을 만족하지 못하는 메서드는 중복 정의로 간주되어 컴파일 시 에러 발생
  • 오버로딩된 메서드들은 매개변수에 의해서만 구별될 수 있으므로 반환 타입은 오버로딩을 구현하는데 아무런 영향을 주지 못함
  • 같은 일을 하지만 매개변수를 달리해야하는 경우에 이름은 같고 매개변수를 다르게 하여 오버로딩을 구현

4.4 오버로딩의 장점

  • 오버로딩을 통해 같은 기능을 하는 여러 메서드들이 하나의 이름으로 정의될 수 있어 이름을 기억하기 쉽고 이름을 짧게 할 수 있어 오류의 가능성을 줄여줌. 또한 메서드의 리름만 보고도 '메서드의 이름이 같으니, 같은 기능을 하겠구나'라고 예측이 가능
  • 메서드의 이름을 절약할 수 있음. 하나의 이름으로 여러 개의 메서드를 정의할 수 있으니, 메서드의 이름을 짓는데 고민을 덜 수 있는 동시에 사용되어야 할 메서드 이름을 다른 메서드의 이름으로 사용할 수 있기 때문

4.5 가변인자(varargs)와 오버로딩

  • 기존에는 메서드의 매개변수 개수가 고정적이었으나 JDK1.5부터 동적으로 지정해줄 수 있게 되었으며, 이 기능을 가변인자(variable arguments)라고 함

  • 타입... 변수명과 같은 형식으로 선언

    public PrintStream printf(String format, Object... args) {...}
  • 가변인자 외에도 매개변수가 더 있다면, 가변인자를 매개변수 중 제일 마지막에 선언해야 함

    • 그렇지 않으면 컴파일 에러 발생. 가변인자인지 아닌지를 구별할 방법이 없기 때문에 허용하지 않음
  • 여러 문자열을 하나로 결합하여 반환하는 concatenat 메서드

    // 1. 가변인자를 사용하지 않을 경우
    String concatenate(String s1, String s2) {...}
    String concatenate(String s1, String s2, String s3) {...}
    String concatenate(String s1, String s2, String s3, String s4) {...}
    // 2. 가변인자를 사용하는 경우
    String concatenate(String... str) {...}
    // 3. 매개변수의 타입을 배열로 하는 경우
    String concatenate(String[] str) {...}
  • 가변인자를 사용하는 경우 인자의 개수를 가변적으로 할 수 있으며 인자가 아예 없어도 되고 배열도 인자가 될 수 있음

  • 가변인자는 내부적으로 배열을 이용하는 것

    • 가변인자가 선언된 메서드를 호출할 때마다 배열이 새로 생성됨
  • 매개변수의 타입을 배열로 하면, 반드시 인자를 지정해 줘야하기 때문에, 2번 코드에서처럼 인자를 생략할 수 없음

    • null이나 길이가 0인 배열을 인자로 지정해줘야 함
  • 가변인자를 선언한 메서드를 오버로딩하면, 메서드를 호출했을 때 구별되지 못하는 경우가 발생하기 쉽기 때문에 가능하면 가변인자를 사용한 메서드는 오버로딩하지 않는 것이 좋음

5. 생성자(Constructor)


5.1 생성자란?

  • 생성자: 인스턴스가 생성될 때 호출되는 '인스턴스 초기화 메서드'
    • 인스턴스 변수의 초기화 작업에 주로 사용
    • 인스턴스 생성 시에 실행되어야 할 작업을 위해서도 사용됨
  • 생성자는 클래스 내에 선언되며, 구조는 메서드와 유사하지만 리턴값이 없다는 점이 다름
    • 리턴값이 없다고 해서 생성자 앞에 키워드 void를 사용하지는 않고, 아무 것도 적지 않음
  • 생성자도 오버로딩이 가능. 하나의 클래스에 여러 개의 생성자가 존재할 수 있음

생성자의 조건

  • 생성자의 이름은 클래스의 이름과 같아야 함
  • 생성자는 리턴 값이 없음

생성자 정의

클래스이름(타입 변수명, 타입 변수명, ...) {
    // 인스턴스 생성시 수행될 코드,
    // 주로 인스턴스 변수의 초기화 코드를 적음
}
class Card {
    Card() {   // 매개변수가 없는 생성자
        ...
    }
    Card(String k, int num) {   // 매개변수가 있는 생성자
        ...
    }
}
  • 연산자 new는 인스턴스를 생성하는 것이지 생성자 인스턴스를 생성하는 것이 아님
    • 생성자는 단순히 인스턴스변수들의 초기화에 사용되는 특별한 메서드일 뿐

Card 클래스의 인스턴스를 생성하는 코드와 수행 단계

Card c = new Card();
  1. 연산자 new에 의해서 메모리(heap)에 Card 클래스의 인스턴스가 생성됨
  2. 생성자 Card()가 호출되어 수행됨
  3. 연산자 new의 결과로, 생성된 Card 인스턴스의 주소가 반환되어 참조변수 c에 저장됨
  • 인스턴스를 생성하기 위해 사용해왓던 클래스이름()이 생성자였던 것
  • 인스턴스를 생성할 때는 ㄴ반드시 클래스 내에 정의된 생성자 중의 하나를 선택하여 지정해주어야 함

5.2 기본 생성자(default constructor)

  • 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 함. 클래스에 생성자를 정의하지 않고도 인스턴스를 생성할 수 있었던 이유는 컴파일러가 제공하는 기본 생성자(default constructor) 덕분
  • 컴파일할 때, 소스파일('*.java')의 클래스에 생성자가 하나도 정의되지 않은 경우 컴파일러는 자동적으로 클래스이름() {}를 기본 생성자로 추가해 컴파일
  • 컴파일러가 자동적으로 추가해주는 기본 생성자는 매개변수도 없고 아무런 내용도 없음
  • 특별히 인스턴스 초기화 작업이 요구되어지지 않는다면 생성자를 정의하지 않고 컴파일러가 제공하는 기본 생성자를 사용하는 것이 좋음
  • 클래스 내에 생성자가 하나도 없을 때만 컴파일러가 자동적으로 기본 생성자를 추가해줌

5.3 매개변수가 있는 생성자

  • 매개변수를 선언하여 호출 시 값을 넘겨받아서 인스턴스의 초기화 작업에 사용 가능
  • 인스턴스마다 각기 다른 값으로 초기화되어야하는 경우가 많기 때문에 매개변수를 사용한 초기화는 매우 유용
  • 클래스를 작성할 때 다양한 생성자를 제공합으로써 인스턴스 생성 후에 별도로 초기화를 하지 않아도 되도록 하는 것이 좋음

5.4 생성자에서 다른 생성자 호출하기 - this(), this

  • 같은 클래스의 멤버들 간에 서로 호출할 수 있는 것처럼 생성자 간에도 서로 호출이 가능

조건

  • 생성자의 이름으로 클래스 이름 대신 this를 사용
  • 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능
  • 생성자에서 다른 생성자를 첫 줄에서만 호출이 가능하도록 한 이유는 생성자 내에서 초기화 작업도중에 다른 생성자를 호출하게 되면, 호출된 다른 생성자 내에서도 멤버변수들의 값을 초기화할 것이므로 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질 수 있기 때문
    • 자세한 내용은 7장에서
  • 'this'는 참조변수로 인스턴스 자신을 가리킴. 참조변수를 통해 인스턴스의 멤버에 접근할 수 있는 것처럼, this로 인스턴스 변수에 접근할 수 있는 것
    • this를 사용할 수 있는 것은 인스턴스 멤버뿐. static 메서드에서는 인스턴스 멤버들을 사용할 수 없는 것처럼, this도 사용 불가
    • static 메서드는 인스턴스를 생성하지 않고도 호출될 수 있으므로 static 메서드가 호출된 시점에 인스턴스가 존재하지 않을 수도 있기 때문
  • this: 인스턴스 자신을 가리키는 참조변수
    • 인스턴스의 주소가 저장되어 있음
    • 모든 인스턴스 메서드에 지역변수로 숨겨진 채로 존재
  • this(), this(매개변수): 생성자
    • 같은 클래스의 다른 생성자를 호출할 때 사용

5.5 생성자를 이용한 인스턴스의 복사

  • 현재 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용할 수 있음
  • 두 인스턴스가 같은 상태를 갖는다는 것은 두 인스턴스의 모든 인스턴스 변수(상태)가 동일한 값을 갖고 있다는 것을 의미

6. 변수의 초기화


6.1 변수의 초기화

  • 변수의 초기화: 변수를 선언하고 처음으로 값을 저장하는 것
    • 초기화는 경우에 따라 필수적 혹은 선택적이기도 하지만, 가능하면 선언과 동시에 적절한 값으로 초기화하는 것이 좋음
  • 멤버변수는 초기화하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화가 이루어지므로 초기화하지 않고 상요해도 되지만, 지역변수는 사용하기 전에 반드시 초기화해야 함

멤버변수(클래스변수와 인스턴스변수)와 배열의 초기화는 선택적이지만, 지역변수의 초기화는 필수적

멤버변수의 초기화 방법

  • 명시적 초기화(explicit initialization)
  • 생성자(constructor)
  • 초기화 블럭(initialization block)
    • 인스턴스 초기화 블럭: 인스턴스 변수를 초기화하는데 사용
    • 클래스 초기화 블럭: 클래스 변수를 초기화하는데 사용

6.2 명시적 초기화(explicit initialization)

  • 변수를 선언과 동시에 초기화하는 것
    class Car {
      int door = 4;            // 기본형 변수의 초기화
      Engine e = new Engine();    // 참조형 변수의 초기화
    }
  • 명시적 초기화가 간단하고 명료하지만, 복잡한 초기화 작업이 필요할 때는 초기화 블럭(initialization block) 또는 생성자를 사용해야 함

6.3 초기화 블럭(initialization block)

클래스 초기화: 블럭 클래스 변수의 복잡한 초기화에 사용
인스턴스 초기화: 블럭 인스턴스변수의 복잡한 초기화에 사용

  • 인스턴스 초기화 블럭은 단순히 클래스 내에 블럭 {}을 만들고 그 안에 코드를 작성하면 됨
    • 클래스 초기화 블럭은 인스턴스 초기화 블럭 앞에 static을 덧붙이면 됨
  • 초기화 블럭 내에는 조건문, 반복, 예외처리구문 등을 자유롭게 사용 가능
  • 초기화 작업이 복잡하여 명시적 초기화만으로는 부녹한 경우 초기화 블럭을 사용
class InitBlock {
    static {/* 클래스 초기화블럭*/}

    {/* 인스턴스 초기화블럭*/}
}
  • 클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때 한번만 수행되며, 인스턴스 초기화 블럭은 생성자와 같이 인스턴스를 생성할 때마다 수행됨
    • 클래스가 처음 로딩될 때 클래스 변수들이 자동적으로 메모리에 만들어지고, 곧바로 클래스 초기화 블럭이 클래스 변수들을 초기화하게 되는 것
  • 생성자보다 인스턴스 초기화 블럭이 먼저 수행됨
  • 인스턴스 변수의 초기화는 주로 생성자를 사용하고, 인스턴스 초기화 블럭은 모든 생성자에서 공통으로 수행돼야 하는 코드를 넣는데 사용
// 1. 인스턴스 초기화 블럭 사용 X
Car () {
    count++;
    serialNo = count;
    color = "White";
    gearType = "Auto";
}
Car (String color, String gearType) {
    count ++;
    serialNo = count;
    this.color = color;
    this.gearType = gearType;
}
// 2. 인스턴스 초기화 블럭 사용 O
{
    count ++;
    serialNo = count;
}
Car () {
    color = "White";
    gearType = "Auto";
}
Car (String color, String gearType) {
    this.color = color;
    this.gearType = gearType;
}
  • 코드의 중복을 제거해 코드의 신뢰성을 높여 주고, 오류 발생 가능성을 줄여줌
    • 재사용성을 높이고 중복을 제거하는 것이 객체지향프로그래밍이 추구하는 궁극적인 목표

6.4 멤버변수의 초기화 시기와 순서

  • 클래스변수의 초기화시점: 클래스가 처음 로딩될 때 단 한번 초기화됨

  • 인스턴스변수의 초기화시점: 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어짐

  • 클래스변수의 초기화순서: 기본값 -> 명시적초기화 -> 클래스 초기화 블럭

  • 인스턴스변수의 초기화순서: 기본값 -> 명시적초기화 -> 인스턴스 초기화 블럭 -> 생성자

  • 클래스 변수는 항상 인스턴스 변수보다 먼새 생성되고 초기화됨

Chapter 6 끝!!!

728x90
LIST

댓글