Строки Java: "String s = новая строка (" глупо ");

Я парень С++, изучающий Java. Я читаю "Эффективная Java", и что-то меня смутило. Он говорит, что никогда не писать такой код:

String s = new String("silly");

Потому что он создает ненужные объекты String. Но вместо этого это должно быть написано так:

String s = "No longer silly";

Хорошо до сих пор... Однако, учитывая этот класс:

public final class CaseInsensitiveString {
    private String s;
    public CaseInsensitiveString(String s) {
        if (s == null) {
            throw new NullPointerException();
        }
        this.s = s;
    }
    :
    :
}

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";
  • Почему первое утверждение одобрено? Не должно быть

    CaseInsensitiveString cis = "Polish";

  • Как заставить CaseInsensitiveString вести себя как String, чтобы приведенный выше оператор был в порядке (с расширением String и без него)? Что это такое о String, которая позволяет ему просто передать это буквально? Насколько я понимаю, в Java нет концепции "конструктор экземпляров"?

Ответ 1

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

String s = new String("Polish");

Поскольку литерал "Polish" уже имеет тип String, и вы создаете лишний ненужный объект. Для любого другого класса, говоря

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

- это правильная (и только в этом случае) вещь.

Ответ 2

Я считаю, что основное преимущество использования литерала (т.е. "foo", а не нового String ( "foo" )) заключается в том, что все строковые литералы "интернированы" виртуальной машиной. Другими словами, он добавляется в пул, так что любой другой код, который создает одну и ту же строку, будет использовать объединенную строку, а не создавать новый экземпляр.

Чтобы проиллюстрировать, следующий код напечатает true для первой строки, но false для второго:

System.out.println("foo" == "foo");
System.out.println(new String("bar") == new String("bar"));

Ответ 3

Строки обрабатываются немного в java, они неизменяемы, поэтому для них безопасно обрабатывать подсчет ссылок.

Если вы пишете

String s = "Polish";
String t = "Polish";

тогда s и t действительно ссылаются на один и тот же объект, а s == t вернет true, для "==" для объектов read "является одним и тем же объектом" (или, во всяком случае, я не уверен, является частью фактической спецификации языка или просто деталью реализации компилятора, поэтому, возможно, небезопасно полагаться на это).

Если вы пишете

String s = new String("Polish");
String t = new String("Polish");

тогда s!= t (потому что вы явно создали новую строку), хотя s.equals(t) вернет true (потому что строка добавляет это поведение равным).

Вещь, которую вы хотите написать,

CaseInsensitiveString cis = "Polish";

не может работать, потому что вы думаете, что цитаты являются своего рода конструктором короткого замыкания для вашего объекта, когда на самом деле это работает только для простых старых java.lang.Strings.

Ответ 4

String s1="foo";

literal войдет в пул и s1 будет ссылаться.

String s2="foo";

на этот раз он будет проверять, что литерал "foo" уже доступен в StringPool или нет, поскольку теперь он существует, поэтому s2 будет ссылаться на один и тот же литерал.

String s3=new String("foo");

"foo" литерал будет создан сначала в StringPool, а затем через конструктор строковых аргументов String Object будет создан i.e "foo" в куче из-за создания объекта через новый оператор, тогда s3 будет ссылаться на него.

String s4=new String("foo");

то же, что и s3

so System.out.println(s1==s2);// **true** due to literal comparison.

и System.out.println(s3==s4);// **false** due to object

сравнение (s3 и s4 создаются в разных местах в куче)

Ответ 5

String являются специальными в Java - они неизменяемы, а строковые константы автоматически превращаются в объекты String.

Невозможно применить ваш пример SomeStringClass cis = "value" к любому другому классу.

Также вы можете расширить String, потому что он объявлен как final, что означает, что не разрешено подкласс.

Ответ 6

Интересны строки Java. Похоже, ответы затронули некоторые интересные моменты. Вот мои два цента.

строки неизменяемы (вы никогда не сможете их изменить)

String x = "x";
x = "Y"; 
  • Первая строка создаст переменную x, которая будет содержать строковое значение "x" . JVM будет искать в своем пуле строковых значений и посмотреть, существует ли "x" , если это так, то укажет на него переменную x, если она не существует, она создаст ее, а затем выполнит назначение
  • Вторая строка удалит ссылку на "x" и посмотрит, существует ли "Y" в пуле строковых значений. Если он существует, он будет назначать его, если он этого не сделает, он сначала создаст его, затем присваивает. Когда используются строковые значения или нет, пространство памяти в пуле строковых значений будет исправлено.

