41bb726c

Dia 17

Exceções

Charles L. Perkins e Laura Lemay


CONTEÚDOS

Os programadores em qualquer língua diligenciam escrever programas sem defeitos, programas que nunca caem, programas que podem tratar qualquer situação com a graça e isto pode recuperar-se de situações excepcionais sem causar ao usuário qualquer stress excessivo. Boas intenções à parte, os programas como isto não existem.

Em verdadeiros programas, os erros ocorrem, porque o programador não esperou cada situação na qual o seu código entraria (ou não teve o tempo para testar o programa bastante), ou por causa de situações fora do controle do programador os maus dados de usuários, arquivos corruptos que não têm os dados direitos neles, conexões de rede que não se unem, dispositivos de hardware que não respondem, lugares de sol, gremlins, tudo o que.

Em Java, estes tipos de eventos estranhos que podem fazer que a um programa falhe chamam-se exceções. E Java define um número de características de língua para tratar com exceções, inclusive

As exceções são coisas excepcionais que podem acontecer nos seus programas Java do lado de fora do comportamento normal ou desejado daquele programa. As exceções incluem erros que podem ser fatais para o seu programa mas também incluir outras situações excepcionais. Por exceções gerentes, pode dirigir erros e possivelmente trabalhar em volta deles.

Exceções, caminho velho e confuso

As linguagens de programação trabalharam muito tempo para resolver o seguinte problema comum:

int  status = callSomethingThatAlmostAlwaysWorks();

if (status == FUNNY_RETURN_VALUE) {
    . . .      // something unusual happened, handle it
    switch(someGlobalErrorIndicator) {
        . . . // handle more specific problems
    }
} else {
    . . .     // all is well, go your merry way
}

O que este bit do código tenta fazer deve dirigir um método que deve trabalhar, mas não podia por alguma razão excepcional. A posição poderia terminar de ser algum valor de retorno excepcional, em que caso o código tenta compreender o que aconteceu e trabalho em volta dele. De qualquer maneira isto parece muito trabalho para fazer para tratar um caso raro. E se a função chamou regressos um int como parte da sua resposta normal, terá de distinguir um número inteiro especial (FUNNY_RETURN_VALUE) como um erro. Alternativamente, pode passar em um ponteiro de valor de retorno especial ou usar uma variável global para guardar aqueles erros, mas então os problemas surgem com o cuidado da pista de múltiplos erros com o mesmo bit do código, ou do erro original guardado no global que se sobregrava por um novo erro antes que tenha uma possibilidade de tratar com ele.

Uma vez que começa a criar sistemas maiores, a gestão incorreta pode tornar-se um problema principal. Os programadores diferentes podem usar valores especiais diferentes para tratar erros e podem não documentá-los demais bem, se em absoluto. Pode usar incongruamente erros nos seus próprios programas. O código para dirigir estas espécies de erros muitas vezes pode obscurecer a intenção original do programa, fazendo aquele código difícil de ler e manter. E, finalmente, se tenta tratar com erros deste modo kludgey, não há caminho fácil para o compilador para verificar a coerência de maneira pode verificar para assegurar-se que chamou um método com os argumentos direitos.

Por todas estas razões, Java tem exceções para tratar com direção, criação e espera de erros e outras situações excepcionais. Por uma combinação de características de língua especiais, a verificação de coerência em compila o tempo e o grupo de classes de exceção extensíveis, os erros e outras condições excepcionais em programas Java podem dirigir-se muito mais facilmente. Considerando estas características, pode acrescentar agora uma nova dimensão inteira ao comportamento e o desenho das suas classes, da sua hierarquia de classe, e do seu sistema total. A sua classe e as definições de interface descrevem como se supõe que o seu programa se comporte dado as melhores circunstâncias. Integrando o manejo de exceção no seu desenho de programa, pode descrever constantemente como o programa se comportará quando as circunstâncias estão não exatamente como boas, e permitem a pessoas que usam as suas classes para saber que esperar naqueles casos.

Exceções de Java

Neste ponto no livro, as possibilidades são bateu em pelo menos uma exceção possivelmente de Java você mistyped um nome de método ou fez um erro no seu código que causou um problema. E as possibilidades consistem em que o seu programa deixou e vomitou um ramo de erros misteriosos à tela. Aqueles erros misteriosos são exceções. Quando o seu programa para, é porque uma exceção "se lançou". As exceções podem lançar-se pelo sistema ou lançar-se por você, e podem pegar-se também (pegando uma exceção implica o procedimento com ele assim o seu programa não cai. Aprenderá mais sobre isto depois). "Uma exceção lançou-se" é a terminologia de Java própria de "um erro acontecido".

As exceções não ocorrem, lançam-se. Java lança uma exceção em resposta a uma situação excepcional. Também pode lançar as suas próprias exceções ou pegar uma exceção para dirigir graciosamente erros.

