상속
- 다른 클래스가 가지고 있는 멤버(필드, 메소드)들을 새로 작성할 클래스에서 직접 만들지 않고 상속을 받음으로써 새 클래스가 자신의 멤버처럼 사용할 수 있는 기능
[접근제한자] class 클래스명(1) extends 클래스명(2) {}
// 클래스명(1) -> 자식클래스 클래스명(2) -> 부모클랙스
public class Academy extends Company {}
모든 클래스는 Object 클래스의 후손
💡 -> 맨 상위 클래스에는 Object 클래스가 있음
부모 클래스의 생성자, 초기화 블록은 상속 안됨
단일상속만 지원함 (다중 상속 ❌)
부모의 private멤버는 상속은 되지만 직접 접근 불가
자식은 부모의 기능을 사용할 수 있지만, 부모는 자식의 기능을 사용할 수 없다
자식입장에서는 부모의 기능을 물려받지만, 부모입장에서는 자식이 기능확장을 하기 위해서 상속함
// 클래스에 상속관계 설정하기
// 상속 : 두개의 클래스간의 부모(super), 자식(sub), 관계를 설정
// 방법 : 클래스 선언부 extends 예약어를 사용
ElectricCar 클래스는 Car 클래스를 상속받는다
public class ElectricCar extends Car{
public void charge(){
}
}
ElectricCar electricCar = new ElectricCar(); // ElectricCar 인스턴스 생성
다음과 같이 ElectricCar 인스턴스를 생성하면, 상속
관계에 있는 Car 까지 포함해서 인스턴스를 생성한다
-> 부모의 필드와 메서드만 물려받는것이 아닌, 부모 클래스 자체도 생성된다
-> 부모와 자식 모두 생성되고 공간도 구분된다
여기서 electricCar.move()
호출 하면?
호출하는 변수의 타입(클래스)을 기준으로 선택
한다.
-> electricCar 타입은 ElectricCar 클래스
이므로 이 타입을 선택
-> 하지만 ElectricCar 클래스
는 move 메소드가 없음
-> 이 경우 ElectricCar
의 부모 클래스인 Car
타입으로 가서 찾음
-> 부모도 존재하지 않으면 컴파일 오류
발생
✅ 정리
클래스가 상속관계일때 부모가 갖고 있는 메소드를 자식 클래스에서 재정의 하는 것
- 메소드 선언부를 부모에 선언된 메소드와 동일하게 선언해야한다.
- 부모와 자식클래스 안에 동일한 함수가 있을시에 자식안의 함수를 먼저 호출함
- 메소드 오버라이드를 할때는 어노테이션으로 표시해서 관리를 해줌 ->
@Override
// Overridechild 클래스
package com.inherit.model.vo;
public class OverrideChild extends OverrideParent{
// 어노테이션 : @어노테이션명
@Override // 오버라이드가 맞는지 확인 -> 선언한 메소드가 부모클래스에 있는지 확인 -> 없으면 오류
public void printMsg() { // 오버라이드
System.out.println("어노테이션으로 오버라이드");
}
// @Override // 밑에 있는 함수를 @Override 할 경우 오류가남 -> 부모클래스에 없는 함수기때문
// public void printMsg(String str) { // 오버로딩 (매개변수나 타입이 다른 동일이름의 함수)
// System.out.println("Override child");
// }
}
// OverrideParent 클래스
package com.inherit.model.vo;
public class OverrideParent {
public void printMsg() {
System.out.println("Override Parent");
}
}
//실행 test 클래스
public void overrideTest() {
OverrideChild oc = new OverrideChild();
oc.printMsg();
// 부모와 자식 둘다 동일한 함수가 있기 때문에
// 자식클래스에 있는 함수 호출됨!
// 어노테이션으로 오버라이드 호출!
✅ 메소드 오버라이딩 조건
✅ 오버라이딩과 메모리구조
객체 내부에서는 자식과 부모가 구분되어 있기 때문에,
자식 타입에서 부모 타입의 기능을 호출할 때, 부모 입장에서 보면 외부에서 호출한 것과 같다.
즉 부모에서 필드를 private
로 선언하면 자식에서 접근할 수 없다
// 패키지1
public class Parent {
public int publicValue;
protected int protectedValue;
int defaultValue;
private int privateValue;
}
// 패키지2
public class Child extends Parent {
public void call() {
publicValue = 1;
protectedValue = 1; //상속 관계 or 같은 패키지
//defaultValue = 1; //다른 패키지 접근 불가, 컴파일 오류
//privateValue = 1; //접근 불가, 컴파일 오류
}
// main문
public static void main(String[] args) {
Child child = new Child();
child.call(); // 자식과 부모는 다른패키이지므로, public과 protected만 접근가능하다
}
super
-> 부모 객체를 가리키는 참조변수this
-> 자기 자신 객체를 가리키는 참조변수자식, 부모의 필드 이름 or 메서드 이름이 동일할 때
super
를 사용해서 부모 클래스에 있는 기능을 사용할 수 있다
// 부모클래스
public class Parent {
public String value = "parent";
public void hello() {
System.out.println("Parent.hello");
}
}
// 자식클래스
public class Child extends Parent {
public String value = "child";
@Override
public void hello() {
System.out.println("Child.hello");
}
public void call() {
System.out.println("this value = " + this.value); //this 생략 가능, 자식 value 호출
System.out.println("super value = " + super.value); // 부모 value 호출
this.hello(); //this 생략 가능, 자식 hello 메소드 호출
super.hello(); // 부모 hello 메소드 호출
}
}
// main문
public class Super1Main {
public static void main(String[] args) {
Child child = new Child();
child.call();
}
}
자식과 부모의 생성자를 모두 호출해야함
📌 상속 관계를 사용하면 자식의 클래스의 생성자에서 부모클래스의 생성자를 반드시 호출해야함
-> (규칙)
상속받은 자식 객체를 생성하면 자동으로 부모 생성자를 호출하기 때문에
부모클래스에서 매개변수가 있는 생성자를 만들경우 기본 생성자를 직접 만들어야한다!
-> 클래스에서 생성자가 아무것도 없으면 자동으로 (기본생성자)
를 만들어줌
// BasicTest 클래스
package com.bs.model;
public class BasicTest extends Parent{
private int number;
public BasicTest() {
super(); // super()은 생략되있음, 항상 생성자 호출시 부모 생성자부터 호출!!
} // 기본생성자
public BasicTest(int number, String title, double weight) { //(2) 매개변수로 받음
super(title, weight); //(3) 부모 생성자 먼저 호출! 생략되어도 부모기본생성자 호출됨
this.number = number; // (6) bt1 객체의 number 필드 값 대입
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String info() {
return number+" "+getTitle()+" "+getWeight();
// = return this.number+" "+super.getTitle()+" "+super.getWeight();
}
}
// Parent 클래스
package com.bs.model;
public class Parent {
private String title;
private double weight;
public Parent() {}
public Parent(String title, double weight) { //(4) 매개변수로 받음
super(); // (5) Object 클래스 생성자 호출 (기본생성자이므로 아무일도 없음)
this.title = title; // = setTitle(title) = bt1의 title필드 값 대입
this.weight = weight; // = setWeight(weight) = bt1의 weight필드 값 대입
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
}
// 컨트롤러 클래스
package com.bs.controller;
import com.bs.model.BasicTest;
public class Controller {
public void test() {
BasicTest bt = new BasicTest(); // BasicTest 객체 bt 생성
// 자식클래스로 접근했지만 부모클래스를 상속받았기 때문에 setTitle,getTitle 사용가능
bt.setTitle("최주영");
System.out.println(bt.getTitle());
BasicTest bt1 = new BasicTest(1,"김주영",65.5); // (1) 매개변수가 있는 생성자 호출
System.out.println(bt1.getNumber()+" "+bt1.getTitle()+" "
+bt1.getWeight()); // 1 김주영 65.5
System.out.println(bt1.info()); // 1 김주영 65.5
}
}
-> 아래 4가지를 자주 구현하자
equals : 객체간의 동등성 비교를 하기 위해 선언한 메소드
hashCode : 객체의 유일한 값을 정수로 출력(주소)
toString : 생성된 객체의 대표하는 문자를 반환(필드의 값을 반환)
clone : 생성된 객체를 복사해주는 기능
-> 위의 함수들은 기본적으로 Object 클래스 안의 함수들이고 Object 클래스는 최상위 클래스기 때문에
별도의 제한 없이 일반적인 객체에서 호출하며 사용할 수 있다!
-> 클래스를 하나 만들어서 그 클래스 안에서 위 함수들을 재 정의 하여 만들어보자
// equals 메소드 재정의!
@Override
public boolean equals(Object o) {
// 객체의 동등성 비교를 하기위해 설정
// 필드의 값을 가지고 생성된 값이 같은지 비교하기 위해
Student s = (Student)o; // 최상위 클래스 o를 Student로 형변환
//(s1) //(s2)
if(getName().equals(s.getName()) // s1과 s2의 모든 필드들이 같을 경우 true로 반환
&& getAge()==s.getAge()
&& getGender() == s.getGender()
&& getAddress().equals(s.getAddress())
&& getPhone().equals(s.getPhone())
&& joinClass.equals(s.joinClass)) {
return true;
}
return false;
}
Student s1 = new Student("최주영",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
Student s2 = new Student("최주영",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
Student s3 = new Student("정상준",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
System.out.println(s1.equals(s2)); // true 나옴
System.out.println(s2.equals(s3)); // false 나옴
// toString 메소드 재정의
@Override
public String toString() { // 원래 있던 toString 메소드를 재정의해서 만듬
return getName()+" "+getAge()+" "+getGender()+" "+
getPhone()+" "+getAddress()+" "+joinClass+" "+ issue+" "+level;
} // get함수들 앞에 this 가 생략되어 있음
Student s1 = new Student("최주영",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
Student s2 = new Student("최주영",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
Student s3 = new Student("정상준",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
System.out.println(s1.toString());
System.out.println(s2); // 클래스에 toString이 오버라이딩 되어있으면 변수명을 출력했을 때
System.out.println(s3); // 자동으로 toString() 메소드를 호출하여 지정된 정보를 출력
// hashCode 메소드 재정의
@Override
public int hashCode() { // 객체를 지정하는 정수값을 출력해주는 함수
// equals에서 동등비교의 기준이 된 필드를 매개변수로 넣으면 됨
return Objects.hash(getName(), getAge(), getGender(),
getPhone(),getAddress(),joinClass);
}
Student s1 = new Student("최주영",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
Student s2 = new Student("최주영",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
Student s3 = new Student("정상준",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
// clone 메소드 재정의
@Override
public Student clone() { //clone : 객체복사 메소드
// 깊은복사를 구현
return new Student(getName(),getAge(),getGender(),getPhone(),getAddress(),
joinClass,issue,level);
}
Student s1 = new Student("최주영",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
Student s2 = new Student("최주영",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
Student s3 = new Student("정상준",26,'남',"010111","경기도 안양시","공공 데이터"
,"선생을 갈굼",'남');
Student copyStudent = s1.clone();
System.out.println(copyStudent); // 최주영 26 남 010111 경기도 안양시 공공 데이터 선생을 갈굼 남
copyStudent.setAge(50);
System.out.println(copyStudent); // 최주영 50 남 010111 경기도 안양시 공공 데이터 선생을 갈굼 남
final
클래스에 final 붙이면 그 클래스는 다른 클래스에게 상속주는것이 불가능
메소드에 final 붙이면 자식 클래스에서 동일한 메소드의 오버라이딩이 불가능함