수학과의 좌충우돌 프로그래밍

[JAVA] 04. 객체지향 프로그래밍 (2) 본문

프로그래밍 언어/Java

[JAVA] 04. 객체지향 프로그래밍 (2)

ssung.k 2020. 12. 27. 22:06

1. 상속(inheritance)

자바에서 상속은 extends라는 키워드를 통해 가능합니다.

class Child extends Parent {
}

 

이 때 멤버만 상속되므로 생성자와 초기화 블럭은 상속되지 않습니다.

 

클래스간의 관계

상속 이외에도 클래스를 재사용하는 또 다른 방법이 존재합니다.

클래스 간에 포함(Composite) 관계를 맺어주는 것입니다.

클래스 간의 포함관계를 맺어주는 것은 한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것을 의미합니다.

 

아래와 같이 Circle과 Point 클래스가 있다고 가정합시다.

class Circle{
	int x;
	int y;
	int r;
}

class Point{
	int x;
	int y;
}

 

Point 클래스를 재사용하여 Circle을 재정의하면 다음과 같이 할 수 있습니다.

class Circle{
	Point c = new Point();
	int r;
}

 

이와 같이 단위클래스 별로 코드를 작게 나누면 코드를 관리하기 수월해집니다.

 

그렇다면 상속관계와 포함관계는 어떻게 구분해야할까?

아래와 같은 두 문장으로 구분이 가능합니다.

상속관계 : ~은 ~이다. (is-a)
포함관계 : ~은 ~을 가지고 있다. (has-a)

 

위 원과 점을 생각해보면,

원은 점이다 라는 문장보다 원은 점을 가지고 있다 라는 문장이 훨씬 더 자연스럽습니다.

 

단일상속

자바는 C++이나 python과 같은 다른 객체지향언어와 다르게 다중상속을 허용하지 않습니다.

 

Object 클래스

Object 클래스는 모든 클래스의 최상위에 있는 조상클래스입니다.

다른 클래스의 상속을 받지않은 모든 클래스는 Object 클래스를 상속받게 됩니다.

그렇기 때문에 우리가 아무렇지 않게 써왔던 toString(), equals() 와 같은 메서드를 쓸 수 있습니다.

 

 

2. 오버라이딩(overriding)

조상 클래스로 부터 상속받은 메서드의 내용을 변경하는 것을 오버라이딩이라고 합니다.

아래는 오버라이딩의 예시입니다.

class Point{
	int x;
	int y;
	
	String getLocation() {
		return "x " + "y"; 
	}
}

class Point3D extends Point {
	int z;
	
	String getLocation() {
		return "x " + "y "+"z"; 
	}
}

 

오버라이딩의 조건

오버라이딩을 하기 위해서는 아래와 같은 조건이 필요합니다.

  1. 메서드의 선언부는 조상의 것과 완벽하게 일치
  2. 자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와 이름, 매개변수, 반환타입이 완벽하게 같아야합니다.
  3. 접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경 불가
  4. 조상 클래스의 메서드보다 많은 수의 예외를 선언 불가
    class Parent {
    	void parentMethod() throws IOException, SQLException {
    		
    	}
    }
    
    class Child extends Parent {
    	void parentMethod() throws IOException {
    		
    	}
    }
    

    아래 경우를 봅시다.
    Exception은 모든 예외에 대한 조상이므로 문제가 발생합니다.
    class Child extends Parent { 
    	    void parentMethod() throws Exception {
           	} 
    }

 

super

super는 자손클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수입니다.

멤버변수와 지역변수의 이름이 같을 때 this를 붙여서 구별했듯이 상속받은 멤버와 자신의 멤버와 이름이 같을 때는 super를 붙여서 구별할 수 있습니다.

super 역시 부모클래스의 인스턴스에 대한 개념이 때문에 인스턴스와 관계없는 클래스메서드(static이 붙은)에서는 사용할 수 없습니다.

 

super를 사용하는 예시를 봅시다.

super.x 일 경우에는 조상의 값을 참조하여 20을 출력합니다.

