Ir para conteúdo

Tutorial Java


Loost

Posts Recomendados

[Tutorial]Programação em JAVA

A Linguagem de

Programação Java

 

 

 

 

 

 

Índice

 

1.Introdução 2

2.Histórico 2

3.As palavras que definem Java 3

4.Tipos primitivos 6

5.Tipos Compostos 6

6.Expressões 7

7.Variíveis 8

8.Comandos 9

9.Java e a Orientação a Objetos 10

1.Classes 10

2.Objetos - Instâncias de uma Classe 11

3.Construtores 12

4.Métodos / Mensagens 13

5.Destrutores 14

6.Herança 15

7.Polimorfismo 17

8.Controle de Acesso 18

9.Variíveis e Métodos de Classe 19

10.Classes e Métodos Abstratos 19

11.Interfaces 20

10.Concorrência (Threads) 21

11.Tratamento de Exceções 22

12.Conclusões 24

13.Bibliografia 25

 

 

 

Introdução

 

Nesse trabalho estaremos falando sobre a linguagem de programação Java. É uma

linguagem singular dentre todas outras jí que é a única que chamou a atenção de pessoas

fora do mundo técnico, o suficiente para que existissem reportagens de mais de 10 minutos

em cadeias de divulgação de massa como a CNN.

 

Apesar de todo o alarde, Java não é uma linguagem tão revolucioníria quanto se clama.

Muitos dos conceitos implementados estão presentes em linguagens orientadas a objeto

modernas, como Eiffel e Modula-3. O que difere Java das outras linguagens é que ela

promete portabilidade total e a possibilidade de criação de pígina WWW interativas para a

Internet.

 

Java é uma linguagem definida como simples, orientada a objetos, robusta e segura, de

arquitetura neutra, portível, interpretada, de alta performance, com suporte a concorrência

e dinâmica. Discutirmos o que cada uma dessas palavras querem dizer e olharemos Java

como qualquer outra linguagem de programação, à luz dos conceitos e tópicos em

linguagens de programação.

 

Java parece ser uma linguagem de programação muito útil e de grande produtividade.

Bjarne Stroustrup, o idealizador de C++: "C faz com que seja muito fícil atirar nos próprios

pés. C++ faz com que isso se torne mais difàcil, mas quando você consegue, destrói toda a

perna." Acreditamos Java que torne muito difàcil atirar na própria perna.

 

Histórico

 

Java originou-se como parte de um projeto de pesquisa que visava a criação de um

software avançado que atendesse a uma extensa variedade de maquinírio de redes e

sistemas embutidos. O objetivo inicial era desenvolver um ambiente operacional pequeno,

confiível, portível, distribuàdo e que operasse em tempo real. Inicialmente, a linguagem

escolhida foi C++. Porém, a com o passar do tempo, as dificuldades encontradas com C++

aumentaram até o ponto em que os problemas poderiam ser melhor endereçados se fosse

criada uma linguagem completamente nova. As decisões de arquitetura e desenho da

linguagem foram inspiradas em linguagens como Eiffel, SmallTalk, Objective C, e Cedar /

Mesa. A sintaxe tem suas raàzes claramente definidas em C e C++.

 

Deste modo, Java foi projetada para atender a vírios requisitos desejíveis em uma LP,

como por exemplo, confiabilidade, devido ao seu gerenciamento de memória, o que resulta

em um ganho de eficiência; redigibilidade, por eliminar alguns conceitos de C e C++ que

dificultavam nesse sentido; reuso de código.

 

 

 

As palavras que definem Java

 

A documentação de Java [1] fornecida pela Sun utiliza algumas palavras para definir a

linguagem, faremos uma cràtica a respeito de cada uma delas.

 

1) Simples

 

Uma caracteràstica marcante de Java é a simplicidade da linguagem, que pode ser

programada sem um treinamento intenso, ou larga experiência anterior. Programadores de

C++ terão uma rípida compreensão de Java devido à sua semelhança com Java. Java foi

criada tão unida quanto possàvel a C++ para se criar sistemas mais compreensàveis. Java

omite muitos termos pouco usados e operações confusas em C++ que trazem mais

complicações que benefàcios.

 

A versão Java, é na verdade, uma versão mais limpa de C++. Nela não hí ponteiros

aritméticos, estruturas, uniões, sobrecarga de operadores, classes bísicas virtuais e etc.

 

Ao mesmo tempo que é uma linguagem simples, existem aspectos sutis na construção de

programas para o mundo real, dada a atual situação da biblioteca de classes e da não muito

avançada programação visual. Com o tempo os recursos tendem a aumentar mais e mais

com a criação de bibliotecas e desenvolvimento de ambientes.

 

2) Orientada a objeto

 

A orientação a objeto é uma técnica que enfoca o modelo do dado (Objeto) e sua interface.

As facilidades de orientação objeto de Java são essencialmente as de C++. As operações

de OO de Java são comparíveis com as de C++. A maior diferença entre Java e C++ estí

em múltipla herança, pois Java parece ter encontrado uma solução melhor.

 

Os programadores podem reutilizar código, utilizar bibliotecas de terceiros com proteção e

encapsulamento, e adicionar funcionalidade às jí existentes.

 

Java possui tipagem forte, o que pode ser uma vantagem para programadores vindos de

