자바프로그래밍 상속

최주영·2023년 3월 17일
0

자바

목록 보기
7/30

상속

  • 다른 클래스가 가지고 있는 멤버(필드, 메소드)들을 새로 작성할 클래스에서 직접 만들지 않고 상속을 받음으로써 새 클래스가 자신의 멤버처럼 사용할 수 있는 기능
[접근제한자] 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 - 부모참조

  • 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();
     }
}

✅ super - 생성자

  • 상속관계의 인스턴스를 생성하면 메모리 내부에는 자식, 부모 클래스 둘다 만들어짐
    -> 자식과 부모의 생성자를 모두 호출해야함

📌 상속 관계를 사용하면 자식의 클래스의 생성자에서 부모클래스의 생성자를 반드시 호출해야함 -> (규칙)

상속받은 자식 객체를 생성하면 자동으로 부모 생성자를 호출하기 때문에
부모클래스에서 매개변수가 있는 생성자를 만들경우 기본 생성자를 직접 만들어야한다!
-> 클래스에서 생성자가 아무것도 없으면 자동으로 (기본생성자) 를 만들어줌

// 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
	}
}

✅ Object 클래스의 메소드를 재정의해서 사용해보기

-> 아래 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 붙이면 자식 클래스에서 동일한 메소드의 오버라이딩이 불가능함

profile
우측 상단 햇님모양 클릭하셔서 무조건 야간모드로 봐주세요!!

0개의 댓글