応用プログラミング 第1回


この授業の目的

授業の概要


出席メール

この授業では出席メールで授業中の課題の実施を確認します.メールを出した時刻,PCの場所(アドレス)を確認します.

to:jhoshino@esys.tsukuba.ac.jp
subject: 応用プログラミング 第1回.氏名,学籍番号
本文: なし(書かなくて結構です)


なぜ Java を使うか?


 
理論

JAVAとは

Javaは,オブジェクト指向型のプログラム言語で,作成されたプログラムはプラットフォーム(実行するためのハードウェアとソフトウェア環境)に依存することなく実行できる。つまり,パソコンや携帯電話といったハードウェアの違いと,Windows,UNIXやMacOSといったOSの違いによらず同じプログラムを実行できるということで,このような性質を"write onece,run anywhere"と呼ぶ。

CやC++言語によく似た構文を持つJavaで書かれたソースプログラムは,「Javaコンパイラ」によってコンパイル(翻訳)されるとき,「バイトコード(中間コード)」という形式に変換される。そのバイトコードはプラットフォームごとに用意されたインタープリタ形式の「Java仮想マシン(JVM:Java Virtual Machine)」上で実行される。また,実行速度を上げるためにJust In Time コンパイラを使用してバイトコードを実行するコンピュータの機械語に変換してから実行する仕組みも用意されている。

JAVAプログラムの種類

Javaで作成されるプログラムは「アプリケーション」と「アプレット」に分けられる。

アプリケーションはJava仮想マシン上で直接実行できる。一方,アプレットはJava仮想マシンを組み込んでいるWWWブラウザ上で実行される。したがって,Webページ上のリンクによってWWWサーバからユーザのコンピュータにダウンロードして実行させることができる。

その他プログラム部品として他のJavaプログラムが利用できるBeans,電化製品などの制御に用いる組込み型,サーバ上で動作するサーブレットがある。


オブジェクト指向の考え方

 オブジェクト指向とは,プログラミングを容易にするために人間が現実の世界を理解する単位である「もの(オブジェクト)」を,ソフトウェア上の仮想的な世界に作り出すことから始まった。 オブジェクトは「属性(プロパティ)」と「機能」をもっている。例えば自動車というオブジェクトの場合,色,デザインなどが属性で,ハンドルを回せば進行方向が変わるというのが機能である。ソフトウェアのオブジェクトでは,属性を「変数」,機能を「メソッド」として記述する。 一つのオブジェクトは属性と機能をひとまとめにして構成されおり,これを「カプセル化」という。これによって,オブジェクトの内部を外部から見えなくし,内部の構造を知らなくても利用できるようにしている。

クラス 自動車

[変数] 車種
[変数] 燃料の残量
[メソッド] 走る
[メソッド] 止まる

オブジェクトとクラスの関係

クラスはデータの持つべき性質を定義したものです。 たとえば「学生」を表すクラスを設計するとしましょう。 クラスの内部のメンバーとして、 「学年」、「所属学部・学科」、「学籍番号」、「氏名」「年齢」など 多くの情報が定義されるでしょう。 しかし、 クラスそのものは1人1人の学生の実体に対応しているわけではありません。 クラスの情報そのものだけではなく、 個別の情報を記憶した存在が必要となります。 これらの個々のデータのことを、 クラスとは区別して「オブジェクト」と呼ぶことにしましょう。 (ここで言う「オブジェクト」の代わりに、 「インスタンス(instance)」という用語もしばしば用いられます。 以下の説明では、「オブジェクト」は「インスタンス」と同じ意味で用います。 同様に「クラス」は「クラスの定義」と同じ意味だと考えてください。)

クラスとオブジェクトの違いをはっきり意識することは非常に大切です。 工業製品を比喩に用いるならば、クラスは製品の仕様を定めた設計図であり、 オブジェクトが1個1個の製品と言えるでしょう。 一般にプログラム内では、 1つのクラスを元にして複数のオブジェクトが生成され、 利用されるのが普通です。

Javaのプログラムの中では、 次のようにクラスは型としてオブジェクトは変数として表現されます。

 


JAVAアプリケーションの構造

 JAVAアプリケーションはロードされたときに,「mainメソッド」が最初に呼び出されて実行される。従って,アプリケーションには必ずmainメソッドが必要である。