Сравнение строк зависит от того, что вы сравниваете

String a1 = new String("A");

String a2 = new String("A");
  • a1 не равно a2
  • a1 и a2 являются объектными ссылками
  • Когда строка явно объявлена, создаются новые экземпляры и их ссылки не будут одинаковыми.

Я думаю, что вы ошибаетесь, пытаясь использовать класс caseinsensitive. Оставьте струны в покое. Что вам действительно нужно, так это то, как вы показываете или сравниваете значения. Используйте другой класс для форматирования строки или для сравнения.

то есть.

TextUtility.compare(string 1, string 2) 
TextUtility.compareIgnoreCase(string 1, string 2)
TextUtility.camelHump(string 1)

Поскольку вы составляете класс, вы можете сделать сравнения делать то, что вы хотите - сравнить текстовые значения.

Ответ 7

Вы не можете. Вещи в двойных кавычках в Java специально распознаются компилятором как строки, и, к сожалению, вы не можете переопределить это (или расширить java.lang.String - он объявлен final).

Ответ 8

Лучший способ ответить на ваш вопрос - познакомить вас с "String constant pool". В java строковые объекты неизменяемы (т.е. их значения не могут быть изменены после их инициализации), поэтому при редактировании строкового объекта вы создаете новый отредактированный строковый объект, где старый объект просто плавает вокруг в специальной памяти ares, называемой "string" постоянный пул ". создание нового строкового объекта

String s = "Hello";

создаст только строковый объект в пуле, и ссылка s будет ссылаться на него, но используя

String s = new String("Hello");

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

Ответ 9

В первом примере вы создаете String, "глупо", а затем передаете его как параметр другому конструктору копии String, что делает вторую String идентичной первой. Так как строки Java неизменяемы (что часто укусывает людей, которые используются для строк C), это бесполезная трата ресурсов. Вместо этого вы должны использовать второй пример, потому что он пропускает несколько ненужных шагов.

Однако строковый литерал не является CaseInsensitiveString, поэтому вы не можете делать то, что хотите в своем последнем примере. Кроме того, нет способа перегрузить оператор кастинга, как вы можете на С++, чтобы буквально не было возможности делать то, что вы хотите. Вместо этого вы должны передать его в качестве параметра в свой конструктор класса. Конечно, я бы просто использовал String.toLowerCase() и покончил с этим.

Кроме того, ваша CaseInsensitiveString должна реализовывать интерфейс CharSequence, а также, возможно, интерфейсы Serializable и Comparable. Конечно, если вы реализуете Comparable, вы должны также переопределить equals() и hashCode().

Ответ 10

- Как заставить CaseInsensitiveString вести себя как String, поэтому вышеприведенный оператор в порядке (с и без расширения строки)? Что это такое в String, что делает его достаточно, чтобы просто передать его буквально? Насколько я понимаю, в Java нет концепции "конструктор экземпляров"?

Достаточно было сказано с первого момента. "Польский" является строковым литералом и не может быть назначен классу CaseInsentiviveString.

Теперь о второй точке

Хотя вы не можете создавать новые литералы, вы можете следовать первому элементу этой книги для "аналогичного" подхода, чтобы выполнялись следующие утверждения:

    // Lets test the insensitiveness
    CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg");
    CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG");

    assert cis5 == cis6;
    assert cis5.equals(cis6);

Здесь код.

C:\oreyes\samples\java\insensitive>type CaseInsensitiveString.java
import java.util.Map;
import java.util.HashMap;

public final class CaseInsensitiveString  {


    private static final Map<String,CaseInsensitiveString> innerPool 
                                = new HashMap<String,CaseInsensitiveString>();

    private final String s;


    // Effective Java Item 1: Consider providing static factory methods instead of constructors
    public static CaseInsensitiveString valueOf( String s ) {

        if ( s == null ) {
            return null;
        }
        String value = s.toLowerCase();

        if ( !CaseInsensitiveString.innerPool.containsKey( value ) ) {
             CaseInsensitiveString.innerPool.put( value , new CaseInsensitiveString( value ) );
         }

         return CaseInsensitiveString.innerPool.get( value );   
    }

    // Class constructor: This creates a new instance each time it is invoked.
    public CaseInsensitiveString(String s){
        if (s == null) {
            throw new NullPointerException();
         }         
         this.s = s.toLowerCase();
    }

