IS-A関係
オブジェクト指向では、「AはBの一種である」ということがいえる関係のことをIS-A関係という。
例えば、サブクラスはスーパークラスの一種である。というように、次のような形で表します。
人間は哺乳類の一種である。 哺乳類は動物の一種である。 鳥は鳥類の一種である。 鳥類は動物の一種である。
というような例をIS-A関係と考えます。
オブジェクト指向では、「AはBの一種である」ということがいえる関係のことをIS-A関係という。
例えば、サブクラスはスーパークラスの一種である。というように、次のような形で表します。
人間は哺乳類の一種である。 哺乳類は動物の一種である。 鳥は鳥類の一種である。 鳥類は動物の一種である。
というような例をIS-A関係と考えます。
前回の例では、継承したスーパークラスを何気に使っていましたが、スーパークラス側で初期化(コンストラクタ)が必要な設計になっている場合、どのように実装したらいいのかが気になります。
javaの言語仕様としては、コンストラクタの呼び出しは自動的に呼ばれる仕様になっているようです。
このときに呼ばれるのは「引数なし」コンストラクタとなるようです。
この自動的に呼び込まれるコンストラクタを自分の明示により呼び出す仕組みが「super()」という呼び出し方です。
以下に例を書きます。
public class SuperClassTest3{ public static void main(String[] args) { //オブジェクトのインスンタンスを生成する TestClassChild tcc = new TestClassChild(); System.out.println(tcc.TestA); System.out.println(tcc.TestB); //tcc.PrintTextC(); //tcc.PrintTextP(); } } class TestClassChild extends TestClassParent { TestClassChild() { //引数なしコンストラクタ用 //スーパークラスの引数なしコンストラクタを呼ぶ super(); } TestClassChild(int TestAA, int TestBB) { //スーパークラスの引数つきコンストラクタを呼ぶ super(TestAA, TestBB); } void PrintTextC() { System.out.print("PrintTextC !! \n"); } void PrintTextP() { System.out.print("PrintTextP オーバーライドテスト !! \n"); } } class TestClassParent { int TestA = 10; int TestB = 20; TestClassParent(int a, int b) { //やっていることは「setTest」メソッドを同じ TestA = a; TestB = b; System.out.print("引数つきコンストラクタ !! \n"); } TestClassParent() { //引数なしコンストラクタ用 System.out.print("コンストラクタ !! \n"); } void PrintTextP() { System.out.print("PrintTextP !! \n"); } }
実行した結果は以下のようになります。
コンストラクタ !! 10 20
結果として「コンストラクタ !! 」と表示されているのは
TestClassChild tcc = new TestClassChild();
でTestClassChildのインスタンスが生成された際に、子クラス(TestClassChild)の引数なしコンストラクタ「TestClassChild()」が反応し
「super();」が呼ばれた結果
親クラスの引数なしコンストラクタ(TestClassParent())が呼ばれて
「System.out.print(“コンストラクタ !! \n”);」が実行されたから
になります。
少しややこしいけど、書きなれて体得していくしかないかと思います。
先ほど書いた実験コードに、フィールドが継承されるかどうかの検証をしてみます。
以下のようなコードを書いてみました。
public class SuperClassTest3{ public static void main(String[] args) { //オブジェクトのインスンタンスを生成する TestClassChild tcc = new TestClassChild(); System.out.println(tcc.TestA); System.out.println(tcc.TestB); //tcc.PrintTextC(); //tcc.PrintTextP(); } } class TestClassChild extends TestClassParent { TestClassChild() { //引数なしコンストラクタ用 } void PrintTextC() { System.out.print("PrintTextC !! \n"); } void PrintTextP() { System.out.print("PrintTextP オーバーライドテスト !! \n"); } } class TestClassParent { int TestA = 10; int TestB = 20; TestClassParent() { //引数なしコンストラクタ用 } void PrintTextP() { System.out.print("PrintTextP !! \n"); } }
結果は下記のようになり、親クラスのTestA、TestBというフィールドの値を参照することができています。
10 20
■注意■
フィールドとメソッドは継承元(親クラス)のものが参照できますが、コンストラクタは継承されず、使うことができません。
先ほど作成したテストコードを改造して、親クラスをextendsした子クラス内で親クラスと同名のメソッドを記述してオーバーライドしてみます。
書いたコードは以下のとおり
public class SuperClassTest2{ public static void main(String[] args) { //オブジェクトのインスンタンスを生成する TestClassChild tcc = new TestClassChild(); tcc.PrintTextC(); tcc.PrintTextP(); } } class TestClassChild extends TestClassParent { TestClassChild() { //引数なしコンストラクタ用 } void PrintTextC() { System.out.print("PrintTextC !! \n"); } void PrintTextP() { System.out.print("PrintTextP オーバーライドテスト !! \n"); } } class TestClassParent { TestClassParent() { //引数なしコンストラクタ用 } void PrintTextP() { System.out.print("PrintTextP !! \n"); } }
子クラス(TestClassChild)内に「PrintTextP」というメソッドを書いて、親クラスのメソッドをオーバーライドしています。
これを実行すると以下のような結果になります。
PrintTextC !! PrintTextP オーバーライドテスト !!
もともと親クラスにあったメソッドが子クラス内に書いたメソッドに置き換えられて実行されている。
スーパークラスから拡張したクラスを作ってみます。
下記のようなテストコードを書いて実行してみます。
public class SuperClassTest{ public static void main(String[] args) { //オブジェクトのインスンタンスを生成する TestClassChild tcc = new TestClassChild(); tcc.PrintTextC(); tcc.PrintTextP(); } } class TestClassChild extends TestClassParent { TestClassChild() { //引数なしコンストラクタ用 } void PrintTextC() { System.out.print("PrintTextC !! \n"); } void PrintTextP() { System.out.print("PrintTextP !! \n"); } } class TestClassParent { TestClassParent() { //引数なしコンストラクタ用 } void PrintTextP() { System.out.print("PrintTextP !! \n"); } }
メインの処理の「SuperClassTest」のクラスの中から「TestClassChild」のクラスのインスタンスを生成して、そのTestClassChild内のメソッドを呼びました。
結果は
PrintTextC !!
という表示がされ、TestClassChild内のメソッドを呼べていることがわかります。
次にに、TestClassChildクラスが継承している「TestClassParent」クラスのメソッドも、同じTestClassChildインスタンス(ここではtccという名前)から呼んでみました。
すると結果は
PrintTextP !!
と表示され、継承元の「PrintTextP」メソッドが正しく動作していることがわかります。
今回の例はシンプルな継承の例ですが、一つの親クラスから、多数の子クラスを派生させることも可能なので、後ほどつめて実験してみようと思います。
スーパークラスについて勉強します。
続きは後日、、
2014.10.09追記
スーパークラスという前に、まずは継承という概念について。
javaやc++等のオブジェクト指向言語で、作成したクラスをもとに、その機能を引き継ぎつつ、違うクラスを作成することができます。
この概念を継承といいます。
元のクラスを継承してできた新しいクラスのことを、サブクラスと呼び、元のクラスのことをスーパークラスといいます。
スーパークラスは原則として1つのみです。
スーパークラスは1個のみですが、サブクラスは1個のみという制約はなく、いくつでも作り出すことができます。
サブクラスは多段に拡張していくことができます。
javaはクラスが階層的に積み重なるように作ることができ、この構造をクラス階層という。
public、protected、privateは、変数やクラスを、どの範囲から参照可能かを決める修飾子です。(他にも色々ありますが、もっとも良く使う修飾子です)
public 自ファイルおよび他ファイル、全てのクラスから参照可能 protected 他ファイルの他クラス以外、全てのクラスから参照可能 private 自ファイルの自クラスのみ参照可能 指定なし 自ファイル内の自クラス、サブクラス、他クラスから参照可能
ちょっとわかりづらいですが、上記のような関係性があり、それらの動きをよく考えつつプログラムを組みます。
クラス内のフィールドやメソッドに対して、修飾子をつけて区別します。
■final
変更不可能な値やクラスを指し示します
主に、「定数」のような使い方をします。
また、finalで指定した変数には、プログラムの途中で値を代入することはできません。
■abstract
抽象クラスや抽象メソッドであることを示します
メソッド本体がないメソッドのことを言います。
メソッドにはつけられるけど、フィールドにはつけられない。
■static
クラスフィールドやクラスメソッドであることを示します
■synchronized
synchronizedメソッド
■native
JAVA言語以外で書かれたメソッドであることを示します
、、、と列挙してみましたが、まだよくわかっていないです。
クラス内にあるメソッドについても、フィールドと同じようにクラスメソッドと呼ぶような宣言の仕方があります。
クラスフィールドと同じように「static」という修飾子をつけて表します。
例として、下記の「countUpTestD」というメソッドをクラスフィールドにしています。
class TestClassSub { //フィールドの初期化 int TestA = 10; int TestB = 30; static int TestC = 20; int TestD = 40; TestClassSub(int a, int b) { //やっていることは「setTest」メソッドを同じ TestA = a; TestB = b; } TestClassSub() { //引数なしコンストラクタ用 } void setTest(int a, int b){ TestA = a; TestB = b; } int sumAB(){ return TestA + TestB; } int countUpTestC(){ return TestC++; } void countUpTestC2(){ TestC++; } static int countUpTestD(){ return TestD++; } }
クラスメソッドは、別名「staticメソッド」という場合もある。
staticをつけずに宣言したメソッドは「インスタンスメソッド」や「staticではない(通常の)メソッド」などと呼び、クラスメソッドと区別をします。
クラスメソッドは特定のインスタンスに関連していな為、インスタンスが生成されていない状態でも呼び出すことができます。
他のクラスから呼び出すときには次のように書きます。
クラス名.メソッド名(引数)
例えば、以下のクラスをnewしてインスタンスを生成する際、インスタンスをnewした回数を取り扱いたい場合は、クラスの中にクラスフィールドという、全インスタンスに共通の情報保存場所が必要になります。
この保存場所をクラスフィールド(またはクラス変数、スタティックフィールド)と呼び、クラス内に宣言することができます。
これまでに作ってきたクラス
class TestClassSub { //フィールドの初期化 int TestA = 10; int TestB = 30; TestClassSub(int a, int b) { //やっていることは「setTest」メソッドを同じ TestA = a; TestB = b; } TestClassSub() { //引数なしコンストラクタ用 } void setTest(int a, int b){ TestA = a; TestB = b; } int sumAB(){ return TestA + TestB; } }
上記のクラスに対し、クラスフィールドを加えたクラス
class TestClassSub { //フィールドの初期化 int TestA = 10; int TestB = 30; static int TestC = 20; TestClassSub(int a, int b) { //やっていることは「setTest」メソッドを同じ TestA = a; TestB = b; } TestClassSub() { //引数なしコンストラクタ用 } void setTest(int a, int b){ TestA = a; TestB = b; } int sumAB(){ return TestA + TestB; } }
上記のTestClassSubクラスを使う場合、一度、クラスのインスタンスを生成し、クラスフィールドを加算するメソッドを何度か呼んでみます。
public class TestClass5 { public static void main(String[] args) { int retA; System.out.print("hello\n"); //引数なしコンストラクタ TestClassSub tc = new TestClassSub(); retA = tc.countUpTestC(); System.out.print("retA -> " + retA + "\n"); retA = tc.countUpTestC(); System.out.print("retA -> " + retA + "\n"); retA = tc.countUpTestC(); System.out.print("retA -> " + retA + "\n"); retA = tc.countUpTestC(); System.out.print("retA -> " + retA + "\n"); retA = tc.countUpTestC(); System.out.print("retA -> " + retA + "\n"); } } class TestClassSub { //フィールドの初期化 int TestA = 10; int TestB = 30; static int TestC = 20; TestClassSub(int a, int b) { //やっていることは「setTest」メソッドを同じ TestA = a; TestB = b; } TestClassSub() { //引数なしコンストラクタ用 } void setTest(int a, int b){ TestA = a; TestB = b; } int sumAB(){ return TestA + TestB; } int countUpTestC(){ return TestC++; } }
すると、実行した結果は次のようになります。
hello retA -> 20 retA -> 21 retA -> 22 retA -> 23 retA -> 24
前例のプログラムで、次のように、クラスからインスタンスを生成しました。
//引数なしコンストラクタ TestClassSub tc = new TestClassSub();
この時、tcが確保されている領域をスタック(stack)と呼びます。
また、TestClassSubのインスタンスが確保されている領域をヒープ(heap)と呼びます。
フィールドの初期化を行うには、コンストラクタの内部でもよく、コンストラクタの外でもOK。
前回に投稿したTestClassSubクラスのフィールドを初期化するには、次のように書いてもOK。
class TestClassSub { int TestA = 10; int TestB = 30; TestClassSub(int a, int b) { //やっていることは「setTest」メソッドを同じ TestA = a; TestB = b; } TestClassSub() { //引数なしコンストラクタ用 } void setTest(int a, int b){ TestA = a; TestB = b; } int getAB(){ return TestA + TestB; } }
初期化されていないフィールドの値は、その型に応じて初期化される。
変数の値については、未定義になる。
具体的には、値は次のようになる。
Boolean型 false;
整数型 0
浮動小数点型 0.0
参照型 null
前回に書いたクラスで、コンストラクタを呼び出した時は以下のように書きました。
TestClass tc = new TestClass(100, 200);
これはコンストラクタを呼ぶと同時に引数、100、200をつけています。
このコンストラクタの呼び方を変更して、引数をつけないで呼び出す方法もやってみます。
具体的には下記のように記述します。
TestClass tc = new TestClass();
また、ひとつのクラスの中にはコンストラクタが複数あってもエラーにはならないです。
全体として下記のように記述してみました。
詳しい動作検証は、また後ほどやります。
public class TestClass2 { public static void main(String[] args) { int retA; System.out.print("hello\n"); //これだとエラーになる //TestClassSub tc = new TestClassSub(); //引数をつけるとエラーにならない(コンストラクタで使う為?) TestClassSub tc = new TestClassSub(100, 200); retA = tc.getAB(); System.out.print("retA -> " + retA); } } class TestClassSub { int TestA; int TestB; TestClassSub(int a, int b) { //やっていることは「setTest」メソッドを同じ TestA = a; TestB = b; } TestClassSub() { //引数なしコンストラクタ用 } void setTest(int a, int b){ TestA = a; TestB = b; } int getAB(){ return TestA + TestB; } }
実行した結果は次のようになります
hello retA -> 0
次のようなプログラムを作り、コンパイル後、実行しました。
public class TestClass { public static void main(String[] args) { int retA; System.out.print("hello\n"); //これだとエラーになる //TestClassSub tc = new TestClassSub(); //引数をつけるとエラーにならない(コンストラクタで使う為?) TestClassSub tc = new TestClassSub(100, 200); retA = tc.getAB(); System.out.print("retA -> " + retA); } } class TestClassSub { int TestA; int TestB; TestClassSub(int a, int b) { //やっていることは「setTest」メソッドを同じ TestA = a; TestB = b; } void setTest(int a, int b){ TestA = a; TestB = b; } int getAB(){ return TestA + TestB; } }
一番最初に、TestClassSubのインスタンスを作ろうとした時に、下記のようなエラーが表示されました。
d:\data\java>javac TestClass.java TestClass.java:7: シンボルを見つけられません。 シンボル: コンストラクタ TestClassSub() 場所 : TestClassSub の クラス TestClassSub tc = new TestClassSub(); ^ エラー 1 個
少し悩んでいたところ、TestClassSubのコンストラクタは引数を書いていたので、インスタンスを作る際にも引数がないとエラーになることに気づき、引数をつけると無事にコンパイルがとおりました。
インスタンスを作り、「getAB」というメソッドを試したまでですが、無事に下記のように表示されました。
d:\data\java>java TestClass hello retA -> 300
まずはエラーにならずにサクっと動作させるようにして進みたいと思います。
エラーでつまずくと、少し後ろ向きになってしまいますが、そうゆう場合には、一旦できているところまでを見直しつつ、なるべく前向きに勉強していきます。
道は長い
先ほど作成した下記のクラス
class TestClass { int TestA; int TestB; void setTest(int a, int b){ TestA = a; TestB = b; } int getAB(){ return TestA + TestB; } }
に対して、コンストラクタを追加します。
class TestClass { int TestA; int TestB; TestClass(int a, int b) { //やっていることは「setTest」メソッドを同じ TestA = a; TestB = b; } void setTest(int a, int b){ TestA = a; TestB = b; } int getAB(){ return TestA + TestB; } }
コンストラクタを呼び出すのは、以下のように書きます。
TestClass tc = new TestClass(100, 200);
さきほど作成したTestClass内にあるメソッドを呼び出して使って見ます。
TestClass tc = new TestClass(); tc.setTest(100, 200);
上記は「setTest」というメソッドを呼んで値を渡しています。
その後処理はメソッドに基づいて計算されます。
新しいインスタンスを作成した後は、フィールドに値を代入してみます。
TestClass tc = new TestClass(); tc.TestA = 1000; tc.TestB = 2000;
TestAやTestBという変数名は意味としてわかりづらいですが、まずは感触をつかむ為にこのように書いてみます。
先ほど作ったTestClassを使うためには、インスタンスを作る必要があります。
具体的には次のように書きます。
new TestClass();
newで宣言するだけなら、なにも計算ができないので、次のようにしてTestClass型として変数に代入します。
TestClass tc = new TestClass();
これまでに何度も出てきている「フィールド」について勉強します。
フィールドは情報を保存する変数のようなものです。
例えば、下記のように書きます。
class TestClass { int TestA; int TestB; }
TestAとTestBはTestClassのフィールドと呼びます。
続いてメソッドについて、考えます。
メソッドは関数のような意味で、宣言します。
class TestClass { int TestA; int TestB; void setTest(int a, int b){ TestA = a; TestB = b; } int getAB(){ return TestA + TestB; } }
抽象的なインタフェースを具体的に実装するクラスを宣言することもできます。
class TestClass implements TestAAA{ }
TestAAAインタフェースを実装したTestClassというクラスを宣言する。という意味になります。
が、まだよくわかっていません。そもそもインタフェースとはなにか、これから掘り下げてみたいと思います。
既に存在しているクラスをもとにして、拡張するクラスを宣言することもできます。
class TestClass extends Thread{ }
上記の場合は、Threadクラスを拡張してTestClassを宣言しています。
javaに標準搭載されているクラスの他に自分でクラスを作る場合は、次のように書きます。
class TestClass{ }
クラスの名前は大文字で始めるのが慣習となっているようです。
うむむ。
クラスの宣言と同時にフィールドの宣言、メソッドの宣言も、書くと次のようになります。
class TestClass{ フィールドの宣言 メソッドの宣言 }
「フィールド」は、そのクラスにおける変数と考え、
「メソッドは、関数、と考えられます。
クラスには様々な種類があります。
例えば
文字列を表す「String」クラス
ファイルを表す「File」クラス
ファイルを読み込む「FileReader」クラス
ファイルに書き込む「FileWrite]クラス
Javaシステム全体で扱う「System」クラス
javaで開発を行う際には、もともと搭載されているクラスにどんなものがあるのかを一旦把握した上で開発するのがよいと思います。実際になにかを開発して、手足のように使いこなすまでは、しらばく勉強を続けたいと思います。
オブジェクト指向を勉強するにあたり、クラスとインスタンスの考え方からやっていきます。
インスタンスはクラスから生成される。という原則をもとに考えると、文字列もまたStringクラスのインスタンス。と考えることができます。
プログラム内で「”Hellow”」と記述した場合は、Stringクラスのインスタンスが生成されたものと同じことになります。
そのクラスの性質としては、「文字列を表す」「文字列の長さを取得できる」といった文字列に関係する操作(関数)ができるようになります。
「インスタンス」とは、上記の「”Hellow”」のように、「特定のもの」を表すと考えることができます。
全てのインスタンスは、クラスというものに属しています。
「インスタンスは具体的な特定のもの」といえます。また、「同じクラスのインスタンスは共通の性質を持つ」といえます。
「クラスとオブジェクト」の時に書いた以下のコード
class TestClass { String test_a; int test_b; }
の部分を詳しくみると、「String test_a;」という部分があります。
この「String」の部分は型、「test_a」の部分はフィールドと呼びます。(クラス内にある関数はメソッドと呼びます)
オブジェクト指向のプログラムを行うときは、インスタンスを生成してフィールド内の(変数の)値を参照したりすることが多くあるので、用語として覚えておきます。
また、クラスの宣言を確認するときは、必ず「フィールド」と「メソッド」をチェックしてプログラムを行います。
インスタンスを生成する際に、初期化を行ったり、初期値を設定したりする時に、コンストラクタというものを使います。
コンストラクタはクラスの中に書き、インスタンスの初期化を行う(生成時に一度実行される性質の為)
下記のように書きます。
また、コンストラクタには戻りの型を書きません。
public class TestClass { String test_a; int test_b; //コンストラクタ public TestClass(String test_a, int test_b) { } }
また、コンストラクタ内ではthisという「現在のインスタンス」を表す変数が使えます。
thisはjavaの予約語で、頻繁に使います。
public class TestClass { String test_a; int test_b; //コンストラクタ public TestClass(String test_a, int test_b) { this.test_a = test_a; this.test_b = test_b; } }
javaの基本的な書き方の次のステップとして、クラスとオブジェクトに差し掛かっていきます。
まずはクラスとはなにかを勉強し、その後にオブジェクト指向の勉強に進めていきます。
2014.09.24 追記
クラスとは、関連した情報を1つにまとめたもの。
以下のように宣言します。
public class TestClass { String test_a; int test_b; }
実際にプログラムして、動きを確かめようと思い、次のようなソースを作りました。(ファイル名は「array_test2.java」としました)
public class array_test2 { public static void main(String[] args) { //「tc」はTestClassのインスタンスという TestClass tc = new TestClass(); tc.test_a = "A"; tc.test_b = 1; //ひとつのクラスから2個目のインスタンスを作る TestClass tc2 = new TestClass(); tc2.test_a = "B"; tc2.test_b = 2; } public class TestClass { String test_a; int test_b; } }
コンパイルを実行する
c:\javac array_test2.java
すると、次のような結果がでます(エラー)。
array_test2.java:6: static でない 変数 this を static コンテキストから参照することはできません。 TestClass tc = new TestClass(); ^
ネットを調べたりしましたが、少しの時間悩み、次のように書き直したところ、動作しました。
public class array_test2 { public static void main(String[] args) { TestClass tc = new TestClass(); } } class TestClass { String test_a; int test_b; }
結局、TestClassのクラスが「public class array_test2 」の外側にないとエラーになるようです。
理由としては、staticのついたメソッド(クラスメソッド)の中で、自クラス内のstaticでないメソッドや
フィールド変数、クラス内クラスを参照することはできない。為でした。
うーん、まだ、感覚的にわからないです。
配列を書いてみます。
public class array_test { public static void main(String[] args) { //配列を宣言 int[] test_array; //配列を3つ設定 test_array = new int[3]; test_array[0] = 10; test_array[1] = 20; test_array[2] = 30; System.out.print("配列の中身は " + test_array[0] + "\n"); System.out.print("配列の中身は " + test_array[1] + "\n"); System.out.print("配列の中身は " + test_array[2] + "\n"); //配列の長さ(要素数)を取得する System.out.print("配列の中身の数は " + test_array.length + "\n"); //配列の初期化(宣言と同時に値を入れる) int [] test_array_s = {10, 20, 30}; //配列「test_array」にまとめて代入する書き方も可能 test_array_s = new int[]{10, 20, 30}; //2次元配列の場合 int[][] test_array_ss; //初期化と同時に値を代入 int[][] test_arrays_sss = { {10, 20, 30}, {40, 50, 60}, {70, 80, 90} }; //出力する場合は次のように書く System.out.print("2次元配列の3行目の2列は " + test_arrays_sss[2][1] + "\n"); //2次元配列の要素数は一定でなくてもOK int[][] test_arrays_ssss = { {10, 20, 30}, {40, 50}, {70, 80, 90} }; } }
実行結果は
配列の中身は 10 配列の中身は 20 配列の中身は 30 配列の中身の数は 3 2次元配列の3行目の2列は 80
となります。
java言語の基礎的な部分がC言語と多いので、基礎的な文法の練習は省きたいと思いますが、メソッド(関数)の書き方についてはjava特有の書き方があるので、勉強してみます。
まずは、以下のサンプル
public static void main (String[] args) { }
このメソッドの宣言を見てみると、次のように解釈できます。
public → アクセス制限(公開されているかどうか) static → クラスメソッド int → 戻り値の型を指定 main → メソッドの名前 String[] args → 引数はString型 argsはこれから勉強する
publicというアクセス制限の部分については、次の段階で細かく勉強していきます。
ランダムな値を表示させるプログラムを書いてみます。
下記のようなプログラムを書いて、コンパイル→実行、をしてみます。
import java.io.*; public class random_test { public static void main(String[] args) { //乱数のテスト int x; x = (int)(Math.random() * 9); System.out.print("乱数の結果->" + x + "\n"); } }
実行結果は
乱数の結果->6
という表示になり、実行の度に数値がランダムに変わります。
少しjavaを書くことに慣れてきました。
C言語と同じような部分が多く、文法的な練習については省きます。