134

Stringがクラスであるのに、なぜJavaで+演算子を使用してStringを追加できるのですか?コードでは、String.javaこの演算子の実装は見つかりませんでした。この概念はオブジェクト指向に違反していますか?

4

7 に答える 7

162

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

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

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

5.1.11。文字列変換

文字列変換により、任意の型を文字列型に変換できます。

プリミティブ型Tの値xは、適切なクラスインスタンス作成式(§15.9)の引数として指定するかのように、最初に参照値に変換されます。

  • Tがブール値の場合は、new Boolean(x)を使用します。
  • Tがcharの場合は、new Character(x)を使用します。
  • Tがbyte、short、またはintの場合は、new Integer(x)を使用します。
  • Tが長い場合は、new Long(x)を使用します。
  • Tがfloatの場合は、new Float(x)を使用します。
  • Tがdoubleの場合は、new Double(x)を使用します。

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

ここで、考慮する必要があるのは参照値のみです。

  • 参照が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オブジェクトの数を減らすことができます。

プリミティブ型の場合、実装は、プリミティブ型から文字列に直接変換することにより、ラッパーオブジェクトの作成を最適化することもできます。

最適化されたバージョンは、実際には最初に完全にラップされた文字列変換を実行しません。

これは、プリミティブの変換がなくても、コンパイラーが使用する最適化されたバージョンの良い例です。ここでは、コンパイラーがバックグラウンドで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イディオムの省略形です。

于 2012-07-10T07:28:38.460 に答える
27

演算子のオペランドをチェックするJavaコンパイラ機能です+。そして、オペランドに基づいてバイトコードを生成します。

  • 文字列の場合、文字列を連結するコードを生成します
  • 数値の場合、数値を追加するコードを生成します。

これはJava仕様が言っていることです:

演算子+および-は加法演算子と呼ばれます。AdditiveExpression:MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression-MultiplicativeExpression

加法演算子は同じ優先順位を持ち、構文的に左結合性です(左から右にグループ化されます)。演算子のいずれかのオペランドの型が、の場合、操作は文字列連結です。+String

それ以外の場合、演算子の各オペランドの+型は、プリミティブ数値型に変換可能な型(§5.1.8)である必要があります。そうでない場合、コンパイル時エラーが発生します。

いずれの場合も、二項演算子の各オペランドの-型は、プリミティブ数値型(§5.1.8)に変換可能な型である必要があります。そうでない場合、コンパイル時エラーが発生します。

于 2012-07-10T07:24:34.807 に答える
14

Stringクラスは+演算子をどのようにオーバーライドしますか?

そうではありません。コンパイラがそれを行います。厳密に言えば、コンパイラは文字列オペランドの+演算子をオーバーロードします。

于 2012-07-10T09:53:45.100 に答える
6

まず第一に、(+)はオーバーライドされずにオーバーロードされます

Java言語は、Java文字列オブジェクトに対してオーバーロードされた文字列連結演算子(+)の特別なサポートを提供します。

  1. 左側のオペランドが文字列の場合、連結として機能します。

  2. 左側のオペランドが整数の場合、加算演算子として機能します

于 2012-07-10T07:26:10.690 に答える
4

Java言語は、文字列連結演算子(+)および他のオブジェクトの文字列への変換を特別にサポートします。StringBuilder文字列の連結は、 (またはStringBuffer)クラスとそのappendメソッドを介して実装されます。

于 2012-07-10T07:25:39.577 に答える
4

+誰もがすでに書いているように、適用されたときの演算子の意味はString言語によって定義されます。これは十分に説得力があるとは思えないので、次のことを考慮してください。

ints、float、およびdoubleはすべて異なるバイナリ表現を持っているため、2つのintを追加することは、2つのfloatを追加することとは、ビット操作の点で異なる操作です。フロートの場合、仮数と指数を別々に処理する必要があります。

したがって、原則として、「追加」は「追加」されるオブジェクトの性質に依存します。Javaは、文字列だけでなく、intとfloat(longs、doubles、...)に対しても定義します。

于 2012-07-10T14:39:40.960 に答える
3

演算子は通常、コンパイル時にに+置き換えられます。その問題の詳細については、StringBuilderこの回答を確認してください。

于 2012-07-10T07:25:03.183 に答える