    public boolean equals( Object other ) {
         if ( other instanceof CaseInsensitiveString ) {
              CaseInsensitiveString otherInstance = ( CaseInsensitiveString ) other;
             return this.s.equals( otherInstance.s );
         }

         return false;
    }


    public int hashCode(){
         return this.s.hashCode();
    }

//Проверяем класс с помощью ключевого слова "assert"

    public static void main( String [] args ) {

        // Creating two different objects as in new String("Polish") == new String("Polish") is false
        CaseInsensitiveString cis1 = new CaseInsensitiveString("Polish");
        CaseInsensitiveString cis2 = new CaseInsensitiveString("Polish");

        // references cis1 and cis2 points to differents objects.
        // so the following is true
        assert cis1 !=  cis2;      // Yes they're different
        assert cis1.equals(cis2);  // Yes they're equals thanks to the equals method

        // Now let try the valueOf idiom
        CaseInsensitiveString cis3 = CaseInsensitiveString.valueOf("Polish");
        CaseInsensitiveString cis4 = CaseInsensitiveString.valueOf("Polish");

        // References cis3 and cis4 points to same  object.
        // so the following is true
        assert cis3 == cis4;      // Yes they point to the same object
        assert cis3.equals(cis4); // and still equals.

        // Lets test the insensitiveness
        CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg");
        CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG");

        assert cis5 == cis6;
        assert cis5.equals(cis6);

        // Futhermore
        CaseInsensitiveString cis7 = CaseInsensitiveString.valueOf("SomethinG");
        CaseInsensitiveString cis8 = CaseInsensitiveString.valueOf("someThing");

        assert cis8 == cis5 && cis7 == cis6;
        assert cis7.equals(cis5) && cis6.equals(cis8);
    }

}

C:\oreyes\samples\java\insensitive>javac CaseInsensitiveString.java


C:\oreyes\samples\java\insensitive>java -ea CaseInsensitiveString

C:\oreyes\samples\java\insensitive>

То есть, создайте внутренний пул объектов CaseInsensitiveString и верните туда экземпляр corrensponding.

Таким образом, оператор "==" возвращает true для двух ссылок на объекты, представляющих то же значение.

Это полезно, когда похожие объекты используются очень часто, а стоимость затрат стоит дорого.

Документация по строковым классам гласит, что класс использует внутренний пул

Класс не является полным, некоторые интересные проблемы возникают, когда мы пытаемся использовать содержимое объекта при реализации интерфейса CharSequence, но этот код достаточно хорош, чтобы показать, как этот элемент в книге может быть применен.

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

Он работает для класса String, потому что он используется интенсивно, а пул состоит только из объекта "интернированного".

Он отлично подходит для булевого класса, потому что есть только два возможных значения.

И наконец, это также причина, по которой valueOf (int) в классе Integer ограничена -128 до 127 значениями int.

Ответ 11

Просто потому, что у вас есть слово String в вашем классе, не означает, что вы получаете все специальные функции встроенного класса String.

Ответ 12

CaseInsensitiveString не является String, хотя он содержит String. A String литерал, например "пример", может быть назначен только String.

Ответ 13

CaseInsensitiveString и String - разные объекты. Вы не можете:

CaseInsensitiveString cis = "Polish";

потому что "Polish" - это String, а не CaseInsensitiveString. Если String расширяет CaseInsensitiveString String, тогда вы будете в порядке, но, очевидно, это не так.

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

В случае String s = new String ( "foobar" ) он делает что-то другое. Сначала вы создаете литеральную строку "foobar", а затем создаете ее копию, создавая из нее новую строку. Нет необходимости создавать эту копию.

Ответ 14

когда говорят, что пишут

String s = "Silly";

вместо

String s = new String("Silly");

они имеют в виду это при создании объекта String, потому что оба вышеупомянутых оператора создают объект String, но новая версия String() создает два объекта String: один в куче, а другой в пуле строковых констант. Следовательно, используя больше памяти.

Но когда вы пишете

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");

вы не создаете String, вместо этого вы создаете объект класса CaseInsensitiveString. Следовательно, вам нужно использовать новый оператор.

Ответ 15

Если я правильно понял, ваш вопрос означает, почему мы не можем создать объект, просто присваивая ему значение, не ограничивая его классом Wrapper класса String в java.

