Сколько объектов String будет создано - PullRequest
6 голосов
/ 10 сентября 2011

У меня есть следующий код Java:

public String makinStrings() {
  String s = "Fred";
  s = s + "47";
  s = s.substring(2, 5);
  s = s.toUpperCase();
  return s.toString();
}

Вопрос как-то прост: сколько String объектов будет создано при вызове этого метода?

Вначале я ответил, что создано 5 объектов String, но в ответе из моей книги говорится, что создано только 3 объекта и объяснений не было (это вопрос SCJP).

С моей точки зрения есть 5 объектов: "Fred", "47", "Fred47", "ed4", "ED4".

Я также нашел этот вопрос на симуляционном экзамене SCJP с тем же ответом 3.

Ответы [ 3 ]

15 голосов
/ 10 сентября 2011

«Фред» и «47» будут получены из пула строковых литералов.Как таковые они не будут созданы при вызове метода.Вместо этого они будут помещены туда при загрузке класса (или ранее, если другие классы используют константы с тем же значением).

"Fred47", "ed4" и "ED4" являются объектами 3 Stringэто будет создано при каждом вызове метода.

2 голосов
/ 10 сентября 2011

На основе выходных данных javap похоже, что во время конкатенации создается StringBuilder, а не String. Затем есть три строки, вызываемые для substring (), toUpperCase () и toString ().

Последний вызов не является избыточным, поскольку он преобразует StringBuilder в строку.

>javap -c Test
Compiled from "Test.java"

public java.lang.String makinStrings();
Code:
0:   ldc     #5; //String Fred
2:   astore_1
3:   new     #6; //class java/lang/StringBuilder
6:   dup
7:   invokespecial   #7; //Method java/lang/StringBuilder."<init>":()V
10:  aload_1
11:  invokevirtual   #8; //Method java/lang/StringBuilder.append:   (Ljava/lang/String;)Ljava/lang/StringBuilder;
14:  ldc     #9; //String 47
16:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19:  invokevirtual   #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
22:  astore_1
23:  aload_1
24:  iconst_2
25:  iconst_5
26:  invokevirtual   #11; //Method java/lang/String.substring:(II)Ljava/lang/String;
29:  astore_1
30:  aload_1
31:  invokevirtual   #12; //Method java/lang/String.toUpperCase:()Ljava/lang/String;
34:  astore_1
35:  aload_1
36:  invokevirtual   #13; //Method java/lang/String.toString:()Ljava/lang/String;
39:  areturn

}

2 голосов
/ 10 сентября 2011

Программы, как правило, содержат много строковых литералов в своем коде.В Java эти константы собраны во что-то, называемое таблица строк для эффективности.Например, если вы используете строку "Name: " в десяти разных местах, JVM (как правило) имеет только один экземпляр этой строки и во всех десяти местах, где она используется, все ссылки указывают на этот один экземпляр.Это экономит память.

Эта оптимизация возможна, поскольку String является неизменным .Если бы можно было изменить строку, то изменив ее в одном месте, это означало бы, что она изменится и в остальных девяти.Вот почему любая операция, которая изменяет строку, возвращает новый экземпляр.Вот почему, если вы сделаете это:

String s = "boink";
s.toUpperCase();
System.out.println(s);

он печатает boink, а не BOINK.

Теперь есть еще один хитрый момент: несколько экземпляров java.lang.String могут указывают на один и тот же базовый char[] для своих символьных данных, другими словами, они могут быть разными представлениями в одном и том же char[], используя только часть массива.Опять же, оптимизация для эффективности.Метод substring() является одним из случаев, когда это происходит.

s1 = "Fred47";

//String s1: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=0, length=6
//                   ^........................^

s2 = s1.substring(2, 5);

//String s2: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=2, length=3
//                             ^.........^
// the two strings are sharing the same char[]!

В вашем вопросе SCJP все это сводится к:

  • Взята строка "Fred"из таблицы String.
  • Строка "47" берется из таблицы String.
  • Строка "Fred47" создается во время вызова метода.// 1
  • Строка "ed4" создается во время вызова метода, совместно использует тот же массив поддержки, что и "Fred47" // 2
  • Строка "ED4"создается во время вызова метода.// 3
  • s.toString() не создает новый, он просто возвращает this.

Один интересный крайний случай всего этого: подумайте, что произойдет, если вынапример, очень длинная строка, например веб-страница, взятая из Интернета, скажем, длина char[] составляет два мегабайта.Если вы возьмете substring(0, 4) этого, вы получите новую строку, которая выглядит , как будто ее длина составляет всего четыре символа, но она все еще разделяет эти два мегабайта резервных данных.Это не так часто встречается в реальном мире, но это может быть огромной тратой памяти!В (редком) случае, когда вы сталкиваетесь с этим как с проблемой, вы можете использовать new String(hugeString.substring(0, 4)), чтобы создать строку с новым небольшим резервным массивом.

Наконец, можно принудительно ввести строку в таблицу строк.во время выполнения, позвонив intern() на него.Основное правило в этом случае: не делай этого.Расширенное правило: не делайте этого, если вы не использовали профилировщик памяти, чтобы убедиться, что это полезная оптимизация.

...