O coração do sistema de exceção de Java é a própria exceção. As exceções em Java são objetos reais, exemplos de classes que herdam da classe Throwable. Quando uma exceção se lança, um exemplo de uma classe de Throwable cria-se. A figura 17.1 mostra uma hierarquia de classe parcial de exceções.

A figura 17.1: A hierarquia de classe de exceção.

Throwable tem duas subclasses: Error e Exception. Os exemplos de Error são erros internos no ambiente de tempo de execução de Java (a máquina virtual). Estes erros são raros e normalmente fatais; não há muito pode fazer sobre eles (para pegá-los ou lançá-los você mesmo), mas existem para que Java possa usá-los se precisar.

A classe Exception é mais interessante. As subclasses de Exception caem em dois grupos gerais:

As exceções em tempo de execução normalmente ocorrem por causa do código que não é muito robusto. Uma exceção de ArrayIndexOutofBounds, por exemplo, nunca deve lançar-se se estiver verificando propriamente para assegurar-se que o seu código não se estende para além do fim de uma tabela. as exceções de NullPointerException não acontecerão se não tentar referir os valores de uma variável que não mantém de fato um objeto. Se o seu programa estiver causando exceções em tempo de execução em alguma circunstância em absoluto, deve estar fixando aqueles problemas antes que até comece a tratar com a gestão de exceção.

O grupo final de exceções é o mais interessante porque estas são as exceções que indicam que algo muito estranho e fora do controle acontece. EOFException s, por exemplo, acontece quando lê em um arquivo e os fins de arquivo antes que o espere a. MalformedURLException s acontece quando um URL não está no formato direito (possivelmente o seu usuário datilografou-o mal). Este grupo inclui exceções que você mesmo cria para transmitir casos excepcionais que podem ocorrer nos seus próprios programas.

As exceções arranjam-se em uma hierarquia como outras classes, onde as superclasses de Exception são erros mais gerais, e as subclasses são erros mais específicos. Esta organização ficará mais importante para você como trata com exceções no seu próprio código.

A maioria das classes de exceção são parte do pacote de java.lang (inclusive Throwable, Exception e RuntimeException). Mas muitos de outros pacotes definem outras exceções, e aquelas exceções usam-se em todas as partes da biblioteca de classe. Por exemplo, o pacote de java.io define uma classe de exceção geral chamada IOException, que se subclassifica não só no pacote de java.io de exceções de entrada e saída (EOFException, FileNotFoundException), mas também nas classes de java.net para transmitir em rede exceções como MalFormedURLException.

Direção de exceções

Assim, agora que sabe qual uma exceção é, como trata com eles no seu próprio código? Em muitos casos, o compilador de Java força a gestão de exceção quando tenta usar métodos aquele uso exceções; precisará de tratar com aquelas exceções no seu próprio código ou simplesmente não compilará. Nesta seção aprenderá sobre aquela verificação de coerência e como usar o try, catch e palavras-chave de língua de finally para tratar com exceções que podem ou podem não ocorrer.

Verificação de coerência de exceção

Mais trabalha com as bibliotecas de classe de Java, mais provavelmente é que baterá em um erro de compilador (uma exceção!) semelhante a este:

TestProg.java:32: Exception java.lang.InterruptedException
must be caught or it must be declared in the throws clause
of this method.

O que isto significa? Em Java, um método pode indicar as espécies de erros que poderia lançar possivelmente. Por exemplo, os métodos que leem em arquivos poderiam lançar potencialmente erros de IOException, portanto aqueles métodos se declaram com um modificador especial que indica erros potenciais. Quando usa aqueles métodos nos seus próprios programas Java, tem de proteger o seu código contra aquelas exceções. Esta regra tem o cumprimento exigido pelo próprio compilador, o mesmo caminho que os cheques de compilador assegurar-se que usa métodos com o número direito de argumentos e que todos os seus tipos variáveis combinam com a coisa que lhes destina.

Porque é este registro de lugar? Tendo métodos declaram as exceções que lançam, e forçando-o a tratar aquelas exceções de algum modo, o potencial de erros fatais em uma ocorrência de programa porque simplesmente não sabia que podem ocorrer minimiza-se. Já não tem de ler cuidadosamente a documentação ou o código de um objeto que vai usar para assegurar-se que tratou com todo Java dos problemas potencial faz a verificação para você. E, de outro lado, se define os seus métodos para que indiquem as exceções que podem lançar, então Java pode contar aos usuários dos seus objetos de tratar aqueles erros.

Proteger código e pegar exceções

Vamos supor que tenha codificado felizmente e durante um teste compilam-no bateu naquela mensagem de exceção. Segundo a mensagem, tem de pegar o erro ou declarar que o seu método o lança. Vamos tratar com o primeiro caso: captura de exceções potenciais.

Para pegar uma exceção, faz duas coisas:

O que try e catch efetivamente significam é "tentativa este bit do código que poderia causar uma exceção. Se realizar okey, continue com o programa. Se não fizer, pegue a exceção e trate com ela".

