클래스 상속
- 형태 : class 자식클래스 extends 부모클래스 { 필드, 생성자, 메소드 }
- 단 하나의 부모클래스만 올 수 있다.
- 부모클래스에서 private 접근 제한을 갖는 필드와 메소드는 상속 불가
- 다른 패키지인 경우, 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() {
this.channel = channel;
System.out.println("채녈 " + channel + "번으로 바꿉니다.");
}
void turnOffDmb() {
System.out.println("DMB 방송 수신을 멈춥니다.");
}
}
public class DmbCellPhoneExample {
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();
dmbCellPhone.turnOffDmb();
}
}
모델 : 자바폰
색상 : 검정
채널 : 10
전원을 켭니다.
벨이 울립니다.
자기: 여보세요.
상대: 안녕하세요 저는 홍길동인데요
자기: 아,네
전화를 끊습니다.
채녈 10번 DMB 방송 수신을 시작합니다.
채녈 10번으로 바꿉니다.
DMB 방송 수신을 멈춥니다.
부모 생성자 호출
<암시적 생성자>
- 위 예제를 보면 자식 객체인 DmbCellPhone 객체만 생성한 것 처럼 보이지만 사실은 내부에서 부모인 CellPhone 객체가 먼저 생성되었다.
- 그렇다면 부모 객체가 생성되기 전에 부모 생성자 호출을 먼저 했다는 것인데.. 그것은 바로 자식생성자의 맨 첫줄에서 호출된다.
- 컴파일러가 자동으로 super(); 를 써주어 부모의 기본 생성자를 호출한다.
- 부모의 생성자가 선언이 되어야 호출도 가능한 것인데 우리가 부모의 생성자를 선언하지 않고 호출했는데도 에러가 난 이유는 컴파일러가 부모의 기본 생성자를 만들었기 때문이다.
<명시적 생성자>
- super(매개값, ...);을 통해 일치하는 타입의 부모 생성자를 찾아서 호출한다.
- 부모 클래스에 기본 생성자 대신 매개변수가 있는 생성자를 만든 경우에는 반드시 super에다가도 매개값을 넣어주어야한다.
- 또한 반드시 위치는 자식 생성자 첫 줄에 위치해야한다.
부모 생성자 호출 예제
public class People {
//필드
public String name;
public String ssn;
//생성자
public People(String name, String ssn) { //기본 생성자가 아닌 명시적 생성자만 있음
this.name = name; //super로 생성자 호출해야함
this.ssn = ssn;
}
}
public class Student extends People{
public int studentNo;
public Student(String name, String ssn, int studentNo) {
super(name, ssn); // 부모 생성자 호출하기위해 매개값으로 넘김, 이렇게 자식 생성자의 맨 첫 줄에 작성
// this.name = name;
// this.ssn = ssn; -> 이렇게 두 줄 처럼 쓰게 되면 에러 발생
this.studentNo = studentNo ; //
}
}
public class StudentExample {
public static void main(String[] args) {
Student student = new Student("홍길동", "123456-1234567", 1);
System.out.println("name: " + student.name); // name: 홍길동
System.out.println("ssn: " + student.ssn); // ssn: 123456-1234567
System.out.println("studentNo: " + student.studentNo); // studentNo: 1
}
}
메소드 재정의 (오버라이딩)
- 부모의 메소드를 상속받아 사용하려고 할 때 자식 클래스가 사용하기에 부적합할 수가 있다.
- 자식 클래스가 메소드를 알맞게 사용하고자 할때 상속된 메소드를 수정할 수가 있는데
- 이런 경우에 제공되는 기능이 바로 메소드 오버라이딩이다.
- 메소드가 오버라이딩 되면 원래 있던 부모 객체의 메소드는 숨겨지게된다.
- 따라서 자식 객체에서 해당 메소드를 호출하면 자동으로 오버라이딩된 메소드가 호출된다.
- 규칙1 : 부모와 동일한 시그니처(리턴타입, 메소드이름, 매개변수들)을 가져야한다
- 규칙2 : 접근 제한을 더 강하게 재정의할 수는 없다. (부모가 default인데 자식에서 private을 사용할 수 없음)
- 규칙3 : 새로운 예외(Exception)를 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; // Math = 자바 표준 API
}
// @Override
double areaCircl(double r) { // @Override를 쓰면 오타를 찾아서 컴파일에러를 낸다.
// @Override를 안쓰면 그냥 새로운 메소드로 받아들임
System.out.println("Computer 객체의 areaCircle() 실행");
return Math.PI * r * r;
}
}
public class ComputerExample { // 자식 클래스 실행
public static void main(String[] args) {
int r = 10;
Calculator calculator = new Calculator(); // 덜 정확한 넓이 계산 메소드
System.out.println("원면적: " + calculator.areaCircle(r) + "\n");
Computer computer = new Computer(); // 정확한 넓이 계산 메소드
System.out.println("원면적: " + computer.areaCircle(r));
}
}
Calculator 객체의 areaCircle() 실행
원면적: 314.159
Computer 객체의 areaCircle() 실행
원면적: 314.1592653589793
이클립스에서 재정의 메소드 자동 생성
재정의 메소드 작성할 위치 커서 > [Source] > [Override/Implement Methods] > 부모클래스에서 재정의될 메소드 선택
재정의되지 않은 원래의 부모 메소드 호출하는 법
- 방법 : super.부모메소드(); 로 호출한다.
- 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) { // 값이 SUPERSONIC일 경우에는 초음속비행
System.out.println("초음속비행합니다.");
} else { // 그렇지 않은 경우에는 부모 클래스의 fly() 메소드를 호출
super.fly();
}
}
}
public class SupersonicAirplaneExample {
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이 붙으면 최종적인 클래스임을 의미하며 상속할 수 없는 (부모가 될 수 없는) 클래스이다.
- 대표적인 final 클래스로는 String 클래스가 있다.
- String 클래스 선언 형태
public final class String {}
- 아래와 같이 String 클래스를 부모 클래스로 지정할 수 없다.
public class newString extends String {} // 불가능
- final 클래스 - Member 클래스 예제
public final class Member {} // final 키워드가 나오면
public class VeryImportantPerson extends Member {} // 상속할 수 없다.
- 이렇게 사용 불가능하다.
final 메소드 - 재정의 불가능
- 메소드 선언시 final을 붙이면 이 메소드는 최종 메소드가 되서 더이상 수정이 불가능하다.
- 이 말은 재정의를 할 수 없는 메소드가 된다는 말이다.
- 자식클래스가 부모클래스를 상속하여 부모클래스의 final 메소드를 재정의하려고 할 때 오류가 난다.
- final 메소드 - Car 클래스 예제
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;}
@Override
public void stop() { // stop() 메소드는 final로 선언되었기 때문에 메소드 재정의가 불가능하다.
System.out.println("스포츠카를 멈춤");
speed = 0;
}
}
- final 메소드 재정의를 시도했을 때 발생되는 에러 내용
protected 접근 제한자
- protected는 필드와 생성자, 메소드 선언에서 사용이 가능하다.
- 같은 패키지에 있는 클래스는 접근 제한이 업지만 다른 패키지에 있는 클래스는 자식 클래스만 접근이 가능하다.
- 같은 패키지에 있는 클래스, 다른 패키지에 있는 클래스, 다른 패키지에 있지만 상속을 받은 클래스 이 세가지 경우에 대해 알아보자
0. 우선 pack1패키지에 A 클래스를 하나 만든다.
package Exercise.ch07.sec1.pack1;
public class A {
protected String field; // protected 필드 선언
protected A() {} // protected 생성자 선언
protected void methode() {} // protected 메소드 선언
}
1. A 클래스와 같은 pack1 패키지에 있는 클래스 B 생성
package Exercise.ch07.sec1.pack1;
public class B {
public static void main(String[] args) {
A a = new A(); // 생성자에 접근 가능 (객체 생성)
a.field = "value"; // 필드에 접근가능 (필드 값 변경)
a.methode(); // 메소드 실행 가능
}
}
2. 다른 패키지 pack2에 있는 C 클래스 생성
package Exercise.ch07.sec1.pack2;
public class C {
public static void main(String[] args) {
A a = new A(); // 다른 패키지에 있어서 접근 불가능
a.field = "value"; // 다른 패키지에 있어서 접근 불가능
a.method(); // 다른 패키지에 있어서 접근 불가능
}
}
3. 다른 패키지 pack2에 있지만 A 클래스로부터 상속을 받은 D 클래스 생성
package Exercise.ch07.sec1.pack2;
import Exercise.ch07.sec1.pack1.A;
public class D extends A{
// 자식 클래스 생성자
public D() {
// A a = new A(); // new 연산자를 통해 생성자 직접 호출은 불가능하다
super(); // supe()로 A생성자 호출 가능
this.field = "value"; // 다른 패키지지만 D가 A의 자식이므로 필드에 접근 가능
this.methode(); // 다른 패키지지만 D가 A의 자식이므로 메소드에 접근 가능
}
}
'👨💻 2. 웹개발_Back end > 2-1 Java' 카테고리의 다른 글
[JAVA] 07-3 추상 클래스 (0) | 2021.08.06 |
---|---|
[JAVA] 07-2 타입 변환과 다형성 (0) | 2021.08.05 |
[JAVA] 06-6 패키지와 접근 제한자 (0) | 2021.08.04 |
[JAVA] 06-5 인스턴스 멤버와 정적 멤버 (0) | 2021.08.03 |
[JAVA] 06-4 메소드 (0) | 2021.08.02 |
댓글