1. ホーム
  2. java

[解決済み] Stringクラスは、どのように+演算子をオーバーライドするのですか?

2022-06-23 08:38:50

質問

JavaではStringはクラスなのに、なぜ+演算子でStringを足すことができるのですか? また String.java のコードでは、この演算子の実装は見つかりませんでした。このコンセプトはオブジェクト指向に違反しているのでしょうか?

どのように解決するのですか?

Javaで次のような簡単な式を見てみましょう。

int x=15;
String temp="x = "+x;

コンパイラは "x = "+x;StringBuilder に変換し、内部で .append(int) を使用して、整数を文字列に追加しています。

5.1.11. 文字列の変換

文字列変換により、任意の型をString型に変換することができます。

プリミティブ型Tの値xは、まず参照値に変換されます。 適切なクラスインスタンス生成式の引数として与えることによって を生成する式(15.9節)の引数として与えることにより、あたかも参照値に変換されます。

  • Tがbooleanの場合、new Boolean(x)を使用します。
  • Tがcharの場合、new Character(x)を使用します。
  • Tがbyte、short、intの場合、new Integer(x)を使用します。
  • Tがlongの場合、new Long(x)を使用します。
  • Tがfloatの場合、new Float(x)を使用します。
  • Tがdoubleであれば、new Double(x)を使用します。

この参照値は、文字列変換によってString型に変換されます。 変換されます。

あとは参照値のみを考慮すればよい。

  • 参照がNULLの場合、文字列 "null"(4つのASCII文字 n, u, l, l )に変換されます。
  • それ以外の場合は、あたかも参照されたオブジェクトのtoStringメソッドを引数なしで呼び出したかのように変換が行われます。 toStringメソッドの呼び出しの結果がNULLであった場合は 文字列 "null"が代わりに使用されます。

toStringメソッドは、原始クラスであるObject (§4.3.2). 多くのクラスがこれをオーバーライドしており、特にBoolean、Character, Integer、Long、Float、Double、そしてStringです。

文字列変換コンテキストの詳細については、§5.4を参照してください。

15.18.1.

文字列連結の最適化: 実装では、変換と連結を一度に行うことで、中間値の生成と破棄を避けることができます。 を1つのステップで行い、中間的な 文字列オブジェクトの作成と破棄を避けるためです。繰り返される文字列連結のパフォーマンスを向上させるために Javaコンパイラは、繰り返される文字列連結のパフォーマンスを向上させるために、StringBufferクラスまたは同様の技術を使用して を使用して、式の評価によって生成される中間 String オブジェクトの数を減らすことができます。 の数を減らすことができます。

プリミティブな型については、実装はまた プリミティブ型から文字列に直接変換することで、ラッパーオブジェクトの生成を最適化することもできます。 型から文字列に直接変換することで、ラッパーオブジェクトの生成を排除することもできます。

最適化されたバージョンでは、実際には最初にフルラップされたStringの変換は行いません。

これは、プリミティブの変換を行わないとはいえ、コンパイラによって使用される最適化バージョンの良い例で、コンパイラがバックグラウンドでStringBuilderに変更するのがわかります。

http://caprazzi.net/posts/java-bytecode-string-concatenation-and-stringbuilder/


このjavaのコードです。

public static void main(String[] args) {
    String cip = "cip";
    String ciop = "ciop";
    String plus = cip + ciop;
    String build = new StringBuilder(cip).append(ciop).toString();
}

2つの連結スタイルがどのように全く同じバイトコードになるかを見てみましょう。

 L0
    LINENUMBER 23 L0
    LDC "cip"
    ASTORE 1
   L1
    LINENUMBER 24 L1
    LDC "ciop"
    ASTORE 2

   // cip + ciop

   L2
    LINENUMBER 25 L2

    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

    ASTORE 3

    // new StringBuilder(cip).append(ciop).toString()

   L3
    LINENUMBER 26 L3

    NEW java/lang/StringBuilder
    DUP
    ALOAD 1
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;

    ASTORE 4
   L4
    LINENUMBER 27 L4
    RETURN

上の例と、与えられた例のソースコードに基づくバイトコードがどのように生成されるかを見てみると、コンパイラが内部で次のような文を変換していることに気づくことができます。

cip+ciop; 

new StringBuilder(cip).append(ciop).toString();

つまり、演算子 + は事実上、文字列連結のための省略記法で、より冗長な StringBuilder イディオムです。