본문 바로가기
Everyday Study

2024.07.08 (월) { super키워드, 클래스캐스트익셉션), 다중상속, 공변리턴타입, 암시적-명시적 변환, clone()메서드 }

by xogns93 2024. 7. 8.

super 키워드

super 키워드는 부모 클래스의 메서드나 생성자를 호출할 때 사용됩니다. 이는 주로 서브 클래스가 부모 클래스의 메서드를 재사용하거나 부모 클래스의 생성자를 호출하여 초기화 작업을 수행하기 위해 사용됩니다.

생성자는 멤버가 아니므로 하위(자식) 클래스에서 상속되지 않지만 상위(부모) 클래스의 생성자는 하위 클래스에서 호출될 수 있습니다.

 

부모클래스

class Parent {
    // 부모 클래스의 생성자
    public Parent(String name) {
        System.out.println("Parent initialized with name: " + name);
    }
    
    // 부모 클래스의 메서드
    public void display() {
        System.out.println("This is the parent class.");
    }
}

 

자식클래스

class Child extends Parent {
    private int age;
    
    // 자식 클래스의 생성자
    public Child(String name, int age) {
        // 부모 클래스의 생성자를 호출
        super(name);
        this.age = age;
        System.out.println("Child initialized with age: " + age);
    }
    
    // 자식 클래스의 메서드
    @Override
    public void display() {
        // 부모 클래스의 메서드를 호출
        super.display();
        System.out.println("This is the child class.");
    }
}

테스트 출력 클래스 예제

public class TestSuper {
    public static void main(String[] args) {
        // 자식 클래스의 객체를 생성
        Child child = new Child("John", 12);
        
        // 자식 클래스의 메서드를 호출
        child.display();
    }
}

super 키워드의 주요 용도

  1. 부모 클래스의 생성자 호출:
    • 자식 클래스의 생성자에서 부모 클래스의 생성자를 호출할 때 사용됩니다. 예를 들어, super(name)은 부모 클래스의 생성자 Parent(String name)을 호출합니다. 이는 부모 클래스의 필드나 초기화 작업을 수행하는 데 필요합니다.
  2. 부모 클래스의 메서드 호출:
    • 자식 클래스에서 부모 클래스의 메서드를 호출할 때 사용됩니다. 예를 들어, super.display()는 부모 클래스의 display() 메서드를 호출하여 부모 클래스의 기능을 재사용합니다.

 


생성자가 상속이 안되는 이유

1. 클래스 초기화의 독립성

각 클래스는 자신의 고유한 상태를 가지며, 이를 초기화하기 위한 생성자를 가집니다. 상속된 클래스의 생성자는 기본적으로 부모 클래스의 생성자와 다른 요구 사항을 가질 수 있습니다. 따라서 자식 클래스는 자신의 생성자를 정의하여 고유한 초기화 과정을 구현해야 합니다.

2. 부모 클래스의 상태 보호

부모 클래스의 생성자는 보통 부모 클래스의 인스턴스 변수들을 초기화합니다. 이를 자식 클래스에 그대로 상속시키는 것은 부모 클래스의 내부 상태를 자식 클래스가 직접적으로 변경할 수 있게 되어 캡슐화 원칙에 어긋납니다. 따라서 자식 클래스는 자신의 생성자에서 부모 클래스의 생성자를 명시적으로 호출하여 초기화를 수행하게 합니다.

3. 다형성 문제

자식 클래스는 부모 클래스와 다른 추가적인 필드나 메서드를 가질 수 있습니다. 따라서 자식 클래스의 인스턴스를 생성할 때 자식 클래스의 필드와 메서드를 포함한 초기화 작업이 필요합니다. 부모 클래스의 생성자가 상속될 경우, 이러한 추가적인 초기화 작업이 제대로 수행되지 않을 수 있습니다.

4. 명시적 초기화 필요

자식 클래스는 부모 클래스의 생성자를 명시적으로 호출하여 부모 클래스의 초기화 작업을 수행합니다. 이는 super() 키워드를 사용하여 이루어지며, 자식 클래스에서 부모 클래스의 생성자를 호출함으로써 부모 클래스의 상태를 적절히 초기화할 수 있습니다.

 


하위 클래스는 하위 클래스가 어떤 패키지에 있는지에 관계없이 상위 클래스의 모든 publicprotected 멤버를 상속합니다. 하위 클래스가 상위 클래스와 동일한 패키지에 있는 경우 상위 클래스의 package-private 멤버도 상속합니다. 상속된 멤버를 그대로 사용하거나, 바꾸거나, 숨기거나, 새 멤버로 보완할 수 있습니다.


Object 클래스

