2009-07-22 23 views
27

Siempre me he preguntado por qué la JVM no te dice qué puntero (o más precisamente, qué variable) es nulo cuando se arroja un NullPointerException.¿Por qué Java no te dice qué puntero es nulo?

Un número de línea no es lo suficientemente específico porque la línea ofensiva a menudo puede contener numerosas variables que podrían haber causado el error.

¿Hay algún indicador de compilación o JVM que haga que estos mensajes de excepción sean más útiles?

+9

Esto realmente nunca ha sido un problema para mí en más de 8 años de ser un desarrollador profesional de Java. Si tiene muchas referencias que pueden ser nulas en una línea, puede ser hora de dividirla en varias líneas. – MattC

+6

@mattc, ¿nunca trabajas con el código de biblioteca de otras personas? –

+0

@MattC: Suele aparecer cuando necesita llamar a una función con varios argumentos en una condición en el medio de un bloque largo if-else-if. Descomponerlo en múltiples líneas significaría subir y declarar un grupo de variables ficticias antes de la parte superior del 'si'. Diría que en la mayoría de los casos esto sería más problemático en términos de legibilidad y estilo del código. – kpozin

Respuesta

49

Es porque la desreferencia siempre ocurre cuando no hay un nombre disponible. El valor se carga en la pila de operandos y luego se pasa a uno de los códigos de operación JRE que lo desreferencia. Sin embargo, la pila de operandos no tiene un nombre para asociar con un valor nulo. Todo lo que tiene es 'nulo'. Con algún código de seguimiento inteligente en tiempo de ejecución, se puede derivar un nombre, pero eso agregaría sobrecarga con un valor limitado.

Debido a esto, no hay una opción JRE que active la información adicional para las excepciones de puntero nulo.

En este ejemplo, la referencia se almacena en la ranura local 1, que se asigna a un nombre de variable local.Pero el desreferenciar sucede en la instrucción invokevirtual, que sólo ve un valor 'nulo' en la pila, y luego lanza una excepción:

15 aload_1 
16 invokevirtual #5 

igualmente válida sería una carga del array seguido por una falta de referencia, pero en este caso no hay un nombre para asignar al valor 'nulo', solo un índice fuera de otro valor.

76 aload 5 
78 iconst_0 
79 aaload 
80 invokevirtual #5 

No se puede asignar los nombres de forma estática a cada instrucción, ya sea - este ejemplo produce una gran cantidad de código de bytes, pero se puede ver que la instrucción desreferenciar recibirá ya sea objA o objB, y que tendría que realizar un seguimiento de este dinámicamente reportar el más adecuado, ya que ambas variables de flujo a la misma instrucción eliminar la referencia:

(myflag ? objA : objB).toString() 
+6

+1 Para obtener una explicación real en lugar de "si escribe código" de la manera correcta, "esto no le sucede a usted". – Zarkonnen

+2

JVM debería poder decirnos qué instrucción causó NPE, y creo que todavía es tremendamente útil, por ejemplo, si una expresión "a.b.c.d.e.f" resulta en NPE. –

12

Una vez que JET el código, es sólo matemáticas de puntero nativo, y si cualquier puntero en el código nativo es nulo arroja la excepción. Tendría un impacto devastador en el rendimiento para revertir ese ensamblaje a la variable original, y considerando que el JIT optimiza el código generado a niveles variables, a menudo ni siquiera es posible.

+0

Limpié mi representante nuevamente justo antes de esta publicación. LOL: o –

+2

@Michael: el JIT rastrea los números de línea durante la generación de código nativo. No realiza un seguimiento a una resolución mayor que esa (sub expresiones, etc.). –

+0

@Michael: Disculpe, estaba hablando desde el CLI JIT: consulte la descripción del miembro "Predeterminado" aquí: http://msdn.microsoft.com/en-us/library/system.diagnostics.debuggableattribute.debuggingmodes.aspx –

0

Si divide la línea en varias líneas en lugar de hacer varias llamadas a métodos en una línea, o si establece un punto de interrupción en esa línea y pasa la línea con un depurador, puede averiguar qué referencia es nula con bastante facilidad .

0

Si

un número de línea no es lo suficientemente específica debido a que la línea en cuestión puede menudo contienen numerosas variables que podrían han causado el error.

entonces sugiero:

  1. Romper esa línea en más de una línea y asigne los valores Generando posible NullPointerException a variables temporales.
  2. Use un depurador y pase por cada llamada a método hasta que encuentre el que causa el problema.
0

Desafortunadamente, esta es la forma en que funciona Java.

Si esta es "su" código a continuación, sólo tiene que añadir fragmentos como

if (foo == null) { 
    throw new NullPointerException("foo == null"); 
} 

justo después de la asignación de foo. Si foo es un parámetro, compruebe inmediatamente al comienzo del cuerpo del método y, en su lugar, ejecute IllegalArgumentException.

Esto debería ayudarlo a aclarar las cosas.

+2

Creo que las declaraciones 'assert' serían preferibles a las declaraciones' if' aquí. – kpozin

+0

Las afirmaciones están deshabilitadas por defecto. No está bien. –

+2

Asesinaría a cualquiera que entrara en una revisión de código con un código como este. – duffymo

0

se puede añadir un punto de interrupción en la excepción de puntero nulo en Eclipse cuando se depura para obtener la causa exacta de la excepción.

Cuestiones relacionadas