본문 바로가기
Java/정리

[Java] 상속(Inheritance)

by 콧등치기국수 2021. 5. 19.

1. 상속이란?

부모 클래스(상위클래스)의 변수와 메소드를 자식 클래스(하위클래스)가 물려받아 사용할 수 있게 해준다.

여기서 부모클래스를 superclass, 자식클래스를 subclass라 부른다.

 

 

1) 표현 : 하위클래스명 + [ extends 상위클래스명 ]

[접근제어자][기타제어자] class 클래스명 extends 상위 클래스명
//Desktop -> 자식 클래스, Product -> 부모 클래스

public class Desktop extends Product{
	
}

 

2) 장점

- 공통적인 코드를 부모클래스로 만들어두어 관리하는 것은 코드의 추가, 변경에 용이하다.

- 코드의 중복을 제거해서 프로그램의 생산성과 유지보수에 크게 기여할 수 있다.

 

3) 특징

- 자바에서는 클래스간 단일상속만 가능하다. (다중상속X)

- 부모클래스의 생성자와 초기화블록은 상속이 불가능하다.

- 부모의 private멤버(필드, 메소드)는 상속은 되지만 직접 접근은 불가능하다.

- 단, 부모클래스의 필드, 메소드를 protected로 하게 되면 후손클래스가 직접 접근이 가능하다.

- 모든 클래스는 object 클래스의 후손

 object 클래스가 제공하는 메소드를 오버라이딩하여 메소드를 사용할 수 있다.

public Desktop(String brand, String pCode, String pName, int price, boolean allInone) {
	super(brand, pCode, pName, price);  //super()생성자에서 부모생성자를 호출하여 사용, 생성자로 접근은 반드시 첫줄
		
	//super.pCode = pCode;  직접접근일때는 부모필드의 접속 제어자를 protected여야한다.(접근하고자 하는 필드가 private일때는 접근불가)
	//super. 외 super는 해당객체의 부모주소를 담고있다. 따라서 super.으로 접근가능
		
        this.allInone = allInone;
}

 

4) 부모클래스로 직접 접근

부모클래스의 필드, 메서드가 protected인 경우, super.으로 자식클래스에서 부모클래스로 직접 접근이 가능하다.

- super.부모클래스 필드,메서드 

- super -> 해당객체의 부모주소를 담고있다.

 

 

5) super  vs  this

- super : 상위클래스를 가리키는 레퍼런스 변수, 은닉변수를 불러온다.

- this : 클래스 인스턴스 나 자신(객체)을 나타내는 레퍼런스 변수

 

 

6) superclass(부모클래스)를 통한 자식클래스의 생성자 생성

- 부모클래스 : Vehicle

public class Vehicle {

	private String name;
	private double mileage;
	private String kind;
	
	public Vehicle() {
		// TODO Auto-generated constructor stub
	}

	public Vehicle(String name, double mileage, String kind) {
		super();
		this.name = name;
		this.mileage = mileage;
		this.kind = kind;
	}

- 자식클래스 : Airplane

public class Airplane extends Vehicle {
	
	private int tire;
	private int wing;
	
	public Airplane() {
		// TODO Auto-generated constructor stub
	}

	public Airplane(String name, double mileage, String kind, int tire, int wing) {
		super(name, mileage, kind);
		
		this.tire = tire;
		this.wing = wing;		
		
	}

public Airplane(String name, double mileage, String kind, int tire, int wing) {
      super(name, mileage, kind);   //부모클래스 생성자
      this.tire = tire;                     //this를 이용한 자식클래스 변수 초기화
      this.wing = wing;
}


2. 오버라이딩

상속받은 부모클래스의 메소드를 자식클래스에서 재정의해서 사용하는 것

부모클래스가 제공하는 메소드를 자식이 일부 고쳐서 사용하겠다는 의미

[ alt + shift + s ]  → Override/ Implements methods 클릭해서 사용

 

 

1) 특징

- 실행하고자 하는 메소드가 자식클래스에 없으면 -> 부모메소드 실행

- 자식클래스에 메소드가 오버라이딩되어있는 경우 자식메소드가 우선적으로 실행

//1. 부모클래스(클래스명: Vehicle)
public void howToMove() {
	System.out.println("움직인다.");
}

//2. 자식클래스(클래스명: Airplane)
@Override
public void howToMove() {
	System.out.println("날개를 가지고 날아다닌다.");
}

//3. 실행파일
Airplane a = new Airplane("비행기",0.021,"제트기",16,5);  //객체생성
c.howToMove();    //오버라이딩

오버라이딩 결과 -> 부모(Vehicle)가 아닌 자식(Airplane)클래스의 howToMove()메소드가 실행됨

날개를 가지고 날아다닌다.

 

2) 오버라이딩 성립조건