모든 클래스의 최상위 루트 클래스입니다. 즉, 자바의 모든 클래스는 Object 클래스를 상속받습니다. Object 클래스는 자바의 모든 객체가 기본적으로 가져야 할 몇 가지 메서드를 정의하고 있습니다.

 

다음은 Object 클래스의 주요 메서드와 그 역할입니다:

  1. equals(Object obj): 두 객체가 같은지 비교합니다.
  2. hashCode(): 객체의 해시 코드를 반환합니다.
  3. toString(): 객체의 문자열 표현을 반환합니다.
  4. clone(): 객체의 얕은 복사를 수행합니다.
  5. finalize(): 객체가 가비지 컬렉션되기 전에 호출됩니다.
  6. getClass(): 객체의 클래스 타입을 반환합니다.
  7. notify(), notifyAll(), wait(): 동기화와 관련된 메서드입니다.

외워 ! - bin폴더에 class생성된거 볼때 $표시 - 이너클래스 임을 알리는 표시

 

자바에서 객체는 메모리에 생성된 인스턴스입니다. 변수는 이 객체를 참조하는 역할을 합니다. 즉, 변수는 객체의 메모리 주소를 저장하여 객체에 접근할 수 있도록 합니다.

 

객체와 참조 변수

자바에서 객체는 메모리에 생성된 인스턴스입니다. 변수는 이 객체를 참조하는 역할을 합니다. 즉, 변수는 객체의 메모리 주소를 저장하여 객체에 접근할 수 있도록 합니다.


ClassCastException  (클래스캐스트익셉션)

자바에서 발생하는 런타임 예외 중 하나로, 객체를 잘못된 타입으로 캐스팅하려고 할 때 발생합니다. 이는 일반적으로 상위 클래스 타입을 하위 클래스 타입으로 강제 형변환할 때 발생합니다.

 

ClassCastException은 잘못된 타입으로 객체를 캐스팅하려고 할 때 발생하는 예외입니다. 이를 방지하기 위해서는 instanceof 연산자를 사용하여 객체의 타입을 확인한 후 캐스팅을 수행하거나, 올바른 타입의 객체를 생성하여 캐스팅을 수행하는 방법이 있습니다.

 


자바에서 클래스는 다중 상속을 지원하지 않지만,

인터페이스는 다중 상속을 지원합니다. 

1. 클래스 다중 상속이 안 되는 이유

다이아몬드 문제(Diamond Problem)

다중 상속을 지원하는 언어에서는 "다이아몬드 문제"라는 복잡한 문제가 발생할 수 있습니다. 예를 들어, 두 개의 클래스가 동일한 메서드를 가지며, 이 두 클래스가 또 다른 클래스에 상속될 때 문제가 발생합니다.

class A {
    void display() {
        System.out.println("Class A");
    }
}

class B extends A {
    void display() {
        System.out.println("Class B");
    }
}

class C extends A {
    void display() {
        System.out.println("Class C");
    }
}

// 만약 자바가 다중 상속을 지원한다면, 다음과 같은 상황이 발생할 수 있습니다.
// class D extends B, C {
//     // D 클래스에서 display() 메서드를 호출하면 어떤 클래스의 메서드가 호출될까요?
// }

위 예제에서 D 클래스가 B와 C를 다중 상속한다면, display() 메서드를 호출할 때 어떤 클래스의 메서드를 호출해야 하는지 결정할 수 없습니다. 이러한 다이아몬드 문제는 다중 상속의 복잡성과 모호성을 증가시킵니다.

 

2.  인터페이스가 다중 상속을 지원하는 이유

상태의 다중 상속 문제 없음

인터페이스에는 필드(상태)가 포함되어 있지 않으므로, 다중 상속으로 인해 발생하는 상태 관리의 복잡성이 없습니다. 인터페이스는 메서드 시그니처(메서드의 선언만 포함)만을 가지기 때문에 다중 상속으로 인해 발생하는 다이아몬드 문제가 없습니다. 

 


Duck duck = new Duck();

duck <- 덕클래스의 객체가 아님

덕클래스 객체를 참조하는 변수, 덕클래스 참조타입

 

Duck duck = new Duck();는 Duck 클래스의 객체를 생성합니다. new Duck()는 메모리에 Duck 클래스의 새로운 인스턴스를 생성하고, duck 변수는 이 객체의 메모리 주소를 참조합니다.

 


공변 리턴 타입(covariant return type)

자바에서 오버라이드된 메서드는 리턴 타입의 하위 타입을 반환할 수 있습니다. 이를 "공변 리턴 타입(covariant return type)"이라고 합니다. 공변 리턴 타입을 사용하면 메서드 오버라이딩 시 반환 타입을 좀 더 구체적인 타입으로 지정할 수 있습니다.