uma linguagem como Smalltalk.

 

3) Robusta e Segura

 

O projeto de Java visou a construção de uma linguagem para escrita de programas

confiíveis. Java enfatiza em muito a checagem em tempo de compilação de possàveis

problemas, e em tempo de execução realiza a checagem dinâmica, e eliminação de situações

que podem gerar erros.

 

A maior diferença entre C/C++ e Java é que Java por definição da própria linguagem

elimina uma gama de erros que geralmente surgem em C/C++ quando utilizamos ponteiros.

 

Java fornece a funcionalidade de ponteiros sem que seja necessírio criar estruturas para tal.

Estas estruturas eliminam a possibilidade de sobreposição de dados na memória

corrompendo dados.

 

Java é designada para operar em ambientes onde segurança é extremamente importante,

como o ambiente de rede.

 

4) Arquitetura Neutra

 

Java é projetada para suportar aplicações que serão distribuàdas para os diversos ambientes

de trabalho em rede. Em tais ambientes aplicações devem ser capazes de executar em uma

grande variedade de míquinas. Nas vírias plataformas de hardware, aplicações devem

executar sobre um universo de sistemas operacionais e operar interagindo com outras

linguagens de programação. Isto é possàvel graças à arquitetura idealizada, onde o

compilador Java gera um código binírio para uma JVM (míquina virtual Java) - uma

arquitetura neutra e portível. Assim, qualquer míquina que possa rodar uma míquina virtual

Java pode rodar programas jí compilados sem requerer a menor mudança.

 

A natureza "interpretada" de Java resolve ambos os problemas de distribuição biníria e de

versão.

 

5) Portível

 

Diferente de C/C++, não hí aspectos de implementação dependentes da especificação. O

tamanho dos tipos primitivos são especificados, bem como os seus comportamentos

aritméticos. Um inteiro tem sempre 32 bits, em C/C++ tem o tamanho da palavra da

míquina.

 

Tendo fixado o tamanho dos tipos numéricos, é eliminada a maior causa de dores de

cabeça. Os dados binírios são armazenados em formato fixo, eliminando a confusão de big

endian e little endian.

 

6) Interpretada

 

O interpretador Java pode executar o código binírio Java diretamente em alguma míquina

para o qual ele tenha sido portado. Em um ambiente interpretado tal como o sistema Java, a

fase de linkagem de um programa é simples, incremental e leve. O processo de

desenvolvimento pode ser muito mais rípido.

 

Esta é uma vantagem enquanto desenvolvemos uma aplicação, mas ela é claramente mais

lenta que um código compilado.

 

7) Alta Performance

 

Enquanto a performance do código binírio interpretado é geralmente adequado, hí

situações onde maior performance é requerida. O código binírio pode ser traduzido em

tempo de execução para outro código de míquina para ser executado em uma aplicação de

uma CPU particular.

 

Espera-se que em breve teremos compiladores de Java também para míquinas reais.

 

8) Dinâmica

 

Java é mais dinâmica que C/C++. Bibliotecas podem livremente somar novos métodos e

instâncias de variíveis sem nenhum efeito em seus clientes. Em Java descobrir o tipo de uma

instância em tempo de execução é algo extremamente simples.

 

9) Concorrência (Threads)

 

Concorrência é a habilidade para um programa fazer mais de uma coisa por vez. Os

benefàcios de multiprocessamento são melhor sensibilidade interativa e comportamento em

tempo real.

 

O threads em Java têm a capacidade para pegar as vantagens de sistemas

multiprocessadores se o sistema operacional faz o mesmo. Por outro lado implementações

threads em plataformas diferem amplamente, e Java não faz esforço para ser uma

independente de plataforma neste caso.

 

Java suporta multiprocessamento no nàvel de linguagem com a adição de sofisticada

sincronização.

 

Tipos primitivos

 

Os tipos inteiros em Java são: byte (8 bits); short (16 bits); int (32 bits) e long (64 bits). O

tipo byte (8 bits) em Java veio a substituir o antigo tipo char (C e C++). Não hí nada em

Java para especificar um tipo inteiro sem sinal como em C e C++.

 

O tipo de ponto flutuante em Java pode ser o float (32 bits) e o double (64 bits). Os

operadores aritméticos seguem o padrão IEEE 754 1.

 

O tipo char declarado em Java define um caracter de 16 bits, seguindo o padrão Unicode

(um código anílogo ao ASCII que define caracteres para todas as lànguas, como um código

universal).

 

Os tipos booleanos em Java, diferentemente dos similares em C, não podem ser convertidos

para nenhum tipo numérico. Valores booleanos em Java assumem apenas true ou false.

 

Todos os tipos primitivos têm valores default. Todos os tipos numéricos são inicializados

para 0, o tipo caracter é inicializado para o Unicode 0 e variíveis do tipo booleano são

inicializados para false.

 

Tipos Compostos

 

As uniões disjuntas, produtos cartesianos e conjuntos de potência foram eliminados em

Java. Os dois primeiros foram deixados de lado pois sendo Java uma linguagem orientada a

objetos, as classes substituem essas construções. Jí os conjuntos de potência são supridos

como classes da API (a classe BitSet). Tipos recursivos são suportados.

 

Todos os tipos em Java que não são primitivos são tipos de referência a objetos, incluindo

arrays.

 