Viu try e catch uma vez antes, quando tratamos com fios. No Dia 10, "Animação simples e Fios", aprendeu sobre um applet que criou um relógio digital e a animação fizeram uma pausa uma vez uma segunda utilização deste bit do código:

try { Thread.sleep(1000) }
catch (InterruptedException e) {}

Enquanto este exemplo usa try e catch, não é um uso muito bom dele. Aqui, o método de classe de Thread.sleep() pode lançar potencialmente uma exceção do tipo InterruptedException (para quando o fio se interrompe de correr). Portanto pusemos a chamada a sleep() dentro da cláusula try para pegar aquela exceção se acontecer. E dentro de catch (dentro dos parênteses), indicamos que procuramos especificamente exceções de InterruptedException. O problema aqui consiste em que não há nada dentro da cláusula em outras palavras de catch, pegaremos a exceção se acontecer, mas então o deixaremos no soalho e fingiremos que não o vimos. Em todos exceto os casos mais simples (como este, onde a exceção realmente não importa), vai querer pôr algo dentro das tiras depois de catch para tentar fazer algo responsável para limpar depois que a exceção acontece.

A parte da cláusula catch dentro dos parênteses é semelhante à lista de parâmetro de uma definição de método; contém a classe da exceção a pegar-se e um nome da variável (e muito usa-se comumente). Dentro do corpo da cláusula de proveito, então pode referir-se ao objeto de exceção, por exemplo, de vir à mensagem de erro detalhada contida no método de getMessage():

catch (InterruptedException e) {
    System.out.println("Ooops.  Error: " + e.getMessage());
}

Aqui está outro exemplo. Diga que tem um programa que lê em um arquivo. Este programa mais provavelmente usa uma das classes de correntes sobre as quais aprenderá no Dia 19, "As correntes e a entrada-saída", mas a ideia básica aqui consiste em que abre uma conexão a um arquivo e logo usa o método de read() para adquirir dados dele. E se algum erro de disco estranho acontecer e o método de read() não pode ler nada? E se o arquivo é truncado e tem menos bytes nele do que esperou? Em qualquer destes exemplos, o método de read() lançará um IOException que, se não o pegou, faria que ao seu programa deixasse de realizar e possivelmente choque. Pondo o seu método de read() dentro de uma cláusula try, então pode tratar graciosamente que o erro dentro de catch para limpar depois do erro e voltar a algum estado seguro, remendar coisas bastante para ser capaz de prosseguir, ou, se tudo o resto falha, para salvar tanto do estado do programa atual quanto possível e sair. Este exemplo faz somente isto; tenta ler no arquivo e pega exceções se acontecerem:

