반응형
자바 생성자는 어떻게 초기화될까?
1. 생성자에서 모든 필드를 초기화할 필요가 없는 이유
자바에서 생성자는 객체가 생성될 때 특정 필드를 초기화하기 위해 사용된다.
그러나 모든 필드를 반드시 생성자에서 초기화해야 하는 것은 아니다.
다음과 같은 이유로 일부 필드는 생성자에서 초기화하지 않아도 된다.
- 선택적 초기화
- 일부 필드는 기본값으로 초기화해도 문제가 없는 경우가 있다.
- 예를 들면 숫자형 필드는 기본적으로 0, 객체형 필드는 null로 초기화된다.
- 다중 생성자
- 여러 생성자를 정의하여 다양한 방식으로 객체를 생성할 수 있다.
- 각 생성자에서 초기화할 필드를 선택적으로 지정할 수 있다.
- 필드 초기화 블록 또는 선언 시 초기화
- 필드를 선언할 때 기본값을 지정할 수 있다.
- 초기화 블록을 사용하여 공통적으로 초기화할 수도 있다.
기본값 초기화
- 자바에서는 클래스의 인스턴스 필드(멤버 변수)는 자동으로 기본값으로 초기화된다. 이는 final 키워드와 상관없이 적용된다.
- 기본값은 필드의 데이터 타입에 따라 다르게 설정된다.
기본형 타입의 초기값
- byte, short, int, long: 0
- float, double: 0.0
- char: '\u0000' (null 문자)
- boolean: false
참조형 타입의 초기값
- 객체형 (예: String, 사용자 정의 클래스 등): null
2. 다른 클래스를 멤버 변수로 가질 때의 초기화
다른 클래스를 멤버 변수로 가질 때
- 다른 클래스를 멤버 변수로 선언하면, 해당 변수는 객체의 레퍼런스(reference)를 저장하게 된다. 만약 생성자에서 이를 초기화하지 않으면 기본값인 null로 설정된다. 이는 메모리 상에 실제 객체가 생성되지 않았음을 의미한다.
예제
public class Address {
private String city;
private String street;
public Address(String city, String street) {
this.city = city;
this.street = street;
}
// getters and setters...
@Override
public String toString() {
return city + ", " + street;
}
}
public class Person {
private String name;
private int age;
private Address address; // 다른 클래스를 멤버 변수로 선언
// 생성자에서 address를 초기화하지 않음
public Person(String name, int age) {
this.name = name;
this.age = age;
// address는 기본값인 null로 남게 됩니다.
}
public void displayInfo() {
System.out.println("Name: " + name); // 정상 출력
System.out.println("Age: " + age); // 정상 출력
System.out.println("Address: " + address); // null 출력
}
public static void main(String[] args) {
Person person = new Person("홍길동", 30);
person.displayInfo();
}
}
출력은 다음과 같다.
Name: 홍길동
Age: 30
Address: null
3. 생성자 오버로딩 (Constructor Overloading)
생성자 오버로딩
- 자바에서는 같은 이름의 생성자를 여러 개 정의할 수 있으며, 이를 생성자 오버로딩이라고 한다. 객체를 생성할 때 전달하는 인자의 개수와 타입에 따라 호출되는 생성자가 결정된다. 한 번 객체를 생성할 때는 하나의 생성자만 호출되며, 다른 생성자들은 독립적으로 존재한다.
- 호출에 대해 조금 더 상세히 설명하자면 자바에서 클래스에 여러 개의 생성자를 오버로딩(overloading)하여 정의한 경우, 객체를 생성할 때 원하는 생성자 하나만 선택적으로 호출할 수 있으며, 나머지 생성자들에는 전혀 영향을 미치지 않는다. 다시 말해, 하나의 생성자를 사용한다고 해서 다른 생성자들을 함께 사용해야 하거나, 자동으로 호출되는 일은 없다.
예제
public class User {
private String username;
private String email;
private int age;
// username만 초기화하는 생성자
public User(String username) {
this.username = username;
}
// username과 email을 초기화하는 생성자
public User(String username, String email) {
this.username = username;
this.email = email;
}
// 모든 필드를 초기화하는 생성자
public User(String username, String email, int age) {
this.username = username;
this.email = email;
this.age = age;
}
// getters and setters...
}
생성자 선택 시 초기화되는 필드
- 첫 번째 생성자 (User(String username)):
- username 필드만 초기화된다.
- email과 age 필드는 기본값으로 초기화된다.
- email: null
- age: 0
- 두 번째 생성자 (User(String username, String email)):
- username과 email 필드가 초기화된다.
- age 필드는 기본값인 0으로 초기화된다.
- 세 번째 생성자 (User(String username, String email, int age)):
- 모든 필드 (username, email, age)가 초기화된다.
- 모든 필드 (username, email, age)가 초기화된다.
위의 예제코드 실행 예시
public class Main {
public static void main(String[] args) {
// 첫 번째 생성자 사용: username만 초기화
User user1 = new User("alice");
System.out.println(user1.getUsername()); // 출력: alice
System.out.println(user1.getEmail()); // 출력: null
System.out.println(user1.getAge()); // 출력: 0
// 두 번째 생성자 사용: username과 email 초기화
User user2 = new User("bob", "bob@example.com");
System.out.println(user2.getUsername()); // 출력: bob
System.out.println(user2.getEmail()); // 출력: bob@example.com
System.out.println(user2.getAge()); // 출력: 0
// 세 번째 생성자 사용: 모든 필드 초기화
User user3 = new User("carol", "carol@example.com", 25);
System.out.println(user3.getUsername()); // 출력: carol
System.out.println(user3.getEmail()); // 출력: carol@example.com
System.out.println(user3.getAge()); // 출력: 25
}
}
4. 주의할 점
final 필드의 초기화
- final로 선언된 필드는 반드시 생성자에서 초기화하거나 선언 시에 초기값을 지정해야 한다. 그렇지 않으면 컴파일 오류가 발생한다.
public class ImmutablePerson {
private final String name;
private final int age;
// 모든 final 필드를 초기화하는 생성자
public ImmutablePerson(String name, int age) {
this.name = name;
this.age = age;
}
// 기본 생성자를 만들면 오류 발생
// public ImmutablePerson() {} // 컴파일 오류
}
로컬 변수와 인스턴스 변수의 차이
- 인스턴스 변수(필드): 클래스 내에 선언된 변수로, 객체가 생성될 때 자동으로 기본값으로 초기화된다.
- 로컬 변수: 메서드 내에 선언된 변수로, 자동으로 초기화되지 않는다. 반드시 사용하기 전에 초기화를 해야 한다.
public void method() {
int localVar;
// System.out.println(localVar); // 컴파일 오류: 초기화되지 않은 변수 사용
localVar = 10;
System.out.println(localVar); // 정상 출력: 10
}
객체의 일관성 유지
- 모든 필드를 생성자에서 초기화하지 않으면 객체의 상태가 일관되지 않을 수 있다. 특히, 중요한 필드가 null 상태로 남아있으면 NullPointerException 등의 문제가 발생할 수 있다. 따라서, 객체의 상태를 항상 유효하게 유지하려면 필요한 필드를 생성자에서 초기화하는 것이 좋다.
5. 예제 코드
기본 필드 초기화 예제
public class Example {
private int number;
private boolean flag;
private String text;
private double decimal;
private Object obj;
// 생성자에서 필드를 초기화하지 않음
public Example() {
// 모든 필드는 기본값으로 초기화됩니다.
}
public void displayValues() {
System.out.println("number: " + number); // 출력: number: 0
System.out.println("flag: " + flag); // 출력: flag: false
System.out.println("text: " + text); // 출력: text: null
System.out.println("decimal: " + decimal); // 출력: decimal: 0.0
System.out.println("obj: " + obj); // 출력: obj: null
}
public static void main(String[] args) {
Example example = new Example();
example.displayValues();
}
}
출력은 다음과 같다.
number: 0
flag: false
text: null
decimal: 0.0
obj: null
내부 클래스 멤버 변수 초기화 예제
public class Company {
private String companyName;
private Employee manager; // 내부 클래스를 멤버 변수로 선언
// 내부 클래스
public class Employee {
private String name;
private int employeeId;
public Employee(String name, int employeeId) {
this.name = name;
this.employeeId = employeeId;
}
@Override
public String toString() {
return name + " (ID: " + employeeId + ")";
}
}
// 생성자에서 manager를 초기화하지 않음
public Company(String companyName) {
this.companyName = companyName;
// manager는 기본값인 null로 남게 됩니다.
}
public void displayInfo() {
System.out.println("Company: " + companyName);
System.out.println("Manager: " + (manager != null ? manager : "미정"));
}
public static void main(String[] args) {
Company company = new Company("TechCorp");
company.displayInfo();
}
}
출력은 다음과 같다.
Company: TechCorp
Manager: 미정
6. 요약 (정리본)
- final이 아닌 필드는 생성자에서 초기화하지 않아도 기본값으로 자동 초기화된다.
- 기본형 타입: 0, false 등
- 참조형 타입: null
- 다른 클래스를 멤버 변수로 가지는 경우
- 초기화하지 않으면 해당 변수는 null이 된다.
- null 상태에서 메서드 호출 시 NullPointerException 발생 가능.
- 생성자, 초기화 블록, 또는 선언 시 초기값 할당을 통해 null 상태를 방지하는 것이 좋다.
- 로컬 변수와 인스턴스 변수
- 인스턴스 변수: 자동 초기화됨.
- 로컬 변수: 반드시 사용 전에 초기화해야 함.
- 객체의 일관성 유지
- 객체의 중요한 멤버 변수(필드)는 반드시 초기화하여 객체의 일관성을 유지하고 잠재적인 오류를 방지해야 한다.
- 객체의 중요한 멤버 변수(필드)는 반드시 초기화하여 객체의 일관성을 유지하고 잠재적인 오류를 방지해야 한다.
- 생성자 오버로딩
- 여러 생성자를 정의하여 필요한 필드만 선택적으로 초기화할 수 있다.
- 하지만, 필요한 필드를 초기화하지 않으면 일부 필드가 기본값으로 남을 수 있으므로 주의해야 한다.
반응형
'JAVA' 카테고리의 다른 글
커맨드 패턴(Command Pattern)이란? (4) | 2024.10.04 |
---|---|
[Java] List를 Optional로 처리할 때 고려해야 할 사항 (1) | 2024.10.01 |
[Java] 자바의 약한 복사(Shallow Copy)란? (0) | 2024.09.21 |
[Java] 스트림(Stream)에서 가변 변수 사용 시 발생하는 문제 및 해결 방법 (0) | 2024.09.20 |
[Java] 스택 프레임과 변수의 생명 주기 이해하기 (0) | 2024.09.20 |