O main() método primeiro verifica se um único argumento de linha de comando foi especificado. Se a verificação for bem-sucedida, ele passa esse argumento para Audio.newAudio() e atribui o retornado Audio referência do objeto a uma variável local chamada audio. main() então prossegue verificando que audio não é nulo e (neste caso) interrogar o Audio objeto, emitindo os valores de amostra do clipe de áudio junto com sua taxa de amostragem.

Copie a Listagem 3 para um arquivo chamado UseAudio.java e coloque este arquivo no mesmo diretório que o ca diretório que você criou anteriormente. Em seguida, execute o seguinte comando para compilar UseAudio.java:


javac UseAudio.java

Se tudo correr bem, você deve observar UseAudio.class no diretório atual.

Execute o seguinte comando para executar UseAudio contra um arquivo WAV fictício chamado audio.wav:


java UseAudio audio.wav

Você deve observar a seguinte saída:


Samples

Sample Rate: 0

Suponha que UseAudio.java não estava localizado no mesmo diretório que ca. Como você compilaria esse arquivo de origem e executaria o aplicativo resultante? A resposta é usar o classpath.

O classpath Java

O Caminho de classe Java é uma sequência de pacotes que a máquina virtual Java (JVM) procura por tipos de referência. É especificado por meio do -classpath (ou -cp) opção usada para iniciar a JVM ou, quando não presente, a CLASSPATH variável de ambiente.

Suponha (em uma plataforma Windows) que a biblioteca de áudio esteja armazenada em C:audio e isso UseAudio.java é armazenado em C:UseAudioque é atual. Especifique os seguintes comandos para compilar o código-fonte e executar o aplicativo:


javac -cp ../audio UseAudio.java
java -cp ../audio;. UseAudio audio.wav

O caráter do período no java– a linha de comando prefixada representa o diretório atual. Ele deve ser especificado para que a JVM possa localizar UseAudio.class.

Tópicos de pacotes adicionais

A linguagem Java inclui um protected palavra-chave, que é útil em um contexto de pacote. Além disso, os pacotes podem ser distribuídos em arquivos JAR. Além disso, a JVM segue uma ordem de pesquisa específica ao pesquisar pacotes para tipos de referência (independentemente de esses pacotes estarem ou não armazenados em arquivos JAR). Exploraremos esses tópicos a seguir.

Acesso protegido

O protected a palavra-chave atribui o nível de acesso protegido a um membro da classe, como um campo ou método (por exemplo, protected void clear()). Declarando um membro de classe protected torna o membro acessível a todo o código em qualquer classe localizada no mesmo pacote e às subclasses, independentemente de seus pacotes.

Joshua Bloch explica a justificativa para dar aos membros da classe acesso protegido em seu livro, Effective Java Segunda Edição (“Item 17: Projete e documente para herança ou então proíba-a”). Eles são ganchos para o funcionamento interno de uma classe para permitir que os programadores “escrevam subclasses eficientes sem dor indevida”. Confira o livro para mais informações.

Arquivos JAR

Distribuir um pacote especificando instruções para criar a estrutura de diretório necessária junto com os arquivos de classe do pacote (e instruções sobre quais arquivos de classe armazenar em quais diretórios) seria uma tarefa tediosa e propensa a erros. Felizmente, os arquivos JAR oferecem uma alternativa muito melhor.

UM Arquivo JAR (arquivo Java) é um arquivo ZIP com um .jar extensão (em vez de .zip extensão). Inclui um especial META-INF diretório contendo manifest.mf (um arquivo especial que armazena informações sobre o conteúdo do arquivo JAR) e uma estrutura de diretório hierárquica que organiza os arquivos de classe.

Você usa o JDK jar ferramenta para criar e manter um arquivo JAR. Você também pode visualizar o índice do arquivo JAR. Para mostrar como é fácil usar esta ferramenta, criaremos um audio.jar arquivo que armazena o conteúdo do ca.javajeff.audio pacote. Então acessaremos esse arquivo JAR ao executar UseAudio.class. Criar audio.jar do seguinte modo:

