Soy un chico de C ++ que está aprendiendo Java. Estoy leyendo Effective Java y algo me confundió. Dice nunca escribir código como este:
String s = new String("silly");
Porque crea String
objetos innecesarios . Pero, en cambio, debería escribirse así:
String s = "No longer silly";
Ok bien hasta ahora ... Sin embargo, dada esta clase:
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 qué está bien la primera declaración? No debería ser
CaseInsensitiveString cis = "Polish";
¿Cómo hago para
CaseInsensitiveString
comportarmeString
así la declaración anterior está bien (con y sin extensiónString
)? ¿Qué tiene String que hace que esté bien poder pasarle un literal como ese? Según tengo entendido, ¿no existe el concepto de "constructor de copias" en Java?
Respuestas:
String
es una clase incorporada especial del idioma. Corresponde a laString
clase única en la que se debe evitar decirString s = new String("Polish");
Porque el literal
"Polish"
ya es de tipoString
y está creando un objeto adicional innecesario. Para cualquier otra clase, diciendoCaseInsensitiveString cis = new CaseInsensitiveString("Polish");
es lo correcto (y único, en este caso) que hacer.
fuente
new String("foo")
, puede preguntarse por quénew String(String)
existe el constructor . La respuesta es que a veces tiene un buen uso: stackoverflow.com/a/390854/1442870Creo que el principal beneficio de usar la forma literal (es decir, "foo" en lugar de una nueva cadena ("foo")) es que todos los literales de cadena son "internados" por la máquina virtual. En otras palabras, se agrega a un grupo de modo que cualquier otro código que cree la misma cadena utilizará el String agrupado en lugar de crear una nueva instancia.
Para ilustrar, el siguiente código imprimirá verdadero para la primera línea, pero falso para la segunda:
System.out.println("foo" == "foo"); System.out.println(new String("bar") == new String("bar"));
fuente
Las cadenas se tratan un poco especialmente en Java, son inmutables, por lo que es seguro que se manejen mediante el recuento de referencias.
Si tú escribes
String s = "Polish"; String t = "Polish";
entonces syt en realidad se refieren al mismo objeto, y s == t devolverá verdadero, porque "==" para los objetos leídos "es el mismo objeto" (o puedo, de todos modos, "no estoy seguro de si esto es parte de la especificación del lenguaje real o simplemente un detalle de la implementación del compilador, por lo que quizás no sea seguro confiar en esto)
Si tú escribes
String s = new String("Polish"); String t = new String("Polish");
entonces s! = t (porque ha creado explícitamente una nueva cadena) aunque s.equals (t) devolverá verdadero (porque la cadena agrega este comportamiento a igual).
Lo que quieres escribir
CaseInsensitiveString cis = "Polish";
no puede funcionar porque está pensando que las citas son una especie de constructor de cortocircuito para su objeto, cuando de hecho esto solo funciona para java.lang.Strings antiguos y sencillos.
fuente
strA = strB
, en lugar destrA = new String(strB)
. realmente no tiene mucho que ver con la práctica de cuerdas.String s1="foo";
literal irá al grupo y s1 hará referencia.
String s2="foo";
esta vez comprobará que el literal "foo" ya está disponible en StringPool o no, ya que ahora existe, por lo que s2 hará referencia al mismo literal.
String s3=new String("foo");
El literal "foo" se creará en StringPool primero y luego a través del constructor de string arg. Se creará el objeto String, es decir, "foo" en el montón debido a la creación del objeto a través del nuevo operador y luego s3 lo referirá.
String s4=new String("foo");
igual que s3
entonces
System.out.println(s1==s2);// **true** due to literal comparison.
y
System.out.println(s3==s4);// **false** due to object
comparación (s3 y s4 se crean en diferentes lugares del montón)
fuente
String
Los s son especiales en Java: son inmutables y las constantes de cadena se convierten automáticamente enString
objetos.No hay forma de que su
SomeStringClass cis = "value"
ejemplo se aplique a ninguna otra clase.Tampoco puede extender
String
, porque se declara comofinal
, lo que significa que no se permiten subclases.fuente
Las cadenas de Java son interesantes. Parece que las respuestas han cubierto algunos de los puntos interesantes. Aquí están mis dos centavos.
las cadenas son inmutables (nunca puedes cambiarlas)
String x = "x"; x = "Y";
las comparaciones de cadenas dependen de lo que esté comparando
String a1 = new String("A"); String a2 = new String("A");
a1
no es iguala2
a1
ya2
son referencias a objetosCreo que estás en el camino equivocado al intentar usar la clase que no distingue entre mayúsculas y minúsculas. Deja las cuerdas en paz. Lo que realmente le importa es cómo muestra o compara los valores. Utilice otra clase para formatear la cadena o para hacer comparaciones.
es decir
TextUtility.compare(string 1, string 2) TextUtility.compareIgnoreCase(string 1, string 2) TextUtility.camelHump(string 1)
Dado que está formando la clase, puede hacer que las comparaciones hagan lo que desee: comparar los valores del texto.
fuente
No puedes. Las cosas entre comillas dobles en Java son especialmente reconocidas por el compilador como cadenas, y desafortunadamente no puede anular esto (o extenderlo
java.lang.String
, está declaradofinal
).fuente
La mejor manera de responder a su pregunta sería familiarizarse con el "Grupo constante de cadenas". En Java, los objetos de cadena son inmutables (es decir, sus valores no se pueden cambiar una vez que se inicializan), por lo que al editar un objeto de cadena, terminas creando un nuevo objeto de cadena editado, por lo que el objeto antiguo simplemente flota en un área de memoria especial llamada "cadena piscina constante ". creando un nuevo objeto de cadena por
String s = "Hello";
solo creará un objeto de cadena en el grupo y las referencias se referirán a él, pero usando
String s = new String("Hello");
crea dos objetos de cadena: uno en el grupo y el otro en el montón. la referencia se referirá al objeto en el montón.
fuente
Ya se ha dicho suficiente desde el primer punto. "Polaco" es un literal de cadena y no se puede asignar a la clase CaseInsentiviveString.
Ahora sobre el segundo punto
Aunque no puede crear nuevos literales, puede seguir el primer elemento de ese libro para un enfoque "similar", de modo que las siguientes declaraciones sean verdaderas:
// Lets test the insensitiveness CaseInsensitiveString cis5 = CaseInsensitiveString.valueOf("sOmEtHiNg"); CaseInsensitiveString cis6 = CaseInsensitiveString.valueOf("SoMeThInG"); assert cis5 == cis6; assert cis5.equals(cis6);
Aquí está el 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(); }
// Prueba la clase usando la palabra clave "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>
Es decir, cree un grupo interno de objetos CaseInsensitiveString y devuelva la instancia correspondiente desde allí.
De esta manera, el operador "==" devuelve verdadero para dos referencias de objetos que representan el mismo valor .
Esto es útil cuando se utilizan objetos similares con mucha frecuencia y la creación de costes es cara.
La documentación de la clase de cadena indica que la clase usa un grupo interno
La clase no está completa, surgen algunos problemas interesantes cuando intentamos recorrer el contenido del objeto al implementar la interfaz CharSequence, pero este código es lo suficientemente bueno para mostrar cómo se podría aplicar ese elemento en el Libro.
Es importante notar que al usar el objeto internalPool , las referencias no se liberan y, por lo tanto, no se pueden recolectar como basura, y eso puede convertirse en un problema si se crean muchos objetos.
Funciona para la clase String porque se usa de forma intensiva y el grupo está constituido únicamente por objetos "internos".
También funciona bien para la clase booleana, porque solo hay dos valores posibles.
Y finalmente, esa es también la razón por la que valueOf (int) en la clase Integer está limitado a valores de -128 a 127 int.
fuente
En su primer ejemplo, está creando una Cadena, "tonta" y luego pasándola como parámetro al constructor de copia de otra Cadena, lo que crea una segunda Cadena que es idéntica a la primera. Dado que las cadenas de Java son inmutables (algo que frecuentemente irrita a las personas que están acostumbradas a las cadenas de C), esto es una pérdida innecesaria de recursos. En su lugar, debería utilizar el segundo ejemplo porque omite varios pasos innecesarios.
Sin embargo, el literal String no es CaseInsensitiveString, por lo que no puede hacer lo que quiera en su último ejemplo. Además, no hay forma de sobrecargar a un operador de conversión como puede hacerlo en C ++, por lo que literalmente no hay forma de hacer lo que quiere. En su lugar, debe pasarlo como parámetro al constructor de su clase. Por supuesto, probablemente solo usaría String.toLowerCase () y terminaría con eso.
Además, su CaseInsensitiveString debería implementar la interfaz CharSequence, así como probablemente las interfaces Serializable y Comparable. Por supuesto, si implementa Comparable, también debe anular equals () y hashCode ().
fuente
El hecho de que tenga la palabra
String
en su clase no significa que obtenga todas las características especiales de laString
clase incorporada .fuente
CaseInsensitiveString
no es unString
aunque contiene unString
. UnString
literal, por ejemplo, "ejemplo", solo se puede asignar a unString
.fuente
CaseInsensitiveString y String son objetos diferentes. No puedes hacer:
CaseInsensitiveString cis = "Polish";
porque "Polaco" es una Cadena, no una CaseInsensitiveString. Si String extendió CaseInsensitiveString String, estaría bien, pero obviamente no es así.
Y no te preocupes por la construcción aquí, no estarás haciendo objetos innecesarios. Si observa el código del constructor, todo lo que está haciendo es almacenar una referencia a la cadena que pasó. No se está creando nada adicional.
En el caso de String s = new String ("foobar"), está haciendo algo diferente. Primero está creando la cadena literal "foobar", luego crea una copia construyendo una nueva cadena a partir de ella. No es necesario crear esa copia.
fuente
cuando dicen escribir
String s = "Silly";
en vez de
String s = new String("Silly");
lo dicen en serio cuando crean un objeto String porque ambas declaraciones anteriores crean un objeto String pero la nueva versión String () crea dos objetos String: uno en el montón y el otro en el grupo constante de cadenas. Por lo tanto, usar más memoria.
Pero cuando escribes
CaseInsensitiveString cis = new CaseInsensitiveString("Polish");
no está creando una cadena, sino que está creando un objeto de la clase CaseInsensitiveString. Por lo tanto, debe utilizar el nuevo operador.
fuente
Si lo entendí correctamente, su pregunta significa por qué no podemos crear un objeto asignándole directamente un valor, no lo limitemos a una clase Wrapper of String en java.
Para responder a eso, solo diría que los lenguajes de programación puramente orientados a objetos tienen algunas construcciones y dice que todos los literales cuando se escriben solos pueden transformarse directamente en un objeto del tipo dado.
Eso significa precisamente que si el intérprete ve 3, se convertirá en un objeto Integer porque integer es el tipo definido para tales literales.
Si el intérprete ve algo entre comillas simples como 'a', creará directamente un objeto de tipo carácter, no es necesario que lo especifique ya que el idioma define el objeto predeterminado de tipo carácter para él.
De manera similar, si el intérprete ve algo en "", se considerará como un objeto de su tipo predeterminado, es decir, una cadena. Este es un código nativo que funciona en segundo plano.
Gracias al curso de conferencias en video del MIT 6.00 donde obtuve la pista para esta respuesta.
fuente
En Java, la sintaxis "texto" crea una instancia de la clase java.lang.String. La asignación:
String foo = "text";
es una tarea simple, sin necesidad de constructor de copias.
MyString bar = "text";
Es ilegal hagas lo que hagas porque la clase MyString no es java.lang.String ni una superclase de java.lang.String.
fuente
Primero, no puede crear una clase que se extienda desde String, porque String es una clase final. Y Java administra Strings de manera diferente a otras clases, por lo que solo con String puede hacerlo
String s = "Polish";
Pero con tu clase tienes que invocar al constructor. Entonces, ese código está bien.
fuente
Solo agregaría que Java tiene constructores de copia ...
Bueno, ese es un constructor ordinario con un objeto del mismo tipo que el argumento.
fuente
En la mayoría de las versiones del JDK, las dos versiones serán iguales:
String s = new String ("tonto");
String s = "Ya no es tonto";
Debido a que las cadenas son inmutables, el compilador mantiene una lista de constantes de cadenas y, si intenta crear una nueva, primero verificará si la cadena ya está definida. Si es así, se devuelve una referencia a la cadena inmutable existente.
Para aclarar, cuando dice "String s =", está definiendo una nueva variable que ocupa espacio en la pila, entonces si dice "Ya no es tonto" o nuevo String ("tonto") sucede exactamente lo mismo: un nuevo La cadena constante se compila en su aplicación y la referencia apunta a eso.
No veo la distinción aquí. Sin embargo, para su propia clase, que no es inmutable, este comportamiento es irrelevante y debe llamar a su constructor.
ACTUALIZACIÓN: ¡Me equivoqué! Basado en un voto negativo y un comentario adjunto, probé esto y me di cuenta de que mi comprensión es incorrecta: la nueva cadena ("tonto") de hecho crea una nueva cadena en lugar de reutilizar la existente. No tengo claro por qué sería esto (¿cuál es el beneficio?) ¡Pero el código habla más que las palabras!
fuente
String es una de las clases especiales en las que puede crearlos sin la nueva parte Sring
es lo mismo que
int x = y;
o
char c;
fuente
Es una ley básica que las cadenas en Java son inmutables y distinguen entre mayúsculas y minúsculas.
fuente
String str1 = "foo"; String str2 = "foo";
Tanto str1 como str2 pertenecen al mismo objeto String, "foo", b'coz Java administra las cadenas en StringPool, por lo que si una nueva variable se refiere a la misma cadena, no crea otra en lugar de asignar la misma alerady presente en StringPool .
String str1 = new String("foo"); String str2 = new String("foo");
Aquí tanto str1 como str2 pertenecen a diferentes Objetos, b'coz new String () crea forzosamente un nuevo String Object.
fuente
Java crea un objeto String para cada literal de cadena que usa en su código. Se
""
usa cualquier tiempo , es lo mismo que llamarnew String()
.Las cadenas son datos complejos que simplemente "actúan" como datos primitivos. Los literales de cadena son en realidad objetos aunque pretendamos que son literales primitivos, como,
6, 6.0, 'c',
etc. Entonces, el "literal" de"text"
cadena devuelve un nuevo objeto de cadena con valorchar[] value = {'t','e','x','t}
. Por lo tanto, llamandonew String("text");
en realidad es similar a llamar
new String(new String(new char[]{'t','e','x','t'}));
Es de esperar que desde aquí pueda ver por qué su libro de texto considera esto redundante.
Como referencia, aquí está la implementación de String: http://www.docjar.com/html/api/java/lang/String.java.html
Es una lectura divertida y puede inspirar algo de comprensión. También es excelente para que los principiantes lean e intenten comprender, ya que el código demuestra un código muy profesional y compatible con las convenciones.
Otra buena referencia es el tutorial de Java sobre cadenas: http://docs.oracle.com/javase/tutorial/java/data/strings.html
fuente