JAVA 7일차

김재현·2022년 8월 5일
0

Java

목록 보기
8/15

매개변수가 있는 생성자

  • 생성자도 메서드처럼 매개변수를 선언, 호출 시 값을 넘겨받아 인스턴스의 초기화 작업에 사용할 수도 있다. 인스턴스마다 각기 다른 값으로 초기화되어야 하는 경우가 많기 때문에 매개변수를 사용한 초기화는 매우 유용하다.
class Car{
	String color;       // 색상
    String gearType;    // 변속기 종류
	int door;           // 문의 개수
    
    Car (){}             // 기본생성자
    Car (String c, String g, int d){
    	color c = c;
        gearType = g;
        door = d;
    }
}
  • Car 인스턴스를 생성할 때, 생성자 Car()을 사용한다면, 인스터스를 생성한 다음에 인스턴스 변수들을 따로 초기화해주어야 하지만, 매개변수가 있는 생성자 Car (String c, String g, int d)를 사용한다면 인스턴스를 생성하는 동시에 원하는 값으로 초기화를 할 수 있게 된다.
Car c = new Car();
c.color = "white";
c.gearType = "auto";
c.door = 4;

Car c = new Car("white", "auto", 4)로 한결 직관적이고 간결하게 만들 수 있다.

인스턴스를 생성할 때는 다음의 2가지 사항을 결정해야 한다.
1) 클래스 : 어떤 클래스의 인스턴스를 생성할 것인가?
2) 생성자 : 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?

Car c1과 Car c2는 동일하지만, c2의 생성자 선언이 더 간결하고 직관적이다.

생성자에서 다른 생성자 호출 - this()

  • 생성자 간에도 서로 호출이 가능하다. 단, 아래 두 조건을 만족시켜야 한다.

1) 생상자의 이름으로 클래스 대신 this 사용.
2) 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫 줄에서만 호출 가능.

  • 생성자에서 다른 생성자를 첫 줄에서만 호출이 가능하도록 한 이유는 생성자 내에서 초기화 작업도중에 다른 생성자를 호출하게 되면, 호출된 다른 생성자 내에서도 멤버변수들의 값을 초기화 할 것이므로 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질 수 있기 때문이다.
Car(String color){
	door = 5;
    Car(color, "auto", 4);
}
  • 위의 예제는 오류가 난다. 생성자 내에서 다른 생성자를 호출할 때 클래스 이름인 Car 대신 this를 사용해야하는데 그러지 않았고, 첫 째 줄이 아닌 두번째 줄에 사용했기 때문이다.
Car() {
	color = "white";
    gearType = "auto";
    door = 4;
}

Car(){
	this("white", "auto", 4);
}

로 나타낼 수 있다.

  • 두 코드는 같은 일을 하지만, 아래 코드는 생성자 Car(String color, String gearType, int door)을 활용하여 더 간략히 한 것이다.
  • 같은 클래스 내의 생성자들은 일반적으로 서로 관계가 깊은 경우가 많아, 이처럼 서로 호출하도록 하여 유기적으로 연결해주면 더 좋은 코드를 얻을 수 있다. 그리고 수정이 필요한 경우에 보다 적은 코드만을 변경하면 되므로 유지보수도 쉬워진다.

객체 자신을 가리키는 참조변수 - this

Car(String c, String g, int d){
	color = c;          // color는 iv(인스턴스변수) c는 lv(지역변수)
    gearType = g;
    door = d;
}
  • 위 코드에서 color = c;는 생성자의 매개변수로 선언된 지역변수 c의 값을 인스턴스변수 color에 저장한다. 이때 color와 c는 이름만으로 구분되므로 아무런 문제가 없다.
Car(String color, String gearType, int door){
	this.color = color;
    this.gearType = gearType;
    this.door = door;
}
  • 이번 코드는 생성자의 매개변수로 선언된 변수 이름이 color로 인스턴스 변수 color와 같고, 이 경우 이름만으로는 두 변수가 구별이 가지 않는다. 이런 경우 인스턴스 변수 앞에 this를 붙이면 된다.
    그렇게 하면 this.color는 인스턴스 변수, color는 지역변수라고 구별 가능하다. 만약 둘 다 color를 사용한다면 모두 지역변수로 간주된다.
  • 생성자와 매개변수로 인스턴스변수들의 초기값을 제공받는 경우가 많기 때문에 매개변수와 인스턴스변수의 이름이 일치하는 경우가 자주 있다. 이때는 매개변수의 이름을 다르게 하는 것보다 this를 사용해서 구별되도록 하는 것이 의미가 더 명확하고 이해하기 쉽다.
  • this는 참조변수로 인스턴스 자신을 가리킨다. 참조변수를 통해 인스턴스의 멤버에 접근할 수 있는 것처럼, this를 사용해서 구별되도록 하는 것이 의미가 더 명확하고 이해하기 쉽다.
    this를 사용할 수 있는 것은 인스턴스멤버 뿐이다.
  • 사실, 생성자를 포함한 모든 인스턴스메서드에서는 자신이 관련된 인스턴스를 가리키는 참조변수 this가 지역변수로 숨겨져있다.

    this : 인스턴스 자신을 가리키는 참조변수. 인스턴스 주소가 저장되어 있음.
    this(), this(매개변수) : 생성자, 같은 클래스의 다른 생성자를 호출할 때 사용.

  • this와 this()는 비슷하게 생겼을 뿐 완전히 다르다. this는 참조변수이고, this()는 생성자이다.

변수의 초기화

  • 변수를 선언하고 처음 값을 저장하는 것.
    경우에 따라서 필수적이기도 선택적이기도 하지만, 가능하면 선언과 동시에 적절한 값으로 초기화하는 것이 바람직하다.
  • 멤버변수는 초기화를 하지 않아도 자동적으로 변수의 자료형에 맞는 기본값으로 초기화되기에 초기화하지 않고 사용해도 되지만, 지역변수는 사용하기 전에 반드시 초기화해야한다.
class InitTest{
	int x;           // 인스턴스 변수
    int y = x;       // 인스턴스 변수
    
    void method(){
    	int i;       // 지역변수
        int j = i;   // 에러 발생. 지역변수를 초기화하지 않고 사용.
    }
}
  • 인스턴스 변수 x는 초기화를 하지 않아도 자동적으로 int형의 기본값인 0으로 초기화되므로, int y = x; 같이 사용할 수 있다.
    하지만, method()의 지역변수 i는 자동적으로 초기화되지 않으므로, 초기화되지 않은 상태에서 변수 j를 초기화 하는데 사용할 수 없다. 에러가 발생한다.

    멤버변수(클래스 변수와 인스턴스 변수)와 배열의 초기화는 선택이지만, 지역변수의 초기화는 필수이다.

멤버변수의 초기화

  • 지역변수와 달리 멤버변수는 각 타입의 기본값으로 자동 초기화된다. 그 다음 명시적 초기화, 초기화 블럭, 생성자의 순서로 초기화된다. 클래스 변수(cv)가 인스턴스 변수(iv)보다 먼저 초기화된다.

    1) 클래스변수(cv) 초기화 > 인스턴스 변수(iv) 초기화
    2) 자동 초기화 > 명시적 초기화(간단) > 초기화 블럭. 생성자 (복잡)

명시적 초기화(Explicit initalization)

  • 변수를 선언과 동시에 초기화하는 것.
    가장 기본적이면서도, 가장 간단한 초기화방법으로 가장 우선적으로 고려되어야 한다.
class Car{
	int door = 4;              // 기본형(primitive type)변수 초기화
    Engine e = new Engine();   // 참조형(reference type)변수 초기화
    // ....
}

명시적 초기화는 간단하고 명료하지만, 보다 복잡한 초기화 작업이 필요할 때는 '초기화 블럭(initialization block)'또는 생성자를 사용해야한다.

초기화 블럭(initialization block)

  • 초기화 블럭에는 '클래스 초기화 블럭'과 '인스턴스 초기화 블럭' 두 가지 종류가 있다.

    클래스 초기화 블럭 : 클래스 변수의 복잡한 초기화에 사용.
    인스턴스 초기화 블럭 : 인스턴스 변수의 복잡한 초기화에 사용.

  • 인스턴스 초기화 블럭은 단순히 클래스 내에 블럭{}을 만들고 그 안에 코드를 작성하면 된다.
    클래스 초기화 블럭은 인스턴스 초기화 블럭 앞에 단순히 static을 덧붙이기만 하면된다.

상속

  • 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것.
    상속을 통해서 클래스를 작성하면 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 있기 때문에 코드의 추가 및 변경이 매우 용이하다.
  • 이러한 특징은 코드의 재사용성을 높이고 코드의 중복을 제거, 프로그램의 생산성과 유지보수에 크게 기여한다.
  • 자바에서 상속을 구현하기 위해선 새로 작성하고자 하는 클래스의 이름 뒤에 extends와 함께 상속받고자 하는 클래스의 이름을 붙여주면 된다.
class Parent {}
class Child extends Parent {
 	///
}
  • 상속해주는 클래스는 '부모 클래스', 상속 받는 클래스는 '자식 클래스'라고 한다. 위의 경우 class Parent는 조상 클래스, class Child는 자손 클래스가 된다.
  • 클래스 간의 상속관계를 그림으로 표현한 것을 상속계층도(class hierarchy)라고 한다.
  • 프로그램이 커질수록 클래스간의 복잡해지는데, 아래와 같이 그림으로 표현하면 이해하기가 수월하다.

    excalidraw
  • 자식 클래스는 부모 클래스의 모든 멤버를 상속받기 때문에, Child클래스는 Parent클래스의 멤버들을 포함한다고 할 수 있다.
    클래스는 멤버들의 집합이므로 클래스 Parent와 Child의 관계를 다음과 같이 포함할 수도 있다.
  • Parent클래스에 age라는 정수형 변수를 멤버변수를 추가하면, 자식 클래스인 Child는 자동적으로 age라는 멤버변수가 추가된 것과 같은 효과를 얻는다.

  • 반대로 자식 클래스에 새로운 멤버로 play()메서드를 추가해도 부모 클래스는 아무 영향도 받지 않는다.

  • 부모 클래스가 변경되면 자식 클래스는 자동으로 영향을 받게 되지만, 자식 클래스가 변경되는 것은 부모 클래스에 아무런 영향을 주지 못한다.
  • 자식 클래스는 부모 클래스의 모든 멤버를 상속받으므로 항상 부모 클래스보다 같거나 많은 멤버를 갖는다.
    상속에 상속을 거듭할수록 상속받는 클래스의 멤버 개수는 점점 늘어나게 된다.
  • 따라서 상속을 받는다는 것은 부모 클래스가 확장한다는 의미로 해석할 수도 있으며 이것이 상속에 사용되는 키워드가 extends인 이유이기도 하다.

    자손 클래스는 조상 클래스의 모든 멤버를 상속받는다. (단, 생성자와 초기화 블럭은 상속되지 않음)
    자손 클래스의 멤버 개수는 조상 클래스보다 많거나 같다.

0개의 댓글