- 부모클래스의 메소드와 메소드명 동일해야 함

- 메소드의 매개변수 갯수, 자료형, 순서 -> 모두 동일

- 반환형 동일

- 부모메소드의 접근제한자보다 범위가 같거나 커야 한다.


 

3. 오버라이딩 활용 (Object 클래스 메소드 오버라이딩)

모든 클래스들은  Object클래스의 자식클래스이므로, Object클래스의 메소드를 오버라이딩하여 사용가능하다!

 

3-1. toString( ) 오버라이딩

자바에서 최상위 부모클래스는 Object이다. 

따라서 toString메소드를 재정의하지 않고 사용한다면, Object의 toString메소드가 실행된다. 

 

Book클래스의 객체로 bk1, bk2를 생성하였다.

객체인 bk1, bk2이 가지고 있는 필드값들을 출력하기 위해 오버라이딩을 하려고 한다.

먼저 오버라이딩 하기 전에는 toString()으로는 어떻게 출력되는지 알아보자.

//run클래스(실행파일)

public static void main(String[] args) {

		Book bk1 = new Book("언어의 온도","이기주",20000);
		Book bk2 = new Book("나무","베르나르베르베르",20000);
		
		System.out.println(bk1.toString()); //출력문에 어떤 레퍼런스를 출력하고자 할때 JVM이 자동으로 해당 레퍼런스.toString()호출해준다.
		System.out.println(bk1);            //toString명시하지 않아도 자동으로 호출됨
//1. toString()오버라이딩 하기 전

com.kh.chap03_override.model.vo.Book@3533949f
com.kh.chap03_override.model.vo.Book@3533949f

주소값이 출력된 것을 확인할 수 있다. 

 

 

그러면 이제 Book클래스로 돌아가서 toString()을 오버라이딩 해주고 run파일을 실행시켜보자.

public class Book {

	@Override
	public String toString() {
		return "toString() --> title : " +title+" author : "+author+"price : "+price;
	}
//1. toString()오버라이딩 후

toString() --> title : 언어의 온도 author : 이기주price : 20000
toString() --> title : 언어의 온도 author : 이기주price : 20000

즉 정리하자면 아래와 같다.

 

- 오버라이딩 전: Object클래스의 toString()이 실행 --> 풀패키지명 @ 해시코드 16진수 값이 리턴
- 오버라이딩 후 : Book클래스에 있는 toString()이 실행 --> 내가 재정의한대로 해당객체가 가지고 있는 필드값에 대한 정보를 리턴.

 

 

 

3-2. equals( ) 오버라이딩

 

Book클래스를 참조하는 bk1, bk3 객체를 생성해서 equals()메소드로 두 객체를 비교해보자.

이때 bk1과 bk3는 같은 필드값을 가진다!

public class Run {

	Book bk1 = new Book("언어의 온도","이기주",20000);
	Book bk3 = new Book("언어의 온도","이기주",20000);  //bk1과 동일한 필드값을 가진 bk3객체 생성
		
	System.out.println("bk1과 bk3가 같은 책입니까? " + (bk1==bk3));         //false --> 주소값 비교이기 때문에
	System.out.println("bk1과 bk3가 같은 책입니까? " + (bk1.equals(bk3)));  //false --> 주소값 비교이기 때문에
//2. equals() 오버라이딩 전

bk1과 bk3가 같은 책입니까? false
bk1과 bk3가 같은 책입니까? false

분명히 같은 값을 가진 두 객체를 equals()메소드로 비교했는데 false가 나왔다!

문자열(String)을 비교할 때는 값이 같으면 true로 반환을 해주었던 것이 기억이 나며 혼란스러울 수 있다.

 

Object클래스 equals 메서드는 주소값이 다른 객체는 서로 다른 객체로 판단한다.

우리가 String()에서 equals메서드로 주소가 아닌 값이 같으면 true로 반환받았던 것은 오버라이딩된 결과였던 것이다.

 

 

그렇다면 주소값은 달라도 값이 같다면 true를 반환해주도록 하기위해 Book클래스로 돌아가서 오버라이딩을 해보자

public class Book {

    private String title;
    private String author;
    private int price;

    @Override
    public boolean equals(Object obj) {
		
		if( !(obj instanceof Book)) {  //obj가 Book으로 변환가능?
			return false;
		}
		
		Book other = (Book)obj; //obj타입을 Book타입으로 강제 형변환, 상속구조에서는 클래스로 형변환이 가능하다.
		
		if(this.title.equals(other.title) && this.author.equals(other.author) && this.price==other.price) {
			return true;
		} else {
			return false;
		}		
	}
    
}

equals메소드는 비교하려는 객체와 Object클래스의 obj객체와 비교하는데,

우리는 Book클래스의 객체와 비교하려고 하니 아래와 같이 

-> obj객체의 참조변수자료형인 Object가 Book으로 형변환이 되는지 

-> 만약 변환이 된다면 필드값이 모두 같은지 확인해야 한다.

 

1) 원래 Object클래스의 obj객체와 비교

public boolean equals( Object obj ) {

}

 

2) obj가 Book클래스로 변환 가능한가? -> 변환이 안된다면 다른 객체이므로 false반환

if( ! (obj instanceof Book) ) {  
    return false;
}

 

3) Book클래스로 변환가능하면 Book클래스로 강제 형변환(Book이 하위클래스이므로 자동형변환X)

