184L ALPOO Teoria Pratica

58
Aplicações de Linguagem de Programação Orientada a Objetos

description

Java Aula lpoo

Transcript of 184L ALPOO Teoria Pratica

Page 1: 184L ALPOO Teoria Pratica

Aplicações de Linguagem de Programação Orientada a Objetos

Page 2: 184L ALPOO Teoria Pratica

2

Módulo 1 – AWT (Abstract Window Toolkit) e Swing

Componentes

Os componentes, também chamados de widgets (windows gadgets, ou dispositivos de janelas)

são os elementos construtivos das interfaces gráficas de usuário ou GUIs, isto é, são botões,

caixas de entrada de texto, caixas de seleção, menus, barras de rolagem, etc., que compõem

qualquer aplicativo visual. Também existem componentes não visuais destinados a oferecer

suporte aos demais componentes, como prover acesso a dados ou outras tarefas com as quais o

usuário do aplicativo não interaja diretamente. A figura abaixo apresenta um exemplo de GUI:

Java oferece uma ampla biblioteca de componentes GUI e de capacidades gráficas na forma de

classes, pertencentes aos pacotes java.awt e javax.swing, que representam os frameworks

(ou conjuntos de classes) responsáveis pela composição dos elementos visuais em um aplicativo.

AWT

A AWT constitui a primeira biblioteca de componentes visuais do Java que permitem a construção

de aplicações GUI, capaz de ser utilizada em grande parte dos sistemas operacionais. A AWT

funciona como uma casca entre o componente do sistema nativo e seu equivalente representado

pela classe correspondente da biblioteca.

Assim, uma aplicação Java AWT que contenha um botão usará o componente nativo para botões

onde a aplicação é executada. Mesmo que a aparência do componente apresente ligeira diferença

entre um sistema operacional e outro para o mesmo aplicativo, o comportamento e funcionamento

durante a operação serão os mesmos.

No entanto, isso exige um mecanismo razoavelmente complexo e pesado de troca de mensagens

entre o sistema operacional e a JVM (máquina virtual Java), o que reduz o desempenho dos

componentes AWT (daí serem chamados de componentes pesados ou heavy-weight

components). Além disso, a quantidade de componentes disponíveis é relativamente pequena e a

construção de novos componentes torna-se complexa ou dependente da plataforma.

A classe abstrata Component é a base para a construção de todos os componentes GUI

oferecidos pelo Java no pacote java.awt. Essa classe oferece a infra-estrutura necessária para

a criação de objetos que possuam uma representação gráfica, as quais poderão ser exibidas num

ambiente gráfico e com as quais o usuário poderá interagir. Sendo uma classe abstrata,

Page 3: 184L ALPOO Teoria Pratica

3

Component não pode ser instanciada diretamente, mas sendo a classe-base comum de todos os

componentes da biblioteca AWT, possibilita por meio do polimorfismo o tratamento generalizado

dos componentes derivados.

Com a API (Application Programming Interface ou Interface de Programação de Aplicativos) da

classe Component é possível determinar tamanho, cores, fonte e cursor utilizados pelo

componente, além do seu estado de funcionamento, posição e visibilidade.

Em particular, a classe Component é a base para a construção de outra classe abstrata especial

denominada Container, a qual representa um compartimento onde outros componentes podem

ser adicionados e posicionados, cuja API oferece facilidades para inclusão e remoção de

componentes. Classes derivadas de Container também são containers, isto é, também podem

conter outros componentes, tais como janelas e painéis.

Atualmente a AWT está integrada à JFC (Java Foundation Classes), um conjunto de bibliotecas

que engloba a própria AWT, o Swing e o Java 2D. A JFC foi desenvolvida para a construção de

aplicativos GUI e também gráficas voltadas para diferentes plataformas operacionais, cujo

destaque é o Swing, uma outra família de componentes.

Swing

Como comentado, o uso do AWT é dependente da plataforma; portanto, usá-la não é fácil e essa

biblioteca não está livre de bugs. Os componentes da biblioteca Swing são mais fáceis de utilizar

e estão muito mais bem implementados, mas alguns de seus componentes ainda necessitam de

classes da biblioteca AWT. A família de componentes Swing é uma parte importante da JFC que

engloba:

• Componentes Swing – uma nova família de componentes com funcionalidade ampliada e

grande capacidade de configuração;

• Suporte para múltiplos Pluggable Look and Feel – dá aos programas Java a

capacidade de utilizarem diferentes aparências visuais, tais como peles (skins) adaptadas

ou não ao ambiente operacional utilizado;

• Acessibilidade – permite o uso integrado de tecnologias para auxílio de portadores de

necessidades especiais, tais como monitores e teclados diferenciados;

• Java 2D – um pacote de classes para desenho 2D de gráficos, texto e imagens de alta

qualidade;

• Suporte para Drag and Drop – oferece capacidade de arrastar e soltar entre aplicações

Java e aplicações nativas.

Além de oferecer um conjunto muito mais amplo e versátil, os componentes Swing são 100% Java

e assim não utilizam código nativo tal como a família AWT, constituindo deste modo componentes

leves (ou light-weight components).

A classe JComponent do pacote javax.swing oferece a infra-estrutura para a maior parte dos

componentes Swing. Por sua vez, a classe JComponent é uma subclasse da classe Container,

Page 4: 184L ALPOO Teoria Pratica

4

do pacote java.awt, uma classe abstrata que oferece a estrutura para construção de

componentes que podem conter outros componentes. Em virtude disso, a estratégia de

representação (ou renderização) utilizada no Swing é diferenciada, sendo dividida em três partes:

a representação do componente em si, a representação da borda do componente e a

representação dos componentes nele contidos. A figura abaixo apresenta o diagrama da

hierarquia de classes da biblioteca Swing e respectivas dependências com a biblioteca AWT.

Como uma interface gráfica trabalha

Programas com interface gráfica de usuário (GUI) são controlados por eventos. Isto significa que o

programa reage às ações do usuário; estas ações são chamadas de eventos. Exemplos de

eventos podem ser: o pressionar de uma tecla do teclado, mover o mouse, pressionar um botão

ou selecionar um item de menu.

Um programa com uma GUI possui uma pequena fase de iniciação na qual a GUI é construída,

mas ainda não exibida. Algumas outras ações (não relacionadas aos componentes visuais)

também são executadas. Esta fase não é controlada por eventos e não é possível a interação com

java.awt.Container JComponent

JTextComponent AbstractButton JComboBox

Texto

JTextArea

JEditorPane

JTextPane

JTextField

Menus Widgets

JLabel

JList

JTree

JToolBar

JTable

JMenuBar

JPopupMenu

JCheckBoxMenuItem

JMenuItem

JMenu

JRadioButtonMenuItem

JSeparator

JToggleButton

JButton

JCheckBox

JRadioButton

JPasswordField

JSlider

JToolTip JScrollBar

JProgressBar

Sub-janelas

java.awt.Panel

java.awt.Applet

JApplet

JPanel

JViewPort

Janelas de nível mais alto

JScrollPane

JRootPane

JTabbedPane

JOptionPane

JInternalFrame

JPane

JSliptPane

JLayeredPane

java.awt.Applet

JWindow

JFrame

JDialog

java.awt.Frame

java.awt.Dialog

Page 5: 184L ALPOO Teoria Pratica

5

o usuário. Depois desta fase a GUI é apresentada na tela e o programa passa a ser controlado

por eventos.

Obviamente, queremos que o programa reaja somente a alguns tipos de eventos, não todos. O

programador precisa especificar quem serão os eventos aos quais o programa deverá reagir e, é

claro, quais as reações que deverá ter para cada evento. Em Java isto é feito acrescentando-se

os chamados listeners, ou observadores de eventos, que são interfaces que ficam aguardando a

ocorrência de determinados tipos de eventos. O observador, então, executa a ação desejada por

meio de seus métodos. Os eventos são processados na seqüência em que ocorrem, sob o

controle de um despachante de eventos que os organizam em fila.

Existem muitos tipos de eventos em Java associados a diferentes fontes de eventos, tais como

botões, menus ou o mouse. Os eventos contêm informações sobre o que aconteceu, isto é, quem

disparou o evento (se um botão ou um item de menu) e onde ele ocorreu (em quais coordenadas

do mouse, por exemplo). Estas são as informações exploradas pelo observador. O diagrama da

figura abaixo indica como se dá este processo.

Cada componente da interface é um objeto de uma classe distinta. O mesmo acontece com cada

tipo de evento, que é um objeto de uma classe particular, da mesma forma que cada observador

(listener) é uma interface que especifica um tipo particular de receptor de eventos. As classes e

interfaces disponíveis no modelo de eventos da AWT estão no pacote java.awt.event. Todos

os eventos da AWT possuem uma raiz comum que é a classe abstrata AWTEvent, existindo tipos

diferentes de eventos responsáveis pelo encapsulamento de informações relativas às diferentes

ações possíveis.

Todos os componentes Swing são capazes de produzir eventos dos seguintes tipos:

ComponentEvent, FocusEvent, KeyEvent e MouseEvent. Tais eventos, respectivamente,

sinalizam a alteração do tamanho, posição e visibilidade do componente, as mudanças de foco, o

acionamento de teclas via teclado e a utilização do mouse sobre o componente, necessitando de

tratamento pelos seus respectivos observadores: ComponentListener, FocusListener,

Programa GUI

Gera eventos

Despachante de eventos

Enfileira os eventos e notifica

Observadores de eventos

Acionam

Métodos de notificação

Page 6: 184L ALPOO Teoria Pratica

6

KeyListener e MouseListener. A tabela a seguir apresenta os eventos adicionais gerados

pelos principais componentes Swing.

Classes

Acti

on

Even

t

Care

tEven

t

Ch

an

geE

ven

t

Do

cu

men

tEven

t

Un

do

ab

leE

dit

Even

t

Item

Even

t

Lis

tSele

cti

on

Even

t

Win

do

wE

ven

t

Outros eventos

JButton * * * JCheckBox * * * JColorChooser * JComboBox * * JDialog * JEditorPane * * * HyperlinkEvent JFileChooser * JFrame * JInternalFrame InternalFrameEvent JList * JMenu MenuEvent

JMenuItem * * * MenuDragMouseEvent, MenuKeyEvent

JOptionPane JPasswordField * * * * JPopupMenu PopupMenuEvent JProgress * JRadioButton * * * JSlider * JSpinner * JTabbedPane *

JTable * TableModelEvent, TableColumnModelEvent, CellEditorEvent

JTextArea * * * JTextField * * * * JTextPane * * * HyperlinkEvent JToggleButton * * *

JTree

TreeExpansionEvent, TreeWillExpandEvent, TreeModelEvent, TreeSelectionEvent

JViewPort *

Os eventos típicos do Swing são aqueles produzidos pelos componentes mais usados pela

grande maioria das aplicações, cujo tratamento é usualmente requisitado:

• ActionEvent – evento semântico que indica a ocorrência de alguma ação predefinida em

um componente. Corresponde ao acionamento de botões (JButton) e itens de menu

(JMenuItem), mas pode ser disparado por outros componentes tal como no acionamento

da tecla ENTER em uma caixa de texto (JTextField). Seu tratamento é realizado por

implementação da interface ActionListener;

• CaretEvent – notifica as classes interessadas que a posição do cursor de edição de um

componente de texto sofreu modificação. É produzido por componentes deste tipo, tais

como JTextField e JTextArea. Seu tratamento é realizado por implementações da

interface CaretListener;

• ChangeEvent – permite avisar às classes interessadas que ocorreu alguma mudança no

componente associado, tal como o posicionamento de um controle deslizante JSlider ou

Page 7: 184L ALPOO Teoria Pratica

7

troca de aba em um JTabbedPane. Seu tratamento é realizado por implementações da

interface ChangeListener;

• DocumentEvent – constitui uma interface para notificação de modificações em

documentos (conteúdo de componentes de texto). São produzidos de modo especializado

por JTextField, JTextArea e outros. Seu tratamento exige a implementação da

interface DocumentListener;

• UndoableEditEvent – indica a ocorrência de uma operação que pode ser desfeita e cujo

tratamento se dá por meio da interface UndoableEditListener;

• ItemEvent – evento semântico que indica a seleção ou remoção da seleção de um

componente. Corresponde a seleção de caixas de opção (JCheckBox), botões de opção

(JRadioButton) ou itens de combos e listas (respectivamente JComboBox e JList).

Seu tratamento é realizado por implementações da interface ItemListener;

• ListSelectionEvent – caracteriza a mudança de seleção em uma caixa de lista (JList)

ou tabela (JTable). Seu tratamento é realizado por implementação da interface

ListSelectionListener;

• WindowEvent – evento de baixo nível que indica a alteração do estado de uma janela. O

tratamento deste evento se dá por meio de implementações da interface

WindowListener.

Os eventos semânticos são aqueles que têm significados bem definidos e cujos tratamentos são

independentes de plataforma. Também são considerados como eventos de alto nível. Já os

demais eventos, chamados de baixo nível, possuem tratamentos que, às vezes, precisam

considerar detalhes específicos da plataforma, sendo portanto de implementação mais complexa.

O tratamento de eventos no Java é relativamente simples. Para um evento qualquer, aqui

denominado XEvent, seu tratamento sempre acontece por meio de uma interface XListener, a

qual será incluída e removida respectivamente pelos métodos addXListener(XListener) e

removeXListener(XListener). Na interface XListener é comum que os métodos a serem

implementados tenham como prefixo o nome do evento (embora existam várias exceções).

A tabela abaixo relaciona os principais eventos, suas interfaces ou classes adaptadoras e

respectivos métodos do Swing:

Evento Interface ou Adaptador Métodos

ActionEvent ActionListener actionPerformed(ActionEvent) CaretEvent CaretListener caretUpdate(CaretEvent) ChangeEvent ChangeListener stateChanged(ChangeEvent)

componentHidden(ComponentEvent) componentMoved(ComponentEvent) componentResized(ComponentEvent)

ComponentEvent ComponentListener ComponentAdapter

componentShow(ComponentEvent) changedUpdate(DocumentEvent) insertUpdate(DocumentEvent) DocumentEvent DocumentListener removeUpdate(DocumentEvent) focusGained(FocusEvent) FocusEvent FocusListener

FocusAdapter focusLost(FocusEvent) ItemEvent ItemListener itemStateChanged(ItemEvent)

keyPressed(KeyEvent) KeyEvent KeyListener KeyAdapter keyReleased(KeyEvent)

Page 8: 184L ALPOO Teoria Pratica

8

keyTyped(KeyEvent) ListSelectionEvent ListSelectionListener valueChanged(ListSelectionEvent)

