Java from Scratch – Parte 06

Introdução

No post anterior (Java from Scratch – Parte 05), separamos a aplicação da classe que desenha e anima as bolinhas, vimos o que é um package, e uma forma simples de gerar um arquivo único para a aplicação (JAR). Agora, vamos aproveitar esse código, e criar um Ping-Pong com interface gráfica 😀

Interface gráfica com Java

O Java tem suporte nativo para aplicações com interface gráfica (Janelas/GUI) através de duas bibliotecas: AWT e SWING. A AWT usa e depende de recursos do sistema operacional em uso no equipamento para desenhar as telas, enquanto a SWING é independente de plataforma — ela é a responsável pela renderização — entre outras diferenças. Embora existam diferenças de “peso” de execução entre os componentes de ambas, Swing possui mais funcionalidades, suporte a MVC, features avançadas, etc.

Como desenhar gráficos na interface ?

Bem, essa pergunta consumiu algumas horas de pesquisa… Os componentes padrão quando se cria uma aplicação com interface gráfica (GUI), normalmente usamos componentes que desenham-se na interface, e podem usar painéis e mecanismos de alinhamento… como eu quero desenhar uma ou mais “bolinhas”, existem duas alternativas: Usar uma imagem de uma bolinha, ou o modo “roots” : Fazer o desenho de uma bolinha 😀

Vamos no modo “roots” 

A pesquisa e o modo encontrado foi — segundo as fontes pesquisadas — a forma mais  “elegante” de se implementar o mecanismo de pintura da tela. Eu consigo desenhar um círculo usando o componente “java/awt/geom/Ellipse2D”, então eu crio uma classe especialista para o PingPong — chamada PingPongPanel, herdando um JPanel — acrescento o painel como o conteúdo do meu JFrame, e sobrescrevo o método responsável pela pintura dos componentes do painel, para também pintar os círculos que eu quero, na posição da tela desejadas para cada círculo. Ao contrário da implementação original em modo texto, eu não “apago” cada bolinha, eu simplesmente redesenho a tela com as bolinhas nas posições novas, no loop de execução do programa principal.

A classe PingPong2, que no post anterior virou um componente do pacote siga0984, também precisa de alterações. Primeiro, o método PingStep() inicialmente implementado também apagava e desenhava as bolinhas, e ele não previa um passo de movimento maior que um , e nem uma “margem” para verificar se uma bolinha vai bater no limite da tela … Então, vamos começar pela “nova classe” PingPong2:

*** arquivo siga0984/PingPong2.java ***

package siga0984;

import java.util.Random;

public class PingPong2
{
  private static Random rand = new Random();

  private int row,col;
  private int ballsize = 1, step = 1;
  private int rowinc = 1 ,colinc = 1; 
  private int maxrow = 25;
  private int maxcol = 80;

  public int getRow() { return row; };
  public int getCol() { return col; };
  public void setSize( int r ) { ballsize = r; }
  public void setStep( int s ) { step = s; }

  public void setBounds( int mrow, int mcol )
  {
    maxrow = mrow;
    maxcol = mcol;
  }

  public void InitBall()
  {
    row = rand.nextInt(maxrow-ballsize)+1;
    col = rand.nextInt(maxcol-ballsize)+1;
    if ( rand.nextInt(2) > 0 )
      rowinc = -1 ;
    if ( rand.nextInt(2) > 0 )
      colinc = -1 ;
  }

  public void PingStep()
  {
    if ( row + ( step * rowinc ) < 1 || row + ballsize + ( step * rowinc ) >= maxrow )
      rowinc = -rowinc;
    row += ( step * rowinc );
    if ( col + ( step * colinc ) < 1 || col + ballsize + ( step * colinc ) >= maxcol )
      colinc = -colinc;
    col += ( step * colinc );
  }

}

Agora a nossa classe de PingPong possui mais propriedades:

row e col — Já existentes, correspondem a coordenada de tela (linha e coluna respectivamente, em modo texto ou gráfico) onde a bolinha será desenhada.

ballsize  — Corresponde ao tamanho da bolinha, valor dafault = 1.

step — Corresponde ao “passo”, isto é, quantas unidades serão acrescentadas nas coordenadas ao mover a bolinha em uma determinada direção — default = 1.

rowinc e colinc  permanecem iguais, eles apenas tem o valor indicador de direção ( 1 ou -1 )

maxrow — Numero de linhas disponíveis na tela, seja modo texto ou gráfico, default = 25 para modo texto.

maxcol — Numero de colunas disponíveis na tela, seja modo texto ou gráfico, default = 80 para modo texto.

Como agora temos mais parâmetros para considerar, e a classe não será mais responsável por desenhar as bolinhas, apenas fazer a “movimentação” delas, eu preciso disponibilizar mais alguns métodos, para consultar as coordenadas de uma bolinha, e para informar os valores a serem usados nas propriedades adicionais, necessárias para o modo gráfico.

Através dos métodos getRow() e getCol(), eu consigo de “fora” da classe — do programa que usa / consome a classe — pegar o número da linha e coluna atuais da bolinha, e através dos métodos setSize() e setStep(), definir o tamanho (diâmetro) da bolinha e o “passo” (incremento de movimento) da bolinha.

Usando o método SetBounds(), eu informo os limites de tamanho da minha interface, seja texto ou gr;afico, e ao invés de sobrescrever o construtor, eu criei um método de inicialização, que sorteia a posição inicial da bolinha levando em conta o tamanho da bolinha e da tela onde ela será pintada.

E, por fim, o método PingStep() agora não pinta mais nada, e faz as verificações de atingimento do limite de tela considerando também o tamanho flexível da interface, e o tamanho da bolinha.

Fonte final da aplicação

Vamos pro “finalmente”, e depois as explicações. Assim ficou o fonte PingPongGUI.java, feito para executar o Ping-Pong em interface gráfica:

// Importa tudo o que é necessário para essa aplicação 

import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.geom.Ellipse2D;

// inclusive a classe PingPong2 que está no Package 
import siga0984.PingPong2;

