2017-04-13 第2回目 Java言語の基礎2

本日のテーマ

Java言語の基礎2

コメント

Javaのコメントは3種類の書き方ができます.

クラス定義の基本形

まずは,以下のクラスファイルの基本形を覚えておきましょう. GivenClassName は与えられたクラス名に置き換えてください. run メソッド内に指定されたプログラムを書いてください. main メソッドは返り値の型の前にpublic staticというキーワードがついていますが,これらのキーワードは今のところは mainのみにつけるものと思ってください.

public class GivenClassName{
    void run(){
        // ここに課題内容のプログラムを書く.
    }
    public static void main(String[] args){
        GivenClassName application = new GivenClassName();
        application.run();
    }
}

ここで注目してもらいたいのは,6行目,GivenClassName application = new GivenClassName();と, 7行目のapplication.run(); です.

6行目のnew GivenClassName()GivenClassName という型の実体を作成し, GivenClassName 型の変数application に代入しています. そして,7行目のapplication.run() は,変数application に対して, run メソッドを呼び出しています.

C言語とは異なり,Java言語では,メソッドはどこかの型の実体に必ず所属しています. そのため,メソッドを呼び出すときは,どの実体に対して呼び出すのかを明示しなくてはいけません.

new演算子

先ほど,new で実体を作成したと言いました. 次のプログラムは今まで書いてきたプログラムの例です.

Integer value1 = 3;
Integer value2 = 5;
Integer result = value1 + value2;
System.out.println(result);

実は,このプログラムは,様々なものが省略されています.省略せずに書くと,次のようなプログラムになります.

Integer value1 = new Integer(3);
Integer value2 = new Integer(5);
Integer result = new Integer(value1 + value2);
System.out.println(result);

このように,実は,実体を作成して変数に代入しています. Java言語では,ほぼ全てこのように実体を作成しなければ変数に代入できないことを覚えておいてください.

変数

宣言場所

変数は宣言する場所によって2種類の呼び方があり,特徴も少し異なります. 一つは,メソッド内で宣言する変数,ローカル変数(local variable), もう一つは,メソッドの外で宣言する変数,フィールド(field)です. メソッドもフィールドもどちらも,クラス宣言の直下になければいけません(クラス宣言の括弧({ })のすぐ内側).

プログラムを書くとき,変数の有効範囲はできるだけ狭く,というのが定石です. そのため,基本的には,ローカル変数を使い,ローカル変数で対応できない場合に,フィールドを利用しましょう.

実体

実体を確認するために,次のプログラムを考えてみましょう.

Date は日付を表す型です.new Date(), もしくは,new Date(Integer year, Integer month, Integer day, Inetger hour, Integer minute) という命令で実体を作成します.ただし,年月日,時間を指定する場合,年は1900年からの経過年数, 月は0から始まる(0が1月を表す)点に注意してください. 古い時代のもので,互換性のために残されているもので,変な仕様ではありますが,これで確認してみましょう.

また,コンパイル時に「非推奨のAPIを使用または...」という注意が出ますが,無視してください. これはコンパイルエラーではなく,注意(Warning)ですので,コンパイルはできています. お勧めできない古い時代のライブラリを使っている時に出ます.基本的には使わない方が良いのですが, 今回は説明のために用います.

なお,Date型を利用するときは,import java.util.Dateクラス宣言の前に必要になります.import文が必要な理由は, FAQ import文とは何かを参照してください.

実際に,入力して,どのような実行結果になるかを考えてから,動かしてみましょう. そして,予想との違いを考えてください.

import java.util.Date;

public class DateExample{
    void run(){
        // year は 1900年からの経過年数.
        // month は 0 から始まる.
        Date date1 = new Date(115, 8, 29, 9, 0);
        Date date2 = date1;
        System.out.printf("date1: %s%n", date1);
        System.out.printf("date2: %s%n", date2);

        // Year を +1 している.
        date1.setYear(date1.getYear() + 1);
        System.out.printf("date1: %s%n", date1);
        System.out.printf("date2: %s%n", date2);
    }
    public static void main(String[] args){
        DateExample example = new DateExample();
        example.run();
    }
}

