Aprendendo Java - Detalhado

Java é uma linguagem de programação orientada a objeto desenvolvida na década de 90. Diferentemente das linguagens convencionais, que são compiladas para código nativo, a linguagem Java é compilada para um bytecode que é executado por uma máquina virtual, com que faz que seu código seja executado em qualquer sistema operacional, desde que haja a máquina virtual java instalada.

©  Este artigo foi retirado do site http://www.tiexpert.net/ e melhorado com novas imagens, códigos e melhores conceitos principalmente sobre Polimorfismo.

O que é Necessário para Programar em JAVA

Para programar em Java, não é necessário ter programado antes. Porém, ficaria mais fácil se tiver conhecimento em linguagem Orientada a Objeto, porque Java é totalmente estruturada em objetos (Classes, métodos, abstração, etc).

Para começar a programar será necessário fazer o download do JDK (Java Development Kit - Kit de desenvolvimento Java) que é gratuito e pode ser obtido direto do site da Sun Microsystem. Além disso, também, um compilador de Java. Existem vários tipos, uns mais leves e simples e outros mais pesados e complexos de usar. Se não souber qual usar, veja nossa lista de compiladores Java.

Então, mãos à obra, instale um compilador e comece seu projeto.

Primeiro Programa em Java

Veremos aqui o nosso primeiro código em linguagem Java.

A única coisa que esse programa faz é imprimir (mostrar) na tela uma mensagem dizendo: Bem vindo ao mundo Java!.

Primeiramente, iremos entender algumas estruturas básicas da linguagem.

//
Usamos // para indicar que foi iniciado um comentário. Um comentário serve apenas para documentar o código. Isso quer dizer que, ao ser executado este programa, as linhas marcadas como comentário serão ignoradas.

Este tipo de comentário é de linha única, ou seja, começa nas barras e termina no final da linha. Se quisermos criar comentário de várias linhas podemos utilizar /* */, no qual /* inicia o comentário e */ termina. Por exemplo:

// Este é um comentário de uma linha

/* Este é
um comentário
de várias
linhas */

public class PrimeiroPrograma
public class PrimeiroPrograma é a criação da classe. Como Java é totalmente orientado a objeto, sua programação é feita dentro de classes e mais classes. O assunto de classes será abordado mais afrente. O importante a princípio é entender que o programa para funcionar deve ter pelo menos uma classe com o método principal (main).

public static void main
public static void main é o método principal do código. Quando executamos um programa é esse método que é chamado. Em outras palavras, sem ele não há programa.

(String[ ] args)
(String[] args) é o argumento passado ao método main. Qualquer programa pode ser iniciado ou não com argumentos que modificam sua execução, trataremos futuramente de argumentos com programas.

{ }
As chaves são usadas para marcar o bloco de dados. No caso deste algorítmo, ele marca o código que será executado na classe MeuPrimeiroPrograma e também todo o código que será executado quando o método main for chamado.

System.out.println
System.out.println é a saída padrão de Java. É este método que indica o que deve aparecer na tela.

("Bem vindo ao mundo Java!")
("Bem vindo ao mundo Java!") é o argumento do método System.out.println. Neste caso é o que deve ser apresentado na tela.

Toda linha de comando em Java termina com um ponto-e-vírgula (;).

Bem, isso é o necessário para começarmos a programar em Java. Logicamente, muitos conceitos foram deixados de lado nesta pequena prévia, mas veremos mais aprofundadamente nas próximas páginas.

Regras e convenções de Nomenclatura

Quando programamos em Java, devemos levar em consideração as convenções de nomenclatura para deixar nosso código o mais legível e documentável possível, pois um dos objetivos da programação orientada a objetos é o reaproveitamento do código.
 
É muito provável que quem não tenha experiência com programação sinta dificuldade de identificar todos os termos utilizados neste artigo, mas garantimos que tudo que será visto aqui será detalhado mais adiante, embora que este artigo se torna trivial a medida que programamos. Portanto, quem já conhece programação em algum outro tipo de linguagem (como Delphi ou C++) aconselhamos que prossiga para a próxima página. Porém, se nunca programou antes, com certeza, este artigo será didático o suficiente para que entenda essas regras.
 
Muitos compiladores como Eclipse e NetBeans dão um grande auxilio na hora de criarmos nosso projeto nos informando sobre as convenções de nomenclatura no momento que estamos criando os pacotes, classes, etc...

Nome de Classes