public class PingPongGUI 
{
  public static void main(String args[]) throws InterruptedException
  { 
    int balls = 1;
    // Recebe o numero de bolinhas pela linha de comando 
    if ( args.length > 0 )
      balls = Integer.parseInt(args[0]);
    if ( balls < 1 )
      balls = 1;

    // Cria a janela principal
    JFrame frame = new JFrame("Ping-Ping GUI in Java");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(800, 600);
    frame.setLocation(100,100);

    // Cria o array para guardar as bolinhas
    PingPong2[] aBalls = new PingPong2[balls];

    // Cria o painel do PingPong para colocar como conteudo do JFrame
    PingPongPanel panel = new PingPongPanel();
    frame.getContentPane().add(panel);

    // Passa a referencia do array de bolinhas para o painel
    panel.aBalls = aBalls;

    // Ativa a janela 
    frame.setVisible(true);

    // Pega as dimensões internas do painel 
    Rectangle r = panel.getBounds();

    // Cria e Inicializa as bolinhas 
    for ( int i = 0; i < balls; i++)
    { 
      PingPong2 oBall = new PingPong2();
      oBall.setBounds(r.width,r.height);
      oBall.setSize(10);
      oBall.setStep(i+1);
      oBall.InitBall();
      aBalls[i] = oBall;
    }

    // Looping que invalida a tela 
    // e repinta todas as bolinhas 
    while ( true )
    {
      panel.validate();
      panel.repaint();
      Thread.sleep(50);
    }

  }

};

// Cria uma classe de painel para o Ping-Pong, herdando o JPanel

class PingPongPanel extends JPanel
{

  // Cria uma bolinha padrao , diametro de 10 pixels
  static Ellipse2D oCircle = new Ellipse2D.Double(0, 0, 10, 10 );

  // Propriedade para guardar o array de bolinhas
  PingPong2[] aBalls;

  // Sobrescreve o evento de pintura de componentes, 
  // Para que a pintura da janela pinte as bolinhas 

  public void paintComponent (Graphics g)
  {
 
    // Seta que a cor de fundo será preta 
    setBackground(Color.BLACK);

    // Propaga a pintura dos demais componentes para a classe superior
    super.paintComponent(g);

    // Faz um cast da interface gráfica 
    // para ser possivel desenhar os círculos 
    Graphics2D g2 = (Graphics2D) g;

    // Seta a cor do desenho 
    g2.setColor(Color.WHITE);
  
    // E Para cada bolinha do array 
    for ( PingPong2 oBall : aBalls )
    {
      // Faz a movimentação
      oBall.PingStep();

      // usa o objeto do círculo já montado 
      // apenas setando as coordenadas atuais, e desenha o círculo na tela 
      oCircle.setFrame(oBall.getRow(),oBall.getCol(),10,10);
      g2.draw(oCircle);
    }
  }
};

Por hora, em ambiente gráfico, o diâmetro da bolinha está fixo em 10 pontos, a janela desenhada na tela possui 800 x 600 pontos gráficos (colunas x linhas), e uma coisa interessante: As bolinhas possuem velocidade diferente. Ao criar cada bolinha, eu defino que o “stepping” ( incremento de coordenadas de movimento ) é 1 para a primeira bolinha, 2 para a segunda, e assim por diante. Então, com 10 bolinhas , a décima bolinha “pula” de 10 em 10 pontos cada vez que ela é desenhada, fazendo ela parecer mais  “rápida” que as outras … quando na verdade ela “pulou” um intervalo maior !

Conclusão

Pode parecer um pouco “complicado” no início … depois piora, e depois melhora ! São muitos eventos e componentes envolvidos em uma sequência hierárquica para criar e interagir com componentes de interface, tudo ao seu tempo, em passos 😀

Espero que vocês estejam aproveitando este conteúdo, e lhes desejo TERABYTES DE SUCESSO !!! 

Referências

 

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

 

 

Java from Scratch – Parte 04

Introdução

No post anterior (Java from Scratch – Parte 03), vimos linha a linha o que faz cada instrução do fonte do Ping-Pong 😀 Agora, vamos ver outras formas de escrever este mesmo código, e começar a usar a orientação a objetos propriamente dita.

Orientação a Objetos

As linhas de código responsáveis por fazer a movimentação da “bolinha” pela tela estão todas no método “main” da classe. Se eu quiser, por exemplo, fazer duas bolinhas (Letras “O”) passear na tela ao mesmo tempo, eu teria que — por exemplo — trocar as variáveis que armazenam a linha e coluna (row e col) atuais da bolinha, e as direções horizontal e vertical atuais (rowinc e colinc) para um tipo “Array” de inteiros, e aplicar cálculos e apagar/pintar a bolinha para os elementos desse array … Isto significaria mexer no código inteiro.

Agora, se usarmos efetivamente a “orientação a objetos”, isso fica muito, muito mais fácil. Vamos ver como, escrevendo agora a classe PingPong2, aproveitando praticamente todo o código escrito na PingPong, e acrescentando algumas “coisinhas” — criando 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);
  }

  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 sim, parece um “polvo” com pernas, braços e tentáculos, esse fonte virou de ponta cabeça. Acho que explicar cada pedaço vai exigir mais alguns posts … Nesse fonte usamos o primeiro “import”, uma classe com construtor , propriedades privadas estáticas e não-estáticas, e um novo método público — além no nosso “main”, que ganhou um corpo completamente diferente, que passa a receber um argumento de linha de comando, e é capaz de “animar” múltiplas bolinhas aleatórias !!!!

Testando o PingPong

Salve o fonte como “PingPong2.java”, compile com “javac PingPong2.java”, e execute com “java PingPong2”. Uma bolinha surge em um ponto qualquer da tela, e vai em uma direção, batendo nas bordas. Agora, execute “java PingPong2 8” — E você verá OITO bolinhas aparecerem na tela, cada uma andando em uma direção, todas se movendo praticamente ao mesmo tempo, e batendo nos cantos da tela !!

 

PingPong2 01

No fonte inicial da classe PingPong, existia apenas o método “main”, que fazia “tudo”, e desenhava apenas uma bolinha. Na classe PingPong2, usamos uma grande parte da flexibilidade da orientação a objetos do Java, para criar uma classe que é responsável por “animar” uma bolinha, e pode ser “instanciada” várias vezes, animando múltiplas bolinhas em pontos distintos da tela. Antes de mais nada, vamos dar uma “passada rápida” no conceito de orientação a objetos, e em suas terminologias, tomando por base a classe de exemplo PingPong2().

