String定数プールの問題


String定数プールに関するいくつかの小さな例:
    [1]
String a = "a1";    
String b = "a" + 1;    
System.out.println((a == b)); //result = true   
String a = "atrue";    
String b = "a" + "true";    
System.out.println((a == b)); //result = true   
String a = "a3.4";    
String b = "a" + 3.4;    
System.out.println((a == b)); //result = true 

 
解析:JVMは文字列定数の「+」号接続に対して、プログラムコンパイル期間、JVMは定数文字列の「+」接続を接続後の値に最適化し、「a」+1にとって、コンパイラによって最適化された後、classの中ですでにa 1である.コンパイル期間中に文字列定数の値が決定されるので、上記のプログラムの最終的な結果はtrueです.
 
    [2]
String a = "ab";    
String bb = "b";    
String b = "a" + bb;    
System.out.println((a == b)); //result = false 

 
解析:JVM文字列参照では、文字列の「+」接続に文字列参照が存在するため、参照の値はプログラムコンパイル期間では確定できません.すなわち、「a」+bbはコンパイラによって最適化されず、プログラム実行期間でのみ動的に割り当てられ、接続後の新しいアドレスをbに割り当てます.だから上のプログラムの結果もfalseです.
 
    [3]
String a = "ab";    
final String bb = "b";    
String b = "a" + bb;    
System.out.println((a == b)); //result = true 

 
解析:bb文字列にfinal修飾が加えられているのは[2]と唯一異なり、final修飾の変数については、コンパイル時に定数値として解析されたローカルコピーが自分の定数プールに格納されたり、バイトコードストリームに埋め込まれたりします.したがって、この場合の「a」+bbと「a」+「b」の効果は同じである.従って、上記のプログラムの結果はtrueである.
 
    [4]
String a = "ab";    
final String bb = getBB();    
String b = "a" + bb;    
System.out.println((a == b)); //result = false    
private static String getBB() {   
return "b";    
} 

 
解析:JVMは文字列参照bbに対して、その値はコンパイル中に確定できず、プログラム実行中にメソッドを呼び出した後、メソッドの戻り値と「a」を動的に接続し、アドレスをbに割り当てるため、上記のプログラムの結果はfalseである.
 
以上の4つの例から分かるように、
  
String  s  =  "a" + "b" + "c"; 
//   
String s = "abc";  

 
String  a  =  "a";   
String  b  =  "b";   
String  c  =  "c";   
String  s  =   a  +  b  +  c; 

//    

StringBuffer temp = new StringBuffer();      
temp.append(a).append(b).append(c);      
String s = temp.toString(); 

   
以上の解析結果から,Stringが接続演算子(+)を用いて効率が低下した原因を解析していることが容易に推定できる.
public class Test {   
public static void main(String args[]) {   
String s = null;   
for(int i = 0; i < 100; i++) {   
s += "a";   
}   
}   
} 

 
+を作るたびにStringBuilderオブジェクトが生成され、append後に捨てられます.次のループが到着すると、StringBuilderオブジェクトが再生成され、append文字列が終了するまでループされます.StringBuilderオブジェクトを直接使用してappendを行うと、N-1でオブジェクトを作成および破棄する時間を節約できます.したがって、ループで文字列接続を行うアプリケーションでは、通常、StringBufferオブジェクトまたはStringBuliderオブジェクトを使用してappend操作を行います.
 
-----------------------------------------------------------
 
Stringオブジェクトのinternメソッドの理解と分析:
public class Test4 {   
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);//false   
System.out.println(s.intern() == a);//true     
}   
} 

 
ここでJAVAに使用すると、定数プールの問題になります.s 1+s 2操作では、実際にはスタック内に新しいオブジェクトが再作成され、sはこの新しいオブジェクトのスタック空間の内容を保存するので、sとaの値は等しくありません.一方、s.intern()メソッドを呼び出すと、aの値が定数プールに格納されているため、s.internとaの値が等しいため、sの定数プール内のアドレス値を返すことができる.
まとめ
スタックには、元のデータ型のローカル変数データとオブジェクトの参照(String,配列.オブジェクトなど)を格納しますが、オブジェクトの内容は格納されません.
スタックにはnewキーを使用して作成するオブジェクトが格納される.
文字列は特殊な包装クラスであり、その参照はスタックに格納され、オブジェクトの内容は作成方法によって異なる(定数プールとスタック)必要がある.コンパイル期間が作成する文字列定数プールに格納され、実行時に作成されるものもある.newキーワードを使用して、スタックに格納します.