Java의 패키지 및 정적 가져 오기

이전 Java 101 튜토리얼에서 참조 유형 (클래스 및 인터페이스라고도 함)을 다른 참조 유형 및 블록의 멤버로 선언하여 코드를 더 잘 구성하는 방법을 배웠습니다. 또한 중첩 된 참조 유형과 동일한 이름을 공유하는 최상위 참조 유형 간의 이름 충돌을 피하기 위해 중첩을 사용하는 방법을 보여주었습니다.

중첩과 함께 Java는 패키지를 사용하여 최상위 참조 유형에서 동일한 이름 문제를 해결합니다. 정적 가져 오기를 사용하면 패키지 된 최상위 참조 유형의 정적 멤버에 대한 액세스도 단순화됩니다. 정적 가져 오기는 코드에서 이러한 멤버에 액세스 할 때 키 입력을 절약하지만 사용할 때주의해야 할 몇 가지 사항이 있습니다. 이 튜토리얼에서는 Java 프로그램에서 패키지 및 정적 가져 오기를 사용하는 방법을 소개합니다.

다운로드 코드 가져 오기이 Java 자습서에서 예제 애플리케이션의 소스 코드를 다운로드합니다. JavaWorld를 위해 Jeff Friesen이 만들었습니다.

패키징 참조 유형

Java 개발자는 관련 클래스와 인터페이스를 패키지로 그룹화합니다. 패키지를 사용하면 참조 유형을보다 쉽게 ​​찾고 사용할 수 있으며, 동일한 이름의 유형 간의 이름 충돌을 방지하고 유형에 대한 액세스를 제어 할 수 있습니다.

이 섹션에서는 패키지에 대해 알아 봅니다. 패키지가 무엇인지 확인하고, packageimport문에 대해 배우고 , 보호 된 액세스, JAR 파일 및 유형 검색에 대한 추가 주제를 탐색합니다.

Java의 패키지는 무엇입니까?

소프트웨어 개발에서는 일반적으로 계층 적 관계에 따라 항목을 구성합니다. 예를 들어, 이전 튜토리얼에서 클래스를 다른 클래스의 멤버로 선언하는 방법을 보여주었습니다. 파일 시스템을 사용하여 다른 디렉토리에 디렉토리를 중첩 할 수도 있습니다.

이러한 계층 구조를 사용하면 이름 충돌을 방지 할 수 있습니다. 예를 들어, 비 계층 적 파일 시스템 (단일 디렉토리)에서는 여러 파일에 동일한 이름을 할당 할 수 없습니다. 반대로 계층 적 파일 시스템에서는 동일한 이름의 파일이 다른 디렉토리에 존재할 수 있습니다. 마찬가지로 두 개의 둘러싸는 클래스는 동일한 이름의 중첩 클래스를 포함 할 수 있습니다. 항목이 다른 네임 스페이스로 분할되기 때문에 이름 충돌이 존재하지 않습니다.

Java를 사용하면 최상위 (중첩되지 않은) 참조 유형을 여러 네임 스페이스로 분할하여 이러한 유형을 더 잘 구성하고 이름 충돌을 방지 할 수 있습니다. Java에서는 패키지 언어 기능을 사용하여 최상위 참조 유형을 여러 네임 스페이스로 분할합니다. 이 경우 패키지 는 참조 유형을 저장하기위한 고유 한 네임 스페이스입니다. 패키지는 클래스와 인터페이스는 물론 다른 패키지 내에 중첩 된 패키지 인 하위 패키지를 저장할 수 있습니다 .

패키지에는 이름이 있으며 예약되지 않은 식별자 여야합니다. 예 : java. 멤버 액세스 연산자 ( .)는 패키지 이름을 하위 패키지 이름에서 분리하고 패키지 또는 하위 패키지 이름을 유형 이름과 분리합니다. 예를 들어,의 두 멤버 액세스 연산자 java.lang.System별도의 패키지 이름 java로부터 lang서브 패키지 이름과 별도의 서브 패키지 이름 lang로부터 System유형 이름.

참조 유형은 public패키지 외부에서 액세스 할 수 있도록 선언되어야 합니다. 액세스 가능해야하는 모든 상수, 생성자, 메서드 또는 중첩 형식에도 동일하게 적용됩니다. 자습서의 뒷부분에서 이러한 예제를 볼 수 있습니다.

패키지 명세서

Java에서는 package 문 을 사용하여 패키지 를 만듭니다. 이 문은 소스 파일의 맨 위에 나타나며 소스 파일 유형이 속한 패키지를 식별합니다. 다음 구문을 따라야합니다.

 package identifier[.identifier]*; 

