Crítica de Java - Criticism of Java

A linguagem de programação Java e a plataforma de software Java foram criticadas por escolhas de design, incluindo a implementação de genéricos, programação orientada a objetos forçada, o tratamento de números sem sinal, a implementação de aritmética de ponto flutuante e um histórico de vulnerabilidades de segurança no Java primário Implementação de VM, HotSpot . O software escrito em Java, especialmente em suas primeiras versões, foi criticado por seu desempenho em comparação com o software escrito em outras linguagens de programação. Os desenvolvedores também observaram que as diferenças em várias implementações Java devem ser levadas em consideração ao escrever programas Java complexos que devem funcionar com todos eles.

Sintaxe e semântica da linguagem

Genéricos

Quando os genéricos foram adicionados ao Java 5.0, já havia uma grande estrutura de classes (muitas das quais já estavam obsoletas ), então os genéricos foram implementados usando o apagamento de tipo para permitir a compatibilidade de migração e a reutilização dessas classes existentes. Isso limitou os recursos que poderiam ser fornecidos, em comparação com outros idiomas.

Como os genéricos são implementados usando apagamento de tipo, o tipo real de um parâmetro de modelo E não está disponível em tempo de execução. Portanto, as seguintes operações não são possíveis em Java:

public class MyClass<E> {
    public static void myMethod(Object item) {
        if (item instanceof E) {  //Compiler error
            ...
        }
        E item2 = new E();   //Compiler error
        E[] iArray = new E[10]; //Compiler error
    }
}

Orientação substantiva

Por design, Java incentiva os programadores a pensar em uma solução em termos de substantivos (classes) interagindo uns com os outros e a pensar em verbos (métodos) como operações que podem ser executadas em ou por esse substantivo. Steve Yegge argumenta que isso causa uma restrição desnecessária na expressividade da linguagem porque uma classe pode ter várias funções que operam nela, mas uma função está vinculada a uma classe e nunca pode operar em vários tipos.

Muitas outras linguagens de multiparadigma suportam funções como uma construção de nível superior. Quando combinado com outros recursos, como sobrecarga de função (um verbo, vários substantivos) e funções genéricas (um verbo, uma família de substantivos com certas propriedades), o programador pode decidir se resolve um problema específico em termos de substantivos ou verbos. Java versão 8 introduziu alguns recursos de programação funcional.

Relação oculta entre código e hardware

Em 2008, o Centro de Suporte de Tecnologia de Software do Departamento de Defesa dos Estados Unidos publicou um artigo no "Journal of Defense Software Engineering" discutindo a inadequação do Java como a primeira linguagem ensinada. As desvantagens eram que os alunos "não tinham noção da relação entre o programa de origem e o que o hardware realmente faria" e a impossibilidade "de desenvolver uma noção do custo de tempo de execução do que está escrito porque é extremamente difícil saber o que a chamada do método acabará por ser executada ". Em 2005, Joel Spolsky criticou Java como uma parte excessivamente focada dos currículos das universidades em seu ensaio The Perils of JavaSchools . Outros, como Ned Batchelder, discordam de Spolsky por criticar as partes da linguagem que ele achou difíceis de entender, alegando que o comentário de Spolsky era mais um 'discurso subjetivo'.

Tipos inteiros sem sinal

Java carece de tipos inteiros não assinados nativos . Os dados não assinados geralmente são gerados a partir de programas escritos em C , e a falta desses tipos impede o intercâmbio direto de dados entre C e Java. Números grandes sem sinal também são usados ​​em vários campos de processamento numérico, incluindo criptografia, o que pode tornar o uso do Java mais inconveniente para essas tarefas. Embora seja possível contornar esse problema usando código de conversão e tipos de dados maiores, torna o uso de Java complicado para lidar com dados não assinados. Enquanto um inteiro assinado de 32 bits pode ser usado para manter um valor sem sinal de 16 bits sem perda, e um inteiro não assinado de 64 bits um inteiro não assinado de 32 bits, não há nenhum tipo maior para manter um inteiro não assinado de 64 bits. Em todos os casos, a memória consumida pode dobrar e, normalmente, qualquer lógica baseada no estouro do complemento de dois deve ser reescrita. Se abstraídas, as chamadas de função tornam-se necessárias para muitas operações que são nativas para algumas outras linguagens. Como alternativa, é possível usar inteiros assinados de Java para emular inteiros não assinados do mesmo tamanho, mas isso requer conhecimento detalhado de operações bit a bit . Algum suporte para tipos inteiros não assinados foi fornecido no JDK 8, mas não para bytes não assinados e sem suporte na linguagem Java.

Sobrecarga do operador

Java foi criticado por não oferecer suporte a operadores definidos pelo usuário. A sobrecarga do operador melhora a legibilidade, portanto, sua ausência pode tornar o código Java menos legível, especialmente para classes que representam objetos matemáticos, como números complexos e matrizes. Java tem apenas um uso não numérico de um operador: +para concatenação de string. Mas isso é implementado pelo compilador, que gera código para criar instâncias de StringBuilder - é impossível criar sobrecargas de operador definidas pelo usuário.

