Як клас String перекриває оператор +?


129

Чому в Java ви можете додати Strings з оператором +, коли String - клас? У String.javaкоді я не знайшов жодної реалізації цього оператора. Чи порушує це поняття орієнтація на об'єкт?


21
+Оператор Plus ( ) - це мовна особливість Java.
adatapost

18
Це все магія компілятора. Ви не можете робити перевантаження оператора в Java.
Лев

18
Думаю, дивним є те, що String реалізований як бібліотека поза мовою (в java.lang.String), але вона має конкретну підтримку всередині мови. Це не так.
13врек


@ 13ren ви помиляєтесь. Рядок є окремим класом java.lang, який означає мову - лавагуа java !!! Всі класи в цьому пакеті особливі. Зверніть увагу, що вам не потрібно імпортувати java.lang, щоб їх використовувати.

Відповіді:


156

Давайте розглянемо наступні прості вирази на Java

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

Компілятор перетворюється "x = "+x;у StringBuilderвнутрішньо і використовує .append(int)для "додавання" цілого числа до рядка.

5.1.11. Перетворення рядків

Будь-який тип може бути перетворений у тип String шляхом перетворення рядків.

Значення x примітивного типу T спочатку перетворюється на опорне значення так, ніби надаючи його як аргумент відповідному виразу створення екземпляра класу (§15.9):

  • Якщо T булеве, використовуйте новий булевий (x).
  • Якщо T - char, використовуйте новий символ (x).
  • Якщо T - байт, короткий або int, використовуйте новий Integer (x).
  • Якщо T довгий, то використовуйте новий Long (x).
  • Якщо T є плаваючою, використовуйте новий Float (x).
  • Якщо T подвійний, використовуйте новий Double (x).

Це опорне значення потім перетворюється у тип String шляхом перетворення рядків.

Тепер потрібно враховувати лише базові значення:

  • Якщо посилання є нульовим, воно перетворюється на рядок "null" (чотири ASCII символи n, u, l, l).
  • В іншому випадку перетворення виконується як би викликом методу toString посиланого об'єкта без аргументів; але якщо результат виклику методу toString є нульовим, то натомість використовується рядок "null".

Метод toString визначається початковим класом Object (§4.3.2). Багато класів переважають його, зокрема булева, персонажа, ціла, довга, плаваюча, подвійна та струнна.

Детальніше про контекст перетворення рядків див. У § 5.4.

15.18.1.

Оптимізація об'єднання рядків: реалізація може вибрати, щоб виконати перетворення та конкатенацію за один крок, щоб уникнути створення, а потім відкидання проміжного об'єкта String. Для підвищення продуктивності повторного об'єднання рядків 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();
}

Це створює - подивіться, як два стилі конкатенації призводять до того самого байтового коду:

 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 = новий StringBuilder (cip) .append (ciop) .toString (); такі самі. і моє запитання полягає в тому, що ця операція порушує орієнтацію на об'єкт?
Пуя

7
Ні, це не так. Перевантаження оператора (як у C ++ та деяких мовах) має деякі недоліки, і дизайнери Java вважають, що це дещо заплутане поняття, і його опустили з Java. Для мене об'єктно-орієнтована мова повинна мати основні поняття успадкування, поліморфізму та інкапсуляції, які має Java.
Лев

2
так, але я думаю, що цей оператор перевантажив для класу String
Pooya

3
Так, перевантаження оператора використовується в Java для об'єднання типу String, однак ви не можете визначити власного оператора (як у C ++, C # та деяких інших мовах).
Лев

5
@Pooya: насправді "int / int" vs. "int / float" вже є перевантаження оператора, тому навіть C має це. Однак у C (і Java) немає операторів, що визначаються перевантаженням: єдине, що визначає різні способи використання оператора (як на C, так і на Java) - це визначення мови (а відмінність реалізована в компілятор). C ++ відрізняється тим, що дозволяє визначені користувачем перевантаження операторів (які часто називають просто "перевантаження оператора").
Йоахім Зауер

27

Ця функція компілятора Java перевіряє операнди +оператора. І на основі операндів генерує код байта:

  • Для String він генерує код до концентрованих рядків
  • Для чисел він генерує код для додавання чисел.

