As classes servem para definir estrutura e comportamento. Na linguagem Java as classes constituem um dos mecanismos de definição de tipos. Os oito tipos primitivos estão disponíveis na linguagem, o programador através da definição de classes define novos tipos. Outro mecanismo de definição de tipo em Java é a interface, isto será visto mais à frente. É utilizando as classes ou suas instâncias que podemos compor programas dentro do paradigma denominado Programação Orientada a Objetos. A estrutura corresponde, tipicamente, a variáveis ou campos e o comportamento corresponde a métodos. O papel dos construtores, já discutido no item String, será discutido de forma ainda mais abrangente mais a frente. Por agora iremos somente repetir que os construtores servem, tipicamente, para definir a iniciação (ou inicialização!) das instâncias. As classes podem ter centenas ou até mesmo milhares de linha de código fonte. Nossos exemplos aqui são artificiais e curtos para simplificar os conceitos. Também para efeito de simplificação, não discutimos várias opções de definição e uso de classes.
A linguagem Java possui várias classes pré-definidas tais como a classe String. O programador além de poder utilizar as classes pré-definidas pode também definir classes. Em Java a capacidade de definir classes corresponde à capacidade de definir tipos. O método main que é o ponto de origem do fluxo de controle deve ser declarado dentro de uma classe. Até este ponto da disciplina esta classe tem sido referida como sendo uma classe que tem o mesmo nome do arquivo onde fica o código fonte do programa. Agora veremos que um arquivo fonte em java pode conter não só a classe que contém o método main, mas também outras classes. É possível organizar o código fonte de um programa em linguagem Java em vários arquivos, mas isto não será discutido aqui.
O programa abaixo tem como objetivo ilustrar a definição de uma classe. Uma classe é definida utilizando-se a palavra chave class seguida do nome da classe e da definição dos membros dentro de um bloco definido por abre e fecha chaves. No exemplo abaixo a classe C não define nenhum membro e a classe programa define apenas o método main. Esta classe C não tem nenhuma utilidade a não ser demonstrar qual é a menor definição possível para uma classe.
class programa{
O programa acima define duas classes. A classe programa cumpre a burocracia de conter o método main que é a origem do fluxo de controle. A classe C corresponde à menor definição possível, não contém nenhum membro. A definição de uma classe define um tipo. A definição da classe C define o tipo C. No programa acima vemos dois objetos do tipo C serem instanciados. Vamos referir à classe programa como sendo uma classe cliente da classe C, pois o método main define variáveis do tipo C. Um primeiro objeto é instanciado na iniciação da variável v1. O operador new cria um objeto e retorna a referência. A expressão que segue o operador new corresponde a chamada de um construtor. No programa acima a classe C não define construtores (isto será visto mais a frente), o compilador Java provê um contrutor default para a classe C. O segundo objeto é instanciado no comando de atribuição da variável v2. A tentativa de impressão de uma referência a qualquer objeto corresponde a um mecanismo de controle de fluxo que será explicado mais a frente na disciplina. O programa acima imprime duas instâncias de String, correspondentes a representações de dois objetos da Classe C.
Toda definição de classe estabelece uma relação de herança entre a classe a ser definida e uma outra classe. O assunto herança será visto mais à frente na disciplina, mas aqui enfatizamos que existe apenas uma classe que não tem relação de herança com outras classes. Esta classe única é considerada a "mãe" de todas as classes, é uma classe pré-definida na linguagem Java e é denominada Object. Uma definição de classe que não explicita a relação de herança corresponde à definição de uma classe que é "filha" direta da classe Object. Ou seja a definição:
class C{}
é uma forma abreviada da definição
class C extends Object{}
Falando de outra maneira, toda definição de classe corresponde a estender a definição de alguma outra classe e a única classe que não estende outras classes é a classe pré-definida Object. Podemos construir uma hierarquia de classes ligadas pelo mecanismo de extensão. A "raiz" desta hierarquia é a classe Object. As outras classes ficam ligadas direta ou indiretamente à classe Object.
O programa abaixo mostra uma classe com dois campos.
class programa{
public static void main(String[] args){
D v1, v2;
v1=new D();
v2=new D();
v1.i=10; v1.d=1.0E2;
v2.i=200; v2.d=3.0;
System.out.println("i de v1:"+v1.i+" d de v1:"+v1.d+" i de v2:"+v2.i);
}
}
class D{
int i; double d;
}
O programa acima define duas classes A classe programa contém um membro, o método estático main. A classe D define duas variáveis ou campos: um campo do tipo inteiro e um campo do tipo double. No método main são instanciados dois objetos do tipo D. As referências para os dois objetos são armazenadas nas variáveis v1 e v2. No caso da definição acima os campos i e d são variáveis de instância. Cada instância da classe D tem seus campos i e d distintos, ou seja, existirão tantos i e d quantas forem as instâncias da classe D. O acesso aos membros, conforme já discutido no item Arranjo e também no item String, é feito através do operador de seleção - um ponto: '.' aplicado sobre uma referência ou ao nome de uma classe. Um campo ou variável pode ser definida como sendo uma variável de classe (e *não* ser uma variável de instância!) prefixando a definição com a palavra chave static. Assim na definição abaixo existirão apenas dois campos não importando quantas instâncias da classe E forem criadas:
class E{
static int i;
static double d;
}
As classes abaixo tentam modelar a estrutura e comportamento de um dado com seis faces numeradas de 1 a 6 e que pode ser lançado.
A classe Cdado abaixo utiliza o método Math.random() para gerar um valor pseudo-aleatório, do tipo double, entre 0 e 1. Este valor entre 0 e 1 é convertido para um valor inteiro entre 1 e 6. A classe abaixo permite às classes clientes acessar um campo do tipo inteiro para armazenar o valor da face corrente. O método estático ou método de classe gerarFace() foi modificado pela palavra chave private. Um método ou campo private só pode ser utilizado internamente à definição da classe, ou seja as classes clientes não têm acesso aos método e campos private. Um método pode ser modificado pela palavra chave public. Um método public pode ser acessado pelas classes clientes.
class programa{
A utilização da classe Cdado certamente não é obrigatória na construção de um programa que lida com dados, o programa abaixo mostra a modelagem de um dado utilizando variáveis do tipo inteiro e um método:
É importante notar que a classe é um mecanismo que suporta abstrações mais expressivas do que o método. O método dá suporte a abstrações de comportamento ou abstrações funcionais. A classe permite não só as abstrações de comportamento, mas também abstrações de estado. Compare os dois programas acima. A estrutura do programa que usa classe dá suporte ao modelo de um dado, enquanto que o programa que tem apenas o método main não separa a modelagem, a definição, o uso do conceito de dados.
Por ser um mecanismo muito poderoso e expressivo a questão de como projetar classes não é um tema simples. Observe a classe Cdado no programa abaixo.
class programa{
No programa acima a classe Cdado não permite mais o acesso ao campo
face. Uma classe cliente deve obter o valor da face através de um método. A principal vantagem desta implementação é que uma classe cliente não pode fazer atribuições ao campo face.Conforme já visto no caso particular da classe String, os construtores funcionam como métodos mas não podem ser invocados da mesmo forma como podem ser os métodos. Os contrutores podem ser invodados utilizando o operador
new na hora da construção de uma instância de uma classe. O identificador de um construtor corresponde ao identificador de sua classe. A distinção dos contrutores de uma mesma classe ocorre em função de seus parâmetros. No programa abaixo adicionamos um construtor à classe Cdado. O construtor adicionado permite ajustar qual é o valor inicial da face (não é verificado se o valor está entre 1 e 6!):class programa{
O compilador Java disponibiliza para o programador a palavra chave this. A palavra chave
this só pode ser usada dentro de construtores e métodos de instância. A palavra chave this funciona como se fosse uma variável contendo a referência da instância que está sendo criada ou através da qual foi feita a chamada do método. Existem várias situações onde o uso da palavra chave this é bastante útil. No programa abaixo a palavra chave this é usada para desambiguar o nome do parâmetro e o nome do campo no contrutor:
A classe abaixo lida com instâncias de CPF (conforme visto anteriormente). Observe o uso de métodos estáticos e não estáticos no trecho de programa abaixo.
class CPF{
private static int digsVer(int n){
Exemplos de utilização da classe CPF:
System.out.println(CPF.digitosVerificadoresStr(987654321)); /*metodo estatico*/
Um programador pode utilizar a classe CPF de forma indevida:
Exercícios: