8.クラス
Copyright(C) 18Sep2013 coskx
クラスを用いると複数の数値や文字列で1つの概念を形成するものの記述が明確になる。
(ここではクラスの機能を「複数の数値や文字列で1つの概念を形成するもの」の範囲に制限して紹介している。)
ここで修得してほしい内容は以下の通りである。
1.複数の値で1つの概念を形成するものはクラスを導入すると表現が自然になることを理解する。
2.クラスで新しい型の定義が行なえる。
3.クラスの任意のメンバ変数に対する値の設定や参照が自由に出来たり,隠せたりする。
4.Javaのひな型プログラムスタイルを理解する。
5.クラスがデータベースに用いられる例を理解する。
内容は以下を含んでいる。
8.1 複数の値で1つの概念を形成するもの
8.2 クラスの導入
8.3 クラスメソッド
8.4 クラスのカプセル化
8.5 static修飾されたメンバ変数,メソッド
8.6 Javaのひな型プログラム
8,7 まとまった作業を行うクラス
8.8 データベースにおけるクラス
このほかに次の2つを含んでいる
(1)再利用可能なパブリッククラスの記述方法
(2)パッケージの利用
注意 クラスの再利用を考えない場合は,複数のクラスを1つのファイル中に記述するが,
クラスを別のプログラム中に再利用する場合は,クラスを別ファイルにする必要がある。
そしてそのクラスをpublicにする。
ここでは,初めて学ぶときの便宜を考え,1つのファイルに複数のクラスを記述する。
そのため,クラスの定義をパブリックではない状態にする。
再利用可能なパブリッククラスの記述方法で改めて,1つのファイルに1つのパブリッククラスを
記述したときのファイル構成,コンパイルの仕方を説明する。
8.1 複数の値で1つの概念を形成するもの |
---|
8.1.1 座標を表す |
平面内の点の座標をプログラムで表現することを考えよう。点の座標は(42,86)のようにx座標とy座標とで表される。単純に考えるために,x座標とy座標ともに整数値しかとらないものとする。
(1)1つの点のみを考える時は,x座標とy座標それぞれにint型変数を割り当てて,
int x; /*x座標*/
int y; /*y座標*/
x=42; y=86;
または
int x=42; /*x座標*/
int y=86; /*y座標*/
とすれば点(42,86)を表したことになるであろう。
(2)数個の点を扱うときでも
int x1=42,y1=86, x2=27,y2=40, x3=12,y3=47;
とすれば点(42,86),(27,40),(12,47)の3点を表したことになるであろう。
(3)100個くらいの点を扱う時は,配列を用いることになるであろう。
int x[100],y[100];
として,ファイルから点の座標を読み込んだり,プログラム中で座標を生成したりするであろう。
x[82]=47;y[82]=25;
というのは点番号82のx座標は47で,y座標は25であるという意味である。このままでもプログラムの見通しが悪いわけではない。
x[34]=x[82];y[34]=y[82];
というのは,点番号82の点の座標を点番号34にコピーしたことになる。
int x[3]={42,27,12};
int y[3]={86,40,47};
というのは点(42,86),(27,40),(12,47)の3点を表したつもりである。ある点の座標が,2箇所にばらばらに記述されることになる。
点の数が100点もあると,見通しが悪い。
8.1.2 分数を表す |
分数をプログラムで表現することを考えよう。分数は 42/86 のように分子と分母とで表される。(約分の必要性は別に考える)
(1)1つの分数のみを考える時は,分子と分母それぞれにint型変数を割り当てて,
int x; /*分子*/
int y; /*分母*/
x=42; y=86;
または
int x=42; /*分子*/
int y=86; /*y分母*/
とすれば分数 42/86 を表したことになるであろう。
(2)数個の分数を扱うときでも
int x1=42,y1=86, x2=27,y2=40, x3=12,y3=47;
とすれば分数 42/86, 27/40, 12/47 の3つの分数を表したことになるであろう。
(3)100個くらいの分数を扱う時は,配列を用いることになるであろう。
int x[100],y[100];
として,ファイルから分数を読み込んだり,プログラム中で分数を生成したりするであろう。
x[82]=47;y[82]=25;
というのは分数番号82の分子は47で,分母は25であるという意味である。このままでもプログラムの見通しが悪いわけではない。
x[34]=x[82];y[34]=y[82];
というのは,分数番号82の分数を分数番号34にコピーしたことになる。
int x[3]={42,27,12};
int y[3]={86,40,47};
というのは分数 42/86, 27/40, 12/47 の3つの分数を表したつもりである。ある分数の分子分母が,2箇所にばらばらに記述されることになる。
分数の数が100個もあると,見通しが悪い。
8.2 クラスの導入 |
---|
1つの座標を1つの変数で表す方法を提供することができる。
(1)クラスの導入
もし,座標を表す変数pntがあり,内部には2つの部屋があり,部屋の名前がxとyとし,変数pntの部屋xに42を,変数pntの部屋yに86を代入し,これを点pntのx座標に42,y座標に86を設定したことにしたい。実際には
pnt.x=42; pnt.y=86;
のように表現したい。
このように1つの変数内に複数の部屋がある変数を使えるようにする仕組みをクラスは与える。
クラスを使うには次のような2段階がある。
(ここでのクラスの使い方はクラスの機能の1部を使うことになる)
あるクラスの変数(例えば上記のpnt)のことはインスタンス変数と呼ばれる。(instanceは実例のこと)
(オブジェクトとも呼ばれるが,これはもう少し広い概念である)
第1段階 内部に2部屋あり部屋の名前がx,yである新しいクラス(変数型のように考えればよい,Cの構造体のような)を作り,クラスの名前をPointとする。
第2段階 クラスPointとインスタンス変数pntを宣言する。(Point型の変数pntを宣言すると考えればよい。)
具体的には
/*クラスPointの定義*/
class Point {
int x; /*x座標の部屋*/
int y; /*y座標の部屋*/
}
/*クラスPointのインスタンスの宣言*/
Point pnt= new Point();
となる。(インスタンス変数はpnt)このような宣言がなされているもとで
pnt.x=42; pnt.y=86;
という表現ができるようになる。
List 8.2.1 クラスPointの導入 /* Point0.java */
class Point {
public int x; /*x座標の部屋*/ //この行のpublicは省略可能
public int y; /*y座標の部屋*/ //この行のpublicは省略可能
}
public class Point0 {
public static void main(String[] args) {
Point0 mainprg = new Point0();
}
public Point0() { //この行のpublicは省略可能
Point pnt= new Point();
pnt.x=42;
pnt.y=86;
System.out.printf("pnt.x=%d pnt.y=%d\n",pnt.x,pnt.y);
}
}
/****実行結果****
pnt.x=42 pnt.y=86
****************/
ここでx,yはクラスの内部で定義されている変数(部屋の名前)なのでクラスのメンバ変数(フィールド)と呼ばれる。
class Point { Pointはクラス
public int x; xはメンバ変数
public int y; yはメンバ変数
}
public class Point0 { /*ソースファイル名はPoint0.java*/
public static void main(String[] args) {
Point0 mainprg = new Point0();
}
public Point0() {
Point pnt= new Point(); pntはインスタンス変数
pnt.x=42;
pnt.y=86;
System.out.printf("pnt.x=%d pnt.y=%d\n", pnt.x, pnt.y);
}
}
もしインスタンス変数pntの宣言が「Point pnt;」だけなら,これまでの「int x;」(変数型名 変数名)などと同じである。
ここまではC言語の構造体と全く同じ表現である。
「Point」が型名で「pnt」が変数名ということになる。
しかし必ず「= new Point()」が必要である。
この後,様々な拡張がなされていく。
(2)コンストラクタ
(1)のPoint型のインスタンス変数が数個あり,変数宣言時の初期化代入をするなら,
コンストラクタ(constructorは生成すること)を用意する。
コンストラクタはメソッドと同じように作り,動作もメソッドと同様であるが,
名前はクラス名と同じでなければならない。
コンストラクタは値は返さないが,voidなどの型名はない。
List 8.2.2 クラスPointのコンストラクタの導入 ファイル名はpoint1.java /* Point1.java */
class Point {
int x; /*x座標の部屋*/
int y; /*y座標の部屋*/
Point(int ix, int iy) { //これがコンストラクタ クラス名と同じ名前でできている
x=ix;
y=iy;
}
}
public class Point1 {
public static void main(String[] args) {
Point1 mainprg = new Point1();
}
Point1() {
Point pnt1= new Point(42,86);
/* Point(42,86)はクラスPointのコンストラクタの呼び出 */
/* しである。ここの2つの値はコンストラク タ */
/* Point(int ix, int iy)の2つの引数に渡され,コンスト */
/* ラクタによりxとyが代入され,インスタンスが作られ, */
/* インスタンス変数pnt1で扱えるように なる */
Point pnt2= new Point(27,40);
Point pnt3= new Point(12,47);
System.out.printf("pnt1.x=%d pnt1.y=%d\n",pnt1.x,pnt1.y);
System.out.printf("pnt2.x=%d pnt2.y=%d\n",pnt2.x,pnt2.y);
System.out.printf("pnt3.x=%d pnt3.y=%d\n",pnt3.x,pnt3.y);
}
}
/****実行結果****
pnt1.x=42 pnt1.y=86
pnt2.x=27 pnt2.y=40
pnt3.x=12 pnt3.y=47
****************/
(3)インスタンスとインスタンス変数このように記述し,点(42,86),(27,40),(12,47)の3点を表すことができる。
実は(1)のようにコンストラクタを記述していない場合は,デフォルトコンストラクタができており,
初期値を与えない形でインスタンスが作成できていた。
Point() { //省略されたデフォルトコンストラクタはこんな形(何もしない)
}
インスタンス変数は参照型であり,int型やDouble型とは異なる構造をしている。
例えばPoint型のインスタンス変数pntがあり,その中のxの値が15でyの値が54だとすると,
15と54が保管された容器があり,それにはIDがついている。>
この容器がインスタンスで,インスタンス変数pntには容器のID(容器への参照)が保管されている。
インスタンス変数pntには15と54からなるインスタンスのID(インスタンスへの参照)が保存される。
*1 ID(参照)を知ることができないのでxxxxxxxxと表した。
class Pointのインスタンス変数名 値 pnt ID xxxxxxxx *1
class Pointのインスタンス (ID xxxxxxxx) メンバ変数名 値 x 15 y 54
(3.1)クラスのインスタンス変数は参照を保存していることに起因する注意(その1)
インスタンス変数には参照が保存されるため,newによって実体を作らないで値を代入しようとすると失敗する。(3.2)クラスのインスタンスは参照であることに起因する注意(その2)
List 8.2.3 クラスPointのコンストラクタの導入 ファイル名はPoint2.java /* Point2.java */
class Point {
int x; /*x座標の部屋*/
int y; /*y座標の部屋*/
Point() { /*明示的に作成した引数なしのコンストラクタ(デフォルトコンストラクタと同じ)*/
}
Point(int ix, int iy) { /*コンストラクタは同じ名前で複数定義できる。これは引数付のもの*/
x=ix;
y=iy;
}
}
class Point2 { /*ソースファイル名はpoint2.java*/
public static void main(String[] args) {
Point2 mainprg = new Point2();
}
Point2() {
Point pnt1= new Point();
Point pnt2; /*変数はできているが,newで実体が作られていない*/
pnt1.x=10;
pnt1.y=20;
System.out.printf("pnt1.x=%d pnt1.y=%d\n",pnt1.x,pnt1.y);
pnt2.x=100;
pnt2.y=200;
System.out.printf("pnt2.x=%d pnt2.y=%d\n",pnt2.x,pnt2.y);
}
}
/****実行結果****
Point2.java:26: エラー: 変数pnt2は初期化されていない可能性があります
pnt2.x=100;
^
エラー1個
****************/
失敗の補足
Point pnt1= new Point();
この段階で,メモリ上にPoint型インスタンスをしまうのに必要な大きさのメモリブロックが確保され,
そのIDがインスタンス変数pnt1に入っている。
Point pnt2;
の段階で,インスタンス変数pnt2が宣言されたが,インスタンスは確保されていない。
この状態では,pnt2.x=100; は実行できない。
インスタンス変数 変数の中身
pnt1
Point型インスタンスをしまうのに必要な大きさの
メモリブロックのID(参照)pnt2 まだ有効なメモリブロックのID(参照)が代入されていない
List 8.2.3では,クラスPointには2つのコンストラクタがある。引数によってどちらのコンストラクタが呼び出されるかが決まる。
このように,引数の異なる同じ名前のコンストラクタが複数あることをコンストラクタのオーバーロードと呼ぶ。
同様に引数の異なる同じ名前のメソッドが複数ある場合でも,メソッドのオーバーロードと呼ぶ。
クラスインスタンスは参照であるため,pnt2=pnt1;のように記述すると,2つのインスタンスが同じ実体を指してしまうことになる。
その結果,次のプログラム前半のように,意図しない結果が生じてしまう。
今のところ,面倒だが,後半のようにインスタンスを別のインスタンスに代入するには,インスタンスのメンバ変数(内部変数)x,yをそれぞれ 代入しなければならない。
List 8.2.4 クラスPointのコンストラクタの導入 ファイル名はPoint3.java
前半はインスタンスの代入に失敗し,意図しない状況になってしまっている。
/* Point3.java */
class Point {
int x; /*x座標の部屋*/
int y; /*y座標の部屋*/
Point() { /*明示的に作成した引数なしのコンストラクタ(デフォルトコンストラクタと同じ)*/
}
Point(int ix, int iy) { /*コンストラクタは同じ名前で複数定義できる。これは引数付のもの*/
x=ix;
y=iy;
}
}
class Point3 { /*ソースファイル名はpoint3.java*/
public static void main(String[] args) {
Point3 mainprg = new Point3();
}
Point3() {
Point pnt1= new Point(42,86); /*引数付のコンストラクタの呼び出し*/
Point pnt2= new Point(); /*引数なしのコンストラクタの呼び出し*/
/*ここまではOK*/
pnt2=pnt1; //pnt2にpnt1を代入したつもり
System.out.printf("A pnt1.x=%d pnt1.y=%d\n", pnt1.x, pnt1.y);
System.out.printf("A pnt2.x=%d pnt2.y=%d\n", pnt2.x, pnt2.y);
pnt2.x=100;
System.out.printf("B pnt1.x=%d pnt1.y=%d\n", pnt1.x, pnt1.y);
System.out.printf("B pnt2.x=%d pnt2.y=%d\n", pnt2.x, pnt2.y);
pnt1.x=200;
System.out.printf("C pnt1.x=%d pnt1.y=%d\n", pnt1.x, pnt1.y);
System.out.printf("C pnt2.x=%d pnt2.y=%d\n", pnt2.x, pnt2.y);
System.out.printf("------------------------------\n");
Point pnt3= new Point(27,40);
Point pnt4= new Point();
pnt4.x=pnt3.x;
pnt4.y=pnt3.y;
System.out.printf("D pnt3.x=%d pnt3.y=%d\n", pnt3.x, pnt3.y);
System.out.printf("D pnt4.x=%d pnt4.y=%d\n", pnt4.x, pnt4.y);
pnt4.x=100;
System.out.printf("E pnt3.x=%d pnt3.y=%d\n", pnt3.x, pnt3.y);
System.out.printf("E pnt4.x=%d pnt4.y=%d\n", pnt4.x, pnt4.y);
pnt3.x=200;
System.out.printf("F pnt3.x=%d pnt3.y=%d\n", pnt3.x, pnt3.y);
System.out.printf("F pnt4.x=%d pnt4.y=%d\n", pnt4.x, pnt4.y);
}
}
/****実行結果****
A pnt1.x=42 pnt1.y=86
A pnt2.x=42 pnt2.y=86
B pnt1.x=100 pnt1.y=86 ←あれれ,pnt2.xを変更しただけなのに,pnt1.xも変化してしまった
B pnt2.x=100 pnt2.y=86
C pnt1.x=200 pnt1.y=86
C pnt2.x=200 pnt2.y=86 ←あれれ,pnt1.xを変更しただけなのに,pnt2.xも変化してしまった
------------------------------
D pnt3.x=27 pnt3.y=40
D pnt4.x=27 pnt4.y=40
E pnt3.x=27 pnt3.y=40
E pnt4.x=100 pnt4.y=40
F pnt3.x=200 pnt3.y=40
F pnt4.x=100 pnt4.y=40
****************/
前半の失敗についての補足
Point pnt1= new Point(42,86);
Point pnt2= new Point();
の段階で,メモリ上に「42,86」が入ったメモリブロックが作成され,そのIDが
インスタンス変数pnt1に入っている。
またインスタンス変数pnt2は存在するものの,初期化されていない状態である。pnt2=pnt1;
インスタンス変数 変数の中身
pnt1
「42,86」が入ったメモリブロックのID(参照) pnt2 2つの値をしまうのに必要な大きさで,pnt1とは異なる
メモリブロックのID(参照) *1
により,インスタンス変数pnt2にもインスタンス変数pnt1と同じID(参照)が入ることになり,
次のようになる。
(*1で確保されたメモリブロックはpnt2=pnt1;の作業以降は操作できないので,いずれ捨てられる。)
インスタンス変数 変数の中身 pnt1 「42,86」が入ったメモリブロックのID(参照) pnt2 「42,86」が入ったメモリブロックのID(参照)
pnt2=pnt1;の作業以降,pnt1に対する操作でも,pnt2に対する操作でも,同じメモリブロックに対して行われることになる。
(4)この型のインスタンスの配列を作る
Point[] pnt= new Point[3];
だけでは,参照型のインスタンス変数が3つできるだけで,インスタンス(実体)はまだできていないため,インスタンスを3つ作らなければならない。
for (i=0; i<3; i++) pnt[i]= new Point();
これで,使えるようになる。
List 8.2.5 クラスPointのインスタンス変数の配列 /* Pointarray.java */
class Point {
int x; /*x座標の部屋*/
int y; /*y座標の部屋*/
Point() {
}
Point(int ix, int iy) {
x=ix;
y=iy;
}
}
public class Pointarray {
public static void main(String[] args) {
Pointarray mainprg = new Pointarray();
}
Pointarray() {
Point[] pnt= new Point[3];
int i;
for (i=0; i<3; i++) pnt[i]= new Point();
for (i=0; i<3; i++) {
pnt[i].x=i*10;
pnt[i].y=i*3;
}
for (i=0; i<3; i++) {
System.out.printf("%d %d\n", pnt[i].x, pnt[i].y);
}
}
}
/****実行結果****
0 0
10 3
20 6
****************/
しかし,初期値を伴いながらクラスの配列を初期化するのは,数値だけを並べればよいわけでなく,面倒である。
List 8.2.6 クラスPointのインスタンスの配列(初期値を伴って初期化) /* Pointarray2.java */
class Point {
int x; /*x座標の部屋*/
int y; /*y座標の部屋*/
Point() {
}
Point(int ix, int iy) {
x=ix;
y=iy;
}
}
class Pointarray2 {
public static void main(String[] args) {
Pointarray2 mainprg = new Pointarray2();
}
Pointarray2() {
Point[] pnt= new Point[]{
new Point(0,0),
new Point(10,3),
new Point(20,6)
};
int i;
for (i=0; i<3; i++) {
System.out.printf("%d %d\n", pnt[i].x, pnt[i].y);
}
}
}
/****実行結果****
0 0
10 3
20 6
****************/
8.3 クラスにメソッドを加える |
---|
(1)クラスにメソッドを追加する
クラス変数内の数値を毎回定型で表示する場合,「クラス変数aよ,自分の持っている値を表示しなさい」といった命令を作れるとしたら,便利である。
この目的のために,クラス内に自分の持っている変数を表示するメソッドprint()を追加する。
メソッドprint()はclass Pointの内部に定義される。print()表示するのは,クラス内の変数x,yである。メソッドprint()は,クラスPointのインスタンスmypntに絡めて,mypnt.print();のように呼び出され,
「mypntよ,自分を表示せよ」の意味である。同様にpnt[i].print()は「pnt[i]よ,自分を表示せよ」になる。
インスタンス名を付けないで単に内部メソッドprint()だけを呼び出すことはできない。
void print()の前にpublicが付いているのは,このメソッドはクラスの外部に公開しており,外部から呼び出し可能であることを意味している。
実は,このように明示的に記述してはないが,int x, int y, Point(), Point(int ix, int iy)はすべてpublicとして扱われている。
List 8.3.1 クラスPointのメソッドprint() /* Point4.java */
class Point {
int x; /*x座標の部屋*/
int y; /*y座標の部屋*/
Point() { /*明示的に作成した引数なしのコンストラクタ(デフォルトコンストラクタと同じ)*/
}
Point(int ix, int iy) { /*コンストラクタは同じ名前で複数定義できる。これは引数付のもの*/
x=ix;
y=iy;
}
public void print() {
System.out.printf("%d %d\n", x, y);
}
}
class Point4{
public static void main(String[] args) {
Point4 mainprg = new Point4();
}
Point4() {
Point mypnt= new Point(120,240);
mypnt.print();
Point[] pnt= new Point[]{
new Point(0,0),
new Point(10,3),
new Point(20,6)
};
int i;
for (i=0; i<3; i++) {
pnt[i].print();
}
}
}
/****実行結果****
120 240
0 0
10 3
20 6
****************/
ファイル入出力の時にすでにクラスを利用していた
ファイル入出力でも,似たような表現が出てきたと気付く。実はファイル入出力用クラスライブラリを用いていた。
クラスの定義を自分で書いたわけではないが,クラスのインスタンスを作って使っていた。
PrintWriter mywriter = new PrintWriter("myfile.txt");においてPrintWriterはクラス名,mywriterはインスタンスである。
mywriter.printf("Hello.\n");
mywriter.close();
new PrintWriter("myfile.txt")は引数付でコンストラクタを呼び出して,mywriterに実体を与えていた。
その後,mywriter.printf("Hello.\n");で「mywriterよ,Helloとファイルへ書き込め」と指示を送っていた。
mywriter.close();で「mywriterよ,閉じよ」と指示を送っていた。
メソッドvoid move(int dx, int dy)は,現在のx,yにそれぞれdx,dyだけ加えよの意味である。
(座標なら点の移動の意味)
mypnt1.move(5,7);は,「mypnt1よ,{x,yにそれぞれdx,dyを加わえた値}を持つようになれ」の意味となる。
このメソッドは,呼び出されたインスタンス自身が変化するメソッドである。
メソッドPoint makeClone()は,x,yと同じ値を持つ新しいインスタンスを作成して,それを返すメソッドである。
mypnt3=mypnt2.makeClone();は,mypnt2と同じ値を持った実体が作成され,mypnt3がそれを指すようになる。
mypnt3=mypnt2;では,mypnt3がmypnt2の実体を指すようになるだけで,
同じ実体を2つの変数が指しているだけになっていた。
メソッドPoint makeMovedClone(int dx, int dy)は,x+dx,y+dyを値として持つ実体を作成して,
それを返すメソッドである。このメソッドは,呼び出されたインスタンス自身は変化せず,
結果を別な実体として返すメソッドである。
mypnt4=mypnt2.makeMovedClone(20,40);は,mypnt2からdx,dyだけ増えた値を持った実体が作成され,
メソッドの値として返し,mypnt4がそれを指すようにする。
List 8.3.2 クラスPointにメソッドを追加 /* Point5.java */
class Point {
int x; /*x座標の部屋*/
int y; /*y座標の部屋*/
Point() { /*明示的に作成した引数なしのコンストラクタ(デフォルトコンストラクタと同じ)*/
}
Point(int ix, int iy) { /*コンストラクタは同じ名前で複数定義できる。これは引数付のもの*/
x=ix;
y=iy;
}
public void print() {
System.out.printf("%d %d\n", x, y);
}
public void move(int dx, int dy) {
x+=dx;
y+=dy;
}
public Point makeClone() {
Point pnt= new Point(x,y);
return pnt;
}
public Point makeMovedClone(int dx, int dy) {
Point pnt= new Point(x+dx,y+dy);
return pnt;
}
}
class Point5{
public static void main(String[] args) {
Point5 mainprg = new Point5();
}
Point5() {
Point mypnt1= new Point(120,240);
mypnt1.print();
mypnt1.move(5,7);
mypnt1.print();
System.out.println("-----------------------------");
Point mypnt2= new Point(100,200);
Point mypnt3;
Point mypnt4;
mypnt2.print();
mypnt3=mypnt2.makeClone();
mypnt3.print();
mypnt4=mypnt2.makeMovedClone(20,40);
mypnt4.print();
}
}
/****実行結果****
120 240
125 247
-----------------------------
100 200
100 200
120 240
****************/
8.4 クラスのカプセル化 |
---|
ここまで取り上げたクラスでは,そのインスタンスのメンバ変数をアクセス(値を見たり,値を変更したりすること)できた。
このままでは不注意で変数の値を変化させてしまうプログラムが書けてしまい,安全性は低い。
このような問題点をクリアするために,メンバ変数へのアクセスをメソッド経由で行う形のクラスを作ることができる。
privateというのは,クラスの外部からはアクセスできないようにするための修飾記述である。
逆にpublicというのは,クラスの外部からアクセスできるようにするための修飾記述である。
これまで,privateもpublicも書いていないメンバ変数を扱ってきたが,これは,(そのファイル内なら)クラス外からアクセス可能という意味になる。
List 8.4.1 クラスPointのメンバ変数をprivateに /* Point6.java */
class Point {
private int x; /* x座標 */
private int y; /* y座標 */
Point() {
}
Point(int x, int y) {
this.x=x; /* 引数の x をこのクラスの x に代入 */
this.y=y; /* 引数の y をこのクラスの y に代入 */
}
public void print() {
System.out.printf("%d %d\n", x, y);
}
public void move(int dx, int dy) {
x+=dx;
y+=dy;
}
public Point makeClone() {
Point pnt= new Point(x,y);
return pnt;
}
public Point makeMovedClone(int dx, int dy) {
Point pnt= new Point(x+dx,y+dy);
return pnt;
}
public void setXY(int x, int y) { /*値の設定*/
this.x=x;
this.y=y;
}
public int getX() { /*メンバ変数xの値の取出し*/
return x;
}
public int getY() { /*メンバ変数yの値の取出し*/
return y;
}
}
class Point6{
public static void main(String[] args) {
Point6 mainprg = new Point6();
}
Point6() {
Point pnt= new Point(120,240);
/* System.out.printf("%d %d\n", pnt.x, pnt.y); これはできない*/
System.out.printf("( %d, %d )\n", pnt.getX(), pnt.getY());
pnt.print();
/* pnt.x=200; これはできない*/
/* pnt.y=300; これはできない*/
pnt.setXY(200,300);
pnt.print();
}
}
/****実行結果****
( 120, 240 )
120 240
200 300
****************/
Point(int x, int y)のthisの記述に見られるように,引数のxとクラスのxを区別する書き方もある。
void setXY(int x, int y)のように値を設定するメソッドは一般的にセッターと呼ばれ,
int getX(),int getY()のように値を持ち帰るメソッドは一般的にゲッターと呼ばれる。このようにクラスのメンバ変数を外部から直接アクセスできないようにすることは,
カプセル化あるいはメンバ変数の隠ぺいと呼ばれる。
このことにより,このクラスを利用するプログラマが不用意に値を変更出来ないようにしている。
また,クラス内部がどのように作成してあるのかを公開しないようにする仕組みでもある。
まとめる意味で分数を表すクラスを作ってみよう。
この中にprivate int getGCD(int x, int y)という外部には公開しないメソッドがある。
約分のため最大公約数を求めるときに使われるが,外部には公開しない。
クラス内のメソッドからのみ使用可能になっている。
List 8.4.2 分数を表すクラスFraction class Fraction {
private int numerator; /*分子を表すプライベートメンバ変数*/
private int denominator; /*分母を表すプライベートメンバ変数*/
Fraction() { /*コンストラクタ*/
numerator=denominator=1;
}
Fraction(int numerator, int denominator) { /*コンストラクタ*/
this.numerator=numerator;
this.denominator=denominator;
}
public void print() {
System.out.printf("%d/%d\n", numerator, denominator);
}
private int getGCD(int x, int y) /*プライベートメソッド*/
{
int r;
/*ユークリッドの互除法で最大公約数を求める*/
r=x%y;
while (r!=0) {
x=y;y=r;
r=x%y;
}
return y;
}
public void reduce() { /*通分する*/
int GCD=getGCD(numerator,denominator);
numerator/=GCD;
denominator/=GCD;
if (denominator<0) {
numerator=-numerator;
denominator=-denominator;
}
}
public void add(Fraction fr) {
int num=this.numerator*fr.denominator+this.denominator*fr.numerator;
int den=this.denominator*fr.denominator;
int GCD=getGCD(den,num);
this.numerator=num/GCD;
this.denominator=den/GCD;
}
public Fraction makeClone() {
Fraction fr= new Fraction(numerator, denominator);
return fr;
}
public void setFraction(int numerator, int denominator) {
this.numerator=numerator;
this.denominator=denominator;
}
public int getNumerator() {
return numerator;
}
public int getDenominator() {
return denominator;
}
}
public class XXXX {
public static void main(String[] args) {
XXXX mainprg = new XXXX();
}
XXXX() {
Fraction fr1= new Fraction(120,360);
Fraction fr2= new Fraction(5,30);
System.out.printf("fr1= ");
fr1.print(); /*fr1よ,自分を表示せよと読めば良い*/
fr1.reduce(); /*fr1よ,自分を約分せよと読めば良い*/
System.out.printf("fr1= ");
fr1.print();
System.out.printf("---------------------\n");
System.out.printf("fr2= ");
fr2.print();
fr2.reduce();
System.out.printf("fr2= ");
fr2.print();
System.out.printf("---------------------\n");
fr1.add(fr2); /*fr1よ,fr2を加えた値になれと読めば良い*/
System.out.printf("fr1= ");
fr1.print();
}
}
/****実行結果****
fr1= 120/360
fr1= 1/3
---------------------
fr2= 5/30
fr2= 1/6
---------------------
fr1= 1/2
****************/
プログラム開発途中では,複数のクラスを同時に差し替えたり,戻したりする。そのための仕組みとしてパッケージがある
興味があったらこちら「パッケージ」
参考 配列を含む例。
配列を含んだクラスと,コンストラクタ (array2Dim.java)
/* Array2Dim.java */
class CAry2D {
private int [][] ary2d = new int[3][3];
CAry2D(int [][] data) { /*これがコンストラクタ クラス名と同じ名前でできている*/
int i,j;
for (i=0;i<3;i++) for (j=0;j<3;j++) ary2d[i][j]=data[i][j];
}
public void PrintAry2D()
{
int i,j;
for (i=0;i<3;i++) {
for (j=0;j<3;j++) {
System.out.printf(" %d", ary2d[i][j]);
}
System.out.printf("\n");
}
}
}
public class Array2Dim {
public static void main(String[] args) {
Array2Dim mainprg = new Array2Dim();
}
Array2Dim() {
int [][] d={ {1,2,3}, {2,3,4}, {3,4,5}};
CAry2D pnt2=new CAry2D(d);
pnt2.PrintAry2D();
}
}
/* 実行結果
1 2 3
2 3 4
3 4 5
*/
課題 8.1 |
---|
(1)List 8.4.1では座標を表す座標を表す変数はすべてint型であった。これをすべてdouble型に変更しなさい。そして,あるインスタンスが表す点が原点を中心に反時計回りにthetaラジアン回転した新しい点の座標になるメソッドをクラスPointに追加しなさい。 (p08ex01.java)
ただし,このメソッドを
public void rotate(double theta)
で定義しなさい。このメソッドは,呼び出されたインスタンス自身が変化するメソッドである。
同時にあるインスタンスが表す点が原点を中心に反時計回りにthetaラジアン回転した新しい点の座標を返すメソッドも追加しなさい。ただしこのメソッドを
public Point makeRotatedPoint(double theta)
で定義しなさい。このメソッドは,呼び出されたインスタンス自身は変化せず,結果を別な実体として返すメソッドである。
(x,y)を反時計回りにθラジアン回転すると( x cosθ - y sinθ , x sinθ + y cosθ )になる。
sin,cos関数は,Math.sin(xxxxx),Math.cos(xxxxx)で使うことができる。
メイン側で次のように検証しなさい。
Point pnt1= new Point(100.0, 100.0);
pnt1.print();
pnt1.rotate(Math.PI/4.0); /*π/4の回転*/
pnt1.print(); /*これで点(100,100)をπ/4の回転した座標が表示されるはず*/
Point pnt2= new Point(100.0, 100.0);
Point pnt3= pnt2.makeRotatedPoint(Math.PI/4.0); /*π/4の回転*/
pnt3.print(); /*これで点(100,100)をπ/4の回転した座標が表示されるはず*/
(2)List 8.4.2をもとに,次のように変更しなさい。
足し算では,メソッドpublic void add()があるが,これは呼び出されたインスタンス自身が変化するメソッドである。
呼び出されたインスタンス自身は変化せず,和を別な実体として返すメソッド
public Fraction makeAddedFraction(Fraction fr)を作りなさい。
さらに引き算,掛け算割り算について同様の2つのメソッドそれぞれ作りなさい。
public void subtract(Fraction fr)
public FractionFraction makeSubtractedFraction(Fraction fr)
public void multiply(Fraction fr)
public FractionFraction makeMultipliedFraction(Fraction fr)
public void divide(Fraction fr)
public FractionFraction makeDividedFraction(Fraction fr)
ただし,割り算において引数の分子が0の場合は,0での割り算になってしまうので,10000000/1を返すものとする。 (p08ex02.java)
検査用メイン()を作り,次の値で検査すること。
1/6,1/3で四則演算(差,商については順番も換えること)
1/6,-1/3で四則演算(差,商については順番も換えること)
-1/6,1/3で四則演算(差,商については順番も換えること)
-1/6,-1/3で四則演算(差,商については順番も換えること)
3/4,16/3で四則演算(差,商については順番も換えること)
3/4,-16/3で四則演算(差,商については順番も換えること)
-3/4,16/3で四則演算(差,商については順番も換えること)
-3/4,-16/3で四則演算(差,商については順番も換えること)
8.5 static修飾されたメンバ変数,メソッド |
---|
メソッドは,クラスのインスタンスを作成してから,インスタンス名の後ろにメソッド名を書いて呼び出していた。
クラスのインスタンスを作成せずに,クラス名の後ろにメソッド名を書いて呼び出すことがある。
そのようなメソッドやメンバ変数には次のように,staticが記述されている。
List 8.5.1 static修飾のあるpublicメンバ変数とpublicメソッド class MyXXXX {
public static int daysOfYear=365;
public static int nibaigaesi(int x) {
return 2*x;
}
public static int jubaigaesi(int x) {
return 10*x;
}
}
class XXXX {
public static void main(String[] args) {
XXXX mainprg = new XXXX();
}
XXXX() {
int x=5;
System.out.printf("x=%d\n", x);
System.out.printf("MyXXXX.nibaigaesi(x)= %d\n", MyXXXX.nibaigaesi(x));
System.out.printf("MyXXXX.jubaigaesi(x)= %d\n", MyXXXX.jubaigaesi(x));
System.out.printf("MyXXXX.daysOfYear= %d\n", MyXXXX.daysOfYear);
}
}
/****実行結果****
x=5
MyXXXX.nibaigaesi(x)= 10
MyXXXX.jubaigaesi(x)= 50
MyXXXX.daysOfYear= 365
****************/
staticの付いたpublicメンバ変数も,publicメソッドも,クラスMyXXXXのインスタンスを作成することなく,
これに対して,「8.4」で取り上げたように,クラスのインスタンスを生成して,
クラス名をかぶせて呼び出されていることがわかる。
(たとえば,MyXXXX.nibaigaesi(x)やMyXXXX.daysOfYear,MyXXXXはクラス名)
このことから,
クラスメンバ変数,クラスメソッドと呼ばれる。
Math.cos(),Math.sin()はこのようなクラスメソッドであり,前についているMathはクラス名である。
これはJava環境が事前にクラスライブラリとして提供しているものである。
また,System.out.printfもクラスSystem内のクラスout内のクラスメソッドprintfを使っていたことになる。
インスタンス名で呼び出されるメンバ変数とメソッドは,上記と区別する場合,
それぞれインスタンスメンバ変数,インスタンスメソッドと呼ばれている。
privateクラスメンバ変数(private static修飾された変数)は,複数のインスタンス側から見ると,
共通にアクセスできる変数(クラス外部からはアクセス出来ない)ということになる。
privateインスタンスメンバ変数は各インスタンスからしかアクセス出来ない。
List 8.5.2 クラスメンバ変数,クラスメソッド,インスタンスメンバ変数,インスタンスメソッド class MyXXXX {
private static int count=0; //クラスメンバ変数
private int myID; //インスタンスメンバ変数
MyXXXX() { //コンストラクタ
myID=count++;
}
public static int getCount() { //クラスメソッド
return count;
}
public int getID() { //インスタンスメソッド
return myID;
}
}
class XXXX {
public static void main(String[] args) {
XXXX mainprg = new XXXX();
}
XXXX() {
System.out.printf("MyXXXX.count=%d\n",MyXXXX.getCount());
MyXXXX insA = new MyXXXX();
MyXXXX insB = new MyXXXX();
MyXXXX insC = new MyXXXX();
System.out.printf("insA ID=%d\n",insA.getID());
System.out.printf("insB ID=%d\n",insB.getID());
System.out.printf("insC ID=%d\n",insC.getID());
System.out.printf("MyXXXX.count=%d\n",MyXXXX.getCount());
}
}
/****実行結果****
MyXXXX.count=0
insA ID=0
insB ID=1
insC ID=2
MyXXXX.count=3
****************/
8.6 Javaのひな型プログラム |
---|
ここで,Javaのひな型プログラムについて,もう一度考えてみよう。
Hello.java
/*Hello.java by Kosaka*/
public class Hello { ←A
public static void main(String[] args) { ←B
Hello mainprg = new Hello(); ←C
}
Hello() { ←D
System.out.println("Hello. How are you?");
System.out.println("Fine, thanks. And You?");
System.out.println("So so.");
}
} ←EA パブリッククラスHelloの宣言部の先頭
B class Helloの中のstaticの付いたメソッドである。
だからclass Helloのインスタンスを作ることなしに実行できている。
特にmainというのは特別な名前で,class Helloを実行しなさいと言ったら,class Hello中の
mainが実行されることになっている。C クラスHelloのインスタンスmainprgの宣言。「= new Hello()」で初期化している。
インスタンスmainprgは作成されただけで,何にも使われなかった。
そのため,このインスタンスの名前は何でもよかった。
D クラスHelloのコンストラクタHello() Dの「= new Hello()」のときに呼び出されている。
すなわち,作成したクラスのコンストラクタをプログラムのメイン部として使っていたことになる。
E パブリッククラスHelloの末尾
public static void main(String[] args) はそのまま実行できるので,次のようなスタイルも可能である。
Hello.java
/*Hello.java by Kosaka*/
public class Hello {
public static void main(String[] args) {
System.out.println("Hello. How are you?");
System.out.println("Fine, thanks. And You?");
System.out.println("So so.");
}
}しかし,staticメソッドから呼び出すメソッドや参照するメンバ変数もstaticである必要が生ずる。
制約が増えるため,本資料では使用していない。
8.7 まとまった作業を行うクラス |
---|
クラスの本来の用途は,舞台裏の細かなことを隠して,まとまった作業行うことである。
(1)クラスのあるメソッドに連続して値を与えると,最新4つの値の平均値をメソッドの値として戻す機能をクラスで作る。このような平均化作業は時間とともに変化する測定値を平滑化する作業に用いられ,移動平均と呼ばれる。
実行結果を見ると,x=3.0以降は最新の4つのxの値の平均値がaveとして出力されていることがわかる。
class Averageでは受け取った値を保存しておく配列xや,受け取った値を次に保存すべきxの要素番号は,privateとし,外部から変更されないように守っている。
List 8.6.1 移動平均
class Average {
private double[] x= {0.,0.,0.,0.};
private int pointer=0;
public double average4(double data) {
x[pointer]=data;
double sum=x[0]+x[1]+x[2]+x[3];
double average=sum/4.0;
pointer++;
if (pointer==4) pointer=0;
return average;
}
}
class XXXX {
public static void main(String[] args) {
XXXX mainprg = new XXXX();
}
XXXX() {
int i;
double x;
double ave;
Average myaverage= new Average();
for (i=0; i<16; i++) {
x=(double)i;
ave=myaverage.average4(x);
System.out.printf("x=%5.2f ave=%8.5f\n", x, ave);
}
}
}
/******** 実行結果 *********
>java XXXX
x= 0.00 ave= 0.00000
x= 1.00 ave= 0.25000
x= 2.00 ave= 0.75000
x= 3.00 ave= 1.50000
x= 4.00 ave= 2.50000
x= 5.00 ave= 3.50000
x= 6.00 ave= 4.50000
x= 7.00 ave= 5.50000
x= 8.00 ave= 6.50000
x= 9.00 ave= 7.50000
x=10.00 ave= 8.50000
x=11.00 ave= 9.50000
x=12.00 ave=10.50000
x=13.00 ave=11.50000
x=14.00 ave=12.50000
x=15.00 ave=13.50000
***************************/
(2)次のプログラムは擬似乱数発生関数を作ったところである。大きな数に別の大きな数を掛け,ある数を加えて作る擬似乱数発生クラスである。コンストラクタで乱数の最大値,最小値を与えると,その範囲の乱数を発生する。与えなければ0~32767の範囲で乱数を発生する。
List 8.6.2 擬似乱数発生クラス class MyRandom {
private long num;
private long min;
private long max;
private long mask;
MyRandom() {
num = System.currentTimeMillis();
min=0;
max=32767;
mask=max-min+1;
}
MyRandom(int min, int max) {
num = System.currentTimeMillis();
this.min=min;
this.max=max;
mask=max-min+1;
}
public long getNext() {
long ret;
num=18397*num+35977;
num &=0x7fffffffffffffffL;
ret=num%mask+min;
return ret;
}
}
class myrand {
public static void main(String[] args) {
XXXX mainprg = new XXXX();
}
XXXX() {
int i;
MyRandom generator= new MyRandom();
for (i=0; i<16; i++) {
System.out.printf("%5d\n", generator.getNext());
}
MyRandom generator6= new MyRandom(1,6);
for (i=0; i<16; i++) {
System.out.printf("%5d\n", generator6.getNext());
}
}
}
/******** 実行結果 *********
>java myrand
24590
23199
24780
12453
19962
13147
7960
3137
10150
20695
30500
25245
14610
20243
5360
12217
2
5
2
3
2
3
6
3
4
1
6
5
2
5
6
1
***************************/
課題 8.2 |
---|
(3)クラスのあるメソッドに連続して値を与えると,それまでに与えた全ての値の平均値を返すような仕組みを作りなさい。
クラス名はAverage,値を受け取り10個前の値を返すメソッドをdouble getAverage(double data)としなさい。
そしてList8.6.2の乱数を使って次のメイン部で検証しなさい。平均値は0に近づくはずである。 (p08ex03.java)
int i;(4)List 8.6.1を参考にして,クラスのあるメソッドに連続して値を与えると,10個前に入力された値を返すような仕組みを作りなさい。
int check=1;
double ran;
double ave;
MyRandom generator= new MyRandom(-100,100);
Average avrg= new Average();
for (i=1; i<10000000; i++) {
ran=generator.getNext();
ave=avrg.getAverage(ran);
if (i==check) {
System.out.printf("%8d %10.5f\n", i,ave);
check<<=1;
}
}
クラス名はDelayOutput,値を受け取り10個前の値を返すメソッドをdouble delay10(double data)としなさい。
そして次のメイン部で検証しなさい。 (p08ex04.java)
int i;
double x;
double y;
DelayOutput mydelayout= new DelayOutput();
for (i=0; i<20; i++) {
x=(double)i;
y=mydelayout.delay10(x);
System.out.printf("x=%5.2f ave=%5.2f\n", x, y);
}
8.8 データベース |
---|
データベースでもクラスが用いられている。
例えば映画のデータベースを作ろう。1つの映画が持っている「属性」にはさまざまなものがあるが,ここでは「題名」「主演」「監督」「公開年度」持たせることにする。メンバ変数を定義するところは次のようになるであろう。class Movie {
private String title; /*題名*/
private String leadingAct ; /*主演俳優*/
private String director; /*監督*/
private int year; /*公開年度*/
}
Movieの配列データなどを持つデータベースもクラスになる。
この中には,クラスMovieのインスタンスの配列が含まれる。
項目ごとにソートのメソッドも作る。
class MovieDatabase {
Movie[] movies;
void print() {
}
:
}
ソートのメソッド中のcompareToIgnoreCaseについて
A,BをString型の変数としたとき
A.compareToIgnoreCase(B)
というのは,文字列Aと文字列Bで辞書順でどちらが前にあるかを示す。
正ならばAのほうがBより後ろという意味になる。(ただし大文字小文字は区別しない)
各sortでは,データの実体を移動せず,参照のみ移動していることになるが,これで問題ない。
List 8.7.1 映画のデータベース class Movie {
String title; /*題名*/
String leadingAct; /*主演俳優*/
String director; /*監督*/
int year; /*公開年度*/
Movie(){
title="";
leadingAct="";
director="";
year=1970;
}
Movie(String title, String leadingAct, String director, int year){
this.title=title;
this.leadingAct=leadingAct;
this.director=director;
this.year=year;
}
public void print() {
System.out.printf("%-29s%-22s%-22s%5d\n",title,leadingAct,director,year);
}
}
class MovieDatabase {
Movie[] movies;
MovieDatabase() {
movies= new Movie[]{
new Movie("Planet of the Apes","Charlton Heston","Franklin J. Schaffner",1968),
new Movie("Lord of the Rings, The","Elijah Wood","Peter Jackson",2001),
new Movie("Saving Private Ryan","Tom Hanks","Steven Spielberg",1998),
new Movie("Gone with the Wind","Vivien Leigh","Victor Fleming",1939),
new Movie("Schindler's List","Liam Neeson","Steven Spielberg",1993),
new Movie("My Neighbor TOTORO","Satsuki","Hayao Miyazaki",1988),
new Movie("Braveheart","Mel Gibson","Mel Gibson",1995),
new Movie("Dances with Wolves","Kevin Costner","Kevin Costner",1990),
new Movie("Born on the Fourth of July","Tom Cruise","Oliver Stone",1989),
new Movie("Platoon","Tom Berenger","Oliver Stone",1986),
new Movie("Kramer vs. Kramer","Dustin Hoffman","Robert Benton",1979),
new Movie("Sting, The","Paul Newman","George Roy Hill",1973),
new Movie("Sound of Music, The","Julie Andrews","Robert Wise (I)",1965),
new Movie("My Fair Lady","Audrey Hepburn","George Cukor",1964),
new Movie("Titanic","Leonardo DiCaprio","James Cameron",1997),
new Movie("Princess Mononoke","Ashitaka","Hayao Miyazaki",1997),
new Movie("Les Miserables","Hugh Jackman","Tom Hooper",2012),
new Movie("Bourne Identity The","Matt Damon","Doug Liman",2002)
};
}
public void print() {
int i;
int num=movies.length;
for (i=0; i<num; i++) {
movies[i].print();
}
}
public void sort1() { /*title sort*/
int i,j;
int num=movies.length;
Movie tmp;
for (i=0; i<num-1; i++) {
for (j=i+1; j<num; j++) {
if (0<movies[i].title.compareToIgnoreCase(movies[j].title)) {
tmp=movies[i];
movies[i]=movies[j];
movies[j]=tmp;
}
}
}
}
public void sort2() { /*leadingAct sort*/
int i,j;
int num=movies.length;
Movie tmp;
for (i=0; i<num-1; i++) {
for (j=i+1; j<num; j++) {
if (0<movies[i].leadingAct.compareToIgnoreCase(movies[j].leadingAct)) {
tmp=movies[i];
movies[i]=movies[j];
movies[j]=tmp;
}
}
}
}
public void sort3() { /*director sort*/
int i,j;
int num=movies.length;
Movie tmp;
for (i=0; i<num-1; i++) {
for (j=i+1; j<num; j++) {
if (0<movies[i].director.compareToIgnoreCase(movies[j].director)) {
tmp=movies[i];
movies[i]=movies[j];
movies[j]=tmp;
}
}
}
}
public void sort4() { /*year sort*/
int i,j;
int num=movies.length;
Movie tmp;
for (i=0; i<num-1; i++) {
for (j=i+1; j<num; j++) {
if (movies[j].year < movies[i].year) {
tmp=movies[i];
movies[i]=movies[j];
movies[j]=tmp;
}
}
}
}
}
class MyMovieCollection {
public static void main(String[] args) {
MyMovieCollection mainprg = new MyMovieCollection();
}
MyMovieCollection() {
MovieDatabase mycollection= new MovieDatabase();
System.out.printf("original\n");
mycollection.print();
System.out.printf("--\n");
mycollection.sort1();
System.out.printf("sorted by title\n");
mycollection.print();
System.out.printf("--\n");
mycollection.sort2();
System.out.printf("sorted by leadingAct\n");
mycollection.print();
System.out.printf("--\n");
mycollection.sort3();
System.out.printf("sorted by director\n");
mycollection.print();
System.out.printf("--\n");
mycollection.sort4();
System.out.printf("sorted by year\n");
mycollection.print();
System.out.printf("--\n");
}
}
/************************** 実行結果 **************************************
original
Planet of the Apes Charlton Heston Franklin J. Schaffner 1968
Lord of the Rings, The Elijah Wood Peter Jackson 2001
Saving Private Ryan Tom Hanks Steven Spielberg 1998
Gone with the Wind Vivien Leigh Victor Fleming 1939
Schindler's List Liam Neeson Steven Spielberg 1993
My Neighbor TOTORO Satsuki Hayao Miyazaki 1988
Braveheart Mel Gibson Mel Gibson 1995
Dances with Wolves Kevin Costner Kevin Costner 1990
Born on the Fourth of July Tom Cruise Oliver Stone 1989
Platoon Tom Berenger Oliver Stone 1986
Kramer vs. Kramer Dustin Hoffman Robert Benton 1979
Sting, The Paul Newman George Roy Hill 1973
Sound of Music, The Julie Andrews Robert Wise (I) 1965
My Fair Lady Audrey Hepburn George Cukor 1964
Titanic Leonardo DiCaprio James Cameron 1997
Princess Mononoke Ashitaka Hayao Miyazaki 1997
Les Miserables Hugh Jackman Tom Hooper 2012
Bourne Identity The Matt Damon Doug Liman 2002
--
sorted by title
Born on the Fourth of July Tom Cruise Oliver Stone 1989
Bourne Identity The Matt Damon Doug Liman 2002
Braveheart Mel Gibson Mel Gibson 1995
Dances with Wolves Kevin Costner Kevin Costner 1990
Gone with the Wind Vivien Leigh Victor Fleming 1939
Kramer vs. Kramer Dustin Hoffman Robert Benton 1979
Les Miserables Hugh Jackman Tom Hooper 2012
Lord of the Rings, The Elijah Wood Peter Jackson 2001
My Fair Lady Audrey Hepburn George Cukor 1964
My Neighbor TOTORO Satsuki Hayao Miyazaki 1988
Planet of the Apes Charlton Heston Franklin J. Schaffner 1968
Platoon Tom Berenger Oliver Stone 1986
Princess Mononoke Ashitaka Hayao Miyazaki 1997
Saving Private Ryan Tom Hanks Steven Spielberg 1998
Schindler's List Liam Neeson Steven Spielberg 1993
Sound of Music, The Julie Andrews Robert Wise (I) 1965
Sting, The Paul Newman George Roy Hill 1973
Titanic Leonardo DiCaprio James Cameron 1997
--
sorted by leadingAct
Princess Mononoke Ashitaka Hayao Miyazaki 1997
My Fair Lady Audrey Hepburn George Cukor 1964
Planet of the Apes Charlton Heston Franklin J. Schaffner 1968
Kramer vs. Kramer Dustin Hoffman Robert Benton 1979
Lord of the Rings, The Elijah Wood Peter Jackson 2001
Les Miserables Hugh Jackman Tom Hooper 2012
Sound of Music, The Julie Andrews Robert Wise (I) 1965
Dances with Wolves Kevin Costner Kevin Costner 1990
Titanic Leonardo DiCaprio James Cameron 1997
Schindler's List Liam Neeson Steven Spielberg 1993
Bourne Identity The Matt Damon Doug Liman 2002
Braveheart Mel Gibson Mel Gibson 1995
Sting, The Paul Newman George Roy Hill 1973
My Neighbor TOTORO Satsuki Hayao Miyazaki 1988
Platoon Tom Berenger Oliver Stone 1986
Born on the Fourth of July Tom Cruise Oliver Stone 1989
Saving Private Ryan Tom Hanks Steven Spielberg 1998
Gone with the Wind Vivien Leigh Victor Fleming 1939
--
sorted by director
Bourne Identity The Matt Damon Doug Liman 2002
Planet of the Apes Charlton Heston Franklin J. Schaffner 1968
My Fair Lady Audrey Hepburn George Cukor 1964
Sting, The Paul Newman George Roy Hill 1973
Princess Mononoke Ashitaka Hayao Miyazaki 1997
My Neighbor TOTORO Satsuki Hayao Miyazaki 1988
Titanic Leonardo DiCaprio James Cameron 1997
Dances with Wolves Kevin Costner Kevin Costner 1990
Braveheart Mel Gibson Mel Gibson 1995
Platoon Tom Berenger Oliver Stone 1986
Born on the Fourth of July Tom Cruise Oliver Stone 1989
Lord of the Rings, The Elijah Wood Peter Jackson 2001
Kramer vs. Kramer Dustin Hoffman Robert Benton 1979
Sound of Music, The Julie Andrews Robert Wise (I) 1965
Schindler's List Liam Neeson Steven Spielberg 1993
Saving Private Ryan Tom Hanks Steven Spielberg 1998
Les Miserables Hugh Jackman Tom Hooper 2012
Gone with the Wind Vivien Leigh Victor Fleming 1939
--
****************************************************************************/
課題 8 |
---|
(1)List 8.4.1では座標を表す座標を表す変数はすべてint型であった。これをすべてdouble型に変更しなさい。そして,あるインスタンスが表す点が原点 を中心に反時計回りにthetaラジアン回転した新しい点の座標になるメソッドをクラスPointに追加しなさい。
(p08ex01.java) (再掲)
ただし,このメソッドを
public void rotate(double theta)
で定義しなさい。このメソッドは,呼び出されたインスタンス自身が変化するメソッドである。
同時にあるインスタンスが表す点が原点を中心に反時計回りにthetaラジアン回転した新しい点の座標を返すメソッドも追加しなさい。ただしこのメソッドを
public Point makeRotatedPoint(double theta)
で定義しなさい。このメソッドは,呼び出されたインスタンス自身は変化せず,結果を別な実体として返すメソッドである。
(x,y)を反時計回りにθラジアン回転すると( x cosθ - y sinθ , x sinθ + y cosθ )になる。
sin,cos関数は,Math.sin(xxxxx),Math.cos(xxxxx)で使うことができる。
main側で次のように検証しなさい。
Point pnt1= new Point(100.0, 100.0);
pnt1.print();
pnt1.rotate(Math.PI/4.0); /*π/4の回転*/
pnt1.print(); /*これで点(100,100)をπ/4の回転した座標が表示されるはず*/
Point pnt2= new Point(100.0, 100.0);
Point pnt3= pnt2.makeRotatedPoint(Math.PI/4.0); /*π/4の回転*/
pnt3.print(); /*これで点(100,100)をπ/4の回転した座標が表示されるはず*/
(2)List 8.4.2をもとに,次のように変更しなさい。
足し算では,メソッドpublic void add()があるが,これは呼び出されたインスタンス自身が変化するメソッドである。
呼び出されたインスタンス自身は変化せず,和を別な実体として返すメソッド
public Fraction makeAddedFraction(Fraction fr)を作りなさい。
さらに引き算,掛け算割り算について同様の2つのメソッドそれぞれ作りなさい。
public void subtract(Fraction fr)
public FractionFraction makeSubtractedFraction(Fraction fr)
public void multiply(Fraction fr)
public FractionFraction makeMultipliedFraction(Fraction fr)
public void divide(Fraction fr)
public FractionFraction makeDividedFraction(Fraction fr)
ただし,割り算において引数の分子が0の場合は,0での割り算になってしまうので,10000000/1を返すものとする。
(p08ex02.java) (再掲)
検査用main()を作り,次の値で検査すること。
1/6,1/3で四則演算(差,商については順番も換えること)
1/6,-1/3で四則演算(差,商については順番も換えること)
-1/6,1/3で四則演算(差,商については順番も換えること)
-1/6,-1/3で四則演算(差,商については順番も換えること)
3/4,16/3で四則演算(差,商については順番も換えること)
3/4,-16/3で四則演算(差,商については順番も換えること)
-3/4,16/3で四則演算(差,商については順番も換えること)
-3/4,-16/3で四則演算(差,商については順番も換えること)
(3)クラスのあるメソッドに連続して値を与えると,それまでに与えた全ての値の平均値を返すような仕組みを作りなさい。
クラス名はAverage,値を受け取り10個前の値を返すメソッドをdouble getAverage(double data)としなさい。
そしてList8.6.2の乱数を使って次のmainで検証しなさい。平均値は0に近づくはずである。 (p08ex03.java)
int i;
int check=1;
double ran;
double ave;
MyRandom generator= new MyRandom(-100,100);
Average avrg= new Average();
for (i=1; i<10000000; i++) {
ran=generator.getNext();
ave=avrg.getAverage(ran);
if (i==check) {
System.out.printf("%8d %10.5f\n", i,ave);
check<<=1;
}
}
(4)List 8.6.1を参考にして,クラスのあるメソッドに連続して値を与えると,10個前に入力された値を返すような仕組みを作りなさい。
クラス名はDelayOutput,値を受け取り10個前の値を返すメソッドをdouble delay10(double data)としなさい。
そして次のmainで検証しなさい。 (p08ex04.java)
int i;
double x;
double y;
DelayOutput mydelayout= new DelayOutput();
for (i=0; i<20; i++) {
x=(double)i;
y=mydelayout.delay10(x);
System.out.printf("x=%5.2f ave=%5.2f\n", x, y);
}
(4)8.6の映画のデータベースを参考にデータベースを作り,適当にソートして表示できるようにしなさい。 (p08ex05.java)
データベースの対象は次のようなものにしなさい。
好きな音楽CD,好きなミュージシャン,好きな野球選手,好きなサッカー選手
また,その属性の例としては
ミュージシャン 名前 デビュー年 代表曲
のようにしなさい