Java 基本チュートリアルカラムの紹介インサイト文字列文字列
Java6 および Java6 における実装原則
以前のバージョンでは、String オブジェクトは char 配列をカプセル化したオブジェクトであり、主に char 配列、オフセット offset、文字数、ハッシュ値 hash の 4 つのメンバー変数を持っていました。 Java7 から Java8 では、String クラスにはオフセット変数とカウント変数がなくなりました。この利点は、String オブジェクトが使用するメモリがわずかに少ないことです。 Java9 バージョンから、char[] フィールドは byte[] フィールドに変更され、エンコード形式の識別子である新しい属性コーダーが維持されています。 char 文字は 16 ビットと 2 バイトを占めます。この場合、文字を 1 バイト エンコード (1 バイトを占める文字) で保存することは非常に無駄です。メモリ領域を節約するために、JDK1.9 の String クラスは 8 ビット、1 バイトのバイト配列を使用して文字列を格納します。 新しい属性コーダーの機能は、文字列の長さを計算するとき、または、indexOf() 関数を使用するときに、このフィールドに基づいて文字列の長さを計算する方法を決定する必要があることです。 coder 属性にはデフォルトで 0 と 1 の 2 つの値があります。0 は Latin-1 (シングルバイトエンコーディング) を表し、1 は UTF-16 を表します。 String が文字列に Latin-1 のみが含まれていると判断した場合、コーダー属性値は 0 になり、それ以外の場合は 1 になります。Immutable
String クラスのコードを見ると、String クラスが Final キーワードによって変更されていることがわかります。クラスを継承することはできません。また、String クラスの変数 char 配列も Final によって変更されるため、String オブジェクトを変更することはできません。 String オブジェクトの不変性には、主に次の利点があります。 まず、String オブジェクトのセキュリティを確保します。 String オブジェクトが変更可能であると仮定すると、String オブジェクトは悪意を持って変更される可能性があります。 2 番目に、ハッシュ属性値が頻繁に変更されないようにし、一意性を確保することで、HashMap に似たコンテナが対応するキーと値のキャッシュ関数を実装できるようにします。 3 番目に、文字列定数プールを実装できます。 Java では、通常、文字列オブジェクトを作成する方法が 2 つあります。 1 つ目は、String str = "abc" などの文字列定数を使用して作成する方法です。 。
String str = new String("abc") など、new の形式で文字列変数を作成することです。
String str = new String("abc") このようにして、まずクラス ファイルをコンパイルするときに、定数文字列「abc」が定数構造体に入れられます。ロード時に、定数プールに「abc」が作成されます。次に、new を呼び出すときに、JVM コマンドが String のコンストラクターを呼び出し、String オブジェクト内の char 配列が
内の「abc」文字を参照します。定数プール String char 配列 、ヒープ メモリに String オブジェクトを作成します。最後に、str は String オブジェクトを参照します。String オブジェクトの参照は、定数プール内の "abc" 文字列の参照とは異なります。 。
String str = new String("abc")、変数 str は String オブジェクトのストレージ アドレスを指します。つまり、str はオブジェクトではなく、単なるオブジェクトです。オブジェクト参照。
文字列連結
定数加算
String str = "ab" + "cd" + "ef";
0 ldc #2 <abcdef>2 astore_13 return
String str= "abcdef";
変数の追加
String a = "ab";String b = "cd";String c = a + b;
0 ldc #2 <ab> 2 astore_1 3 ldc #3 <cd> 5 astore_2 6 new #4 <java/lang/StringBuilder> 9 dup10 invokespecial #5 <java/lang/StringBuilder.<init>>13 aload_114 invokevirtual #6 <java/lang/StringBuilder.append>17 aload_218 invokevirtual #6 <java/lang/StringBuilder.append>21 invokevirtual #7 <java/lang/StringBuilder.toString>24 astore_325 return
String c = new StringBuilder().append("ab").append("cd").toString();
String。 intern
String a = new String("abc").intern();String b = new String("abc").intern();System.out.print(a == b);
true
constantでは、オブジェクトはデフォルトで定数プールに配置されます。例: String a = "123"
在字符串变量中,对象是会创建在堆内存中,同时也会在常量池中创建一个字符串对象,String 对象中的 char 数组将会引用常量池中的 char 数组,并返回堆内存对象引用。例如:String b = new String("abc")
如果调用 intern 方法,会去查看字符串常量池中是否有等于该对象的字符串的引用,如果没有,在 JDK1.6 版本中会复制堆中的字符串到常量池中,并返回该字符串引用,堆内存中原有的字符串由于没有引用指向它,将会通过垃圾回收器回收。
在 JDK1.7 版本以后,由于常量池已经合并到了堆中,所以不会再复制具体字符串了,只是会把首次遇到的字符串的引用添加到常量池中;如果有,就返回常量池中的字符串引用。
下面开始分析上面的代码块:
在一开始字符串”abc”会在加载类时,在常量池中创建一个字符串对象。
创建 a 变量时,调用 new Sting() 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回常量池中的字符串引用。
创建 b 变量时,调用 new Sting() 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回常量池中的字符串引用。
而在堆内存中的两个String对象,由于没有引用指向它,将会被垃圾回收。所以 a 和 b 引用的是同一个对象。
如果在运行时,创建字符串对象,将会直接在堆内存中创建,不会在常量池中创建。所以动态创建的字符串对象,调用 intern 方法,在 JDK1.6 版本中会去常量池中创建运行时常量以及返回字符串引用,在 JDK1.7 版本之后,会将堆中的字符串常量的引用放入到常量池中,当其它堆中的字符串对象通过 intern 方法获取字符串对象引用时,则会去常量池中判断是否有相同值的字符串的引用,此时有,则返回该常量池中字符串引用,跟之前的字符串指向同一地址的字符串对象。
以一张图来总结 String 字符串的创建分配内存地址情况:
使用 intern 方法需要注意的一点是,一定要结合实际场景。因为常量池的实现是类似于一个 HashTable 的实现方式,HashTable 存储的数据越大,遍历的时间复杂度就会增加。如果数据过大,会增加整个字符串常量池的负担。
判断字符串是否相等
// 运行环境 JDK1.8String str1 = "abc";String str2 = new String("abc");String str3= str2.intern();System.out.println(str1==str2); // falseSystem.out.println(str2==str3); // falseSystem.out.println(str1==str3); // true
// 运行环境 JDK1.8String s1 = new String("1") + new String("1");s1.intern();String s2 = "11";System.out.println(s1 == s2); // true , 如果不执行1.intern(),则返回false
String s1 = new String("1") + new String("1")
会在堆中组合一个新的字符串对象"11"
,在s1.intern()
之后,由于常量池中没有该字符串的引用,所以常量池中生成一个堆中字符串"11"
的引用,此时String s2 = "11"
返回的是堆字符串"11"
的引用,所以s1==s2
。
在JDK1.7版本以及之后的版本运行以下代码,你会发现结果为true,在JDK1.6版本运行的结果却为false:
String s1 = new String("1") + new String("1");System.out.println( s1.intern()==s1);
StringBuilder与StringBuffer
由于String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的对象。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
以上がインサイトストリング stringの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。