Uma classe é 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.

Na classe usada como exemplo, o caractere de escape, o numero da linha e coluna atuais da bolinha e a direção horizontal e vertical que ela se move são “propriedades” da classe PingPong2, e as funcionalidades de inicializar uma nova bolinha, mover a bolinha e o ponto de entrada de execução ( main() ) são métodos desta classe.

Vamos ver a abordagem prática do ponto de vista da execução, para entender o papel das declarações e do resto do código. Vamos começar pelo método main():

public static void main(String args[]) throws InterruptedException
{
  // Quantas bolinhas serão criadas
  int balls = 1;
  if ( args.length > 0 )
    balls = Integer.parseInt(args[0]);
  if ( balls < 1 )
    balls = 1;
  // Criação e animação das bolinhas
  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);
  }
}

Recebimento de parâmetros de linha de comando

O parâmetro args, do tipo Array de Strings, declarado no método main(), pode receber um ou mais argumentos que você informar ao executar a sua classe. Por exemplo, para executar a classe PingPong2.class, você usa o comando “java PingPong2”, certo ? O que mais você colocar depois disso, separado por espaços em branco, será recebido como uma String no array args. Por exemplo, executar a classe usando “javac PingPong2 8”, o número 8 vai ser recebido como uma string, no primeiro (e único) elemento do array args.

Como eu posso ou não informar um argumento adicional, a primeira parte do código cria uma variável do tipo primitivo “int”, para armazenar a quantidade de bolinhas que serão desenhadas na tela, e já atribui o valor 1. Então, o programa verifica se foi informado algum argumento adicional, e caso tenha sido, o programa realiza a conversão desse argumento de String para número, e assume que este será o número de bolinhas a ser desenhado. Como eu posso passar um argumento 0 ou negativo, o programa somente considera o argumento, caso ele seja informado, e seja maior que zero.

Observação : O aplicativo parte da premissa que, se um argumento foi informado, será possível converter ele para um número. Se, ao invés do número 8, você informar uma letra, a aplicação será interrompida com a ocorrência de erro  “Exception in thread “main” java.lang.NumberFormatException: For input string: “A”“. Mais para a frente vamos ver formas de lidar e tratar esse tipo de ocorrência.

Declaração de um Array de Objetos

PingPong2[] aBalls = new PingPong2[balls];

Dessa forma nós declaramos um array de objetos de tamanho fixo, preparado para conter objetos do tipo “PingPong2”, limitado ao número de bolinhas que eu quero desenhar na tela — armazenado na variável balls. Lembrando que essa é apenas a definição do array aBalls. Após a instrução ser executada, a variável aBalls passa a existir, como um container / lista de objetos do tipo PingPong2, com um determinado número de elementos (informado na variável balls), mas cada elemento dessa lista não contém um objeto … o valor de cada elemento está “null”.

Criação dos objetos para popular o Array 

for ( int i = 0; i < balls; i++)
   aBalls[i] = new PingPong2();

A instrução for() é uma instrução de repetição um pouco mais sofisticada que o while(). Ela permite a execução de uma expressão de inicialização, executada apenas uma vez, que foi usada para declarar e inicializar uma variável inteira chamada “i” com o valor zero, que será visível somente dentro do escopo ( ou bloco ) de instruções após o for(). Permite também uma expressão condicional — i < balls — que será verificada ao final do bloco para determinar se o bloco deve ser executado novamente ( igual ao while() ) , e uma última expressão, executada a cada final do bloco executado, que no nosso caso incrementa a variável i. Tudo isso, serve para que a linha abaixo do for() seja executada uma vez para cada bolinha que deve ser criada. Trata-se de uma atribuição no elemento de número i do Array aBalls, que recebe uma nova instância (objeto) da classe PingPong2. Agora sim, após esta execução, cada elemento do Array aBalls terá uma instância distinta da classe PingPong2.

Em Java e c++, entre outras linguagens, o primeiro elemento de um array é o elemento  “0” (zero). Em AdvPL e outras linguagens, o primeiro elemento é o “1” (um).

Método construtor da classe PingPong2

Cada operação “new PingPong2()” vai executar o método PingPong2() da classe PingPong2. Quando criamos um método com o mesmo nome da classe, este método é chamado de “construtor”, pois cada vez que um programa chamar um “new PingPing2()”, esse método será executado.

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 ;
}

No construtor de uma nova bolinha, usamos uma função randômica da classe Random() do Java, para sortear  um numero entre 0 e o número informado menos um … Logo, nextInt(25) sorteia um numero entre 0 e 24. Sempre somamos 1 a este resultado, pois ele será a linha inicial da bolinha na tela. E, sorteamos também um número entre 0 e 79 ( somando 1 ao resultado ) para se a coluna da bolinha. Assim, cada bolinha que vai ser desenhada na tela pode ter um ponto de início diferente. E, partimos por default que a bolinha originalmente está se movendo para a direita e para baixo, mas ao sortear um numero entre 0 e 1, se ele for maior que zero, a bolinha pode inciar o movimento para a esquerda, o sorteamos de novo um numero entre 0 e 1, para decidir se a bolinha deve começar o movimento subindo, ao invés de descer a tela.

Animando todas as bolinhas

while (true)
{
  for ( PingPong2 oBall : aBalls )
    oBall.PingStep();
  Thread.sleep(250);
}

Aqui a mágica acontece. Primeiro, eu tenho um for() diferenciado, especialista. Esta forma de declaração cria dentro do escopo de repetição uma variável chamada oBall, do tipo objeto “PingPong2”, que a cada repetição do bloco, vai conter um elemento do array aBalls. Esta sintaxe de uso seria o equivalente a fazer isso aqui:

for ( int i = 0; i < aBalls.length; i++)
{
   PingPong2 oBall = aBalls[i];
   /* demais instruções a repetir ... */ 
}

Da mesma forma que a while() e a if(), se eu não iniciar um bloco  — abrindo  e fechando chaves para delimitar as instruções que eu quero executar / repetir — apenas a próxima instrução do for() será executada por n repetições. No caso, vamos executar o método PingStep() para cada uma das bolinhas, uma de cada vez.

Movimentação e pintura

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);
}

