
Orientação a Objetos, parte III: Herança e Polimorfismo
Hiritage
Override
Public Private Protected
Object
Polimorfismo
Polimorfismo, classes-objetos
Polimorfismo, SubClasses
Qual a Classe do Objeto
Strings & Caracteres
Herança em Java - o que é, para que serve, exemplos e quando usar
Chegamos em um ponto muito importante em nosso curso de Java no que se refere a Programação Orientada a Objetos.
Herança é uma das maiores características desse tipo tão importante e usado tipo de programação.
Através desse recurso, você irá criar classes de uma maneira bem mais rápida, eficiente e fácil de manter: baseando classes em outras.
O que é herança em Java - Superclasse e subclasse
O que é herança, na vida real?
É quando um pessoa deixa seus bens para outra. No Java também.
Geralmente a herança ocorre dentre membros de uma mesma família. No Java também.
Herança, em Java, nada mais é do que criar classes usando outras classes já existentes.
Obviamente, você vai fazer uma classe herdar as características de outra se estas tiverem uma relação (se forem parecidas).
Outro ponto importante é que, quando fazemos uso da herança, nós podemos adicionar mais atributos a classe.
Exemplo 1: Carros e motos
Imagine que você tem uma revenda de veículos: carros e motos.
Todos são veículos. Ora, crie a classe "Veiculo".
O que esses veículos tem em comum?
Motor, preço, marca, nome do cliente que vai comprar, quantos quilômetros fazem com 1 litro de combustível etc.
Todos os objetos da classe "Veiculo" tem essas características.
Porém, existem algumas características que as motos tem que um carro não tem: capacete, somente duas rodas, cilindrada etc.
Também existem características que os carros tem que as motos não tem: podem ter 4 portas, banco de couro, ar-condicionado etc.
Para resolver esse problema, e deixar a aplicação MUITO MAIS ORGANIZADA, vamos fazer duas classes: "Moto" e "Carro".
Cada uma dessas irá herdar a classe "Veiculo", pois também são um tipo de veículos.
Dizemos que "Veiculo" é a superclasse,
"Moto" e "Carro" são subclasses.
Faz sentido, não?
Exemplo 2: Alunos de uma escola
Se for criar uma aplicação para um colégio, muito provavelmente você vai criar uma classe chamada "Aluno".
O que todo aluno tem em comum, para colocarmos nessa classe?
Tem seu nome, cursa uma série, tem seu número de matrícula etc.
Mas um aluno do 2o grau tem matérias como Física, Biologia e Química.
Coisa que aluno do ensino fundamental não tem.
Ou seja, se você tentar criar uma classe e colocar as matérias Física, Química e Biologia para um aluno que não cursa essas matérias, estará tendo problemas, pois muito provavelmente você deveria preencher as notas e faltas dessas matérias no construtor.
E pra calcular a média? A média inclui todas as matérias, ora. Mas os alunos de uma escola não tem as mesmas disciplinas.
E agora? Criar vários métodos diferentes dentro da classe, e acionar o método certo para o cálculo da média através de um booleano?
Claro que não! Vamos criar outras classes: "fundamental" e "medio".
A nossa classe mãe - ou superclasse - será a "Aluno", com dados e disciplinas QUE TODOS os alunos possuem em comum.
Fazemos "fundamental" e "medio" herdarem as características da "Aluno", e dentro de cada subclasse (ou classe filha) dessas irá ter seus métodos e variáveis a mais.
Note que, ao herdar, você é mais eficiente, pois não precisa criar duas classes de alunos diferentes.
Ao herdar, automaticamente as subclasses irão possuir as mesmas variáveis e métodos.
Exemplo 3: Funcionários de uma empresa
Ok, você já sabe que os funcionários da tesouraria, do suporte técnico e do alto escalão são diferentes, fazem diferentes tarefas e tem diferentes características profissional. Então você cria uma classe para cada tipo de setor: "FuncionarioTesouraria", "FuncionarioSecretaria", "FuncionarioTI" etc. Uma trabalheira...ao término disso tudo, terá dezenas de classes só para representar os funcionários.
Porém, as redes sociais, como Facebook e Twitter estão muito na moda e são usadas, inclusive, em meios profissionais.
Sabia que as empresas olham o facebook de um candidato ao emprego antes de contratar?
Então seu chefe pediu para você adicionar o campo "Facebook" no registro de cada funcionário.
Nossa! E agora? Vai em cada uma das dezenas de classes, adicionar esse campo?
Agora não vai mais, pois aprendeu sobre herança aqui no Java Progressivo (e de graça, que bom hein?).
Uma boa solução seria criar a superclasse "Funcionario", com dados que TODOS os funcionários possuem: idade, nome, salário, setor que trabalha etc.
Qual a vantagem disso?
Ora, se você adicionar o campo "facebook" na classe "Funcionario", todas as subclasses passarão a ter esse campo! Pois todas as subclasses (Tesouraria, Secretaria, TI...) são também classes do tipo "Funcionario".
Notou a importância e a mão-na-roda que é herança?
Como saber que é hora de usar herança em Java - Relação 'é um'
Para saber quando usar, e detectar o uso de herança, use a relação 'é um'.
Nos exemplos anteriores:
"Moto" é um "Veiculo", e "Carro" é um "Veículo".
"alunoMedio" é um "Aluno", assim como "alunoFundamental" é um "Aluno".
"FuncionarioTesouraria" é um "Funcionario" e também "FuncionarioTI" é um "Funcionario".
Bem simples e óbvio, não?
Essa é a vantagem do Java e da programação orientada a objetos(POO).
POO é uma imitação do mundo real.
Hierarquia - subclasse de subclasse
Você notou que, por exemplo, dentro do setor "Tesouraria" de uma empresa, podem existir diversos tipos de profissionais, com características únicas e diferentes?
Ora, uma secretária faz uma coisa. O chefe da tesouraria faz outras coisas.
Logo, uma única classe "FuncionarioTesouraria" não seria o suficiente para definir todos os trabalhadores dessa seção?
O que fazer, então?
Crie classes que herdam a classe 'FuncionarioTesouraria' para cada setor da Tesouraria.
Aí teríamos que a 'FuncionarioTesouraria' seria a superclasse da subclasse "TesourariaSecretarias",
que tem os atributos que só as secretárias da seção da tesouraria teriam.
Mas "FuncionarioTesouraria" continuaria sendo uma subclasse da "Funcionario".
E "TesourariaSecretarias" também são subclasses da classe "Funcionario", afinal, todos são funcionários.
Então "Funcionario" é superclasse de "FuncionarioTesouraria" e também é superclasse de "TesourariaSecretarias".
Porém, somente a classe "FuncionarioTesouraria" é superclasse direta da "TesourariaSecretarias".
Bem óbvio e parecido com o mundo.
Uma maravilha essa Orientação a Objetos
Outra vantagem da Herança - Organização
Muitas vezes, durante sua carreira de programador, você terá que estudar o código de outras pessoas.
Se o outro programador que você está estudando (ou decifrando) seu código não tiver estudado no Java Progressivo, vai ser muito desorganizado.
Contextualizando com nossos exemplos, será bem comum você ver:
"TesourariaFuncionarios", "TesourariaSecretarias", "CozinheiraExecutivos", "FuncionariosSecretaria", "Chefes" etc.
Ou seja, muitas classes, muitas informações e tudo completamente desorganizado.
Ao criar superclasses e subclasses você terá uma hierarquia.
Sua IDE vai mostrar as superclasses e suas respectivas subclasses em uma árvore, tudo bem bonito e organizado.
chegar e ver isso na sua IDE:
Funcionários: -Tesouraria: -Secretárias -Banco -Chefes -Secretaria -Secretárias -Almoxarifado -Supervisores -TI -Redes -Office -Programação -Banco de dados -Java -C -C++
Você entende como funciona a empresa somente vendo.
Por exemplo, que tipos de funcionários de TI existem?
Os responsáveis pela Rede, outro pra ensinar e tirar dúvidas sobre o Office e os programadores.
E dentre os programadores, que tipos existem?
É óbvio achar e ver que existem os responsáveis pelo Banco de dados, os que fazem aplicações em Java, C e os que fazem em C++.
Organizado, não?
Herança de construtores e Override
Agora que você já sabe o que é herança, sua importância, quando usar e viu vários exemplos práticos do mundo real, vamos mostrar como fazer uma classe herdar as características de outra.
Nesse tutorial de Java ensinaremos também a relação dos construtores com as superclasses e subclasses, e como usar o tão importante @Override.
Sintaxe de declaração da Herança
Se quisermos declarar uma classe "Filha" que herda os atributos do pai, simplesmente usamos a palavra "extends" na hora de criar a classe:
public class Filha extends Pai{
}
Herança:
o construtor da Subclasse sempre invoca o construtor da Superclasse
Um fato importante: os construtores são algo único a cada classe, portanto não são herdados.
Porém, é possível invocar os construtores de uma superclasse através da subclasse.
Vale lembrar que, para uma aplicação funcionar corretamente, algumas variáveis devem ser iniciadas. Algumas dessas variáveis são iniciadas em uma superclasse, portanto, sempre que o método construtor de uma subclasse roda, roda também o construtor de sua superclasse.
Usando um dos exemplos de nosso tutorial passado sobre herança, na classe "AlunoMedio", que tem algumas disciplinas (como Física e Química), que não existem na classe dos alunos do ensino médio, ela foi herdada da classe "Aluno".
Porém, no construtor da superclasse "Aluno" é que são inicializadas informações de todos os alunos, como nome e número de matrícula.
Portanto, a primeira coisa que construtor da "AlunoMedio" faz é rodar o construtor da "Aluno" - sua superclasse - pois informações e ações importantes ocorrem no construtor da "Aluno".
Resumindo: a primeira coisa que um construtor faz é rodar o construtor de sua superclasse, pois ações importantes podem estar acontecendo lá - como inicialização de variáveis que poderão ser usadas na subclasse.
Vamos ver isso na prática, criando duas classes: a Pai e a Filha, e óbvio, fazendo a Filha herdar a classe Pai.
--->Heranca.java
public class Heranca { public static void main(String[] args) { new Filha(); } }
--->Pai.java
public class Pai { public Pai(){ System.out.println("Método construtor da classe Pai"); } }
--->Filha.java
public class Filha extends Pai { public Filha(){ System.out.println("Método construtor da classe Filha"); } }
O resultado disso é:
Método construtor da classe Pai
Método construtor da classe Filha
Isso mostra que a primeira coisa que um construtor de uma subclasse faz é invocar o construtor da superclasse.
Só depois é que esse construtor da classe Filha faz suas ações.
O exemplo a seguir mostra como a inicialização de variáveis realmente ocorre na superclasse antes de ocorrer na subclasse.
Definimos o nome do pai na classe "Pai" e fazemos a classe "Filha" herdar a pai.
Note que classe "Filha" usamos a variável 'nomePai', mas não declaramos ela na classe Filha.
Porém podemos usar esse atributo pois ele pertence a classe Pai.
Viu que esse valor já vem inicializado? Ou seja, o construtor da classe Pai realmente ocorreu antes da classe Filha.
Heranca.java
public class Heranca { public static void main(String[] args) { Filha filha = new Filha("Mariazinha"); } }
Pai.java
public class Pai { public String nomePai; public Pai(){ this.nomePai = "Neil"; } }
Filha.java
public class Filha extends Pai { private String nomeFilha; public Filha(String nomeFilha){ this.nomeFilha = nomeFilha; System.out.println("O nome da filha é '" + this.nomeFilha + "' e o do pai é '" + nomePai + "'."); } }
Caso o 'nomePai' fosse private, você poderia ter criado um método público na classe "Pai" que retorna o nome do pai, e usar esse método na classe "Filha" sem problema algum.
Herança: quando a superclasse não tem método construtor
Ok, a chamada da superclasse sempre ocorre. Mas pode ocorrer de duas formas: ou você claramente invoca ela ou ela ocorre sozinha.
Quando ela ocorre sozinha, o método construtor chamado é o padrão, que não recebe argumentos.
Note que sua superclasse pode não conter nenhum construtor. Mesmo assim ele é invocado e nada ocorre, pois o Java sempre cria um construtor vazio, que não faz nada, para as classes que são definidas sem construtor.
Então se você não quer que o método construtor da superclasse interfira na subclasse, é só não criar nenhum construtor na superclasse, que nada ocorrerá. Veja o exemplo a seguir, em que apenas definimos a variável 'nomePai' na classe "Pai". Ela não tem nenhum construtor.
Então, ao ser invocado o construtor da "Pai" na "Filha", nada ocorre na "Filha".
Heranca.java
public class Heranca { public static void main(String[] args) { Filha filha = new Filha("Mariazinha"); } }
Pai.java
public class Pai { public String nomePai; }
Filha.java
public class Filha extends Pai { private String nomeFilha; public Filha(String nomeFilha){ this.nomeFilha = nomeFilha; System.out.println("O nome da filha é '" + this.nomeFilha + "' e o do pai é '" + nomePai + "'."); } }
O que é e como fazer Override em Java
Suponha que na classe Pai tenha o método nome(), que mostra uma String na tela, com o nome da pessoa:
public void nome(){
System.out.println("O nome do pai é '" + this.nomePai + "'.");
}
Se você usar esse método na classe Filha verá o nome do pai, correto?
Claro, pois a classe filha herda os métodos também da classe pai também.
Mas esse método retorna o nome da pessoa da classe. Não faz sentido ver o nome do pai, quando invocamos esse método na classe filha.
Para isso, vamos criar um método próprio da classe "Filha", que retorne o nome dela, e não o do pai.
Ficará assim:
public void nome(){
System.out.println("O nome da filha é '" + this.nomeFilha + "'.");
}
Porém vamos usar o mesmo nome nesse método, 'nome()'.
E agora? Ao chamar esse método, o que Java vai mostrar? O método da classe Pai ou da classe Filha?
Não vamos nos estressar com o que ele vai mostrar, pois vamos usar um Override, ou seja, vamos sobrescrever um método.
O método original, é claro que é o da classe "Pai".
Porém, quando criarmos uma classe filha e invocarmos o método 'nome()' queremos que apareça o nome da filha, e não o do pai. Então queremos que o método chamado seja o método da subclasse e não o método da superclasse.
Para dizer isso ao Java escrevemos "@Override" antes do método da subclasse.
Então, nosso programa fica assim:
Heranca.java
public class Heranca { public static void main(String[] args) { Filha filha = new Filha("Mariazinha"); Pai pai = new Pai(); filha.nome(); pai.nome(); } }
Pai.java
public class Pai { public String nomePai; public Pai(){ this.nomePai="Neil"; } public void nome(){ System.out.println("O nome do pai é '" + nomePai + "'."); } }
Filha.java
public class Filha extends Pai {
private String nomeFilha;
public Filha(String nomeFilha){
this.nomeFilha = nomeFilha; }
@Override public void nome(){
System.out.println("O nome da filha é '" + this.nomeFilha + "'.");
}
}
Pronto. Com o @Override, o Java vai mostrar o nome da filha, caso o objeto seja do tipo Filha.
E vai mostrar o nome do pai caso o objeto seja apenas do tipo Pai.
super: Chamando o construtor da Superclasse
Muitas vezes, em nossas aplicações Java, criamos um objeto que é subclasse de outra.
Porém passamos 'direto' pela superclasse e vamos usar diretamente o objeto da subclasse.
Mas acontece que a subclasse depende das variáveis inicializadas na superclasse.
E como vai ser inicializada essas variáveis, já que não criamos um objeto da superclasse, e sim da subclasse?
A resposta é: usando a keyword super.
Com a super, nós chamamos o construtor da superclasse.
Por exemplo, vamos supor que queiramos criar somente um objeto da classe "Filha". Ou seja, não queremos criar a "Pai". Somente a filha!
Mas a filha usa o nome do pai. Então vamos chamar o construtor da Pai no construtor da Filha, passando o argumento para ele (que é o nome do pai).
Nosso código ficará assim:
Heranca.java
public class Heranca { public static void main(String[] args) { Filha filha = new Filha("Mariazinha", "Neil"); filha.nome(); } }
Pai.java
public class Pai { public String nomePai; public Pai(String nomePai){ this.nomePai=nomePai; } public void nome(){ System.out.println("O nome do pai é '" + nomePai + "'."); } }
Filha.java
public class Filha extends Pai { private String nomeFilha; public Filha(String nomeFilha, String nomePai){ super(nomePai); this.nomeFilha = nomeFilha; } @Override public void nome(){ System.out.println("O nome da filha é '" + this.nomeFilha + "', e do pai '"+nomePai+"'."); } }
private, public e protected: Protegendo suas informações em Java
Já vimos a relação de membros e métodos definidos como public e private, bem como a relação deles com o restante da aplicação.
Sabemos que membros private não podem ser acessados fora da classe em que foram declarados.
Mas em relação as suas subclasses? E se eu quiser esconder algum dado das subclasses?
Veremos nesse artigo, de nosso curso online de Java, como proteger membros de uma classe, limitar e controlar o acesso aos dados importantes.
Herança de atributos public
Atributos public não tem segredo. Podem ser 'vistos' e acessados de qualquer parte de uma aplicação Java.
Na prática, elementos public são utilizados em variáveis e métodos universais, onde não há problema nem necessidade de segurança daquele dado. Exemplo: horas, constantes, variáveis globais, nome do programa ou da empresa etc.
O nome já fala tudo por si só, é público.
1'
Herança de atributos private
Diferente dos elementos public, os private são informações 'escondidas', ou no mínimo controladas.
Por exemplo, é bem comum ver todas as variáveis de uma classe definidas como priva, e usar métodos public do tipo set, para dar um valor a esta variável.
Ora, se o set() for só para definir o valor da variável, essa atitude é totalmente inútil.
Só devemos usar o set() para manipular variáveis private quando fizemos algo a mais, como checar se o valor recebido é positivo, se está dentro do intervalo que esperamos ou mesmo para contar quantas vezes aquela variável foi acessada.
Isso tudo tem um motivo: variáveis private não podem ser acessadas nem por suas subclasses.
Tenha isso em mente: priva é sinônimo de segurança.
O seguinte exemplo, dando continuidade aos exemplos de Herança de nossa tutorial de Java passado, vamos mostrar que é impossível acessar diretamente a variável 'senhaPai' a partir da subclasse "Filha":
-->Heranca.java
public class Heranca { public static void main(String[] args) { Filha filha = new Filha(); } }
-->Pai.java
public class Pai { private String senhaPai; public Pai(){ this.senhaPai = "papaidanadao"; } }
-->Filha.java
public class Filha extends Pai { public Filha(){ System.out.println(senhaPai); } }
Obteremos o seguinte erro:
"The field Pai.senhaPai is not visible"
Literalmente: o campo Pai.senhaPai não é visível. Nem pra classe filha a senha da classe pai é vista.
Isso que é segurança, hein?
Pense bem se seus atributos devem ser private ou não. Lembre-se que se não forem, outras pessoas - hackers - podem usar isso para obter informações de sua aplicação.
Herança de atributos protected
Vamos agora introduzir outro modo de declarar seus atributos, o modificador de acesso protected.
O protected é pra quando você não quer deixar um atributo public, livre para todos.
Porém, você quer compartilhar ele com as subclasses. O protected é um intermediário entre public e private.
É um segredo de família. Por família entende: a superclasse, as subclasses e classes do mesmo package.
Para todos as outras classes, atributos protected são invisíveis. Ou seja, estamos protegendo do acesso externo.
Outro detalhe importante: quando atributos são declarados como public ou protected, eles continuarão como public ou protected.
Lembra que dissemos que subclasses podem ser superclasse também? Ora, basta declarar outra classe que extends a classe Filha, e teremos uma subclasse da subclasse Filha.
Então, vamos chamar essa classe de Neta.
Se declararmos uma variável como protected na classe Pai, ela será protected na classe Filha, e será vista como protected na classe Neta.
O mesmo para public.amos criar dois pacotes: o package "Familia", que irá conter as classes "Heranca", "Pai", "Filha" e "Neta ; e o pacote "Vizinho", que contém a classe "Vizinho".
Para fazer isso basta escrever no início: package Familia ou package Vizinho
A senha do pai poderá ser vista por qualquer um do pacote "Familia", mas obteremos um erro quando tentarmos ver essa senha através da classe "Vizinho", pois o vizinho não faz parte do pacote "Familia".
Nosso exemplo ficará assim:
-->Heranca.java
package familia; import Vizinho.Vizinho; public class Heranca { public static void main(String[] args) { Neta neta = new Neta(); System.out.println("Senha vista da classe Herança: "+neta.senhaPai); Vizinho vizinho = new Vizinho(neta); } }
-->Pai.java
package familia; public class Pai { protected String senhaPai; public Pai(){ this.senhaPai = "papaidanadao"; } }
-->Filha.java
package familia; public class Filha extends Pai { public Filha(){ System.out.println("Senha vista pela filha: "+senhaPai); } }
-->Neta.java
package familia; public class Neta extends Filha{ public Neta(){ System.out.println("Senha vista pela Neta: " + senhaPai); } }
-->Vizinho.java
package Vizinho; import familia.Neta; public class Vizinho { public Vizinho(Neta neta){ System.out.println("Senha vista pelo vizinho: "+neta.senhaPai); } }
Obtemos o resultado:
Senha vista pela filha: papaidanadao
Senha vista pela Neta: papaidanadao
Senha vista da classe Herança: papaidanadao
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The field Pai.senhaPai is not visible
Ou seja, podemos ver a senha do Pai por qualquer uma das subclasses e classes do pacote "Familia".
Porém, ao tentarmos ver por uma classe que não faz parte nem do pacote "familia" nem é subclasse de "Pai", "Filha" ou "Neta" obtemos um erro, pois a senha do pai não será visível para o vizinho.
Seguro esse Java, não?
Não é à toa que ele é bastante usado por empresas, como seu bank online. Pode confiar no Java!
A classe Object: o que é, uso e principais métodos
Agora que já sabemos o que é herança, como a herança se comporta em relação aos construtores, uso do @Override e vimos os modificadores de acesso private, public e protected, vamos apresentar uma classe de suma importância bem como alguns de seus métodos: a classe Object.
O que é e para que serve a classe Object
Essa classe é importante por um motivo bem simples: toda e qualquer classe em Java é subclasse da Object.
Mesmo quando não usamos 'extends Object', o Java automaticamente faz sua classe herdar a Object.
Lembra que, várias vezes, ao longo de nosso curso online de Java usamos o método toString() sem nem ter declarado ele?
Pois é, ele já é declarado na classe Object, por isso podemos usar ele diretamente.
Como nossa classe é subclasse da Object, ela também tem os métodos da Object.
Métodos da classe Object:
getClass()
Esse método retorna informações do objeto atual, como o package e o nome da classe.
Muito importante caso você tenha vários tipos de objeto, onde um herda do outro etc.
Ele vai retornar o nome da classe que objeto foi criado, e não sua superclasse.
clone()
Retorna uma referência - ou cópia - de um objeto.
Você deve implementar esse método conforme sua necessidade. Para uma cópia total, você deve implementar a cópia de cada variável corretamente.
Veja sobre a interface Cloneable: http://docs.oracle.com/javase/7/docs/api/java/lang/Cloneable.html
No exemplo ao final do artigo, fizemos um @Override do método para que ele retorne uma referência objeto que queremos copiar.
Pode usar tanto:
Object copia = original.clone()
Ou:
NomeDaClasse copia = (NomeDaClasse) original.clone();
Mais informações em: http://en.wikipedia.org/wiki/Clone_(Java_method)
toString()
Retorna uma string com a package, nome da classe e um hexadecimal que representa o objeto em questão.
equals(Object obj)
Faz a comparação entre dois Objects, e retorna true se os objetos forem o mesmo, e false se não forem o mesmo.
É útil para saber se dois objetos apontam para o mesmo local na memória.
hashCode()
Esse método retorna um inteiro único de cada objeto, muito usado em Collections.
Falaremos mais dele nos artigos sobre coleções, em breve.
Veja sobre HashMap: http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
O hashCode tem uma ligação com o método equals(), conforme você pode ver no exemplo dado, lá embaixo.
É bastante útil quando trabalhamos com arquivos, para agilizar buscas e comparações.
Outros métodos:
Veja a documentação da classe Object:
http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html
Exemplo dos métodos da classe Object:
-->objectClass.java
package teste; public class objectClass{ public static void main(String[] args) { classeTeste classe1 = new classeTeste(); classeTeste2 classe2 = new classeTeste2(); System.out.println("\ngetClass() da classeTeste: " + classe1.getClass()); System.out.println("getClass() da classeTeste2: " + classe2.getClass()); // Object classe3 = classe2.clone(); classeTeste2 classe3 = (classeTeste2) classe2.clone(); System.out.println("\nObjeto classe3 é clone ao classe2? "+classe3.equals(classe2)); System.out.println("\ntoString da classe1: "+classe1.toString()); System.out.println("toString da classe2: "+classe2.toString()); System.out.println("\nObjeto classe1 é igual classe2 ? " + classe2.equals(classe1)); System.out.println("Objeto classe1 é igual classe1 ? " + classe1.equals(classe1)); System.out.println("Objeto classe2 é igual classe3 ? " + classe2.equals(classe3)); System.out.println("\nHash code da classe 1: "+classe1.hashCode()); System.out.println("Hash code da classe 2: "+classe2.hashCode()); System.out.println("Hash code da classe 3: "+classe3.hashCode()); } }
-->classeTeste.java
package teste; public class classeTeste extends Object{ public String s1; public classeTeste(){ System.out.println("Objeto da classeTeste criado!"); this.s1= "oi"; } @Override protected Object clone(){ return this; } }
--> classeTeste2.java
package teste; public class classeTeste2 extends classeTeste{ public String teste; public classeTeste2(){ System.out.println("Objeto da classe classeTeste2 criado"); } }
Polimorfismo em Java:
o que é, pra que serve, como e onde usar
Agora que aprendemos os conceitos mais importantes e vimos o uso da Herança em Java, vamos estudar outra características marcante da programação Java e, de uma maneira mais geral, da programação orientada a objetos: o polimorfismo.
Com o polimorfismo vamos ter um controle maior sobre as subclasses sem ter que nos preocupar especificamente com cada uma delas, pois cada uma terá autonomia para agir de uma meneira diferente.
Definição de polimorfismo em Java
Traduzindo, do grego, ao pé da letra, polimorfismo significa "muitas formas".
Essas formas, em nosso contexto de programação, são as subclasses/objetos criados a partir de uma classe maior, mais geral, ou abstrata.
Polimorfismo é a capacidade que o Java nos dá de controlar todas as formas de uma maneira mais simples e geral, sem ter que se preocupar com cada objeto especificamente.
Mais uma vez, somente pela definição é muito complicado de entender.
Vamos partir para algo comum e de preocupação máxima de nosso curso de Java online: exemplos práticos.
Exemplo 1 de Polimorfismo: Aumento no preço dos carros
Vamos pra nossa loja de carros, onde você é o programador Java de lá.
Lembra que aprendeu, através de exemplos, a criar uma classe bem genérica, chamada "Carro"?
E depois criamos várias subclasses, de fuscas, ferraris, gols etc.
Imagine que, todo ano, todos na empresa tem um aumento.
A Ferrari teve aumento de 5%. o fusca terá aumento de 3% e o gol terá de 1%.
Note que, embora todos sejam "Carro", cada objeto terá que calcular seu aumento de forma diferente, pois terão diferentes valores de aumento. Como criar, então, um método na superclasse que atenda todas essas necessidades diferentes?
Não é na superclasse que se resolve, mas nas subclasses, criando o método 'aumento()' em cada uma.
Ou seja, vai criar vários métodos, e para fazer o aumento realmente ocorrer de maneira correta, é só invocar o método do objeto específico.
Então: objetoFerrari.aumento() é diferente de objetoFusca.aumento().
Note que usamos o mesmo nome do método para todas as subclasses, porém cada método é diferente um do outro.
Isso é o polimorfismo em ação: embora todos os objetos sejam "Carro", eles terão uma forma diferente de agir, pois implementamos os métodos de maneira diferente. Apenas invocamos, e todo objeto sabe exatamente o que fazer.
Por isso o nome polimorfismo, pois cada objeto terá sua forma própria de como rodar, pois os métodos 'aumento()' dos objetos são diferentes.
Outra vantagem do polimorfismo: você já viu que, criando o método aumento() em toda as subclasses, ela agirão de maneira independente da superclasse e diferente de outros objetos.
Agora, quando chegar outro carro na sua loja você de adicionar o método aumento(), e terá um novo tipo de objeto, sem grandes alterações no código.
Exemplo 2 de Polimorfismo: animais mugindo, latindo, berrando...
Imagine que você é o criador do joguinho Colheita feliz ou Fazenda Feliz (sei lá), onde terá vários bichos nesse jogo.
Como você é esperto, vai logo abstrair e criar uma classe "Animal" com as características que todos tem: idade, peso, espécie etc.
Porém, nesse game, os animais fazem seu som característico: o cachorro late, o gato mia, o pinto pia, a vaca muge etc.
E aí? Como criar um método na superclasse que sirva para todos estes animais?
Ora, não cria, pois cada animal age diferente nesse aspecto.
Veja, polimorfismo é isso: embora objetos sejam da mesma superclasse, vão agir de maneira diferente em algum aspecto. Ou seja, terão várias(poli) formas diferentes (morfismo).
A saída é criar um método chamado 'som()' na superclasse (só o cabeçalho, como veremos nos próximos tutoriais)
e em cada subclasse criar um método diferente, que caracterize cada bicho.
Veja que se não fizéssemos isso e invocássemos os métodos: vaca.som(), cachorro.som(), gato.som(), todos iriam fazer o mesmo barulho.
Com o polimorfismo: vaca.som() faria a vaquinha mugir, cachorro.som() faria o cachorro latir e gato.som() faria o objeto miar.
Porém, todos continuam sendo, também, objetos da classe "Animal".
E quando chegar mais animais na sua fazenda, adicione o método som() nesse animal, de modo que ele poderá agir conforme suas características.
Resumindo: polimorfismo permite que uma mesma superclasse possua subclasses com características - ou formas - diferentes.
Polimorfismo: Classes abstratas e Métodos abstratos
Desde que iniciamos nossos tutoriais sobre classes e objetos vínhamos tratando as classes como entidades 'reais' ou 'concretas',
pois podíamos instanciar objetos a partir dela.
Nesse artigo vamos introduzir um conceito importante e bastante útil na orientação a objetos: a abstração.
Pode parecer estranho, ou no mínimo confuso, classificar e utilizar algo que se diz ser abstrato,
mas veremos que esse tipo de idéia é bem interessante e bem mais realista que nossa noção antiga de classe e objeto.
O que são classes abstratas em Java
Ao pé da letra, a definição de classe abstrata é: classes que não serão instanciadas.
Ou seja, são classes em que não criaremos nenhum objeto dela, diretamente.
Para evitar confusão, é mais correto se referir a esse tipo de classes como superclasses abstratas.
Então, qual o sentido de se ter classes em que nunca vamos instanciar um objeto a partir delas?
Se você for perspicaz, deve ter notado as palavras 'diretamente' e 'superclasse'.
Não vamos criar objetos dessas superclasses abstratas, mas sim de suas subclasses.
Por isso que explicamos em detalhes os conceitos de herançaantes de introduzir os de polimorfismo.
Classes abstratas no mundo real
Usamos classes abstratas para representar grupos que tem características comum, mas que, em alguns detalhes específicos, agem de maneira diferente. Note que as classes abstratas são bastante relacionadas com polimorfismo em Java.
Vamos aos nossos exemplos práticos.
Se você for a uma concessionária para comprar um veículo, você nunca irá escolher um "Carro", você escolhe um gol, um fiat, enfim, um modelo específico.
Se for dono de um empresa e for contratar alguém, você não contra simplesmente um "Funcionario", você contrata uma secretárias, um técnico de TI, enfim, você contrata alguém com uma profissão específica para fazer ma função específica.
Do tutorial passado sobre os animais no joguinho da Colheita Feliz, você não vai colocar um "Animal" no jogo, você coloca uma vaca, um cão ou uma ovelha. Ou seja, um animal específico.
Na verdade, em nosso mundo real, só existem objetos. As classes são abstratas, servem para classificar grupos de objetos.
Por exemplo, "Carro" é uma classe abstrata que reúne um conjunto de características que classificam os carros: potência do motor, possuem pneus, possuem portas, se locomovem etc.
"Funcionario" é uma classe abstrata que reúne um conjunto de características, gerais, de um funcionário: tem salário, uma missão na empresa, pagam impostos, tem um chefe etc.
Ou seja, não existe "Carro", existe fusca ou ferrari.
Ninguém pede carne de "Animal" na churrascaria, e sim de boi ou carneiro.
E ninguém se candidata a uma vaga para trabalhar como "Funcionario".
São apenas modelos, abstrações que agrupam e definem um conjunto de objetos com características semelhantes.
Classes abstratas são superclasses que servem como modelo. Esses modelos possuem, no entanto, somente as características gerais.
Por que gerais?
Ora, não podemos dizer qual a potência do motor do "Carro", nem o número de portas ou quantos quilômetros fazem com 1 litro.
Isso é uma característica de cada tipo específico de carro. Também não podemos dizer que "Animal" berra ou late, ou se é herbívoro ou carnívoro. Muito menos podemos especificar o salário de um "Funcionário" ou que as línguas estrangeiras que sabem ou função desempenhada, pois isso são características específicas.
Vale lembrar que podemos também ter subclasses abstratas.
Por exemplo, "Animal" é abstrato, e as subclasses "Mamiferos" e "Repteis" também seriam abstratas, pois são um modelo para um grupo mais específico e seleto de animais.
E da classe "Carro", que subclasses, também abstratas porém mais específicas, poderíamos citar?
"Volkswagen", "Toyota" etc.
Logo, classes abstratas servem para criar um modelo, um conjunto de características bem gerais.
Para que servem as classes abstratas e os métodos abstratos em Java
Tínhamos citado, em nosso tutorial de Java passado, que através do polimorfismo as subclasses poderiam se portar de maneira diferente.
Abstração e polimorfismo estão intimamente relacionados.
Vamos criar classes abstratas, e declarar somente o cabeçalho dos métodos.
Esses serão os métodos abstratos: somente vamos dizer que tipos de métodos existem, através da declaração deles.
Não podemos definir e criar o código desses métodos por uma razão bem óbvia:
os métodos vão se comportar de modo diferente nas subclasses, ora. Porém, eles terão o mesmo nome.
Declarando classes abstratas e métodos abstratos em Java
Usamos, em ambos casos, a keyword 'abstract' logo após o modificador de acesso (ou seja, logo após public, private ou protected):
classes: public abstract class Animal { ... }
método: public abstract void som() { ... }
Quando for implementar os métodos nas subclasses, não esqueça de usar o @Override.
Exemplo de código em Java: Polimorfismo e abstração dos animais
Note que podemos declarar variáveis, criar construtores e métodos que serão o mesmo em todas as subclasses.
Porém, os métodos que serão diferentes em cada subclasse nós apenas declaramos seu cabeçalho.
Vamos usar as variáveis "nome" e "numeroPatas" na superclasse abstrata, porém o método som(), que cada objeto emite, será diferente.
Depois, criamos um objeto para cada tipo diferente de objeto e invocamos o método som() de cada um, e você verá que, embora todos façam parte da superclasse abstrata "Animal", eles vão agir de forma diferente.
É a união da herança, polimorfismo e abstração:
-->Bichos.java
public class Bichos { public static void main(String[] args) { Vaca mimosa = new Vaca(); Gato bichano = new Gato(); Carneiro barnabe = new Carneiro(); mimosa.som(); bichano.som(); barnabe.som(); } }
-->Animal.java
public abstract class Animal { protected String nome; protected int numeroPatas;
public abstract void som(); }
-->Vaca.java
public class Vaca extends Animal { public Vaca(){ this.nome = "Vaca"; this.numeroPatas = 4; }
@Override public void som(){ System.out.println("A " + this.nome + " que tem " + this.numeroPatas + " patas, faz MUUUU"); } }
-->Gato.java
public class Gato extends Animal{ public Gato(){ this.nome = "Gato"; this.numeroPatas = 4; }
@Override public void som(){ System.out.println("O " + this.nome + " que tem " + this.numeroPatas + " patas, faz MIAU"); } }
-->Carneiro.java
public class Carneiro extends Animal{ public Carneiro(){ this.nome = "Carneiro"; this.numeroPatas = 4; }
@Override public void som(){
System.out.println("O " + this.nome + " que tem " + this.numeroPatas + " patas, faz BÉÉÉ");
} }
Manipulando polimorficamente subclasses
Agora que você domina completamente os conceitos de herança, polimorfismo e abstração, vamos aos exemplos práticos - códigos em Java.
Vamos mostrar como é possível uma superclasse abstrata manipule suas subclasses concretas.
Superclasse abstratas se tornando subclasses concretas
Uma poderosa arma do polimorfismo e abstração em Java é a capacidade que temos de manipular as subclasses através de uma superclasse. Como cada subclasse concreta foi criada com métodos diferentes, elas agirão de forma diferente, conforme vimos em nossos tutoriais sobre polimorfismo em Java. Isso nos dá um incrível poder e versatilidade, afinal podemos usar qualquer objeto que seja uma subclasse de nossa classe abstrata. Vamos usar o exemplo de nosso tutorial sobre Polimorfismo, Classes e Métodos Abstratos: o exemplo dos animais. Através da classe abstrata "Animal", criamos 3 objetos: uma vaca, um gato e um carneiro. Vamos fazer a classe "Animal" receber uma referência de cada um desses objetos e invocar seu método 'som()', que é abstrato e age de forma diferente em cada subclasse.
Ou seja, vamos manipular polimorficamente os animais. Começamos essa manipulação ao fazer com que um Array de "Animal" receba os objetos da classe "Vaca", "Gato" e "Carneiro":
Animal bichos[] = {mimosa, bichano, barnabe};
Depois, fazemos com que um objeto da classe "Animal", o 'animal' tenha uma referência para cada um desses objetos (mimosa, bichano e barnabé):
for(Animal animal : bichos) { System.out.print("Esse animal é da classe " + animal.getClass().getName() + " e faz "); animal.som(); System.out.println(); }
Note como ficou simples chamar todos as subclasses da classe abstrata "Animal", fazendo com que o objeto 'animal' receba cada um dos tipos ("Vaca", "Gato" e "Carneiro") !
Imagine se fosse uma aplicação real, de um game, com dezenas ou centenas de animal, que a cada 3 segundos tivessem que fazer seu som? Imagine a trabalheira que seria invocar o método de cada objeto desse? Agora você já sabe que é melhor manipular polimorficamente cada objeto que é subclasse da classe abstrata! Aproveitamos também para mostrar a utilidade de se saber os métodos da classe Object. No caso, usamos o método getClass(), que retorna 'classe NomeDaClasse' e o método getName() que retorna só o 'NomeDaClasse'
Código Java de como manipular polimorficamente as subclasses
-->zoo.java
public class zoo { public static void main(String[] args) { Vaca mimosa = new Vaca(); Gato bichano = new Gato(); Carneiro barnabe = new Carneiro(); Animal bichos[] = {mimosa, bichano, barnabe}; for(Animal animal : bichos) { System.out.print(animal.nome + " é da classe " + animal.getClass().getName() + ", tem " + animal.numeroPatas + " patas e faz "); animal.som(); System.out.println(); } } }
-->Animal.java
public abstract class Animal { protected String nome; protected int numeroPatas; public abstract void som(); } -->Vaca.java public class Vaca extends Animal { public Vaca(){ this.nome = "Mimosa"; this.numeroPatas = 4; } @Override public void som(){ System.out.print("MUUUU"); } }
-->Gato.java
public class Gato extends Animal{ public Gato(){ this.nome = "Bichano"; this.numeroPatas = 4; } @Override public void som(){ System.out.print("MIAU"); } } -->Carneiro.java public class Carneiro extends Animal{ public Carneiro(){ this.nome = "Banabé"; this.numeroPatas = 4; } @Override public void som(){ System.out.print("BÉÉÉ"); } }
Descobrindo a classe de um objeto: instanceof
Nesse rápido, e útil, tutorial ensinaremos como descobrir a classe de um objeto através da keyword:
instanceof
Conforme sua aplicação Java for crescendo, facilmente ela atingirá dezenas ou centenas de classes, subclasses, superclasses, classes abstratas e outros nomes da Orientação a Objetos.
Somente pelo nome do objeto pode ficar difícil descobrir a qual classe ela pertence.
Porém, o Java provém uma solução simples pra isso, a keyword: instanceof
O nome é bem sugestivo e fácil de entender. Seu uso, mais ainda.
A sintaxe é:
nomeObjeto instanceof Classe
Essa é uma expressão, que retorna true ou false.
Vamos usar o exemplo de nosso tutorial passado, sobre Como Manipular Subclasses através do Polimorfismo o exemplo dos animais.
-->zoo.java
public class zoo {
public static void main(String[] args) {
Vaca mimosa = new Vaca();
Gato bichano = new Gato();
Carneiro barnabe = new Carneiro();
Animal bichos[] = {mimosa, bichano, barnabe};
for (int i = 0; i < bichos.length; i++) {
if (bichos[i] instanceof Vaca) {
System.out.print("A vaca tem " + bichos[i].numeroPatas + " patas e faz ");
bichos[i].som();
System.out.println();
}
if (bichos[i] instanceof Gato) {
System.out.print("O gato tem " + bichos[i].numeroPatas + " patas e faz ");
bichos[i].som();
System.out.println();
}
if (bichos[i] instanceof Carneiro) {
System.out.print("O carneiro tem " + bichos[i].numeroPatas + " patas e faz ");
bichos[i].som();
System.out.println();
}
}
}
}
-->Animal.java
public abstract class Animal {
protected String nome;
protected int numeroPatas;
public abstract void som();
}
-->Vaca.java
public class Vaca extends Animal {
public Vaca() {
this.nome = "Mimosa";
this.numeroPatas = 4;
}
@Override
public void som() {
System.out.print("MUUUU");
}
}
-->Gato.java
public class Gato extends Animal {
public Gato() {
this.nome = "Bichano";
this.numeroPatas = 4;
}
@Override
public void som() {
System.out.print("MIAU");
}
}
-->Carneiro.java
public class Carneiro extends Animal {
public Carneiro() {
this.nome = "Banabé";
this.numeroPatas = 4;
}
@Override
public void som() {
System.out.print("BÉÉÉ");
}
}
Strings e Caracteres: Escrevendo em Java
Java: A Classe String
Strings é uma sequência de caracteres. Estes podem ser letras, dígitos ou caracteres especiais, como + _ - * > .<
São tão importantes que o Java possui mais de uma classe só para tratar as strings.
Vejamos, por exemplo, construtores para a classe String:
char[] blog = {'p', 'r' , 'o' , 'g' , ' ' , 'p' , 'r' , 'o' , 'g' , 'r' , 'e' , 's' , 's' , 'i' , 'v' , 'a'};
String stg = new String( "teste" );
String stg1 = new String();
String stg2 = new String( stg );
String stg3 = new String( blog );
String stg4 = new String( blog, 5, 11 );
A saída seria:
stg → teste
stg1 →
stg2 → teste
stg3 → prog progressiva
stg4 → progressiva
Essa classe possui uma série de métodos para formatar strings. No último caso, 'stg4' é iniciada com parte da string 'blog', ela receberá 11 caracteres dessa string, a partir do caractere de índice 4.
Aqui vai uma importante ressalva: String objects são imutáveis! Não é possível mudar seus caracteres depois que você criou!
As strings, por serem da classe String, possuem métodos característicos:
-
stg.length() -> informa o tamanho da string
-
stg.charAt(indice) -> mostra determinado caractere da string
-
stg.getChars(int inicio, int fim, char[] , int inicio2)
Diferente de C/C++, por exemplo, String e Vetor de caracteres são duas coisas diferentes em Java.
No último método, queremos passar os caracteres da String 'stg', a partir do índice 'inicio' até o índice 'fim' para um vetor de caracteres, a partir da posição de índice 'inicio2' nesse vetor
stg.compareTo(stg2) -> 0 se forem iguais; negativo caso stg < stg2 ; positivo caso stg > stg2 , lexicograficamente falando (na forma do dicionário).
A classe String também oferece ferramentas para compararmos strings. Veja alguns desses métodos:
-
stg.equals(stg2) -> retorna 'true' ou 'false', com case sensitive (maiúsculo/minúsculo importam)
-
stg.equalsIgnoreCase -> retorna 'true' ou 'false', sem case sensitive (maiúsculo/minúsculo não importam)
-
stg == "teste" -> retorna 'true' ou 'false' em relação ao fato de serem o mesmo OBJETO, não conteúdo!!! Só retorna 'true' se apontarem pro mesmo endereço de memória!
Se tivéssemos feito:
stg="teste" , o método retornaria 'true', pois o Java trata todos os objetos de string literal de mesmo conteúdo como o mesmo objeto, só com mais de uma referência. Por exemplo:
string1="Programacao Progressiva"
string2="Programacao Progressiva"
Como ambas possuem o mesmo conteúdo, Java trata elas como o mesmo objeto. Ambas apontam pro mesmo endereço de memória.
Porém, com stg = new String("hello") , se cria um novo endereço de memória para a string. O que resultaria em 'false'.
-
stg.regionMatches(int a, String stg2, int b, int c) -> compara somente certas regiões das strings. Começa a partir do índice 'a' da 'stg', compara com a string 'stg2' a partir do índice 'b' desta, e elas comparam 'c' elementos das strings.
Também existem métodos para a busca, concatenação, alteração maiúsculo/minúsculo, substituição total/parcial e extração de trechos de texto ou caractere em strings, presentes na classe String.
Porém, devido ao grande número e flexibilidade dos métodos oferecidos pela classe String, é inviável mostrar todos aqui, deixamos alguns aqui e outros você pode ver na documentação, conforme sua necessidade. Exemplo:
-
stg.startsWith()
-
stg.endsWith()
-
stg.lastIndexOf()
-
stg.indexOf()
-
stg.substring()
-
stg.concat()
-
stg.replace()
-
stg.toUpperCase()
-
stg.toLowerCase()
-
stg.toCharArray()
Também existem métodos para 'quebrar' uma determinada string, cujos partes são separadas por um separador, como o espaço " ", por exemplo.
Escreva algo:
-
String stg= scanner.nextLine();
Receba as 'partes', separadas por espaço:
-
String[] pedacos = stg.split(" ");
-
pedacos.length -> retorna o numero de pedaços
Vamos imprimir esses pedaços, que nada mais são que as palavras que voce digitou:
for(String palavra : pedacos)
System.out.println(palavra);
Java: A Classe StringBuilder
Essa sim é modificável, diferente da classe String.
Com strings dessa classe podemos usar as strings de forma dinâmica, criando, modificando, copiando, anexando, invertendo, adicionando e retirando caracteres, o tanto que desejarmos.
Porém, devemos ainda usar a classe String, especialmente quando os dados contidos na string não necessitarem de mudança (como em Labels, mensagens de erro, de log etc, pois por conta dessa limitação, essa classe é bem otimizada. Mas para strings que se alteram, usamos a StringBuilder.
Existe ainda a StringBuffer, para caso seja utilizado threads.
Vejamos alguns métodos e características destes:
StringBuilder teste = new StringBuilder("123456789");
teste.toString() -> retorna a string 123456789
teste.length() -> o tamanho é 9
teste.capacity() -> a capacidade inicial é 25 (9 + 16, toda StringBuilder inicia com 16, por padrão)
teste.ensureCapacity(100) -> garante uma capacidade pra string, para nao ser necessário crescer dinamicamente ao longo de um programa, o que pode comprometer a performance.
teste.setLength(5) -> agora, teste é somente "12345"
teste.reverse() -> inverte os caracteres da StringBuilder
teste.charAt(int) -> recebe um inteiro e retorna o caractere referente aquele índice
teste.getChars(a, b, char[] c, d) -> copia caracteres da StringBuilder 'teste' a partir do índice 'a' até o índice 'b' para o vetor de caracteres 'c', a partir do índice 'd' neste vetor.
Teste este método, passando a string para o vetor de caracteres e imprimindo-o:
teste.getChars(0, teste.length(), vetorChar, 0);
for( char caractere : vetorChar )
System.out.print(caractere);
teste.setCharAt(a, 'b') -> insere o caractere 'b' na posição de índice 'a'
teste.insert(a, objeto) -> insere, a partir do índice 'a', o 'objeto' na BuildString. Objeto, em Java, representa uma gama enorme de coisas, como String, vetor de caracteres, booleanos, inteiros, float, doubles, chars etc.
teste.deleteCharAt( a ) -> deleta especificamente o caractere da posição de índice 'a'
teste.delete(a, b) -> dele o caractere de índice 'a' até o de índice 'b'
teste.append( variosObjetos ) -> anexa, ao fim da String Builder 'teste' algum 'variosObjetos';
É possível, inclusive, a concatenação:
String teste2 = new StringBuilder().append("Programação").append("\n").append("Progressiva").toString();
Com esses métodos, é possível notar a flexibilidade e utilidade da StringBuilder.
Java: A Classe Character
Assim como outros tipos, a Character é uma classe do tipo wrapper. Ou seja, não é apenas um tipo e pronto, como int e float.
Ela já vem com uns métodos e funcionalidades que facilitarão bastante a vida do programador Java.
O Java provém, automaticamente, para os caracteres, diversos métodos, como checagem pra saber se o caractere é Unicode, se é letra, se é maiúscula ou minúscula, se é número etc, além de métodos de comparação entre caracteres. Vamos ver:
char caractere;
-
Character.isDefined( caractere ) -> checa se 'caractere' está definido no Unicode; retorna true ou false
-
Character.isDigit( caractere ) -> 'true' se 'caractere' é um dígito, 'false' caso contrário
-
Character.isLetter( caractere ) -> 'true' se 'caractere' for letra, 'false' o contrário
-
Character.isLowerCase( caractere ) -> 'true' se 'caractere' for minúsculo, 'false' o contrário
-
Character.isUpperCase( caractere ) -> 'true' se 'caractere' for maiúsculo, 'false' o contrário
-
Character.toUpperCase( caractere ) -> transforma o 'caractere' em maiúsculo
-
Character.toLowerCase( caractere ) -> transforma o 'caractere' em minúsculo
-
Character.isLetterOrDigit( caractere ) -> 'true' se 'caractere' for letra ou dígito, 'false' o contrário
-
Character.isJavaIdentifierStart( caractere ) -> esse método é interessante. Ele identifica se o 'caractere' pode ser o primeiro caractere de um identificador em Java, ou seja, se pode ser letra, underlina _ ou o sinal de dinheiro $.
-
Character.isJavaIdentifierPart( caractere ) -> já esse checa se o 'caractere' pode ser parte de um identificador, ou seja, se é letra, número, underline _ ou dindin $ ; ambos retornam 'true' ou 'false'
Para usarmos os métodos comparativos, vamos declarar os caracteres como objetos 'Character' ao invés do tipo 'char' para explicar melhor alguns métodos da CLasse, pois já mostramos os da wrapper:
Character a = 'a';
Character b = 'b';
-
a.charValue() -> retorna o valor do objeto em formta de caractere
-
a.toString() -> retorna o objeto, mas na forma de String
-
a.equals(b) -> compara se o objeto 'a' possui o mesmo conteúdo do objeto 'b', onde o caso sensitivo importa. Ou seja, 'p' é diferente de 'P'
Embora muitas pessoas ainda confundam, mesmo tendo ciência de métodos que envolvam números, caracteres também incluem dígitos!
Portanto, os métodos em Java também provém meios para a conversão entre bases decimais, também conhecidas como 'radix'.
Ambos métodos a seguir são usados principalmente no tratamento de números hexadecimais, ou seja, para radix=16.
-
Character.forDigit( numero, base ) -> converte o 'numero' para o character correspondente, na base 'base'
-
Character.digit( caractere, base ) -> converte o 'caractere' para número, na base 'base'
Java: Expressões Regulares (regex) em Java
Também conhecidas como regex (regular expressions), expressões regulares são sentenças com códigos que podem representar uma string, de forma genérica.
Por exemplo, o CEP é da forma: ddddd-ddd
Onde 'd' é um dígito qualquer, entre 0 e 9. Essa expressão representa toda a gama de CEP's em nosso país.
São usadas também para a criação de compiladores, através de expressões regulares bem complexas, que podem representar qualquer trecho de código, e acusar erro de sintaxe, caso o código escrito não case com as expressões regulares.
Em nossos aplicativos, usaremos expressões regulares para garantir que a entrada de dados esteja sob uma forma específica.
Por exemplo, caso necessitemos que o usuário digite um CEP e ele escrever:
dddd*-ddd
O que ele digitou não vai casar com a expressão regular que usamos para validar o CEP, pois o número de CEP não aceita asterisco.
As expressões regulares são uma ferramenta bem antiga, usada desde a época do Unix, e é usada nas mais diversas linguagens de programação, como em Perl, e em programas, como o sed.
Vamos a alguns exemplos:
\d -> representa qualquer dígitos
\D -> representa tudo, menos dígito
\w -> representa qualquer caractere de palavra
\W -> representa tudo que não seja um caractere de palavra
\s -> representa qualquer espaço em branco(" ", tab)
\S -> representa tudo que não seja espaço em branco
[a-z] -> representa qualquer letra minúscula do alfabeto
[A-Z] -> representa qualquer letra maiúscula do alfabeto
[a-zA-Z] -> representa qualquer letra do alfabeto, seja maiúscula ou minúscula
| -> representa o 'ou'. "a|b" casa com 'a' ou com 'b', ou com os dois
Vejamos alguns quantificadores
. -> substitui qualquer caractere
* -> o caractere anterior aparece nenhuma ou mais vezes
+ -> o caractere anterior aparece uma ou mais vezes
{a} -> o caractere anterior se repete 'a' vezes
{a,} -> o caractere anterior se repete pelo menos 'a' vezes
{a,b} -> o caractere anterior se repete entre 'a' e 'b' vezes
A representação de dígito é '\d', mas dentro de strings, para '\' valer, temos que escapá-la.
Logo, podemos representar um CEP como: "\\d\\d\\d\\d\\d-\\d\\d\\d"
Ou "\\d{5}-\\d{3}"
O método ficaria:
meu_cep.matches("\\d{5}-\\d{3}") -> retorna 'true' se o CEP for digitado correto, e 'false' caso errado
Para validar um nome, sabemos que o primeiro caractere é maiúsculo:
nome.matches("[A-Z][a-zA-Z]*") -> retorna 'true' se a primeira letra do nome for maiúscula, e 'false' caso errado
O Java provém alguns métodos para o uso de regex e substituições:
std = std.replaceAll("a", "b") -> substitui todas as ocorrências de 'a' por 'b'
Também podemos usar expressões regulares no método 'replaceAll':
std = std.replaceAll("\\w+","a") -> substitui todas as palavras por 'a'
std = std.replaceFirst("\\d","a") -> substitui a primeira ocorrência de um dígito por 'a'
String[] pedacos = std.split("a") -> divide a string 'std' em partes, cujo separador é 'a' e armazena no vetor de strings 'pedacos'. Por exemplo, para separarmos as palavras de uma string, usamos "," ou ",\\s*" como separador
É importante frisar que, nos vários métodos e classes para o tratamento de regex, use a classe String.
Se usar a StringBuilder terá erros.
Caso queira prosseguir no mundo das regex e em Java, esse package possui duas classes que lhe serão bastante úteis:a Pattern, para usar uma expressão regulas e a Matcher, que também usa uma expressão regular mas também uma CharSequence, onde você irá procurar o padrão para casar.
Para expressões regulares que serão usadas somente uma vez, se recomenda usar o método static 'matches' da Pattern. Porém, se for usada mais de uma vez, se aconselha a usar o método static 'compile', que retorna um objeto do tipo Pattern, onde posteriormente se pode chamar o método 'matcher'.
A classe Matcher também provém o método 'matches' e faz a mesma coisa, porém não recebe argumentos. Ela age por encapsulamento no objeto do tipo Matcher.
Essa classe também possui outros métodos bastante úteis, como o 'replaceFirst', 'replaceAll', 'find' , 'lookingAt' etc, que, pelo nome, é possível saber o que cada um faz.
Tenha em mente que as possibilidades das expressões regulares são inúmeras e ilimitadas. Compiladores são feitos usando expressões regulares! Isso por si só já mostra o poder das regex.
Por isso, não entrarei tão afundo no assunto.
Tendo estudado esse artigo, você saberá as possibilidades das Expressões Regulares, e irá se lembrar delas caso um dia necessite delas.
Expressões Regulares são universais. Você pode aprender e usar em vários ramos e linguagens.
Eu mesmo já falei aqui em outros artigos, como no de sed. Lá está até mais completo, se eu falasse mais aqui, estaria simplesmente repetindo o que está escrito lá.
Portanto, dê um pulinho no artigo de Regex da parte de 'sed', é uma continuação daqui:
http://programacaoprogressiva.blogspot.com.br/2012/07/sed-parte-vi-expressoes-regulares.html
Regex é assunto pra livros! E é isso que vou indicar, um site de um brasileiro, o Aurelio Jargas, mestre em Expressões Regulares que publica e divulga GRATUITAMENTE seu material!
http://aurelio.net/regex/
Vale ressalvar que o java possui um pacote (package) voltado para expressões regulares.
Vale uma conferida na documentação:
java.util.regex