Ao contrírio de C e C++, os arrays em Java são objetos de primeira classe. É possàvel ao

programador declarar arrays de qualquer tipo, e ainda declarar arrays de arrays para

vetores multidimensionais.

 

Um array em Java, como qualquer outro objeto deve ser alocado dinamica e explicitamente

com o operador new. Uma vez que o objeto é alocado e tem um certo tamanho, este não

muda. Tais procedimentos garantem mais confiabilidade ao código escrito em Java, jí que

ficam eliminados os erros por violação de memória e perda de dados durante a execução

do programa. Se o programador viola os limites de um vetor, é gerada uma exceção.

 

De acordo com as definições descritas acima, pode-se classificar os arrays em Java como

semi-dinâmicos, semelhante aos arrays em Ada.

 

O acesso aos elementos do array segue o estilo de indexação de C. Porém, no momento

em que é feita qualquer referência a um desses elementos é checado o tamanho do array

para se verificar se não estí sendo tentado um acesso a um àndice fora do tamanho do

array. Caso isso ocorra, é gerada uma exceção. Arrays não são a única implementação de

vetores existente em Java. A API fornece a classe Vector que possibilidade a construção de

vetores dinâmicos. Vale lembrar que o acesso a essa classe Vector é toda feita através de

métodos e não de operadores de arrays.

 

Strings em Java são objetos, e não "pseudo-arrays" como em C. Existem duas classes na

API de Java que implementam arrays: StringBuffer e String. A definição da linguagem define a

sobrecarga do operador de adição para a utilização com a classe String, e de literais do tipo

String.

 

Expressões

 

Uma expressão em Java pode produzir três resultados:

 

um valor

uma variível (uma referência a uma variível)

nada (a expressão é void)

 

As expressões literais providas são de números inteiros e de ponto flutuante, caracter,

booleano e string. Não são suportados agregados, e as expressões condicionais são

suportadas através do operador ternírio (o mesmo de C).

 

Uma expressão produz nada se e somente se ela é uma chamada a um método de tipo de

retorno void. Tal expressão pode ser usada somente como uma expressão comando,

porque qualquer outro contexto em que uma expressão possa ser usada requer que a

expressão produza um valor ou uma variível. Uma expressão que é uma chamada a um

método pode ter o seu valor de retorno silenciosamente descartado. Além disso, a

execução de expressões pode produzir efeitos colaterais, pois além de chamadas a funções

e atribuições, expressões podem conter operadores como ++ e --.

 

Variíveis

 

Em Java, como foi dito anteriormente, qualquer variível que não seja de um tipo primitivo é

uma variível de referência a um objeto. Daà concluàmos que somente as variíveis de tipos

primitivos são realmente storables. Todas as variíveis de referência tem campos que podem

ser atualizados seletivamente.

 

Além de todas variíveis de tipos compostos (não primitivos) serem referências, estas tem

ter seus objetos alocados explicita e dinamicamente através do operador new, e não é

permitido o uso de variíveis não inicializadas. Isto serí discutido adiante no tópico que se

refere a orientação a objetos. Vale lembrar que todos os problemas de ponteiros foram

eliminados, através da ausência de ponteiros (e consequentemente aritmética de ponteiros) e

com a existência do garbage collector. As variíveis de referência suprem a funcionalidade

ponteiros para a criação de listas encadeadas e outras estruturas de dados que dependem

da noção de ponteiro, apesar de não poderem ser de-referenciadas. As variíveis de

referência a objetos têm valor default null, ou seja, não apontam para nenhum objeto.

 

Java faz o maior esforço para fazer a checagem de tipos em tempo de compilação. Uma

variível de referência a um objeto pode guardar uma referência a um objeto cujo tipo em

tempo de compilação pode ser convertido sem o uso de type casting. Veremos isso

adiante. Java utiliza equivalência nominal, o que era de se esperar de uma linguagem

orientada a objetos, jí que permite a construção de tipos abstratos em que a implementação

(estrutura interna dos dados) é ignorada.

 

Em Java, as variíveis locais são declaradas e alocadas dentro de um bloco e são

descartadas na saàda do bloco. Parâmetros de métodos também são consideradas variíveis

locais.

 

Variíveis constantes são definidas com a palavra chave final. Estas devem ser inicializadas

da declaração, e não poderão ser modificadas (amarrações para valor). Podem também ser

declaradas como volatile, o que quer dizer que sabe-se que elas serão modificadas

assincronamente. Isso quer dizer que as variíveis que são declaradas como volatile serão

carregadas e salvadas na memória a cada uso, de forma que possam estar coerentes em

multiprocessadores.

 

Comandos

 

Os comandos de controle de fluxo são extremamente semelhantes aos de C, com exceção

do goto, que apesar de ser uma palavra reservada na linguagem não é implementado. Todos

os comandos são sequenciais.

 

Os comandos de seleção são o if e o switch, sendo o if de caminho simples (com else) e o

switch de múltiplos caminhos:

 

if ( Expressão ) bloco_de_comandos else bloco_de_comandos

 

switch ( Expressão ) {

 

case ExpressãoConstante : bloco_de_comandos

 

case ExpressãoConstante : bloco_de_comandos

 

default : bloco_de_comandos

 

}

 

Os de iteração são while, do while e for.

 

while ( Expressão ) bloco_de_comandos

 

do bloco_de_comandos while ( Expressão );

 

for ( ExpressãoInicialização; Expressão; ExpressãoAvanço );

 

Os comandos de escapes englobam o return, o continue, o break e o throw.

 

O return é utilizado para interromper a execução de um método, especificando a expressão

de retorno, ou não, se o método não for void.

 

O break transfere o controle para o final da iteração no qual estí inserido, ou encerra a

execução de um switch. O continue transfere o controle para o ponto de continuação do

loop de um comando de iteração. Ambos break e continue podem ser acompanhados de

identificadores que especificam a qual dos loops externos estamos nos referindo, jí que

existe a possibilidade de amarrar labels a comandos de iteração.

 

O throw "dispara", "joga" uma exceção, desviando o fluxo para o bloco de tratamento de

exceções ou interrompendo o programa.

 

Além destes comandos existem os chamados guarding statements que estabelecem

condições para o tratamento de exceções e multithreading. São eles o try, o synchronized,

o catch e o finally (serão explicados posteriormente nas seções de tratamento de exceções e

concorrência).

 

Java e a Orientação a Objetos

 

A orientação a objetos tem provado seu valor nos últimos quase 30 anos, e é inconcebàvel que uma

linguagem de programação moderna não seja orientada a objetos. Na especificação da linguagem Java

[2] ela é definida como "uma linguagem orientada a objetos baseada em classes", e portanto provê essas

caracteràsticas mànimas, suficientes para possibilitar modularização, proteção e reutilização:

 

Encapsulamento - Ocultamento de Informações (Information Hiding) e modularidade (abstrações)

 

Polimorfismo - a mesma mensagem enviada a diferentes objetos resultam em um comportamento que

depende da natureza dos objetos recebendo a mensagem.

 

Herança - novas classes são definidas em termos de classes existentes, possibilitando o reuso de

código.

 

Amarração Dinâmica - os objetos são possivelmente amarrados em tempo de execução, o que quer dizer

que um objeto pode ser até passado por uma rede. A amarração dinâmica provê flexibilidade em tempo

de execução, e é a chave para o polimorfismo real.

 

As caracteràsticas da orientação a objetos de Java são similares às de C++. A maior diferença entre elas é

que Java parece ter achado uma boa solução para substituir a herança múltipla. Todos os tipos de

dados, com exceção dos primitivos, são classes.

 

Classes

 

Uma classe é uma construção de software que define variíveis de instância e métodos de um objeto.

Uma classe não é um objeto, uma classe é a "definição" de como um objeto se comportarí quando este

for criado ou instanciado.

 

A declaração bísica de uma classe em Java é a seguinte:

 

class nome {

 

// informações sobre a classe

 

}

 

Abaixo temos um exemplo de uma classe muito simples:

 

class Pessoa {

 

public String Nome; /* variível de instância */

 

public String Telefone; /* variível de instância */

 

}

 

Objetos - Instâncias de uma Classe

 

Para utilizarmos uma classe nós devemos instanciar objetos. Em Java isto quer dizer que criaremos uma

referência para um objeto (não existe a idéia de um tipo expandido como em Eiffel [6] e amarrações para

valores são possàveis somente para tipos primitivos) - com um fragmento de código como este:

 

Pessoa meuAmigo;

 

// declara uma variível que faz referência a um objeto da classe Pessoa

 

meuAmigo = new Pessoa();

 

// aloca uma instância de um objeto da classe Pessoa

 

É possàvel acessar uma variível do objeto de uma classe através do nome da variível, qualificando o

mesmo com o nome do objeto, da mesma forma com que acessamos uma estrutura em C. Por exemplo:

 

meuAmigo.Nome = "Fulano de Tal";

 

// atribuição à variível de instância Nome da variível de objeto meuAmigo

 

Todo objeto deve ser alocado dinamica e explicitamente através do operador new, e deve ser inicializado

antes de ser usado. Uma variível de objeto que não se refere a nenhum objeto deve ter o valor null. Um

erro de tempo de execução ocorre se for utilizado um ponteiro não inicializado ou null.

 

Um objeto em Java pode ser visto de forma aníloga a ponteiros para objetos em C++. Se você copia uma

variível para outra, ambas se referem ao mesmo objeto, são "ponteiros" para o mesmo objeto. A

diferença reside que, em C++, é muito fícil se criar ponteiros "ruins" (usando aritmética de ponteiros,

não existente em Java) ou termos problemas de objetos e referências pendentes. O garbage collector

garante que tais problemas não ocorrerão.

 

Construtores

 

Ao declarar uma classe em Java, opcionalmente definimos construtores. Construtores fazem a

inicialização de objetos ao serem instanciados. Os construtores são métodos com o mesmo nome da

classe e que não têm tipo de retorno especificado.

 

Quando um objeto é criado (instanciado), o método construtor apropriado é automaticamente invocado,

para, por exemplo, dar um estado inicial ao objeto. Se uma classe não contém declarações de

construtores, então um construtor default é fornecido automatica e implicitamente.

 

Um construtor só pode ser chamado com a palavra chave new, e não pode ser aplicado a um objeto

existente.

 

Exemplo:

 

