새소식

JAVA

자바 기초 : 상속

  • -

핵심개념

  • 상속 : 객체지향프로그래밍에서 부모클래스의 멤버를 자식클래스에게 물려주는 것. 중복되는 코드를 줄여주며 유지 보수시간을 최소화한다는 장점이 있다.

상속

 

  • 클래스 상속 : 자식클래스 선언 시 어떤 부모 클래스를 상속받을 지 선택한뒤 extends 뒤에 부모클래스를 기술한다.
    ex) class 자식클래스 extends 부모클래스 {
             //필드
             //생성자
             //메소드
          }

  • 상속의 특징
    1. 한개의 부모클래스만 상속받을 수 있다.
    2. 부모클래스에서 private접근 제한을 갖는 필드와 메소드는 상속대상에서 제외된다.
    3. 부모와 자식 클래스가 다르 패키지에 존재할 경우 default접근 제한을 갖는 필드와 메소드 역시 상속대상에서 제외된다.
public class CellPhone {
   //필드
   String model;
   String color;
   //생성자
   //메소드
   void powerOn() {System.out.println("전원을 켭니다.");}
   void powerOff() {System.out.println("전원을 끕니다.");}
   void bell() {System.out.println("벨이 울립니다.");}
   void sendVoice(String message) {System.out.println("본인 : " + message);}
   void receiveVoice(String message) {System.out.println("상대방 : " + message);}
   void hangUp() {System.out.println("전화를 끊습니다.");}
}

------------------------------------------------------------------------------------------------------------------

public class DmbCellPhone extends CellPhone {
   //필드
   int channel;
   //생성자
   DmbCellPhone(String model, String color, int channel){
      this.model = model;
      this.color = color;
      this.channel = channel;
   }
   // 메소드
   void turnOnDmb() {
      System.out.println("채널 : " + channel + "번 DMB 방송 수신을 시작합니다."); }
   void changeChannelDmb(int channel) {
      this.channel = channel;
      System.out.println("채널 : " + channel + "번으로 바꿉니다."); }
   void turnOffDmb() {
      System.out.println("DMB 방송 수신을 멈춥니다.");
     }
   }

------------------------------------------------------------------------------------------------------------------

public class DmbCellPhoneEx {
   public static void main(String[] args) {
   //DmbCellPhone 객체 생성
   DmbCellPhone dmbCellPhone = new DmbCellPhone("자바폰", "화이트", 10);

   //CellPhone 클래스로부터 상속받은 필드
   System.out.println("모델 : " + dmbCellPhone.model);
   System.out.println("색상 : " + dmbCellPhone.color);

   //DmbCellPhone 클래스의 필드
   System.out.println("채널 : " + dmbCellPhone.channel);

   //CellPhone 클래스로부터 상속받은 메소드 호출
   dmbCellPhone.powerOn();
   dmbCellPhone.bell();
   dmbCellPhone.sendVoice("여보세요");
   dmbCellPhone.receiveVoice("안녕하세요. 저는 김땡땡입니다.");
   dmbCellPhone.sendVoice("전화 잘못걸었습니다.");
   dmbCellPhone.hangUp();

   //DmbCellPhone 클래스의 메소드 호출
   dmbCellPhone.turnOnDmb();
   dmbCellPhone.changeChannelDmb(10);
   dmbCellPhone.turnOffDmb();
   }
}
실행결과 : 
모델 : 자바폰
색상 : 화이트
채널 : 10
전원을 켭니다.
벨이 울립니다.
본인 : 여보세요
상대방 : 안녕하세요. 저는 김땡땡입니다.
본인 : 전화 잘못걸었습니다.
전화를 끊습니다.
채널 : 10번 DMB 방송 수신을 시작합니다.
채널 : 10번으로 바꿉니다.
DMB 방송 수신을 멈춥니다.

 

  • 부모 생성자 호출 : 부모객체가 먼저 생성되고 그 다음 자식 객체가 생성된다. 모든 객체는 클래스의 생성자를 호출해야만 생성된다. 부모생성자도 마찬가지인데, 부모 생성자는 자식 생성자의 맨 첫 줄에서 호출된다.
    ex)명시적으로 부모 생성자를 호출하지 않은 경우
    DmbCellPhone dmbCellPhone = new DmbCellPhone();
    -----------------------------------
    public DmbCellPhone(){
       super();   //부모의 기본 생성자를 호출. 직접 코딩하지 않아도 컴파일러가 자동으로 넣어준다.
    ------------------------------------
    public CellPhone() { //기본생성자
    }
    //호출은 자식생성자가 먼저 되었지만, 종료는 부모생성자가 먼저 끝나고 자식 생성자가 종료되므로 부모객체가 먼저 생성되고 자식 객체가 생성된다고 볼 수 있다.

    ex)명시적으로 부모 생성자를 호출하려는 경우
    자식클래스(매개변수선언,...){
    super(매개값,...); //매개값의 타입과 일치하는 부모 생상자를 호출. 없다면 컴파일 에러발생
    }
