この記事は主に Java String ソースコード分析を紹介し、Sting が不変である理由についての関連情報を紹介します。
ご存知のとおり、Java では String クラスは不変です。では、不変オブジェクトとは正確には何でしょうか? 次のように考えることができます。オブジェクトの作成後にその状態を変更できない場合、そのオブジェクトは不変です。状態は変更できません。つまり、基本データ型の値を含むオブジェクト内のメンバー変数は変更できません。また、参照型が指すオブジェクトの状態も変更できません。変えられる。
オブジェクトとオブジェクト参照を区別する
Java 初心者にとって、String が不変オブジェクトであることについては常に疑問があります。次のコードを見てください:
String 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 オブジェクトは不変だとまだ言えるのでしょうか? 実際、ここには誤解があります。s は String オブジェクトへの単なる参照であり、オブジェクト自体ではありません。オブジェクトはメモリ内のメモリ領域であり、メンバ変数が増えるほど、このメモリ領域が占有する領域も大きくなります。参照は、参照先のオブジェクトのアドレスを格納する 4 バイトのデータであり、このアドレスを通じてオブジェクトにアクセスできます。
つまり、 s は特定のオブジェクトを指す単なる参照であり、このコードが実行された後、新しいオブジェクト "123456" が作成され、参照 s は再び を指します。このハートの元のオブジェクト「ABCabc」はまだメモリ内に存在しており、変更されていません。メモリ構造は次の図に示されています。
Java と C++ の違いの 1 つは、Java ではオブジェクト自体を直接操作することができないことです。すべてのオブジェクトは参照によってポイントされ、オブジェクト自体が参照されます。メンバー変数の値の取得、オブジェクトのメンバー変数の変更、オブジェクトのメソッドの呼び出しなど、この参照を通じてアクセスする必要があります。 C++ には、参照、オブジェクト、ポインターの 3 つがあり、これら 3 つすべてがオブジェクトにアクセスできます。実際、Java の参照と C++ のポインタは概念的に似ています。ただし、Java では、参照は加算や減算のように使用することができません。 C++ のポインターのように実行されます。
なぜ String オブジェクトは不変なのでしょうか?
String の不変性を理解するには、まず String クラスのメンバー変数を見てください。 JDK1.6 では、String のメンバー変数には次のものが含まれます。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
{
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0
JDK1.7 では、String クラスにいくつかの変更が加えられ、主に実行時の部分文字列メソッドの動作が変更されました。これはこれと一致しています。記事 トピックは関係ありません。 JDK1.7 の String クラスの主なメンバー変数は 2 つだけです:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0
上記のコードからわかるように、Java の String クラスは実際には文字配列のカプセル化です。 JDK6 では、value は String によってカプセル化された配列、offset は値配列内の String の開始位置、count は String が占める文字数です。 JDK7 では、値変数は 1 つだけです。つまり、value 内のすべての文字は String オブジェクトに属します。この変更は、この記事の説明には影響しません。 さらに、String オブジェクトのハッシュ値のキャッシュであるハッシュ メンバー変数もありますが、このメンバー変数もこの記事の説明とは無関係です。 Java では、配列もオブジェクトです (以前の記事「Java における配列の特性」を参照してください)。 したがって、value は単なる参照であり、実際の配列オブジェクトを指します。実際、コード String s = "ABCabc"; を実行した後、実際のメモリ レイアウトは次のようになります:
value、offset、count の 3 つの変数はすべてプライベートであり、パブリックには提供されません。これらの値を変更するには setValue、setOffset、setCount などのメソッドが使用されるため、String クラスの外部で String を変更することはできません。つまり、一度初期化すると変更することはできず、これら 3 つのメンバーには String クラスの外部からアクセスすることはできません。さらに、3 つの変数 value、offset、count はすべて最終的な値です。つまり、String クラス内では、これら 3 つの値が初期化されると変更できません。したがって、String オブジェクトは不変であると考えることができます。
つまり、String には明らかにいくつかのメソッドがあり、それらを呼び出すと変更された値を取得できます。これらのメソッドには、substring、replace、replaceAll、toLowerCase などが含まれます。たとえば、次のコード:
String a = "ABCabc"; System.out.println("a = " + a); a = a.replace('A', 'a'); System.out.println("a = " + a);
出力される結果は次のようになります:
a = ABCabc a = aBCabc
那么a的值看似改变了,其实也是同样的误区。再次说明, a只是一个引用, 不是真正的字符串对象,在调用a.replace('A', 'a')时, 方法内部创建了一个新的String对象,并把这个心的对象重新赋给了引用a。String中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的成员变量是private final 的,也就是初始化之后不可改变。那么在这几个成员中, value比较特殊,因为他是一个引用变量,而不是真正的对象。value是final修饰的,也就是说final不能再指向其他数组对象,那么我能改变value指向的数组吗? 比如将数组中的某个位置上的字符变为下划线“_”。 至少在我们自己写的普通代码中不能够做到,因为我们根本不能够访问到这个value引用,更不能通过这个引用去修改数组。
那么用什么方式可以访问私有成员呢? 没错,用反射, 可以反射出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始终引用的同一个String对象,但是再反射前后,这个String对象发生了变化, 也就是说,通过反射是可以修改所谓的“不可变”对象的。但是一般我们不这么做。这个反射的实例还可以说明一个问题:如果一个对象,他组合的其他对象的状态是可以改变的,那么这个对象很可能不是不可变对象。例如一个Car对象,它组合了一个Wheel对象,虽然这个Wheel对象声明成了private final 的,但是这个Wheel对象内部的状态可以改变, 那么就不能很好的保证Car对象不可变。
以上就是Java String源码分析并介绍Sting 为什么不可变的详细介绍的内容,更多相关内容请关注PHP中文网(www.php.cn)!

javaispopularforsoss-platformdesktopapplicationsduetoits "writeonce、runaynay" philosophy.1)itusesbytecodatiTatrunnanyjvm-adipplatform.2)ライブラリリケンディンガンドジャヴァフククレアティック - ルルクリス