Ось що говорить специфікація Java :

Оператори + і -називаються операторами добавок. AdditiveExpression: MultiplicativeExpression AdditiveExpression + MultiplicativeExpression AdditiveExpression - MultiplicativeExpression

Оператори добавок мають однаковий пріоритет і є синтаксично лівим асоціативом (вони групуються зліва направо). Якщо тип будь-якого операнда +оператора є String, то операція є конкатенацією рядків.

В іншому випадку тип кожного операнда +оператора повинен бути типом, який може бути конвертованим (§5.1.8) у примітивний числовий тип, або виникає помилка часу компіляції.

У кожному випадку тип кожного операнда двійкового -оператора повинен бути типом, який може бути конвертованим (§5.1.8) у примітивний числовий тип, або виникає помилка часу компіляції.


7
Цитата з специфікації взагалі не стосується цього питання.
Ернест Фрідман-Хілл

Це витяг "Якщо типом будь-якого операнда оператора + є String, тоді операція є конкатенацією рядків. В іншому випадку тип кожного операнда оператора + повинен бути типом, який може бути конвертованим (§5.1.8 ) до примітивного числового типу або виникає помилка часу компіляції ". Скажіть, будь ласка, чому це не актуально.
Рамеш ПВК

7
У ньому нічого не сказано про те, як це реалізовано, в чому питання. Я думаю, що плакат вже розуміє, що функція існує.
Ернест Фрідман-Хілл

14

Як клас String замінює + оператор?

Це не так. Укладач це робить. Строго кажучи, компілятор перевантажує оператор + для String операндів.


6

Насамперед (+) перевантажений, а не перекритий

Мова Java забезпечує спеціальну підтримку оператора конкатенації рядків (+), який був перевантажений для об'єктів Java Strings.

  1. Якщо операнд лівої сторони - String, він працює як конкатенація.

  2. Якщо операнд лівої сторони є Integer, він працює як оператор додавання


3
(2) Якщо лівий операнд є цілим числом, він автоматично відключається до коду, intі тоді застосовуються звичайні правила Java.
Маркіз Лорн

2
Два правила, наведені нижче цитати, помилкові: я вважаю, що вони повинні бути: два примітиви (або нерозбірні класи) = додавання; принаймні один рядок = конкатенація
mwfearnley

4

Мова Java забезпечує спеціальну підтримку оператора конкатенації рядків (+) та для перетворення інших об'єктів у рядки. Зв'язування рядків реалізується через StringBuilder(або StringBuffer) клас та його appendметод.


4

Значення +оператора при зверненні Stringвизначається мовою, як уже написали всі. Оскільки вам здається це не досить переконливим, врахуйте це:

Інти, поплавці та подвійні мають різні бінарні уявлення, і тому додавання двох int - це інша операція, з точки зору маніпулювання бітом, ніж додавання двох поплавків: Для ints ви можете додавати побіт, переносячи біт і перевіряючи на переповнення; для поплавців ви повинні мати справу з мантіями та експонентами окремо.

Отже, в принципі "додавання" залежить від природи об'єктів, що додаються. Java визначає його як для Strings, так і для ints та floats (longs, double, ...)


3

+Оператор зазвичай замінений StringBuilderпід час компіляції. Перевірте цю відповідь, щоб отримати детальнішу інформацію з цього питання.


Якщо це так, чи є причина, чому StringBuilder взагалі існує для публічного використання? Чи є випадки, коли +оператора не замінюють StringBuilder?
Джемре

2
Питання, яке ви хочете задати, "чому оператор + взагалі існує для загальнодоступного використання?", Оскільки тут гидота. Щодо вашого іншого питання, я його точно не знаю, але я б здогадався, що такого випадку немає.
Скорпіон

Компілятор може використовувати замість concat (), якщо є лише два елементи. Також компілятор не може замінити concat () StringBuilder (або використовує декілька StringBuilders і додає їх разом), коли програміст будує рядок у довгий вкладений / циклічний код - використання одного, явного StringBuilder буде кращим для продуктивності.
користувач158037
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.