자바 생성자와 변수의 초기화에 대해 알아보자

2025. 2. 7. 13:00JAVA

728x90
반응형

 

 안녕하세요. 진득코딩입니다.

 

 이번 시간에는 객체지향 프로그래밍의 마지막 부분인 생성자와 변수의 초기화에 대해서 알아보도록 하겠습니다.

 

생성자(Constructor)

 

  • 생성자는 인스턴스가 생성될 때 호출되는 '인스턴스 초기화 메서드'입니다.

  • 생성자 역시 메서드처럼 클래스 내에 선언되며, 구조도 메서드와 유사하지만 리턴값이 없다는 점이 다릅니다.


 1. 생성자의 이름은 클래스의 이름과 같아야 합니다.

 2. 생성자는 리턴값이 없습니다.

 

  • 생성자는 다음과 같이 정의합니다.

생성자 정의

 

  • 생성자도 오버로딩이 가능하므로 하나의 클래스에 여러 개의 생성자가 존재할 수 있습니다.

  • 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것이 아닙니다.

  • 생성자는 단순히 인스턴스 변수들이 초기화에 사용되는 조금 특별한 메서드일 뿐입니다.

  • 인스턴스 생성이 수행되는 과정을 단계별로 나누어보면 다음과 같습니다.


 Card c = new Card();

 1. 연산자 new에 의해서 메모리(heap)에 Card클래스의 인스턴스가 생성됩니다.

 2. 생성자 Card()가 호출되어 수행됩니다.

 3. 연산자 new의 결과로, 생성된 Card인스턴스의 주소가 반환되어 참조변수 c에 저장됩니다.

 

  • 지금까지 인스턴스를 생성하기 위해 사용해왔던 '클래스이름()'이 바로 생성자였습니다.

  • 인스턴스를 생성할 때는 반드시 클래스 내에 정의된 생성자 중의 하나를 선택하여 지정해주어야 합니다.

기본 생성자(default constructor)

 

  • 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 합니다.

  • 지금까지 클래스에 생성자를 정의하지 않고도 인스턴스를 생성할 수 있었던 이유는 컴파일러가 제공하는 '기본 생성자(default constructors)' 덕분이었습니다.

  • 컴파일할 때, 소스파일(*. java)의 클래스에 생성자가 하나도 정의되지 않은 경우 컴파일러는 자동적으로 아래와 같은 내용의 기본 생성자를 추가하여 컴파일합니다.


 클래스이름() { }

 Card() { }

 

  • 그동안 인스턴스를 생성할 때 컴파일러가 제공한 기본 생성자를 사용해 왔던 것입니다.

  • 클래스의 '접근 제어자(Access Modifier)'가 public인 경우에는 기본 생성자로 'public 클래스이름() {}'이 추가됩니다.

예제 6-23

 

  • 위 예제를 컴파일하면 위와 같은 에러메시지가 나타납니다.

  • 이것은 Data2에서 Data2()라는 생성자를 찾을 수 없다는 내용의 에러메시지입니다.

  • 이는 Data2에 생성자 Data2()가 정의되어 있지 않기 때문에 에러가 발생한 것입니다.

  • Data1에는 정의되어 있는 생성자가 하나도 없으므로 컴파일러가 기본 생성자를 추가해 주었지만, Data2에는 이미 생성자 Data2(int x)가 정의되어 있으므로 기본 생성자가 추가되지 않았기 때문입니다.

  • 컴파일러가 자동적으로 기본 생성자를 추가해 주는 경우는 '클래스 내에 생성자가 하나도 없을 때뿐입니다.

  • 컴파일 에러가 발생하지 않도록 하기 위해서는 Data2의 인스턴스를 생성할 때 생성자 Data2(int x)를 사용하던가, 아니면 클래스 Data2에 생성자 Data2()를 추가로 정의해 주면 됩니다.


 기본 생성자가 컴파일러에 의해서 추가되는 경우는 클래스에 정의된 생성자가 하나도 없을 때뿐입니다.

 

 매개변수가 있는 생성자

 

  • 생성자도 메서드처럼 매개변수를 선언하여 호출 시 값을 넘겨받아서 인스턴스의 초기화 작업에 사용할 수 있습니다.

