なぜStringオブジェクトを変更しません

転載します。https://www.cnblogs.com/leskang/p/6110631.html

まず、不変オブジェクトは何ですか?

        我々はすべて知っているように、中ジャワでは、Stringクラスのオブジェクトは不変です。だから、最終的に不変オブジェクトは何ですか?考えることができる:それが作成された後、オブジェクトが、その状態を変更できない場合、オブジェクトは不変です。状態は、変更することができない基本的なデータ型を含む、メンバ変数のオブジェクトで変更することができないことを意味する値を変更することができない、可変基準タイプを指すことができない他のオブジェクト(最終変更)、変更しないことを指しているオブジェクト参照のタイプの状態。

 

第二に、オブジェクトとオブジェクト参照の区別

Stringオブジェクトは不変であるためのJava初心者のために、常に疑いがあります。以下のコードを見てください:

文字列s = "ABCABC"。
System.out.println( "S =" + S)。
S = "123456"; 
System.out.println( "S =" + S)。

 印刷結果:S = ABCABC

S = 123456は、

最初のStringオブジェクトSを作成し、sの値が「ABCABC」で、その後、Sをさせることは「123456」であるしましょう 印刷された結果から分かるように、Sの値を変更しません。だから、Stringオブジェクトは不変でどのように言いましたか?実際には、ここで誤解があります:ちょうどStringオブジェクトの参照、じゃないオブジェクトそのものオブジェクトは、より多くのメンバ変数メモリのメモリ領域であり、より大きなこのメモリ領域は、スペースを占領しました。参照は、オブジェクトのみのアドレスを、それが指すオブジェクト内に格納されている4バイトのデータは、このアドレスを介してアクセスすることができます。このコードが実行された後、再心に新しいオブジェクト「123456」と、基準点を作成し、それは、特定のオブジェクトを指すだけ参照、S =「123456」よオブジェクトは、まだ元のオブジェクト「ABCABC」ヒープの存在には、それが変更されていません。メモリ構造は、以下に示すように:

 ヒント: JavaとC ++との間の一つの違いは、直接操作オブジェクト自体はJavaで不可能であることで、ポイントによって参照されるすべてのオブジェクトは、オブジェクト自体にアクセスするには、この参照を渡す必要があり、取得メンバ変数の値などは、メンバオブジェクトを変更します変数、オブジェクトのメソッドを呼び出します。そして、C ++、オブジェクトやポインタ三つの内の基準があり、三つのことは、オブジェクトにアクセスすることができます。実際には、JavaやC ++におけるポインタ参照は、そのアドレス値のオブジェクトがメモリに格納され、概念的に類似しているが、Javaでは、など失われたいくつかの柔軟性、引用 Javaで参照するようにすることはできません加算および減算はC ++ポインタのように行われます。 

第三に、なぜStringオブジェクトは不変ですか?

