Java - 생성자, 초기화 블록
생성자와 초기화 블록에 대해 알아보자.
생성자
생성자는 인스턴스가 생성될 때 호출되는 인스턴스 초기화 메서드이다.
인스턴스가 생성될 때 필요한 동작을 위해 활용한다.
생성자 구조
생성자는 클래스 이름과 같은 이름을 활용하며 반환값이 없다.
반환값이 없으므로 void만 올 수 있고 이를 생략한다.
아래 코드에서 Car() {...}가 생성자이다.
public class ConstructorPractice {
public static void main(String[] args) {
Car car = new Car();
}
}
class Car {
Car() {
System.out.println("Hello world");
}
}
이를 실행해 보면 Hello world가 출력된다.
인스턴스가 생성되고 생성자가 동작했기 때문이다.
기본 생성자
생성자를 구현하지 않아도 모든 클래스는 하나의 생성자를 가진다.
컴파일러가 기본 생성자를 추가하기 때문이다.
기본 생성자는 '클래스명() { }'의 형태로 되어있다.
만약 생성자를 하나라도 직접 구현했다면 기본 생성자는 추가되지 않는다.
생성자 오버로딩
생성자는 오버로딩이 가능해 여러 경우를 고려해 활용할 수 있다.
아래는 Car 클래스에 대해 2개의 생성자를 만든 코드이다.
Car 인스턴스를 생성할 때 인자로 정수를 보내면 Car(int door)를, 인자를 비우면 Car()를 실행한다.
public class ConstructorPractice {
public static void main(String[] args) {
Car car1 = new Car();
Car car2 = new Car(2);
System.out.println("car1: " + car1.door);
System.out.println("car2: " + car2.door);
}
}
class Car {
int door;
Car() {
door = 4;
}
Car(int door) {
this.door = door;
}
}
생성자에서 다른 생성자 호출: this()
this()를 활용해 한 생성자에서 다른 생성자를 호출할 수 있다.
아래 코드는 Book 클래스에 대해 Book(), Book(String, int), Book(String, int, String) 3개의 생성자를 구현했다.
이후 Book()과 Book("Hello", 200) 인스턴스를 생성했다.
public class ConstructorPractice {
public static void main(String[] args) {
Book book = new Book();
System.out.println(book.toString());
}
}
class Book {
String name;
int pages;
String color;
Book() {
this("Default", 100);
}
Book(String name, int pages) {
this(name, pages, "Blue");
}
Book(String name, int pages, String color) {
this.name = name;
this.pages = pages;
this.color = color;
}
public String toString() {
return "name: " + name + ", pages: " + pages + ", color: " + color;
}
}
결과는 아래와 같다.
먼저 Book()으로 Book 인스턴스를 생성한 경우 아래 코드가 실행되었다.
Book() {
this("Default", 100);
}
Book() 생성자의 this(String, int)는 Book(String, int)를 호출했다.
Book(String name, int pages) {
this(name, pages, "Blue");
}
Book(String, int) 생성자는 Book(String, int, String)을 호출했다.
Book(String name, int pages, String color) {
this.name = name;
this.pages = pages;
this.color = color;
}
최종적으로 이 생성자에서 변수에 값을 담은 것이다.
단, this()는 생성자의 첫 줄에만 사용할 수 있다.
만약 아래와 같이 코드를 작성하면
Book() {
System.out.println("Test");
this("Default", 100);
}
컴파일 에러가 발생한다.
첫 줄에만 사용할 수 있는 이유는 만약 뒷 줄에 this()를 호출하면 기존의 초기화 작업이 무의미해지기 때문이다.
초기화 블록
초기화 블록은 마치 변수, 메서드처럼 클래스 초기화 블록과 인스턴스 초기화 블록이 있다.
실제로 차이점도 static의 유무이다.
public class InitBlockPractice {
{ } //인스턴스 초기화 블록
static { } //클래스 초기화 블록
}
클래스 초기화 블록은 클래스가 메모리에 적재될 때 1회만 실행되며
인스턴스 초기화 블록은 인스턴스가 생성될 때마다 실행된다.
기억해야 할 것은 초기화 블록이 생성자보다 먼저 수행된다는 점이다.
public class InitBlockPractice {
public static void main(String[] args) {
Book book = new Book();
System.out.println(book.name);
}
}
class Book {
String name;
{
name = "초기화 블록";
}
Book() {
name = "생성자";
}
}
위 코드의 출력 결과는 "생성자"이다.
생성자와 역할이 비슷한데 초기화 블록을 활용하는 이유는 생성자에서 반복되는 코드를 줄이기 위함이다.
아래는 사람이 1명씩 증가할 때마다 숫자를 1씩 늘려 입력하는 코드이다.
public class InitBlockPractice {
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person("Happy");
Person person3 = new Person("Cake");
System.out.println(person1.toString());
System.out.println(person2.toString());
System.out.println(person3.toString());
}
}
class Person {
static int count;
String name;
int num;
{
count++;
num = count;
}
Person() {
name = "None";
}
Person(String name) {
this.name = name;
}
public String toString() {
return "번호: " + num + ", 이름: " + name;
}
}
만약 초기화 블록을 사용하지 않으면 Person 클래스의 생성자는 중복되는 코드를 가져야 한다.
class Person {
static int count;
String name;
int num;
Person() {
name = "None";
count++;
num = count;
}
Person(String name) {
this.name = name;
count++;
num = count;
}
public String toString() {
return "번호: " + num + ", 이름: " + name;
}
}
만약 생성자가 더욱 많아지면 유지보수 측면에서 손해이다.
물론 아래 코드처럼 this()를 활용할 수는 있으나
class Person {
static int count;
String name;
int num;
Person() {
this("None");
}
Person(String name) {
this.name = name;
count++;
num = count;
}
public String toString() {
return "번호: " + num + ", 이름: " + name;
}
}
동일한 작업을 한다는 점을 중요히 여겨 초기화 블록으로 구분해 놓는 것이 이후의 작업에 좋다.
클래스 초기화 블록
아래 코드는 클래스 초기화 블록을 사용한 것이다.
public class InitBlockPractice {
public static void main(String[] args) {
Person person1 = new Person("Happy");
Person person2 = new Person("Cake");
System.out.println(person1.toString());
System.out.println(person2.toString());
}
}
class Person {
static int count;
String name;
int num;
static {
System.out.println("클래스 초기화 블록은 1회만 실행됩니다.");
}
Person(String name) {
this.name = name;
count++;
num = count;
}
public String toString() {
return "번호: " + num + ", 이름: " + name;
}
}
출력이 처음 1회만 된 것을 알 수 있다.