Primeiro, certifique-se de que o diretório atual contém o criado anteriormente ca / javajeff / audio hierarquia de diretórios e que audio contém audio.class e WavReader.class.

Segundo, execute o seguinte comando:


jar cf audio.jar cajavajeffaudio*.class

O c opção significa “criar novo arquivo” e a f opção significa “especificar nome do arquivo”.

Agora você deve encontrar um audio.jar arquivo no diretório atual. Prove a si mesmo que este arquivo contém os dois arquivos de classe executando o seguinte comando, onde o t opção significa “listar índice”:


jar tf audio.jar

Você pode correr UseAudio.class adicionando audio.jar para seu classpath. Por exemplo, supondo que audio.jar está localizado no mesmo diretório que UseAudio.classvocê pode correr UseAudio no Windows através do seguinte comando:


java -classpath audio.jar;. UseAudio

Para sua conveniência, você pode especificar o mais curto -cp em vez do mais longo -classpath.

Procurando pacotes para tipos de referência

Os novatos em pacotes Java frequentemente ficam frustrados com “nenhuma definição de classe encontrada” e outros erros. Essa frustração pode ser parcialmente evitada ao entender como a JVM procura por tipos de referência. Para entender esse processo, você deve perceber que o compilador é um aplicativo Java especial que roda sob o controle da JVM. Além disso, há duas formas de busca: busca em tempo de compilação e busca em tempo de execução.

Quando o compilador encontra uma expressão de tipo (como uma chamada de método) no código-fonte, ele deve localizar a declaração desse tipo para verificar se a expressão é legal. Como exemplo, ele pode verificar se um método existe na classe do tipo, cujos tipos de parâmetro correspondem aos tipos dos argumentos passados ​​na chamada de método.

O compilador primeiro pesquisa os pacotes da plataforma Java (em rt.jar e outros arquivos JAR), que contêm os tipos de biblioteca de classes padrão do Java (como java.lang‘s System classe). Ele então pesquisa pacotes de extensão para tipos de extensão. Se o -sourcepath opção é especificada ao iniciar javaco compilador pesquisa os arquivos de origem do caminho indicado.

Caso contrário, o compilador pesquisa o classpath (na ordem da esquerda para a direita) para o primeiro arquivo de classe ou arquivo de origem que contém o tipo. Se nenhum classpath estiver presente, o diretório atual será pesquisado. Se nenhum pacote corresponder ou o tipo ainda não puder ser encontrado, o compilador relata um erro. Caso contrário, ele registra as informações do pacote no arquivo de classe.

Quando o compilador ou qualquer outro aplicativo Java é executado, a JVM encontrará tipos e deverá carregar seus arquivos de classe associados por meio de um código especial conhecido como carregador de classe. A JVM usará as informações do pacote armazenadas anteriormente associadas ao tipo encontrado em uma busca pelo arquivo de classe desse tipo.

A JVM pesquisa os pacotes da plataforma Java, seguidos pelos pacotes de extensão, seguidos pelo classpath ou diretório atual (quando não há classpath) para o primeiro arquivo de classe que contém o tipo. Se nenhum pacote corresponder ou o tipo não puder ser encontrado, um erro “nenhuma definição de classe encontrada” será relatado. Caso contrário, o arquivo de classe será carregado na memória.

Importando membros estáticos estaticamente

Em Effective Java Segunda Edição, Item 19Joshua Bloch menciona que os desenvolvedores Java devem usar apenas interfaces para declarar tipos. Não devemos usar interfaces para declarar interfaces constantesque são interfaces que existem apenas para exportar constantes. Listagem 4’s Switchable A interface constante fornece um exemplo.

Listagem 4. Uma interface constante (Switchable.java)


public interface Switchable
{
   boolean OFF = false;
   boolean ON = true;
}

Os desenvolvedores recorrem a interfaces constantes para evitar ter que prefixar o nome da constante com o nome do seu tipo de referência (por exemplo, Math.PI). Por exemplo, considere a Listagem 5 Light classe, que implementa o Switchable interface para que o desenvolvedor tenha liberdade para especificar constantes OFF e ON sem ter que incluir prefixos de classe (se eles foram declarados em uma classe).

