目次
インタビューの質問1:String、StringBuffer、StringBuilderの違い
インタビューの質問2:String a = new String( "kexin");いくつかのオブジェクトが生成されます
-
ソースコード
-
初期化部分
Serializable、Comparable、CharSequenceの3つのインターフェイス、つまりserialization、compareTo、CharSequenceを実装し、3番目は主にいくつかの一般的なメソッド、length、charAt、subSequenceなどを継承しました。subSequenceはsubStringに似ており、効果は同じで、戻り値のタイプは異なります。 、基本的には使用しないでください
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// 存储空间,字符数组形式
private final char value[];
// hashcode缓存,String常用于比较,每次计算太过麻烦,这样节省时间
private int hash;
// 序列号(尚未理解)
private static final long serialVersionUID = -6849794470754667710L;
-
方法
コピー
//内部方法,将String的字符数组value整个复制到dst字符数组中,在dst数组的dstBegin位置开始拷贝
void getChars(char dst[], int dstBegin) {
System.arraycopy(value, 0, dst, dstBegin, value.length);
}
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
比較
public boolean equals(Object anObject) {
//如果对象引用地址相同
if (this == anObject) {
return true;
}
//这个是?存疑
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
//字符数组长度相同
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
//每个字符相同
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
比較(追加されるままにする)
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
切る
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
//生成新对象
return ((beginIndex == 0) && (endIndex == value.length)) ? this
: new String(value, beginIndex, subLen);
}
交換(追加されるままにする)
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
//替换
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
上記のソースコードから、Stringオブジェクトへの変更は元のオブジェクトに影響を与えず、関連する変更操作によって新しいオブジェクトが生成されることもわかります。
-
JVM
-
作成する
ここにFlyweightと呼ばれる概念があります。私の理解は、定数プール内の既存の要素を共有し、メモリコストを削減
することです
。作成する方法は2つあります
。1。Stringa = "a";作成するとき、jvmが最初に判断します「a」が定数プールにすでに存在するかどうか、存在する場合は直接引用され、存在しない場合は文字列がインスタンス化されて引用されます。
最後に、文字列定数プール内の「a」の参照アドレスがスタックに格納されます
2.String b = new String( "A");
最初に新しい文字列オブジェクトがヒープに配置され、ヒープが定数プール参照アドレス "A"に格納され、定数プール参照アドレスが上記のように取得されます。
最後に、スタックが格納されます。ヒープ内の文字列オブジェクトの参照アドレス。
したがって、定数プールに2つの同一の文字列が存在することはできません。これは、文字列の不変性の現れです。
-
一定のプール
定数プールには、静的定数プールと実行時定数プールの2種類があります。
コンパイル時間-静的定数プール-実行前にすでに配置されています-定数-たとえば、String a = "1"; String a1 = "1" + "2";
run時間-実行時定数プール-実行時にのみ生成-変数-たとえば、String b = new String( "1"); String b1 = "1" + b;
注:
(1):JVMペアString str = "abc"オブジェクトはコンパイル時に定数プールに配置され、文字列str3 = str1 + str2は実行時にのみ認識できます。新しいオブジェクトも実行時に実行されます。
(2):文字通りの「+」スプライシングはコンパイル中に実行され、スプライシングされた文字列は文字列プールに格納されます;文字列参照の「+」スプライシング操作は実際には実行時に実行され、新しく作成された文字列積み重ねて保管してください。
静的定数プールは、クラスファイル内に、クラスのメモリスペースの大部分を占める文字列、クラス、メソッド情報などを格納します。
ランタイム定数プール。jvmがクラスのロード操作を完了すると、クラス定数プールをメソッド領域に配置します。 、一般的に、定数プールはメソッド領域の定数プールを指します
インターン
internメソッドはネイティブメソッドです。internメソッドは、現在の文字列が文字列定数プールから存在するかどうかを照会します。存在する場合は、現在の文字列を直接返します。存在しない場合は、現在の文字列を定数プールに入れてから、戻る。
-
+そして追加
Stringの+は、StringBufferオブジェクトを作成し、append()メソッドを呼び出し、toString()を呼び出し、最後に
String A = "11" + "22" + Str1 + "33"などのコンパイルプロセスでStringBufferオブジェクトを破棄すると解釈されます。 ;このプロセスは、コンパイル時にString A = new StringBuffer( "1122")。append(Str).append( "33")。toString()として解釈されます。
したがって、実際には、ループで+操作を実行する必要がある場合StringBufferの作成と破棄で多くのパフォーマンスを消費します。ここでは、Stringの代わりにStringBufferを手動で新規作成するのが最善です。
-
インタビューの質問1:String、StringBuffer、StringBuilderの違い
インタビューでよくある質問の1つ
(1)不変である可能性があり、Stringはfinalで変更され、静的に不変であり、他の2つは長さが可変であるため、+の代わりにappendが使用されることがあります。Appendは元のベースで拡張され、+は新しく作成されたString
(2)スレッドは安全ですか?Stringは不変(定数として理解できます)であり、複数のスレッドが同時にリソースを変更することによって引き起こされる競合がないため、安全であると見なされます
。StringBufferは、同期を使用して同期ロックを追加します。スレッドは安全です。 StringBufferが存在しないため、スレッドセーフではありません
(3)実行効率、比較的言えば、StringBuilder> StringBuffer> String、特定の状況の特定の分析
(4)各機能、少量のデータStringは柔軟で変更可能、大量のデータマルチスレッドStringBufferスレッドの安全性、大量のデータシングルスレッドのStringBuilderの方が高速です
-
インタビューの質問2:String a = new String( "kexin");いくつかのオブジェクトが生成されます
クラスの読み込み段階では、オブジェクト「kexin」が生成されてヒープに配置されます。
実行段階では、オブジェクトa
が生成されるため、2つあります。
個人的な理解の上のドープされた部分、私を議論して修正することを歓迎します
参考文献
https://blog.csdn.net/yulungggg/article/details/81039655
https://blog.csdn.net/qq_34490018/article/details/82110578
https://www.cnblogs.com/xiaoxi/p/6036701.html