Por convenção, toda classe deve começar com uma letra maiúscula e, de preferência, não pode conter letras não ASCII (caracteres de língua de origem latina, como caracteres acentuados). Portanto, não é possível declarar uma classe com qualquer caracter especial (@, #, $, %, &, *, _, etc...) ou número.
 
Caso o nome de uma classe seja composto por mais de uma palavra, a primeira letra de cada palavra deve ser em maiúscula.
 
O nome da classe deve ser exatamente o mesmo nome de seu arquivo fonte ( .java ).
 
O nome da classe deve fazer referência total ao seu objeto (atributos e métodos contidos dentro da classe). Por exemplo: se tivermos uma classe com os atributos canal, volume e sintonia; e os métodos mudarCanal (), aumentarVolume () e diminuirVolume (); então, possivelmente chamaríamos esta classe de TV ou Televisao. Contudo, em uma classe que contivesse o atributo corDasPenas e o método voar () jamais chamaríamos de Pessoa (por que pessoas não tem penas e nem voam).
 
Exemplos de nomes de classes: Pessoa, ImpostoDeRenda, Conta, AgenciaDeEmprego, ...
 

Nome de Pacotes

Os pacotes devem começar com uma letra minúscula e podem usar letras não ASCII. Jamais poderemos iniciar o nome de um pacote com caracteres especiais (@, #, $, %, &, *, _, etc...) ou número.
 
Caso o nome de um pacote seja composto por mais de uma palavra, a primeira letra de cada palavra deve ser em maiúscula.
 
O nome do pacote deve ser o mesmo nome da pasta a qual ele se refere. Por exemplo, se os arquivos fonte estão na pasta criptografia, então o nome do pacote deve ser criptografia.
 
O nome do pacote deve fazer referência total às funções exercidas pelas classes dentro do pacote, pois pacotes servem basicamente para organizar os arquivos de código fonte de nosso projeto.
 
Exemplos de nomes de pacotes: criptografia, usuarios, conexoesDeBancoDeDados , ...

Nome de atributos e variáveis

Os atributos (variáveis) podem começar com qualquer letra e os caracteres $ ou _, porém não podem começar com números.
 
Caso o nome de um atributo (variável) seja composto por mais de uma palavra, a primeira letra de cada palavra deve ser em maiúscula.
 
Exemplos de nomes de atributos ou variáveis: x, y, resultado, valorDeX, valorDeY, ligado, ...

Nome de atributos finais ou constantes

Os atributos finais (constantes) devem ser escritos em letras maiúsculas.
 
Usamos underline (_) para separar nomes compostos de atributos finais (constantes).
 
Exemplos de nomes de atributos finais ou constantes: TAMANHO, PARAR_DE_EXECUTAR, ...
 

O que são atributos e variáveis

Para quem já programou antes em alguma linguagem de programação não orientada a objetos, deve achar estranho o termo atributo porque, na verdade, está mais acostumado com o termo variável.
 
Mas, qual a diferença entre atributos e variáveis? Nenhuma.
 
Atributos e variáveis são a mesma coisa em questão de funcionalidade. Ambos são endereços de memória que tem um espaço ou tamanho definido de acordo com o tipo de dado que será guardado, por exemplo: caracter, número, número decimal, etc. Em Java, costumamos utilizar o termo atributo, que é nada além do que uma variável que está dentro de uma classe. Como tudo que fazemos em Java está contido dentro de uma classe, então usamos o termo atributo ao invés de variável.

Tipos de dados e declaração de atributos

Todo programa de computador deve ser capaz de lidar com dados para conseguir fazer seus processos como, por exemplo, somar, multiplicar, dividir, etc... Usar atributos é a melhor forma de manipular os dados.
 
Os tipos de dados são o que definem a quantidade de memória do computador que será utilizado para guardar tal dado. Cada tipo de dado tem um tamanho diferente e por consequência seu alcance também. O que queremos dizer é que se declararmos um atributo para guardar um número inteiro, jamais poderemos guardar um número decimal, porque um número decimal ocupa mais espaço de memória que um inteiro.
 
Para declararmos qualquer tipo de atributo, usamos a seguinte ordem: primeiro o tipo de dado e depois o nome do atributo. Se não souber como nomear um atributo, veja as regras e convenções de nomenclatura.
 
Vamos iniciar com os tipos de dados inteiros que são os mais simples. Em Java, os números inteiros são divididos em quatro tipos: byte, short, int e long.
 
Esses quatro guardam o mesmo tipo de dado, mas ocupam espaços de memória diferente, o que afeta em seu alcance.
 
O tipo byte é o de menor alcance entre os inteiros. Como o próprio nome sugere, ele consome apenas um byte (8 bits) e pode guardar valores entre -128 e 127.
 
O tipo short guarda inteiros de 2 bytes (16 bits) e pode guardar números entre -32.768 a 32.767.
 
O tipo int é o tipo de dado mais comum. Ele consome 4 bytes (32 bits) e guarda valores entre -2.147.483.648 e 2.147.483.647.
 
Long é o tipo de dado com maior alcance entre os inteiros. Consequentemente, também é o que ocupa mais espaço (8 bytes ou 64 bits). Tem um grande alcance que fica entre -9,22E+18 (exatos -9.223.372.036.854.775.808) e 9,22E+18 (exatos 9.223.372.036.854.775.807).
 
Existe os tipos de dados próprios para caracteres que é o char.
 
O tipo char ocupa 2 bytes, o que torna Java ideal para programar em língüas latinas, asiáticas ou qualquer outra que utilize caracteres diferentes do padrão ASCII.
 
O padrão ASCII utiliza apenas um byte que fornece 256 letras diferentes, mas o padrão utilizado em Java (ISO) nos dá a possibilidade de até 65.536 caracteres diferentes.
 
Para números decimais utilizamos dois tipos de dados, dependendo da necessidade de seu alcance e precisão (números após a vírgula).
 
O tipo de dado mais comum para número decimal é o float. Em um tipo de dado float, podemos guardar números grandes que vão desde 1,4E-45 até 3,4028235E+38
 
Para número decimais com grande precisão ou valores extraordinários (geralmente utilizados em matemática aplicada e complexa como cálculos físicos, químicos, astrológicos, meteorológicos, etc) utilizamos o tipo de dado double.
 
Double é o tipo de dado mais complexo que há em Java e o maior valor possível de se armazenar é 1,797.693.134.862.315.7E+308. Muito mais do que qualquer programador precisa.
 
Para ilustra melhor essa explicação, abaixo está um pequeno algorítmo com alguns atributos e seus valores mínimos e máximos.

Atribuição de Valores a Variáveis ou Atributos

Como vimos, há vários tipos de dados em Java, cada um com um consumo de memória determinado que afeta diretamente o seu alcance.
 
Veremos agora como atribuir valores a esses endereços de memória (atributo ou variável).
 
Para atribuirmos valor a uma variável ou atributo usamos o operador =. Neste caso, o sinal de igual não significa igualdade, mas que um valor será atribuído.
 
A regra básica é que sempre o que estiver à esquerda do "=" receberá o valor que estiver à direita.
 
Por exemplo: se expressarmos var1 = 10 queremos dizer que a variável var1 receberá o número 10. Pronto! Agora, temos o número 10 gravado na memória.
 
Podemos também atribuir valores contidos em outras variáveis. Por exemplo:
 
var1 = 20;
var2 = var1;
 
A variável var1 recebe o número 20, depois a variável var2 recebe o valor de var1 (que é 20). Pronto! Agora, temos duas variáveis guardando o mesmo número (20).
 
Inicialização de variáveis ou atributos
Muitas vezes, precisamos inicializar as variáveis ou atributos para que possuam um valor desde o início do programa.
 
Como há muitos tipos de dados em Java, seus criadores desenvolveram formas de escrita para que diferenciam os vários tipos de dados.
 
Primeiramente, começaremos com os mais simples: os inteiros.
 
Os número inteiros comuns e do tipo byte são os mais simples de se inicializar, pois basta atribuirmos seu valor diretamente. Logicamente, precavendo-se dos limites de alcance do tipo inteiro vistos na página de tipos de dados. Por exemplo:
 
int var1 = 10;
int var2 = 500;
int var3 = 65000;
byte var1 = -100;
byte var2 = 50;
 
Os números inteiros longos têm duas formas de serem inicializados. Podemos escrevê-lo diretamente se, e somente se, seu valor for menor que o alcance de um inteiro comum. Mas se o valor atribuído for maior que o valor permitido por um inteiro comum, então, no final deve ser acrescentado uma letra L (maiúscula ou minúscula, não há diferença aqui). Exemplo:
 
long var1 = -65000;
long var2 = 3590;
long var3 = 15000000000L;
long var4 = -6740000000L;
 
A inicialização do tipo double e float é um pouco diferente. É possível apenas digitar um número como sendo inteiro que o compilador entenderá facilmente, porém, se quisermos utilizar as casas decimais, devemos utilizar um ponto para separar a parte inteira da decimal. Outro fato ao utilizar casas decimais é que o tipo float deve ser diferenciado do tipo double. Conseguimos fazer isso facilmente digitando uma letra F ao final do número (não importa se a letra é maiúscula ou minúscula). Exemplo:
 
double var1 = 12045741359;
float var1 = 745621;
double var2 = 10.658745965;
float var2 = 0.5f;
 
Char deve ser inicializado com uma letra entre aspas simples. O importante é entender que em uma variável do tipo char só pode ser colocada uma letra, não uma frase. Além do mais, neste caso há diferença entre maiúsculas e minúsculas. Exemplo:
 
char var1 = 'a';
char var2 = 'A';
 
Se quisermos criar uma frase devemos guarda-la em uma string. Apesar de String não ser um tipo de dado em Java, mas sim uma classe, este é com certeza o elemento mais utilizado. Uma cadeia de caracteres (string) pode ser inicializada de três formas: 1) Como uma string inexistente, ou seja, sem valor algum. Para isso utilizamos a palavra reservada null; 2) Como uma frase vazia; 3) Como uma string completa.
 
