2012-06-05 23 views
46

Si se escriben dos clases Java públicas con el mismo nombre insensible a mayúsculas y minúsculas en directorios diferentes, ambas clases no se pueden usar en el tiempo de ejecución. (Probé esto en Windows, Mac y Linux con varias versiones de HotSpot JVM. No me sorprendería que haya otras JVM donde se puedan usar simultáneamente.) Por ejemplo, si creo una clase llamada a y una llamada A como tal :Sensibilidad de mayúsculas y minúsculas de los nombres de clases Java

// lowercase/src/testcase/a.java 
package testcase; 
public class a { 
    public static String myCase() { 
     return "lower"; 
    } 
} 

// uppercase/src/testcase/A.java 
package testcase; 
public class A { 
    public static String myCase() { 
     return "upper"; 
    } 
} 

Tres proyectos de Eclipse que contienen el código anterior son available from my website.

Si trato que llamar myCase en ambas clases de este modo:

System.out.println(A.myCase()); 
System.out.println(a.myCase()); 

El typechecker tiene éxito, pero cuando ejecuto el archivo de clase generar por el código directamente encima me sale:

Exception in thread "main" java.lang.NoClassDefFoundError: testcase/A (wrong name: testcase/a)

En Java, los nombres son en general sensibles a mayúsculas y minúsculas. Algunos sistemas de archivos (por ejemplo, Windows) no distinguen entre mayúsculas y minúsculas, por lo que no me sorprende que ocurra el comportamiento anterior, pero parece que es incorrecto. Lamentablemente, las especificaciones de Java son extrañamente inexistentes sobre qué clases son visibles. El Java Language Specification (JLS), Java SE 7 Edition (Sección 6.6.1, página 166) dice:

If a class or interface type is declared public, then it may be accessed by any code, provided that the compilation unit (§7.3) in which it is declared is observable.

En la sección 7.3, el JLS define observabilidad de una unidad de compilación en términos muy vagos:

All the compilation units of the predefined package java and its subpackages lang and io are always observable. For all other packages, the host system determines which compilation units are observable.

la Java Virtual Machine Specification es igualmente vaga (sección 5.3.1):

The following steps are used to load and thereby create the nonarray class or interface C denoted by [binary name] N using the bootstrap class loader [...] Otherwise, the Java virtual machine passes the argument N to an invocation of a method on the bootstrap class loader to search for a purported representation of C in a platform-dependent manner.

Todo esto conduce a cuatro preguntas en orden descendente de importancia:

  1. ¿Hay alguna garantía sobre qué clases pueden cargar los cargadores de clases predeterminados en cada JVM? En otras palabras, ¿puedo implementar una JVM válida pero degenerada que no cargue ninguna clase excepto las de java.lang y java.io?
  2. Si hay alguna garantía, ¿el comportamiento en el ejemplo anterior infringe la garantía (es decir, el comportamiento es un error)?
  3. ¿Hay alguna manera de hacer que HotSpot cargue a y A simultáneamente? ¿Funcionaría la escritura de un cargador de clases personalizado?
+3

Así que vamos a ver si lo entiendo .. tienes dos clases con nombres similares '' testcase.a' recta y testcase.A ', en dos * diferentes * directorios en su classpath (porque no puede tenerlos en el mismo directorio en un sistema de archivos insensible a mayúsculas y minúsculas) - y se pregunta por qué la JVM no puede encontrar el archivo de clase correcto para cargar? –

+0

@GregHewgill Ha descrito correctamente el escenario al comienzo de mi pregunta, pero mis preguntas son más amplias. –

+0

Bueno, la idea de un "classpath" depende de la plataforma desde el punto de vista de JLS y JVMS. Ninguno de esos documentos lo ayudará a resolver esta pregunta. –

Respuesta

18
  • Are there any guarantees about which classes are loadable by the bootstrap class loader in every JVM?

Los pedacitos de base y piezas de la lengua contiene, junto con clases de implementación. No se garantiza que incluya ninguna clase que escriba. (La JVM normal carga tus clases en un cargador de clases separado del bootstrap uno, y de hecho el cargador de arranque normal carga sus clases fuera de un JAR normalmente, ya que esto hace una implementación más eficiente que una vieja estructura de directorios llena de clases).

  • If there are any guarantees, does the behavior in the example above violate the guarantee (i.e. is the behavior a bug)?
  • Is there any way to make "standard" JVMs load a and A simultaneously? Would writing a custom class loader work?

