×
Namespaces

Variants
Actions

Tratamento de Exceções em Java

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata

Artigo
Tradução:
Por thiagobrunoms
Última alteração feita por hamishwillee em 31 Jul 2013

Neste artigo é discutido o uso de tratamento de exceções em Java. Regras gerais para a aplicação de tratamento de exceções são apresentadas de forma clara e concisa. Adicionalmente, fragmentos de código são usados para ilustrar as regras.

Contents

Por que utilizar Exceções?

Por que a linguagem Java possui os blocos "exceptions" e "try ... catch"? Existem diversas linguagens de programação que sobrevivem sem eles. C, por exemplo.

Programas em C ainda têm de lidar com exceções. Pelo menos, eles têm de lidar com situações excepcionais, como erros durante a abertura de um arquivo ou de uma conexão de rede. O que a linguagem C não possui, especificamente, é a manipulação de exceção estruturada.

Você se lembra de programação estruturada. Pascal, essas coisas. Talvez algum professor universitário já mencionou este tipo de paradigma. Talvez tenha soado como algo que você faz na universidade, e que os programadores reais não fazem (como ter suas mãos na posição "10-2" enquanto você dirige um carro). Talvez tenha soado como algo que tem sido substituída por orientação a objetos.

Acontece que a programação estruturada é realmente muito boa. Orientação a Objetos não o substitui, ela foi desenvolvida com base no paradigma estruturado. Técnicas de programação estruturada tem como objetivo reduzir o tempo de desenvolvimento, reduzir custos de desenvolvimento, e aumentar a legibilidade e facilidade de manutenção.

No entanto ... quando se trata de situações excepcionais, a programação estruturada pode realmente não ser suficiente.

Programação estruturada, lembre-se, significa que temos de trabalhar em blocos, e não pode dar saltos arbitrários de um lugar para outro. Nós não podemos usar expressões como "goto" (OK, ele não existe em Java, mas temos em C e C ++), ou "break e "continue", e não podemos usar o "return" em qualquer lugar que não seja como a última linha do método.

Em qualquer pedaço de código C, onde você tem que adquirir vários recursos (alocação de memória, abertura de arquivos, conexões com bancos de dados, etc), código de escrita estruturada significa geralmente envolver o código em múltiplas declarações "if" aninhadas.

Programadores C, portanto, de forma sensata, não fazem o código-fonte ilegível com todo esse lixo. Eles escrevem o código não-estruturados. No entanto, uma desvantagem para isso é que, muitas vezes, os recursos não são limpos adequadamente em situações excepcionais, levando a problemas como "memory leaks" (partes da memória que não são devolvidas para o SO e nem utilizadas pela própria aplicação).

A Manipulação de exceção estruturada (SEH) nos permite manipular exceções de maneira mais elegante, de uma forma relativamente estruturada, sem ter que escrever muitos códigos aninhados.

O que não fazer com as Exceções

Muitos programadores veem as exceções como "erros", algo a ser removido de qualquer forma, o mais rápido possível. Apesar da linguagem Java obrigar a lidar com certas classes de exceção, muitos códigos como o demonstrado abaixo podem ser vistos:

public boolean saveData(byte[] data) {
    try {
        RecordStore rs = RecordStore.openRecordStore(RS_NAME, false);
        rs.addRecord(data, 0, data.length);
        rs.closeRecordStore();
    } catch (RecordStoreException e) {
        return false;
    }
    return true;
}

Ótimo, a exceção é tratada! Ah, mas agora não sabemos se o arquivo de registro não pôde ser aberto, ou se a inclusão do registro falhou. E se a inclusão do registro falhou, então deixamos o armazém de registros aberto. Ok...

public boolean saveData(byte[] data) {
    RecordStore rs = null;
    try {
        rs = RecordStore.openRecordStore(RS_NAME, false);
        rs.addRecord(data, 0, data.length);
    } catch (RecordStoreException e) {
        return false;
    } finally {
        if (rs != null) {
            try {
                rs.closeRecordStore();
            } catch (RecordStoreException e) {
                // ignore
            }
        }
    }
    return true;
}

Que confusão!!!

Além disso, o código que chama esse método possui como resposta apenas verdadeira ou falsa (sucesso ou falha) como valor de retorno, o que significa que quem chama deve tratar com declarações "if". E assim, estamos de volta aos tempos antes do SEH, só que agora com o código ainda mais confuso.

Regra 1: Exceções não são "erros". São avisos de que alguma coisa errada ocorreu. Avisos estes que podem ser reutilizados. Capturar este aviso no local mais apropriado possível.

