[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

情報処理IIIA(Javaプログラミング入門)第11回

目次 索引
11.1 データ型
11.1.1 データ型とは
11.1.2 整数と浮動小数点数
11.1.3 真理値とブール式
11.1.4 文字
11.2 インスタンスメソッド(1)
11.2.1 メッセージ受渡しモデル
11.3 クラス変数
11.3.1 大域変数としてのクラス変数
11.3.2 定数としてのクラス変数
11.4 インスタンスメソッド(2)
11.4.1 インスタンスメソッドの使用例
11.5 演習11
11.6 レポート課題
ASCII  Unicode  基本データ型  局所変数  クラス変数  参照データ型  真理値  大域変数  定数  データ型  バイト  ビット  ブール式  浮動小数点数  メッセージ受渡しモデル  文字符号  論理型 

11.1 データ型

11.1.1 データ型とは

データ型data type ) とは、データの種類のことです。 これまでに、整数、配列、およびクラスの3種類のデータ型を扱ってきました。 Java言語には、この他のデータ型も用意されています。

Java言語のデータ型は、まず基本データ型と参照データ型の2つに大きく分類されます。 基本データ型primitive data type ) には、整数型、浮動小数点数型、論理型、および文字型があります。 参照データ型reference data type ) には、クラス型と配列型があります。 以下は基本データ型の詳細です。

表 11.1  Java言語の基本データ型
種類 型名 ビット 説明
整数型 byte 8 -27(-128)〜27- 1(127)
整数型 short 16 -215(-32768)〜215- 1(32767)
整数型 int 32 -231〜231- 1(9桁程度)
整数型 long 64 -263〜263- 1(18桁程度)
浮動小数点数型 float 32 指数+38〜-45, 有効桁数7桁程度
浮動小数点数型 double 64 指数+308〜-324, 有効桁数14桁程度
論理型 boolean - trueまたはfalse
文字型 char 16 \u0000(0)〜\uFFFF(65535)

コンピュータのデータの単位は ビットbit ) であることを思い出してください。 ただし、これは理論上の話です。 現実のコンピュータ・システムは、 バイトbyte ) を単位としています。 1バイトは8ビットです。 基本データ型が8ビット単位であるのは、ここに理由があります。

11.1.2 整数と浮動小数点数

整数を扱う場合、普通は int 型を用います。 ただし、 int 型は9桁程度が限界であり、これを超えますと意味のない計算をしてしまいます。 int 型の範囲を超えるような大きい数を扱うときに、 long 型を用います。

浮動小数点数floating point number ) とは、小数点をずらすことによって、非常に大きな数や非常に小さな数も表現できるようにした小数のことです。 浮動小数点数を扱う場合、普通は double 型を用います。 限られたビット数で表現する以上、小数点をずらす桁数には限度がありますし、小数の精度にも限界があります。

11.1.3 真理値とブール式

論理型logical type ) とは、真理値を表わすデータ型です。 ここで 真理値truth value ) とは、真を表す記号 true と偽を表す記号 false のことだと思ってください。 Java では、型名 boolean で論理型を表わします。 値が論理型である式を、 ブール式Boolean expression ) とよびます。 これまで「条件が成り立つ」、「条件が成り立たない」と言ってきたことは、正確にはブール式の値が true になること、false になることです。

11.1.4 文字

Javaは、文字( char 型)を1バイトではなく2バイトで表現します。 これは、英数字だけではなく、漢字なども文字として扱おうという設計です。 なお、ちょうど1バイトで表現されるデータ型として、バイト( byte )が用意されています。

文字は、内部では整数として処理されます。 この、文字から整数への対応を 文字符号character code ) と呼びます。 Javaの採用している文字符号は、 Unicode と呼ばれる体系です。 Unicodeは ASCII という体系の拡張になっています。 主な文字のASCII符号は次の通りです。

