상속(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() {} }; //익명클래스
    }
}

 

복사했습니다!