Java Strings: “String s = new String (” bobo “);”

Eu sou um cara de C ++ aprendendo Java. Estou lendo o Effective Java e algo me confundiu. Diz nunca escrever código como este:

String s = new String("silly"); 

Porque cria objects String desnecessários. Mas em vez disso, deve ser escrito assim:

 String s = "No longer silly"; 

Ok bem até agora … No entanto, dada esta class:

 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. Por que a primeira afirmação está ok? Não deveria ser

    CaseInsensitiveString cis = "Polish";

  2. Como faço para que CaseInsensitiveString se comporte como String modo que a declaração acima seja OK (com e sem extensão de String )? O que é sobre String que faz com que seja apenas capaz de passar um literal assim? Pelo que entendi, não há conceito de “construtor de cópias” em Java?

String é uma class interna especial da linguagem. É para a class String apenas em que você deve evitar dizer

 String s = new String("Polish"); 

Porque o literal "Polish" já é do tipo String , e você está criando um object extra desnecessário. Para qualquer outra class, dizendo

 CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); 

é a coisa correta (e apenas, neste caso) a ser feita.

Acredito que o principal benefício de usar a forma literal (isto é, “foo” em vez de new String (“foo”)) é que todos os literais String são “internados” pela VM. Em outras palavras, ele é adicionado a um conjunto de forma que qualquer outro código que crie a mesma sequência usará a String agrupada em vez de criar uma nova instância.

Para ilustrar, o código a seguir será exibido como verdadeiro para a primeira linha, mas falso para o segundo:

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

Strings são tratadas um pouco especialmente em java, elas são imutáveis, então é seguro para elas serem manipuladas por contagem de referência.

Se você escreve

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

então s e t realmente se referem ao mesmo object, e s == t retornará verdadeiro, pois “==” para objects lidos “é o mesmo object” (ou pode, de qualquer forma, eu não tenho certeza se isso é parte de a especificação do idioma atual ou simplesmente um detalhe da implementação do compilador – então talvez não seja seguro confiar nisto).

Se você escreve

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

então s! = t (porque você criou explicitamente uma nova string) embora s.equals (t) retorne true (porque string adiciona esse comportamento a equals).

A coisa que você quer escrever

 CaseInsensitiveString cis = "Polish"; 

não pode funcionar porque você está pensando que as citações são algum tipo de construtor de curto-circuito para o seu object, quando na verdade isso só funciona para java.lang.Strings.

 String s1="foo"; 

literal irá no pool e s1 irá se referir.

 String s2="foo"; 

desta vez ele irá verificar se o literal “foo” já está disponível em StringPool ou não como agora existe, então s2 irá se referir ao mesmo literal.

 String s3=new String("foo"); 

O literal “foo” será criado primeiro no StringPool e então através da string arg o construtor String Object será criado, isto é, “foo” na pilha devido à criação do object através do novo operador, então s3 irá referenciá-lo.

 String s4=new String("foo"); 

o mesmo que s3

então, System.out.println(s1==s2);// **true** due to literal comparison.

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

comparação (s3 e s4 é criado em diferentes locais no heap)

String s são especiais em Java – elas são imutáveis ​​e as constantes de strings são transformadas automaticamente em objects String .

Não há como o seu exemplo de SomeStringClass cis = "value" se aplicar a qualquer outra class.

Nem você pode estender o String , porque ele é declarado como final , o que significa que nenhuma subclass é permitida.

As strings de Java são interessantes. Parece que as respostas cobriram alguns dos pontos interessantes. Aqui estão meus dois centavos.

strings são imutáveis ​​(você nunca pode mudá-las)

 String x = "x"; x = "Y"; 
  • A primeira linha irá criar uma variável x que conterá o valor da string “x”. A JVM procurará em seu conjunto de valores de string e verá se “x” existe, se isso acontecer, apontará a variável x para ela, se ela não existir, irá criá-la e então fará a atribuição
  • A segunda linha removerá a referência a “x” e verá se “Y” existe no pool de valores de string. Se existir, ele será atribuído, caso contrário, será criado primeiro e depois atribuído. Como os valores de string são usados ​​ou não, o espaço de memory no pool de valores de string será recuperado.

comparações de cordas são contingentes sobre o que você está comparando

 String a1 = new String("A"); String a2 = new String("A"); 
  • a1 não é igual a2
  • a1 e a2 são referências de objects
  • Quando string é declarada explicitamente, novas instâncias são criadas e suas referências não serão as mesmas.

Eu acho que você está no caminho errado ao tentar usar a class caseinsensitive. Deixe as cordas sozinhas. O que você realmente gosta é como você exibe ou compara os valores. Use outra class para formatar a string ou fazer comparações.

ou seja

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

Desde que você está fazendo a class, você pode fazer as comparações fazer o que quiser – comparar os valores de texto.