Чтобы ответить, я бы просто сказал, что чисто объектно-ориентированные языки программирования имеют некоторые конструкции, и он говорит, что все литералы, если они написаны отдельно, могут быть напрямую преобразованы в объект данного типа.

Это означает, что если интерпретатор увидит 3, он будет преобразован в объект Integer, потому что integer - это тип, определенный для таких литералов.

Если интерпретатор видит что-либо в одинарных кавычках, таких как 'a', он будет непосредственно создавать объект типа character, вам не нужно указывать его, поскольку язык определяет для него объект по умолчанию для символа типа.

Аналогично, если интерпретатор видит что-то в "", он будет рассматриваться как объект его типа по умолчанию, т.е. строки. Это некоторый собственный код, работающий в фоновом режиме.

Благодаря учебной лекции MIT 6.00, где я получил намек на этот ответ.

Ответ 16

В Java синтаксис "text" создает экземпляр класса java.lang.String. Назначение:

String foo = "text";

- простое назначение, без необходимости использовать конструктор копирования.

MyString bar = "text";

Это незаконно, что вы делаете, потому что класс MyString не является java.lang.String или суперклассом java.lang.String.

Ответ 17

Во-первых, вы не можете создать класс, который простирается от String, потому что String является окончательным классом. И java управляет строками иначе, чем другие классы, поэтому только с помощью String вы можете делать

String s = "Polish";

Но если у вас есть класс, вы должны вызвать конструктор. Итак, этот код в порядке.

Ответ 18

В большинстве версий JDK две версии будут одинаковыми:

Строка s = новая строка ( "глупая" );

Строка s = "Больше не глупо";

Поскольку строки неизменяемы, компилятор поддерживает список строковых констант, и если вы попытаетесь создать новый, сначала проверьте, не определена ли строка. Если это так, возвращается ссылка на существующую неизменяемую строку.

Чтобы прояснить - когда вы говорите "String s =", вы определяете новую переменную, которая занимает место в стеке, - тогда вы говорите: "Больше не глупо" или новая String ( "глупо" ), точно такая же вещь происходит - новая константная строка компилируется в ваше приложение, и ссылки указывают на это.

Я не вижу здесь различия. Однако для вашего собственного класса, который не является неизменным, это поведение не имеет значения, и вы должны вызвать свой конструктор.

ОБНОВЛЕНИЕ: Я был неправ! Основываясь на приведенном ниже голосовании и комментариях, я протестировал это и понял, что мое понимание ошибочно - новый String ( "Глупо" ) действительно создает новую строку, а не повторно использует существующую. Я не понимаю, почему это было бы (в чем польза?), Но код говорит громче, чем слова!

Ответ 19

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

Ну, это обычный конструктор с объектом того же типа, что и аргумент.

Ответ 20

String - один из специальных классов, в котором вы можете создать их без новой части Sring

это то же самое, что и

int x = y;

или

char c;

Ответ 21

Это основной закон, что строки в java неизменяемы и чувствительны к регистру.

Ответ 22

 String str1 = "foo"; 
 String str2 = "foo"; 

Оба str1 и str2 принадлежат к одному и тому же объекту String, "foo", b'coz Java управляет Strings в StringPool, поэтому, если новая переменная относится к одной и той же String, она не создает другого, а назначает один и тот же alerady присутствует в StringPool.

 String str1 = new String("foo"); 
 String str2 = new String("foo");

Здесь оба str1 и str2 принадлежат к различным объектам, b'coz new String() принудительно создает новый объект String.

Ответ 23

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

Строки - это сложные данные, которые просто "действуют" как примитивные данные. Строковые литералы на самом деле являются объектами, хотя мы делаем вид, что они примитивные литералы, такие как 6, 6.0, 'c', и т.д. Таким образом, строковый "литерал" "text" возвращает новый объект String со значением char[] value = {'t','e','x','t}. Поэтому вызов

new String("text"); 

на самом деле сродни вызову

new String(new String(new char[]{'t','e','x','t'}));

Надеюсь, здесь вы увидите, почему ваш учебник считает это излишним.

Для справки, вот реализация String: http://www.docjar.com/html/api/java/lang/String.java.html

Это весело читать и может вдохновить некоторых прозрение. Это также отлично подходит для начинающих, чтобы читать и пытаться понять, поскольку код демонстрирует очень профессиональный и условно-совместимый код.

Еще одна хорошая рекомендация - это учебник по Java для строк: http://docs.oracle.com/javase/tutorial/java/data/strings.html