패키지 명령문은 예약어로 시작 package하고 식별자로 계속되며 선택적으로 마침표로 구분 된 식별자 시퀀스가 ​​뒤 따릅니다. 세미콜론 ( ;)은이 문을 종료합니다.

첫 번째 (가장 왼쪽) 식별자는 패키지 이름을 지정하고 이후의 각 식별자는 하위 패키지 이름을 지정합니다. 예를 들어 package a.b;에서 소스 파일에 선언 된 모든 유형은 b패키지의 하위 패키지에 속합니다 a.

패키지 / 서브 패키지 명명 규칙

관례 적으로 패키지 또는 하위 패키지 이름을 소문자로 표현합니다. 이름이 여러 단어로 구성된 경우 첫 번째 단어를 제외하고 각 단어를 대문자로 표시 할 수 있습니다. 예 : generalLedger.

컴파일 문제를 방지하려면 패키지 이름 시퀀스가 ​​고유해야합니다. 예를 들어, 두 개의 서로 다른 graphics패키지 를 만들고 각 graphics패키지 Triangle에 서로 다른 인터페이스를 가진 클래스가 포함되어 있다고 가정합니다 . Java 컴파일러가 아래와 같은 것을 발견하면 Triangle(int, int, int, int)생성자가 존재 하는지 확인해야 합니다.

 Triangle t = new Triangle(1, 20, 30, 40); 

삼각형 경계 상자

Triangle생성자는 삼각형을 그릴 경계 상자를 지정하는 것으로 생각하십시오 . 처음 두 매개 변수는 상자의 왼쪽 위 모서리를 식별하고 두 번째 두 매개 변수는 상자의 범위를 정의합니다.

컴파일러는 클래스 graphics가 포함 된 패키지를 찾을 때까지 액세스 가능한 모든 패키지를 검색합니다 Triangle. 발견 된 패키지 TriangleTriangle(int, int, int, int)생성자가 있는 적절한 클래스가 포함되어 있으면 모든 것이 정상입니다. 그렇지 Triangle않고 발견 된 클래스에 Triangle(int, int, int, int)생성자 가 없으면 컴파일러는 오류를보고합니다. (이 튜토리얼의 뒷부분에서 검색 알고리즘에 대해 자세히 설명하겠습니다.)

이 시나리오는 고유 한 패키지 이름 시퀀스 선택의 중요성을 보여줍니다. 고유 한 이름 시퀀스를 선택하는 규칙은 인터넷 도메인 이름을 바꾸고 시퀀스의 접두사로 사용하는 것입니다. 예를 들어, 내 도메인 이름 ca.javajeff이므로 접두사로 선택합니다 javajeff.ca. 그런 다음 ca.javajeff.graphics.Triangle액세스하도록 지정 합니다 Triangle.

도메인 이름 구성 요소 및 유효한 패키지 이름

도메인 이름 구성 요소가 항상 유효한 패키지 이름은 아닙니다. 하나 이상의 구성 요소 이름이 숫자 ( 3D.com)로 시작 -하거나 하이픈 ( ) 또는 다른 잘못된 문자 ( ab-z.com)를 포함하거나 Java의 예약어 ( short.com) 중 하나 일 수 있습니다 . 규칙에 따라 숫자 앞에 밑줄 ( com._3D)을 붙이고, 잘못된 문자를 밑줄 ( )로 바꾸고 com.ab_z, 예약어 앞에 밑줄 ( )을 붙이 도록합니다 com.short_.

package 문에서 추가 문제를 방지하려면 몇 가지 규칙을 따라야합니다.

  1. 소스 파일에서 하나의 패키지 문만 선언 할 수 있습니다.
  2. 패키지 문 앞에 주석을 제외하고는 아무것도 올 수 없습니다.

두 번째 규칙의 특별한 경우 인 첫 번째 규칙은 여러 패키지에 참조 유형을 저장하는 것이 타당하지 않기 때문에 존재합니다. 패키지는 여러 유형을 저장할 수 있지만 유형은 하나의 패키지에만 속할 수 있습니다.

소스 파일이 패키지 문을 선언하지 않으면 소스 파일의 유형이 이름지정되지 않은 패키지 에 속한다고 합니다 . 중요하지 않은 참조 유형은 일반적으로 자체 패키지에 저장되며 이름없는 패키지는 피합니다.

Java implementations map package and subpackage names to same-named directories. For example, an implementation would map graphics to a directory named graphics. In the case of the package a.b, the first letter, a would map to a directory named a and b would map to a b subdirectory of a. The compiler stores the class files that implement the package's types in the corresponding directory. Note that the unnamed package corresponds to the current directory.

Example: Packaging an audio library in Java

A practical example is helpful for fully grasping the package statement. In this section I demonstrate packages in the context of an audio library that lets you read audio files and obtain audio data. For brevity, I'll only present a skeletal version of the library.

