기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다.
적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 잇기 때문에 코드의 추가 및 변경이 매우 용이하다.
코드의 재사용성을 높이고 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여한다.
class 자손클래스 extends 조상클래스 {
// ...
}
조상 클래스: 부모(parent)클래스, 상위(super)클래스, 기반(base)클래스
자손 클래스: 자식(child)클래스, 하위(sub)클래스, 파생된(derived)클래스
상속관계(inheritance)
공통부분은 조상에서 관리하고 개별부분은 자손에서 관리한다.
조상의 변경은 자손에 영향을 미치지만, 자손의 변경은 조상에 아무런 영향을 미치지 않는다.
포함관계(composite)
한 클래스의 멤버변수로 다른 클래스를 선언하는 것
작은 단위의 클래스를 먼저 만들고, 이 들을 조합해서 하나의 커다란 클래스를 만든다.
다른 클래스로부터 상속 받지 않는 모든 클래스들은 자동적으로 Object클래스로부터 상속받게 한다.
CaptionTv → Tv → Object
class Tv {}
class captionTv extends Tv {}
class Tv extends Object {}
class captionTv extends Tv {}
조상클래스로부터 상속받은 메서드의 내용을 상속받는 클래스에 맞게 변경하는 것
선언부가 같아야 한다.(이름, 매개변수, 리턴타입)
접근제어자를 좁은 범위로 변경할 수 없다.
조상의 메서드가 protected라면, 범위가 같거나 넓은 protected나 public으로만 변경할 수 없다.
조상클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
📙 Obeject클래스를 제외한 모든 클래스의 생성자 첫 줄에 생성자, this() 또는 super()를 호출해야 한다.
그렇지 않으면 컴파일러가 자동적으로 ‘super();’를 생성자의 첫줄에 삽입한다.
왼쪽의 코드와 같이 작성했을 경우 컴파일러는 오른쪽 코드와 같이 실행한다.
class Point {
int x;
int y;
Point() {
this(0,0);
}
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Point extends Obejct {
int x;
int y;
Point() {
this(0,0);
}
Point(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
package 패키지명;
클래스패스는 컴파일러나 JVM등이 클래스의 위치를 찾는데 사용되는 경로이다.
[참고] java.exe의 ‘cp’옵션을 이용해서 일시적으로 클래스패스를 지정해 줄 수도 있다.
import 패키지명.클래스명;
// or
import 패키지명.*;import문을 사용하면 클래스의 패키지명을 생략할 수 있는 것과 같이 static import문을 사용하면 static멤버를 호출할 때 클래스 이름을 생략할 수 있다.
import static java.lang.Integer.*;
import static java.lang.Math.random;
import static java.lang.System.out;
// 위와같이 import문을 선언하였다면
System.out.println(Math.random());
// 위의코드를 다음과 같이 간략히 할 수 있다.
out.println(random());
static이 사용될 수 있는 곳은 멤버변수, 메서드, 초기화 블럭이다.
| 제어자 | 대상 | 의미 |
|---|---|---|
| static | 멤버변수 | 모든 인스턴스에 공통적으로 사용되는 클래스변수가 된다. |
클래스변수는 인스턴스를 생성하지 않고도 사용 가능하다.
클래스가 메모리에 로드될 때 생성된다. |
| | 메서드 | 인스턴스를 생성하지 않고도 호출이 가능한 static메서드가 된다.
static메서드 내에서는 인스턴스멤버들을 직접 사용할 수 없다. |
final이 사용될 수 있는 곳은 클래스, 메서드, 멤버변수, 지역변수이다.
| 제어자 | 대상 | 의미 |
|---|---|---|
| final | 클래스 | 변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다. |
| 그래서 final로 지정된 클래스는 다른 클래스의 조상이 될 수 없다. | ||
| 메서드 | 변경될 수 없는 메서드, final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없다. | |
| 멤버변수, 지역변수 | 변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다. |
final이 붙은 변수는 상수이므로 보통은 선언과 초기화를 동시에 하지만, 인스턴스변수의 경우 생성자에서 초기화 할 수 있다.
abstract가 사용될 수 있는 곳은 클래스, 메서드이다.
| 제어자 | 대상 | 의미 |
|---|---|---|
| abstract | 클래스 | 클래스 내에 추상메서드가 선언되어 있음을 의미한다. |
| 메서드 | 선언부만 작성하고 구현부는 작성하지 않은 추상메서드임을 알린다. |
[참고] 추상메서드가 없는 클래스도 abstract를 붙여서 추상클래스로 선언하는 것이 가능하나 그렇게 해야 할 이유는 없다.
접근제어자가 사용될 수 있는 곳은 클래스, 멤버변수, 메서드, 생성자이다.
멤버 또는 클래스에 사용되어, 외부로부터의 접근을 제한한다.
| 제어자 | 같은 클래스 | 같은 패키지 | 자손클래스 | 전체 |
|---|---|---|---|---|
| public | ✅ | ✅ | ✅ | ✅ |
| protected | ✅ | ✅ | ✅ | |
| default | ✅ | ✅ | ||
| private | ✅ |
클래스에는 public과 default만 사용할 수 있고, 멤버변수와 메서드에는 접근제어자 4개를 모두 사용할 수 있다.
default제어자는 따로 사용하지 않는다. 어떤 것도 사용하지 않으면 default이기 때문이다.
접근제어자를 사용하는 이유
| 대상 | 사용가능한 접근 제어자 |
|---|---|
| 클래스 | public, (default) |
| 메서드 | public, protected, default, private |
| 멤버변수 | public, protected, default, private |
| 지역변수 | 없음 |
보통 멤버변수의 값을 읽는 메서드의 이름을 ‘get멤버변수이름’으로 하고, 멤버변수의 값을 변경하는 메서드의 이름을 ‘set멤버변수이름'으로 한다. (암묵적인 규칙)
get으로 시작하는 메서드를 ‘겟터(getter)’, set으로 시작하는 메서드를 ‘셋터(setter)’라고 부른다.
[참고] 하나의 소스파일(.java)에는 public클래스가 단 하나만 존재할 수 있으며, 소스파일의 이름은 반드시 public클래스의 이름과 같아야 한다.
캡슐화란?
접근제어자를 이용해서 멤버변수들은 privite로 내부에 감추고 메서드들은 public으로 외부에 노출시킴으로써 같은 클래스에 정의된 멤버들끼리는 서로 자유롭게 접근하고, 외부에서는 메서드를 통해서만 멤버변수에 접근할 수 있는 구조로 만드는 것.
직접 객체의 멤버변수에 접근할 수 없으며 메서드를 통해서만 멤버변수에 접근할 수 있다.
외부에서는 객체의 노출된 public 메서드만 호출할 수 있을 뿐 실제 내부가 어떻게 되어있는지 알 수없다.
상속을 통해 확장될 것이 예상되는 클래스라면 멤버에 접근 제한을 주되 자손클래스에서 접근하는 것이 가능하도록 하기 위해 private대신 protected를 사용한다.
| 대상 | 사용가능한 제어자 |
|---|---|
| 클래스 | public,(default), final, abstract |
| 메서드 | 모든 접근 제어자, final, abstract, static |
| 멤버변수 | 모든 접근 제어자, final, static |
| 지역변수 | final |
메서드에 static과 abstract를 함께 사용할 수 없다.
static메서드는 몸통(구현부)이 있는 메서드에만 사용할 수 있기 때문이다.
클래스에 abstract와 final을 동시에 사용할 수 없다.
클래스에 사용되는 final은 클래스를 확장할 수 없다는 의미이고, abstract는 상속을 통해서 완성되어야 한다는 의미이므로 서로 모둔되기 때문이다.
abstract메서드의 접근제어자가 private일 수 없다.
abstract메서드는 자손클래스에서 구현해주어야 하는데 접근 제어자가 private이면, 자손클래스에서 접근할 수 없기 때문이다.
메서드에 private과 final을 같이 사용할 필요는 없다.
접근 제어자가 private인 메서드는 오버라이딩될 수 없기 때문이다. 이 둘 중 하나만 사용해도 의미가 충분하다.
⭐️다형성은 중요하니 잘 이해하고 넘어가야한다.
클래스가 서로 상속관계에 있을 경우, 조상클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하도록 하는 것도 가능하다.
조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있다.
반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.
인스턴스를 같은 타입의 참조변수로 참조하는것과 조상타입의 참조변수로 참조하는 것의 차이
조상타입의 참조변수로 참조하면 인스턴스 중에서 조상클래스의 멤버들만 사용할 수 있으며, 조상클래스에 정의 되지 않는 멤버는 사용이 불가능하다.
즉, 둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.
반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 불가능하다.
실제 인스턴스의 멤버 개수보다 자손타입의 인스턴스의 멤버개수가 더 많기 때문이다.
자손타입 → 조상타입 (Up-casting): 형변환 생략가능
조상타입 → 자손타입 (Down-casting): 형변환 생략불가
참조변수의 형변환은 참조할 수 있는 멤버의 갯수가 달라질 뿐 다른 변화는 없다.