Java from Scratch – Parte 05

Introdução

No post anterior (Java from Scratch – Parte 04), aproveitamos todo o código original do primeiro PingPong e criamos o PingPong2 MULTIBALL, usando a orientação a objetos do Java. Agora, vamos completar alguns conceitos que ainda não foram abordados, e ver como gerar e disponibilizar uma aplicação em um arquivo único – o “JAR” 😀

Classes, objetos, instâncias, propriedades e métodos

No post anterior, a definição de “Classe” foi descrita como “… uma forma de agrupar um conjunto de atributos nomeados e qualificados, que representam uma unidade de informação complexa, para a qual podemos criar funções associadas a este conjunto, que realizem ações baseadas nas propriedades e mudanças de estado dessas propriedades.

No exemplo do PingPong, as propriedades da classe são os valores que registram o estado do objeto — as coordenadas atuais da bolinha e a direção que ela está se movendo — e o método de movimentação da bolinha — PingStep() — muda o estado do objeto, atualizando as coordenadas e desenhando novamente a bolinha na tela.

Usando um Array de objetos, eu posso criar várias instâncias da classe PingPong2, cada uma delas com o “seu” controle de movimento. Eu consigo facilmente acrescentar uma cor para as bolinhas, criando uma nova propriedade na classe, e sorteando uma cor no construtor, ou criando um método pata atribuir a cor, e consumindo essa propriedade no momento de fazer a pintura.

Modificadores de acesso

Existem atributos especiais que eu posso usar da declaração de classes, propriedades e métodos. Eles permitem modificar a visibilidade da classe, propriedade ou método, quando utilizamos herança de classes. Nem todos os modificadores podem ser aplicados a estes três componentes ( classe, propriedade e método), vamos a eles:

private

Este modificador pode ser aplicado apenas para propriedades e métodos, e indica que o elemento em questão somente será visível para os métodos da própria classe.

protected

Este modificador permite que o elemento em questão somente seja visível por uma classe que use esta como herança.

<default>

Quando não utilizamos nenhum modificador ao declarar a classe/propriedade/método, o Java assume o comportamento default, que garante a visibilidade da classe, propriedade ou método apenas para outras classes do mesmo pacote.

public

Este modificador pode ser aplicado para a classe, propriedades e métodos. Ele indica explicitamente que o elemento para o qual ele foi aplicado será visível ( e pode ser utilizado ) por qualquer outro código que consuma a classe.

final

Indica que o elemento para o qual ele foi aplicado não pode ser sobrescrito por herança. Isto é, você não pode herdar a classe, ou sobrescrever um método. Vamos ver isso com mais clareza na herança de classes.

abstract

Modificador aplicado apenas para a classe, impede a classe de ser instanciada. Uma classe abstrata é apenas uma definição, sem a implementação. Parece não fazer sentido no momento, mas isso é um recurso bem interessante.

static

Esse modificador especial, quando aplicado a uma propriedade da classe, faz com que a propriedade faça parte da classe em si, e não da sua instância. Ela passa a fazer parte da classe em si, e se comporta como se fosse “compartilhada” entre as instâncias. Inclusive, uma propriedade estática pode ser consultada sem eu precisar criar um novo objeto ou instância da classe, bem como um método static pode ser executado sem a criação de uma instância — desde que este método não acesse nenhuma propriedade, ou acesse apenas uma propriedade também static.

A criação de métodos static permite emular um comportamento de uma “função”. Um método public static pode ser chamado para uma determinada finalidade, sem a necessidade de se criar uma instância da classe em si, da mesma forma que uma função de escopo global de aplicação escrita em outras linguagens.

Antes do pacote e da herança …

Bem, para chegar nesses conceitos, antes de mais nada precisamos entender como podemos criar e usar mais de uma classe. A grosso modo, devemos prezar por usar uma classe por arquivo fonte. É possível criar mais de uma classe em um fonte .java, porém apenas uma pode ser declarada como pública, e isso tem seus “impactos”.  Vamos começar quebrando nosso PingPong2 em dois arquivos java!

O método “main” da classe PingPong2 ficou muito “prático”, mas ele é a aplicação em si, que consome instâncias da sua própria classe … Isso ficaria bem mais elegante se eu criasse uma classe de aplicação em outro arquivo — PingPongApp.java por exemplo — e esse fonte consumisse a classe PingPong2  — que não precisa mais ter o “main”. Vamos ver como fica ?

(arquivo PingPongApp.java)

public class PingPongApp
{
  public static void main(String args[]) throws InterruptedException
  {
    int balls = 1;
    if ( args.length > 0 )
      balls = Integer.parseInt(args[0]);
    if ( balls < 1 )
      balls = 1;
    PingPong2[] aBalls = new PingPong2[balls];
    for ( int i = 0; i < balls; i++)
      aBalls[i] = new PingPong2();
    while (true)
    {
      for ( PingPong2 oBall : aBalls )
        oBall.PingStep();
      Thread.sleep(250);
    }
  } 
}

Agora, removemos o “main” do arquivo PingPong2.java, que fica apenas com a classe e a sua implementação:

(arquivo PingPong2.java)

import java.util.Random;

public class PingPong2
{

  private static char escCode = 27;
  private static Random rand = new Random();

  private int row,col;
  private int rowinc = 1 ,colinc = 1;

  public PingPong2()
  {
    row = rand.nextInt(25)+1;
    col = rand.nextInt(80)+1;
    if ( rand.nextInt(2) > 0 )
      rowinc = -1 ;
    if ( rand.nextInt(2) > 0 )
      colinc = -1 ;
  }

  public void PingStep()
  {
    System.out.printf("%c[%d;%df ",escCode,row,col);
    if ( row + rowinc < 1 || row + rowinc > 25 )
      rowinc = -rowinc;
    row += rowinc;
    if ( col + colinc < 1 || col + colinc > 80 )
      colinc = -colinc;
    col += colinc;
    System.out.printf("%c[%d;%dfO%c[1D",escCode,row,col,escCode);
  }

}

Agora, com estes dois arquivos na nossa pasta de testes “\Projetos\Java”, vamos apagar todos os arquivos “.class” ( byecodes compilados ) , compilar a nossa aplicação “PingPongApp.java”, e executá-la, usando os comandos :

del *.class
javac PingPongApp.java
java PingPongApp

Por que funcionou ? 

Hummm… e não era pra funcionar ? Bem, eu tenho dois fontes java … o PingPongApp.java e o PingPong2.java. Eu mandei compilar apenas o PingPongApp.java, que gerou o bytecode PingPongApp.class … mas eu não menciono em nenhum lugar do meu PingPongApp que eu preciso do fonte PingPong2.java … e se eu olhar na minha pasta, o bytecode  “PingPong2.class” foi gerado também ?! Bruxaria !

R: O compilador do Java identifica que o fonte PingPongApp.java cria uma uma instância de uma classe chamada “PingPong2”. Para ser possível consumir essa classe, o compilador verifica se existe um “PingPong2.class” (bytecode) na pasta atual, e depois nas pastas configuradas na variável de ambiente CLASSPATH. Se ele encontrar o bytecode dessa classe, ele consegue compilar o PingPongApp. Mas como o bytecode PingPong2.class ainda não existe, o compilador verifica se existe um fonte java com o mesmo nome no diretório atual … e se existir ele compila 😀 Afinal, como o nome do fonte é o nome da classe, fica fácil para ele resolver !

E, o que pode dar errado ? 

Da forma que este pequeno aplicativo foi feito, eu tenho dois arquivos “.class”, onde o PingPongApp.class depende do PingPong2.class. Sem esta dependência compilada, se eu executar o PingPongApp, ocorre o erro :