public class People {
   public String name;
   public String ssn;

   //생성자(name과 ssn을 매개값으로 받아 객체 생성
   public People(String name, String ssn) {
   this.name = name;
   this.ssn = ssn;
   }
}

---------------------------------------------------------------------------

public class Student extends People {
   //기본생성자
   //public Student() { super(); }
   public int studentNo;

   //자식클래스 생성 시 부모 클래시의 매개변수를 받아야함
   //super()로 People 클래스의 생성자 호출
   public Student(String name, String ssn, int studentNo) {
      super(name, ssn);
      this.studentNo = studentNo;
   }
}

---------------------------------------------------------------------------

public class StudentEx {
   public static void main(String[] args) {
   Student student = new Student("김땡떙", "121212-9879879", 1);
      System.out.println("이름 : " + student.name);
     System.out.println("주민번호 : " + student.ssn);
      System.out.println("학생번호 : " + student.studentNo);
   }
}
실행결과 : 
이름 : 김땡떙
주민번호 : 121212-9879879
학생번호 : 1

 

  • 메소드 재정의 : 자식클래스에서 부모 클래스의 메소드를 재정의 하는 것. 부모클래스의 메소드가 자식클래스에서 사용하기에 부적합 한 경우 상속된 일부 메소드를 자식 클래스에서 다시 수정하여 사용한다. 메소드가 재정의될 경우 부모 객체 메소드가 숨겨지며, 자식 객체에서 메소드를 호춣면 재정의된 자식 메소드가 호출된다.

  • 메소드 재정의 방법 : 메소드를 재정의할 때 다음과 같은 규칙에 주의해서 작성.
    1. 부모 메소드와 동일한 시그니처(리턴타입, 메소드 이름, 매개변수목록)을 가져야한다.
    2. 접근 제한을 더 강하게(좁게) 재정의할 수 없다.
        ex)부모가 public일 경우 자식이 default 또는 private로 할 수 없다.
    3. 새로운 예외를 throw할 수 없다.
public class Calculator {
   double areaCircle(double r) {
      System.out.println("Calculator 객체의 areaCircle() 실행");
      return 3.14159*r*r;
   }
}

-----------------------------------------------------------------------------

public class Computer extends Calculator {

@Override//생략해도되지만, 붙여주면 재정의 여부를 컴파일러가 확인하기 때문에 실수를 줄인다.
   double areaCircle(double r) {
      System.out.println("Computer객체의 areaCircle() 실행");
      return Math.PI*r*r;
      //Calculator에서는 파이를 3.14159로 계산하엿지만, 좀 더 정밀한 처리를 위해 Math.PI 상수로 재정의
      //Math는 자바 표준 API이다.
   }
}

-----------------------------------------------------------------------------

public class ComputerEx {
   public static void main(String[] args) {
   int r = 10;
   Calculator calculator = new Calculator();
   System.out.println("원면적 : " + calculator.areaCircle(r));
   System.out.println();

   Computer computer = new Computer();
   System.out.println("원면적 : " + computer.areaCircle(r));
   }
}
실행결과 : 
Calculator 객체의 areaCircle() 실행
원면적 : 314.159

Computer객체의 areaCircle() 실행
원면적 : 314.1592653589793

 

  • 부모 메소드 호출 : 자식 클래스 내부에서 재정의된 부모 클래스의 메소드를 호출해야하는 경우 명시적으로 super 키워드를 붙여 부모 메소드를 호출 할 수 있다.
    ex) super.부모메소드();