Java carga las clases asignando el nombre completo de la clase a un nombre de archivo que luego se busca en classpath. Por lo tanto testcase.a va a testcase/a.class y testcase.A va a testcase/A.class. Algunos sistemas de archivos mezclan estas cosas y pueden servir a la otra cuando se solicita. Otros lo hacen bien (en particular, la variante del formato ZIP utilizado en archivos JAR es completamente sensible a mayúsculas y minúsculas). No hay nada que Java pueda hacer al respecto (aunque un IDE podría manejarlo guardando los archivos .class lejos del FS nativo, no sé si alguno realmente lo hace y el JDK javac ciertamente no es tan inteligente) .

Sin embargo, ese no es el único punto a tener en cuenta aquí: los archivos de clase conocen internamente de qué clase están hablando. La ausencia de la clase esperada del archivo solo significa que la carga falla, lo que lleva al NoClassDefFoundError que recibió. Lo que obtuve fue un problema (un mal despliegue, al menos en algún sentido) que se detectó y se trató de forma robusta. Teóricamente, podría crear un cargador de clases que pudiera manejar tales cosas al seguir buscando, pero ¿por qué preocuparse? Poner los archivos de clase dentro de un JAR arreglará cosas mucho más robustamente; esos se manejan correctamente En general, si se encuentra con este problema de verdad, lleve a cabo creaciones de producción en un Unix con un sistema de archivos sensible a mayúsculas y minúsculas (se recomienda un sistema CI como Jenkins) y las clases con solo diferencias de casos y hacen que pare ya que es muy confuso.

fina explicación
+0

Re: "haz que se detengan". No puedo! Estoy escribiendo un compilador para otro idioma que compila en bytecode de Java. Los identificadores en la fuente deben coincidir con los identificadores en el destino (bytecode), y varios ejemplos importantes en el otro idioma tienen el mismo nombre insensible a mayúsculas y minúsculas. –

+0

Bueno, la forma normal de tratar con tales cosas es normalizar el caso en la traducción. –

+0

Eso es lo que hice, pero hace que la interoperabilidad entre Java y el otro idioma sea mucho más incómoda. El código de Java que se refiere a clases creadas compilando código escrito en el otro idioma tiene que usar nombres normalizados en lugar de los nombres de origen. Dicho esto, los detalles de mi proyecto están un poco al margen, estoy más interesado en entender la descripción de la especificación JVM de la carga de clase y el comportamiento particular de la JVM de HotSpot. –

-2

No piense solo en las carpetas.

Utilice espacios de nombres explícitos diferentes ("paquetes") para sus clases, y tal vez use carpetas para que coincidan con sus clases.

Cuando menciono "paquetes", no me refiero a "*" JAR archivos, pero, sólo el concepto de:

package com.mycompany.mytool; 

// "com.mycompany.mytool.MyClass" 

public class MyClass 
{ 
    // ... 
} // class MyClass 

Cuando no se especifica un paquete para su código, el java herramientas (compilador, IDE, lo que sea), suponga que utiliza el mismo paquete global para todos. Y, en el caso de varias clases similares, tienen una lista de carpetas donde buscar.

Los paquetes son como carpetas "virtuales" en su código, y se aplican a todos sus paquetes en su classpath o instalación de Java. Puede tener varias clases, con el mismo ID, pero, si están en un paquete diferente y especifica qué paquete buscar, no tendrá ningún problema.

Sólo mis 2 centavos, para su taza de café de Java

1

de Donal deja poco que añadir, pero permítanme musa brevemente en esta frase:

... Java classes with the same case-insensitive name ...

Nombres y Cuerdas en general nunca son sensibles a mayúsculas en sí mismos, es sólo allí interpretación que puede ser. Y en segundo lugar, Java no hace esa interpretación.

lo tanto, una redacción correcta de lo que tenía en mente sería:

... Java classes whose file representations in a case-insensitive file-system have identical names ...

+0

Un nombre puede no distinguir entre mayúsculas y minúsculas (y he encontrado cadenas insensibles a mayúsculas y minúsculas, hace mucho tiempo en un mainframe extraño; brrr!) Pero los nombres de Java siempre distinguen entre mayúsculas y minúsculas, y las cadenas de todos han distinguido entre mayúsculas y minúsculas ... bueno, sin duda toda mi carrera al menos. –

Cuestiones relacionadas