Home >> Columns >> Standard Java Programming
Java SEの実行形態は、次のような3つがあります。
- アプリケーション…そのコンピュータ上で稼働するプログラム
- アプレット…HTMLファイルと共に、サーバからクライアントに移動して稼働するプログラム
- JNLP…ネットワークからロードされて、そのコンピュータ上で稼働するプログラム
これらの実行形態の違いは、そのコンピュータ上のファイルや、 ネットワーク上のファイルサーバやWebサーバをアクセス(参照)するときの違いとして現れてきます。
そのコンピュータ上で実行されるのですから、ネットワークを介して他のサーバにアクセスすることや、 そのコンピュータ上のファイルを読み書きすることに一切の制限がありません(基本的には)。
WebサーバからHTMLファイルと共に運ばれてきてから、手元のコンピュータで実行されますので、 手元のコンピュータが持つファイルなどを参照することができません。セキュリティ的に問題になります。 また、Webサーバについても、アプレットが置かれているサーバのファイル(画像や音声も含む)を参照する ことだけはできます。それ以外のサーバについては、セキュリティとして事前に許されていない限り、 参照することはできません。また、サーバのファイルに書き込むことはできません。
Jarファイルと呼ばれる形式で、ネットワークからロードされます。基本的には、このJarファイルは 複数のファイルをまとめた「アーカイブ(Archive)」と呼ばれるファイルなのですが、このアーカイブの中に 含まれているファイルを読むことしかできません。
SFCのタートル・グラフィックスのアプリケーションは、基本的にはアプリケーションとして実行されています。 ですから、アプレットやJNLPのようなセキュリティ的な制限は基本的にはありません。 なお、最終制作課題として提出されるプログラムについては、 JNLPの形でネットワークから配布するようにしています。 そのため、ファイルを書き換えるようなプログラムについては、制限を設けるようにしています。 JNLPは、上記のような制約があるために、最も安全なプログラムの配布形態になっています。
基本的には、ファイルへの参照の制限以外の部分では、アプリケーションとJNLPでは、 記述方法の違いはなく、同じように記述することができます。
public class SampleApplication { public static void main(String [ ] args) { System.out.println( "Hello, Java" ); } }
System.out.printlnは、printLineの略なので、println(エルエヌ)と記述してください。 改行を伴わないターミナルへの表示は、System.out.printです。それ以外にも、C/C++言語で用いることが できるprintfと同じ機能をもった(表記方法について違いがあります)System.out.printfもあります。
上記の記述は、SampleApplicationという外部に公開された(public)クラスの定義です。 その中の、同じく外部に公開されたクラスメソッド(public static)で、値を返さない(void)メソッドである 「main」という名前のメソッドが呼び出されます。 C/C++のプログラムも、同じように「main」という名前の関数が呼び出されるところから、 このような記述の方法になっています。
アプレットの場合も、JNLPと同じく、Jarファイルの形式で置くことができます。あるいは、単体の クラスファイル(名前が.classという拡張子で終るファイル)でも置くことができます。記述に関しては、 プログラム上での違いはありません。しかしながら、アプレットは、HTMLと共に移動しますので、 HTML上での記述の仕方に違いが出てきます。
アプレットは、Webブラウザ上の表示領域にグラフィックの表示を行ないますが、その際に、 元より使われているAWT(Abstract Window Toolkit)ライブラリを使うのか、それを拡張した Swingライブラリを使うのかで、記述の仕方が異なってきます。 まずは、簡単なAWTライブラリを使う方法で記述してみましょう。
import java.applet.*; import java.awt.*; public class SampleApplet extends Applet { public void paint(Graphics g) { g.drawString("Sample Applet", 100, 100); } }
paintメソッドが、Webブラウザ上で再描画を要求される度に呼び出されます。 paintメソッドは、何回も呼び出されることに注意してください。呼び出されるときに、Webブラウザ上の アプレットに用意された描画領域が、Graphicsクラスのオブジェクト(プログラム中では、「g」という 変数(仮引数)と共に呼び出されます。Graphicsクラスのオブジェクトは、「drawString」などのメソッドを 持っており、これを使って描画領域に、文字列を描画します。この例では、座標(100, 100)の位置に、 「Sample Applet」と描画します。もちろん、文字列には日本語なども記述しても構いません。
上記のプログラムを今度は、Swingライブラリで記述してみます。
import java.applet.*; import java.awt.*; import javax.swing.*; public class SampleJApplet extends JApplet { public void paint(Graphics g) { super.paint( g ); g.drawString("Sample Swing Applet", 100, 100); } }
このレベルでは、AWT版とほとんど差がありませんが、まず、ライブラリを使うimport文が一つ多くなっているのと、 paintメソッドの最初に、super.paint(g);という記述が入っていることです。 また、継承される(extendsの後に置かれる)クラスは、Appletクラスではなくて、 「JApplet」クラスになっていることにも注意してください。
実行してみるとわかるのですが、Swing版のアプレットは、AWT版のアプレットと異なり、 プラットフォーム(コンピュータやOS)に依存した形で表示が行なわれます。
上記のプログラムがコンパイルされているとして、これを単体のクラスファイルとして稼働させる HTMLファイルは以下のように記述します。幅600、高さ500の領域をアプレットの描画領域として 割り当てています。
<applet code="SampleApplet.class" width=600 height=500>
同じプログラムがJar形式のファイルとして作成されている場合、これを稼働させる HTMLファイルは以下のように記述します。これは場合によって、いろいろな記述の仕方があります。
<applet archive="SampleApplet.jar" code="SampleApplet" width=600 height=500>
ウィンドウを表示するアプリケーションの場合は、ちょっと厄介です。 これは、むしろアプレットに近い記述をします。 アプレットと同じように、AWTライブラリを使うのか、Swingライブラリを使うのかで、 違いが出てきます。
import java.awt.*; public class SampleFrame extends Frame { public static void main( String [ ] args ) { new SampleFrame( ); // 自クラスのオブジェクト1つ作る } //SampleFrame クラスのインスタンスのためのコンストラクタ public SampleFrame( ) { super( "SampleFrame" ); setSize( 500, 500 ); // ウィンドウのサイズ setVisible( true ); // 表示させる } public void paint( Graphics g ) { g.drawString( "Hello, Frame Window", 100, 100 ); } }
Swingライブラリを使って、同じ内容のプログラムを記述してみましょう。
import java.awt.*; import javax.swing.*; public class SampleJFrame extends JFrame { public static void main( String [ ] args ) { new SampleJFrame( ); // 自クラスのオブジェクト1つ作る } //SampleJFrame クラスのインスタンスのためのコンストラクタ public SampleJFrame( ) { super( "SampleJFrame" ); setSize( 500, 500 ); // ウィンドウのサイズ setVisible( true ); // 表示させる } public void paint( Graphics g ) { super.paint( g ); g.drawString( "Hello, JFrame Window", 100, 100 ); } }
本来は、上記のプログラムは、ウィンドウリスナーをおいて、ウィンドウ上でウィンドウを 削除するボタン(WindowsやLinuxでは上部タイトルバーの右にある「×」印の付いたボタン、 Macintoshでは上部タイトルバーの左にある赤い丸ボタン)に対応しなければいけないのですが、 内容の都合上、その部分に関しては記述していません。 終了のさせ方は、Macintoshでは、アプリケーションメニューの「〜を終了(Command+Q)」か、 Windowsの場合は、タスクマネージャ(Control+Alt+Delete)を出して 終了させるようにして下さい。
Java言語上で用いる以下の小文字の予約語 (Reserved word:Java言語仕様ではキーワードKeywordと呼んでいます)は、 変数名などに使ってはいけません。以下のような予約語があります。
abstract assert boolean break byte case catch char class const continue default do double else enum extends final finally float for if goto implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while
次にJavaでは、次のような不文律があります。これは言語上の制約ではありませんが、多くのJavaの プログラマが守っている規則で、タートル・グラフィックスのライブラリでも、この規則に基づいていろいろな 名前が付けられています。これらの規則を理解していると、名前を見ただけで、それがどのような性質のものかが 他のプログラマ(あるいは将来の自分)にも一目瞭然となります。
- 「変数名」
- 基本的には1つのワードであることが多いです。名詞であることも多いです。あるいは、 「n」とか「i」とか「x」とか、単なる一文字の変数名もあります。 しかし、共通しているのは「小文字である」ということです。大文字で始まる変数名はつけてはいけません。
- 「クラス名」
- 大文字で始まり、その後は小文字になります。名詞であることが多いです。 複合語であるときは、その都度大文字になります。 たとえば、「TurtleFrame」などは、複合語ですが「Frame」の「F」も大文字になっています。 なお、複合語のときに間に空白は入れません。空白は名前の終わりと見なされます。
- 「クラス変数名」
- 大文字で始まります。これは、値を変えることができない、一定の値を持つ定数についた名前だと 思って下さい。C/C++言語との互換性を保つために、すべて大文字である場合もありますが、 大文字で始まって小文字であることもあります。タートル・グラフィックスでも色指定するときに「Red」などの 名前が使えますが、これもクラス変数名の1つです。基本的には変数名なので、名詞であることが多いです。
- 「メソッド名」
- 小文字で始まります。大文字で始まる場合はコンストラクタ(Constructor)という特殊なメソッドが言語上 規約されていますので、それとの混同を避けるために、小文字始まりになっています。「move」や「add」などの 動詞であることが多いようです。また、補語の名詞を伴う場合は、補語の部分が大文字始まりになります。例えば、 「drawLine」や「setStatus」みたいな感じです。また多くのライブラリでは補語を伴う場合は、 以下の規則があります。
- 何かの情報を返すときは、「get+補語」が使われる。たとえば、「getColor」など。
- 論理値を返すときは、まず、状態を返すときは「is+補語」とする。たとえば「isVisible」など。あるいは 何かを保持しているときは「has+補語」も使われる。たとえば「hasComponent」など。
- 状態を設定するときは、「set+補語」が使われる。たとえば、「setColor」など。
メソッド名については、プログラム上では、必ず丸括弧を伴って出てきますので、文章中でもメソッドで あることを明示するために名前だけでなく丸括弧をつけて記述している本も多いようです。 たとえば、「getColor」は、「getColor( )」という感じで記述されています。
プログラムの中で、例えば大文字から始まる「Turtle」と小文字から始まる「turtle」が出てきます。 これらは、上の規則から考えると、まったく異なる2つの別の内容を示しています。 大文字から始まる「Turtle」の方は、クラスの名前を指しています。しかし、小文字から始まる「turtle」 の方は、変数の名前になっています。大文字と小文字の区別に注意して下さい。
さらに、皆さんが注意しなければならないのは、クラス名をつけるときに、既存のライブラリの中にあるクラス名と 同じものをつけてしまうと、コンパイラが皆さんのつけたクラス名の方を優先させますので、特定の機能が 動かなくなることです。そこで、クラス名をつけるときのちょっとしたコツを教えます。たとえば、 「RandomNumberTester」みたいな感じで複合語にすることです。 BlueJでクラスを生成するときは、指定した名前がクラス名になりますので、入力する名前は、 大文字で始まっている必要があります。
加えて、かなりの注意が必要なのは、複合語の間には空白をいれないことです。 上記の「RandomNumberTester」を「Random Number Tester」と空白を入れてしまうとコンパイラが異なった 解釈をし始めます。また、Xcodeでプロジェクト生成するときも、注意されますし、BlueJでクラスを作成するときも だめだしを喰らいます。「名前は空白で区切らない」ことを憶えておいて下さい。
ユーザの応答に対応するプログラミングをよく理解するために、 MVCと呼ばれるプログラミング手法を知っておきましょう。
1973年Xerox PaloAlto研究所でALTOワークステーションの最初のマシンが開発されました。 このマシンは、現在普及しているパーソナルコンピュータの原型をなすものです。 ウィンドウシステムを持ち、マウスで情報を制御することができ、ネットワークで他のコンピュータと接続されていました。 Smalltalkは、ALTOワークステーションで稼働するプログラミング言語環境でしたが、 ここで後のウィンドウシステムのプログラミング方法に影響を与えるMVC(Model-View-Controller)という概念が確立しました。 ALTOワークステーションは、Alan KayのDynabook(東芝のその名前を借用したコンピュータとは関係がありません)の構想の実現のために作られたもので、6歳〜13歳ぐらいの子供にコンピュータを使わせて、コンピュータのユーザインターフェースやプログラミングの評価をしました。 もちろん、Smalltalkも子供たちが利用してさまざまなアプリケーションを作っていました。そのいくつかは、現在使われている主要なソフトウェアの原型となるものでした。
図A-1 Altoワークステーションと子供たち
MVCのプログラム方法では、表現したい対象をモデルと呼んでいます。 また、それが実際に画面に描画された表象を指してビューと呼びます。 そしてユーザと応対し、ビューやモデルを制御する機構をコントローラと呼んでいます。 このようにしておけば、モデルは1つでもあって、複数のビューを持つことができますし、 各ビューに対してコントローラを用意することもできます。
図A-2 MVCの例
この図の例では、モデルはある関連づけられたデータの集合を表しています。 ビューでは、それをグラフにして表すこともできますし、表計算ソフトウェアのように表すこともできます。 それぞれのビューには、対応するユーザインターフェースを実現するためのコントローラが用意されています。
ビュー上で、ユーザが何かの情報の更新を行なえば、コントローラからモデルに対して変更が行なわれます。 モデルの更新は、複数のビューが存在すれば別のビューにも反映されることになるでしょう。 例えば、上の例では表計算のセル(1つのデータを表す枠のことを指す)上で、データが変更されれば、 グラフにもその変更が反映されることになります。
Javaにおいて、MVCはどのように実現するのでしょうか? タートル・グラフィックスにおいては、ユーザとの対応をするコントローラは、 keyPressedメソッドを付加することにより実現しています。 なお、現在はプログラムを示すクラス自身がコントローラも兼ねていますが、Java言語のライブラリによっては、 別クラスに独立させることも可能になっています。
では、モデルとビューはどのように切り分けるのでしょうか? タートルを移動させるプログラムの例を取って考えてみましょう。
import sfc.turtle.TurtleFrame; import sfc.turtle.Turtle; public class MVCTester extends TurtleFrame { public static void main(String [ ] args) { new MVCTester( ); } Turtle turtle; int y; // タートルのy座標とする public void start( ) { turtle = new Turtle( this ); y = 250; // 最初のy座標は250とする while ( true ) { turtle.setY( y ); // yの示す値に、タートルの縦の位置を設定する update( ); sleep( 0.3 ); } } public void keyPressed( int keycode ) { y = y - 10; // yの値を10減らす if ( y < 0 ) { y = 250; } // yの値がマイナスになったら、250に戻す update( ); // 再描画させる } }
このプログラムにおいては、特に表現したいデータというものはありませんが、強いてモデルとビューに分けて考えると、インスタンス変数のyの値をタートルの縦方向の座標として表示したプログラムと捉えることができます。
モデル: インスタンス変数yが保持する整数値
ビュー: 整数値に対応した場所に表示されるタートル
コントローラ:キーが押されたときに行なわれる変数の値の更新と再描画(結果的にタートルの移動)
このように考えれば、別のビューを想定することも可能になってきます。 たとえば、ビューを整数値に対応したカラーの表示(タートル自身の色で表示される)としましょう。 startメソッドは次のように書き換えることができるでしょう。
public void start( ) { turtle = new Turtle( this ); y = 250; // 最初のyの値は250とする while ( true ) { turtle.setTurtleColor( new java.awt.Color( y, 0, 0 ) ); // yの値は赤色の強さを表す update( ); sleep( 1 ); } }
コントローラは変わっていませんが、ビューが上のように入れ換えられた場合、 キーが押されたときに色が変化することになります。 このように、ユーザとの応対を行なう複雑なプログラムを設計するときは、モデルとビュー、 およびコントローラに分けて、それらを相互連携させながらプログラミングを行なうことが重要になってきます。
タートル・グラフィックスのプログラムにおいては、次のようにMVCを実現するのが良いでしょう。
import java.awt.*; import java.awt.event.*; import java.applet.*; public class MultipleMouseDrag extends Applet implements ActionListener, MouseListener, MouseMotionListener { Rectangle rectangle [ ] = new Rectangle[ 50 ]; int count = 0; int startx, starty, endx, endy; public void init( ) { addMouseListener( this ); addMouseMotionListener( this ); Button button = new Button( "Reset" ); button.addActionListener( this ); add( button ); } public void paint( Graphics g ) { for ( int i = 0; i < count ; i ++ ) { g.drawRect( rectangle[ i ].x, rectangle[ i ].y, rectangle[ i ].width, rectangle[ i ].height ); } g.drawRect( smaller( startx, endx ), smaller( starty, endy ), difference( startx, endx ), difference( starty , endy ) ); } public void mousePressed( MouseEvent e ) { startx = endx = e.getX( ); starty = endy = e.getY( ); repaint( ); } public void mouseReleased( MouseEvent e ) { if ( count < 50 ) { rectangle[ count ++ ] = new Rectangle( smaller( startx, endx ), smaller( starty, endy ), difference( startx, endx ), difference( starty , endy ) ); } } public void mouseDragged( MouseEvent e ) { endx = e.getX( ); endy = e.getY( ); repaint(); } public void actionPerformed( ActionEvent e ) { startx = starty = endx = endy = 0; count = 0; repaint( ); } int smaller( int w, int u ) { return ( w < u ) ? w : u ; } // 小さい方を返します int difference( int w, int u ) { return ( w > u ) ? w - u : u - w; } // 差を返します public void mouseClicked( MouseEvent e ) { } public void mouseEntered( MouseEvent e ) { } public void mouseExited( MouseEvent e ) { } public void mouseMoved( MouseEvent e ) { } }プログラムでは、smallerとdifferenceという2つのメソッドを自作で定義して、条件分岐をする手間を省いています。このような自作のメソッドの記述の仕方は次の章で説明します。これらの2つのメソッドで、短くプログラムを記述することができています。また、ボタンを1つ用意して、押されたら、それまで覚えていた内容を全部リセットしています。
⋏ Return to Columns | >>Java Arrays and Strings |