본 내용은 KH정보교육원 에서 제공한 교재 내용을 개인적인 공부를 위한 목적으로 작성되었습니다.
만약 아래의 내용을 무료로 배부하거나 상업적으로 이용할 경우 법적 처벌을 받을 수 있음을 경고합니다.
기존의 class(부모 클래스 / 슈퍼 클래스)를 재사용하여 새로운 class(자식 클래스 / 서브 클래스)를 작성하는 것을 말한다. 기존 부모 클래스의 필드와 메소드를 내려 받고, 여러 후손 클래스들의 중복되는 멤버들은 부모 클래스에서 작성 관리하므로써 멤버의 중복도 줄이면서, 적은 양의 코드로도 다양한 기능을 처리할 수 있는 새로운 클래스를 만들 수 있다.
자바에서는 멤버의 모호성을 없애기 위해, 클래스 간의 다중 상속은 허용하지 않는다.
자바에서 클래스 간의 상속을 지정할 때는 새로 만들어지는 클래스 이름 뒤에 extends 키워드를 사용하고 extends와 함께 상속 받을 클래스 이름을 기입한다.
접근제한자 class 클래스이름 extends 부모클래스명 { // 멤버 정의 }
01 public class A { 02 private int num; 03 public void method1() { ... } 04 } 05 public class B extends A { 06 private String str; 07 public void method2() { ... } 08 }
extends 키워드를 이용해 A 클래스와 B 클래스는 상속 관계를 맺었다. 여기서 B는 자식 클래스가 되고 A는 부모 클래스가 된다. B 클래스는 필드 num과 메소드 method1()를 상속받아 직접 구현하지 않았어도 자신이 가지고 있는 것처럼 사용이 가능하다.
단, 부모의 private 멤버는 상속은 받지만 접근은 불가능하다.
상속하는 클래스를 슈퍼(super) 클래스라고 한다.
자식(sub) 클래스
상속 받는 클래스를 후손 / 하위 / 자식 / 파생 클래스라고 한다.
메소드 재정의라고 하고, 부모 클래스로부터 상속 받은 메소드를 자식 클래스에서 다시 정의하는 것을 말하며 후손 클래스에서 부모 메소드를 재정의 할 때 메소드 상단에 '@Override' 어노테이션을 사용하여 재정의 한다는 것을 표시해 준다.
test.inherit.TestOverrinding.java
01 package test.inherit;
02
03 public class TestOverring {
04 public static void main(String[] args) {
05 Parent p = new Parent();
06 p.display();
07 // p.out() // 부모객체는 자식객체의 멤버에 접근한 수 없다.
08
09 Child c = new Child();
10 c.out();
11 c.display();
12 }
13 }
------
부모 클래스 메소드
상속 받아 재정의한 메소드
자식 클래스 메소드
inherit.sample.Parent.java
01 package inherit.sample
02
03 public class Parent {
04 private int num;
05
06 void display() {
07 System.out.println("부모 클래스 메소드");
08 }
09 }
inherit.sample.Child.java
01 package inherit.sample;
02
03 public class Child extends Parent {
04 public void out() {
05 System.out.println("자식 클래스 메소드");
06 }
07 @Override
08 public void display() {
09 System.out.println("상속 받아 재정의한 메소드");
10 }
11 }
메소드에 대한 호출과 실제 구현된 메소드의 코드와 연결되는 것을 바인딩(binding)이라고 한다. 바인딩에는 크게 정적 바인딩과 동적 바인딩이 있다.
정적 바인딩은 객체의 타입이 컴파일러에 의해 컴파일 되는 시점에 결정되는 것을 말하며 private, final, static 메소드의 경우 모두 정적 바인딩이 된다.
동적 바인딩은 객체의 타입이 실행 중에 결정되는 것을 말하며, 상속 관계의 오버라이딩 된 메소드가 동적 바인딩을 한다. 오버라이딩 된 메소드를 호출하는 코드가 존재할 경우 컴파일 시에는 단지 문법적으로 타입 에러만을 체크하고, 실제 프로그램 동작 중에 레퍼런스가 참조하는 객체의 타입이 결정되어 호출된 오버라이딩 메소드가 부모 클래스의 메소드인지 자식 클래스의 메소드인지 판단하여 동작하게 된다.
바인딩의 예 (Parent.java와 Child.java 소스는 이전 예제와 동일함)
inherit.bind.TestBinding.java
01 package inherit.bind;
02
03 public class TestBinding {
04 public static void main(String[] args) {
05 Parent p = new Child();
06
07 p.display(); // 컴파일 시에는 정적 바인딩, 실행 시에는 동적 바인딩이 됨
08 }
09 }
------
상속 받아 재정의한 메소드
상속 관계에 있는 클래스는 부모 클래스 타입 레퍼런스 변수에 자식 클래스의 인스턴스의 주소를 기록할 수 있다. 이 경우 p 레퍼런스를 통해 display() 메소드를 호출할 때 정적 바인딩이 이루어 진다면 p 레퍼런스로는 Parent 클래스의 display() 메소드만 호출 할 수 있게 된다. 하지만 display 메소드는 오버라이딩 되어 있기 때문에 동적 바인딩이 되므로, p 레퍼런스가 참조하는 객체의 타입이 Child 클래스이면, 후손의 오버라이딩 된 display() 메소드가 실행되게 되는 것이다.
자식 클래스에서 부모 클래스의 생성자나 멤버에 접근하고 싶을 때 super 키워드를 사용한다.
super() 부모 클래스의 생성자를 호출하는데 사용 할 수 있으며 자식 클래스에서 생성자를 정의 할 때 사용하면 코드의 재사용성을 높일 수 있다.
super
super()
inherit.test.TestSuper.java
01 package inherit.test;
02
03 public class TestSuper {
04 public static void main(String[] args) {
05 Child2 c = new Child2();
06 c.out();
07 }
08 }
------
부모 생성자
자식 생성자
부모 메소드
자식 메소드
inherit.sample.Parent2.java
01 package inherit.sample;
02
03 public class Parent2 {
04 public Parent2() {
05 System.out.println("부모 기본 생성자");
06 }
07
08 public void out() {
09 System.out.println("부모 out() 메소드");
10 }
11 }
inherit.sample.Child2.java
01 package inherit.sample;
02
03 public class Child2 extends Parent2 {
04 public Child2() {
05 super(); // 부모 기본 생성자 호출
06 System.out.println("자식 기본 생성자");
07 }
08
09 @Override
10 public void out() {
11 super.out(); // 부모 메소드 out() 호출
12 System.out.println("자식 Out() 메소드");
13 }
14 }
더 이상 확장이 불가능함을 알리는 예약어로, 지역변수, 필드, 메소드, 클래스에 붙일 수 있으며 각각 의미하는 바가 다르다.
변수에 기록된 값을 변경 할 수 없도록 한다. 즉, 변수를 상수화 시킨다.
상수화 시키면 초기값도 변경 할 수 없으므로, 반드시 선언과 함께 초기화도 하여야 한다.final 데이터타입 변수명 = 초기화값;
test.keyword.TestFinalVariable.java
01 package test.keyword
02
03 public class TestFinalVariable {
04 public static void main(String[] args) {
05 final int num = 10;
06 num = 100; // 에러. 변경 불가
07 }
08 }
상속 받은 후손 클래스에서 부모의 메소드를 오버라이딩 할 수 없게 한다.
접근제한자 final 리턴타입 메소드이름 (자료형 메개변수) { ... }
public class Parent { public final void out() { System.out.println("final 메소드"); } } public class Child2 extends Parent2 { @Override protected void out() { // 에러 발생 } }
종단 클래스라고 하며, 더 이상 상속에 사용할 수 없는 클래스라고 지정 할 때 사용한다. 즉, 부모 클래스가 될 수 없는 후손 클래스를 파생시킬 수 없는 클래스라는 의미가 된다.
접근제한자 final class 클래스이름 { ... }
public final class Parent { private int num; } public class Child extends Parent { // 에러 발생 // ... }
inherit.sample.Phone.java
01 package inherit.sample;
02
03 public class Phone {
04 private String model;
05 private String number;
06
07 public Phone(){}
08 public Phone(String model, String number) {
09 this.model = model;
10 this.number = number;
11 }
12
13 public String getModel() {
14 return model;
15 }
16 public String setModel(String model) {
17 this.model = model;
18 }
19 public String getNumber() {
20 return number;
21 }
22 public String setNumber(String number) {
23 this.number = number;
24 }
25 }
inherit.sample.IPhone.java
01 package inherit.sample;
02
03 public class IPhone extends Phone {
04 private int memorySize;
05
06 public IPhone() {}
07 public IPhone(String model, String number, int memorySize) {
08 super(model, number);
09 this.memorySize = memorySize;
10 }
11
12 public void setMemorySize(int memorySize) {
13 this.memorySize = memorySize;
14 }
15 public int getMemorySize() { return memorySize; }
16 }
inhert.sample.GPhone.java
01 package inherit.sample;
02
03 public class GPhone extends Phone {
04 private String os = "Android";
05
06 public GPhone() {}
07 public GPhone(String model, String number) {
08 super(model, number);
09 }
10
11 public String getOs() {
12 return os;
13 }
14 public void setOs(String os) {
15 this.os = os;
16 }
17 }
test.inherit.PhoneMain.java
01 package test.inhert;
02
03 import inherit.sample.*;
04
05 public class PhoneMain {
06 public static void main(String[] args) {
07 IPhone iPhone = new IPhone("7", "010-xxxx-xxxx", 32);
08 GPhone gPhone = new GPhone("Vega", "010-yyyy-yyyy");
09
10 System.out.println(iPhone.getModel() + ", " + iPhone.getNumber()
11 + ", " + iPhone.getMemorySize());
12 System.out.println(gPhone.getModel() + ", " + gPhone.getNumber()
13 + ", " + gPhone.getOs());
14 }
15 }
------
7, 010-xxxx-xxxx, 32
Vega, 010-yyyy-yyyy, Android