É exatamente igual ao primeiro PingPong .. porém nesse contexto, as variáveis row, col, rowinc e colinc são propriedades da do objeto. Como cada objeto possui a sua coordenada e movimento, cada chamada do método PingStep() é executada para a instância atual do objeto, obtida no array de objetos. Assim, movemos todas as bolinhas desenhadas na tela em um loop, e depois damos uma pausa de 1/4 de segundo, para mover todas novamente. 😀

Importação de pacotes 

import java.util.Random;

Salvo algumas classes básicas do Java, qualquer recurso adicional da linguagem é disponibilizado na forma de “packages” — ou pacotes. Para consumir uma função de um determinado pacote, no início do fonte .java, o pacote necessário deve ser “importado”, usando o statement “import”, seguido do nome completo do pacote. Veja por exemplo, o programa utiliza uma classe chamada “Random”, para a geração de números randômicos (ou “sorteados”). A documentação dessa funcionalidade está em https://docs.oracle.com/javase/8/docs/api/java/util/Random.html, e ao abrir a documentação, repare no trecho destacado em vermelho:

Java - Random

Este é o nome do pacote que disponibiliza essa funcionalidade. Para usá-la no seu fonte, você deve fazer o “import” desse pacote. Sem ele, o fonte não compila, pois o compilador não sabe quem é  “Random()” …. Mesmo sendo uma funcionalidade trivial, o Java foi concebido para trabalhar com o mecanismo de declaração explícita de dependências, assim ele carrega para dentro da maquina virtual apenas os componentes necessários para executar um determinado código.

Conclusão

Ainda falta explicar mais da metade dos “porquês” do código, não exatamente sobre a funcionalidade do PingPong2, mas sim sobre a orientação a objetos em si, os modificadores de acesso de propriedades e métodos (public , private, static, final), onde e quando podemos usá-os e o que isso muda no comportamento. E isso vai abrir caminho para as heranças de classe, classes de interface ( ou “abstratas” ) … 😛

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

Referências

 

Java from Scratch – Parte 03

Introdução

No post anterior (Java from Scratch – Parte 02), vimos a montagem de um programa “Ping-Pong”, em modo texto, por hora sem usar praticamente “nada” de orientação a objetos. Antes de ver mais sobre propriedades e métodos, vamos ver com uma lupa esse fonte, e explicar cada pedacinho dele 😀

O Fonte – Arquivo “PingPong.java”

Propositalmente o fonte abaixo possui uma numeração de linhas, para facilitar as explicações sobre o que cada pedacinho dele faz:

01. class PingPong
02. { 
03.   public static void main(String args[]) throws InterruptedException
04.   {
05.     char escCode = 0x1B; 
06.     int row = 1, col = 1; 
07.     int rowinc = 1 ,colinc = 1; 
08      while (true)
09.     {
10.       System.out.print(String.format("%c[%d;%df ",escCode,row,col));
11.       if ( row + rowinc < 1 || row + rowinc > 25 )
12.         rowinc = -rowinc;
13.       row += rowinc;
14.       if ( col + colinc < 1 || col + colinc > 80 )
15.         colinc = -colinc;
16.       col += colinc;
17.       System.out.print(String.format("%c[%d;%dfO%c[1D",escCode,row,col,escCode));
18.       Thread.sleep(250);
19.     }
20.   } 
21. }

Começando na linha 01, temos a declaração da classe PingPong, seguido da abertura de chaves na linha 02, e o respectivo fechamento na linha 21. Dento das chaves após a declaração da classe, devemos colocar as propriedades e métodos da classe.

Cada abertura de chave no Java ( e no C++ também ) inicia um bloco ou um “contexto”, ao longo dos posts vamos explorar mais o que isso significa.

Na linha 03, temos a declaração padrão do método reservado “main”, na forma apropriada para ser a entrada/execução dessa classe, seguido da abertura de chaves na linha 04 para escrever o código que compõe este método, e o respectivo fechamento do método na linha 20.

Devido ao uso da classe Thread dentro do método, houve a necessidade de complementar a declaração do método com a instrução “throws InterruptedException” — Veremos o por que disso mais pra frente, isso está relacionado a forma que o Java espera que sejam implementados tratamentos de erro.

Na linha 04, iniciamos o “corpo” ou a implementação o método main, com a seguinte instrução:

char escCode = 0x1B;

Esta linha está declarando a existência de uma variável, do tipo primitivo “char”, com o nome  “escCode”, e já atribuindo a ela o número 27 — que, escrito em notação HEXADECIMAL, é representado por “0x1B”. Existem oito tipos primitivos no Java, e são chamados assim pois eles não são objetos, não possuem propriedades ou métodos, apenas um determinado valor. Está curioso para saber quais são os demais tipos primitivos, dê uma olhada no link W3Schools – Java Data Types 

Foi necessário declarar uma variável com este valor para a utilização das Sequencias de Escape de terminal ANSI — para conseguir movimentar o “cursor” para qualquer coordenada da tela livremente, para imprimir e apagar da tela a letra “O”.

Por quê o conteúdo desse “char” está em HEXADECIMAL ? Eu preciso fazer isso ? 

R: Não, você não é obrigado a usar uma notação hexadecimal. O autor do post que fez um exemplo de uso das escape sequences no Java usou dessa forma no exemplo dele. O tipo primitivo “char” poderia muito bem receber simplesmente o número 27, assim:

char escCode = 27;

O comportamento e funcionamento será exatamente o mesmo. O Java sabe que o valor 27, escrito dessa forma, indica um número em notação decimal (base 10). Quando eu inicio um sequencia de caracteres com o identificaoor “0x” (zero xis), o Java entende que os valores a partir deste ponto estão representados em Hexadecimal.

Na linha 06 eu tenho algo parecido, mas com outro tipo primitivo de variável, e com declarando e inicializando na mesma linha mais de uma variável:

int row = 1, col = 1;

A instrução acima declara e inicializa duas variáveis de tipo primitivo “int” (inteiro), uma chamada “row”, atribuindo a ela o valor 1, e outra chamada “col”, também do tipo inteiro, também inicializada com o valor 1. Para fazer estas duas declarações de variáveis do mesmo tipo, na mesma instrução, eu apenas separo as variáveis com uma vírgula.