Tipos de valores compostos

Java carece de tipos de valores compostos, como structs em C, pacotes de dados que são manipulados diretamente em vez de indiretamente por meio de referências. Os tipos de valor às vezes podem ser mais rápidos e menores do que as classes com referências. Por exemplo, o Java HashMapé implementado como uma matriz de referências a HashMap.Entryobjetos, que por sua vez contêm referências a objetos de chave e valor. Pesquisar algo requer desreferenciamento duplo ineficiente. Se Entryfosse um tipo de valor, o array poderia armazenar pares de valores-chave diretamente, eliminando o primeiro engano, aumentando a localidade de referência e reduzindo o uso de memória e fragmentação de heap . Além disso, se o Java suportasse tipos primitivos genéricos, as chaves e os valores poderiam ser armazenados na matriz diretamente, removendo ambos os níveis de indireção.

Matrizes grandes

Java foi criticado por não suportar matrizes de 2 31 (cerca de 2,1 bilhões) ou mais elementos. Esta é uma limitação da linguagem; a Especificação da linguagem Java , Seção 10.4, afirma que:

As matrizes devem ser indexadas por valores int ... Uma tentativa de acessar um componente da matriz com um valor de índice longo resulta em um erro de tempo de compilação.

O suporte a matrizes grandes também exigiria mudanças na JVM. Essa limitação se manifesta em áreas como coleções limitadas a 2 bilhões de elementos e a incapacidade de mapear na memória segmentos de arquivo contínuos maiores que 2 GB. Java também carece de matrizes multidimensionais (blocos únicos de memória alocados de forma contígua acessados ​​por um único caminho indireto), o que limita o desempenho para computação científica e técnica.

Não há uma maneira eficiente de inicializar matrizes em Java. Ao declarar um array, o JVM o compila em bytecodes com instruções que configuram seus elementos um por um no tempo de execução. Como os métodos Java não podem ser maiores que 64 KB, os arrays de tamanhos modestos com valores atribuídos diretamente no código exibirão a mensagem "Erro: código muito grande" na compilação.

Integração de primitivas e matrizes

Arrays e primitivos são um tanto especiais e precisam ser tratados de maneira diferente das classes. Isso foi criticado porque requer muitas variantes de funções ao criar bibliotecas de uso geral.

Paralelismo

Per Brinch Hansen argumentou em 1999 que a implementação do Java do paralelismo em geral, e dos monitores em particular, não fornece as garantias e reforços necessários para uma programação paralela segura e confiável. Enquanto um programador pode estabelecer convenções de design e codificação , o compilador não pode fazer nenhuma tentativa de aplicá-las, então o programador pode escrever inadvertidamente código inseguro ou não confiável.

Serialização

Java fornece um mecanismo chamado serialização de objetos, onde um objeto pode ser representado como uma sequência de bytes que inclui seus campos de dados, junto com informações de tipo sobre si mesmo e seus campos. Depois que um objeto é serializado, ele pode ser desserializado posteriormente; ou seja, as informações de tipo e bytes que representam seus dados podem ser usados ​​para recriar o objeto na memória. Isso levanta riscos de segurança reais e teóricos muito sérios.

Aritmética de vírgula flutuante

Embora a aritmética de ponto flutuante do Java seja amplamente baseada no IEEE 754 ( Padrão para Aritmética de Ponto Flutuante Binário ), alguns recursos padrão obrigatórios não são suportados, mesmo ao usar o strictfpmodificador, como Sinalizadores de Exceção e Arredondamentos Direcionados. Os tipos de precisão estendidos definidos pelo IEEE 754 (e suportados por muitos processadores) não são suportados pelo Java.

atuação

Antes de 2000, quando o HotSpot VM foi implementado em Java 1.3, havia muitas críticas ao seu desempenho. Foi demonstrado que o Java é executado em uma velocidade comparável ao código nativo otimizado, e as implementações modernas de JVM são regularmente comparadas como uma das plataformas de linguagem mais rápidas disponíveis - normalmente não mais do que três vezes mais lento que C e C ++.

O desempenho melhorou substancialmente desde as primeiras versões. O desempenho dos compiladores JIT em relação aos compiladores nativos mostrou ser bastante semelhante em alguns testes otimizados.

O bytecode Java pode ser interpretado em tempo de execução por uma máquina virtual ou compilado em tempo de carregamento ou tempo de execução em código nativo que é executado diretamente no hardware do computador. A interpretação é mais lenta do que a execução nativa, mas a compilação no tempo de carregamento ou tempo de execução tem uma penalidade de desempenho inicial. Todas as implementações JVM modernas usam a abordagem de compilação, portanto, após o tempo de inicialização inicial, o desempenho é semelhante ao código nativo.

