プログラミング/8

出典: CourseWiki

目次

クラス(その1)

もう少し複雑で実用的なプログラムを作るためには,今まで(難しいので避けてきた)クラスを使う必要があるので,クラスについて少し解説します.

クラス(class)とは,何らかのひとかたまりのデータと,そのデータを操作するための手段(メソッド)を定義するためのものです.

メソッドの定義が以前と違うと思う人がいるかも知れません.細かい説明はクラスを定義する方法を学ぶまで置いておくことにします.

例えば学校の生徒ならば,共通して名前,学籍番号,成績などの属性を持っているでしょう.生徒を表すStudentクラスを作成すれば,これらの属性をひとくくりにしてまとめることができます.さらに,成績を取得したり,成績を更新する処理が必要ですが,これもStudentクラスで定義できます.

クラスを定義すると,そのクラスに対応するインスタンス(instance)を作ることができます(インスタンスを作ることをインスタンス化するといいます). インスタンスとは,クラスの定義に基づいて作られた具体的なデータのことです.学校の生徒の例だと,1つのインスタンスには特定の1人の生徒の情報が入ります.Javaでは,インスタンスはnew演算子によってヒープメモリ上に作られます.

一つのクラス定義から,複数のインスタンスを作ることができます.

オブジェクト(object)という語は,インスタンスの総称として使われます.この資料では,オブジェクトとインスタンスを等価なものとして扱います.

オブジェクトとインスタンスについては,詳しくは http://itpro.nikkeibp.co.jp/article/lecture/20070130/260070/?P=1&ST=lecture あたりを読んでください(無料会員登録が必要).

幸いなことに,クラスの内部を知らなくてもクラス定義さえあればクラスを使うことはできるので(これはオブジェクト指向プログラミングの利点の一つです),まずはJavaで最初から用意されているクラスを使ってみます.

文字列

Javaで文字列を扱うには,Stringクラスを使います.

Stringクラスは,「0文字以上の文字の並び」をひとまとめにして扱うクラスです.これによって文字列を表現します.また,文字列を扱うためのメソッドを数多く備えています.Javaでは最も頻繁に使うクラスの1つです.

解説していませんでしたが,Javaでは文字を扱うための char という基本型があります. char は Unicode 文字を表すための型で,2バイトの大きさを持ちます.Stringクラス内部では,文字列は char の配列 (char[]) として実現されています.

JDKのソースコードをダウンロードすればStringクラスの定義を読むことができます.


String h;              // Stringクラスの変数 h を作る
h = "Hello, ";         // 文字列定数は "" でくくる
String w = "World";
System.out.print(h);
System.out.println(w);

