JAVA

[JAVA] 내부 클래스

shb 2022. 2. 17. 18:37

Inner Class(내부 클래스)
 1. Member inner class(멤버 내부 클래스)다른 클래스 내부에서 선언된 클래스
 2. Static inner class(static 내부 클래스)다른 클래스의 내부에서 static으로 선언된 클래스
 
 3. Local class(지역 클래스)
   1) Local inner class(지역 내부 클래스)메소드 내부에서 선언된 클래스
   2) Anonymous inner class(익명 내부 클래스)이름이 없는 local class

 

Member inner class(멤버 내부 클래스)

TestOuter 클래스 내부에서 TestInner 클래스를 정의
TestOuter: 외부 클래스(Outer Class, Enclosing Class)
TestInner: 멤버 내부 클래스(Member Inner Class)
1) 멤버 내부 클래스는 외부 클래스의 인스턴스가 생성된 이후에야 인스턴스 생성이 가능함.
2) 멤버 내부 클래스는 외부 클래스의 모든 멤버들(private 포함)을 사용 가능

package com.lec.java.inner01;

/*
 	Member inner class(멤버 내부 클래스)
 	
	TestOuter 클래스 내부에서 TestInner 클래스를 정의
	TestOuter: 외부 클래스(Outer Class, Enclosing Class)
	TestInner: 멤버 내부 클래스(Member Inner Class)
	1) 멤버 내부 클래스는 외부 클래스의 인스턴스가 생성된 이후에야 
	인스턴스 생성이 가능함.
	2) 멤버 내부 클래스는 외부 클래스의 모든 멤버들(private 포함)을 사용 가능
*/


// 클래스: 멤버 변수들 (+ 생성자들) + 멤버 메소드들 = 데이터 타입
public class TestOuter {
	// 멤버 변수:
	private int value;
	
	// 생성자:
	public TestOuter() {}
	public TestOuter(int value) {
		this.value = value;
	}
	
	// 메소드:
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
	}

	
	// Member Inner Clsss 정의:
	public class TestInner {
		// 멤버변수
		private int innerValue;
		
		// 생성자
		public TestInner() {}
		public TestInner(int val) {
			this.innerValue = val;
		}
		
		// 멤버 메소드
		public void printOuterValue() {
			System.out.println("value = " + value);
			// 멤버 내부 클래스는 외부 클래스의 멤버를 직접 접근 가능
		}
		
		public void printInnerValue(){
			System.out.println("innerValue = " + innerValue);
		}
		
	}
	
	
	
} // end class TestOuter
public class Inner01Main {

	public static void main(String[] args) {
		System.out.println("Member Inner Class(멤버 내부 클래스)");
		
		// 외부 클래스의 인스턴스 생성
		TestOuter out = new TestOuter(100);
		
		// 멤버 내부 클래스의 인스턴스 생성
		// 멤버 내부 클래스의 이름: [외부클래스 이름].[멤버 내부클래스 이름]
		// [외부클래스 이름].[내부클래스 이름] 참조변수 =
		//      [외부클래스 인스턴스].new 내부클래스 생성자();
		TestOuter.TestInner in = out.new TestInner(111);
		in.printOuterValue();
		in.printInnerValue();
		
		// 하나의 외부 클래스 인스턴스를 이용해서
		// 멤버 내부 클래스의 인스턴스는 여러개를 생성할 수 있다.
		TestOuter.TestInner in2 = out.new TestInner(123);
		in2.printOuterValue();
		in2.printInnerValue();

		TestOuter.TestInner in3 = out.new TestInner(999);
		in3.printOuterValue();
		in3.printInnerValue();
		
		
		TestOuter.TestInner in4 = new TestOuter(30).new TestInner(330);
		in4.printOuterValue();
		in4.printInnerValue();
		
	} // end main()

} // end class Inner01Main

언제 내부 클래스로 설계?  
 
 상속 관계로 묶을 수는 없지만,
 A라는 객체가 생성된 이후에야 존재할 수 있는 B라는 객체가 있다고 한다면,
 B를 A의 멤버 '내부 클래스'로 선언한다.
 (예) 컴퓨터-CPU/메모리, 자동차-타이어

 반면
 '햄버거 세트 메뉴' 객체의 경우
 햄버거 객체와 콜라 객체는 별개의 객체로도 존재 가능하니까
 '햄버거' 와 '콜라' 는 '세트메뉴' 객체의 '멤버변수'로 붙도록 하는게 낳다
 
  is-a  : 상속관계
  has-a (종속) : 멤버내부클래스
  has-a (독립) : 멤버변수