最初のStringクラスのメンバ変数が何であるかを見て、文字列の不変性を理解するために。JDK1.6では、String型のメンバ変数は、次のとおりです。
コードをコピー
パブリック最終クラスStringは
    java.io.Serializableの、同等の<string>を実装し、CharSequence引数{ 
    / **値は、文字の保存に使用されています。* / 
    プライベート最終char値[] 
 
    / **オフセットが使用されるストレージの最初の指標です。* / 
    民間最終int型のオフセット。
 
    / **カウントは、文字列の文字数です。* / 
    民間最終int型のカウント。
 
    / **キャッシュ文字列* /のハッシュコード
    プライベートint型のハッシュ。//デフォルト</文字列> 0に
コードをコピー
JDK1.7では、Stringクラスは、主に、実行のsubstringメソッドの動作を変更するために、いくつかの変更を加え、これは、この記事のテーマとは関係ありません。残りの二つにStringクラスのJDK1.7メインメンバ変数:
コードをコピー
パブリック最終クラスStringは
    java.io.Serializableの、同等の<string>を実装し、CharSequence引数{ 
    / **値は、文字の保存に使用されています。* / 
    プライベート最終char値[]。
 
    / **キャッシュ文字列* /のハッシュコード
    プライベートint型のハッシュ。//デフォルト</文字列> 0に
コードをコピー
実際には、上記のコードから分かるように、Javaで文字列クラスのアレイのパッケージ。JDK6では、値はオフセット、String配列のパッケージであるString配列でこの値の開始位置で、カウントが占有文字列の文字の数です。JDK7では、すべての文字の値であり、唯一の変数の値は、このStringオブジェクトに属します。この変更は、この議論に影響を与えません。また、ハッシュメンバ変数があるStringオブジェクトのキャッシュされたハッシュ値であり、これは本明細書で説明するメンバ変数に何も持っていません。Javaでは、配列はオブジェクト(JavaはI、アレイの前に物品の特性を指すことができる)です。だから、値が参照のみで、それは本当の配列オブジェクトを指します。実際には、文字列S =「ABCABC」の実装は、このコードの後、実際のメモリレイアウトは次のようになります。 \

値、オフセットとカウントこれら三つの変数がプライベートであり、setValueのを提供していない、setOffsetおよびこれらの値を変更するために、他のパブリックメソッドをsetCountは、外部のString Stringクラスで編集することはできません。それは、一度初期化を変更することはできません、とStringクラスの3人のメンバーの外からアクセスすることができません。また、値は、オフセット及びこれら三つの変数は、以下の3つの値が初期化され、変更することはできません一度それは、Stringクラス内で、最終的なものである数えます。Stringオブジェクトがアップ不変であるとみなすことができます。 
文字列では、明らかにいくつかの方法があるので、彼らが得ることができる変更された値を呼び出します。これらの方法は、サブストリング、交換してください、でReplaceAll、toLowerCaseメソッドなどが挙げられます。たとえば、次のコード:
列A = "ABCABC"。
System.out.println( "A =" + A)。
= a.replace( 'A'、 'A')。
System.out.println( "A =" + A)。

 印刷結果:A = ABCABC

= ABCABCは、

次いで、一見変更の値が、それはまだ同じエラーです。再び、への参照は単に、(「A」、「A a.replaceを呼び出すときに、真の文字列オブジェクトではない 」)、 内部メソッドは、新しい文字列オブジェクトを作成し、この新しい
オブジェクトが再割り当てされていますへの参照。文字列でメソッドを置き換えるソースコードの問題を記述することができます
 
读者可以自己查看其他方法,都是在方法内部重新创建新的String对象,并且返回这个新的对象,原来的对象是不会被改变的。这也是为什么像replace, substring,toLowerCase等方法都存在返回值的原因。也是为什么像下面这样调用不会改变对象的值: 
String ss = "123456";
System.out.println("ss = " + ss);
ss.replace('1', '0');
System.out.println("ss = " + ss);

 打印结果: ss = 123456

ss = 123456

四、String对象真的不可变吗?

从上文可知String的成员变量value是private final 的,也就是初始化之后不可改变。那么在这几个成员中, value比较特殊,因为他是一个引用变量,而不是真正的对象。value是final修饰的,也就是说final不能再指向其他数组对象,那么我能改变value指向的数组吗? 比如将数组中的某个位置上的字符变为下划线“_”。 至少在我们自己写的普通代码中不能够做到,因为我们根本不能够访问到这个value引用(private修饰),更不能通过这个引用去修改数组。 那么用什么方式可以访问私有成员呢? 没错,用反射, 可以反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。下面是实例代码: 

コードをコピー
public static void testReflection() throws Exception {
     
    //创建字符串"Hello World", 并赋给引用s
    String s = "Hello World"; 
     
    System.out.println("s = " + s); //Hello World
     
    //获取String类中的value字段
    Field valueFieldOfString = String.class.getDeclaredField("value");
     
    //改变value属性的访问权限
    valueFieldOfString.setAccessible(true);
     
    //获取s对象上的value属性的值
    char[] value = (char[]) valueFieldOfString.get(s);
     
    //改变value所引用的数组中的第5个字符
    value[5] = '_';
     
    System.out.println("s = " + s);  //Hello_World
}
コードをコピー

打印结果为: s = Hello World

S = Hello_World

       このプロセスの文字列オブジェクトと、常に引用されたS、その後前後に反射し、文字列オブジェクトが反射することによって、すなわち、変更された、いわゆる「不変」オブジェクトを変更することができます。しかし、一般的に、我々はいたしません。これは、インスタンスにも問題がある可能性が反映:オブジェクトは、彼の状態の組み合わせの他のオブジェクトが変更できない場合、そのオブジェクトはおそらく不変ではありません。例えば、この文は、民間最終の対象となっていますが、オブジェクトの内側の車輪の状態を変更することができますが、ホイールオブジェクトホイールを組み合わせた車のオブジェクトは、その後、我々は良い車不変オブジェクトを保証することはできません。
 

おすすめ

転載: www.cnblogs.com/parrot/p/11512162.html