Javaの変数はほぼ全てがポインタとなっています. ポインタとは,メモリ領域の特定の場所を指し示すだけの変数です. 上のプログラムの date2 = date1という命令が実行されると,date2date1 の参照先が代入されたことを表します. その結果,date1date2 は同じ参照先の実体を指し示すようになり, 一方を変更すると(date1.setYear(...);), もう一方も変更されることになります. 下の図をクリックすると図が更新されます.何度かクリックして見て動作を確認しましょう.

基礎プログラミング演習で学習したものとは変数の扱いが異なる場合がありますので,注意してください. Java言語では,全ての変数は,メモリ上のどこかにある実体を参照しているものと思ってください.

参照の一致性

先のプログラムrun メソッドに次のコードを追加して,実行結果を確認してみましょう.

void run(){
    Date date1 = new Date(115, 8, 29, 9, 0);
    Date date2 = date1;
    date1.setYear(date1.getYear() + 1);
    // 以下のプログラムを追加する.
    Date date3 = new Date(116, 8, 29, 9, 0);

    System.out.println(date1 == date2);
    System.out.println(date1 == date3);
    System.out.println(date2 == date3);
}

各変数が指し示す Date 型変数の値は全て,2016-09-29 9:00 になっているはずですが, なぜ truefalse という違いが現れるのでしょうか. 実は,== は参照先が同じであるかを判定する演算子であり,値が一致するか否かは判定されません. 以下の画像をクリックし,動作を確認してみましょう.

値の一致性

では,2つの変数の値が一致するかどうかを判定するにはどうすれば良いのでしょうか. それには,Objects型のequalsメソッドを利用します. 先ほどと同じく,runメソッドに次の処理を追加し,実行結果を確認しましょう.

import java.util.Date;
import java.util.Objects;

public class DateExample{
    void run(){
        // ...
        // 次の処理を追加する.
        System.out.println(Objects.equals(date1, date2));
        System.out.println(Objects.equals(date1, date3));
        System.out.println(Objects.equals(date2, date3));
    }
    // ...
}

このようにすることで,値の一致性を確認できます. Javaで比較する時は,参照の一致か,値の一致か,どちらを判定したいのかを区別して比較しましょう.

なお,Objects型を利用するときは,クラス宣言の前に import 文が必要です. import java.util.Objects と書いてください.

配列

宣言方法

Java言語にもC言語と同じように,配列が存在します. ただし,宣言の方法はC言語とは異なります.

// C言語は以下のように,変数名に配列を表す記号をつけていた.
int array[];
// Java言語では,型名の方に配列を表す記号をつける.
// Integerの配列型である array を宣言する,と考えてください.
Integer[] array;

配列の長さ

Java言語で配列の長さを取得するには,配列型の変数に .length をつけることで取得できます.

Integer[] array = // ... ;
System.out.println(array.length); // => arrayの長さが出力される.

配列の各要素

配列の各要素にアクセスするには,C言語と同じく,array[0]のように書きます. この array[0] の 0 をインデックスと呼びます. 配列の要素は,0から始まり,配列の長さ - 1 のインデックスまでが配列の有効な範囲です.

配列の範囲外の要素にアクセスしようとすると,ArrayIndexOutOfBoundsException というエラーが発生します.

配列の例

では,次のように,String型の配列であるarray の各要素から図のように文字列が参照されている例を考えましょう.

配列の例

この時,以下の処理を行うと,どのような結果になるか考えましょう.

  1. System.out.println(array.length);
  2. System.out.println(array[1]);
  3. System.out.println(array[5]);
  4. System.out.println(array[6]);

Moodle上の小テストを受験してください.

配列の作成方法

この授業では扱いません.基本的に自分で配列を作成することはなく, すでにあるものを利用するのみとします. 配列が必要な場合は,4回目で扱う Listを利用するようにしましょう.

コマンドライン引数

今まで,main メソッドの引数に String型の配列がついていました. この変数を使うと,コマンドライン引数で受け取った値をプログラムから利用できます. 次のプログラムで,コマンドライン引数で与えられた値を全て出力してみましょう.