package com.lec.java.inner02;

public class Car {
	// 멤버 변수( outer )
	private String color;
	
	// 생성자
	public Car(String color) {
		this.color = color;
	}
	
	// 메소드
	public void displayCarInfo() {
		System.out.println("color: " + color);
	}
	
	// 멤버 내부 클래스
	public class Tire {
		private int radius;
		
		public Tire(int radius) {
			this.radius = radius;
		}
		
		public void displayInfo() {
			System.out.println("--- 타이어 정보 ---");
			System.out.println("차량 color: " + color);
			System.out.println("tire : " +  radius);
		}
	}
	
} // end class Car
public class Inner02Main {

	public static void main(String[] args) {
		System.out.println("멤버 내부 클래스 활용");
		
		Car myCar = new Car("Indigo Blue");
		Car.Tire myTire1 = myCar.new Tire(17);
		Car.Tire myTire2 = myCar.new Tire(19);
		
		myTire1.displayInfo();
		System.out.println();
		myTire2.displayInfo();
		
		
	} // end main()

} // end class Inner02Main
package com.lec.java.inner03;

public class TestOuter {
	private int value;  // 1.
	
	public TestOuter(int value) {
		this.value = value;
	}
	
	public int getValue() {
		return value;
	}
	
	// 멤버 내부 클래스
	public class TestInner {
		private int value;  // 2.
		
		public TestInner(int value) {
			this.value = value;
		}
		
		public void printValue(int value) {  // 3.
			System.out.println("value = " + value);  // 3
			System.out.println("this.value = " + this.value);  // 2.
			System.out.println("TestOuter.this = " + TestOuter.this.value);  // 1
		}
	}
	

} // end class TestOuter
package com.lec.java.inner03;

public class Inner03Main {

	public static void main(String[] args) {
		System.out.println("외부/내부 클래스의 this");
		
		TestOuter out = new TestOuter(100);  // 1.
		TestOuter.TestInner in1 = out.new TestInner(111);  // 2.
		in1.printValue(10); // 3.

	} // end main()

} // end class Inner03Main

Nested Class(중첩 클래스):
    다른 클래스의 내부에서 멤버로 정의된 클래스인데, 
    static 키워드가 사용된 내부 클래스 (static inner class)
   
   static클래스의 인스턴스가 생성되지 않아도 사용될 수 있는 멤버(변수, 메소드)에 사용
     따라서, nested class는 외부 클래스의 인스턴스를 생성하지 않고, 내부 클래스의 인스턴스를 생성할 수 있다.
   
   nested(static) class는 
   (외부 클래스에서) static으로 선언된 변수와 메소드만 사용 가능
   
중첩 클래스의 인스턴스 생성:
타입 참조변수 = new 생성자()
중첩 클래스의 이름(타입): [외부클래스 이름].[내부클래스 이름]
중첩 클래스의 생성자: new [외부클래스 이름].생성자()

 

package com.lec.java.inner04;

public class TestOuter {

	// 멤버변수
	private int value;  // 인스턴스 변수
	private static int count = 100; // 클래스 변수 (static)
	
	// 생성자
	public TestOuter(int value) {
		this.value = value;
	}
	
	public static class TestNested {
		
		public void displayInfo() {
//			System.out.println(value);  static 클래스에너 외부의 non-static 사용 못함
			System.out.println("count = " + count);
		}
		
		public static void println() {}
	}
	
	
	
} // end class TestOuter


// TestOuter: 외부 클래스(outer class, enclosing class)
// TestNested: 중첩 클래스(nested class, static inner class)

 

public class Nested01Main {

	public static void main(String[] args) {
		System.out.println("Nested Class(중첩 클래스): static inner class");

		TestOuter.TestNested nest1 = new TestOuter.TestNested();
		nest1.displayInfo();
		
		TestOuter.TestNested.println();
		
		
	} // end main()

} // end class Nested01Main

Local Inner Class블록({ ... }) 내부에서 정의된 클래스
   1. 정의가 된 블록(메소드) 내부에서만 사용 가능 - 참조변수 선언, 인스턴스 생성
   2. 접근 수식어(public, protected, private)는 쓸 수 없다.
   3. 외부 클래스의 멤버 변수(private 포함)는 모두 사용 가능
   4. effectively final인 지역변수나 매개변수만 사용 가능
 
 effectively final 변수란?
  1) final로 선언된 변수, 또는
  2) 한 번 초기화가 된 이후로 값이 변경되지 않은 변수(Java 8에서 도입)

