the specification language docs java jvm jls
disponibles en mi sitio web

java - specification - Sensibilidad a mayúsculas y minús



java documentation (3)

Si uno escribe dos clases Java públicas con el mismo nombre insensible a mayúsculas y minúsculas en directorios diferentes, entonces ambas clases no se pueden usar en 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 en las que puedan utilizarse simultáneamente). Por ejemplo, si creo una clase llamada a y una llamada A como tal. :

// 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 están disponibles en mi sitio web .

Si intento llamar a myCase en ambas clases, así:

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

El typechecker tiene éxito, pero cuando ejecuto el archivo de clase generado por el código directamente arriba obtengo:

Excepción en el hilo "principal" java.lang.NoClassDefFoundError: testcase / A (nombre incorrecto: 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 incorrecto . Lamentablemente, las especificaciones de Java son extrañamente inexistentes sobre qué clases son visibles. La Especificación del lenguaje Java (JLS), Java SE 7 Edition (Sección 6.6.1, página 166) dice:

Si una clase o tipo de interfaz se declara pública, se puede acceder mediante cualquier código, siempre que la unidad de compilación (§7.3) en la que se declara sea observable.

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

Todas las unidades de compilación del paquete predefinido Java y sus subpaquetes lang y yo son siempre observables. Para todos los demás paquetes, el sistema host determina qué unidades de compilación son observables .

La especificación de la máquina virtual Java es similarmente vaga (sección 5.3.1):

Los siguientes pasos se utilizan para cargar y crear la clase o la interfaz nonarray C indicada por [nombre binario] N utilizando el cargador de clases bootstrap [...] De lo contrario, la máquina virtual Java pasa el argumento N a una invocación de un método en el cargador de clases bootstrap para buscar una supuesta representación de C de una manera dependiente de la plataforma.

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

  1. ¿Hay alguna garantía sobre qué clases son cargables por los cargadores de clases por defecto 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 garantías, ¿el comportamiento en el ejemplo anterior viola 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?

  • ¿Hay alguna garantía sobre qué clases puede cargar el cargador de clases de arranque en cada JVM?

Las partes principales del lenguaje, más las clases de implementación de apoyo. 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).

  • Si hay garantías, ¿el comportamiento en el ejemplo anterior viola la garantía (es decir, el comportamiento es un error)?
  • ¿Hay alguna manera de hacer que las JVM "estándar" carguen A y A simultáneamente? ¿Funcionaría la escritura de un cargador de clases personalizado?

Java carga las clases asignando el nombre completo de la clase a un nombre de archivo que luego se busca en classpath. Así 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 manteniendo los archivos .class lejos del FS nativo, no sé si alguno realmente lo hace y el javac del JDK 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 construir un cargador de clases que pudiera manejar tales cosas al seguir buscando, pero ¿para qué molestarse? Poner los archivos de clase dentro de un JAR arreglará las cosas mucho más sólidamente; esos se manejan correctamente

De manera más general, si te encuentras con este problema de verdad, lleva a hacer compilaciones de producción en Unix con un sistema de archivos sensible a las mayúsculas y minúsculas (se recomienda un sistema de CI como Jenkins) y busca los desarrolladores que nombran clases con solo diferencias de casos y hazlos parar ya que es muy confuso!


La buena explicación de Donal deja poco que añadir, pero permítanme reflexionar brevemente sobre esta frase:

... clases de Java con el mismo nombre que no distingue entre mayúsculas y minúsculas ...

Los nombres y las cadenas en general nunca son insensibles a mayúsculas y minúsculas, es solo la interpretación lo que puede ser. Y en segundo lugar, Java no hace esa interpretación.

Entonces, una frase correcta de lo que tenía en mente sería:

... clases de Java cuyas representaciones de archivos en un sistema de archivos insensible a mayúsculas / minúsculas tienen nombres idénticos ...


No pienses solo sobre carpetas.

Use 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 los archivos "* .JAR", sino simplemente al concepto de:

package com.mycompany.mytool; // "com.mycompany.mytool.MyClass" public class MyClass { // ... } // class MyClass

Cuando no especifica un paquete para su código, las herramientas de Java (compilador, IDE, lo que sea), asumen que usan 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 la misma ID, pero, si están en paquetes diferentes, y especifica qué paquete buscar, no tendrá ningún problema.

Solo mis 2 centavos, por tu taza de café Java