mouseClicked(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mousePressed(MouseEvent)

MouseListener MouseAdapter

mouseReleased(MouseEvent) mouseDragged(MouseEvent)

MouseEvent

MouseMotionListener MouseMotionAdapter mouseMoved(MouseEvent)

MouseWheelEvent MouseWheelListener mouseWheelMoved(MouseWheelEvent) UndoableEditEvent UndoableEditListener undoableEditHappened(UndoableEditEvent)

windowActivated(WindowEvent) windowClosed(WindowEvent) windowClosing(WindowEvent) windowDeactivated(WindowEvent) windowDeiconified(WindowEvent) windowIconified(WindowEvent)

WindowEvent WindowListener WindowAdapter

windowOpened(WindowEvent)

Módulo 2 – Swing parte 1: Criação de objetos via código

Construindo uma aplicação gráfica

Quando se decide pela construção de uma aplicação gráfica, é necessário um projeto da interface

para a aplicação, ou seja, é preciso determinar quais tarefas devem ser realizadas pela interface,

quais componentes serão utilizados, qual o objetivo de seu uso e qual a disposição desejada para

tais componentes. Dessa maneira, é possível dizer que a representação de uma aplicação gráfica

envolve seis passos simples, os quais devem ser repetidos para cada janela pretendida para a

aplicação:

1. Esboço da janela: desenho da janela pretendido que inclui disposição dos componentes

(leiaute), identificação e tipo dos componentes, especificação das ações do usuário e

reações correspondentes;

2. Implementação de uma subclasse JFrame: construção de uma subclasse de JFrame para

cada janela;

3. Declaração dos componentes: declaração dos componentes ativos como campos privados

da classe criada e, opcionalmente, dos componentes não ativos;

4. Representação do construtor: definição de construtor capaz de organizar os componentes

na interface e registrar os eventos necessários;

5. Programação dos processadores de eventos (event listeners): representação dos métodos

que processarão os eventos selecionados;

6. Início da aplicação GUI: caso a janela seja a principal da aplicação (a primeira janela a ser

utilizada), é adequado incluir o método main(String[] args).

Frames

Os elementos básicos de qualquer sistema operacional gráfico são as janelas, que são áreas

retangulares nas quais texto, imagens ou outras informações podem ser exibidas. As janelas

também podem conter elementos de interação com usuários, como menus, botões ou áreas para

Page 9: 184L ALPOO Teoria Pratica

9

entrada de textos. Muitos outros componentes gráficos não podem ser exibidos isoladamente,

sendo necessário que sejam colocados dentro de uma janela. A aparência final de uma janela

dependerá do sistema operacional utilizado, especialmente a largura e o tipo de borda ao redor de

uma janela. A posição e a cor dos botões podem variar.

Em Java, o termo frame (moldura) é usado para aquilo que genericamente é denominado janela.

No Swing, os frames são representados pela classe JFrame. Um frame é uma área retangular

com uma barra de título no topo. A barra de título também contém botões para fechar, maximizar

e minimizar a janela. Como comentado, o tipo e a posição desses botões dependem da

plataforma (Windows, MacOS, Linux, Solaris, etc.). Abaixo da barra de título existe uma área na

qual outros componentes gráficos podem ser embutidos. Esta área é dividida em duas partes: um

espaço para uma barra de menu na parte superior e o painel de conteúdo, logo abaixo. O painel

de conteúdo pode conter diversos componentes gráficos embutidos. Se não for incluída uma barra

de menu, então o painel de conteúdo é expandido para o espaço vazio deixado pela ausência da

barra de menu. As funções básicas como redimensionamento ou movimentação do frame com o

mouse são automaticamente fornecidas e não precisam ser programadas pelo programador.

Existe um outro componente chamado window na biblioteca Swing, mas este componente não

possui moldura, isto é, não possui uma barra de título, uma borda nem botões de fechar,

maximizar ou minimizar. A figura abaixo mostra a estrutura de um frame:

O construtor padrão do frame é o JFrame(), que gera um frame sem título. Gerar aqui significa

que a informação para desenhar-se o frame é fornecida; entretanto, o frame não é exibido na tela.

Isto é obtido chamando-se o método setVisible.

O método setVisible(boolean b) torna o frame visível se b for true. Se b for false, o

frame é ocultado da tela, mas a informação utilizada para desenhá-lo não é destruída. Assim, se b

for true novamente, o frame tornar-se-á visível na tela mais uma vez, ou seja, não será

necessário chamar o construtor uma segunda vez.

Painel de conteúdo

Título X □ _

Espaço para a barra de menu

Page 10: 184L ALPOO Teoria Pratica

10

O método setTitle(String title) ajusta o título na barra de título do frame com o valor

passado como argumento em title.

O método setSize(int width, int height) ajusta a largura do frame de acordo com o

valor passado como argumento em width e a altura com o valor passado em height.

O método setLocation(int horizontal, int vertical) posiciona o canto superior

esquerdo do frame na tela de acordo com os valores passados em horizontal e vertical.

O método pack() redimensiona o frame de modo que se ajuste firmemente ao redor dos

componentes embutidos em seu painel de conteúdo.

Finalmente, o método setDefaultCloseOperation(int operation) determina o que

acontece se o botão de fechar do frame for pressionado. Neste caso, pode-se utilizar a opção

JFrame.EXIT_ON_CLOSE como parâmetro passado ao argumento operation.

Panels e layouts

Embutiremos agora outro componente gráfico em um frame. Os componentes usados chamam-se

panels (painéis). Panels são componentes retangulares, que servem a dois propósitos: podem ser

usados como se fossem uma tela onde se pode desenhar ou podem ser usados como

compartimentos (containers) para embutir mais componentes gráficos.

No Swing a classe JPanel representa um panel. Abaixo temos seu construtor e alguns métodos:

JPanel() é o construtor. O tamanho do panel é ajustado para valores padronizados, 10 por 10

pixels na maioria dos sistemas.

setBackground(Color c) ajusta a cor de fundo do panel. A classe Color vem da biblioteca

da AWT. Assim, algumas cores são pré-definidas, isto é, vermelho será Color.red. Por padrão a

cor de fundo é cinza (grey).

setPreferredSize(Dimension d) ajusta o tamanho do panel. A classe Dimension vem da

biblioteca da AWT. O construtor possui a sintaxe Dimension(int width, int height).

Tanto width quanto height estão em pixels. É importante lembrar que estes valores são apenas

recomendações para o tamanho de um componente. Dependendo do tamanho de outros

componentes, o sistema – em tempo de execução – poderá escolher valores diferentes para eles!

Os valores realmente usados são determinados por um LayoutManager (gerenciador de leiaute)

em tempo de execução. Esta flexibilidade é importante ao Java para que seja independente de

plataforma.

Componentes gráficos são embutidos da seguinte maneira: chamemos o componente que

queremos embutir de componente filho (child component). Aqueles componentes do Swing

capazes de embutir outros possuem um método add (adicionar). Então, para embutir um

componente filho (childComp) em um componente pai (parentComp), a sintaxe utilizada é:

parentComp.add(childComp)

Page 11: 184L ALPOO Teoria Pratica

11

Vamos agora especificar como os componentes são acomodados no painel de conteúdo. A fim de

ser uma plataforma independente, os projetistas do Java deram alguma flexibilidade ao sistema

gráfico. O programador apenas especifica a estrutura de arranjo dos componentes, não suas

posições absolutas. Por exemplo, especifica-se que “o componente A está à direita do

componente B” ao invés de definir que “o componente A está na posição (x, y)”. Em tempo de

execução, as posições dos componentes é que são determinadas. Isto é feito pelo chamado

layout manager (gerenciador de leiaute) que está associado ao componente pai. Existem

diferentes gerenciadores de leiaute pré-definidos e o programador pode definir os seus próprios.

Um JFrame possui por padrão um BorderLayout; mais precisamente o painel de conteúdo

possui um gerenciador de leiaute do tipo BorderLayout. Ele permite que o usuário posicione um

(grande) componente central e até quatro componentes nas bordas. As posições são

determinadas pelas constantes CENTER, NORTH, SOUTH, EAST e WEST. Estas constantes estão

definidas na classe BorderLayout. Se um componente de borda não estiver presente, então o

componente central expande-se naquela direção. O componente central geralmente contém a

informação principal. Os componentes de borda contêm informações extras. Para inserir um

componente filho (childComp) dentro do painel de conteúdo na posição pos, usamos o método:

this.getContentPane().add(childComp, pos)

Onde pos é uma das seguintes constantes: BorderLayout.CENTER, BorderLayout.NORTH,

BorderLayout.SOUTH, BorderLayout.EAST ou BorderLayout.WEST. A figura abaixo

apresenta a distribuição dos componentes ao redor do frame.

Vejamos agora mais dois gerenciadores de leiaute: o gerenciador de leiaute por fluxo

(FlowLayout) e o gerenciador de leiaute por grade (GridLayout). Os leiautes não estão

restritos a frames. Todo componente Swing no qual outros componentes possam ser embutidos

(os chamados compartimentos ou containers) possui um leiaute. O leiaute padrão é uma borda.

Para que se possa alterar o leiaute de um componente pai parentComp para outro leiaute

newLayout usa-se o comando:

parentComp.setLayout(newLayout)

Se um componente tem um leiaute de fluxo então os componentes embutidos são dispostos em

fila única da esquerda para a direita. Se uma linha estiver completa, a próxima é iniciada. Leiaute

em linha (normalmente) respeita as dimensões dos componentes embutidos. A ordem de

chamada do parentComp.add(child) determina a ordem dos componentes embutidos no

componente pai. A altura de uma linha é determinada em tempo de execução, verificando-se

CNorth

CSouth

CWest C

East

CCenter

Page 12: 184L ALPOO Teoria Pratica

12

todos os componentes daquela linha, conforme indicado na figura abaixo, onde os componentes

C3 e C5 é que estão determinando a altura de cada uma das duas linhas presentes no frame.

A seguir, alguns construtores do FlowLayout:

FlowLayout()

FlowLayout(int align)

FlowLayout(int align, int hdist, int vdist)

O primeiro construtor gera um leiaute que, por padrão, deixa cinco pixels de distância entre os

componentes de uma linha e cinco pixels de distância entre linhas. Complementarmente, o

segundo construtor especifica o tipo de alinhamento dos componentes, onde align é um destes:

FlowLayout.RIGHT, FlowLayout.LEFT ou FlowLayout.CENTER

Isto determina quais componentes em cada linha estarão “empacotados” à direita, à esquerda ou

quais estarão centralizados. O terceiro construtor, além disso, também especifica a distância

hdist horizontal entre os componentes de uma linha e a distância vdist vertical entre linhas.

O terceiro gerenciador de leiaute a ser descrito é o GridLayout, que ordena os componentes em

uma grade. O componente pai (em nosso exemplo, o painel de conteúdo) é dividido em l x c

células retangulares, onde c é o número de células por linha e l é o número de linhas. Todas as

células têm o mesmo tamanho. Os componentes ficam embutidos dentro das células em fila única

da esquerda para a direita. Se houver mais células que componentes embutidos o gerenciador de

leiaute tentará preencher todas as linhas o mais separado possível e poderá gerar menos que c

colunas! Se houver menos células que componentes então mais colunas são adicionadas.

Basicamente o gerenciador de leiaute por grade ignora o número de colunas. A fim de ter um

número fixo c > 0 de colunas, o número de linhas deve ser ajustado para l = 0 e o número de

colunas para c. Assim, sempre haverá c colunas e o número de linhas dependerá do número de

componentes embutidos. A figura abaixo exemplifica o uso do gerenciador de leiaute por grade.

C3 C2 C1

C4 C5

C3 C2 C1

C4 C5 Vazio

Page 13: 184L ALPOO Teoria Pratica

13

Exercícios de Laboratório do módulo 2

1. Crie a classe abaixo, referente à representação de um frame principal. Teste os diferentes

métodos dessa classe no método main, alterando o tamanho do frame, posição na tela e o título.

Crie mais um método exibeFrame de modo que seja possível também ao programador usuário

dessa classe alterar as dimensões da janela, que está limitada no construtor a 200 por 200 pixels. package br.unip.cc4si4.ALPOO;

import javax.swing.JFrame;

public class JanelaPrincipal extends JFrame{

public JanelaPrincipal(){

this.setSize(200,200);

this.setLocation(200,200);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

//Torna o frame visivel

public void exibeFrame(){

this.setVisible(true);

}

//Torna o frame visivel e define o texto do titulo

public void exibeFrame(String titulo){

this.setTitle(titulo);

this.setVisible(true);

}

//Torna o frame visivel e define o texto do titulo

//e a posicao da janela

public void exibeFrame(String titulo, int x, int y){

this.setTitle(titulo);

this.setLocation(x,y);

this.setVisible(true);

}

//Torna o frame invisivel

public void ocultaFrame(){

this.setVisible(false);

}

//Cria e exibe o frame

public static void main(String[] args){

new JanelaPrincipal().exibeFrame("Janela Principal", 150, 150);

}

}

2. Crie a classe abaixo, referente à representação de um panel. package br.unip.cc4si4.ALPOO;

import java.awt.*;

import javax.swing.JPanel;

public class PanelColorido extends JPanel{

//Gera um JPanel com cor de fundo cor

public PanelColorido(Color cor){

this.setBackground(cor);

}

//Gera um JPanel com cor de fundo cor,

//largura e altura

public PanelColorido(Color cor,int largura,int altura){

this.setPreferredSize(new Dimension(largura,altura));

this.setBackground(cor);

}

}

3. Altere o construtor da classe JanelaPrincipal conforme indicado abaixo e inclua também o

seguinte import em seu cabeçalho: import java.awt.*; public JanelaPrincipal(){

this.setSize(200,200);

this.setLocation(200,200);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

PanelColorido PCWest = new PanelColorido(Color.white,50,20);

PanelColorido PCEast = new PanelColorido(Color.red);

PanelColorido PCNorth = new PanelColorido(Color.yellow);

PanelColorido PCSouth = new PanelColorido(Color.green);

PanelColorido PCCenter = new PanelColorido(Color.blue);

this.getContentPane().add(PCWest,BorderLayout.WEST);

this.getContentPane().add(PCEast,BorderLayout.EAST);

this.getContentPane().add(PCNorth,BorderLayout.NORTH);

Page 14: 184L ALPOO Teoria Pratica

14

this.getContentPane().add(PCSouth,BorderLayout.SOUTH);

this.getContentPane().add(PCCenter,BorderLayout.CENTER);

}

Se você quisesse que o programador usuário dessa classe fosse capaz de alterar a cor de fundo,

que complementação de código seria necessária na classe JanelaPrincipal?

4. Altere novamente o construtor da classe JanelaPrincipal conforme indicado abaixo:

public JanelaPrincipal(LayoutManager layout){

this.getContentPane().setLayout(layout);

this.setSize(200,200);

this.setLocation(200,200);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

PanelColorido PC1 = new PanelColorido(Color.red ,30,30);

PanelColorido PC2 = new PanelColorido(Color.yellow ,40,20);

PanelColorido PC3 = new PanelColorido(Color.green);

PanelColorido PC4 = new PanelColorido(Color.blue);

PanelColorido PC5 = new PanelColorido(Color.white,80,20);

this.getContentPane().add(PC1);

this.getContentPane().add(PC2);

this.getContentPane().add(PC3);

this.getContentPane().add(PC4);

this.getContentPane().add(PC5);

}

Altere também o método main conforme indicado a seguir: public static void main(String[] args){

FlowLayout flowLayout1 = new FlowLayout();

JanelaPrincipal flow1Frame = new JanelaPrincipal(flowLayout1);

flow1Frame.exibeFrame("Leiaute por fluxo 1",60,60);

FlowLayout flowLayout2 = new FlowLayout(FlowLayout.LEFT,40,30);

JanelaPrincipal flow2Frame = new JanelaPrincipal(flowLayout2);

flow2Frame.exibeFrame("Leiaute por fluxo 2",300,60);

GridLayout gridLayout = new GridLayout(2,4);

JanelaPrincipal gridFrame = new JanelaPrincipal(gridLayout);

gridFrame.exibeFrame("Leiaute por grade",540,60);

}

O que acontece com a distribuição dos componentes embutidos no frame se alterarmos suas

dimensões de 200 por 200 para 230 por 200 pixels? Porque o construtor recebe como argumento

um gerenciador de leiaute? Esse construtor poderia ter um gerenciador de leiaute sem precisar

receber um como argumento? Explique para que serve cada um dos argumentos (se houver) dos

3 objetos gerenciadores de leiaute criados no método main.

Módulo 3 – Swing parte 2: Utilização de objetos visualmente

Labels

Um label (rótulo) é um componente retangular que exibe texto que não pode ser editado pelo

usuário (mas pode ser alterado pelo programa). A classe JLabel cria rótulos no Swing. Aqui

apresentamos dois construtores e alguns métodos:

public JLabel(String texto)

public JLabel(ImageIcon imagem)

public String getText()

public void setText(String texto)

public void setText(String texto, int alinhamento)

Page 15: 184L ALPOO Teoria Pratica

15

public void setForeground(Color cor)

public void setBackground(Color cor)

public void setOpaque(boolean b)

JLabel(String texto) constrói um rótulo que exibe o texto: texto;

JLabel(ImageIcon imagem) constrói um rótulo que exibe a imagem: imagem;

getText() devolve o texto atualmente exibido no rótulo como uma String;

setText(String texto) substitui o texto atualmente exibido no rótulo pelo texto: texto. O

novo texto é instantaneamente apresentado no rótulo;

setText(String texto, int alinhamento) substitui o texto atualmente exibido no rótulo

pelo texto: texto. Ele também ajusta o alinhamento do texto à esquerda, à direita ou ao centro.

Os valores possíveis para alinhamento são encontrados na classe SwingConstants; por

exemplo, use SwingConstants.CENTER para centralizar o texto no rótulo;

setForeground(Color cor) ajusta a cor do texto para cor;

setBackground(Color cor) ajusta a cor do fundo do rótulo para cor. Note que rótulos são

transparentes por padrão e suas cores de fundo não são visíveis. De fato, observa-se que a cor

do componente pai é a que se destaca. Para mudar a cor de fundo de um rótulo primeiro deve-se

torná-lo opaco usando-se o método setOpaque;

setOpaque(boolean b) torna o rótulo transparente se b for false e opaco se b for true.

Buttons

Buttons (botões) são áreas retangulares, geralmente com uma linha delineando-as, que – tal

como os rótulos – podem exibir texto. Eles diferem dos rótulos porque podem disparar eventos.

Um evento ocorre sempre que um botão é pressionado. O sistema em tempo de execução do

Java monitora botões e reconhece quando um evento desse tipo ocorre. Note que um botão não é

necessariamente pressionado clicando-o com o mouse, pode haver também um dedo

pressionando-o em uma tela sensível a toque (as chamadas touch screens). A fim de detectar

quando um botão é pressionado, alguém fica de olho nele; esse alguém é o listener (observador),

um componente não gráfico da biblioteca AWT. O observador tem que estar associado ao botão

para que possa monitorá-lo. Se ocorrer um evento com o botão em tempo de execução, o sistema

informará o observador, que poderá então analisar o evento e dar início a alguma ação específica.

A classe JButton representa botões no Swing. Apresentaremos apenas o construtor e o método

que associa um observador ao botão:

public JButton(String texto);

public void addActionListener(ActionListener observador);

JButton(String texto) constrói um botão que contém o texto: texto;

addActionListener(ActionListener observador) adiciona um observador que detectará

eventos ocorridos no botão.

Page 16: 184L ALPOO Teoria Pratica

16

Menus

Menus são normalmente muito utilizados como componentes de frames. Eles estão organizados

hierarquicamente. O componente mais elevado dessa hierarquia é a barra de menu, uma área

retangular localizada abaixo da barra de título de um frame e acima do painel de conteúdo. Os

componentes intermediários são os menus. Um menu aparece como um texto na barra de menu.

Quando alguém clica em um desses textos, o menu selecionado se descortina, apresentando

diversos itens de menu, que estão na base da hieraquia. Os itens de menu são componentes que

disparam eventos. Eles podem ser considerados como um tipo especial de botão. De fato, tal

como os botões, os itens de menu fazem uso de observadores de eventos para monitorá-los.

Às vezes é necessário desabilitar determinados itens de menu por algum período de tempo. Um

item de menu desabilitado não dispara nenhuma ação quando é clicado. Em muitas plataformas o

texto de um item de menu desabilitado aparece esmaecido em relação aos itens habilitados. A

barra de menu é representada pela classe JMenuBar:

JMenuBar()

add(JMenu menu)

JMenuBar() constrói uma barra de menu;

add(JMenu menu) adiciona um menu em uma barra de menu.

Os menus são adicionados à barra de menu da esquerda para a direita na ordem dos comandos

add. Para adicionar uma barra de menu em um frame, usamos o seguinte método da classe

JFrame:

setJMenuBar(JMenuBar barraDeMenu)

Note que existem dois métodos distintos: setJMenuBar e setMenuBar para adicionar um

JMenuBar (usando Swing) e um MenuBar (usando AWT).

Da classe JMenu precisamos do construtor que recebe o título do menu em uma string e métodos

para adicionar itens de menu ou separadores, que são linhas horizontais que separam os itens de

menu em blocos:

JMenu(String tituloDoMenu)

add(JMenuItem itemDeMenu)

addSeparator()

Em um menu, os itens de menu ou separadores são inseridos do topo para a base na ordem dos

comandos add.

Finalmente, da classe JMenuItem necessitamos do construtor que recebe o título do item em

uma string e um método para associar um observador de eventos ao item de menu. Os itens de

menu comportam-se de modo muito semelhante aos botões. Em particular, o observador de

eventos (action listener) é automaticamente informado quando o item é clicado. O observador

então inicia as ações desejadas. Finalmente precisamos de um método para habilitar ou

desabilitar um item de menu:

Page 17: 184L ALPOO Teoria Pratica

17

JMenuItem(String textoDoItem)

addActionListener(ActionListener observador)

setEnabled(boolean b)

A chamada setEnabled(false) desabilita um item de menu. O item aparece com um texto

suavizado e qualquer observador associado a ele não é mais notificado cada vez que o item for

clicado. Assim, um item desabilitado não dispara qualquer reação na aplicação. Chamando

setEnabled(true) habilitará o item de menu novamente. O texto torna-se bem destacado e os

observadores associados ao item de menu passam a ser notificados dos eventos.

Exercícios de Laboratório do módulo 3

Neste laboratório teremos o primeiro exemplo de uma aplicação GUI real. Real aqui significa que

teremos um programa que interage com o usuário. A interação é realizada usando botões e itens

de menu em uma tela gráfica. Apesar de o programa ser muito simples, gostaríamos de introduzir

o conceito de uma abordagem modelo-visão-controle (model-view-control ou MVC) neste ponto. A

parte referente ao modelo do programa, não gráfica, lida com o armazenamento, manutenção e

manipulação de dados. A parte referente à visão exibe os dados e fornece os componentes

necessários à interação com o usuário, neste caso, botões e menu. A parte referente ao controle,

também não gráfica, assegura que as ações do usuário resultarão nas respostas desejadas dadas

pelo programa. A parte de controle é a ponte entre o modelo e a visão.

A separação em estruturas de modelo, visão e controle geralmente é uma abordagem

fundamental para o desenvolvimento de uma aplicação rápida e com sucesso. Ela também auxilia

os programadores iniciantes a melhor reconhecer os conceitos essenciais e suas interconexões.

Em Java, tal separação é facilmente obtida usando-se orientação a objetos, isto é, diferentes

classes ou ao menos diferentes métodos para diferentes partes. Em aplicações complexas,

pacotes separados (com seus conjuntos de classes) podem ser usados para partes diferentes da

aplicação.

Vamos então iniciar a especificação da interface gráfica de usuário que queremos representar.

Queremos montar um contador. Ele está especificado como uma estrutura de dados abstrata. O

contador possui uma variável chamada valor do tipo inteiro. Inicialmente ela vale zero. O contador

permite três operações:

• Incremento – incrementa o valor do contador de 1;

• Decremento – decrementa o valor do contador de 1;

• Restauro – reinicia o valor do contador para 0.

Vamos agora definir a aparência da tela e como ela funcionará. A GUI que temos em mente deve

exibir o valor atual do contador e permitir que o usuário possa incrementá-lo, decrementá-lo ou

restaurá-lo. Para esta finalidade teremos três botões. Pressionando o primeiro (incrementar) com

o mouse, incrementaremos o contador. Pressionando o segundo (decrementar) decrementaremos

o contador e pressionando o terceiro (restaurar) zeraremos o contador. Estas opções deverão

Page 18: 184L ALPOO Teoria Pratica

18

estar disponíveis também no menu. Assim, teremos um menu Opções com os seguintes itens de

menu: Incrementar, Decrementar e Restaurar. E um segundo menu Programa com o item de

menu Sair, para finalizar a aplicação. A figura abaixo mostra como queremos que a GUI se

pareça.

Vamos agora programar um contador em uma classe não gráfica que chamaremos de Contador.

Esta classe terá apenas uma variável inteira chamada valor que será iniciada em zero no

construtor, conforme exigido pela especificação. A variável valor é privada, de modo que outras

classes só possam manipulá-la através dos métodos fornecidos pela classe Contador.

Representaremos as três operações requeridas na especificação em três métodos com nomes

óbvios: incrementa(), decrementa() e restaura()e um quarto método, getValor(), para

ler o conteúdo da variável valor. Abaixo temos a listagem da classe: package br.unip.cc4si4.ALPOO;

public class Contador {

private int valor;

//O constructor incia o contador em zero.

public Contador() {

valor = 0;

}

public void incrementa(){

valor++;

}

public void decrementa(){

valor--;

}

public void restaura(){

valor = 0;

} public int getValor(){

return valor;

}

}

Cuidaremos agora da parte gráfica. Existem muitas maneiras de se arranjar os componentes

(botões, rótulos e menu). Uma delas seria “colar” os botões, o rótulo e o menu diretamente no

painel de conteúdo do frame. Utilizaremos aqui uma abordagem diferente: aplicaremos no painel

de conteúdo do frame um panel intermediário que ficará responsável por embutir os botões, o

rótulo e o menu. A classe responsável por essa disposição de elementos será chamada

PainelDoContador. Assim, PainelDoContador é que será colada ao frame. A vantagem

Incrementar

☺ Aplicação 1 x □ _

Decrementar

Restaurar

Rótulo contendo o

valor do contador

Opções

Incrementar

Decrementar

Restaurar

Programa

Page 19: 184L ALPOO Teoria Pratica

19

desta abordagem é que se pode reutilizar a classe PainelDoContador como um módulo pronto

em outras aplicações. Reutilização de código, portanto. Veja na figura abaixo a disposição desses

elementos na aplicação:

Abaixo, temos a listagem da classe PainelDoContador: package br.unip.cc4si4.ALPOO;

import javax.swing.JMenu;

import javax.swing.JMenuItem;

import javax.swing.JPanel;

import javax.swing.JMenuBar;

import javax.swing.JButton;

import javax.swing.JLabel;

import java.awt.BorderLayout;

import javax.swing.SwingConstants;

public class PainelDoContador extends JPanel {

private Contador contador;

private JLabel valorDoLabel;

public PainelDoContador() {

//Cria um objeto contador e define o

//gerenciador de leiaute como sendo BorderLayout.

contador = new Contador();

BorderLayout bordLay = new BorderLayout();

this.setLayout(bordLay);

//Cria uma barra de menu e a adiciona ao panel

JMenuBar barraDeMenu = new JMenuBar();

this.add(barraDeMenu,BorderLayout.NORTH);

//Cria e adiciona os menus à barra de menu

JMenu opcoes = new JMenu("Opções");

JMenu programa = new JMenu("Programa");

barraDeMenu.add(opcoes);

barraDeMenu.add(programa);

//Cria os itens de menu e os adiciona aos menus

JMenuItem incrementar = new JMenuItem("Incrementar");

JMenuItem decrementar = new JMenuItem("Decrementar");

JMenuItem restaurar = new JMenuItem("Restaurar");

JMenuItem sair = new JMenuItem("Sair");

opcoes.add(incrementar);

opcoes.add(decrementar);

opcoes.addSeparator();

opcoes.add(restaurar);

programa.add(sair);

//Cria os botões de incrementar, decrementar e restaurar

//e cria o rotulo que exibe o valor do contador.

JButton botaoIncrementa = new JButton("Incrementar");

JButton botaoDecrementa = new JButton("Decrementar");

JButton botaoRestaura = new JButton("Restaurar");

valorDoLabel = new JLabel(""+contador.getValor(),SwingConstants.CENTER);

//Adiciona os botões e o rótulo no panel utilizando as regras

//de posicionamento de elementos do gerenciador de objetos BorderLayout.

this.add(botaoIncrementa,BorderLayout.WEST);

this.add(botaoDecrementa,BorderLayout.EAST);

this.add(botaoRestaura,BorderLayout.SOUTH);

this.add(valorDoLabel,BorderLayout.CENTER);

}

public void incrementa(){

☺ Aplicação 1 □ _ x

Opções Programa

Incrementar

Decrementar

Restaurar

Rótulo contendo o valor do contador

JFrame

JPanel

JMenuBar

JButton

JLabel

Page 20: 184L ALPOO Teoria Pratica

20

contador.incrementa();

valorDoLabel.setText(""+contador.getValor());

}

public void decrementa(){

contador.decrementar();

valorDoLabel.setText(""+contador.getValor());

}

public void restaura(){

contador.restaurar();

valorDoLabel.setText(""+contador.getValor());

}

}

E em seguida, a listagem da classe JanelaDoContador, que é o frame responsável por embutir

o panel PainelDoContador: package br.unip.cc4si4.ALPOO;

import javax.swing.JFrame;

import java.awt.BorderLayout;

public class JanelaDoContador extends JFrame {

public JanelaDoContador(){

//Define o tamanho e a posição do frame na tela

this.setSize(300,200);

this.setLocation(200,200);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Cria e insere um panel do tipo PainelDoContador

PainelDoContador pdc = new PainelDoContador();

this.getContentPane().add(pdc,BorderLayout.CENTER);

}

public void exibeFrame(String title){

this.setTitle(title);

this.setVisible(true);

}

public void ocultaFrame(){

this.setVisible(false);

}

public static void main(String[] args) {

//Cria um objeto frame e o exibe na tela

JanelaDoContador jdc = new JanelaDoContador();

jdc.exibeFrame("Aplicação 1");

}

}

Se você rodar o programa, observará que ao clicar nos botões ou nos itens de menu nada

acontece. A conexão entre as ações do usuário (como pressionar um botão) e a aplicação é

estabelecida pelos observadores (listeners). Estes são componentes não-gráficos fornecidos pela

biblioteca AWT: java.awt.events.*. Existem diferentes observadores para diferentes tipos de

eventos (pressionar um botão, mover o mouse, etc.). Observadores são, em geral, interfaces e

não classes.

Vamos começar descrevendo como o conceito de um observador normalmente funciona. Alguns

componentes do Swing são capazes de disparar eventos. Por exemplo, um botão pode ser

pressionado ou um item de menu pode ser selecionado. Tais eventos são automaticamente

notificados pelo Java ao sistema em tempo de execução. Agora, o programador pode criar um

observador e associá-lo ao componente gráfico, digamos um botão. Assim, o observador fica

aguardando até que o botão seja pressionado e tome as ações necessárias se isto acontecer. O

sistema em tempo de execução notifica o observador se o botão foi pressionado. A notificação é

feita chamando-se um método específico do observador. O nome deste método é predefinido na

interface do observador. O programador tem que caracterizar este método inserindo o código que

será executado em resposta ao botão que foi pressionado.

Page 21: 184L ALPOO Teoria Pratica

21

Em nossa aplicação, o contador tem que ser incrementado ou decrementado em resposta ao

respectivo botão pressionado. O observador é portanto associado aos três botões, ou seja, ele

monitora todos. Se um botão for pressionado o observador é avisado. A fim de tomar a ação

apropriada ele tem que saber qual botão foi pressionado. Esta informação é fornecida pelo

sistema em tempo de execução na forma de um objeto do tipo ActionEvent. Esta classe

também se encontra na biblioteca AWT (em java.awt.events). Um objeto do tipo

ActionEvent contém informação acerca do evento notificado pelo sistema em tempo de

execução.

Para este exercício, criaremos um observadores na classe ObservadorPainel. Esta classe

representa a interface Java ActionListener. A criação da classe requererá ainda a definição

de um único método actionPerformed (ação executada):

public void actionPerformed(ActionEvent evento)

Este é o método chamado pelo sistema em tempo de execução sempre que um botão for

pressionado ou um item de menu for selecionado. Você não terá que chamar esse método, aliás

nem deve. O sistema em tempo de execução também gera uma ação evento e a passa como

um argumento do método actionPerformed. Este código é então executado em resposta ao

pressionar de um botão. A informação contida na ação evento pode ser utilizada para obter mais

informações sobre o quê disparou o evento. A listagem abaixo é da classe ObservadorPainel: package br.unip.cc4si4.ALPOO;

import java.awt.event.ActionListener;

import java.awt.event.ActionEvent;

public class ObservadorPainel implements ActionListener{

private PainelDoContador pdc;

public ObservadorPainel(PainelDoContador painel) {

pdc = painel;

}

//Este método é chamado pelo sistema em tempo de execução.

//Você deve adicionar o código a ser executado

//em resposta ao evento.

public void actionPerformed(ActionEvent evento){

//Início do seu código:

String actionCommand = evento.getActionCommand();

if(actionCommand.equals("Incrementar")){

pdc.incrementa();

}

else if(actionCommand.equals("Decrementar")){

pdc.decrementa();

}

else if(actionCommand.equals("Restaurar")){

pdc.restaura();

}

else if(actionCommand.equals("Sair")){

System.exit(0);

}

else{

System.out.println("ERRO: ActionCommand inesperado.");

}

//Fim do seu código

}

}

O coração da classe ObservadorPainel é a codificação do método actionPerformed.

Primeiro, determinamos quais dos botões foi pressionado ou qual dos itens de menu foi

selecionado. Isto é feito inspecionando-se o objeto de ação evento que o método

actionPerformed recebeu do sistema em tempo de execução. A linha:

Page 22: 184L ALPOO Teoria Pratica

22

String actionCommand = evento.getActionCommand();

Extrai o comando da ação do objeto evento. O comando da ação é geralmente o texto do botão

que foi pressionado ou o texto do item de menu. Em nosso caso ele só pode ser “Incrementar”,

“Decrementar”, “Restaurar” ou “Sair”. Para verificar qual desses textos foi obtido usamos uma

estrutura if-then-else. Dependendo do resultado do teste, chamamos o método incrementa(),

decrementa() ou restaura() do painel, ou ainda finalizamos a aplicação. Neste ponto vemos

porque ao observador é dada uma referência para um objeto PainelDoContador no construtor

da classe. O observador, dessa maneira, sabe qual painel ele tem que atuar em resposta ao

evento. Isto é importante se tivermos diversos painéis em uma aplicação. Para fazer com que o

painel e o observador se comuniquem, acrescente no construtor da classe PainelDoContador

as seguintes linhas de código: //Cria os observadores de botao.

ObservadorPainel op = new ObservadorPainel(this);

botaoIncrementa.addActionListener(op);

botaoDecrementa.addActionListener(op);

botaoRestaura.addActionListener(op);

//Cria os observadores de menu/itens de menu.

opcoes.addActionListener(op);

incrementar.addActionListener(op);

decrementar.addActionListener(op);

restaurar.addActionListener(op);

sair.addActionListener(op);

1. Desenhe o diagrama de classes do pacote br.unip.cc4si4.ALPOO, apresentando os

métodos de cada classe e as relações entre as classes, inclusive as classes da biblioteca Swing

utilizadas para gerar os diferentes componentes gráficos desta aplicação.

Módulo 4 – Swing parte 3: Tratamento de eventos

Caixas de texto

Caixas de texto apresentam uma única linha de texto. O texto pode ser editado pelo usuário. A

classe JTextField representa caixas de texto no Java. Para editar um texto, clique dentro da

caixa de texto e um cursor piscante aparecerá. A caixa de texto automaticamente desloca o

conteúdo na horizontal se o texto for maior que a caixa. Esta funcionalidade é inerente. Caixas de

texto são opacas e têm cor de fundo branca e texto preto por padrão. Abaixo, o construtor e

alguns métodos:

public JTextField(String texto)

public String getText()

public void setText(String texto)

public void setForeground(Color cor)

public void setBackground(Color cor)

JTextField(String texto) constrói uma caixa de texto que exibe texto.

setText(String texto) substitui o texto atualmente exibido na caixa de texto por texto.

getText() devolve o texto atualmente exibido na caixa de texto no formato String.

Page 23: 184L ALPOO Teoria Pratica

23

setForeground(Color cor) ajusta a cor do texto para cor.

setBackground(Color cor) ajusta a cor de fundo para cor.

Botões de rádio

Botões de rádio são representados usando-se a classe JRadioButton. Um botão de rádio é

retangular. Ele contém uma área circular à sua esquerda e um rótulo à sua direita. Botões de rádio

podem ser pressionados bem como podem ser agrupados. Somente um botão de rádio pode ser

pressionado de cada vez e o botão permanecerá pressionado até que outro botão do grupo o

seja. Um ponto preto surge dentro da área circular do botão que for selecionado. A seguir, o

construtor e alguns de seus métodos:

JRadioButton(String nomeDoBotao)

setSelected(boolean pressionado)

setActionCommand(String comando)

String getActionCommand()

JRadioButton(String nomeDoBotao) cria um botão rotulado de nomeDoBotao.

setSelected(boolean pressionado) determina qual botão foi selecionado (pressionado

= true) ou não (pressionado = false). No primeiro caso o ponto preto fica visível dentro da

área circular do botão. Este método é utilizado inicialmente para definir qual dos botões estará

pressionado antes da primeira interação com o usuário.

setActionCommand(String comando) associa a string comando como uma ação de

comando para o botão. Botões de rádio não são associados automaticamente a uma ação de

comando. Isto porque seus rótulos geralmente recebem uma imagem e não um texto. Definimos a

ação de comando nós mesmos através do método setActionCommand.

getActionCommand() devolve a ação de comando associada ao botão de rádio.

Além disso, devemos nos certificar que somente um botão será pressionado de cada vez. Isto é

conseguido agrupando-se os botões. Usamos para isso a classe ButtonGroup da biblioteca AWT.

Abaixo listamos o seu construtor e alguns métodos:

JButtonGroup()

add(JRadioButton botao)

String getSelection().getActionCommand()

JButtonGroup() constrói um grupo de botões mas ainda sem conter nenhum botão.

add(JRadioButton botao) adiciona o botão de rádio botao ao grupo.

String getSelection().getActionCommand() devolve a ação de comando do botão que

estiver atualmente selecionado dentro do grupo.

Uma vez os botões dentro de um grupo, somente um de cada vez pode ser pressionado.

Pressionar um botão não pressionado libera o que estiver pressionado.

Page 24: 184L ALPOO Teoria Pratica

24

Botões de seleção

O botão de seleção pode ser utilizado para indicar conjuntos de opções em que nenhuma, uma,

várias ou todas as opções são selecionadas. Botões de seleção são representados pela classe

JCheckBox. Um botão de seleção é retangular e contém uma área quadrada à sua esquerda e

um rótulo à sua direita. Quando um botão de seleção é pressionado, um sinal de verificação (�)

surge na área quadrada do botão. A seguir, um de seus construtores:

JCheckBox(String texto)

JCheckBox(String texto) constrói um botão de seleção rotulado de texto.

Exercícios de Laboratório do módulo 4

1. Construir uma aplicação que contenha um frame, um panel e, dentro deste, uma caixa de texto,

um botão e dois rótulos. Esta aplicação deve ser capaz de ler uma temperatura em graus Celsius

fornecida pelo usuário e convertê-la em Farenheit, ao clicar o botão da aplicação. Abaixo, a

listagem da classe JanelaTemperaturas: package br.unip.cc4si4.ALPOO;

import java.awt.BorderLayout;

import javax.swing.JFrame;

public class JanelaTemperaturas extends JFrame {

public JanelaTemperaturas(){

//Define o tamanho e a posição do frame na tela

this.setSize(200, 100);

this.setLocation(200, 200);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Cria e insere um panel do tipo PainelTemperaturas

PainelTemperaturas pt = new PainelTemperaturas();

this.getContentPane().add(pt, BorderLayout.CENTER);

}

public void exibeFrame(String title){

this.setTitle(title);

this.setVisible(true);

}

public static void main(String[] args){

JanelaTemperaturas jt = new JanelaTemperaturas();

jt.exibeFrame("Conversor");

}

}

A seguir, a listagem do panel PainelTemperaturas: package br.unip.cc4si4.ALPOO;

import javax.swing.JPanel;

import javax.swing.JLabel;

import javax.swing.JButton;

import javax.swing.JTextField;

import java.awt.GridLayout;

public class PainelTemperaturas extends JPanel {

private JLabel rotuloCelsius, rotuloFarenheit;

private JButton botaoConverteTemperatura;

private JTextField caixaTextoTemperatura;

public PainelTemperaturas(){

//Define o gerenciador de leiaute

GridLayout gridlayout = new GridLayout(2,2,1,1);

this.setLayout(gridlayout);

//Cria, na sequência indicada, uma caixa de texto

//um rótulo, um botão e outro rótulo

caixaTextoTemperatura = new JTextField();

rotuloCelsius = new JLabel("Celsius", JLabel.RIGHT);

botaoConverteTemperatura = new JButton("Converter");

rotuloFarenheit = new JLabel("Farenheit", JLabel.RIGHT);

//Acrescenta uma dica na caixa de texto e no

Page 25: 184L ALPOO Teoria Pratica

25

//botão para auxílio do usuário

caixaTextoTemperatura.setToolTipText("Temperatura em graus Celsius");

botaoConverteTemperatura.setToolTipText("Converte Celsius para Farenheit");

//Exibe os componentes gráficos no painel

this.add(caixaTextoTemperatura);

this.add(rotuloCelsius);

this.add(botaoConverteTemperatura);

this.add(rotuloFarenheit);

//Insere o observador dos componentes do painel

ObservadorPainelTemperaturas opt = new ObservadorPainelTemperaturas(this);

//Conecta o observador aos componentes

caixaTextoTemperatura.addActionListener(opt);

botaoConverteTemperatura.addActionListener(opt);

}

//Método para leitura do conteúdo da caixa de texto

public float leTemperatura(){

try{

float temp = 1.8f*Float.parseFloat(caixaTextoTemperatura.getText());

rotuloFarenheit.setText(temp+" F");

return temp;

} catch (NumberFormatException nfe) {

rotuloFarenheit.setText("Valor invalido!");

return 0.0f;

}

}

}

E por fim o observador do panel, ObservadorPainelTemperaturas: package br.unip.cc4si4.ALPOO;

import java.awt.event.ActionListener;

import java.awt.event.ActionEvent;

public class ObservadorPainelTemperaturas implements ActionListener {

//Cria um atributo privado do tipo PainelTemperaturas

private PainelTemperaturas pt;

//Define o construtor da classe que recebe como parâmetro

//o painel PainelTemperaturas

public ObservadorPainelTemperaturas(PainelTemperaturas painel){

pt = painel;

}

//Este método é chamado pelo sistema em tempo de execução

//Você deve adicionar o código a ser executado em resposta ao evento

public void actionPerformed(ActionEvent evento){

//Seu código

pt.leTemperatura();

}

}

2. Construir uma aplicação que contenha dois grupos de botões: um com botões de verificação e

outro com botões de rádio. Incluir também dois rótulos que indiquem as opções feitas pelo

usuário. Utilizar um panel colado ao frame que contenha os dois grupos de botões e seus rótulos.

Para esta aplicação teremos cinco classes: uma para o frame, duas para os panels e demais

componentes gráficos e outras duas classes contendo os observadores de eventos, um para cada

panel. A listagem abaixo apresenta a classe JanelaDeBotoes, que representa o frame: package br.unip.cc4si4.ALPOO;

import javax.swing.JFrame;

import java.awt.GridLayout;

public class JanelaDeBotoes extends JFrame {

public JanelaDeBotoes(){

//Define o tamanho e a posicao do frame na tela

this.setSize(200,150);

this.setLocation(200,200);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//Cria e define o gerenciador de leiaute do frame

GridLayout gridlayout = new GridLayout(1,2);

this.setLayout(gridlayout);

//Cria a insere o panel PainelBotaoDeRadio no frame

PainelBotaoDeRadio pbdr = new PainelBotaoDeRadio();

this.getContentPane().add(pbdr);

//Cria e insere o panel PainelBotaoDeVerificacao no frame

PainelBotaoDeVerificacao pbdv = new PainelBotaoDeVerificacao();

this.getContentPane().add(pbdv);

Page 26: 184L ALPOO Teoria Pratica

26

}

public void exibeFrame(String titulo){

this.setTitle(titulo);

this.setVisible(true);

}

public void ocultaFrame(){

this.setVisible(false);

}

public static void main(String[] args){

JanelaDeBotoes jdb = new JanelaDeBotoes();

jdb.exibeFrame("Aplicação 2");

}

}

A seguir, a listagem de PainelBotaoDeRadio, que gera um panel que será colado ao frame: package br.unip.cc4si4.ALPOO;

import javax.swing.JPanel;

import javax.swing.ButtonGroup;

import javax.swing.JRadioButton;

import javax.swing.JLabel;

import java.awt.GridLayout;

public class PainelBotaoDeRadio extends JPanel {

private JRadioButton rb[] = new JRadioButton[3];

private JLabel botaoradio = new JLabel("Sem seleção");

public PainelBotaoDeRadio(){

//Define o gerenciador de leiaute do panel

GridLayout gridleiaute = new GridLayout(4,1);

this.setLayout(gridleiaute);

//Cria um grupo de botões

ButtonGroup bg = new ButtonGroup();

//Cria o observador dos botões de rádio

ObservadorDoPainelBotaoDeRadio opbr = new ObservadorDoPainelBotaoDeRadio(this);

//Cria e insere os botões de verificação no panel, o grupo de botões

//bem como os seus observadores de evento

for(int i=0; i<3; i++){

this.add(rb[i] = new JRadioButton("Alternativa "+(i+1)));

bg.add(rb[i]);

rb[i].addItemListener(opbr);

}

//Insere o label no panel bem como o seu observador de evento

this.add(botaoradio);

}

//Método para renomear o texto do label: botaoradio

public void renomeiaBotaoRadio(String nome){

botaoradio.setText(nome);

}

}

Agora, a listagem de PainelBotaoDeVerificação: package br.unip.cc4si4.ALPOO;

import javax.swing.JPanel;

import javax.swing.JCheckBox;

import javax.swing.JLabel;

import java.awt.GridLayout;

public class PainelBotaoDeVerificacao extends JPanel {

private JCheckBox cb[] = new JCheckBox[3];

private JLabel botaoverificacao = new JLabel("Sem seleção");

public PainelBotaoDeVerificacao(){

//Define o gerenciador de leiaute do panel

GridLayout gridleiaute = new GridLayout(4,1);

this.setLayout(gridleiaute);

//Cria o observador de botões de verificação

ObservadorDoPainelBotaoDeVerificacao opbv = new ObservadorDoPainelBotaoDeVerificacao(this);

//Cria e insere os botões de verificação no panel

//bem como os seus observadores de evento

for(int i = 0; i<3; i++){

this.add(cb[i] = new JCheckBox("Alternativa "+(i+1)));

cb[i].addItemListener(opbv);

}

//Insere o label no panel bem como o seu observador de evento

this.add(botaoverificacao);

}

//Método para verificar se um botão foi selecionado

public boolean botaoFoiSelecionado(int i){

Page 27: 184L ALPOO Teoria Pratica

27

if(cb[i].isSelected())

return true;

else

return false;

}

//Método para renomear o label botaoverificacao

public void renomeiaBotaoVerificacao(String nome){

botaoverificacao.setText(nome);

}

}

Temos em seguida a listagem do observador ObservadorDoPainelBotaoDeRadio:

package br.unip.cc4si4.ALPOO;

import javax.swing.JRadioButton;

import java.awt.event.ItemListener;

import java.awt.event.ItemEvent;

public class ObservadorDoPainelBotaoDeRadio implements ItemListener {

//Cria um atributo privado do tipo PainelBotaoDeRadio

private PainelBotaoDeRadio pbdr;

//Define o construtor da classe que recebe como parametro

//o painel PainelBotaoDeRadio

public ObservadorDoPainelBotaoDeRadio(PainelBotaoDeRadio painel){

pbdr = painel;

}

//Este metodo e chamado pelo sistema em tempo de execucao

//Voce deve adicionar o codigo a ser executado em resposta ao evento

public void itemStateChanged(ItemEvent evento){

//Inicio do seu codigo:

JRadioButton rb = (JRadioButton)evento.getSource();

String nomeDoBotao = rb.getText();

pbdr.renomeiaBotaoRadio("Selecao: " + nomeDoBotao.charAt(nomeDoBotao.length()-1));

//Fim do seu codigo.

}

}

Finalmente, a listagem do ObservadorDoPainelBotaoDeVerificacao:

package br.unip.cc4si4.ALPOO;

import java.awt.event.ItemListener;

import java.awt.event.ItemEvent;

public class ObservadorDoPainelBotaoDeVerificacao implements ItemListener {

//Cria um atributo privado do tipo PainelBotaoDeVerificacao

private PainelBotaoDeVerificacao pbdv;

//Define o construtor da classe que recebe como parametro

//o painel PainelBotaoDeVerificacao

public ObservadorDoPainelBotaoDeVerificacao(PainelBotaoDeVerificacao painel){

pbdv = painel;

}

//Este metodo e chamado pelo sistema em tempo de execucao

//Voce deve adicionar o codigo a ser executado em resposta ao evento

public void itemStateChanged(ItemEvent evento){

//Inicio do seu codigo:

StringBuffer itens = new StringBuffer("Selecao: ");

for(int i=0; i<3; i++){

itens.append(pbdv.botaoFoiSelecionado(i)?(i+1)+" ":"");

}

pbdv.renomeiaBotaoVerificacao(itens.toString());

//Fim do seu codigo.

}

}

3. Desafio: construir uma calculadora em Java. A calculadora deve ter: um JTextField onde o

usuário visualiza os números e as contas; JButtons para cada número e pelo menos as quatro

operações básicas, além de botões para limpar e corrigir, bem como um JPanel que receberá os

componentes gráficos e será colado ao frame, criado com um JFrame. Os botões com números

devem reagir às ações do teclado e somente responder a teclas numéricas, ponto decimal e sinais

para soma, subtração, divisão e multiplicação. Para isso, deve-se utilizar o observador de eventos

de teclado: KeyListener e criar uma classe observadora que trate os eventos gerados pelo

teclado com o KeyEvent. A aplicação pode ser aprimorada com um menu, via JMenu.

Page 28: 184L ALPOO Teoria Pratica

28

Módulo 5 – JDBC: java.sql – Conexão com Banco de Dados

SGBD (Sistema Gerenciador de Banco de Dados) é um conjunto de dados que pode ser utilizado

por meio de um grupo particular de programas, proporcionando um ambiente conveniente e

eficiente para o armazenamento e a recuperação de informações.

Os bancos de dados tornaram-se extremamente populares e importantes no segmento

empresarial, principalmente aqueles que operam segundo o modelo relacional, os quais são

conhecidos também como SGBDR (Sistema Gerenciador de Banco de Dados Relacionais).

JDBC (Java Database Connectivity) é uma API definida nos pacotes java.sql e javax.sql

para a conexão arbitrária de um banco de dados a um programa Java. A API JDBC é um conjunto

de classes e interfaces que provê um padrão para tornar possível, aos desenvolvedores de

aplicações e ferramentas, a construção de software que acesse banco de dados. Ela ainda provê

acesso universal a dados para a linguagem de programação Java, permitindo o acesso a qualquer

tipo de dados (bancos de dados relacionais ou não relacionais, planilhas e arquivos de dados).

Uma analogia

Uma das melhores formas de se entender os princípios básicos da JDBC é através de uma

analogia. Suponha que tocamos um pequeno negócio que comercializa gadgets. Os gadgets são

produzidos em uma fábrica, que fica além de um rio à frente do nosso comércio. Sem um barco ou

uma ponte, não há maneira de ir à fábrica ou da fábrica entregar seus bens à nossa loja.

Esta situação é análoga a um banco de dados e um programa Java: o programa Java é a loja e o

banco de dados é a fábrica. Sem alguma coisa para ajudar, não há maneira para o programa Java

fazer requisições ao banco de dados, nem o banco de dados é capaz de se comunicar com o

programa Java. A fim de que os produtos saiam da fábrica para a loja, seria lógico construir uma

ponte sobre o rio. A peça de software correspondente à ponte para permitir a comunicação entre o

programa Java e o banco de dados é uma interface chamada driver (controlador). A figura abaixo

mostra a analogia apresentada.

A arquitetura JDBC é conceitualmente semelhante ao ODBC (Open Database Connectivity),

amplamente utilizado em sistemas Windows. No entanto, JDBC é mais flexível, e seu emprego

independe de plataforma operacional em uso, um princípio fundamental do Java.

Page 29: 184L ALPOO Teoria Pratica

29

Por meio dessa API, torna-se possível o acesso, genérico e uniforme, a qualquer SGBDR, sendo

que os dados são efetivamente acessados por meio do SQL (Structured Query Language). Cabe

ao programador escrever uma interface simples com os elementos da API JDBC para conectar-se

ao banco de dados, escolhendo a forma de interação. Com o auxílio do SQL deverão ser

especificadas as operações de consulta, de inclusão, de remoção ou de alteração de dados, sem

necessidade de programação adicional para a realização destas operações. Desta forma, o uso

do SQL é imprescindível, enquanto o JDBC constitui uma interface para o acesso aos bancos de

dados.

Mapeamento de tipos

Outra característica importante do JDBC é o mapeamento transparente dos tipos de dados

existentes no banco de dados para os tipos nativos do Java oferecendo, quando necessário,

classes adicionais para esta tarefa. A tabela abaixo apresenta a correspondência entre os tipos

SQL e os tipos Java. É necessário destacar que alguns bancos de dados podem apresentar

exceções para tipos específicos.

Tipo SQL Tipo Java BIGINT long BINARY byte[ ] BIT boolean BLOB java.sql.Blob CHAR java.lang.String CLOB java.sql.Clob DATE java.sql.Date DECIMAL java.math.BigDecimal DOUBLE double FLOAT double INT, INTEGER int LONGVARBINARY byte[ ] LONGVARCHAR java.lang.String NUMERIC java.math.BigDecimal REAL float SMALLINT short TIME java.sql.Time TIMESTAMP java.sql.Timestamp TINYINT byte VARBINARY byte[ ] VARCHAR java.lang.String

Todos os tipos comuns do SQL são equivalentes a tipos primitivos Java. Outros tipos (tais como

DATE, TIME e TIMESTAMP) são suportados por classes específicas, enquanto tipos complexos,

BLOB e CLOB, são suportados pelas interfaces java.sql.Blob e java.sql.Clob, cuja

implementação definitiva depende do banco de dados em uso.

Componentes do JDBC

Identificam-se quatro elementos centrais na arquitetura JDBC:

Page 30: 184L ALPOO Teoria Pratica

30

• Classes e interfaces de sua API, contidas nos pacotes java.sql e javax.sql, que

constituem os elementos de interligação padronizada entre aplicações Java e bancos de

dados relacionais;

• Suíte de testes de drivers, disponibilizada no site da Sun voltado ao JDBC, que permite

avaliar as funcionalidades e a compatibilidade de drivers JDBC. Site de consulta:

http://java.sun.com/products/jdbc;

• Drivers JDBC que representam, de fato, os mecanismos padronizados de conexão e de

interação com cada banco de dados;

• Ponte JDBC-ODBC que possibilita o uso de drivers ODBC como drivers JDBC, permitindo

o uso de banco de dados acessíveis exclusivamente por meio do ODBC.

A classe DriverManager (gerenciador de driver) e os drivers JDBC são particularmente

importantes. Por meio desta classe é selecionado o driver apropriado para acesso ao banco de

dados específico que será utilizado por uma aplicação. Essa classe também será responsável

pela conexão inicial com o banco escolhido. Além do DriverManager é possível utilizar um

objeto javax.sql.DataSource para conexão com um banco de dados, mas isto exige que o

serviço JNDI (Java Naming and Directory Interface) esteja ativo na rede em uso.

Drivers e seus tipos

Um driver JDBC é uma classe especial que deve representar a interface java.sql.Driver. Os

drivers podem ser escritos completamente em Java, de modo que possam ser carregados

dinamicamente por aplicações ou applets. Também podem ser representados usando código

nativo, criando uma conexão para outras bibliotecas ou drivers destinados ao acesso a bancos de

dados específicos.

A Sun exige que um driver suporte, no mínimo, a funcionalidade prevista no padrão ANSI SQL-92

para passar nos testes de compatibilidade do JDBC. Por meio de um driver, uma aplicação tem

acesso a codificações específicas das classes que são capazes de converter as requisições de

operações (comandos SQL) em ações particulares para um banco de dados, sendo responsáveis

pela efetiva integração das aplicações Java com os diversos bancos de dados.

Existem quatro tipos diferentes ou categorias de drivers:

1. Ponte JDBC-ODBC (JDBC-ODBC bridge plus ODBC driver);

2. API nativa parcialmente Java (native API partial Java);

3. Cliente (JDBC-net pure Java);

4. Protocolo nativo 100% Java (native protocol pure Java).

A figura abaixo ilustra os tipos de drivers e o esquema geral de conexão entre um programa Java

e um banco de dados via JDBC:

Aplicação Java / Applet

JDBC API

JDBC

Page 31: 184L ALPOO Teoria Pratica

31

Os drivers de protocolo nativo 100% Java são aqueles codificados integralmente em Java e que

representam diretamente o protocolo proprietário do banco de dados, convertendo as chamadas

JDBC em chamadas do sistema de banco de dados locais ou remotas. Usualmente exibem

desempenho superior, devido ao acesso direto às facilidades oferecidas pelos SGBDRs. São

conhecidos como drivers de categoria 4.

Os drivers de cliente traduzem as chamadas JDBC em outras independentes do protocolo do

banco de dados. Tais chamadas são direcionadas a um servidor ou middleware (camada

intermediária de software), o qual as converte para o protocolo do banco de dados. É uma

alternativa bastante flexível, pois o cliente pode se manter 100% Java, enquanto o servidor ou

middleware pode se conectar a múltiplos bancos. São conhecidos como drivers de categoria 3.

Os drivers de API nativa parcialmente Java representam diretamente o protocolo proprietário do

banco de dados, convertendo as chamadas JDBC em chamadas do sistema do banco efetuadas

por meio de sua API proprietária. Assim, requer o uso de código específico de plataforma e do

banco de dados em cada máquina cliente. São conhecidos como drivers de categoria 2.

Finalmente, com o uso da ponte JDBC-ODBC, torna-se possível o uso de drivers ODBC de

sistemas Windows (categoria 1) para obter o acesso aos bancos de dados que não dispõem de

drivers de outras categorias.

Page 32: 184L ALPOO Teoria Pratica

32

Sempre que possível, devem ser empregadas soluções baseadas no uso de drivers das

categorias 3 e 4. O uso de drivers de categoria 1 e 2 deve ser visto como uma solução temporária

e de desempenho inferior devido ao uso de numerosas camadas entre a aplicação e o banco de

dados.

A classe DriverManager

A utilização de m banco de dados requer uma conexão estabelecida por meio de um driver

adequado. A classe DriverManager é responsável por administrar e selecionar tal driver

conforme o banco de dados específico, possibilitando efetuar a conexão.

A solicitação de uma conexão com um banco de dados é realizada pelo método

getConnection(String) da classe DriverManager, que recebe uma URL (Uniform

Resource Locator) no padrão JDBC para indicar qual o banco de dados desejado. A classe

DriverManager analisa a URL recebida e seleciona o driver apropriado para efetuar a conexão

dentre a lista de drivers disponíveis. Um pedido de conexão tem a forma: try{

Connection con = DriverManager.getConnection(“jdbc:hsqldb:hsql://omega/ssolar”);

} catch (SQLException excecao){

//Conexão não foi possível

}

Caso a conexão não seja possível, a exceção java.sql.SQLException, de tratamento

obrigatório, é lançada.

URLs JDBC

A API JDBC utiliza uma estrutura de endereçamento semelhante às URLs para especificar qual

banco de dados será utilizado. Tais URLs têm a seguinte formatação:

jdbc:<subprotocolo>:<subnome>

O prefixo inicial jdbc indica o uso desta API. O subprotocolo é a denominação do banco de dados,

a qual deverá ser reconhecida pelos drivers disponíveis. O subnome depende do banco e,

geralmente, inclui: host, onde opera o banco de dados e o nome desse banco. Mas podem incluir

também a indicação de outro subprotocolo, portas específicas, usuários, etc. A tabela abaixo

apresenta alguns formatos de URLs JDBC.

SGBD URL MySQL jdbc:mysql://hostname:3306/database Oracle jdbc:oracle:thin@hostname:1526:database Firebird jdbc:firebirdsql://hostname:3050/database PostgreSQL jdbc:postgresql://hostname:5432/database SQLServer jdbc:microsoft:sqlserver://hostname:1433/database ODBC jdbc:odbc:datasource

O MySQL

MySQL é um sistema de gerenciamento de banco de dados relacional de código-fonte aberto e de

nível corporativo. Foi desenvolvido por uma empresa de consultoria na Suécia chamada TcX em

Page 33: 184L ALPOO Teoria Pratica

33

1994, encabeçado por Michael Widenius e David Axmark. Eles precisavam de um sistema de

banco de dados que fosse extremamente rápido e flexível. Infelizmente, não conseguiam

encontrar nada no mercado que pudesse fazer o que queriam. Então, eles criaram o MySQL, que

é vagamente baseado em outro sistema de gerenciamento de bancos de dados chamado MSQL.

O produto criado é rápido, confiável e extremamente flexível. É utilizado em muitos lugares por

todo o mundo. Universidades, provedores de Internet e organizações sem fins lucrativos são os

principais usuários do MySQL, principalmente por ser gratuito via licença GPL. Mas cada vez mais

ele vem permeando o mundo dos negócios como um SGBDR confiável e rápido. Entre suas

principais características, podemos destacar:

• Suporta praticamente qualquer plataforma atual;

• Pouco exigente quanto a recursos de hardware;

• Excelente desempenho e estabilidade;

• Suporta controle transacional, triggers, cursores (non-scrollable e non-updatable), stored

procedures e functions;

• Replicação facilmente configurável;

• Interfaces gráficas (MySQL Toolkit) de fácil utilização cedidas pela MySQL Inc.

O MySQL foi comprado pela Sun Microsystems em 16 de Janeiro de 2008 por U$ 1 bilhão (um

preço jamais visto no setor de licenças livres) e a Sun, em 20 de Abril de 2009, foi adquirida pela

Oracle. Portanto, atualmente o MySQL é um produto Oracle. Este será o SGBDR utilizado para

conectar-se aos aplicativos Java.

O driver do MySQL

O driver JDBC oficial para o MySQL é o MySQL Connector/J. É um arquivo compactado e pode

ser baixado do site do MySQL. Usaremos aqui a versão 5.1 desse driver, cujo nome do arquivo é:

mysql-connector-java-5.1.8.zip

As orientações abaixo são baseadas no material didático do Prof. Vladimir Camelo. Abra o arquivo

com um programa de descompactação e descompacte somente o arquivo:

mysql-connector-java-5.1.8-bin.jar

Salve este arquivo nas pastas:

C:\Program Files\Java\jdk1.6.0_11\jre\lib\ext

C:\Program Files\Java\jre6\lib\ext

Se o seu sistema operacional estiver em português, a pasta “Program Files” chama-se “Arquivo de

Programas”. Altere o nome do arquivo mysql-connector-java-5.1.8-bin.jar para:

mysql.jar

Como indicado na figura abaixo.

Page 34: 184L ALPOO Teoria Pratica

34

Crie no CLASSPATH um alias (apelido) para esse arquivo. Chame o CLASSPATH acessando o

botão “Iniciar” do Windows, depois selecione o item “Painel de Controle” e, na janela que se abre,

selecione o ícone “Sistema”. Será aberta uma segunda janela, indicada abaixo à esquerda. Clique

no botão “Variáveis de ambiente” (na imagem o botão está em inglês, destacado em vermelho).

Edite a variável de ambiente CLASSPATH, destacado em vermelho na figura acima, à direita.

Page 35: 184L ALPOO Teoria Pratica

35

Na última figura, na caixa de texto com o valor da variável (destacada em vermelho) insira, após o

último texto que lá estiver, um ponto-e-vírgula (;) e em seguida o endereço onde se encontra o

arquivo mysql.jar, ou seja, insira:

C:\Program Files\Java\jdk1.6.0_11\jre\lib\ext\mysql.jar

Preste atenção: se o seu Windows estiver em português, utilizar o nome dos diretórios existentes

em língua portuguesa e não os nomes aqui indicados em inglês, já que este exemplo está usando

um Windows de língua inglesa!

Finalizados estes passos, o driver do MySQL está pronto para ser utilizado em conjunto com as

aplicações Java. Para trabalhar com o banco de dados, criando tabelas, inserindo dados, etc.,

utilizaremos o MySQL Toolkit, também disponível no site do banco de dados. A instalação é

simples e segue o mesmo procedimento de qualquer programa executável no ambiente Windows

e, por esse motivo, não serão dados maiores detalhes sobre sua instalação, mas será a

ferramenta utilizada nos exercícios.

Exercícios de Laboratório do módulo 5

Neste exercício criaremos um total de 4 tabelas no MySQL que serão utilizadas num aplicativo

Java que será criado posteriormente para conectar-se a elas. As tabelas, seus campos e tipos,

chave primárias e estrangeiras estão indicados abaixo.

Para criar essas tabelas, acesse o banco de dados MySQL utilizando o aplicativo MySQL

Administrator, que faz parte do MySQL Toolkit. Ao chamar o aplicativo, a janela abaixo se abre.

Clientes

ClienteID Int Chave primária ClienteNome Varchar ClienteEndereco Varchar ClienteTelefone Varchar

Títulos TituloID Int Chave primária Titulo Varchar Ano Int

Preco Float

CD CDID Int Chave primária TituloID Int Chave estrangeira Tipo Varchar

Pedidos NumeroPedido Int Chave primária ClienteID Int Chave estrangeira CDID Int Chave estrangeira DataPagamento Date Status Char

Page 36: 184L ALPOO Teoria Pratica

36

A conexão ALPOO foi previamente configurada. Nos computadores do laboratório, consulte o

professor para receber as informações necessárias à conexão. Uma vez inseridas as informações,

clique em OK. A janela principal do MySQL Administrator será aberta, como se vê na figura

abaixo.

A criação de tabelas exige uma base de dados para isso; no MySQL Administrator isso é feito

selecionando-se a opção Catalogs, na janela à esquerda. A seleção desse item apresenta as

bases de dados já existentes, como se observa abaixo.

Para criar uma nova base de dados, clique com o botão direito do mouse sobre a janela que

apresenta as bases de dados existentes e, na nova janela que se abre, selecione a opção Create

new schema, como destacado na figura a seguir:

Bases de dados já existentes

Page 37: 184L ALPOO Teoria Pratica

37

Surgirá a caixa de diálogo solicitando que informe o nome da nova base de dados, como se

observa a seguir. Digite o seu nome, por exemplo, para diferenciar das bases de dados dos

demais alunos.

Clique em OK para criar a base. Uma vez, criada a base, ela aparece juntos às existentes. Para

criarmos nossa primeira tabela, temos duas opções:

• Clicar sobre o nome da base recém criada e, com o botão direito do mouse, selecionar a

opção Create new table, ou;

• Clicarmos sobre o botão Create table.

Observe a seguir as possíveis opções de criação de uma tabela, destacadas em vermelho.

Qualquer que seja a seleção feita, uma janela surgirá, com diversos campos a serem preenchidos.

A figura abaixo apresenta como devemos definir nossa primeira tabela, que será a “Clientes”.

Page 38: 184L ALPOO Teoria Pratica

38

Para criar um campo, basta clicar duas vezes abaixo do nome: Column Name; fazendo isto, surge

uma caixa de texto com um cursor piscando, o que significa que o MySQL Administrator está

aguardando a entrada do nome do novo campo a ser criado. Siga esse procedimento para cada

novo campo que queira incluir na tabela. Por padrão do aplicativo, o primeiro campo criado já vem

com chave primária; como estamos criando o primeiro campo da tabela “Clientes” (ClientesID),

mantenha essa sugestão do programa. Para validar as informações inseridas, clique no botão

Apply Changes (aplicar alterações), destacado em vermelho, abaixo.

O MySQL Administrator ainda pedirá sua confirmação sobre a criação da tabela, apresentando as

configurações da mesma no formato SQL numa nova janela, como se observa abaixo. Confirme

clicando em Execute.

Finalizada esta seqüência a tabela estará criada, como se vê na próxima figura. Siga o mesmo

procedimento para a criação das tabelas “Titulos”, “CD” e “Pedidos”. Observação: desmarque a

opção de auto-incremento das chaves primárias das tabelas “Titulos” e “CD”.

Page 39: 184L ALPOO Teoria Pratica

39

Uma vez criadas todas as tabelas, precisamos agora ajustar alguns campos que funcionarão

como chave estrangeira, relacionando duas ou mais tabelas entre si. Para a criação de chaves

estrangeiras (caso das tabelas “CD” e “Pedidos”), siga este procedimento:

1. Edite a tabela em que deseja inserir uma chave estrangeira, neste exemplo usaremos a

tabela “CD”, que tem como chave estrangeira o campo “TituloID”. Selecione na caixa de

edição a aba Foreign Keys, e pressione o sinal de “+”, logo abaixo da caixa de área de

texto, à esquerda, como indicado na figura abaixo.

2. Surgirá uma pequena janela pedindo que informe o nome do campo que servirá de chave

estrangeira, como indicado abaixo. Digite o nome do campo “TituloID”, que já foi criado

nesta tabela; em seguida confirme pressionando o botão OK.

Page 40: 184L ALPOO Teoria Pratica

40

3. O campo “TituloID” passa a constar da caixa de área de texto. Agora, selecione a tabela

“Titulos” na caixa de texto Ref. Table, que fará o vínculo com esta tabela através do campo

de mesmo nome. Observe que na tabela abaixo da caixa Ref. Table surgem os nomes dos

campos vinculados de ambas as tabelas, indicando a criação da chave estrangeira, como

se observa na figura abaixo.

4. Selecione a opção Cascade para ambas as caixas de texto: On Delete e On Update, como

destacado na figura acima. Aplique as mudanças efetuadas na tabela “CD” clicando sobre

o botão Apply Changes e siga o mesmo procedimento para a criação das chaves

estrangeiras na tabela “Pedidos”.

A criação das tabelas está pronta; resta-nos agora popular estas tabelas, ou seja, inserir dados

nos diversos campos de cada tabela criada. Para isso, utilizaremos a ferramenta MySQL Query

Browser, uma interface gráfica amigável que facilitará a inserção dos dados nas tabelas. Ao

fazermos a chamada à ferramenta, uma caixa de diálogo surge, pedindo o nome da base de

dados que será utilizada, como se observa na figura abaixo. Informe o nome da base de dados e

pressione OK para entrar no MySQL Query Browser.

Page 41: 184L ALPOO Teoria Pratica

41

A janela que se abre está indicada abaixo, exibindo à direita todas as tabelas criadas na base de

dados deste exercício, bem como todas as bases de dados existentes no MySQL.

Para popular a tabela “Clientes” com vários scripts SQL de uma única vez, clique no item de menu

“Arquivo” e selecione a opção New Script Tab. Surgirá uma tela de script denominada Script 1,

como indicado na figura abaixo.

Page 42: 184L ALPOO Teoria Pratica

42

Copie e cole nessa área os comandos de inserção de dados a seguir: Insert into Clientes values (1,'Mike Barott','1756 7th St','(325)200-2378');

Insert into Clientes values (2,'Catherine Brannen','7927 Ave H','(218)354-1091');

Insert into Clientes values (3,'David Carroll','491 18th St','(563)668-4177');

Insert into Clientes values (4,'Donna Courville','7764 Ave K','(562)513-8534');

Insert into Clientes values (5,'Jack Dodd','4627 11th St','(779)601-7769');

Insert into Clientes values (6,'Joe Ferguson','3060 Ave K','(515)801-1337');

Insert into Clientes values (7,'Marvin Gilmer','7394 12th St','(371)439-3652');

Insert into Clientes values (8,'Matthew Hand','1756 18th St','(600)591-2392');

Insert into Clientes values (9,'Antonio Holder','8691 Ave I','(172)408-3976');

Insert into Clientes values (10,'John Johnson','1334 Ave J','(473)366-5094');

Insert into Clientes values (11,'Pam Kwon','4508 15th St','(292)166-5268');

Insert into Clientes values (12,'Manuel Machac','764 Ave U','(552)693-9213');

Insert into Clientes values (13,'Basil McMeans','7464 12th St','(745)195-8065');

Insert into Clientes values (14,'Dana Murphy','3934 10th St','(715)121-8090');

Insert into Clientes values (15,'Aubrey Patterson','7567 Ave Z','(230)325-6709');

Insert into Clientes values (16,'Loyd Ramsower','1230 18th St','(697)307-9647');

Insert into Clientes values (17,'Brenda Rowland','1067 21th St','(657)578-1850');

Insert into Clientes values (18,'James Simmons','5816 Ave W','(136)907-5726');

Insert into Clientes values (19,'Lydia Stratton','546 16th St','(304)589-2216');

Insert into Clientes values (20,'Sue Turman','1341 Ave A','(695)123-9553');

Uma vez colados os comandos SQL na área de scripts do MySQL Query, Browser, clique no

botão Execute, destacado em vermelho na figura acima, para inserir os dados. Siga o mesmo

procedimento para popular a tabela “Titulos” (apague os comandos anteriores antes de inserir

estes): Insert into Titulos values (1,'The Perfect Storm',2000,1.50);

Insert into Titulos values (42,'Fantasia',2000,0.40);

Insert into Titulos values (2,'Mission Impossible 2',2000,2.10);

Insert into Titulos values (4,'Terminator 2: Judgement Day',1991,2.80);

Insert into Titulos values (5,'The Patriot',2000,1.96);

Insert into Titulos values (6,'Any Given Sunday',1999,1.75);

Insert into Titulos values (7,'American Psycho',2000,1.89);

Insert into Titulos values (8,'American Beauty',1999,1.62);

Insert into Titulos values (9,'U-571',2000,1.89);

Insert into Titulos values (10,'Leon - The Professional',1994,2.10);

Insert into Titulos values (11,'The Sound of Music',1965,2.10);

Insert into Titulos values (13,'Magnolia',2000,2.10);

Insert into Titulos values (14,'High Fidelity',2000,2.20);

Insert into Titulos values (15,'The Princess Bride',1987,1.40);

Insert into Titulos values (16,'Toy Story 2',1995,2.80);

Insert into Titulos values (17,'The Little Mermaid 2',2000,2.10);

Insert into Titulos values (18,'Edward Scissorhands',1990,2.10);

Insert into Titulos values (19,'North by Northwest',1959,1.75);

Insert into Titulos values (20,'The Matrix',1999,1.75);

Insert into Titulos values (21,'Erin Brockovich',2000,1.89);

Insert into Titulos values (22,'Jurassic Park : Lost World',1993,3.78);

Insert into Titulos values (23,'Pitch Black',2000,1.89);

Insert into Titulos values (24,'Braveheart',1995,2.10);

Insert into Titulos values (25,'Saving Private Ryan',1999,2.45);

Insert into Titulos values (26,'The Shawshank Redemption',1994,1.40);

Insert into Titulos values (27,'Shanghai Noon',2000,2.10);

Page 43: 184L ALPOO Teoria Pratica

43

Insert into Titulos values (28,'28 Days',2000,1.75);

Insert into Titulos values (29,'Independence Day',1996,2.45);

Insert into Titulos values (30,'The Sopranos',1999,7.00);

Insert into Titulos values (31,'Fargo',1996,1.40);

Insert into Titulos values (32,'The Green Mile',1999,1.75);

Insert into Titulos values (34,'Final Destination',2000,1.75);

Insert into Titulos values (35,'Titan A. E.',2000,1.89);

Insert into Titulos values (36,'Mission to Mars',2000,2.10);

Insert into Titulos values (37,'Frequency',2000,1.75);

Insert into Titulos values (38,'The Nightmare Before Christmas',1993,2.10);

Insert into Titulos values (39,'The Rocky Horror Picture Show',1975,2.10);

Insert into Titulos values (40,'Chicken Run',2000,1.89);

Insert into Titulos values (41,'Gladiator',2000,1.80);

Insert into Titulos values (3,'Men in Black',1997,2.40);

Insert into Titulos values (12,'This is Spinal Tap',1984,1.89);

Insert into Titulos values (33,'The Bridge over the River Kwai',1957,1.75);

Siga o mesmo procedimento para popular a tabela “CD”, inserindo no Script 1 os comandos

abaixo: Insert into CD values (12345,1,'DVD');

Insert into CD values (12352,42,'DVD');

Insert into CD values (12357,2,'DVD');

Insert into CD values (12364,4,'VHS');

Insert into CD values (12378,5,'DVD');

Insert into CD values (12389,6,'VHS');

Insert into CD values (12400,7,'VHS');

Insert into CD values (12405,8,'DVD');

Insert into CD values (12414,9,'DVD');

Insert into CD values (12426,10,'DVD');

Insert into CD values (12437,11,'DVD');

Insert into CD values (12446,13,'VHS');

Insert into CD values (12453,14,'DVD');

Insert into CD values (12465,15,'VHS');

Insert into CD values (12479,16,'DVD');

Insert into CD values (12487,17,'VHS');

Insert into CD values (12492,18,'DVD');

Insert into CD values (12498,19,'VHS');

Insert into CD values (12506,20,'DVD');

Insert into CD values (12513,21,'VHS');

Insert into CD values (12514,21,'DVD');

Insert into CD values (12526,21,'DVD');

Insert into CD values (12527,22,'DVD');

Insert into CD values (12528,22,'VHS');

Insert into CD values (12535,23,'DVD');

Insert into CD values (12544,24,'VHS');

Insert into CD values (12550,25,'VHS');

Insert into CD values (12557,26,'DVD');

Insert into CD values (12558,26,'DVD');

Insert into CD values (12568,27,'VHS');

Insert into CD values (12577,28,'VHS');

Insert into CD values (12583,29,'VHS');

Insert into CD values (12592,30,'VHS');

Insert into CD values (12603,31,'VHS');

Insert into CD values (12612,32,'DVD');

Page 44: 184L ALPOO Teoria Pratica

44

Insert into CD values (12613,32,'VHS');

Insert into CD values (12626,34,'VHS');

Insert into CD values (12639,35,'VHS');

Insert into CD values (12650,36,'DVD');

Insert into CD values (12655,37,'VHS');

Insert into CD values (12667,38,'DVD');

Insert into CD values (12674,39,'DVD');

Insert into CD values (12679,40,'VHS');

Insert into CD values (12691,41,'DVD');

Insert into CD values (12700,3,'VHS');

Insert into CD values (12713,12,'DVD');

Insert into CD values (12716,33,'VHS');

Insert into CD values (12725,1,'VHS');

Insert into CD values (12726,40,'VHS');

Insert into CD values (12727,6,'VHS');

Insert into CD values (12728,30,'VHS');

Insert into CD values (12736,15,'VHS');

Insert into CD values (12743,33,'VHS');

Finalmente, para a tabela “Pedidos”, insira no Script 1 os comandos abaixo: Insert into Pedidos values (1,1,12345,date'2009-08-05','I');

Insert into Pedidos values (2,1,12352,date'2009-08-05','I');

Insert into Pedidos values (3,2,12364,date'2009-08-07','I');

Insert into Pedidos values (4,3,12674,date'2009-08-08','I');

Insert into Pedidos values (5,4,12550,date'2009-08-09','I');

Insert into Pedidos values (6,5,12592,date'2009-08-09','I');

Insert into Pedidos values (7,6,12639,date'2009-08-09','I');

Insert into Pedidos values (8,7,12716,date'2009-08-12','I');

Insert into Pedidos values (9,8,12713,date'2009-08-12','I');

Insert into Pedidos values (10,9,12437,date'2009-08-13','I');

Insert into Pedidos values (11,10,12655,date'2009-08-14','I');

Insert into Pedidos values (12,10,12716,date'2009-08-14','I');

Insert into Pedidos values (13,11,12389,date'2009-08-15','I');

Insert into Pedidos values (14,14,12352,date'2009-08-16','I');

Insert into Pedidos values (15,16,12400,date'2009-08-17','I');

Insert into Pedidos values (16,17,12479,date'2009-08-17','I');

Insert into Pedidos values (17,18,12453,date'2009-08-18','I');

Insert into Pedidos values (18,19,12592,date'2009-08-19','I');

Insert into Pedidos values (19,20,12713,date'2009-08-20','I');

Módulo 6 – Manipulação de dados com linguagem SQL

Princípios das aplicações JDBC

Uma aplicação que necessite acessar um banco de dados deve utilizar várias classes e interfaces

da API JDBC para realizar as tarefas desejadas. A figura abaixo apresenta os principais

elementos do JDBC, destacando as classes, as interfaces e os métodos mais usados.

A execução de praticamente todas as operações possíveis pode provocar o lançamento da

exceção java.sql.SQLException, tornando obrigatório o seu tratamento.

Page 45: 184L ALPOO Teoria Pratica

45

Conexão

Considerando que o driver necessário para carregar o banco de dados encontra-se carregado, é

utilizada uma das versões do método getConnection da classe DriverManager para obter-se

uma conexão com o banco, tal como:

Connection connection = DriverManager.getConnection(url, usuario, senha);

Uma aplicação pode possuir uma ou mais conexões com o mesmo ou com diferentes bancos de

dados. Connection é, portanto, uma interface que representa uma sessão entre uma aplicação e

um banco de dados. Abaixo, alguns de seus métodos:

• void close(): fecha a conexão com o banco de dados, liberando os recursos utilizados.

• void commit(): torna permanentes as alterações realizadas desde o último commit, ou

rollback.

• Statement createStatement(): cria objeto para envio de comandos SQL estáticos ao

banco de dados, opcionalmente com os tipos de navegação e concorrência indicados.

• boolean getAutoCommit(): verifica se o banco de dados está ou não está em modo

auto-commit.

• DatabaseMetaData getMetData(): obtém informações sobre o banco de dados e a

conexão em uso.

• boolean isReadOnly(): verifica se o banco de dados está ou não está em modo

somente leitura.

• CallableStatement prepareCall(String sql): cria objeto apropriado para

chamada de procedimentos armazenados.

• PreparedStatement prepareStatement(String sql): cria objeto para envio de

comandos SQL parametrizados ao banco de dados, opcionalmente com os tipos de

navegação e concorrência indicados.

DriverManager

getConnection()

DataSource

getConnection()

Connection

createStatement()

prepareCall()

prepareStatement()

CallableStatement

set...()

execute()

get...()

PreparedStatement

set...()

executeUpdate()

execute()

getResultSet()

executeQuery()

Statement

executeUpdate()

getResultSet()

executeQuery()

ResultSet

get...()

next()

set...()

Page 46: 184L ALPOO Teoria Pratica

46

• void rollback(): cancela as alterações efetuadas desde o último commit ou rollback

realizado.

• void setAutoCommit(boolean estado): ajusta o estado de auto-commit do banco

de dados.

• void setReadOnly(boolean estado): ajusta o estado de leitura exclusiva do banco

de dados.

Como padrão, uma conexão é estabelecida em modo auto-commit, que consolida as alterações

realizadas a cada operação. Uma vez que a conexão for estabelecida com sucesso, as operações

sobre os dados ou sobre a estrutura do banco de dados poderão ser realizadas de quatro

maneiras diferentes: interativa, em lote, preparada (otimizada) ou acionamento de stored

procedures (procedimentos armazenados).

Se o modo de operação for ajustado para transacional com setAutoCommit(false), as

operações que provocam alterações deverão ser confirmadas por meio de chamadas ao método

commit() ou canceladas com rollback().

Operação interativa

A operação interativa se faz por meio da interface java.sql.Statement, a qual permite o envio

de comandos SQL estáticos, isto é, não parametrizados, para o banco de dados e o retorno dos

resultados obtidos. Por meio do método createStatement(), disponível na interface

Connection, é possível obter um objeto que representa esta interface, como segue:

Statement statement = connection.createStatement();

O padrão dos objetos Statement criados é TYPE_FORWARD_ONLY e CONCUR_READ_ONLY, que

possibilita apenas navegação à frente nos resultados obtidos, com leitura concorrente dos

registros. Abaixo, alguns métodos da interface Statement:

• void addBatch(String): adiciona o comando dado à lista de comandos corrente.

• void cancel(): cancela a operação SQL em andamento, se o cancelamento for

suportado pelo driver e pelo banco de dados.

• void clearBatch(): elimina todos os comandos da lista de comandos corrente.

• void close(): libera todos os recursos utilizados por este objeto.

• boolean execute(String sql): executa o comando SQL dado, indicando se existe o

retorno de resultados.

• int[] executeBatch(): submete a lista de comandos para execução.

• ResultSet executeQuery(String sql): executa o comando SELECT dado,

retornando um objeto ResultSet.

• int executeUpdate(String sql): executa o comando SQL dado, retornando o

número de linhas afetadas;

Page 47: 184L ALPOO Teoria Pratica

47

• boolean getMoreResults(): indica se existem mais objetos ResultSet retornados

pela última operação realizada por este Statement.

• ResultSet getResultSet(): retorna o objeto ResultSet correspondente aos

resultados obtidos.

Operação em lote

Ainda utilizando objetos do tipo Statement, é possível definir uma lista ou lote de comandos

(batch), o qual será enviado e executado de uma vez pelo banco de dados, acelerando o

processamento de tarefas mais complexas.

Neste caso, emprega-se o método addBatch(String) para adicionar um comando à lista

corrente. Quando o lote de comandos estiver pronto, sua execução pode ser solicitada por meio

de executeBatch(), que retorna um array de valores inteiros correspondentes ao número de

linhas afetadas por cada comando do lote. Finalmente, os comandos do lote podem ser

descartados com clearBatch(), o que permite reutilizar o objeto Statement para o preparo e

para a execução de outro lote de comandos.

Stored Procedure (operação preparada)

Um objeto do tipo PreparedStatement permite definir um comando SQL que será pré-

compilado no banco de dados, permitindo sua execução eficiente. O comando SQL preparado

pode conter um ou mais parâmetros de entrada representados pelo símbolo de interrogação (?),

que é conhecido como marcador de parâmetro. Os parâmetros de entrada devem ser fornecidos

antes de cada execução do comando SQL preparado.

O componente JTable

O JTable é o componente da família Swing destinado a exibição tabular de dados. Como todo

componente Swing, as responsabilidades de apresentação dos dados e interface com o usuário

ficam a cargo do próprio JTable, enquanto as demais, relacionadas ao armazenamento e à

obtenção dos dados exibidos são encontradas em modelos específicos de dados. Isso permite

que a representação interna dos dados e a sua forma de obtenção sejam mantidas separadas das

tarefas de apresentação. A seguir, um resumo das principais características deste componente:

• AUTO_RESIZE_ALL_COLUMNS ...: constantes para redimensionamento da tabela.

• JTable(TableModel tm): constrói uma tabela para exibir o conteúdo e os nomes de

colunas, usando o modelo dado.

• void clearSelection(): retira a seleção corrente.

• int columnAtPoint(Point p), int rowAtPoint(Point p): retorna o índice da

coluna ou linha sob o ponto dado.

Page 48: 184L ALPOO Teoria Pratica

48

• boolean editCellAt(int linha, int coluna): inicia a edição da célula indicada

por (linha, coluna), se possível.

• TableCellEditor getCellEditor(int linha, int coluna): retorna o editor de

célula apropriado para a célula especificada.

• Class<?> getColumnClass(int coluna): retorna a classe dos elementos da coluna

indicada.

• int getColumnCount(), int getRowCount(): retorna o número de colunas ou linhas

da tabela.

• int getEditionColumn(), getEditionRow(): obtém o número da coluna ou linha

em edição.

• Color getGridColor(), getSelectionBackground(), getSelectionForeground():

retorna, respectivamente, as cores usadas para o desenho da grade, do fundo e do

primeiro plano das seleções.

• TableModel getModel(): obtém o modelo de dados da tabela.

• int getSelectedColumn(), getSelectedRow(): obtém o índice da coluna e da linha

selecionada.

• Object getValueAt(int linha, int coluna): retorna o conteúdo da célula dado

por (linha, coluna).

• boolean isCellEditable(int linha, int coluna), isCellSelected(int

linha, int coluna): determina se a célula é ou não editável ou se está ou não

selecionada.

• boolean isEditing(): retorna se existe ou não alguma célula em edição.

• boolean isShowGrid(): retorna se a grade é exibida ou não.

Modelo de dados para JTable

Pra que seja possível maior controle sobre o conteúdo e as características de um JTable,

sugere-se a construção de um modelo de dados próprio. Isto pode ser feito estendendo a classe

javax.swing.table.AbstractTableModel, que define as funcionalidades padronizadas de

um modelo de dados para tabelas, definidas pela interface javax.swing.table.TableModel.

Abaixo, algumas características da interface TableModel, que especifica como um JTable

acessará seu modelo de dados:

• void addTableModelListener(TableModelListener tml): adiciona um

observador que será notificado com as mudanças ocorridas com o modelo de dados.

• int getColumnCount(): retorna a quantidade de colunas ao modelo de dados.

• String getColumnName(int coluna): retorna o nome da coluna indicada.

• int getRowCount(): retorna a quantidade de linhas do modelo de dados.

Page 49: 184L ALPOO Teoria Pratica

49

• Object getValueAt(int linha, int coluna): retorna o objeto contido na linha e

na coluna do modelo de dados.

• void setValueAt(Object o, int linha, int coluna): ajusta com conteúdo a

célula indicada com o objeto fornecido.

Exercícios de Laboratório do módulo 6

1. Criar uma classe Java para facilitar a utilização de um banco de dados qualquer. A conexão

com um banco de dados por meio da JDBC emprega algum código repetitivo, assim uma classe

de conexão com métodos para estabelecer e para encerrar a conexão com um banco de dados

facilita o trabalho do programador. A classe deve seguir o padrão de projeto Singleton. Singleton

trata da criação de objetos; através dele, garantimos a existência de uma única instância de uma

classe, mantendo um único ponto de acesso dentro do sistema. E qual a utilidade disso? Simples:

objetos responsáveis pela utilização racional das conexões feitas a um banco de dados, ou pelo

gerenciamento dos documentos a serem impressos em uma impressora, entre outras aplicações,

não devem ser instanciados mais de uma vez. Imagine o problema que seria criar duas instâncias

em uma aplicação que lidam com uma mesma impressora ao mesmo tempo: isso geraria

inúmeros problemas. Para prevenir que uma classe seja instanciada mais de uma vez, criamos

um construtor privado dentro da classe. Dessa forma, a classe só pode ser instanciada dentro de

algum de seus próprios métodos. Uma vez que só podemos instanciar a classe dentro de um

método, temos que fazer com que esse método nos garanta que apenas um objeto seja criado.

Para isso, criaremos uma variável estática que guardará o único objeto criado. Caso a variável

esteja nula, o método instanciará a classe e retornará o objeto ao usuário. Caso haja uma

instância, o método apenas retorna o objeto existente. Abaixo a listagem da classe ConectaBD,

utilizando o padrão de projeto Singleton: package br.unip.cc4si4.ALPOO;

import java.sql.*;

public class ConectaBD {

//Variável estática que guardará a instância da classe ConectaBD.

private static ConectaBD instancia = null;

//Variavel para conexao à base de dados.

private Connection conexao = null;

//Variável para determinação da quantidade de clientes conectados à base.

private int quantidadeDeConexoes = 0;

//Construtor privado

private ConectaBD(){

try{

//Propriedades da conexão à base de dados:

//Especifica o driver JDBC utilizado.

String bdDriver = "com.mysql.jdbc.Driver";

//Cria a url de acesso ao banco MySQL fornecendo:

//o driver jdbc, o nome banco, o endereço (localhost

//neste caso) com a porta de acesso e a base de

//dados criada na aula passada.

String bdUrl = "jdbc:mysql://localhost:3306/alpoo";

//Define o usuário de acesso ao banco de dados.

String bdUsuario = "root";

//Define a senha de acesso ao banco de dados.

String bdSenha = "";

//Carregamento dinâmico do driver por meio do método

//forName(String) da classe Class.

Class.forName(bdDriver);

//Acesso à base de dados com usuário e senha.

Page 50: 184L ALPOO Teoria Pratica

50

if(bdUsuario.length() != 0)

conexao = DriverManager.getConnection(bdUrl, bdUsuario, bdSenha);

//Acesso direto à base de dados (sem usuário e senha).

else

conexao = DriverManager.getConnection(bdUrl);

System.out.println("BD[Conexao OK]");

}

//Caso haja algum problema com o driver utilizado,

//será gerada uma exceção.

catch(ClassNotFoundException cnfe){

System.out.println("Driver JDBC nao encontrado!");

}

//Caso haja algum problema com o comando SQL,

//será gerada uma exceção.

catch(SQLException sqle){

System.out.println("Problemas na conexao com a base de dados!");

}

}

//Método estático que retorna uma instância unica

//da classe ConectaBD. Padrão Singleton.

public static ConectaBD getInstancia(){

if(instancia == null)

instancia = new ConectaBD();

return instancia;

}

//Método para retornar a conexão disponível,

//contabilizando a quantidade de clientes

//acessando a base de dados.

public Connection getConexao(){

if(conexao == null)

throw new RuntimeException("conexao == null");

quantidadeDeConexoes++;

System.out.println("BD[Cliente conectado]");

return conexao;

}

//Método que decrementa o número de clientes

//conectados à base, efetuando o encerramento

//da conexão quando não restarem mais clientes.

public void encerraConexao(){

System.out.println("BD[Cliente desconectado]");

quantidadeDeConexoes--;

if(quantidadeDeConexoes > 0)

return;

try{

conexao.close();

instancia = null;

System.out.println("BD[Conexao encerrada]");

conexao = null;

} catch(SQLException sqle){

System.out.println("Problemas ao desconectar com a base de dados!");

}

}

}

2. A consulta aos dados de uma tabela em um banco de dados e sua exibição em um componente

JTable é uma estratégia elegante e conveniente para a construção de aplicações, mas exige a

adaptação do modelo de dados da tabela para possibilitar tal operação via JDBC. A classe

ModeloDeTabela, cuja listagem segue abaixo, é capaz de obter seu conteúdo de um banco de

dados conforme uma consulta (query) especificada. package br.unip.cc4si4.ALPOO;

import java.sql.*;

import java.util.ArrayList;

import javax.swing.table.AbstractTableModel;

public class ModeloDeTabela extends AbstractTableModel {

//Atributos da classe ModeloDeTabela.

private ArrayList<Object[]> linhas;

private String[] colunas;

private Statement comando;

private String consulta;

//Construtor da classe.

public ModeloDeTabela(String consulta){

try{

//Tenta efetuar uma conexão ao banco de dados.

comando = ConectaBD.getInstancia().getConexao().createStatement();

Page 51: 184L ALPOO Teoria Pratica

51

//Se a conexão teve êxito, leia a consulta SQL desejada.

this.consulta = consulta;

//Instancia o atributo linhas, que receberá um array de objetos

//que é o resultado obtido na consulta SQL enviada ao banco.

linhas = new ArrayList<Object[]>();

//Chama o método carregaDados, responsável pela estruturação dos

//dados obtidos na consulta SQL enviada ao banco de dados.

carregaDados();

//Se houve algum problema na conexão ao banco de dados, exibe erro.

}catch(Exception e){

System.out.println("Modelo de Dados["+e.toString()+"]");

}

}

//Método que fará o carregamento dos dados.

public void carregaDados(){

System.out.println("Modelo de Dados[carregaDados()]");

try{

//Tenta ler a massa de dados obtida apos a execução

//da consulta SQL ao banco de dados.

ResultSet resultado = comando.executeQuery(consulta);

//Obtém o número, tipo e propriedades da massa de dados.

ResultSetMetaData rsmd = resultado.getMetaData();

//Obtém a quantidade de colunas da massa de dados.

int numeroDeColunas = rsmd.getColumnCount();

//Instancia o atributo colunas.

colunas = new String[numeroDeColunas];

//Para a quantidade de colunas obtida na massa de dados,

//obtenha o nome de cada coluna.

for(int i=1; i<numeroDeColunas; i++)

colunas[i] = rsmd.getColumnName(i);

//Apague o conteúdo do atributo linhas, haja algo ou não.

linhas.clear();

//Enquanto houver linhas não lidas na massa de dados...

while(resultado.next()){

//...crie um objeto linha, do tipo array, com a mesma

//quantidade de colunas obtida na massa de dados.

Object linha[] = new Object[numeroDeColunas];

//Para cada coluna lida...

for(int i=0; i<numeroDeColunas; i++)

//...armazene o seu conteúdo na posição i da linha.

linha[i] = resultado.getObject(i+1);

//E adicione essa linha ao atributo privado linhas,

//que é quem armazena todas as linhas obtidas, ou seja,

//toda a massa de dados da consulta.

linhas.add(linha);

}

//Encerra a leitura da massa de dados.

resultado.close();

//Atualiza os dados exibidos na JTable

fireTableDataChanged();

//Se houve algum problema na leitura dos dados, exibe erro.

}catch(Exception e){

System.out.println("Modelo de Dados["+e.toString()+"]");

}

}

//Métodos de implementação obrigatória

//da interface AbstractTableModel:

//Obtém a quantidade de colunas.

public int getColumnCount(){

return colunas.length;

}

//Obtém a quantidade de linhas.

public int getRowCount(){

return linhas.size();

}

//Obtém o conteúdo de uma célula (coluna x linha).

public Object getValueAt(int linha, int coluna){

try{

Object celula[] = linhas.get(linha);

return celula[coluna];

}catch(Exception e){

System.out.println("Modelo de Dados["+e.toString()+"]");

}

return null;

}

}

3. Construir uma aplicação que componha um frame contendo um JPanel e este um JTable, o

qual exibe os dados da tabela “Clientes” por meio de uma consulta SQL. O modelo de dados do

Page 52: 184L ALPOO Teoria Pratica

52

JTable já foi montado no exercício 2. A consulta e o título da janela são fornecidos na própria

classe. A conexão com o banco de dados fica a cargo da classe ConectaBD, feita no exercício 1.

A detecção de eventos fica por conta de uma classe observadora específica para isso. Abaixo a

listagem da classe JanelaExibeDados: package br.unip.cc4si4.ALPOO;

import java.awt.*;

import javax.swing.*;

public class JanelaExibeDados extends JFrame {

//Construtor

public JanelaExibeDados(){

//Cria e insere um objeto PainelExibeClientes.

PainelExibeClientes pec = new PainelExibeClientes();

this.getContentPane().add(pec, BorderLayout.CENTER);

//Instancia o observador deste frame.

ObservadorDoPainelExibeClientes odpec = new ObservadorDoPainelExibeClientes(this);

this.addWindowListener(odpec);

//Fecha o frame.

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

pack();

}

//Método para encerrar a conexão com o banco de dados.

public void encerrarConexao(){

ConectaBD.getInstancia().encerraConexao();

}

//Método executável.

public static void main(String[] args){

JanelaExibeDados jed = new JanelaExibeDados();

jed.setTitle("Clientes");

jed.setVisible(true);

}

}

A seguir, a listagem da classe que compõe o painel, PainelExibeClientes: package br.unip.cc4si4.ALPOO;

import java.awt.*;

import javax.swing.*;

public class PainelExibeClientes extends JPanel {

//Atributos da classe.

private JTable tabela;

private ModeloDeTabela modelo;

private JButton botaoatualiza;

//Construtor

public PainelExibeClientes(){

//Seleciona gerenciador de leiaute.

BorderLayout borderlayout = new BorderLayout();

this.setLayout(borderlayout);

//Requisitos da consulta.

String consulta = null;

consulta = "select clienteid, clientenome, clienteendereco, " +

"clientetelefone from clientes";

//Cria um objeto modelo de dados.

modelo = new ModeloDeTabela(consulta);

//E utiliza este modelo para criar um JTable.

tabela = new JTable(modelo);

//Cria e insere um painel rolante ao painel.

JScrollPane sp = new JScrollPane(tabela);

this.add(sp, BorderLayout.CENTER);

//Cria e insere um botao ao painel.

botaoatualiza = new JButton("Atualizar");

botaoatualiza.setSize(5, 2);

this.add(botaoatualiza, BorderLayout.SOUTH);

//Instancia o observador deste painel.

ObservadorDoPainelExibeClientes odpec = new ObservadorDoPainelExibeClientes(this);

//Gera evento para atualizacao dos dados da tabela.

botaoatualiza.addActionListener(odpec);

}

//Método para carregar a tabela Clientes.

public void carregarClientes(){

modelo.carregaDados();

}

}

E a listagem da classe de observação de eventos, ObservadorDoPainelExibeClientes:

Page 53: 184L ALPOO Teoria Pratica

53

package br.unip.cc4si4.ALPOO;

import java.awt.event.ActionListener;

import java.awt.event.WindowListener;

import java.awt.event.ActionEvent;

import java.awt.event.WindowEvent;

public class ObservadorDoPainelExibeClientes implements ActionListener, WindowListener {

//Atributos privados.

private PainelExibeClientes pec;

private JanelaExibeDados jed;

//Construtores, um para o painel, outro para o frame.

public ObservadorDoPainelExibeClientes(PainelExibeClientes painel){

pec = painel;

}

public ObservadorDoPainelExibeClientes(JanelaExibeDados janela){

jed = janela;

}

//Este método é chamado pelo sistema em tempo de execução.

//Você deve adicionar o código a ser executado

//em resposta à carga da tabela no painel.

public void actionPerformed(ActionEvent evento){

//Início do seu código:

pec.carregarClientes();

}

//Este método é chamado pelo sistema em tempo de execução.

//Você deve adicionar o código a ser executado

//em resposta ao fechamento do frame.

public void windowClosing(WindowEvent e){

//Início do seu código:

jed.encerrarConexao();

}

//Métodos de implementação obrigatória, são necessários

//para que a classe não gere erros e compile corretamente,

//mas não serão utilizados para este exemplo.

public void windowDeiconified(WindowEvent e){

System.out.println(e.toString());

}

public void windowIconified(WindowEvent e){

System.out.println(e.toString());

}

public void windowActivated(WindowEvent e){

System.out.println(e.toString());

}

public void windowDeactivated(WindowEvent e){

System.out.println(e.toString());

}

public void windowClosed(WindowEvent e){

System.out.println(e.toString());

}

public void windowOpened(WindowEvent e){

System.out.println(e.toString());

}

}

4. Desafio: construir uma aplicação contendo um frame e, dentro deste, quatro painéis, um para

cada uma das quatro tabelas criadas, mas agora os dados devem ser exibidos em caixas de texto

e deve haver dois botões dentro de cada painel: um para avançar e outro para retroceder na

massa de dados obtida. Montar no frame uma barra de menu que controle o painel a ser exibido.

Módulo 7 – MVC: Model View Controller

O padrão de projeto Modelo-Visão-Controlador foi inventado para a linguagem Smalltalk com o

objetivo de desacoplar a interface gráfica de uma aplicação do código que realmente executa o

trabalho. Este padrão se tornou tão importante que afetou completamente o código no qual o

Smalltalk foi construído, sendo aplicável também a outras linguagens orientadas a objeto.

Page 54: 184L ALPOO Teoria Pratica

54

O Modelo

No MVC o Modelo é o código que realiza alguma tarefa. Ele é estruturado sem necessariamente

haver uma preocupação sobre como ele “se parece e se sente” quando apresentado ao usuário.

Possui uma interface puramente funcional, o que significa que contém um conjunto de funções

públicas que podem ser utilizadas para alcançar toda a sua funcionalidade. Algumas dessas

funções são métodos de consulta que permitem a um “usuário” obter informação sobre o estado

atual do modelo. Entretanto, um modelo tem que estar apto a “registrar” visões e tem que estar

apto a “notificar” todas as suas visões registradas quando qualquer uma de suas funções força

uma mudança em seu estado. Em Java, um Modelo consiste de uma ou mais classes que

extendem a classe java.util.Observable (observado). Esta superclasse fornecerá toda a

infraestrutura de registro / notificação necessária para suportar um conjunto de visões.

Muitas Visões

Um modelo em MVC pode conter diversas visões. Dois exemplos seriam: a visão de linhas e

colunas de uma planilha e a visão de um gráfico de pizza de alguma coluna dessa mesma

planilha. Uma visão fornece componentes GUI para um modelo. Quando um usuário manipula a

visão de um modelo, essa visão informa um controlador da mudança desejada pelo usuário. Em

Java, as visões são construídas com componentes AWT ou Swing. Entretanto, as visões têm que

representar a interface java.util.Observer (observador).

Muitos controladores

As visões em MVC estão associadas a controladores que atualizam o model sempre que

necessário, à medida que um usuário interage com a visão associada. Obviamente, o modelo

notificará todas as visões registradas que uma mudança ocorreu e então elas atualizarão aquilo

que elas exibem ao usuário da maneira apropriada. Em Java, os controladores são os

observadores da estrutura de eventos da linguagem.

Como tudo isso funciona

Primeiro você escreve um modelo que extenda a classe java.util.Observable. Você fornece

seus métodos acessores (getX()) de classe para obterem informação acerca de seu estado

atual e codifica seus métodos modificadores (setX()) para atualizar o estado. Cada método

modificador deve chamar um método do tipo setChanged() e um ou outro método

notifyObservers() depois que ele realmente alterou o estado. Os métodos

notifyObservers() enviarão uma mensagem de atualização a cada observador registrado

(visão). Existem versões desse método que permitem que você forneça informação adicional

sobre a mudança.

Depois você pode criar uma ou mais visões. Cada visão deve representar a interface

java.util.Observer e portanto codificar o método de atualização. O objeto no segundo

Page 55: 184L ALPOO Teoria Pratica

55

parâmetro do método será usado para receber informação adicional, se fornecida, como se

observa a seguir: interface Observer{

void update(Observable t, Object o);

}

A visão deve representar o método de atualização consultando o modelo (na verdade, o

Observable t) para as mudanças que ele necessita e depois efetuar ela própria as alterações

apropriadas.

A visão também precisa se registrar junto ao modelo que ela deseja observar, enviando-lhe a

mensagem addObserver. O modelo notificará todas as visões registradas, de modo que ele

possa atualizá-las posteriormente.

O motivo para que um modelo extenda a classe java.util.Observable é que esta classe

fornece toda a infraestrutura necessária para registrar e notificar um modelo, portanto você não

terá que codificar nada disso e pode se concentrar na funcionalidade de sua aplicação.

Um modelo pode ter diversas visões. O MVC foi criado especificamente para permitir isso.

Também, uma visão pode se registrar em diversos modelos e obter atualizações de cada um. Não

é preciso construir seu modelo de modo que ele seja um único Observable (observado). Ao

contrário, diversas partes do modelo podem ser Observable separadamente, cada parte com

seus próprios Observers (observadores).

Exercícios de Laboratório do módulo 7

1. Neste exercício temos um modelo que é realmente uma classe muito simples de ser codificada.

Ela encapsula a idéia de uma temperatura em graus Celsius e em Farenheit. Queremos obter

dessa classe uma temperatura em ambas as unidades de medida de temperatura bem como

alterar a temperatura nessas mesmas unidades de medida. Observe ainda que não existem

elementos gráficos aqui, mas temos alguma infraestrutura, nomeadamente as chamadas

setChanged() e notifyObservers() nos métodos modificadores. Abaixo a listagem da

classe ModeloDeTemperatura:

package br.unip.cc4si4.ALPOO;

import java.util.Observable;

public class ModeloDeTemperatura extends Observable {

//Atributo privado da classe.

private double temperaturaFarenheit = 32.0;

//Método para leitura da temperatura em Farenheit.

public double getFarenheit(){

return temperaturaFarenheit;

}

//Método para leitura da temperatura em Celsius

//a partir da conversão da temperatura em Farenheit.

public double getCelsius(){

return (temperaturaFarenheit - 32.0)*5.0/9.0;

}

//Método para mudar a temperatura em Farenheit.

public void setFarenheit(double tempF){

//Determina a nova temperatura.

temperaturaFarenheit = tempF;

//Marca a instância desta classe, que é do tipo observada,

//como tendo sido alterada.

setChanged();

Page 56: 184L ALPOO Teoria Pratica

56

//Como a instância desta classe foi alterada,

//notifique todos os seus observadores.

notifyObservers();

}

//Método para mudar a temperatura em Celsius

//a partir da conversão da temperatura em Farenheit.

public void setCelsius(double tempC){

temperaturaFarenheit = tempC*5.0/9.0 + 32.0;

setChanged();

notifyObservers();

}

}

Page 57: 184L ALPOO Teoria Pratica

57

Page 58: 184L ALPOO Teoria Pratica

58