Com exceção da inexistente, toda string deve estar dentro de aspas duplas ( " " ). Se por acaso, precisarmos digitar uma aspas dentro de uma string, estas aspas devem vir precedidas de uma barra invertida (\). Exemplo:
 
String var1 = null;
String var2 = "";
String var3 = "Cadeia de caracteres";
String var4 = "entre aspas: \"String em Java\".";
 
O tipo booleano possui apenas dois valores: verdadeiro e falso. True para verdadeiro e False para falso. Exemplo:
 
boolean var1 = true;
boolean var2 = false;
 
Neste tipo de dado não há possibilidade de ter outro valor.

Método de Saída Padrão

Um método de saída é utilizado para mostrar alguma coisa ao usuário. No caso de Java, existe um método padrão que utiliza uma saída padrão (monitor de vídeo) para criar a comunicação com o usuário - estamos falando de println.
 
O método println faz parte da classe System que controla a maioria das funcionalidades do computador, e se tratando de uma saída, mais especificamente de out.
 
Basicamente, o método println imprime na tela uma string (cadeia de caracteres - frase) que é passada ao método como argumento entre parênteses.
 
A saída padrão é exibida sempre no console e não em uma janela.
 
O importante é saber que o método PRINTLN sempre pulará uma linha quando seu argumento chega ao final.
 
Então, procedemos da seguinte maneira: 1) A classe ao qual pertence (System); 2) o membro que indica a saída (out); 3) o método (println); 4) O argumento contendo a string que deve ser apresentada na tela.
 
O método, resumidamente, ficaria assim: System.out.println ("string")

Concatenação

Concatenação é o ato de unir duas ou mais cadeias de caracteres (strings).
 
Por muito tempo, operações envolvendo strings eram os pesadelos de qualquer programador, pois havia a necessidade de tratar o elemento pelo seu dado bruto, ou seja, caracter por caracter. Então, juntar por exemplo, duas frases era um trabalho árduo.
 
Com o surgimentoda orientação a objeto e o advento do Java, as operações envolvendo strings foram muito simplificadas. A melhoria mais significativa nesse assunto, sem sombra de dúvidas, foi utilizar a concatenação de strings.
 
A concatenação de strings é dada pelo operador +, mas não o confunda com o operador de adição que utiliza o mesmo símbolo.
 
Dessa forma, com apenas este símbolo, podemos unir duas cadeias de caracteres diferentes em apenas uma.
 
Veja este exemplo, que apesar de bobo, ilustra exatamente o que acontece na concatenação:
 
João + zinho = Joãozinho;
Passa + tempo = Passatempo;
Acredite, simples desse jeito.
Colocando isso em um código ficaria:

Operadores Lógicos

Abaixo está uma tabela com os operadores mais comuns
 
+ adição ou concatenação
- subtração
* multiplicação
/ divisão
% módulo (divisão no qual é obtido o resto)
++ incremento de 1
-- decremento de 1
Java segue a convenção de expressões matemáticas. Por exemplo:
 
Se você fizer 10-2/4, o resultado será -9,5 e não 2, pois a divisão deve ser calculada primeiro. Para esta conta dar 2 devemos indicar que a subtração deve ser feita antes da divisão. Ela ficaria assim, (10-2)/4. O que está entre parênteses deve ser calculado primeiro.
 
Em Java, podemos facilmente simplificar operações que envolvam a própria variável. Por exemplo, vamos imaginar que o novo valor de var1 seja o valor da própria variável var1 mais o valor da variável var2. Podemos construir, então, a seguinte sentença -> var1 = var1 + var2, ou podemos simplificá-la em -> var1 += var2.
 
Veja abaixo a lista de todas as operações possíveis:
 
Símbolo de Operações
+= somar (quando os dados forem algum número)
+= concatenar (quando um dos dados for String) Ver Concatenação
-= subtração
*= multiplicação
/= divisão
%= módulo (a variável à esquerda recebe o resto da divisão dele com a variável à direita)

Condições Lógicas - IF e ELSE

Uma ação muito importante que o processador de qualquer computador executa, e que o torna diferente de qualquer outra máquina, é a tomada de decisão definindo o que é verdadeiro e o que é falso.
 
Se quisermos fazer um bom programa, esse programa deve ser capaz de definir caminhos diferentes de acordo com decisões que o próprio programa toma. Para isso, precisamos de uma estrutura seletiva da qual o único valor possível é o bit 1 ou 0, resumindo: retornar o valor VERDADEIRO ou FALSO.
 
Em Java, como em muitas linguagens de programação, quem faz isso é o IF (SE traduzindo).
 
O ELSE é o que chamamos de caso contrário, ou seja, se for falso execute o que está no ELSE.
 
Exemplificando: Se (IF) for tal coisa, faça isso! Caso contrário (ELSE), faça aquilo!
 
Usando IF
Para usar o IF basta digitar entre parênteses o que deve ser comparado.
 
IMPORTANTE: IF é uma palavra reservada que não aceita ponto-e-vírgula (;) no final.
 
Se for verdadeiro, o programa executará a primeira linha logo abaixo do if.
 
Mas, e se quisermos executar várias linhas se if for verdadeiro?
 
Se o if tiver que executar várias linhas, todas as linhas que devem ser enquadradas dentro do bloco de dados - as chaves ({}).
 
Usando ELSE
O ELSE só existe se tiver um IF. O else só será executado se o IF for falso.
 
Else executará só a primeira linha abaixo dele. Se o else tiver que executar várias linhas, vale a mesma regra de if. Todas as linhas a ser executadas deverão estar contidas dentro do bloco de dados ({}).
 
Para que IF chegue a uma decisão de falso e verdadeiro são necessários operadores lógicos. Dos quais destacam-se 6:
 
> - maior que
< - menor que
>= - maior ou igual a
<= - menor ou igula a
== - igual a
!= - diferente de

WHILE

While é uma estrutura de repetição.
 
While executa uma comparação com a variável. Se a comparação for verdadeira, ele executa o bloco de instruções ( { } ) ou apenas a próxima linha de código logo abaixo.
 
Procedemos da seguinte maneira:
 
WHILE (comparação)
 
O problema com estruturas de repetição, principalmente com while, é o que chamamos de looping infinito. Damos esse nome ao fato de que o programa fica repetindo a mesma sequência de códigos esperando por um resultado que nunca irá acontecer.
Portanto, é imprescindível que uma determinada variável seja modificada de acordo com cada loop. Veja o exemplo

FOR

FOR é uma estrutura de repetição que exerce a mesma função que WHILE e DO WHILE.
 
A principal diferença entre eles é a sintaxe e também a forma de trabalhar.
 
O FOR necessita de três parâmetros: a inicialização da variável, a condição que irá manter o looping (repetição) e o modificador da variável inicializada que pode ser incrementada ou decrementada, ou seja, pode aumentar seu valor ou diminuir. Um ponto importante é que todos os parâmetros devem ser separados por ponto-e-vírgula ( ; ).
 
Dessa forma, temos resumidamente a seguinte construção: FOR (inicialização ; condição ; incremento ou decremento).
 
