본문 바로가기

Java/Chapter 07. 클래스와 인스턴스

[Java] 07.02 - 생성자(Constructor)와 String 클래스의 소개

들어가며

 지금까지 BankAccount 클래스의
정의를 통해서

 클래스에 대한 여러 가지를
이해하고 관찰하였다.

 그러나 이 클래스에는 몇 가지
문제가 있다.

 그중 하나는 인스턴스를
생성하는 과정에서

 적절한 초기화를 진행하지
못했다는 점이다.

 

String 클래스에 대한 첫 소개

 다음 이야기의 전개를 위해
문자열 처리에 대한

 기술 몇 가지를 소개하고자
한다.

 문자열은 큰따옴표로 묶어서
표현함을 이미 알고 있다.

 그런데 이러한 문자열은
다음과 같이

 참조변수를 선언해서 참조할
수도 있다.

 String myJava = "Java";    // String형 참조변수의 문자열 참조

 위의 상황은 참조변수 myJava가
문자열 "Java"를 가리키는 상황이다.

 지금은 이정도의 이해면 충분하다.

 사실 String은 자바에서 제공하는
클래스의 이름인데

 이러한 내용은 이후에 별도로
설명을 한다.

 따라서 문자열의 활용 측면에서만
위의 문장을 이해하기 바란다.

 그럼 이와 관련하여 다음 예제를
소개하겠다.

public class temp {
    public static void main(String[] args){
        // 문자열 선언과 동시에 참조변수로 참조한다.
        String str1 = "Happy";
        String str2 = "Birthday";
        System.out.println(str1 + str2);

        // 메소드에 문자열을 전달하는 다양한 방법을 보여준다.
        printString(str1);
        printString(" ");
        printString(str2);
        printString("\n");
        printString("End of program! \n");
    }

    // String 참조변수를 매개변수로 선언하여 문자열을 전달 받을 수 있다.
    public static void printString(String str){
        System.out.print(str);
    }
}
/*
실행 결과
HappyBirthday
Happy Birthday
End of program! 
*/

 위 예제를 통해 알 수 있는,

 그리고 알아야 할 사실 2가지는
다음과 같다.

  • 문자열을 메소드의 인자로 전달할 수 있다.
  • 매개변수로 String형 참조변수를 선언하여 문자열을 인자로 전달받을 수 있다.

 앞으로 이 정도의 이해 수준에서
문자열을 활용하자.

 그러면 문자열의 활용 능력이
배로 증가할 것이다.

 

인스턴스를 구분할 수 있는 유일한 정보를 갖게 하라

 앞서 정의한 BankAccount 클래스를
다시 관찰하자.

 은행에서는 고객이 계좌를
개설할 때마다

 이 클래스의 인스턴스를
생성해야 한다.

class BankAccount {
    int balance = 0;
    public int deposit(int amount){
        . . .
    }
    public int withdraw(int amount){
        . . . 
    }
    public int checkMyBalance() {
        . . .
    }
}

 그런데 문제는 인스턴스를
구분할 수 있는 정보가
빠졌다는 것이다.

 즉 위의 클래스에 최소한
다음 2가지 정도는 추가가
되어야 한다.

 그래야 누구의 계좌인지
구분할 수 있다.

  • 계좌번호        String accNumber
  • 주민번호        String ssNumber

 그리고 이를 반영하여 BankAccount
클래스를 수정한 결과는
다음과 같다.

public class BankAccountUniID {
    public static void main(String[] args){
        BankAccount user1 = new BankAccount();  // 계좌 생성
        user1.initAccount("12-34-56", "000000-0000000", 10000); // 계좌 초기화

        BankAccount user2 = new BankAccount();  // 계좌 생성
        user2.initAccount("11-22-33", "111111-1111111", 10000); // 계좌 초기화

        user1.deposit(5000);
        user2.deposit(3000);
        user1.withdraw(2000);
        user2.withdraw(2000);
        user1.checkMyBalance();
        user2.checkMyBalance();
    }
}