The audio library currently consists of only two classes: Audio and WavReader. Audio describes an audio clip and is the library's main class. Listing 1 presents its source code.

Listing 1. Package statement example (Audio.java)

 package ca.javajeff.audio; public final class Audio { private int[] samples; private int sampleRate; Audio(int[] samples, int sampleRate) { this.samples = samples; this.sampleRate = sampleRate; } public int[] getSamples() { return samples; } public int getSampleRate() { return sampleRate; } public static Audio newAudio(String filename) { if (filename.toLowerCase().endsWith(".wav")) return WavReader.read(filename); else return null; // unsupported format } } 

Let's go through Listing 1 step by step.

  • The Audio.java file in Listing 1 stores the Audio class. This listing begins with a package statement that identifies ca.javajeff.audio as the class's package.
  • Audio is declared public so that it can be referenced from outside of its package. Also, it's declared final so that it cannot be extended (meaning, subclassed).
  • Audio declares privatesamples and sampleRate fields to store audio data. These fields are initialized to the values passed to Audio's constructor.
  • Audio's constructor is declared package-private (meaning, the constructor isn't declared public, private, or protected) so that this class cannot be instantiated from outside of its package.
  • Audio presents getSamples() and getSampleRate() methods for returning an audio clip's samples and sample rate. Each method is declared public so that it can be called from outside of Audio's package.
  • Audio concludes with a public and staticnewAudio() factory method for returning an Audio object corresponding to the filename argument. If the audio clip cannot be obtained, null is returned.
  • newAudio() compares filename's extension with .wav (this example only supports WAV audio). If they match, it executes return WavReader.read(filename) to return an Audio object with WAV-based audio data.

Listing 2 describes WavReader.

Listing 2. The WavReader helper class (WavReader.java)

 package ca.javajeff.audio; final class WavReader { static Audio read(String filename) { // Read the contents of filename's file and process it // into an array of sample values and a sample rate // value. If the file cannot be read, return null. For // brevity (and because I've yet to discuss Java's // file I/O APIs), I present only skeletal code that // always returns an Audio object with default values. return new Audio(new int[0], 0); } } 

WavReader is intended to read a WAV file's contents into an Audio object. (The class will eventually be larger with additional private fields and methods.) Notice that this class isn't declared public, which makes WavReader accessible to Audio but not to code outside of the ca.javajeff.audio package. Think of WavReader as a helper class whose only reason for existence is to serve Audio.

Complete the following steps to build this library:

  1. Select a suitable location in your file system as the current directory.
  2. Create a ca/javajeff/audio subdirectory hierarchy within the current directory.
  3. Copy Listings 1 and 2 to files Audio.java and WavReader.java, respectively; and store these files in the audio subdirectory.
  4. Assuming that the current directory contains the ca subdirectory, execute javac ca/javajeff/audio/*.java to compile the two source files in ca/javajeff/audio. If all goes well, you should discover Audio.class and WavReader.class files in the audio subdirectory. (Alternatively, for this example, you could switch to the audio subdirectory and execute javac *.java.)

Now that you've created the audio library, you'll want to use it. Soon, we'll look at a small Java application that demonstrates this library. First, you need to learn about the import statement.

Java's import statement

Imagine having to specify ca.javajeff.graphics.Triangle for each occurrence of Triangle in source code, repeatedly. Java provides the import statement as a convenient alternative for omitting lengthy package details.

The import statement imports types from a package by telling the compiler where to look for unqualified (no package prefix) type names during compilation. It appears near the top of a source file and must conform to the following syntax:

 import identifier[.identifier]*.(typeName | *); 

An import statement starts with reserved word import and continues with an identifier, which is optionally followed by a period-separated sequence of identifiers. A type name or asterisk (*) follows, and a semicolon terminates this statement.

The syntax reveals two forms of the import statement. First, you can import a single type name, which is identified via typeName. Second, you can import all types, which is identified via the asterisk.

The * symbol is a wildcard that represents all unqualified type names. It tells the compiler to look for such names in the right-most package of the import statement's package sequence unless the type name is found in a previously searched package. Note that using the wildcard doesn't have a performance penalty or lead to code bloat. However, it can lead to name conflicts, which you will see.

For example, import ca.javajeff.graphics.Triangle; tells the compiler that an unqualified Triangle class exists in the ca.javajeff.graphics package. Similarly, something like

 import ca.javajeff.graphics.*; 

tells the compiler to look in this package when it encounters a Triangle name, a Circle name, or even an Account name (if Account has not already been found).

Avoid the * in multi-developer projects

다중 개발자 프로젝트에서 작업 할 때 *다른 개발자가 소스 코드에서 사용되는 유형을 쉽게 볼 수 있도록 와일드 카드를 사용하지 마십시오 .