✍Inheritance(상속)
다른 클래스가 가지고 있는 멤버(필드, 메소드)들을 새로 작성할 클래스에서 직접 만들지 않고 상속을 받음으로써 새 클래스가 자신의 멤버처럼 사용할 수 있는 기능
부모클래스의 멤버(필드, 메소드)를 자식이 물려 받아 자신의 것처럼 사용하는 것
자식의 공통된 코드를 하나의 부모 클래스에 모은다.
-> 상속을 구현하는데는 추상화로 자식의 공통된 코드를 중복제거
부모클래스 == 하나
(그 부모클래스를 물려받는) 자식클래스 == 여러개
부모에게 적용된 것 은 공통적으로 모든 자식에게 동일하게 적용
부모 클래스에게 규칙을 정해두면 자식들은 그 규칙을 전부 따르게 된다.
1. 상속의 목적
(부모 타입의) 클래스 재사용, 연관된 일련의 클래스들에 대한 공통적인 규약 정의
같은 부모를 둔 자식 클래스들은 공통된 규약이 있다.
2. 상속의 장점
코드의 재사용성 증가 + 코드 길이 감소
1. 보다 적은 양의 코드로 새로운 클래스 작성 가능
2. 코드를 공통적으로 관리하기 때문에 코드의 추가 및 변경 용이
3. 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여
3. 상속 받는 방법 (extends)
클래스 간의 상속 시에는 extends 키워드 사용
자식의 멤버에 부모의 멤버가 추가되어 자식의 몸이 확장되는 것을 표현한다. (+ 메모리 구조)
표현식
[접근제한자] class 클래스명 extends 클래스명 { }
ex) public class Academy extends Company { }
➡ extends : 확장하다
자식 클래스는 부모로부터 상속 받아서 확장되기때문이다.
🔎extends 라는 키워드를 사용하는 이유!
- 자식이 부모의 멤버(필드, 메서드)를 상속 받음으로써 자식이 가지게 되는 멤버의 양이 늘어나기(확장) 때문이다.
예제 - https://dbdbd.tistory.com/39
public class Student extends Person { private int grade; // 학년 private int classRoom; // 반 //생성자 public Student() { } //Student getter/setter 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; } }
➡Student 는 Person 클래스를 상속 받았으므로 Person의 멤버를 물려 받아 자신의 것처럼 사용
=> 부모의 멤버만 물려받는 것이 아닌 부모 객체를 자식 객체 안에 생성하여 자식이 자기것처럼 사용
특징
🚩단일 상속과 다중 상속
1. ⭐ Single Inheritance (단일 상속 )⭐
클래스간의 관계가 다중 상속보다 명확하고 신뢰성 있는 코드 작성
자바에서는 다중 상속 미지원 == 단일상속만 지원
➡ C 자식 클래스의 경우 A,B 누구에게 상속 받았는지 명확하게 알 수 없기 때문에
public class A{
public void ex1() { }
}public class B{
public void ex1() { }
}public class C extends A, B{
public void ex1() { }
}
자바는 클래스간의 다중상속이 불가하다.
참고) 인터페이스간에는 다중상속 가능.
2. Multiple Inheritance (다중 상속 )
자바는 클래스에서 다중상속 X 인터페이스는 다중상속 O
C++에서 가능한 기능으로 여러 클래스로부터 상속을 받으며 복합적인 기능을 가진 클래스를 쉽게 작성 가능 서로 다른 클래스로부터 상속 받은 멤버 간의 이름이 같은 경우 문제 발생
🚩 상속의 특징
1. ⭐ 모든 클래스는 Object클래스의 후손 ⭐
: 모든 객체가 가지는 기능(메서드)를 추상화하여 모아둔 클래스
자바에서 모든 객체들이 가져야할 공통적인 기능을 추상화를 통해 추출하여 모아둔 최상위 클래스
Object클래스가 제공하는 메소드를 오버라이딩하여 메소드 재구현 가능
ex) java.lang.String 클래스의 equals()와 toString()
//Person Class public class Person extends Object{
p1.hashCode(); //Person 클래스에는 hashCode()가 작성되지 않았지만 사용할 수 있다. // == 상속 받은 Object의 hashCode()를 사용할 수 있다. s1.hashCode(); //s1같은 경우에는 Object -> Person -> Student 계속 상속 받아 사용할 수 있다.
➡ 클래스명 앞에 상속 구문이 누락된 경우
컴파일러에 의해서 자동으로 extends Object 구문이 추가된다. 그래서 hashCode를 사용할 수 있다.
🔎 컴파일러가 자동으로 생성해 주는 구문
- 기본 생성자
- extends Object
- import java.lang.* (*은 모든걸 의미)
- .toString()
- 자동형변환
- super()
➡ 오브젝트, 스트링의 패키지는 java.lang인데 내가 만든 패키지에서 import없이 사용가능한 이유!
➡ java.lang 패키지를 컴파일러가 자동으로 import하는 구문을 추가하여 String 같은 클래스를 바로 사용 가능하게한다.
String, Scanner, Arrays를 Object 객체로부터 받아왔는지에 대한 상속 계층도
출처: https://docs.oracle.com/javase/8/docs/api/
- Object는 java.lang에 존재한다.
- Object는 가장 위에 있기 때문에 상속 받는 계층이 없다.
➡ 클래스 개체(Objct)는 클래스 계층의 루트(최상위)입니다.
모든 클래스는 오브젝트를 슈퍼 클래스로 가지고 있습니다. 배열을 포함한 모든 개체는 이 클래스의 메서드를 구현합니다.
2. 부모클래스의 생성자, 초기화 블록은 상속 안 된다.
상속 시 부모의 생성자, 초기화 블록은 상속되지 않는다.
단, 자식 생성자 내부에서 부모 생성자를 호출하는 super() 생성자 사용 가능하다.
super() 생성자는 반드시 자식 생성자 제일 위에 작성되어야한다.
자식을 호출할때 그 안에 부모도 호출해야하니깐 super()사용
-> 위에 작성하지 않으면 자식이 부모를 계속 만들어내는 경우가 생긴다.
자식 클래스 생성 시, 부모 클래스 생성자가 먼저 실행되고 자식 클래스 생성자 안에서 부모 클래스 생성자 호출을 명시하고 싶으면 super() 활용한다.
3. 부모의 private멤버는 상속은 되지만 직접 접근 불가
상속시 부모의 private 멤버는 상속되나 자식이 직접 접근은 불가하다.
-> 간접 접근 방법은 사용할 수 있다 : 부모의 getter/setter, super() 생성자
➡ 설명
: 자식 객체 생성 시에 부모의 필드 값도 전달 받은 경우, 자식 생성자 안에서 부모의 private 필드에 직접 접근하여 대입 불가 super() 이용하여 전달받은 부모 필드 값을 부모 생성자 쪽으로 넘겨서 생성하거나 setter, getter 메소드를 이용하여 접근하면 된다.
🚩super() & super.
1. super()
: 부모 부분을 참조하는 참조변수
부모 객체의 생성자를 호출하는 메소드로 기본적으로 후손(자식) 생성자에 부모 생성자 포함 후손 객체 생성 시에는 부모부터 생성된다.
그렇기 때문에 후손 클래스 생성자 안에는 부모 생성자를 호출하는 super()가 첫 줄에 존재해야한다. (부모 생성자가 가장 먼저 실행되어야 하기 때문에 명시적으로 작성 시에도 반드시 첫 줄에만 작성)
매개변수 있는 부모 생성자 호출은 super(매개변수, 매개변수)를 넣으면 된다.
-> 부모부분에 있는 변수를 초기화하는것
자식 객체 내의 부모 객체를 생성하기 위해 작성한다.
미작성 시 컴파일러가 자동으로 추가
방법1) 부모 필드 간접 접근 방법인 setter 이용
public Student(int grade, int classRoom, String name, int age, String nationality) { this.grade = grade; this.classRoom = classRoom; setName(name); //setName은 상속받은 person에 존재 setAge(age); setNationality(nationality); }
this.name이 안되는 이유 : ➡ 상속 받은 부모의 필드가 private이면 직접접근이 불가하다.
setName(name); ➡ setName은 상속받은 person에 존재
방법2) super( )생성자
public Student(int grade, int classRoom, String name, int age, String nationality) { super(name, age, nationality ); this.grade = grade; this.classRoom = classRoom; }
➡ super(); ( )안에 매개 변수 적어주기
자식 생성자 내부 첫 줄에 작성 , 자식 객체 생성 시 내부의 부모 부분을 생성하는데 사용되는 생성자
참고) 자식 객체 부분의 생성보다 부모 부분이 먼저 생성된다.
Service 클래스
Student s2 = new Student(2, 10, "봉봉", 16, "대한민국"); System.out.println("===super 생성자 확인==="); System.out.println(s2.getName()); System.out.println(s2.getAge()); System.out.println(s2.getNationality());
➡ super생성자 사용하면 코드 재사용성 증가, 코드 길이 감소
2. super.
상속을 통한 자식 클래스 정의 시 해당 자식 클래스의 부모 객체를 가리키는 참조변수
자식 클래스 내에서 부모 클래스 객체에 접근하여 필드나 메소드 호출 시 사용
코드재사용성 증가, 코드길이 감소
➡ 설명
:자식 객체 안에 부모객체가 있는데 그 부모 객체에도 주소가 있다. super. (참조변수)는 자식객체 안에 있는 부모변수를 가리킨다.
Student 클래스
public String information() { return getName() + " / " + getAge() + " / " + getNationality() + " / " + grade + " / " + classRoom; }
➡ 이름 / 나이 / 국적 / 학년 / 반 값 가져오기
Student 클래스가 가진 고유한 필드와 간접적으로 얻어온 필드 3개(이름/ 나이/국적)를 가져오기
Service 클래스
Student s2 = new Student(2, 10, "봉봉", 16, "대한민국"); System.out.println(s2.information());
➡ 출력 : 봉봉 / 16 / 대한민국 / 2 / 10
Student 클래스의 information 출력
❗ 만약 Student 클래스에서
public String information() { return information() + " / " + grade + " / " + classRoom; } // StackOverfolw가 생긴 상황
👉문제 : 부모클래스의 information을 쓰려고 여기 호출했다.
그런데 메소드 이름이 information으로 동일 상속받은것과 호출한게 이름이 같다.
(ctrl눌러서 위치를 확인하면 무한 반복)
👉과정 : 우선순위는 information() 이라고 작성한 것에 대해
해당 information이 부모? 자식? 누구의 것인지 확실히 구분되지 않아 자식의 메소드로 판단하여 호출한다.
=> 무한 굴레에 빠진다.
같은 메소드가 호출되는 현상( 재귀호출 )이 무한히 반복되어
메소드가 쌓이는 메모리 영역 Stack이 넘쳐 흐르는 것 ==> StackOverflow
public String information() { return super.information() + " / " + grade + " / " + classRoom; }
👉해결 : information앞에 super. 추가하면 해결
super 참조변수를 붙여 이름이 같은 메소드 중 부모의 메소드를 참조하도록 지정한다.
==> 코드길이 감소, 원래 코드 재사용
🚩오버라이딩(Overriding)
Over : 위에 + Riding: 올라타다 => 덮어쓰기
=> 코드 재정의
자식 클래스가 상속 받은 부모 클래스의 메서드를 재정의(재작성) 하는 것
메서드가 오버라이딩 된 경우 자식, 부모 두 클래스 같은 이름의 메서드가 존재하게 되는데
오버라이딩 된 자식의 메서드가 호출 시 우선권을 가진다.
자식 클래스가 상속 받은 부모 메소드를 재작성 하는 것 부모가 제공하는 기능을
후손이 일부 고쳐 사용하겠다는 의미로 자식 객체를 통한 실행 시 후손 것이 우선권을 가짐
오버로딩과 비교해서 많이 물어본다 == 공부해라
1. ⭐ @Override Annotation ⭐
메소드 헤드라인 위에 반드시 Annotation, @Override 표시
접근 제어자를 부모 것보다 같거나 넓은 범위로 변경 가능하다.
부모 메소드의 예외처리 클래스 처리범위보다 좁은 범위로 예외처리 클래스 수정 가능하다.(..?)
@Override public String information() { }
➡ Annotation은 컴파일러가 보는 주석이라고 알려주는 용도라고 생각하면된다.
2. 성립 조건
🔎 부모 클래스의 메소드와 자식 클래스의 메소드 비교
1. 메소드명이 동일해야한다. (오버로딩과 동일)
2. 매개변수의 개수, 타입, 순서가 모두 동일해야한다.
3. 반환형(return 타입)도 동일해야한다.
4. 부모의 private 메소드는 접근이 불가해서 오버라이딩 할 수 없다.
5. 접근 제한자의 범위는 같거나 더 넓어야 한다.
(이해하기 위해선 접근제한자의 범위를 다 알아야한다.)
6. 예외 처리의 범위는 같거나 더 구체적이여야 한다.
요약
- 메소드 이름 동일 (오버로딩과 동일/ 밑에 나머지는 다 다름)
- 매개변수의 개수, 타입, 순서 동일
- 리턴 타입 동일
- private 메소드 오버라이딩 불가
- 접근 제한자는 같거나 더 넓게
- 예외 처리의 범위는 같거나 더 구체적
//Person 클래스 public String overridingTest() { return "Person 클래스에 작성된 오버라이딩 테스트 메서드"; } //Student 클래스 public String overridingTset() { //이름이 다르니깐 다른 메서드인데 @override하면 에러뜬다. return "Student에서 오버라이딩 되었다."; } //Service클래스 public void example3() { Student s1 = new Student(); System.out.println(s1.overridingTset()); //오버라이딩 된 자식의 메서드가 수행된다. }
➡ run에서의 결과 Student에서 오버라이딩 되었다.
1) Object. toString() 오버라이딩
toString() : 객체의 모든 필드 정보를 문자열로 반화하는 용도의 메소드
➡ 객체들 정보 출력할때 toString을 이용해 전부 제공한다.
Object의 toString() 메소드 : 클래스명 + @ + 16진수 해시코드
➡ 사용 목적에 맞지 않으므로 상속 받은 자식이 재정의
HexString : 16진수 문자열로 hashCode를 출력
해시코드 : 메모리상의 주소를 다른 방법으로 표시
Person 클래스
@Override //Override Annotation : 해당 메소드는 오버라이딩 되었다고 컴파일러에게 알려주는 구문 public String toString() {//매개변수 없으니 () // 내부 코드 재정의 return name + " / " + age + " / " + nationality; }
Service 클래스
1. Object의 toString을 오버라이딩 하기 전
public void example2() { //오버라이딩, 오버로딩 예제 Person p1 = new Person("봉국" , 14, "대한민국"); System.out.println(p1.toString()); }
➡ 오버라이딩 하기전에 p1.tostring()하면 부모 밖에 없기때문에 그 내부에서만 진행
2. Object의 toString을 오버라이딩 후public void example2() { // 봉국 / 14 / 대한민국 Person p1 = new Person("봉국" , 14, "대한민국"); System.out.println( p1 ); }
➡ p1.tostring() 오버라이딩 된 toString()이 호출된다
print 관련 구문 내에 참조변수를 작성하면 해당 객체인 p1 출력한다.
p1만 출력하면 자동으로 연결이 되어 봉국 / 14 / 대한민국 이렇게 나온다.
run 클래스
System.out.println(p1.toString()); 의 결과
1. Object의 toString 오버라이딩 하기 전
toString 결과 출력 예시 -> getClass().getName() + '@' + Integer.toHexString(hashCode())
결과 : edu.kh.Inheritance.ex.model.vo.Person@139a55
2. Object의 toString 오버라이딩 후
결과 : 봉국 / 14 / 대한민국
+ Object의 toString() 상속 받아 오버라이딩한 Person을 상속 받은 Student에서 toString()을 또 오버라이딩 하기
Student 클래스(부모인 person클래스 상속받음)
@Override public String toString(){ //접근제한자 범위 작으면 안된다. 자료형, 매개변수 다 똑같아야한다 return super.information() + " / " + grade + " / " + classRoom; return super.toString() + " / " + grade + " / " + classRoom; }
3. 오버라이딩 오버로딩 차이점!
오버로딩: 다 달라야한다
오버라이딩: 전두 다 다른건 안된다.
Student 클래스
//메서드 오버로딩 public int sum(int num1, int num2) { int sum = num1 + num2; return sum; //호출 됐을때 num1, num2를 전달받아 인트값 두개를 반환하는 메서드 } public double sum(double num1, double num2) { //접근제한자 같고 반환형 다르고 변수명 똑같고 타입 다르고 이름 같고 double sum = num1 + num2; return sum; //호출 됐을때 num1, num2를 전달받아 int값 두개를 반환하는 메서드 }
Service 클래스public void example3() { Student s1 = new Student(); System.out.println(s1.sum(10,20)); System.out.println(s1.sum(3.14,2.12)); }
결과:
30
5.26
➡ 이름은 같지만 매개변수를 다르게 해서 오버로딩 하게해서 실수도 계산되게 만든다,
메서드 호출 시 원하는 결과는 같으나 매개변수의 타입, 개수, 순서에 따라 수행되는 코드가 달라져야할 때 오버로딩을 사용한다.
대표적인 응용은 print
🚩 final 예약어
상속 불가능 클래스
== 절대로 바뀌어서는 안되는 클래스
상속 관계 내에서 더이상 오버라이딩 되지않는다 => 정의 될 수 있는 마지막 메서드
person 클래스
public final void breath() { System.out.println("사람은 코나 입으로 호흡을 해야한다. "); }
➡ person 객체만의 기능 + 상속 되어도 오버라이딩 불가
student class
@Override public void breath(){ // Cannot override the final method from Person // ->person에 있는 final 메서드로 오버라이딩 불가 }
➡ final 메서드 오버라이딩 불가 확인
해결법
다른이름으로 바꾸거나 override하지 않기
person class
public final void breath() { System.out.println("사람은 코나 입으로 호흡을 해야한다. "); }
student class
public final class Student extends Person {}
해결법
다른이름으로 바꾸거나 override하지 않기
'Backend > Java' 카테고리의 다른 글
[Java] Scanner 주의사항/ .next()와 .nextline() 차이 (0) | 2021.09.16 |
---|---|
[Java] Polymorphism(다형성)_ 업·다운 캐스팅, 인터페이스, 추상클래스·메서드, 바인딩 (0) | 2021.09.11 |
[Java] OOP 4_Method(메소드), 객체 배열 (0) | 2021.09.08 |
[JAVA] 분기문_break, continue (0) | 2021.09.08 |
[Java] OOP 3_Constructor(생성자) (0) | 2021.09.07 |