Experimente remover as atribuições ( = 1 ) dessa linha, e compilar o código, e veja o que acontece …

As variáveis que eu chamei de row e col contém o valor da linha e coluna “atuais” do cursor na tela, que serão usados para apagar e desenhar a “bolinha”. Na linha 07 eu crio mais duas variáveis do tipo primitivo “int”, e inicializo ambas com o valor 1, para controlar se a bolinha está se movendo horizontalmente para a esquerda ou para a direita, e se ela está se movendo verticalmente para cima ou para baixo. 

int rowinc = 1 , colinc = 1;

Visto isso, na linha 08 é iniciado um bloco de repetição condicional — usando a instrução while ( boolean <condition> )  Veja a documentação dela no link W3Schools – Java While Loop 

No nosso caso, a condição informada é valor booleano e constante truecom o objetivo do código dentro do contexto ou bloco da instrução while deve ser repetido “para sempre” (ou até o programa ser interrompido pelo usuário). O início e final do bloco a ser repetido é demarcado pela abertura de chaves na linha 09, e respectivo fechamento da linha 19.

Entre a linha 10 e a linha 18, estão as instruções que fazem a “mecânica” da aplicação — apagar a bolinha, calcular as novas coordenadas para desenhar a bolinha, verificar se a bolinha vai “bater” nas bordas da tela e mudar a direção. Começando pela linha 10, com a seguinte instrução:

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

Explicar essa instrução aqui render alguns parágrafos … Esta instrução é uma chamada “encadeada” de métodos. Na classe System, usando o objeto de saída out, é chamado o método print(), que recebe como argumento ou parâmetro o retorno da chamda do método format da classe String, que por sua vez recebe como parâmetros uma String constante, usando elementos que identificam como os demais parâmetros informados do método format() — escCode, row e col — devem ser ‘formatados” para gerar a sequencia de caracteres que será enviada para a tela do terminal.

Têm forma mais simples de fazer isso ? 

R: Sim , claro. O exemplo que eu tomei por base foi feito dessa forma, mas eu poderia simplesmente usar o método “printf()” do objeto de saída “out“, e essa linha ficaria um pouco mais simples e eficiente, e com o mesmo resultado:

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

O método format() da classe String gera uma string de resultado, que eu poderia armazenar em uma outra variável, e posteriormente imprimir esta variável na tela do terminal, e dentro de um arquivo. Como a única coisa que eu foi fazer com este resultado formatado é enviar ao terminal, eu uso o método “printf” ao invés do “print” — pois o printf() é justamente “imprimir formatado”, e recebe os mesmo argumentos que o método format() da String.

Basicamente, na string informada como parâmetro para definir o critério de formatação, cada caractere “%” (por cento) da string, indica o inicio do tratamento do próximo argumento ou parâmetro desta chamada, e deve ser sucedido por um ou mais caracteres identificadores do formato. Por exemplo, %c indica que o argumento deve ser acrescentado no resultado como um caractere (tipo primitivo char), enquanto %d indica que o argumento deve ser tratado como um número inteiro, para compor a string de resultado.

Sendo a variável escCode igual ao caractere de código 27, row contém o valor numérico 1, e row também contém o valor 1, a string resultante dessa formatação será uma sequencia de 7 bytes, onde o primeiro será um caractere de controle ( ESC, código 27 ) , seguido por uma abertura de colchetes, o número 1, um ponto-e-vírgula, mais um número 1, a letra f minusculo, e um espaço em branco. A partir do primeiro caractere ( ESC ), a sequencia será interpretada até a letra “f” pelo terminal ou console utilizado como um comando de posicionamento de cursor, e por fim o espaço em branco será desenhado na posição atual do cursor.

Agora, na linhas 11 e 12 , o primeiro desvio condicional de processamento:

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

A instrução if ( boolean <condition> ) serve para avaliar uma condição informada, e caso esta condição seja verdadeira, a próxima linha ou o próximo bloco (ou contexto) será executado. Caso contrário, a próxima linha ou bloco não serão executados, e a aplicação continua o processamento do programa após a linha / bloco.

A condição usada é uma expressão lógica, obtida como o resultado de duas comparações do resultado de duas operações aritméticas com as variáveis de tipo primitivo “int”,  caso uma delas seja verdadeira. Vejamos por partes:

A primeira comparação é row + rowinc < 1  — O java vai calcular a soma dos conteúdos (valores) das variáveis row e rowinc, e usar o operador < menor que) comparando com o valor 1. Esta comparação retornará verdadeiro se o valor for menor que 1.

A segunda comparação é row + rowinc > 80 —  o Java vai calcular a soma dos conteúdos das variáveis row e rowinc novamente, e usando o operador  > (maior que), compara o resultado obtido com o numero 80. O resultado dessa comparação será verdadeiro caso a soma seja maior que 80.

E, por fim, as duas comparações dentro do “if” estão separadas pelo operador “OU” — “logical OR” — , que no Java (e no C++) é representado pela sequencia “||” (dois caracteres pipe). O operador “ou lógico” entre duas expressões lógicas retorna verdadeiro (true) caso pelo menos uma das expressões comparadas seja verdadeira (true).

Dê uma olhada na documentação do if() e de outros statements no W3Schools — a documentação disponibilizada nele é bem interessante — tudo em inglês, claro — mas existem outras referencias de consulta em Português 😀

Eu não preciso abrir chaves depois do if ?

R: Não precisa, mas pode. Se uma determinada condição deve executar apenas uma instrução caso a condição seja verdadeira, você coloca essa instrução na linha de baixo do if(), e não precisa abrir chaves. Mas você pode abrir e fechar chaves ( c++ também é assim), e se você quiser executar mais de uma instrução caso a condição seja verdadeira, você deve usar as chaves 😀

Voltando pro enunciado, esta verificação é feita antes de realizar a atualização de conteúdo da variável que contém a linha atual da bolinha, para verificar SE, ao fazer a atualização do valor, o resultado ultrapassaria os limites da tela.

E, caso a operação vai atingir os limites estabelecidos, a linha 12 faz a “inversão” do sinal da variável rowinc — Se a variável era 1, ela passa a ser -1, se ela era -1, ela passa a ser -(-1) , ou seja , (+) 1 . .

rowinc = -rowinc;