Por exemplo, vamos criar um código que nos dá o resultado do fatorial de 5. Como sabemos para calcular o fatorial de algum número basta multiplicarmos ele pelo número anterior regressivamente até 1, ou seja, seria algo como 5 × 4 × 3 × 2 × 1. Dessa forma, sabemos exatamente em quanto devemos começar nossa conta (5), sabemos por quanto tempo o looping irá continuar executando (enquanto a variável for maior que 1) e sabemos quanto devemos modificar essa variável (para cada repetição irá subtrair - decrementar - 1).
 
Em um código ficaria:
 
Observemos apenas um detalhe, como toda variável é automaticamente inicializada como nulo (NULL), então, necessitamos inicializar a variável resultado em 1, porque seria impossível multiplicar a variável resultado pela variável fator (ex.: NULL × 5 = ?).

Switch, Case e Default

Uma estrutura muito utilizada em programação é o switch. A estrutura switch verifica uma variável e age de acordo com seus cases. Os cases são as possibilidades de resultados que são obtidos por switch.
 
Basicamente, o switch serve para controlar várias ações diferentes de acordo com o case definido dentro dele.
 
A estrutura do Switch é:
 
SWITCH (variável) {
    CASE valor :
        Código a ser executado caso o valor de case seja o mesmo da variável de switch
}
 
Então, detalhadamente, switch recebe uma variável e abre um bloco de dados ( { } ), dentro desse bloco de dados há os cases. Cada case recebe um valor único, ou seja, que não pode ser repetido no mesmo bloco de dados. Então, marcamos cada case com dois pontos ( : ). Após os dois pontos colocamos todo código que deverá ser executado que pode conter quantas linhas nós quisermos.
 
Dica importante: É um bom costume sempre terminar um código após o case com um comando break. Assim, nós evitamos que o resto do código seja executado por acidente. E vale também ressaltar que case não gera resultados booleanos, portanto, não há a possibilidade de fazer comparações (Ex. Isso está totalmente errado-> case var1 > var2:).
 
Default
Como switch pode receber várias possibilidades, pode ocorrer de algum caso estar fora do alcance ou não definido. Nesse momento, default faz seu papel. Default pega qualquer resultado que não esteja definido no case. Ou seja, ele é o bloco de código padrão que deve ser executado quando nenhum case for satisfeito. Podemos coloca-lo onde quisermos dentro de switch , mas, geralmente, o colocamos no final.
 
Para exemplificar, vamos ver o código abaixo.
 
 
Como podemos perceber, existe uma variável inteira chamada diaDaSemana. Essa variável é passada ao switch e dentro desse switch há 7 cases que correspondem aos dias da semana. Como o valor inicial é 1, então, o bloco logo abaixo de case 1 é executado, ou seja, ele imprimirá Domingo no monitor. Para evitar que o código dos outros cases sejam executados, logo após o método println há um comando BREAK;
 
Se quiser, baixe este código e modifique o valor inicial de diaDaSemana para verificar os possíveis resultados e como este algoritmo funciona.

Break e Continue

Break e Continue são dois comandos de controle de estruturas largamente utilizados em loops (repetições) como for e while.
 
Break
O comando break serve para determinar uma quebra de estrutura, ou seja, ele faz com que, por exemplo, um loop (repetição) pare. No comando switch, por exemplo, ele determina que não pode ser executado o case seguinte, e assim por diante.
 
No exemplo abaixo, temos uma repetição que se inicia em 1 e deve terminar em mil (1.000), mas dentro desta estrutura há uma condição: se a variável for igual a 10 saia da estrutura de repetição. Vejamos:
 
 
Continue
Continue também é muito utilizado em estruturas e repetição e sua função é ignorar o código, e não sair como acontece com o break.
 
Exemplificando, faremos um código contendo uma estrutura de repetição que irá contar de 1 a 100, mas sempre que o número não for múltiplo de 5 o código para apresentar o número na tela será ignorado e a repetição continuará com o número seguinte.
 
Ou seja, desta forma, apenas os múltiplos de 5 entre 1 e 100 aparecem na tela.

Arrays

Arrays, também muito conhecidos como vetores, são variáveis que servem para guardar vários valores do mesmo tipo de forma uniforme na memória. Por exemplo, se tivemos que criar 20 variáveis do mesmo tipo que querem dizer a mesma coisa, nós não criaríamos -> int var1, var2, var3, var4, var5, ... ao invés disso, criaríamos apenas uma variável de array para guardar todos os 20 números de uma vez.
 
Como um array pode guardar vários valores temos que definir quantos valores ele deve guardar para que seja reservado o espaço necessário em memória.
 
Primeiramente, vamos aprender como declarar um array. Para declarar um array devemos especificar a classe ou o tipo de dado que será armazenado nele. Após isso, damos um nome a esse array. E, para finalizar, indicamos que ele é um array, simplesmente abrindo e fechando colchetes ( [ ] ). Portanto, se quiséssemos um array de números inteiros chamado meu array, declararíamos da seguinte forma:
 
int meuArray [];
 
Agora que já declaramos o array, devemos dar um tamanho a ele, para que seja reservado espaço suficiente em memória. Para fazermos isso, utilizaremos um instanciador chamado new.
 
New é muito importante, pois ele irá criar de fato o array.
 
Para indicarmos o tamanho usamos o instanciador new, depois o tipo de dado do array e entre colchetes o tamanho do array. Tomando o exemplo acima, vamos indicar que meuArray terá o tamanho 4. Então, a linha de cima ficaria:
 
int meuArray[] = new int [4];
 
Na memória é representado da seguinte forma:
 
Uma única variável com 4 espaços nos quais podem ser guardados números inteiros.
 
Acessando uma posição no Array
Agora, para acessarmos um local específico dessa memória devemos indicar entre colchetes a posição desejada no array que chamamos de index. O importante mesmo é saber que não importa o tamanho do array, o index (número que indica a posição) sempre começa em 0. Ou seja, um array de tamanho 20 vai da posição 0 a 19, um array de tamanho 180 vai da posição 0 a 179. Portanto, um array de tamanho 4 vai da posição 0 a 3.
 
Agora, se quisermos atribuir os valores 540 na posição 1 e 8456 na posição 3, faríamos: meuArray [1]=540; meuArray [3]=8456.
 
Não podemos nunca nos esquecer que o limite do array é sempre seu tamanho menos 1. Usando o exemplo: array de tamanho 4, posição máxima é 3 (pois 4-1=3). Então, se atribuirmos um valor a posição 4 ocorrerá um erro. Resumidamente, jamais poderíamos fazer meuArray [4]=200.
 
Inicialização direta de Array
Podemos inicializar um array diretamente, sem a necessidade de instanciá-lo com new. Para isso, após a declaração do array, basta colocar seus valores em chaves, separando cada valor por vírgula. Por exemplo, se quiséssemos inicializar o meuArray com os valores 450, 200, 1000, 700, faríamos:
 
int meuArray [] = { 450, 200, 1000, 700 };
 
Matrizes
Matrizes são arrays multidimensionais, ou seja, eles não são totalmente lineares, e sim, geométricos.
 