import java.io.IOException;
import java.sql.SQLException;

public class HelloWorld {

	public static void main(String[] args) {
		Child c = new Child();
		c.method();
	}
}

class Parent {
	int x = 10;
}

class Child extends Parent {
	int x = 20;
	
	void method () {
		System.out.println(x);
    // 20
		System.out.println(this.x);
    // 20
		System.out.println(super.x);
    // 10
	}
}

 

변수 뿐만 아니라 메서드도 super와 함께 사용가능합니다.

 

 

super()

this()와 마찬가지로 super()도 생성자입니다.

하지만 this()는 같은 클래스의 다른 생성자를 호출하는데 사용된다면, super()는 조상 클래스의 생성자를 호출하는데 사용됩니다.

조상 클래스의 멤버의 초기화 작업이 수행되어야 하기 때문에 자손 클래스의 생성자 첫 줄에서 조상 클래스의 생성자가 호출되어야 합니다.

첫 줄에서 호출해야 하는 이유는 자손 클래스의 멤버가 조상 클래스의 멤버를 사용할 수도 있으므로 조상의 멤버들이 먼저 초기화 되어 있어야 하기 때문입니다.

 

 

3. package와 import

패키지(package)

패키지란 클래스의 묶음입니다.

패키지에는 클래스 또는 인터페이스를 포함시킬 수 있으며 서로 관련된 클래스들끼리 그룹 단위로 묶어 놓은으로써 클래스를 효율적으로 관리할 수 있습니다.

같은 이름의 클래스가 서로 다른 패키지에 존재하는 것이 가능하므로 자신만의 패키지 체계를 유지함으로써 다른 개발자가 개발한 클래스 라이브러리의 클래스와 이름이 충돌하는 것을 피할 수 있습니다.

지금까지는 단순히 클래스 이름으로만 클래스를 구분했지만 사실 클래스의 실제 이름은 패키지명을 포함한 것입니다.

예를 들면 String클래스의 실제 이름은 java.lang.String입니다.

클래스가 물리적으로 하나의 클래스파일인 것과 같이 패키지는 물리적으로 하나의 디렉토리입니다.

디렉토리가 하위 디렉토리를 가질 수 있는 것처럼, 패키지도 다른 패키지를 포함할 수 있으며 . 으로 구분합니다.

 

패키지의 선언

클래스나 인터페이스 소스파일 맨 위에 아래와 같이 기재함으로서 패키지를 선언할 수 있습니다.

package 패키지명;

 

이는 주석과 공백을 제외하고 무조건 첫 번째 문장이어야 합니다.

모든 클래스는 하나의 패키지에 포함되어야 하며 패키지를 기재하지 않을 경우에는 자바에서 기본으로 제공하는 이름없는 패키지로 속하게 됩니다.

 

 

import문

소스코드를 작성할 때 다른 패키지의 클래스를 사용하려면 패키지명이 포함된 클래스 이름을 사용해야 합니다.

import문으로 사용하고자 하는 클래스의 패키지를 미리 명시해주면 소스코드에서 사용되는 클래스이름에서 패키지명은 생략할 수 있습니다.

여러 개의 클래스가 사용 될 때는 *를 사용하여 나타낼 수 있지만 이는 하위 패키지까지 모두 포함하는 것은 아닙니다.

 

따라서 위 두 문장을 아래 문장으로 대체할 수 없습니다.

import java.util.*;
import java.text.*;

import java.*;

 

static import문

import문을 사용하여 클래스의 패키지명을 생략할 수 있는 것과 같이 static import문을 사용하면 static멤버를 호출할 때 클래스 이름을 생략할 수 있습니다.

import static java.lang.System.out;

System.out.println(x); // 대신에
out.println(x); // 다음과 같이 사용할 수 있습니다.

 

 

4. 제어자(modifier)

제어자는 클래스, 변수 또는 메서드의 선언부에 함께 사용되어 부가적인 의미를 부여합니다.

제어자의 종류는 크게 접근 제어자와 그 외의 제어자르 나눌 수 있습니다.

