Точная разница между CharSequence и String в Java
Я прочитал этот предыдущий пост. Кто-нибудь может сказать, какая точная разница между CharSequence
и строка, кроме того факта, что String
инвентарь CharSequence
и это String
такое последовательность символов? Например:
CharSequence obj = "hello";
String str = "hello";
System.out.println("output is : " + obj + " " + str);
Что происходит, когда "привет" назначен obj
и снова str
?
8 ответов
Общие отличия
Есть несколько классов, которые реализуют CharSequence
интерфейс помимо String
, Среди них
StringBuilder
для символьных последовательностей переменной длины, которые могут быть измененыCharBuffer
для низкоуровневых последовательностей символов фиксированной длины, которые могут быть изменены
Любой метод, который принимает CharSequence
может работать на все это одинаково хорошо. Любой метод, который принимает только String
потребуется преобразование. Итак, используя CharSequence
как тип аргумента во всех местах, где вы не заботитесь о внутренних органах, разумно. Однако вы должны использовать String
в качестве типа возврата, если вы на самом деле возвращаете String
потому что это позволяет избежать возможных преобразований возвращаемых значений, если вызывающий метод действительно требует String
,
Также обратите внимание, что карты должны использовать String
как тип ключа, а не CharSequence
Ключи карты не должны меняться. Другими словами, иногда неизменный характер String
необходимо.
Фрагмент кода
Что касается вставленного вами кода: просто скомпилируйте его и посмотрите на байт-код JVM, используя javap -v
, Там вы заметите, что оба obj
а также str
являются ссылками на один и тот же постоянный объект. Как String
неизменен, этот вид обмена все в порядке.
+
оператор String
составляется как вызовы различных StringBuilder.append
звонки. Так что это эквивалентно
System.out.println(
(new StringBuilder())
.append("output is : ")
.append((Object)obj)
.append(" ")
.append(str)
.toString()
)
Должен признаться, я немного удивлен, что мой компилятор javac 1.6.0_33
компилирует + obj
с помощью StringBuilder.append(Object)
вместо StringBuilder.append(CharSequence)
, Первое, вероятно, включает в себя вызов toString()
метод объекта, тогда как последний должен быть возможен более эффективным способом. С другой стороны, String.toString()
просто возвращает String
сам по себе, так что там штрафов мало. Так StringBuilder.append(String)
может быть более эффективным при вызове одного метода.
ТЛ; др
Одним из них является интерфейс (CharSequence
) в то время как другое является конкретной реализацией этого интерфейса (String
).
CharSequence animal = "cat" // `String` object presented as the interface `CharSequence`.
Как интерфейс, обычно CharSequence
было бы чаще видно, чем String
, но некоторая искаженная история привела к тому, что интерфейс был определен спустя годы после реализации. Так что в старых API мы часто видим String
в то время как в более новых API мы склонны видеть CharSequence
используется для определения аргументов и возвращаемых типов.
подробности
В настоящее время мы знаем, что обычно API/framework должны фокусироваться на экспорте интерфейсов в первую очередь и конкретных классов во вторую очередь. Но мы не всегда знали этот урок так хорошо.
String
класс пришел первым на Яве. Только позже они разместили интерфейс, CharSequence
,
Искаженная история
Немного истории может помочь с пониманием.
В первые дни Java была спешно выпущена на рынок немного раньше времени из-за интернет-мании, оживляющей индустрию. Некоторые библиотеки были не так хорошо продуманы, как следовало бы. Обработка строк была одной из тех областей.
Кроме того, Java была одной из первых производственных ориентированных неакадемических объектно-ориентированных программ (ООП). Единственными успешными реальными реализациями OOP, отвечающими требованиям реального времени, были некоторые ограниченные версии SmallTalk, а затем Objective-C с NeXTSTEP / OpenStep. Таким образом, многие практические уроки еще предстоит выучить.
Java началась с String
класс и StringBuffer
учебный класс. Но эти два класса не были связаны, не связаны друг с другом ни наследованием, ни интерфейсом. Позже команда Java поняла, что должна быть объединяющая связь между реализациями, связанными со строками, чтобы сделать их взаимозаменяемыми. В Java 4 команда добавила CharSequence
интерфейс и задним числом реализовали этот интерфейс на String и String Buffer, а также добавив еще одну реализацию CharBuffer
, Позже в Java 5 они добавили StringBuilder
в основном несинхронизированная и, следовательно, несколько более быстрая версия StringBuffer
,
Таким образом, эти классы, ориентированные на строки, немного беспорядочные и немного запутанные для изучения. Многие библиотеки и интерфейсы были созданы, чтобы принимать и возвращать String
объекты. В настоящее время такие библиотеки должны быть построены так, чтобы ожидать CharSequence
, Но (а) String
кажется, все еще доминирует в пространстве ума, и (б) могут быть некоторые тонкие технические проблемы при смешении различных CharSequence
Реализации. С учетом ретроспективного взгляда 20/20 мы видим, что со всеми этими струнными вещами можно было бы справиться лучше, но мы здесь.
В идеале Java должна начинаться с интерфейса и / или суперкласса, который будет использоваться во многих местах, где мы сейчас используем String
так же, как мы используем Collection
или же List
интерфейсы вместо ArrayList
или же LinkedList
Реализации.
Интерфейс против класса
Ключевое отличие о CharSequence
является то, что это интерфейс, а не реализация. Это означает, что вы не можете напрямую создать экземпляр CharSequence
, Скорее вы создаете экземпляр одного из классов, который реализует этот интерфейс.
Например, здесь мы имеем x
это выглядит как CharSequence
но под ним на самом деле StringBuilder
объект.
CharSequence x = new StringBuilder( "dog" );
Это становится менее очевидным при использовании строкового литерала. Имейте в виду, что когда вы видите исходный код с кавычками вокруг символов, компилятор преобразует его в объект String.
CharSequence y = "cat"; // Looks like a CharSequence but is actually a String instance.
Есть некоторые тонкие различия между "cat"
а также new String("cat")
как обсуждалось в этом другом Вопросе, но здесь не имеет значения.
Диаграмма классов
Эта диаграмма классов может помочь вам. Я отметил версию Java, в которой они появились, чтобы продемонстрировать, насколько много изменений произошло в этих классах и интерфейсах.
Необработанные строковые литералы
В будущей версии Java может появиться новая функция необработанных строковых литералов. Это сделает написание строк встроенного кода, таких как SQL, более удобным. Смотри JEP 326.
В этом JEP предлагается новый тип литерала - необработанный строковый литерал, который откладывает как Java-экранирование, так и спецификации терминатора строки Java, чтобы обеспечить символьные последовательности, которые при многих обстоятельствах более удобочитаемы и доступны для обслуживания, чем существующий традиционный строковый литерал.
Для более подробного обсуждения см. Этот пост и этот пост от Гетца, Необработанные строковые литералы - где мы находимся, как мы сюда попали.
CharSequence
это контракт ( интерфейс), и String
это реализация этого контракта.
public final class String extends Object
implements Serializable, Comparable<String>, CharSequence
Документация для CharSequence
является:
CharSequence - это читаемая последовательность значений char. Этот интерфейс обеспечивает равномерный доступ только для чтения к различным типам последовательностей символов. Значение char представляет символ в базовой многоязычной плоскости (BMP) или суррогат. Обратитесь к представлению символов Unicode для деталей.
кроме факта, что String реализует CharSequence, а String является последовательностью символов.
В вашем коде происходит несколько вещей:
CharSequence obj = "hello";
Это создает String
буквальным, "hello"
, который является String
объект. Быть String
, который реализует CharSequence
это также CharSequence
, (Вы можете прочитать этот пост о кодировании интерфейса, например).
Следующая строка:
String str = "hello";
немного сложнее. String
литералы в Java хранятся в пуле (интернированы), поэтому "hello"
на этой линии находится тот же объект (личность), что и "hello"
на первой линии. Следовательно, эта строка присваивает только String
буквально str
,
На этом этапе оба obj
а также str
ссылки на String
буквальный "hello"
и поэтому equals
, ==
и они оба String
и CharSequence
,
Я предлагаю вам протестировать этот код, показывая в действии то, что я только что написал:
public static void main(String[] args) {
CharSequence obj = "hello";
String str = "hello";
System.out.println("Type of obj: " + obj.getClass().getSimpleName());
System.out.println("Type of str: " + str.getClass().getSimpleName());
System.out.println("Value of obj: " + obj);
System.out.println("Value of str: " + str);
System.out.println("Is obj a String? " + (obj instanceof String));
System.out.println("Is obj a CharSequence? " + (obj instanceof CharSequence));
System.out.println("Is str a String? " + (str instanceof String));
System.out.println("Is str a CharSequence? " + (str instanceof CharSequence));
System.out.println("Is \"hello\" a String? " + ("hello" instanceof String));
System.out.println("Is \"hello\" a CharSequence? " + ("hello" instanceof CharSequence));
System.out.println("str.equals(obj)? " + str.equals(obj));
System.out.println("(str == obj)? " + (str == obj));
}
Я знаю, это довольно очевидно, но CharSequence - это интерфейс, тогда как String - конкретный класс:)
java.lang.String - реализация этого интерфейса...
Из Java API CharSequence:
CharSequence - это читаемая последовательность символов. Этот интерфейс обеспечивает равномерный доступ только для чтения ко многим различным типам последовательностей символов.
Этот интерфейс затем используется String, CharBuffer и StringBuffer для обеспечения согласованности всех имен методов.
Рассмотрим UTF-8. В UTF-8 кодовые точки Unicode строятся из одного или нескольких байтов. Класс, инкапсулирующий байтовый массив UTF-8, может реализовывать интерфейс CharSequence, но определенно не является String. Конечно, вы не можете передать байтовый массив UTF-8, где ожидается String, но вы, безусловно, можете передать класс-оболочку UTF-8, который реализует CharSequence, когда контракт ослаблен, чтобы разрешить CharSequence. В моем проекте я разрабатываю класс CBTF8Field (Compressed Binary Transfer Format - Eight Bit), чтобы обеспечить сжатие данных для xml, и собираюсь использовать интерфейс CharSequence для реализации преобразований из байтовых массивов CBTF8 в / из символьных массивов (UTF-16).) и байтовые массивы (UTF-8).
Причина, по которой я пришел сюда, состояла в том, чтобы получить полное понимание контракта подпоследовательности.
В charSequence у вас нет очень полезных методов, доступных для String. Если вы не хотите просматривать документацию, введите: obj. и ул.
и посмотрите, какие методы предлагает вам ваш компилятор. Это основная разница для меня.