Day26 Java프로그래밍 기초(상속)
- 상속
1.1 상속의 개념
부모클래스를 상속받은 자식클래스에서 부모클래스의 필드,메소드를 활용 하는 방법을 말한다.
부모클래스의 멤버를 자식클래스에 복사하는것이 아니라, 부모클래스의 멤버를 사용하는 것일 뿐이다.
직접적으로 바이트 코드를 복사하지 않는다.
1.2 클래스 상속
클래스 상속 extends를 사용하여 자식클래스가 부모클래스로 부터 상속 받았다는것을 표기한다.
public class Parent{ 필드 메소드() }
public class Child extends Parent{ 필드 메소드() }
1.3 부모 생성자 호출
클래스 상속을 받은 자식클래스를 인스턴스 하면 부모클래스의 생성자가 자동적(super())으로 호출된다.
public class SmartPhone extends Phone { //자식 생성자 선언 public SmartPhone(String model, String color) { super(); this.model = model; this.color = color; System.out.println(“SmartPhone(String model, String color) 생성자 실행됨”); } }
부모클래스의 생성자가 매개변수를 가지고 있다면, 자식클래스도 super(매개변수)의 구조로 사용해야한다.
1.4 메서드 오버라이딩
- 자식클래스에서 부모클래스의 메서드를 재정의 하는 방법이다.
- 부모 메서드의 선언부(리턴 타입, 메서드명, 매개변수)는 동일해야한다.
- 접근제한을 더 강하게 오버라이딩 할수 없다.
- 오버라이딩시 어노테이션(@override)를 사용하면 컴파일 오류를 막을 수 있다.
public class Computer extends Calculator { //메소드 오버라이딩 @Override public double areaCircle(double r) { System.out.println(“Computer 객체의 arearCircle() 실행”); return Math.PI * r * r; } }
1.5 부모메서드 호출
자식클래스에서 부모클래스의 메서드를 호출 시에는 super.부모메서드명()을 사용한다.
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 { //Airpanle 객체의 fly() 메소드 호출 super.fly(); } }}
1.6 타입변환
- 자동 타입변환 : 부모클래스 타입의 변수가 자식 타입의 객체를 사용하면 자동 타입변환이 이 일어난다.
- 부모클래스에 선언된 필드와 메서드만 접근이 가능하다.
- 자식클래스에 오버라이딩 된 메서드가 있다면 오버라이딩 메소드가 실행된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package ch07.sec07.exam02;
public class ChildExample {
public static class Parent {
// 메소드 선언
public void method1() {
System.out.println("Parent-method1()");
}
// 메소드 선언
public void method2() {
System.out.println("Parent-method2()");
}
}
public static class Child extends Parent {
// 메소드 오버라이딩
@Override
public void method2() {
System.out.println("Child-method2()");
}
// 메소드 선언
public void method3() {
System.out.println("Child-method3()");
}
}
public static void main(String[] args) {
// 자식 객체 생성
// Child child = new Child();
// 자동 타입 변환
Parent parent = new Child();
// 메소드 호출
parent.method1(); //Parent-method1()
parent.method2(); // child-method2()
// parent.method3(); (호출 불가능)
}
}
강제 타입변환 : 자식클래스 타입의 변수가 부모클래스의 타입의 변수로 생성되면 강제(캐스팅)을 해야한다.
package ch07.sec07.exam03;
public class ChildExample { public static class Parent { // 필드 선언 public String field1;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
// 메소드 선언 public void method1() { System.out.println("Parent-method1()"); } // 메소드 선언 public void method2() { System.out.println("Parent-method2()"); } } public static class Child extends Parent { // 필드 선언 public String field2; // 메소드 선언 public void method3() { System.out.println("Child-method3()"); } } public static void main(String[] args) { // 객체 생성 및 자동 타입 변환 Parent parent = new Child(); // Parent 타입으로 필드와 메소드 사용 parent.field1 = "data1"; parent.method1(); parent.method2(); /* * parent.field2 = "data2"; //(불가능) parent.method3(); //(불가능) */ // 강제 타입 변환 Child child = (Child) parent; // Child 타입으로 필드와 메소드 사용 child.field2 = "data2"; // (가능) child.method3(); // (가능) } }
1.7 다형성
자동 타입변환과 메서드 오버라이딩으로 인해 다형성이 가능해진다.
- 다형성이란 같은 메서드선어부로 다른 결과를 만들수 있는 성질을 말한다.
- 타이어 굴린다()의 메서드를 상속 받은 한국타이어,금호타이어가 있다고 가정하자.
- 타이어 : 굴린다() = 타이어가 굴러간다.
- 한국타이어 : 굴린다() = 한국 타이어가 굴러간다.
- 금호타이어 : 굴린다() = 금호 타이어가 굴러간다.
- 타이어 t1 = new 타이어 -> t1.굴린다() = 타이어가 굴러간다.
- 타이어 t1 = new 한국타이어 -> t1.굴린다() = 한국타이어가 굴러간다.
- 타이어 t1 = new 금호타이어 -> t1.굴린다() = 금호타이어가 굴러간다.
이러한 성질을 사용하면, 매개변수의 다형성을 만들 수 있다.
public class Driver { //메소드 선언(클래스 타입의 매개변수를 가지고 있음) public void drive(Vehicle vehicle) { vehicle.run(); } }
public class Vehicle { //메소드 선언 public void run() { System.out.println(“차량이 달립니다.”); } }
public class Bus extends Vehicle { //메소드 재정의(오버라이딩) @Override public void run() { System.out.println(“버스가 달립니다.”); } }
- 자동 타입 변환 : Vehicle vehicle = new Bus();
- 메서드 오버라이딩 : vehicle.run() - > bus.run()
public class DriverExample { public static void main(String[] args) { //Driver 객체 생성 Driver driver = new Driver();
//매개값으로 Bus 객체를 제공하고 driver 메소드 호출 Bus bus = new Bus(); driver.drive(bus); //driver.drive(new Bus()); 와 동일 }}
이것을 응용하면 instanceof와 강제 형변환을 통해 부모클래스의 변수를 자식 클래스의 멤버를 사용할 수 있게 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
public static void main(String[] args) { // 수퍼 클래스의 레퍼런스는 하위 클래스의 인스턴스를 담을 수 있다. Vehicle[] arr = new Vehicle[] {new Truck("기아자동차", 5, 10000, 32, 15f, true)}; printCar(arr[0]); } // Vehicle v = new Truck("기아자동차", 5, 10000, 32, 15f, true); // 부모타입의 변수(v)로 자식타입(Truck)을 선언 static void printCar(Vehicle v) { System.out.printf("모델: %s\n", v.model); System.out.printf("수용인원: %s\n", v.capacity); // v는 car의 부모클래스 // car 가능 if (v instanceof Car) { Car c = (Car) v; System.out.printf("cc: %s\n", c.cc); System.out.printf("밸브: %s\n", c.valve); if (v instanceof Sedan) { Sedan s = (Sedan) v; System.out.printf("썬루프: %b\n", s.sunroof); System.out.printf("오토: %b\n", s.auto); } else if (v instanceof Truck) { //강제 형변환 Truck t = (Truck) v; //오버라이딩 메서드 실행 System.out.printf("톤: %f\n", t.ton); System.out.printf("덤프여부: %b\n", t.dump); } } else if (v instanceof Bike) { Bike b = (Bike) v; System.out.printf("engine: %b\n", b.engine); } System.out.println("-----------------------"); }
}
1.8 추상클래스
- 추상메서드는 클래스들간에 공통적인 멤버를 선언하되 부모클래스를 직접적으로 사용할 수 없는 클래스이다.
- 추상클래스 이기 때문에 new연산자를 통해 직접적인 생성을 불가능하다.
- 자식클래스에서 상속을 받아 사용 해야한다.
- 상속 받은 클래스에서는 필드, 메서드 생성자를 사용할 수 있다.
// 추상 클래스 선언 public abstract class Phone { //필드 선언 String owner;
//생성자 선언 Phone(String owner) { this.owner = owner; } //메소드 선언 void turnOn() { System.out.println(“폰 전원을 켭니다.”); }void turnOff() { System.out.println(“폰 전원을 끕니다.”); }1 2
//메서드 선언만 있으면 상속받은 클래스에서는 메서드를 완성해야한다. void internetSearch();
}
// 자식 클래스 public class SmartPhone extends Phone { //생성자 선언 SmartPhone(String owner) { //Phone 생성자 호출 super(owner); //부모클래스의 생성자가 있으므로 생성해야한다. }
//메소드 선언1 2 3 4 5 6 7 8 9
//메서드 선언만 있으면 상속받은 클래스에서는 메서드를 완성해야한다. void internetSearch() { System.out.println("인터넷 검색을 합니다."); // 자식 클래스에서 추상클래스의 메서드를 오버라이딩 할 수 있다. @override void turnOff() { System.out.println("스마트폰 전원을 끕니다."); } }
public class PhoneExample { public static void main(String[] args) { //Phone phone = new Phone();
SmartPhone smartPhone = new SmartPhone(“홍길동”); smartPhone.turnOn(); //전원을 킵니다. smartPhone.internetSearch(); //인터넷 검색을 합니다. smartPhone.turnOff(); //스마트폰 전원을 끕니다. }}
1.9 this와 super
this | super | |
---|---|---|
메서드 | 현재 객체가 선언된 클래스의 메서드 | 현재 객체의 부모클래스 |
필드 | 현재 객체의 필드 | 현재 객체의 부모 메서드 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package study.oop.clazz;
public class TestThis {
static class A {
String name = "A";
boolean working = true;
void print() {
System.out.println("A.print():");
System.out.printf(" => this.name(%s)\n", this.name);
System.out.printf(" => this.working(%s)\n", this.working);
}
void m1() {
System.out.println("super.m1() 메서드=> A.m1()");
}
}
static class A2 extends A {
String name = "A2";
int age = 20;
@Override
void print() {
System.out.println("A2의 print문");
System.out.printf("this.name 필드 = > %s\n", this.name);
this.m1();
System.out.printf("super.name 필드 = > %s\n", super.name);
super.m1();
}
@Override
void m1() {
System.out.println("A2.m1()");
}
}
static class A3 extends A2 {
String name = "A3";
String gender = "남";
@Override
void m1() {
System.out.println("this.m1() 메서드=> A3.m1()");
}
}
public static void main(String[] args) {
A2 obj = new A3();
obj.print();
}
}
- 결과
A2의 print문
this.name 필드 = > A2
this.m1() 메서드=> A3.m1()
super.name 필드 = > A
super.m1() 메서드=> A.m1()- 메서드의 경우 override가 필요하여 A3로 선언된 오버라이딩이 우선
- 필드의 경우 override가 없으니 A2가 우선