public class ArgsPrinter{
    void run(String[] args){
        for(Integer i = 0; i < args.length; i++){
            System.out.printf("%d: %s%n", i, args[i]);
        }
    }
    public static void main(String[] args){
        ArgsPrinter printer = new ArgsPrinter();
        printer.run(args);
    }
}

入力できれば,上記のプログラムを実行して実行結果を確認してください. 実行する時,コマンドライン引数をいくつか与えてみてください.

$ java ArgsPrinter
$ java ArgsPrinter abc def ghi
$ java ArgsPrinter abcdefg
$ java ArgsPrinter 0 1 2 3 4 5 6 7 8 9

このように,run メソッドが引数を受け取るようにすることもできます. 必要に応じてコマンドライン引数を run メソッドでアクセスできるようにしましょう.

ページのトップに戻る

練習問題

1. HelloWorld 改

コマンドライン引数で与えられた人に,挨拶しましょう. もし,誰も指定されない場合は,"Hello, World"と出力してください. クラス名は,HelloWorld2 とし,クラス定義の基本形に従ってプログラムを書いてください. コマンドライン引数も参考になるでしょう.

出力例

$ java HelloWorld2 Tamada
Hello, Tamada
$ java HelloWorld2 Sagisaka
Hello, Sagisaka
$ java HelloWorld2
Hello, World

2. HelloWorld 改2

コマンドライン引数で与えられた人に,挨拶しましょう. 基本的には,HelloWorld 改と同じですが,もし, "World"が指定されたら,"Hi, World"と気さくに挨拶してください. クラス名は,HelloWorld3 とし,クラス定義の基本形に従ってプログラムを書いてください. コマンドライン引数も参考になるでしょう.

出力例

$ java HelloWorld3 Sagisaka
Hello, Sagisaka
$ java HelloWorld3 Tamada
Hello, Tamada
$ java HelloWorld3 World
Hi, World
$ java HelloWorld3
Hello, World

3. 階乗

コマンドライン引数であたえられた数値の階乗を計算するプログラムを作成してください. $n$の階乗は,$n! = n \times (n - 1) \times (n - 2) … 2 \times 1$ で求められます.

public class Factorial{
    void run(String[] args){
        Integer number = new Integer(args[0]);
        Integer factorial;
        // number の階乗を計算する.
        System.out.printf("%d! = %d%n", number, factorial);
    }
    // mainメソッドは省略
}

String型からInteger型への変換

なお,String型の変数からInteger型に変換するには,以下のように行います. Factorial の3行目で同じことを行っています.

// Integer max = "15"; // コンパイルエラー."15"という文字列はそのまま Integer 型に代入できない.
String numberString = "15";
Integer max = new Integer(numberString); // => max には 15 という数値が代入される.

出力例

$ java Factorial 3
3! = 6
$ java Factorial 4
4! = 24
$ java Factorial 5
5! = 120

4. FizzBuzz

FizzBuzz は1から特定の値までの数値を順に出力します. ただし,3の倍数の時は,数値の代わりに Fizz という文字列を, 5の倍数の時は,数値の代わりに Buzz という文字列を, 3と5の公倍数の時は,数値の代わりに FizzBuzz という文字列を出力します. クラス名は,FizzBuzzとしてください.

コマンドラインから受け取った値まで,上記のルールに従って値を出力してみましょう. もし,コマンドラインで値が指定されなかったときは,15が指定されたものとして処理を進めてください.

出力例

$ java FizzBuzz 3
1
2
Fizz
$ java FizzBuzz
1
2
Fizz
4
Buzz
... 途中省略
13
14
FizzBuzz

5. Fibonacci数列

Fibonacci数列とは,次の漸化式で表される数列です. このFibonacci数列を初項からコマンドライン引数で指定された項まで出力してみましょう. コマンドライン引数が指定されなかった場合は,20項目(20項も出力結果に含む)までを出力するようにしてください. クラス名は,Fibonacciとします.

\[ F_i = \begin{cases} 1 & i=1\\
1 & i=2\\
F_{i-2}+F_{i-1} & i\geq 3 \end{cases} \]

出力例

$ java Fibonacci 3
1 1 2
$ java Fibonacci 8
1 1 2 3 5 8 13 21
$ java Fibonacci 15
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 
$ java Fibonacci
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765 

ページのトップに戻る

まとめ