상속
"기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것"
장점 : 적은 양의 코드로 새로운 클래스를 작성할 수 있고, 코드를 공통적으로 관리할 수 있기 때문에 추가 및 변경에 용이.
코드의 재사용성을 높이고 중복을 제거하여 생산성과 유지보수에 기여함.
class Child extends Parent {
...
}
Child(자식 클래스) -> Parent(부모 클래스)
class Parent { } // 부모 클래스
class Child extends Parent { } // Child 클래스가 Parent 클래스를 상속 받음
Parent 클래스에 age라는 정수형 변수를 멤버변수로 추가하면, Child 클래스는 모두 상속받기 때문에 Child 클래스에도 자동으로 age 멤버변수가 추가된다.

| 클래스 | 클래스의 멤버 |
| Parent | age |
| Child | age |
반대로 자식 클래스 Child에 멤버변수를 추가하면 부모 클래스에 아무런 영향을 주지 못한다.
- 멤버변수만 상송된다. 생성자와 초기화 블럭 X
- 자식 클래스의 멤버 개수는 부모 클래스보다 항상 같거나 많다.
부모 클래스로부터 상속받는 자식 클래스가 여러개인 경우 자식 클래스간에는 서로 아무런 관계도 성립하지 않는다.
class Parent { }
class Child1 extends Parent { }
class Child2 extends Parent { }
Child1 -> Parent <- Child2
자식 클래스로부터 상속받는 GrandChild라는 새로운 클래스를 추가하면
class Parent { }
class Child1 extends Parent { }
class Child2 extends Parent { }
class GrandChild extends Child1 { }
GrandChild -> Child1 -> Parent <- Child2