Listagem 5. Light implementa Switchable (Light.java, versão 1)


public class Light implements Switchable
{
   private boolean state = OFF;

   public void printState()
   {
      System.out.printf("state = %s%n", (state == OFF) ? "OFF" : "ON");
   }

   public void toggle()
   {
      state = (state == OFF) ? ON : OFF;
   }
}

Uma interface constante fornece constantes que devem ser usadas na implementação de uma classe. Como um detalhe de implementação, você não deve vazar constantes na API exportada da classe porque elas podem confundir outros que usam sua classe. Além disso, para preservar a compatibilidade binária, você está comprometido em dar suporte a elas, mesmo quando a classe não as estiver mais usando.

Importações estáticas

Para satisfazer a necessidade de interfaces constantes e, ao mesmo tempo, evitar os problemas impostos pela sua utilização, o Java 5 introduziu importações estáticas. Este recurso de linguagem pode ser usado para importar membros estáticos de um tipo de referência. Ele é implementado por meio do import static declaração cuja sintaxe aparece abaixo:


import static packagespec . typename . ( staticmembername | * );

Colocação static depois import distingue esta declaração de uma declaração de importação regular. A sintaxe é semelhante à da declaração de importação regular. import declaração em termos da lista padrão separada por períodos de nomes de pacotes e subpacotes. Você pode importar um único nome de membro estático ou todos os nomes de membros estáticos (graças ao asterisco). Considere os seguintes exemplos:


import static java.lang.Math.*;   // Import all static members from Math.
import static java.lang.Math.PI;  // Import the PI static constant only.
import static java.lang.Math.cos; // Import the cos() static method only.

Depois de importá-los, você pode especificar membros estáticos sem precisar prefixá-los com seus nomes de tipo. Por exemplo, depois de especificar a primeira ou terceira importação estática, você pode especificar cos diretamente, como em (>


double
      cosine = cos(angle);

Para corrigir a Listagem 5 para que ela não dependa mais de implements Switchablepodemos inserir uma importação estática, conforme demonstrado na Listagem 6.

Listagem 6. Uma importação estática melhora a implementação do Switchable (Light.java, versão 2)


package foo;

import static foo.Switchable.*;

public class Light
{
   private boolean state = OFF;

   public void printState()
   {
      System.out.printf("state = %s%n", (state == OFF) ? "OFF" : "ON");
   }

   public void toggle()
   {
      state = (state == OFF) ? ON : OFF;
   }
}

A listagem 6 começa com um package foo; declaração porque você não pode importar membros estáticos de um tipo localizado no pacote sem nome. Este nome de pacote aparece como parte da importação estática subsequente:


import static
      foo.Switchable.*;

O que observar ao usar importações estáticas

Há dois cuidados adicionais relacionados a importações estáticas.

Primeiro, quando duas importações estáticas importam o mesmo membro nomeado, o compilador relata um erro. Por exemplo, suponha que o pacote physics contém um Math classe que é idêntica a java.lang‘s Math classe na medida em que implementa o mesmo PI constante e métodos trigonométricos. Quando confrontado com o seguinte fragmento de código, o compilador relata erros porque não consegue determinar se java.lang.Math‘s ou physics.Math‘s PI constante está sendo acessado e cos() método está sendo chamado:


import static java.lang.Math.cos;
import static physics.Math.cos;

double angle = PI;
System.out.println(cos(angle));

Segundo, o uso excessivo de importações estáticas polui o namespace do código com todos os membros estáticos que você importa, o que pode tornar seu código ilegível e impossível de manter. Além disso, qualquer um que leia seu código pode ter dificuldade para descobrir de qual tipo um membro estático vem, especialmente ao importar todos os nomes de membros estáticos de um tipo.

Conclusão

Pacotes ajudam você a criar bibliotecas reutilizáveis ​​de tipos de referência com seus métodos. Se você chamar um método (seja empacotado em uma biblioteca ou não) com um argumento ilegal (como um índice negativo para um array), você provavelmente encontrará uma exceção Java.