Enquanto um array tem apenas uma linha com vários valores, uma matriz pode, por exemplo, tem várias linhas com vários valores, que comumente chamamos de linhas e colunas.
 
Para criarmos um array multidimensional (ou matriz), procedemos da mesma forma que um array normal, porém com mais um dimensionador (os colchetes). Então, se quisermos criar um array (matriz) bidimensional com 3 linha e 5 colunas, faríamos:
 
int minhaMatriz [][] = new int [3][5];
 
Na memória é representado como:
 
 
Isso também pode ser feito para adquirirmos o formato de matriz que quisermos. Ou seja, se fosse uma matriz tridimensional, bastaria ter três dimensionadores, se fosse 4D, então, 4 dimensionadores, se fosse 5D (apesar de geometricamente difícil de imaginar) seriam 5 dimensionadores. E assim por diante...

Entendendo Static

Entendendo Static
Sempre que programamos devemos levar em conta como nosso próprio programa irá acessar os dados que ele mesmo produz. Isso é o simples entendimento e visualização da diferença entre o que é local e o que é global.
 
Local é qualquer tipo de dado que só pode ser acessado em seu próprio contexto. Por exemplo, uma variável dentro da função principal (main) só pode ser acessada pela função main.
 
Global é qualquer tipo de dado que pode ser acessado diretamente de qualquer contexto dentro da classe inteira. Por exemplo, podemos declarar uma variável como sendo da classe e criarmos também outras três funções que podem acessar essa mesma variável diretamente, pois ela é global e não pertence a nenhuma das três funções especificamente.
 
Portanto, sempre usamos a palavra reservada static para indicar que algo (função, método, variável, etc.) é global e pode ser acessado diretamente por todos os elementos de uma classe.
 
Para exemplificar de uma maneira bem simples e direta, no código abaixo há três variáveis: duas declaradas no corpo da classe e uma declarada na função main.
 
Traduzindo o código acima, a regra é a seguinte:
 
Quando o código é executado, a função main é chamada;
Dentro da função main podemos atribuir um valor para x, porque x é um inteiro marcado como global (static) e pode ser acessado de qualquer lugar.
y não pode ser modificado dentro da função main, pois ele só pertence a classe GlobalELocal.
z só pode ser modificado dentro da função main, e não pode ser modificado na classe GlobalELocal.

Constantes

Constantes são o oposto das variáveis. A diferença está em não podermos atribuir um novo valor, pois ele recebe um valor final imutável. Isso ajuda na hora que temos que lembrar de algum número muito grande ou muito pequeno, ou até mesmo um valor que aparentemente não nos diz nada, mas que significa alguma coisa. Um exemplo bem simples seria 000000. Mas, o que é 000000? É difícil saber o que ele significa, mas é muito mais fácil entender que preto em Java é Color.BLACK. Ou seja BLACK é uma constante que guarda sempre o mesmo valor (000000).
 
Para criarmos uma constante em Java é muito simples. Primero nos atemos ao fato de que uma constante criada dentro de uma classe deve ser acessada por qualquer membro da classe, então, por definição, essa constante é também global ou estática (static). Como uma constante não muda seu valor, então, marcamos ela com a palavra chave final, que fará com que o valor nunca seja modificado. Toda constante é um dado, portanto, o seu tipo de dado também é obrigatório declarar.
 
Então, para declarar uma constante faríamos:
 
static final long CONST1 = 1L; ou final long CONST2 = 1L;
 
No qual, CONST1 seria uma constante global (ou seja, foi declarada no corpo da classe) e CONST2 seria uma constante local (ou seja, foi declarada no corpo de alguma função ou método, por exemplo, dentro de main)
 
Para ilustrar bem o uso de constantes, vamos calcular a área e o comprimento de um círculo. O porque deste cálculo é que necessitaremos de um número constante chamado PI. PI é um número irracional, ou seja, não há uma representação exata dele, mas, para o nosso exemplo, usaremos ele com uma precisão de 8 casas decimais. Portanto, nosso pi será do tipo double que valerá 3,14159265. Então, perceba que é muito mais fácil se lembrar da palavra pi do que do número 3,14159265. Além de, obviamente, digitarmos bem menos.
 
Então, só para esclarecer o que irá acontecer: O cálculo da área do círculo equivale a raio² × pi, e o comprimento é medido em 2 × raio × pi, no qual o raio é a distância entre a borda do círculo e seu centro. Vamos usar um raio de 25cm. Vejamos o resultado.
 
Exite uma infinidade de constantes que são implementadas em várias classes diferentes como por exemplo BLACK da classe Color, PLAIN_MESSAGE da classe JOptionPane e até mesmo o PI da classe Math com muito mais precisão do que este que usamos no exemplo.

Métodos/Funções

Métodos ou Funções são rotinas ou sub-rotinas automatizadas. Sempre que pretendemos usar a mesma codificação para algo específico, criamos uma função. Dessa forma, sempre que quisermos utilizar aquela codificação, ao invés de nós a digitarmos inteira novamente, simplesmente chamamos a função. Funções são extremamente úteis e adaptáveis, e o conceito de funções é importante para mais a frente entendermos o funcionamento e criação dos métodos.
 
Criando funções sem argumentos
Para criar uma função, temos que ter sempre em mente que toda função é global, ou seja, é estática (static).
 
As funções mais básicas são aquelas que apenas executam uma rotina, portanto, não recebem nenhum argumento. No código, digitaríamos dentro da classe, mas fora da função main o seguinte:
 
public static void nomeDaFunção () { código da função }
 
Static porque pode ser acessado globalmente; void porque não retorna nenhum valor; mesmo não tendo argumentos, é necessário ter parênteses.
 
Vamos ver o exemplo:
 
O código acima funciona da seguinte maneira:
 
Primeiro é criada a função mostrarMensagem(), que em seu corpo tem apenas um println com uma mensagem. Depois iniciamos nosso programa com o main. Dentro do main chamamos a função. Para isso, basta colocar o nome da função. Quando a função é chamada, o código dentro do corpo da função é executado. Concluindo, o programa acima apenas mostra na tela a mensagem Minha Mensagem.
 
Funções com argumentos
Funções com argumentos funcionam e são criadas da mesma forma que uma função sem argumento, porém com uma diferença.
 
A diferença está que haverá informações necessárias para que a função processe, e essas informações serão descritas dentro dos parênteses.
 
Uma função pode ter um ou vários argumentos desde que separados por vírgula (,). Cada argumento deve ter seu tipo de dado declarado. Então, todos os exemplos abaixo são funções válidas:
 
public static void funcao1 (String arg1){}
public static void funcao2 (int arg1, int arg2){}
public static void funcao3 (String arg1, char arg2, int arg3, float arg4, Object arg5) {}
 
Para demonstrar este tipo de função criaremos uma função que mostra na tela o resultado fatorial de um número. Além disso, colocaremos esta função dentro de um loop que irá de 1 a 10 para ele mostrar cada um desses fatoriais.
 
Simplificando, x que está sendo passado pelo main para fatorar() será o valor int numero da função fatorar.
 
