String クラスRevised: Dec./20th/2003: Since: Jan./1st/2002
文字列は基本データ型ではありません。 String クラスのオブジェクトとして保持します。
java.lang.Object | +--java.lang.String
API 仕様では次のように説明されています:
Stringクラスは文字列を表します。Java プログラム内の"abc"などのリテラル文字列はすべて、このクラスのインスタンスとして実行されます。文字列は定数です。この値を作成したあとに変更はできません。可変文字列は
StringBufferクラスでサポートします。
String クラスのコンストラクタは沢山あります。ここでは全ては説明しません。詳細は API 仕様を参照ください。
String() |
新しく生成された String オブジェクトを初期化して、空の文字列を表すようにします。 |
String(byte[] bytes) |
プラットフォームのデフォルトの文字コードを使って、指定されたバイト配列を変換することによって新しい String を構築します。 |
String(char[] value) |
新しい String を割り当てて、これが文字配列引数に現在含まれている文字列を表すようにします。 |
String(String value) |
新しく生成された String オブジェクトを初期化して、引数と同じ文字列を表すようにします。 |
String(StringBuffer buffer) |
StringBuffer 型の引数に現在含まれている文字列を持つ新しい文字列を構築します。 |
byte 型配列と char 型配列をくっつけて、一つの文字列にできるところが面白いのではないでしょうか。
今までも String 型は何度も使ってきました。このクラスのインスタンス化は、通常のインスタンス化の書式に則っても良いのですが、今までのように簡略化することも出来ます。
class TestStringInst{
public static void main(String args[]){
String objSt1=new String("こんにちは!");
String objSt2="Hello!";
System.out.println(objSt1);
System.out.println(objSt2);
}
}
String クラスのオブジェクトは基本データ型の変数と同様の書式でインスタンス化できるように決められています。それでもオブジェクトでもあります。
C:\Java>javac TestStringInst.java C:\Java>java TestStringInst こんにちは! Hello!
メソッド、メンバ変数については、沢山あるので全ては紹介しません。詳細は API 仕様を直接ご確認ください。
| 修飾子 | 戻り値型 | メソッド | 概要 |
|---|---|---|---|
char | charAt(int index) |
指定されたインデックス位置にある文字を返します。 | |
boolean | equals(Object anObject) |
この文字列と指定されたオブジェクトを比較します。 | |
byte[] | getBytes() |
String をプラットフォームのデフォルトの文字エンコーディングに従ってバイトに変換し、結果を新しいバイト配列に格納します。 | |
void | getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) |
この文字列から、コピー先の文字配列に文字をコピーします。 | |
int | indexOf(int ch) |
この文字列内で、指定された文字が最初に出現する位置のインデックスを返します。 | |
int | indexOf(String str) |
この文字列内で、指定された部分文字列が最初に出現する位置のインデックスを返します。 | |
int | length() |
この文字列の長さを返します。 | |
String | replace(char oldChar, char newChar) |
この文字列内にあるすべての oldChar を newChar に置換した結果生成される、新しい文字列を返します。 | |
String | substring(int beginIndex, int endIndex) |
この文字列の部分文字列である新しい文字列を返します。 | |
char[] | toCharArray() |
この文字列を新しい文字配列に変換します。 | |
String | toLowerCase() |
Locale.getDefault によって返されるデフォルトロケールの規則を使って、この String 内のすべての文字を小文字に変換します。 | |
String | toString() |
このオブジェクト (すでに文字列である) 自身が返されます。 | |
String | toUpperCase() |
Locale.getDefault によって返されるデフォルトロケールの規則を使って、この String 内のすべての文字を大文字に変換します。 | |
String | trim() |
この文字列の両端から空白を除去します。 |
他にも、静的メソッド (static) として、基本データ型を引数にとる valueOf() メソッドなどがあります。このメソッドは引数の基本データ型の変数やリテラルを文字列型に変換します。
文字列比較のメソッド equals() の使い方を覚えておくと良いでしょう。文字列比較は、比較演算子 == ではなく、オブジェクト比較のメソッド equals() を使います(文字列比較)。
コマンドラインから文字列を引数に受け取って、いくつかのメソッドを試してみます。
class TestString{
public static void main(String args[]){
//文字列検索
char ch=args[0].charAt(2);
int i=args[0].indexOf('は');
int i2=args[0].indexOf("である");
System.out.println(" 2: "+ch);
System.out.println(" は: "+i);
System.out.println("である: "+i2);
//置換
String st=args[0].replace('は', 'が');
System.out.println(st);
//部分文字列
String sub=args[0].substring(3,6);
System.out.println("3-6: "+sub);
}
}
インデックスは0から始まり、半角英数字も日本語も一文字は一文字として数えられます。例えば、部分文字列を取得するメソッド、 substring(3,6) では、「四文字目から7文字目まで」を指定したことになります。但し、このとき3文字目は部分文字列に含まれ、7文字目は含まれません。
C:\Java>javac TestString.java
C:\Java>java TestString 我輩は猫である。
2: は
は: 2
である: 4
我輩が猫である。
3-6: 猫であ
次は英数文字列で試してみます。マクベスの冒頭の一節をコマンドライン引数に取ることを想定しています: "Fair is foul, and foul is fair"
TestString2.java:
class TestString2{
public static void main(String args[]){
System.out.println("args[0]: "+args[0]);
//文字列の比較
int j=0;
for(int i=0;i<=args.length-1;i++){
if("is".equals(args[i])){
j++;
}
}
System.out.println("is: "+j);
//文字列の連結
String str=args[0];
for(int i=1;i<=args.length-1;i++){
str=str.concat(" ");
str=str.concat(args[i]);
}
System.out.println("str: "+str);
System.out.println("length: "+str.length());
//大文字と小文字への変換
String strU, strL;
strL=str.toLowerCase();
strU=str.toUpperCase();
System.out.println("Lower Case: "+strL);
System.out.println("Upper Case: "+strU);
//単語検索
int i1, i2;
i1=strL.indexOf("fair");
i2=strL.lastIndexOf("fair");
System.out.println("first fair: "+i1);
System.out.println("last fair: "+i2);
}
}
equals() メソッドは文字列の比較です。concat() メソッドは引数の文字列をオブジェクト末尾に追加します。length() メソッドは文字列の長さを取得します。関係ありませんが、 args.length は配列 args[] の要素の個数を返します。toLowerCase() と toUpperCase() はオブジェクトを小文字と大文字に変換します。indexOf() メソッドは引数の文字列が最初に現れる位置のインデックスを返しますが、 lastIndexOf() メソッドは最後に現れる位置のインデックスを返します。C:\Java>javac TestString2.java C:\Java>java TestString2 Fair is foul, and foul is fair args[0]: Fair is: 2 str: Fair is foul, and foul is fair length: 30 Lower Case: fair is foul, and foul is fair Upper Case: FAIR IS FOUL, AND FOUL IS FAIR first fair: 0 last fair: 26
一般に、メソッド equals() が true を返す等価なオブジェクトを、演算子 == が true を返す一意なオブジェクトで置き換えることを、カノニカライゼーション (Canonicalization) と呼びます。オブジェクトの種類ごとに生成されるインスタンスの個数を一つに制限するので、インスタンス化の回数を減らし、データの一意化を促します。
String 型オブジェクトの生成は、JVMの実行時コンスタント・プールで低レベルに実装されているものの一つです。
次のコードでは、同じ文字列オブジェクトが参照されます。
String str1 = "Hello"; String str2 = "Hello"; // str1 == str2 is true
次のコードは別の文字列オブジェクトを生成して参照します。
String str1 = "Hello";
String str2 = new String("Hello"); // str1 == str2 is false
str1 == str2 は、前者の例では true を返しますが、後者の例では false を返します。== は変数に代入されている値を比較するものなので、別のオブジェクトへの参照を保持していれば、false が返るのです。文字列オブジェクトに保持されている文字列が等価であることを評価する場合は equals() を使います。次のコードは、いずれの場合でも true を返します。
boolean bln = str1.equals(str2); // bln is true
ソース・コードの中で文字列リテラルが明記されると、String クラスによって private に管理されているテーブル内に、等価なオブジェクトが存在するのならば、プール内のそオブジェクトの参照が返されます。存在しなければ、新たにオブジェクトがプール内に追加され、その参照が返されます。
例外として、生成されるスレッドが異なったり、明示的に new する場合は、新規のオブジェクトが割り振られることになります。
別々に割り当たられたオブジェクトから、カノニカル表現を返すために、メソッド String#intern() が用意されています。intern() が返す文字列のカノニカル表現は、管理テーブル内で一意のオブジェクトであり、他の管理テーブル内のカノニカル表現と一致するかどうかは、そのとき参照している管理テーブルが同一のものであるかどうかに依存します。比較的新しい JVM の場合は、一つの管理テーブルを参照するので、常に同一のオブジェクトへの参照に置き換えられます。
次のコードは、メソッド intern() の動作を確認するものです。
class CanonicalStringDemo {
public static void main(String[] args) {
String str1 = args[0];
String str2 = "こんにちは";
String str3 = new String("こんにちは");
// 非カノニカル表現
System.out.println("Not Canonical representation:");
System.out.println("\t" + (str1 == str2));
System.out.println("\t" + (str1 == str3));
// カノニカル表現
System.out.println("Canonical representation:");
System.out.println("\t" + (str1.intern() == str2.intern()));
System.out.println("\t" + (str1.intern() == str3.intern()));
}
}
実行結果は次のようになります。
>javac CanonicalStringDemo.java
>java CanonicalStringDemo "こんにちは"
Not Canonical representation:
false
false
Canonical representation:
true
true
言語仕様で文字列でのカノニカル表現をサポートすることは、生成がそれほど重い処理ではないので、パフォーマンスに対する寄与よりも、一意性の確保のメリットが大きいと言えます。一般のオブジェクトをカノニカル化 (Canonicalization) することも、パフォーマンスの観点で有効であることがあります。そのために、デザインパターンの Flyweight パターン(後述)を使うことができます。
生成処理が重い場合、JVM の外側のネイティブ資源にアクセスする場合は、オブジェクトを事前に生成しておいて、プールされたオブジェクトを再利用するようにしましょう。
文字列を扱うときは、文字化けに関する障害がよく報告されています。まず、Java では UNICODE を使っていることを再確認し、UNICODEでの文字コードがどのようになっているかを確認してくだいさい。
StringDump.java:
class StringDump {
public static void main(String[] args) {
String str = "問題の発生している文字列";
System.out.println(str);
char[] buf = str.toCharArray();
for (int i = 0; i < buf.length; i++) {
System.out.print(Integer.toString(buf[i], 16) + " ");
}
}
}
C:\java>javac StringDump.java C:\java>java StringDump 問題の発生している文字列 554f 984c 306e 767a 751f 3057 3066 3044 308b 6587 5b57 5217 C:\java>
UNICODE は下位7ビットが ASCII (Latin-1) と互換性を保つように設計されているので、空白類文字、アルファベット、数字は ASCII と同じです。特に、改行、空白などの空白類文字については、システム依存性があるので、認識しておくと良いでしょう。
| Unicode | ASCII | EBCDIC | ||
|---|---|---|---|---|
| CR | 000D | 0D | 0D | 0D |
| LF | 000A | 0A | 25 | 15 |
| CRLF | 000D,000A | 0D,0A | 0D,25 | 0D,15 |
| NEL | 0085 | 85 | 15 | 25 |
| VT | 000B | 0B | 0B | 0B |
| FF | 000C | 0C | 0C | 0C |
| LS | 2028 | n/a | n/a | n/a |
| PS | 2029 | n/a | n/a | n/a |
EBCDIC は、IBM 互換メインフレーム機(ホスト系と呼ばれる)で使われている文字符号化方法で、エンタープライズでは標準的なものです。