class Pessoa {

 

public String Nome; /* variível de instância */

 

public String Telefone; /* variível de instância */

 

Pessoa() { /* construtor default */

 

Nome = "";

 

Telefone = ""; }

 

Pessoa(String N, String T) {

 

Nome = N;

 

Telefone = T; }

 

}

 

Pessoa outroAmigo = new Pessoa("Fulano 2","444-4444");

 

// acima temos um exemplo de utilização de construtor:

 

outroAmigo.Pessoa("XXX","222");

 

/* ERRO! construtores só podem ser utilizados para

 

instanciar elementos com new */

 

Métodos / Mensagens

 

Métodos são a forma com que os objetos interagem entre si. Na orientação a objetos, para se atingir

encapsulamento e proteção, todas as interações entre objetos (ditas mensagens) devem ser feitas

através dos métodos de uma classe. Não hí funções e procedimentos em Java, haja vista que a

programação procedural foi totalmente substituàda. Mesmo assim, a forma de se invocar métodos em

Java se assemelha à de funções em C e C++. Qualquer método pertence a uma classe, e deve ser definido

dentro do corpo da mesma.

 

Podemos como exemplo modificar a nossa classe Pessoa:

 

class Pessoa {

 

private String Nome; /* variível de instância */

 

private String Telefone; /* variível de instância */

 

Pessoa() { /* construtor default */

 

Nome = "";

 

Telefone = ""; }

 

Pessoa(String N, String T) {

 

Nome = N;

 

Telefone = T; }

 

public void mudaNome(String Nome) {

 

this.Nome = Nome;

 

}

 

public String leNome() {

 

return Nome;

 

}

 

public String leTelefone() {

 

return Telefone;

 

}

 

}

 

No exemplo acima as variíveis de instância Nome e Telefone foram precedidas pela palavra chave

private, o que quer dizer que essas variíveis só poderão ser acessadas por métodos da classe à qual

pertencem.

 

No exemplo acima utilizamos a palavra chave this. Essa variível se refere ao objeto que estí recebendo a

mensagem, ou seja, o objeto no qual o método estí sendo aplicado (Em C++ this é o ponteiro para o

objeto que recebe a mensagem, e em Java é a referência para esse objeto).

 

Invocando esses métodos qualificamos o nome do método com o nome do objeto:

 

Pessoa meuAmigo = new Pessoa("Fulano de Tal","999-9999");

 

meuAmigo.mudaTelefone("222-2222");

 

/* invocamos o método mudaTelefone da classe Pessoa sobre o objeto meuAmigo,

 

ou melhor, mandamos uma mensagem mudaTelefone para o objeto meuAmigo. */

 

Java permite a sobrecarga independente de contexto de métodos, mas não permite a sobrecarga de

operadores. Para sobrecarregarmos um método basta declararmos outro método de mesmo nome com

uma assinatura diferente (tipo e ordem dos parâmetros formais).

 

Métodos podem ser declarados nativos, utilizando a palavra chave native. Isto quer dizer que eles serão

implementados de forma dependente de plataforma, por exemplo em C ou assembly. Java trata um

método nativo como outro qualquer.

 

Destrutores

 

Além de construtores, a linguagem provê a construção de métodos destrutores. Os destrutores são

métodos responsíveis por liberar os recursos alocados durante a vida de um certo objeto. Eles são

invocados automaticamente pelo garbage collector, imediatamente antes deste "recolher" um objeto.

Esse método deve se chamar finalize.

 

/* Fecha um arquivo aberto quando o garbage collector atua */

 

protected void finalize() {

 

try {

 

file.close();

 

} catch (Exception e) {

 

}

 

}

 

O exemplo acima mostra um tàpico caso em que o seu objeto deve liberar os recursos alocados do

sistema operacional, como por exemplo fechar descritores de arquivos, liberar contextos em ambientes

gríficos, etc.

 

Herança

 

A herança é um mecanismo através do qual novas e mais especializadas classes podem ser definidas em

termos de classes pré-existentes. Um exemplo é a criação de uma classe Funcionario, como uma

subclasse de Pessoa. Em Java:

 

class Funcionario extends Pessoa {

 

// Novos métodos e instâncias podem ser inseridos aqui

 

}

 

class Secretaria extends Funcionario {

 

...

 

}

 

class Gerente extends Funcionario {

 

...

 

}

 

A palavra chave extends diz que Funcionario é uma subclasse de Pessoa, chamada de classe base.

Dizemos que Funcionario herda as caracteràsticas de Pessoa, jí que este reutilizarí boa parte do código e

funcionalidade descrita na classe Pessoa.

 

class Funcionario extends Pessoa {

 

private float salario;

 

public Funcionario(String Nome, String Telefone,

 

float salario) { super(Nome,Telefone);

 

this.salario = salario;

 

}

 

public Funcionario aumenta_salario(float aumento) {

 

salario = salario + aumento;

 

}

 

}

 

A classe Funcionario herda as variíveis de instância Nome e Telefone, e os métodos (mudaTelefone,

leTelefone, leNome) da classe Pessoa, com a exceção dos construtores. Nesse caso devemos criar novos

construtores que façam, opcionalmente, na primeira linha do corpo, uso da palavra chave super para se

referir ao construtor da superclasse, como no construtor do exemplo acima. Se a subclasse não tem

definições de construtores, o construtor super() é invocado, ou seja, o construtor default da classe