Funções que retornam valores
Aqui veremos a grande vantagem de se criar uma função. Funções podem retornar valores de um processo executado dentro delas e esse valor pode ser guardado dentro de uma variável no programa. Isso com certeza deixa nosso código mais simples, pois podemos destacar processos repetitivos e guardar em uma variável apenas o resultado daquele processo.
 
Para criar uma função que retorna valores temos que nos ater ao fato retorno. Como todas as outras funções não retornavam valores, então, seu retorno era vazio - void. Agora, temos que declarar que tipo de retorno virá da função.
 
O retorno é dado pelo comando return, que finaliza a função e mostra o retorno. A variável ou valor que utilizarmos para return será o tipo de retorno da função.
 
Vamos imaginar que três funções, sendo que a primeira irá retornar um inteiro, a segunda um double e a terceira uma string. Então, as criaríamos da seguinte maneira:
 
public static int funcaoDeInteiro (){}
public static double funcaoDeDouble (){}
public static String funcaoDeString (){}
 
No exemplo abaixo, nós criaremos uma função que irá retornar um valor booleano (verdadeiro ou falso). Usando este retorno, determinaremos o que fazer dentro de uma estrutura seletiva (if).
 
O algoritmo utilizado acima é muito solicitado em cursos de programação e faculdades. O que a função faz é verificar se um número é primo ou não.

Criação de Classes

Começaremos agora a ver a alma da programação Java que é a orientação a objeto.
 
Sempre que programamos em Java, devemos pensar em como montar as definições de nosso objeto. Ou seja, quais atributos uma classe deve conter, simplificando, quais características tem tal objeto.
 
É como pensar em alguma coisa. Por exemplo, se pensarmos em relógio, a primeira coisa que nós vem a cabeça é hora, minuto, segundo e assim por diante. Nós também podemos ajustar as horas, ajustar o alarme, etc.
 
A princípio, vamos nos ater aos detalhes técnicos da programação de uma classe.
 
Para criar uma classe usamos a palavra chave class, e após a definição do nome de nossa classe, nós definimos seus atributos. Para exemplificar, criaremos uma classe que terá as características mais simples de uma televisão.
 
Pronto, esses são os atributos de uma classe, ou seja, nós definimos que existe um objeto chamado TV e que ele tem três características: uma dessas características é o tamanho, outra é um valor inteiro identificado por canal e mais outra característica que determina se ele está ligado ou não.
 
Como podemos perceber, isso é mais um conceito (paradigma) de como devemos pensar. Uma classe (ou objeto) pode ser muito implementado, tornando-se um objeto com várias características que pode ser facilmente manipulado e incorporado por outros objetos. Isso é o que chamamos de reutilização de código, que envolve vários apectos importantes da orientação a objeto como herança, interface, enumeradores, sobrecarga de métodos entre outros que nós estudaremos nas próximas páginas.

Construtores

Vimos anteriormente que é simples criar uma classe. Mas, para realmente conseguirmos utilizar a classe, ela deve conter pelo menos um método construtor.
 
 
O método construtor é desenvolvido da mesma forma que uma função, a única diferença é que ele tem o mesmo nome da classe.
 
Isso se deve ao fato de que um objeto deve ser construído cada vez que chamamos a classe. E a responsabilidade de fazer isso é do construtor. Isso parte do princípio que podemos ter dois objetos com a mesma característica, mas que não são os mesmos objetos.
 
Ou seja, nós podemos ter uma TV de 29" ligada no canal 15 e nosso amigo tem uma outra TV que também é de 29" e está ligada no canal 15. Perceba que ambas as TVs têm as mesmas características, mas continuam sendo duas TVs diferentes.
 
Sempre que criamos uma classe, Java automaticamente vincula um método construtor padrão interno com o mesmo nome da classe, mas sem inicializar nenhum atributo.
 
Para demonstrar um método construtor, criaremos um construtor padrão sem argumentos no qual já contém os valores dos atributos definidos por nós mesmos.
 
Então, vamos imaginar que sempre que uma TV é construída, o seu padrão é tamanho 21", desligada e no canal 0. Então, podemos definí-lo como:
 
 
Pronto! Com isso, nós já somos capazes de instanciar nossa classe.

Instanciar Objetos

Objetos são estruturas de dados definidas e agrupas dentro de uma classe. Sempre que utilizamos um objeto ou classe devemos reservar espaço em memória para que aquele objeto seja manipulado sem maiores problemas.
 
Além do mais, também podemos utilizar a mesma classe (com todos os seus métodos e atributos) para manipular outros objetos que serão tratados de forma diferente (mesmo se tiverem as mesmas características do objeto anterior), pois serão dois endereços de memória diferentes.
 
A vantagem de Java é nos possibilitar uma instanciação rápida e simples, sem termos que nos preocupar com referência a endereços e alocação dinâmica de memória, pois quem trata de manipular a memória é o próprio Java. Sem contar que, se um objeto não é mais referenciado dentro do programa, o próprio Java trata de liberar os recursos de memória consumidos pelo objeto usando o Garbage Colletor - Coletor de Lixo.
 
Quem faz o papel de instanciador em Java é o new. New trata de reservar memória o suficiente para o objeto e criar automaticamente uma referência a ele. Para new conseguir determinar o objeto, precisamos usar o método construtor que será usado como base para instanciar a classe e gerar o objeto.
 
Tecnicamente, declaramos uma variável qualquer como sendo do tipo da classe (ex.: TV minhaTV; ), depois instanciamos o objeto atribuindo a variável o resultado obtido por new mais o método construtor (ex.: minhaTV = new TV(); ).
 
Apesar de parecer muitos detalhes, isso fica mais facilmente visualizado no código abaixo.
 
 
No código acima, criamos uma classe chamada TV com os mesmos atributos do exemplo da página Criação de Classes, e criamos um método construtor que inicializa os atributos da classe TV com alguns valores.
 
Declaramos duas variáveis chamadas objeto1 e objeto2 sendo do tipo TV. Depois, instanciamos o objeto usando new e o método construtor.

This

This é usado para fazer auto-referência ao próprio contexto em que se encontra. Resumidamente, this sempre será a própria classe ou o objeto já instanciado.
 
Esse conceito de auto-referência é importante para que possamos criar métodos construtores sobrecarregados e métodos acessores mais facilmente.
 
Por base, se criarmos um método que receba um argumento chamado ligado que queremos atribuir para o atributo da classe, que também se chama ligado, devemos diferenciar ambos mostrando a quem cada um pertence. Como this se refere ao contexto empregado, então o usamos para identificar que ligado será o atributo da classe e ligado sem o this se refere ao parâmetro do método. O que resultaria nisto:

Sobrecarga de Métodos

Java nos permite criar vários métodos com o mesmo nome desde que tenham parâmetros diferentes. Isso é o que chamamos de sobrecarga de métodos.
 
A sobrecarga de métodos consiste em criarmos o mesmo método com possibilidades de entradas diferentes. Essas entradas, caracterizadas como parâmetros, devem sempre ser de tipos diferentes, quantidades de parâmetros diferentes ou posições dos tipos diferentes.
 
Para visualizarmos um cenário, vejamos a classe abaixo:
 
Quando for chamado um método chamado ligar, dependendo da quantidade de parâmetros um dos métodos da classe será chamado.

Acesso à atributos e métodos