E, por fim, a linha 13 acrescenta o incremento atual ( positivo ou negativo ) ao valor da variável row:

row += rowinc;

O operador “+=” significa “atribuição com soma” … na prática, o mesmo efeito seria obtido escrevendo o fonte dessa forna:

row = row + rowinc;

Bem, as linhas de 13 a 16 cuidam dos limites e do incremento/decremento das linhas … enquanto as linhas 17, 18 e 19 fazem a mesma coisa para a variável que contém a coluna (col) e o incremento da coluna (colinc) 😀

Por fim, a linha 20, agora com os valores de col e row atualizados, imprime a letra “O” na tela, na coordenada informada por estas variáveis, e ainda acrescenta uma sequencia adicional de escape, para depois de escrever a letra “O”, voltar o cursor uma casa para a esquerda, fazendo ele parar em cima da letra que foi impressa:

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

E, para o programa não fazer a bolinha passear na tela “na velocidade da luz”, na linha 18 usamos uma espera ( sleep ), de 250 milissegundos:

Thread.sleep(250);

E agora ? 

Bem, depois de esperar 1/4 de segundo com a letra “O” na tela, na posição determinada pelas variáveis row e col,  o programa chega na linha 19, que é o fechamento da chave da instrução “while”. Quando isso acontece, a execução do programa retorna para a linha 08 — onde o while() foi declarado — e verifica se a condição informada para o while) é verdadeira.  No nosso caso, sempre vai ser, então o programa continua sendo executado a partir da linha 10, apagando a bolinha, calculando a nova posição da bolinha, imprimindo na tela, e esperando 1/4 de segundo novamente !

Conclusão

Mesmo que você não tenha montado o ambiente com o Java, mas apenas acompanhou o passo a passo da explicação, é possível começar a entender a sequência logica e o comportamento do programa ao ser executado, e começar a assimilar algumas terminologias que serão a base de toda e qualquer linguagem de programação, como :  declaração, atribuição, comparação, desvio condicional, repetição (ou loop) condicional, camada de função (ou método ), retorno de função (ou método) !

Espero que este conhecimento lhe seja muito útil, e lhes desejo novamente TERABYTES DE SUCESSO !!! 

Referências

 

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

 

Java from Scratch – Parte 01

Introdução

Dentre as linguagens de programação de mercado, Java definitivamente possui uma boa fatia de mercado, por muitas razões — Orientação a objeto, relativa simplicidade de implementação, multi-plataforma (inclusive sistemas embarcados e celulares), etc. A alguns meses, resolvi pesquisar com mais ênfase sobre o que é o Java e como funciona. O universo é amplo, SDKs, IDEs, JVM, JRE, J2EE, e por aí vai. Iniciando nesse post, vou compartilhar o que e como eu estou descobrindo mais sobre essa linguagem.

Java from Scratch

Antes de mais nada, visto de uma forma bem simplista, Java é uma linguagem orientada a objetos, que possui uma ampla base de componentes nativos, e uma infinidade de componentes adicionais para múltiplas aplicações. Uma aplicação em Java não gera um arquivo “executável”, mas sim um ByteCode — um código de máquina interpretado por uma máquina virtual (no caso, a JVM — Java Virtual Machine). Logo, um código simples em Java pode ser executado em qualquer equipamento ou plataforma que o seu “interpretador/executor” (JVM) seja homologado.

Embora existam uma infinidade de IDEs (Integrated Development Environment — ou Ambiente Integrado de Desenvolvimento), visando facilitar e padronizar os processos de desenvolvimento, depuração e distribuição (deploy) das aplicações, a criação de aplicações simples em Java possuem requisitos mínimos que, num momento inicial, não dependem de nenhuma IDE. Logo, a abordagem inicial sobre o assunto será extremamente minimalista, apenas para entender os componentes envolvidos e como eles se integram. Vamos começar pelo JRE (Java Runtime Environment) e JDK (Java Development Environment).

  • JRE – Ambiente com os aplicativos necessários para executar um programa Java.
  • JDK – Ambiente com os aplicativos necessários para desenvolver (compilar e debugar) um programa Java.

Se você vai desenvolver uma aplicação Java, você precisa instalar ambos. Para executar uma aplicação Java, você somente precisa do JRE.

Plataformas de execução e desenvolvimento do Java

Existem 4 “plataformas” de execução de aplicações Java : SE (Standard Edition), EE (Enterprise Edition), ME (Micro Edition ) e JAVAFX . Cada uma delas possui suas diretivas de licenciamento, aplicabilidade, capacidades , versionamento e finalidades distintas. Java é ao mesmo tempo uma linguagem e uma plataforma. Os testes e exemplos desse post partem das funcionalidades existentes na plataforma Java Standard Edition Versão 8 (ou apenas JAVA SE 8).  Versões mais novas do Java trazem novos recursos, mas ele possui uma compatibilidade de execução deveras interessante: Uma aplicação java compilada usando um JDK SE 8 roda sem problemas em um ambiente com JRE 8 ou superior. Embora o ciclo de vida do Java SE8 8 esteja “no final”, eu optei por iniciar os estudos nela.

Primeiros Passos

Ao entrar no GOOGLE ou no seu engine de busca favorito, e pesquisar por “JAVA SE JDK 8 DOWNLOAD”, você vai encontrar o site da ORACLE — https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html — onde você pode baixar a versão do instalador do JDK 8 para Windows, RPM para Linux, entre outras plataformas.  Dependendo da distribuição do Linux onde você quer usar o JDK, você pode usar o próprio gerenciador de pacotes e atualizações do Linux para pegar a versão mais atualizada ou aquela que você deseja instalar no seu ambiente.

Instalando o JDK no Windows

Eu comecei pelo JDK 8, mas o procedimento é basicamente o mesmo para qualquer versão. Vá para a página de downloads da Oracle (https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html), aceite os termos de licenciamento e baixe o JDK para Windows 64 bits:

Java 01

Quando eu solicito o Download, eu sou direcionado para uma página de login da Oracle, como eu já tinha criado uma conta — gratuita, de desenvolvimento — na Oracle, após o login o download é iniciado. Finalizado o download, execute o instalador (no caso, o arquivo baixado “jdk-8u241-windows-x64.exe“). Normalmente o Windows pede uma confirmação de execução do instalador, basta conferir se o instalador é da Oracle mesmo, e confirmar. Então, a seguinte tela será exibida:

