Que erros devem ser evitados ao programar em Java? No artigo seguinte, respondemos a esta pergunta.
Java é uma língua popular com uma posição estabelecida no mundo da desenvolvimento de software. É uma linguagem de programação forte e versátil. Cerca de 3 mil milhões de dispositivos em todo o mundo funcionam com Java e, por conseguinte, foram cometidos pelo menos 3 mil milhões de erros ao utilizá-lo. Neste artigo, vamos concentrar-nos em como não cometer mais nenhum.
1. Obter uma exceção de modificação simultânea
Este é, de longe, o erro mais comum com que me deparei. No início da minha carreira, também o cometi muitas vezes. Este erro ocorre quando se tenta modificar a coleção enquanto se itera através dela. O ConcurrentModificationException também pode ser levantada quando se trabalha com várias threads mas, por agora, vamos concentrar-nos num cenário básico.
Suponha que tem um Coleção de utilizadores, alguns dos quais são adultos e outros não. A sua tarefa é filtrar as crianças.
para (Utilizador : utilizadores) {
se (!user.isAdult()) {
users.remove(user);
}
}
Executar o já referido código acaba por obter ConcurrentModificationException. Onde é que errámos? Antes de terminar a nossa iteração, tentámos remover alguns elementos. É isso que desencadeia a exceção.
Como é que o posso evitar?
Há algumas abordagens que podem ajudar nesse caso. Em primeiro lugar, tirar partido de Java 8's goodness - Fluxo.
List adults = users.stream()
.filter(User::isAdult)
.toList();
Utilizar um Predicado fizemos o inverso da condição anterior - agora determinamos os elementos a incluir. A vantagem desta abordagem é que é fácil encadear outras funções após a remoção, por exemplo mapa. Mas, por amor de Deus, não tentem fazer algo como o que se segue:
Também pode acabar no ConcurrentModificationException porque está a modificar a fonte do fluxo. Também pode dar origem a mais algumas excepções que não serão fáceis de depurar.
Para resolver ConcurrentModificationException num cenário de thread único. pode também passar a utilizar diretamente Iterador e a sua remover() ou pode simplesmente não remover elementos durante a iteração. No entanto, a minha recomendação é utilizar o método Fluxos - estamos em 2022.
2. Armazenamento de palavras-passe como cadeias de caracteres
Como estou cada vez mais envolvido com a cibersegurança, não estaria a ser fiel a mim próprio se não mencionasse pelo menos um Erro de Java que pode levar a um problema de segurança. Armazenar as palavras-passe recebidas dos utilizadores numa Cordas objeto é exatamente algo de que se deve ter medo.
O problema (ou talvez a vantagem) de Cordas é o facto de ser imutável. No mundo cibernético, este facto cria uma ameaça potencial, uma vez que não é possível apagar o valor de um valor criado uma vez Cordas objeto. Um atacante que tenha acesso à memória do seu computador pode encontrar aí palavras-passe em texto simples.
Em segundo lugar, as cadeias de caracteres em Java são internados pela JVM e armazenados no espaço PermGen ou no espaço heap. Quando se cria um Cordas ele é armazenado em cache e só é removido quando o Coletor de Lixo começa a fazer seu trabalho. Não pode ter a certeza de quando a sua palavra-passe é eliminada do conjunto de String, uma vez que o Coletor de Lixo funciona de forma não determinística.
Como evitá-lo?
A abordagem recomendada é utilizar char[] ou, melhor ainda, a biblioteca que suporta o armazenamento de senhas como char[], por exemploPalavra-passe4j. O char[] é mutável e pode ser modificado depois de ter sido inicializado. Depois de processar uma senha, você pode simplesmente apagar o char[] escrevendo caracteres aleatórios no array de senhas. No caso de os atacantes terem acesso à memória do computador, apenas verão alguns valores aleatórios que não têm nada a ver com as palavras-passe dos utilizadores.
3. (Des)tratamento das excepções
Os programadores novatos e também os mais avançados não sabem como lidar corretamente com as excepções. O seu principal pecado nesta matéria é simplesmente ignorá-las. NUNCA É UMA BOA ABORDAGEM.
Infelizmente, não podemos oferecer-lhe uma solução que se adapte a todas as situações. Exceçãos" com que se depara. É preciso pensar em cada caso separadamente. No entanto, podemos dar-lhe alguns conselhos sobre como começar a trabalhar nesse tema.
Como é que o posso evitar?
Ignorar Exceçãos nunca é uma boa prática. Exceçãos são introduzidos por alguma razão, pelo que não os deve ignorar.
try {...} catch(Exception e) { log(e); } raramente é a abordagem correta para Exceção manuseamento.
Arremesso Exceçãomostrar uma caixa de diálogo de erro ao utilizador ou, pelo menos, acrescentar uma mensagem exaustiva ao registo.
Se deixou as excepções por tratar (o que não deve fazer), pelo menos explique-se no comentário.
4. Utilizar o nulo
Infelizmente, é bastante comum encontrar uma função Java que, em alguns casos, retorna um nulo. O problema é que essa função obriga o seu cliente a efetuar uma verificação de nulidade no resultado. Sem isso, a função NullPointerException é lançado.
A outra coisa é passar um nulo valor. Porque é que pensou nisso? Nesse caso, a função tem de efetuar uma verificação de nulidade. Quando se utilizam bibliotecas de terceiros, não se pode alterar o interior das funções. E depois?
Mais importante ainda, outros programadores que lêem o seu código e vêem que passa nulo provavelmente ficará desorientado quanto à razão de ter escolhido uma forma tão bizarra para implementar a sua funcionalidade.
Como é que o posso evitar?
Não devolver um nulo valor! Nunca! Caso a sua função devolva algum tipo de Coleção, pode simplesmente devolver um Coleção. Se o usuário lida com objetos individuais, pode utilizar o padrão de projeto de objeto nulo. Desde que Java 8, é implementado como Opcional. Para além disso, a abordagem menos recomendada consiste em apresentar um Exceção.
5. Concatenação de cadeias de caracteres pesadas
Esperemos que não seja um erro que cometas, uma vez que é a pergunta de entrevista mais popular (ou talvez a segunda mais popular depois do FizzBuzz). Como já deves saber, uma Cordas é imutável em Java - uma vez criada, não pode ser modificada. Assim, a concatenação de Cordas significa uma grande quantidade de alocação de memória desnecessária. Concatenar Cordas requer a criação de um ficheiro temporário StringBuilder e alterando-o novamente para uma cadeia de caracteres. Por conseguinte, esta solução não é de todo adequada se quisermos combinar um grande número de caracteres.
Como é que o posso evitar?
Para resolver esse problema, utilize StringBuilder. Cria um objeto mutável que pode ser facilmente manipulado. É claro que você sempre pode usar StringBuffer se o seu projeto é utilizado num contexto concorrente.
6. Não utilizar as soluções existentes
Quando se desenvolve software, conhecer as bases da linguagem em que se escreve é uma obrigação, mas não é suficiente. Muitos dos problemas algorítmicos com que se deparou durante a implementação de uma nova funcionalidade já foram resolvidos por outra pessoa. Já vi demasiadas vezes alguém implementar um algoritmo de segurança a partir do zero. Esta abordagem é propensa a erros. Uma pessoa não pode testar exaustivamente uma solução tão complexa. O conhecimento coletivo da equipa que consiste em programadores de nível médio avançado é quase sempre melhor do que a grandeza de um prodígio Programador Java. Não é necessário reinventar a roda - basta adaptar a solução existente às suas necessidades.
Como é que o posso evitar?
Tente procurar bibliotecas que abordem o problema em que está a trabalhar. Tente encontrar soluções semelhantes. Muitas das bibliotecas que estão disponíveis na Web são gratuitas e foram aperfeiçoadas e testadas por programadores experientes e por toda a comunidade Java. Não tenha medo de as utilizar.
7. Não encontrar tempo suficiente para escrever testes
É tentador acreditar que o nosso código funcionará sempre na perfeição. Não escrever testes para o código é o pior pecado de Java programadores de software. Muitos dos nós preferem testes manuais e exploratórios em vez de testes unitários, o que é uma loucura. Porquê perder tempo a escrever testes quando se pode concentrar em fornecer o melhor código do mundo para o seu projeto, que DEFINITIVAMENTE não tem bugs?<joke>. Acontece que a realidade é brutal e não podemos fornecer código de alta qualidade sem escrever testes.
Como é que o posso evitar?
Deve sempre preparar testes para o seu código. Eu sei que a abordagem TDD não é tão fácil de manter, mas pelo menos deve fornecer testes que cubram todas as condições em que o seu código pode ser executado. Isto inclui testar situações excepcionais. Os testes unitários são necessários. Tem de os fornecer para todas as funcionalidades do seu projeto se quiser garantir que o seu código é fácil de refactorizar e extensível em futuros desenvolvimentos.
Mais uma coisa. Mantenham um elevado nível de qualidade do vosso código de teste - valerá a pena. Este é o conselho do tio Bob e eu concordo plenamente com ele.
Além disso, não se esqueça de outros tipos de testes. Os testes de integração são algo que deve considerar em todos os seus projectos.
8. Esquecer os modificadores de acesso
Privado e público, certo? Como é que nos podemos esquecer deles? Acontece que há mais. Quando começou a aprender JavaNo início do ano passado, o utilizador aprendeu definitivamente sobre os modificadores de acesso protegido. Estes podem ser úteis nalguns casos, pelo que vale a pena conhecer a sua existência.
Programadores Java muitas vezes parecem esquecer-se do âmbito do pacote. É fácil não se lembrar de o utilizar, uma vez que está implícito e não requer qualquer Java palavras-chave. O escopo do pacote é importante. Ele permite que você teste um método protegido. Os itens protegidos são acessíveis a partir do caminho da classe de teste, desde que o pacote seja o mesmo.
Como é que o posso evitar?
Lembre-se do modificador protegido e de que o âmbito do pacote lhe permite testá-lo.
9. Utilizar JavaEE puro em vez de Spring
O próximo passo depois de aprender Java SE é aprender a gerir Java em servidores, como criar uma aplicação de nível empresarial.
Os principiantes caem frequentemente na armadilha de aprender JavaEE, uma vez que existe um grande número de tutoriais sobre o assunto. Até mesmo 'Thinking in Java', o Programadores de Java', menciona JavaEE e não diz nada sobre as outras opções.
Como é que o posso evitar?
Java EE é uma canção do passado. Hoje em dia, o Spring é uma coisa que se usa e o Java EE é apenas bom de se ter. Todas as aplicações modernas de nível empresarial utilizam Spring, pelo que deve considerar fortemente a possibilidade de aprender aqui.