/* HelloWorldを表示するためのプログラム(Application) */
class HelloWorld{
 // C言語でメイン文に相当する
  public static void main(String args[]){
    System.out.println("Hello World");
  }

}

 

 

1行目:「これから Hello という名前のクラスを宣言する」ことを意味する.
class は Java 言語で特別な意味をもつ予約語であるが,今は一種のオマジナイだと思っておいて良い.ここで特に大事なのは,
 * Java 言語では,大文字と小文字の区別は極めて重要.
 * クラスの名前は大文字で始めるのが慣習.
 * クラスの名前 Hello は,このプログラムを保存したファイル名の Hello.java の始めの部分と一致.
 * ここでの中括弧 { } は,クラスの宣言の範囲を示す.
である.以上をまとめると以下の通り.

class クラス名 {
(ここにクラスの宣言内容が書かれる)
}


* 2行目:「main という名前をもつメソッドを宣言する」ことを意味する.
public static void もまた予約語.メソッドはクラスに附属する関数のようなもの.
括弧で与えられる (String[ ] args) は,main メソッドに与えられる入力.
ここで特に大事なのは以下のこと.
 * Java プログラムは,main メソッドから開始する.
 * ここでの中括弧 { } は,main メソッドの宣言の範囲を示す.
以上をまとめると以下の通り.

class クラス名 {
  public static void main(String[] args) {
 (ここに main メソッドの宣言内容が書かれる)
 }
}


* 3行目:「"Hello World!"という文字列を表示して終了する」ことを意味する.
System.out.println の部分は,文字列を画面(標準出力)に表示するメソッドを表す.続いて登場する括弧の中身は,そのメソッドへの入力(引数と呼ぶ)を示す."…" は二重引用符と言い,Java 言語では文字列を表す.最後にある ; (セミコロン)は,処理の一区切りを表す(これを忘れるとコンパイル・エラーが生じるので,要注意!).


クラスとプログラムの構造

オブジェクト指向(Object Oriented)のプログラミングの特徴と、 オブジェクト指向プログラミングそのものを説明に入る前に、 従来のプログラムがどんな組立てになっていたかを簡単に復習しておきましょう。 ここでは(初期の)BASIC と C を例として見てみます。

たとえばディスプレイに "Welcome to Java!" というメッセージを表示させる プログラムを作りたいとします。 BASICであれば次のようなたった1行のプログラムで実現が可能です。


print "Welcome to Java!"

ところが Cの場合は次のように多少付け加える必要が出てきます。


void main( int argc, char *argv[] ) {
 
      printf( "Welcome to Java!" );
}
 

BASICのプログラムは、いわば「裸」の状態です。 実行したい命令(画面へのメッセージの表示)そのものしか記述する必要はありません。 Cの場合にはそれだけでは済まなくなります。 関数 main() という構造が登場します。 中括弧 "{" と "}" で囲まれた範囲が関数 main()の内部です。 関数という「入れ物」が用意され、 実行したい命令はその内部に記述されます。 関数の存在は慣れてしまえば当たり前のものです。 しかし初めて上のプログラムを見る人は、 なぜこのような形式が存在するのか疑問に思うかもしれません。 その理由を知るためには、 プログラムがもっと大規模になり、 何百行、何千行にものぼるような場合を考えてみる必要があります。

BASICのプログラムでは goto文を用いることで、 ある行からプログラム内のどんな場所へもジャンプすることができます。 これはちょっと考えると便利に思えるでしょう。 何か新しい必要が生じたら、すぐその場で付け加えることができるからです。 確かにプログラムの規模がきわめて小さい場合には問題はありません。 プログラム全体を把握することが可能だからです。 しかし、プログラムの規模が大きくなってくるとたちまち行き詰まります。 goto文を多用したプログラムは、 どの部分とどの部分が互いに関連し合っているのか見えにくくなりがちです。 十分注意をはらわないと、 いわゆる「スパゲティ・プログラム」と呼ばれるやっかいものになってしまいます。 結果として思いがけないバグが発生することになり、 修正の作業も大きな負担となります。 原因となる小さなミスを発見するために、 プログラムの隅から隅まで目を通さなければならないからです。

