[JAVA] 초기화 블럭과 생성자

김민기·2021년 2월 11일
2

Java

목록 보기
10/20
post-thumbnail

멤버 초기화

초기화

  • 스태틱 변수와 인스턴스 변수는 초기화하지 않아도 기본값이 존재
    • 0 혹은 0에 준하는 값, 참조형이라면 null
  • 지역변수는 초기화하지 않으면 사용할 수 없음
    • 초기화 방법 : 명시적 초기화, 생성자, 초기화 블록(인스턴스, 클래스)

멤버 변수 초기화 순서

  • 시점
    • 스태틱 변수 - 클래스가 로딩될 때 딱 한번 초기화
    • 인스턴스 변수 - 인스턴스가 생성될 때마다 각 인스턴스 별로 초기화
  • 순서
    • 스태틱변수 - 기본값 → 명시적 초기화 → 클래스 초기화 블럭
    • 인스턴스 변수 - 기본값 → 명시적 초기화 → 인스턴스 초기화 블럭 → 생성자
class initTest{
		static int a = 1;    //1
		int b = 2;           //2
	
		static { a = 3; }    //3
		{ b = 4 ;}           //4
		initTest(){
			b = 5;             //5
		}

초기화 블럭

  • 초기화 블럭은 스태틱 초기화 블럭과 인스턴스 초기화 블럭이 있다. 이들은 클래스 내에 선언되며 각각 static {}, {} 로 작성한다

static block

  • 스태틱 초기화 블럭은 클래스가 로딩될 때 호출되며 따라서 각 클래스당 최초 1회만 실행된다.
  • 스태틱 블럭은 객체가 생성되기 이전에 수행되므로 인스턴스 멤버에 접근할 수 없다.
  • 클래스가 로딩 될 때 복잡한 초기화 과정을 수행해야 할 때 사용할 수 있다.

그렇다면 스태틱 메서드를 스태틱 초기화 블럭에서 호출할 수 있을까?

class SBTest{
	static int A;
	static {
		test();
		A++;
		System.out.println("static block  : " + A);		
	}

	static void test() {
		A++;
		System.out.println("static method  : " + A);
	}
}

/** 
	 * 출력은 다음과 같다
	 * static method : 1
	 * static block : 2
 */
  • 스태틱 블록에서도 스태틱 메소드를 호출할 수 있다.
  • 클래스 로딩시 멤버를 먼저 힙에 올린 후 초기화 블럭이 수행된다.
  • 이 때 클래스가 로딩되는 시점은 레퍼런스 변수를 만드는 것을 제외한 클래스가 최초로 호출될 때이다.
public class StaticBlockTest {
	public static void main(String[] args) {
		SBTest.test();
	}
}
/** 
	 * 출력은 다음과 같다
	 * static method : 1
	 * static block : 2
	 * static method : 3
 */
  • 위와같이 스태틱 메서드를 호출했을 때 다음과 같은 순서로 수행된다
    1. 클래스 로딩
      1. 멤버 초기화
      2. 스태틱 블록 호출
        1. 스태틱 메서드 호출
    2. 스태틱 메서드 호출
  • 따라서 첫번째 static method 출력은 스태틱 블록이 호출한 메서드, 이후 세 번째 스태틱 메서드 호출이 메인 메서드에서 호출한 것이다.

instance block

  • 인스턴스 초기화 블럭은 인스턴스가 생성될 때마다 수행된다.
  • 모든 생성자가 공통적인 내용을 수행해야할 때 작성하여 중복을 최소화할 수 있다.
  • 인스턴스를 new 키워드를 통해 생성했을때 수행 순서는 다음과 같다.
    1. 인스턴스 멤버 초기화
    2. 인스턴스 초기화 블럭 수행
    3. 생성자 수행
  • 따라서 인스턴스 메서드를 초기화 블럭 및 생성자에서 호출할 수 있다.
class SBTest{
	static int A ;
	static {
		test();
		A++;
		System.out.println("static block  : " + A);
	}

	{
		instanceMethod();
		A++;
		System.out.println("instance block  : " + A);
	}

	static void test() {
		A++;
		System.out.println("static method  : " + A);
	}

	public SBTest() {
		instanceMethod();
		A++;
		System.out.println("contsructor  : " + A);
	}

	private void instanceMethod() {
		A++;
		System.out.println("instance method : " + A);
	}
}

public class StaticBlockTest {
	public static void main(String[] args) {

		SBTest.test();
		SBTest a = new SBTest();		
		SBTest b = new SBTest();
		/** 
		 * 출력은 다음과 같다
		 * static method  : 1
		 * static block  : 2
		 * static method  : 3
		 * instance method : 4
		 * instance block  : 5
		 * instance method : 6
		 * contsructor  : 7
		 * instance method : 8
		 * instance block  : 9
		 * instance method : 10
		 * contsructor  : 11
		*/
	}
}
  • 위와 같이 인스턴스 블럭은 인스턴스가 생성될 때마다 수행되며 멤버의 초기화 이후, 생성자 호출 이전에 수행됨.

생성자

