Java from Scratch – Parte 02

Introdução

No post anterior (Java from Scratch – Parte 01), vimos um passo a passo de instalar o JDK do Java SE 8 no Windows, e a criação de um ambiente (sem IDE) para compilar e executar programas, e um modesto e simples “Hello, World”. Agora, vamos ver um pouco mais sobe os “fundamentos” da orientação a objetos, e como o Java implementa isso 😀

Programação Procedural

Qualquer terminologia para quem não conhece programação é “grego”, mas eu acho que dá pra explicar a ideia em “poucas linhas”…  Linguagens de programação não orientadas a objetos (ou “procedurais”) possuem uma forma de organização de códigos onde você implementa as funcionalidades de um programa criando “funções”: Uma função é uma parte delimitada de um código, identificado por um “nome” e uma lista de argumentos ( ou parâmetros), criada para realizar um processamento com os argumentos fornecidos. Com isso você consegue modularizar a sua aplicação, e reaproveitar código, consumindo as suas próprias funções.

Exemplo prático : Ping-Pong

Vamos partir de um exemplo prático “old school” … Eu quero fazer um programa, que simule uma bolinha “batendo” nos cantos da tela, sozinha. Ao chamar o programa, em uma interface de linha de comandos — tela “texto”, sem gráfico ou janelas — eu quero que o programa mostre uma bolinha vermelha na tela, que se “move” em uma diagonal, e ao chegar no “limite” da tela, simule o efeito de “bater” neste limite e continuar se movendo na diagonal, 90 graus em relação ao movimento anterior. Na prática, eu quero ver uma bolinha andando sozinha e batendo nos cantos da tela.

Por hora, o desafio é criar isso em Java, identificar quais classes que eu preciso usar para endereçar uma interface “texto” e desenhar e apagar uma letra “O” maiúscula — que será a “bolinha”, para criar a ilusão de “movimento” dessa bolinha batendo nos cantos da tela. Porém, num primeiro momento, eu vou escrever esse programa como uma sequencia ou lista de instruções única, que fará esse trabalho.

Pesquisando

Depois de algumas (horas) de buscas, pude concluir que uma aplicação em modo “texto / console”, para ser executada sem interface gráfica, possui apenas duas formas nativas do Java para entrada e saída de dados: System ( propriedade de entrada System.in e saída via System.out), ou os métodos de acesso do objeto Console — recuperado através do método System.console(). Porém, vi que existem duas implementações de bibliotecas externas que também poderiam ser utilizadas : lanterna e jcurses. Ainda não vamos usar nenhuma biblioteca, o objetivo inicial é fazer “na raça” pra conhecer mais o que a linguagem oferece de forma nativa e dar exemplos práticos da orientação a objetos.

Por fim, achei uma forma interessante de implementar o Ping-Pong: Usando uma sequencia de dados interpretadas pelo aplicativo de console em uso como “sequencias de escape ANSI”. Os objetos de saída disponíveis para a interface de console ou linha de comando do Java permitem apenas saída simples de texto ou dados, não permitem manipular diretamente a tela. Eu não tenho essa flexibilidade de escolher uma coordenada da minha janela de console e escrever um texto qualquer, a saída é “sequencial”, o texto de saída é impresso onde está o “cursor” da tela, e o cursor é deslocado após a impressão .

Só tem um detalhe … Usando o Windows por exemplo, a interpretação desta sequencia especial de caracteres — que me permite por exemplo posicionar livremente o cursor em qualquer linha e coluna da área visível ( dentro da janela do terminal ), trocar a cor e escrever alguma coisa — foi re-implementada recentemente de forma nativa no Windows 10, MAS precisa ser habilitada explicitamente com um programa auxiliar OU uma chamada direta a uma função do sistema operacional — que não é tão simples quando executar um comando … Vamos ver isso depois, primeiro precisamos escrever um PING-PONG !

Vamos ao código , pedaço por pedaço

Tudo é CLASSE em Java .. então vamos criar uma classe chamada PingPong. Como o nome do fonte em java deve ser o nome da classe com a extensão “.java”, nosso arquivo com o código em Java deve ter o nome  “PingPong.java”. Como vamos criar um programa que deverá ser “executado”, criamos também o “entry-point” ou o ponto de início da execução da classe, o famigerado “public static void main( String args[] )”

class PingPong
{
   public static void main(String args[])
   {
       // aqui vamos implementar o PING-PONG
   }
}

A sintaxe básica de declaração de uma classe em Java é iniciada com a palavra “class”, seguida do nome da classe, seguida por um bloco delimitado por chaves “{}”, onde dentro das chaves declaramos propriedades e métodos — vamos ver isso com mais detalhes mais pra frente. O que importa agora é apenas o método de entrada / execução da classe, o “main”.