상속구조에서는 클래스로 형변환이 가능하다.

Book으로 타입을 변경한 객체를 other변수로 받는다.

Book other = ( Book ) obj ;

 

4) true -> 실제 값들이 같은경우(주소값 비교X)  /  false -> 실제 값들 중 하나라도 다르면 false

if( this.title.equals(other.title) && this.author.equals(other.author) && this.price==other.price) {
      return true;
} else {
      return false;
}

//2. equals() 오버라이딩 후
bk1과 bk3가 같은 책입니까? false
bk1과 bk3가 같은 책입니까? true

 

 

 

3-3. hashCode( ) 오버라이딩

 

문자열(String)에서 hashCode()메소드를 사용해보면

주소는 달라도 값이 같은 문자열이라면 hashCode( )결과로 모두 같은 값을 반환해준다.

 

이 예제도 주소는 달라도 값이 같은 객체라면 hashCode( ) 결과는 모두 같도록 오버라이딩해주자.

public class Book {

	@Override
	public int hashCode() {
		return Objects.hash(title, author, price);
	}
}

1) Objects.hash( ) -> 주어진 값들을 이용해서 해시코드를 생성한다.

따라서 동일한 필드값을 가지는 객체는 동일한 해시코드를 가질 수 있다. 

 

public class Run {
	public static void main(String[] args) {
    
    	Book bk1 = new Book("언어의 온도","이기주",20000);
		Book bk2 = new Book("나무","베르나르베르베르",20000);
        Book bk3 = new Book("언어의 온도","이기주",20000);
    
		System.out.println("bk1의 hash코드 " + bk1.hashCode());
		System.out.println("bk1의 hash코드 " + bk2.hashCode());
		System.out.println("bk1의 hash코드 " + bk3.hashCode());
	}
}

결과 :

//3. hashCode() 오버라이딩 전
bk1의 hash코드 942731712
bk1의 hash코드 971848845
bk1의 hash코드 1910163204
//3. hashCode() 오버라이딩 후
bk1의 hash코드 892572831
bk1의 hash코드 -336869437
bk1의 hash코드 892572831

 

즉, 

- 오버라이딩 전: Object클래스의 hashCode()이 실행 --> 해당 객체의 실제 주소값 10진수로 계산한 결과 리턴
- 오버라이딩 후 : Book클래스에 있는 toString()이 실행 --> 두 객체의 실제 멤버 값들이 같은 경우 같은 해시코드 값 (두 객체의 실제 주소값 비교X)

 

 

 

 

출처 : kh정보교육원 교육자료

'Java > 정리' 카테고리의 다른 글

[Java] 예외처리_try with resource  (0) 2021.05.25
[Java] 예외처리_BufferedReader사용  (0) 2021.05.25
[Java] 문자열 compareTo()_문자열비교메소드  (0) 2021.05.17
[Java] 객체  (0) 2021.05.14
[Java] 2차원 배열 문제  (0) 2021.05.12