Você não pode. Coisas em aspas duplas em Java são especialmente reconhecidas pelo compilador como Strings, e infelizmente você não pode sobrescrever isto (ou estender java.lang.String – é declarado final ).

A melhor maneira de responder à sua pergunta seria familiarizá-lo com o “pool constante de strings”. Em java string objects são imutáveis ​​(ou seja, seus valores não podem ser alterados assim que são inicializados), então ao editar um object string você acaba criando um novo object string editado onde o object antigo simplesmente flutua em uma memory especial chamada de “string piscina constante “. criando um novo object string por

 String s = "Hello"; 

criará apenas um object de string no pool e os s de referência se referirão a ele, mas usando

 String s = new String("Hello"); 

você cria dois objects string: um no pool e outro no heap. a referência se referirá ao object no heap.

Faço como CaseInsensitiveString se comportar como String para a instrução acima está ok (com e sem extender String)? O que é sobre String que faz com que seja apenas capaz de passar um literal assim? Pelo que entendi, não há conceito de “construtor de cópias” em Java, certo?

Já foi dito o suficiente desde o primeiro ponto. “Polonês” é uma string literal e não pode ser atribuído à class CaseInsentiviveString.

Agora sobre o segundo ponto

Embora não seja possível criar novos literais, você pode seguir o primeiro item desse livro para uma abordagem “semelhante”, de modo que as seguintes afirmações sejam verdadeiras:

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

Aqui está o código.

 C:\oreyes\samples\java\insensitive>type CaseInsensitiveString.java import java.util.Map; import java.util.HashMap; public final class CaseInsensitiveString { private static final Map innerPool = new HashMap(); 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(); } 

// Testar a class usando a palavra-chave “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> 

Ou seja, crie um pool interno de objects CaseInsensitiveString e retorne a instância corrensponding de lá.

Dessa forma, o operador “==” retorna true para referências de dois objects representando o mesmo valor .

Isso é útil quando objects semelhantes são usados ​​com muita frequência e a criação de custo é cara.

A documentação da class de string indica que a class usa um pool interno

A class não está completa, alguns problemas interessantes surgem quando tentamos percorrer o conteúdo do object ao implementar a interface CharSequence, mas esse código é bom o suficiente para mostrar como esse item no Book poderia ser aplicado.

É importante notar que, usando o object internalPool , as referências não são liberadas e, portanto, não são coletas de lixo, e isso pode se tornar um problema se muitos objects forem criados.

Ele funciona para a class String porque é usado intensivamente e o pool é constituído apenas de objects “internados”.

Também funciona bem para a class booleana, porque existem apenas dois valores possíveis.

E finalmente essa também é a razão pela qual valueOf (int) na class Integer está limitado a -128 a 127 int valores.

Em seu primeiro exemplo, você está criando um String, “bobo” e, em seguida, passando-o como um parâmetro para o construtor de cópia de outro String, que faz um segundo String que é idêntico ao primeiro. Como as strings Java são imutáveis ​​(algo que frequentemente ataca pessoas acostumadas com strings C), isso é um desnecessário desperdício de resources. Em vez disso, você deve usar o segundo exemplo porque ele ignora várias etapas desnecessárias.

No entanto, o literal String não é um CaseInsensitiveString, portanto, você não pode fazer o que deseja em seu último exemplo. Além disso, não há como sobrecarregar um operador de casting como você pode em C ++, portanto, não há literalmente nenhuma maneira de fazer o que você quer. Você deve, em vez disso, passá-lo como um parâmetro para o construtor de sua class. Claro, eu provavelmente usaria String.toLowerCase () e terminaria com isso.

Além disso, seu CaseInsensitiveString deve implementar a interface CharSequence, bem como provavelmente as interfaces Serializable e Comparable. Claro, se você implementar Comparable, você deve replace equals () e hashCode () também.

Só porque você tem a palavra String em sua class, não significa que você tenha todos os resources especiais da class String incorporada.

CaseInsensitiveString não é uma String embora contenha uma String . Um literal String por exemplo, “exemplo”, pode ser atribuído apenas a uma String .

CaseInsensitiveString e String são objects diferentes. Você não pode fazer

 CaseInsensitiveString cis = "Polish"; 

porque “polonês” é uma seqüência de caracteres, não um CaseInsensitiveString. Se String estendida CaseInsensitiveString String, então você estaria OK, mas obviamente isso não acontece.

E não se preocupe com a construção aqui, você não estará fazendo objects desnecessários. Se você olhar o código do construtor, tudo o que ele está fazendo é armazenar uma referência à string que você passou. Nada extra está sendo criado.

No caso String s = new String (“foobar”) está fazendo algo diferente. Você está criando primeiro a string literal “foobar”, então criando uma cópia dela construindo uma nova string a partir dela. Não há necessidade de criar essa cópia.

quando eles dizem para escrever

 String s = "Silly"; 

ao invés de

 String s = new String("Silly"); 