예제 6-24

 

  • Car인스턴스를 생성할 때, 생성자 Car()를 사용한다면, 인스턴스를 생성한 다음에 인스턴스변수들을 따로 초기화해주어야 하지만, 매개변수가 있는 생성자 Car(String color, String gearType, int door)를 사용한다면 인스턴스를 생성하는 동시에 원하는 값으로 초기화할 수 있게 됩니다.

생성자에서 다른 생성자 호출하기

 

  • 생성자 간에도 서로 호출이 가능합니다.
  • 이때 만족시켜야 할 두 조건에 대해 먼저 살펴보도록 하겠습니다.


 - 생성자의 이름으로 클래스이름 대신 this를 사용합니다.

 - 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출이 가능합니다.

 

  • 생성자에서 다른 생성자를 첫 줄에서만 호출이 가능합니다.

  • 그 이유는 생성자 내에서 초기화 작업 도중에 다른 생성자를 호출하게 되면, 호출된 다른 생성자 내에서도 멤버변수들의 값을 초기화를 할 것이므로 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질 수 있기 때문입니다.

예제 6-25

 

  • 생성자 Car()에서 또 다른 생성자 Car(String color, String gearType, int door)를 호출하였습니다.

  • 이처럼 생성자 간의 호출에는 생성자의 이름 대신 this를 사용해야만 하므로 'Car' 대신 'this'를 사용하였습니다.

  • 또한 생성자 Car()의 첫째 줄에서 호출하였습니다.

  • Car c1 = new Car();와 같이 생성자 Car()를 사용해서 Car인스턴스를 생성한 경우에, 인스턴스변수 color는 "white", gearType은 "auto", door는 4로 초기화되도록 하였습니다.

  • 같은 클래스 내의 생성자들은 일반적으로 서로 관계가 깊은 경우가 많아서 이처럼 서로 호출하도록 하여 유기적으로 연결해 주면 더 좋은 코드를 얻을 수 있습니다. 

  • 위 코드에서 this.color는 인스턴스 변수이고, color는 생성자의 매개변수로 정의된 지역변수로 서로 구별이 가능합니다.

  • 'this'를 사용해서 구별되도록 하는 것이 의미가 더 명확해지고 이해하기 쉽습니다.

  • 'this'는 참조변수로 인스턴스 자신을 가리킵니다.

  • 'this'를 사용할 수 있는 것은 인스턴스멤버뿐입니다.

  • 생성자를 포함한 모든 인스턴스메서드에는 자신이 관련된 인스턴스를 가리키는 참조변수 'this'가 지역변수로 숨겨진 채로 존재합니다.


 this : 인스턴스 자신을 가리키는 참조변수, 인스턴스의 주소가 저장되어 있습니다.

 this(), this(매개변수) : 생성자, 같은 클랙스의 다른 생성자를 호출할 때 사용합니다.

 

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

 

  • 현재 사용하고 있는 인스턴스와 같은 상태를 갖는 인스턴스를 하나 더 만들고자 할 때 생성자를 이용할 수 있습니다.

  • 두 인스턴스가 같은 상태를 갖는다는 것은 두 인스턴스의 모든 인스턴스 변수(상태)가 동일한 값을 가지고 있다는 것을 의미합니다.

  • 하나의 클래스로부터 생성된 모든 인스턴스의 메서드와 클래스변수는 서로 동일하기 때문에 인스턴스 간의 차이는 인스턴스마다 각기 다른 값을 가질 수 있는 인스턴스변수뿐입니다.

인스턴스 복사

 

  • 위의 코드는 Car클래스의 참조변수를 매개변수로 선언한 생성자입니다.

  • 매개변수로 넘겨진 참조변수가 가리키는 Car인스턴스의 인스턴스변수인 color, gearType, door의 값을 인스턴스 자신으로 복사하는 것입니다.

  • 어떤 인스턴스의 상태를 전혀 알지 못해도 똑같은 상태의 인스턴스를 추가로 생성할 수 있습니다.

