다른 클래스가 가지고 있는 멤버(필드, 메소드)들을 새로 작성할 클래스에서 직접 만들지 않고
상속을 받음으로써 새 클래스가 자신의 멤버처럼 사용할 수 있는 기능
-> extends : 확장하다, 연장하다
[접근제한자] class 클래스명 extends 클래스명 {}
public class Student extends Person {}
-> Student 클래스에 Person 클래스 내용을 연장한다.
== Student 클래스에 Person 클래스 내용(필드, 메소드)을 추가하여 확장한다.
클래스의 재사용, 연관된 일련의 클래스들에 대한 공통적인 규약 정의
- 클래스간의 관계가 다중 상속보다 명확하고 신뢰성 있는 코드 작성
- 자바에서는 다중 상속 미지원 → 단일상속만 지원
package edu.kh.inheritance.model.vo;
public class Person {
// 필드
private String name;
private int age;
private String nationality; // 국적
// 생성자
// 기본 생성자
public Person() {}
// 매개변수 생성자
public Person(String name, int age, String nationality) {
this.name = name;
this.age = age;
this.nationality = nationality;
}
// 메소드
// getter / setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getNationality() {
return nationality;
}
public void setNationality(String nationality) {
this.nationality = nationality;
}
// 상속의 특징 : 코드 추가 및 수정에 용이함
// (새로운 메소드를 Person에 추가하였을 때
// Student, Employee가 사용 가능한지 확인)
public void breath() {
System.out.println("사람은 코나 입으로 숨을 쉰다.");
}
public void move() {
System.out.println("사람은 움직일 수 있다.");
}
}
package edu.kh.inheritance.model.vo;
public class Employee extends Person {
// 필드
private String company;
// 기본 생성자 : Ctrl + Space - Constructor
public Employee() {}
// 매개변수 생성자 : Alt + Shift + s - Generate Constructor using Fields
public Employee(String name, int age, String nationality, String company) {
this.company = company;
}
// 메소드
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
}
package edu.kh.inheritance.model.vo;
public class Student extends Person {
// Student 클래스에 Person 클래스 내용을 연장한다.
// == Student 클래스에 Person 클래스 내용(필드, 메소드)을 추가하여 확장한다.
// *** 상속 ***
// extends : 확장하다, 연장하다+
// 필드
private int grade; // 학년
private int classRoom; // 반
// 생성자
public Student() {} // 기본 생성자
// 매개변수 생성자
public Student(String name, int age, String nationality, int grade, int classRoom) {
this.grade = grade;
this.classRoom = classRoom;
}
// 메소드
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public int getClassRoom() {
return classRoom;
}
public void setClassRoom(int classRoom) {
this.classRoom = classRoom;
}
}
package edu.kh.inheritance.model.service;
import edu.kh.inheritance.model.vo.Employee;
import edu.kh.inheritance.model.vo.Person;
import edu.kh.inheritance.model.vo.Student;
public class InheritanceService {
public void ex1() {
// 상속 확인
// - Person을 상속받은 Student가 Person의 필드, 메소드를 사용할 수 있는가?
Person p = new Person();
// p.name = "홍길동"; // private 때문에 직접 접근 불가
p.setName("홍길동");
p.setAge(25);
p.setNationality("대한민국");
System.out.println(p.getName());
System.out.println(p.getAge());
System.out.println(p.getNationality());
System.out.println("--------------------------------");
// Student 객체 생성
Student std = new Student();
// Student만의 고유한 메소드
std.setGrade(3);
std.setClassRoom(5);
// Person 클래스로부터 상속받은 메소드
std.setName("고아라");
std.setAge(19);
std.setNationality("대한민국");
System.out.println(std.getGrade());
System.out.println(std.getClassRoom());
// Student가 Person의 메소드뿐만 아니라 필드도 상속받았는지 확인
System.out.println(std.getName());
System.out.println(std.getAge());
System.out.println(std.getNationality());
System.out.println("--------------------------------");
Employee emp = new Employee();
// Employee만의 고유 메소드
emp.setCompany("KH정보교육원");
// Person 클래스로부터 상속받은 메소드
emp.setName("다나카");
emp.setAge(35);
emp.setNationality("일본");
System.out.println(emp.getCompany());
System.out.println(emp.getName());
System.out.println(emp.getAge());
System.out.println(emp.getNationality());
System.out.println("--------------------------------");
// 추가된 breath() 메소드 확인하기
p.breath();
std.breath();
emp.breath();
}
}
package edu.kh.inheritance.run;
import edu.kh.inheritance.model.service.InheritanceService;
public class InheritanceRun {
public static void main(String[] args) {
InheritanceService service = new InheritanceService();
service.ex1();
}
}
Person()을 상속받아 그 속성과 기능을 사용 가능한 모습을 볼 수 있다.
부모의 생성자를 호출하는 코드
super : 상위 <-> sub : 하위
- 반드시 자식 생성자 최 상단에 작성되어야 함
- super() 생성자 때문에 자식 객체 내부에 부모 객체가 생성됨
- super() 생성자 미작성 시 컴파일러가 컴파일 단계에서 자동으로 추가해 줌
- 상속을 통한 자식 클래스 정의 시 해당 자식 클래스의 부모 객체를 가리키는 참조변수
- 자식 클래스 내에서 부모 클래스 객체에 접근하여 필드나 메소드 호출 시 사용
public class Student extends Person {
// Student 클래스에 Person 클래스 내용을 연장한다.
// == Student 클래스에 Person 클래스 내용(필드, 메소드)을 추가하여 확장한다.
// *** 상속 ***
// extends : 확장하다, 연장하다+
// 필드
private int grade; // 학년
private int classRoom; // 반
// 생성자
public Student() {
// Student() 객체 생성 시
// 내부에 상속받은 Person 객체를 생성하기 위한
// Person 생성자 호출 코드가 필요하다!
// super : 상위 <-> sub : 하위
// super == Person
super(); // super() 생성자
// 부모의 생성자를 호출하는 코드
// 반드시 자식 생성자 최 상단에 작성되어야 한다!
} // 기본 생성자
// 매개변수 생성자
public Student(String name, int age, String nationality, int grade, int classRoom) {
// 전달받은 값 중 name, age, nationality
// 부모 필드에 세팅하기
// this.name = name; // (X)
// 상속받은 부모의 필드(name)를
// 자식의 필드처럼 사용하려 했으나 오류 발생
// 왜? 부모의 필드에 private 접근 제한자가 있어서
// 아무리 물려받은 자식이라도 다른 객체이기 때문에 직접 접근 불가
// -> 간접 접근 방법을 사용
// setName(name);
// setAge(age);
// setNationality(nationality);
// 부모의 setter를 이용하면 되지만 비효율적임
super(name, age, nationality); // 부모 매개변수 생성자 호출
this.grade = grade;
this.classRoom = classRoom;
}
public void ex2() {
// super() 생성자 사용 방법
// Student 매개변수 5개 짜리 생성자
Student std = new Student("공유", 35, "한국", 1, 3);
System.out.println(std.getName()); // 공유
System.out.println(std.getAge()); // 안 나옴
System.out.println(std.getNationality()); // 한국
System.out.println(std.getGrade()); // 1
System.out.println(std.getClassRoom()); // 3
}
자식 클래스가 상속 받은 부모 메소드를 재작성 하는 것
- 부모가 제공하는 기능을 후손이 일부 고쳐 사용하겠다는 의미로
자식 객체를 통한 실행 시 후손 것이 우선권을 가짐
어노테이션(Annotation) : 컴파일러에게 알려 주기 위한 코드 (컴파일러 인식용 주석)
@Override 어노테이션 : 해당 메소드가 오버라이딩 되었음을 컴파일러에게 알려 주는 역할
- 메소드 이름 동일
- 반환형 동일
- 매개변수 동일
- 접근 제한자 같거나 더 넓은 범위
ex) (부) protected -> (자) protected 또는 public- 예외처리 범위는 같거나 더 좁게
- 부모의 private 메소드는 오버라이딩 불가
-> 왜? 자식이 접근할 수 없기 때문에
// Person으로부터 상속받은 메소드 중
// move() 메소드를 Employee에 맞게 재정의(== 오버라이딩)
// @Override 어노테이션 : 해당 메소드가 오버라이딩 되었음을
// 컴파일러에게 알려 주는 역할
@Override // 오버라이딩 시 작성해 주는 것이 좋다.
public void move() {
// 기존 부모 코드 삭제 후 자식이 새롭게 재정의
System.out.println("오버라이딩된 move() 메소드");
System.out.println("효율적으로 빨리 일처리하고 야근을 하지 않는다.");
}
public void ex3() {
// 오버라이딩 확인 예제
Student std = new Student();
Employee emp = new Employee();
std.move(); // 오버라이딩 X // Person의 메소드 수행
emp.move(); // 오버라이딩 O // Employee 메소드 수행
}
오버라이딩 된 메소드가 출력된 것을 볼 수 있다.
모든 클래스는 Object 클래스의 후손
== 모든 클래스의 최상의 부모는 Object
public class Person extends Object {
// class명에 상속 구문이 없다면
// 컴파일러가 자동으로 extends Object 구문을 추가해 줌
.
.
.
public void ex3() {
// Object.toString() 메소드 오버라이딩
// - toString() 메소드는 객체가 가지고 있는 모든 값(필드)을
// 하나의 문자열로 반환하는 용도의 메소드
@Override
public String toString() {
return name + " / " + age + " / " + nationality;
// 김철수 / 15 / 대한민국
}
public void ex4() {
// 모든 클래스는 Object 클래스의 후손
// == 모든 클래스의 최상의 부모는 Object
// 1) Object 상속 여부 확인
Person p = new Person(); // Object를 상속받은 Person 객체 생성
Student std = new Student(); // Person을 상속받은 Student 객체 생성
// Object - Person - Student
Scanner sc = new Scanner(System.in);
String str = "abc";
// ** Object 상속과 단계적인 상속 확인
System.out.println(p.hashCode()); // Object에서 물려받은 hashCode()
System.out.println(std.getAge()); // Person에서 물려받은 getAge()
System.out.println(std.hashCode());
// Person이 Object에서 물려받은 hashCode()를
// Student가 또 물려받아 사용
// -> 상속의 상속의 상속의 .... 상속 가능
// * Object가 모든 클래스의 최상위 부모인지 확인
System.out.println( sc.hashCode() );
// Object - haseCode()
System.out.println( str.hashCode() );
// String - hashCode()
// -> String이 Object에게 물려받은 hashCode를 오버라이딩
}
// toString() 오버라이딩
@Override
public String toString() {
// super 참조 변수
return super.toString() + grade + " / " + classRoom;
// toString() 오버라이딩
@Override
public String toString() {
return super.toString() + " / " + company;
}
public void ex5() {
Person p = new Person("이동휘", 28, "한국");
System.out.println( p.toString() ); // 이동휘 / 28 / 한국
System.out.println( p ); // 이동휘 / 28 / 한국
// print 구문 수행 시 참조 변수명을 작성하면
// 자동으로 toString() 메소드를 호출해서 출력한다!
System.out.println("-------------------------------------");
Student std = new Student("윈터", 27, "미국", 2, 6);
System.out.println(std.toString());
// 1) Student 클래스 toString() 오버라이딩 전
// Person으로부터 상속받은 오버라이딩된 toString() 수행
// 2) Student 클래스 toString 오버라이딩 후
// Student의 toString() 수행
Employee emp = new Employee("김근로", 26, "한국", "OO증권");
System.out.println(emp.toString());
}
마지막 클래스라는 의미로 "더 이상 상속 불가"를 뜻함
package edu.kh.inheritance.model.vo;
public /*final*/ class Parent { // 부모 클래스
// Object 상속 중
// ** final 클래스 **
// -> 마지막 클래스라는 의미로
// "더 이상 상속 불가"를 뜻함
public /*final*/ void method() {
System.out.println("테스트용 메소드");
}
}
package edu.kh.inheritance.model.vo;
public class Child extends Parent { // 자식 클래스
// Parent 상속 중
//The type Child cannot subclass
// the final class Parent
// (final 클래스인 Parent는
// Child 클래스를 자식으로 가질 수 없다.)
@Override
public void method() {
System.out.println("오버라이딩 성공!!");
// Cannot override
// the final method from Parent
}
}