Vimos, até agora, como criar uma classe, definir um método construtor, sobrecarregar métodos e instânciar objetos. Nesse momento, vamos ver como acessar seus membros e métodos.
 
Os acessos a membros ou métodos de uma classe são dados a partir do objeto instanciado usando um separador que é o ponto (.).
 
Portanto, para acessar qualquer membro da classe, basta que usemos o nome do objeto, mais um ponto e o nome do membro que queremos acessar.

Pacotes

Usamos pacotes para organizar as classes semelhantes. Pacotes, a grosso modo, são apenas pastas ou diretórios do sistema operacional onde ficam armazenados os arquivos fonte de Java e são essenciais para o conceito de encapsulamento, no qual são dados níveis de acesso as classes.
 
Java posssui um pacote padrão que é utilizado quando programamos, mesmo embora não seja recomendado pela Sun usá-lo.
 
Criar Pacotes
Muitos compiladores criam automaticamente os pacotes como uma forma eficaz de organização, mas a criação de pacote pode ser feita diretamente no sistema operacional. Basta que criemos uma pasta e lhe demos um nome. Após isso, devemos gravar todos os arquivos fonte de java dentro desta pasta.
 
Definir Pacotes
Agora que já possuímos a pasta que será nosso pacote, devemos definir em nossa classe a qual pacote ela pertence. Isso é feito pela palavra reservada package.
 
Package deve ser a primeira linha de comando a ser compilada de nossa classe.
 
Portanto, se tivéssemos criado uma pasta chamada tiexpert e fossemos criar uma classe nesta pasta (pacote), o começo de nosso código seria: package tiexpert;.
 
Importar Pacotes
Java possui vários pacotes com outros pacotes internos e várias classes já prontas para serem utilizadas.
 
Dentre os pacotes Java podemos determinar dois grandes pacotes: o pacote java, que possui as classes padrões para o funcionamento do algorítmo; e o pacote javax, que possui pacotes de extensão que fornecem classes e objetos que implementam ainda mais o pacote java.
 
Exemplo: o pacote AWT (Abstract Windowing Toolkit) possui as classes necessárias para se criar um ambiente gráfico API (Janelas) e está fortemente ligado as funções nativas do Sistema Operacional, ou seja, ele pertence ao pacote java. Mas, o pacote SWING não é ligado fortemente as funções nativas do Sistema Operacional, mas as funções do AWT, ou seja, SWING complementa o AWT, portanto SWING faz parte do pacote de extensão javax.
 
Para utilizar as milhares de classes contidas nos inúmeros pacotes de Java devemos ou nos referenciar diretamente a classe ou importá-la.
 
Para importar um pacote usamos o comando import.
 
Para separar um pacote de seu sub-pacote usamos um ponto (como no acesso a membros de classe).
 
Ao importarmos um pacote podemos utilizar um coringa, o asterísco (*). O asterísco serve para importar todos os sub-pacotes e classes do pacote que está definido.
 
Ex.: import java.awt.*;. Isso importará todos os sub-pacotes pertencentes ao pacote AWT.
 
Ou podemos definir diretamente qual pacote desejamos.
 
Ex.: import javax.swing.JOptionPane;. Isso irá importar apenas o sub-pacote JOptionPane do pacote SWING.
 
A diferença entre as duas formas de importação de pacotes é o consumo de recursos do computador. Como o asterísco importa todos os sub-pacotes, o consumo de memória será alto e, muito provavelmente, não usaremos todas as classes de todos os pacotes importados. Por isso, o recomendado é sempre importar apenas o pacote que será utilizado.
 
Para consolidar o que foi visto até aqui, o código abaixo ilustra o uso de pacotes.

Encapsulamento (Public, Private, Protected e Default)

Se outros programadores usam nossa classe, nós queremos garantir que erros pelo mau uso não ocorram. Encapsulamento serve para controlar o acesso aos atributos e métodos de uma classe.
 
É uma forma eficiente de proteger os dados manipulados dentro da classe, além de determinar onde esta classe poderá ser manipulada.
 
Usamos o nível de acesso mais restritivo que faça sentido para um membro particular. Sempre usamos private, a menos que tenhamos um bom motivo para deixá-lo com outro nível de acesso.
 
Não devemos permitir o acesso público aos membros, exceto em caso de ser constantes. Isso porque membros públicos tendem a nos ligar a uma implementação em particular e limita a nossa flexibilidade em mudar o código.
 
Basicamente, usamos quatro tipos de encapsulamento que são divididos em dois níveis:
 
Nível de classe ou topo: Quando determinamos o acesso de uma classe inteira que pode ser public ou package-private (padrão);
Nível de membro: Quando determinamos o acesso de atributos ou métodos de uma classe que podem ser public, private, protected ou package-private (padrão).
Para utilizarmos estes modificadores de acesso, basta que nós os digitemos antes do nome da variável, atributo, método, função ou classe, com exceção de package-private, que é entendido como padrão, portanto, é qualquer membro ou classe que não tenha modificador especificado.
 
Exemplo:
 
Public
O modificador public deixará visível a classe ou membro para todas as outras classes, subclasses e pacotes do projeto Java.
 
Private
O modificador private deixará visível o atributo apenas para a classe em que este atributo se encontra.
 
Protected
O modificador protected deixará visível o atributo para todas as outras classes e subclasses que pertencem ao mesmo pacote. A principal diferença é que apenas as classes do mesmo pacote tem acesso ao membro. O pacote da subclasse não tem acesso ao membro.
 
Sem Modificador (Padrão)
Por padrão, a linguagem Java permite acesso aos membros apenas ao pacote em que ele se encontra.
 
De forma ilustrativa, abaixo está uma tabela demonstrando todas estas características.
 
Modificador Classe Pacote Subclasse Globalmente
Public sim sim  sim sim
Protected sim sim sim não
Default/Padrão sim sim não não
Private sim não não não

Get e Set

Como visto anteriormente, o encapsulamento "protege" os atributos ou métodos dentro de uma classe, portanto devemos prover meios para acessar tais membros quando eles são particulares, ou seja, quando possuem o modificador private.
 
O que torna isso possível é a criação de métodos.
 
Em programação orientada a objetos, esses métodos são chamados de métodos acessores ou getters e setters, pois eles provêm acesso aos atributos da classe, e geralmente, se iniciam com get ou set, daí a origem de seu nome.
 
Na verdade, não há nada de diferente entre os métodos comuns e os métodos acessores. A única importância está no fato da orientação a objeto. Pois, sempre que formos acessar um membro em Java usaremos get ou set.
 
 
Set
Nomeamos um método acessor com set toda vez que este método for modificar algum campo ou atributo de uma classe, ou seja, se não criarmos um método acessor set para algum atributo, isso quer dizer que este atributo não deve ser modificado.
 
Portanto, como o valor de um atributo da classe será modificado, não é necessário que este método retorne nenhum valor, por isso, os métodos setters são void. Porém, obrigatoriamente, eles tem que receber um argumento que será o novo valor do campo.
 
Get
Nomeamos um método acessor com get toda vez que este método for verificar algum campo ou atributo de uma classe.
 
Como este método irá verificar um valor, ele sempre terá um retorno como String, int, float, etc. Mas não terá nenhum argumento.
 
