javaboiii의 JAVA/JAVA 요약정리(멘토씨리즈)

JAVA -10)상속

javaboiii 2024. 7. 21. 19:17

JAVA

1. 상속(Inheritance)

상속이란

상속은 부모가 자식에게 무언가를 물려주는 것을 상속이라고 부르는 것처럼, 자바에서도 부모 역할을 하는 클래스가

자식 역할을 하는 클래스에게 클래스 멤버와 매서드를 물려주는 것을 상속이라고 합니다.

상속은 클래스를 재사용하기 때문에 중복을 줄여주고 수정을 최소화하는 특징을 가지고 있습니다.

 

상속을 해주는 클래스 : 부모 클래스, 상위 클래스, 기반 클래스

상속을 받는 클래스 : 자식 클래스, 하위 클래스, 파생 클래스

 

자바에서 상속을 구현하는 방법은 자식 클래스를 선언할 때, extends라는 키워드를 사용해 상속받을 클래스를

지명합니다. 자식 클래스에서 선택받은 클래스는 부모 클래스 역할을 하게 됩니다.

class A{	// 부모 클래스는 작식 클래스에서 지명받기 전에는 부모 클래스의 역할을 하지 않습니다.

}

class B extends A{	// B클래스에서 extends A를 작성함으로써 A는 B의 부모 클래스가 되고,
			// B클래스는 A 클래스의 자식 클래스가 됩니다.
}

자바는 다중 상속을 허용하지 않기 때문에 extends 뒤에는 하나의 부모 클래스만 허용됩니다.

 

부모 클래스의 상속을 받은 자식 클래스는 부모 클래스의 모든 멤버를 그대로 가져다 쓸 수 있습니다.

2. 상속에서의 생성자

자식 클래스의 객체를 생성할 때, 자식 클래스는 자식의 생성자를 통해 자식 객체를 생성합니다.

특별한 역할을 하지 않은 기본 생성자는 비어있는 것이 맞지만 자식 클래스의 기본 생성자는 다릅니다.

필드 초기화와 같은 특별한 역할을 하고 있지 않더라도, super()라는 메서드를 가지고 있습니다.

super()

this() 메서드가 같은 클래스의 다른 생성자를 호출할 때 사용된다면, super() 메서드는 부모 클래스의 생성자를

호출할 때 사용합니다.

 

자식 클래스로 객체를 생성하기 위해 기본 생성자가 호출되면 super()라는 메서드를 통해 부모 클래스의

기본 생성자를 호출합니다. 그렇게 부모 객체를 먼저 생성한 후, 부모 객체를 감싸고 자식 객체를 생성합니다.

즉, 자식 객체 안에는 부모 객체가 들어있게 됩니다.

 

따라서, 개발자가 직접 생성자를 선언할 때도 자식 클래스에서는 반드시 부모 클래스의 생성자를 호출해줘야 합니다.

만약 부모 클래스의 생ㅅ어자가 호출될 때 매개변수로 값을 전달받아 부모 클래스의 필드들을 초기화 하도록

구현되어 있다면, 자식 클래스의 생성자에서 부모 클래스의 생성자를 호출할 때도 부모 클래스 대신 값을 매개변수로

받아서 부모 생성자에 넣어줘야 합니다.

super() 메서드는 자식 생성자의 첫 줄에서 호출해야 합니다.

 

3. 오버라이딩(overriding)

부모 클래스를 상속받은 자식 클래스는 부모 클래스의 필드와 메서드를 가져와서 그대로 사용할 수 있습니다.

하지만 필요하다면 자식 클래스가 상속받은 메서드의 내용을 변경해서 사용할 수도 있습니다. 이렇게 상속받은

메서드를 변경해서 다시 구현하는 것을 '오버라이딩(Overriding)'이라고 합니다.

메서드 오버라이딩을 위해서는 다음과 같은 제약 상항을 지켜야 합니다.

  • 부모 클래스의 메서드 이름/반환 타입/ 매개변수와 동일해야 합니다.
  • 부모 클래스의 메서드 보다 접근 제한을 줄일 수는 있으나(범위를 넓힐 수 있으나) 접근 제한을 늘릴 수는 없습니다.(범위를 좁힐 수는 없습니다)

