メソッドの呼び出し
さきほど作成したTestClass内にあるメソッドを呼び出して使って見ます。
TestClass tc = new TestClass(); tc.setTest(100, 200);
上記は「setTest」というメソッドを呼んで値を渡しています。
その後処理はメソッドに基づいて計算されます。
さきほど作成した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言語と同じような部分が多く、文法的な練習については省きます。
入力待ち状態のプログラムを書きます。
import java.io.*; public class input_test { public static void main(String[] args) { System.out.print("なにか入力してください\n"); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); try { String line = reader.readLine(); System.out.print("入力した文字は" + line + "です\n"); } catch (IOException e) { System.out.print(e); } catch (NumberFormatException e) { System.out.print("なにか間違っています\n"); } } }
実行すると、次のようになります。
なにか入力してください 129873469127384 入力した文字は129873469127384です
初歩の為に書いたプログラムは簡単でしたが、上記のプログラムは見たことがない記述が出て気ました。
まず行頭でいきなりimportという命令が来ます。
これはクラスライブラリを利用する時に宣言する記述です。
そもそもBufferedReaderというものを見たことも書いたこともなかったです。
どうやらこれはデータの読み込みを行う為のクラスということでした。
C言語を勉強した時には出てこなかったtryという書き方もjavaでは頻繁に使うようです。
あとは、クラスをnewして使う部分もC言語では出てこなかったと思います。
とにかく一度書いて動かしてみて慣れていくのがいいのかもしれません。
言語に慣れる為に、いろいろと書いて実行をさせてみます。
文法的にはC言語に近い部分があるので、初歩の段階では得に違和感もなく書いて動かしてみました。
public class hello { public static void main(String[] args) { System.out.print("hello\n"); //改行を試す System.out.print("hello2\n"); System.out.print("hello3\n"); //日本語を試す System.out.print("でますか?\n"); //加減乗除を試す System.out.print("たしざん" + (3+2) + "\n"); System.out.print("ひきざん" + (3-2) + "\n"); System.out.print("かけざん" + (3*2) + "\n"); System.out.print("わりざん" + (3/2) + "\n"); //結果は余りが表示される //変数を試す int x; x = 5; System.out.print("変数の中身は" + x + "です"); } }
実行すると次のような結果になります。
hello hello2 hello3 でますか? たしざん5 ひきざん1 かけざん6 わりざん1 変数の中身は5です
JAVAでソースコードを書いて、コンパイルし、hellow worldを出力したとがないので、やってみます。(いままではIEDとかを使用していました)
次のコードを書いて、hello.javaというファイルに保存します。
public class Hellow { public static void main(String[] args) { System.out.print("hello"); } }
次にDOSコマンドプロンプトを立ち上げて、ソースコードを保存したディレクトリに移動します。
//便宜的に下記のディレクトリを作業ディレクトリにしました cd C:\works\java
次にソースコードをコンパイルするコマンドを入力します。
コンパイルは「javac」の後にスペースを空けてソースコードを指定します。
C:\works\java>javac hello.java
と、ここで下記のようなエラーが発生しました。
'javac' は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。
少し調べてみましたが、原因はまだわかっていません。
PC内の環境変数をチェックしてみましたが、正しくPATHが追加されていました。
(20140828追記)
その後、一旦PCから離れ改めてjavacを実行してみると次のように表示されました。
おそらくですが、JDKをインストールし、環境変数のpathを変更したあとにPCの再起動をした為かと思います。
C:\works\java>javac 使い方: javac <options> <source files> 使用可能なオプションには次のものがあります。 -g すべてのデバッグ情報を生成する -g:none デバッグ情報を生成しない -g:{lines,vars,source} いくつかのデバッグ情報だけを生成する -nowarn 警告を発生させない -verbose コンパイラの動作についてメッセージを出力する -deprecation 推奨されない API が使用されているソースの位置を出力する -classpath <path> ユーザークラスファイルおよび注釈プロセッサを検索する位置を指定する -cp <path> ユーザークラスファイルおよび注釈プロセッサを検索する位置を指定する -sourcepath <path> 入力ソースファイルを検索する位置を指定する -bootclasspath <path> ブートストラップクラスファイルの位置を置き換える -extdirs <dirs> インストール済み拡張機能の位置を置き換える -endorseddirs <dirs> 推奨規格パスの位置を置き換える -proc:{none,only} 注釈処理やコンパイルを実行するかどうかを制御します。 -processor <class1>[,<class2>,<class3>...]実行する注釈プロセッサの名前。デフォルトの検出処理をバイパス -processorpath <path> 注釈プロセッサを検索する位置を指定する -d <directory> 生成されたクラスファイルを格納する位置を指定する -s <directory> 生成されたソースファイルを格納する場所を指定する -implicit:{none,class} 暗黙的に参照されるファイルについてクラスファイルを生成するかどうかを指定する -encoding <encoding> ソースファイルが使用する文字エンコーディングを指定する -source <release> 指定されたリリースとソースの互換性を保つ -target <release> 特定の VM バージョン用のクラスファイルを生成する -version バージョン情報 -help 標準オプションの概要を出力する -Akey[=value] 注釈プロセッサに渡されるオプション -X 非標準オプションの概要を出力する -J<flag> <flag> を実行システムに直接渡す
さらにコンパイルを実行してみると、コンパイルに失敗。
C:\works\java>javac hello.java hello.java:1: クラス Hellow は public であり、ファイル Hellow.java で宣言しなければなりません。 public class Hellow { ^ エラー 1 個
プログラム内のクラス名がおかしいので修正をする。
public class hello { public static void main(String[] args) { System.out.print("hello"); } }
改めてコンパイル
C:\works\java>javac hello.java C:\works\java>
何事もなく完了。コンパイルしたjavaを実行します。
C:\works\java>java hello hello
と表示されました。
ケアレスミスが多くとまどりましたが、環境構築はひとまず完了です。
まず最初にJAVAの開発環境を用意します。
JAVAは移植性の高い言語と呼ばれています。
その理由は一度書いたソースコードはJVMと呼ばれる、プラットフォーム上で、ネイティブコードに変換して実行する仕組みになっている為、どの機械でも動作しやすいように設計されています。
配布時にはプラットフォームから独立したJAVAバイトコードになっていて、それをプラットフォーム固有のネイティブコードに変換して実行します。
この変換と実行を行う部分がJVMと呼ばれるものです。
実行前にまとめて変換することで実行時のオーバーヘッドをなくして実行速度を向上させたものをJITコンパイラと呼びます。
■開発環境を整える
まずは、JAVAを動作させる為に、JDKをインストールします。
公式のサイトからJDKをダウンロードし、自マシンにインストールします。
■公式サイト
http://www.oracle.com/technetwork/java/javase/downloads/index-jsp-138363.html#javasejdk
■JDKのダウンロード(ここから適宜、バージョンを選択してダウンロードする)
http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
これからJAVAの言語を勉強していきます。
今まではおぼろげながらJAVAに触れてきましたが、ひととおりの知識を得る為に、ひとつひとつ実験しながら、体系的にやっていきます。
体系的に知ることが目的なので、深すぎる部分については、次の段階で落とし込もうと思います。
任意の場所でプログラムの実行を中止させる場合は、exit()関数を使います。
stdlib.hが必要です。
#include <"stdio.h"> #include <"strlib.h"> int main(void) { int i; if (i) { exit(); //ここで突然終わります。 } }
配列内の数値や文字列を並びかえるにはqsort()関数を使います。
参考例を実行してみましたが、動作せず・・・。
#include <stdio.h> #include <stdlib.h> int compare(const void *a, const void*b) { if (*a > *b) { return 1; } else if(*a < *b) { return -1; } else if(*a == *b) { return 0; } } int main(void) { int num[] = {4, 6, 5}; qsort(num, sizeof(int), compare); printf("%d\n", num); return 0; }
エラーメッセージは次のように出ました。
関数呼び出しに指定されているパラメータ数が少ないです。 呼び出している関数の引数の数を再確認してください。
「qsort(num, sizeof(int), compare);」この行でエラーが出ていることがわかっていますので、C言語ライブラリのマニュアルを検索してみると、次のように書かれています。
#include <stdlib.h> void qsort( void * data , size_t data_cnt , size_t data_size , int( * func )( const void * , const void * ); ■戻り値: なし
指数や平方根などの数学レベルでの計算が必要な場合に使います。
実際に使う場合は、math.hで定義された数学用の関数を読み込みます。
■乱数を作る
ランダムな文字列を作成するには、stdlib.hを読み込み、rand()関数を使います。
実際に書いてみます。
#include <stdio.h> #include <stdlib.h> int main(void) { int n; srand(time(NULL)); n = rand(); printf("%d\n", n); return 0; }
結果は、実行するたびにランダムな数字が表示されます。
日時を取得する為には、time()関数とlocaltime()関数を組み合わせて使います。
これらの関数の定義はtime.hの中で定義されています。
■現在時刻を得る
time_t ct; //time_t型という型になります ct = time(NULL); struct tm *now; now = localtime(&ct); //「&ct」はtime関数で得た値が入った変数のアドレスを指定します。
time.hを利用したtm構造体のメンバはtm_secやtm_min等があります。
その他のメンバや、使い方等はC言語の標準ライブラリについて調べる必要があります。
シフト演算子とは、ビット列を左右に指定した分だけずらす動きをします。
シフト演算子は2種類あり、「右シフト演算子」と「左シフト演算子」があります。
■右シフト演算子(ビット列を右に2ビットシフトする)
↓最上位ビット ↓最下位ビット 元のビット列 1 0 1 1 0 0 1 0 ~~~ここが シフトしたビット列 1 1 0 0 1 0 1 0 ~~~ここにシフトされる
■左シフト演算子(ビット列を左に2ビットシフトする)
↓最上位ビット ↓最下位ビット 元のビット列 1 0 1 1 0 0 1 0 ~~~ここが シフトしたビット列 1 0 1 0 1 1 0 0 ~~~ここにシフトされる
データをビット単位で、比較、操作する際に使うのがビット演算子です。
■論理積(and) &
各ビットを比べて、「両方とも1なら1、そうでなければ0」を返す演算
参考例
a = 170, b = 245
変数名 10進数 2進数 a 170 1 0 1 0 1 0 1 0 b 245 1 1 1 1 0 1 0 1 a&b 160 1 0 1 0 0 0 0 0
■論理和(or) |
「片方が1なら1、そうでなければ0」を返す演算(これはどちらも1の場合は1となる)
変数名 10進数 2進数 a 170 1 0 1 0 1 0 1 0 b 245 1 1 1 1 0 1 0 1 a|b 160 1 1 1 1 1 1 1 1
■排他的論理和(xor) ˆ
「片方が1で、もう片方が0なら1、そうでなければ0」を返す演算
変数名 10進数 2進数 a 170 1 0 1 0 1 0 1 0 b 245 1 1 1 1 0 1 0 1 aˆb 160 1 1 1 1 1 1 1 1
■1の補数表現(not) ~
「各ビットを反転させたもの」
変数名 10進数 2進数 a 170 1 0 1 0 1 0 1 0 a~ 160 0 1 0 1 0 1 0 1
列挙型とは、整数値に特定の名前を与えることです。
列挙型を使うと、int型の整数値に名前をつけることができます。
列挙型の宣言はenumという記述ではじめます。
サンプルを書いて動かしてみます
#include <stdio.h> int main(void) { //列挙型の宣言 enum _mon{ Jan, Feb, Mar, Apr, Jun } mon; printf("%d\n", mon); //結果「4198543」と表示される //明示的な値を代入する場合 //列挙型の宣言 enum _mon{ Jan = 1, Feb, Mar = 5, //ここはわざと5にする Apr, Jun } mon; //↑の例では、1,2,5,6,7,という値が自動的に割り振られることになる //列挙型の値を使う時の例。列挙指定子のいずれかの名前を使った代入や参照ができる mon = Feb; printf("%d\n", mon); //結果「2」と表示される return 0; }
共用体とは、1つのアドレスに異なるデータを割りあてることです。
共用体は、1つのメモリ領域で異なる型の変数のどれかひとつを選んで使うことができます。
共用体の宣言は以下のようになります。
//「uniondata」は共用体名、「unilist1」は共用体変数 union uniondata{ int no; char name[10]; float weight; }; union uniondata unilist1;
共用体を使ったサンプルを試してみます。
#include <stdio.h> int main(void) { //「uniondata」は共用体名、「unilist1」は共用体変数 union uniondata{ int no; char name[10]; float weight; }; union uniondata unilist1; unilist1.no = 1; printf("%d\n", unilist1.no); strcpy(unilist1.name, "test!!"); printf("%s\n", unilist1.name); unilist1.weight = 123.4; printf("%f\n", unilist1.weight); return 0; }
unilist1がメモリに占める領域は一番大きな型にあわせた大きさになる。
共用体はまだまだ深いので、改めて追記して理解を深めることにします。
出力結果は以下のなります。
1 test!! 123.400002
#defineを使うと、引数をもち、関数のように動作するマクロを定義することができます。
例
#define HIKU(x, y) ((x) - (y))
上記の書き方は有効範囲は1行です。
複数行の場合は次のように定義します。
#define hiku2(a, b, c) ((a) * (b) * (c) )\ + a+b+c)
実際に動かしてみます
#include <stdio.h> #define HIKU(x, y) ((x) - (y)) int main(void) { int i; printf("マクロ!%d\n", HIKU(10, 8)); return 0; } //結果は「マクロ!2」と表示されます
引数つきマクロは、実行する処理全体とその中の引数はカッコでくくります。
//正しい例 #define HIKU(x, y) ((x) - (y)) //間違った例 #define HIKU(x, y) (x - y)
マクロ名と()の間にスペースを入れてしまうと、区切りが正しく認識されません。
//正しい例 #define HIKU(x, y) ((x) - (y)) //間違った例 #define HIKU (x, y) ((x) - (y))
条件に応じて、必要な部分だけ抜き出してコンパイルしたい場合、次のように書きます。
#if 条件 指定範囲 #endif
#ifdef 識別子 指定範囲 #endif
#ifndef 識別子 指定範囲 #endif
また、複数の条件を判断することもできます。
#ifdef 識別子 範囲指定A //指定範囲Aをコンパイルする #elsif 条件B 範囲指定B //指定範囲Bをコンパイルする #else 範囲指定C //指定範囲Cをコンパイルする #endif
複数のプログラムファイル内でヘッダファイルを使うと、同じヘッダファイルを重複インクルードしてしまう場合があります。
これを防ぐためには次のように書きます。
#ifndef _MYHEADER_ #define _MYHEADER_ //2回目以降は既に呼ばれているので、この内容は読み込まれない void MyFunc(); extern int x; #endif