[???] /
[Java FAQ] / [S017]
S017: パッケージ - package
[S017 Q-01]
パッケージ(package)ってなんですか?
[S017 A-01]
多数のクラスを機能、種別によってグループに分けるための仕組みです。
パッケージはディレクトリ構造のような階層によって表現されます。一般的にクラスファイルは、
パッケージと同一の階層をもつディレクトリ階層に格納されます。各クラスの完全な名称
(FQCN:fully-qualified class name) は各階層の名称を "." で連結したパッケージ名と
クラス名によって表現されます。
例えば、java.util パッケージの Vector クラスを考えます。
この場合、下図のような階層に配置されます。
java/
|
+- util/
|
+- Vector.class
パッケージには以下のような特徴があります。
・名前空間はパッケージごとに独立
・パッケージ内のクラスから、同一パッケージ内のアクセス修飾子
(public、protected、private)のない変数を使用することが可能
パッケージの特殊な場合として、package を指定しない場合にクラスが属する
デフォルトパッケージがあります。
[S017 Q-02]
自作のプログラムを公開したいのですが、パッケージ名はどのようにつけたら良いのでしょうか?
[S017 A-02]
世界的に一意なパッケージ名を付けなければなりません。
指針は以下の URL に詳細があります。
The Java Language Specification, 7.7 Unique Package Names
http://java.sun.com/docs/books/jls/second_edition/html/packages.doc.html#40169
世界的に一意なパッケージ名(Globally Unique Package Names)は
基本的にはドメイン名を元に決定することになります。
あなたの所属するドメインが
my.domain.co.jp
の場合、パッケージ名は
jp.co.domain.my
JP.co.domain.my
から始まります。
(*)現在は、最初の書式が推奨されています。
もし独自のドメイン名を持っていない場合は、以下の URL にて
パッケージ名登録サービスが行われています。
http://www.java-conf.gr.jp/wg_bof/package/
参考記事[JavaHouse-Brewers:6405]
[S017 Q-03]
インポートの際 "*" でパッケージをまるごと指定するのと、クラス名指定では違いがあるのでしょうか?
[S017 A-03]
本質的な違いはありません。
ただし、前者の場合では、指定されるパッケージが大きい場合
コンパイル時にエラーが発生する、といった例もあるようです。
また前者には、ソース中に現れたクラスがどのパッケージ
からインポートされているのかが特定しにくい、という面も
ありますので、後者のようにクラスごとにインポートする
ことをお勧めします。
参考記事[JavaHouse-Brewers:15723],[JavaHouse-Brewers:13783]
[S017 Q-04]
パッケージの宣言をしていないクラスをインポートしたいのですが。
[S017 A-04]
インポート宣言にクラス名を書きます。
例えば、あるディレクトリに次のようにクラスを配置したとします。
classes/
|
+- A.class
|
+- test/
|
+- B.class
A.class はパッケージ指定なし、B.class はパッケージ test として定義
されています。
この場合、B.class から A.class を参照するには
//B.java
// デフォルトパッケージからのインポート
package test;
import A;
public class B {
...
}
のようにインポートする必要があります。
もし、
import A;
を記述しなかったら、A.class は test パッケージから検索されます。
従って、上記階層が
classes/
|
+- A.class
|
+- test/
|
+- A.class
|
+- B.class
のような構成となっている場合には、コンパイル時にエラーは出ませんので
注意が必要です。
[S017 Q-05]
パッケージの指定をしないとどうなるのですか?
[S017 A-05]
デフォルトパッケージとなります。
デフォルトパッケージとは、 package 宣言によって、明示的に所属パッケージを指定しない場合に、その
クラスが所属しているとみなされるパッケージです。
(*)無名パッケージ(Unnamed Packages)とも呼ばれます。
デフォルトパッケージもパッケージですので、S017-01 と同じことがいえます。
[S017 Q-06]
パッケージを宣言したらコンパイルできなくなったのですが。
[S017 A-06]
クラスパスが正しく設定されていないためです。
仮にパッケージを宣言する前が次のようなプログラムだったとします。
class Foo {}
class Bar extends Foo {}
ここで、パッケージ foobar を宣言します。
package foobar;
class Foo {}
package foobar;
class Bar extends Foo {}
これをコンパイルしようとするとコンパイルエラーが発生します。
これを解決するには次のような方法があります。
(1) javac の -d オプションを用いると指定したディレクトリの下に
パッケージと同じ名前のディレクトリが作成され、その下にクラス
ファイルが生成されます。
例) javac -d . Bar.java
この場合、暗黙的に設定されるクラスパス(カレントディレクトリ)が
適用されるのでコンパイルできます。
(2) パッケージと同じ名前のディレクトリを作成し、ソースファイルを
パッケージ名のディレクトリに移動します。ここで次のように
コマンドを実行するとパッケージ名のディレクトリにクラスファイルが
生成されます。
例) javac foobar/Bar.java
(1)同様、暗黙的に設定されるクラスパス(カレントディレクトリ)が
適用されるのでコンパイルできます。
(3) パッケージと同じ名前のディレクトリを作成し、ソースファイルを
パッケージ名のディレクトリに移動します。ここでソースファイルと
同じディレクトリに移動して次のようにコマンドを実行すると
このディレクトリにクラスファイルが生成されます。
例) javac -classpath .. Bar.java
この場合、明示的にクラスパスを設定しているのでコンパイルできます。
参考記事[JavaHouse-Brewers:7625],[JavaHouse-Brewers:4071],[JavaHouse-Brewers:3178],[JavaHouse-Brewers:683]
[S017 Q-07]
パッケージを宣言したら実行できなくなったのですが。
[S017 A-07]
パッケージ名を指定していないかクラスパスの問題です。
ichinichi ディレクトリの下に asa ディレクトリを作成し、 asa に次のプログラムをコンパイルした Aisatu.class を置いたとします。
package asa;
class Aisatu {
public static void main(String[] args) {
System.out.println("Good Morning !");
}
}
Aisatu.class を実行するには
(1) ichinichi ディレクトリで
java asa.Aisatu
のように実行します。
(2) asa ディレクトリで
java -cp .. asa.Aisatu
のように実行します。
[S017 Q-08]
既存パッケージの中のクラスを自作のものに置き換えたいのですが。
[S017 A-08]
やむを得ない事情がない限り、やめるべきです。
JDK のクラスは契約事項として置き換えを禁止しています。
[S017 Q-09]
JDK のライブラリのパッケージ名と同じ名前のパッケージのクラスを作成できますか?
[S017 A-09]
作成することは可能ですが、お勧めできません。
理由は、クラスの追加もパッケージレベルでは改変と考えられるためです。
S017-08を参照してください。
参考記事[JavaHouse-Brewers:4341]
[S017 Q-10]
デフォルトパッケージってなんですか?
[S017 A-10]
package 宣言によって、明示的に所属パッケージを指定しない場合に、その
クラスが所属しているとみなされるパッケージです。
(*)無名パッケージ(Unnamed Packages)と同様
デフォルトパッケージもパッケージですので、S017-01 と同じことがいえます。
[S017 Q-11]
パッケージを使う場合の CLASSPATH、CODEBASE の設定方法は?
[S017 A-11]
S017-06 を参照して下さい。
[S017 Q-12]
パッケージ名に "-" が使えないのですが?
パッケージ名に "interface" が使えないのですが?
[S017 A-12]
パッケージ名として使用できる文字は
A から Z
a から z
0 から 9
_ 及び $
です。
パッケージ名称の区切り文字は "." です。
加えて以下の制限があります。
各識別子は数字で始まってはいけません。
各識別子には Java の予約語は使用できません。
従って、"-" や "*" はパッケージ名には使用できませんし、
"interface" や "null" なども駄目です。
(*)より正確には、識別子(Identifier)に使用できる文字は
先頭文字については
java.lang.Character.isJavaIdentifierStart(char)
先頭以外については
java.lang.Character.isJavaIdentifierPart(char)
がそれぞれ True となる文字です。
上記で記述できない名称の場合は、以下が推奨されます。
・"01class" のように数字で始まる名称を使用したい。
先頭に "_" を付加して、"_01class" のようにする。
・"interface" や "public" などの予約語を使用したい。
Java では大文字、小文字が区別されますので、"Interface" や
"Public" などを使用する。
または、先頭に "_" を付加する。
・特にドメイン名をパッケージ名に用いる場合に "-" がドメイン名
に含まれている.
"-" を "_" に置き換える。
参考記事[JavaHouse-Brewers:7036],[JavaHouse-Brewers:7102]
[S017 Q-13]
java.lang パッケージはインポートしていないのですが
System.out.println() 等が使えるようです。なぜ?
[S017 A-13]
java.lang パッケージはシステムの動作に必要となるクラス群が格
納されているため、ほっておいてもインポートされた状態になるため
です。
[S017 Q-14]
パッケージのバージョンを管理したいのですが?
[S017 A-14]
Java 2 以降であればパッケージにバージョン情報を付加すること
ができます。
例えば、2つのパッケージを1JAR ファイルにまとめてそれぞれに
バージョン情報を付加する場合の例を考えます。
//test1 パッケージの Test クラス
package test1;
public class Test {
public static void main(String[] argv) {
System.out.println("--- package info ---");
Package pkg = Test.class.getPackage();
System.out.println(pkg.getSpecificationTitle());
System.out.println(pkg.getSpecificationVendor());
System.out.println(pkg.getSpecificationVersion());
System.out.println(pkg.getImplementationTitle());
System.out.println(pkg.getImplementationVendor());
System.out.println(pkg.getImplementationVersion());
System.out.println("--------------------");
}
}
//test2 パッケージの Test クラス
package test2;
public class Test {
public static void main(String[] argv) {
System.out.println("--- package info ---");
Package pkg = Test.class.getPackage();
System.out.println(pkg.getSpecificationTitle());
System.out.println(pkg.getSpecificationVendor());
System.out.println(pkg.getSpecificationVersion());
System.out.println(pkg.getImplementationTitle());
System.out.println(pkg.getImplementationVendor());
System.out.println(pkg.getImplementationVersion());
System.out.println("--------------------");
}
}
上記をコンパイルして
/CLASSROOT/
|
+- test1
| |
| +- Test.class
|
+- test2
|
+- Test.class
のようなパッケージ、クラス階層となっているとします。
この場合は以下の手順でバージョン情報を付加することが
できます。
1.マニフェストファイル用エントリの作成
Manifest-version: 1.0
Name: test1/
Implementation-Title: "test1"
Implementation-Version: "build01"
Implementation-Vendor: "SAMPLE system. Inc."
Specification-Title: "version sample 1"
Specification-Version: "1.0"
Specification-Vendor: "SAMPLE co.ltd."
Name: test2/
Implementation-Title: "test2"
Implementation-Version: "build02"
Implementation-Vendor: "SAMPLE system. Inc."
Specification-Title: "version sample 2"
Specification-Version: "2.0"
Specification-Vendor: "SAMPLE co.ltd."
上記の内容をファイル MFSAMPLE として保存します。
2.JAR ファイルの作成
> jar cvmf MFSAMPLE sample.jar test1 test2
と入力し JAR ファイル(sample.jar)を作成します。
これで、バージョン情報の付いた JAR ファイルが生成されました。
次のコマンドにより、バージョン情報を確認できます。
> java -cp sample.jar test1.Test
--- package info ---
"version sample 1"
"SAMPLE co.ltd."
"1.0"
"test1"
"SAMPLE system. Inc."
"build01"
--------------------
> java -cp sample.jar test2.Test
--- package info ---
"version sample 2"
"SAMPLE co.ltd."
"2.0"
"test2"
"SAMPLE system. Inc."
"build02"
--------------------
参考記事[JavaHouse-Brewers:22498]
[S017 Q-15]
import は C の #include とは違うのですか?
[S017 A-15]
違います。
Java での import 宣言は必須ではありません。
例えば
//サンプルその1
//import 宣言によって java.util.HashTable をインポートする
import java.util.HashTable;
public class Test {
public void sample() {
HashTable hashTable = new HashTable();
....
}
}
//サンプルその2
//インポートしない
public class Test {
public void sample() {
java.util.HashTable hashTable = new java.util.HashTable();
....
}
}
これらの2つのサンプルは全く等価です。
import 宣言による利点は、
完全な名称(fully-qualified class name)を用いずに、単一の識別子に
よる名称によって異なるパッケージに配置されたクラスを使用することが
できる。
ということです。
また、ソース上の import 宣言は、どのパッケージのクラスを使用している
かという情報をもあらわします。
したがって、import 宣言によるインポートは、必要最小限のパッケージ、
クラスについてのみ行うようにし、import Package.*; のようなインポート
は避けたほうがよいでしょう。
参考記事[JavaHouse-Brewers:24495]
contributor: Osamu Hasegawa
コメントの送り先 Java FAQ BBS