Java 02

Basta clicar em “NEXT” para continuar a instalação.

Java 03

Pode manter as configurações default, e “NEXT” 😀 Neste momento o JDK será instalalado, e ao término da instalação, a tela abaixo será exibida:

Java 04

Ao clicar no botão “NEXT STEPS”, será aberta a página “https://docs.oracle.com/javase/8/docs/” no navegador padrão configurado no seu ambiente. Ela contém tudo e mais um pouco sobre o Java SE 8 😀  Para finalizar o instalador, basta clicar em “CLOSE”.

O que foi instalado

Se abrirmos a pasta onde o JDK foi instalado, devemos encontrar a seguinte estrutura de diretórios:

bin
include
jre
lib

A pasta BIN possui vários utilitários do JDK, entre eles o que nos interessa para o momento: JAVAC.EXE — o compilador do Java. A pasta INCLUDE possui alguns headers em C/C++ para alguns tipos de integrações, a pasta JRE possui o ambiente de execução do Java, divididos nas pastas JRE/BIN e JRE/LIB. Por hora, o que nos interessa na JRE é p aplicativo JAVA.EXE — o “Executor” de programas 😀

Etapas após a instalação

Como por hora a ideia é acessar o java sem nenhuma IDE, precisamos criar algumas variáveis de ambiente para o JDK e a JRE funcionarem certinho. Por hora, para não mexer nas configurações do Windows, podemos criar um arquivo de lote (BAT) para setar o ambiente de uso do Java, por exemplo: 

@echo off
SET PATH=C:\Program Files\Java\jdk1.8.0_241\bin;%PATH%
SET JAVA_HOME=C:\Program Files\Java\jdk1.8.0_241
SET JDK_HOME=C:\Program Files\Java\jdk1.8.0_241
SET JRE_HOME=C:\Program Files\Java\jdk1.8.0_241\jre
SET CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\jre\lib
Echo Variaveis de ambiente do JAVA definidas.
Echo * Versao do JVM / JRE
java -version
echo * Versao do Compilador JAVA 
javac -version
Echo.

Para realizar meus testes, eu criei uma pasta chamada C:\Projetos\Java , e dentro dela salvei o script acima com o nome de setjava.bat. E, para deixar o acesso ao prompt de comando “mais fácil”, criei um atalho no Desktop com o comando “CMD /k c:\Projetos\Java\setjava.bat”, colocando o nome do atalho de “Java CMD”, e depois alterei a propriedade do atalho para abrir direto na pasta “c:\Projetos\Java“.

Primeiro programa – HELLO WORLD

O primeiro desafio foi fazer um programa o mais simples possível, apenas para confirmar que o compilador e o executor estão “de acordo” — o famoso “Helo World”, apenas mostrar uma mensagem ( texto puro , sem janela ) no prompt de comando…. vamos ao fonte:

class HelloWorld
{
    public static void main(String args[])
    {
        System.out.println("Hello, World");
    }
}

 

Pontos importantes : 

  • JAVA é CASE-SENSITIVE : Letras maiúsculas e minúsculas são diferentes …
  • Use um editor de textos simples, pode ser o NOTEPAD do Windows mesmo.
  • Salve o arquivo com o nome de “HelloWorld.java”, dentro da pasta c:\Projetos\Java

Agora, com o arquivo “HelloWorld.java” criado no disco, vamos executar dois comandos:

javac HelloWorld.java

java HelloWorld

O resultado destes comandos, deveria ser o seguinte:

Java 06

A chamada do compilador não deve mostrar nada, nada mesmo. Se não mostrou nada, a compilação foi feita com sucesso, e agora você deve encontrar no disco um arquivo chamado  “HelloWorld.class”.

E, para executar a classe que acabamos de compilar, usamos o comando “JAVA”, informando apenas o nome do arquivo que contém a classe, sem informar a extensão do arquivo. E, como resultado, devemos ver a mensagem  “Hello, World” sendo mostrada no console.

Por dentro do que houve 

Primeiro, a compilação … vamos rodar a compilação novamente, porém acrescentando o parâmetro “-verbose”  … o comando fica assim:

javac HelloWorld.java -verbose

E, agora sim podemos ver na tela uma parte do que o compilador precisou usar para gerar o bytecode no arquivo  “HelloWord.class” :

Java 07

Eu sei, as letrinhas ficaram pequenas … mas basicamente o compilador verificou as variáveis de ambiente, para buscar nas pastas configuradas tudo o que ele precisava saber para validar se o nosso HelloWorld.java está escrito “certinho”, e gerar a classe compilada no arquivo “HelloWorld.class”.

OS PRIMEIROS PORQUÊ(S)

Bem, agora que o primeiro programa super-simples funcionou … por que ele precisa ser escrito exatamente dessa forma ?

  1. A sintaxe de um fonte em Java é similar ao C++. Case sensitive, escopo de código entre chaves ({ }), o ponto-e-vírgula (;) é usado para indicar o final de uma instrução — ou final de linha.
  2. Cada fonte em java DEVE SER uma CLASSE. E o nome do fonte deve ser o mesmo nome da classe. É uma definição e comportamento da linguagem.
  3. O método implementado deve ser “public static void main(String args[])”, no nosso caso, pois essa é a forma de declarar o método “main”, um método reservado do Java para ser o inicio da execução de um aplicativo. Ele é “public”, pois deve ser visível para componentes fora da classe declarada, “static” pois não depende do instanciamento direto da classe, “void” pois não tem retorno, e por ser “main”, ele deve/pode receber um array de strings.
  4. Como tudo no Java é orientado a objetos, mostrar uma mensagem em uma aplicação de linha de comando — como a do nosso exemplo — é feito pela classe System — uma classe do Java que contém métodos para interagir com entrada e saída de dados — mais especificamente stdin e stdout ( conceito e implementação originalmente em C para entrada e saída de dados , também usado no Unix/Linux). Quer ver tudo o que a classe System faz ? Veja a documentação dela no link https://docs.oracle.com/javase/8/docs/api/java/lang/System.html

Esse CLASS roda no LINUX ?