imediatamente superior.

 

Toda classe em Java é subclasse (não necessariamente imediata) da classe Object. Quando não

utilizamos a palavra chave extends numa declaração de classe, implicitamente esta se torna subclasse

imediata de Object.

 

Uma referência a um objeto da superclasse B (ou classe base) pode também ser referência de uma

subclasse S de B. Jí o contrírio exige o uso de type casting, ou seja a coerção (coercion) deve ser

explàcita.

 

Object o;

 

Pessoa fulano;

 

Funcionario contador1 =

 

new Funcionario("Fulano de Tal","222-2222");

 

Pessoa meuAmigo =

 

new Pessoa("Amigo da Onça","555-5555");

 

fulano = contador1; // ok! um funcionírio é uma pessoa

 

o = contador1; // a classe Funcionario é derivada indireta de Object

 

contador1 = meuAmigo;

 

// ERRO! meuAmigo é da classe pessoa, que é uma superclasse de Funcionario

 

contador1 = (Funcionario) meuAmigo;

 

// com a utilização de type casting esta conversão se torna possàvel

 

Se for necessírio modificar um método da classe superior, ou especializí-lo, é possàvel declarando-se um

método de mesma assinatura na subclasse. Não é possàvel sobrepor um método declarado como final.

Ao contrírio de C++, no qual as funções virtuais devem ser declaradas como tal, em Java, por default, as

funções são virtuais. Para acessarmos o método "sobreposto" (overrided) utilizamos o seletor super,

que é, na verdade uma referência à superclasse, equivalente a ((superclasse)this).

 

Java implementa herança simples, ao contrírio de C++ e Eiffel que implementam herança múltipla. Isso

quer dizer que uma classe só pode ter uma superclasse imediata. Isso pode, a primeira vista, parecer uma

caracteràstica limitante, mas em Java o conceito de Interfaces (presente também no Objective C como

protocols) faz com que a linguagem não perca o seu poder expressivo.

 

Polimorfismo

 

O polimorfismo é uma das caracteràsticas marcantes de linguagens orientadas a objetos. Ela permite que

a mesma mensagem enviada a diferentes objetos resultem em um comportamento que depende da

natureza dos objetos recebendo a mensagem, ou seja, permite a construção de abstrações que operem

uniformemente sob uma famàlia de tipos relacionados.

 

Quando uma mensagem é enviada para um objeto de uma subclasse em Java:

 

A subclasse checa se ela tem um método aquele nome e com exatamente os mesmos parâmetros. Se a

resposta for positiva, ela usa esse método.

 

Caso contrírio, Java envia a mensagem à classe imediatamente superior.

 

Se toda a hierarquia for caminhada sem que um método apropriado seja encontrado um erro em tempo de

compilação ocorre.

 

A amarração dinâmica é a chave para o polimorfismo. O compilador não gera código em tempo de

compilação para uma chamada de método, ele gera código para determinar que método chamar em tempo

de execução usando a informação de tipo disponàvel para o objeto.

 

Java não suporta abstrações genéricas, como o template de C++. Essa funcionalidade é implementada

com a utilização da classe Object. Uma clara diferença dessa falta é que ele permite uma flexibilidade

muito grande, que deve ser administrada corretamente pois pode ser perigosa. Vamos tomar como

exemplo uma lista de Object. Essa lista pode conter Funcionarios, Retangulos, Frutas, etc. Jí em C++ ou

Eiffel, na declaração do tipo genérico teràamos a criação de uma nova abstração especàfica para um certo

tipo. Tomando C++ como exemplo, criaràamos uma lista<Funcionario> e teràamos um novo tipo que

somente aceitaria Funcionarios.

 

Controle de Acesso

 

Ao declararmos uma classe em Java, indicamos o nàvel de acesso permitido para suas instâncias e

métodos. Existem quatro nàveis, e três modificadores: public, protected, e private. Definimos também o

nàvel de acesso da classe inteira, existindo apenas o nàvel public e o "friendly".

 

O nàvel public permite o acesso ilimitado, o protected significa que os campos são acessàveis somente

em subclasses, e o private que são acessàveis somente dentro da classe onde são declarados. O quarto

nàvel não tem um nome, mas é freqüentemente chamado de "friendly". Esse é o nàvel default. Ele indica

que os campos são disponàveis para todos objetos dentro daquele pacote (package).

 

Os packages em Java são coleções de classes que têm nomes hierírquicos. Ao utilizar um pacote, o seu

código deve incluir uma clíusula import, ou qualificar os métodos, variíveis e constantes utilizadas. Isso

permite que você distribua adequadamente suas "bibliotecas" de classes e que essa classes sejam

utilizadas em outros programas.

 

import java.net.*; // importa todas as classes do pacote java.net

 

import java.util.Bitset;

 

// importa somente a classe BitSet do pacote java.util

 

java.util.Date hoje = new java.util.Date();

 

/* utilizando a classe Date dentro de java.util, pacote que não

 

foi completamente importado */Variíveis e Métodos de Classe

 

Normalmente quando definimos variíveis em uma classe definimos variíveis de instâncias. Para cada

objeto criado uma nova variível de instância é criada, e utilizada apenas para representar o estado deste

objeto. Jí as variíveis de classe são compartilhadas por todos objetos de uma classe, sendo utilizadas

