Eu estava lendo esta pergunta no SO, que discute algum comportamento indefinido comum em C ++, e me perguntei: o Java também tem um comportamento indefinido?
Se for esse o caso, quais são algumas das causas comuns de comportamento indefinido em Java?
Caso contrário, quais recursos do Java o libertam de tais comportamentos e por que as versões mais recentes do C e C ++ não foram implementadas com essas propriedades?
java
c++
c
undefined-behavior
Oito
fonte
fonte
Respostas:
Em Java, você pode considerar indefinido o comportamento do programa sincronizado incorretamente.
O Java 7 JLS usa a palavra "indefinido" uma vez, no 17.4.8. Execuções e requisitos de causalidade :
A documentação da API Java especifica alguns casos em que os resultados são indefinidos - por exemplo, no construtor (descontinuado) Date (int year, int month, int day) :
Javadocs para o estado ExecutorService.invokeAll (Collection) :
Um tipo menos formal de comportamento "indefinido" pode ser encontrado, por exemplo, em ConcurrentModificationException , em que os documentos da API usam o termo "melhor esforço":
Apêndice
Um dos comentários da pergunta refere-se a um artigo de Eric Lippert, que fornece uma introdução útil aos tópicos: comportamento definido pela implementação .
Eu recomendo este artigo pelo raciocínio independente de idioma, embora valha a pena lembrar que o autor tem como alvo C #, não Java.
Acima é apenas uma cobertura muito breve; o artigo completo contém explicações e exemplos para os pontos mencionados neste trecho; é muito vale a pena ler. Por exemplo, detalhes fornecidos para o "sexto fator" podem fornecer uma visão da motivação de muitas instruções no Java Memory Model ( JSR 133 ), ajudando a entender por que algumas otimizações são permitidas, levando a um comportamento indefinido enquanto outras são proibidas, levando a limitações como acontecer antes e requisitos de causalidade .
fonte
Em primeiro lugar, não acho que exista um comportamento indefinido em Java, pelo menos não no mesmo sentido que em C ++.
A razão para isso é que existe uma filosofia diferente por trás do Java e por trás do C ++. Um dos principais objetivos do design do Java era permitir que os programas rodassem inalterados entre as plataformas, por isso a especificação define tudo muito explicitamente.
Por outro lado, uma das principais metas de design do C e C ++ é a eficiência: não deve haver nenhum recurso (incluindo independência da plataforma) que custe o desempenho, mesmo que você não precise deles. Para esse fim, a especificação deliberadamente não define alguns comportamentos, pois defini-los causaria trabalho extra em algumas plataformas e, assim, reduziria o desempenho, mesmo para pessoas que escrevem programas especificamente para uma plataforma e conhecem todas as suas idiossincrasias.
Existe até um exemplo em que o Java foi forçado a introduzir retroativamente uma forma limitada de comportamento indefinido exatamente por esse motivo: a palavra-chave strictfp foi introduzida no Java 1.2 para permitir que os cálculos de ponto flutuante se desviem de seguir exatamente o padrão IEEE 754, conforme exigido anteriormente pelas especificações. , porque isso exigia trabalho extra e tornava todos os cálculos de ponto flutuante mais lentos em algumas CPUs comuns, enquanto produzia resultados piores em alguns casos.
fonte
int x=-1; foo(); x<<=1;
a filosofia hipermoderna favoreceria a reescrita, defoo
modo que qualquer caminho que não saia deve ser inacessível. Isso, sefoo
forif (should_launch_missiles) { launch_missiles(); exit(1); }
um compilador, poderia (e de acordo com algumas pessoas) simplificar isso de maneira simpleslaunch_missiles(); exit(1);
. O UB tradicional era a execução aleatória de código, mas isso costumava ser limitado pelas leis do tempo e da causalidade. O novo UB aprimorado não está vinculado a nenhum dos dois.Java tenta bastante exterminar comportamentos indefinidos, precisamente por causa das lições das linguagens anteriores. Por exemplo, variáveis em nível de classe são inicializadas automaticamente; as variáveis locais não são inicializadas automaticamente por motivos de desempenho, mas há uma sofisticada análise de fluxo de dados para impedir que alguém escreva um programa capaz de detectar isso. As referências não são ponteiros, portanto, referências inválidas não podem existir e a desreferenciação
null
causa uma exceção específica.Obviamente, existem alguns comportamentos que não são totalmente especificados e você pode escrever programas não confiáveis, se assumir que são. Por exemplo, se você iterar sobre um normal (não classificado)
Set
, o idioma garante que você verá cada elemento exatamente uma vez, mas não na ordem em que os verá. A ordem pode ser a mesma em execuções sucessivas ou pode mudar; ou pode permanecer o mesmo desde que nenhuma outra alocação ocorra, ou desde que você não atualize seu JDK etc. É quase impossível se livrar de todos esses efeitos; por exemplo, você precisaria ordenar ou aleatoriamente explicitamente todas as operações de coleções, e isso simplesmente não vale o pequeno adicional indefinido.fonte
Você precisa entender o "comportamento indefinido" e sua origem.
Comportamento indefinido significa um comportamento que não é definido pelos padrões. O C / C ++ possui muitas implementações diferentes de compilador e recursos adicionais. Esses recursos adicionais vincularam o código ao compilador. Isso ocorreu porque não havia desenvolvimento de linguagem centralizado. Portanto, alguns dos recursos avançados de alguns compiladores se tornaram "comportamentos indefinidos".
Enquanto em Java a especificação da linguagem é controlada pela Sun-Oracle e não há mais ninguém tentando fazer especificações e, portanto, nenhum comportamento indefinido.
Editado Respondendo especificamente à pergunta
fonte
Java elimina essencialmente todo o comportamento indefinido encontrado em C / C ++. (Por exemplo: Estouro de número inteiro assinado, divisão por zero, variáveis não inicializadas, dereferência de ponteiro nulo, deslocamento acima da largura de bit, liberação dupla, até mesmo "nenhuma nova linha no final do código-fonte".) Mas o Java tem alguns comportamentos indefinidos obscuros que raramente são encontrados pelos programadores.
Java Native Interface (JNI), uma maneira de o Java chamar código C ou C ++. Há muitas maneiras de estragar a JNI, como errar a assinatura da função, fazer chamadas inválidas para os serviços da JVM, corromper a memória, alocar / liberar coisas incorretamente e muito mais. Eu cometi esses erros antes e geralmente a JVM inteira falha quando qualquer thread que executa o código JNI comete um erro.
Thread.stop()
, que está obsoleto. Citar:https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html
fonte