2017年11月26日日曜日

classpath import package


パッケージした場合のコマンドからのコンパイルエラーについて
総合開発環境を使っている場合は、以下がすべて自動でやってくれるので
こんなことになっていたのかと思う。

HelloWorld から Message をよびだしている
Messge.java は otamesi  に パッケージされている
HelloWorld.java は test に パッケージされている
ともに、c:\Source\Java 以下にある。 
以下のような感じ

Javaにはpackageなるものがあるそうで、それを試してみんとす。
パッケージ構成は、
  • otamesi
  • (Messageクラス)
  • test
  • (HelloWorld2クラス)

C:\Source\Java\
┣━otamesi
┗━test


C:\Source\Java\test>javac HelloWorld2.java とすると
エラー。
     HelloWorld2.java:9: シンボルを解釈処理できません。
     シンボル: クラス Message
HelloWorld.java がどこにあるのかわからないらしい。
HelloWorld.java に 以下コードをいれる。
なお
imort 文はその直下のクラスをインポートできる。
さらに その階下のフォルダにあるクラスはインポートできない
import otamesi.*;

     C:\Source\Java\test>javac HelloWorld2.java HelloWorld2.java:3: パッケージ
     otamesi は存在しません。
      import otamesi.*; ^ HelloWorld2.java:11: シンボルを解釈処理できません。
     シンボル: クラス Message

まだわからんらしい。
importとpackageの仲を取り持つCLASSPATH
CLASSPATHは、コンパイルする時にも実行する時にも使われます。
その目的は、皆さんが作ったクラスの中で利用している他のクラスがどこにあるかを、 javacやjavaコマンドなどに教えることです。
以下を実行。

C:\Source\Java\test>set CLASSPATH=c:\source\java

   C:\Source\Java\test>javac HelloWorld2.java 
   コンパイルが通る。
実行する。
C:\Source\Java\test>java HelloWorld2
    エラーする
    Exception in thread "main" java.lang.NoClassDefFoundError: HelloWorld2

いや、ちょっと待てよ、HelloWorld2クラスから見たMessageクラスへの参照は解決したけど、 HelloWorld2そのもに付けたpackagetestだったぞ。 ということはHelloWorld2クラスの正しいパッケージ表現はtest.HelloWorld2のはず。
そのtestの直前までのパスはCLASSPATHc:\source\javaと設定したわけだから んじゃぁ、これでどうじゃ!

C:\Source\Java\test>java test.HelloWorld2
     Hello world               
      実行できな。




 



 






 
packageの使い方
 

多数のクラスを整理するためのpackage

多分皆さん初めてJavaに出会った時に、HelloWorldクラスを作った(見た)ことでしょう。
世界一有名なJavaクラス
/**
 *  世界中で7億人が見たクラス
 *  @author YTP
 */
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello world!");
    }
}
                        

ところがこれだけではつまらないので、他のクラスから利用されるようなクラスを 作ってみたくなりますよね。
一人前のクラス
/**
 *  初めて作るであろういっちょまえのクラス
 *  @author YTP
 */
public class Message {
    /**
     *  インスタンス変数だってあるもん
     */
    private String sMessage_ = null;
    
    /**
     *  初めてのコンストラクタでーす
     */
    public Message() {
    }
    
    /**
     *  初めてのセッターメソッドでーす
     *  @param sMessage 幸せなメッセージを下さい
     */
    public void setMessage(String sMessage) {
        sMessage_ = sMessage;
    }
    
    /**
     *  お披露目でーす
     */
    public void showMessage() {
        System.out.println(sMessage_);
    }
}
                        

自分が作ったクラスを使うクラス
/**
 *  自分が作ったクラスを呼びたいっ!!
 *  @author YTP
 */
public class HelloWorld2 {
    public static void main(String[] args) {
        Message mumumu = new Message();
        mumumu.setMessage("Hello world!");
        mumumu.showMessage();
    }
}
                        

ここまでは何のことはなく動きます

でも、そんなクラスをいくつも作っているうちに、ディレクトリの中はクラスでいっぱい!
Javaにはpackageなるものがあるそうで、それを試してみんとす。
パッケージ構成は、
  • otamesi
  • (Messageクラス)
  • test
  • (HelloWorld2クラス)
の二つとしましょう。

まずはMessageクラスにパッケージ名を付けます。
packageの付いたMessageクラス
package otamesi;

/**
 *  パッケージ名を付けてみました
 *  @author YTP
 */