class BankAccount {
    String accNumber;   // 계좌번호
    String ssNumber;    // 주민버혼
    int balance = 0;    // 예금 잔액

    public void initAccount(String acc, String ss, int bal){
        accNumber = acc;
        ssNumber = ss;
        balance = bal;  // 계좌 개설 시 예금액으로 초기화
    }

    public int deposit(int amount){
        balance += amount;
        return balance;
    }
    public int withdraw(int amount){
        balance -= amount;
        return balance;
    }
    public int checkMyBalance() {
        System.out.println("계좌번호: " + accNumber);
        System.out.println("주민번호: " + ssNumber);
        System.out.println("잔   액: " + balance + '\n');
        return balance;
    }
}
/*
실행 결과
계좌번호: 12-34-56
주민번호: 000000-0000000
잔   액: 13000

계좌번호: 11-22-33
주민번호: 111111-1111111
잔   액: 11000
*/

 위 예제의 클래스에는
다음 메소드가 추가되었다.

public void initAccount(String acc, String ss, int bal){
    accNumber = acc;
    ssNumber = ss;
    balance = bal;  // 계좌 개설 시 예금액으로 초기화
}

 이 메소드는 다음과 같은
부분에서

 다은 메소드들과 성격상
구분된다.

  • 인스턴스의 초기화를 위한 메소드이다.
  • 때문에 인스턴스 생성 시 반드시 한번 호출해서 초기화를 진행해야 한다.

 그러나 위와 같이 메소드를
정의하지 않고

 '생성자(Constructor)'라는 것을
정의해서

 인스턴스의 초기화를 진행할
수도 있다.

 생성자는 인스턴스 생성 과정에서
초기화를 위해 자동으로 호출되는
일종의 메소드이다.

 

생성자(Constructor)

 생성자는 메소드와 모습이
같다.

 따라서 생성자를 '생성자 메소드
(Constructor Method)'로 표현하는
경우도 있다.

 그러나 생성자는 다음과 같은
부분에서 메소드와 차이가 있다.

 달리 말하면 이는 생성자가
되기 위한 조긴이기도 하다.

  • 생성자의 이름은 클래스의 이름과 동일해야 한다.
  • 생성자는 값을 반환하지 않고 반환형도 표시하지 않는다.

 위의 조건을 모두 만족하면
이는 자바 컴파일러에 의해서
생성자로 인식된다.

 따라서 인스턴스 생성 시
자동으로 호출되어

 인스턴스를 초기화하게 된다.

 그럼 앞서 예제에서 인스턴스
초기화를 위해 정의한 메소드를
보자.

public void initAccount(String acc, String ss, int bal){
    accNumber = acc;
    ssNumber = ss;
    balance = bal;
}

 예제에서 위 메소드가 속한
클래스의 이름은 BankAccount
였다.

 따라서 위의 메소드를 생성자가
되게 하려면

 다음과 같이 수정하면 된다.

public BankAccount(String acc, String ss, int bal){
    accNumber = acc;
    ssNumber = ss;
    balance = bal;
}

 위 메소드의 이름은 클래스의
이름과 동일하다.

 그리고 반환하지 않으며,
반환형도 선언하지 않았다.

 따라서 생성자의 조건을
모두 갖췄다.

 그럼 이 생성자를 활용하는
형태로 예제를 수정해 보겠다.

public class BankAccountUniID {
    public static void main(String[] args){
        // 계좌 생성 및 초기화
        BankAccount user1 = new BankAccount("12-34-56", "000000-0000000", 10000);
        BankAccount user2 = new BankAccount("11-22-33", "111111-1111111", 10000);
        
        user1.deposit(5000);
        user2.deposit(3000);
        user1.withdraw(2000);
        user2.withdraw(2000);
        user1.checkMyBalance();
        user2.checkMyBalance();
    }
}

class BankAccount {
    String accNumber;   // 계좌번호
    String ssNumber;    // 주민버혼
    int balance = 0;    // 예금 잔액