Este fonte, exatamente desse jeito, não faz praticamente “nada”. Ele pode ser compilado e executado ( compilar, usando o nosso prompt de comando “preparado” e usando o comando “javac PingPong.java” , e executar usando “java PingPong“). Ao ser executado, a JVM vai carregar o bytecode (o arquivo .class compilado), localizar e encontrar o método main(), e executá-lo … mas ele não tem instrução nenhuma, então o programa simplesmente “termina” sem fazer nada, apenas carregou e descarregou a JVM…

Acrescentando o “miolo”

Primeiramente, para criar o efeito de “movimento” e mover a bolinha (letra O) pela tela ou janela do terminal texto utilizado, eu preciso ter uma forma de imprimir e “apagar” a letra “O” em qualquer coordenada da tela. Um pouco de GOOGLE e Stack Overflow, achei uma forma de fazer isso usando sequencias de ESCAPE ANSI 😀 — Pesquise nos links de referência para maiores detalhes. — Esse post do Stack Overflow tinha o que eu procurava … Stack Overflow – Java gotoxy(x,y) for console applications

A interface de texto de um terminal ( Windows ou Linux, tela cheia ou janela de texto ) possui linhas e colunas. Normalmente os terminais são abertos com resolução mínima de 80×25 ( 80 colunas, numeradas de 1 a 80, e 25 linhas, numeradas de 1 a 25).

Eu posso começar na linha 1 e coluna 1 — ou apenas posição 1,1 — e imprimir uma letra “O”, esperar um pouco, “apagar” a letra imprimindo um espaço em branco na posição 1,1 e desenhar a letra “O” agora na posição 2,2 — uma linha para baixo, uma coluna para a direita, e assim sucessivamente. A “bolinha” está descendo para a direita, e logo deve chegar na linha 25. Quando isso acontecer, eu devo “inverter” o movimento vertical, e começar a “subir” as linhas, e inverter novamente quando eu atingir a linha 1… E, da mesma forma, eu estou movendo a bolinha horizontalmente para a direita — a coluna sempre aumenta — mas ao chegar na coluna 80, eu preciso continuar o movimento voltando em direção a coluna 1 — movendo-se para a esquerda, e inverter o movimento horizontal novamente quando chegar na coluna 1.

Logo, eu preciso de pelo menos 4 variáveis… duas para guardar a linha e coluna atuais da bolinha, e duas para guardar a direção do movimento horizontal e e do vertical. Vamos usar um TIPO PRIMITIVO do Java, o “int” (inteiro):

int row = 1; int col = 1; // Posição atual da "bolinha"
int rowinc = 1 ,colinc = 1; // Incremento de posição da bolinha

Para apagar a bolinha, eu preciso posicionar na coordenada da tela desejada e imprimir um espaço em branco. Para isso , eu uso o seguinte comando:

 System.out.print(String.format("%c[%d;%df ",escCode,row,col));

O método format da classe String monta para mim uma sequencia de bytes para usar a sequencia de escape ANSI de posicionamento de cursor, recebendo como parâmetros o código de escape, as coordenadas a posicionar, e complementa a string com um espaço em branco, que será desenhado na posição do cursor — a variável escCode setá vista daqui a pouco.

Movimentando a bolinha

Bem, antes de desenhar a bolinha, vamos partir da posição atual ( linha e colunas ) e verificar se, ao utilizar os incrementos atuais ( variáveis rowinc e col inc ), a nova coordenada atingiu os limites da tela, e fazer a “inversão” do incremento. Parece complicado, mas não é  .. veja abaixo:

if ( row + rowinc < 1 || row + rowinc > 25 )
   rowinc = -rowinc;
row += rowinc;
if ( col + colinc < 1 || col + colinc > 80 )
   colinc = -colinc;
col += colinc;

Explicando por dentro, traduzindo para português …

Se a linha somada com o incremento de linha for menor que um , ou a linha somada com o incremento de linha for maior que 25
   o incremento de linha é igual ao valor dele mesmo, invertendo o sinal
Acrescente o incremento de linha no valor da linha 
Se a coluna somada com o incremento de coluna for menor que um , ou a coluna somada com o incremento de coluna for maior que 80
   o incremento de coluna é igual ao valor dele mesmo, invertendo o sinal
Acrescente o incremento de coluna no valor da coluna

Quando um incremento é positivo, a operação de acrescentar o valor dele ao valor de outra variável é uma  “soma”, o valor atual será aumentado, mas se o incremento é negativo, acrescentar ele a um determinado valor vai diminuir esse valor — por exemplo, 2 + (-1) = 1.

Pintando a bolinha