表 11.2  主な文字のASCII符号
制御文字 水平タブ 改行 復帰 スペース
表示 '\t' '\n' '\r' ' '
符号 9 10 13 32
文字 ! " # $ % & ' ( ) * + , - . /
符号 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
文字 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
符号 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
文字 @ A B C D E F G H I J K L M N O
符号 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
文字 P Q R S T U V W X Y Z [ \ ] ^ _
符号 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
文字 ` a b c d e f g h i j k l m n o
符号 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
文字 p q r s t u v w x y z { | } ~
符号 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126

プログラムの中で文字を表す場合、その文字をシングルクオート( ' )で囲みます。 ただし、シングルクオート自身、およびダブルクオート( " )、バックスラッシュ( \ )については、それぞれ '\'' , '\"' , '\\' とします。


11.2 インスタンスメソッド(1)

11.2.1 メッセージ受渡しモデル

以前、メソッドにはインスタンスメソッドとクラスメソッドがあると言いました。 これまではクラスメソッドのみを使ってきましたが、Javaらしいプログラムではインスタンスメソッドのほうがよく用いられます。 クラスメソッドと比較しながら、インスタンスメソッドについて説明します。

前回、以下のようなプログラムを紹介しました。

Time.java
/*  1*/ class Time {
/*  2*/     int hour, minute;
/*  3*/ }
TimeTest5.java
/*  1*/ class TimeTest5 {
/*  2*/     public static void main (String[] args) {
/*  3*/         Time x = new Time();
/*  4*/         setHour(x, 14);
/*  5*/         setMinute(x, 45);
/*  6*/         System.out.println(getHour(x) + ":" + getMinute(x));
/*  7*/         setMinute(x, 30);
/*  8*/         System.out.println(getHour(x) + ":" + getMinute(x));
/*  9*/     }
/* 10*/     static int getHour (Time t) {
/* 11*/         return t.hour;
/* 12*/     }
/* 13*/     static int getMinute (Time t) {
/* 14*/         return t.minute;
/* 15*/     }
/* 16*/     static void setHour (Time t, int hour) {
/* 17*/         t.hour = hour;
/* 18*/     }
/* 19*/     static void setMinute (Time t, int minute) {
/* 20*/         t.minute = minute;
/* 21*/     }
/* 22*/ }

これを、インスタンスメソッドを用いて書きなおしますと、次のようになります。

Time.java(第2版)
/*  1*/ class Time {
/*  2*/    int hour, minute;
/*  3*/    int getHour () {
/*  4*/       return this.hour;
/*  5*/    }
/*  6*/    int getMinute () {
/*  7*/       return this.minute;
/*  8*/    }
/*  9*/    void setHour (int hour) {
/* 10*/       this.hour = hour;
/* 11*/    }
/* 12*/    void setMinute (int minute) {
/* 13*/       this.minute = minute;
/* 14*/    }
/* 15*/ }
TimeTest5.java(第2版)
/*  1*/ class TimeTest5 {
/*  2*/    public static void main (String[] args) {
/*  3*/       Time x = new Time();
/*  4*/       x.setHour(14);
/*  5*/       x.setMinute(45);
/*  6*/       System.out.println(x.getHour() + ":" + x.getMinute());
/*  7*/       x.setMinute(30);
/*  8*/       System.out.println(x.getHour() + ":" + x.getMinute());
/*  9*/    }
/* 10*/ }
b00a001@AsiaA1:~/java% java TimeTest5
14:45
14:30
b00a001@AsiaA1:~/java%

まず、メソッド定義を実行プログラム( main )には並べて書かず、フィールド定義に並べて書きます。 レコード型としてのクラスはフィールドとコンストラクタを定義しますが、一般のクラスはさらにメソッドも定義します。 メソッドは、実行プログラムよりもデータ(インスタンス)に強く結び付くと考えます。

次に、キーワード static を取り除きます。 メソッド定義で static と書くとクラスメソッド、書かないとインスタンスメソッドになります。

メソッド呼出しの形も変えます。 値を返さないインスタンスメソッドを呼び出すには、文

instance.methodname(argument, ...);

を用います。 ここで instance は、インスタンスを値とする式です。 例えば、インスタンス(への参照)が格納された変数などです。 値を返すインスタンスメソッドを呼び出すには、式

instance.methodname(argument, ...)

を用います。 なお、括弧がなければインスタンスのフィールドからデータを取り出す形になります。

この式 instance は、メソッド呼出しの実引数としての役割を果たします。 この実引数に対応する仮引数は、メソッド定義でのキーワード this です。 メソッド呼出しの際、 this にはインスタンス instance への参照がコピーされます。

インスタンスメソッドとは、引数の中からインスタンスを一つ選び、それを特別扱いしたものと考えてください。 特別扱いされたインスタンスは、実引数としては、括弧の中ではなく、メソッド名の左に書かれます。 仮引数としては、括弧の中では定義されず、キーワード this で表されます。

この、引数の一つのインスタンスを特別扱いすることは、 メッセージ受渡しモデルmessage-passing model ) に基づいています。 これは、データ(インスタンス)がメッセージを受け取ると、それに応じて操作(メソッド)が引き起こされるという考え方です。 Javaでのメッセージは、メソッド名のことだと思ってください。 インスタンスにメッセージを送りますと、そのインスタンスのクラスで定義されたメソッドが起動されるということです。 メッセージ受渡しモデルに従いますと、上記のメソッド呼出しは、インスタンス instance にメッセージ methodname ( argument , ... ) を送ることに相当します。

インスタンスメソッドにできて、クラスメソッドにできないことのひとつに、メソッド定義で this 参照を用いることがあげられます。 this 参照は、インスタンスメソッドの呼出しのときにメッセージを受け取ったインスタンスを指します。 クラスメソッドでは、メッセージを受け取るインスタンスが存在しませんので、 this 参照が使えないのです。


11.3 クラス変数

11.3.1 大域変数としてのクラス変数

これまで説明した通り、変数は宣言すれば使えます。 しかし、いつまでも使い続けられるわけではありません。 例えば、メソッド定義

static void printStars (int m) {
    int i;
    for (i = 0; i < m; i++) {
        System.out.print("*");
    }
}

での変数 i は、メソッド呼出しの際に用意されます。 しかし、この呼出しが終了しますと、変数 i は使えなくなります。 このような変数は 局所変数local variable ) とよばれます。

データを局所変数に格納しても、他のメソッドからはそのデータは利用できません。 複数のメソッドの間でデータを共有するには、大域変数というものを用います。 大域変数global variable ) とは、プログラムのどこからでも使える変数です。 あるメソッドで大域変数にデータを格納し、他のメソッドでそれを取り出すことができます。

実は、Javaには大域変数は用意されていません。 これは、クラス変数というもので代用します。 クラス変数class variable ) とは、クラスに割り当てられる変数と考えてください。 クラス変数と対照的なのがインスタンス変数(すなわちフィールド)です。 クラスのインスタンスを生成しますと、インスタンスごとにインスタンス変数が用意されます。 一方、クラス変数は、インスタンスをいくら生成しても、クラスにひとつだけ用意されます。 次の図は、クラス変数とインスタンス変数のイメージです。

Class variables and instance variables
図 11.1  クラス変数とインスタンス変数

クラス変数の宣言は、フィールドと同じように行います。 フィールドとの違いは、キーワード static を書くところです。

クラス class のクラス変数 variable にアクセスするには、 class . variable と書きます。 ただし、同じクラスからアクセスする場合は、 variable だけで十分です。

次のプログラムは、出勤時刻に関して遅刻の回数を数えるものです。 Attendance.javaの3行目で、クラス変数 lateCount を用意します。 10行目では、単に lateCount でアクセスできますが、AttendanceTest.javaの10行目では Attendace.lateCount と指定します。

Attendance.java
/*  1*/ class Attendance {
/*  2*/     int hour, minute;
/*  3*/     static int lateCount = 0;
/*  4*/     Attendance (int hour, int minute) {
/*  5*/         this.hour = hour;
/*  6*/         this.minute = minute;
/*  7*/     }
/*  8*/     void checkLate () {
/*  9*/         if (540 < 60 * this.hour + this.minute) {
/* 10*/             lateCount++;
/* 11*/         }
/* 12*/     }
/* 13*/ }
AttendanceTest.java
/*  1*/ class AttendanceTest {
/*  2*/     public static void main (String[] args) {
/*  3*/         int i;
/*  4*/         Attendance[] atts = {new Attendance(8, 55),
/*  5*/                              new Attendance(9, 10),
/*  6*/                              new Attendance(9, 5)};
/*  7*/         for (i = 0; i < atts.length; i++) {
/*  8*/             atts[i].checkLate();
/*  9*/         }
/* 10*/         System.out.println(Attendance.lateCount);
/* 11*/     }
/* 12*/ }
b00a001@AsiaA1:~/java% java AttendanceTest
2
b00a001@AsiaA1:~/java%

11.3.2 定数としてのクラス変数

大域変数を用いて定数を表すことがよくあります。 定数constant ) とは、常に変わらない値のことです。 プログラムに定数を直接書きますと、それが何を表しているのか分かりにくいものです。 そこで、大域変数に定数を代入しておき、定数の代りに変数名を書きます。 定数に分かりやすい名前をつけ、プログラムを読みやすくするのです。

定数を表す変数へは、新たな代入が行われないようにしなくてはいけません。 変数を宣言するときに、キーワード final を書きますと、値の変更が禁止されます。 これによって、どの変数が定数を表すのかが明確になります。

上記のプログラムでは、Attendance.java の9行目の条件

540 < 60 * this.hour + this.minute

540 が何を表しているのかが分かりにくいでした。 これは始業時刻9:00の絶対時刻(分)です。 定数を

final static int OPENINGHOUR = 9;
final static int OPENINGMINUTE = 0;

と定め、条件を

60 * OPENINGHOUR + OPENINGMINUTE
    < 60 * this.hour + this.minute

と書きますと、出勤時刻が始業時刻以後ならば…と読めるわけです。

Attendance.java(第2版)
/*  1*/ class Attendance {
/*  2*/     int hour, minute;
/*  3*/     static int lateCount = 0;
/*  4*/     final static int OPENINGHOUR = 9;
/*  5*/     final static int OPENINGMINUTE = 0;
/*  6*/     Attendance (int hour, int minute) {
/*  7*/         this.hour = hour;
/*  8*/         this.minute = minute;
/*  9*/     }
/* 10*/     void checkLate () {
/* 11*/         if (60 * OPENINGHOUR + OPENINGMINUTE
/* 12*/                 < 60 * this.hour + this.minute) {
/* 13*/             lateCount++;
/* 14*/         }
/* 15*/     }
/* 16*/ }

11.4 インスタンスメソッド(2)

11.4.1 インスタンスメソッドの使用例

インスタンスメソッドの例として、アニメーションのアプレットを作成します。 アニメーションは、以下のような12個のランプを次々とフラッシュさせるものです。 ちゃんとしたアニメーションを実現するには、高度なプログラミングが必要なのですが、ここではなるべく簡単な方法で行います。

Position of lamps
図 11.2  ランプの位置

アニメーションのアプレットのパターンは次の通りです。

import java.applet.*;
import java.awt.*;

public class program name extends Applet {
    variable declaration and initialization ...
    public void paint (Graphics g) {
        update graphics ...
        try {
            Thread.sleep(msec);
        } catch (InterruptedException e) {
        }
        repaint();
    }
}

まず、変数の宣言と初期化は、今までと違って paint ブロックの外側で行います。 paint ブロックの内側では、グラフィックスの更新処理を行います。 ここは、何度も繰り返し実行されます。 更新処理の後、ある一定の時間、実行を停止します。 パターン通りに書きますと、実行が msec ミリ秒停止します。 最後の repaint() で、アニメーションが続行されます。

ファイルLamp.javaでは、ランプの位置を格納するために、クラス Lamp を定義します。 フィールド posXposY にランプの座標を格納します。 コンストラクタも定義します。

ファイルLamp.javaでは、インスタンスメソッド light を定義します。 これは、ランプを点灯させるものです。 引数は、アプレットに特有の、 Graphicsg のみです。 このメソッドは値を返しませんので、 void と指定します。 メソッド定義の要点は、ランプの座標に白く塗り潰された円を描くことです。

なお、アプレットに関係するクラスの場合、次の1〜2行目を忘れないでください。

Lamp.java
/*  1*/ import java.applet.*;
/*  2*/ import java.awt.*;
/*  3*/
/*  4*/ class Lamp {
/*  5*/     int posX, posY;
/*  6*/     Lamp (int posX, int posY) {
/*  7*/         this.posX = posX;
/*  8*/         this.posY = posY;
/*  9*/     }
/* 10*/     void light (Graphics g) {
/* 11*/         g.setColor(Color.white);
/* 12*/         g.fillOval(this.posX, this.posY, 10, 10);
/* 13*/     }
/* 14*/ }

ファイルLampMain.javaでは、 paint ブロックの外側でアニメーションの初期化を行います(5行目から11行目)。

アルゴリズムは次のようになります。

  1. アプレット全体を黒く塗り潰す。
  2. index 番目のランプ lamps [ index ] に、光るようにとメッセージ light ( g ) を送る。
  3. 変数 index の値を1増加させる。
  4. もし index がランプ数以上になったら、{
  5.     0を変数 index に格納する。
  6. 200ミリ秒停止する。
  7. アニメーションを続行する。

プログラムは以下のようになります。

LampMain.java
/*  1*/ import java.applet.*;
/*  2*/ import java.awt.*;
/*  3*/
/*  4*/ public class LampMain extends Applet {
/*  5*/     int index = 0;
/*  6*/     Lamp[] lamps = {new Lamp(95, 15), new Lamp(135, 25),
/*  7*/                     new Lamp(165, 55), new Lamp(175, 95),
/*  8*/                     new Lamp(165, 135), new Lamp(135, 165),
/*  9*/                     new Lamp(95, 175), new Lamp(55, 165),
/* 10*/                     new Lamp(25, 135), new Lamp(15, 95),
/* 11*/                     new Lamp(25, 55), new Lamp(55, 25)};
/* 12*/     public void paint (Graphics g) {
/* 13*/         g.setColor(Color.black);
/* 14*/         g.fillRect(0, 0, 200, 200);
/* 15*/         lamps[index].light(g);
/* 16*/         index++;
/* 17*/         if (index >= lamps.length) {
/* 18*/             index = 0;
/* 19*/         }
/* 20*/         try {
/* 21*/             Thread.sleep(80);
/* 22*/         } catch (InterruptedException e) {
/* 23*/         }
/* 24*/         repaint();
/* 25*/     }
/* 26*/ }

11.5 演習11

ピンポン球が壁に跳ね返るアニメーションのアプレットを作成します。 壁は長方形の辺とし、その内側をピンポン球が動くものとします。

ピンポン球には動く方向が定まっており、壁に当たるまではその方向に動き続けます。 ここで、動く方向とは単位時間後の位置のずれと考えます。 つまり、座標ごとに今の位置と方向を足せば、次の位置になるわけです。

縦の( y 軸に平行な)壁に当たった場合は、横方向( x 軸方向)に動けませんので、動く方向の x 座標の正負が反転します。 横の( x 軸に平行な)壁に当たった場合は、縦方向( y 軸方向)に動けませんので、動く方向の y 座標の正負が反転します。 壁に当たることの判定は、次の位置を計算し、それが長方形からはみ出す場合とします。 その際に、ピンポン球の大きさも算入する必要があります。

ピンポン球の大きさは縦横10ピクセルとし、長方形は幅300ピクセル、高さ200ピクセルとします。

A rectangle and a ping-pong ball
図 11.3  長方形とピンポン球

ファイルPingPong.javaでは、ピンポン球の位置と動く方向を格納するために、クラス PingPong を定義します。 フィールド posXposY には、位置の x 座標と y 座標を格納します。 フィールド stepXstepY には、方向の x 座標と y 座標を格納します。 コンストラクタは、引数が位置の x 座標、 y 座標、方向の x 座標、 y 座標の順になるように定義します。

ファイルPingPong.javaでは、インスタンスメソッド nextX を定義します。 これは、次の位置の x 座標を返すものです。 引数は、ありません。 このメソッドは整数を返しますので、 int と指定します。 メソッド定義の要点は、位置の x 座標 this. posX と方向の x 座標 this. stepX の和を返すことです。 y 座標についてのインスタンスメソッド nextY も同様に定義します。

ファイルPingPong.javaでは、インスタンスメソッド turnX を定義します。 これは、方向の x 座標の正負を反転させるものです。 引数は、ありません。 このメソッドは値を返しませんので、 void と指定します。 メソッド定義の要点は、フィールド this. stepX に、その値のマイナスを格納することです。 y 座標についてのインスタンスメソッド turnY も同様に定義します。

ファイルPingPong.javaでは、インスタンスメソッド move を定義します。 これは、ピンポン球の位置を変更して、その位置にピンポン球を描くものです。 引数は、 Graphicsg です。 このメソッドは値を返しませんので、 void と指定します。 メソッド定義の要点は、フィールド this. posX の値を this. stepX だけ増加させ、フィールド this. posY の値を this. stepY だけ増加させ、左上の座標が ( this. posX , this. posY ) で縦横が10ピクセルの白く塗り潰された円を描くことです。

ファイルPingPongMain.javaでは、アニメーションの初期化を行います。 これは、

PingPong pp = new PingPong(50, 100, 10, 10);

とします。

アルゴリズムは次のようにします。

  1. 幅300ピクセル、高さ200ピクセルで、緑色に塗り潰された長方形を描く。
  2. もし、次の位置の x 座標 pp . nextX () が0未満か290超ならば、{
  3.     ピンポン球 pp に、方向を変えるようにとメッセージ turnX () を送る。
  4. もし、次の位置の y 座標 pp . nextY () が0未満か190超ならば、{
  5.     ピンポン球 pp に、方向を変えるようにとメッセージ turnY () を送る。
  6. ピンポン球 pp に、動くようにとメッセージ move ( g ) を送る。
  7. 50ミリ秒停止する。
  8. アニメーションを続行する。

このアルゴリズムをプログラムにしてください。

余力のある人は、アニメーションの初期化を以下のように行って、5つのピンポン球を動かしてください。

PingPong[] pps = {new PingPong(50, 100, 10, 10),
                  new PingPong(60, 110, 10, 10),
                  new PingPong(70, 120, 10, 10),
                  new PingPong(80, 130, 10, 10),
                  new PingPong(90, 140, 10, 10)};

なお、ピンポン球同士の衝突は考えなくても構いません。


11.6 レポート課題

今日の演習11に従ってJavaプログラムを作成し、そのプログラムをkonishi@twcu.ac.jpあてにメールで提出してください。 メールには、学生番号、氏名、科目名、授業日(12/4)を明記してください。


[小西ホームページ]   [目次・索引]   [前の授業]   [次の授業]

2003年12月4日更新
konishi@twcu.ac.jp
Copyright (C) 2003 Zenjiro Konishi. All rights reserved.