객체의 필드, 메소드를 하나로 묶고 실제 구현 내용을 감추는 것
상위(부모)객체의 필드와 메소드를 하위(자식) 객체에게 물려주는 행위
extends
: 자식 클래스가 상속할 부모 클래스 지정하는 키워드[효과]
🖌️ 중복 코드 줄이는 방법
1. 반복문이나 배열/리스트 사용
2. 함수로 재사용하기
3. 클래스(필드+함수)로 재사용하기
4. 클래스 상속 이용 ⇒ 타입의 유연성을 위해 “다형성” 등장
5. 추상화 클래스나 인터페이스 사용
[상속 대상 제한]
private
접근 필드와 메소드 제외default
필드와 메소드 제외// 클래스의 상속
// : 부모(상위) 클래스의 자산(필드, 메소드)을 자녀(하위)클래스가 물려받는 것
// 사용 이유
// 1. 코드 중복 피할 수 있음.
// 2. 계층적인 구조로 코드를 설계 가능
// 예) 강아지: 동물 속성/행동(중복) + 강아지고유의 속성/행동(유니크)
// 고양이: 동물 속성/행동(중복) + 고양이고유의 속성/행동(유니크)
class Animal {
int age = 10;
void eat() {
System.out.println("먹는다.");
}
}
class Dog extends Animal {
void bark() {
System.out.println("짖는다.");
}
}
class Cat extends Animal {
void grooming() {
System.out.println("손질한다.");
}
}
public class ex36 {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(dog.age); // 10
dog.eat(); // 먹는다.
dog.bark(); // 짖는다.
Cat cat = new Cat();
System.out.println(cat.age); // 10
cat.eat(); // 먹는다.
cat.grooming(); // 손질한다.
}
}
부모의 생성자가 먼저 호출된 후, 자식 생성자 호출됨 → 이때 부모 생성자는 기본 생성자가 호출됨
// 상속관계에서 생성자 호출되는 순서(초기화 순서)
class Energy {
int price = 1000;
// 생성자 자동생성 -> 우클릭 -> 생성 -> 생성자
public Energy() { // 기본 생성자
System.out.println("Energy 기본생성자");
}
public Energy(int price) {
this.price = price;
System.out.println("Energy 필드(매개변수)가 있는 생성자");
}
}
class WindEnergy extends Energy {
int price = 2000;
public WindEnergy() {
System.out.println("Wind 기본생성자");
}
public WindEnergy(int price) {
this.price = price;
System.out.println("Wind 필드 생성자");
}
}
public class ex39 {
public static void main(String[] args) {
WindEnergy we1 = new WindEnergy();
WindEnergy we2 = new WindEnergy(3000);
}
}
부모의 필드 생성자가 호출되게 하고 싶은 경우 → super
사용
// 상속관계에서 생성자 호출되는 순서(초기화 순서)
class Energy {
int price = 1000;
// 생성자 자동생성 -> 우클릭 -> 생성 -> 생성자
public Energy() { // 기본 생성자
System.out.println("Energy 기본생성자");
}
public Energy(int price) {
this.price = price;
System.out.println("Energy 필드(매개변수)가 있는 생성자");
}
}
class WindEnergy extends Energy {
int price = 2000;
public WindEnergy() {
System.out.println("Wind 기본생성자");
}
public WindEnergy(int price) {
// super(); // 부모의 기본생성자 호출
super(price); // 부모의 필드 생성자 호출!
// super는 반드시 첫줄에 넣어야 함!
this.price = price;
System.out.println("Wind 필드 생성자");
}
}
public class ex39 {
public static void main(String[] args) {
WindEnergy we1 = new WindEnergy();
// Energy 기본생성자
// Wind 기본생성자
WindEnergy we2 = new WindEnergy(3000);
// Energy 기본생성자
// Wind 필드 생성자
}
}
⭐
this.
: 자기클래스의 필드/메소드 접근
this()
: 자기클래스의 생성자 함수 호출
super.
: 부모클래스의 필드/메소드 접근
super()
: 부모클래스의 생성자 함수 호출
부모 클래스의 메소드를 자식 클래스에서 재정의한다.
@Override
사용해도되고 안해도됨@
로 시작하는 컴파일 지시어 → 개발자나 컴파일러에게 해당 속성 알려줌// 오버라이드(Override)
// : 부모 클래스의 메소드를 자식 클래스에서 재정의하는 것
class Machine {
String name = "일반기계";
void work() {
System.out.println("일한다.");
}
}
class CoffeeMachine extends Machine {
String name = "커피머신";
// 부모 클래스에도 존재하는 메소드를 재정의 -> 오버라이드
// 오버라이드 메소드: 반환타입 함수이름 매개변수 동일!!
// 어노테이션: @로 시작하는 컴파일 지시어
// - 역할: 개발자나 컴파일러에게 해당 속성을 알려줌.
@Override
// @Override는 생략 가능
void work() {
System.out.println("커피머신이 일한다.");
}
}
public class ex40 {
public static void main(String[] args) {
CoffeeMachine cm = new CoffeeMachine();
cm.work(); // 커피머신이 일한다.
}
}
final 키워드 용도
final 필드: 상수처럼 동작, 한번 값이 들어가면 다시 대입 X
// 상수 선언시
final double PI = 3.141592;
PI = 6.13; // 다시 값을 넣으려고 하면 오류
class FinalClass {
String name = "파이널 클래스";
int age; // 0으로 초기화, 힙 영역의 참조변수는 강제 초기화
final int price = 0; // final 시에는 반드시 초기화
}
public class ex41 {
public static void main(String[] args) {
FinalClass fc = new FinalClass();
fc.price = 2000; // error -> final 이므로 재할당 불가!
fc.age = 30; // final 이므로 재할당 불가
}
}
final 클래스: 부모로 사용 불가한 클래스, 상속 불가
final class FinalClass {
String name = "파이널 클래스";
}
// error -> final 클래스이므로 상속 불가
class LastClass extends FinalClass {
}
public class ex41 {
public static void main(String[] args) {
FinalClass fc = new FinalClass();
}
}
final 메소드: 자식이 재정의할 수 없는 메소드, 오버라이드 불가
class FinalClass {
String name = "파이널 클래스";
final void disp() {
}
}
class LastClass extends FinalClass {
@Override
void disp(){} // final 메소드이므로 사용 불가
}
public class ex41 {
public static void main(String[] args) {
FinalClass fc = new FinalClass();
}
}
하나의 타입에 여러가지 객체를 대입해 다양한 실행 결과 얻는 것 ⇒ 유연성!
instanceof
// 다형성: Polymorphism
// : 자식 클래스 객체가 자기 클래스 또는 부모클래스의 타입을 모두 가질 수 있는 것
// 이유: 타입의 유연성을 가지기 위해서
// : 타입이 정확하지 않더라도 객체(참조변수)를 전달가능
class Parent {
String name = "Parent";
void parentMethod() {
System.out.println("Parent 내 이름은 " + this.name);
}
}
class Child extends Parent {
String name = "Child";
void childMethod() {
System.out.println("Child 내 이름은 " + this.name);
}
}
public class ex43 {
public static void main(String[] args) {
Parent p1 = new Parent();
System.out.println(p1.name);
Child c1 = new Child();
System.out.println(c1.name);
// 객체는 Child이지만, 타입은 Parent
// 1. 업캐스팅: 자식 객체가 부모 클래스 타입을 가지는 것
// 업캐스팅 할 수 있는 경우
// 1) 대입연산자를 통해서 자동형변환 될 때 -> 강제형변환
Parent p2 = new Child();
System.out.println(p2.name); // Parent
// 2) 형변환 연산자를 통해서
Parent p3 = (Parent) c1;
System.out.println(p3.name); // Parent
// 2. 다운캐스팅: 자식 객체가 자녀클래스의 타입을 갖는 것
// -> 강제형변환 불가
// 1) 형변환 연산자를 통해서 -> 강제형변환
Child c2 = (Child) p2;
System.out.println(c2.name); // Child
// 4가지 형태
Parent a = new Parent();
Child b = new Child();
Parent c = new Child(); // 업캐스팅
Child e = (Child) c; // 다운캐스팅
Child b = new Parent(); // 불가능 - 다형성과 무관
}
}
다운캐스팅 | 업캐스팅 | |
---|---|---|
Child 클래스 객체 | Child | Parent |
사용 이유: 다양한 객체를 전달하고 싶을 때, 부모 타입을 이용헤서 전달하면 여러 객체 전달 가능!
class People {
void think() {
System.out.println("생각한다.");
}
}
class Man extends People {
@Override
void think() {
System.out.println("남자가 생각한다.");
}
void shave() {
System.out.println("면도한다.");
}
}
class Woman extends People {
@Override
void think() {
System.out.println("여자가 생각한다.");
}
void makeup() {
System.out.println("화장한다.");
}
}
public class ex44 {
public static void main(String[] args) {
// 1. 업캐스팅: 자식객체가 부모클래스 타입을 가지는 것
People people = new Man();
people.think(); // 남자가 생각한다. // 오버라이드 메소드가 실행됨
// 2. 다운캐스팅: 자식객체가 부모클래스 타입으로 변경되었다가, 다시 자식 클래스 타입 갖는 것
((Man) people).shave();
// 다형성을 이용해서 Man 객체와 Woman 객체도 전달해보자.
myFunc(new Man());
myFunc(new Woman());
} // main
static void myFunc(People p) {
// instanceof 연산자: 객체 타입을 확인하는 연산자
if (p instanceof Man) {
((Man) p).shave();
}
if (p instanceof Woman) {
((Woman) p).makeup();
}
}
} // class
추상(abstract): 실체들 간에 공통되는 특성을 추출한 것 → 유사한 특성 유추
ex) 새, 곤충, 물고기 → 동물(추상)
추상 클래스: 실체 클래스들의 공통되는 필드와 메소드를 정의한 클래스
가상함수: 함수의 선언만 있고, 본체(코드)가 없는 것
→ 본체는 하위 클래스에서 재정의(Override)해서 사용함!
abstract
키워드 사용!public abstract class 클래스 {
// 필드
// 생성자
// 메소드
}
혹은
abstract public class 클래스 {
// 필드
// 생성자
// 메소드
}
[사용 이유]
// 추상화 클래스
abstract class Picture {
// 가상함수(추상 메소드): 선언만 있고, 코드본체가 없다.
abstract void draw();
// 일반함수
void sale() {
System.out.println("판다.");
}
}
class Picasso extends Picture {
// 메소드 재정의(오버라이드)를 통해 실제 코드를 구현함.
@Override
void draw() {
System.out.println("피카소가 그림을 그린다.");
}
}
// 기존 코드 건드리지 않고 새로운 기능을 확장할 수 있다.
// 새로운 기능을 가진 구현클래스로 버전업 할 수 있음 -> 안정성
class SuperPicasso extends Picture {
// 메소드 재정의(오버라이드)를 통해 실제 코드를 구현함.
@Override
void draw() {
System.out.println("수퍼 피카소가 그림을 그린다.");
}
}
public class ex45 {
public static void main(String[] args) {
Picasso picasso = new Picasso();
picasso.draw(); // 피카소가 그림을 그린다.
SuperPicasso sp = new SuperPicasso();
sp.draw(); // 수퍼 피카소가 그림을 그린다.
}
}
가상함수(추상메서드)만 있는 코드 뭉치
추상화 클래스 | 인터페이스 | |
---|---|---|
1. 가상함수 | 있음 | 있음 |
2. 일반함수 | 있음 | 없음(JDK8버전 이후 default 메서드 로 사용 가능) |
3. 예약어 | abstract class ,abstract 메서드명(abstract 생략 가능) , extends | interface , implements |
4. 다중상속 | 불가능 | 가능 |
5. 객체 생성 | 불가능(자식클래스에서 상속해야가능) | 불가능(구현해야) |
6. 접근제한자 | public /protected /private | public 만 가능 |
7. 필드선언 | 가능 | public static 만 가능 |
interface 인터페이스명 {
// 상수
타입 상수명 = 값;
// 추상 메소드
타입 메소드명(매개변수, ...);
// 디폴트 메소드
default 타입 메소드명(매개변수, ...){...}
// 정적 메소드
static 타입 메소드명(매개변수) {...}
}
public static final
이 컴파일과정에서 붙음interface IDrawing {
// 일반함수 선언안됨
// void sale() {
// System.out.println("판다.");
// }
// 가상함수만 선언 가능, abstract는 써도 되고, 안써도됨
// abstract (접근제어자) 반환타입 추상화메서드명();
abstract void draw();
// abstract void public draw(); 오류
void sketch();
}
class Painter implements IDrawing {
@Override
public void draw() {
System.out.println("드로잉");
}
@Override
public void sketch() {
System.out.println("스케치");
}
}
public class ex46 {
public static void main(String[] args) {
Painter p = new Painter();
p.draw();
p.sketch();
// 자체적으로 new 사용 못함
// IDrawing d = new IDrawing(); // 인터페이스 // error
// Abs a = new Abs(); // 추상클래스 // error
}
}
abstract class Abs {
}
// 추상화클래스와 인터페이스 차이점
// 추상화(일반) 클래스: 다중상속은 안됨, 다단계 상속은 가능
// 다단계 상속 -> 가능!
class A {
}
class B extends A {
}
class C extends B {
}
// class D extends C, B, A { // 다중 상속 -> 불가능!
// }
interface IA {
}
interface IB {
}
class E implements IA, IB { // 다중상속 (한방에 기능 확장!)
}
class SupermanClass extends C implements IA, IB {
}
public class ex47 {
public static void main(String[] args) {
}
}
인터페이스에서 일반함수처럼 코드를 가진 메소드 정의
class ClassM {
int price = 10; // 접근제한자가 default -> 앞에 default라고 명시하면 안됨!
}
interface Vehicle {
public void drive(); // 추상메소드(가상함수)
public default void run() {
System.out.println("달린다.");
}
}
// 인터페이스 상속을 통한 확장
interface Truck extends Vehicle {
}
class Tesla implements Vehicle {
@Override
public void drive() {
System.out.println("운전한다.");
}
}
public class ex48 {
public static void main(String[] args) {
Tesla tesla = new Tesla();
tesla.drive();
tesla.run();
}
}