java中のString類常量池の詳細
32455 ワード
文章の回転:http://www.cnblogs.com/brokencode/archive/2011/08/24/2151450.html詳しく書いてありますが、私に役に立つので転載して共有します。
一つのブログから見た6つの問題を先に見てみてください。全部できたら、この部分の知識はよく身につけられます。出力結果はコードコメントの後にあります。test 1:
一つのブログから見た6つの問題を先に見てみてください。全部できたら、この部分の知識はよく身につけられます。出力結果はコードコメントの後にあります。test 1:
package StringTest;
public class test1 {
/**
* @param args
*/
public static void main(String[] args){
String a = "a1";
String b = "a"+ 1;
System.out.println(a==b);
}//true
}
test 2:package StringTest;
public class test2 {
/**
* @param args
*/
public static void main(String[] args){
String a = "ab";
String bb = "b";
String b = "a"+ bb; //
System.out.println(a==b);
}//false
}
test 3:package StringTest;
public class test3 {
/**
* @param args
*/
public static void main(String[] args){
String a = "ab";
final String bb = "b";
String b = "a"+ bb; //bb final , b
System.out.println(a==b);
}//true
}
test 4:package StringTest;
public class test4 {
/**
* @param args
*/
public static void main(String[] args){
String a = "ab";
final String bb = getBB();
String b = "a"+ bb;//bb , final , , bb
System.out.println(a==b);
}//false
private static String getBB(){ return "b"; }
}
test 5:package StringTest;
public class test5 {
/**
* @param args
*/
private static String a = "ab";
public static void main(String[] args){
String s1 = "a";
String s2 = "b";
String s = s1 + s2;//+
System.out.println(s == a);
System.out.println(s.intern() == a);//intern
}//flase true
}
test 6:package StringTest;
public class test6 {
/**
* @param args
*/
private static String a = new String("ab");
public static void main(String[] args){
String s1 = "a";
String s2 = "b";
String s = s1 + s2;
System.out.println(s == a);
System.out.println(s.intern() == a);
System.out.println(s.intern() == a.intern());
}//flase false true
}
------------------------------------------------------------------------String常量池詳細:1.Stringはprvate final char value[]を使用して文字列の記憶を実現し、つまりStringオブジェクトが作成された後、このオブジェクトに格納されている文字列の内容を再修正することができない。そのため、Stringタイプは可変ではないということです。Stringクラスには特殊な作成方法があります。「」ダブルクォーテーションを使って作成します。例えばnew String(i am)は実際に2つのStringオブジェクトを作成しました。一つは「i am」が「」ダブルクォーテーションで作成しました。もう一つはnewで作成しました。ただし、彼らが作成した時期は違っています。一つはコンパイル期間です。一つは運行期間です。javaはSteringタイプに+操作子を載せています。直接+を使って2つの文字列を接続できます。Steringクラスのintern()メソッドを呼び出して、String Poolにオブジェクトを動的に追加することができます。例1 String s 1="sss 111"//この文は同じString s 2=「sss 111」です。System.out.println(s 1=s 2)//結果はtrue例2 String s 1=new String(「sss 111」)である。String s 2="sss 111"System.out.println(s 1=s 2)//結果はfalse例3 String s 1=new String(「sss 111」)である。s 1=s 1.intern()String s 2="sss 111"System.out.println(s 1=s 2)//結果はtrue例4 String s 1=new String(111);String s 2="sss 111"String s 3=「sss」+「111」String s 4="sss"+s 1;System.out.println(s 2=s 3)//true System.out.println(s 2==s 4)//false System.out.println(s 2==s 4.intern()//true結果上で分析した結果、まとめて次のようになりました。1.単独で「」引用符を使って作成した文字列は全部定数です。コンパイル期間はもうString Poolに格納されることが確定しました。2,new String(「」)を使って作成したオブジェクトはheapに格納され、運転期間が新しく作成されました。3,定数のみを含む文字列接続子を使用して「aa」+「aa」のように作成したものも定数であり、コンパイル期間はString Poolに記憶されていると確定できます。4,変数を含む文字列コネクタを使用して「aa」+s 1のように作成したオブジェクトは実行期間で作成され、heapに格納されます。いくつかのよく受ける面接試験問題があります。String s 1=new String("s 1")String s 2=new String(s 1);いくつかのStringオブジェクトが作成されましたか?3つのコンパイル期間Compstant Poolの中で1つを作成し、運営期間heapの中で2つを作成します。String s 2=s 1;s 2="s 2"s 1が指す対象の文字列は何ですか?答え:“s 1”。(いつまでもStringが可変でないことを忘れないでください。s 2=s 2;実際s 2の方向は変わっています。Stringを変えてはいけないからです。)--------------------------------------------------------------------------------------------------------------------------------------------------------------------使用可能: String str=new String(abc); String str="abc" 二つの形で作成します。一つ目はnew()を使ってオブジェクトを新規作成します。これはヒープに保存されます。呼び出しごとに新しいオブジェクトが作成されます。 第二は、まずスタックの中にString類のオブジェクト参照変数strを作成し、記号引用によって文字列定数プールに「abc」があるかどうかを探します。ない場合は、「abc」を文字列常量池に保存し、strを「abc」に指します。 クラスの中の数値が等しいかどうかを比較する時は、equals()の方法を使います。二つの包装類の引用が同一の対象を指すかどうかをテストする時、===を使って、次の例で上の理論を説明します。 String str 1="abc" String str 2="abc" System.out.println(str 1=str 2)true str 1とstr 2は同じ対象を指すことが分かります。 String str 1=new String(abc); String str 2=new String("abc") System.out.println(str 1=str 2)false new方式で異なるオブジェクトを生成します。一回ごとに一つを生成します。 したがって、2番目の方法で複数の「abc」文字列を作成します。メモリには一つのオブジェクトしか存在しません。このような表記はメモリの節約に有利です。同時に、プログラムの実行速度をある程度上げることができます。JVMは自動的にスタックのデータの状況に応じて新しいオブジェクトを作成する必要があるかどうかを決定します。String str=new String(abc);のコードは、すべて新しいオブジェクトを作成します。文字列の値が等しいかどうかは関係なく、新しいオブジェクトを作成する必要があります。 一方、String str=「abc」を使っています。のフォーマットを定義してクラスを定義する時、いつも当然に思って、String種類の対象のstrを創建しました。落とし穴が心配!オブジェクトは作成されていないかもしれません。前に作成したオブジェクトだけを指すことができます。new()の方法によって毎回新しいオブジェクトを作成することが保証されます。 String類のimmutable性質のため、String変数が常にその値を変換する必要がある場合は、Stering Buffer類を使用してプログラム効率を向上させることを考慮すべきである。 1.まずStringは8つの基本データタイプではなく、Stringは1つのオブジェクトです。 オブジェクトのデフォルト値はnullなので、Stringのデフォルト値もnullです。しかし、他のオブジェクトにはないいくつかの特性があります。 2.new String()とnew String(""")は全部新しい空の文字列を説明しています。空の文字列はnullではありません。 3.String str="kvill";String str=new String("kvill")の違いを見た例1: String s 0="kvill" String s 1="kvill" String s 2="kv"+"ill" System.out.println(s 0==s 1) System.out.println(s 0==s 2) 結果: true true まず、結果はJavaによって確認されます。文字列の定数はコピーだけです。 例のs 0とs 1の「kv ill」は文字列の定数であるため、コンパイル期間ではs 0==s 1はtrueであり、「kv」と「ill」も文字列の定数であり、一つの文字列が複数の文字列の定数で接続されている場合は、それ自体も文字列の定数であることは間違いないので、s 2も同様にコンパイル期間で1つの文字列の定数に解析されています。s 2も定数池の中の「kvill」の一つの引用であるため、s 0==s 1==s 2;new String()で作成した文字列は定数ではなく、コンパイル期間では確定できないので、new String()で作成した文字列は定数プールに入れず、自分のアドレスの空きがある。 例2を見ます String s 0="kvill" String s 1=new String("kvill") String s 2=「kv」+new String(「ill」) System.out.println(s 0==s 1) System.out.println(s 0==s 2) System.out.println(s 1=s 2) 結果: false false false 例2 s 0は定数セルにおける「kvill」のアプリケーションであり、s 1はコンパイル期間では確定できないため、実行時に作成された新しいオブジェクト「kvill」の参照であり、s 2は後半部分new String("ill")があるためコンパイル期間では確定できないので、新たに作成されたオブジェクト"kvill"のアプリケーションでもある。これがわかったら、なぜこの結果が出たのかが分かります。 4.String.intern(): もう一つ紹介します。classファイルに存在する常量池は、運行期間中にJVMに積載され、拡充できます。Stringのintern()方法は、常量池を拡張する方法の一つである。Stringインスタンスstrがintern()方法を呼び出すとき、Javaはユニバーサル・プールに同じUnicodeの文字列定数があるかどうかを調べ、ある場合はその参照を返します。ない場合は、ユニバーサル・プールにUniodeがstrに等しい文字列を追加して参照を返します。例3を見れば分かります 例3: String s 0="kvill" String s 1=new String("kvill") String s 2=new String("kvill") System.out.println(s 0==s 1) System.out.println(*****) s 1.intern() s 2=s 2.intern()//常量池の「kvill」の引用をs 2に賦課する。 System.out.println(s 0==s 1) System.out.println(s 0==s 1.intern(); System.out.println(s 0==s 2) 結果: false ********** false//s 1.intern()を実行しましたが、その戻り値はs 1に割り当てられていませんでした。 true//説明s 1.intern()は常量池の「kvill」の引用を返します。 true 最後にもう一つの間違いを理解しました。「String.internを使う」と言われました。方法としては、一つのStringクラスを一つのグローバルStringテーブルに保存してもよく、同じ値を持つUnicode文字列がすでにこの表にある場合、この方法は、テーブル内の既存文字列のアドレスを返します。同じ値の文字列がない場合、自分のアドレスを表に登録します。彼が言ったこのグローバルのStringテーブルを定数プールとして理解したら、彼の最後の言葉は「表に同じ値の文字列がない場合、自分の住所を表に登録する」というのは間違いです。 例4: String s 1=new String("kvill") String s 2=s 1.intern() System.out.println(s 1==s 1.intern(); System.out.println(s 1+「」+s 2) System.out.println(s 2==s 1.intern(); 結果: false kvill kvill true このクラスには「Kvill」の常数がありませんので、常量池の中に最初は「kvill」はありません。s 1.internを呼んでから常量池に「kvill」の常量を追加しました。元々は常量池にない「kvill」が存在します。「自分の住所を常量池に登録します」というわけではありません。 s 1==s 1.intern()はfalseのためにもとの"kvill"を説明して依然として存在します。s 2は現在常量池の中の「kvill」の住所ですので、s 2==s 1.intern()がtrueです。