この事態を解決する手段として導入されたのが「構造化」と呼ばれる手法でした。 一連の手続きを「関数(もしくはサブルーチン)」という形式にまとめ、 さらに条件分岐や繰り返しなどの処理の適用範囲も 「ブロック」という構造にまとめます。 それらを階層的に組み立てる形でプログラムが記述されます。 Cの場合、プログラムの中で一番大きな構造は関数になります。 構造化の導入で原理的には goto文の追放が可能となりました。 Cにも goto文は存在しますが、ごく例外的な場面でしか用いられません。 また Cの goto文には、関数の外にはジャンプできないという強い制約があります。 したがって関数の処理を急に途中から始めたりはできません。 必ず関数の呼び出しという形式で関数を丸ごと指定しなくてはいけないのです。 この制約のおかげで処理の流れの見通しがよくなり、 デバッグの負担も大きく軽減されることになりました。

さて、前置きが多少長くなりましたが、今度は Javaの場合を見てみましょう。 BASICやCのサンプルほど単純ではありません。 それは次のようになります。


public class Welcome {
 
       public static void main( String argv[] ) {
 
              System.out.println("Welcome to Java!");
       } 
}
 

これまでの3つの言語のプログラムを比較すると、 なんとなく「言語の進化」のようなものが見えてくるでしょう。 Cでは最も大きな構造は関数でしたが、 Javaの場合には Cの関数に相当するもの(メソッドと呼ばれます)の外側に さらに大きな「入れ物」が存在しています。 この大きな構造が「クラス(class)」なのです。


プログラム言語の構造の進化

BASIC と C の比較でもそうでしたが、 プログラムの規模が小さいうちは、 クラスを導入する意味があまりよくわからないかもしれません。 むしろ余計に覚えなければならないルールが増えただけのように感じられるでしょう。 これはオブジェクト指向の言語にとって避けられない宿命です。 「簡単だ」と言われる Javaの場合であってもそれは変わりません。 小規模なプログラムを作るだけならば、 Javaよりも Cの方が(そしてさらに BASICの方が) 短くてわかりやすいものになるでしょう。 しかし、ある規模以上のプログラムになると立場は逆転します。 クラスを導入する意味はいろいろあるのですが、 ここでは大きな「入れ物」を用意するという観点からまず見てみましょう。

Cでは、プログラムを構成する関数はすべて「平等」です。 同じプログラム内のある関数から別の関数を呼び出すことは全く自由に行えます。 これは BASICで行単位のジャンプを自由に行えたことを連想させます。 もちろん プログラムが行単位で入り組んでいる場合に比べればはるかにましですが、 プログラムの規模がさらに巨大になってきたらどうなるでしょうか?  関数の数が何百個にもなれば、 お互いにどう呼び出し合っているかの関係をチェックするだけで大変な作業です。 無計画にプログラムを作ってしまうと、 結局はデバッグやバージョンアップのたびにプログラム全体を チェックし直さなければなりません。

クラスという新しい「入れ物」は、このような混乱を自然な形で解決してくれます。 特定のデータや手続きに対して、 クラスの内部でしかアクセスできないように コントロールすることが可能になるからです。 その結果としてプログラムの部分部分の関係が明確になり、 アプリケーションの開発や保守の効率が向上するでしょう。
C などにおける開発でも、 大規模なプログラムは分割して取り扱うのが普通です。 しかし、その仕組みは言語の文法とはあくまで独立したものです。 ライブラリ化したモジュールの管理も、 システムごとに異なったルールに基づいて行われることになります。 プログラマは言語の文法以外にも、 それらのルールを「開発手法」として学習しなくてはいけません。

これに対して Javaのクラスやパッケージの考え方は、 モジュールへの分割やその管理のルールを言語仕様そのものとして取り入れたものです。 モジュール管理は半ば自動化されて作業は著しく軽減されてます。 そうしたルールはどんなシステムであっても共通なため、 改めて学習し直す必要もありません。


基本的なデータ型

JAVAのデータ型はC言語と類字しています.

character 16ビットUnicodeキャラクタデータ char
boolean 真偽値 boolean
byte 8ビット符号付き整数 byte
short 16ビット符号付き整数 short
integer 32ビット符号付き整数 int
long 64ビット符号付き整数 long
float 32ビット符号付き浮動小数点 float
double 64ビット符号付き浮動小数点 double

 