package com.lec.java.inner05;

public class TestOuter {
	// TestOuter 클래스의 멤버 변수
	private int num1 = 100;
	
	// TestOuter 클래스의 멤버 메소드
	public void localMethod() {
		
		int num2 = 200;
		
		// localMethod() 내부에서 정의된 Local inner class
		class TestLocal {
			
			private int num3 = 300;
			
			public void showNumbers() {
				// 외부 클래스의 멤버변수 출력
				System.out.println("num1 = " + num1);
				
				// 지역클래스의 동일 scope 의 지역변수 출력
				System.out.println("num2 = " + num2);
				
				// 로컬 내부 클래스 (자신)의 멤버변수 출력
				System.out.println("num3 = " + num3);
			}
			
		} // end local inner class
		
		// 지역 내부 클래스 인스턴스 생성은 클래스가 정의된 메소드(블럭) 안에서만 가능
		TestLocal local = new TestLocal();
		
		// num2 = 400;  // num2 값을 변경하면.. 아래 showNumbers()에선
					// 200 이 찍혀야 하나? 400이 찍혀야 하나?
					// 그래서 로컬내부클래스에서 사용 가능한 지역의 변수는 
					// 반드시 effectively final 이어야 한다
					// 	  즉 한번 초기화 된후 값이 변경되지 않거나, final 이어야 한다.
		
		local.showNumbers();
		
	} // end localMethod()
	

} // end class TestOuter
package com.lec.java.inner05;

public class TestOuter2 {
	 
	//TestOuter 클래스의 멤버 변수
	private int num = 100; // ① 
	
	// TestOuter 클래스의 멤버 메소드
	public void localMethod() {
		
		int num = 200;  // ②
		
		class TestLocal {
			
			private int num = 300;  // ③
			
			public void showNumber() {
				
				int num = 400;   // ④ 
				
				// ①, ②, ③, ④  출력 가능?
				
				System.out.println("TestOuter2.this.nu = " + TestOuter2.this.num);  // ①
				System.out.println("this.num = " + this.num); // ③
				System.out.println("num = " + num);  // ④
				
				//② 는 사용 불가!
				
			}
			
		} // end local inner class
		
		TestLocal local = new TestLocal();
		local.showNumber();
		
	} // end localMethod()
	

} // end class TestOuter
public class Local01Main {

	public static void main(String[] args) {
		System.out.println("Local Inner Class(지역 내부 클래스)");

		// 외부 클래스 생성
		TestOuter out = new TestOuter();
		out.localMethod();
		
		System.out.println();
		TestOuter2 out2 = new TestOuter2();
		out2.localMethod();
		
	} // end main()

} // end class Local01Main


 지역 클래스는 메소드 실행이 끝나게 되면 정의 자체가 사라지게 되는 클래스임.
 메소드 내부에 정의된 지역 클래스 타입을 리턴하는 메소드는 만들 수 없다.
 경우에 따라서는, 지역 클래스에 정의된 메소드를 외부에서 직접 사용하고자 하는 경우가 발생할 수도 있습니다.
 그걸 가능하게 하는 방법이 

 인터페이스(interface) + 다형성(polymorphism):
 
 1. 외부에서 사용하고 싶은 메소드를 선언한 인터페이스를 작성
 2. 메소드의 리턴타입은 정의한 인터페이스 타입으로 정의
 3. 로컬 클래스는 인터페이스를 구현(implements)하도록 정의
 4. 로컬 클래스의 인스턴스를 생성하고 리턴해줌

package com.lec.java.inner06;

public class Person {
	// Person 외부 클래스의 멤버 변수
	private String name;
	
	// Person 외부 클래스의 생성자
	public Person(String name) {
		this.name = name;
	}
	
	public void readAge(final int age) {
		
//		age = 10;
		
		// local inner class
		class PersonWithAge {
			public void readInfo() {
				System.out.println("이름: " + name);
				System.out.println("나이: " + age);
			}
		}
		
		PersonWithAge p = new PersonWithAge();
		p.readInfo();
		
	}
	
	/*
	public PersonWithAge createInstance(int age) {
		class PersonWithAge {
			public void readInfo() {
				System.out.println("이름: " + name);
				System.out.println("나이: " + age);
			}
		}
		
		PersonWithAge p = new PersonWithAge();
		return p;
	}
	*/
	