eles significam isso ao criar um object String porque ambas as instruções acima criam um object String, mas a nova versão String () cria dois objects String: um na pilha e o outro no conjunto constante da cadeia. Por isso, usando mais memory.

Mas quando você escreve

 CaseInsensitiveString cis = new CaseInsensitiveString("Polish"); 

você não está criando uma String, em vez disso, está criando um object da class CaseInsensitiveString. Daí você precisa usar o novo operador.

Se eu entendi corretamente, sua pergunta significa por que não podemos criar um object diretamente atribuindo-lhe um valor, não vamos restringi-lo a uma class Wrapper of String em java.

Para responder que eu diria apenas, as linguagens puramente de programação orientada a objects têm algumas construções e dizem que todos os literais, quando escritos isoladamente, podem ser transformados diretamente em um object do tipo dado.

Isso significa precisamente que, se o interpretador vir 3, ele será convertido em um object Integer, porque integer é o tipo definido para tais literais.

Se o interpretador vir qualquer coisa entre aspas simples, como ‘a’, ele criará diretamente um object do tipo character, não é necessário especificá-lo, pois a linguagem define o object padrão do tipo character para ele.

Da mesma forma, se o intérprete vir algo em “”, ele será considerado como um object de seu tipo padrão, isto é, string. Isso é algum código nativo trabalhando em segundo plano.

Graças ao curso de palestras em vídeo do MIT, 6.00, recebi a dica para essa resposta.

Em Java, a syntax “text” cria uma instância da class java.lang.String. A atribuição:

 String foo = "text"; 

é uma atribuição simples, sem o construtor de cópia necessário.

 MyString bar = "text"; 

É ilegal o que você faz porque a class MyString não é java.lang.String ou uma superclass de java.lang.String.

Primeiro, você não pode criar uma class que se estenda desde String, porque String é uma class final. E o java gerencia Strings diferentemente de outras classs, então apenas com String você pode fazer

 String s = "Polish"; 

Mas com a sua class você tem que invocar o construtor. Então, esse código está bem.

Gostaria apenas de acrescentar que o Java tem construtores Copy …

Bem, isso é um construtor comum com um object do mesmo tipo que o argumento.

Na maioria das versões do JDK, as duas versões serão as mesmas:

String s = new String (“bobo”);

String s = “Não é mais bobo”;

Como as strings são imutáveis, o compilador mantém uma lista de constantes de strings e, se você tentar criar uma nova, primeiro verificará se a string já está definida. Se for, então, uma referência à string imutável existente será retornada.

Para esclarecer – quando você diz “String s =” você está definindo uma nova variável que ocupa espaço na pilha – então se você diz “Não é mais bobo” ou nova String (“bobo”) exatamente a mesma coisa acontece – um novo string constante é compilada em seu aplicativo e a referência aponta para isso.

Eu não vejo a distinção aqui. No entanto, para sua própria class, que não é imutável, esse comportamento é irrelevante e você deve chamar seu construtor.

ATUALIZAÇÃO: Eu estava errado! Baseado em um voto baixo e um comentário em anexo, eu testei isso e percebi que meu entendimento está errado – new String (“Silly”) realmente cria uma nova string em vez de reutilizar a existente. Não estou claro por que isso seria (qual é o benefício?), Mas o código fala mais alto que as palavras!

String é uma das classs especiais em que você pode criá-las sem a nova parte do Sring

é o mesmo que

int x = y;

ou

char c;

É uma lei básica que o Strings em java é imutável e diferencia maiúsculas de minúsculas.

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

Tanto str1 quanto str2 pertencem ao mesmo object String, “foo”, b’coz Java gerencia Strings em StringPool, portanto, se uma nova variável se referir à mesma String, ela não cria outra, em vez de atribuir a mesma alerady presente em StringPool .

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

Aqui, str1 e str2 pertencem a objects diferentes, e o novo String () cria com força um novo String Object.

Java cria um object String para cada literal de string que você usa em seu código. A qualquer momento "" é usado, é o mesmo que chamar new String() .

Strings são dados complexos que apenas “agem” como dados primitivos. Os literais de string são na verdade objects, embora finjamos que sejam literais primitivos, como 6, 6.0, 'c', etc. Portanto, o "text" literal” String retorna um novo object String com o valor char[] value = {'t','e','x','t} . Portanto, chamando

 new String("text"); 

é na verdade semelhante a chamar

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

Espero que daqui, você possa ver porque seu livro considera isso redundante.

Para referência, aqui está a implementação de String: http://www.docjar.com/html/api/java/lang/String.java.html

É uma leitura divertida e pode inspirar algum insight. Também é ótimo para iniciantes ler e tentar entender, já que o código demonstra um código muito profissional e compatível com a convenção.

Outra boa referência é o tutorial Java em Strings: http://docs.oracle.com/javase/tutorial/java/data/strings.html