A pintura da bolinha vai ser um pouco mais “rebuscada” que a limpeza … pois o meu terminal pode ter um “cursor” setado, que eu posso não ter como “desligar”, então eu uso a sequencia de escape ANSI  ESC [ <row>,<col> f para posicionar o cursor, imprimo a letra ‘O’, e depois uso a sequencis ESC [ 1 D , para mover o cursor uma casa para a esquerda da posição atual — que vai ser justamente a próxima coluna à direita da letra “O” que acabou de ser pintada, então eu imprimo a letra “O” e posiciono o cursor exatamente nela, com a instrução abaixo:

 System.out.print(String.format("%c[%d;%dfO%c[1D",escCode,row,col,escCode));

Esperando um pouco

O Java possui uma instrução de “sleep” — trata-se de um método estático da classe Thread do Java — que faz com que o processo atual “adormeça” e não faça nada por um período de tempo informado como parâmetro em milissegundos. Se eu quero mover e pintar a bolinha na tela quatro vezes por segundo, eu divido 1 segundo por 4, chegando ao valor de 0,25 segundos, e depois multiplico ele por 1000 para chegar ao valor em milissegundos (250). Logo, para fazer uma “espera” de 1/4 de segundo, eu uso o comando abaixo:

Thread.sleep(250);

Loop de Execução

Eu quero que esse programa fique processando e pintando e apagando a bolinha da tela “para sempre”, isto é, até o usuário resolver “derrubar” ou “interromper” o programa em execução. Para isso , eu uso a instrução abaixo:

while (true)
{
 // Código a repetir ...
}

Juntando tudo

De posse de todas essas informações e pedaços de código, agora vamos juntar tudo  .. e o nosso fonte vai ficar assim :

class PingPong
{ 
  public static void main(String args[]) throws InterruptedException
  {
    char escCode = 0x1B; 
    int row = 1, col = 1; 
    int rowinc = 1 ,colinc = 1; 
    while (true)
    {
      System.out.print(String.format("%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.print(String.format("%c[%d;%dfO%c[1D",escCode,row,col,escCode));
      Thread.sleep(250);
    }
  } 
}

Instruções adicionais

  • A variável escCode usa o tipo primitivo “char”, e recebe o código hexadecimal 1B — equivalente ao número 27, que é o código de escape (ESC) usado nas sequencias de escapes ANSI.
  • A declaração do método “main” tem na frente dela uma instrução a mais : “throws InterruptedException”. Isto é necessário devido a um tipo de erro que pode ser retornado pela classe Thread — que usamos no fonte para fazer o a pausa de processamento (sleep).

Compilando e executando

Para compilar, execute “javac PingPong.java”, e para executar , use “java PingPong”. Podemos fazer isso no próprio terminal de linhas de comando do Windows, em um terminal de conexão remoto (PUTTY/SSH) no Linux, ou mesmo em um terminal texto de uma máquina Linux. Vamos ver o que acontece ao rodar o programa no Windows ? — Eu abri esse terminal definindo o tamanho dele com 80×25:

PingPong01

Então, não tem NADA de PingPong aí …risos … como eu comentei no inicio do post, o console texto do Windows por default não interpreta as sequencias de escape ANSI … logo, como elas não são interpretadas pelo terminal como “comandos” de interface, o terminal imprime a tela o conteúdo que o programa enviou… Agora, ao fazer isso em um Linux … (Ubuntu 16 em modo texto em uma VirtualBox):

PingPong02

No círculo vermelho, está a letra “O” passeando na tela … e você pode ver o “estrago” que ela já fez ao passar pela tela 😀

Existe uma forma de, por exemplo, no Windows 10, habilitar o tratamento das sequencias de escape ANSI … basta baixar um aplicativo chamado ANSICON, e executá-lo no terminal de comandos, antes de chamar a aplicação em Java. O Download desse aplicativo para o Windows pode ser feito no link https://www.npackd.org/p/ansicon/1.89. Veja como ficou:

PingPong03

Bem, como o programa fica em looping “para sempre”, você deve interromper a execução do programa usando a combinação de teclas Control+C….

Se você montou o ambiente para compilar e executar este programa, experimente ver o que acontece se você remover ou comentar a linha que faz o sleep() … 😀

Conclusão

Por hora, para um “começo” ainda tem muitas lacunas e informações a serem vistas. A partir do próximo post, vamos ver com uma lupa o que faz e por que as coisas acontecem nesse código, linha por linha, e ver que existem várias formas de se obter o mesmo resultado 😀

Agradeço a todos novamente pela audiência, e lhes desejo TERABYTES DE SUCESSO !!!

Referências

 

Uma resposta em “Java from Scratch – Parte 02

  1. Pingback: Java from Scratch – Parte 03 | Tudo em AdvPL

Deixe um comentário