JavaにおけるStringの不変性の理解方法
20961 ワード
文書ディレクトリ問題 Stringクラスの宣言 finalキーワードの役割 Stringの不変性 Stringの不変性はどれらの利益があります Stringは本当に絶対に可変ではありませんか に質問
どうしてみんなStringが可変ではないと言っているのですか?
Stringクラスの宣言
JAvaのString宣言は
finalキーワードの役割
final修飾変数を使用すると、コンパイラは修正を許可しません.例を挙げます.
上のvalue=value 2の行は間違って報告され、final修飾の変数に値を割り当てることはできません.final修飾を使用する変数の参照は変更できません.ただし、参照を変更できないことは、中の値を変更できないことを意味しません.次のように説明します.
印刷結果:
Stringの不変性
ここで簡単な例でStringの不変性をテストします
結果出力:
test 1は1回の操作の後も元のままになっていますが、test 2は変更されています.実はStringという可変性は私たちがよく何気なく使用しています.最も一般的な使用シーンはHashSetの値です.次の例を示します.
StringBuilderストレージを使用すると、何気ないビジネス操作でSetの値が変更され、不要なトラブルを引き起こす可能性があります.
Stringの不変性にはどのようなメリットがありますか?は、文字列が可変である場合にのみ、文字列プールが実現される.文字列プールの実装は、異なる文字列変数がプール内の同じ文字列を指すため、実行時に多くのheap空間を節約することができる.文字列が可変である場合、String interningは実現できません(String interningとは、異なる文字列に対して1つだけ保存することを意味します.つまり、同じ文字列は複数保存されません).これにより、変数がその値を変更すると、他の値を指す変数の値も一緒に変更されます. 文字列が可変である場合、深刻なセキュリティ問題が発生します.たとえば、データベースのユーザー名、パスワードは文字列で入力されてデータベースの接続を取得したり、socketプログラミングではホスト名やポートが文字列で入力されたりします.文字列は可変であるため、その値は変更できません.そうしないと、ハッカーたちは穴に潜り、文字列が指すオブジェクトの値を変更し、セキュリティ・ホールをもたらすことができます. 文字列は可変であるため、マルチスレッドは安全であり、同じ文字列インスタンスを複数のスレッドで共有することができる.これにより、スレッドセキュリティの問題で同期を使用する必要がなくなります.文字列自体がスレッドで安全です. 文字列は可変であるため、hashcodeは作成時にキャッシュされ、再計算する必要はありません.これにより、文字列はMapのキーとして適切になり、文字列の処理速度は他のキーオブジェクトよりも速くなります.これは、HashMapのキーが文字列を使用することが多いことです.
Stringって本当に絶対変わらないのかな
次の例を示します.
印刷結果は次のとおりです.
反射によってStringの値を変更できることを説明します.
参考:
https://www.zhihu.com/question/20618891
どうしてみんなStringが可変ではないと言っているのですか?
Stringクラスの宣言
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
...
...
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
}
JAvaのString宣言は
final
タイプで、Stringが継承できないことを示しています.Stringのコアストレージ値のvalueはchar[]配列でもfinal
修飾を使用しており、value
は変更されていないことを示しています.finalキーワードの役割
final修飾変数を使用すると、コンパイラは修正を許可しません.例を挙げます.
@Test
public void finalTest(){
final char[] value = {'a','a','a'};
char[] value2 = {'b','b','b'};
value = value2;//cannot assign a value to final variable
}
上のvalue=value 2の行は間違って報告され、final修飾の変数に値を割り当てることはできません.final修飾を使用する変数の参照は変更できません.ただし、参照を変更できないことは、中の値を変更できないことを意味しません.次のように説明します.
@Test
public void finalTest() {
final char[] value = {'a', 'a', 'a'};
System.out.println(value);
value[2] = 'b';// 2 b
System.out.println(value);
}
印刷結果:
aaa
aab
Stringの不変性
ここで簡単な例でStringの不変性をテストします
@Test
public void testString() {
String test1 = new String("aaa");
StringBuilder test2 = new StringBuilder("aaa");
System.out.println(" test1:" + test1);
System.out.println(" test2:" + test2.toString());
//
String afaddStr = addString(test1);
StringBuilder afaddStrBuild = addStringBuilder(test2);
System.out.println(" test1:" + test1);
System.out.println(" test2:" + test2.toString());
}
/**
* bbb
*
* @param str
*/
public String addString(String str) {
str = str + "bbb";
return str;
}
/**
* bbb
*
* @param str
*/
public StringBuilder addStringBuilder(StringBuilder str) {
str.append("bbb");
return str;
}
結果出力:
test1:aaa
test2:aaa
test1:aaa
test2:aaabbb
test 1は1回の操作の後も元のままになっていますが、test 2は変更されています.実はStringという可変性は私たちがよく何気なく使用しています.最も一般的な使用シーンはHashSetの値です.次の例を示します.
@Test
public void testString2() {
String key1 = new String("aaa");
StringBuilder key2 = new StringBuilder("aaa");
HashSet set = new HashSet<>();
set.add(key1);
set.add(key2);
System.out.println(" set :"+ set.toString());
/************** ******************/
String afaddStr = addString(key1);
StringBuilder afaddStrBuild = addStringBuilder(key2);
/************** ****end***********/
System.out.println(" set :"+ set.toString());
}
/**
* bbb
*
* @param str
*/
public String addString(String str) {
str = str + "bbb";
return str;
}
/**
* bbb
*
* @param str
*/
public StringBuilder addStringBuilder(StringBuilder str) {
str.append("bbb");
return str;
}
StringBuilderストレージを使用すると、何気ないビジネス操作でSetの値が変更され、不要なトラブルを引き起こす可能性があります.
Stringの不変性にはどのようなメリットがありますか?
Stringって本当に絶対変わらないのかな
次の例を示します.
@Test
public void testString3() throws IllegalAccessException, NoSuchFieldException {
String strObj = new String("aaa");
System.out.println(" :" + strObj);
System.out.println(" hash :" + strObj.hashCode());
Field field = strObj.getClass().getDeclaredField("value");
field.setAccessible(true);
char[] value = (char[]) field.get(strObj);
value[2] = 'b';
System.out.println(" :" + strObj);
System.out.println(" hash :" + strObj.hashCode());
}
印刷結果は次のとおりです.
:aaa
hash :96321
:aab
hash :96321
反射によってStringの値を変更できることを説明します.
参考:
https://www.zhihu.com/question/20618891