부모 클래스의 메서드보다 많은 수의 예외를 추가하거나 새로운 예외를 만들 수 없습니다.

 

메서드 오버라이딩을 구현해 자식 객체를 생성하여 해당 메서드를 호출하면 자식 클래스에서 구현한 메서드가 실행됩니다. 자식 클래스에서 오버라이딩된 메서드는 자식 객체를 통해 호출하면 자식 클래스에서 구현한 메서드가 실행되고,

부모 객체를 통해 호출하면 자식 클래스와는 상관없이 부모 클래스의 원래 메서드로 호출됩니다.

 

오버로딩과 오버라이딩

오버로딩과 오버라이딩은 단어가 유사하여 혼동하기 쉽지만 개념은 확실히 다릅니다.

오버로딩은 새로운 메서드를 정의하는 것이고, 오버라이딩은 상속받은 기존의 메서드를 재정의 하는 것입니다.

@Override 어노테이션

자바에서 @키워드를 어노테이션(annotation)이라고 부릅니다. 주석(comments)과 마찬가지로 코드를 실행하는데

직접적인 영향을 미치지 않습니다. 자동완성으로 메서드 오버라이딩을 구현하면, 자동으로 @Override가 메서드 상단에

추가 됩니다.

super 키워드

단순히 코드를 똑같이 작성할 수도 있으나 상속 관계에서 사용할 수 있는 spuer 키워드를 활용하면 중복을 줄일 수 있습니다. super 키워드는 부모 클래스에서 상속받을 필드나 메서드를 자식 클래스에서 참조하는 데 사용하는 참조 변수 입니다.

 

this와 마찬가지로 super 참조 변수를 사용할 수 있는 대상도 인스턴스 메서드뿐이며, 클래스 메서드에서는 사용할 수 없습니다.

4. 접근 제한자(Access Modifier)

제한자(modifier)

제한자 클래스, 변수 또는 메서드의 선언부에 함께 사용해 부가적인 의미를 부여하는 키워드를 의미합니다.

자바에서 제한자는 접근 제한자와 기타 제한자로 구분합니다.

제한자는 경우에 따라 여러 개를 함께 조합해 사용할 수 있지만, 접근 제한자의 경우 하나만 선택해서 사용해야 합니다.

접근 제한자

모든 클래스와 모든 멤버에 외부에서 접근하지 못하도록 접근 권한을 제한할 수 있습니다. 접근이 필요하지 않은 곳에서의

사용을 막거나, 특정 범위에서만 접근할 수 있게 하기 위함입니다.. 이는 객체 지향 프로그래밍의 특징 중에 하나인

정보 은닉을 지키기 위한 중요한 부분이기도 합니다.

 

말 그대로 '공공의'라는 뜻을 가지는 대표적인 접근 제한자 public이 있습니다.

또 하나의 접근 제한자 default가 있습니다. 접근 제한자를 별도로 명시하지 않았다면 자동으로 default가

접근 제한자가 되며, 이는 생략되어 있습니다.

 

자바에서는 다음과 같이 public과 default를 포함한 4가지 접근 제한자를 제공합니다.

public 제한 없이 모든 패키지, 모든 클래스에서 접근이 가능합니다.
protected 같은 패키지 안에서 접근이 가능하며, 다른 패키지라도 자식 클래스라면 접근이 가능합니다.
default 같은 패키지 내에서만 접근이 가능합니다.
private 같은 클래스 내에서만 접근이 가능합니다.

접근 제한자를 접근 범위가 넓은 쪽에서 좁은 쪽의 순으로 나열하면 다음과 같습니다.

 

public > protected > default > private

 

접근 제한자의 선언 위치는 다음과 같습니다.

접근 제한자 class 클래스명{	// 클래스 접근 제한자 선언 위치
	...
    접근 제한자 자료형 필드명;	// 클래스 필드 접근 제한자 선언 위치
    
    접근 제한자 클래스명(){	// 생성자 접근 제한자 선언 위치
    	...
    }
    
    접근 제한자 반환 타입 메서드명(){ // 메서드 접근 제한자 선언 위치
    	...
    }
}

 클래스의 접근 제한자

클래스는 접근 제한자로 public과 default만 가질 수 있습니다.

