패키지(Package)
패키지는 파일 시스템 방식을 채택한 클래스 분류를 위한 도구입니다. 자바는 클래스 분류를 위해서 패키지라는 파일 경로를 사용합니다. 이때 파일 경로 내 디렉토리(파일)의 구분은 파일 구분자(\) 대신 도트(.)를 사용합니다.
package com.java.example;
패키지는 클래스 분류를 위한 목적 외에도 클래스의 유일성을 보장하는 식별자 역할도 수행합니다. 예를 들어, 동일한 이름의 클래스가 프로그램에 포함될 수 있으나 이 클래스는 서로 다른 패키지에 위치해야 합니다.
프로그램에서 클래스의 이름은 (패키지 경로+클래스 이름)으로 구성됩니다. 따라서 클래스 이름이 중복되더라도, 서로 다른 패키지에 위치하고 있다면 자바는 두 클래스를 서로 다른 클래스라고 인식합니다.
com.java.example.abc.SomeClass a = new com.java.example.abc.SomeClass();
com.java.example.def.SomeClass b = new com.java.example.def.SomeClass();
System.out.println(a.getClass().getName()); // com.java.example.abc.SomeClass
System.out.println(b.getClass().getName()); // com.java.example.def.SomeClass
사용 목적
패키지는 파일 시스템 방식을 채택하므로 클래스 분류에 탁월합니다. 우리가 사용하는 개인 PC에서 게임, 사진, 음악 등을 각각의 디렉토리에 분류하듯이, 프로그램에서 사용하고 편집하는 클래스 역시 패키지를 사용하여 분류하면 관리가 용이합니다. 작은 프로젝트라 할지라도 수십수백개의 클래스를 작성하게 되므로, 각 클래스의 목적과 성격에 따라서 그룹화하여 패키지에 분류하는 것은 관리 효율 향상에 기여합니다.
또한 서로 다른 부서 또는 사업체 간 협업, 오픈 API 사용 등에서도 패키지는 중요한 역할을 합니다. 각 작업 주체간에 패키지와 클래스 이름의 엄격한 구분은 사실상 불가능합니다. 새로운 클래스를 작성할 때마다 협력 관계자와 클래스 이름의 중복을 검사하기란 불가능하며, 다른 API를 불러와서 사용하는 경우 역시 마찬가지입니다.
하지만 패키지 이름에 대해서 작업 주체간 중복될 수 없는 규칙을 부여하면, 모든 작업물에 대한 클래스 이름(패키지 경로를 포함한)은 항상 유일하게 됩니다.
관례적으로 패키지 이름은 각 사업체의 도메인(Domain)을 포함하게 됩니다. 도메인은 등록 기관에서 유일한 이름을 사용하므로, 도메인을 사용한 패키지 역시 유일합니다.
com.comapny.projectname.classgroup
보통 패키지는 도메인 이름의 역순으로 작명합니다. 도메인 이름에 이어 프로젝트의 이름, 그리고 프로젝트 내에 클래스가 속하는 그룹이 포함됩니다. 이는 더 포괄적인 개념이 상위 디렉토리에 위치하게끔 하기 위해서입니다. 이처럼 패키지는 도메인 이름을 사용하여 여러 작업 주체(사업체)간의 클래스 이름의 중복을 사전에 방지하기 위한 목적이 있습니다.
패키지 선언(package)과 참조(import)
패키지는 클래스를 작성하는 소스 파일의 가장 맨 먼저 등장합니다. 아래 예시는, SomeClass가 프로젝트 소스 디렉토리 내에서 com\java\example\abc에 위치하고 있음을 보여줍니다. 물론 소스 파일은 실제로도 해당 패키지 경로에 위치해야 합니다. IDE에서 자바 소스 파일을 생성하는 방식으로 패키지를 생성 할 수 있습니다.
package com.java.example.abc;
public class SomeClass {
}
같은 패키지에 위치한 클래스 간에 상호 참조는 별도의 선행 작업이 필요 없습니다. 하지만 다른 패키지에 위치한 클래스를 참조하려고 하는 경우, 해당 클래스의 패키지를 지정해야만 합니다.
가장 쉽게 패키지를 지정하는 방법은 클래스의 풀 네임(Full-Name)을 사용하는 것입니다. 풀 네임은 (패캐지 경로+클래스 이름)으로 구성됩니다.
com.java.example.abc.SomeClass a = new com.java.example.abc.SomeClass();
하지만 클래스의 참조시마다 풀 네임을 사용해야 하므로, 프로그램 작성 시 불편함과 가독성이 떨어진다는 단점이 있습니다. 풀 네임을 단순히 클래스 이름만으로 사용하되, 패키지를 지정하기 위해서는 import 키워드를 사용 할 수 있습니다. 이때 import 구문은 package 구문에 이어서 바로 작성해야 합니다.
package com.java.example;
import com.java.example.abc.SomeClass;
public class Main {
public static void main(String[] _args) {
SomeClass a = new SomeClass();
}
}
import 키워드는 해당 소스 파일에서 참조하는 클래스의 패키지 위치를 결정합니다. 앞으로 작성되는 클래스 이름은 해당 패키지 경로를 내포하고 있음을 미리 선언하는 것입니다.
import [패키지 경로].[클래스 이름]
만약 특정 패키지의 모든 클래스를 참조하기 위해서는, 다음과 같이 패키지를 import 할 수 있습니다.
import [패키지 경로].*
import 구문은 하나의 소스 파일 내에서 제한 없이 사용 할 수 있습니다. 만약 참조하려는 패키지가 10개인 경우, 각 패키지의 import를 10번 선언 할 수 있습니다. 또한 import는 지정된 패키지만을 포함하며, 하위 패키지를 참조하려는 경우는 각각의 하위 패키지에 대해서도 별도로 import를 수행해야 합니다.
패키지 접근 제한자
접근 제한자에 따라서 패키지의 접근 가능 여부가 결정되기도 합니다. 접근 제한자에 대한 전체 내용은 다음 포스트를 참고하시길 바랍니다. 패키지 접근 가능 여부 측면에서만 접근 제한자를 분류하면, 다음과 같습니다.
- public
모든 패키지로부터의 접근 허용 - protected
동일한 패키지 내에서의 접근 허용, 다른 패키지로부터의 접근 차단 - default
동일한 패키지 내에서의 접근 가능, 다른 패키지로부터의 접근 차단 - private
패키지 접근 불가
예를 들어, public 외의 접근 제한자로 선언된 클래스는 다른 패키지에서 접근이 불가능합니다. 만약 해당 클래스에 대한 참조를 수행하는 경우 다음과 같은 컴파일 에러가 발생합니다. 이는 클래스에 대한 패키지를 import하고 있더라도 접근이 불가능합니다.
'com.java.example.abc.SomeClass' is not public in 'com.java.example.abc'. Cannot be accessed from outside package
클래스 뿐만 아니라, 클래스 내부에 선언된 필드와 메소드 등 역시 접근 제한자에 따라서 다른 패키지로부터의 접근 가능성을 결정합니다. 예를 들어, public으로 지정된 어떤 클래스에서 protected 이상의 엄격한 접근 제한자를 지정한 필드가 있다고 생각해봅시다. 이 클래스에 대한 참조는 public이므로 어떤 패키지에서든 가능하지만, 클래스가 갖는 필드는 protected이므로 접근이 불가능합니다.
'Java > Java SE, EE' 카테고리의 다른 글
[Java/Java SE, EE] 클래스 상속(Inheritance) (0) | 2022.02.20 |
---|---|
[Java/Java SE, EE] 어노테이션(Annotation)과 적용 대상(@Target), 유지 정책(@Retention) (0) | 2022.02.17 |
[Java/Java SE, EE] 상수(static final) 키워드, final은 상수가 아닌 이유 (0) | 2022.02.14 |
[Java/Java SE, EE] 불변 객체(Immutable object)와 방어 복사(Defensive copy) 구현 (0) | 2022.02.08 |
[Java/Java SE, EE] final 키워드 (0) | 2022.02.07 |