O designer de jogos e programador John D. Carmack concluiu em 2005 sobre Java em telefones celulares : "O maior problema é que Java é realmente lento. Em um nível puro de CPU / memória / display / comunicação, a maioria dos telefones celulares modernos deve ser consideravelmente melhor para jogos plataformas do que um Game Boy Advance. Com o Java, na maioria dos telefones você fica com a potência da CPU de um IBM PC original de 4,77 mhz (sic) e um péssimo controle sobre tudo. "

Segurança

A plataforma Java fornece uma arquitetura de segurança projetada para permitir que o usuário execute bytecode não confiável em uma "área restrita" para proteger contra software malicioso ou mal escrito. Esse recurso de "sandboxing" destina-se a proteger o usuário, restringindo o acesso aos recursos da plataforma e APIs que podem ser explorados por malware , como acesso ao sistema de arquivos local ou rede, ou execução de comandos arbitrários.

Em 2010, houve um aumento significativo de softwares mal-intencionados visando falhas de segurança nos mecanismos de área restrita usados ​​por implementações Java, incluindo Oracle. Essas falhas permitem que códigos não confiáveis ​​contornem as restrições da sandbox, expondo o usuário a ataques. As falhas foram corrigidas por atualizações de segurança, mas ainda eram exploradas em máquinas sem as atualizações.

Os críticos sugeriram que os usuários não atualizem suas instalações Java porque não sabem que as possuem ou como atualizá-las. Muitas organizações restringem a instalação de software pelos usuários, mas são lentas para implantar atualizações.

A Oracle foi criticada por não fornecer prontamente atualizações para bugs de segurança conhecidos. Quando a Oracle finalmente lançou um patch para falhas amplamente exploradas no Java 7, ele removeu o Java 6 das máquinas dos usuários, apesar de ser amplamente utilizado por aplicativos corporativos que a Oracle afirmou não terem sido afetados pelas falhas.

Em 2007, uma equipe de pesquisa liderada por Marco Pistoia expôs outra falha importante do modelo de segurança Java, com base na inspeção de pilha . Quando um recurso sensível à segurança é acessado, o gerenciador de segurança aciona o código que percorre a pilha de chamadas, para verificar se a base de código de cada método possui autoridade para acessar o recurso. Isso é feito para evitar ataques confusos de deputados , que ocorrem sempre que um programa legítimo e mais privilegiado é enganado por outro para fazer mau uso de sua autoridade. O problema do confused deputy é um tipo específico de escalonamento de privilégios . Pistoia observou que quando um recurso sensível à segurança é acessado, o código responsável por adquirir o recurso pode não estar mais na pilha. Por exemplo, um método executado no passado pode ter modificado o valor de um campo de objeto que determina qual recurso usar. Essa chamada de método pode não estar mais na pilha quando for inspecionada.

Algumas permissões são implicitamente equivalentes às do Java AllPermission. Isso inclui a permissão para alterar o gerenciador de segurança atual (e substituí-lo por um que poderia potencialmente ignorar a inspeção de pilha), a permissão para instanciar e usar um carregador de classe personalizado (que pode escolher se associar AllPermissiona uma classe maliciosa ao carregá-lo), e a permissão para criar uma permissão personalizada (que pode se declarar tão poderosa quanto AllPermissionpor meio de seu impliesmétodo). Esses problemas estão documentados nos dois livros de Pistoia sobre Java Security: Java 2 Network Security (Second Edition) e Enterprise Java Security .

Instalações paralelas

Antes do Java 7, era normal que o instalador não detectasse ou remova instalações Java mais antigas. Era bastante comum em um computador Windows ver várias instalações do Java 6 no mesmo computador, variando apenas por pequenas revisões. Várias instalações são permitidas e podem ser usadas por programas que dependem de versões específicas.

Isso tem o efeito de que novas instalações Java podem fornecer novos recursos de linguagem e correções de bugs, mas não corrigem vulnerabilidades de segurança, porque programas maliciosos podem usar as versões mais antigas.

O Java 7 atualizou versões anteriores de si mesmo, mas não o Java 6 ou anterior.

Atualizações automáticas

A partir de 2014, ferramentas comuns de terceiros (como Adobe Flash e Adobe Reader) foram sujeitas a análises de vulnerabilidades de segurança. A Adobe e outros passaram a fazer atualizações automáticas no Windows. Eles não precisam de nenhuma ação do usuário e garantem que os problemas de segurança sejam prontamente resolvidos com o mínimo de esforço pelos usuários ou administradores.

Em 2015, o Java 8 ainda requer que os próprios usuários atualizem o Java. Mas no Windows apenas aqueles com privilégios de administrador podem atualizar o software. O atualizador do Windows Java frequentemente dispara um prompt de elevação do controle de conta do usuário perturbador: independentemente do que os usuários escolherem, eles ainda receberão a mesma mensagem "Java precisa ser atualizado".

Veja também

Notas

links externos