    public BankAccount(String acc, String ss, int bal){
        accNumber = acc;
        ssNumber = ss;
        balance = bal;  // 계좌 개설 시 예금액으로 초기화
    }

    public int deposit(int amount){
        balance += amount;
        return balance;
    }
    public int withdraw(int amount){
        balance -= amount;
        return balance;
    }
    public int checkMyBalance() {
        System.out.println("계좌번호: " + accNumber);
        System.out.println("주민번호: " + ssNumber);
        System.out.println("잔   액: " + balance + '\n');
        return balance;
    }
}
/*
실행 결과
계좌번호: 12-34-56
주민번호: 000000-0000000
잔   액: 13000

계좌번호: 11-22-33
주민번호: 111111-1111111
잔   액: 11000
*/

 위 예제에서는 인스턴스의
생성 문장이 다음과 같이
바뀌었다.

BankAccount user1 = new BankAccount("12-34-56", "000000-0000000", 10000);
BankAccount user2 = new BankAccount("11-22-33", "111111-1111111", 10000);

 소괄호 안에 값을 전달하고
있는데,

 이 값들은 생성자가 호출될 때

 생성자의 매개변수로 전달이
된다.

 즉, 위와 같이 문장을 구성하면
'인스턴스 생성 마지막 단계'에서

 다음의 생성자가 호출되면서
값들이 전달된다.

 그리고 이 값들로 인스턴스
변수가 초기화 된다.

// 생성자
public BankAccount(String acc, String ss, int bal){
    accNumber = acc;  // 변수 accNumber 초기화
    ssNumber = ss;    // 변수 ssNumber 초기화
    balance = bal;    // 변수 balance 초기화
}

 예제의 코드와 그 실행
결과를 통해서

 '생성자가 호출되었음'을
그리고 이를 통해서 '원하는
값으로 인스턴스가 초기화
되었음'을 확인할 수 있다.

 이러한 생성자와 관련하여
다음 사실을 반드시 기억해야
한다.

"인스턴스 생성의 마지막 단계는 생성자 호출이다."

"어떠한 이유로든 생성자 호출이
생략된 인스턴스는 인스턴스가 아니다."

 

디폴트 생성자

 인스턴스 생성의 마지막 단계는
생성자 호출이라 하였다.

  그리고 생성자 호출이 생략된
인스턴스는 인스턴스가 아니라
하였다.

 하지만 앞서 생성자가 없는
클래스를 수차례 정의하였고

 이들을 대상으로 인스턴스를
생성한 바 있다.

 그렇다면 이렇게 생성된 인스턴스는
인스턴스가 아니라는 뜻인가?

 사실 다음과 같이 생성자를
생략한 상태의 클래스를
정의하면

 자바 컴파일러가 '디폴트 생성자'
라는 것을 클래스 정의에 넣어준다.

class BankAccount {
    String accNumber;   
    String ssNumber;
    int balance = 0;

    // 컴파일러에 의해 자동으로 생성되는 '디폴트 생성자'
    public BankAccount(){
       // 비어있음
    }

    public int deposit(int amount){ . . . }
    public int withdraw(int amount){ . . . }
    public int checkMyBalance() { . . . }
}

 위에서 보이듯이,

 디폴트 생성자는 인자를
전달받지 않는 형태로
정의되어 삽입된다.

 물론 내부적으로 하는 일도
없다.

 하지만 이로 인해서 인스턴스
생성 규칙인 '생성자의 호출'은
유지가 된다.

 생성자를 정의하지 않더라도
말이다.

 그런데 컴파일러에 의해서
디폴트 생성자가 삽입이
되더라도

 생성자는 직접 정의해 주는
것이 좋다.

 아주 예외적인 상황이 아니라면,

 생성자가 필요 없는 클래스는
잘 정의된 클래스가 아닐 확률이
매우 높기 때문이다.


참고 및 출처

윤성우의 열혈 Java 프로그래밍
국내도서
저자 : 윤성우
출판 : 오렌지미디어 2017.07.05
상세보기