
Orientação a Objetos, parte I:
Criando e declarando classes - Construtores
O que são e para que servem as Classes e Objetos
O conceito de orientação a objetos, é, sem dúvida, o mais importante em Java.
E é por isso que ensinaremos desde o início, em nosso curso de Java.
Por ser um conceito um pouco abstrato, é normal que demore um pouco até entender tudo. E até entender bem, vai demorar mais ainda.
Porém, vamos ensinar, ao longo de vários tutoriais, divididos em várias seções, estes tão importantes detalhes.
Nesta parte, como só vimos o básico e laços da programação Java, vamos apenas ensinar como declarar as classes, instanciar os objetos (com e sem construtores) e como usar testes condicionais dentro das classes que criamos.
Isso dará uma idéia do que são classes e objetos, e é importante que se habitue a estas idéias.
O que são Classes e Objetos em Java
Como havíamos comentado em nosso artigo com a explicações sobre o primeiro programa que criamos, classes podem ser vistas como abstrações ou definições maiores das coisas e objeto já é algo mais real, mais concreto, é um elemento ou tipo daquela classe.
Usando essas definições, é realmente difícil entender os conceitos, por isso vamos usar exemplos.
Por exemplo, podemos ver "Pessoa" como uma classe. É uma classe que representa seres humanos, que possuem cabeça, coração, cérebro etc. É uma generalização maior. Podemos declarar você, caro leitor, como um objeto dessa classe, com seu nome e características específicas de uma "Pessoa". Você pertence a classe "Pessoa". Eu também, pois possuímos cabeça, coração, cérebro etc.
Nós temos essas características gerais, que todos da classe "Pessoa" possuem. Ou seja, em Java, dizemos que somos instâncias da classe "Pessoa".
Utilidade das Classes e Objetos em Java
Esse tipo de representação vai nos ajudar muito em programação Java.
Por exemplo, imagine que você foi contratado para criar um aplicativo para uma empresa - em Java, claro.
Você tem que cadastrar os milhares de funcionários da empresa.
É claro que você não vai declarar milhares de strings para armazenar o nome de cada um, nem de inteiros para armazenar seus números nem de floats para armazenar seus salários.
Seria humanamente impossível fazer isso.
Agrupar coisas semelhantes
Aí que entra a vantagem da programação orientada a objetos. Podemos ver todos esses funcionários de uma mesma maneira: como a classe Funcionario. O que a classe "Funcionario" tem em comum?
Tem um nome, uma idade, uma data de contratação, um salário, um setor em que trabalham e outras coisas específicas da empresa.
Pronto.
Você pode ver essa classe como um tipo de dado.
Assim como 'int' ou 'float', agora existe o tipo 'Funcionario'. Toda vez que entrar alguém novo na empresa, você declara esse elemento como fazendo parte do tipo 'Funcionario'. Ou seja, estará criando um objeto dessa classe.
O objeto, diferente da classe, é algo mais específico, ele que terá as informações pessoais de cada funcionário.
CLASSE > funcionário (classe, como um tipo de dado, int, float, funcionário)
tipos: string, float, int etc - podem ser declarados na classe funcionário
OBJETO > fabio
ATRIBUTO > tem carro / não tem
Crie uma vez, use quantas vezes quiser
A grande vantagem desse tipo de 'visão', é que, ao declarar a classe, você declara dentro dela os tipos: string, float, int etc, que estão dentro da classe. Então, quando for criar um objeto, automaticamente esses dados estarão criados!
Aí que reside a beleza do Java e da orientação a objetos. É muito, mas muito útil e prático. É um novo jeito de pensar e ver o mundo.
Dizemos que é um tipo de paradigma de programação diferente.
Altere uma parte do código,
e a mudança se propagará em todo o código
Ok, você criou seu aplicativo usando a classe "Funcionario".
Porém, a pessoa que te contratou - que muitas vezes não são da área de TI - esqueceu de te informar que os funcionários devem ter uma informação no cadastro: se possuem carro ou não.
E aí? Alterar tudo? Começar do zero?
Claro que não. Simplesmente vá na classe e coloque esse atributo (informação), e automaticamente todos os objetos passarão a ter esse dado, "carro".
Então é só pedir para os funcionários preencherem esse dado no seu aplicativo de cadastro.
Classe à parte, vida à parte
Uma coisa interessante que a programação orientada a objetos nos proporciona é a divisão das partes do programa.
Dois programadores podem programar duas classes de forma totalmente independente e fazer com que elas funcionem perfeitamente.
Coisa que em outros paradigmas de programação é quase impossível.
Por exemplo, você criou a classe "Funcionario".
Nessa classe você precisa a informação do salário de cada funcionário, porém você não tem acesso aos detalhes financeiros da empresa.
Ora, nem precisa e nem deve ter, é algo mais restrito.
Outro programador, responsável pelo setor financeiro, pode ter criado a classe "Salario" que recebe os dados de cada pessoa, sua posição na empresa, bônus, horas extras etc etc, e te dá somente o resultado final: o número.
Esse dado é o que você vai usar na sua classe "Funcionario".
Isso todo pode ser feito de uma maneira totalmente eficiente, segura e independente, tanto por você como pelo programador que fez a outra classe. A única troca de informação é que você pega um float dele e ele pega o nome do seu funcionário ou código dele da empresa.
O mais importante disso é: em nenhum momento foi necessário um ver o código do outro!
Na hora de trabalhar, isso nos diz muito em termos de rendimento!
Como saber quando usar Classes e Objetos em Java
Como você pode notar, através dos exemplos dos Carros, das Pessoas e dos Funcionários, as Classes são nada mais que um grupo de informações. Sempre que quiser usar essas informações, declare um Objeto daquela classe.
Ou seja, sempre que quiser generalizar ou criar um grupo com características parecidas, não tenha dúvida, use Classe e Objetos.
Em um jogo, Worms Armageddon ou Counter-Strike, por exemplo. Existem vários jogadores. Ora, eles são parecidos, tem semelhanças e características em comum. Não perca tempo declarando seus atributos individualmente, use classes e objetos.
Na verdade, em Java, tudo são classes e objetos, então não há muito essa preocupação.
Nosso programa principal, que contém a 'main()', é uma classe. Para receber dados, usamos a classe Scanner.
Em C, não existe Orientação a Objetos.
Em C++, você escolhe se usa ou não.
Adiante, veremos que uma classe pode se parecer com uma e outra, pegar 'emprestado' detalhe de outras, implementar ou estender outras.
Existem vários recursos para serem usados nas Classes, inclusive de segurança, que permitem que alguns objetos tenham acesso a alguns dados, outros não.
Recursos gráficos: por exemplo, as janelas dos aplicativos Java, botões, menu e tudo mais são classes.
Como dissemos, Java gira em torno de orientação a objetos, e Java é uma linguagem de programação riquíssima e ilimitada. Logo, o que é possível fazer com as classes e objetos também é bem vasto.
Como criar uma Classe e Declarar Objetos
Agora que você já leu no artigo passado sobre a utilidade das classes e objetos em Java,
vamos ensinar nesse tutorial como criar uma classe em sua IDE e como instanciar os objetos dessa classe.
Para isso, vamos usar o conceito de Orientação a Objeto, criando uma classe chamada Aluno, que usaremos em nosso próximo artigo para conter o nome e notas de alunos, bem como preencher e acessar os dados desses objetos.
Criando uma classe em Java
Sem mais delongas, crie seu projeto de Java.
Eu chamei o meu de PrimeiraClasse.
Ao criar esse, note que se chamará PrimeiraClasse.java
Nesse tutorial vamos criar outra classe, a classe "Aluno", que o Java irá chamar de "Aluno.java".
Para isso, se estiver no NetBeans vá em File -> New File
Em Categories, escolha Java Class, e Next.
Dê o nome de Aluno para sua nova classe.
Pronto. Sua nova classe está criada.
Note que apareceu outra aba, ao lado da "PrimeiraClasse.java", e se chama "Aluno.java"
Nesse novo arquivo, deverá ver algo como:
public class Aluno{
}
Que é nossa classe, e que, ainda, está em branco.
Criada a classe, vamos aprender como criar os objetos dessa classe.
Declarando um objeto de uma classe em Java
Se lembrar bem, você já declarou objetos de classe em nosso curso de Java.
Mais especificamente, declarou o objeto 'entrada', da classe 'Scanner', várias vezes.
Agora, vamos fazer o mesmo com nossa classe Aluno.
Vamos criar um objeto, um aluno da classe "Aluno".
A sintaxe é (digite isso na aba da PrimeiraClasse.java):
Aluno donaFifi = new Aluno();
Aluno patropi = new Aluno();
Aluno programador = new Aluno();
Pronto, criamos três objetos do tipo Aluno.
Poderíamos ter feito:
Aluno donaFifi;
donaFifi = new Aluno();
O new Aluno() é o que cria o objeto. E atribuímos ele à variável donaFifi.
Essa variável, porém, não é um objeto. Ela contém uma referência ao objeto.
É como se ela apontasse, como se tivesse o endereço da localização do objeto.
Então, sempre que mudamos essa variável, estamos mudando diretamente o objeto.
Porém, fica muito caxias dizer 'declare uma referência ao objeto Aluno'.
No dia-a-dia, simplesmente dizemos: criamos um objeto do tipo Aluno.
Mas na verdade essas variáveis não são objetos, e sim referências a objetos. O importante é saber disso.
Qualquer coisa, métodos ou dados que colocarmos na classe "Aluno", fará parte dos objetos "donaFifi", "patropi" e "programador".
No próximo tutorial você verá bem isso.
Por exemplo, se quisermos adicionar o campo para armazenar o nome completo deles, criamos uma string dentro da classe "Aluno",
assim, todos os objetos terão essa string.
Qual a vantagem disso?
Ora, criamos esse campo somente uma vez! Na classe! E todos os objetos herdaram isso!
Imagine numa situação real! Em uma escola, com milhares de alunos. Você declara a string uma vez, e ela passa a fazer parte da ficha da cadastro de milhares de alunos. Muito útil esse Java e a Orientação a Objetos, não?
Acessando e modificando variáveis de Classes e Objetos
Visto para que servem os objetos e classe em Java, como declarar, criar uma classe e instanciar um objeto,
vamos agora, nesta seção, colocar algumas variáveis dentro de nossa classe e aprender como atribuir valores para elas, bem como acessá-las.
Este tutorial é o primeiro passo para criarmos atributos (características) em nossas classes.
Variáveis de métodos e Variáveis de Classes
A diferença das variáveis declaradas nas classes daquelas declaradas no método 'main' ou em outros métodos (que você aprenderá a criar numa seção futura do curso), é que nos métodos elas são locais e temporárias.
Ou seja, as variáveis locais método podem ser acessadas somente dentro daquele método, e quando este acaba, seu valor é perdido.
Quando declaramos um objeto de uma classe, suas variáveis passam a existir a partir daquele momento até enquanto o objeto existir.
Quanto ao seu acesso, o Java provém artifícios interessantíssimos de segurança.
Podemos declarar variáveis de uma classe como 'public' (que podem ser acessados de qualquer lugar do programa),
'private' (só elementos da própria classe podem acessar esses dados) e
'protected' (só elementos da própria classe e subclasses que podem ver essa variável).
Veremos mais isso em Encapsulamento, numa seção futura do nosso curso online de Java, quando falarmos mais sobre Orientação a Objetos. Por hora, saiba apenas que existe e para que servem - segurança e organização.
Criando classes com atributos
Voltando a nossa classe "Aluno", vamos criar 3 variáveis:
"Nome", que vai receber o nome do aluno e "notaMat" e "notaFis" que vão receber a nota de matemática e física do aluno.
A nossa classe ficará:
public class Aluno {
public String nome;
public double notaMat,
notaFis;
}
Sim, simplesmente isso.
Declaramos as variáveis como 'public' porque vamos acessá-las de outra classe, a classe "PrimeiraClasse", que contém o método 'main'.
Quando estudarmos Métodos, mais adiante em nosso curso online de Java, veremos como declarar esses atributos de forma 'private', de modo que eles ficam inacessíveis à outras classes. E como vamos acessar esses dados então? Através de métodos dessa classe.
Assim podemos controlar como essas variáveis serão usadas.
Alterando e Acessando atributos de uma classe
Variáveis criadas, vamos dar valor a elas. Vamos usar a classe Scanner para perguntar ao usuário o nome do aluno e suas notas.
Note que, se estas variáveis estivesse na classe principal, que contém o método main, simplesmente atribuiríamos valores da seguinte forma:
notaMat = [numero]
Porém, essas variáveis não estão nessa classe. Estão em outra.
Então temos que acessar através do nome do objeto.
No meu caso, declarei o objeto Aluno com o nome "patropi", então para acessar seus elementos, simplesmente vamos usar:
patropi.notaMat
Ou seja: nomeDoObjeto.nomeDaVariavel;
Então, nossa classe principal, que instância um objeto, preenche o nome e duas notas do Aluno, depois mostra esses valores e a média será:
PrimeiraClasse.java
import java.util.Scanner;
public class PrimeiraClasse {
public static void main(String[] args) {
Aluno patropi = new Aluno();
Scanner entrada = new Scanner (System.in);
System.out.print("Nome do aluno: ");
patropi.nome = entrada.nextLine();
System.out.print("Nota em Matemática: ");
patropi.notaMat = entrada.nextDouble();
System.out.print("Nota em Física: ");
patropi.notaFis = entrada.nextDouble();
//Exibindo os dados
System.out.printf("O aluno \"%s\" tirou %2.2f em Matemática, " + "%2.2f em Física e obteve média %2.2f\n", patropi.nome, patropi.notaMat, patropi.notaFis, (patropi.notaMat+patropi.notaFis)/2);
}
}
Aluno.java
public class Aluno { public String nome; public double notaMat, notaFis; }
Dentro do printf usamos \" para exibir aspas duplas.
Construtor padrão e com parâmetros:
o que são, para que servem e como usar
-
Você reparou que, no tutorial passado, sobre como declarar e criar classe e instanciar objetos, nós fizemos:
Aluno fulano = new Aluno();
Você reparou como repetimos 'Aluno' duas vezes?
Por que? Não é inútil ou perda de tempo repetir isso?
Em Java - aliás, em programação - nada é à toa (se for, logo eles mudam). Tudo tem um motivo.
Nesse artigo você irá aprender sobre os Construtores (constructors) e saberá o motivo dessa repetição e como usufruir e ganhar muito tempo a partir deles
As explicações e exemplos deste tutorial são baseadas nos outros artigos desta seção:
O que são e para que servem Classes e Objetos em Java
Como criar uma Classe e declarar um Objeto
Acessando e modificando variáveis de Classes e Objetos
O que são construtores/constructor em Java
Quando usamos a keyword 'new' para criar um objeto de uma determinada classe estamos alocando um espaço na memória. Ao fazer isso, o Java requer que algumas variáveis sejam iniciadas com algum valor.
Esse ato de inicializar, ou construir, é feito pelos construtores, que são métodos - que iremos estudar mais a fundo em nossa apostila de Java logo mais.
O Java automaticamente inicializa as variáveis globais quando criamos um objeto de uma classe.
No artigo passado de nossa apostila, fizemos um exemplo com a classe "Aluno", onde criamos três variáveis: "nome", "notaMat" e "notaFis", que são consideradas variáveis globais da classe, pois não fazem parte de nenhum método.
Logo, elas devem ser inicializadas pelo Java. Para você ver como isso realmente acontece, vá na sua classe principal, que contém a 'main', e após a criação do objeto "patropi" da classe "aluno" adicione as seguintes linhas de código, de print, para você ver como essas variáveis da classe já possuem um valor, sem nem mesmo você ter atribuído nada:
Aluno patropi = new Aluno();
System.out.println(patropi.nome);
System.out.println(patropi.notaMat);
System.out.println(patropi.notaFis);
O resultado é:
null
0.0
0.0
Ou seja, por padrão, as strings são iniciadas com valores 'null' (nulos) e valores zeros para números.
Isso é o que ocorre automaticamente, quando nenhum construtor é especificado. Vamos mostrar como especificar construtores.
O que é e como utilizar o construtor padrão em Classes
Construtor é um método, em Java.
Embora não tenhamos estudado métodos ainda, diremos como utilizar.
É bem simples, e em breve você aprenderá tudo sobre método em nosso curso de Java.
Métodos são um trecho de código que fazem uma coisa específica, em programação.
Se já estudou C ou C++, lá eles são chamados de funções ou de rotinas ou sub-rotinas em outras linguagens, como Perl.
Podemos criar vários construtores. O padrão é aquele que não recebe nenhum parâmetro.
Em termos mais simples, é aquele que não recebe nenhuma informação. Ele sempre vai ser executado quando você criar um objeto.
Para criar um construtor, crie um método com o mesmo nome da classe.
Métodos tem a seguinte sintaxe:
nomeDoMetodo( tipoDoParametro nomeDosParametros) {
// código dos métodos
}
Como nossa classe se chama "Aluno", nosso método é um construtor dessa classe e ela é padrão (não recebe parâmetros) e é 'public', ficará assim:
public Aluno(){
}
Para melhor visualizar a utilidade do construtor padrão, vamos adicionar um print:
public Aluno(){
System.out.println("Objeto criado");
}
Ao rodar seu programa, você verá a mensagem "Objeto criado" sendo exibida logo após ter criado o objeto "patropi".
Esse método é chamado construtor default. Ao contrário do que muitos pensam, default não é padrão (embora usemos muito como se fosse).
Default é omissão. Quando estamos omitindo outros construtores - que recebem parâmetro - é esse construtor 'padrão' (default) que será chamado.
Note que ao criar o objeto, usamos "new Aluno();" - logo, não passamos nenhum parâmetro.
Então, chamamos claramente o construtor padrão.
Criando um construtor que recebe parâmetros
No nosso aplicativo que cadastro um aluno, seu nome e duas notas, estamos atribuindo os valores dessas variáveis direto no objeto:
patropi.nome = entrada.nextLine();
patropi.notaMat = entrada.nextDouble();
patropi.notaFis = entrada.nextDouble();
O que vamos fazer agora, para ilustrar como funciona os construtores que recebem parâmetros, é pedir esses dados antes e iniciar o objeto "patropi" com esses dados.
Por exemplo, vamos criar as variáveis "Nome", "NotaMat", "NotaFis" na 'main' e receber esses dados pela Scanner:
Nome = entrada.nextLine();
NotaMat = entrada.nextDouble();
NotaFis = entrada.nextDouble();
Agora vamos criar nosso construtor na classe "Aluno" que recebe esses três parâmetros: uma string e dois doubles.
Ele vai ficar assim:
public Aluno(String Nome, double NotaMat, double NotaFis){
nome=Nome;
notaMat=NotaMat;
notaFis=NotaFis;
}
Isso pode parecer um pouco estranho ou complicado agora, mas quando estudar melhor métodos, fará todo o sentido do mundo e verá como é simples.
O que esse método faz é receber uma string "Nome", dois doubles "NotaMat" e "NotaFis" e atribuir eles aos valores da classe aluno.
Veja que nossa classe tem uma variável string "nome" e dois doubles "notaMat" e "notaFis", que seriam inicializados com 'null' e '0.0', conforme mostramos no início deste tutorial.
O que estamos fazendo com esse método é inicializar estas variáveis com outros valores! E que valores são estes? Ora, são os valores das variáveis "Nome" e "NotaMat" e "NotaFis", que definimos a partir da classe Scanner no método 'main'.
Embora estejamos engatinhando na orientação a objeto, vamos fazer uma coisa interessante e mostrar o quão poderosa e útil essa técnica é. Vamos adicionar na classe uma variável, um double de nome "media", e no construtor vamos adicionar uma linha de código:
media = (notaMat + notaFis)/2;
Ou seja, nosso construtor vai ficar:
public Aluno(String Nome, double NotaMat, double NotaFis){
nome=Nome;
notaMat=NotaMat;
notaFis=NotaFis;
media = (notaMat + notaFis)/2;
}
O que quer dizer isso?
Que quando iniciarmos nosso objeto com as notas, esse método vai automaticamente calcular a média dessas notas!
Você pode pensar "Mas já fazíamos isso na 'main', durante o print. Não é a mesma coisa?".
Imagine que você vai preencher os dados de 20 alunos.
Então vai colocar 20 vezes essa fórmula? Claro que não.
Aí é que reside a beleza das Classes & Objetos.
Você declara essa fórmula uma única vez, e poderá usar ela sempre que criar um objeto!
Criou o objeto, iniciou ele com as variáveis, ele vai calcular automaticamente a média!
Para acessar essa variável basta fazer: patropi.media
E ela estará lá, bonitinha para você usar.
Como iniciar Objetos com parâmetros
Agora que criamos nossos métodos construtores padrão e com parâmetros, vamos mostrar como iniciar um objeto com dados.
No construtor padrão, vínhamos fazendo:
Aluno patropi = new Aluno();
Agora, que já temos as variáveis "Nome", "NotaMat" e "NotaFis" e um construtor que está preparado para receber esses três tipos de dados, podemos criar um objeto e lançar essas variáveis:
Aluno patropi = new Aluno( Nome, NotaMat, NotaFis);
Note que você só deve criar esse objeto depois de ter essas variáveis com os valores corretos, senão vai iniciar o objeto com valores errados.
Um detalhe importante é que o nome dessas variáveis, "Nome", "NotaMat" e "NotaFis", não precisam ser iguais aos nomes que declaramos lá no construtor da classe "Aluno". Poderíamos passar:
Aluno patropi = new Aluno( name, math, phys);
Desde que "name" seja uma string e "math" e "phys" doubles.
Quando elas 'chegam' no construtor, elas 'chegam' com os nomes "Nome", "NotaMat" e "NotaFis".
Você se acostumará melhor sobre isso quando estudar métodos.
Classes com mais de um construtor
O que ocorre se deixarmos o construtor: Aluno()
e o construtor: public Aluno(String Nome, double NotaMat, double NotaFis)
juntos, na classe "Aluno"?
Nada. Aliás, é até recomendável. É um excelente costume.
Se você iniciar um objeto dessa classe sem nenhum parâmetro, o Java inicia o método construtor padrão (que mostra a mensagem "Objeto criado".
Se iniciar um objeto com 3 parâmetros, ele atribui esses valos que você passou aos valores do objeto e ainda calcula a média.
Você pode ainda criar outros métodos, que recebem só o nome do aluno, só a nota de matemática ou só de a física.
Enfim, os construtores são feitos para inicializar os atributos e preparar o objeto para o uso, seja lá qual for o uso que você queira dar.
Se você utilizar vários construtores, o Java é inteligente o suficiente para saber qual método utilizar dependendo dos parâmetros que você passar. Obviamente se criar dois construtores com a mesma lista de parâmetros, terá problemas.
Para mostrar um exemplo do uso de dois construtores - o padrão e o que recebe 3 parâmetros - ,
vamos criar dois objetos, o "donaFifi", logo no início, e veremos a mensagem "Objeto criado".
Depois pedimos os dados do aluno patropi e inicializamos o objeto "patropi" com esses dados.
Em seguida, usamos as variáveis do objeto desse aluno, inclusive sua média, que foi calcula pelo construtor.
Veja como ficou o código:
PrimeiraClasse.java
import java.util.Scanner; public class PrimeiraClasse { public static void main(String[] args) { Aluno donaFifi = new Aluno(); String nome; Double notaMat, notaFis; Scanner entrada = new Scanner (System.in); System.out.print("Nome do aluno: "); nome = entrada.nextLine(); System.out.print("Nota em Matemática: "); notaMat = entrada.nextDouble(); System.out.print("Nota em Física: "); notaFis = entrada.nextDouble(); Aluno patropi = new Aluno(nome, notaMat, notaFis); System.out.printf("O aluno \"%s\" tirou %2.2f em Matemática, " + "%2.2f em Física e obteve média %2.2f\n", patropi.nome, patropi.notaMat, patropi.notaFis, patropi.media); } }
Aluno.java
public class Aluno { public String nome; public double notaMat, notaFis, media; public Aluno(){ System.out.println("Objeto criado"); } public Aluno(String Nome, double NotaMat, double NotaFis){ nome=Nome; notaMat=NotaMat; notaFis=NotaFis; media = (notaMat + notaFis)/2; } }
Métodos: O que são, para que servem,
como e quando usar os methods
Para ganhar uma seção na apostila online "Java Progressivo", você já deve suspeitar da importância dos métodos.
Nesse artigo, daremos uma descrição sobre o que são métodos, para que servem, como declarar, como usar e quando usar
Introdução aos métodos em Java
Até o presente momento, criamos aplicações que seguiram sempre a seqüência padrão do código.
Ou seja, de cima pra baixo. Com o uso dos laços (while, for, do ... while), testes condicionais if else e os comandos switch, break e continue, passamos a ter um pouco mais de controle e alteramos um pouco o fluxo, ou seja, o jeito que os programas ocorriam.
Porém, eles ainda ocorrem de cima pra baixo. Métodos nada mais são que um bloco de códigos que podem ser acessados a qualquer momento e em qualquer lugar de nossos programas. Ou seja, não precisam estar na ordem 'de cima pra baixo' no código.
A utilidade dos métodos em Java
As utilidade são duas:
1. Organização
Tudo que é possível fazer com os métodos, é possível fazer sem.
Porém, os programas em Java ficariam enormes, bagunçados e pior: teríamos que repetir um mesmo trecho de código inúmeras vezes.
Uma das grandes vantagens, senão a maior, que mostraremos a seguir, de se utilizar os métodos em Java é que escrevemos um trecho de código uma vez (que são os ditos cujos métodos) e podemos acessá-los várias vezes.
2. Reusabilidade
Quando você for iniciar um grande projeto, dificilmente você terá que fazer tudo do zero.
Se fizer, muito provavelmente é porque você se organizou muito mal ou não fez o Curso Java Progressivo.
Aqui vai uma das dicas mais importantes do curso Java Progressivo:
Crie métodos que façam coisas específicas e bem definidas.
Isso será útil para reusabilidade.
O que queremos dizer com métodos específicos?
Em vez de criar um método que constrói uma casa, crie um método que cria um quarto, outro método que cria a sala, outro que cria o banheiro, um método pra organizar a casa etc.
Assim, quando tiver que criar um banheiro, já terá o método específico para aquilo.
Veja os métodos como peças de um quebra-cabeça. Porém, são peças 'coringa', que se encaixam com muitas outras peças.
Reusabilidade é tempo. E tempo é dinheiro.
Por exemplo, no estudo deste curso, você vai criar um método que retorna as raízes de uma equação do segundo grau.
Guarde esse método com um nome que você lembre.
Em trabalhos futuros, você precisará desse método. E aí, vai sempre digitar tudo de novo?
Claro que não! Se organize! Se lembre que já fez esse método, onde guardou, vá lá, copie e coloque em seu novo projeto.
Como declarar métodos em Java
Há várias, mas várias maneiras mesmo de se declarar um método.
Alguns detalhes só iremos explicar melhor quando você souber Orientação a Objetos.
A sintaxe 'geral' dos métodos é a seguinte:
[características do método] nome_do_metodo (tipos parâmetros) {
// código // do seu // método }
Usando/chamando métodos
Vamos criar um método que simplesmente imprime a mensagem na tela: "Curso Java Progressivo".
Vamos chamar esse método de 'mensagem'.
Ele será um método público (public) e não irá retornar nada (void).
Um método que calcula do delta da equação do segundo grau, retorna o valor do delta, ou seja, um float.
No nosso caso, o método vai apenas imprimir uma mensagem na tela.
O 'static', assim como o 'public' e o 'void' serão explicados no decorrer da apostila.
Ele ficará assim:
public static void mensagem(){
System.out.println("Curso Java Progressivo!");
}
Colocamos esse método dentro de alguma classe. No nosso caso, dentro da classe que tem o método 'main'.
Pronto. Código escrito, método declarado. Como usar o método?
Chamando! Como se chama algo? Pelo nome, ué!
Basta escrever 'mensagem();' em seu programa, onde quiser, que o método será chamado e executado.
Teste:
public class metodoTeste {
public static void mensagem()
{
System.out.println("Print de menssagem por função/método!");
}
public static void main(String[] args)
{
System.out.print("Exibindo a mensagem uma vez: ");
mensagem();
System.out.println("Exibindo a mensagem 3 vezes:");
for(int count=1 ;
count<=3 ; c
ount++)
{
mensagem();
}
}
}
Note que nossa classe agora tem dois métodos.
Um é o método 'main', que serve pra inicia os aplicativos Java. O outro exibe uma mensagem.
Já aqui notamos um detalhe importante: podemos, e com freqüência, invocamos um método dentro de outro.
No caso, estávamos na 'main()', quando invocamos o 'mensagem();'.
Note outro detalhe também: em vez de escrever várias vezes:
System.out.println("Curso Java Progressivo!");
Escrevemos só uma vez, no método, e chamamos o método. Mais prático, não?
Esse Java!
Mostraremos, no próximo exemplo, como criar um menu interativo, usando os métodos.
Você vai ver que com os methods as coisas ficam mais organizadas, economiza-se linhas de códigos e fica mais fácil de entender quando se olha os códigos. Além da questão da reusabilidade do código, já que você poderá sempre usar esses métodos em outros aplicativos.
Clique aqui e veja como criar um menu de opções em Java, usando laços (do ... while), o comando switch (para escolher entre as opções do menu) e 5 métodos.
Aplicativo:
menu simples usando métodos, laços e o comando switch
Usando um método para exibir um menu de opções
Focando o objetivo de nosso curso, que é fazer e mostrar coisas úteis, vamos mostrar uma utilidade de métodos que simplesmente exibem mensagens na tela.
Programa em Java: Como criar um menu
Vamos usar o exercício 9, sobre saídas simples (Clique aqui para ver a questão).
Basicamente, vamos usar nossos conhecimentos em laços (do ... while), o comando switch (para escolher entre as opções do menu) e 5 métodos.
Um método para cada opção (inclui, altera, exclui, consulta) e outro método que mostra esse menu de opções.
Como as opções são números, usaremos um tipo inteiro, chamado 'opcao', para receber as opções do usuário.
Como queremos que o menu seja exibido ao menos uma vez, usamos o do...while.
Ao entrar nesse laço, o menu é exibido com o comando 'menu();' que chama o método que exibe o menu.
Logo após, o programa espera a entrada do usuário. Dependendo do que foi digitado, o método específico - inclui(), altera(), exclui() ou consulta() - é selecionado pelo switch.
O programa só termina se o usuário digitar 0.
import java.util.Scanner; public class menu { public static void menu(){ System.out.println("\tCadastro de clientes"); System.out.println("0. Fim"); System.out.println("1. Inclui"); System.out.println("2. Altera"); System.out.println("3. Exclui"); System.out.println("4. Consulta"); System.out.println("Opcao:"); } public static void inclui(){ System.out.println("Você entrou no método Inclui."); } public static void altera(){ System.out.println("Você entrou no método Altera."); } public static void exclui(){ System.out.println("Você entrou no método Exclui."); } public static void consulta(){ System.out.println("Você entrou no método Consulta."); } public static void main(String[] args) { int opcao; Scanner entrada = new Scanner(System.in); do{ menu(); opcao = entrada.nextInt(); switch(opcao){ case 1: inclui(); break; case 2: altera(); break; case 3: exclui(); break; case 4: consulta(); break; default: System.out.println("Opção inválida."); } } while(opcao != 0); } }
O comando RETURN:
obtendo informações dos métodos
Nesta seção do curso Java Progressivo iremos mostrar para que serve e como usar o comando return nos métodos.
Return: Retornando informações úteis em Java
Embora seja possível não retornar nada dos métodos (como simplesmente mostrar uma mensagem ou um menu de opções, como fizemos no artigo passado), o mais comum é que os métodos retornem algo.
Como assim 'retornar'?
Retornar no sentido de resultado. Por exemplo, uma soma.
O método pode ser simplesmente um trecho de código que calcula a soma de dois números e retorna esse resultado.
Ou pode ser um método que recebe os coeficientes de uma equação do segundo grau e retorna suas raízes.
O retorno pode ser um inteiro, um float, uma string, pode ser simplesmente uma decisão (como um boolean) ou outra coisa mais elaborada que você desejar.
Mas como eu disse, e mais uma vez vou repetir, crie métodos simples e os mais específicos possíveis.
Não crie métodos que fazem mil coisas. Ao invés disso, crie mil métodos, onde cada um faz uma coisa bem determinada.
Isso é importante para você se organizar, não se perder e poder reutilizar seus códigos no futuro.
Retornando inteiros, floats, doubles... em Java
No exemplo do artigo passado onde criamos um método que simplesmente mostrava uma mensagem na tela, ele não retornava nada.
Isso poderia ser visto por um detalhe na declaração do método, o 'void'.
Para retornar um inteiro, usaremos 'int'.
Por exemplo, vamos criar um método que retornar o valor de '1+1'.
Para fazer isto basta escrever 'return' e o que quisermos retornar após. Veja:
public static int soma(){ return 1+1; }
E agora, como usamos um retorno de inteiro?
É o mesmo que perguntar 'o que podemos fazer com um inteiro?'
Já que o método retorna um inteiro, você pode ver esse method como um inteiro.
Ou seja, você pode imprimir, somar com outro inteiro, com um float, ou simplesmente atribuir a um tipo inteiro.
Veja:
public class returnTest { public static int soma(){ return 1+1; } public static void main(String[] args) { System.out.print("Declarando a variável 'res_soma' e recebendo o método soma(): "); int res_soma=soma(); System.out.println(res_soma); System.out.println("Imprimindo diretamente o resultado do return: " + soma()); System.out.println("Usando em uma soma: 2 + soma() = " + (2 + soma())); System.out.println("Usando em um produto: 3 * soma() = " + (3 * soma())); } }
Se quisermos retornar um float: return 1.1*3.3
Devemos declarar o método como 'public static float soma()' ou obteremos um erro.
Retornando uma String em Java
Vamos refazer o programa do artigo passado. Porém, ao invés de imprimir mensagem na tela, ele vai retornar a string:
public class returnString { public static String mensagem(){ return "Curso Java Progressivo!"; } public static void main(String[] args) { System.out.println("Exibindo a mensagem uma vez: "+ mensagem()); System.out.println("Exibindo a mensagem 3 vezes:"); for(int count=1 ; count<=3 ; count++){ System.out.println(mensagem()); } } }
Mais uma vez, você poderia ter declarado uma variável do tipo String para receber o 'return'.
Note que, dentro do método, seria a mesma coisa se tivéssemos feito:
public static String mensagem(){ String mensagem; mensagem="Curso Java Progressivo!"; return mensagem; }
Retornando boolean em Java
Se você é um brasileiro e defensor ferrenho da língua portuguesa, pode definir os seguintes métodos:
public static boolean verdade(){; return true; } public static boolean falso(){ return false; }
E pronto. Agora poderá usar
if (verdade() ) ao invés de if(true)
ou if (falso() ) no lugar de if(false)
:)
Até o momento nossos métodos não são flexíveis, pois sempre imprimem e retornam a mesma coisa.
Isso vai mudar quando você aprender como os method são utilizados em programas reais, através de argumentos e parâmetros,
que será o assunto do próximo artigo de nossa apostila online Java Progressivo.
Parâmetros e Argumentos:
passando informações para os métodos
Em nosso curso de Java, até o momento, os nossos métodos faziam coisas bem específicas,
como exibir uma string, um menu ou realizavam cálculos matemáticos. Isso, na prática, não é muito útil.
Diferença entre parâmetro e Argumento em Java
Parâmetro e argumentos são os meios na quais nós passaremos dados para o método,
e estes métodos irão trabalhar especificamente em cima dessas informações que nós demos.
E, geralmente, vão nos retornar algum resultado.
Declarando métodos com parâmetros
Sempre que um method receber dados, estes serão recebidos através dos parâmetros.
Em Java, assim como em C ou C++, você precisa deixar bem claro quais e qual o tipo dos dados que você vai passar.
Em algumas linguagens, como Perl, isso não é necessário.
Isso tem a ver com tipagem, alocação de memória e outras coisas de mais baixo nível.
(Quando falamos em baixo nível, em Java ou computação, estamos no referindo ao nível de abstração.
Quanto mais baixo mais próximo do hardware ou dos bits estamos falando).
A sintaxe da declaração fica assim:
[dados_do_método] tipo_de_retorno nome_do_método (tipo nome_do_parametro1, tipo nome_do_parametro2)
{
//código // do // method
}
Uma função que recebe um inteiro e retorna um inteiro teria a seguinte 'cara':
public int metodo(int numero)
{
//código return intNumber;
}
Esse tipo, no caso 'int', que vem logo antes do nome do método se refere ao tipo do retorno.
Se seu method retorna float, deverá ser: public float metodo(....)
Se for String, deverá ser: public static String metodo(...)
O static será explicado mais adiante em nosso curso Java Progressivo, na seção de Orientação a Objetos da apostila online.
Até então estamos usando pois estamos chamando os métodos a partir da main, que é um static também.
Exemplo: Passando argumentos para um método
Função que calcula o quadrado de um número
Pela declaração, vemos que o seguinte método recebe um inteiro, o 'num' e retorna um inteiro também.
Dentro de métodos podemos declarar tipos. No caso, declaramos o tipo 'quadrado', que recebe o valor de 'num' elevado ao quadrado, ou seja: num*num
e retorna esse 'quadrado'.
public static int quadrado(int num){ int quadrado; quadrado = num * num; return quadrado; }
Para usarmos um método que recebe um inteiro como parâmetro precisamos passar um argumento ao método. Por argumento, entenda um valor, algo real. No programa, a chamada do método seria a seguinte: quadrado(numero);
Nos exemplos passados não colocávamos nada entre parênteses, pois os métodos que criamos nos artigos passado de nosso curso não tinham parâmetro. Agora, temos que passar um número, ficaria algo do tipo: quadrado(2), quadrado(3) etc.
Veja como ficaria esse método em um aplicativo Java que pede ao usuário um número inteiro e retorna o quadrado desse valor:
import java.util.Scanner; public class quadrado { public static int quadrado(int num){ int quadrado; quadrado = num * num; return quadrado; } public static void main(String[] args) { int numero, numero_quadrado; Scanner entrada = new Scanner(System.in); System.out.print("Entre com um inteiro: "); numero = entrada.nextInt(); numero_quadrado=quadrado(numero); System.out.printf("%d elevado ao quadrado é %d", numero, numero_quadrado); } }
Note que, na 'main', declaramos o valor 'numero', fizemos com que ele receba um valor inteiro e passamos esse valor inteiro ao método.
Você pode achar estranho o fato de usarmos 'numero' na 'main' e 'num' no método 'quadrado()'. Porém, é assim mesmo.
Na 'main', o número que o usuário forneceu fica armazenado na variável 'numero',
mas quando passamos este valor ao método, estamos passando um número.
Este número, agora, será armazenado na variável 'num' do método 'quadrado()'.
Chamamos essa variável, 'num', de variável local, pois ela só existe dentro do método.
Se você tentar usar ela fora do método, obterá um erro.
Teste, adicione a linha seguinte ao programa:
System.out.println(num)
Você nem vai conseguir compilar, pois para a 'main', não existe 'num'.
Declaramos dentro do method 'quadrado()' a variável 'quadrado' somente para efeitos didáticos, para mostrar que podemos declarar variáveis dentro de um método, mas à rigor, ela não é necessária, poderíamos simplesmente ter feito:
public static int quadrado(int num){ return num * num; }
Nesse exemplo, 'num' seria um parâmetro e o valor de 'numero' seria o argumento.
Exemplo: passando uma lista de parâmetros para um método
Cálculo do IMC
No artigo sobre operações Matemáticas em Java, passamos como exercício o cálculo do IMC (índice de massa corporal).
Que nada mais é um número, que é calculado pelo peso (em kilogramas) dividido pelo quadrado da altura (em metros) da pessoa.
O nosso método IMC recebe dois valores float, o peso e a altura, e retorna outro float, que é o valor do IMC. A declaração de um método com mais de um parâmetro ficaria assim:
public static float IMC(float peso, float altura)
Nosso programa ficaria assim:
import java.util.Scanner; public class IMC { public static float IMC(float peso, float altura){ float imc; imc = peso/(altura*altura); return imc; } public static void main(String[] args) { float peso, altura, imc; Scanner entrada = new Scanner(System.in); System.out.print("Entre com seu peso, em kilos: "); peso = entrada.nextFloat(); System.out.print("Entre com sua altura, em metros: "); altura = entrada.nextFloat(); imc = IMC(peso, altura); System.out.printf("Seu IMC vale: %.2f",imc); } }
Note que agora invocamos o método IMC e passamos dois argumentos: peso e altura
Note que na 'main', usei exatamente as mesmas variáveis que usei dentro do method IMC(), fiz isso pra mostrar que não há problemas em fazer isso. Porém, quando usamos essas variáveis dentro da 'main', os valores serão os que o usuário passou.
Dentro do método, esses valores podem ser alterados e poderão representar outros valores.
Cuidado para não confundir. Embora tenham o mesmo nome, estamos usando em lugares diferentes de nosso aplicativo Java.
Exemplo: chamando um método dentro do outro
Cálculo do IMC
Lembra que falei várias vezes sobre organização, fazer métodos simples e diretos?
Pois é, vamos mostrar, na prática, como fazer isso.
Notou que no cálculo do IMC elevamos um número ao quadrado, a altura?
Notou que o primeiro método calcula um número ao quadrado?
Você deve estar pensando:
'Ora, vamos usar o primeiro exemplo para fazer um cálculo dentro do segundo.'
Quase. Note que o primeiro exemplo usamos inteiros! Mas a altura, que usamos no segundo exemplo desse artigo, se refere a float!
Então vamos modificar um pouco nossa função que calcula o quadrado de um número, para receber e retornar decimais e usar dentro do método IMC();
Os métodos ficariam assim:
public static float quadrado(float num){ return num*num; } public static float IMC(float peso, float altura){ float imc; imc = peso/quadrado(altura); return imc; }
E o aplicativo Java ficaria assim:
import java.util.Scanner; public class IMC { public static void main(String[] args) { float peso, altura; Scanner entrada = new Scanner(System.in); System.out.print("Entre com seu peso, em kilos: "); peso = entrada.nextFloat(); System.out.print("Entre com sua altura, em metros: "); altura = entrada.nextFloat(); System.out.printf("Seu IMC vale: %.2f", IMC(peso, altura)); } public static float IMC(float peso, float altura){ return peso/quadrado(altura); } public static float quadrado(float num){ return num*num; } }
Classe Math: constantes, principais métodos
e chamando métodos de outras classes
Vamos estudar uma importante classe, a Class Math, que provém várias funcionalidades matemáticas que nos ajudarão bastante em nosso curso de Java.
Aliado a essa grande e poderosa classe com a nossa seção de Métodos, vamos aprender como usar os methods que estão declarados em uma classe diferente daquela que estamos fazendo a chamada.
Usando métodos de outras classes em Java
Até o presente momento em nossa apostila online Java Progressivo, chamamos os métodos da seguinte maneira:
nome_do_metodo(argumentos);
Fizemos isso porque definimos esses métodos dentro da mesma classe (até o momento só fizemos aplicativos Java contendo uma classe. Na próxima seção estudaremos classes).
Porém, quando o método não está presente na classe em que estamos fazendo a chamada, a sintaxe é diferente, ela é a seguinte:
Nome_da_classe.Nome_do_metodo(argumentos);
Vale salientar que a declaração é feita dessa maneira pois esses métodos são estáticos (static).
Ou seja, não foi necessário criar um objeto da classe Math (diferente da classe Scanner, por exemplo).
Na próxima seção do curso, sobre Classes, você entenderá mais sobre static.
A Classe Math (Class Math) do Java
Para ilustrar o uso de métodos de outras classes e, de quebra, para nos auxiliar nos cálculos de constantes (como do número pi, do número de euler), no cálculo de funções trigonométricas (senos, cossenos, tangentes etc) e outras funcionalidades, vamos apresentar e usar a classe Math.
Essa classe já está na package (pacote) java.lang. Ou seja, não precisamos importar, que nem fazemos com a classe Scanner.
Constantes da classe Math:
Vamos imprimir o valor de Pi e da constante de euler, o 'e' dos números exponenciais.
Esses valores estão armazenados nas constantes PI e E, da classe Math.
As constantes de outras classes são acessadas da mesma maneira que os métodos, ou seja:
Math.PI
Math.E
Vamos imprimir esses valores:
public class constantes { public static void main(String[] args){ System.out.println("O valor de pi é: " + Math.PI); System.out.println("O valor de E é: " + Math.E); } }
Exponencial e potenciação na classe Math
Para calcular valores do tipo: e^x
usamos o método: exp() , que recebe um double e retorna um double.
numero = Math.exp(argumento)
Para calcular qualquer tipo de potências, da forma: a^b
onde a e b são do tipo double, usamos o método pow() da classe Math
numero = Math.pow(a,b)
Veja:
public class Mathtest { public static void main(String[] args){ System.out.println("'e' elevado ao quadrado = "+ Math.exp(2)); System.out.println("2 elevado ao cubo = " + Math.pow(2, 3)); } }
Lembre-se que estes métodos retornam double.
Caso você tenha declarado um float e queira receber o resultado no sua variável float, use o cast.
Por exemplo, no caso dos nossos methods para o cálculo de IMC, o 'quadrado' retorna um float, então fazemos:
(float)Math.pow(altura,2);
Assim, o método Math.pow() irá retorna um float, ao invés do double.
Veja como ficariam os nossos métodos:
public static float IMC(float peso, float altura){ return peso/(float)Math.pow(altura, 2); } public static float quadrado(float num){ return Math.pow(num,2); }
Na verdade, nem precisaríamos mais do método quadrado(), pois o pow() já faz isso.
Calculando a Raiz quadrada em Java através da classe Math
Para calcular a raiz quadrada de um número positivo, usamos o método sqrt(), de square root (raiz quadrada em inglês).
Que recebe e retorna um double.
Por exemplo, um programa que mostra a raiz quadrada de PI:
public class RaizDePi { public static void main(String[] args){ System.out.println("A raiz quadrada de Pi é = "+ Math.sqrt( Math.PI ) ); } }
Calculando logaritmos naturais em Java através da classe Math
Calcula logaritmos naturais (ou seja, de base 'e') através do método: log()
que recebe e retorna um double.
Por exemplo, um programa que mostra o logaritmo de natural de 10 e do número 'e':
public class logaritmos { public static void main(String[] args){ System.out.println("O logaritmo natural de 10 é = "+ Math.log(10) ); System.out.println("O logaritmo natural de 'e' é = "+ Math.log( Math.E ) ); } }
Calculando senos, cossenos, tangentes e outras funções trigonométricas em Java através da classe Math
As principais funções trigonométricas são seno, cosseno e tangente, e são calculadas através dos métodos:
Math.sin()
Math.cos()
Math.tan()
Que recebem e retornam valores do tipo double. Porém, os valores que estas funções recebem devem ser em RADIANOS!
public class Trigonometricas { public static void main(String[] args){ System.out.println("O seno de 90 é = "+ Math.sin( (Math.PI)/2 ) ); System.out.println("O cosseno de 0 é = "+ Math.cos(0) ); System.out.println("A tangente de 45 é= "+ Math.tan( (Math.PI)/4 )); } }
Módulo, máximo, mínimo e arredondamento em Java
através da classe Math
Para calcular o módulo de um número 'numero' usamos: Math.abs(numero)
Para calcular o valor mínimo de dois números 'num1' e 'num2', usamos: Math.min(num1,num2)
Para calcular o valor máximo de dois números 'num1' e 'num2', usamos: Math.max(num1,num2)
Para arredondar um número 'numero' para cima, usamos: Math.ceil(numero)
Para arredondar um número 'numero' para baixo, usamos: Math.floor(numero)
Estes métodos, assim como todos os outros, recebem e retornam double. Caso deseje receber a passar outro tipo, use cast conforme foi explicado no método pow().
Sobrecarga de métodos (method overloading):
declarando métodos com o mesmo nome
Em nosso artigo sobre parâmetros e argumentos em Java, mostramos um exemplo sobre um método que recebe um número e retorna o quadrado desse número.
Porém esse método tem uma limitação: ele só recebia inteiros.
E se você quiser criar métodos com float? double?
Faça uma sobrecarga.
Overloading ou sobrecarga em Java
O termo sobrecarga vem do fato de declararmos vários métodos com o mesmo nome, estamos carregando o aplicativo com o 'mesmo' método. A única diferença entre esses métodos são seus parâmetros e/ou tipo de retorno. Por exemplo, vamos declarar, num mesmo aplicativo, dois métodos quadrados:
um que recebe e retorna inteiros
e outro que recebe e retorna double.
public static int quadrado(int num){ int quadrado; quadrado = num * num; return quadrado; }
public static double quadrado(double num){ double quadrado; quadrado = num * num; return double; }
Quando declaramos estes dois métodos, estamos fazendo umas sobrecarga de métodos, ou method overloading em Java.
O que acontece, então, quando fazemos o chamado da função? Já que existem duas.
Embora os nomes sejam os mesmos, elas atuam de forma diferentes e ocupam espaços diferentes em memória, pois estão lidando com tipos diferentes de variáveis.
Quando invocamos o método com um inteiro como um argumento, o Java é inteligente o suficiente para invocar corretamente o método que foi declarado com inteiro como parâmetro. Caso invoquemos o método usando um double como um argumento, o method a ser executado será aquele que foi declarado com o tipo double em seu parâmetro.
Veja:
public class Sobrecarga { public static int quadrado(int num){ int quadrado; quadrado = num * num; return quadrado; } public static double quadrado(double num){ double quadrado; quadrado = num * num; return quadrado; } public static void main(String[] args){ System.out.println("Inteiro 2 ao quadrado: " + quadrado(2)); System.out.println("Double PI ao quadrado: " + quadrado( Math.PI )); } }
Varargs - passando uma lista de argumentos,
de tamanho qualquer, para um método
Até o momento, em nosso curso de Java, sempre especificamos o número exato de argumentos que um método pode receber.
Quando estudarmos Arrays e ArrayLists logo mais veremos que podemos passar uma quantidade qualquer de valores aos métodos.
Há, porém, uma outra maneira de fazer isso em Java, através das reticências: ...
Com o uso desse artifício, podemos passar um, dois, três ou um número qualquer de argumentos para um mesmo método que ele vai saber como tratar esses dados.
Sintaxe:
Na hora de declarar seu método, use as reticências logo após o tipo do parâmetro:
tipo_de_retorno nomeDoMétodo( tipo... nomeDoParametro ){
//código do seu method
}
Pronto.
O Java é esperto o bastante para saber que 'nomeDoParametro' é, na verdade, uma lista de valores.
Exemplo de uso
Código Java: Crie um aplicativo que receba 5 valores do usuário e calcule a média dos 5, dos primeiros 4, 3 e 2 valores inseridos. Use apenas um método que receba uma lista de argumentos de tamanho qualquer.
import java.util.Scanner; public class medias{ public static float media(float... valores){ float media=0; for(float valor: valores){ media +=valor; } return media/valores.length; } public static void main(String[] args){ float valor1, valor2, valor3, valor4, valor5; Scanner entrada = new Scanner(System.in); System.out.print("Entre com o valor 1: "); valor1 = entrada.nextFloat(); System.out.print("Entre com o valor 2: "); valor2 = entrada.nextFloat(); System.out.print("Entre com o valor 3: "); valor3 = entrada.nextFloat(); System.out.print("Entre com o valor 4: "); valor4 = entrada.nextFloat(); System.out.print("Entre com o valor 5: "); valor5 = entrada.nextFloat(); System.out.println("A média dos 5 números é: "+media(valor1,valor2,valor3,valor4,valor5)); System.out.println("A média dos 4 primeiros números é: "+media(valor1,valor2,valor3,valor4)); System.out.println("A média dos 3 primeiros números é: "+media(valor1,valor2,valor3)); System.out.println("A média dos 2 primeiros números é: "+media(valor1,valor2)); } }
Orientação a Objetos, parte II: Os métodos set e get - Composição - Enum
Auto-referência com o THIS
Invocando métodos de Classes e Objetos
No tutorial de Java passado relacionado a Orientação a Objetos, falamos como criar construtores, com ou sem parâmetros, em nossas classes.
Nesta pequena aula, iremos aprender como referenciar membros de um objeto através da keyword 'this', que é uma ferramenta bastante usada por programadores Java em métodos dentro de Classes.
Referenciando membros da classe com this
Imagine que criemos uma classe chamada "Funcionario", onde seu construtor padrão recebe uma String com o nome do funcionário, um inteiro com seu número de identificação e um double com seu salário.
Se esses dados, dentro da classe são:
private String nome;
private int ID;
private double salario;
E o cabeçalho do construtor é:
public Funcionario( String nome, int ID, double salario)
Como faríamos a atribuição? Ora, do mesmo jeito que fizemos antes:
nome = nome;
ID = ID;
salario = salario;
Epa!
Notou que as variáveis da classe e as variáveis do cabeçalho tem o mesmo nome?
E agora, como o Java vai saber que as variáveis da esquerda se referem as variáveis 'private' da classe a as da direita são as que o usuário mandou pra criar o objeto?
Já sei! Basta criar o método com nomes de variáveis diferentes, como fizemos antes:
public Funcionario( String Nome, int id, double Salario){
nome = Nome;
ID = id;
salario = Salario;
}
Ok, isso funcionaria perfeitamente. Mas seria extremamente incômodo, e desorganizado, criar dois nomes pra um mesmo tipo de variável.
Não pode parecer problema agora, com essa simples aplicação. Mas em uma situação real, em que seu programa em Java terá centenas de variáveis e você tiver que criar e decorar nomes de variáveis, isso vai ser um baita problema.
Para isso, existe o 'this', que referencia - ou seja, aponta - a própria classe!
'this' em inglês, significa 'isso', 'isto'. É bem fácil seu uso, veja como ficaria nosso construtor:
public Funcionario( String nome, int ID, double salario){
this.nome = nome;
this.ID = ID;
this.salario = salario;
}
Pronto.
Agora ficou óbvio que 'this.nome' é a variável 'nome' da classe "Funcionario"
e 'nome' é a variável que a classe recebeu para criar um objeto!
Usamos o 'this' dentro da classe. Assim, sempre que colocarmos 'this.' antes de uma variável, fica implícito ao Java que estamos nos referindo aos atributos daquela Classe.
Podemos usar, inclusive, em um print, caso esteja dentro da classe. Em um método, por exemplo, como veremos a seguir.
Outra utilidade do 'this' é passar o objeto atual como parâmetro.
public Object getObjeto(){
return this;
}
Outro exemplo disso é criar o método...:
public String toString()
...na sua classe, e usar 'this' dentro de um print. O Java entenderá que deve ser impresso o que estiver dentro desse método 'toString()'.
Outra utilidade do 'this' é invocar outros construtores. Para invocar um construtor dentro de outro, essa chamada deve ser o primeiro comando do construtor atual.
Por exemplo, fazendo simplesmente:
this;
Estamos chamando o construtor padrão, que não recebe parâmetros.
Fazendo:
this(2112);
Estamos invocando o construtor que recebe um inteiro como parâmetro.
Lembrando que quando criamos um construtor que não é o padrão, o Java não vai mais criar o construtor padrão vazio.
Esse construtor padrão vazio só é criado automaticamente quando não criamos nenhum construtor.
Como invocar métodos de objetos que criamos
Vamos criar um método, dentro da classe "Funcionário", que exibe todas as informações de um objeto dessa classe. Vamos chamar de 'exibir':
public void exibir(){
System.out.printf("O funcionário %s, de número %d recebe %.2f por mês", this.nome,this.ID,this.salario);
}
Para invocar, basta colocar '.exibir()' após o nome do objeto, que fará com que este método rode.
Note, porém, que conforme explicamos em nosso artigo sobre Classes e Objetos, essa classe é apenas uma abstração.
Ou seja, esse método não existe de verdade! Ele só vai passar a existir quando criarmos um objeto dessa classe!
(Na verdade ele pode existir, caso a classe fosse estática. Estudaremos isso em breve).
Para ilustrar a chamada de métodos de um objeto e o uso do 'this', vamos criar um funcionário - um objeto - de nome 'chefe'.
O código ficará assim:
thisMetodo.java
public class thisMetodo{ public static void main(String[] args){ String nome = "Neil Peart"; int ID=2112; double salario = 1000000; Funcionario chefe = new Funcionario(nome, ID, salario); chefe.exibir(); } }
Funcionario.java
public class Funcionario { private String nome; private int ID; private double salario; public Funcionario(){ System.out.println("Método construtor padrão invocado!"); } public Funcionario( String nome, int ID, double salario){ this(); System.out.println(this); this.nome = nome; this.ID = ID; this.salario = salario; } public String toString(){ return "Foi usado : System.out.println(this)"; } public void exibir(){ System.out.printf("O funcionário %s, de número %d recebe %.2f por mês", this.nome,this.ID,this.salario); }
set e get:
o que são e como usar esses métodos de forma correta
Vamos aprender nesta aula de nosso curso online de Java os dois métodos mais usados pelos programadores Java: os getters e setters.
O que são e para que servem os métodos get e set
get e set nada mais são que métodos, que freqüentemente vemos em classes de Java.
Eles servem para pegarmos informações de variáveis da classe que são definidas como 'private',
porém esses método são definidos como 'public'.
Daí surge uma pergunta natural: por que criar métodos para acessar variáveis, se podemos acessar elas diretamente?
Simples: questão de segurança.
As variáveis 'private' só podem ser acessadas de dentro da Classe. É como se elas fossem invisíveis foram do escopo da classe/objeto.
Assim, evitamos que outros métodos, classes ou hackers tenham acesso aos dados de determinada classe, que muitas vezes podem ser dados privados, como é caso de aplicações para empresas e bancos.
Como tratar, então, essas variáveis?
Aí que entra a questão dos métodos. Vamos permitir o acesso a essas variáveis, claro (senão não haveria sentido em usar esses atributos).
Porém, vamos ter um total controle sobre essas variáveis através dos métodos.
Por exemplo: suponha que você tenha que criar uma aplicação para um banco, onde vai limitar o número de saques de um cliente.
Os valores do saldo desse cliente ficarão armazenados na variável 'saldo'. Se você der acesso total a essa variável, o cliente poderia usar de forma descontrolada os serviços do banco.
Porém, podemos criar um método em que, cada vez que o cliente saque dinheiro ele ative um contador:
public void setSaque(double valor){
saldo -= valor;
contador++;
}
Nesse trecho de código, quando o usuário tenta sacar, através do menu do caixa eletrônico, ele vai para o método 'saque()'.
Então cada vez que ele saca, seu saldo diminui em 'valor' e um contador (que é uma variável da classe) é incrementado. É impossível fugir desse método!
Sacou? O contador roda! Sempre!
Sabemos que temos um limite diário de saque, vamos supor que seja três. Então vamos limitar o número de saques:
public void setSaque(double valor){
if(contador <=3){
saldo -= valor;
contador++;
} else {
System.out.println("Você atingiu o limite de saques diários");
}
}
Pronto. Se você sacar 3x, o contador terá valor 4 (supondo que seu valor inicial seja 1),
e agora é impossível ele sacar mais, pois sempre vai cair no else.
No próximo artigo criaremos uma aplicação que simula um banco.
Como usar os métodos get e set
Usamos get para obter informações. Esse tipo de método sempre retorna um valor.
Usamos set para definir valores. Esse tipo de método geralmente não retorna valores.
Usando o exemplo da classe "Funcionario", do artigo passado sobre this e uso de métodos em Classe e Objetos, temos o código:
getSet.java
public class getSet{ public static void main(String[] args){ String nome = "Neil Peart"; int ID=2112; double salario = 1000000; Funcionario chefe = new Funcionario(); chefe.setNome(nome); chefe.setID(ID); chefe.setSalario(salario); chefe.exibir(); } }
Funcionario.java
public class Funcionario { private String nome; private int ID; private double salario; public void exibir(){ System.out.printf("O funcionário %s, de número %d recebe %.2f por mês", getNome(),getID(),getSalario()); } public void setNome( String nome ){ this.nome = nome; } public void setID( int ID ){ this.ID = ID; } public void setSalario( double salario ){ this.salario = salario; } public String getNome(){ return this.nome; } public int getID(){ return this.ID; } public double getSalario(){ return this.salario; } }
Como invocar métodos de dentro do construtor
Outra maneira de inicializarmos as variáveis de um Objeto, é usando os métodos 'set', direto do construtor:
setGet.java
public class setGet{ public static void main(String[] args){ String nome = "Neil Peart"; int ID=2112; double salario = 1000000; Funcionario chefe = new Funcionario(nome, ID, salario); chefe.exibir(); } }
Funcionario.java
public class Funcionario { private String nome; private int ID; private double salario; public Funcionario( String nome, int ID, double salario){ setNome(nome); setID(ID); setSalario(salario); } public void exibir(){ System.out.printf("O funcionário %s, de número %d recebe %.2f por mês", getNome(),getID(),getSalario()); } public void setNome( String nome ){ this.nome = nome; } public void setID( int ID ){ this.ID = ID; } public void setSalario( double salario ){ this.salario = salario; } public String getNome(){ return nome; } public int getID(){ return ID; } public double getSalario(){ return salario; } }
Uso errado do get e set
Qual a diferença entre alterar e pegar o valor de uma variável diretamente (caso ela seja pública), e alterar e pegar o valor dela através de set e get (se ela for privada) ? Não é segurança, já que podemos alterar seu valor como bem quisermos, através dos métodos set.
Definir get e set para todas as variáveis, de forma direta, é um exemplo de péssimo hábito de programação. Infelizmente é uma técnica bem comum e muitos programadores, até os mais experientes, cometem esse ato inútil.
Para que servem então? Quando usar?
Só use set e get quando você for fazer algo além de simplesmente mudar o valor de uma variável 'private'.
Lá no começo de nosso tutorial, usamos o exemplo de contador, que limita o número de saques de um cliente a um caixa eletrônico.
Outro exemplo disso, seria em um aplicativo de vendas.
Ao comprar, o método set ou get automaticamente define o valor de desconto ou juros. Assim esse método faz algo mais além do que mudar a variável. Assim, sua aplicação se torna robusta e segura, pois se torna impossível mudar o valor da variável sem que seja alterada o valor do juros ou desconto.
Sempre que seu método set do tipo for:
retorno metodo(tipo valor){
this.nome_da_variavel = valor;
}
Muito provavelmente ele é inútil. Para saber quando usar é simples: use quando for fazer algo além de fornecer valor.
Um exemplo util:
retorno metodo(tipo valor){
this.nome_da_variavel = valor * juros;
};
Vamos treinar!
Exercício: Caixa Eletrônico em Java - Caixa.java
Crie um protótipo de caixa eletrônico em Java. No início, ele pede seu nome e valor $$ inicial que tem na conta. O programa deve fornecer um número de 4 dígitos - número da conta - para o usuário (use Random). Esses dados serão usados para criar um objeto da classe "Conta.java"
A seguir, mostra um menu com as opções (esse menu deverá ser um método):
- Extrato: exibe o nome, número da conta, saldo e quantos saques já foram realizados
- Sacar: recebe o valor a ser sacado, informa se pode ser sacado (não pode ficar negativo) e mostra o saldo
- Depositar: recebe o valor a ser depositado e mostra o novo saldo
- Sair
Esse menu aparece até o usuário escolher sair. As outras opções são métodos que devem fazer parte da "Conta.java" (setters e getters). Note que o usuário pode sacar no máximo 5 vezes por dia.
***************************************************************
Aplicativo: Simulação simples de conta bancária
Aplicativo: Conta bancária/Caixa eletrônico simples em Java
Crie um protótipo de caixa eletrônico na linguagem de programação Java. No início, ele pede seu nome e valor $$ inicial que tem na conta. O programa deve fornecer um número de até 4 dígitos - número da conta - para o usuário (use Random). Esses dados serão usados para criar um objeto da classe "Conta.java"
A seguir, mostra um menu com as opções (esse menu deverá ser um método):
- Extrato: exibe o nome, número da conta, saldo e quantos saques já foram realizados
- Sacar: recebe o valor a ser sacado, informa se pode ser sacado (não pode ficar negativo) e mostra o saldo
- Depositar: recebe o valor a ser depositado e mostra o novo saldo
- Sair
Esse menu aparece até o usuário escolher sair. As outras opções são métodos que devem fazer parte da "Conta.java" (setters e getters). Note que o usuário pode sacar no máximo 5 vezes por dia.
Nesse exercício não vamos usar explicitamente as palavras 'set' e 'get' nos nomes dos métodos. Mas isso não é necessário. O que vamos usar é sua ideia: a ideia por trás do 'set' é a de alterar valores de variáveis, pra isso vamos usar os métodos 'sacar' e 'depositar', que altera o valor do saldo ; a ideia por trás do 'get' é de simplesmente obter informações das variáveis, como é o caso do método 'extrato'.
Use a main só para iniciar o aplicativo
Inicialmente, no nosso arquivo "Caixa.java", que contém a 'main', criamos uma conta, pedindo um nome e um valor inicial.
Através do comando: 1 + numero.nextInt(9999) nós sorteamos um número de conta de até 4 dígitos (nextInt(9999) gera números de 0 até 9998, somando 1 gera de 1 até 9999).
Com esses dados, criamos uma conta, que na verdade é o objeto 'minhaConta' da classe "Conta.java".
Iniciamos nosso banco ou caixa eletrônico através do método 'iniciar()'.
Note que tentamos enxugar a 'main', pois é uma boa prática. No geral, ela é usada apenas como 'gatilho', pra começar o programa e não pra ser enchida de variáveis e linhas de código.
Vamos pra classe "Conta.java".
Sistema bancário simples em Java
Nossos atributos (variáveis) são: nome, saldo, conta e saques.
Aqui vamos usar a real função do construtor: inicializar as variáveis. Nesse caso é obrigatório, pois não tem como, em um sistema bancário, criar uma conta sem ter - no mínimo - esses dados.
Vamos ver agora os principais métodos desse sistema bancário:
extrato()
Método simples, que exibe todas as informações do usuário.
sacar(int valor)
Esse método altera a variável 'saldo'. No caso, ele reduz ela.
Porém, só faz sentido reduzir (tirar dinheiro), se 'valor' for menor que o 'saldo', por isso é feito um tratamento através do teste condicional if.
Caso seja possível realizar o saque, devemos incrementar a variável 'saques', para termos controle do número de saques realizados. Caso não seja possível, exibimos uma mensagem informando o problema e nada ocorre.
Ou seja, é um belo exemplo de como usar o método set.
depositar(int valor)
Simplesmente adiciona um valor ao saldo atual.
iniciar()
Aqui é a tela inicial de nosso Caixa Eletrônico Progressivo Java.
Ele usa um laço do while que irá rodar o mini-sistema bancário enquanto o usuário não selecionar a opção de sair, que é o número 4 ( while(opcao != 4 ) ).
A cada iteração é exibido o menu através do método exibeMenu(), é pedido uma entrada (número) ao usuário e esse número é enviado para o método que vai direcionar o programa para a opção escolhida pelo usuário, o escolheOpcao().
exibeMenu()
Um método simples desse sistema bancário é o 'exibeMenu()', que não recebe nenhum argumento nem retorna nenhuma variável. Como o nome diz, ele simplesmente exibe a lista de opções de nosso sistema.
escolheOpcao(int opcao)
Vamos realmente escolher a opção que queremos no método 'escolheOpcao', que recebe um número.
Mas que número é esse?
Ora, é o referente ao menu. Você vê as opções, entra com o número e esse método serve pra escolher a opção desejada.
Escolher opção...isso te lembra algo? Sim, o comando switch.
Caso tenha escolhido a opção 1, eles nos envia para o método 'extrato()'.
Caso seja a 2, deveria ir para o método 'sacar()', porém não é sempre que podemos sacar. Só podemos se tivermos realizado menos de 3 saques.
Caso seja possível realizar o saque, tanto o caso 2 com o caso 3 devem receber um valor do usuário, que é o montante que vai ser sacado ou depositado.
O case 4 é para encerrar o sistema e qualquer outra opção cai na default que acusa como erro.
Código fonte Java do Aplicativo
Caixa.java
import java.util.Scanner; import java.util.Random; public class Caixa { public static void main(String[] args){ // Declarando as variáveis, Scanner e Random String nome; double inicial; Scanner entrada = new Scanner(System.in); Random numero = new Random(); int conta = 1 + numero.nextInt(9999); //Obtendo os dados iniciais do Cliente System.out.println("Cadastrando novo cliente."); System.out.print("Ente com seu nome: "); nome = entrada.nextLine(); System.out.print("Entre com o valor inicial depositado na conta: "); inicial = entrada.nextDouble(); //Criando a conta de um cliente Conta minhaConta = new Conta(nome, conta, inicial); minhaConta.iniciar(); } }
Conta.java
import java.util.Scanner; public class Conta { private String nome; private int conta, saques; private double saldo; Scanner entrada = new Scanner(System.in); public Conta(String nome, int conta, double saldo_inicial){ this.nome=nome; this.conta=conta; saldo=saldo_inicial; saques=0; } public void extrato(){ System.out.println("\tEXTRATO"); System.out.println("Nome: " + this.nome); System.out.println("Número da conta: " + this.conta); System.out.printf("Saldo atual: %.2f\n",this.saldo); System.out.println("Saques realizados hoje: " + this.saques + "\n"); } public void sacar(double valor){ if(saldo >= valor){ saldo -= valor; saques++; System.out.println("Sacado: " + valor); System.out.println("Novo saldo: " + saldo + "\n"); } else { System.out.println("Saldo insuficiente. Faça um depósito\n"); } } public void depositar(double valor) { saldo += valor; System.out.println("Depositado: " + valor); System.out.println("Novo saldo: " + saldo + "\n"); } public void iniciar(){ int opcao; do{ exibeMenu(); opcao = entrada.nextInt(); escolheOpcao(opcao); }while(opcao!=4); } public void exibeMenu(){ System.out.println("\t Escolha a opção desejada"); System.out.println("1 - Consultar Extrato"); System.out.println("2 - Sacar"); System.out.println("3 - Depositar"); System.out.println("4 - Sair\n"); System.out.print("Opção: "); } public void escolheOpcao(int opcao){ double valor; switch( opcao ){ case 1: extrato(); break; case 2: if(saques<3){ System.out.print("Quanto deseja sacar: "); valor = entrada.nextDouble(); sacar(valor); } else{ System.out.println("Limite de saques diários atingidos.\n"); } break; case 3: System.out.print("Quanto deseja depositar: "); valor = entrada.nextDouble(); depositar(valor); break; case 4: System.out.println("Sistema encerrado."); break; default: System.out.println("Opção inválida"); } } }
Treinamento Hacker:
Encontre falhas, brechas e erros no aplicativo acima.
As respostas estão em cor branca, logo abaixo, bastando selecionar o texto para visualizar.
Porém, não olhe antes de tentar pensar bastante.
Composição: trocando informações entre objetos
Um dos melhores artifícios do Java é a comunicação entre seus elementos, variáveis, métodos, objetos, classes e coisas que verá mais adiante. Mais que isso, o bacana é que podemos controlar essa comunicação, ela pode ser: pública, privada e protegida.
Nesse tutorial, daremos início a essa comunicação, falando sobre a Composição (Composition), que é simplesmente o ato de passar um objeto para outro, para usar seus métodos ou atributos.
O que é Composição em Java
É instanciar, ou usar, uma classe/objeto em outra(o).
É como se elas se comunicassem, trocassem informações. Ou seja, serve para reutilizar dados, sem ter que criar mais código pra isso.
Simplesmente passamos a informação - na forma de Objeto - para outro Objeto, e este se encarrega de obter os dados e como trabalhar em cima dele. Costuma-se dizer que composição é o ato de delegar trabalho para outro objeto.
Isso deixa seu código mais elegante, menor e mais seguro.
Para que serve a Composição em Java
Como citamos em nosso artigo introdutório sobre Classes e Objetos, a comunicação entre seus dados é essencial.
Talvez não note muito essa necessidade em projetos pequenos e de iniciantes, mas quando for um profissional, vai ter muita dor de cabeça com isso. Através de uma simplificação de projetos reais, vamos mostrar exemplos de códigos e passaremos exercícios para você notar a importância da Composição em aplicações Java.
Demos o exemplo de setores diferentes em uma empresa naquele artigo inicial.
Dificilmente uma empresa de grande porte vai mandar você fazer e ter acesso a todo o sistema, principalmente o setor financeiro.
Geralmente esse é mantido à sete chaves. Mas como expliquei lá, eles podem simplesmente compartilhar certos dados com você, como o salário final de cada funcionário, para você usar em sua classe "Funcionario".
Notou? Você criar sua classe, faz seu projeto e usa dados de outro Objeto/Classe. E o mais importante: é seguro!
Você não faz a mínima idéia do que aconteceu lá, de como é calculado as coisas, dos segredos financeiros da empresa etc.
Graças a orientação a objetos - e outras coisas - isso é possível. Assim você não precisa repetir código.
Além da segurança, a Composição em Java faz com que não seja necessário repetir código. Você simplesmente usa o que já foi feito em outra Classe/Objeto, como é o caso do exemplo da empresa. Imagina se o programador não soubesse Java?
Ia ter que repetir todo o código do setor financeiro dentro da classe "Funcionario"?
Acredite, há coisas bizarras sendo feitas por aí.
Herança x Composição
Mais a frente, em nosso curso online de Java, iremos falar sobre uma das mais importantes, e controversas, e usadas funcionalidades do Java:
a Herança. Isso nos leva ao encapsulamento de dados.
Não vamos entrar em muitos detalhes agora, mas basicamente herança é capacidade de uma classe herdar dados de outra.
Por exemplo, você cria uma classe mãe. Quer criar uma classe parecida com a mãe - mas com outras funcionalidades, você não precisa reescrever tudo. Basta fazer com que a classe filha herde a classe mãe.
Por exemplo, podemos criar a classe "Carro" quem atributos como "motor", "portas", "carburador" etc, pois todos atributos estão presentes em todos carros. Fazemos então a classe "Fusca" e a classe "Ferrari" herdar a classe "Carro". Ora, é claro que vamos adicionar mais funcionalidades na classe "Ferrari", pois ela possuir mais atributos que o padrão genérico generalizado da classe "Carro". Mas não deixa de ser um carro.
Essa generalização, o fato de algumas classes herdadas não precisarem de todos os atributos da classe mãe, também devido ao fato de ao se mudar a classe mãe mudar todas as classes filhas e uma porção de outros detalhes, faz com que herança seja um conceito perigoso e não recomendado. Alguns simplesmente abominam a herança. Não entraremos em detalhe agora, mas usar Composição é inofensivo e bem mais recomendado. Porém, cada caso é um caso.
Exemplo de uso de Composição em Java
Exercício: Descobrindo se um Funcionário chegou atrasado e seu tempo de trabalho.
Nesse exemplo, vamos criar 2 classes adicionais: "Funcionario" e "Hora"
Vamos criar um funcionário, o 'geddyLee', criar um objeto para armazenar a hora que ele chegou 'horaChegada' e a hora que ele saiu 'horaSaida'.
A partir da hora de chegada, vamos verificar se ele chegou atrasado (após as 8h) e usando informações dos dois objetos da classe "Hora", vamos saber quanto tempo ele trabalhou e armazenar essa informação na variável "tempoTrabalhado", do objeto 'geddyLee' da classe "Funcionario", e vamos guardar a informação caso ele tenha chegado atrasado
Ou seja, vamos trocar bastante informações entre esses objetos !
controleHorario.java
Essa é nossa classe principal, que tem o método main.
Na main, simplesmente criamos os objetos de Hora e Funcionário mencionados no enunciado.
Iniciamos os objetos de "Hora" já com seus valores (hora, minuto,segundo). Você pode mudar a seu gosto, para testes ou pedir ao usuário, através da classe Scanner.
A seguir, a título de informação, ele exibe a hora de chegada, de saída e o tempo total trabalhado em horas.
A main é bem pequena e enxuta, como deve ser.
Hora.java
Essa classe recebe três elementos inteiros: horas, minutos e segundos, e faz um tratamento.
O tratamento é: as horas devem ser inteiros entre 0 e 23, os minutos e segundos devem ser inteiros entre 0 e 59.
Caso não seja, é lançada uma excessão (artíficio para mostrar que ocorreu um erro e a aplicação é encerrada).
Experimente colocar uma data errada para ver a mensagem de erro.
Os métodos dessa classe são simples getters um toString().
Método toString()
O método toString é um método especial, existente em todos os objetos de Java (pois está na classe Objects e todas classes são derivadas desta).
Como o nome pode sugerir, ele serve para retornar uma String.
Vamos formatar uma string com argumentos, por isso vamos usar o método format, para exibir as horas, minutos e segundos no formato: hh:mm:ss
Uma outra característica especial deste método é que ele se torna o padrão e automaticamente invocado, caso tente imprimir um 'objeto'.
Isso mesmo, se colocar apenas um objetos em uma função de print, esse método será invocado.
Por isso , para imprimir na main, fizemos apenas:
System.out.println("Hora de chegada: " + horaChegada); System.out.println("Hora de saída: " + horaSaida);
Mas poderíamos ter feito:
System.out.println("Hora de chegada: " + horaChegada.toString()); System.out.println("Hora de saída: " + horaSaida.toString());
Funcionario.java
Essa classe que vai recebe a informação de outros objetos. No caso, ela recebe objetos da classe "Hora".
Vamos comentar seus métodos:
-
public double tempoAtraso(Hora horaChegada)
Vamos descobrir o tempo que o funcionário atrasou convertendo a hora de chegada em segundos.
Simplesmente transformamos a hora em que o funcionário chegou em segundos:
horaChegada.getHour()*60*60 + horaChegada.getMinute()*60 + horaChegada.getSecond()
E subtraímos do tempo que ele deveria chegar na empresa (8h00min):
8*3600.0
E dividimos por 3600.0, para o resultado ser um double que representa as horas atrasadas.
-
public double horasTrabalhadas(Hora horaChegada, Hora horaSaida)
Para o cálculo do tempo trabalhado de um funcionário, vamos levar em conta somente as horas trabalhadas.
Primeiro, vamos transformar a hora da saída e a hora da chegada em minutos e subtrair esse valor:
(horaSaida.getHour()*60 + horaSaida.getMinute()) - (horaChegada.getHour()*60 + horaChegada.getMinute())
Agora temos a diferença entre os horários de chegada e saída, em minutos.
Então, dividimos tudo por 60.0 para ter o tempo trabalhado, em horas.
Caso esse tempo seja negativo, é porque você colocou uma data de saída anterior a data de chegada.
Então, um erro, com a explicação, é lançado e aplicação é encerrada.
-
public double getHorasTrabalhadas()
Um simples exemplo de get, que retorna a variável 'tempoTrabalhado'.
Código do nosso programa:
controleHorario.java
public class controleHorario { public static void main(String[] args) { Hora horaChegada = new Hora(8, 0, 1); Hora horaSaida = new Hora(9, 30, 0); Funcionario geddyLee = new Funcionario("Geddy Lee", horaChegada, horaSaida); System.out.println("Hora de chegada: " + horaChegada); System.out.println("Hora de saída: " + horaSaida); System.out.printf("Horas trabalhadas: %.1f\n",geddyLee.getHorasTrabalhadas()); } }
Hora.java
public class Hora { private int hours, minutes, seconds; public Hora (int hours, int minutes, int seconds){ //preenchendo as horas if(hours>=0 && hours <24 ) this.hours = hours; else throw new IllegalArgumentException("Hora inválida"); //preenchendo os minutos if(minutes >=0 && minutes < 60) this.minutes = minutes; else throw new IllegalArgumentException("Minutos inválidos"); //preenchendo os segundos if( seconds >=0 && seconds < 60) this.seconds = seconds; else throw new IllegalArgumentException("Segundos inválidos"); } @Override public String toString(){ return String.format("%d:%d:%d", getHour(), getMinute(), getSecond()); } public int getHour(){ return this.hours; } public int getMinute(){ return this.minutes; } public int getSecond(){ return this.seconds; } }
Funcionario.java
public class Funcionario { private String nome; private boolean atraso; private double tempoTrabalhado, tempoAtraso; public Funcionario(String nome, Hora horaChegada, Hora horaSaida){ this.nome=nome; this.tempoAtraso = tempoAtraso(horaChegada); if(this.tempoAtraso > 0) this.atraso=true; if(atraso){ System.out.println("Funcionário '" + this.nome + "' atrasado. "); } this.tempoTrabalhado = horasTrabalhadas(horaChegada, horaSaida); } public double tempoAtraso(Hora horaChegada){ return ((horaChegada.getHour()*60*60 + horaChegada.getMinute()*60 + horaChegada.getSecond()) - 8*3600.0)/3600.0; } public double horasTrabalhadas(Hora horaChegada, Hora horaSaida){ double horas = ( (horaSaida.getHour()*60 + horaSaida.getMinute()) - (horaChegada.getHour()*60 + horaChegada.getMinute()) )/60.0; if(horas < 0) throw new IllegalArgumentException("Hora de saída anterior a hora de chegada"); return horas; } public double getHorasTrabalhadas(){ return this.tempoTrabalhado; } }
Exercício: Aplicativo Java para um Supermercado
Você foi selecionado para criar um aplicativo - em Java, claro - para um supermercado.
O que o dono do estabelecimento pediu a funcionalidade "Promoção para você", que funciona da seguinte maneira:
Após as 20h, todos os produtos recebem um desconto de 10%.
Aos sábados e domingos esse desconto vale o dia inteiro.
Ou seja, além de ter uma classe para os produtos do supermercado, você deve criar outra com o horário da compra.
Use a técnica de composition para passar o objeto referente ao horário da compra para o objeto referente ao produto que está sendo comprado. Assim, no objeto produto o preço é calculado com base no horário e dia da semana.
Na sua main, peça o preço do produto, dia da semana e horário da compra (hora, minuto e segundos), e devolva o valor final do produto com base naquele horário.
PS: Em uma aplicação real, não seria necessário fornecer esses dados. Eles seriam obtidos por um leitor de código de barras para saber o preço do produto, e pegaria o horário do sistema, pra preencher os dados referentes ao horário.
Pedimos, porém, por questão de aprendizado, já que você não deve ter um leitor desses em casa...
Use constantes, e não números - declarando variáveis com o final
Suponha que o governo te contratou para criar um aplicativo que, dentre outras coisas, analisa se um cidadão é maior de idade ou não.
Como você faria este teste condicional?
if(idade >= 18)...ou quem sabe if(idade > 17) ?
Ambos corretos, mas absolutamente não recomendáveis.
Nesse artigo ensinaremos uma importante lição de Java: Não usar números, e sim constantes.
Fugindo, um pouco, do escopo da seção "Orientação a Objetos", vamos introduzir uma boa prática na programação Java, que é o uso de constantes. Essa seção tem por objetivo introduzir conceitos pro próximo artigo, sobre enum.
Por que usar constantes ao invés de números?
Imagine que você fez a aplicação. Uma aplicação real assim leva, fácil, milhares de linhas de código.
Durante todo o seu programa você usou esse número mágico, o 18.
Ora, é claro que ele vai aparecer várias vezes, pois a aplicação Java sobre maior idade etc.
Ok, até aí tudo bem.
Como você estudou pelo curso Java Progressivo, seu programa está funcionando que é uma beleza.
Agora imagine a situação: eleições.
Novos governantes, novas leis...a maioridade agora é 16.
O que eles fazem?
'Chama o programador!' e lá vai você, sair catando no código o número 18 e substituindo por 16.
Imagina o trabalho...
Um mais esperto pode dizer: 'use regex ou ctrl+f' ou outro atalho qualquer pra achar 18 e substituir por 16.
Mas e se no seu aplicativo o número 18 aparecer e não tem nada a ver com idade?
Vai ter um baita bug.
Ou em vez de 18, você usou os números 0 ou 1 em sua aplicação, e agora precisa mudar.
Não dá pra substituir todos os 1 ou 0, tem que sair catando no código e mudando manualmente !!!
A vantagem de usar constantes é essa: você define uma vez, e usa o NOME da constante!
O certo seria:
int maioridade=18;
if( idade >= maioridade)
Pronto. A variável 'maioridade' poderia aparecer 1 milhão de vezes no seu código.
No dia que o governo mudar ela, mude só a declaração do valor, de 18 pra 16 e isso será reproduzido em todo seu código.
Outra vantagem é a facilidade pra ler seu código, por você (depois de meses sem ver o código, a gente esquece o que significa aquele amontoado de códigos Java) ou pra outra pessoa que for ler.
Note:
if(idade > 17)
Não dá pra saber, exatamente o que é isso, só olhando. Tem que ver alguma documentação pra ter certeza.
Porém, se você se deparar com:
if(idade > maioridade)
...ora, maioridade é maioridade! Já sei do que se trata! Esse 'if' checa se a pessoa é adulta!
Outro exemplo:
A = 3.14*r*r
O que é isso? Não sabe?
E agora...
A = PI*r*r
Agora ficou óbvio que é o número pi, então é algo relacionado com geometria. No caso, a área de um círculo.
O mais indicado seria definir constantes e usar seus nomes.
Fica mais bonito, elegante e profissional.
Ou seja, defina o valor de suas variáveis e use constantes.
final - declarando variáveis constantes
O Java reservou um artifício bem interessante para você usar suas constantes, que é a keyword 'final'
Quando você define uma variável com o 'final', ela se torna constante.
Se tentar mudar ela, obterá um erro.
A declaração é a seguinte:
final tipo nomeDaVariavel
Por exemplo:
final int SIM = 1;
final int NAO = 0;
Pronto. Agora você pode usar 'SIM' e 'NAO', ao invés de usar números.
Usar um 'OK' ou 'ERRO', ao invés de números estranho e bizarros.
Em classes, seria:
private final int STATUS_ON = 1;
private final int STATUS_OFF = 0;
De agora em diante, usaremos pouco números.
Mesmo que suas aplicações sejam simples e curtas - e aparente não haver necessidade de constantes - use-as.
É um costume que levará quando se tornar profissional e criar aplicações reais.
final em Classes e Objetos
Em uma classe, você poderá declarar um atributo como final de duas maneiras possíveis:
-
Ao inicializar a variável, inicie com seu valor. Só faça isso se quiser que todos os objetos daquela classe possuam essa variável com o mesmo valor.
-
Você também poderá inicializar suas variáveis através de um método construtor. Assim, cada objeto inicializado poderá ter um valor diferente.
Mas se lembre: após inicializadas em sua declaração ou no construtor, não podem mais ser alteradas.
Certifique-se de que quer mesmo ter uma constante em sua aplicação. Se sim, sempre use a keyword final, pois isso evitará que a variável tenha seu valor alterado por acidente ou má programação.
enum: A melhor maneira para manusear constantes
Conforme vimos no tutorial de Java da aula passada, o uso de constantes em suas aplicações é de vital importância pra reusabilidade e legibilidade do código. O uso de constantes em Java é tão, mas tão importante que a linguagem possui uma ferramenta especial para você manusear - com muita facilidade - suas constantes: o enum
O que é enum em Java
enum nada mais são que um recurso, muito útil e parecido com Orientação a Objetos, feito para tratar, guardar e usar com facilidade suas constantes. Embora sejam do tipo referência, possuam construtores, declaração e uso parecido com Objetos/Classes, não são. Mas se parecem, por isso vamos apresentar os enum nesta seção de nossa apostila online de Java.
De uma maneira simplificada, você pode ver enum como uma 'classe' especial para tratar constantes.
Você notará melhor isso na sintaxe de declaração e criação do código das enums.
Declarando uma enum em Java
Você pode declarar uma enum da seguinte maneira:
public enum nome_da_enum{
//código de sua enum
}
Por exemplo:
public enum Bebidas{
CocaCola, Suco, Agua;
}
Obviamente, essas constantes não servem de nada, pois não atribuímos nada a elas.
De antemão, você pode ver cada uma dessas constantes como um, digamos, 'objeto' da 'classe' enum.
Usando enum em Java
Vamos adicionar algumas funcionalidades a essa enum, como o preço de cada bebida:
public enum Bebida{
CocaCola(2.00),
Suco(1.50),
Agua(1.00);
private double preco;
Bebida(double preco){
this.preco = preco;
}
public double getPreco(){
return this.preco;
}
}
Primeiro declaramos nossas constantes: CocaCola, Suco e Agua.
Note que inicializamos ela com um double, pois criamos um método construtor que recebe um double: Bebidas(double preco) {...}
Tudo bem parecido com Classes/Objetos, inclusive temos que definir uma variável interna, a 'preco'.
Para invocar um método fazemos: nome_do_enum.nomeDaConstante.metodo()
Por exemplo, para saber quanto custa uma Agua: Bebida.Agua.getPreco();
Notes nomes: Bebida, Agua, getPreco.
Note mesmo, como fica óbvio. Você não precisa se preocupar com o valor numérico,e sim com o nome.
Nesse caso, o nome é bem fácil lembrar, pois usamos palavras óbvias.
Exemplo de uso de enum em Java
Exercício: Aplicativo para Lanchonete
O seguinte aplicativo exibe um menu de uma lanchonete, com 3 tipos de comida e 3 tipos de bebida.
O cliente escolhe o que quer comer e o programa retorna o valor total da conta.
Para tal criamos duas enums: 'Bebida' e 'Comida'.
Cada uma dessas enums recebe dois valores: seu nome (String nome) e seu preço (double preco).
Além disso, as enum tem método construtor, variáveis privadas e métodos que retorna o nome e preço do produto.
Também criamos o método 'menu()', que simplesmente exibe o cardápio.
Através do objeto 'entrada', da classe Scanner, perguntamos a opção desejada ao cliente, e enviamos essa informação para o método 'preco()', que retorna o valor do ítem pedido pelo cliente.
Quando terminar de pedir, ele deve digitar '0' e a aplicação se encerrará.
EnumTest.java
import java.util.Scanner; public class EnumTest { public enum Bebida{ CocaCola("Coca-cola", 2.00), Suco("Suco", 1.50), Agua("Agua", 1.00); private double preco; private String nome; Bebida(String nome, double preco){ this.nome = nome; this.preco = preco; } public double getPreco(){ return this.preco; } public String getNome(){ return this.nome; } } public enum Comida{ Sanduiche("Sanduiche", 4.0), HotDog("HotDog", 3.0), Xburger("X-Burger", 3.5); private double preco; private String nome; Comida(String nome, double preco){ this.nome = nome; this.preco = preco; } public double getPreco(){ return this.preco; } public String getNome(){ return this.nome; } } public static void menu(){ System.out.println("\tBebidas"); System.out.println("1." + Bebida.CocaCola.getNome() + ": R$" + Bebida.CocaCola.getPreco()); System.out.println("2." + Bebida.Suco.getNome() + ": R$" + Bebida.Suco.getPreco()); System.out.println("3." + Bebida.Agua.getNome() + ": R$" + Bebida.Agua.getPreco()); System.out.println("\tComidas"); System.out.println("4." + Comida.Sanduiche.getNome() + ": R$" + Comida.Sanduiche.getPreco()); System.out.println("5." + Comida.HotDog.getNome() + ": R$" + Comida.HotDog.getPreco()); System.out.println("6." + Comida.Xburger.getNome() + ": R$" + Comida.Xburger.getPreco()); System.out.println("0. Sair"); System.out.print("Escolha sua opção: "); } public static double preco(int opcao){ switch( opcao ){ case 1: return Bebida.CocaCola.getPreco(); case 2: return Bebida.Suco.getPreco(); case 3: return Bebida.Agua.getPreco(); case 4: return Comida.Sanduiche.getPreco(); case 5: return Comida.HotDog.getPreco(); case 6: return Comida.Xburger.getPreco(); default: return 0.0; } } public static void main(String[] args) { double total=0.0; int opcao=0; Scanner entrada = new Scanner(System.in); do{ menu(); opcao = entrada.nextInt(); total += preco(opcao); System.out.println("Opção escolhida: " + opcao); System.out.println("Valor de sua conta: " + total + "\n"); }while(opcao != 0); }
STATIC
Usando membros estáticos em Java
Agora que você sabe a importância das constantes em aplicações Java e como manusear as constantes de seus programas com enum,
vamos mostrar uma aplicação mais direta das constantes em nosso estudo de Orientação a Objetos. Esse artigo Java mostra o uso das variáveis do tipo static, artifício muito usado para manter o controle sobre todos os objetos de uma mesma classe, por exemplo.
O que é static em Java
Só pelo nome, já dá pra desconfiar que é algo relacionado com constante, algo 'parado' (estático).
Quando definimos uma classe e criamos vários objetos dela, já sabemos que cada objeto irá ser uma cópia fiel da classe, porém com suas próprias variáveis e métodos em lugares distintos da memória.
Ou seja, o objeto 'fusca' tem suas variáveis próprias, diferentes do objeto 'ferrari',
embora ambos tenham o mesmo 'modelo', que é a classe 'Carro'.
Quando definimos variáveis com a palavra static em uma classe ela terá um comportamento especial: ela será a mesma para todos os objetos daquela classe. Ou seja, não haverá um tipo dela em cada objeto. Todos os objetos, ao acessarem e modificarem essa variável, acessarão a mesma variável, o mesmo espaço da memória, e a mudança poderá ser vista em todos os objetos.
Como declarar uma variável static em Java
Basta colocar a palavra static antes do tipo:
static tipo nomeDaVariavel
Em uma classe:
private static int vendidos;
public static int totalAlunos;
Quando usar variáveis static em Java
Principalmente quando você quiser ter um controle sobre os objetos ou quando todos os objetos devem partilhar uma informação
(evitar ter que fazer Composição ou chamar métodos de outros objetos).
-
Exemplo 1: Para controle de número total de objetos
Imagine que você é dono de uma loja de venda de veículos.
Cada um que vende, é um comprador diferente, dados diferentes etc. Portanto, cada carro será um objeto.
Você cria a variável estática 'total', e no construtor a incrementa (total++). Pronto, saberá quantos carros foram vendidos, automaticamente.
Um exemplo parecido, seria para um aplicativo de uma escola ou empresa, para controle de quantos funcionários existem na empresa, ou em cada setor dela.
-
Exemplo 2: Para compartilhar uma informação
Muitas aplicações Java, principalmente jogos, usam o static para compartilhar informações sobre o número de objetos.
O uso tem a ver algoritmos sobre inteligência computacional.
Por exemplo, em um jogo de futebol, quando você joga contra o computador.
A máquina tem uma estratégia diferente dependendo do time que você escolher, do modo que você jogar, depende do número de jogadores dele e seu também.
E como saber o número atual de jogadores do time deles?
Ora, um método static. Pois todos os jogadores são objetos de uma mesma classe.
Em um jogo de luta, se existirem 3 inimigos contra 2 personagens seus. Provavelmente o computador vai te atacar.
Mas se você matar dois deles, eles estarão em desvantagem.
Existe então um método que faz com que os inimigos corram, caso o número de jogadores dele seja menor que o seu.
Para obter essa informação, os objetos devem partilhar da informação: número de personagens vivos.
Exemplo de código com static
O seguinte código cria uma classe bem simples, a "Carro" que simplesmente informa quando o objeto é criado - através do método construtor padrão main - e incrementa a variável 'total', que vai guardar a informação do número total de objetos/carros criados em sua aplicação.
staticTest.java
public class staticTest { public static void main(String[] args) { Carro fusca = new Carro(); Carro ferrari = new Carro(); Carro jipe = new Carro(); } }
Carro.java
public class Carro { public static int total=0; Carro(){ total++; System.out.println("Objeto criado. Existem "+total+" objetos dessa classe"); } }
Jogo: Campo Minado em Java
Dando continuidade aos games que estamos fazendo, e ensinando a fazer, vamos agora mostrar como fazer e programar o famoso jogo Campo Minado em Java.
Se não acompanhou os outros jogos feitos em nosso curso de Java, veja:
Como fazer o Jogo da Velha em Java
Como fazer o jogo Batalha Naval em Java
Aqui mostraremos as classes e código do jogo, no próximo artigo vamos explicar em detalhes como programar o jogo:
Código comentado sobre a criação do Campo Minado em Java
Como jogar o nosso Campo Minado em Java
Existe um tabuleiro 8x8, onde estão localizadas 10 minas.
A cada rodada você irá fornecer o número da linha e da coluna (ou seja, números de 1 até 8).
Caso exista uma mina naquele lugar, você perde o jogo.
Caso não exista uma mina naquela posição, será exibido número naquele local e números nos locais vizinhos aquele que você escolheu, exceto onde há minas.
Esses números informam quantos minas existem ao redor daquele local.
Por exemplo, se você escolhe um local e aparece o número '2' lá, é porque existem duas minas nas vizinhanças daquele local. Vale salientar que 'ao redor' e 'vizinhança' significam todos os blocos ao redor, incluindo na diagonais.
Objetivo: Deixar os 10 campos que possuem minas livres. Ou seja, onde você deduzir que existe mina, não marque, simplesmente deixe o '_' lá, pois quando existem 10 underlines você ganhará o jogo.
Código do jogo Campo Minado em Java
-->campoMinado.java
public class campoMinado { public static void main(String[] args) { Jogo jogo = new Jogo(); } }
-->Jogo.java
public class Jogo { private Tabuleiro board; boolean terminar = false; boolean ganhou = false; int[] jogada; int rodada=0; public Jogo(){ board = new Tabuleiro(); Jogar(board); jogada = new int[2]; } public void Jogar(Tabuleiro board){ do{ rodada++; System.out.println("Rodada "+rodada); board.exibe(); terminar = board.setPosicao(); if(!terminar){ board.abrirVizinhas(); terminar = board.ganhou(); } }while(!terminar); if(!board.ganhou()){ System.out.println("Havia uma mina ! Você perdeu!"); board.exibeMinas(); } else { System.out.println("Parabéns, você deixou os 8 campos de minas livres em "+rodada+" rodadas"); board.exibeMinas(); } } }
-->Tabuleiro.java
import java.util.Random; import java.util.Scanner; public class Tabuleiro { private int[][] minas; private char[][] tabuleiro; private int linha, coluna; Random random = new Random(); Scanner entrada = new Scanner(System.in); public Tabuleiro(){ minas = new int[10][10]; tabuleiro = new char[10][10]; iniciaMinas(); // coloca 0 em todas as posições do tabuleiro de minas sorteiaMinas(); //coloca, aleatoriamente, 10 minas no tabuleiro de minas preencheDicas();//preenche o tabuleiro de minas com o número de minas vizinhas iniciaTabuleiro();//inicia o tabuleiro de exibição com _ } public boolean ganhou(){ int count=0; for(int line = 1 ; line < 9 ; line++) for(int column = 1 ; column < 9 ; column++) if(tabuleiro[line][column]=='_') count++; if(count == 10) return true; else return false; } public void abrirVizinhas(){ for(int i=-1 ; i<2 ; i++) for(int j=-1 ; j<2 ; j++) if( (minas[linha+i][coluna+j] != -1) && (linha != 0 && linha != 9 && coluna != 0 && coluna != 9) ){ tabuleiro[linha+i][coluna+j]=Character.forDigit(minas[linha+i][coluna+j], 10); } } public int getPosicao(int linha, int coluna){ return minas[linha][coluna]; } public boolean setPosicao(){ do{ System.out.print("\nLinha: "); linha = entrada.nextInt(); System.out.print("Coluna: "); coluna = entrada.nextInt(); if( (tabuleiro[linha][coluna] != '_') && ((linha < 9 && linha > 0) && (coluna < 9 && coluna > 0))) System.out.println("Esse campo já está sendo exibido"); if( linha < 1 || linha > 8 || coluna < 1 || coluna > 8) System.out.println("Escolha números de 1 até 8"); }while((linha < 1 && linha > 8) && (coluna < 1 && coluna > 8) || (tabuleiro[linha][coluna] != '_') ); if(getPosicao(linha, coluna)== -1) return true; else return false; } public void exibe(){ System.out.println("\n Linhas"); for(int linha = 8 ; linha > 0 ; linha--){ System.out.print(" "+linha + " "); for(int coluna = 1 ; coluna < 9 ; coluna++){ System.out.print(" "+ tabuleiro[linha][coluna]); } System.out.println(); } System.out.println("\n 1 2 3 4 5 6 7 8"); System.out.println(" Colunas"); } public void preencheDicas(){ for(int line=1 ; line < 9 ; line++) for(int column=1 ; column < 9 ; column++){ for(int i=-1 ; i<=1 ; i++) for(int j=-1 ; j<=1 ; j++) if(minas[line][column] != -1) if(minas[line+i][column+j] == -1) minas[line][column]++; } } public void exibeMinas(){ for(int i=1 ; i < 9; i++) for(int j=1 ; j < 9 ; j++) if(minas[i][j] == -1) tabuleiro[i][j]='*'; exibe(); } public void iniciaTabuleiro(){ for(int i=1 ; i<minas.length ; i++) for(int j=1 ; j<minas.length ; j++) tabuleiro[i][j]= '_'; } public void iniciaMinas(){ for(int i=0 ; i<minas.length ; i++) for(int j=0 ; j<minas.length ; j++) minas[i][j]=0; } public void sorteiaMinas(){ boolean sorteado; int linha, coluna; for(int i=0 ; i<10 ; i++){ do{ linha = random.nextInt(8) + 1; coluna = random.nextInt(8) + 1; if(minas[linha][coluna] == -1) sorteado=true; else sorteado = false; }while(sorteado); minas[linha][coluna] = -1; } } }
Campo Minado em Java: código comentado
Vamos agora explicar, em detalhes, como fazer, entender e programar toda a lógica de um campo minado na linguagem de programação Java.
O código do jogo se encontra em nosso artigo passado de nosso curso online de Java:
Jogo: Campo Minado em Java
Dividimos o jogo em 3 classes, a 'campoMinado' que simplesmente contém a main e cria um objeto do tipo 'Jogo', temos a classe 'Jogo' que irá ministrar toda a lógica e fluxo do jogo e finalmente a classe 'Tabuleiro', que irá gerar os tabuleiros (de minas e visualização) bem como os métodos que lidam com os tabuleiros.
Campo Minado em Java: A classe Tabuleiro.java
Essa classe é a responsável pelo tabuleiro e os métodos que o envolvem.
Na verdade, vamos lidar com dois tabuleiros:
- int[][] minas
Esse tabuleiro é inicialmente preenchido com números '0', através do método 'iniciaMinas()'.
Após isso, vamos sortear 10 locais para colocar as minas, através do método 'sorteiaMinas()'. Nos locais onde existirem minas, vamos colocar o inteiro '-1'.
Esse método sorteia dois números inteiros, entre 1 e 8, através do uso da classe Random. Vamos fazer isso com umlooping do while, que a cada iteração vai checar se naquela posição sorteada já existe o número '-1'. Se aquele local já tiver sido sorteado, o booleano 'sorteado' ficará como true e o looping irá se repetir, até que tenhamos 10 locais diferentes com o número '-1'.
Depois disso, vamos preencher as dicas, ou seja, vamos colocar em cada bloco desse tabuleiro o número de minas que existem ao redor.
Assim, se uma posição do tabuleiro tiver o número '2', é porque existem duas minas ao redor daquele bloco.
Fazemos isso apenas contando quantas bombas existem ao redor de cada bloco.
Criamos, na verdade, uma matriz 10x10, onde não usaremos a linha 0 nem a linha 9, bem como a coluna 0 e a coluna 9. Por que isso?
Para que, ao calcular quantas bombas existem na vizinhança, apenas contemos quantas bombas existem nos 8 locais ao redor. Isso é necessário pois se usássemos um tabuleiro 8x8, as casas da borda não teriam 8 vizinhos.
Para checar as bombas ao redor, usamos dois laços for:
for(int i=-1 ; i<=1 ; i++)
for(int j=-1 ; j<=1 ; j++)
Se queremos checar quantas bombas há ao redor do bloco: mines[linha][coluna], colocamos dentro desses laços:
mines[linha+i][coluna+j]
Esses dois laços irão percorrer os 8 locais ao redor do bloco mines[linha][coluna].
Porém, só vamos checar os arredores se o local em questão não tiver uma mina:
if(minas[line][column] != -1)
Após checado isso, checamos se existe mina na posição ao redor, e se tiver, incrementamos o local do tabuleiro 'minas', pois ele também recebe o número de minas ao redor:
if(minas[line+i][column+j] == -1)
minas[line][column]++;
Pronto. Agora temos um tabuleiro 10x10, mas usaremos só o 8x8, e nesse tabuleiro 'interno' temos 10 minas e todos os blocos que não são minas armazenarão quantas minas existem ao seu redor.
- char[][] tabuleiro
É um tabuleiro de caracteres, inicialmente carregado com underline '_' através do método 'iniciaTabuleiro()', isso quer dizer que esse campo ainda não foi escolhido.
Temos ainda o método 'exibe()', que simplesmente exibe todo esse tabuleiro de caracteres de maneira formatada.
O método 'exibeMinas()' coloca um asterisco, '*', em todos os locais onde existem minas. Este método serve para mostrar onde existia minas e será acionado quando o jogador perder a partida.
Fazendo uma jogada:
A jogada é feita através do método 'setPosicao()', que retorna 'true' caso você perca (ou seja, caso exista uma mina onde você jogou) e 'false', caso não exista uma mina no local que você escolheu.
Devemos deixar esse método bem robusto, nos certificando que o jogador não entre com números fora do espero (ou seja, maior que 8 ou menor que 1):
(linha < 1 || linha > 8 || coluna < 1 || coluna > 8)
Bem como checar se já o local ele jogou já tenha sido escolhido antes.
(tabuleiro[linha][coluna] != '_')
Ainda no método 'setPosicao()', usamos o método 'getPosicao()', que retorna o que existe no bloco quando passamos a linha e a coluna. Caso existe '-1' é porque existe mina, retorna 'true' e o jogo acaba. Caso exista qualquer outro número, o método retorna 'false' e o jogo não acaba.
Após jogar, vamos checar se o jogador ganhou através do método 'ganhou()', que simplesmente conta quantos blocos ainda não foram exibidos, ou seja, quantos underlines existem. Caso existam somente 10, é porque os blocos que sobraram foram justamente os que continham minas, o método retorna 'true' e o jogador ganha. Caso tenha mais de 10, é porque ainda não terminou e retorna 'false'.
Outro método importante, e talvez o mais complicado, é o 'exibirVizinhas()', que vai checar todas as casas vizinhas, no tabuleiro de minas, exceto se essas vizinhas estejam localizadas nas linhas 0 ou 9, ou nas colunas 0 ou 9. Durante a checagem, vamos checar se a vizinha possui uma mina, e caso não possua vamos exibir essa casa no tabuleiro de caracteres:
for(int i=-1 ; i<2 ; i++)
for(int j=-1 ; j<2 ; j++)
if( (minas[linha+i][coluna+j] != -1) && (linha != 0 && linha != 9 && coluna != 0 && coluna != 9) )
tabuleiro[linha+i][coluna+j]=Character.forDigit(minas[linha+i][coluna+j], 10);
Note que usamos o método 'Character.forDigit(int num,int base)', que recebe um inteiro que representa um número e sua base.
O que esse método faz é pegar um número e transformar em caractere. No nosso caso, os número estão na base decimal (por isso o 10) e esses números nada mais são que os inteiros do tabuleiros de minas. Ou seja, estamos colocando números no tabuleiro de caracteres.
Campo Minado em Java: A classe Jogo.java
Essa classe começa com o construtor padrão criando um tabuleiro, o 'board' e chama o método que irá controlar o jogo, o 'Jogar', que recebe o objeto 'board', do tipo Tabuleiro.
O jogo, como de praxe, é controlado por um laço do while, que só termina quando o booleano 'terminar' for verdadeiro.
Esse booleano só se torna 'true' se o método 'setPosicao()' retornar true, dizendo que encontramos uma mina, ou quando o método 'ganhou()' retornar true, dizendo que o jogador ganhou, pois só restavam 10 blocos no jogo.
Usamos o inteiro 'rodada', que irá contar quantas rodadas o jogo teve.
A jogada é feita em:
terminar = board.setPosicao();
Caso não tenhamos atingido uma mina:
if(!terminar)
Vamos abrir os blocos vizinhos desse bloco que escolhemos na 'setPosicao()' para ver as dicas, isso ocorre em:
board.abrirVizinhas();
Após aberto as casas vizinhas, pode ser que o jogador tenha ganhado, fazemos isso checando:
terminar = board.ganhou();
Quanto o jogo terminar, ou seja, quando o aplicativo sair do laço do while, vamos mostrar a mensagem de parabéns ou de perda.
Quando o método 'ganhou()' retorna true é porque o jogador ganhou, então exibimos uma mensagem de parabéns bem como em quantas rodadas ele venceu:
if(board.ganhou()){
System.out.println("Parabéns, você deixou os 8 campos de minas livres em "+rodada+" rodadas");
board.exibeMinas();
}
Ora, se não retorna true no condicional if, o else vai ser o caso em que ele tenha perdido. Então dizemos que ele perdeu e mostramos onde havia minas através do método 'exibeMinas()'.
Apostila de Java, Capítulo 4 - Orientação a objetos básica
Nesse artigo iremos comentar e resolver os exercícios propostos no capítulo 4, sobre Orientação a objetos básica da apostila FJ-11: Java e Orientação a Objetos, da Caelum.
Clique aqui para saber sobre a Caelum e sua apostila.
Clique aqui para baixar a apostila.
Recomendamos que tentem ao máximo resolver os exercícios, e só quando conseguir (ou depois de MUITO tentar) veja o código. Caso tenha dificuldades, veja a Solução, que conterá uma explicação a respeito do código, e tente criar seu código.
Programação é criatividade, logo existem mais de uma solução para o mesmo exercício.
Página 51, 4.12 Exercícios: Orientação a Objetos básica
Enunciados
QUESTÃO 01:
Modele um funcionário. Ele deve ter o nome do funcionário, o departamento onde trabalha, seu salário
(double), a data de entrada no banco (String) e seu RG (String).
Você deve criar alguns métodos de acordo com sua necessidade. Além deles, crie um método bonifica
que aumenta o salario do funcionário de acordo com o parâmetro passado como argumento. Crie também um método calculaGanhoAnual, que não recebe parâmetro algum, devolvendo o valor do salário multiplicado por 12..
A ideia aqui é apenas modelar, isto é, só identifique que informações são importantes e o que um funcionário faz. Desenhe no papel tudo o que um Funcionario tem e tudo que ele faz.
QUESTÃO 02:
Transforme o modelo acima em uma classe Java. Teste-a, usando uma outra classe que tenha o main.
Você deve criar a classe do funcionário chamada Funcionario, e a classe de teste você pode nomear como
quiser. A de teste deve possuir o método main.
Um esboço da classe:
class Funcionario { double salario; // seus outros atributos e métodos void bonifica(double aumento) { // o que fazer aqui dentro? } double calculaGanhoAnual() { // o que fazer aqui dentro? } }
Você pode (e deve) compilar seu arquivo java sem que você ainda tenha terminado sua classe
Funcionario. Isso evitará que você receba dezenas de erros de compilação de uma vez só. Crie a
classe Funcionario, coloque seus atributos e, antes de colocar qualquer método, compile o arquivo java.
Funcionario.class será gerado, não podemos “executá-la” pois não há um main, mas assim verificamos
que nossa classe Funcionario já está tomando forma.
Esse é um processo incremental. Procure desenvolver assim seus exercícios, para não descobrir só no fim
do caminho que algo estava muito errado.
Um esboço da classe que possui o main:
class TestaFuncionario { public static void main(String[] args) { Funcionario f1 = new Funcionario(); f1.nome = "Fiodor"; f1.salario = 100; f1.bonifica(50); System.out.println("salario atual:" + f1.salario); System.out.println("ganho anual:" + f1.calculaGanhoAnual()); }
Incremente essa classe. Faça outros testes, imprima outros atributos e invoque os métodos que você criou a mais.
Lembre-se de seguir a convenção java, isso é importantíssimo. Isto é, nomeDeAtributo, nomeDeMetodo, nomeDeVariavel, NomeDeClasse, etc...
QUESTÃO 03:
Crie um método mostra(), que não recebe nem devolve parâmetro algum e simplesmente imprime todos
os atributos do nosso funcionário. Dessa maneira, você não precisa ficar copiando e colando um monte
de System.out.println() para cada mudança e teste que fizer com cada um de seus funcionários, você
simplesmente vai fazer:
Funcionario f1 = new Funcionario();
// brincadeiras com f1....
f1.mostra();
Veremos mais a frente o método toString, que é uma solução muito mais elegante para mostrar a representação de um objeto como String, além de não jogar tudo pro System.out (só se você desejar).
O esqueleto do método ficaria assim:
class Funcionario { // seus outros atributos e métodos void mostra() { System.out.println("Nome: " + this.nome); // imprimir aqui os outros atributos... // tambem pode imprimir this.calculaGanhoAnual() } }
QUESTÃO 04:
Construa dois funcionários com o new e compare-os com o ==. E se eles tiverem os mesmos atributos?
Para isso você vai precisar criar outra referência:
Funcionario f1 = new Funcionario(); f1.nome = "Fiodor"; f1.salario = 100; Funcionario f2 = new Funcionario(); f2.nome = "Fiodor"; f2.salario = 100; if (f1 == f2) { System.out.println("iguais"); } else { System.out.println("diferentes"); }
QUESTÃO 05:
Crie duas referências para o mesmo funcionário, compare-os com o ==. Tire suas conclusões. Para criar
duas referências pro mesmo funcionário:
Funcionario f1 = new Funcionario():
f1.nome = "Fiodor";
f1.salario = 100;
Funcionario f2 = f1;
O que acontece com o if do exercício anterior?
QUESTÃO 06:
Em vez de utilizar uma String para representar a data, crie uma outra classe, chamada Data.
Ela possui 3 campos int, para dia, mês e ano. Faça com que seu funcionário passe a usá-la. (é parecido
com o último exemplo, em que a Conta passou a ter referência para um Cliente).
class Funcionario { Data dataDeEntrada; // qual é o valor default aqui? // seus outros atributos e métodos } class Data { int dia; int mes; int ano; }
Modifique sua classe TestaFuncionario para que você crie uma Data e atribua ela ao Funcionario:
Funcionario f1 = new Funcionario();
//...
Data data = new Data(); // ligação!
f1.dataDeEntrada = data;
Faça o desenho do estado da memória quando criarmos um Funcionario.
QUESTÃO 07:
Modifique seu método mostra para que ele imprima o valor da dataDeEntrada daquele Funcionario:
class Funcionario { // seus outros atributos e métodos Data dataDeEntrada; void mostra() { System.out.println("Nome: " + this.nome); // imprimir aqui os outros atributos... System.out.println("Dia: " + this.dataDeEntrada.dia); System.out.println("Mês: " + this.dataDeEntrada.mes); System.out.println("Ano: " + this.dataDeEntrada.ano); } }
Teste-o. O que acontece se chamarmos o método mostra antes de atribuirmos uma data para este
Funcionario?
QUESTÃO 08:
O que acontece se você tentar acessar um atributo diretamente na classe? Como, por exemplo:
Funcionario.salario = 1234;
Esse código faz sentido? E este:
Funcionario.calculaGanhoAtual();
Faz sentido perguntar para o esquema do Funcionario seu valor anual?
___________________________________________________________________________________________________________________________
SOLUÇÕES
QUESTÕES 1, 2, 3, 4, e 5
As questões de número 1, 2, 3, 4 e 5 se referem a somente um aplicativo em Java, feito passo-a-passo através das questões.
Basicamente criamos a classe Funcionario com os atributos: nome, departamento, dataEntrada, RG e salario. Além disso, essa classe também possui o método mostra(), que ao ser solicitado, exibe todas as informações de um objeto.
Já a classe CaelumCap4 é a que contém a main, e que cria dois objetos do tipo Funcionario: f1 e f2.
Mostramos os dados ao iniciar, depois fazemos uma bonificação em f1 e mostramos seus dados novamente. Note que o valor do salário e o ganho anual realmente mudaram.
Depois criamos um clone de f1: f2.
Porém, embora possua os mesmos atributos e valores, esses objetos referenciam regiões na memória, portanto não são iguais, como mostra o teste condicional if.
Depois criamos outro objeto: f3
Esse objeto é referenciado para apontar o mesmo local de memória que f1 referencia.
Portanto, ao comparar, vemos que f1 e f2 são iguais.
Nossa solução fica:
QUESTÕES 6, 7 e 8
A criação da classe Data é bem simples e sem mais complicações.
Na classe Funcionario criamos um objeto da classe Data, mas ele só é instanciado na classe principal, a CaelumCap4.
Ao final da main mostramos como é possível acessar e alterar diretamente seus valores.
Nossa classes ficam assim:
Página 55, Desafios
Enunciados
QUESTÃO 01:
Um método pode chamar ele mesmo. Chamamos isso de recursão. Você pode resolver a série de fibonacci
usando um método que chama ele mesmo. O objetivo é você criar uma classe, que possa ser usada da
seguinte maneira:
Fibonacci fibo = new Fibonacci();
int i = fibo.calculaFibonacci(6);
System.out.println(i);
Aqui imprimirá 8, já que este é o sexto número da série.
Este método calculaFibonacci não pode ter nenhum laço, só pode chamar ele mesmo como método.
Pense nele como uma função, que usa a própria função para calcular o resultado.
QUESTÃO 02:
Por que o modo acima é extremamente mais lento para calcular a série do que o modo iterativo (que se
usa um laço)?
QUESTÃO 03:
Escreva o método recursivo novamente, usando apenas uma linha. Para isso, pesquise sobre o operador
condicional ternário. (ternary operator)
Soluções
QUESTÃO 01:
Vamos criar um método chamado calculaFibonacci que recebe um inteiro como argumento. Chamemos esse número de n.
Da definição dos números de FIbonacci, sabemos que se esse número n for 0, o valor do número Fibonacci é 0.
Caso n seja 1, o valor do número de Fibonacci é 1.
E para TODOS os demais, o número de Fibonacci é a soma dos dois anteriores.
Por exemplo, para n=3:
calculaFibonacci(3) = calculaFibonacci(2) + calculaFibonacci(1)
Porém: calculaFibonacci(2) = calculaFibonacci(1) + calculaFibonacci(0) = 1 + 0 = 1
Agora colocamos esse valor na outra equação:
calculaFibonacci(3) = calculaFibonacci(2) + calculaFibonacci(1) = 1 + 1 = 2
Agora, para calcular n=4:
calculaFibonacci(4) = calculaFibonacci(3) + calculaFibonacci(2)
Mas: calculaFibonacci(3) = calculaFibonacci(2) + calculaFibonacci(1)
E: calculaFibonacci(2) = calculaFibonacci(1) + calculaFibonacci(0)
Então:
calculaFibonacci(4) = calculaFibonacci(2) + calculaFibonacci(1) + calculaFibonacci(1) + calculaFibonacci(0)
Substituindo calculaFibonacci(2) novamente:
calculaFibonacci(4) = calculaFibonacci(1) + calculaFibonacci(0) + calculaFibonacci(1) + calculaFibonacci(1) + calculaFibonacci(0) = 1 + 1 +1 +1 +1 = 5
Ou seja, não importa o número que coloquemos, ele será sempre substituído pela soma dos dois elementos anteriores. Cada um desses elementos anteriores serão substituído pela soma de seus respectivos elementos anteriores. E assim vai indo...até sobrar somente uma soma de calculaFibonacci(1) e calculaFibonacci(0).
Interessante, não?
Podemos representar em Java essa idéia matemática da seguinte maneira:
QUESTÃO 02:
Experimente colocar n=30, na questão passada. É calculado bem rápido.
Agora tente n=50 e verá uma baita demora.
Se você testar obter esses números com a técnica utilizada nos exercícios do capítulo 3 da apostila da Caelum, que usa somente laços, verá que é muito mais rápido.
Isso porque os métodos são bem mais 'pesados', em termos computacionais.
Se notar bem, o método calculaFibonacci é chamado muitas vezes.
Para n=6, o método é invocado 25 vezes.
Para n=20, o método é invocado 21891 vezes !!!
(para saber quantas vezes o método foi chamado, crie uma variável inteira na classe Fibonacci, e faça 'chamadas++' assim que se inicia o método, e ela guardará quantas vezes esse método foi acessado)
QUESTÃO 03:
O operador condicional ternário tem a seguinte sintaxe:
(teste de condição) ? (retorno isso caso seja true) : (retorna isso caso seja false) ;
Ele faz um teste condicional (n < 1, a == b, x >= 0 etc). Caso ele seja verdadeiro, retorna o que vem logo após a interrogação '?', e caso seja falso, retorna o que vem após os dois pontos ':'
Sim, é como se fosse um teste condicional IF ELSE, mas usamos apenas '?' e ':'
Voltemos ao Fibonacci.
Você noto que, se n=0, o retorno é 0. E se n=1, o retorno é 1?
Ou seja: se n < 2, o número de Fibonacci é o próprio n.
E se não for? Aí o retorno é a soma dos dois elementos anteriores.
Agora, de posse dessas informações, podemos calcular os números de Fibonacci em apenas uma linha:
Código:
public class Fibonacci { int calculaFibonacci(int n){ return ( n < 2 ? n : ( calculaFibonacci(n-1) + calculaFibonacci(n-2) ) ); } }
