Строки являются объектами в Java, так почему бы нам не использовать "new" для их создания?
Обычно мы создаем объекты, используя new
ключевое слово, как:
Object obj = new Object();
Строки являются объектами, но мы не используем new
создать их:
String str = "Hello World";
Почему это? Могу ли я сделать строку с new
?
14 ответов
В дополнение к тому, что уже было сказано, строковые литералы [т.е. "abcd"
но не как new String("abcd")
] в Java интернированы - это означает, что каждый раз, когда вы ссылаетесь на "abcd", вы получаете ссылку на один String
экземпляр, а не новый каждый раз. Итак, вы будете иметь:
String a = "abcd";
String b = "abcd";
a == b; //True
но если бы вы имели
String a = new String("abcd");
String b = new String("abcd");
тогда возможно иметь
a == b; // False
(и если кому-то нужно напомнить, всегда используйте .equals()
сравнить строки; ==
тесты на физическое равенство).
Строковые литералы Interning хороши тем, что их часто используют более одного раза. Например, рассмотрим (надуманный) код:
for (int i = 0; i < 10; i++) {
System.out.println("Next iteration");
}
Если бы у нас не было интернирования Strings, "Следующая итерация" должна была бы создаваться 10 раз, тогда как теперь она будет создаваться только один раз.
Строки являются "специальными" объектами в Java. Разработчики Java мудро решили, что строки используются так часто, что им необходим собственный синтаксис, а также стратегия кэширования. Когда вы объявляете строку, говоря:
String myString = "something";
myString является ссылкой на объект String со значением "что-то". Если вы позже заявите:
String myOtherString = "something";
Java достаточно умен, чтобы понять, что myString и myOtherString одинаковы и будут хранить их в глобальной таблице String как один и тот же объект. Это зависит от того факта, что вы не можете изменить строки для этого. Это снижает объем требуемой памяти и позволяет проводить сравнения быстрее.
Если вместо этого вы пишете
String myOtherString = new String("something");
Java создаст для вас совершенно новый объект, отличный от объекта myString.
String a = "abc"; // 1 Object: "abc" added to pool
String b = "abc"; // 0 Object: because it is already in the pool
String c = new String("abc"); // 1 Object
String d = new String("def"); // 1 Object + "def" is added to the Pool
String e = d.intern(); // (e==d) is "false" because e refers to the String in pool
String f = e.intern(); // (f==e) is "true"
//Total Objects: 4 ("abc", c, d, "def").
Надеюсь, что это очищает несколько сомнений.:)
Мы обычно используем строковые литералы, чтобы избежать создания ненужных объектов. Если мы используем новый оператор для создания объекта String, то он будет создавать новый объект каждый раз.
Пример:
String s1=“Hello“;
String s2=“Hello“;
String s3= new String(“Hello“);
String s4= new String(“Hello“);
Для приведенного выше кода в памяти:
Это ярлык. Изначально это не было так, но Java изменила это.
Этот FAQ говорит об этом кратко. Об этом также говорится в руководстве по спецификации Java. Но я не могу найти его в Интернете.
Строка подвергается нескольким оптимизациям (из-за отсутствия лучшей фразы). Обратите внимание, что String также имеет перегрузку операторов (для оператора +) - в отличие от других объектов. Так что это очень особый случай.
В Java строки являются особым случаем со многими правилами, которые применяются только к строкам. Двойные кавычки заставляют компилятор создавать объект String. Поскольку объекты String являются неизменяемыми, это позволяет компилятору интернировать несколько строк и создавать пул строк большего размера. Две одинаковые строковые константы всегда будут иметь одинаковую ссылку на объект. Если вы не хотите, чтобы это имело место, вы можете использовать новую строку (""), и это создаст объект строки во время выполнения. Метод intern() раньше был общим, чтобы динамически создаваемые строки проверялись по таблице поиска строк. Как только строка в интерне, ссылка на объект будет указывать на канонический экземпляр String.
String a = "foo";
String b = "foo";
System.out.println(a == b); // true
String c = new String(a);
System.out.println(a == c); // false
c = c.intern();
System.out.println(a == c); // true
Когда загрузчик классов загружает класс, все константы String добавляются в пул String.
Ну, StringPool реализован с использованием Hashmap в java. Если мы всегда создаем с новым ключевым словом, он не выполняет поиск в String Pool и не создает для него новую память, которая может понадобиться позже, если у нас выполняется операция с интенсивным использованием памяти, и если мы создаем все строки с новым ключевым словом, которое может повлиять на производительность нашего приложения. Поэтому рекомендуется не использовать новые ключевые слова для создания строки, потому что тогда только она перейдет в пул строк, который, в свою очередь, является Hashmap (память сохранена, представьте, если у нас есть много строк, созданных с новым ключевым словом), здесь он будет сохранен и если строка уже существует, ссылка на нее (которая обычно находится в памяти стека) будет возвращена во вновь созданную строку. Итак, это сделано для повышения производительности.
Вы все еще можете использовать new String("string")
, но было бы сложнее создавать новые строки без строковых литералов... вам пришлось бы использовать символьные массивы или байты:-) Строковые литералы имеют одно дополнительное свойство: все одинаковые строковые литералы из любого класса указывают на один и тот же строковый экземпляр (они интернирован).
Синтаксический сахар.
String s = new String("ABC");
синтаксис все еще доступен.
Потому что String является неизменным классом в Java.
Теперь, почему он неизменен? Поскольку String является неизменным, он может быть разделен между несколькими потоками, и нам не нужно синхронизировать операцию String извне. В качестве String также используется механизм загрузки классов. Таким образом, если бы String был изменяемым, тогда java.io.writer мог быть изменен на abc.xyz.mywriter
Не стесняйтесь создавать новую строку с
String s = new String("I'm a new String");
Обычные обозначения s = "new String";
это более или менее удобный ярлык, который следует использовать из соображений производительности, за исключением тех довольно редких случаев, когда вам действительно нужны строки, подходящие для уравнения
(string1.equals(string2)) && !(string1 == string2)
РЕДАКТИРОВАТЬ
В ответ на комментарий: это был не совет, а просто прямой ответ на тезис спрашивающих, что мы не используем ключевое слово "new" для строк, что просто не соответствует действительности. Надеюсь, что это редактирование (в том числе выше) проясняет это немного. Кстати, есть пара хороших и гораздо лучших ответов на поставленный выше вопрос о SO.
Литеральный пул содержит любые строки, созданные без использования ключевого слова. new
,
Есть разница: String без новой ссылки хранится в пуле литералов String, а String with new говорит, что они находятся в динамической памяти.
Строка с новым в другом месте в памяти, как и любой другой объект.
Почти нет необходимости добавлять новую строку, так как литерал (символы в кавычках) уже является объектом String, созданным при загрузке класса хоста. Совершенно законно вызывать методы для литерала и дона, главное отличие заключается в удобстве литералов. Это было бы большой болью и тратой времени, если бы нам пришлось создать массив символов и заполнить его char символом, а они должны создать новую строку (массив символов).
TString obj1 = new TString("Jan Peter");
TString obj2 = new TString("Jan Peter");
if (obj1.Name == obj2.Name)
System.out.println("True");
else
System.out.println("False");
Выход:
Правда
Я создал два отдельных объекта, оба имеют поле (ref) "Имя". Так что даже в этом случае "Ян Питер" поделился, если я понимаю, как Java работает..