h や w はString型の変数です.String型の変数は(ヒープメモリ上にある)Stringクラスのインスタンスへの参照を保持しています(配列と場合と同様です.プログラミング/6#ヒープメモリとnew演算子参照).String型の変数には,Stringクラスのインスタンスへの参照しか代入することはできません.

h = "Hello, "; では,文字列 "Hello, " が入っているString型のインスタンスがヒープメモリ上に確保されて,そのオブジェクトへの参照が h に代入されます.

画像:String.png

文字列はよく使われるので,JavaではStringクラスは(配列同様)特別扱いされていて,いちいち new 演算子を使わなくても書けるようになっています(ダブルクォーテーションで囲んだものはStringクラスのオブジェクトとして扱われるようになっている).

メソッド呼び出し

インスタンスを操作するためには,そのインスタンスのクラスで定義されているメソッドを使います (ここでいう「操作」には,インスタンスの状態を調べたりすることを含みます).

Stringクラスのインスタンスを操作するには,Stringクラスで定義されているメソッドを使うことになります.

文字列の長さ(length)

キーボードから入力された文字列の文字数を数えるプログラムをつくってみましょう.

あるStringの文字数(正確には,Stringクラスのインスタンスに格納されている文字数)を数えるためには Stringクラスで定義されているlengthメソッドを使います.このメソッドは文字数をint型の値で返します.

String型の変数 s があるとき,s.length() と書くことで,lengthメソッドを呼び出すことができます. このとき,lengthメソッドは,sの指すインスタンスに対して働く(つまり,sの指すインスタンスの文字数が返ってくる)ことに注意してください.

なお,キーボードから文字列を入力するには Keyboard.stringValue() を使います. これは,キーボードから入力された文字列をString型で返すメソッドです.

s.length()と書く場合,メソッド名(length)の前(s)は(String型の)変数ですが, Keyboard.stringValue() ではメソッド名(stringValue)の前(Keyboard)は変数ではありません. 実はKeyboardというのはクラス名なのですが,この違いは後で説明します.

最近のEclipseでは,実行画面(コンソール画面)で日本語を入力できないことがあるようです(刺さってしまう). うまくいかなければ,コマンドラインで実行して下さい.

// 文字数を数える
public class CountNameLength {
    public static void main(String[] arg) {
        System.out.print("名前は? ");
        String name = Keyboard.stringValue();
        System.out.println(name + "さん,こんにちわ!");
        
        // nameの文字数を計算して表示
        int n = name.length();
        System.out.println(name + "さんの名前は" + n + "文字ですね!");
    }
}

部分文字列(substring)

文字列の中の部分文字列を取り出すには,次のようにsubstringメソッドを使います. substringメソッドは,ある文字列の指定した部分を取り出して,別のStringインスタンスとして返します.

String hello = "Hello";
// helloの1文字目から4文字目の直前まで取り出す
String s1 = hello.substring(1, 4);  // s1 は "ell" になる
// helloの1文字目以降を取り出す.
String s2 = hello.substring(1);     // s2 は "ello" になる

Javaでは文字列の最初の文字は(配列と同様に)0文字目と数えることになっています.

substringメソッドには,引数が1つのメソッドと2つのメソッドがあります. 引数が1つのメソッドでは,指定された場所から最後までを取り出します.

Javaではこのように引数のパターンが違う同じ名前のメソッドを定義できます

引数が負の数だったり,文字列の長さより大きい場合は,StringIndexOutOfBoundsException という例外が発生します.

文字列の比較(equals)

String a と String b の中身が等しいかどうかを判定するために==は使えません. (よく間違えるところなので注意!)

String型変数に対して==を使うと,参照が等しいかを判定します.つまり,a == b は,aが指しているヒープメモリ上での場所とbが指しているヒープメモリ上での場所が等しいかどうかを判定します.

中身が等しいか(つまり,文字数が同じで,かつ0文字目から最後の文字までが一致するかどうか)を判定するためにはStringクラスのequalsメソッドを使って,a.equals(b) (あるいは b.equals(a)) のように書きます.equalsメソッドは,等しければ true,等しくなければ false を返すメソッドです.

String a = Keyboard.stringValue(); // ここで End と入力したとすると...
String b = "End";
String c = a;
if (a == b) {    // aとbは違うオブジェクトを指しているので false
  ...
}
if (a == c) {    // aとcは同じオブジェクトを指しているので true
  ...
}
if (a.equals(b)) {    // aとbは中身が同じなのでtrue
  ...
}

画像:StringEquals.png

文字列の連結

メソッドではありませんが,文字列を扱う上で一つ重要な機能を書いておきます.

+ 記号を使って2つの文字列を連結することができます.

String h = "Hello, ";
String w = "World";
String hw = h + w;
System.out.println(hw); // Hello, World と表示する

文字列と文字列でないものを + で連結するときは,後者は文字列に変換されることになっています.

// 数値である28が,文字列である"28"に変換されて連結される
String tetsujin = "鉄人" + 28 + "号";

(プログラミング/2#文字列の連結で既にやっていますが),この機能を使うと計算結果を簡単に表示できます.

double area = 3.14 * r * r;
System.out.println("面積は " + area + " です");

実は文字列の連結処理はJavaコンパイラが StringBuilder クラス(可変文字列を扱うクラス)を呼び出すコードに変換することで実現されています.

一般に,プログラミング言語で,他の書き方でも書ける処理を,人間に分かりやすく書けるようにするための構文を syntax sugar (糖衣構文,あるいは構文糖)といいます.この文字列の "+" は Java の syntax sugar の一つです. 文字列を String s = "Sugar"; のように new なしで生成できるのも syntax sugar です.

文字列とメソッド

配列と同様に,Stringもメソッドの引数やメソッドの返り値として使えます.

public class StringExample {
    public static void main(String[] args) {
        String s = "Hello, World";
        for (int i = 0; i < s.length(); i++) {
            String r = rotateString(s, i);
            System.out.println(r);
        }
        System.out.println();
        System.out.println(repeatString("Yah ", 3));
 
    }
    
    // 文字列の先頭からn文字を切り取り,末尾に追加した文字列を返す
    private static String rotateString(String s, int n) {
        String left = s.substring(0, n);
        String right = s.substring(n);
        return right + left;
    }
 
    // 文字列sをn回繰り返した文字列を返す
    private static String repeatString(String s, int n) {
        String t = "";
        for (int i = 0; i < n; i++) {
            t = t + s;
        }
        return t;
    }
}

実行結果:

Hello, World
ello, WorldH
llo, WorldHe
lo, WorldHel
o, WorldHell
, WorldHello
 WorldHello,
WorldHello, 
orldHello, W
rldHello, Wo
ldHello, Wor
dHello, Worl

Yah Yah Yah 

演習 (しりとり1)

文字列をくりかえし入力して,最後に「ん」がつく単語が入力されたら終了するプログラム Shiritori1 を書いてください.

単語: りんご 
単語: ごりら
単語: らいおん
あなたの負け!
  • とりあえず,「しりとり」になっているか(前の単語の最後の文字と次の単語の最初の文字が一致しているか)のチェックは不要
  • 与えられた文字列の最後の文字を返すメソッド private static String getLast(String s) を定義すること
  • ヒント: 文字列の最後の文字を得るためには,length()で長さを求めてsubstring()を使う
  • 1文字も入力せずにEnterキーを押した場合,エラーにならないようにせよ.
    • 何も考えないと,getLastメソッドで StringIndexOutOfBoundsException が発生するはず

Eclipseで実行して入力がうまくいかない場合は,ターミナル上で実行してみてください.

演習(一人しりとり)の回答例

continue文

上のプログラムの繰り返し(while文)では,ある条件が満たされた場合(ここでは word.length() == 0),繰り返しの残りの部分はスキップして再度繰り返しを行います.

このような場合はよくあるので,Javaではcontinue文が用意されています.

while文やfor文の繰り返しの中で continue と書くと,繰り返しの最後にジャンプします.break文は繰り返しから脱出しますが,continue文は脱出しません.(break文と同様,繰り返しの外では使えません)

while (条件) {
  処理1;
  if (条件) {
     continue;   // 処理2をスキップ
  }
  処理2;
}

これは,以下のプログラムと同等です.下のように書くよりも,continue文を使ったほうが「再度繰り返しを行う」という意図が明確ですし,インデントも浅くて済むので分かりやすいプログラムになります(特に処理2が長い場合).

while (条件) {
  処理1;
  if (!条件) {
    処理2;
  }
}

continue文を使ったしりとりプログラム(しりとり2)

演習(continue文)

演習(エラトステネスの篩)の回答例のプログラムを,continue文を使って書き直してください.

演習(continue文)の回答例

課題8-1(しりとり3)

先ほどのしりとりでは,ちゃんとしりとりになっているチェックを省略していました.これを省略しないバージョン Shiritori3 を書いてください.入力がちゃんとしりとりになっていなければ,再度入力を求めるようにしてください(下の例を参照). 単語の重複チェックは不要です.

最初はしりとりの「り」から始めることにします.

しりとりをしましょう(して下さい?).さいしょはしりとりの「り」からです.
単語: ごりら
「り」から始まることばを入れてください
単語: りんご
単語: きりん
「ご」から始まることばを入れてください
単語: ごりら
単語: 
「ら」から始まることばを入れてください
単語: らーめん
あなたの負け

ヒント:

  • 文字列の先頭の文字を得るメソッドも定義しましょう.
  • 直前の単語の最後の文字を覚えるための変数を用意します (最初は "り" にしておく).

課題8-2(回文判定)

文字列を入力して,逆順に表示し,さらに回文かどうかを判定するプログラム Palindrome を作ってください.

回文では小文字(ゃゅょなど)大文字の差異や濁点・半濁点の有無を無視する場合もありますが,ここではそのようなルールはないものとします.

文字列を入力してください: This is a pen.
逆順にすると,.nep a si sihT です.
回文ではありません.
文字列を入力してください: わたしまけましたわ
逆順にすると,わたしまけましたわ です.
回文です.
  • 与えられた文字列を逆順にするメソッド private static String reverse(String s) を書く
  • reverse では文字列の長さを length() で求めて,右端から左端に向かって1文字づつ連結する
  • 回文かどうかの判定は,逆順にした文字列と元の文字列を比較すれば良い


Copyright (C) 2002-2015 Kota Abe, Osaka City University

ナビゲーション