Estaba leyendo esta pregunta en SO, que analiza algunos comportamientos indefinidos comunes en C ++, y me preguntaba: ¿Java también tiene un comportamiento indefinido?
Si ese es el caso, ¿cuáles son algunas causas comunes de comportamiento indefinido en Java?
Si no, ¿qué características de Java lo liberan de tales comportamientos y por qué no se han implementado las últimas versiones de C y C ++ con estas propiedades?
java
c++
c
undefined-behavior
Ocho
fuente
fuente
Respuestas:
En Java, puede considerar el comportamiento del programa sincronizado incorrectamente indefinido.
Java 7 JLS usa la palabra "indefinido" una vez, en 17.4.8. Ejecuciones y requisitos de causalidad :
La documentación de la API de Java especifica algunos casos en que los resultados no están definidos, por ejemplo, en la fecha del constructor (en desuso) (int año, int mes, int día) :
Javadocs para el estado ExecutorService.invokeAll (Collection) :
Se puede encontrar un tipo menos formal de comportamiento "indefinido", por ejemplo, en ConcurrentModificationException , donde los documentos API utilizan el término "mejor esfuerzo":
Apéndice
Uno de los comentarios de las preguntas se refiere a un artículo de Eric Lippert que proporciona una introducción útil a los temas: comportamiento definido por la implementación .
Recomiendo este artículo para el razonamiento independiente del lenguaje, aunque vale la pena tener en cuenta que el autor se dirige a C #, no a Java.
Lo anterior es solo una cobertura muy breve; El artículo completo contiene explicaciones y ejemplos de los puntos mencionados en este extracto; es mucho vale la pena leer. Por ejemplo, los detalles dados para el "sexto factor" pueden dar una idea de la motivación para muchas declaraciones en el Modelo de memoria Java ( JSR 133 ), lo que ayuda a comprender por qué se permiten algunas optimizaciones, lo que lleva a un comportamiento indefinido mientras que otras están prohibidas, lo que lleva a limitaciones como suceder antes y requisitos de causalidad .
fuente
Fuera de mi cabeza, no creo que haya ningún comportamiento indefinido en Java, al menos no en el mismo sentido que en C ++.
La razón de esto es que hay una filosofía diferente detrás de Java que detrás de C ++. Un objetivo de diseño central de Java era permitir que los programas se ejecuten sin cambios en las plataformas, por lo que la especificación define todo de manera muy explícita.
En contraste, un objetivo central de diseño de C y C ++ es la eficiencia: no debe haber ninguna característica (incluida la independencia de la plataforma) que cueste el rendimiento, incluso si no las necesita. Con este fin, la especificación deliberadamente no define algunos comportamientos porque definirlos causaría un trabajo adicional en algunas plataformas y, por lo tanto, reduciría el rendimiento incluso para las personas que escriben programas específicamente para una plataforma y conocen todas sus idiosincrasias.
Incluso hay un ejemplo en el que Java se vio obligado a introducir retroactivamente una forma limitada de comportamiento indefinido por exactamente esa razón: la palabra clave estricta fp se introdujo en Java 1.2 para permitir que los cálculos de coma flotante se desviaran de seguir exactamente el estándar IEEE 754 como la especificación había exigido previamente , porque hacerlo requería trabajo adicional e hizo que todos los cálculos de punto flotante fueran más lentos en algunas CPU comunes, mientras que en realidad producía peores resultados en algunos casos.
fuente
int x=-1; foo(); x<<=1;
la filosofía hipermoderna favorecería la reescriturafoo
para que cualquier camino que no salga sea inalcanzable. Esto, si sefoo
trata deif (should_launch_missiles) { launch_missiles(); exit(1); }
un compilador, podría (y según algunas personas debería) simplificar eso simplementelaunch_missiles(); exit(1);
. El UB tradicional era la ejecución de código aleatorio, pero eso solía estar sujeto a las leyes del tiempo y la causalidad. Nueva UB mejorada no está vinculada por ninguno de los dos.Java se esfuerza bastante por exterminar el comportamiento indefinido, precisamente debido a las lecciones de los lenguajes anteriores. Por ejemplo, las variables de nivel de clase se inicializan automáticamente; Las variables locales no se inicializan automáticamente por razones de rendimiento, pero hay un sofisticado análisis de flujo de datos para evitar que alguien escriba un programa que pueda detectar esto. Las referencias no son punteros, por lo que no pueden existir referencias no válidas, y la desreferenciación
null
causa una excepción específica.Por supuesto, quedan algunos comportamientos que no están completamente especificados, y puede escribir programas poco confiables si supone que sí. Por ejemplo, si itera sobre un valor normal (no ordenado)
Set
, el lenguaje garantiza que verá cada elemento exactamente una vez, pero no en qué orden los verá. El orden puede ser el mismo en ejecuciones sucesivas o puede cambiar; o puede permanecer igual mientras no ocurran otras asignaciones, o mientras no actualice su JDK, etc. Es casi imposible deshacerse de todos esos efectos; por ejemplo, tendrías que ordenar o aleatorizar explícitamente todas las operaciones de Colecciones, y eso simplemente no vale la pena la pequeña indefinición adicional.fuente
Tienes que entender el "Comportamiento indefinido" y su origen.
Comportamiento indefinido significa un comportamiento que no está definido por los estándares. C / C ++ tiene demasiadas implementaciones de compilador diferentes y características adicionales. Estas características adicionales vinculaban el código al compilador. Esto se debió a que no había un desarrollo centralizado del lenguaje. Por lo tanto, algunas de las funciones avanzadas de algunos de los compiladores se convirtieron en "comportamientos indefinidos".
Mientras que en Java la especificación del lenguaje está controlada por Sun-Oracle y nadie más está tratando de hacer especificaciones y, por lo tanto, no hay comportamientos indefinidos.
Editado específicamente respondiendo la pregunta
fuente
Java elimina esencialmente todo el comportamiento indefinido que se encuentra en C / C ++. (Por ejemplo: desbordamiento de entero firmado, división por cero, variables no inicializadas, desreferencia de puntero nulo, desplazamiento de más de ancho de bit, doble libre, incluso "sin nueva línea al final del código fuente".) Pero Java tiene algunos comportamientos oscuros indefinidos que rara vez se encuentran con los programadores.
Java Native Interface (JNI), una forma para que Java llame a código C o C ++. Hay muchas formas de fastidiar en JNI, como hacer que la firma de la función sea incorrecta, realizar llamadas no válidas a los servicios de JVM, corromper la memoria, asignar / liberar cosas incorrectamente y más. He cometido estos errores antes y, en general, toda la JVM se bloquea cuando un subproceso que ejecuta código JNI comete un error.
Thread.stop()
, que está en desuso. Citar:https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
fuente