Javaでプラットフォーム固有のコードを作成する理由には、特定のオペレーティングシステム機能へのアクセス、特定のハードウェアとの対話、パフォーマンスの最適化が含まれます。 1)JNAまたはJNIを使用して、Windowsレジストリにアクセスします。 2)JNIを介してLinux固有のハードウェアドライバーと対話します。 3)金属を使用して、JNIを介してMacOSのゲームパフォーマンスを最適化します。それにもかかわらず、プラットフォーム固有のコードを書くことは、コードの移植性に影響を与え、複雑さを高め、パフォーマンスのオーバーヘッドとセキュリティのリスクをもたらす可能性があります。

Javaは、クラウドネイティブアプリケーション、マルチプラットフォームの展開、および言語間の相互運用性を通じて、プラットフォームの独立性をさらに強化します。 1)クラウドネイティブアプリケーションは、GraalvmとQuarkusを使用してスタートアップ速度を向上させます。 2)Javaは、埋め込みデバイス、モバイルデバイス、量子コンピューターに拡張されます。 3)Graalvmを通じて、JavaはPythonやJavaScriptなどの言語とシームレスに統合して、言語間の相互運用性を高めます。

Javaの強力なタイプ化されたシステムは、タイプの安全性、統一タイプの変換、多型を通じてプラットフォームの独立性を保証します。 1)タイプの安全性は、コンパイル時間でタイプチェックを実行して、ランタイムエラーを回避します。 2)統一された型変換ルールは、すべてのプラットフォームで一貫しています。 3)多型とインターフェイスメカニズムにより、コードはさまざまなプラットフォームで一貫して動作します。

JNIはJavaのプラットフォームの独立を破壊します。 1)JNIは特定のプラットフォームにローカルライブラリを必要とします。2)ローカルコードをターゲットプラットフォームにコンパイルおよびリンクする必要があります。3)異なるバージョンのオペレーティングシステムまたはJVMは、異なるローカルライブラリバージョンを必要とする場合があります。

新しいテクノロジーは、両方の脅威をもたらし、Javaのプラットフォームの独立性を高めます。 1)Dockerなどのクラウドコンピューティングとコンテナ化テクノロジーは、Javaのプラットフォームの独立性を強化しますが、さまざまなクラウド環境に適応するために最適化する必要があります。 2)WebAssemblyは、Graalvmを介してJavaコードをコンパイルし、プラットフォームの独立性を拡張しますが、パフォーマンスのために他の言語と競合する必要があります。

JVMの実装が異なると、プラットフォームの独立性が得られますが、パフォーマンスはわずかに異なります。 1。OracleHotspotとOpenJDKJVMは、プラットフォームの独立性で同様に機能しますが、OpenJDKは追加の構成が必要になる場合があります。 2。IBMJ9JVMは、特定のオペレーティングシステムで最適化を実行します。 3. Graalvmは複数の言語をサポートし、追加の構成が必要です。 4。AzulzingJVMには、特定のプラットフォーム調整が必要です。

プラットフォームの独立性により、開発コストが削減され、複数のオペレーティングシステムで同じコードセットを実行することで開発時間を短縮します。具体的には、次のように表示されます。1。開発時間を短縮すると、1セットのコードのみが必要です。 2。メンテナンスコストを削減し、テストプロセスを統合します。 3.展開プロセスを簡素化するための迅速な反復とチームコラボレーション。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

SecLists
SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SAP NetWeaver Server Adapter for Eclipse
Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

SublimeText3 Linux 新バージョン
SublimeText3 Linux 最新バージョン

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