  • 생성자는 new 키워드를 통해 인스턴스를 생성할 때 인스턴스 블록 이후 수행한다.
  • 모든 생성자는 생성자의 가장 첫줄에 단 한번 다른 생성자를 호출해야한다. 첫 줄이 아니거나, 여러번 생성자를 호출 할 수 없다

this(), this, super(), super

  • this()
    • 생성자 내에서 다른 생성자를 호출할 때는 행성자의 원형 대신 this()를 통해 호출한다
    • this()의 매개변수를 달리하여 원하는 생성자를 호출
  • this
    • 클래스 내에서 해당 객체를 가르킴
    • this.name → 해당 객체의 name 변수 호출
    • this로 메소드, 생성자 내의 지역변수와 인스턴스 변수 구분하는 것이 컨벤션
      • this 키워드로는 static 변수에 접근할 수 없음. → 객체 내에서 포함되지 않기 때문에
    • 모든 인스턴스에는 this키워드가 포함된 채로 존재한다. 하지만 static 메소드는 인스턴스에 관련된 정보가 업스므로 static 메소드에서는 참조불가
    • 모든 인스턴트 메소드에서는 this 키워드가 자동으로 할당됨
  • super()
    • 슈퍼 클래스의 생성자를 호출
    • 생성자에서 명시적으로 다른 생성자를 호출하지 않았다면 자동으로 super() 호출
    • 따라서 수퍼클래스의 기본생성자가 없다면, 하위 클래스에서는 명시적으로 다른생성자를 호출해야 컴파일 에러가 나지 않는다.
    • 모든 클래스는 Object를 상속받으므로 최소한 한개의 생성자는 Object() 호출
  • super
    • 서브클래스의 인스턴스에서 슈퍼클래스의 멤버를 호출할 때 사용
    • 서브클래스 인스턴스 메서드에 자동할당.
    • 인스턴스 호출이므로 static 메서드에서는 사용 불가능

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

class SuperClass{
	int A;
	public SuperClass() {
		//super() 가 생략됨
		A++;
	}
}

class SubClass extends SuperClass{
	
	public SubClass(){
		//super() 가 생략됨
		A--;
	}

	public SubClass(int num) {
		//super() 가 생략됨
		A += num;
	}

	public SubClass(int num, int num2) {
		this(num);
		A -= num2;
	}
}

public class ConstructorTest {
	public static void main(String[] args) {
		SuperClass s = new SubClass(10);
		System.out.println(s.A);

		SuperClass s2 = new SubClass(10, 5);
		System.out.println(s2.A);
		/**
		 * 출력되는 값
		 * 11
		 * 6
		 */
	}
}
  • 모든 생성자는 this()나 super()로 다른 생성자를 호출해야한다. 이 때 명시적으로 작성하지 않는다면 컴파일러가 자동으로 super()를 호출한다.
  • 클래스 내에 아무런 생성자도 작성하지 않았다면 컴파일러가 기본 생성자를 추가한다.
  • 명시적으로 생성자를 작성했다면 기본생성자를 생성하지 않는다.

class SuperClass{
	int A;
	public SuperClass(int num) {
		//super() 가 생략됨
		A =+ num;
	}
}

class SubClass extends SuperClass{
	public SubClass(){
		//super() 가 생략됨
		//super() 가 존재하지 않으므로 컴파일 에러
		A--;
	}

	public SubClass(int num, int num2) {
		super(num);
		A -= num2;
	}
}
  • 위의 코드는 슈퍼클래스의 생성자를 작성했기 때문에 컴파일러가 기본생성자를 만들지 않는다.
  • 서브클래스의 기본생성자에서 명시적 생성자 호출을 하지 않았기 때문에 super()를 호출하게 되는데, super()가 존재하지 않으므로 컴파일 에러.
  • 서브클래스의 두번째 생성자에서는 슈퍼클래스의 생성자를 명시적으로 호출했기 때문에 super()를 호출하지 않으므로 정상적으로 동작한다.

생성자를 통한 인스턴스 복사

  • 생성자의 매개변수로 자기 자신의 레퍼런스를 받을 수 있다.
class Car{
	int a
	public Car(Car c){
		this(c.a)
	}
	public Car(int a){
		this.a = a;
	}
}
//car 객체를 복제한 객체의 레퍼런스를 만듬
  • 생성자의 매개변수로 레퍼런스를 받아 해당 객체의 내용을 복사하는 과정
profile
민기1

2개의 댓글

comment-user-thumbnail
2021년 2월 22일

무야호

답글 달기
comment-user-thumbnail
2024년 5월 16일

덕분에 호출 순서에 대해 이해했습니다! 감사합니다~~

답글 달기