クラスをたくさん作成すると、クラス名の重複が起こったり、たくさんあるクラスを管理する手間がかかったりします。
そこで、パッケージおよびインポートという機能を利用することで、たくさんのクラスを管理および利用しやすくなります。
もくじ
パッケージ
Javaでパッケージという機能を利用するには、「package宣言」「ディレクトリ(フォルダ)管理」を行う必要があります。
パッケージのイメージ
パッケージはクラスを管理するための機能です。プログラム内でもpackageという記述が必要になりますが、加えてディレクトリでどのようにクラスファイルを管理するかがポイントになります。
普段コンピュータを利用する際、ファイルが多くなるとディレクトリ毎に分類して保存していると思います。パッケージも役割や種類に応じてクラスファイルを分類していきます。
このように分類を行うと、管理がしやすいだけでなく、クラス名が重複しても管理ができます。(例えば、各ディレクトリにテストを行うTestクラスがあっても区別される)
パッケージで分類するには
クラスをパッケージで分類するには、プログラム内にpackageという宣言を行い、どのパッケージに属しているかを明示する必要があります。
package宣言は以下のように、クラス宣言よりも前に記述する必要があります。
package宣言 | package パッケージ名;
public class クラス名{ |
---|---|
記述例 | package game ;
public class Test{ |
※classの左にpublicが付加されていますが、理由については後述します。
パッケージはpackage宣言だけでなく、基本的にはディレクトリ構造と紐づけて分類化していきます。
ディレクトリの名前は分類するための名前を付けるだけですが、package宣言時には注意が必要です。ディレクトリを入れ子にしてパッケージ内にパッケージを作成する形をサブパッケージと呼び、サブパッケージは「パッケージ名.サブパッケージ名」という宣言を行う必要があります。
(サブパッケージは「パッケージ名.パッケージ名.パッケージ名」というように階層を深くすることもできます)
以下、図解。
gemeパッケージに属する、入れ子のmaterialとcharacterというパッケージは「game.material」「game.character」というpackage宣言となります。
補足
materialとcharacterは「game.material」「game.character」となっていましたが、それぞれのクラスをコンパイルする際に「material」「character」というpackage宣言でも場合によってはエラーは出ません。しかし、gameパッケージの入れ子としては利用できなくなります。
理由は、パッケージに含まれるクラスを利用する場合の記述(次の項目)で記載します。
パッケージに含まれるクラスを利用するには
クラスにpackage宣言を追加すると、そのクラスはパッケージに属するという考え方になります。
パッケージ宣言は基本的にディレクトリ構造と紐づけるため、package宣言でgameとした場合は、ディレクトリgame内に格納する必要があります。
gameパッケージ(ディレクトリgame内)のTestクラスを利用する際には、パッケージ名を含めた名称でクラスを指定します。(完全限定名)
完全限定名での インスタンス生成 |
パッケージ名.クラス名 インスタンス変数 = new パッケージ名.クラス名();
※サブパッケージの場合は[パッケージ名.サブパッケージ名.クラス名] |
---|---|
記述例 | game.Test test = new game.Test() ;
game.material.Test test2 = new game.material.Test() ; |
パッケージ サンプルプログラム
workフォルダにgameフォルダを作成してください。
以下のプログラムを「Test.java」「Number37.java」という名前で、Test.javaはgameフォルダ内、Number37.javaはworkフォルダ内に保存します。(以下、階層構造図)
保存が完了したら、コマンドプロンプトを起動し、Number37.javaをコンパイルおよび実行をしてみましょう。
1 2 3 4 5 6 7 |
package game; class Test{ void exe(){ System.out.println("gameパッケージのTestクラス"); } } |
1 2 3 4 5 6 7 |
class Number37{ public static void main(String[] args){ //gameパッケージ内のTestクラスを利用する game.Test test = new game.Test(); test.exe(); } } |
実行例
C:\work>javac Number37.java
Number37.java:4: エラー: gameのTestはpublicではありません。パッケージ外からはアクセスできません
game.Test test = new game.Test();
^
Number37.java:4: エラー: gameのTestはpublicではありません。パッケージ外からはアクセスできません
game.Test test = new game.Test();
※実行例はコンパイルの例を表示しています。
サンプルプログラムの解説
サンプルプログラムではコンパイルエラーとなります。
原因はエラーにも書かれている通り、クラスにpublic宣言を行っていないからです。
パッケージ外からクラスを利用させたい場合は、classの前にアクセス修飾子「public」を付加する必要があります。同様の理由で、メソッドにも「public」を付加します。
クラスはフィールドやメソッドそれぞれにアクセス修飾子を付けてアクセスを制限することができます。classにアクセス修飾子を付加したからといって、フィールドやメソッドがそのアクセス修飾子に変更されるわけではありません。
パッケージ外からクラスをインスタンス生成させメソッドを利用させたい場合は、クラスだけでなくメソッドも個別にpublicを付加しましょう。
パッケージ サンプルプログラム[修正]
以下のように「Test.java」を修正して、上書き保存してください。
保存が完了したら、コマンドプロンプトを起動し、Number37.javaをコンパイルおよび実行をしてみましょう。
1 2 3 4 5 6 7 8 |
package game; //classの左にpublicを追加 public class Test{ //voidの左にpublicを追加 public void exe(){ System.out.println("gameパッケージのTestクラス"); } } |
実行例
C:\work>javac Number37.java
C:\work>java Number37
gameパッケージのTestクラス
※実行例はコンパイルおよび実行の例を表示しています。
パッケージとコンパイルとクラスファイル
前述したように、パッケージはpackage宣言とそれに紐づくディレクトリ構成が大事です。
今までは複数のクラスを利用したプログラムでは、別ディレクトリにあるクラスを利用せず、同じディレクトリにあるクラスを利用していました。
パッケージでクラスを管理した場合、コンパイル時に特別な指定をしない限り、以下のようなことが行われます。
- クラス名にパッケージ名が付与されている場合、コンパイルを行うファイルの場所から指定されたパッケージ名と同一のフォルダを検索して、その中のクラスを検索する
- サブパッケージも付与されている場合は、パッケージの構造を元に、フォルダの構造をたどってクラスファイルを探す
- パッケージはディレクトリ構造と紐づけるが、ディレクトリ構造自体ではないため、コンパイルを行うファイルから上層にあるパッケージを検索することはできない
文章ではわかりにくい部分もあると思うので、イメージ図で説明します。
※上記ナンバリングとイメージの番号を関連しています
補足 クラスファイルの検索場所を変更する
コンパイル時のクラスファイルやパッケージを検索する場所はカレントディレクトリが基本です。クラスファイルやパッケージを検索する場所を変更(複数指定も可)する場合は、以下のようにクラスパスを指定してコンパイルを行います。
検索ディレクトリは複数指定も可能です。例えば、「c:\work」と「c:\parent」の両方を指定する際は「-cp c:\work;c:\parent」とします。
ただし、c:\parentをディレクトリ構造に持つparentパッケージを検索する際は、「c:」を指定する必要があります。(c:\parentの指定では、c:\parent内からスタートしてparentパッケージを検索することになる為)
パッケージ 復習問題
- pakeage宣言として正しいものを選んでください。
- 解答群
package pack,subpack;
import pack/subpack;
package pack.subpack;
package pack;subpack;
- パッケージ化されたクラスをインスタンス化する記述として正しいものを選んでください。
- 解答群
pack Test test = new pack Test();
pack.Test test = new pack.Test();
pack.Test test = new Test();
Test test = new pack Test();
- 別パッケージからアクセスする際の記述として正しいものを選んでください。
- 解答群
- クラスやメソッドにpublicを付加する
- クラスやメソッドにprivateを付加する
- クラスやメソッドにはアクセス修飾子は付加しない
- クラスのみpublicを付加し、メソッドにはprivateを付加する
- お疲れ様でした。
まとめ
- packageを利用すると、クラス名の重複を防いだりクラスの管理がしやすくなります
- packageはソース内の先頭にpackage宣言を行います
- package管理を行う場合は、ディレクトリ構造も同じようにする必要がある
- package内にサブパッケージを作成することができる
- コンパイルする際にカレントディレクトリ以外からクラスファイルを検索する際にはクラスパス指定を行う必要がある
- package化されたクラスを利用する際には、「パッケージ名.クラス名」という完全限定名でクラスを指定する必要がある
- 別パッケージからアクセスさせる場合は、クラスやメソッドにはpublicを付加する