para representar o estado de toda uma classe. Jí os métodos de classe servem para manipular as

variíveis de classe, e não podem, de forma alguma manipular variíveis de instâncias, ou invocar

métodos de instâncias.

 

Para declarar variíveis ou métodos de classe, utilizamos o modificador static:

 

class Vendedor extends Funcionario {

 

private static float comissao = 2.5;

 

public void static mudaComissao(float c) {

 

comissao = c;

 

}

 

/* a comissão de todos os vendedores de uma loja é a mesma,

 

logo ela deve ser declarada como uma variível de classe */

 

}

 

Classes e Métodos Abstratos

 

Classes Abstratas são construções poderosas no paradigma da orientação a objetos. Elas têm grande

influência sobre a etapa de design, principalmente na concepção das relações de hierarquia entre as

classes.

 

Ao subirmos na hierarquia de herança, as classes se tornam cada vez mais gerais e provavelmente mais

abstratas. Hí um ponto em que elas são tão abstratas que as vemos como uma representação para

derivação de outras classes, ao invés de as observarmos como classes das quais objetos serão criados.

 

Métodos abstratos são aqueles que não podem ser implementados em classes tão abstratas, mas que

devem ser implementados em subclasses. Uma classes deve ser declarada abstrata se ela tem pelo menos

um método abstrato (os métodos abstratos eqüivalem às funções virtuais puras de C++ e às deffered

features de Eiffel)

 

Para declararmos métodos e classes abstratas devemos utilizar a palavra chave abstract. Classes

abstratas não têm construtores nem podem ser instanciadas.

 