public class Airplane {
   public void land() {
      System.out.println("착륙합니다.");
   }
   public void fly() {
      System.out.println("일반 비행합니다.");
   }
   public void takeOff() {
      System.out.println("이륙합니다.");
   }
}

------------------------------------------------------------

public class SupersonicAirplane extends Airplane {
   public static final int NORMAL = 1;
   public static final int SUPERSONIC = 2;
   public int flyMode = NORMAL;

   @Override
   public void fly() {
      if(flyMode == SUPERSONIC) {
         System.out.println("초음속 비행합니다.");
      }else {
         //Airplane 객체의 fly 메소드 호출
         super.fly();
      }
   }
}

------------------------------------------------------------

public class SupersonicAirplaneEx {
   public static void main(String[] args) {

   SupersonicAirplane sa = new SupersonicAirplane();

   sa.takeOff();

   sa.fly();

   sa.flyMode = SupersonicAirplane.SUPERSONIC;
   sa.fly();

   sa.flyMode = SupersonicAirplane.NORMAL;
   sa.fly();

   sa.land();
   }
}
실행결과 :
이륙합니다.
일반 비행합니다.
초음속 비행합니다.
일반 비행합니다.
착륙합니다.

 

  • final클래스와 final메소드 : final키워드는 해당 선언이 최종상태이며 수정될 수 없음을 의미한다. 필드를 선언할 때 final을 작성하면 초기값 설정 후 더이상 값을 변경할 수 없다는 것을 의미한다. 반면 클래스 및 메소드 선언 시 final키워드를 사용하면 상속과 관련된 의미를 지닌다. 클래스에서의 final은 상속할 수 없다는 의미를 메소드에서의 final은 재정의 할 수 없다는 의미를 가진다.

  • 상속할 수 없는 final 클래스 : 최종적인 클래스이므로 상속할 수 없다. 즉, 부모클래스가 될 수 없어 자식클래스를 만들 수 없다는 것이다.
    ex) public final class String {...}
    --------------------------------------------
          public final class NewString extends String {...} (x)

  • 재정의할 수 없는 final 메소드 : 최종적인 메소드이므로 재정의할 수 없다. 즉, 부모클래스에서 선언된 final메소드는 자식클래스에서 재정의 할 수 없다는 것이다.
public class Car {
   //필드
   public int speed;

   //메소드
   public void speedUp() {
   speed +=1;
   }
   //final 메소드
   public final void stop() {
   System.out.println("차를 멈춤");
   speed = 0;
   }
}

-------------------------------------------------

public class SportsCar extends Car {
   @Override
   public void speedUp() {
      speed += 10;
   }

   /*재정의할 수 없다.
   public void stop() {
      System.out.println("스포츠카를 멈춤");
      speed = 0;
   }*/
}

 

  • 상속에서의 protected 접근제한자 : 같은 패키지에서는 default와 같이 접근제한이 없지만, 다른 패키지에서는 자식 클래스만 접근이 가능하다.
package ch07.sec01.ex07.pack1;
public class A {
   //protected : 같은 패키지 또는 자식클래스에서 사용가능
   protected String field; //필드
   protected A() { //생성자
   }
   protected void method() { //메소드
   }
}

-----------------------------------------------------------------------

package ch07.sec01.ex07.pack1;
//A와 같은 패키지
public class B {
   public void method() {
      A a = new A();
      a.field = "Value";
      a.method();
   }
}

-----------------------------------------------------------------------

package ch07.sec01.ex07.pack2;
import ch07.sec01.ex07.pack1.A;
//A와 다른패키지이므로 자식클래스만 사용가능

public class C extends A{
   public C() {
      super();
      this.field = "Value";
      this.method();
   }
}
-----------------------------------------------------------------------

package ch07.sec01.ex07.pack2;
//다른패키지, 자식패키지x이므로 사용 불가

public class D {
   public void method() {
      /*A a = new A();
      a.field = "Value";
      a.method();*/
   }
}

 

 

*Tip : Override할 메소드를 입력 후 ctrl + space를 하면 쉽게 메소드 재정의 가능

 

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.