목표
자바의 패키지에 대해 학습하기
package 키워드
패키지란, 클래스의 묶음이다. 패키지에는 클래스 또는 인터페이스를 포함시킬 수 있으며, 서로 관련된 클래스들끼리 그룹 단위로 묶어 놓음으로써 클래스를 효율적으로 관리할 수 있다. 또, 클래스의 실제 이름 (full name)은 패키지명을 포함한다. 예를 들어 String 클래스의 실제 이름은 java.lang.String 이다. 만약 내가 동일한 String 클래스명을 가지는 클래스를 개발해도 패키지 이름이 다르면 사용이 가능하다.
클래스가 물리적으로 하나의 파일인 것과 같이 패키지는 물리적으로 하나의 디렉토리이다. 따라서 어떤 패키지에 속하는 클래스는 해당 패키지 디렉토리 안에 존재하는 파일이어야 한다.
디렉토리가 하위 디렉토리를 가질 수 있는 것 처럼, 패키지도 하위 패키지를 포함할 수 있으며 점 '.' 으로 구분한다. (java.lang에서 lang 패키지는 java 패키지의 하위 패키지이다.)
패키지는 소스파일의 첫 번째 문장으로 한 번만 선언할 수 있다.
package java.lang;
class String {
}
패키지명은 대소문자를 모두 사용할 수 있지만, 클래스와 분리하기 위해서 소문자만 사용한다.
모든 클래스는 하나의 패키지에 포함되어야 하지만, 패키지를 선언하지 않은 경우 '이름없는 패키지' 에 속하게 된다.
import 키워드
소스코드를 작성할 때, 다른 패키지의 클래스를 사용하려면 패키지명이 포함된 클래스 이름을 사용해야한다. 이 때, 모든 코드에서 패키지명을 포함한 클래스명을 사용하기엔 번거로움이 있기에 import문으로 사용하고자 하는 클래스의 패키지를 미리 명시해주면 소스코드에서는 패키지명을 생략할 수 있다. 컴파일러가 컴파일시에 import문을 보고 소스코드에 사용된 클래스들의 패키지를 알아낸 다음, 모든 클래스이름 앞에 패키지명을 붙여준다.
import 문은 pakage 문 다음에 , 그리고 클래스 선언문 이전에 위치해야 한다.
import 패키지명.클래스명;
import 패키지명.*;
import java.util.Calendar;
import java.util.Date;
import java.util.ArrayList;
import java.util.*;
import 문은 패키지명.클래스명으로 사용할 수 있지만 같은 패키지 안에 여러 클래스를 사용하는 경우 * 을 이용해 간단하게 표현할 수 있다. 이 때, 하위 패키지의 클래스까지 포함되는 것은 아니다. 또한, 같은 이름의 클래스를 다른 패키지에서 사용하고자 하는 경우, 어떤 패키지를 바라봐야할 지 찾을 수 없는 경우가 생길 수 있다.
예를 들어 com.study 패키지 하위에 A, B, C 클래스가 있고, com.live 하위에 C 클래스가 있을 때, 소스코드에서 com.study 의 A, B 클래스와 com.live의 C 클래스를 사용하고자 한다.
class Application {
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();
}
}
이 때, import 구문을 아래와 같이 작성하게 되면 C 클래스를 어느 패키지를 바라봐야 할 지 컴파일러가 찾을 수 없다.
import com.java.*;
import com.live.*;
class Application {
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();
}
}
이 때는 소스코드에 명시적으로 패키지명을 포함한 클래스명을 작성하거나,import 구분을 좀 더 상세히 나열하도록 수정해야한다.
import com.java.A;
import com.java.B;
import com.live.C;
class Application {
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();
}
}
import com.java.*;
class Application {
public static void main(String[] args) {
A a = new A();
B b = new B();
com.live.C c = new com.live.C();
}
}
. * 을 사용하면 컴파일러는 해당 패키지에서 일치하는 클래스이름을 찾아야 하는 수고를 더 해야 할 것이다. 컴파일 타임에서 조금 더 수고 스러울 뿐 런타임 시, 실제 실행 시 성능상의 차이는 전혀 없다.
클래스패스
클래스패스는 컴파일러나 JVM등이 클래스의 위치를 찾는데 사용되는 경로이다. 패키지를 추가하는 경우, 패키지의 루트를 클래스패스에 포함시켜야 해당 클래스를 찾아준다. 이 클래스패스를 지정하기 위한 방법에는 CLASSPATH 환경변수를 이용하는 것과 java runtime에 -classpath 옵션을 사용하는 2가지 방법이 있다.
CLASSPATH 환경변수
CLASSPATH 라는 환경변수를 통해 클래스패스를 관리할 수 있다. 다중으로 등록할 수 있으며 세미콜론 ';' 으로 구분한다. JVM 이 시작될 때, JVM의 클래스 로더는 이 환경 변수를 호출하여, 해당 디렉토리의 파일들을 먼저 JVM에 로드한다. 따라서 필수 클래스들이 위치한 디렉토리를 등록한다.
-classpath 옵션
java나 javac 등 java 실행파일이 실행될 때 -classpath (줄여서 -cp 로 표현가능) 옵션을 이용해, 환경변수에 등록되지 않은 디렉토리를 참조하도록 지정할 수 있다.
java -cp c:\jdk1.8\work com.study.live.A
접근지시자 (접근제어자)
멈버변수, 메소드, 생성자, 클래스에 사용되며 해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할을 한다. 접근 지시자가 지정되어 있지 않다면 default 임을 뜻한다. 주로 멤버에 접근 제어자를 사용하는 이유는 클래스의 내부에 선언된 데이터를 보호하기 위해서이다. 데이터가 유효한 값을 유지하고, 외부에서 함부로 변경하지 못 하도록 하는 것이며, 이를 캡슐화(encapsulation)이라 한다.
private
같은 클래스 내에서만 접근이 가능하다. 클래스에는 사용할 수 없다.
default
같은 패키지 내에서만 접근이 가능하다.
protected
같은 패키지 내에서, 그리고 해당 클래스를 상속한 하위 클래스에서 접근이 가능하다. 클래스에는 사용할 수 없다.
public
접근 제한이 전혀 없다.
생성자의 접근 제어자
생성자에 접근 제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있다. 생성자를 private으로 선언하면 외부에서는 인스턴스를 생성할 수 없지만 내부에서는 생성이 가능하다. 이 때 getInstance와 같은 public 메소드를 생성하여 내부의 객체를 반환하도록 할 수 있는데 아래와 같이 구현하면 Singleton 인스턴스를 사용하는 어느 곳에서도 Singleton 클래스 내부에서 생성한 1개의 인스턴스만을 참조하여 사용하게 된다. 이런 형태를 Singleton 디자인 패턴이라 하며, 인스턴스를 각각 생성하는게 불필요한 경우 자원의 재활용을 위한 목적으로 사용된다.
class Singleton {
private static Singleton s = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return s;
}
}
또, 싱글턴 패턴이 아니라 클래스 변수와 클래스 메소드만 외부에 제공하는 경우에도 객체 생성이 필요하지 않으므로 생성자를 감춰서 외부로부터 불필요한 접근을 막을 수 있다.
※ 참고
Java의 정석
최근 7년동안 자바 분야의 베스트 셀러 1위를 지켜온 '자바의 정석'의 최신판. 저자가 카페에서 12년간 직접 독자들에게 답변을 해오면서 초보자가 어려워하는 부분을 잘 파악하
m.yes24.com
스프링 입문을 위한 자바 객체 지향의 원리와 이해
자바 엔터프라이즈 개발을 편하게 해주는 오픈소스 경량 애플리케이션 프레임워크인 스프링은 자바와 객체 지향이라는 기반 위에 굳건히 세워져 있다. 따라서 스프링을 제대로
m.yes24.com