class FormaGeometrica extends Object {

 

protected Ponto centro; // centróide da forma geométrica

 

public void mudaCentro(Ponto centro) {

 

this.centro = centro;

 

}

 

abstract void desenha(); // método abstrato }Interfaces

 

Uma declaração de uma interface declara um tipo que consiste num conjunto de métodos e constantes

sem especificar a sua implementação. Interfaces podem ser bons substitutos para classes abstratas sem

nenhuma implementação, e fornecem o poder da herança múltipla de interfaces, sem a confusão causada

pela herança múltipla de classes (que por sua vez contêm implementação). Uma interface pode herdar

suas caracteràsticas de diversas superinterfaces.

 

Quando uma classe é declarada ela pode opcionalmente fornecer uma lista de interfaces as quais são

implementadas por ela. Assim sendo, ela se torna responsível por fazer tal implementação. Uma

subclasse de uma classe que implementa alguma interface automaticamente herda a implementação

dessa interface.

 

Um exemplo claro é a implementação de uma interface Ordenavel:

 

public interface Ordenavel {

 

public int compara(Ordenavel a);

 

/* compara elementos, retornando positivo para maior que,

 

negativo para menor que, e zero para iguais */

 

}

 

/* classe que implementa a interface Ordenavel,

 

lembre-se que interfaces declaram novos tipos, logo podem

 

ser utilzados como parâmetros formais */

 

class Circulo extends FormaGeometrica

 

implements Ordenavel, ... {

 

private float raio;

 

public void desenha() {

 

...

 

}

 

public int compara(Ordenavel a) {

 

Circulo ca = (Circulo) a;

 

return raio - a.raio;

 

}

 

}

 

Concorrência (Threads)

 

A concorrência permite que vírias partes do seu software estejam rodando simultaneamente. Usuírios

de computadores sofisticados tornam-se impacientes ao fazer uma coisa de cada vez em seus

computadores pessoais.

 

Infelizmente os programadores que necessitam de concorrência em linguagens como C e C++ enfrentam

muitas dificuldades, jí que essas linguagens não suportam diretamente multithreading. Java o faz a

nàvel da linguagem e com classes apropriadas em sua API, com multitarefa pre-emptiva.

 

A nàvel da linguagem, métodos declarados synchronized não rodam simultaneamente. Tais métodos

rodam sob o controle de monitores que se asseguram que as variíveis permanecerão em um estado

consistente. Java faz o controle de locks automaticamente, sem que o usuírio precise se preocupar.

 

Para criarmos um objeto que rode em outra thread simplesmente derivamos a nossa classe da classe

Thread, ou implementamos a interface Runnable. A partir de então, sobrecarregamos o método run() com

o código desejado. Quando instanciamos um objeto dessa classe e invocamos o seu método start(), este

imediatamente retornarí e a aplicação ficarí sendo executada em background. Além da simplicidade de

se trabalhar com threads existe a possibilidade do controle de prioridades.

 

class PingPongThread extends Thread {

 

String mensagem;

 

int atraso;

 

PingPongThread(String mensagem, int atraso) {

 

this.mensagem = mensagem;

 

this.atraso = atraso;

 

start();

 

}

 

public void run() {

 

while (true) { // loop infinito

 

System.out.println(mensagem);

 

// imprimimos a mensagem

 

sleep(atraso); // geramos o atraso

 

}

 

}

 

}

 

new PingPongThread("ping",500);

 

new PingPongThread("pong",600);

 

Esse trecho de código cria dois objetos que rodarão em diferentes threads. As mensagens de "ping" e

"pong" serão intercaladas na tela.

 

Tratamento de Exceções

 

O tratamento de exceções permite que um erro seja tratado pelo programador, ou seja, que ele tome

decisões acerca de erro ocorrido. A missão principal do tratamento de exceções é transferir o controle do

ponto onde o erro ocorreu para um tratador de exceção que sabe como lidar com a situação.

 

O modelo de tratamento de exceções de Java é similar ao de Delphi e C++. Existe uma hierarquia de

exceções, implementadas como classes, pré-definidas. Isso não descarta, de forma alguma, a

possibilidade do programador criar as suas próprias classes de exceções, derivando subclasses dessa

hierarquia. Abaixo temos um diagrama simplificado da hierarquia de exceções em Java:

 

A hierarquia Error descreve erros internos e escassez de recurso do sistema Java. Programadores não

deve "jogar" exceções desse tipo, e sim aqueles da hierarquia Exception.

 

Para criar um trecho de código que trate uma exceção, você deve fazer uma construção try/catch. Além

disso, na declaração do método onde uma exceção vai ser possivelmente jogada é necessíria a inclusão

de uma especificação de exceção.

 

class Nome_da_Classe {

 

...

 

tipo_de_retorno Método (parâmetros formais)

 

throws Tipo_de_Exceção1, Tipo_de_Exceção2

 

{

 

try {

 

bloco de código

 

...

 

}

 

catch (TipodaExeceção e)

 

{

 

tratador para esse tipo de exceção

 

}

 

catch (OutroTipodaExeceção e)

 

{

 

tratador para esse tipo de exceção

 

}

 

finally

 

{

 

código que serí executado até mesmo

 

na presença de uma exceção

 

}

 

}

 

Para "jogar" uma exceção, usa-se o comando throw. Quando um comando throw é encontrado, a

execução normal do programa é suspensa, enquanto um tratador adequado é procurado. A clíusula

catch que melhor se adequar para o tratamento daquela exceção serí executada, respeitando a seguinte

ordem:

 

1.O tipo de exceção da clíusula catch é exatamente da exceção jogada.

2.O tipo de exceção é uma superclasse daquela jogada.

3.O tipo de exceção é uma interface que a exceção jogada implementa.

 

Qualquer código dentro do bloco try depois da clíusula throw não serí saltado. Uma exceção pode ser

jogada novamente dentro de uma clíusula catch se houver necessidade de propagí-la.

 

Quando um bloco catch completa sua execução, a clíusula finally é procurada. Se esta não existe, a

execução continua na próxima instrução depois do bloco try. Uma clíusula finally é usada para a parte

do código que deve ser executada mesmo se uma exceção ocorrer.

 

Se uma exceção não é tratada num método, a execução deste é cancelada e a exceção é propagada para o

método chamador. Se ela não for tratada em lugar algum, a aplicação é interrompida com uma mensagem

(no caso de aplicações gríficas a mensagem é mostrada mais a aplicação não é encerrada).

 

Conclusões

 

Java parece ser uma linguagem razoavelmente simples, se, comparada a C++. Muitas das caracteràsticas

de C++ indesejadas e herdadas de C foram eliminadas. Ainda assim com certeza é familiar a

programadores C/C++. É uma linguagem significativamente segura, com a ausência de ponteiros e

garbage collecting. Permite a programação em larga escala, através da orientação a objeto, com

proteção, encapsulamento, reuso de código e outras caracteràsticas desejíveis em uma linguagem de uso

geral. Concorrência e tratamento de exceções são duas caracteràsticas que se destacam na linguagem,

uma preocupação de linguagens modernas, assim como a sua natureza portítil / interpretada. A

linguagem não provê abstrações genéricas, o que consideramos uma falta (apesar de que é possàvel criar

abstrações que possam tratar quaisquer objetos). As interfaces parecem ser uma boa idéia para a

substituição de herança múltipla de classes.

 

Apesar de suas vantagens como linguagem de programação, Java tem a desvantagem de ser uma

linguagem ainda muito nova. A falta de ambientes de desenvolvimento e ferramentas estí sendo

rapidamente suprida, jí que a linguagem chamou muita atenção da comunidade de programadores.

Esperamos que em breve teremos muitos sistemas desenvolvidos para Java, jí que as próximas versões

de sistemas operacionais para microcomputadores como o Windows e o OS/2 estarão incluindo a

míquina virtual Java. Além disso, existem projetos em andamento para a construção de um processador

Java que acelerarí a execução de aplicações executando o código binírio Java em hardware.

 

Bibliografia

 

[1] GOSLING, James e McGILTON, Henry, The Java Language Environment - A White Paper , Sun

Microsystems Computer Corporation, Outubro de 1995.

 

[2] The Java Language Specification - Version 1.0 Beta, Sun Microsystems Computer Corporation,

Beta Draft of October 30, 1995.

 

[3] RITCHEY, Tim, Programming with Java! Beta 2.0, Ed. New Riders, 1995.

 

[4] CORNELL, Gary e HORSTMANN, Cay, Core Java, Ed. Prentice Hall.

 

[5] Object-Oriented Languages in the Industry: A Comparison

http://www.eiffel.com./doc/manuals/tech ... index.html

 

[6] MEYER, Bertrand, An Invitation to Eiffel, http://www.eiffel.com/doc/manuals/language/intro

 

 

By: Loost :XTibia_smile:

Link para o comentário
Compartilhar em outros sites

×
×
  • Criar Novo...