Точная разница между CharSequence и String в java

Я прочитал этот предыдущий пост. Может ли кто-нибудь сказать, что такое точная разница между CharSequence и String, кроме того, что String реализует CharSequence и что String - это последовательность символов? Например:

CharSequence obj = "hello";
String str = "hello";
System.out.println("output is : " + obj + "  " + str);

Что происходит, когда "привет" назначается obj и снова на str?

Ответ 1

Общие отличия

Существует несколько классов, которые реализуют интерфейс 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) может быть более эффективным примерно одним вызовом метода.

Ответ 2

ТЛ; др

Один - это интерфейс ( CharSequence), а другой - конкретная реализация этого интерфейса ( String).

CharSequence animal = "cat"  // 'String' object presented as the interface 'CharSequence'.

Как интерфейс, обычно CharSequence можно увидеть чаще, чем String, но некоторая искаженная история привела к тому, что интерфейс определялся спустя годы после реализации. Поэтому в старых API мы часто видим String а в более новых API мы склонны видеть CharSequence используемый для определения аргументов и возвращаемых типов.

подробности

В настоящее время мы знаем, что обычно API/framework должны фокусироваться на экспорте интерфейсов в первую очередь и конкретных классов во вторую очередь. Но мы не всегда знали этот урок так хорошо.

Класс String появился первым на Java. Только позже они разместили интерфейс, CharSequence на передней панели, 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. Но (a) String похоже, все еще доминирует в пространстве ума, и (b) могут возникнуть некоторые тонкие технические проблемы при смешивании различных реализаций 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, в которой они появились, чтобы продемонстрировать, насколько много изменений произошло в этих классах и интерфейсах.

diagram showing the various string-related classes and interfaces as of Java 8

Текстовые блоки

За исключением все большего количества смайликов и других персонажей, которые приходят с последовательными версиями поддержки Unicode, в последние годы в Java мало что изменилось для работы с текстом... до Java 13.

Java 13 может предлагать предварительный просмотр новой функции: текстовые блоки. Это сделает написание строк встроенного кода, такого как SQL, более удобным. Смотри JEP 355.

Этому усилию предшествовал JEP 326: Необработанные строковые литералы (превью).

Ответ 3

CharSequence является контрактом (interface), а String является implementation этого договора.

public final class String extends Object 
    implements Serializable, Comparable<String>, CharSequence

документация для CharSequence:

CharSequence является читаемой последовательностью значений char. Этот интерфейс обеспечивает единообразный доступ только для чтения к различным типам charпоследовательности. Значение char представляет символ в Basic Многоязычный самолет (BMP) или суррогат. Обратитесь к символу Unicode Представление для деталей.

Ответ 4

кроме того, что 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 и a 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));
}

Ответ 5

Я знаю это как нечто очевидное, но CharSequence - это интерфейс, тогда как String - это конкретный класс:)

java.lang.String - это реализация этого интерфейса...

Ответ 6

Рассмотрим UTF-8. В UTF-8 кодовые точки Юникода построены из одного или нескольких байтов. Класс, инкапсулирующий массив байтов UTF-8, может реализовать интерфейс CharSequence, но наиболее определенно не является строкой. Конечно, вы не можете передать массив байтов UTF-8, где ожидается String, но вы, безусловно, можете передать класс оболочки UTF-8, который реализует CharSequence, когда контракт ослаблен, чтобы разрешить CharSequence. В моем проекте я разрабатываю класс под названием CBTF8Field (сжатый двоичный формат передачи - восемь бит) для обеспечения сжатия данных для xml и я хочу использовать интерфейс CharSequence для реализации преобразований из массивов байтов CBTF8 в/из массивов символов (UTF-16 ) и байтовые массивы (UTF-8).

Я пришел сюда, чтобы получить полное представление о контракте подпоследовательности.

Ответ 7

Из API Java CharSequence:

CharSequence - это читаемая последовательность символов. Этот интерфейс обеспечивает единообразный доступ только для чтения ко многим различным типам последовательностей символов.

Этот интерфейс затем используется String, CharBuffer и StringBuffer, чтобы сохранить согласованность для всех имен методов.

Ответ 8

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

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