Em vez de retornar um valor booleano, nós precisamos apenas permitir que o aviso da exceção seja anunciada a quem chama para informar sobre o erro.

public void saveData(byte[] data) throws RecordStoreException {
    RecordStore rs = RecordStore.openRecordStore(RS_NAME, false);
    try {
        rs.addRecord(data, 0, data.length);
    } finally {
        rs.closeRecordStore();
    }
}

Este código é agora mais simples e mais legível. É seguro, e sempre limpa as variáveis depois que finaliza. Em vez de retornar um booleano, ou ele gera uma exceção, ou não.

Quem invoca o método pode usar SEH para lidar com os problemas, se quiser. Ou, pode deixar que eles se propaguem para onde eles podem ser melhor manipulados.

(Handling, neste caso provavelmente significa relatando um problema para o usuário.)

Quem chama também passa a ter mais informações. Um booleano só pode informar um sinal do tipo "está OK", ou "não está OK". Uma exceção é um objeto, com uma classe que faz parte de uma hierarquia de classes. Por exemplo, o código pode ver uma RecordStoreException, e saber que havia um problema no armazém de registros. Ou, ele pode olhar mais longe, e diferenciar entre um RecordStoreFullException ou um RecordStoreNotFoundException.

Quando aceitar para não lidar com o erro

As vezes pode ser necessário deixar o código catch{} sem um tratamento formal. Por exemplo:

    try {
        Thread.sleep(someTime);
    } catch (InterruptedException e) {
        // never thrown in CLDC-1.0
    }

Não deixe a estrutura catch{} completamente vazia. Coloque um comentário informando a razão dela não estar sendo tratada. Existe alguma razão para isso! Não uma desculpa!

Tome cuidado com códigos como este:

private Image background;


    try {
        background = Image.createImage("/background.png");
    } catch (IOException e) {
        background = null;
    }

Referenciar variáveis com "null" apenas se você realmente precisa tê-la como "null"

Se referenciar variáveis como "null" (apenas para fazer algo na estrutura catch{}), você está correndo o risco de ter um "NullPointerException" mais tarde. Tratar exceções erradas no local errado apenas irá deixar mais difícil de identificar o problema.

Se você pretende referenciar a variável como nula, como um marcador que esta compilação não contém uma imagem de fundo (talvez é uma compilação de um dispositivo com heap limitada, ou uma restrição do tamanho do arquivo JAR), e você tem o código em outro lugar como:

    if (background != null) {
        g.drawImage(background, 0, 0, Graphics.TOP|Graphics.CENTER);
    } else {
        // Esta compilação não tem nenhuma imagem de fundo 
        g.setColor(BACKGROUND_COLOR);
        g.fillRect(0, 0, screenWidth, screenHeight);
    }

então cuidado e fique em alerta para escrever códigos mais flexíveis.

Use Exceptions

/**
 * @return false se não poder ser interrompido
 */
public boolean stop() {
    if (!started) {
        return false;
    }
    started = false;
    return true;
}

Quando outros programadores utilizam seu código, permita que eles utilizem SEH.

Regra 2: Não utilize valores de retorno para informar a falha ou o sucesso.

Valores de retorno em métodos podem ser utilizados para:

  • Funções: Métodos que calculam algum valor baseando-se em alguns parâmetros.
  • Métodos "Get": Métodos que retornam informações sobre o estado de um objeto.


Valores de retorno que correspondem a erros são normalmente ignorados, e valores ignorados são inúteis.

/**
 * @throws IllegalStateException if not started
 */
public void stop() throws IllegalStateException {
    if (!started) {
        throw new IllegalStateException("cannot stop, not started");
    }
    started = false;
}

Provided everyone keeps to RULE 1, exceptions won't get ignored as easily as a return value. This will make bugs in the code easier to spot.

Desde que todos utilizem a regra 1, as exceções não serão ignoradas tão facilmente como um valor de retorno. Isso fará com que erros no código sejam mais fáceis de serem detectadas.

Onde tratar as Exceções

Se a situação não pode ser reparada, em seguida, jogá-lo até a interface de usuário, portanto, pode ser a melhor opção, relatando o problema ao usuário.

Regra 3: Até mesmo exceções não capturadas que travam sua aplicação pode ser mais útil que algum valor que seja ignorado.

Exceções informam o que está errado no programa. Se você os ignorar, você apenas estará fazendo o trabalho por você mesmo.

Resumo

Exceções existem para facilitar sua vida. Se você as utilizar, ela fará.

This page was last modified on 31 July 2013, at 08:57.
114 page views in the last 30 days.
×