public class Message {

★略

                        

パッケージはディレクトリと対応するので、otamesiというディレクトリを作って そこにMessage.javaを置きコンパイルします。
packageの付いたMessageクラスをコンパイル
C:\Source\Java\otamesi>javac Message.java

C:\Source\Java\otamesi>
                        
うまくいきました。

続いてMessageクラスを使う側のHelloWorld2クラスも直します。
packageの付いたHelloWorld2クラス
package test;

/**
 *  自分が作ったクラスを呼びたいっ!!
 *  @author YTP
 */
public class HelloWorld2 {
    public static void main(String[] args) {
        Message mumumu = new Message();
        mumumu.setMessage("Hello world!");
        mumumu.showMessage();
    }
}
                        

これで準備万端、testディレクトリを作ってそこでコンパイルしてみます。
天罰が下った図 その2
C:\Source\Java\test>javac HelloWorld2.java
HelloWorld2.java:9: シンボルを解釈処理できません。
シンボル: クラス Message
位置    : test.HelloWorld2 の クラス
        Message mumumu = new Message();
        ^
HelloWorld2.java:9: シンボルを解釈処理できません。
シンボル: クラス Message
位置    : test.HelloWorld2 の クラス
        Message mumumu = new Message();
                             ^
エラー 2 個

C:\Source\Java\test>
                        
うぎゃ~、でたぁー、シンボルを解釈処理できません じゃー!!!
いやちょっと待てよ。パッケージを作ったんだから、 さっきやったimportがないとうまくいかんではないか。
そうそう、importじゃ
packageとimportの付いた完全無欠なHelloWorld2クラス
package test;

import otamesi.*;

/**
 *  自分が作ったクラスを呼びたいっ!!
 *  @author YTP
 */
public class HelloWorld2 {

★略
                        

今度こそおっけー、コンパイルしてみます。
八方塞がりの図
C:\Source\Java\test>javac HelloWorld2.java
HelloWorld2.java:3: パッケージ otamesi は存在しません。
import otamesi.*;
^
HelloWorld2.java:11: シンボルを解釈処理できません。
シンボル: クラス Message
位置    : test.HelloWorld2 の クラス
        Message mumumu = new Message();
        ^
HelloWorld2.java:11: シンボルを解釈処理できません。
シンボル: クラス Message
位置    : test.HelloWorld2 の クラス
        Message mumumu = new Message();
                             ^
エラー 3 個

C:\Source\Java\test>
                        
うぉぉぉぉぉぉぉぉ~、なんじゃこりゃぁぁぁぁぁぁ!!!!さっきよりエラーが増えたーっ!!
しかも、「パッケージ otamesi は存在しません」「シンボルを解釈処理できません」 じゃとー!!!
爆死するしかないのかっー?!!

いえいえ、そんなことはありません。これでやっとCLASSPATH大明神のお出ましです。

CLASSPATHの役割に続きます。



Copyright © 2002-2016, Your Technology Partner. All rights reserved.






 

 
CLASSPATHの役割
 

importとpackageの仲を取り持つCLASSPATH

CLASSPATHは、コンパイルする時にも実行する時にも使われます。
その目的は、皆さんが作ったクラスの中で利用している他のクラスがどこにあるかを、 javacやjavaコマンドなどに教えることです。

例えば、JDBCドライバ自体はJDKに入っていないので、 RDBベンダーが提供するドライバを利用することになります。そのドライバが入っているファイルはどれ? という情報を設定するのがCLASSPATHというわけです。Windowsの場合は、
set CLASSPATH=C:\orant\lib\classes12.zip
というように設定します。
複数のファイルを指定したい場合は;(セミコロン)(UNIXの場合は:(コロン))で区切ります。
set CLASSPATH=C:\orant\lib\classes12.zip;D:\tomcat\lib\crimson.jar
という要領です。

先程のpackageを追加したHelloWorld2クラスがコンパイルに失敗したのは、 CLASSPATHが設定されていないことが原因です。 つまり、HelloWorld2クラスの中で利用(参照)しようとしているMessageクラスがおらんっ!! と怒られたんです。
でも、Messageクラスは自分で作ったクラスだし、zipファイルやjarファイルの中になんかありませんよね? 一体どうやって指定するんでしょうか?
実はCLASSPATHではファイルだけでなく、ディレクトリも指定出来るようになっています。
MessageクラスやHelloWorld2クラスがあるディレクトリは、
C:\Source\Java\
┣━otamesi
┗━test
という構成になっています。ですからCLASSPATHに次のように設定します。
CLASSPATH大明神お参り の図
C:\Source\Java\test>set CLASSPATH=c:\source\java

C:\Source\Java\test>

                        
ほんとに設定出来たかを確認しましょう。
CLASSPATH大明神のお札を頂く の図
C:\Source\Java\test>set CLASSPATH
CLASSPATH=c:\source\java

C:\Source\Java\test>
                        
ここで再びコンパイルしてみます。
CLASSPATH大明神の神通力 の図
C:\Source\Java\test>javac HelloWorld2.java

C:\Source\Java\test>
                        
うぉぉぉぉぉぉ、コンパイルが通ったじゃー!!!
この勢いで実行ですじゃ。
CLASSPATH大明神の神通力が通じない?! の図
C:\Source\Java\test>java HelloWorld2
Exception in thread "main" java.lang.NoClassDefFoundError: HelloWorld2

C:\Source\Java\test>
                        
なにぃぃぃぃぃぃっ?! なんで、なんでぇぇぇぇ?!
CLASSPATHが通じないなんて....... HelloWorld2クラスがNoClassDefFoundErrorだとっ?!

いや、ちょっと待てよ、HelloWorld2クラスから見たMessageクラスへの参照は解決したけど、 HelloWorld2そのもに付けたpackagetestだったぞ。 ということはHelloWorld2クラスの正しいパッケージ表現はtest.HelloWorld2のはず。
そのtestの直前までのパスはCLASSPATHc:\source\javaと設定したわけだから んじゃぁ、これでどうじゃ!
CLASSPATH大明神はやっぱりすごかった の図
C:\Source\Java\test>java test.HelloWorld2
Hello world!

C:\Source\Java\test>
                        
ひょぇぇぇぇぇ、実行できたじゃー!!!

そうです。HelloWorld2クラス自体がどこにあるかはimportコマンドで指定することが出来ないので、 パッケージ名付きでクラスを指定しないとjavaコマンドが実行出来なかったんです。

私はJavaで開発する複数のプロジェクトをかけもちしています。 その場合CLASSPATHに複数のディレクトリを指定するといずれのプロジェクトのクラスも参照出来るようになります。
set CLASSPATH=C:\Project\えっくす;C:\Project\さんでー

「えっくす」プロジェクトでmemberというパッケージを作り、 その中にShikaishaというクラスがあったとします。
C:\Project\えっくす\
┗━member\
┗━Shikaisha.class
という構成になっています。
クラスの中はこんな感じです。
ご存じお休み中の「くぼじゅん」です
package member;

/**
 *  プロジェクトえっくすの司会者クラスでーす
 *  @author YTP
 */
public class Shikaisha {
    /**
     *  コンストラクタでーす
     */
    public Shikaisha() {
    }
    
