S-JIS[2008-07-05/2023-09-23] 変更履歴

Javaパッケージ


パッケージ

Javaの管理上、相互に関連する複数のクラスを 1つのパッケージとして扱う。[2003-07-06]
(ディレクトリー=パッケージの中に、ファイル=クラスが複数入っているイメージ)

自分がどのパッケージに属しているかは、それぞれのソースファイルの先頭に「package パッケージ名;」を記述することによって指定する。

package jp.hishidama.example;

class Example {
}

パッケージ宣言を書かない場合、「デフォルトパッケージ(無名パッケージ)」に属するものとして扱われる。[2008-07-05]

1つのソースファイル内には1つのパッケージ宣言しか書けない。[2008-07-05]
1つのソースファイル内には複数のクラスを書けるが、みな同じパッケージに属することになる。

ちなみに、パッケージ名の中のピリオド「.」の前後には、スペースや改行を入れても大丈夫。[2010-01-10]


普通は「ソースファイルを管理するディレクトリー」と「パッケージ名」は対応させる。

C:\workspace\example\src>tree /f
C:.
└─jp
    └─hishidama
        └─example
                Example.java

が、本当は、ソースファイル自身がパッケージ構成と対応している必要は特に無い。[/2003-09-07]
(ただし、javacの-sourcepathオプションでソースディレクトリーを指定する場合は、そのソースファイルの場所はパッケージ構成と対応している必要がある。[2020-05-29]

クラスファイルの場所はパッケージ構成と対応している必要がある
コンパイルオプションで-dを指定 すると、パッケージ名に応じた場所(階層)にclassファイルが作られる。

パッケージの下(右)の部分をサブパッケージと呼ぶ。(ディレクトリー構造でサブディレクトリーというのと同じ)[2008-09-13]
例えば、java.awtやjava.langやjava.utilでは、パッケージjavaに対するawt, lang, utilがサブパッケージ。
例えば、java.awt.imageでは、パッケージjava.awtに対するimageがサブパッケージ。


なお、パッケージ名は全世界でユニークとなるように付けるものらしい
よくあるのが、URLを利用して、インターネットドメイン名を逆にして付ける方法。
例えばfoo.co.jpというドメインの会社の場合だと、jp.co.foo.project1といった感じ。


import

Javaでは、例えばjava.util.Listjava.util.ArrayListを使う場合は (限定名(FQN)を使って)以下のように書く。

class ImportExample {
	private java.util.List list = new java.util.ArrayList();
}

しかし毎回これを書くとソースが長くなってしまうので、「import」を使ってクラスを指定することにより、パッケージを省略して単純名だけで書けるようになる。[2003-07-06]

import java.util.List;
import java.util.ArrayList;

class ImportExample {
	private List list = new ArrayList();
}

※パッケージ名の中のピリオド「.」の前後には、スペースや改行を入れても大丈夫。[2010-01-10]

備考: ただし、自分と同じパッケージに属しているクラスに関しては、import宣言しなくても単純名でアクセスできる。[2010-01-10]

package example1;

class A {
}
package example1;

class B {
	private A a = new A();	//単純名でexample1.Aを指定できる。
}

トップレベルのクラス(ソースファイル内の直下に書かれているクラス)の可視性は、publicpackage private(何も書かない)しか無い。
パッケージプライベートだと同一パッケージにあるクラスにはアクセスできるし、publicだとどこからでもアクセスできるので、自分と同じパッケージのクラスには必ずアクセスできることになる。


異なるパッケージだがクラス名が同一の場合、2つ共を同時にインポートすることは出来ない。[2008-07-05]
例えばjava.util.Datejava.sql.Dateを同時に使う場合、

import java.util.Date;
import java.sql.Date;	←コンパイルエラーになる

importはどちらか片方だけにして、他方は限定名で指定する必要がある。

import java.util.Date;

	Date dt = 〜;		//java.util.Date(FQCN指定でない場合、importしたクラスとして扱われる)
	java.sql.Date sql = 〜;	//java.sql.Date
	java.util.Date d2 = 〜;	//java.util.Date(importしていてもFQCNで指定可能)

どうでもいいが、全く同じimport文を複数書いてもエラーにはならない。[2008-09-13]

import java.util.Date;
import java.util.Date;	//Eclipse3.2では「一度も使用されていません」という警告になるけどねー

また、同一パッケージに属するクラスはまとめてインポートすることが出来る。

import java.util.*;	//java.utilパッケージ直下のクラスを全てインポート

これは「オンデマンドのインポート宣言」と呼ばれるもの。[2008-09-13]
すなわち、ソース中で必要とされたクラスがこの中から探される。(つまり、この中のクラス全てがインポートされるわけではない。「*」は「全て」という意味ではなく、「必要に応じて」)

importでは、「*」でインポートするよりも個別にクラスを書く方が推奨されている。(どのクラスを使っているのかが明示的になる(はっきりする)ので)

また、複数のimportで「*」を使っていて同一クラス名が在る場合、そのクラスを単純名で使うことは出来ない。(「あいまいである」というコンパイルエラーになる。先にimportした方が使われるというわけではない)
例えばJDK1.4で以下のような書き方をしていた場合、

import java.util.*;
import javax.jms.*;

class QueueExample {
	private Queue q = null;	//javax.jmsのQueueクラス
}

JDK1.5でコンパイルすると、JDK1.5でjava.util.Queueが導入されたので、単純名「Queue」だけではどちらのものか判断できなくなり、コンパイルエラーになってしまう。
この場合、個別のクラス名のimport文を入れてやれば解決できる。

import javax.jms.Queue;
import java.util.*;
import javax.jms.*;
//import javax.jms.Queue;	入れる場所は、java.util.*やjavax.jms.*のimport文の前でも後でもいい

class QueueExample {
	private Queue q = null;	//javax.jmsのQueueクラス
}

内部クラスもアクセス可能(public)であればインポートすることが出来る。[2008-07-05]

package jp.hishidama.example1;

public class Root {
	public class Inner {
	}
}
package jp.hishidama.example2;

import jp.hishidama.example1.Root.Inner;

class Example2 {
	Inner obj;
}

デフォルトパッケージのクラス(package指定の無いクラス)はimportすることが出来ない。[2010-01-10]

public class A {
}
package example;

import A;		←コンパイルエラーになる

class B {
	void test() {
		A a = new A();
	}
}

JavaFAQにはデフォルトパッケージのクラスをimportする例が載っているが、JDK1.3以前のjavacは出来たらしい。
が、これはバグで、出来ないのが正しい動作らしく、JDK1.4以降のjavacではコンパイルエラーになる。(バージョンに1.3以前を指定したとしても)
(参考: 人力検索はてな

Java言語仕様7.5.1章によると、インポート宣言できるのはクラスやインターフェースの完全限定名。すなわち「名前 . 識別子」という形式。
したがってデフォルトパッケージは(ピリオドより前の部分が無いから)指定できない、という事らしい。
Java言語仕様第3版では完全限定名でなく正準名に改められたようだが、ピリオドが必要な形式であることに変わりは無い)

自分もデフォルトパッケージに属している(package宣言をしていない)なら、「同一パッケージのクラスにはimportを書かなくてもアクセスできる」というルールにより、ソース中にそのまま書けばアクセスできる。
(デフォルトパッケージ同士も、同一パッケージ扱いされるから)

class C {
	void test() {
		A a = new A();
	}
}

でも、別パッケージからでも、リフレクションではアクセスできるんだそうだ。(参考: 人力検索はてな
まぁ、importはコンパイル時のクラスの特定に使われるのであって、リフレクションは実行時だからどこから呼ばれようと関係ないわなぁ。

package example;

class B {
	void test() throws Exception {
		Object a = Class.forName("A").newInstance();
	}
}

importとimportの間に余分なセミコロンがあるのは文法違反。[2023-09-23]

import java.util.List;;	// importとimportの間に余分なセミコロンがある
import java.util.Map;;	// 最後のimportの末尾は(importの間ではないので)余分なセミコロンがあっても問題ない

public class ImportExample {
}

しかし、Java21より前は特にエラーにはならなかった。
Java21以降ではコンパイル時にチェックされるようになった為、上記のソースはコンパイルエラーになる。


import文の所感

importは よくC言語の#includeに例えられるようだが、どちらかというとextern宣言に近いと思う。[2003-07-06]
#includeは読み込んだソースをそのままその位置に展開して一緒にコンパイルするプリプロセッサの機能だが、externは「その関数を使うぞ」という宣言に過ぎず、その関数のコンパイルは別に行う。
importも「他でコンパイルされたクラスを使うぞ」と宣言ではないか?

もっとも、実際はコンパイルで使う(クラスのメソッドやフィールドを見る)為にそのクラスのバイトコードから定義を読み込んでいるのではないかと思うので、そういう意味では単なる宣言(extern)ではない。


Java標準パッケージ

Javaのライブラリーとして、様々なクラス(パッケージ)が用意されている。[/2008-07-05]
このうち、java.langパッケージに属しているクラスは、明示的にimportしなくても使用することが出来る。

主要なJavaパッケージ
パッケージ名 用途 備考 更新日
java.lang Javaの基本機能 特に指定しなくても、暗黙にimportされる。
Java言語仕様第3版7.5.5章『自動インポート』
2008-09-13
java.util ユーティリティー(リスト操作・時計操作…)    
java.io
java.nio
入出力 ファイルI/O  
java.math 算術演算    
java.net ネットワーク処理    
java.awt
javax.swing
グラフィカルユーザーインターフェース swing  
java.applet アプレット作成 Javaアプレット  

staticインポート

JDK1.5から、static importという構文が追加された。[2007-03-26]
→Sunのstaticのインポート
Java言語仕様第3版7.5.4章『オンデマンドのstaticインポート宣言』

これは、staticなメンバー(例えばstatic final(定数)のフィールド)をクラス名の指定無しに使えるようにするもの。

import static パッケージ.クラス.メンバー;
import static パッケージ.クラス.*;
import static java.lang.Math.PI;

public class StaticImport {
	public static void main(String[] args) {
		System.out.println(PI);
	}
}

インターフェース定数を定義してそれをimplementsすれば似たような事が出来るが、インターフェースは本来そういう風に使うものではないらしい。
それに そのやり方だと、全ての定数がpublicになってしまうので、本来公開したくない定数も公開されてしまう。(Javadocにまで出てしまう)[2008-05-02]


staticフィールド(メンバー変数)だけでなく、staticなメソッドもimportできる。[2007-09-11]
つまり、普通は他のクラスのstaticメソッドは「他クラス.method()」としなければ呼べないが、staticインポートしておけばクラスの指定無し、すなわち「method()」だけで呼ぶことが出来るようになる。

staticメソッドをオンデマンド(「*」)指定でインポートする場合、同名メソッド(すなわちオーバーロードされたメソッド)があれば、それら全てがインポートされる。[2008-09-13]

JUnit4でこの方法が使われている。


staticインポートするクラスの親クラスにstaticメンバーがあると、それらもstaticインポートできる。[2021-12-18]
インポートしたクラスと親クラスに同名のstaticメンバーがある場合、インポートしたクラスのものが使用される。

package example.static_import;

public class Static1 {

	public static void method1() {
		System.out.println("Static1.method1");
	}

	public static void method3() {
		System.out.println("Static1.method3");
	}

	public static int member1 = 11;
	public static int member3 = 13;
}
package example.static_import;

public class Static2 extends Static1 {

	public static void method2() {
		System.out.println("Static2.method2");
	}

	public static void method3() {
		System.out.println("Static2.method3");
	}

	public static int member2 = 22;
	public static int member3 = 23;
}
import static example.static_import.Static2.*;

public class StaticUse {

	public static void main(String[] args) {
		method1();
		method2();
		method3();

		System.out.println(member1);
		System.out.println(member2);
		System.out.println(member3);
	}
}

なお、toStringという名前のメソッドはstaticインポートできないらしいorz[2021-12-18]

staticインポート自体はエラーにならないが、toStringを使おうとすると、インポートしたものが認識されないでエラーになる。

参考:stackoverflowのHow to use a static import for java.util.Arrays.toString?


単純名・限定名・正準名

クラス名を指定するには、単純名限定名という方法がある。[2008-09-13]

単純名(simple name)は、単一の識別子で表す。例えば「String」とか「Date」とか。
限定名(qualified name)は、ピリオドを付けて必ず一意に定まるようにした表し方。例えば「java.lang.String」とか「java.util.Date」「java.sql.Date」とか。

パッケージ名を付けて指定したクラス名の書き方のことを、完全修飾クラス名(Fully Qualified Class Name:FQCN)あるいは完全限定名(fully qualified name:FQN)と呼ぶ。

また、正準名canonical name)という表現もある。基本的には限定名と同じだが、importで指定できるのは限定名でなく正準名。
例えば以下のようなクラスがあるとき、

package pack;

class Outer1 {
	class Inner {
	}
}

class Outer2 extends Outer1 {
}

pack.Outer1.Inner」と「pack.Outer2.Inner」はどちらも完全限定名であり、同じクラス(Outer1内のInner)を指す。
しかし正準名は「pack.Outer1.Inner」のみ(Innerが定義されているのはOuter1内だから)。


package-info

パッケージにJavadocを書きたい場合やアノテーションを付けたい場合は、package-info.javaというファイルを作り、そこに書く 。(JDK1.5以降)[2020-05-29]

package-info.java:

/**
 * パッケージのJavadoc
 */
@PackageAnnotation
package com.example;

package-info.javaをコンパイルすると、package-info.classが作られる。(作られない場合もある模様?)


Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま