String 클래스는 + 연산자를 어떻게 재정의합니까?


129

Java에서 String이 클래스 일 때 + 연산자를 사용하여 String을 추가 할 수있는 이유는 무엇입니까? 에서 String.java코드 나는이 연산자에 대한 구현을 찾을 수 없습니다. 이 개념이 객체 방향을 위반합니까?


21
플러스 ( +) 연산자는 Java의 언어 기능입니다.
adatapost

18
모두 컴파일러의 마법입니다. Java에서는 연산자 오버로드를 수행 할 수 없습니다.
Lion

18
이상한 점은 String이 언어 외부 의 라이브러리 (java.lang.String)로 구현되었지만 언어 내부 에서 특정 지원 한다는 것입니다. 그건 옳지 않아
13ren


@ 13ren 당신은 틀 렸습니다. 문자열은 언어를 나타내는 java.lang 클래스와 별개입니다-java lanaguage !!! 이 패키지의 모든 수업은 특별합니다. java.lang을 가져 와서 사용할 필요는 없습니다.

답변:


156

자바에서 다음과 같은 간단한 표현을 봅시다

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"(4 개의 ASCII 문자 n, u, l, l)로 변환됩니다.
  • 그렇지 않으면, 인수없이 참조 된 객체의 toString 메소드를 호출하는 것처럼 변환이 수행됩니다. 그러나 toString 메소드를 호출 한 결과가 널이면 문자열 "null"이 대신 사용됩니다.

toString 메소드는 기본 클래스 Object (§4.3.2)에 의해 정의됩니다. Boolean, Character, Integer, Long, Float, Double 및 String과 같은 많은 클래스가이를 재정의합니다.

문자열 변환 컨텍스트에 대한 자세한 내용은 §5.4를 참조하십시오.

15.18.1.

문자열 연결 최적화 : 구현은 중간 문자열 개체를 생성 한 다음 폐기하지 않기 위해 한 단계로 변환 및 연결을 수행하도록 선택할 수 있습니다. 반복 문자열 연결 성능을 향상시키기 위해 Java 컴파일러는 StringBuffer 클래스 또는 유사한 기술을 사용하여 표현식 평가에 의해 생성 된 중간 문자열 객체의 수를 줄일 수 있습니다.

프리미티브 유형의 경우 구현시 기본 유형에서 문자열로 직접 변환하여 랩퍼 오브젝트 작성을 최적화 할 수도 있습니다.

최적화 된 버전은 실제로 전체 랩핑 된 문자열 변환을 먼저 수행하지 않습니다.

다음은 컴파일러가 백그라운드에서 StringBuilder로 변경되는 것을 볼 수있는 프리미티브를 변환하지 않아도 컴파일러가 사용하는 최적화 된 버전을 잘 보여줍니다.

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


이 자바 코드 :

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

이것을 생성합니다-두 연결 스타일이 어떻게 같은 바이트 코드로 이어지는 지보십시오.

 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관용구 의 약칭입니다 .


3
고마워요, 나는 jvm 바이트 코드에 익숙하지 않지만 String plus = cip + ciop; 그리고 String build = new StringBuilder (cip) .append (ciop) .toString (); 동일합니다. 내 질문은이 작업이 객체 방향을 위반한다는 것입니다.
Pooya

7
아닙니다. C ++ 및 일부 언어에서와 같이 연산자 오버로드에는 몇 가지 단점이 있으며 Java 디자이너는 다소 혼란스러운 개념이라고 생각하여 Java에서 생략했습니다. 나에게 객체 지향 언어는 Java가 가지고있는 상속, 다형성 및 캡슐화의 주요 개념을 가져야한다.
Lion

2
예,하지만이 연산자는 String 클래스에 오버로드 된 것 같습니다
Pooya

3
예, 문자열 유형의 연결을 위해 연산자 오버로드 가 Java에서 사용되지만 C ++, C # 및 기타 언어와 같이 고유 한 연산자를 정의 할 수 없습니다.
Lion

5
@Pooya : 실제로 "int / int"와 "int / float"는 이미 연산자 오버로드이므로 C조차도 그렇게합니다. 그러나 C (및 Java)에 없는 것은 사용자 정의 연산자 오버로드입니다 .C와 Java에서 연산자를 사용할 수있는 다른 방법을 정의하는 유일한 것은 언어 정의입니다. 컴파일러). C ++은 사용자 정의 연산자 오버로딩을 허용한다는 점에서 다릅니다 (이것은 종종 "오퍼레이터 오버로딩"이라고도 함).
Joachim Sauer

27

+연산자 의 피연산자를 확인하는 Java 컴파일러 기능입니다 . 그리고 피연산자를 기반으로 바이트 코드를 생성합니다.

  • String의 경우 문자열을 연결하는 코드를 생성합니다.
  • 숫자의 경우 숫자를 추가하는 코드를 생성합니다.

이것은 Java 스펙이 말하는 것입니다 .

연산자 +를 -추가 연산자라고합니다. AdditiveExpression : MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression-MultiplicativeExpression

덧셈 연산자는 우선 순위가 동일하며 구문 적으로 왼쪽 연관되어 있습니다 (왼쪽에서 오른쪽으로 그룹화). (A)의 피연산자를 입력 할 경우 +작업자는 String다음 동작은 문자열 연결된다.

그렇지 않으면 연산자의 각 피연산자 +유형이 기본 숫자 유형으로 변환 가능한 (§5.1.8) 유형이어야합니다. 그렇지 않으면 컴파일 타임 오류가 발생합니다.

모든 경우에 이진 -연산자 의 각 피연산자 유형은 기본 숫자 유형으로 변환 가능한 유형 (§5.1.8)이어야합니다. 그렇지 않으면 컴파일 타임 오류가 발생합니다.


7
사양의 인용문은이 질문과 전혀 관련이 없습니다.
어니스트 프리드먼

"+ 연산자의 피연산자 중 하나의 유형이 문자열 인 경우 작업은 문자열 연결입니다. 그렇지 않으면 + 연산자의 각 피연산자 유형은 변환 가능한 유형이어야합니다 (§5.1.8). )를 원시 숫자 유형으로 변환하거나 컴파일 타임 오류가 발생했습니다. " 왜 관련이 없는지 알려주세요.
Ramesh PVK

7
구현 방법에 대해서는 아무 것도 말하지 않습니다. 이것이 문제입니다. 나는 포스터가 이미 그 기능이 존재한다는 것을 이해하고 있다고 생각합니다.
어니스트 프리드먼

14

String 클래스가 + 연산자를 어떻게 재정의합니까?

그렇지 않습니다. 컴파일러가 수행합니다. 엄밀히 말하면 컴파일러 는 String 피연산자에 + 연산자를 오버로드 합니다.


6

우선 (+)가 오버로드되지 않은 오버로드

Java 언어는 Java 문자열 오브젝트에 과부하 된 문자열 연결 연산자 (+)에 대한 특별한 지원을 제공합니다.

  1. 왼쪽 피연산자가 문자열이면 연결로 작동합니다.

  2. 왼쪽 피연산자가 정수이면 덧셈 연산자로 작동합니다


3
(2) 왼쪽 피연산자가 정수인 경우 자동 언 박싱 된 int다음 일반 Java 규칙이 적용됩니다.
Lorne의 후작

2
인용문 아래에 주어진 두 가지 규칙은 잘못되었습니다. 나는 그들이해야한다고 생각합니다. 적어도 하나의 문자열 = 연결
mwfearnley

4

Java 언어는 문자열 연결 연산자 (+) 및 다른 객체를 문자열로 변환하는 데 특별한 지원을 제공합니다. 문자열 연결은 클래스 StringBuilder또는 메소드를 통해 구현됩니다 .StringBufferappend


4

+적용될 때 연산자 의 의미는 String모든 사람들이 이미 작성한 것처럼 언어에 의해 정의됩니다. 충분히 설득력이없는 것으로 보이므로 다음 사항을 고려하십시오.

Int, float 및 double은 모두 서로 다른 이진 표현을 가지므로 두 개의 int를 추가하는 것은 비트 조작 측면에서 두 개의 float를 추가하는 것과 다른 작업입니다. 플로트의 경우 가수와 지수를 별도로 처리해야합니다.

따라서 원칙적으로 "추가"는 "추가 된"개체의 특성에 따라 다릅니다. Java는 문자열과 int 및 float (long, double, ...)에 대해 그것을 정의합니다.


3

+운영자는 일반적으로 대체됩니다 StringBuilder컴파일시. 해당 문제에 대한 자세한 내용은 이 답변 을 확인하십시오 .


이 경우 StringBuilder가 공개적으로 사용되는 이유가 있습니까? +운영자가 교체하지 않은 경우 가 StringBuilder있습니까?
Cemre

2
당신이 묻고 자하는 질문은 "+ 연산자가 왜 공개적으로 사용 되는가?"입니다. 당신의 다른 질문에 관해서는, 나는 그것을 정확히 알지 못하지만 그러한 경우는 없을 것이라고 생각합니다.
Scorpio

두 개의 요소 만있는 경우 컴파일러에서 대신 concat ()을 사용할 수 있습니다. 또한 프로그래머가 긴 중첩 / 루프 코드로 문자열을 작성할 때 컴파일러가 concat ()을 StringBuilder로 대체하지 못하거나 여러 StringBuilder를 사용하여 함께 추가합니다. 단일 명시 적 StringBuilder를 사용하면 성능이 향상됩니다.
user158037
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.