    /**
     *  司会者が挨拶をします
     */
    public void greet() {
        System.out.println("皆さんこんばんは、久保純子でーす");
    }
}
                        
「えっくす」司会者に挨拶をさせるクラス
package test;

import member.*;

/**
 *  司会者に挨拶をしていただきましょう!
 *  @author YTP
 */
public class HelloMC {
    public static void main(String[] args) {
        // くぼじゅんさん出番でーす
        Shikaisha kubojun = new Shikaisha();
        kubojun.greet();
    }
}
                        
実行すると次のようになります。
はやく復帰しないかな の図
C:\Project\えっくす\test>java test.HelloMC
皆さんこんばんは、久保純子でーす

C:\Project\えっくす\test>
                        

かけもちしているプロジェクトは「さんでー」です。
このプロジェクトでは、先の「えっくす」で作ったクラスをちょこっと修正して利用しようと考えました。
新たに考えるのが面倒くさいのでパッケージ名やクラス名はおんなじでいきます。
C:\Project\さんでー\
┗━member\
┗━Shikaisha.class
という構成になっています。
クラスの中はこんな感じです。
最近やたらまるくなった「しんすけ」です
package member;

/**
 *  プロジェクトさんでーの司会者クラスでーす
 *  @author YTP
 */
public class Shikaisha {
    /**
     *  コンストラクタでーす
     */
    public Shikaisha() {
    }
    