Is
Nomeamos um método acessor com is toda vez que este método for verificar algum campo ou atributo de uma classe que tenha retorno do tipo boolean.
 
Levando em consideração as informações acima, nossa classe de exemplo TV ficaria:
 
O padrão BEAN, fala que deve haver os métodos getters e setters e o construtor vazio.

Herança

Na Programação Orientada a Objetos o significado de herança tem o mesmo significado para o mundo real. Assim como um filho pode herdar alguma característica do pai, na Orientação a Objetos é permitido que uma classe herde atributos e métodos da outra, tendo apenas uma restrição para a herança. Os modificadores de acessos das classes, métodos e atributos só podem estar com visibilidade public e protected para que sejam herdados.
Uma das grandes vantagens de usar o recurso da herança é na reutilização do código. Esse reaproveitamento pode ser acionado quando se identifica que o atributo ou método de uma classe será igual para as outras. Para efetuar uma herança de uma classe é utilizada a palavra reservada chamada extends.
 
Enquanto programamos em Java, há a necessidade de trabalharmos com várias classes. Muitas vezes, classes diferentes tem características comuns, então, ao invés de criarmos uma nova classe com todas essas características usamos as características de um objeto ou classe já existente.
 
Ou seja, herança é, na verdade, uma classe derivada de outra classe.
 
 
Para simplificar de uma forma mais direta, vejamos:
 
Vamos imaginar que exista uma classe chamada Eletrodomestico, e nela estão definidos os seguintes atributos: ligado (boolean), voltagem (int) e consumo (int).
 
Se levarmos em conta a classe TV que estamos usando de exemplo até agora, podemos dizer que TV deriva de Eletrodomestico. Ou seja, a classe TV possui todas as características da classe Eletrodomestico, além de ter suas próprias características.
 
Extends
Para fazermos uma classe herdar as características de uma outra, usamos a palavra reservada extends logo após a definicação do nome da classe. Dessa forma:
 
class NomeDaClasseASerCriada extends NomeDaClasseASerHerdada
 
Importante: Java permite que uma classe herde apenas as características de uma única classe, ou seja, não pode haver heranças múltiplas. Porém, é permitido heranças em cadeias, por exemplo: se a classe Mamifero herda a classe Animal, quando fizermos a classe Cachorro herdar a classe Mamifero, a classe Cachorro também herdará as características da classe Animal.
 

Interfaces

Interface é um recurso da orientação a objeto utilizado em Java que define ações que devem ser obrigatoriamente executadas, mas que cada classe pode executar de forma diferente.
 
Uma classe pode herdar de mais de uma interface. É como um contrato que depende que seja fechado para a classe ser válida. Foi criado para suprir a necessidade de heranças múltiplas.
 
Interfaces contém valores constantes ou assinaturas de métodos que devem ser implementados dentro de uma classe.
 
E por que isso?
 
Isso se deve ao fato que muitos objetos (classes) podem possuir a mesma ação (método), porém, podem executá-la de maneira diferente.
 
Usando um exemplo bem drástico, podemos ter uma interface chamada aéreo que possui a assinatura do método voar(). Ou seja, toda classe que implementar aéreo deve dizer como voar(). Portanto, se eu tenho uma classe chamada pássaro e outra chamada avião, ambas implementando a interface aéreo, então, nestas duas classes devemos codificar a forma como cada um irá voar().
 
Uma interface é criada da mesma forma que uma classe, mas utilizando a palavra-chave interface no lugar de class.
 
interface nomeDaInterface { métodoAbstrato (argumentos); }
 
Usando uma Interface em uma Classe
Como vimos anteriormente, uma classe pode extender suas funcionalidades obtendo as características de outra classe num processo que chamamos de herança. Uma interface não é herdada, mas sim, implementada. Porque todo o código dos métodos da interface deve ser escrito dentro da classe que o chama. Dessa forma, obtemos as assinaturas dos métodos da interface em uma classe usando a palavra-chave implements.
 
A vantagem principal das interfaces é que não há limites de quantas interfaces uma classe pode implementar. O que ajuda no caso de heranças múltiplas que não é possível ser feito em Java, pois uma classe apenas pode herdar as características de uma outra classe.
 
Trazendo ao exemplo já usado por nós, teríamos o seguinte conceito.

Classes Abstratas e Métodos

Classes abstratas tem uma função importante na orientação a objeto em Java.
 
De forma objetiva, uma classe abstrata serve apenas como modelo para uma classe concreta (classe que comumente usamos).
 
Como classes abstratas são modelos de classes, então, não podem ser instanciadas diretamente com o new, elas sempre devem ser herdadas por classes concretas.
 
Outro fato importante de classes abstratas é que elas podem conter ou não métodos abstratos, que tem a mesma definição da assinatura de método encontrada em interfaces. Ou seja, uma classe abstrata pode implementar ou não um método.
 
Os métodos abstratos definidos em uma classe abstrata devem obrigatoriamente ser implementados em uma classe concreta. Mas se uma classe abstrata herdar outra classe abstrata, a classe que herda não precisa implementar os métodos abstratos.
 
Para criarmos uma classe ou método abstrato usamos a palavra-chave abstract.
 
Para demonstrar, usaremos uma classe abstrata chamada Eletrodomestico, que servirá de modelo para todos os objetos que possam ser eletrodomésticos.
 
Como podemos observar, criamos um modelo de classe que compreende a uma gama enorme de outros objetos.
 
Agora, qualquer objeto que derivar desse modelo deve ter sua voltagem especificada e dizer se está ligado ou não.
 
Então, vamos criar outras duas classes que herdam as características da classe abstrata (modelo).
 
A primeira é a classe TV:

Polimorfismo

Polimorfismo, que vem do grego "muitas formas". É o termo definido em linguagens orientadas a objeto - como o Java - para a possibilidade de se usar o mesmo elemento de forma diferente.
 
Essas formas, em nosso contexto de programação, são as subclasses/objetos criados a partir de uma classe maior, mais geral, ou abstrata.
Polimorfismo é a capacidade que o Java nos dá de controlar todas as formas de uma maneira mais simples e geral, sem ter que se preocupar com cada objeto especificamente.
 
Especificamente em Java, polimorfismo se encontra no fato de podermos tratar objetos especilizados de forma mais genérica no caso de Animal, Elefante e Cachorro.
 
Eu posso tratar um objeto Elefante como sendo animal, pois herda características de Animal, porém eu não posso tratar todo Animal como Elefante, pois nem todo animal é Elefante. O mesmo vale pra cachorro, de forma prática seria representado da seguinte maneira:

Instanceof

No exemplo anterior, se tentássemos atribuir obj diretamente a tv ocorreria um erro de tipos incompatíveis (type mismatch).
 
Quando lidamos com classes, podemos testar qual seu tipo de instancia usando o operador instanceof.
 
Instanceof indica se um objeto (já instanciado) pertence a uma classe.
 
O uso de instanceof é: objeto instanceof nomeDaClasse.
 
Para melhorar o exemplo anterior, usaremos instanceof antes de atribuirmos obj a tv.

Published on 05/07/2013 at 16:21 by Mauricio N. Ferreira, tags