Exception in thread "main" java.lang.NoClassDefFoundError: PingPong2
at PingPongApp.main(PingPongApp.java:16)
Caused by: java.lang.ClassNotFoundException: PingPong2
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)

JAR  – Java ARquive

Uma das formas de você disponibilizar uma aplicação ou um conjunto de classes é o arquivo JAR. Ele é um arquivo compactado, que contém as classes (bytecodes) e um arquivo de “manifesto”, entre outros recursos que a aplicação ou biblioteca utilize. Por exemplo, o nosso programa de PingPong poderia ser disponibilizado em um arquivo JAR.

Primeiro, vamos criar um arquivo texto para ser o “manifesto”. O nome do arquivo deve ser  “manifest.mf”, e deve conter apenas a linha:

Main-class: PingPongApp  

Agora, criamos o JAR do Ping Pong Multiball usando o comando abaixo:

jar -cmf manifest.mf ThePingPong.jar PingPong2.class PingPongApp.class

Com isso, eu crio um arquivo chamado “ThePingPong.jar”, e ele contém o aplicativo PingPongApp.class e a classe que ele depende PingPong2.class, e o manifesto. Agora, para executar o aplicativo no formato JAR, eu chamo o java com o parâmetro  “-jar” seguido do arquivo em questão:

java -jar ThePingPong.jar

Com isso, eu disponibilizo esta aplicação em um arquivo único, que leva consigo as dependências internas do código. O manifesto possui muitas funcionalidades, por hora a única que nós usamos foi informar qual é a classe que contém o método “main” que deve ser executado quando o JAR for executado.

Packages

“Package” é uma forma do Java usada para aglutinar e organizar grupos de classes, sub-pacotes e interfaces. É um mecanismo de organização diretamente ligado a estrutura de diretórios da aplicação. Em poucas palavras, você cria um subdiretório para conter as suas classes, e o nome desse subdiretório é o nome do pacote. E dentro dos fontes das classes que você coloca nessa pasta, você identifica o pacote ao qual ele pertence.

Por exemplo, eu quero disponibilizar o “core” do Ping-Pong como um pacote. Meu pacote vai se chamar “siga0984”. Então, dentro da minha pasta /Projetos/Java, eu crio a pasta “siga0984”, e movo o arquivo “PingPong2.java” para dentro dessa pasta. E, no início do arquivo PingPong2.java, eu acrescento a linha abaixo:

package siga0984;

Então, eu compilo o fonte “PingPong2.java” usando o comando

javac PingPong2.java

Agora, na pasta anterior, onde está o aplicativo “PingPongApp.java”, vamos editá-lo para ele importar esse pacote, acrescentando no inicio desse fonte a linha abaixo:

import siga0984.PingPong2;

Apos apagar os arquivos *.class da pasta, eu compilo novamente o arquivo PingPongApp.java:

javac PingPongApp.java

Agora, para gerar o “ThePingPong.jar”, podemos colocar esse pacote dentro do JAR, junto do manifesto, usando o comando :

jar -cmf manifest.mf ThePingPong.jar PingPongApp.class siga0984\PingPong2.class

Pronto, agora temos um JAR com o aplicativo PingPong e o package siga0984, com a classe PingPong2 ! E esse JAR pode ser distribuído 😀

Conclusão

O mecanismo de pacotes permite criar ainda sub-pastas e sub-pacotes, veremos isso um pouco mais para frente. Mas de modo geral, já dá para se ter uma ideia da flexibilidade desse mecanismo !

Agora que já temos uma “base” da orientação a objetos, pacotes e afins, no próximo post vamos aproveitar os códigos escritos até agora, e criar um Ping-Pong em modo gráfico, dentro de uma janela — apenas criando uma nova classe e implementando uma herança !!!

Espero que estas informações lhes sejam úteis e interessantes, e desejo a todos TERABYTES DE SUCESSO !! 

Referências

 

 

Um comentário sobre “Java from Scratch – Parte 05

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s