try {
    while (numbytes <= mybuffer.length) {
        myinputstream.read(mybuffer);
        numbytes;++
    }
} catch (IOException e) {
  System.out.println("Ooops, IO Exception.  Only read " + numbytes.");
  // other cleanup code
}

Aqui, "outro código de limpeza" pode ser algo que quer que ele seja; possivelmente pode continuar com o programa usando a informação parcial que adquiriu do arquivo, ou possivelmente quer levantar um diálogo dizendo que o arquivo é corrupto e deixar o usuário tentar selecionar outro arquivo ou fazer alguma outra operação.

Observe que porque as classes de Exception se organizam em hierarquias como outras classes são, e por causa da regra que pode usar uma subclasse em qualquer lugar espera-se uma superclasse, pode pegar "grupos" de exceções e tratá-los com o mesmo código de catch. Por exemplo, embora haja vários tipos diferentes de IOException s (EOFException, FileNotFoundException, e assim por diante - veem o pacote de java.io de exemplos), pegando IOException também pega exemplos de qualquer subclasse de IOException.

E se realmente quiser pegar tipos muito diferentes de exceções que não se relacionam pela herança? Pode usar múltiplas cláusulas de proveito para um try dado, como isto:

try {
   // protected code
} catch (OneKindOfException e) {
  ...
} catch (AnotherKindOfException e2) {
  ....
} catch (YetAnotherException e3) {
  ...
} catch (StilMoreException e4) {
 ....
}

Observe que porque o alcance de variáveis locais dentro de catch é o mesmo como o alcance do bloco exterior (a definição de método ou um laço se for o interior), terá de usar variáveis locais diferentes para cada catch individual.

Como a primeira cláusula catch que os jogos se realizam, pode construir cadeias como o seguinte:

try {
    someReallyExceptionalMethod();
} catch (NullPointerException n) {  // a subclass of RuntimeException
    . . .
} catch (RuntimeException r) {      // a subclass of Exception
    . . .
} catch (IOException i) {           // a subclass of Exception
    . . .
} catch (MyFirstException m) {      // our subclass of Exception
    . . .
} catch (Exception e) {             // a subclass of Throwable
    . . .
} catch (Throwable t) {
    . . .  // Errors, plus anything not caught above are caught here
}

Enumerando subclasses antes das suas classes de pais, o pai pega algo que pegaria normalmente isto também é não uma das subclasses acima dele. Fazendo jogos de mão com cadeias como estes, pode exprimir quase qualquer combinação de testes.

A cláusula de finally

Suponha que há alguma ação no seu código que absolutamente deve fazer, não importa o que acontece, se uma exceção se lança ou não. Normalmente, isto deve libertar algum recurso externo depois de adquiri-lo, para fechar um arquivo depois de abri-lo ou algo semelhante. Enquanto pode pôr aquela ação tanto dentro de um catch como do lado de fora dele, que estaria duplicando o mesmo código em dois lugares diferentes. Em vez disso, ponha uma cópia daquele código dentro de uma parte opcional especial da cláusula try...catch, chamada finally:

SomeFileClass  f = new SomeFileClass();

if (f.open("/a/file/name/path")) {
    try {
        someReallyExceptionalMethod();
    { catch (IOException e) {
        // deal with errors
    } finally {
        f.close();
    }
}

A cláusula finally é exceções exteriores de fato úteis; também pode usá-lo para realizar o código de limpeza depois de um regresso, um intervalo ou uns laços interiores continuar. Para os últimos casos, pode usar uma cláusula try com um finally mas sem uma cláusula catch.

Aqui está um exemplo regularmente complexo de como isto poderia trabalhar:

int  mysteriousState = getContext();

while (true) {
    System.out.print("Who ");
    try {
        System.out.print("is ");
        if (mysteriousState == 1)
            return;
        System.out.print("that ");
        if (mysteriousState == 2)
            break;
        System.out.print("strange ");
        if (mysteriousState == 3)
            continue;
        System.out.print("but kindly ");
        if (mysteriousState == 4)
            throw new UncaughtException();
        System.out.print("not at all ");
    } finally {
        System.out.print("amusing man?\n");
    }
    System.out.print("I'd like to meet the man");
}
System.out.print("Please tell me.\n");

Aqui está a produção produzida dependendo do valor de mysteriousState:

1     Who is amusing man? Please tell me.
2     Who is that amusing man? Please tell me.
3     Who is that strange amusing man? Who is that strange ....
4     Who is that strange but kindly amusing man? Please tell me.
5     Who is that strange but kindly not at all amusing man?
      I'd like to meet that man. Who is that strange ...

Observar
Em casos 3 e 5, a produção nunca termina até que deixe o programa. Em 4, uma mensagem de erro gerada pelo UncaughtException também se imprime.

Declaração de métodos que poderiam lançar exceções

No exemplo prévio aprendeu como tratar com métodos que poderiam lançar possivelmente exceções protegendo o código e pegando qualquer exceção que ocorre. O compilador de Java verificará para assegurar-se que tratou de qualquer maneira com exceções de um método - mas como sabia que exceções dizer-lhe de em primeiro lugar?

A resposta é que o método original indicou na sua assinatura as exceções que poderia lançar possivelmente. Pode usar este mecanismo nos seus próprios métodos de fato, é o bom estilo para fazer assim para assegurar-se que outros usuários das suas classes se alertam aos erros que os seus métodos podem encontrar.

Para indicar que um método pode lançar possivelmente uma exceção, usa uma cláusula especial na definição de método chamada throws.

A cláusula de throws

Para indicar que algum código no corpo do seu método pode lançar uma exceção, simplesmente acrescente a palavra-chave de throws depois da assinatura do método (antes que a tira inicial) com o nome ou nomes da exceção que o seu método lança:

public boolean myMethod (int x, int y) throws AnException {
   ...
}

Se o seu método puder lançar possivelmente múltiplas espécies de exceções, pode pôr todos eles na cláusula throws, separada por vírgulas:

public boolean myOtherMethod (int x, int y)
  throws AnException, AnotherExeption, AThirdException {
   ...
}

Observe que como com catch pode usar uma superclasse de um grupo de exceções para indicar que o seu método pode lançar qualquer subclasse daquela exceção:

public void YetAnotherMethod() throws IOException {
...
}

Tenha em mente que acrescentar um método de throws à sua definição de método simplesmente significa que o método poderia lançar uma exceção se algo dá errado, não que de fato vai. A cláusula throws simplesmente fornece a extra informação à sua definição de método sobre exceções potenciais e permite a Java assegurar-se que o seu método se está usando corretamente por outras pessoas.

Pense na descrição total de um método como um contrato entre o desenhista daquele método (ou classe) e o chamador do método (pode ser qualquer lado daquele contrato, naturalmente). Normalmente, a descrição indica os tipos de argumentos de um método, o que devolve, e a semântica geral do que normalmente faz. Usando throws, acrescenta a informação sobre as coisas anormais que pode fazer também. Esta nova parte do contrato ajuda a separar e fazer explícito todos os lugares onde as condições excepcionais devem tratar-se no seu programa, e isto faz o desenho amplo mais fácil.

Que exceções deve lançar?

Uma vez que decide declarar que o seu método poderia lançar uma exceção, tem de decidir que exceções poderia lançar (e de fato lançá-los ou chamar um método que lançará eles-you'll aprendem sobre o lançamento das suas próprias exceções na seguinte seção). Em muitos exemplos, isto será evidente da operação do próprio método. Possivelmente cria e lançando as suas próprias exceções, em que caso saberá exatamente que exceções lançar.

Realmente não tem de enumerar todas as exceções possíveis que o seu método pode lançar; algumas exceções tratam-se pelo próprio tempo de execução e são tanto comum (bem, não comum, mas ubíquo) que não tem de tratar com eles. Especialmente, as exceções da classe Error ou de RuntimeException (ou alguma das suas subclasses) não têm de enumerar-se na sua cláusula throws. Adquirem o tratamento especial porque podem ocorrer em qualquer lugar dentro de um programa Java e são normalmente condições que, como o programador, não causou diretamente. Um bom exemplo é OutOfMemoryError, que pode acontecer em qualquer lugar, em qualquer momento, e para qualquer número de razões. Estas duas espécies de exceções chamam-se exceções implícitas, e não tem de incomodar-se com eles.

As exceções implícitas são exceções que são as subclasses das classes RuntimeException e Error. As exceções implícitas lançam-se normalmente pelo próprio tempo de execução de Java. Não tem de declarar que o seu método os lança.

Observar
Pode decidir, naturalmente, enumerar estes erros e exceções em tempo de execução na sua cláusula throws se você gostar, mas os chamadores dos seus métodos não se forçarão a tratá-los; as exceções só não-em tempo de execução devem tratar-se.

Todas outras exceções chamam-se exceções explícitas e são candidatos potenciais de uma cláusula throws no seu método.

Transmissão de exceções

Além da declaração de métodos que lançam exceções, há um outro exemplo no qual a sua definição de método pode incluir uma cláusula throws. Neste caso, quer usar um método que lança uma exceção, mas não quer pegar aquela exceção ou tratar com ela. Em muitos casos, poderia fazer mais sentido do método que chama o seu método para tratar com aquela exceção em vez de para você para tratar com ele. Não há nada mal com isto; é uma ocorrência regularmente comum que não tratará de fato com uma exceção, mas a passará atrás ao método que chama seu. Pelo menos, é uma melhor ideia de transmitir exceções à chamada de métodos do que pegá-los e ignorá-los.

Em vez de usar as cláusulas try e catch no corpo do seu método, pode declarar o seu método com uma cláusula throws tal que, também, poderia lançar possivelmente a exceção apropriada. Então é a responsabilidade do método que chama o seu método para tratar com aquela exceção. Isto é outro caso que satisfará o compilador de Java que fez algo com um método dado. Aqui está outro modo de implementar um exemplo que lê carateres em uma corrente:

public void readTheFile(String filename) throws IO Exception {
    // open the file, init the stream, etc.
    while (numbytes <= mybuffer.length) {
        myinputstream.read(mybuffer);
        numbytes;++
    }

Este exemplo é semelhante ao exemplo usado anteriormente hoje; lembre-se de que se declarou que o método de read() lançasse um IOException, portanto teve de usar try e catch para usá-lo. Uma vez que declara que o seu método lance uma exceção, contudo, pode usar outros métodos que também lançam aquelas exceções dentro do corpo deste método, sem precisar de proteger o código ou pegar a exceção.

Observar
Pode tratar, naturalmente, com outras exceções usando try e catch no corpo do seu método além da transmissão das exceções que enumerou na cláusula throws. Também pode tratar ambos com a exceção de algum modo e logo re-lançá-lo para que o método de chamada do seu método tenha de tratar com ele de qualquer maneira. Aprenderá como lançar métodos na seguinte seção.

throws e herança

Se a sua definição de método ignorar um método em uma superclasse que inclui uma cláusula throws, há regras especiais para como o seu método ignoto trata com throws. Diferentemente de com outras partes da assinatura de método, o seu novo método não tem de ter o mesmo jogo de exceções enumeradas na cláusula throws. Como há um potencial que o seu novo método pode tratar melhor com exceções, em vez de lançá-los somente, o método da sua subclasse pode lançar potencialmente menos tipos de exceções do que a definição de método da sua superclasse, até e inclusive o lançamento de nenhuma exceção em absoluto. Isto significa que pode ter duas seguintes definições de classe e as coisas trabalharão somente perfeitas:

public class Fruit {
    public void ripen() throws RotException {
       ...
    }
}

public class WaxFruit extends Fruit {
    public void ripen() {
       ...
    }
}

A oposta desta regra não é verdade; o método de uma subclasse não pode lançar mais exceções (exceções de tipos diferentes ou classes de exceção mais gerais) do que o método da sua superclasse.

Criar e lançar as suas próprias exceções

Há dois lados a cada exceção: o lado que lança a exceção e o lado que o pega. Uma exceção pode lançar-se em volta várias vezes a um número de métodos antes que se pegue, mas consequentemente vai se pegar e vai se tratar.

Mas quem faz o lançamento real? Onde as exceções vêm de? Muitas exceções lançam-se pelo tempo de execução de Java, ou por métodos dentro das próprias classes de Java. Também pode lançar alguma das exceções padrão que as bibliotecas de classe de Java definem, ou pode criar e lançar as suas próprias exceções. Esta seção descreve todas estas coisas.

Lançamento de exceções

Declarar que o seu método lança uma exceção é só útil para usuários do seu método e para o compilador de Java, que verifica para assegurar-se que todas as suas exceções se estão tratando. Mas a própria declaração não faz algo para lançar de fato aquela exceção a deve ocorrer; tem de fazer isto você mesmo no corpo do método.

Lembre-se de que as exceções são todos os exemplos de alguma classe de exceção, da qual há muitos definidos nas bibliotecas de classe de Java padrão. Para lançar uma exceção, por isso, precisará de criar um novo exemplo de uma classe de exceção. Uma vez que tem aquele exemplo, use a afirmação de throw para lançá-lo (isto pode ser um pouco mais fácil?). O modo mais simples de lançar uma exceção parece-se simplesmente com isto:

throw new ServiceNOteAvailableException();

Nota técnica
Só pode lançar objetos que são os exemplos das subclasses de Throwable. Isto é diferente de C ++ exceções, que lhe permitem lançar objetos de qualquer tipo.

Dependendo da classe de exceção usa, a exceção também pode ter argumentos ao seu construtor que pode usar. O mais comum destes é um argumento de cadeia, que o deixa descrever o problema real no maior detalhe (que pode ser muito útil para depurar objetivos). Aqui está um exemplo:

throw new ServiceNotAvailableException("Exception:
   service not available, database is offline.");

Uma vez que uma exceção lança-se, as saídas de método imediatamente, sem realizar qualquer outro código (outro do que o código dentro de finally, se aquela cláusula existir) e sem devolver um valor. Se o método de chamada não tiver um try ou catch que rodeia a chamada ao seu método, o programa pode muito sair bem baseado na exceção que lançou.

Criar as suas próprias exceções

As exceções são simplesmente classes, como qualquer outra classe na hierarquia de Java. Embora haja um número justo de exceções na biblioteca de classe de Java que pode usar nos seus próprios métodos, há uma possibilidade forte que possa querer criar as suas próprias exceções para tratar tipos diferentes de erros nos quais os seus programas poderiam bater. Afortunadamente, a criação de novas exceções é fácil.

A sua nova exceção deve herdar de alguma outra exceção na hierarquia de Java. Procure uma exceção que isto está perto daquele que cria; por exemplo, uma exceção por um mau formato de arquivo seria logicamente um IOException. Se não puder encontrar uma exceção estreitamente relacionada pela sua nova exceção, considerar a herança de Exception, que se forma o "topo" da hierarquia de exceção de exceções explícitas (lembre-se de que as exceções implícitas, que incluem subclasses de Error e RuntimeException, herdam de Throwable).

As classes de exceção tipicamente têm dois construtores: O primeiro não toma nenhum argumento e o segundo toma uma cadeia única como um argumento. No último caso quererá chamar super() naquele construtor para assegurar-se que a cadeia se aplica ao lugar certo na exceção.

Além daquelas três regras, as classes de exceção olham como outras classes. Pode pô-los nos seus próprios arquivos originais e compilá-los tão como ia outras classes:

public class SunSpotException extends Exception {
   public SunSpotException() {}
   public SunSpotExceotion(String msg) {
      super(msg);
   }
}

Realização de tudo isso: combinando throws, try e throw

E se quer combinar todas as aproximações mostradas por enquanto? No seu método, você gostaria de tratar exceções de entrada você mesmo, mas também você gostaria de passar a exceção até o seu chamador. Simplesmente usar try e catch não transmite a exceção, e simplesmente soma que uma cláusula throws não lhe dá uma possibilidade de tratar com a exceção. Se quiser tanto dirigir a exceção como transmiti-la ao chamador, use os três mecanismos: a cláusula throws, a declaração de try, e relançando explicitamente a exceção:

public void  responsibleExceptionalMethod() throws MyFirstException {
    MyFirstExceptionalClass  aMFEC = new MyFirstExceptionalClass();

    try {
        aMFEC.anExceptionalMethod();
    } catch (MyFirstException m) {
        . . .        // do something responsible
        throw m;     // re-throw the exception
    }
}

Isto trabalha porque os treinadores de exceção podem aninhar-se. Trata a exceção fazendo algo responsável com ele, mas decide que é demasiado importante não dar a um treinador de exceção que poderia estar no seu chamador uma possibilidade de tratá-lo também. As exceções fazem flutuar durante todo o tempo a cadeia de chamadores de método este caminho (normalmente não tratado pela maioria deles) até finalmente o próprio sistema trata quaisquer não capturados abortando o seu programa e imprimindo uma mensagem de erro. Em um programa autônomo, isto não é uma tão má ideia; mas em um applet, pode fazer que ao browser caia. A maior parte de browseres protegem-se deste desastre pegando todas as próprias exceções sempre que dirijam um applet, mas nunca pode contar. Se for possível para você pegar uma exceção e fazer algo inteligente com ele, deve.

Quando e quando não usar exceções

Para terminar a lição de hoje, aqui estão um sumário rápido e algum conselho sobre quando usar exceções … e quando não os usar.

Quando usar exceções

Como lançar, pegando e declarando exceções é conceitos relacionados e pode ser muito confuso, aqui está um sumário rápido de quando fazer que.

Se o seu método usar alguém o método de else, e aquele método tem uma cláusula throws, pode fazer uma de três coisas:

Em casos onde um método lança mais de uma exceção, pode tratar, naturalmente, cada uma daquelas exceções diferentemente. Por exemplo, poderia pegar algumas daquelas exceções permitindo a outros deixar passar a cadeia de chamada.

Se o seu método lançar as suas próprias exceções, deve declarar que lança aqueles métodos usando a cláusula throws. Se o seu método ignorar o método de uma superclasse que tem uma cláusula throws, pode lançar os mesmos tipos de exceções ou as subclasses daquelas exceções; não pode lançar nenhum tipo diferente de exceções.

E, finalmente, se o seu método se declarou com uma cláusula throws, não esqueça de lançar de fato a exceção no corpo do seu método usando throw.

Quando não usar exceções

As exceções são frescas. Mas não são tão frescos. Há vários casos nos quais não deve usar exceções, embora possam parecer apropriados no momento.

Em primeiro lugar, não deve usar exceções se a exceção for algo que espera e um teste simples para evitar que a condição excepcional fizesse muito mais sentido. Por exemplo, embora possa confiar em uma exceção de ArrayIndexOutofBounds para dizer-lhe quando foi para além do fim da tabela, um teste simples do comprimento da tabela no seu código para assegurar-se que não vem aquela exceção em primeiro lugar é uma ideia muito melhor. Ou se os seus usuários vão entrar em dados que tem de ser uma carta, testando para assegurar-se que os dados são uma carta é uma ideia muito melhor do que lançamento de uma exceção e procedimento com ele em outro lugar.

As exceções tomam muito tempo de processamento do seu programa Java. Ao passo que pode achar exceções com estilo interessantes para o seu próprio código, um teste simples ou a série de testes correrão muito mais rápido e farão o seu programa que muito mais eficiente. As exceções, como mencionei antes, só devem usar-se para casos realmente excepcionais que são fora do seu controle.

Também é fácil levar-se com exceções e tentar assegurar-se que se declarou que todos os seus métodos lancem todas as exceções possíveis que podem lançar possivelmente. Além da criação do seu código mais complexo em geral, se outras pessoas estarão usando o seu código, terão de tratar com o manejo de todas as exceções que os seus métodos poderiam lançar. Faz mais trabalho para todo o mundo implicado quando se leva com exceções. Declarar um método lançar poucos ou muitas exceções é uma troca; mais exceções que o seu método potencialmente lança, mais complexo que o método deve usar. Só declare as exceções que têm uma possibilidade razoavelmente justa do acontecimento e que fazem sentido para o desenho total das suas classes.

Mau estilo usando exceções

Quando primeiro começa a usar exceções, poderia estar apelando para o trabalho em volta dos erros de compilador que resultam quando usa um método que declarou uma cláusula throws. Enquanto é legal acrescentar uma cláusula de proveito vazia ou acrescentar uma cláusula throws ao seu próprio método (e há razões apropriadas de fazer ambas destas coisas), intencionalmente deixando exceções no soalho e subvertendo os cheques que o compilador de Java faz para você é o estilo muito mau.

O sistema de exceção de Java projetou-se para que se um erro potencial puder ocorrer, se avise sobre ele. Ignorar aqueles avisos e trabalhar em volta deles permitem a erros fatais de ocorrer nos seus erros do programa que pode ter evitado com algumas linhas do código. E, as cláusulas throws até piores, acrescentam aos seus métodos para evitar exceções significam que os usuários dos seus métodos (objetos além disso na cadeia de chamada) terão de tratar com eles. Acaba de fazer mais trabalho para alguém mais e fazer os seus métodos mais difíceis de usar para outras pessoas.

Os erros de compilador quanto a exceções estão lá para lembrar-lhe de refletir nestas questões. Não apresse-se para tratar com as exceções que podem afetar o seu código. Este extra cuidado o recompensará ricamente como reutiliza as suas classes em projetos posteriores e em programas maiores e maiores. Naturalmente, a biblioteca de classe de Java escreveu-se com exatamente este grau do cuidado, e isto é uma das razões é bastante robusto para usar-se na construção de todos os seus projetos de Java.

Sumário

Hoje aprendeu sobre como as exceções ajudam o desenho do seu programa e a robustez. As exceções dão-lhe um modo de dirigir erros potenciais nos seus programas e de alertar usuários dos seus programas que os erros potenciais podem ocorrer. A biblioteca de classe de Java tem uma tabela vasta de exceções definidas e lançadas, e também lhe permite definir e lançar as suas próprias exceções. Usar try, catch e finally pode proteger o código que pode resultar em exceções, pegar e tratar aquelas exceções se ocorrerem, e realiza o código se uma exceção se gerou.

O manejo de exceções é só a metade da equação; outra metade gera e lançando exceções você mesmo. Hoje aprendeu sobre a cláusula throws, que diz aos usuários do seu método que o método poderia lançar uma exceção. throws também pode usar-se para "transmitir" uma exceção de uma chamada de método no corpo do seu método.

Além da informação dada pela cláusula throws, aprendeu como criar de fato e lançar os seus próprios métodos estar definindo novas classes de exceção e lançando exemplos de qualquer classe de exceção usando throw.

E, finalmente, a confiança de Java no manejo de exceção estrito realmente coloca algumas restrições no programador, mas aprendeu que estas restrições são leves em comparação com as recompensas.

Perguntas e Respostas

Q:
Ainda não estou seguro que entendo as diferenças entre exceções, erros e exceções em tempo de execução. Lá há outro modo de olhar para eles?
A:
Os erros causam-se pela vinculação dinâmica ou problemas de máquina virtuais, e são assim demasiado de baixo nível para a maior parte de programas para preocupar-se com - ou ser capazes de tratar mesmo se realmente se preocuparam com eles. As exceções em tempo de execução geram-se pela execução normal do código de Java, e embora ocasionalmente reflitam uma condição que quererá tratar explicitamente, mais muitas vezes refletem um erro de codificação pelo programador e assim simplesmente têm de imprimir um erro de ajudar a marcar aquele erro. As exceções que não são exceções em tempo de execução (exceções de IOException, por exemplo) são condições que, por causa da sua natureza, devem tratar-se explicitamente por algum robusto e código "bem descoberto". A biblioteca de classe de Java escreveu-se usando só alguns destes, mas aqueles poucos são extremamente importantes para a utilização do sistema seguramente e corretamente. O compilador ajuda-o a tratar estas exceções propriamente via os seus cheques de cláusula throws e restrições.
Q:
Lá algum caminho é "vir em volta" das restrições estritas colocadas em métodos pela cláusula throws?
A:
Sim. Suponha que pensou muito tempo e muito e decidiu que tem de lograr esta restrição. Isto quase nunca é o caso, porque a solução direita é voltar e redesenhar os seus métodos para refletir as exceções que tem de lançar. Suponha, contudo, que por alguma razão uma classe de sistema o tem em uma camisa-de-força. A sua primeira solução é subclassificar RuntimeException para compor uma exceção nova, isenta do seu próprio. Agora pode lançá-lo ao conteúdo do seu coração, porque a cláusula throws que foi aborrecida você não precisa de incluir esta nova exceção. Se precisar da maior parte de tais exceções, uma aproximação elegante é misturar-se em algumas interfaces de exceção novas para as suas novas classes de Runtime. É livre de selecionar tudo o que o subconjunto destas novas interfaces quer a catch (nenhuma das exceções de Runtime normais precisa de pegar-se), enquanto se permite (legalmente) que qualquer exceção de Runtime restante (nova) atravesse isto método padrão aborrecido de outra maneira na biblioteca.
Q:
Ainda me confundo um pouco por cadeias longas de cláusulas catch. Pode etiquetar o exemplo prévio com que as exceções se tratam por cada linha do código?
A:
Certamente. Aqui é:

tente {
    someReallyExceptionalMethod ();
} proveito (NullPointerException n) {  
    ...  //trata NullPointerExceptions
} proveito (RuntimeException r) {
    ...  //trata RuntimeExceptions
          //não é NullPointerExceptions
} proveito (IOException I) {
    ...  //trata IOExceptions
} proveito (MyFirstException m) {
    ...  //trata MyFirstExceptions
} proveito (Exceção e) {  
    ...//trata Exceções que não são
          //RuntimeExceptions nem IOExceptions
          //nem MyFirstExceptions
} proveito (Throwable t) {
    ...  //trata Throwables isto
          //não são Exceções (isto é, Erros)
}

Q:
Considerando o que aborrecido pode dever às vezes tratar condições excepcionais propriamente, o que me para de rodear qualquer método como se segue:

try { thatAnnoyingMethod(); } catch (Throwable t) { }

e simplesmente ignorando todas as exceções?

A:
Nada, outro do que a sua própria consciência. Em alguns casos, não deve fazer nada, porque é a coisa correta a fazer para a implementação do seu método. De outra maneira, deve lutar pelo aborrecimento e adquirir experiência. O bom estilo é uma luta até pelo melhor de programadores, mas as recompensas são de fato ricas.