Sim, o mecanismo de entrada e saída de dados das aplicações executadas em linha de comando no CMD (Windows) ou BASH (Linux) é a mesma. Logo, eu posso pegar uma máquina Linux qualquer, que tenha a JRE 8 ou superior instalada, copiar apenas o arquivo “HelloWorld.class” ( bytecode compilado ) e executá-lo ! Foi exatamente o que eu fiz com o meu Ubuntu 16 — onde previamente eu já havia instalado um JDK:

Java 08

Conclusão

Por hora, a introdução já ganhou um “volume” considerável, e o que acabou de nascer foi apenas um programa que mostra uma mensagem fixa na tela, feito para ser executado por um interpretador de comandos. Usar o JAVA como primeira linguagem de programação vai dar um bom trabalho, pois você vai ter que conhecer e entender o conceito e a implementação do conceito de orientação a objetos dele. Mas não esquenta a cabeça agora … a gente chega lá, um passo por vez 😀

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

Referências

 

 

Introdução ao Processamento de Dados e Programação – Parte 02

Introdução

No post anterior (Introdução ao Processamento de Dados e Programação – Parte 01), levantamos cinco possibilidades de calcular a média de uma lista de notas salvas em um arquivo texto, onde quatro delas usam um script ou um programa. Nesse post, vamos tentar resolver o mesmo problema com outras linguagens:  C++ ( Windows e Linux ), Java (Windows e Linux), Visual Basic .NET (Windows), e Linux Bash.

Recapitulando

Um arquivo texto (por exemplo “notas.txt) possui 30 linhas, cada linha com a nota da prova de um aluno — de 0 a 100 — e eu preciso criar um programa que leia todas as linhas do arquivo, calcule a soma de todas as notas, e calcule a média inteira das notas.

C++

Usando por exemplo o Visual Studio da Microsoft, podemos gerar um aplicativo sem dependências, que pode ser executado em qualquer máquina com Windows 7 pra cima — acho até que roda em Windows mais antigos, não tenho nenhum “na manga” pra teste.

#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <sstream>
#include <string>

int main()
{
  int alunos = 0;
  int media = 0;
  int notas = 0;
  std::ifstream infile("notas.txt");
  std::string line;
  while (std::getline(infile, line))
  {
    notas += atoi(line.c_str());
    alunos++;
  }
  media = notas / alunos;
  printf("Alunos ........... %d\n", alunos);
  printf("Soma das notas ... %d\n", notas);
  printf("Media da Classe .. %d\n", media);
  
return 0;
}


E, o resultado obtido:

CalcNotas CPP

Este mesmo código foi compilado no linux (g++), e funcionou (quase) perfeitamente:

CalcNotas G++

— Tem uma diferença na contagem de linhas, um comportamento diferente entre plataformas. Faltou uma proteção no código para ignorar uma linha vazia — depois da ultima nota preenchida.

Java

O mesmo código abaixo foi compilado no Windows, e a classe gerada foi executada no Windows e no Linux, tranquilo — Claro, apos baixar o Java JDK do Windows (Ferramentas de desenvolvimento e compilação do Java) e o Java JRE (Máquina Virtual Java para executar aplicações) no Linux.

import java.io.*;

public class calcnotas 
{ 
  public static void main(String[] args)throws Exception 
  { 
    File file = new File("notas.txt"); 
    int alunos = 0;
    int notas = 0;
    int media = 0;
    BufferedReader br = new BufferedReader(new FileReader(file)); 

    String st; 
    while ((st = br.readLine()) != null) 
    {
      notas += Integer.valueOf(st);
      alunos++;
    }

    media = notas / alunos;

    System.out.printf("Alunos ........... %d\n", alunos);
    System.out.printf("Soma das notas ... %d\n", notas);
    System.out.printf("Media da Classe .. %d\n", media);

  }
}

Resultado no Windows

CalcNotas Java

Visual Basic .NET

Usando o Visual Studio 2017 com VB .NET, funcionou também. Alguns passos para criar uma aplicação “Console”, depois codificar, compilar, corrigir, compilar, debugar … pronto, funciona.

Imports System

Module Module1

  Sub Main()

    Dim fileReader As System.IO.StreamReader
    fileReader = My.Computer.FileSystem.OpenTextFileReader("notas.txt")
    Dim stringReader As String
    Dim notas As Integer
    Dim alunos As Integer
    Dim media As Integer
    notas = 0
    alunos = 0
    stringReader = fileReader.ReadLine()
    Do While stringReader IsNot Nothing
      alunos = alunos + 1
      notas = notas + Convert.ToInt32(stringReader)
      stringReader = fileReader.ReadLine()
    Loop
    media = notas \ alunos
    Console.WriteLine("Alunos ........... " + alunos.ToString())
    Console.WriteLine("Soma das notas ... " + notas.ToString())
    Console.WriteLine("Media da Classe .. " + media.ToString())

  End Sub

End Module

Resultado obtido:

CalcNotas VB

Linux Bash

O “bash” ou interpretador de comandos do Linux também possui funcionalidades excepcionais. Vamos calcular usando um script — calcnotas.sh

#!/bin/bash
input="notas.txt"
notas=0
alunos=0
while IFS= read -r line
do
  if [ ! -z "$line" ]
  then
    alunos=$(( alunos + 1 ))
    notas=$(( notas + line ))
  fi
done < "$input"
media=$(( notas / alunos ))
echo "Alunos ........... $alunos"
echo "Soma das notas ... $notas"
echo "Media da Classe .. $media"

Resultado obtido:

calcnotas_bash

Conclusão

Bem, por hora já temos bastante exemplos funcionais. Cada linguagem mostrada nestes posts tem as suas características especificas, sintaxe e comportamentos. Algumas são case-sensitive ( considera letras minúsculas são diferentes de maiúsculas ), em algumas (como Python) o espaçamento dos comandos (identação, espaços a esquerda de cada linha) definem inicio e final de bloco, em outras a identação é apenas uma boa prática … as declarações mudam, os tipos de dados também, os limites e comportamentos, cada uma tem o seu “charme” … E eu usei POR ENQUANTO APENAS 8 linguagens de exemplo 😀

Espero que estas informações lhes sejam úteis, ou pelo menos interessantes !!! E desejo a todos como sempre TERABYTES DE SUCESSO !!!