	/*
	 지역 클래스는 메소드 실행이 끝나게 되면 정의 자체가 사라지게 되는 클래스임.
	 메소드 내부에 정의된 지역 클래스 타입을 리턴하는 메소드는 만들 수 없다.
	 경우에 따라서는, 지역 클래스에 정의된 메소드를
	 외부에서 직접 사용하고자 하는 경우가 발생할 수도 있습니다.
	 그걸 가능하게 하는 방법이 
	
	 인터페이스(interface) + 다형성(polymorphism):
	 
	 1. 외부에서 사용하고 싶은 메소드를 선언한 인터페이스를 작성
	 2. 메소드의 리턴타입은 정의한 인터페이스 타입으로 정의
	 3. 로컬 클래스는 인터페이스를 구현(implements)하도록 정의
	 4. 로컬 클래스의 인스턴스를 생성하고 리턴해줌
	*/
	
	
	//  2. 메소드의 리턴타입은 정의한 인터페이스 타입으로 정의
	public MyReadable createInstance(int age) {
		
		// 3. 로컬 클래스는 인터페이스를 구현(implements)하도록 정의
		class PersonWithAge implements MyReadable {
			@Override
			public void readInfo() {
				System.out.println("이름: " + name);
				System.out.println("나이: " + age);
			}
		}
		
		// 4. 로컬 클래스의 인스턴스를 생성하고 리턴해줌
		MyReadable person = new PersonWithAge();  // 다형성
		
		return person;
	}
	
} // end class Person


// 1. 외부에서 사용하고 싶은 메소드를 선언한 인터페이스를 작성

interface MyReadable {
	public abstract void readInfo();
}
package com.lec.java.inner06;

public class Local02Main {

	public static void main(String[] args) {
		System.out.println("Local 내부 클래스의 활용");
		
		Person person = new Person("ABC");
		person.readAge(10);

		MyReadable r = person.createInstance(16);
		r.readInfo();  // 다른 클래스에서도 로컬클래스가 정의한 메소드를 사용 가능
		
	} // end main()

} // end class Local02Main

Anonymous inner class(익명 내부 클래스):
 - 이름이 없는 local inner class
 - 이름이 없기 때문에 생성자로 만들 수가 없습니다.
 - 클래스의 정의와 동시에 인스턴스를 생성합니다.

익명 내부 클래스:
 인스턴스 생성과 동시에 이름없는 클래스가 정의됨.

 new 인터페이스() { 익명 클래스 내부 작성 };
 new 부모클래스() { 익명 클래스 내부 작성 };

익명 내부 클래스 (Anonymous Inner class) 는
 인터페이스 뿐 아니라, 일반 클래스, 추상클래스 등도 가능하다.
 상속 받은 (이름없는) 클래스의 인스턴스를 생성.
 - 멤버 변수/ 메소드 선언
 - 메소드 오버라이딩 

package com.lec.java.inner07;

public class Person {
	
	// 외부 클래스 멤버변수
	private String name;
	
	// 외부 클래스 생성자
	public Person(String name) {
		this.name = name;
	}
	
	public MyReadable createInstance(int age) {
		
		// 익명 내부 클래스:
		// 인스턴스 생성과 동시에 클래스가 정의됨.
		// new 인터페이스() { 익명 클래스 내부 작성 }; 
		// new 부모클래스() { 익명 클래스 내부 작성 };
		
		// MyReadable 을 implement 한 이름없는 익명 class 를 정의하고 그 instance 생성
		// 익명클래스 구문 사용!!

		return new MyReadable() {
			
			@Override
			public void readInfo() {
				System.out.println("이름: " + name);
				System.out.println("나이: " + age);
			}
		};
		
	}
	
	
	
} // class Person

interface MyReadable {
	public abstract void readInfo();
}
public class Anonymous01Main {

