O FileInputStream(String filename)
construtor cria um fluxo de entrada para o arquivo identificado por filename
. Este construtor lança FileNotFoundException
quando o arquivo não existe, se refere a um diretório ou ocorre outro problema relacionado. O FileOutputStream(String filename)
construtor cria um fluxo de saída para o arquivo identificado por filename
. Ele joga FileNotFoundException
quando o arquivo existe, mas faz referência a um diretório, não existe e não pode ser criado, ou ocorre outro problema relacionado.
FileInputStream
fornece um int read()
método para ler um byte e retorná-lo como um inteiro de 32 bits. Este método retorna -1 no fim do arquivo. FileOutputStream
fornece um void write(int b)
método para escrever o byte nos 8 bits inferiores de b
. Qualquer método lança IOException
quando algo dá errado.
A maior parte do exemplo é um while
loop que repetidamente read()
é o próximo byte do fluxo de entrada e write()
s que byte para o fluxo de saída, até read()
sinaliza fim de arquivo.
O try
A lógica de cópia de arquivo do bloco é fácil de seguir porque essa lógica não é combinada com código de verificação de exceção (if
testes e relacionados throw
declarações ocultas nos construtores e métodos), código de tratamento de exceções (que é executado em um ou mais associados catch
blocos) e código de limpeza (para fechar os arquivos de origem e destino; este código é relegado a um associado finally
bloco). Em contraste, a falta de uma estrutura orientada a exceções semelhante em C resulta em um código mais detalhado, conforme ilustrado pelo seguinte trecho de um C maior cp
aplicativo (no arquivo de código deste artigo) que copia um arquivo de origem para um arquivo de destino:
if ((fpsrc = fopen(argv(1), "rb")) == NULL)
{
fprintf(stderr, "unable to open %s for readingn", argv(1));
return;
}
if ((fpdst = fopen(argv(2), "wb")) == NULL)
{
fprintf(stderr, "unable to open %s for writingn", argv(1));
fclose(fpsrc);
return;
}
while ((c = fgetc(fpsrc)) != EOF)
if (fputc(c, fpdst) == EOF)
{
fprintf(stderr, "unable to write to %sn", argv(1));
break;
}
Neste exemplo, a lógica de cópia de arquivo é mais difícil de seguir porque a lógica é misturada com verificação de exceção, tratamento de exceção e código de limpeza:
- Os dois
== NULL
e um== EOF
os cheques são o equivalente ao ocultothrow
declarações e cheques relacionados. - Os três
fprintf()
chamadas de função são o código de tratamento de exceções cujo equivalente Java seria executado em um ou maiscatch
blocos. - O
fclose(fpsrc);
chamada de função é um código de limpeza cujo equivalente Java seria executado em umfinally
bloquear.
Usando blocos catch para capturar exceções
A capacidade de tratamento de exceções do Java é baseada em catch
blocos. Esta seção apresenta catch
e vários catch
blocos.
O bloco de captura
Java fornece o catch
bloco para delimitar uma sequência de instruções que manipulam uma exceção. Um catch
bloco tem a seguinte sintaxe:
catch (throwableType throwableObject)
{
// one or more statements that handle an exception
}
O catch
block é semelhante a um construtor, pois tem uma lista de parâmetros. No entanto, essa lista consiste em apenas um parâmetro, que é um tipo lançável (Throwable
ou uma de suas subclasses) seguido por um identificador para um objeto desse tipo.
Quando ocorre uma exceção, um throwable é criado e lançado para a JVM, que procura o mais próximo catch
bloco cujo tipo de parâmetro corresponde diretamente ou é o supertipo do objeto throwable lançado. Quando encontra esse bloco, a JVM passa o throwable para o parâmetro e executa o catch
instruções do bloco, que podem interrogar o throwable passado e, de outra forma, manipular a exceção. Considere o seguinte exemplo:
catch (FileNotFoundException fnfe)
{
System.err.println(fnfe.getMessage());
}
Este exemplo (que estende o anterior try
exemplo de bloco) descreve um catch
bloco que captura e manipula objetos arremessáveis do tipo FileNotFoundException
. Somente itens lançáveis que correspondem a este tipo ou subtipo são capturados por este bloco.
Suponha que o FileInputStream(String filename)
construtor joga FileNotFoundException
. A JVM verifica o catch
bloco seguinte try
para ver se o tipo do parâmetro corresponde ao tipo lançável. Ao detectar uma correspondência, a JVM passa a referência do lançável para fnfe
e transfere a execução para o bloco. O bloco responde invocando getMessage()
para recuperar a mensagem da exceção, que então é gerada.
Especificando vários blocos catch
Você pode especificar vários catch
blocos depois de um try
bloco. Por exemplo, considere este trecho maior do mencionado Copy
aplicativo:
FileInputStream fis = null;
FileOutputStream fos = null;
{
fis = new FileInputStream(args(0));
fos = new FileOutputStream(args(1));
int c;
while ((c = fis.read()) != -1)
fos.write(c);
}
catch (FileNotFoundException fnfe)
{
System.err.println(fnfe.getMessage());
}
catch (IOException ioe)
{
System.err.println("I/O error: " + ioe.getMessage());
}
O primeiro catch
alças de bloco FileNotFoundException
s lançados de qualquer construtor. O segundo catch
alças de bloco IOException
é jogado do read()
e write()
métodos.
Ao especificar vários catch
blocos, não especifique um catch
bloco com um supertipo antes de um catch
bloco com um subtipo. Por exemplo, não coloque catch (IOException ioe)
antes catch (FileNotFoundException fnfe)
. Se você fizer isso, o compilador relatará um erro porque catch
também lidaria
(IOException ioe)FileNotFoundException
areia catch (FileNotFoundException
nunca teria a chance de executar.
fnfe)
Da mesma forma, não especifique vários catch
blocos com o mesmo tipo de arremesso. Por exemplo, não especifique dois catch (IOException ioe) {}
blocos. Caso contrário, o compilador relata um erro.
Usando blocos finally para limpar exceções
Independentemente de uma exceção ser tratada ou não, você pode precisar executar tarefas de limpeza, como fechar um arquivo aberto. Java fornece o finally
bloco para esta finalidade.
O finally
bloco consiste em palavra-chave finally
seguido por uma sequência de instruções delimitadas por chaves para executar. Pode aparecer após o final catch
bloquear ou depois do try
bloquear.
Limpeza em um contexto try-catch-finally
Quando os recursos devem ser limpos e uma exceção não está sendo lançada de um método, um finally
o bloco é colocado após o final catch
bloco. Isso é demonstrado pelo seguinte Copy
trecho:
FileInputStream fis = null;
FileOutputStream fos = null;
try
{
fis = new FileInputStream(args(0));
fos = new FileOutputStream(args(1));
int c;
while ((c = fis.read()) != -1)
fos.write(c);
}
catch (FileNotFoundException fnfe)
{
System.err.println(fnfe.getMessage());
}
catch (IOException ioe)
{
System.err.println("I/O error: " + ioe.getMessage());
}
finally
{
if (fis != null)
try
{
fis.close();
}
catch (IOException ioe)
{
// ignore exception
}
if (fos != null)
try
{
fos.close();
}
catch (IOException ioe)
{
// ignore exception
}
}
Se o try
o bloco é executado sem exceção, a execução passa para o finally
bloco para fechar os fluxos de entrada/saída do arquivo. Se uma exceção for lançada, o finally
o bloco é executado após o apropriado catch
bloquear.
FileInputStream
e FileOutputStream
herdar um void close()
método que lança IOException
quando o fluxo não pode ser fechado. Por esse motivo, envolvi cada um dos fis.close();
e fos.close();
em um try
bloco. Eu deixei o associado catch
bloco vazio para ilustrar o erro comum de ignorar uma exceção.
Um vazio catch
bloco que é invocado com o throwable apropriado não tem como relatar a exceção. Você pode perder muito tempo rastreando a causa da exceção, apenas para descobrir que poderia tê-la detectado antes se o vazio catch
block havia relatado a exceção, mesmo que apenas em um log.
Limpeza em um contexto de tentativa-final
Quando os recursos devem ser limpos e uma exceção está sendo lançada de um método, um finally
o bloco é colocado após o try
bloco: não há catch
blocos. Considere o seguinte trecho de uma segunda versão do Copy
aplicativo:
public static void main(String() args)
{
if (args.length != 2)
{
System.err.println("usage: java Copy srcfile dstfile");
return;
}
try
{
copy(args(0), args(1));
}
catch (IOException ioe)
{
System.err.println("I/O error: " + ioe.getMessage());
}
}
static void copy(String srcFile, String dstFile) throws IOException
{
FileInputStream fis = null;
FileOutputStream fos = null;
try
{
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(dstFile);
int c;
while ((c = fis.read()) != -1)
fos.write(c);
}
finally
{
if (fis != null)
try
{
fis.close();
}
catch (IOException ioe)
{
System.err.println(ioe.getMessage());
}
if (fos != null)
try
{
fos.close();
}
catch (IOException ioe)
{
System.err.println(ioe.getMessage());
}
}
}
A lógica de cópia de arquivo foi movida para um copy()
método. Este método é projetado para relatar uma exceção ao chamador, mas primeiro fecha cada arquivo aberto.
Este método é throws
cláusula apenas lista IOException
. Não é necessário incluir FileNotFoundException
porque FileNotFoundException
subclasses IOException
.
Mais uma vez, o finally
cláusula apresenta muito código apenas para fechar dois arquivos. Na segunda parte desta série, você aprenderá sobre a try-with-resources
declaração, que elimina a necessidade de fechar explicitamente esses arquivos.
Para concluir
Este artigo apresentou a você os fundamentos do framework tradicional orientado a exceções do Java, mas há muito mais para entender. A segunda metade deste tutorial apresenta os recursos de linguagem orientados a exceções mais avançados do Java e os tipos de biblioteca, incluindo try-with-resources
.