예제 6-26
예제 6-26 console 출력

 

  • 인스턴스 c2는 c1을 복사하여 생성된 것이므로 서로 같은 상태를 갖지만, 서로 독립적으로 메모리공간에 존재하는 별도의 인스턴스이므로 c1의 값들이 변경되어도 c2는 영향을 받지 않습니다.

  • 생성자를 잘 활용하면 보다 간결하고 직관적인, 객체지향적인 코드를 작성할 수 있습니다.


 **인스턴스를 생성할 때 결정해야 할 2가지 사항

 1. 클래스 - 어떤 클래스의 인스턴스를 생성할 것인가?

 2. 생성자 - 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?

 

 변수의 초기화

 

  •  변수의 초기화는 변수를 선언하고 처음으로 값을 저장하는 식을 의미합니다.

  • 가능하면 선언과 동시에 적절한 값으로 초기화하는 것이 바람직합니다.

  • 지역변수는 사용하기 전에 반드시 초기화해야 합니다.


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

 

  • 각 타입의 기본값(default value)은 다음 표와 같습니다.

자료형 기본값
boolean false
char '\u0000'
byte, short, int 0
long 0L
float 0.0f
double 0.0d 또는 0.0
참조형 변수 null

 

  • 변수의 초기화에 대한 예를 표로 정리하여 살펴보도록 하겠습니다.

선언 예 설명
 int i = 10;
 int j = 10;
 int형 변수 i를 선언하고 10으로 초기화 합니다.
 int형 변수 j를 선언하고 10으로 초기화 합니다.
 int i=10, j=10;  같은 타입의 변수는 콤마(,)를 사용해서 함께 선언하거나 초기화 할 수 있습니다.
 int i = 10, long j=0;  에러. 타입이 다른 변수는 함께 선언하거나 초기화 할 수 없습니다.
 int i=10;
 int j=i;
 변수 i에 저장된 값으로 변수 i를 초기화 합니다.
 변수 j는 i의 값인 10으로 초기화 됩니다.
 int j=i;
 int i=10;
 에러. 변수 i가 선언되기 전에 i를 사용할 수 없습니다.

 


 **멤버변수의 초기화 방법

 1. 명시적 초기화(explicit initialization)

 2. 생성자(constructor)

 3. 초기화 블럭(initialization block)
   - 인스턴스 초기화 블럭 : 인스턴스변수를 초기화하는 데 사용.
   - 클래스 초기화 블럭 : 클래스변수를 초기화하는데 사용

 

명시적 초기화(explicit initialization)

 

  • 명시적 초기화란 변수를 선언과 동시에 초기화하는 것을 의미합니다.

  • 보다 복잡한 초기화 작업이 필요할 때는 '초기화 블럭(initialization block)' 또는 생성자를 사용해야 합니다.

초기화 블럭(initialzation block)

 

  • 초기화 블럭에는 '클래스 초기화 블럭'과 '인스턴스 초기화 블럭' 두 가지 종류가 있습니다.

  • 클래스 초기화 블럭은 클래스변수의 초기화에 사용되고, 인스턴스 초기화 블럭은 인스턴스변수의 초기화에 사용합니다.


 클래스 초기화 블럭 : 클래스변수의 복잡한 초기화에 사용됩니다.

 인스턴스 초기화 블럭 : 인스턴스변수의 복잡한 초기화에 사용됩니다.

 

  • 인스턴스 초기화 블럭은 단순히 클래스 내에 블럭 { }을 만들고 그 안에 코드를 작성하기만 하면 됩니다.

  • 클래스 초기화 블럭은 인스턴스 초기화 블럭 앞에 단순히 static을 덧붙이기만 하면 됩니다.

  • 클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때 한번만 수행되며, 인스턴스 초기화 블럭은 생성자와 같이 인스턴스를 생성할 때마다 수행됩니다.

  • 또한 생성자보다 인스턴스 초기화 블럭이 먼저 수행됩니다.

  • 인스턴스 변수의 초기화는 주로 생성자를 사용하고, 인스턴스 초기화 블럭은 모든 생성자에서 공통으로 수행되어야 하는 코드를 넣는 데 사용합니다.

  • 클래스의 모든 생성자에 공통으로 수행되어야 하는 문장들이 있을 때, 이 문장들을 각 생성자마다 써주기보다는 인스턴스 블럭에 넣어주면 코드가 보다 간결해집니다.

  • 코드의 중복을 제거하는 것은 코드의 신뢰성을 높여주고, 오류의 발생가능성을 줄여 준다는 장점이 있습니다.

  • 재사용성을 높이고 중복을 제거하는 것, 이것이 바로 객체지향프로그래밍이 추구하는 궁극적인 목표입니다.