접근제어자 : public, protected, default, private
그 외 : static, final, abstract, native, transient, synchronized, volatile, strictfp

 

하나의 대상에 대해서 여러 제어자를 조합하여 사용하는 것이 가능하지만 접근제어자는 하나만 선택해서 사용이 가능합니다.

 

static

'클래스' 또는 '공통적인'의 의미를 가지고 있으며 앞에서도 많이 접해보았습니다.

static이 사용될 수 있는 곳은 멤버변수, 메서드, 초기화 블럭이 있습니다.

class StaticTest {
	static int x = 200; // 클래스 변수
	static {} // 클래스 초기화 블럭
	static int max(int a, int b) { // 클래스 메서드
		return a > b ? a : b;
	}
}

 

final

'마지막의', '변경될 수 없는'의 의미를 가지고 있으며 거의 모든 대상에 사용 될 수 있습니다.

final이 사용될 수 있는 곳은 클래스, 멤버변수, 메서드, 지역변수가 있습니다.

final class FinalTest { // 조상이 될 수 없는 클래스
	final int MAX_SIZE = 10; // 값을 변경할 수 없는 변수
	final int getMaxSize() { // 오버라이딩 할 수 없는 메서드
		return MAX_SIZE;
	}
}

 

abstract

'미완성의'의 의미를 가지고 있으며 메서드의 선언부만 작성부만 작성하고 실제 수행내용은 구현하지 않은 추상 메서드를 선언하는데 사용됩니다.

abstract가 사용될 수 있는 곳은 클래스와 메서드입니다.

클래스 앞에 붙으면 추상클래스가 되어 인스턴스를 생성할 수 없게 됩니다.

abstract class AbstractTest {
	abstract void move();
}

 

 

접근 제어자

접근 제어자는 멤버 또는 클래스에 사용되어, 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 합니다.

접근 제어자를 사용할 수 있는 곳은 클래스, 멤버변수, 메서드, 생성자가 있습니다.

단 클래스에는 public, default만 사용가능합니다.

private : 같은 클래스 내에서만 접근 가능
default : 같은 패키지 내에서만 접근 가능
protected : 같은 패키지 내에서 그리고 다른 패키지의 자손클래스에서 접근 가능
public : 접근 제한 X

 

위를 표로 정리하면 다음과 같습니다. (O : 접근가능)

제어자 같은 클래스 같은 패키지 자손클래스 전체
public O O O O
protected O O O  
default O O    
private O      

 

아무런 접근 제어자를 기재하지 않을 시 default 입니다.

 

제어자의 조합

제어자를 조합할 때 몇 가지 주의사항이 있습니다.

  • 메서드에 static과 abstract를 함께 사용할 수 없습니다.
  • static 메서드는 몸통이 있는 메서드에만 사용할 수 있습니다.
  • 클래스에 abstract와 final을 동시에 사용할 수 없습니다.
  • final은 클래스를 확장할 수 없다는 의미고 abstract는 상속을 통해서 완성되어야 한다는 의미이므로 모순입니다.
  • abstract 메서드의 접근 제어자가 private일 수 없습니다.
  • abstract메서드는 자손 클래스가 구현해주어야 하는데 접근 제어자가 private면 자손 클래스에 접근할 수 없기 때문에 모순입니다.
  • 메서드에 private와 final을 같이 사용할 필요는 없습니다.
  • 접근 제어자가 private인 메서드는 오버라이딩될 수 없기 때문에 둘 중 하나만 사용해도 의미가 충분합니다.

'프로그래밍 언어 > Java' 카테고리의 다른 글

[JAVA] 06. 예외처리  (0) 2021.01.07
[JAVA] 05. 객체지향 프로그래밍 (3)  (0) 2020.12.27
[JAVA] 03. 객체지향 프로그래밍 (1)  (0) 2020.12.27
[JAVA] 02. 변수  (0) 2020.11.27
[JAVA] 01. 자바를 시작하기 전에  (2) 2020.11.27
Comments