/* DataType.java 基本データ型の紹介プログラム */

class DataType{
 public static void main(String args[]){
   char ch = 'c';
   boolean booltrue = true;
   boolean boolfalse = false;
   byte bt = 127;
   short s = 12767;
   int i = 2147483647;
   long l = 3223372036854775807L;  //long型のデータを初期化するときにはLを付けます.
   float f = 3.4232454f;         //floatの初期化はfを付けます.
   double d = 1.4976931348623153;
   System.out.println("char "+ch);
   if(booltrue) System.out.println("boolean "+booltrue);
   if(!boolfalse) System.out.println("boolean "+boolfalse);
   System.out.println("byte "+bt);
   System.out.println("short "+s);
   System.out.println("int "+i);
   System.out.println("long "+l);
   System.out.println("float "+f);
   System.out.println("double "+d);
 }
}

配列の使い方

配列の定義はint x[] = new int[N]のように書きます.

class ArrayTest1{
  public static void main(String args[]){
    int test3[] = new int[3];
    test3[0] = 70;
    test3[1] = 90;
    test3[2] = 85;
    for(int i=0;i<3;i++){
      System.out.println("Pattern3 "+test3[i]);
    }
    //最初に配列を初期化する
    //例:初期値が分かっている場合
 int test4[] = {70,90,85};
    for(int i=0;i<3;i++){
      System.out.println("Pattern4 "+test4[i]);
    }
  }
}
forループの扱い

class ForTest{
  public static void main(String args[]){
    // forループの扱い
    //C言語では次のように先に変数を定義しておきループで使用する
    int i = 0;
    for(i=0;i<3;i++){
      System.out.println("C_Loop "+i);
    }
    //Java言語では次のようにループ内で変数を定義して使用できる
    for(int j=0;j<3;j++){
      System.out.println("Java_Loop "+j);
    }
  }
}
実習

JAVAプログラムのコンパイルと実行

この授業ではプログラムを作成して貰いながら説明をしますので,作業用のディレクトリを予め作っておいて下さい.名前は何でも良いのですが,例えば,~/java というディレクトリを作ります.


cd ~

mkdir java

cd java

次のサンプルプログラムを入力して, HelloWorld.java というファイル で保存してみましょう.
このテキストは全角の空白などが含まれているため,cut&pasteをするとエラーになるので,muleなどのエディタを使って自分で打ち込むこと.

コンパイル

ファイルを保存したら, ktermのwindowでUnixコマンドの ls を実行して, ファイルができていることを確認する. コンパイラを起動するためには,windowで次のように入力する.

javac HelloWorld.java

正しくソースファイルが作られている場合は, 何もメッセージを出さずに終了 する. メッセージが出る場合は, どこかに問題があるので,ソースコードをもう一度見直すこと.

実行

上のプログラムをjavacコマンドを使って,コンパイルすると「クラス名. class」というファイルが作られる.実行のためのコマンドは java という名前である. コンパイルしたプログラムは,

java クラス名

とすると,そのクラスの定義中の main という名前のメソッド(名前だけでな く,型定義も一致する必要はあるが)から実行を開始する.この場合は,

java HelloWorld

と入力すると,

Hello World

と出力されるはずである.プログラムの内容をもう一度確認すると,mainのメソッドの定義は ,

System.out.println("Hello World");

という文からなる. System.out.printlnというのは「Systemというクラ スのstatic データである out というPrintStreamのprintlnというメソッド」 というものを意味するが, 今のところは一まとまりで「文字列(String)を改行 つきで出力する命令」と考えて良い. 後で説明する制御文などでない普通の文 の並びは上から順に実行されるので,

Hello World

のように実行される.

プログラムを変更する

サンプルのプログラムを変更してみよう. まず, printlnと良く似たメソッド として, printというものがあるのでそれを使ってみる. エディタを使って, 最初のprintlnをprintに変更してみよう

class HelloWorld{
  public static void main(String[] argv) {
   System.out.print("Hello World");
   System.out.println("Hello Again");
   }
}

ファイルを保存してから,

javac HelloWorld.java

としてコンパイルしてから

java HelloWorld

を実行してみよう. 今度は,

Hello WorldHello Again

と表示される.

