Sou um cara C ++ aprendendo Java. Estou lendo Effective Java e algo me confundiu. Diz para nunca escrever código como este:
String s = new String("silly");
Porque cria String
objetos desnecessários . Mas, em vez disso, deve ser escrito assim:
String s = "No longer silly";
Tudo bem até agora ... No entanto, dada esta aula:
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";
Por que a primeira afirmação está correta? Não deveria ser
CaseInsensitiveString cis = "Polish";
Como faço para me
CaseInsensitiveString
comportar deString
forma que a declaração acima esteja OK (com e sem extensãoString
)? O que há em String que torna OK apenas ser capaz de passar um literal como esse? Do meu entendimento, não existe um conceito de "construtor de cópia" em Java?
Respostas:
String
é uma classe interna especial da linguagem. É apenas para aString
aula em que você deve evitar dizerString s = new String("Polish");
Porque o literal
"Polish"
já é do tipoString
e você está criando um objeto extra desnecessário. Para qualquer outra classe, dizendoCaseInsensitiveString cis = new CaseInsensitiveString("Polish");
é a coisa correta (e única, neste caso) a fazer.
fonte
new String("foo")
, você pode se perguntar por que o construtornew String(String)
existe. A resposta é que às vezes há um bom uso para ele: stackoverflow.com/a/390854/1442870Acredito que o principal benefício de usar a forma literal (ou seja, "foo" em vez de nova String ("foo")) é que todos os literais String são 'internados' pela VM. Em outras palavras, ele é adicionado a um pool de forma que qualquer outro código que crie a mesma string usará a String agrupada em vez de criar uma nova instância.
Para ilustrar, o código a seguir imprimirá verdadeiro para a primeira linha, mas falso para a segunda:
System.out.println("foo" == "foo"); System.out.println(new String("bar") == new String("bar"));
fonte
Strings são tratados um pouco especialmente em java, eles são imutáveis, então é seguro para eles serem tratados por contagem de referência.
Se você escrever
String s = "Polish"; String t = "Polish";
então s e t realmente se referem ao mesmo objeto, e s == t retornará verdadeiro, pois "==" para objetos lidos "é o mesmo objeto" (ou pode, de qualquer maneira, não tenho certeza se isso faz parte de a especificação real da linguagem ou simplesmente um detalhe da implementação do compilador - então talvez não seja seguro confiar nisso).
Se você escrever
String s = new String("Polish"); String t = new String("Polish");
then s! = t (porque você criou explicitamente uma nova string), embora s.equals (t) retornará true (porque string adiciona esse comportamento a igual).
O 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 seu objeto, quando na verdade isso só funciona para java.lang.Strings simples e antigo.
fonte
strA = strB
, em vez destrA = new String(strB)
. realmente não tem a ver muito com o estágio de string.String s1="foo";
literal irá para o 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 ele existe, então s2 irá se referir ao mesmo literal.
String s3=new String("foo");
O literal "foo" será criado em StringPool primeiro e, em seguida, por meio do construtor string arg String O objeto será criado, ou seja, "foo" no heap devido à criação do objeto por meio de um novo operador, então s3 irá referenciá-lo.
String s4=new String("foo");
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 são criados em locais diferentes no heap)
fonte
String
s são especiais em Java - eles são imutáveis e constantes de string são automaticamente transformadas emString
objetos.Não há como seu
SomeStringClass cis = "value"
exemplo se aplicar a qualquer outra classe.Nem você pode estender
String
, porque é declarado comofinal
, o que significa que nenhuma subclasse é permitida.fonte
As strings 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";
as comparações de strings dependem do que você está comparando
String a1 = new String("A"); String a2 = new String("A");
a1
não é iguala2
a1
ea2
são referências de objetoAcho que você está no caminho errado ao tentar usar a classe que não diferencia maiúsculas de minúsculas. Deixe as cordas em paz. O que realmente importa é como você exibe ou compara os valores. Use outra classe para formatar a string ou para fazer comparações.
ie
TextUtility.compare(string 1, string 2) TextUtility.compareIgnoreCase(string 1, string 2) TextUtility.camelHump(string 1)
Como você está criando a classe, pode fazer as comparações fazerem o que quiser - comparar os valores de texto.
fonte
Você não pode. Coisas entre aspas duplas em Java são especialmente reconhecidas pelo compilador como Strings e, infelizmente, você não pode sobrescrever isso (ou estender
java.lang.String
- é declaradofinal
).fonte
A melhor maneira de responder à sua pergunta seria familiarizá-lo com o "pool de constantes de string". Em java string, os objetos são imutáveis (ou seja, seus valores não podem ser alterados depois de inicializados), então, ao editar um objeto string, você acaba criando um novo objeto string editado, enquanto o objeto antigo apenas flutua em uma área de memória especial chamada de "string piscina constante ". criando um novo objeto string por
String s = "Hello";
irá apenas criar um objeto string no pool e as referências irão se referir a ele, mas usando
String s = new String("Hello");
você cria dois objetos de string: um no pool e outro no heap. a referência se referirá ao objeto no heap.
fonte
Já foi dito o suficiente desde o primeiro ponto. "Polish" é um literal de string e não pode ser atribuído à classe CaseInsentiviveString.
Agora sobre o segundo ponto
Embora você não possa 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<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(); }
// Teste a classe 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 objetos CaseInsensitiveString e retorne a instância correspondente a partir daí.
Desta forma, o operador "==" retorna verdadeiro para referências de dois objetos que representam o mesmo valor .
Isso é útil quando objetos semelhantes são usados com muita frequência e a criação de custos é cara.
A documentação da classe string afirma que a classe usa um pool interno
A aula não está completa, alguns problemas interessantes surgem quando tentamos percorrer o conteúdo do objeto na implementação da interface CharSequence, mas este código é bom o suficiente para mostrar como aquele item do livro pode ser aplicado.
É importante notar que usando o objeto internalPool , as referências não são liberadas e, portanto, não podem ser coletadas como lixo, e isso pode se tornar um problema se muitos objetos forem criados.
Funciona para a classe String porque é usada intensamente e o pool é constituído apenas de objeto "internado".
Também funciona bem para a classe Booleana, porque existem apenas dois valores possíveis.
E, finalmente, essa também é a razão pela qual valueOf (int) na classe Integer está limitado a -128 a 127 valores int.
fonte
Em seu primeiro exemplo, você está criando uma String, "boba" e, em seguida, passando-a como um parâmetro para outro construtor de cópia de String, o que faz uma segunda String idêntica à primeira. Uma vez que Java Strings são imutáveis (algo que freqüentemente incomoda pessoas que estão acostumadas com strings C), isso é um desperdício de recursos desnecessário. Em vez disso, você deve usar o segundo exemplo porque ele pula várias etapas desnecessárias.
No entanto, o literal de 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 conversão como você faz em C ++, portanto, literalmente, não há como fazer o que você deseja. Em vez disso, você deve passá-lo como um parâmetro para o construtor de sua classe. Claro, eu provavelmente apenas usaria String.toLowerCase () e acabaria com isso.
Além disso, seu CaseInsensitiveString deve implementar a interface CharSequence, bem como provavelmente as interfaces Serializable e Comparable. Obviamente, se você implementar Comparable, deverá substituir equals () e hashCode () também.
fonte
Só porque você tem a palavra
String
em sua classe, não significa que você obtém todos os recursos especiais daString
classe integrada .fonte
CaseInsensitiveString
não é um,String
embora contenha umString
. UmString
literal, por exemplo "exemplo", só pode ser atribuído a umString
.fonte
CaseInsensitiveString e String são objetos diferentes. Você não pode fazer:
CaseInsensitiveString cis = "Polish";
porque "Polish" é uma String, não uma CaseInsensitiveString. Se String extendesse CaseInsensitiveString String, então você estaria bem, mas obviamente não está.
E não se preocupe com a construção aqui, você não estará fazendo objetos desnecessários. Se você olhar o código do construtor, tudo o que ele faz é armazenar uma referência à string que você transmitiu. Nada extra está sendo criado.
No caso de String s = new String ("foobar"), ele está fazendo algo diferente. Você está criando primeiro a string literal "foobar" e, em seguida, criando uma cópia dela, construindo uma nova string a partir dela. Não há necessidade de criar essa cópia.
fonte
quando eles dizem para escrever
String s = "Silly";
ao invés de
String s = new String("Silly");
eles querem dizer isso ao criar um objeto String porque ambas as instruções acima criam um objeto String, mas a nova versão String () cria dois objetos String: um no heap e outro no conjunto de constantes de string. Portanto, usando mais memória.
Mas quando você escreve
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
você não está criando uma String, em vez disso, está criando um objeto da classe CaseInsensitiveString. Portanto, você precisa usar o novo operador.
fonte
Se entendi corretamente, sua pergunta significa por que não podemos criar um objeto atribuindo-lhe um valor diretamente, não vamos restringi-lo a uma classe Wrapper of String em java.
Para responder a isso, eu apenas diria que as linguagens de Programação Orientada a Objetos puramente têm algumas construções e dizem que todos os literais quando escritos sozinhos podem ser transformados diretamente em um objeto do tipo dado.
Isso significa precisamente que, se o interpretador vir 3, ele será convertido em um objeto Integer, pois integer é o tipo definido para tais literais.
Se o interpretador vir qualquer coisa entre aspas simples como 'a', ele criará diretamente um objeto do tipo caractere, você não precisa especificá-lo, pois o idioma define o objeto padrão do tipo caractere para ele.
Da mesma forma, se o interpretador vir algo em "", ele será considerado um objeto de seu tipo padrão, ou seja, string. Este é um código nativo trabalhando em segundo plano.
Agradeço ao curso de vídeo-aula 6.00 do MIT, onde obtive a dica para essa resposta.
fonte
Em Java, a sintaxe "text" cria uma instância da classe java.lang.String. A atribuição:
String foo = "text";
é uma atribuição simples, sem a necessidade de um construtor de cópia.
MyString bar = "text";
É ilegal o que quer que você faça porque a classe MyString não é java.lang.String ou uma superclasse de java.lang.String.
fonte
Primeiro, você não pode fazer uma classe que se estenda de String, porque String é uma classe final. E o java gerencia Strings de maneira diferente de outras classes, então apenas com String você pode fazer
String s = "Polish";
Mas com sua classe, você deve invocar o construtor. Então, esse código está bom.
fonte
Gostaria apenas de acrescentar que o Java tem construtores de cópia ...
Bem, esse é um construtor comum com um objeto do mesmo tipo que o argumento.
fonte
Na maioria das versões do JDK, as duas versões serão iguais:
String s = nova String ("bobo");
String s = "Não é mais bobo";
Como as strings são imutáveis, o compilador mantém uma lista de constantes de string e, se você tentar fazer 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ê disser "Não é mais bobo" ou uma nova String ("bobo") exatamente a mesma coisa acontece - um novo string constante é compilada em seu aplicativo e os pontos de referência para isso.
Eu não vejo a diferença aqui. No entanto, para sua própria classe, que não é imutável, esse comportamento é irrelevante e você deve chamar seu construtor.
ATUALIZAÇÃO: eu estava errado! Com base em uma votação negativa e um comentário anexado, testei isso e percebi que meu entendimento está errado - nova String ("Silly") de fato 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 do que as palavras!
fonte
String é uma das classes especiais em que você pode criá-los sem a nova parte Sring
é o mesmo que
int x = y;
ou
char c;
fonte
É uma lei básica que Strings em java são imutáveis e diferenciam maiúsculas de minúsculas.
fonte
String str1 = "foo"; String str2 = "foo";
Ambas str1 e str2 pertencem ao mesmo objeto String, "foo", b'coz Java gerencia Strings em StringPool, então se uma nova variável se refere à mesma String, ela não cria outra, em vez de atribuir a mesma alerta presente em StringPool .
String str1 = new String("foo"); String str2 = new String("foo");
Aqui str1 e str2 pertencem a objetos diferentes, b'coz new String () cria à força um novo objeto String.
fonte
Java cria um objeto String para cada literal de string que você usa em seu código. Qualquer tempo
""
é usado, é o mesmo que chamarnew String()
.Strings são dados complexos que apenas "agem" como dados primitivos. Literais de string são na verdade objetos, embora finjamos que são literais primitivos, como
6, 6.0, 'c',
etc. Portanto, o "literal" de String"text"
retorna um novo objeto String com valorchar[] value = {'t','e','x','t}
. Portanto, chamandonew String("text");
é realmente semelhante a ligar
new String(new String(new char[]{'t','e','x','t'}));
Esperançosamente, a partir daqui, você pode ver por que 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 alguns insights. Também é ótimo para iniciantes ler e tentar entender, pois o código demonstra um código muito profissional e compatível com as convenções.
Outra boa referência é o tutorial Java sobre Strings: http://docs.oracle.com/javase/tutorial/java/data/strings.html
fonte