    /**
     *  司会者が挨拶をします
     */
    public void greet() {
        System.out.println("皆さんおはようございます、伸助です");
    }
}
                        
「さんでー」司会者に挨拶をさせるクラス
package test;

import member.*;

/**
 *  司会者に挨拶をしていただきましょう!
 *  @author YTP
 */
public class HelloMC {
    public static void main(String[] args) {
        // 伸助さん出番でーす
        Shikaisha shinsuke = new Shikaisha();
        shinsuke.greet();
    }
}
                        
実行すると次のようになります。
えっ、くぼんじゅんが転職復帰?! の図
C:\Project\さんでー\test>java test.HelloMC
皆さんこんばんは、久保純子でーす

C:\Project\さんでー\test>
                        
なんじゃこりゃ? なんで伸助が出て来ないのぉ? 目が(・_・)です。

実はこれ、CLASSPATHの副作用です。CLASSPATHの設定を確認してみましょう。
えっ、くぼんじゅんが転職復帰?! の図
C:\Project\さんでー\test>set CLASSPATH
CLASSPATH=C:\Project\えっくす;C:\Project\さんでー

C:\Project\さんでー\test>
                        
えっくすが先に書いてありますね。
そのためjavaコマンドは先に見つかったC:\Project\えっくすの中のtest.HelloMCクラスを実行します。
試しにCLASSPATHの順を逆にして実行してみましょう。
冗談のようなほんとの話!! の図
C:\Project\さんでー\test>set CLASSPATH=C:\Project\さんでー;C:\Project\えっくす

C:\Project\さんでー\test>java test.HelloMC
皆さんおはようございます、伸助です

C:\Project\さんでー\test>
                        
ちゃんと伸助さんが挨拶してくれました。

さて、じゃぁ、私のようにこき使われる悲しきかけ持ち社員はどのようにしたらいいんでしょう?!
毎回 set CLASSPATHってやるのぉ?
いえいえ 解決策はちゃんとあります。しかも二通りもっ!!

■その一 オプション指定
javacやjavaには実行時にCLASSPATHを指定出来るようにちゃんとオプションが用意されています。
javac あるいは java -h
と実行するとそれぞれのオプションが確認出来ます。
javaの場合は-cpというオプションがそれです。やってみましょう。
くぼじゅん復帰はまだ先 の図
C:\Project\さんでー\test>set CLASSPATH=C:\Project\えっくす;C:\Project\さんでー

C:\Project\さんでー\test>java test.HelloMC
皆さんこんばんは、久保純子でーす

C:\Project\さんでー\test>java -cp C:\Project\さんでー test.HelloMC
皆さんおはようございます、伸助です

C:\Project\さんでー\test>
                        

■その二 CLASSPATHでのピリオドの利用 ←YTPのお薦め
CLASSPATHに.(ピリオド)を指定すると、 javacやjavaはカレントディレクトリを起点にパッケージを探してくれます。 つまり、現在扱っているプロジェクトのディレクトリのみを相手にしてくれるようになるんです。
ピリオドも試してみんとす の図
C:\Project\さんでー\test>set CLASSPATH=.

C:\Project\さんでー\test>java test.HelloMC
Exception in thread "main" java.lang.NoClassDefFoundError: test/HelloMC

C:\Project\さんでー\test>
                        
なにぃー、うそつきーっ! またエラーがでたやんけー!

ちょっと待って下さい、メッセージをよく読みましょう。
NoClassDefFoundError: test/HelloMC となっています。
CLASSPATHにピリオドを書くとそれはカレントディレクトリを意味するって言いましたよね? カレントディレクトリは今どこですか? そう、C:\Project\さんでー\test です。
ここにはtest/ なんてディレクトリがあるはずがありません。だって今そこにいるんだものっ!
だったら.....
ピリオドに感謝 の図
C:\Project\さんでー\test>cd ..

C:\Project\さんでー>java test.HelloMC
皆さんおはようございます、伸助です

C:\Project\さんでー\test>
                        

ピリオドが便利なのはここからです
ディレクトリを変更すれば-cpオプションなんくぁっまったく必要ありませんぜっ、だんなっ!
ピリオド様に大感謝 の図
C:\Project\さんでー>cd ..\えっくす

C:\Project\えっくす>java test.HelloMC
皆さんこんばんは、久保純子でーす

C:\Project\えっくす>
                        

ピリオドを使うとコンパイルのときにももちろん有効です
コンパイルしたいクラスがimportしている他のクラスを、 カレントディレクトリを基準にして探しに行きます。
ピリオド様はコンパイルもお手の物 の図
C:\Project\えっくす>javac test\HelloMC.java

C:\Project\えっくす>
                        
コンパイル対象のファイルにはカレントディレクトリからの相対パスも指定していることに注意して下さい。

CLASSPATHって?に戻ります。



Copyright © 2002-2016, Your Technology Partner. All rights reserved.

0 件のコメント:

コメントを投稿

About

参加ユーザー

連絡フォーム

名前

メール *

メッセージ *

ページ

Featured Posts