클래스의 관계는 크게 다음 두가지로 나뉠 수 있다.
상속(is - a)
상속 관계 : '~은 ~이다.(is-a)'
포함(has - a)
포함 관계 : '~은 ~을 가지고 있다.(has-a)'
상속은 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다. 상속을 통해서 클래스를 작성하면 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 있기 때문에 코드의 추가 및 변경이 매우 용이하다. 즉 다음과 같은 특징을 통해 프로그램의 생산성과 유지보수에 크게 기여한다.
자바에서는 단일 상속만을 허용한다. 이러한 이유는 다중상속을 하면 여러 클래스로 부터 상속을 받을 수 있기 때문에 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있지만 관계가 매우 복잡해지기 깨문에 자바는 단일 상속만을 허용한다.
많은 클래스가 존재하게 된다면 이름은 동일하지만 내용이 달라 충돌이 발생할 경우가 생길 수 있다. 프로그램이 크고 상속관계가 복잡하게 되면 이러한 관계를 찾기 쉽지 않을 수 있다.
오버라이딩은 조상클래스로부터 상속받은 메서드의 내용을 상속받는 클래스에 맞게 변경하는 것을 이야기 한다.
즉, 클래스에 맞게 변경하는 것을 말한다.
오버라이딩시 반드시 지켜야 할 조건
오버라이딩 변경시 제한 조건
접근 제어자는 조상 클래스의 메서드 보다 좁은 범위로 변경할 수 없다.
조상 클래스 보다 많은 수의 예외를 선언할 수 없다.
오버로딩(overloading) 기존에 없는 새로운 메서드를 정의하는 것(new)
오버라이딩(overriding) 상속받은 메서드의 내용을 변경하는 것(change, modify)
포함 관계란 한 클래스의 멤버 변수로 다른 클래스 타입의 참조변수를 선언해주는 것이다.
이와 같은 방식은 하나의 거대한 클래스를 작성하는 것보다 단위별로 여러 개의 클래스를 작성한 다음, 이 단위 클래스들을 포함관계로 재사용하면 보다 간결하고 손쉽게 프로그램을 작성하고 재사용할 수 있다.
참조변수 super는 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는 데 사용되는 참조변수이다.
class Parent{ int x = 10 ; /* super */}
class Child extends Parent{
int x = 20; // this.x
void method() {
System.out.println("x = " + x);
System.out.println("this.xx = " + this.x);
System.out.println("super.x = " + super.x);
/*
* result
* x =20
* this.x = 20
* super.x =10
* */
}
}
this()처럼 super()도 생성자이다. this()는 같은 클래스의 다른 생성자를 호출하는 데 사용되는데, super는 조상의 생성자를 사용된다.
class Point{
int x, y;
public Point(int x, int y) {
//compile 시 자동으로 super() 호출됨
this.x = x;
this.y = y;
}
}
class Point3D extends Point {
int z;
public Point3D(int x, int y, int z) {
//생성자 첫 출에는 반드시 생성자를 호출한다.
super(x, y); // 조상의 클래스의 생성자 Point를 호출
this.z = z;
}
}
클래스, 변수, 메성드의 선언부에 사용 부가적인 의미를 부여
제어자는 크게 접근 제어자와 그 외 제어자로 나뉨
하나의 대상에 여러 개의 제어자를 조합해서 사용할 수 있으나, 접근 제어자는 단 하나만 사용할 수 있다.
접근 제어자는 멤버 또는 클래스에 사용되어 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다.
즉, 접근 제어자를 사용하는 이유는 캡슐화와 관련이 있다.
일반적으로 생성자의 접근 제어자는 클래스의 접근 제어자와 일치한다.
생성자에 접근 제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있다.
final class Singleton{
private static Singleton s = new Singleton();
private Singleton(){
}
public static Singleton getInstance() {
if (s == null) {
s = new Singleton();
}
return s;
}
}
class SingltonTest {
public static void main(String[] args) {
// Singleton s = new Singleton(); //error!
Singleton s1 = Singleton.getInstance();
}
}
멤버변수
메서드
final이 붙은 변수는 상수이므로 보통은 선언과 초기화를 동시에 하지만, 인스턴스 마다 고정값을 갖는 인스턴스 변수의 경우 생성자에서 초기화한다.
class Card{
final int num;
final String kind;
public Card(int num, String kind) {
this.num = num;
this.kind = kind;
}
}
참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 사용, true, false를 반환하는 데 true를 반환하면 참조변수 변환 가능하다는 것
왼쪽에는 참조변수를 오른쪽에는 타입
완전히 같거나
자손 타입이거나
형변환 전 반드시 체크하고 변환하는 것이 좋다.
멤버 변수가 중복 정의된 경우 참조 변수 타입에 따라 연결되는 멤버변수가 달라진다.
메서드가 중복 정의된 경우 참조변수 타입에 관계없이 항상 실제 인스턴스 타입에 정의된 메서드가 호출된다.
class Parent{
int x = 10;
void method() {
System.out.println("Parent method");
}
}
class Child extends Parent{
int x = 20; // this.x
void method() {
System.out.println("Child method");
}
}
class Main{
public static void main(String[] args) {
Parent p = new Child();
Child c = new Child();
System.out.println("p.x = " + p.x);
p.method();
System.out.println("c.x = " + c.x);
c.method();
}
}
/*
* *** result ***
* p.x = 10
* Child
* c.x = 20
* Child
* */
참고문헌
자바의 정석