상속
- 부모 클래스의 필드와 메소드를 자식 클래스에게 물려줌
- private 접근 제한을 갖는 필드와 메소드는 상속 대상이 아님
- default 접근 제한을 갖는 필드와 메소드는 다른 패키지에 있는 자식에게 상속 불가능
- 자식클래스에 "extends 부모클래스" 키워드를 넣어 사용 가능
- 자바에서는 단일 상속만 가능
- super(); 부모의 생성자 호출. 이 키워드 위쪽은 실행문이 올수없음
- super 키워드는 현재 만들어지고 있는 부모객체의 주소를 참조하는 키워드
- 이때 부모에게 기본 생성자가 있다면 자식 클래스에서는 부모 생성자를 생략할 수 있다.
- 모든 자식은 부모 객체부터 생성되어야 하므로 super() 호출 위로 타 실행문이 올 수 없다.
- 부모에게 기본 생성자가 존재하는 경우 자식 생성자에서는 super() 생성자를 생략할 수 있다.
- 만약 부모 클래스에 기본 생성자가 존재하지 않는 경우 자식 클래스에서는 반드시 super 생성자를 호출해야한다.
public class Phone {
//필드 선언
public String model;
public String color;
//매개변수를 갖는 생성자 선언
public Phone(String model,String color) {
this.model = model;
this.color = color;
System.out.println("Phone(String model, String color) 생성자 실행");
}
}
public class SmartPhone extends Phone {
//자식 생성자 선언
public SmartPhone(String model,String color) {
super(model,color);
System.out.println("SmartPhone(String model,String color) 생성자 실행됨");
}
}
public class SmartPhoneExample {
public static void main(String[] args) {
//SmartPhone 객체 생성
SmartPhone myPhone = new SmartPhone("갤럭시","은색");
//Phone으로부터 상속받은 필드 읽기
System.out.println("모델:" + myPhone.model);
System.out.println("색상:" + myPhone.color);
}
}
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 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();
}
}
메소드 오버라이딩
- 재정의를 의미
- 상속에서 부모가 가진 메소드의 선언부를 유지한채 실행문만 다시 재정의하는 것을 메소드 오버라이딩이라고 한다.
오버라이딩 규칙!
-상위 클래스의 메소드 선언부가 접근제한자를 제외하고는 반드시 일치해야한다.
-접근제한자는 더 약하게는 재정의할 수 있으나 더 강하게는 재정의할 수 없다.
public 으로 선언되었을 경우 다른 접근제한자로 수정 불가능
@Override 어노테이션을 사용하면 강력한 오버라이딩 컴파일을 할 수 있다.
만약 부모와 일치하는 메소드 선언부가 없다면 컴파일 오류를 낸다.
메소드 오버라이딩을 하게 되면 자식객체 외부에서는 오버라이딩 이전 메소드는 사용할 수 없다.
이때 경우에 따라 오버라이딩 이전의 메소드가 필요하면 super키워드를 사용하여 재정의 이전 메소드를 사용할 수 있다.
public class Tire {
public void run() {
System.out.println("일반 타이어가 굴러갑니다.");
}
}
public class SnowTire extends Tire {
@Override
public void run() {
System.out.println("스노우 타이어가 굴러갑니다.");
}
}
public class SnowTireExample {
public static void main(String[] args) {
SnowTire snowtire = new SnowTire();
Tire tire = snowtire;
snowtire.run();
tire.run();
}
}
public class TireRoll {
public void roll() {
System.out.println("회전합니다.");
}
}
public class HankookTire extends TireRoll {
@Override
public void roll() {
System.out.println("한국 타이어가 회전합니다.");
}
}
public class KumhoTire extends TireRoll {
@Override
public void roll() {
System.out.println("금호 타이어가 회전합니다.");
}
}
public class Car {
//필드 선언
public TireRoll tireroll;
public void run() {
//tireroll 필드에 대입된 객체의 roll() 메소드 호출
tireroll.roll();
}
}
public class CarExample {
public static void main(String[] args) {
Car myCar = new Car();
//TireRoll 객체 장착
myCar.tireroll = new TireRoll();
myCar.run();
//HankookTire 객체 장착
myCar.tireroll = new HankookTire();
myCar.run();
//KumhoTire 객체 장착
myCar.tireroll = new KumhoTire();
myCar.run();
}
}
final 필드 : 수정 불가 필드
final 클래스 : 부모로 사용 불가능한 클래스. 상속할 수 없음
final 메소드 : 자식이 재정의할 수 없는 메소드. 오버라이딩 불가능
자동타입변환
부모클래스 변수 = 자식클래스타입;
자식클래스 타입을 부모클래스 변수에 대입하면 자동타입변환이 됨
생성된 모든 자식 객체는 부모객체를 가지고 있기 때문에 부모 타입 변수에 자식 객체를 대입할 때 자동 형변환이 이루어진다.
자식 객체가 대입된 부모 타입 변수에는 자식 객체의 주소를 참조한다.
부모타입 변수에 참조된 자식객체의 사용 범위는 부모 타입 구성요소까지이다.
자식 객체가 대입되어 있지만 그릇은 부모이기 때문이다.
강제 타입 변환
자식클래스 변수 = (자식클래스) 부모클래스타입;
자식 타입을 부모 타입으로 자동 변환 후, 다시 자식 타입으로 변환할 때
부모객체를 생성한다고해서 반드시 자식객체가 생성되지는 않으므로
만약 강제로 형변환 하려는 부모객체가 해당 타입의 자식객체를
가지고 있지 않으면 실행시 오류가 발생한다
참조하고 있는 부모타입의 자식객체 타입과 강제형변환하는 타입이
일치하지 않으면 실행시 오류가 발생한다.
instanceof 연산자를 사용하면 변수안에 참조된 객체의 타입을 체크할 수 있다.
객체 instanceof 클래스
Child01 c1 = new Child01();
Parent p_1 = c1;
if(p_1 instanceof Child02) {
System.out.println("Child02로 강제형변환");
Child02 c2_2 = (Child02)p_1;
}else if(p_1 instanceof Child01) {
System.out.println("Child01로 강제형변환");
Child01 c1_2 = (Child01)p_1;
}
다형성
-같은 실행코드이지만 대입되는 값에 따라 결과가 달라지는 것을 의미한다.
상속에서는 자동 타입변환 원리를 사용하여 자바의 다형성을 구현할 수 있다.
1)모든 자식 객체는 부모로 자동 타입변환이 된다.
2)부모메소드를 재정의(오버라이딩)하면 부모의 메소드는 숨겨지고
자식 메소드가 실행된다.
필드의 다형성 : 특정 객체의 필드를 부모타입으로 뒀을 때 이루어지는 다형성
매개변수의 다형성
: 메소드의 매개변수가 부모타입으로 뒀을 때 이루어지는 다형성
Child01 c1 = new Child01();
Parent p_1 = c1;
p_1 = new Parent();
p_1.parentMethod(); //재정의 되지 않은 메소드 호출
c1 = new Child01();
c1.parentMethod(); //재정의 된 메소드 호출
p_1 = c1;
p_1.parentMethod(); //부모의 이전 메소드는 숨겨졌기 때문에 오버라이딩된 메소드가 실행
상속관계 : GrandMam 클래스 -> Parent 클래스 -> Child01, Child02 클래스
GrandMam 클래스 -> Parent02 클래스 -> Child03 클래스
public class GrandMam {
public String gField01 = "할머니 필드1";
public GrandMam(String data) {
gField01 = data;
System.out.println("할머니 만들어지는 중!!");
}
}
public class Parent extends GrandMam {
public String field01 = "부모 필드01";
String field02 = "부모 필드02(default)";
protected String field03 = "부모 필드03(protected)";
private String field04 = "부모 필드04(private)";
public Parent() {
//System.out.println("자식 생성중");
super("data");
}
void parentMethod() {
System.out.println("부모 parent의 메소드 호출입니다.");
}
public void parentMethod2() {
System.out.println("부모 parent의 메소드2 호출입니다.");
}
public final void finalMethod() {
System.out.println("최종의 메소드 호출입니다.");
}
public String getField04() { return field04; }
public void setField04(String field04) { this.field04 = field04; }
}
public class Parent02 extends GrandMam {
public String field01 = "최종 parent02 클래스 필드";
public Parent02() {
super("data");
}
}
public class Child01 extends Parent {
@Override
public void parentMethod() {
System.out.println("오버라이딩 된 메소드 호출!!");
}
@Override
public void parentMethod2() {
System.out.println("수정된 메소드2 호출!!");
}
public void beforeParentMethod2() {
super.parentMethod2();
}
}
public class Child02 extends Parent {
public String child02Field = "자식 객체의 필드";
}
public class Child03 extends Parent02 {
}
public class Vclass {
//필드의 다형성 : 특정 객체의 필드를 부모타입으로 뒀을 때 이루어지는 다형성
public Parent parent;
//매개변수의 다형성 : 메소드의 매개변수가 부모타입으로 뒀을 때 이루어지는 다형성
public void method(Parent p) {
p.parentMethod();
}
}
Main01
public class Main01 {
public static void main(String[] args) {
Parent parent01 = new Parent();
System.out.println(parent01.gField01);
System.out.println(parent01.field01);
System.out.println(parent01.field02);
System.out.println(parent01.field03);
//System.out.println(parent01.field04); private는 접근 x
System.out.println(parent01.getField04());
System.out.println("-----------------------");
Child01 child01 = new Child01();
System.out.println(child01.gField01);
//부모 Parent의 field01 상속됨
System.out.println(child01.field01);
System.out.println(child01.field02);
System.out.println(child01.field03);
//부모의 private 접근제한 필드는 상속x
//System.out.println(child01.field04);
System.out.println(child01.getField04());
child01.parentMethod();
parent01.parentMethod();
child01.beforeParentMethod2();
}
}
Main02
public class Main02 {
public static void main(String[] args) {
Child02 child02 = new Child02();
/*
자식 객체는 상속된 부모의 모든 구성요소와 자기 자신의 구성요소에 접근할 수 있다.
*/
/*
생성된 모든 자식 객체는 부모객체를 가지고 있기 때문에
부모 타입 변수에 자식 객체를 대입할 때 자동 형변환이 이루어진다.
*/
Parent parent = child02;
/*
자식 객체가 대입된 부모 타입 변수에는 자식 객체의 주소를 참조한다.
*/
System.out.println(child02 == parent);
/*
부모타입 변수에 참조된 자식객체의 사용 범위는 부모 타입 구성요소까지이다.
*/
//System.out.println(parent.child02Field);
Child03 c3 = new Child03();
//c3의 부모에 Parent02가 존재하므로 자동 형변환
Parent02 p2 = c3;
//c3의 부모 또는 조상 중에는 Parent가 없으므로 자동 형변환 X
//Parent p = c3;
//c3의 조상 중에 GrandMam이 존재하므로 자동 형변환
GrandMam gm = c3;
//부모타입 변수에 대입되는 자식 객체의 주소값은 전부 동일하다
System.out.println(gm == c3);
//-----------------------------------------------------
Child02 c2 = new Child02();
Parent p = c2; //자동형변환(자식 객체를 부모타입 대입)
c2 = (Child02)p; //강제형변환(부모 객체를 자식타입 대입)
/*
부모객체를 생성한다고해서 반드시 자식객체가 생성되지는 않으므로
만약 강제로 형변환 하려는 부모객체가 해당 타입의 자식객체를
가지고 있지 않으면 실행시 오류가 발생한다
*/
//c2 = (Child02)new Parent(); //자식인 Child02는 만들지 않아 자식에 대입할 수 없어 오류남
Child01 c1 = new Child01();
Parent p_1 = c1;
/*
참조하고 있는 부모타입의 자식객체 타입과 강제형변환하는 타입이
일치하지 않으면 실행시 오류가 발생한다.
*/
//Child02 c2_2 = (Child02)p_1;
/*
instanceof 연산자를 사용하면 변수안에 참조된 객체의 타입을 체크할 수 있다.
*/
if(p_1 instanceof Child02) {
System.out.println("Child02로 강제형변환");
Child02 c2_2 = (Child02)p_1;
}else if(p_1 instanceof Child01) {
System.out.println("Child01로 강제형변환");
Child01 c1_2 = (Child01)p_1;
}
System.out.println();
p_1 = new Parent();
p_1.parentMethod(); //재정의 되지 않은 메소드 호출
c1 = new Child01();
c1.parentMethod(); //재정의 된 메소드 호출
p_1 = c1;
p_1.parentMethod(); //부모의 이전 메소드는 숨겨졌기 때문에 오버라이딩된 메소드가 실행
}
}
Main03
public class Main03 {
public static void main(String[] args) {
Parent p = new Parent();
Child01 c1 = new Child01();
Vclass vclass = new Vclass();
/*
다형성
-같은 실행코드이지만 대입되는 값에 따라 결과가 달라지는 것을 의미한다.
*/
vclass.parent = p;
vclass.parent.parentMethod(); //Parent의 메소드 호출
vclass.parent = c1;
vclass.parent.parentMethod(); //재정의된 Child01의 메소드 호출
System.out.println();
vclass.method(p); //Parent의 메소드 호출
vclass.method(c1); //재정의된 Child01의 메소드 호출
}
}
추상클래스
- 실체 클래스들의 공통되는 필드와 메소드 정의한 클래스
- 추상클래스는 실체클래스의 부모 클래스 역할(단독 객체 x). 상속만 가능
- 추상클래스를 사용하여 객체를 만들 수 없음(new 키워드 사용 불가능)
- 추상메소드를 재정의하지 않으면 자식 클래스도 추상클래스가 되어야함
<추상클래스를 사용하는 경우>
-실체화된 클래스들의 공통 속성 또는 정형화 되어져있는 메소드명을 제공하여 실제 클래스들의 빠른 개발을 위하여 사용
-실체화 클래스를 일반적으로 선언하게 되면 같은 기능의 메소드명이 들쭉날쭉해질 수 있는 부분을 추상클래스를 사용하여 정형화 할 수 있다.
추상메소드는 반드시 실체화 클래스에서 실행 블록을 구현해야한다
그렇지 못할 경우 컴파일 오류가 발생한다
추상메소드를 쓸 경우 상속받는 자식 클래스에서 반드시
해당 추상메소드를 오버라이딩해줘야 한다는 뜻이다.
추상메소드는 중괄호 없이 선언한다
public abstract void intro();
public abstract class Phone {
String owner;
Phone(String owner){
this.owner = owner;
}
void turnOn() {
System.out.println("폰 전원을 켭니다");
}
void turnOff() {
System.out.println("폰 전원을 끕니다");
}
}
public class SmartPhone extends Phone {
SmartPhone(String owner){
super(owner);
}
void internetService() {
System.out.println("인터넷 검색을 합니다");
}
}
public class PhoneExample {
public static void main(String[] args) {
//Phone phone = new Phone();
SmartPhone smartPhone = new SmartPhone("홍길동");
smartPhone.turnOn();
smartPhone.internetService();
smartPhone.turnOff();
}
}
public abstract class Animal {
public String kind;
public String legCount;
public String sound;
public abstract void intro();
}
public class Cat extends Animal {
@Override
public void intro() {
System.out.println("종류는 " + kind);
System.out.println("다리수는 " + legCount);
System.out.println("울음소리는 " + sound);
}
}
public class Dog extends Animal {
@Override
public void intro() {
System.out.println("종류:" + kind);
System.out.println("다리수:" + legCount);
System.out.println("울음소리:" + sound);
}
}
public class Main04 {
public static void main(String[] args) {
Cat cat = new Cat();
cat.kind = "고양이";
cat.legCount = "4";
cat.sound = "야옹";
Dog dog = new Dog();
dog.kind = "강아지";
dog.legCount = "4";
dog.sound = "멍멍";
cat.intro();
dog.intro();
}
}
'Java' 카테고리의 다른 글
[Java] 익명 객체, 다중인터페이스 (0) | 2024.08.07 |
---|---|
[Java] 인터페이스 (0) | 2024.08.06 |
[Java] 인스턴스 멤버, 정적 멤버, 정적 필드, 정적 메소드, 상수, 패키지, 접근제한자, 싱글톤 (0) | 2024.08.01 |
[Java] class, 필드, 생성자, 메소드, 오버로딩 (0) | 2024.07.31 |
[Java] 배열, 향상된 for문, 열거 (0) | 2024.07.29 |