= 인스턴스가 생성될 때 호출되는 인스턴스 초기화 메서드
(인스턴스 초기화 = 인스턴스 변수를 초기화)
class TV {
TV() {//기본생성자}
TV(int remote) {//매개변수 있는 생성자}
}
위의 생성자를 보면 우리가 그동안 new 연산자를 활용해 인스턴스를 선언했던 부분은 생성자를 호출하는 코드였던 것이었던 것이었다..!!
※ 여기서 주의해야 할 점은
new
연산자가 인스턴스를 생성하는 것이지, 생성자가 인스턴스를 생성하는 것은 아니다! 생성자도 그저 하나의 특수한 메서드일뿐,,
모든 클래스에는 하나 이상의 생성자가 정의되어야 한다.
그러나 사실 우리가 이 단원을 배우기 전까지는 생성자를 정의하지 않았던 것 같은데, 어떻게 생성자가 알아서 정의되었던 것일까?
이건 컴파일러가 제공하는 기본 생성자
덕택이었다
컴파일 시 .java 클래스에 생성자가 없다면 자동적으로 위의 생성자 조건을 갖춘 기본 생성자를 컴파일러가 추가해준다. 만약 특별히 생성자를 만들어 줄 필요가 없다면, 기본 생성자를 불러와도 된다.
그러나 기본 생성자는 생성자를 따로 선언해주지 않는 경우에만 만들어지기 때문에, 생성자를 하나라도 선언했다면 그 형태에 맞게 선언해줘야 한다! 아니라면 오류가 발생한다. 아래의 예시를 보자.
class Data1 {int value;}
class Data2 {
int value;
Data2(int x) {value = x;}
}
class Ex1 {
public static void main(String[] args) {
Data1 d1 = new Data1();
Data2 d2 = new Data2(); //이거 무조건 컴파일 에러!!!!!
}
}
호출 시 값을 넘겨 받아 인스턴스를 생성해야 하는 생성자도 있다.
class Car {
String color;
String gearType;
int door;
Car() {}
Car(String c, String g, int d) {
color = c;
gearType = g;
door = d;
}
}
public static void main(String[] args) {
Car car1 = new Car();
car.color = "yellow";
car.gearType = "auto";
car.door = 4;
Car car2 = new Car("yello", "auto", 4);
//2개의 생성자를 활용해 만든 두 인스턴스는 사실 같은 속성을 갖지만
//누가 봐도 생성하는데 두번째가 더 간편하지 않은가?
}
= 생성자 끼리도 호출이 가능하단 말씀!
= this
: 인스턴스 자신을 가리키는 참조변수
(this를 통해 인스턴스변수에 접근)
= this()
: 생성자(같은 클래스의 다른 생성자 호출 시)
class Car {
String color;
String gearType;
int door;
Car() {this("white", "auto", 4);}
Car(String color) {
this(color, "auto", 4);
}
Car(String color, String gearType, int door) {
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
class Ex1 {
public static void main(String[] args) {
Car c1 = new Car();
Car c2 = new Car("blue");
//c1은 white, auto, 4
//c2는 blue, auto, 4
}
}
(생성자 내에서 초기화 작업도중 다른 생성자를 호출하게 되면, 호출된 다른 생성자 내에서도 멤버변수들의 값을 초기화 할 것이므로 다른 생성자를 호출하기 이전의 작업이 무의미해질 수도 있어서,,)
-> 현재 사용되고 있는 인스턴스와 같은 상태의 새로운 인스턴스를 만들고 싶을 때!
(두 인스턴스가 같은 상태이다 = 모든 인스턴스 변수가 같은 값을 갖는다)
//위의 예시에서 인스턴스 복사를 위한 새로운 생성자를 하나 더 만든다
Car(Car c) {
color = c.color;
gearType = c.gearType;
door = c.door;
}
-> 변수를 선언하고 처음으로 값을 저장하는 것
-> 가능하면 선언과 동시에 적절한 값으로 초기화 하는 것이 Good!
class Ex1 {
int x; // 인스턴스 변수
int y = x;
void method() {
int i;
int j = i; // 이러면 컴파일 에러!!
}
//지역변수인 i를 초기화하지 않은 상태에서 j에도 i 값을 부여했으므로
}
멤버변수(클래스 변수, 인스턴스 변수)와 배열의 초기화는 선택적이지만, 지역변수는 반드시 사용 전에 초기화를 해주어야 함
명시적 초기화
= 변수를 선언과 동시에 초기화하는 것
class Car {
int door = 4;
Engine e = new Engine();
}
class InitBlock {
static {//클래스 초기화 블럭}
{//인스턴스 초기화 블럭}
}
한번만
수행되고, 인스턴스 초기화 블럭은 인스턴스가 생성될 때 마다
수행된다.재사용성을 높이고 중복을 제거
= 객체지향프로그래밍의 목표!!위의 설명이 코드로 그려질만한 간단한 아래의 예제를 보자.
class Ex1 {
static {System.out.println("static { }");}
{System.out.pritnln("{ }");
public Ex1() {System.out.println("생성자");}
public static void main(String[] args) {
System.out.println("Ex1 e1 = new Ex1();");
Ex1 e1 = new Ex1();
System.out.println("Ex1 e2 = new Ex1();");
Ex1 e2 = new Ex1();
}
}
//출력결과
static { }
Ex1 e1 = new Ex1();
{ }
생성자
Ex1 e2 = new Ex1();
{ }
생성자
클래스 초기화 블럭 -> main 메서드 -> 인스턴스 생성 후 초기화 블럭
클래스 변수의 초기화 시점: 클래스가 처음 로딩될 때 단 한번
인스턴스 변수의 초기화 시점: 인스턴스가 생성될 때마다 초기화
클래스 변수의 초기화 순서: 기본값 -> 명시적 -> 클래스 초기화 블럭
인스턴스 변수의 초기화 순서: 기본값 -> 명시적 -> 인스턴스 초기화 블럭
-> 생성자
class InitTest {
static int cv = 1;
int iv = 1; //여가까지는 명시적 초기화
static {cv = 2;} //클래스 초기화 블럭
{iv = 2;} //인스턴스 초기화 블럭
InitTest() { //생성자
iv = 3;
}
}
- 클래스 초기화(클래스가 처음 메모리에 로딩될 때 수행)
기본값: cv = 0
명시적 초기화: cv = 1
클래스 초기화 블럭: cv = 2- 인스턴스 초기화(인스턴스 생성 시 차례대로 수행)
기본값: cv = 2, iv = 0
명시적 초기화: cv = 2, iv = 1
인스턴스 초기화 블럭: cv = 2, iv = 2
생성자: cv = 2, iv = 3
6-10)
(b) 객체를 생성 = new 연산자, 객체 초기화 = 생성자
6-11)
(b) this 키워드는 인스턴스 메서드 내에서만
6-16)
(e) 지역변수는 호출스택 영역에 생겼다가 소멸됨
** heap 영역에는 인스턴스 변수가!
6-17)
(f) 호출 스택에서 가장 상위 메서드가 아닌 메서드들은 대기중!
6-19)
public static void change(String str) {
str += "456";
}
public static void main(String[] args) {
String str = "ABC123";
System.out.println(str);
change(str);
System.out.println("After change:"+str);
}
//둘 모두 출력 결과는 ABC123
String은 불변 자료형이기 때문에 change 메서드에서 주어진 매개변수에 456을 추가하게 되면 새로운 문자열이 생성된다. 그러나 마지막 코드에서 불러오는 str은 원래의 주솟값이기 때문에 원래 문자열 그대로 출력된다!!