| 클래스 | 클래스의 멤버 |
| Parent | age |
| Child1 | age |
| Child2 | age |
| GrandChild | age |
Parent 클래스에 추가된 멤버변수 age는 Parent 클래스의 자손인 Child1, Child2, GrandChild에서도 추가되며 Parent 클래스에서 삭제되면 모두 삭제된다.
단일 상속
자바는 오직 단일 상속만을 허용한다. 두 개 이상의 클래스로부터 상속 받을 수 없다.
class TVCR extends TV, VCR { // 에러. 부모 클래스는 하나만 허용
...
}
Object 클래스
모든 클래스의 조상 클래스. 마지막 최상위 조상은 Object 클래스.
오버라이딩
"부모 클래스로부터 상속받은 메서드의 내용을 변경하는 것"
class Point {
int x;
int y;
String getLocation() {
return "x : " + x + ", y : " + y;
}
}
class Point3D extends Point {
int z;
String getLocation() { // 오버라이딩
return "x : " + x + ", y : " + y + ", z : " + z;
}
}
조건
자손 클래스와 오버라이딩하는 메서드는 조상 클래스의 메서드와
- 이름이 같아야 함
- 매개변수가 같아야 함
- 반환타입이 같아야 함
오버라이딩과 오버로딩
오버로딩 = 기존에 없는 새로운 메서드는 정의
오버라이딩 = 상속받은 메서드의 내용을 변경하는 것
class Parent {
void parentMethod() { }
}
class Child extends Parent {
void parentMethod() { } // 오버라이딩
void parentMethod(int i) { } // 오버로딩
void childMethod() { }
void childMethod(int i) { } // 오버로딩
void childMethod() { } // <--- 에러. 중복정의
super
"자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조 변수"
this와 super 차이
class Parent {
String name = "Parent";
void display() {
System.out.println("부모 클래스 메서드");
}
}
class Child extends Parent {
String name = "Child";
void display() {
System.out.println("자식 클래스 메서드");
}
void test() {
System.out.println(this.name); // Child
System.out.println(super.name); // Parent
this.display(); // 자식 클래스 메서드
super.display(); // 부모 클래스 메서드
}
}
public class Main {
public static void main(String[] args) {
Child c = new Child();
c.test();
}
}
/* 출력 결과
Child
Parent
자식 클래스 메서드
부모 클래스 메서드
*/
package와 import
패키지 : 클래스의 묶음
package 패키지명;
import : 다른 패키지의 클래스를 사용할 때 사용
import 패키지명.클래스명;
또는
import 패키지명.*;
제어자
클래스, 변수 또는 메서드의 선언부에 사용되어 부가적인 의미를 부여
접근 제어자 : public, protected, default, private
그 외 : static, final, abstract, native, transient, synchronized, volatile, strictfp
final
변수에 사용되면 값을 변경할 수 없는 상수가 됨. 오버라이딩 불가능.
final class FinalTest {
final int MAX_SIZE = 10;
final void getMaxSize() {
final int LV = MAX_SIZE;
return MAX_SIZE;
}
}
abstract
추상 메서드. 메서드의 선언부만 작성하고 실제 수행내용은 구현하지 않음.
abstract class AbstractTest { // 추상 클래스
abstract void move(); // 추상 메서드
}
접근 제어자
| 제어자 | 같은 클래스 | 같은 패키지 | 자손 클래스 | 전체 |
| public | ㅇ | ㅇ | ㅇ | ㅇ |
| protected | ㅇ | ㅇ | ㅇ | |
| (default) | ㅇ | ㅇ | ||
| private | ㅇ |
캡슐화. 접근 제어자를 사용하는 이유는 클래스의 내부에 선언된 데이터를 보호하기 위함.
다형성
"여러 가지 형태를 가질 수 있는 능력" 자바는 한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 한다.
조상 클래스 타입의 참조 변수로 자손 클래스의 인스턴스를 참조할 수 있도록 한다는 뜻.
class tv {
boolean power;
int channel;
void power() { power = !power; }
void channelUp() { ++channel; }
void channelDown() { --channel; }
}
class CaptionTv extends Tv {
String text;
void caption() { ... }
}
Tv t = new CaptionTv(); // 조상 타입의 참조변수로 자손 인스턴스를 참조
둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.
CaptionTv c = new CaptionTv();
TV t = new CaptionTv();
반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 불가능. 에러가 발생한다.
CaptionTv c = new Tv(); // 에러 발생
instanceof 연산자
참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용한다.
참조변수 instanceof 타입(클래스명)
// 연산 결과로 true , false 중 하나 반환
// ex
if (c instanceof FireEngine) {
...
}
instanceof 연산의 결과가 true라는 것은 검사한 타입으로 형변환이 가능하는 것을 뜻한다.
여러 종류의 객체를 배열로 다루기
Product p1 = new Tv();
Product p2 = new Computer();
Product p3 = new Audio();
// 위 코드를 배열로 처리
Product p[] = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
추상클래스
클래스는 설계도, 추상클래스는 미완성 설계도.
미완성 설계도로 완성된 제품을 만들 수 없듯이 추상클래스로 인스턴스를 생성할 수 없다.
추상클래스는 상속을 통해서 자손 클래스에 의해서만 완성될 수 있다.
arstract class 클래스이름 {
...
}
추상 메서드
메서드는 선언부와 구현부(몸통)로 구성되어 있다.
선언부만 작성하고 구현부는 작성하지 않은 채로 남겨 둔 것이 추상메서드이다.
/* 주석을 통해 어떤 기능을 수행할 못적으로 작성하였는지 설명 */
abstract 리턴타입 메서드이름();
abstract class Player { // 추상클래스
abstract void play(int pos); // 추상 메서드
abstract void stop(); // 추상 메서드
}
class AudioPlayer extends Player {
void play(int pos) { ... } // 추상메서드 구현
void stop() { ... } // 추상메서드 구현
}
abstract class AbstractPlayer extends Player {
void play(int pos) { ... } //추상메서드 구현
}
굳이 abstract를 붙여서 추상메서드로 선언하는 이유는 자손 클래스에서 추상 메서드를 반드시 구현하도록 강요하기 위해서이다.
class Marine {
int x, y;
void move(int x, int y) {}
void stop() {}
void stimPack() {}
}
class Tank {
int x, y;
void move(int x, int y) {}
void stop() {}
void changeMode() {}
}
class Dropship {
int x, y;
void move(int x, int y) {}
void stop() {}
void load() {}
void unload() {}
}
이 코드를 공통 부분을 뽑아내어 하나의 클래스로 만들고, 이 클래스로부터 상속받도록 코드 변경
abstract class Unit {
int x, y;
abstract void move(int x, int y);
void stop() {}
}
class Marine extends Unit {
void move(int x, int y) {}
void stimPack() {}
}
class Tank extends Unit {
void move(int x, int y) {}
void changeMode() {}
}
class Dropship extends Unit {
void move(int x, int y) {}
void load() {}
void unload() {}
}
인터페이스
일종의 추상클래스.추상화 정도가 높아서 몸통을 갖춘 일반 메서드 또는 멤버 변수를 구성원으로 가질 수 없다.
오직 추상메서드와 상수만을 멤버로 가질 수 있다.
추상클래스 = 미완성 설계도
인터페이스 = 기본 설계도
interface 인터페이스 이름 {
public static final 타입 상수이름 = 값;
public abstract 메서드이름(매개변수목록);
}
인터페이스의 제약사항
- 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
- 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.
인터페이스의 상속
인터페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와 달리 다중상속, 즉 여러 개의 인터페이스로부터 상속을 받는 것이 가능하다.
interface Movable {
// 지정된 위치(x, y)로 이동하는 기능의 메서드
void move(int x, int y);
}
interface Attackable {
// 지정된 대상을(u) 공격하는 기능의 메서드
void attack(Unit u);
}
interface Fightable extends Movable, Attackable {}
인터페이스를 구현할 때는 extends 대신 implements를 사용한다
class 클래스이름 implements 인터페이스이름 {
//
}
class Fighter implements Fightable {
public void move(int x, int y) { }
public void attack(Unit u) { }
}
구현하려는 인터페이스의 메서드 중 일부만 구현한다면 abstract를 붙인다
abstract class Fighter implements Fightable {
public void move(int x, int y) {}
}
다음과 같이 상속과 구현을 동시에 할 수 있다
class Fighter extends Unit implements Fightable {
public void move(int x, int y) { }
public void attack(Unit u) { }
}
인터페이스의 장점
- 개발시간을 단축시킬 수 있다
- 표준화가 가능하다
- 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다
- 독립적인 프로그래밍이 가능하다
'자바 > Java의 정석' 카테고리의 다른 글
| [Chapter 09] java.lang 패키지와 유용한 클래스 (0) | 2024.11.24 |
|---|---|
| [Chapter 08] 예외처리 (0) | 2024.11.23 |
| [Chapter 06] 객체지향 프로그래밍 I (1) | 2024.11.19 |
| [Chapter 05] 배열 array (1) | 2024.11.17 |
| [Chapter 04] 조건문과 반복문 (1) | 2024.11.17 |