Подтвердить что ты не робот

Базовый механизм объединения строк в Java?

Мне было любопытно, почему строки могут быть созданы без вызова new String(), поскольку API упоминает, что это Object из class java.lang.String

Итак, как мы можем использовать String s="hi", а не String s=new String("hi")?

Этот пост разъяснил использование оператора == и отсутствие new и говорит, что это связано с String литералами, которые интернированы или взяты из литерала пул JVM, следовательно Strings являются неизменяемыми.

При просмотре выражения типа

String s="hi"

в первый раз, что действительно происходит?

  • Является ли JVM заменять его так: String s=new String("hi"), в котором создается объект и добавляется "hi" в строку литеральный пул, и поэтому последующие вызовы, такие как String s1="hi", являются взято из пула?

  • Является ли это тем, как работает основной механизм? Если это так, то

    String s=new String("Test");
    String s1="Test";
    

    то же, что и

    String s="Test";
    String s1="Test";
    

    с точки зрения использования и эффективности памяти?

  • Кроме того, есть ли способ, с помощью которого мы можем получить доступ к String Pool, чтобы проверьте, сколько литералов String присутствует в нем, сколько места занято и т.д.?

4b9b3361

Ответ 1

  • String s="hi" в первый раз, что действительно происходит?

Заменяет ли JVM это как String s=new String("hi"), в котором создается объект и добавляется "привет" в строковый литерал пул, и поэтому последующие вызовы, такие как String s1 = "hi", берутся из пул?.

Нет. Что на самом деле происходит - литералы String разрешаются во время компиляции и интернированы (добавляются в пул строковых констант), как только класс загружается/инициализируется или лениво. Таким образом, они становятся доступными для классов в JVM. Обратите внимание, что даже если у вас есть String со значением "hi" в пуле констант Строки, new String("hi") создаст еще одну String в куче и вернет ссылку.

  1. является
 String s=new String("Test"); 
 String s1="Test"; 

то же, что и

 String s="Test"; 
 String s1="Test"; 

с точки зрения использования памяти и эффективность?

Нет, в первом случае создаются строки 2 "Test". Один будет добавлен в пул констант String (если он там еще не присутствует), а другой в куче. Второй может быть GCed. Во втором случае только один строковый литерал присутствует в пуле констант String и есть 2 ссылки на него (s и s1).

  1. Также, если есть способ, с помощью которого мы можем получить доступ к пулу строк, как в проверьте, сколько строк в нем присутствует, занятое пространство и т.д. из программы или из любого инструмента мониторинга?

Я не думаю, что мы можем увидеть содержимое пула констант String. Мы можем просто предположить и подтвердить поведение, основанное на наших предположениях.

Ответ 2

Компилятор Java имеет специальную поддержку строковых литералов. Предположим, что это не так, тогда было бы очень громоздко создавать строки в исходном коде, вам нужно написать что-то вроде:

// Suppose that we would not have string literals like "hi"
String s = new String(new char[]{ 'h', 'i' });

Чтобы ответить на ваши вопросы:

  • Более или менее, и если вы действительно хотите узнать подробности, вам нужно изучить исходный код JVM, который вы можете найти в OpenJDK, но будьте осторожны, чтобы это было огромным и сложным.

  • Нет, эти два не эквивалентны. В первом случае вы явно создаете новый объект String:

    String s=new String("Test");
    

    который будет содержать копию объекта String, представленного литералом "Test". Обратите внимание, что никогда не рекомендуется писать new String("some literal") в Java - строки неизменяемы, и никогда не нужно делать копию строкового литерала.

  • Я не знаю, что проверить в пуле строк.

Ответ 3

Это не тесно связано с объектом, но всякий раз, когда вы сомневаетесь в том, что сделает java-компилятор, вы можете использовать

javap -c CompiledClassName

чтобы напечатать то, что на самом деле происходит. (CompiledClassName из каталога, где CompiledClassName.class)

Чтобы добавить к ответу Jesper, на работе больше механизмов, например, когда вы объединяете строку из литералов или конечных переменных, она все равно будет использовать внутренний пул:

String s0 = "te" + "st";
String s1 = "test";
final String s2 = "te";
String s3 = s2 + "st";
System.out.println(s0==s1); //true
System.out.println(s3==s1); //true

Но когда вы объединяетесь с использованием не конечных переменных, он не будет использовать пул:

String s0 = "te";
String s1 = s0 + "st";
String s2 = "test";
System.out.println(s1 == s2); //false

Ответ 4

Ниже приводится небольшое упрощение, поэтому не пытайтесь приводить из него точные данные, но применяются общие принципы.

Каждый скомпилированный Java-класс содержит блок данных, который указывает, сколько строк было объявлено в этом файле класса, как долго каждый из них, и символы, принадлежащие всем им. Когда класс загружен, загрузчик классов создаст String[] подходящего размера, чтобы удерживать все строки, определенные в этом классе; для каждой строки он генерирует char[] подходящего размера, считывает соответствующее количество символов из файла класса в char[], создает String, инкапсулируя эти символы, и сохраняет ссылку в классе String[].

При компиляции некоторого класса (например, Foo) компилятор знает, какой строковый литерал он встречает первым, вторым, третьим, пятым и т.д. Если код говорит myString = "George";, а Джордж - шестой строковый литерал, который появится в код в виде команды "строка строки литерала №6"; компилятор просто в момент, когда он генерирует код для этой команды, генерирует команду для извлечения ссылки шестой строки, связанной с этим классом.

Ответ 5

  • Как-то, но не совсем. Строковые константы создаются и интернируются во время постоянного разрешения пула. Это происходит при первом выполнении байт-кода LDC, который загружает строковый литерал. После первого выполнения JVM заменяет тег пула JVM_CONSTANT_UnresolvedString тегом JVM_CONSTANT_String таким образом, что в следующий раз LDC вместо существующей строки будет создана существующая строка.

  • Нет. Первое использование "Test" создаст новый строковый объект. Затем new String("Test") создаст второй объект.

  • Да, используя агент обслуживания HotSpot. Вот example.

Ответ 6

Я считаю, что основным механизмом создания String является StringBuilder, который собирает объект String в конце. По крайней мере, я точно знаю, что если у вас есть строка, которую вы хотите изменить, например:

String str = "my String";
// and then do
System.out.println(str + "new content");

Итак, что это значит, он создает StrigBuilder из старого объекта и заменяет его новым, созданным из построителя. Вот почему более эффективно использовать память StringBuilder вместо обычной строки, к которой вы просто добавляете материал.

Существует способ доступа к уже созданному пулу String, который используется методом String.intern(). Он сообщает java использовать одно и то же пространство памяти для строк, которое дает ссылку на это место в памяти. Это также позволяет использовать оператор == для сравнения строк и более эффективно использовать память.

Ответ 7

Строковый пул, так как это пул строки, хранящийся в куче для exp:

String s="Test";
String s1="Test";    

оба сохраняются в куче и ссылаются на один "Тест", таким образом, s1 = s, в то время как

String s=new String("Test");

- это объект, который также хранится в куче, но в другой форме s1 = s см. здесь