今回の話題の一つは文字列についてです。
これまでの例題では、 文字列を String というキーワードを用いて、 宣言し利用してきました。 実は Java では、文字列もオブジェクトとして扱っています。 クラス String は、 文字列を表すクラスであり、 あらかじめ Java のクラスライブラリに存在しているクラスです。
クラス String は少し特殊な性質を持っています。
クラスからインスタンスを作成するためには、 new 演算子を用い次のように書くのでしたね。
String str = new String("Hello");
ただし、クラス String に限り、 上記のように new 演算子を用いる以外に次のようにして インスタンスを生成できます。
String str = "Hello";
この式の右辺のように、 文字列をダブルクォーテーション (") で括って記述することで、 String のインスタンスを生成することが可能です。 このように Java では、ダブルクォーテーションで括り記述された文字列は、 全て String のオブジェクトとして扱われることになっています。
次の例は String のインスタンスを生成する式です。
new String("Hello, Java.") "Java progamming is easy." "Linux" + " is also easy." "I have studied Java for " + day + " days."
特に、上の例の 3 番目や 4 番目のように、 プラス (+) 演算子を用いて文字列や数値を連結させることができます。 そして、連結した結果の文字列のオブジェクトが新たに生成されることになります。
また、本講義では扱いませんが、 自由に文字列を追加したり、長さを変更できるクラスに StringBuffer があります。
String のオブジェクトが表す文字列の内容が同一であるかを調べるには、 比較演算子 == を用いることができません。 クラス String の メソッド equals を用います。
例えば、String のオブジェクト str1 と str2 の内容が同一であるかを 調べるには以下のように書きます。
if (str1.equals(str2)) { ... 等しい場合の処理 } else { ... 異なる場合の処理 }
メソッド equals は、自らのオブジェクト (上の例では str1) の内容と、 引数で与えられたオブジェクト (上の例では str2) の内容を比較し、 等しい場合には true を、異なる場合には false を返します。
文法的には以下のようになります。
基準となるStringのオブジェクト . equals ( 比較したいStringのオブジェクト )
実は、クラス String にはメソッド equals 以外にも、 様々なメソッドがあらかじめ用意されています。 その中で、主要なものを紹介します。
例: String message = "California Girls"; System.out.println(message.length()); (文字列 message の長さ 16 が表示される)
例: String girls = "California Girls"; String boys = "California Boys"; if (girls.compareTo(boys) == 0) System.out.println(girls + "と" + boys + "は等しい") else if (girls.compareTo(boys) < 0) System.out.println(girls + "は" + boys + "より辞書順で前") else // girls.compareTo(boys) > 0 System.out.println(girls + "は" + boys + "より辞書順で後ろ")
例: String message = "California Girls"; char ch = 'a'; System.out.println(message + "には" + ch + "という文字が" + message.indexOf(keyword) + "文字目にあります");
String message = "California Girls"; String keyword = "Girl"; System.out.println(message + "には" + keyword + "というキーワードが" + message.indexOf(keyword) + "文字目にあります");
String message = "California Girls"; System.out.println(message.replace("Girls", "Boys"); (message の中の "Girls" の部分が "Boys" に置き換えられて表示される)
他にもたくさんのメソッドがあります。 JDK 5.0 ドキュメント、 もしくは、 /usr/java 以下に展開した同様のドキュメントを参照してください。
このドキュメントから分かるように、 実は Java には様々なクラスがあらかじめ用意されています。 このように再利用可能なクラス集をクラスライブラリと言います。
また、クラスライブラリの一部は、 プログラムと外部とのインタフェースであることから、 API (Application Program Interface) と呼ばれます。
残念ながらこの講義では、 クラスライブラリの内容全てを取り上げることは不可能です。 しかし、 Java でプログラムを効率的に開発するためには、 クラスライブラリは非常に有効です。 また、ウィンドウ、アイコン、ボタン等の GUI の部品や、 画面入出力やファイル入出力のためのストリームも クラスライブラリに含まれます。
クラスライブラリを使いこなすことが上達への早道です。
今回取り上げる 2 つめの話題は修飾子です。 修飾子は、クラスの属性やメソッドを、 他のクラスからアクセスできるようにするかどうかや、 変数の書き換えを禁止させるかどうか といった様々な設定を行うためのものです。
クラスの利点の1つに、 他のオブジェクトによるアクセスから クラスの属性を守ることができるということがあります。 属性は外部に対して公開されたメソッドを通じてのみ、 変更や参照を行うことができます。 これにより、意図しない属性の操作を防ぐことができます。
これまでは、メソッドを用いることで、 内部の属性に対して直接操作を行わないような プログラムの書き方をするように心掛けてきました。 しかし、属性を直接書き換えるような、 好ましくないプログラムも作ろうと思えば作れてしまいます。 Java には、 属性やメソッドへのアクセスを明示的に許可、禁止する方法があります。
Java では、クラスの属性とメソッドを宣言するときに、 それらを保護するために、 以下の 4 つのアクセスレベルを指定することができます。 private, protected, public そして指定なしのアクセスレベルです。
例えば、製品を表す次のようなクラスがあったとします。
class Product { String name; String id; int price; void setNameIDAndPrice(String n, String i, int p) { name = n; id = i; price = p; } }
Java では、特に何も指定しないと属性やメソッドは、 クラスの外に対して公開されることになります。 しかし、製品名 (name), 型番 (id), 価格 (price) の各属性が外部に公開されたままであると、 次のように、外部から値を代入したり、参照することが可能です。
class Stranger { public static void main(String[] args) { Product pc = new Product(); pc.name = "TDU ThinkPad X40"; pc.id ="2371-A85"; pc.price = 200000; // ここで、本来あり得ない型番を登録できてしまう pc.id = "9999-XXX"; // 値段を不正に操作できてしまう pc.price = 100; } }
上のプログラムのように、 製品名と一致しない不正な型番を登録してしまったり、 値段を改竄して不正な取引きを行うことができてしまいます。 このように、クラスの設計に意図しない使われ方ができてしまいます。 また、内容を秘密にしたい情報を属性に持つオブジェクトを、 別のオブジェクトに渡して処理を依頼したいという場合に、 内部の秘密情報に勝手にアクセスされては困ります。
以上のことから、オブジェクトにアクセスするためには、 常に正しく設計され、外部に対して公開されたメソッドを用いるべきなのです。
以下に、属性とメソッドに対する 4 つのアクセスレベルについて説明します。
もっとも厳しいアクセス制限レベルは private です。 private のメンバは、 そのクラスの中でのみアクセス可能となります。 外部からアクセスされたくない属性や、 外部から実行されたくないメソッドを private にします。
private のメンバを宣言するには、 宣言時にキーワード private を使用します。
前に示した理由から、一般に属性の値は外部から直接値を参照したり、 書き換えることを避けるべきです。 このような場合に private を使います。 次の例は、クラス Product の各属性を private にした場合の プログラムです。
class Product { private String name; private String id; private int price; void setNameIDAndPrice(String n, String i, int p) { name = n; id = i; price = p; } }
属性だけでなく、メソッドも private にすることができます。 private メソッドは、外部のクラスからはアクセスできず、 そのクラスの中からのみ実行することができるようになります。 例えば、クラスの中の下請け処理を行うようなメソッドは、 private とし不用意に外部から使われないようにするのが好ましいのです。
いまのところ protected は private とほぼ同じ アクセスレベルであると思っていてください。
細かい相違点は改めて説明することにします。
最もオープンなアクセスレベルは public です。 public メンバは外部に対して公開され、 任意のクラスからアクセスすることが可能です。 例えば、外部から実行されるメソッドは public にします。
public のメンバを宣言するには、 宣言時にキーワード public を使用します。
次の例は、クラス Product の各属性を private にし、 各メソッドを public にした場合のプログラムです。
class Product { private String name; private String id; private int price; public void setNameIDAndPrice(String n, String i, int p) { name = n; id = i; price = p; } }
これまでのプログラムでは、 main メソッドを除いて、 修飾子を特に指定しませんでした。
この場合、 そのクラスのファイルが存在する 同じディレクトリ内にあるすべてのクラスに対しては、 メンバにアクセス可能となります。 これは「同じパッケージ内では公開される」という決まりなのですが、 パッケージについての詳しい内容は本講義では取り扱わないこととします。
private, public, protected のキーワードは、 いずれか一つのみを指定することしかできません。
クラスの属性や局所変数 (ローカル変数) の宣言時に指定することができる 修飾子に以下のものがあります。
- final
- static
final で指定された変数は、 宣言時に初期化し、それ以降値の変更ができないことを示します。
プログラムの中で一度決めたら途中で変更することがない定数として用いる場合、 キーワード final を用いて変数宣言を行います。 以下に、簡単な例を示します。
public Circle { // 円周率 PI を 3.14 に初期化 private final double PI = 3.14; ... public void somethingToCalculate() { // 自然対数の底 E を 2.72 に初期化 final double E = 2.72; ... } }
なお、上の変数 PI の宣言のように、 キーワード final は private, public などの他の修飾子と組み合わせて使うことができます。
static については、後の回で扱います。
private, public, protected に加えて、 メソッドに指定することができる修飾子に以下のものがあります。
- abstract
- final
- static
- syncronized
- native
static については、後の回で扱いますが、 その他の修飾子については本講義では扱いません。 皆さんの自習にまかせることにします。