private와 protected의 경우, 클래스 맴버들을 위한 접근 제한자로 클래스 외부에서 접근을 막을지 말지에 대한

접근을 제한하는 용도로 사용되기 때문에 클래스의 접근 제한자로 사용될 수 없습니다.

public

public은 접근 제한자 중에서 가장 사용 범위가 큰 제어자입니다. public으로 선언된 클래스와 멤버들은 같은 패키지는

물론 다른 패키지의 클래스에서도 접근할 수 있습니다.

  • public 클래스/생성자 : 모든 패키지, 모든 클래스 어디서나 해당 클래스로 객체를 생성할 수 있습니다.
  • public 멤버(필드, 생성자, 메서드) : 모든 패키지, 모든 클래스 어디서나 객체를 통해서 접근할 수 있습니다.

객체 지향의 특징

캡슐화

객체 내부의 멤버(필드, 메서드 등)를 객체 외부에서 볼 수 없도록 캡슐화 합니다.

접근이 필요한 값의 경우 public 메서드를 활용하여 접근을 허용하고, 이외의 값들은 모두 캡슐화를 통해 정보를 은닉합니다.

 

추상화

공통된 기능과 정보를 추출해 객체화합니다.

 

상속

미리 정의된 부모 클래스의 모든 멤버를 자식 클래스가 물려받습니다.

 

다형성

하나의 방법으로 여러 객체를 호출하여 사용할 수 있습니다.

default

접근 제한자를 명시하지 않으면 클래스와 멤버들은 자동으로 default라는 접근 제한자를 가집니다.

default로 선언된 클래스와 멤버들은 멤버들은 같은 패기지 안에서는 어디서든 접근 및 사용이 가능하지만

다른 패키지에서는 접근이 불가능합니다.

  • default 클래스/생성자 : 같은 패키지 내에서 어디서나 호출이 가능하며, 객체를 생성할 수 있습니다.
  • default 필드/메서드 : 같은 패키지 내에서 제한 없이 접근 및 사용할 수 있습니다.

protected

클래스 멤버를 위한 제한자로, 클래스의 접근 제한자로 사용하지 않는 protected는 이름처럼 조금 특별하게

클래스 멤버를 보호하고 있습니다. default 제한자와 동일하게 같은 패키지 안에서 접근과 사용을 허용하지만

다른 패키지에서의 접근을 완전히 제한하는 것이 아닌 "해당 클래스와 상속 관계에 있는 자식 클래스"라면

다른 패키지라도 접근 및 사용이 가능합니다.

즉, 같은 패키지에서 접근이 가능하며, 다른 패키지라면 자식 클래스만 접근을 허용한다는 뜻 입니다.

  • protected 생성자 : 같은 패키지의 클래스에서 생성자를 호출해 객체를 생성할 수 있습니다. 또한, 다른 패키지더라도 해당 클래스의 자식 클래스라면 생성자를 호출해 객체를 생성할 수 있습니다.
  • protected 필드/메서드 : 같은 패키지의 클래스에서 접근 및 사용할 수 있으며, 해당 클래스의 자식 클래스라면 다른 패키지에서라도 사용할 수 있습니다.

private

private는 접근 제한자 중에서 가장 사용 범위가 좁은 클래스 멤버를 위한 제한자 입니다.

클래스가 public / default이더라도, private로 선언된 멤버들은 클래스 외부에서 접근이 전혀 불가능합니다. 오직 선언된

클래스 내부에서만 접근하여 사용할 수 있습니다. 따라서 private 멤버는 public 인터페이스를 직접 구성하지 않고,

클래스 내부의 세부적인 동작을 구현하는 데 사용합니다.

  • private 생성자 : 클래스 외부에서 객체를 생성할 수 없으며, 클래스 내부에서만 생성자를 호출해 객체를 생성할 수 있습니다.
  • private 필드/메서드 : 클래스 외부에서 접근할 수 없으며, 클래스 내부에서만 사용할 수 있습니다.
  • protected와 마찬가지로 클래스의 접근 제한자로 사용하지 않습니다.

5. 2차 상속

현실에서 우리는 누군가의 자식이 될 수 있지만 누군가의 부모도 될 수 있듯이, 상속 역시 원한다면 다음 세대에게,

