2008-12-02 5 views
79

Я парень на C++, изучающий Java. Я читаю «Эффективная Java», и что-то меня смутило. Он не говорит, что никогда не писать код так:Строки Java: «Строка s = новая строка (« глупая »);

String s = new String("silly"); 

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

String s = "No longer silly"; 

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

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"; 
  1. Почему первое заявление в порядке? Не должна ли она быть

    CaseInsensitiveString cis = "Polish";

  2. Как сделать CaseInsensitiveString ведут себя как String поэтому приведенное выше утверждение в порядке (с и без расширения String)? Что это такое о String, которая позволяет ему просто передать это буквально? Насколько я понимаю, в Java нет понятия «конструктор экземпляров»?

+2

Строка str1 = "Foo"; String str2 = "foo"; Оба str1 и str2 принадлежат к одному и тому же объекту String, «foo», b'coz для Java управляет Strings в StringPool, так что новая переменная относится к той же String, она не создает другого, а назначает тот же самый алертад, присутствующий в StringPool. Но когда мы это делаем: String str1 = new String ("foo"); String str2 = new String ("foo"); Здесь str1 и str2 принадлежат к различным объектам, b'coz new String() принудительно создает новый объект String. – Akash5288 2013-12-27 08:23:46

ответ

12

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

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

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

6

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

+0

Финал - это красная сельдь. Даже если String не был окончательным, расширение String не помогло бы ему в этом случае. – Darron 2008-12-02 16:31:12

+1

Я думаю, вы имеете в виду, к счастью, вы не можете переопределить это. :) – 2008-12-02 20:15:53

+0

@Motlin: Ha! вы вполне можете быть правы. Я думаю, что я где-то читал, что Java была предназначена для того, чтобы остановить любого, кто когда-либо делал что-то глупо, преднамеренно исключая все, что было бы так интересно ... – 2008-12-02 22:45:44

106

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

String s = new String("Polish"); 

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

CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); 

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

+7

Второй момент заключается в том, что компилятор может вообразить, что переводчик-посредник со строковыми литералами, которые не обязательно возможны с таким странным functioncall, как «String (literal)» – Tetha 2009-05-12 20:46:03

+4

Поскольку вы никогда не должны называть `new String (" foo ")`, вы можете спросить себя, почему существует конструктор `new String (String)`. Ответ заключается в том, что иногда это полезно для использования: http://stackoverflow.com/a/390854/1442870 – Enwired 2012-06-22 17:17:05

+0

FYI, комментарий Тета выше с ошибкой произнес слово «интернирование», как в [string interning] (https: // en.m.wikipedia.org/wiki/String_interning). – 2015-07-01 16:51:45

2

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

CaseInsensitiveString cis = "Polish"; 

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

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

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

+0

Даже если вы расширили String, это не сработает. Вам понадобится String для расширения CaseInsensitiveString. – Darron 2008-12-02 16:24:07

+0

Ах, правильно. Думаю, мне нужно было больше кофе – Herms 2008-12-02 17:03:18

+0

в любом случае его невозможно, потому что строка построена или потому, что ее объявленный окончательный – luke 2008-12-02 20:20:31

0

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

String foo = "text"; 

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

MyString bar = "text"; 

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

0

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

String s = "Polish"; 

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

53

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

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

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

Аналогичным образом, вот почему FindBugs сообщает вам заменить «новый Integer (N)» на «Integer».valueOf (N) "- из-за этого интернирования. – 2008-12-02 16:40:54

3

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

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

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

30

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

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

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

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

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

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

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

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

CaseInsensitiveString cis = "Polish"; 

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

0

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

указатель на строку = новый String ("глупый");

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

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

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

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

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

0

Я хотел бы только добавить, что Java имеет Copy constructors ...

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

7

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

строки являются неизменными (вы не можете изменить их)

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

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

String a1 = new String("A"); 

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

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

т.е.

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

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

3

- Как заставить 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(); 
    } 

// Тестирование класса с использованием «ключевого слова» утверждают,

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's 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 экземпляр оттуда.

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

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

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

строка гласит, что класс использует an internal pool

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

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

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

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

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

0

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

это то же самое, как

Int х = у;

или

char c;

3

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

3

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

6

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

String s = "Hello"; 

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

String s = new String("Hello"); 

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

2

, когда они говорят, чтобы написать

String s = "Silly"; 

вместо

String s = new String("Silly"); 

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

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

CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); 

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

1

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

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

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

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

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

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

0

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

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

Оба str1 и str2 принадлежит тха же строкового объекта, «Foo», b'coz Java управляет строк в StringPool, поэтому, если новая переменная относится к одной и той же строки, он не создает еще один довольно назначьте тот же alerady, присутствующий в StringPool.

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

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

18
String s1="foo"; 

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

String s2="foo"; 

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

String s3=new String("foo"); 

«Foo» буквальным будет создан в StringPool первом затем через строку аргумент конструктора строковый объект будет создан I.e "foo" в куче из-за создания объекта с помощью нового оператора, тогда s3 будет ссылаться на него.

String s4=new String("foo"); 

же, как s3

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

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

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

-1

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'})); 

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

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

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

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

Смежные вопросы