상속이라는 개념은 캡슐화, 추상화와 더불어 객체 지향 프로그래밍을 구성하는 중요한 특징 중 하나이다.
상속을 이용하면 기존에 정의되어 있는 클래스의 모든 필드와 메소드를 물려받아 새로운 클래스를 생성할 수 있다.

class 자식클래스명 extends 부모클래스명 { ... }

위 말을 그림으로 나타내어보면 알 수 있듯 자식 클래스는 부모클래스를 싸고 있는 모습을 볼 수 있다.
따라서 부모 클래스에 새로운 필드를 추가하면, 자식클래스에서도 자동으로 해당 필드가 추가되는 것처럼 동작한다.
*! 필드와 메소드만 상속되며, 생성자와 초기화 블록은 상속되지 않는다 !
클래스를 객체로 만들어서 사용할수도 있다. 아래 코드는 그 예를 보여준다.
public class MotionBook {
public void read() {
System.out.println("읽기");
}
public void close() {
System.out.println("닫기");
}
}
public class Book {
public static void main(String[] args) {
MotionBook m1 = new MotionBook();
m1.read();
m1.close();
}
}
super 키워드는 부모 클래스로부터 상속받은 필드나 메소드를 자식 클래스에서 참조하는 데 사용하는 참조 "변수"이다.
결과적으로 우리는 부모클래스의 멤버와 자식 클래스의 멤버 이름이 같을 경우 super 키워드를 사용하여 구별할 수 있다.
class Parent { int a = 10; }
class Child extends Parent {
int a = 20;
void display() {
System.out.println(a);
System.out.println(this.a);
System.out.println(super.a);
}
}
public class Inheritance {
public static void main(String[] args) {
Child ch = new Child();
ch.display();
}
}

위 코드를 참조하면 알 수 있듯이, 첫 a와 두 번째 a는 20을 출력하였고, 마지막 a는 10을 출력하였음을 참고해보면 super는 부모클래스의 a에 접근했다는 것을 알 수 있다.
super 와 super()는 다르다.
this() 메서드가 같은 클래스의 다른 생성자를 호출할 때 사용된다면, super() 메서드는 부모 클래스의 생성자를 호출할 때 사용하게 된다.
만약, 부모클래스의 생성자가 없다면 에러가 발생한다.
class Parent {
int a;
Parent() {
a = 10;
}
Parent(int n) {
a = n;
}
}
class Child extends Parent {
int b;
Child() {
super(40);
b = 20;
}
void display() {
System.out.println(a);
System.out.println(b);
}
}
public class Inheritance {
public static void main(String[] args) {
Child ch = new Child();
ch.display();
}
}

super()를 이용하여 Parent 클래스의 두 번째 생성자에 접근해 초기화되어 a는 40으로 출력되는 모습을 볼 수 있을 것이다.
메서드 오버로딩이란 같은 이름의 메서드를 중복하여 정의하는 것을 의미한다.
자바에서는 원래 한 클래스 내에 같은 이름의 메서드를 둘 이상 가질 수 없다.
메서드 오버로딩은 객체 지향 프로그래밍의 특징 중 하나인 다형성을 구현하는 방법 중 하나로 println() 메서드는 그 예로 적합하다.
메서드 오버로딩의 조건은 두 가지가 있는데
아래는 오버로딩의 예제를 보여준다.
class Parent {
int a;
Parent() {
a = 10;
}
Parent(int n) {
a = n;
}
}
class Child extends Parent {
int b;
Child() {
super(40);
b = 20;
}
void display() {
System.out.println(a);
System.out.println(b);
}
}
public class Inheritance {
public static void main(String[] args) {
Child ch = new Child();
ch.display();
}
}