그리고 또 다음 세대로 이어질 수 있습니다. 자바 세상에서도 마찬가지로, 우리는 한 번의 상속에서 끝내지 않고

2차, 3차 ...N차까지 원하는 만큼 상속을 이어서 받을 수 있습니다.

6. final 클래스와 final 메서드

final 키워드는 상수를 뜻하는 키워드로, 필드 앞에 선언하여 사용합니다. 초기화 이후 값을 바꿀 수 없으며 시간이

지나도 처음 정의된 상태가 변하지 않는다는 의미를 가지고 있습니다.

final 클래스

클래스 앞에 final을 추가할 경우, 이 클래스는 상송의 마지막 클래스임을 뜻합니다. 어떠한 클래스도 이 클래스의 자식

클래스가 될 수 없고, 자연스럽게 이 클래스는 어떤 클래스의 부모 클래스가 될 수 없음을 의미합니다.

 

만약 final 클래스를 상속받고자 시도한다면 컴파일 에러가 발생합니다.

final 메서드

메서드 앞에 final을 추가하면 이 메서드는 상속받더라도, 오버라이딩할 수 없는 메서드임을 뜻합니다.

즉, 자식 클래스이더라도 부모 클래스에 final로 선언된 메서드는 자식 클래스에서 오버라이딩하지 못하고 있는

그대로 사용해야 한다는 뜻입니다.

 

생성자에는 final을 추가할 수 없습니다.

생성자는 접근 제한자인 public, protected, (default), private만 추가할 수 있습니다 따라서, 클래스를 final로 선언하더라도

생성자를 final로 선언할 수는 없습니다. 만약 final을 추가하면 에러 메세지가 나타납니다.

응용문제

1. 다음 중 상속과 관련된 단어를 모두 고르세요.

① super

② super()

③ this

④ extends

정답 : ① ② ④

2. 다음 코드를 실행했을 때 콘솔창에 출력되는 결과는 무엇입니까 ?

package section10;

class Person{
	void printHello(){
    	System.out.println("안녕하세요");
    }
}

class Student extends Person{
}

public class PRACTICE_10_02{
	public static void main(String[] args){
    	Person p = new PerSon();
        p.printHello();
        
        Student s = new Student();
        s.printHello();
    }
}

실행 결과 : 
안녕하세요
안녕하세요

 3. <PRACTICE_10_02.java> 코드에서 class Student가 Person의 printHello()를 다음과 같이 오버라이드 했을 때, 콘솔창에 출력되는 결과는 무엇입니까 ?

class Student extends Person{
	@Override
    void printHello(){
    	System.out.println("안녕하세요, 저는 자바를 공부하는 학생입니다.");
    }
}
실행결과 : 
안녕하세요
안녕하세요, 저는 자바를 공부하는 학생입니다.

4. 다음 코드는 컴파일 에러가 발생합니다. 그 이유는 무엇이며 어떻게 해결해야 할까요 ?

[ClassA.java]

package section10;

public class ClassA{
	private int a;
    
    private ClassA(int a){
    	this.a = a;
    }
    
    public void methodA(){
    	System.out.println("ClassA클래스의 methodA() 메서드입니다.");
        System.out.println("필드 a의 값은 "+a+"입니다.");
    }
}

[ClassB.java]

package section10;

public class ClassB{
	public static void main(String[] args){
    	Class ca = new ClassA(3);
        ca.methodA();
    }
}

에러 이유 : ClassA의 생성자가 private이기 때문에 클래스 외부에서 생성할 수 없음

해결 방법 : ClassA의 생성자를 default나 public 으로 바꾼다

5. 다음 두 개의 클래스는 상속 관계에 있으나, 다른 패키지에 속해 있습니다. Child 클래스에서 Parent 클래스의 parentMethod 메서드를 호출하기 위해 어떤 코드를 작성해야 할까요 ?

[Parent.java]

package section.access1;

public class Parent{
	protected void parentMethod(){
    	System.out.println("parentMethod is called");
    }
}

[Child.java]

package section.access2;
import section10.access1.*;

public class Child{
	void accessParentMethod(){
    	ㅁㅁㅁㅁㅁ
    }
}

상속관계에 있다면 다른 패키지에서도 부모의 메서드를 사용할 수 있으므로

super();