Точная разница между 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 8

Необработанные строковые литералы

В будущей версии 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. и ул.

и посмотрите, какие методы предлагает вам ваш компилятор. Это основная разница для меня.

Другие вопросы по тегам