상속(inheritance)
상속 : 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것
상속의 장점
- 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있고
- 코드를 공통적으로 관리할 수 있기에 코드의 추가 및 변경이 매우 용이
상속을 구현하는 방법
class Parent { }
class Child extends Parent { }
상속을 받는다는 것은 조상 클래스를 확장(extend)한다는 의미
- 생성자와 초기화 블럭은 상속되지 않음. 멤버만 상속
- 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많음
클래스간의 관계 - 포함관계
포함관계 : 한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언
class Circle {
int x;
int y;
int r;
}
class Point {
int x;
int y;
}
//포함관계
class Circle {
Point c = new Point();
int r;
}
//상속관계
class Circle extends Point {
int r;
}
- 상속관계
- ~은 ~이다 (is - a)
- 포함관계
- ~은 ~을 가지고 있다 (has - a)
=> '원은 점이다' 보다 '원은 점을 가지고 있다' 가 더 자연스러우므로 포함관계를 맺는것이 옳음
단일상속 (single inheritance)
자바에서는 단일 상속만을 허용
다중 상속의 장단점
- 장점
- 여러 클래스로부터 상속받을 수 있기 때문에 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있음
- 단점
- 클래스간의 관계가 매우 복잡해짐
- 서로 다른 클래스로부터 상속받은 멤버간의 이름이 같은 경우 구별할 수 없음
=> 이런 문제점으로 다중 상속의 장점을 포기하고 자바는 단일 상속만을 허용
Object 클래스 - 모든 클래스의 조상
Object 클래스 : 모든 클래스 상속계층도의 최상위에 있는 조상 클래스
다른 클래스로부터 상속 받지 않는 모든 클래스들은 자동적으로 Object클래스로부터 상속받게 함
모든 상속계층도의 최상위에는 Object클래스가 위치하기에, toString(), equals(Object o)와 같은 메서드들을 사용할 수 있음
오버라이딩(overriding)
오버라이딩 : 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것
오버라이딩의 조건
자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와
- 이름이 같아야 함
- 매개변수가 같아야 함
- 반환 타입이 같아야 함
다만, 접근 제어자와 예외는 제한된 조건하에서만 다르게 변경 가능
- 접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없음
- 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없음
- 인스턴스 메서드를 static 메서드로 도는 그 반대로 변경할 수 없음
오버로딩 vs 오버라이딩
- 오버로딩(overloading)
- 기존에 없는 새로운 메서드를 정의하는 것 (new)
- 오버라이딩(overriding)
- 상속받은 메서드의 내용을 변경하는 것(change, modify)
super
super : 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조 변수
상속받은 멤버와 자신의 클래스에 정의된 멤버의 이름이 같을 때는 super을 사용해서 구별할 수 있음
super()
super() : 조상 클래스의 생성자를 호출하는데 사용
Object 클래스를 제외한 모든 클래스의 생성자 첫 줄에는 생성자, this() 또는 super()를 호출해야한다
그렇지 않으면 컴파일러가 자동적으로 super()를 생성자의 첫 줄에 삽입함
Package와 import
패키지(package)
패키지 : 클래스의 묶음
패키지에는 클래스 또는 인터페이스를 포함시킬 수 있으며, 서로 관련된 클래스들끼리 그룹단위로 묶어 놓음으로써 클래스를 효율적으로 관리할 수 있음
- 하나의 소스파일에는 첫 번째 문장으로 단 한 번의 패키지 선언만을 허용
- 모든 클래스는 반드시 하나의 패키지에 속해야 함
- 패키지는 점을 구분자로 하여 계층구조로 구성할 수 있음
- 패키지는 물리적으로 클래스 파일을 포함하는 하나의 디렉토리
패키지를 선언하는 법
package 패키지명;
import문
import문으로 클래스의 코드를 작성하기 전에, 사용하고자 하는 클래스의 패키지를 미리 명시해주면 소스코드에 사용되는 클래스 이름에서 패키지명 생략 가능
import문을 선언하는 법
import 패키지명.클래스명;
import 패키지명.*;
static import문을 사용하면 static멤버를 호출할 때 클래스 이름을 생략할 수 있음
제어자(modifier)
제어자 : 클래스, 변수 또는 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여
- 접근 제어자
- public, protected, default, private
- 그 외
- static, final, abstract, native, trasient, synchronized, volatile, strictfp
static - 클래스의, 공통적인
static 멤버 변수는 인스턴스에 관계없이 같은 값을 가짐
static이 붙은 멤버변수와 메서드, 초기화 블럭은 인스턴스가 아닌 클래스에 관계된 것이기 때문에 인스턴스를 생성하지 않고도 사용할 수 있음
static이 사용될 수 있는 곳 - 멤버변수, 메서드, 초기화 블럭
제어자 | 대상 | 의미 |
static | 멤버변수 | -모든 인스턴스에 공통적으로 사용되는 클래스변수가 됨 -클래스 변수는 인스턴스를 생성하지 않고도 사용 가능 -클래스가 메모리에 로드될 때 생성 |
메서드 | -인스턴스를 생성하지 않고도 호출이 가능한 static 메서드가 됨 -static메서드 내에서는 인스턴스 멤버들을 직접 사용할 수 없음 |
final - 마지막의, 변경될 수 없는
final이 변수에 사용되면 값을 변경할 수 없는 상수가 되며, 메서드에 사용되면 오버라이딩을 할 수 없게 되고, 클래스에 사용되면 자신을 확장하는 자손클래스를 정의하지 못하게 됨
final이 사용될 수 있는 곳 - 클래스, 메서드, 멤버변수, 지역변수
제어자 | 대상 | 의미 |
final | 클래스 | 변경될 수 없는 클래스, 확장될 수 없는 클래스가 됨 따라서 final로 지정된 클래스는 다른 클래스의 조상이 될 수 없음 |
메서드 | 변경될 수 없는 메서드 final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없음 |
|
멤버변수 / 지역변수 | 변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 됨 |
abstract - 추상의, 미완성의
abstract : 메서드의 선언부만 작성하고, 실제 수행내용은 구현하지 않은 추상 메서드를 선언하는데 사용
abstract가 사용될 수 있는 곳 - 클래스, 메서드
제어자 | 대상 | 의미 |
abstract | 클래스 | 클래스 내에 추상 메서드가 선언되어 있음을 의미 |
메서드 | 선언부만 작성하고 구현부는 작성하지 않은 추상 메서드임을 알림 |
추상 클래스는 아직 완성되지 않은 메서드가 존재하지 않은 '미완성 설계도'이므로 인스턴스를 생성할 수 없음
abstract class AbstractTest { //추상 클래스(추상 메서드를 포함한 클래스)
abstract void move(); //추상 메서드(구현부가 없는 메서드)
}
다른 클래스가 추상 클래스를 상속 받아, 일부의 원하는 메서드만 오버라이딩해도 된다는 장점
접근 제어자(access modifier)
접근 제어자 : 멤버 또는 클래스에 사용되어, 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할
접근 제어자가 사용될 수 있는 곳 - 클래스, 멤버변수, 메서드, 생성자
- private : 같은 클래스 내에서만 접근이 가능
- (default) : 같은 패키지 내에서만 접근이 가능
- protected : 같은 패키지 내에서, 그리고 다른 패키지의 자손 클래스에서 접근이 가능
- public : 접근 제한이 없음
제어자 | 같은 클래스 | 같은 패키지 | 자손 클래스 | 전체 |
public | O | O | O | O |
protected | O | O | O | |
(default) | O | O | ||
private | O |
대상에 따라 사용할 수 있는 접근 제어자
대상 | 사용가능한 접근 제어자 |
클래스 | public, (default) |
메서드 | public, protected, (default), private |
멤버변수 | public, protected, (default), private |
지역변수 | 없음 |
접근 제어자를 사용하는 이유
- 외부로부터 데이터를 보호하기 위해
- 외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해
생성자의 접근 제어자
생성자의 접근 제어자를 private로 지정하면, 외부에서 생성자에 접근할 수 없으므로, 인스턴스를 생성할 수 없게 됨
대신, 인스턴스를 생성해서 반환해주는 public메서드를 제공하므로써 외부에서 이 클래스의 인스턴스를 사용하도록 할 수 있음
이때 이 메서드는 public이면서 static이어야 함
제어자(modifier)의 조합
대상에 따라 사용할 수 있는 제어자
대상 | 사용가능한 제어자 |
클래스 | public, (default), final, abstract |
메서드 | 모든 접근 제어자, final, abstract, static |
멤버변수 | 모든 접근 제어자, final, static |
지역변수 | final |
제어자를 조합해서 사용할 때 주의해야 할 사항
- 메서드에 static과 abstract를 함께 사용할 수 없음
- static메서드는 몸통이 있는 메서드에만 사용할 수 있기 때문
- 클래스에 abstract와 final을 동시에 사용할 수 없음
- 클래스에 사용되는 final은 클래스를 확장할 수 없다는 의미이고, abstract는 상속을 통해서 완성되어야 한다는 의미이므로 서로 모순되기 때문
- abstract메서드의 접근 제어자가 private일 수 없음
- abstract메서드는 자손 클래스에서 구현해주어야 하는데, 접근 제어자가 private이면, 자손 클래스에서 접근할 수 없기 때문
- 메서드에 private와 final을 같이 사용할 필요는 없음
- 접근 제어자가 private인 메서드는 오버라이딩될 수 없기 때문
- 이 둘 중 하나만 사용해도 의미가 충분
다형성(polymorphism)
다형성 : 여러 가지 형태를 가질 수 있는 능력을 의미하며, 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 한 것
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인스턴스 멤버 중에서 Tv클래스에 정의 되지 않은 멤버, text와 caption()은 참조변수 t로 사용할 수 없음
조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있음
반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수 없음
Tv t = new CaptionTv(); //가능
CaptionTv c = new Tv(); //불가능
참조변수의 형변환
자손타입 -> 조상타입(Up-casting) : 형변환 생략 가능
자손타입 <- 조상타입(Down-casting) : 형변환 생략 불가
class Car {
string color;
int door;
void drive() {
System.out.println("drive, Brrrr~");
}
void stop() {
System.out.println("stop!!");
}
}
class FireEngin extends Car {
void water() {
System.out.println("water!!");
}
}
class Ambulance extends Car {
void siren() {
System.out.println("siren~~");
}
}
FireEngine f;
Ambulance a;
a = (Ambulance)f; //에러. 상속관계가 아닌 클래스간의 형변환 불가
f = (FireEngine)a; //에러. 상속관계가 아닌 클래스간의 형변환 불가
Car car = null;
FireEngine fe = new FireEngine();
FireEnginue fe2 = null;
car = fe; //car = (Car)fe에서 형변환 생략됨. 업 캐스팅
fe2 = (FireEngine)car; //형변환을 생략불가. 다운 캐스팅
instanceof 연산자
instanceof 연산자 : 객체가 특정 클래스에 속하는지 할 수 있음
주로 if문 안에 쓰이며 반환값은 true, false
참조변수 instanceof 타입
//ex
void doWork(Car c) {
if (c instanceof FireEngine) {
FireEngine fe = (FireEngine)c;
fe.water();
} else if (c instanceof Ambulance) {
Ambulance a = (Ambulance)c;
a.siren();
}
}
추상클래스(abstract class)
추상클래스 : 미완성 설계도에 비유할 수 있으며, 미완성 메서드를 가지고 있는 클래스
추상클래스 자체로는 클래스로서 역할을 다 못하지만, 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상클래스로서 중요한 의미를 가짐
추상메서드(abstract method)
추상메서드 : 선언부만 작성하고 구현부는 작성하지 않은 채로 남겨둔 것
메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 때문에 조상 클래스에서는 선언부만을 작성하고, 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려주고, 실제 내용은 상속받는 클래스에서 구현하도록 비워두는 것
//주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명
abstract 리턴타입 메서드이름();
추상클래스로부터 상속받는 자손 클래스는 오버라이딩을 통해 조상인 추상클래스의 추상메서드를 모두 구현해주어야 함
만일, 조상으로부터 상속받은 추상메서드 중 하나라도 구현하지 않는다면, 자손 클래스 역시 추상클래스로 지정해야 함
인터페이스(interface)
인터페이스 : 추상클래스처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서 추상클래스와 달리 몸통을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없음. 오직 추상메서드와 상수만을 멤버로 가질 수 있음
interface 인터페이스이름 {
public static final 타입 상수이름 = 값;
public abstract 메서드이름(매개변수목록);
}
- 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있음
- 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있음
인터페이스는 인터페이스로부터만 상속 받을 수 있으며, 클래스와는 달리 다중 상속이 가능
인터페이스의 장점
- 개발시간을 단축시킬 수 있음
- 표준화가 가능
- 서로 관계없는 클래스에게 관계를 맺어 줄 수 있음
- 독립적인 프로그래밍이 가능
인터페이스의 이해
- 클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있음
- 메서드를 사용(호출)하는 쪽(User)에서는 사용하려는 메서드(Provider)의 선언부만 알면 됨
class A {
void autoPlay(I i) {
i.play();
}
}
interface I {
public abstract void play();
}
class B implements I {
public void play() {
System.out.println("play in B class");
}
}
class C implements I {
public void play() {
System.out.println("play in C class");
}
}
class InterfaceTest2 {
public static void main(String[] args) {
A a = new A();
a.autoPlay(new B());
a.autoPlay(new C());
}
}
//결과
play in B class
play in C class
내부 클래스(inner class)
내부 클래스 : 클래스 내에 선언된 클래스
내부 클래스의 장점
- 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있음
- 코드의 복잡성을 줄일 수 있음(캡슐화)
내부 클래스의 종류와 특징
- 인스턴스 클래스(instance class)
- 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 인스턴스 멤버처럼 다루어짐
- 주로 외부 클래스의 인스턴스 멤버들과 관련되 직업에 사용될 목적으로 선언
- 스태틱 클래스(static class)
- 외부 클래스의 멤버변수 선언위치에 선언하며, 외부 클래스의 static 멤버처럼 다루어짐
- 주로 외부 클래스의 static 멤버, 특히 static 메서드에서 사용될 목적으로 선언
- 지역 클래스(local class)
- 외부 클래스의 메서드나 초기화블럭 안에 선언하며, 선언된 영역 내부에서만 사용될 수 있음
- 익명 클래스(anonymous class)
- 클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용)
익명 클래스(anonymous class)
이름이 없고, 클래스의 선언과 객체의 생성을 동시에 하기 때문에 단 한번만 사용될 수 있고, 오직 하나의 객체만을 생성할 수 있는 일회용 클래스
new 조상클래스이름() {
//멤버 선언
}
//또는
new 구현인터페이스이름() {
//멤버 선언
}
//ex
class InnerEx6 {
Object iv = new Object() { void method() {} }; //익명클래스
static Object cv = new Object() { void method() {} }; //익명클래스
void myMethod() {
Object lv = new Object() { void method() {} }; //익명클래스
}
}
'study > Java의 정석' 카테고리의 다른 글
[Java의 정석] Chapter 9 java.lang패키지와 유용한 클래스 (0) | 2023.03.22 |
---|---|
[Java의 정석] Chapter 8 예외처리 (0) | 2023.03.22 |
[Java의 정석] Chapter 6 객체지향 프로그래밍1 (0) | 2023.03.15 |
[Java의 정석] Chapter 5 배열 (0) | 2023.03.07 |
[Java의 정석] Chapter 4 조건문과 반복문 (0) | 2023.03.07 |