Я парень C ++, изучающий 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 нет концепции "конструктора копирования"?
Ответы:
String
это специальный встроенный класс языка. Это дляString
класса только , в которой вы должны избегать говоряString s = new String("Polish");
Потому что литерал
"Polish"
уже имеет типString
, а вы создаете лишний ненужный объект. Для любого другого класса, говоряCaseInsensitiveString cis = new CaseInsensitiveString("Polish");
- это правильный (и единственный в данном случае) поступок.
источник
new String("foo")
, вы можете спросить себя, почемуnew String(String)
существует конструктор . Ответ заключается в том, что иногда для этого есть хорошее применение: stackoverflow.com/a/390854/1442870Я считаю, что главное преимущество использования буквальной формы (т.е. «foo», а не new String («foo»)) заключается в том, что все строковые литералы «интернированы» виртуальной машиной. Другими словами, он добавляется в пул, так что любой другой код, создающий ту же строку, будет использовать объединенную строку, а не создавать новый экземпляр.
Чтобы проиллюстрировать, следующий код будет печатать истину для первой строки, но ложь для второй:
System.out.println("foo" == "foo"); System.out.println(new String("bar") == new String("bar"));
источник
Строки в java обрабатываются немного особым образом, они неизменяемы, поэтому их можно безопасно обрабатывать с помощью подсчета ссылок.
Если вы напишете
String s = "Polish"; String t = "Polish";
тогда s и t фактически относятся к одному и тому же объекту, а s == t вернет истину, поскольку "==" для прочитанных объектов "является одним и тем же объектом" (или, в любом случае, я не уверен, является ли это частью фактическая спецификация языка или просто деталь реализации компилятора - так что, возможно, полагаться на это небезопасно).
Если вы напишете
String s = new String("Polish"); String t = new String("Polish");
тогда s! = t (потому что вы явно создали новую строку), хотя s.equals (t) вернет true (потому что строка добавляет это поведение к equals).
То, что ты хочешь написать,
CaseInsensitiveString cis = "Polish";
не может работать, потому что вы думаете, что цитаты являются своего рода конструктором короткого замыкания для вашего объекта, хотя на самом деле это работает только для простых старых java.lang.Strings.
источник
strA = strB
вместоstrA = new String(strB)
. это действительно не имеет отношения к интернированию строк.String s1="foo";
literal войдет в пул, а s1 будет ссылаться.
String s2="foo";
на этот раз он проверит, доступен ли литерал «foo» в StringPool или нет, поскольку он существует сейчас, поэтому s2 будет ссылаться на тот же литерал.
String s3=new String("foo");
Литерал "foo" сначала будет создан в StringPool, затем будет создан строковый конструктор arg String Object, т.е. "foo" в куче из-за создания объекта с помощью оператора new, тогда 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 создаются в разных местах в куче)
источник
String
s особенные в Java - они неизменяемы, а строковые константы автоматически превращаются вString
объекты.Ваш
SomeStringClass cis = "value"
пример нельзя применить к любому другому классу.Вы также не можете расширить
String
, потому что он объявлен какfinal
, что означает, что подклассы не разрешены.источник
Строки Java интересны. Похоже, ответы затронули некоторые интересные моменты. Вот мои два цента.
строки неизменяемы (вы никогда не можете их изменить)
String x = "x"; x = "Y";
сравнение строк зависит от того, что вы сравниваете
String a1 = new String("A"); String a2 = new String("A");
a1
не равноa2
a1
иa2
являются ссылками на объектыЯ думаю, вы ошиблись, пытаясь использовать класс нечувствительности к регистру. Оставьте струны в покое. Что вас действительно волнует, так это то, как вы отображаете или сравниваете значения. Используйте другой класс для форматирования строки или для сравнения.
т.е.
TextUtility.compare(string 1, string 2) TextUtility.compareIgnoreCase(string 1, string 2) TextUtility.camelHump(string 1)
Поскольку вы составляете класс, вы можете заставить сравнения делать то, что вы хотите - сравнивать текстовые значения.
источник
Вы не можете. Объекты в двойных кавычках в Java специально распознаются компилятором как строки, и, к сожалению, вы не можете переопределить это (или расширить
java.lang.String
- это объявленоfinal
).источник
Лучший способ ответить на ваш вопрос - познакомить вас с «пулом констант строк». В java строковые объекты неизменяемы (т.е. их значения не могут быть изменены после инициализации), поэтому при редактировании строкового объекта вы в конечном итоге создаете новый отредактированный строковый объект, тогда как старый объект просто плавает в специальных областях памяти, называемых "строкой" постоянный бассейн ». создание нового строкового объекта с помощью
String s = "Hello";
создаст только строковый объект в пуле, и ссылка s будет ссылаться на него, но с использованием
String s = new String("Hello");
вы создаете два строковых объекта: один в пуле, а другой в куче. ссылка будет относиться к объекту в куче.
источник
С самого начала было сказано достаточно. «Польский» - это строковый литерал, и его нельзя присвоить классу 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'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 и верните оттуда соответствующий экземпляр.
Таким образом, оператор "==" возвращает истину для двух ссылок на объекты, представляющих одно и то же значение. .
Это полезно, когда похожие объекты используются очень часто и создание затрат обходится дорого.
В документации по строковому классу указано, что класс использует внутренний пул.
Класс не завершен, некоторые интересные проблемы возникают, когда мы пытаемся пройтись по содержимому объекта при реализации интерфейса CharSequence, но этот код достаточно хорош, чтобы показать, как этот элемент в Книге может быть применен.
Важно отметить, что при использовании объекта internalPool ссылки не освобождаются и, следовательно, не подлежат сборке мусора, и это может стать проблемой, если создается много объектов.
Он работает для класса String, потому что он интенсивно используется, а пул состоит только из «интернированных» объектов.
Это хорошо работает и для логического класса, потому что есть только два возможных значения.
И, наконец, это также причина, по которой valueOf (int) в классе Integer ограничено от -128 до 127 значений int.
источник
В вашем первом примере вы создаете строку "глупо", а затем передаете ее в качестве параметра конструктору копирования другой строки, который создает вторую строку, идентичную первой. Поскольку строки Java неизменяемы (что часто вызывает у людей, привыкших к строкам C), это ненужная трата ресурсов. Вместо этого вам следует использовать второй пример, потому что он пропускает несколько ненужных шагов.
Однако литерал String не является CaseInsensitiveString, поэтому в последнем примере вы не можете делать то, что хотите. Кроме того, нет способа перегрузить оператор приведения, как в C ++, поэтому буквально нет способа делать то, что вы хотите. Вместо этого вы должны передать его в качестве параметра конструктору вашего класса. Конечно, я бы просто использовал String.toLowerCase () и покончил с этим.
Кроме того, ваш CaseInsensitiveString должен реализовывать интерфейс CharSequence, а также, возможно, интерфейсы Serializable и Comparable. Конечно, если вы реализуете Comparable, вы также должны переопределить equals () и hashCode ().
источник
Тот факт, что у вас есть слово
String
в вашем классе, не означает, что вы получаете все специальные возможности встроенногоString
класса.источник
CaseInsensitiveString
не является,String
хотя и содержитString
.String
Буквальный , например , «пример» может быть назначен толькоString
.источник
CaseInsensitiveString и String - разные объекты. Вы не можете:
CaseInsensitiveString cis = "Polish";
потому что «Polish» - это строка, а не CaseInsensitiveString. Если String расширяет CaseInsensitiveString String, тогда все будет в порядке, но, очевидно, это не так.
И не беспокойтесь о конструкции здесь, вы не будете делать лишние предметы. Если вы посмотрите на код конструктора, все, что он делает, это сохраняет ссылку на переданную вами строку. Ничего лишнего не создается.
В случае String s = new String ("foobar") он делает что-то другое. Сначала вы создаете буквальную строку «foobar», а затем создаете ее копию, создавая из нее новую строку. Нет необходимости создавать эту копию.
источник
когда говорят писать
String s = "Silly";
вместо того
String s = new String("Silly");
они имеют в виду это при создании объекта String, потому что оба приведенных выше оператора создают объект String, но новая версия String () создает два объекта String: один в куче, а другой в пуле строковых констант. Следовательно, требуется больше памяти.
Но когда ты пишешь
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
вы не создаете String, вместо этого вы создаете объект класса CaseInsensitiveString. Следовательно, вам нужно использовать новый оператор.
источник
Если я правильно понял, ваш вопрос означает, почему мы не можем создать объект, напрямую присвоив ему значение, не ограничивая его классом Wrapper of String в java.
Чтобы ответить на этот вопрос, я бы просто сказал, что чисто объектно-ориентированные языки программирования имеют некоторые конструкции, и в нем говорится, что все литералы, написанные отдельно, могут быть напрямую преобразованы в объект данного типа.
Это точно означает, что если интерпретатор увидит 3, он будет преобразован в объект типа Integer, потому что integer - это тип, определенный для таких литералов.
Если интерпретатор видит что-либо в одинарных кавычках, например 'a', он непосредственно создает объект типа character, вам не нужно указывать его, поскольку язык определяет для него объект по умолчанию типа character.
Точно так же, если интерпретатор видит что-то в "", это будет рассматриваться как объект его типа по умолчанию, т.е. строка. Это некий собственный код, работающий в фоновом режиме.
Благодаря видеокурсу 6.00 MIT, где я получил подсказку для этого ответа.
источник
В Java синтаксис «текст» создает экземпляр класса java.lang.String. Назначение:
String foo = "text";
- простое присвоение, не требующее конструктора копирования.
MyString bar = "text";
Все, что вы делаете, является незаконным, потому что класс MyString не является ни java.lang.String, ни суперклассом java.lang.String.
источник
Во-первых, вы не можете создать класс, который расширяется от String, потому что String является последним классом. И Java управляет строками иначе, чем другие классы, поэтому только с String вы можете делать
String s = "Polish";
Но с вашим классом вы должны вызывать конструктор. Итак, этот код в порядке.
источник
Я бы просто добавил, что в Java есть конструкторы копирования ...
Ну, это обычный конструктор с объектом того же типа в качестве аргумента.
источник
В большинстве версий JDK две версии будут одинаковыми:
Строка s = новая Строка («глупо»);
String s = «Больше не глупо»;
Поскольку строки неизменяемы, компилятор поддерживает список строковых констант, и если вы попытаетесь создать новый, сначала проверит, определена ли уже строка. Если это так, то возвращается ссылка на существующую неизменяемую строку.
Чтобы уточнить - когда вы говорите «String s =», вы определяете новую переменную, которая занимает место в стеке - тогда независимо от того, говорите ли вы «Больше не глупо» или новую строку («глупо»), происходит точно то же самое - новый постоянная строка компилируется в ваше приложение, и ссылка на это указывает.
Я не вижу здесь разницы. Однако для вашего собственного класса, который не является неизменным, это поведение не имеет значения, и вы должны вызвать свой конструктор.
ОБНОВЛЕНИЕ: я ошибался! Основываясь на голосовании против и прилагаемом комментарии, я проверил это и понял, что мое понимание неверно - новая строка ("глупо") действительно создает новую строку, а не повторно использует существующую. Я не понимаю, почему это может быть (в чем польза?), Но код говорит громче, чем слова!
источник
String - это один из специальных классов, в котором вы можете создавать их без новой части Sring.
это то же самое, что
int x = y;
или
char c;
источник
Это основной закон, согласно которому строки в java неизменяемы и чувствительны к регистру.
источник
String str1 = "foo"; String str2 = "foo";
И str1, и str2 принадлежат одному и тому же объекту String, "foo", b'coz Java управляет строками в StringPool, поэтому, если новая переменная ссылается на ту же строку, она не создает другую, а назначает тот же алеради, присутствующий в StringPool .
String str1 = new String("foo"); String str2 = new String("foo");
Здесь и str1, и str2 принадлежат разным объектам, b'coz new String () принудительно создает новый объект String.
источник
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
источник