오버로딩은 새로운 메서드를 정의하는 것이다.
오버라이딩은 상속받은 기존의 메서드를 "재정의"하는 것이다.
class Parent {
void display() {
System.out.println("부모 클래스의 display 메서드임.");
}
}
class Child extends Parent {
// display() 오버라이딩
@Override
void display() {
super.display();
}
void display(String str) {
System.out.println(str); // display() 오버로딩
}
}
public class ExOver {
public static void main(String[] args) {
Child ch = new Child();
ch.display();
ch.display("오버로딩된 display()임");
}
}
메서드 디스패치란 어떤 메서드를 호출할지 결정하여 실제로 실행시키는 과정이다.
자바는 런타임 시 객체를 생성하고, 컴파일 시에는 생성할 객체 타입에 대한 정보만 보유한다.
이 과정은 static( 정적 ), dynamic( 동적 )으로 분류되게 된다.
컴파일 할 때, 컴파일러가 main과 같은 어떤 메소드를 호출할지 명확하게 알고 있는 경우이다.
class SubClass {
void print() {
System.out.println("print 호출됨");
}
}
public class StaticDynamic {
public static void main(String[] args) {
new SubClass().print();
}
}
컴파일 할 때, 컴파일러가 어떤 메소드를 호출할지 모르는 경우로 이 경우에는 런타임 시점에서 컴파일러가 알게 된다.
class SubClass {
void print() {
System.out.println("print 호출됨");
}
}
class SubClass2 extends SubClass {
void print() {
System.out.println("SubClass2의 print 호출됨");
}
}
class SubClass3 extends SubClass {
void print() {
System.out.println("SubClass3의 print 호출됨");
}
}
public class StaticDynamic {
public static void main(String[] args) {
SubClass s1 = new SubClass();
SubClass s2 = new SubClass2();
SubClass s3 = new SubClass3();
s1.print();
s2.print();
s3.print();
}
}

자바에서는 하나 이상의 추상 메소드를 포함하는 클래스를 추상 클래스라고 한다.
추상 메소드는 자식 클래스에서 반드시 오바라이딩해야만 사용할 수 있는 메소드를 의미한다.
추상 메소드를 선언하여 사용하는 목적은 추상 메소드가 포함된 클래스를 상속받는 자식 클래스가 반드시 추상 메소드를 구현하도록 하기 위함이다.
abstract class Animal { abstract void cry(); }
class Dog extends Animal {
@Override
void cry() {
System.out.println("왕왕");
}
}
class Cat extends Animal {
@Override
void cry() {
System.out.println("애옹");
}
}
public class Polymorphism {
public static void main(String[] args) {
Dog d = new Dog();
Cat c = new Cat();
d.cry();
c.cry();
}
}

final 변수는 한 번 값을 할당하게 되면 수정할 수 없다는 특징을 가지고 있다.
한 번 값을 할당한다는 것은 한번의 초기화가 가능하다는 것이다.
결과적으로 값을 할당하고 수정을 원치 않는 경우에 사용하면된다.
class, method, variable 등 접근제한자를 사용할 수 있는 경우라면 모든 곳에 사용할 수 있다.
java.lang 패키지 중 가장 많이 사용되는 클래스는 Object 클래스이다.
Object 클래스는 자바의 모든 클래스 최상위에 위치하는 조상 클래스이다.
따라서 모든 클래스는 Object 클래스의 모든 메소드를 바로 사용할 수 있다.
상속의 사용 이유는 아래와 같았다.
상속은 캡슐화를 깨뜨린다는 단점을 가지고 있다.
그로 인해 우리는 조합( composition )을 개발할 때 주로 사용하고 있다.
조합 : 기존 클래스가 새로운 클래스의 구성요소로 쓰인다. 새로운 클래스를 만들로 private 필드로 기존 클래스의 인스턴스를 참조하는 것이다.
public class Composition2 {
private Composition composition;
private Menu menu;
}
위 코드와 같은 식으로 조합을 사용하게 되는데, 우리는 알게 모르게 다들 조합을 쓰고 있었을 것이다. 나 또한 extends 잘 사용하지 않았다.
인터페이스를 상속하는 implement 또한 잘 사용하지 않았었다.
자바에서는 다중상속을 허용하지 않고 있다. 그 이유는 다이아몬드 문제라고 한다.

위와 같은 상속관계 속에서 Child는 Father와 Mother를 상속받아 멤버 필드와 메소드를 받을 것이다.
그런데 Father과 Mother에 같은 메소드가 있고, Father와 Mother는 Person에서 member()을 서로 다르게 오버라이딩 했다고 하면 Child.member()는 어떤식으로 출력이 될지 의문이다.
우리도 의문인데 기계는 당연히 오류를 낸다.
그래서 해결방법으로 나온게 인터페이스를 통한 다중상속이다.
interface Person {
String contry ="한국";
void member();
}
interface Father extends Person {
@Override
void member();
}
interface Mother extends Person {
@Override
void member();
}
public class Child implements Father, Mother {
@Override
public void member() {
System.out.println("======");
}
}
위와 같은 식으로 다중 상속을 가능하게 할 수 있다.