数字の表示

次に,数字を表示させるプログラムを書いてみます.

class PrintInt{
  public static void main(String[] args){
    System.out.print("1分は");
    System.out.print(60);
    System.out.println("秒です.");
  }
}

このように,
System.out.print(60);
として数字60を表示できる.これは,
System.out.print("60");

とどこが違うのだろうか.出力結果は変わらないが,文字列と数値は意味が違う.文字列の「"60"」は,「'6'」という文字の後に「'0'」という文字が並ん だ列を表しているのに対し,数値「60」は60という数を表す.


次に,例題のプログラムを入力して出力結果を確認して下さい.
練習問題

 

forループを利用して右の図形(底辺が10の三角形)を表示するJAVAアプリケーションを作成して下さい.

ソースコードを:jhoshino@esys.tsukuba.ac.jpに送って下さい.Subject欄に「応用プログラミング 第1回課題.氏名,学籍番号」と書いて下さい.

締め切りは次の授業の開始時まで.

ソースコードはメールに添付あるいは本文中にコピーのどちらでも良い。

 

*
**
***
****
*****
******
*******
********
*********
**********

 


参考資料

コメント(注釈)

プログラムは,コンパイラが処理して実行するためのものですが,後から見ても分かるようにコメントを書いておくことをお勧めします.

先ほどのプログラムにコメントを加えたものが以下のプログラムである.コンパイル結果 ,実行結果はこれまでと変わらない.

class Test { // クラス Test の定義の開始
    // メソッド main の定義の開始
  public static void main(String[] argv){
      // "Hello World"という文字列を改行付で出力する.
    System.out.println("Hello World");
      // "Hello Again"という文字列を改行付で出力する.
    System.out.println("Hello Again");
  } // メソッド main の定義の開始
}// クラス Test の定義の終了

 

配列の使い方


//全てのパターンで同じ結果を出力する
class ArrayTest1{
  public static void main(String args[]){
    //パターン1  先に配列を宣言して,後に配列の要素を準備する
    //例:機能としてはこの変数が必要だと分かっているが,内容はまだ決まっていない場合,
    //クラスの変数として宣言し,後から初期化する場合など
 int test1[];
 test1 = new int[3];
    test1[0] = 70;
    test1[1] = 90;
    test1[2] = 85;
    for(int i=0;i<3;i++){
      System.out.println("Pattern1 "+test1[i]);
    }
    //パターン2 まとめて準備する
    //例:メソッドの内部変数
    int test2[] = new int[3];
    test2[0] = 70;
    test2[1] = 90;
    test2[2] = 85;
    for(int i=0;i<3;i++){
      System.out.println("Pattern2 "+test2[i]);
    }

    //パターン3 まとめて準備する
    //パターン2との違いは[]の位置 
    //パターン2はint型の配列[],パターン3はint[]型と解釈だけの違いで効果は同じなので好みで用いると良い
    int[] test3 = new int[3];
    test3[0] = 70;
    test3[1] = 90;
    test3[2] = 85;
    for(int i=0;i<3;i++){
      System.out.println("Pattern3 "+test3[i]);
    }

    //パターン4
    //最初に配列を初期化する
    //例:初期値が分かっている場合
 int test4[] = {70,90,85};
    for(int i=0;i<3;i++){
      System.out.println("Pattern4 "+test4[i]);
    }
  }
}


//配列の要素数(長さ)
class ArrayTest2{
  public static void main(String args[]){
    //例えばC言語では,汎用的なプログラムを書くときなどに配列の要素数を複数の場所で参照したい場合,
    //次のように定数をN=3などとして配列の要素数を別の変数(ここではN)に保存しておく必要がある
    int N = 3;
    int test_c[] = new int[N];
    test_c[0] = 70;
    test_c[1] = 90;
    test_c[2] = 85;
    for(int i=0;i<N;i++){
      System.out.println("C "+test_c[i]);
    }
    //Java言語では,配列の要素数がint型変数lengthに保存される
    //配列名.lengthで扱うことができる
    int test_java[];
    test_java = new int[3];
    test_java[0] = 70;
    test_java[1] = 90;
    test_java[2] = 85;
    for(int i=0;i<test_java.length;i++){
      System.out.println("Java "+test_java[i]);
    }
  }
}