예제 6-27

 

  • BolckTest가 메모리에 로딩될 때, 클래스 초기화 블럭이 가장 먼저 수행되어 'static { }'이 화면에 출력됩니다.

  • main메서드가 수행되어 BlockTest인스턴스가 생성되면서 인스턴스 초기화 블럭이 먼저 수행되고, 끝으로 생성자가 수행됩니다.

  • 클래스 초기화 블럭은 처음 메모리에 로딩될 때 한번만 수행되었지만, 인스턴스 초기화 블럭은 인스턴스가 생성될 때마다 수행되었습니다.

예제 6-28

 

  • 명시적 초기화를 통해 배열 arr을 생성하고, 클래스 초기화 블럭을 이용해서 배열의 각 요소들을 random()을 사용해서 임의의 값으로 채우도록 했습니다.

  • 배열이나 예외처리가 필요한 초기화에서는 명시적 초기화만으로는 복잡한 초기화 작업을 할 수 없습니다.

  • 이런 경우에 추가적으로 클래스 초기화 블럭을 사용하도록 합니다.

  • 인스턴스변수의 복잡한 초기화는 생성자 또는 인스턴스 초기화 블럭을 사용합니다.

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

 

 
 클래스 변수의 초기화 시점 : 클래스가 처음 로딩될 때 단 한번 초기화됩니다.
 인스턴스 변수의 초기화 시점 : 인스턴스가 생성될 때마다 각 인스턴스별로 초기화가 이루어집니다.

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

 

예제 6-29

 

  •  공장에서 제품을 생산할 때 제품마다 생산일련번호(serial no)를 부여하는 것과 같이 Product클래스의 인스턴스가 고유의 일련번호(serialNo)를 갖도록 하였습니다.

  • 생성자가 하나밖에 없기 때문에 인스턴스 블럭 대신, Product클래스의 생성자를 사용해도 결과는 같지만, 코드의 의미상 모든 생성자에서 공통으로 수행되어야 하는 내용이기 때문에 인스턴스 블럭을 사용하였습니다.

예제 6-30

 

  • 문서(Document)를 생성할 때, 문서의 이름을 지정하면 그 이름의 문서가 생성되지만, 문서의 이름을 지정하지 않으면 프로그램이 일정한 규칙을 적용해서 자동으로 이름을 결정합니다.


 

 이번 시간에는 생성자와 변수의 초기화에 대해 알아보았습니다.

 

 생성자와 변수의 초기화의 개념을 코드를 작성하게 되면 좀 더 객체지향적인 코드를 작성할 수 있습니다.

 

 이는 코드의 신뢰성을 향상하고 오류 발생 확률을 낮춰줄 수 있기 때문에 좋은 코드를 작성할 때 굉장히 유용하게 작용할 거라고 생각합니다.

 

 이번 포스팅은 여기까지입니다.

 

 궁금하신 사항이나 문의하실 사항이 있다면 댓글에 남겨주시면 열심히 답글 달도록 하겠습니다.

 

 끝까지 봐주셔서 감사합니다.😊

728x90
반응형
LIST