	public static void main(String[] args) {
		System.out.println("Anonymous Inner Class(익명 내부 클래스)");
		
		Person p = new Person("QWERTY");
		MyReadable r = p.createInstance(20);
		r.readInfo();
		
		
		//------------------------------------------------
		System.out.println();
		MyReadable r2 = new MyReadable() {
			
			int a = 10;   // 익명클래스의 멤버변수 추가
			
			@Override
			public void readInfo() {
				System.out.println("readInfo()");
				System.out.println("a = "+ a);
			}
		};
		r2.readInfo();
		
		System.out.println(r2);
		
		new MyReadable() {
			
			int a = 200;
			
			@Override
			public void readInfo() {
				System.out.println(a + 100);
			}
			
		}.readInfo();
		
		//------------------------------------------------
		System.out.println();
		System.out.println(new MyClass().methodA(30));
		System.out.println(new MyClass() {
			int d = 400;
			
			@Override
			int methodA(int c) {
				return a + b + c + d;
			}
		}.methodA(30));
		
	
		int k = 300;
//		k = 150;   // 익명클래스는 기본적으로 local inner class 이기에
				// 동일 scope 의 지역변수를 사용할때는
				// 그 지역변수는 effective final 이어야 한다
		int result = new MyAbstract() {

			@Override
			int methodA(int a) {
				return a + n + d + k;
			}
			
		}.methodA(100);
		System.out.println("result = " + result);

		System.out.println("\n프로그램 종료");
	} // end main()

} // end class

//익명 내부 클래스 (Anonymous Inner class) 는
//인터페이스 뿐 아니라, 일반 클래스, 추상클래스 등도 가능하다.
//상속 받은 (이름없는) 클래스의 인스턴스를 생성.
//	- 멤버 변수/ 메소드 선언
//	- 메소드 오버라이딩 

abstract class MyAbstract{

	int n = 10;
	int d = 2;
	
	abstract int methodA(int a);

} // end class

class MyClass {

	int a = 10;
	int b = 20;
	
	int methodA(int c) {
		return a + b + c;
	}

} // end class

- 위와 같이 특정 추상 메소드만 implement 하는 목적으로 설계되는 인터페이스의 이름은 보통 ~ able 로 작명 경우가 많다.  
- 자바에서 제공하는 대표적으로 많이 사용하는 이러한 인터페이스들.
- Serializable, Cloneable, Readable, Appendable, Closeable,  
- AutoCloseable, Observable, Iterable, Comparable, Runnable,
- Callable, Repeatable, 

package com.lec.java.inner08;

public class Anonymous02Main {

	public static void main(String[] args) {
		System.out.println("익명 내부 클래스 활용");
		
		System.out.println();
		System.out.println("1. 이름있는 클래스를 사용하여 구현");
		Calculable tc1 = new TestMyMath();
		double result = tc1.operate(1.0, 2.0);
		System.out.println("result = " + result);
		
		System.out.println();
		System.out.println("2. 익명 클래스 사용");
		Calculable tc2 = new Calculable() {
			
			@Override
			public double operate(double x, double y) {
				return x - y;
			}
		};
		result = tc2.operate(1.0, 2.0);
		System.out.println("result = " + result);

		
	} // end main()

} // end class Anonymous02Main

interface Calculable {
	public abstract double operate(double x, double y);
}

/*
위와 같이 특정 추상 메소드만 implement 하는 목적으로 설계되는 인터페이스의 이름은
보통 ~ able 로 작명 경우가 많다.  
자바에서 제공하는 대표적으로 많이 사용하는 이러한 인터페이스들.
Serializable, Cloneable, Readable, Appendable, Closeable,  
AutoCloseable, Observable, Iterable, Comparable, Runnable,
Callable, Repeatable, 
*/


class TestMyMath implements Calculable {

	@Override
	public double operate(double x, double y) {
		return x + y;
	}
	
}

* 이벤트 중심의 프로그래밍 & 리스너 (Listener) 
이벤트 중심의 프로그래밍 (웹, 앱, GUI 환경 등..) 에선
특정 '객체' 에  특정 '이벤트' 가 발생하면 어떠한 '동작'을 수행하도록 프로그래밍 한다.
  
 ex) 'A버튼 객체' 는 '클릭' (이벤트)가 발생하면 '~~동작' 을 수행
     'B버튼 객체' 는 '더블클릭' (이벤트)가 발생하면 '~~동작' 을 수행
   
- 이때 '객체' 는  API 제공사 측에서 제공하는 코드이고, 공통적인 기본 동작은 수행하나
이 API 를 사용하는 사용자(개발자) 들이 추가해야 하는 코드를 위해 API 제공사에서 인터페이스 등으로 제공받게 된다. 

package com.lec.java.inner09;

//Button 객체를 개발하여 제공하는 측의 코드
public class Button {

	String name;
	
	public Button(String name) {
		this.name = name;
	}
	