오버라이드된 메서드가 리턴 타입의 하위 타입을 반환할 수 있도록 허용하는 기능입니다.

 


자바에서 메서드 오버라이딩 시 접근 제한자는 더 넓은 접근을 허용할 수 있지만, 더 좁은 접근을 허용할 수는 없습니다. 이는 하위 클래스가 상위 클래스의 계약을 준수하고, 상위 클래스에서 기대되는 동작을 변경하지 않도록 하기 위한 규칙입니다.

접근 제한자의 넓이

접근 제한자는 다음 순서대로 접근 범위가 넓습니다:

  1. private: 같은 클래스 내에서만 접근 가능.
  2. default (아무 것도 명시하지 않은 경우, 패키지-프라이빗): 같은 패키지 내에서만 접근 가능.
  3. protected: 같은 패키지와 하위 클래스에서 접근 가능.
  4. public: 어디서든 접근 가능.

하위 클래스는 하위 클래스가 어떤 패키지에 있는지에 관계없이 상위 클래스의 모든 public 및 protected 멤버를 상속합니다. 하위 클래스가 상위 클래스와 동일한 패키지에 있는 경우 상위 클래스의 package-private 멤버도 상속합니다

 


암시적(Implicit)

"암시적"이란 프로그램에서 명시적으로 작성되지 않았지만, 컴파일러나 런타임 시스템이 자동으로 수행하는 동작을 의미합니다.

암시적 타입 변환

암시적 타입 변환(자동 타입 변환)은 자바에서 작은 크기의 데이터 타입이 큰 크기의 데이터 타입으로 자동으로 변환되는 경우를 말합니다. 예를 들어, 정수형 int를 실수형 double로 변환할 때입니다.

int intValue = 42;
double doubleValue = intValue; // 암시적 타입 변환

위 코드에서 intValue는 int 타입이지만, doubleValue에 할당될 때 암시적으로 double 타입으로 변환됩니다. 

 

명시적(Explicit)

"명시적"이란 프로그래머가 코드에 직접 작성하여 명확하게 지시하는 동작을 의미합니다.

명시적 타입 변환

명시적 타입 변환(캐스팅)은 개발자가 특정 데이터 타입을 다른 데이터 타입으로 변환하도록 명시적으로 지시하는 경우를 말합니다. 이는 주로 큰 크기의 데이터 타입을 작은 크기의 데이터 타입으로 변환할 때 사용됩니다.

double doubleValue = 42.0;
int intValue = (int) doubleValue; // 명시적 타입 변환

위 코드에서 doubleValue는 double 타입이지만, (int) 캐스팅 연산자를 사용하여 명시적으로 int 타입으로 변환됩니다.

 


clone() 메서드

 

clone() 메서드는 자바에서 객체의 복사를 수행하는 메서드입니다. 이 메서드는 java.lang.Object 클래스에 정의되어 있으며, 객체의 "얕은 복사(shallow copy)"를 수행합니다. 이를 사용하려면 클래스가 Cloneable 인터페이스를 구현해야 하며, clone() 메서드를 오버라이드하여 접근 제한자를 public으로 변경하는 것이 일반적입니다.

얕은 복사와 깊은 복사

  • 얕은 복사(Shallow Copy): 객체의 필드 값을 그대로 복사합니다. 필드가 참조 타입인 경우 참조 주소만 복사되므로, 원본 객체와 복사본 객체는 같은 인스턴스를 참조합니다.
  • 깊은 복사(Deep Copy): 객체의 필드 값뿐만 아니라 참조 타입 필드가 가리키는 객체까지 모두 복사합니다. 따라서 원본 객체와 복사본 객체는 독립적인 객체를 참조하게 됩니다.

요약

  • clone() 메서드: java.lang.Object 클래스의 메서드로, 객체의 얕은 복사를 수행합니다.
  • Cloneable 인터페이스: clone() 메서드를 사용할 수 있도록 클래스에서 구현해야 하는 인터페이스입니다.
  • 얕은 복사: 객체의 필드 값을 그대로 복사. 참조 타입 필드는 같은 객체를 가리킴.
  • 깊은 복사: 객체와 그 객체가 참조하는 객체까지 모두 복사. 이를 위해 clone() 메서드를 오버라이드하여 직접 구현해야 함.

마커 인터페이스(Marker Interface)

자바에서 인터페이스 내에 메서드나 필드를 정의하지 않고, 특정 클래스를 "마킹"하기 위해 사용되는 인터페이스입니다. 이러한 인터페이스는 클래스가 특정 기능을 지원하거나 특정 속성을 가지고 있음을 나타냅니다. 마커 인터페이스를 구현하는 클래스는 런타임 시 특정 작업을 수행할 수 있는 대상으로 간주됩니다.