今回の話題の一つは文字列についてです。
これまでの例題では、 文字列を 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 以外にも、 様々なメソッドがあらかじめ用意されています。 その中で、主要なものを紹介します。
他にもたくさんのメソッドがあります。 JDK 5.0 ドキュメント、 もしくは、 /usr/java 以下に展開した同様のドキュメントを参照してください。
このドキュメントから分かるように、 実は Java には様々なクラスがあらかじめ用意されています。 このように再利用可能なクラス集をクラスライブラリと言います。
また、クラスライブラリの一部は、 プログラムと外部とのインタフェースであることから、 API (Application Program Interface) と呼ばれます。
残念ながらこの講義では、 クラスライブラリの内容全てを取り上げることは不可能です。 しかし、 Java でプログラムを効率的に開発するためには、 クラスライブラリは非常に有効です。 また、ウィンドウ、アイコン、ボタン等の GUI の部品や、 画面入出力やファイル入出力のためのストリームも クラスライブラリに含まれます。
クラスライブラリを使いこなすことが上達への早道です。
今回取り上げる 2 つめの話題は修飾子です。 修飾子は、クラスの属性やメソッドを、 他のクラスからアクセスできるようにするかどうかや、 変数の書き換えを禁止させるかどうか といった様々な設定を行うためのものです。
エッセンシャル Java pp.176-182 を参照してください。
クラスの利点の1つに、 他のオブジェクトによるアクセスから クラスの属性を守ることができるということがあります。 属性は外部に対して公開されたメソッドを通じてのみ、 変更や参照を行うことができます。 これにより、意図しない属性の操作を防ぐことができます。
これまでは、メソッドを用いることで、 内部の属性に対して直接操作を行わないような プログラムの書き方をするように心掛けてきました。 しかし、属性を直接書き換えるような、 好ましくないプログラムも作ろうと思えば作れてしまいます。 Java には、 属性やメソッドへのアクセスを明示的に許可、禁止する方法があります。
Java では、クラスの属性とメソッドを宣言するときに、 それらを保護するために、 以下の 4 つのアクセスレベルを指定することができます。 private, protected, public そして指定なしのアクセスレベルです。
以下のクラス TDUStudent の定義を見てみましょう。 (メソッドの内容は省略しています。)
class TDUStudent { int id; String name; int math; int english; int physics; TDUStudent(int i, String n) { ... } void setScore(int m, int e, int p) { ... } String getName() { ... } int average() { ... } }
id, name, math, english, physics の各属性が外部に公開されたままであると、 次のように、外部から値を代入したり、参照することが可能です。
class Stranger { public static void main(String[] args) { TDUStudent john = new TDUStudent(1, "John Lennon"); john.name = "Ringo Starr"; john.math = 20; System.out.println(john.math); } }
このままでは、勝手に氏名が書き換えられてしまったり、 本来平均点を求めることしかできないはずであるのに、 勝手に数学の点数を盗み見ることができてしまったりと、 クラスの設計に意図しない使われ方ができてしまいます。
秘密にしたい情報を属性に持つオブジェクトを、 別のオブジェクトに渡して処理を依頼したいという場合に、 内部の秘密情報に勝手にアクセスされては困ります。 オブジェクトにアクセスするためには、 常に公開されたメソッドを用いるべきなのです。
以下に、それぞれ 4 つのアクセスレベルについて説明します。
もっとも厳しいアクセス制限レベルは private です。 private のメンバは、 そのクラスの中でのみアクセス可能となります。 外部からアクセスされたくない属性や、 外部から実行されたくないメソッドを private にします。
private のメンバを宣言するには、 宣言時にキーワード private を使用します。
次の例は、クラス TDUStudent の各属性を private にした場合の プログラムです。
class TDUStudent { private int id; private String name; private int math; private int english; private int physics; ...... ...... }
属性だけでなく、メソッドも private にすることができます。 private メソッドは、外部のクラスからはアクセスできず、 そのクラスの中からのみ実行することができるようになります。
いまのところ protected は private とほぼ同じ アクセスレベルであると思っていてください。
細かい相違点は改めて説明することにします。
最もオープンなアクセスレベルは public です。 public メンバは外部に対して公開され、 任意のクラスからアクセスすることが可能です。 外部から参照される属性や、 外部から実行されるメソッドは public にします。
public のメンバを宣言するには、 宣言時にキーワード public を使用します。
次の例は、クラス TDUStudent の各属性を private にし、 各メソッドを public にした場合のプログラムです。
class TDUStudent { private int id; private String name; private int math; private int english; private int physics; public TDUStudent(int i, String n) { ... } public void setScore(int m, int e, int p) { ... } public String getName() { ... } public int average() { ... } }
これまでのプログラムでは、 main メソッドを除いて、 修飾子を特に指定ませんでした。
この場合、 そのクラスのファイルが存在する 同じディレクトリ内にあるすべてのクラスに対しては、 メンバにアクセス可能となります。 これは「同じパッケージ内では公開される」という決まりなのですが、 パッケージについての詳しい内容は本講義では取り扱わないこととします。
private, public, protected のキーワードは、 いずれか一つのみを指定することしかできません。
これらをまとめると、 エッセンシャル Java p.178 あるいは p.182 の表のようになります。
クラスの属性や局所変数 (ローカル変数) の宣言時に指定することができる 修飾子に以下のものがあります。
- final
- static
final で指定された変数は、 宣言時に初期化し、それ以降値の変更ができないことを示します。
プログラムの中で一度決めたら途中で変更することがない定数として用いる場合、 キーワード final を用いて変数宣言を行います。 以下に、簡単な例を示します。
public AClass { // 円周率 PI を 3.14 に初期化 private final double PI = 3.14; ... public void aMethod() { // 自然対数の底 E を 2.72 に初期化 final double E = 2.72; ... } }
なお、上の変数 PI の宣言のように、 キーワード final は他の修飾子と組み合わせて使うことができます。
static については、後で扱います。
private, public, protected に加えて、 メソッドに指定することができる修飾子に以下のものがあります。
- abstract
- final
- static
- syncronized
- native
static については、後で扱いますが、 その他の修飾子については本講義では扱いません。 皆さんの自習にまかせることにします。
今回の 3 つめの話題はメンバの種類についてです。
オブジェクトの属性とメソッドをメンバと呼びます。 メンバにはインスタンスメンバと静的メンバの二種類があります。
これまでのプログラムで用いてきたクラスの属性やメソッドは、 インスタンスを生成することで使用することができます。 これらのメンバをインスタンスメンバと呼びます。 一方、インスタンスを作成せずに使用できる属性やメソッドもあります。 これを静的メンバと呼びます。
これまで、用いてきた属性やメソッドは、 メソッド main を除き、 全てインスタンスメンバに分類されます。
インスタンスは、クラスをもとに生成される「実体」のことでした。 インスタンスはいくつでも生成することができ、 個々のインスタンスはそれぞれが別個のものとして振る舞います。 また、その属性もインスタンスごとに別個の値を保持します。
このように個々のインスタンスが独立してもつ属性やメソッドのことを インスタンスメンバ (instance member) と呼びます。
クラスをもとにインスタンスを一つ生成すれば、 インスタンスメンバも各一つ存在することになり、 インスタンスを n 個生成すれば、 インスタンスメンバも n 個存在することになります。
インスタンスメンバは個々のインスタンスで独立したメンバであり、 複数のインスタンスで共有することはできません。 また、インスタンスメンバにアクセスするには、 あらかじめ、インスタンスを生成しておく必要があります。
静的メンバはクラスに属する属性やメソッドです。 オブジェクトの有無とは無関係に使うことができます。 したがって、静的メンバを使うためにはインスタンスを生成する必要はありません。
静的属性 (static field, 静的変数) は、インスタンスごとに独立した値とはならず、 クラス単位で共通に使える属性です。 静的属性はクラスに対して一つだけ割り当てられる変数であり、 同じクラスを型とする全てのインスタンスで共有されます。 また、静的属性の値はオブジェクトの生成、消滅とは無関係に、 プログラムの実行を開始してから終了するまで値が保持されます。
静的属性を宣言するには、 次のように、クラス定義の中の属性の宣言部分にキーワード static を用います。
static 型名 変数名;
静的属性の使用例として定数があげられます。 定数はクラスに共通して使用される値であり、 途中で変更することがありません。 そこで、定数であることを示すキーワード final と、 アクセス修飾子 public を用いて定数を宣言すると以下のようになります。
class MathConstants { public static final double PI = 3.14; public static final double E = 2.72; } class Circle { .... // 上で定義した定数を用いた計算例 area = radius * radius * MathConstants.PI; }
静的属性にアクセスするには、 クラス名と属性名をドットでつなぎ、以下のように記述します。
クラス名 . 属性名
なお、 同じクラス定義の中では「クラス名 . 」の部分を省略することができます。
静的メソッド (static method) は、 インスタンスの有無に関わらず利用することができるメソッドです。 静的メソッドを宣言するには、キーワード static を用いて以下のように書きます。
static 返り値の型 メソッド名 (引数1, 引数2, ...)
静的メソッドを呼び出すには、 クラス名とメソッド名をドットでつなぎ、以下のように記述します。
クラス名 . メソッド名 ( 引数1, 引数2, ... )
静的メンバを使う場面は限られています。 例として、 クラスに関係する定数を静的属性として宣言する場合や、 オブジェクトを生成する必要がない処理のまとまりを 静的メソッドとして宣言すること等が考えられます。
次のプログラムは、四則演算を行うクラス Caluculation の例です (0 で除算した場合の処理は省略)。
class Calculation { public static int add(int a, int b) { return a + b; } public static int subtract(int a, int b) { return a - b; } public static int multiply(int a, int b) { return a * b; } public static int divide(int a, int b) { return a / b; } }
四則演算の機能を実現するメソッドを考えると、 インスタンスを生成する必要がありません。 このような場合は、静的メソッドを用いるのが適していると言えます。 しかし、計算途中の値を覚える等の機能を持つ等、 内部の状態を属性として持つようなクラスは、 静的メソッドを用いず、インスタンスを生成して使うべきです。
一般に、プログラムで行う仕事の内容を考えたときに、 対象 (もの) として考えられるものについては、 インスタンスを生成すべきだと言えます。
プログラムの実行が開始されるメソッドとして main メソッドがあります。 これは、 public 、すなわち外部から実行可能なメソッドとして、 以下のように宣言されています。
public static void main(String[] args) { ... }
実行時には、 java コマンドで指定されたクラス内にあるメソッド main が、 まず最初に実行されるという決まりになっています。 また、メソッド main は static メソッドとして宣言されていますので、 インスタンスを生成すること無く実行できます。