	// 클릭시 수행하는 리스너  제공
	// 리스너 인터페이스 : OnClickListener
	//         - 를릭시 동작 메소드 : onClick();
	// 장착 메소드 : setOnClickListener
	//---------------------------------------------------
	// 리스너 인터페이스
	// TODO
	
	// 장착 리스너
	// TODO  
	
	// 리스너 장착 메소드
	// TODO
	
	//----------------------------------------------
	public void actionClick() {
		System.out.println(name + " 버튼 클릭!");
		
		// TODO
		
	}
	
	
	// TODO
	// 더블클릭시 수행하는 리스너를  제공해보세요
	// 리스너 인터페이스 : OnDblClickListener
	//         - 더블를릭시 동작 메소드 : onDblClick();
	// 장착 메소드 : setOnDBlClickListener
	
	//---------------------------------------------------
	// 리스너 인터페이스
	// TODO
	
	// 장착 리스너
	// TODO  
	
	// 리스너 장착 메소드
	// TODO
	
	//----------------------------------------------
	public void actionDblClick() {
		System.out.println("--------------------");
		System.out.println(name + " 버튼 클릭!");
		System.out.println("버튼 클릭 동작 공통 처리 동작들A");

		// TODO : 장착된 리스너의 동작 수행
		
		System.out.println("버튼 클릭 동작 공통 처리 동작들B");
		System.out.println("--------------------");
	}
	
	
	
	
} // end class
// Button 객체를 사용하는 코드 
public class Anonymous09Main {
	public static void main(String[] args) {
		System.out.println("익명 클래스 응용 : Listener");
		
		Button btnOk = new Button("OK");
		Button btnCancel = new Button("CANCEL");
		btnOk.actionClick();
		btnCancel.actionClick();

		System.out.println();
		// 리스너 장착: 익명클래스
		// TODO
		
		
		System.out.println("\n 프로그램 종료");
	} // end main()
	
} //end class

Builder Pattern

 * 객체를 생성하는 방식 3가지
1 점층적 생성자 방식
2 자바빈 패턴 방식
3 Builder Pattern  
 *  객체를 생성할 때 많이 사용하는 패턴
 *  생성자에 매개변수가 많다면 빌더를 고려하자!
 *  static inner class 사용하여 구현

 https://johngrib.github.io/wiki/builder-pattern/#%EB%B9%8C%EB%8D%94-%ED%8C%A8%ED%84%B4effective-java-%EC%8A%A4%ED%83%80%EC%9D%BC

 

점층적 생성자 패턴

점층적 생성자 패턴을 만드는 방법
  필수 인자를 받는 필수 생성자를 하나 만든다.
 1 개의 선택적 인자를 받는 생성자를 추가한다.
 2 개의 선택적 인자를 받는 생성자를 추가한다.
…반복
 모든 선택적 인자를 다 받는 생성자를 추가한다.

장점
 - new Member("홍길동", "출신지역 비공개", "취미 비공개") 같은 호출이 빈번하게 일어난다면, 
 - new Member("홍길동")로 대체할 수 있다.

단점
- 다른 생성자를 호출하는 생성자가 많으므로, 인자가 추가되는 일이 발생하면 코드를 수정하기 어렵다.
- 코드 가독성이 떨어진다.
- 특히 인자 수가 많을 때 호출 코드만 봐서는 의미를 알기 어렵다.

자바빈 패턴(JavaBeans pattern)
 setter메서드를 이용해 생성 코드를 읽기 좋게 만드는 것

 장점
이제 각 인자의 의미를 파악하기 쉬워졌다.
복잡하게 여러 개의 생성자를 만들지 않아도 된다.

단점
- 객체 일관성(consistency)이 깨진다.
- 1회의 호출로 객체 생성이 끝나지 않았다.
- 즉 한 번에 생성하지 않고 생성한 객체에 값을 떡칠하고 있다.
- setter 메서드가 있으므로 변경 불가능(immutable)클래스를 만들 수가 없다. (즉 final 멤버변수에선 사용 불가)
- 스레드 안전성을 확보하려면 점층적 생성자 패턴보다 많은 일을 해야 한다.

Builder Pattern
 장점
- 각 인자가 어떤 의미인지 알기 쉽다.
- setter 메소드가 없으므로 변경 불가능(immutable) 객체를 만들 수 있다.
- 한 번에 객체를 생성하므로 객체 일관성이 깨지지 않는다.
- build() 함수가 잘못된 값이 입력되었는지 검증하게 할 수도 있다.