Java Como Programar [8 Edição]
 9788576055631

Table of contents :
Prefácio......Page 19
Antes de você começar......Page 29
1. Introdução aos computadores, à Internet e à World Wide Web......Page 33
1.1 Introdução......Page 34
1.3 Organização do computador......Page 35
1.5 Computação pessoal distribuída e computação cliente/servidor......Page 36
1.7 Linguagens de máquina, linguagens assembly e linguagens de alto nível......Page 37
1.9 História do Java......Page 38
1.11 Fortran, Cobol, Pascal e Ada......Page 39
1.13 Ambiente típico de desenvolvimento Java......Page 40
1.15 Testando um aplicativo Java......Page 43
1.16 Estudo de caso de engenharia de software: introdução à tecnologia de objetos e à UML......Page 47
1.17 Web 2.0......Page 49
1.18 Tecnologias de software......Page 50
1.20 Recursos da Web......Page 51
2. Introdução aos aplicativos Java......Page 60
2.2 Nosso primeiro programa Java: imprimindo uma linha de texto......Page 61
2.3 Modificando nosso primeiro programa Java......Page 66
2.4 Exibindo texto com printf......Page 67
2.5 Outro aplicativo: somando inteiros......Page 68
2.6 Conceitos de memória......Page 72
2.7 Aritmética......Page 73
2.8 Tomada de decisão: operadores de igualdade e operadores relacionais......Page 75
2.9 Conclusão......Page 79
3. Introdução a classes e objetos......Page 88
3.2 Classes, objetos, métodos e variáveis de instância......Page 89
3.3 Declarando uma classe com um método e instanciando um objeto de uma classe......Page 90
3.4 Declarando um método com um parâmetro......Page 93
3.5 Variáveis de instância, métodos set e get......Page 95
3.6 Tipos primitivos versus tipos por referência......Page 99
3.7 Inicializando objetos com construtores......Page 100
3.8 Números de ponto flutuante e tipo double......Page 102
3.9 (Opcional) Estudo de caso de GUI e imagens gráficas: utilizando caixas de diálogo......Page 105
3.10 Conclusão......Page 107
4. Instruções de controle: Parte I......Page 113
4.3 Pseudocódigo......Page 114
4.4 Estruturas de controle......Page 115
4.5 A instrução de seleção única if......Page 116
4.6 A instrução de seleção dupla if...else......Page 117
4.7 A instrução de repetição while......Page 120
4.8 Formulando algoritmos: repetição controlada por contador......Page 121
4.9 Formulando algoritmos : repetição controlada por sentinela......Page 125
4.10 Formulando algoritmos: instruções de controle aninhadas......Page 131
4.12 Operadores de incremento e decremento......Page 134
4.14 (Opcional) Estudo de caso de GUI e imagens gráficas: criando desenhos simples......Page 137
4.15 Conclusão......Page 140
5. Instruções de controle: Parte 2......Page 151
5.2 Princípios básicos de repetição controlada por contador......Page 152
5.3 Instrução de repetição for......Page 153
5.4 Exemplos com a estrutura for......Page 156
5.5 Instrução de repetição do…while......Page 159
5.6 A estrutura de seleção múltipla switch......Page 161
5.7 Instruções break e continue......Page 166
5.8 Operadores lógicos......Page 168
5.9 Resumo de programação estruturada......Page 172
5.10 (Opcional) Estudo de caso de GUI e imagens gráficas: desenhando retângulos e ovais......Page 176
5.11 Conclusão......Page 179
6. Métodos: uma visão mais aprofundada......Page 186
6.2 Módulos de programa em Java......Page 187
6.3 Métodos static, campos static e classe Math......Page 188
6.4 Declarando métodos com múltiplos parâmetros......Page 190
6.6 Pilha de chamadas de método e registros de ativação......Page 193
6.7 Promoção e coerção de argumentos......Page 194
6.8 Pacotes da Java API......Page 195
6.9 Estudo de caso: geração de números aleatórios......Page 196
6.10 Estudo de caso: um jogo de azar; introdução a enumerações......Page 200
6.11 Escopo das declarações......Page 204
6.12 Sobrecarga de método......Page 206
6.13 (Opcional) Estudo de caso de GUI e imagens gráficas: cores e formas preenchidas......Page 208
6.14 Conclusão......Page 211
7. Arrays e ArrayLists......Page 221
7.2 Arrays......Page 222
7.3 Declarando e criando arrays......Page 223
7.4 Exemplos que utilizam arrays......Page 224
7.5 Estudo de caso: simulação de embaralhamento e distribuição de cartas......Page 231
7.6 A estrutura for aprimorada......Page 234
7.7 Passando arrays para métodos......Page 235
7.8 Estudo de caso: classe GradeBook utilizando um array para armazenar notas......Page 237
7.9 Arrays multidimensionais......Page 241
7.10 Estudo de caso: classe GradeBook utilizando um array bidimensional......Page 244
7.11 Listas de argumentos de comprimento variável......Page 249
7.12 Utilizando argumentos de linha de comando......Page 250
7.13 Classe Arrays......Page 251
7.14 Introdução a coleções e classe ArrayList......Page 253
7.15 (Opcional) Estudo de caso de GUI e imagens gráficas: desenhando arcos......Page 255
7.16 Conclusão......Page 257
8. Classes e objetos:uma visão mais aprofundada......Page 273
8.2 Estudo de caso da classe Time......Page 274
8.3 Controlando o acesso a membros......Page 277
8.4 Referenciando membros do objeto atual com a referência this......Page 278
8.5 Estudo de caso da classe Time: construtores sobrecarregados......Page 280
8.7 Notas sobre os métodos set e get......Page 284
8.8 Composição......Page 285
8.9 Enumerações......Page 288
8.11 Membros da classe static......Page 290
8.12 Importação static......Page 293
8.13 Variáveis de instância fi nal......Page 294
8.14 Estudo de caso da classe Time: criando pacotes......Page 296
8.15 Acesso de pacote......Page 299
8.16 (Opcional) Estudo de caso de GUI e imagens gráficas: utilizando objetos com imagens gráficas......Page 301
8.17 Conclusão......Page 303
9. Programação orientada a objetos: herança......Page 310
9.2 Superclasses e subclasses......Page 311
9.4 Relacionamento entre superclasses e subclasses......Page 313
9.6 Engenharia de software com herança......Page 329
9.7 Classe Object......Page 330
9.8 (Opcional) Estudo de caso de GUI e imagens gráficas: exibindo texto e imagens com rótulos......Page 331
9.9 Conclusão......Page 333
10. Programação orientada a objetos: polimorfismo......Page 336
10.1 Introdução......Page 337
10.2 Exemplos de polimorfismo......Page 338
10.3 Demonstrando um comportamento polimórfico......Page 339
10.4 Classes e métodos abstratos......Page 341
10.5 Estudo de caso: sistema de folha de pagamentos utilizando polimorfismo......Page 342
10.7 Estudo de caso: criando e utilizando interfaces......Page 353
10.8 (Opcional) Estudo de caso de GUIs e imagens gráficas: desenhando compolimorfismo......Page 362
10.9 Conclusão......Page 363
11. Tratamento de exceções......Page 367
11.2 Visão geral do tratamento de erros......Page 368
11.3 Exemplo de divisão por zero sem tratamento de exceções......Page 369
11.4 Exemplo de tratamento de ArithmeticExceptions e InputMismatchExceptions......Page 371
11.5 Quando usar tratamento de exceções......Page 374
11.6 Hierarquia de exceções no Java......Page 375
11.7 Bloco finally......Page 377
11.8 Desempilhamento de pilha......Page 380
11.9 printStackTrace, getStackTrace e getMessage......Page 381
11.10 Exceções encadeadas......Page 383
11.11 Declarando novos tipos de exceção......Page 385
11.13 Assertivas......Page 386
11.14 Conclusão......Page 387
12. Estudo de caso de ATM, Parte 1: projeto orientado a objetos com UML......Page 392
12.2 Examinando o documento de requisitos......Page 393
12.3 Identificando classes em um documento de requisitos......Page 398
12.4 Identificando atributos de classe......Page 403
12.5 Identificando estados e atividades dos objetos......Page 406
12.6 Identificando operações de classe......Page 409
12.7 Indicando colaboração entre objetos......Page 414
12.8 Conclusão......Page 419
13. Estudo de caso de ATM, Parte 2: Implementando um projeto orientado a objetos......Page 422
13.2 Começando a programar as classes do sistema ATM......Page 423
13.3 Incorporando herança e polimorfismo ao sistema ATM......Page 427
13.4 Implementação do estudo de caso de ATM......Page 431
13.5 Conclusão......Page 448
14. Componentes GUI: Parte 1......Page 450
14.1 Introdução......Page 451
14.2 A nova interface Nimbus do Java......Page 452
14.3 Entrada/saída baseada em GUI simples com JOptionPane......Page 453
14.4 Visão geral de componentes Swing......Page 455
14.5 Exibição de texto e imagens em uma janela......Page 457
14.6 Campos de texto e uma introdução ao tratamento de evento com classes aninhadas......Page 460
14.7 Tipos comuns de eventos GUI e interfaces ouvintes......Page 465
14.8 Como o tratamento de evento funciona......Page 466
14.9 JButton......Page 467
14.10 Botões que mantêm o estado......Page 470
14.11 JComboBox e uso de uma classe interna anônima para tratamento de evento......Page 475
14.12 JList......Page 477
14.13 Listas de seleção múltipla......Page 479
14.14 Tratamento de evento de mouse......Page 481
14.15 Classes adaptadoras......Page 484
14.16 Subclasse Jpanel para desenhar com o mouse......Page 487
14.17 Tratamento de evento-chave......Page 490
14.18 Introdução a gerenciadores de layout......Page 492
14.19 Utilizando painéis para gerenciar layouts mais complexos......Page 499
14.20 JTextArea......Page 500
14.21 Conclusão......Page 503
15. Imagens gráficas e Java 2D™......Page 515
15.2 Contextos gráficos e objetos gráficos......Page 516
15.3 Controle de cor......Page 518
15.4 Manipulando fontes......Page 523
15.5 Desenhando linhas, retângulos e ovais......Page 527
15.6 Desenhando arcos......Page 530
15.7 Desenhando polígonos e polilinhas......Page 532
15.8 Java 2D API......Page 535
15.9 Conclusão......Page 540
16. Strings, caracteres e expressões regulares......Page 547
16.2 Fundamentos de caracteres e strings......Page 548
16.3 Classe String......Page 549
16.4 Classe StringBuilder......Page 558
16.5 Classe Character......Page 563
16.6 Tokenização de Strings......Page 567
16.7 Expressões regulares, classe Pattern e classe Matcher......Page 568
16.8 Conclusão......Page 574
17. Arquivos, fluxos e serialização de objetos......Page 583
17.2 Hierarquia de dados......Page 584
17.3 Arquivos e fluxos......Page 586
17.4 Classe File......Page 587
17.5 Arquivos de texto de acesso sequencial......Page 590
17.6 Serialização de objetos......Page 601
17.7 Classes java.io adicionais......Page 608
17.8 Abrindo arquivos com JFileChooser......Page 610
17.9 Conclusão......Page 613
18. Recursão......Page 621
18.1 Introdução......Page 622
18.3 Exemplo que utiliza recursão: fatoriais......Page 623
18.4 Exemplo que utiliza recursão: série de Fibonacci......Page 626
18.5 Recursão e a pilha de chamadas de método......Page 628
18.6 Recursão vs. iteração......Page 629
18.7 Torres de Hanói......Page 630
18.8 Fractais......Page 632
18.9 Retorno recursivo......Page 639
18.10 Conclusão......Page 640
19. Pesquisa, classifIcação e Big O......Page 646
19.2 Algoritmos de pesquisa......Page 647
19.3 Algoritmos de classificação......Page 654
19.4 Conclusão......Page 664
20. Coleções genéricas......Page 668
20.2 Visão geral das coleções......Page 669
20.4 Autoboxing e auto-unboxing......Page 670
20.6 Listas......Page 671
20.7 Métodos de coleções......Page 677
20.8 Classe Stack do pacote java.util......Page 686
20.9 Classe PriorityQueue e interface Queue......Page 687
20.10 Conjuntos......Page 688
20.11 Mapas......Page 690
20.12 Classe Properties......Page 693
20.13 Coleções sincronizadas......Page 695
20.15 Implementações abstratas......Page 696
20.16 Conclusão......Page 697
21. Classes e métodos genéricos......Page 702
21.2 Motivação para métodos genéricos......Page 703
21.3 Métodos genéricos: implementação e tradução em tempo de compilação......Page 705
21.4 Questões adicionais da tradução em tempo de compilação: métodos que utilizam um parâmetro de tipo como tipo de retorno......Page 707
21.5 Sobrecarregando métodos genéricos......Page 709
21.6 Classes genéricas......Page 710
21.7 Tipos brutos......Page 715
21.8 Curingas em métodos que aceitam parâmetros de tipo......Page 719
21.10 Conclusão......Page 722
22. Estruturas de dados genéricas personalizadas......Page 726
22.2 Classes autorreferenciais......Page 727
22.4 Listas vinculadas......Page 728
22.5 Pilhas......Page 736
22.6 Filas......Page 739
22.7 Árvores......Page 741
22.8 Conclusão......Page 746
23. Applets e Java Web Start......Page 754
23.2 Applets de exemplo fornecidas com o JDK......Page 755
23.3 Applet Java simples: desenhando uma string......Page 759
23.4 Métodos de ciclo de vida de applet......Page 762
23.5 Inicializando uma variável de instância com o método init......Page 763
23.6 Modelo de segurança da caixa de areia......Page 764
23.7 Java Web Start e o Java Network Launch Protocol (JNLP)......Page 765
23.8 Conclusão......Page 769
24. Multimídia: applets e aplicativos......Page 774
24.2 Carregando, exibindo e dimensionando imagens......Page 775
24.3 Animação de uma série de imagens......Page 780
24.4 Mapas de imagem......Page 785
24.5 Carregando e reproduzindo clipes de áudio......Page 788
24.6 Reproduzindo vídeo e outros tipos de mídia com o Java Media Framework......Page 790
24.8 Recursos da Web......Page 793
25. Componentes GUI: Parte 2......Page 800
25.2 JSlider......Page 801
25.3 Windows: notas adicionais......Page 804
25.4 Utilizando menus com frames......Page 805
25.5 JPopupMenu......Page 811
25.6 Aparência e comportamento plugável......Page 813
25.7 JDesktopPane e JInternalFrame......Page 816
25.8 JTabbedPane......Page 819
25.9 Gerenciadores de layout: BoxLayout e GridBagLayout......Page 821
25.10 Conclusão......Page 830
26. Multithreading......Page 835
26.1 Introdução......Page 836
26.2 Estados de thread: ciclo de vida de uma thread......Page 837
26.3 Prioridades de thread e agendamento de thread......Page 839
26.4 Criando e executando threads......Page 840
26.5 Sincronização de thread......Page 844
26.6 Relacionamento entre produtor e consumidor sem sincronização......Page 850
26.7 Relacionamento de produtor/consumidor: ArrayBlockingQueue......Page 855
26.8 Relacionamento entre produtor e consumidor com sincronização......Page 857
26.9 Relacionamento de produtor/consumidor: buffers limitados......Page 861
26.10 Relacionamento de produtor/consumidor: as interfaces Lock e Condition......Page 867
26.11 Multithreading com GUI......Page 872
26.13 Conclusão......Page 883
27. Redes......Page 890
27.1 Introdução......Page 891
27.2 Manipulando URLs......Page 892
27.3 Lendo um arquivo em um servidor da Web......Page 895
27.4 Estabelecendo um servidor simples utilizando sockets de fluxo......Page 898
27.5 Estabelecendo um cliente simples utilizando sockets de fluxo......Page 899
27.6 Interação cliente/servidor com conexões de socket de fluxo......Page 900
27.7 Interação cliente/servidor sem conexão com datagramas......Page 909
27.8 Jogo da velha cliente/servidor que utiliza um servidor com multithread......Page 914
27.10 Conclusão......Page 925
28. Acesso a bancos de dados com o JDBC......Page 930
28.1 Introdução......Page 931
28.3 Visão geral de um banco de dados relacional: o banco de dados books......Page 932
28.4 SQL......Page 935
28.6 Instruções para configuração de uma conta de usuário MySQL......Page 942
28.8 Manipulando bancos de dados com o JDBC......Page 943
28.9 Interface RowSet......Page 956
28.10 Java DB/Apache Derby......Page 958
28.11 PreparedStatements......Page 959
28.12 Procedures armazenadas......Page 970
28.15 Recursos da Web......Page 971
29. Aplicativos Web JavaServer™Faces......Page 978
29.2 Transações HTTP simples......Page 979
29.4 Tecnologias Web Java......Page 982
29.5 Criando e executando um aplicativo simples no NetBeans......Page 984
29.6 Componentes JSF......Page 993
29.7 Monitoramento de sessão......Page 1002
29.8 Conclusão......Page 1018
30. Aplicativos Web JavaServer™Faces compatíveis com Ajax......Page 1026
30.2 Acessando bancos de dados em aplicativos Web......Page 1027
30.3 Componentes JSF compatíveis com o Ajax......Page 1037
30.4 Criando um Text Field de autocompletamento e utilizando formulários virtuais......Page 1038
30.5 Conclusão......Page 1045
31. Serviços Web......Page 1049
31.1 Introdução......Page 1050
31.3 Simple Object Access Protocol (SOAP)......Page 1051
31.6 Publicando e consumindo serviços Web baseados em SOAP......Page 1052
31.7 Publicando e consumindo serviços Web XML baseados em REST......Page 1060
31.8 Publicando e consumindo serviços Web JSON baseados em REST......Page 1064
31.9 Rastreamento de sessão em um serviço Web baseado em SOAP......Page 1067
31.10 Consumindo um serviço Web baseado em SOAP orientado a banco dedados......Page 1077
31.11 Gerador de equação: retornando tipos definidos pelo usuário......Page 1083
31.12 Conclusão......Page 1091
Apêndices A. Tabela de precedênciade operadores......Page 1098
Apêndices B. Conjunto de caracteres ASCII......Page 1100
Apêndice C. Palavras-chave epalavras reservadas......Page 1101
Apêndices D. Tipos primitivos......Page 1102
E.2 Navegando pela Java API......Page 1103
Apêndices F. Utilizando o depurador......Page 1109
F.2 Pontos de interrupção e os comandos run, stop, cont e print......Page 1110
F.3 Os comandos print e set......Page 1113
F.4 Controlando a execução utilizando os comandos step, step up e next......Page 1114
F.5 O comando watch......Page 1117
F.6 O comando clear......Page 1118
F.7 Resumo......Page 1120
Apêndices G. Saída formatada......Page 1122
G.3 Formatando a saída com printf......Page 1123
G.5 Imprimindo números de ponto flutuante......Page 1124
G.7 Imprimindo datas e horas......Page 1126
G.8 Outros caracteres de conversão......Page 1128
G.9 Imprimindo com larguras e precisões de campos......Page 1130
G.10 Utilizando flags na string de formato printf......Page 1131
G.11 Imprimindo com índices de argumento......Page 1134
G.13 Formatando saída com a classe Formatter......Page 1135
G.14 Conclusão......Page 1136
Apêndice Q. Padrões de design......Page 1141
Índice remissivo......Page 1142

Citation preview

VIRA

VIRA

VIRA

VIRA

Você tem em mãos a mais consagrada obra de introdução à programação orientada a objetos que se baseia no DEITEL® live-code para ensinar tudo o que você precisa saber sobre Java™ Standard Edition 6, JDBC™ 4, desenvolvimento de aplicativos Web e Web Services! O Java™ é atualmente a linguagem de programação mais popular do mundo. Esta nova edição, completamente atualizada, utiliza uma abordagem agradável e cuidadosamente compassada para que o estudante desenvolva aplicativos baseados em Web e desktop. Os novos exercícios “Fazendo a diferença” envolvem questões relacionadas à consciência social que são importantes tanto para estudantes como para profissionais. “Um ótimo livro-texto com uma enorme variedade de exemplos de vários domínios de aplicações — excelente para um curso de ciência da computação.” — William E. Duncan, Louisiana State University “Esta nova edição atualizada reflete o estado da arte em tecnologias Java. Como sempre, suas explicações profundas e claras tornam o livro indispensável.” — José Antonio González Seco, Parlamento da Andaluzia “Java — como programar introduz noções de boas práticas de projetos e metodologias desde o começo. É um excelente ponto de partida para o desenvolvimento de aplicações Java robustas e de alta qualidade.” — Simon Ritter, Sun Microsystems “Tem um estilo fácil de ler, como se fosse um bate-papo. Traz exemplos de códigos claros que permitem ao leitor tornar-se proficiente em Java.” — Patty Kraft, San Diego State University “O melhor livro-texto introdutório que já vi. Gostaria que este livro tivesse sido publicado quando eu estava aprendendo a programar!” — Lance Andersen, Sun Microsystems “As técnicas de projetos orientados a objetos estão presentes em todo o livro. Os exemplos são de fácil compreensão e trazem ótimas oportunidades de aprendizado! A explicação sobre herança é muito boa — o conceito é construído com base no exemplo, podendo ser facilmente compreendido. Fiquei muito contente de ver o desenho de elementos gráficos opcionais no início do livro. Os exercícios são desafiadores e divertirão os estudantes.” — Sue McFarland Metzger, Villanova University “Traz aos novos programadores o conhecimento proveniente de muitos anos de experiência no desenvolvimento de softwares.” — Edward F. Gehringer, North Carolina State University “Parabenizo os autores pela dedicação a pesquisas detalhadas e pelo desenvolvimento de exemplos tão ilustrativos. A visão geral passo a passo de projetos orientados a objetos é excelente.” — Ric Heishman, George Mason University “Acho os novos exercícios ‘Fazendo a diferença’ muito interessantes. Abordando temas contemporâneos, eles encorajam o estudante a pesquisar dados na Internet e aplicá-los no exercício em questão.” — Vince O’Brien, Pearson Education “Este livro oferece uma base sólida para programação em Java. A maioria dos principais conceitos é ilustrada por programas completos. A abordagem é compreensível e detalhada. Há uma grande variedade de exercícios para testar sua compreensão do assunto.” — Shyamal Mitra, University of Texas at Austin Esta oitava edição de Java — como programar apresenta uma abordagem abrangente sobre a programação orientada a objetos e traz vários estudos de caso que tratam de: classes (GradeBook, Time, Employee), sistema OOD/UML™ ATM opcional, GUI e desenho de elementos gráficos opcionais, aplicativo Web baseado em banco de dados operando em múltiplas camadas (livro de endereços) e serviços Web (Blackjack, sistema de reservas de passagens aéreas, gerador de equações etc.) Paul J. Deitel e o dr. Harvey M. Deitel são os fundadores da Deitel & Associates, Inc., uma organização internacionalmente reconhecida de treinamento corporativo, criação e desenvolvimento de negócios na Internet, especializada em Java™, C, C++, C#, Visual Basic®, Visual C++®, Python®, XML, Internet, Web e tecnologias de objeto. Os Deitel são autores de muitos livros-texto, livros profissionais e cursos em vídeo líderes de venda internacionalmente. Visite os sites do livro (www.pearson.com.br/deitel e www.deitel.com/books/jhtp8). Contate os autores pelo e-mail [email protected]. Para receber o newsletter Deitel® Buzz Online e para informações sobre a série de treinamentos corporativos Deitel® Dive Into® Series, oferecida em todo o mundo, acesse o site www.deitel.com/newsletter/subscribe.html.

www.pearson.com.br/deitel No site, professores têm acesso ao manual de soluções (em inglês) e a apresentações em PowerPoint, enquanto estudantes contam com exercícios de múltipla escolha e com os códigos (em inglês) dos exemplos apresentados no livro. ISBN 978-85-7605-563-1

w w w. p e a r s o n . c o m . b r

9788576055631_Deitel_Dez2012.indd 1

27/12/2012 11:18:03

Java



COMO PROGRAMAR OITAVA EDIÇÃO

Java



COMO PROGRAMAR OITAVA EDIÇÃO

Paul Deitel

Deitel & Associates, Inc.

Harvey Deitel

Deitel & Associates, Inc. Tradução Edson Furmankiewicz Docware Traduções Técnicas

Revisão técnica Fábio Luis Picelli Lucchini

Bacharel em Ciência da Computação pelo Centro Universitário Padre Anchieta Mestre em Engenharia da Computação pela Unicamp (FEEC) Professor Universitário do Centro Universitário Padre Anchieta

São Paulo São Paulo Brasil Argentina Colômbia Costa Rica Chile Espanha Peru Porto Rico Venezuela Brasil Argentina ColômbiaGuatemala Costa Rica México Chile Espanha Guatemala México Peru Porto Rico Venezuela

© 2010 by Pearson Education do Brasil © 2009 by Prentice Hall, uma empresa do grupo Pearson Education, Inc. Tradução autorizada a partir da edição original em inglês, Java™ How to Program -- Eighth Edition, de Harvey M. Deitel e Paul J. Deitel, publicada pela Pearson Education Inc., sob o selo Prentice Hall PTR. Todos os direitos reservados. Nenhuma parte desta publicação pode ser reproduzida ou transmitida de qualquer modo ou por qualquer meio, eletrônico ou mecânico, incluindo fotocópia, gravação ou qualquer outro tipo de armazenamento e transmissão de informação, sem prévia autorização, por escrito, da Pearson Education do Brasil. Muitas das designações utilizadas por fabricantes e vendedores para distinguir seus produtos são protegidas como marcas comerciais. Onde essas aparecem no livro, e a Manning Publications estava ciente de uma proteção de marca comercial, as designações foram impressas com a primeira letra ou todas as letras maiúsculas. Os nomes de empresas e produtos mencionados neste livro são marcas comerciais ou registradas de seus respectivos proprietários.

Diretor editorial: Roger Trimer Gerente editorial: Sabrina Cairo Supervisor de produção editorial: Marcelo Françoso Editoras: Gabriela Trevisan e Marina S. Lupinetti Revisão: Letícia Scarp, Adriane Schirmer e Norma Gusukuma Capa: Casa de ideias sobre o projeto original de Abbey S. Deitel, Harvey M. Deitel, Francisco Santalucia e Kristine Carney Editoração eletrônica: Docware Traduções Técnicas

Dados Internacionais de Catalogação na Publicação (CIP) (Câmara Brasileira do Livro, SP, Brasil) Deitel, P. J. Java : como programar / Paul Deitel e Harvey Deitel ; tradução Edson Furmankiewicz ; revisão técnica Fábio Luis Picelli Lucchini. -- 8. ed. -- São Paulo : Pearson Prentice Hall, 2010. Título original: Java : how to program. ISBN 978-85-7605-563-1. 1. Java (Linguagem de programação para computador) I. Deitel, Harvey. II. Lucchini, Fábio Luis Picelli. III. Título. 09-09979

CDD-005.133 Índice para catálogo sistemático: 1. Java : Linguagem de programação : Computadores : Processamento de dados 005.133

2a reimpressão – novembro 2012 Direitos exclusivos para a2010 língua portuguesa cedidos à Direitos exclusivos para a língua portuguesa cedidos à Pearson Education do Brasil Ltda., Pearson Education do Brasil, uma empresa do grupo Pearson Education uma empresa do grupo Pearson Education Rua Nelson Francisco, 26 Av. Ermano Marchetti,1435 CEP 02712-100 – São Paulo – SP – Brasil CEP: 05038-001 — São Paulo — SP Fone: 11 2178-8686 – Fax: 11 2178-8688 Tel.: (11) 2178-8686 — Fax: (11) 2178-8688 e-mail: [email protected]

Em memória de Kristen Nygaard, coinventor do Simula — a primeira linguagem de programação orientada a objetos do mundo. Paul e Harvey Deitel

Sumário Prefácio Antes de você começar

1

Introdução aos computadores, à Internet e à World Wide Web

xvii xxvii 1

1.1 Introdução........................................................................................................................................................................................... 2 1.2 Computadores: Hardware e software ................................................................................................................................................ 3 1.3 Organização do computador ............................................................................................................................................................. 3 1.4 Primeiros sistemas operacionais ........................................................................................................................................................ 4 1.5 Computação pessoal distribuída e computação cliente/servidor .................................................................................................... 4 1.6 A Internet e a World Wide Web .......................................................................................................................................................... 5 1.7 Linguagens de máquina, linguagens assembly e linguagens de alto nível ...................................................................................... 5 1.8 História do C e do C++ .................................................................................................................................................................... 6 1.9 História do Java .................................................................................................................................................................................. 6 1.10 Bibliotecas de classe do Java ............................................................................................................................................................ 7 1.11 Fortran, Cobol, Pascal e Ada .......................................................................................................................................................... 7 1.12 Basic, Visual Basic, Visual C++, C# e .NET ................................................................................................................................. 8 1.13 Ambiente típico de desenvolvimento Java ....................................................................................................................................... 8 1.14 Notas sobre o Java e este livro ........................................................................................................................................................ 11 1.15 Testando um aplicativo Java............................................................................................................................................................ 11 1.16 Estudo de caso de engenharia de software: introdução à tecnologia de objetos e à UML ........................................................ 15 1.17 Web 2.0 ............................................................................................................................................................................................ 17 1.18 Tecnologias de software .................................................................................................................................................................. 18 1.19 Conclusão........................................................................................................................................................................................ 19 1.20 Recursos da Web ............................................................................................................................................................................. 19

2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9

Introdução aos aplicativos Java

28

Introdução ......................................................................................................................................................................................... 29 Nosso primeiro programa Java: imprimindo uma linha de texto .................................................................................................. 29 Modificando nosso primeiro programa Java ................................................................................................................................... 34 Exibindo texto com printf ............................................................................................................................................................. 35 Outro aplicativo: somando inteiros.................................................................................................................................................. 36 Conceitos de memória ....................................................................................................................................................................... 40 Aritmética .......................................................................................................................................................................................... 41 Tomada de decisão: operadores de igualdade e operadores relacionais ......................................................................................... 43 Conclusão ........................................................................................................................................................................................... 47

viii

3

Sumário

Introdução a classes e objetos

56

3.1  Introdução.......................................................................................................................................................................................... 57 3.2  Classes, objetos, métodos e variáveis de instância............................................................................................................................ 57 3.3  Declarando uma classe com um método e instanciando um objeto de uma classe......................................................................... 58 3.4  Declarando um método com um parâmetro..................................................................................................................................... 61 3.5  Variáveis de instância, métodos set e get............................................................................................................................................ 63 3.6  Tipos primitivos versus tipos por referência..................................................................................................................................... 67 3.7  Inicializando objetos com construtores............................................................................................................................................ 68 3.8  Números de ponto flutuante e tipo double...................................................................................................................................... 70 3.9  (Opcional) Estudo de caso de GUI e imagens gráficas: utilizando caixas de diálogo.................................................................... 73 3.10  Conclusão.......................................................................................................................................................................................... 75

4

Instruções de controle: Parte I

81

4.1  Introdução.......................................................................................................................................................................................... 82 4.2  Algoritmos.......................................................................................................................................................................................... 82 4.3  Pseudocódigo...................................................................................................................................................................................... 82 4.4  Estruturas de controle........................................................................................................................................................................ 83 4.5  A instrução de seleção única if......................................................................................................................................................... 84 4.6  A instrução de seleção dupla if...else. ......................................................................................................................................... 85 4.7  A instrução de repetição while......................................................................................................................................................... 88 4.8  Formulando algoritmos: repetição controlada por contador........................................................................................................ 89 4.9  Formulando algoritmos: repetição controlada por sentinela......................................................................................................... 93 4.10  Formulando algoritmos: instruções de controle aninhadas......................................................................................................... 99 4.11  Operadores de atribuição composta............................................................................................................................................. 102 4.12  Operadores de incremento e decremento...................................................................................................................................... 102 4.13  Tipos primitivos.............................................................................................................................................................................. 105 4.14  (Opcional) Estudo de caso de GUI e imagens gráficas: criando desenhos simples.................................................................... 105 4.15  Conclusão........................................................................................................................................................................................ 108

5

Instruções de controle: Parte 2

119

5.1  Introdução........................................................................................................................................................................................ 120 5.2  Princípios básicos de repetição controlada por contador............................................................................................................. 120 5.3  Instrução de repetição for. ............................................................................................................................................................. 121 5.4  Exemplos com a estrutura for. ....................................................................................................................................................... 124 5.5  Instrução de repetição do…while. ................................................................................................................................................... 127 5.6  A estrutura de seleção múltipla switch.......................................................................................................................................... 129 5.7  Instruções break e continue ......................................................................................................................................................... 134 5.8  Operadores lógicos........................................................................................................................................................................... 136 5.9  Resumo de programação estruturada............................................................................................................................................. 140 5.10  (Opcional) Estudo de caso de GUI e imagens gráficas: desenhando retângulos e ovais............................................................ 144 5.11  Conclusão........................................................................................................................................................................................ 147

6 6.1  6.2  6.3  6.4  6.5  6.6  6.7 

Métodos: uma visão mais aprofundada

154

Introdução........................................................................................................................................................................................ 155 Módulos de programa em Java........................................................................................................................................................ 155 Métodos static, campos static e classe Math.............................................................................................................................. 156 Declarando métodos com múltiplos parâmetros........................................................................................................................... 158 Notas sobre a declaração e utilização de métodos......................................................................................................................... 161 Pilha de chamadas de método e registros de ativação.................................................................................................................... 161 Promoção e coerção de argumentos................................................................................................................................................ 162



Sumário

ix

6.8  Pacotes da Java API.......................................................................................................................................................................... 163 6.9  Estudo de caso: geração de números aleatórios............................................................................................................................. 164 6.10  Estudo de caso: um jogo de azar; introdução a enumerações...................................................................................................... 168 6.11  Escopo das declarações.................................................................................................................................................................. 172 6.12  Sobrecarga de método.................................................................................................................................................................... 174 6.13  (Opcional) Estudo de caso de GUI e imagens gráficas: Cores e formas preenchidas................................................................. 176 6.14  Conclusão........................................................................................................................................................................................ 179

7

Arrays e ArrayLists

189

7.1  Introdução........................................................................................................................................................................................ 190 7.2  Arrays................................................................................................................................................................................................ 190 7.3  Declarando e criando arrays............................................................................................................................................................ 191 7.4  Exemplos que utilizam arrays.......................................................................................................................................................... 192 7.5  Estudo de caso: simulação de embaralhamento e distribuição de cartas...................................................................................... 199 7.6  A estrutura for aprimorada........................................................................................................................................................... 202 7.7  Passando arrays para métodos........................................................................................................................................................ 203 7.8  Estudo de caso: classe GradeBook utilizando um array para armazenar notas............................................................................ 205 7.9  Arrays multidimensionais................................................................................................................................................................ 209 7.10  Estudo de caso: classe GradeBook utilizando um array bidimensional...................................................................................... 212 7.11  Listas de argumentos de comprimento variável........................................................................................................................... 217 7.12  Utilizando argumentos de linha de comando.............................................................................................................................. 218 7.13  Classe Arrays. ................................................................................................................................................................................ 219 7.14  Introdução a coleções e classe ArrayList..................................................................................................................................... 221 7.15  (Opcional) Estudo de caso de GUI e imagens gráficas: desenhando arcos................................................................................. 223 7.16  Conclusão........................................................................................................................................................................................ 225

8

Classes e objetos: uma visão mais aprofundada

241

8.1  Introdução........................................................................................................................................................................................ 242 8.2  Estudo de caso da classe Time.......................................................................................................................................................... 242 8.3  Controlando o acesso a membros.................................................................................................................................................... 245 8.4  Referenciando membros do objeto atual com a referência this................................................................................................... 246 8.5  Estudo de caso da classe Time: construtores sobrecarregados...................................................................................................... 248 8.6  Construtores padrão e sem argumentos.......................................................................................................................................... 252 8.7  Notas sobre os métodos set e get...................................................................................................................................................... 252 8.8  Composição....................................................................................................................................................................................... 253 8.9  Enumerações..................................................................................................................................................................................... 256 8.10  Coleta de lixo e o método finalize............................................................................................................................................... 258 8.11  Membros da classe static............................................................................................................................................................. 258 8.12  Importação static........................................................................................................................................................................ 261 8.13  Variáveis de instância final............................................................................................................................................................ 262 8.14  Estudo de caso da classe Time: criando pacotes........................................................................................................................... 264 8.15  Acesso de pacote............................................................................................................................................................................. 267 8.16  (Opcional) Estudo de caso de GUI e imagens gráficas: utilizando objetos com imagens gráficas........................................... 269 8.17  Conclusão........................................................................................................................................................................................ 271

9 9.1  9.2  9.3  9.4  9.5 

Programação orientada aobjetos: herança

278

Introdução........................................................................................................................................................................................ 279 Superclasses e subclasses................................................................................................................................................................... 279 Membros protected........................................................................................................................................................................ 281 Relacionamento entre superclasses e subclasses.............................................................................................................................. 281 Construtores em subclasses.............................................................................................................................................................. 297

x

Sumário

9.6  9.7  9.8  9.9 

Engenharia de software com herança............................................................................................................................................. 297 Classe Object.................................................................................................................................................................................... 298 (Opcional) Estudo de caso de GUI e imagens gráficas: exibindo texto e imagens com rótulos.................................................. 299 Conclusão.......................................................................................................................................................................................... 301

10 10.1  10.2  10.3  10.4  10.5  10.6  10.7  10.8  10.9 

Programação orientada a objetos: polimorfismo

304

Introdução...................................................................................................................................................................................... 305 Exemplos de polimorfismo............................................................................................................................................................. 306 Demonstrando um comportamento polimórfico......................................................................................................................... 307 Classes e métodos abstratos........................................................................................................................................................... 309 Estudo de caso: Sistema de folha de pagamentos utilizando polimorfismo............................................................................... 311 Métodos e classes final ................................................................................................................................................................. 321 Estudo de caso: criando e utilizando interfaces........................................................................................................................... 321 (Opcional) Estudo de caso de GUI e imagens gráficas: desenhando com polimorfismo........................................................... 330 Conclusão........................................................................................................................................................................................ 331

11

Tratamento de exceções

335

11.1  Introdução...................................................................................................................................................................................... 336 11.2  Visão geral do tratamento de erros............................................................................................................................................... 336 11.3  Exemplo de divisão por zero sem tratamento de exceções........................................................................................................... 337 11.4  Exemplo de tratamento de ArithmeticExceptions e InputMismatchExceptions................................................................... 339 11.5  Quando usar tratamento de exceções............................................................................................................................................ 342 11.6  Hierarquia de exceções no Java...................................................................................................................................................... 343 11.7  Bloco finally.................................................................................................................................................................................. 345 11.8  Desempilhamento de pilha............................................................................................................................................................ 348 11.9  printStackTrace, getStackTrace e getMessage....................................................................................................................... 349 11.10  Exceções encadeadas.................................................................................................................................................................... 351 11.11  Declarando novos tipos de exceção............................................................................................................................................. 353 11.12  Pré-condições e pós-condições.................................................................................................................................................... 354 11.13  Assertivas...................................................................................................................................................................................... 354 11.14  Conclusão..................................................................................................................................................................................... 355

12 12.1  12.2  12.3  12.4  12.5  12.6  12.7  12.8 

360

Introdução a estudos de caso:........................................................................................................................................................ 361 Examinando o documento de requisitos....................................................................................................................................... 361 Identificando classes em um documento de requisitos................................................................................................................. 366 Identificando atributos de classe................................................................................................................................................... 371 Identificando estados e atividades dos objetos............................................................................................................................. 374 Identificando operações de classe.................................................................................................................................................. 377 Indicando colaboração entre objetos............................................................................................................................................ 382 Conclusão........................................................................................................................................................................................ 387

13 13.1  13.2  13.3  13.4  13.5 

Estudo de caso de ATM, Parte 1: projeto orientado a objetos com UML

Estudo de caso de ATM, Parte 2: implementando um projeto orientado a objetos

390

Introdução...................................................................................................................................................................................... 391 Começando a programar as classes do sistema ATM.................................................................................................................... 391 Incorporando herança e polimorfismo ao sistema ATM.............................................................................................................. 395 Implementação do estudo de caso ATM........................................................................................................................................ 399 Conclusão........................................................................................................................................................................................ 416



Sumário

14

Componentes GUI: Parte 1

xi

418

14.1  Introdução...................................................................................................................................................................................... 419 14.2  A nova interface Nimbus do Java................................................................................................................................................... 420 14.3  Entrada/saída baseada em GUI simples com JOptionPane........................................................................................................ 421 14.4  Visão geral de componentes Swing............................................................................................................................................... 423 14.5  Exibição de texto e imagens em uma janela.................................................................................................................................. 425 14.6  Campos de texto e uma introdução ao tratamento de eventos com classes aninhadas.............................................................. 428 14.7  Tipos comuns de eventos GUI e interfaces ouvintes..................................................................................................................... 433 14.8  Como o tratamento de evento funciona........................................................................................................................................ 434 14.9  JButton........................................................................................................................................................................................... 435 14.10  Botões que mantêm o estado....................................................................................................................................................... 438 14.11  JComboBox e uso de uma classe interna anônima para tratamento de eventos..................................................................................................443 14.12  JList. ........................................................................................................................................................................................... 445 14.13  Listas de seleção múltipla............................................................................................................................................................ 447 14.14  Tratamento de evento de mouse................................................................................................................................................... 449 14.15  Classes adaptadoras..................................................................................................................................................................... 452 14.16  Subclasse Jpanel para desenhar com o mouse........................................................................................................................... 455 14.17  Tratamento de eventos chave........................................................................................................................................................ 458 14.18  Introdução a gerenciadores de layout......................................................................................................................................... 460 14.19  Utilizando painéis para gerenciar layouts mais complexos....................................................................................................... 467 14.20  JTextArea. ................................................................................................................................................................................... 468 14.21  Conclusão..................................................................................................................................................................................... 471

15 15.1  15.2  15.3  15.4  15.5  15.6  15.7  15.8  15.9 

Imagens gráficas e Java 2D™

483

Introdução...................................................................................................................................................................................... 484 Contextos gráficos e objetos gráficos............................................................................................................................................ 484 Controle de cor............................................................................................................................................................................... 486 Manipulando fontes....................................................................................................................................................................... 491 Desenhando linhas, retângulos e ovais......................................................................................................................................... 495 Desenhando arcos........................................................................................................................................................................... 498 Desenhando polígonos e polilinhas.............................................................................................................................................. 500 Java 2D API.................................................................................................................................................................................... 503 Conclusão........................................................................................................................................................................................ 508

16

Strings, caracteres e expressões regulares

515

16.1  Introdução...................................................................................................................................................................................... 516 16.2  Fundamentos de caracteres e strings............................................................................................................................................. 516 16.3  Classe String . ............................................................................................................................................................................... 517 16.4  Classe StringBuilder . ................................................................................................................................................................. 526 16.5  Classe Character . ......................................................................................................................................................................... 531 16.6  Tokenização de Strings ................................................................................................................................................................ 535 16.7  Expressões regulares, classe Pattern e classe Matcher................................................................................................................ 536 16.8  Conclusão........................................................................................................................................................................................ 542

17

Arquivos, fluxos e serialização de objetos

551

17.1  Introdução...................................................................................................................................................................................... 552 17.2  Hierarquia de dados....................................................................................................................................................................... 552 17.3  Arquivos e fluxos............................................................................................................................................................................. 554 17.4  Classe File . ................................................................................................................................................................................... 555 17.5  Arquivos de texto de acesso sequencial.......................................................................................................................................... 558

xii

Sumário

17.6  Serialização de objeto.................................................................................................................................................................... 569 17.7  Classes java.io adicionais............................................................................................................................................................ 576 17.8  Abrindo arquivos com JFileChooser. ......................................................................................................................................... 578 17.9  Conclusão........................................................................................................................................................................................ 581

18

Recursão

589

18.1  Introdução...................................................................................................................................................................................... 590 18.2  Conceitos de recursão.................................................................................................................................................................... 591 18.3  Exemplo que utiliza recursão: fatoriais........................................................................................................................................ 591 18.4  Exemplo que utiliza recursão: série de Fibonacci........................................................................................................................ 594 18.5  Recursão e a pilha de chamadas de método.................................................................................................................................. 596 18.6  Recursão vs. Iteração..................................................................................................................................................................... 597 18.7  Torres de Hanói.............................................................................................................................................................................. 598 18.8  Fractais........................................................................................................................................................................................... 600 18.9  Retorno recursivo........................................................................................................................................................................... 607 18.10  Conclusão..................................................................................................................................................................................... 608

19

Pesquisa, classificação e Big O

614

19.1  Introdução..................................................................................................................................................................................... 615 19.2  Algoritmos de pesquisa.................................................................................................................................................................. 615 19.3  Algoritmos de classificação............................................................................................................................................................ 622 19.4  Conclusão........................................................................................................................................................................................ 632

20

Coleções genéricas

636

20.1  Introdução...................................................................................................................................................................................... 637 20.2  Visão geral das coleções.................................................................................................................................................................. 637 20.3  Classes empacotadoras de tipo para tipos primitivos.................................................................................................................. 638 20.4  Autoboxing e auto-unboxing......................................................................................................................................................... 638 20.5  Interface Collection e classe Collections................................................................................................................................. 639 20.6  Listas............................................................................................................................................................................................... 639 20.7  Métodos de coleções....................................................................................................................................................................... 645 20.8  Classe Stack do pacote java.util. .............................................................................................................................................. 654 20.9  Classe PriorityQueue e interface Queue. ..................................................................................................................................... 655 20.10  Conjuntos..................................................................................................................................................................................... 656 20.11  Mapas............................................................................................................................................................................................ 658 20.12  Classe Properties...................................................................................................................................................................... 661 20.13  Coleções sincronizadas................................................................................................................................................................. 663 20.14  Coleções não modificáveis............................................................................................................................................................ 664 20.15  Implementações abstratas............................................................................................................................................................ 664 20.16  Conclusão..................................................................................................................................................................................... 665

21 21.1  21.2  21.3  21.4 

Classes e métodos genéricos

670

Introdução...................................................................................................................................................................................... 671 Motivação para métodos genéricos............................................................................................................................................... 671 Métodos genéricos: implementação e tradução em tempo de compilação................................................................................. 673 Questões adicionais da tradução em tempo de compilação: métodos que utilizam um parâmetro de tipo como tipo de retorno........................................................................................................................................................................... 675 21.5  Sobrecarregando métodos genéricos............................................................................................................................................ 677 21.6  Classes genéricas............................................................................................................................................................................. 678 21.7  Tipos brutos.................................................................................................................................................................................... 683



Sumário

xiii

21.8  Curingas em métodos que aceitam parâmetros de tipo.............................................................................................................. 687 21.9  Genéricos e herança: notas........................................................................................................................................................... 690 21.10  Conclusão..................................................................................................................................................................................... 690

22

Estruturas de dados genéricas personalizadas

694

22.1  Introdução...................................................................................................................................................................................... 695 22.2  Classes autorreferenciais................................................................................................................................................................ 695 22.3  Alocação dinâmica de memória..................................................................................................................................................... 696 22.4  Listas vinculadas............................................................................................................................................................................ 696 22.5  Pilhas............................................................................................................................................................................................... 704 22.6  Filas................................................................................................................................................................................................. 707 22.7  Árvores............................................................................................................................................................................................ 709 22.8  Conclusão........................................................................................................................................................................................ 714

23

Applets e Java Web Start

722

23.1  Introdução...................................................................................................................................................................................... 723 23.2  Applets de exemplo fornecidas com o JDK................................................................................................................................... 723 23.3  Applet Java simples: desenhando uma string................................................................................................................................ 727 23.4  Métodos de ciclo de vida de applet................................................................................................................................................ 730 23.5  Inicializando uma variável de instância com o método init...................................................................................................... 731 23.6  Modelo de segurança da caixa de areia......................................................................................................................................... 732 23.7  Java Web Start e o Java Network Launch Protocol (JNLP)....................................................................................................... 733 23.8  Conclusão........................................................................................................................................................................................ 737

24

Multimídia: applets e aplicativos

742

24.1  Introdução...................................................................................................................................................................................... 743 24.2  Carregando, exibindo e dimensionando imagens........................................................................................................................ 743 24.3  Animação de uma série de imagens................................................................................................................................................ 748 24.4  Mapas de imagem........................................................................................................................................................................... 753 24.5  Carregando e reproduzindo clipes de áudio................................................................................................................................. 756 24.6  Reproduzindo vídeo e outros tipos de mídia com o Java Media Framework............................................................................. 758 24.7  Conclusão........................................................................................................................................................................................ 761 24.8  Recursos da Web............................................................................................................................................................................. 761

25

Componentes GUI: Parte 2

768

25.1  Introdução...................................................................................................................................................................................... 769 25.2  JSlider........................................................................................................................................................................................... 769 25.3  Windows: notas adicionais............................................................................................................................................................ 772 25.4  Utilizando menus com frames....................................................................................................................................................... 773 25.5  JPopupMenu..................................................................................................................................................................................... 779 25.6  Aparência e comportamento plugável........................................................................................................................................... 781 25.7  JDesktopPane e JInternalFrame................................................................................................................................................. 784 25.8  JTabbedPane................................................................................................................................................................................... 787 25.9  Gerenciadores de layout: BoxLayout e GridBagLayout. ............................................................................................................. 789 25.10  Conclusão..................................................................................................................................................................................... 798

26

Multithreading

803

26.1  Introdução...................................................................................................................................................................................... 804 26.2  Estados de thread: ciclo de vida de uma thread........................................................................................................................... 805 26.3  Prioridades de thread e agendamento de thread.......................................................................................................................... 807

xiv

Sumário

26.4  Criando e executando threads....................................................................................................................................................... 808 26.5  Sincronização de thread................................................................................................................................................................. 812 26.6  Relacionamento entre produtor e consumidor sem sincronização............................................................................................. 818 26.7  Relacionamento de produtor/consumidor: ArrayBlockingQueue............................................................................................. 823 26.8  Relacionamento entre produtor e consumidor com sincronização............................................................................................ 825 26.9  Relacionamento de produtor/consumidor: buffers limitados..................................................................................................... 829 26.10  Relacionamento de produtor/consumidor: as interfaces Lock e Condition......................................................................... 835 26.11  Multithreading com GUI............................................................................................................................................................ 840 26.12  Interfaces Callable e Future...................................................................................................................................................... 851 26.13  Conclusão..................................................................................................................................................................................... 851

27

Redes

858

27.1  Introdução...................................................................................................................................................................................... 859 27.2  Manipulando URLs....................................................................................................................................................................... 860 27.3  Lendo um arquivo em um servidor da Web................................................................................................................................... 863 27.4  Estabelecendo um servidor simples utilizando sockets de fluxo.................................................................................................. 866 27.5  Estabelecendo um cliente simples utilizando sockets de fluxo..................................................................................................... 867 27.6  Interação cliente/servidor com conexões de socket de fluxo........................................................................................................ 868 27.7  Interação cliente/servidor sem conexão com datagramas............................................................................................................ 877 27.8  Jogo da velha cliente/servidor que utiliza um servidor com multithread.................................................................................. 882 27.9  [Bônus Web] Estudo de caso: servidor e cliente DeitelMessenger . ......................................................................................... 893 27.10  Conclusão..................................................................................................................................................................................... 893

28

Acesso a bancos de dados com o JDBC

898

28.1  Introdução...................................................................................................................................................................................... 899 28.2  Bancos de dados relacionais.......................................................................................................................................................... 900 28.3  Visão geral de um banco de dados relacional: o banco de dados books .................................................................................... 900 28.4  SQL................................................................................................................................................................................................. 903 28.5  Instruções para instalar o MySQL e o MySQL Conector/J......................................................................................................... 910 28.6  Instruções para configuração de uma conta de usuário MySQL................................................................................................. 910 28.7  Criando banco de dados books no MySQL.................................................................................................................................. 911 28.8  Manipulando bancos de dados com o JDBC................................................................................................................................ 911 28.9  Interface RowSet............................................................................................................................................................................. 924 28.10  Java DB/Apache Derby................................................................................................................................................................ 926 28.11  PreparedStatements. ................................................................................................................................................................. 927 28.12  Procedures armazenadas.............................................................................................................................................................. 938 28.13  Processamento de transações....................................................................................................................................................... 939 28.14  Conclusão..................................................................................................................................................................................... 939 28.15  Recursos da Web........................................................................................................................................................................... 939

29

Aplicativos Web JavaServer™ Faces

946

29.1  Introdução...................................................................................................................................................................................... 947 29.2  Transações HTTP simples.............................................................................................................................................................. 947 29.3  Arquitetura de aplicativo multithread.......................................................................................................................................... 950 29.4  Tecnologias Web Java...................................................................................................................................................................... 950 29.5  Criando e executando um aplicativo simples no NetBeans.......................................................................................................... 952 29.6  Componentes JSF.......................................................................................................................................................................... 961 29.7  Monitoramento de sessão.............................................................................................................................................................. 970 29.8  Conclusão........................................................................................................................................................................................ 986



Sumário

30

Aplicativos Web JavaServer™ Faces compatíveis com Ajax

xv

994

30.1  Introdução...................................................................................................................................................................................... 995 30.2  Acessando bancos de dados em aplicativos Web........................................................................................................................... 995 30.3  Componentes JSF compatíveis com o Ajax................................................................................................................................. 1005 30.4  Criando um Text Field de autocompletamento e utilizando formulários virtuais........................................................................... 1006 30.5  Conclusão...................................................................................................................................................................................... 1013

31

Serviços Web

1017

31.1  Introdução.................................................................................................................................................................................... 1018 31.2  Fundamentos do serviço Web...................................................................................................................................................... 1019 31.3  Simple Object Access Protocol (SOAP)...................................................................................................................................... 1019 31.4  Representational State Transfer (REST).................................................................................................................................... 1020 31.5  JavaScript Object Notation (JSON)........................................................................................................................................... 1020 31.6  Publicando e consumindo serviços Web baseados em SOAP..................................................................................................... 1020 31.7  Publicando e consumindo serviços Web XML baseados em REST........................................................................................... 1028 31.8  Publicando e consumindo serviços Web JSON baseados em REST.......................................................................................... 1032 31.9  Rastreamento de sessão em um serviço Web baseado em SOAP................................................................................................ 1035 31.10  Consumindo um serviço Web baseado em SOAP orientado a banco de dados...................................................................... 1045 31.11  Gerador de equação: retornando tipos definidos pelo usuário.............................................................................................. 1051 31.12  Conclusão................................................................................................................................................................................... 1059

A

Tabela de precedência de operadores

1066

B

Conjunto de caracteres ASCII

1068

C

Palavras-chave e palavras reservadas

1069

D

Tipos primitivos

1070

E

Usando a documentação da Java API

1071

E.1  Introdução...................................................................................................................................................................................... 1071 E.2  Navegando pela Java API.............................................................................................................................................................. 1071

F F.1  F.2  F.3  F.4  F.5  F.6  F.7 

G G.1  G.2  G.3  G.4 

Utilizando o depurador

1077

Introdução....................................................................................................................................................................................... 1078 Pontos de interrupção e os comandos run, stop, cont e print.................................................................................................. 1078 Os comandos print e set .............................................................................................................................................................. 1081 Controlando a execução utilizando os comandos step, step up e next..................................................................................... 1082 O comando watch ......................................................................................................................................................................... 1085 O comando clear.......................................................................................................................................................................... 1086 Conclusão........................................................................................................................................................................................ 1088

Saída formatada

1090

Introdução...................................................................................................................................................................................... 1091 Fluxos............................................................................................................................................................................................. 1091 Formatando a saída com printf.................................................................................................................................................. 1091 Imprimindo inteiros...................................................................................................................................................................... 1092

xvi

Sumário

G.5  Imprimindo números de ponto flutuante..................................................................................................................................... 1092 G.6  Imprimindo strings e caracteres................................................................................................................................................... 1094 G.7  Imprimindo datas e horas............................................................................................................................................................. 1094 G.8  Outros caracteres de conversão..................................................................................................................................................... 1096 G.9  Imprimindo com larguras e precisões de campos........................................................................................................................ 1098 G.10  Utilizando flags na string de formato printf........................................................................................................................... 1099 G.11  Imprimindo com índices de argumento..................................................................................................................................... 1102 G.12  Imprimindo literais e sequências de escape................................................................................................................................ 1103 G.13  Formatando saída com a classe Formatter............................................................................................................................... 1103 G.14  Conclusão..................................................................................................................................................................................... 1104

Apêndices na Web

1109

H

Sistemas de numeração

I

GroupLayout

J

Java Desktop Integration Components (JDIC)

K

Mashups

XXVI

L

Unicode®

XXIX

M

Criando documentaçãocom o javadoc

XXXIV

N

Manipulação de bits

O

Instruções rotuladas break e continue

LIV

P

UML 2: Tipos de diagramas adicionais

LVII

Q

Padrões de design

Índice remissivo

I

XII XXI

XLIII

LVIII 1110

Prefácio Nunca mais viver em fragmentos, apenas se conectar. — Edgar Morgan Foster Bem-vindo ao Java e ao Java — Como programar, oitava edição! Este livro apresenta tecnologias de computação de ponta para estudantes, instrutores, desenvolvedores de software e profissionais de TI. Utilizamos a característica “abordagem live-code” de nossos livros, apresentando a maioria dos conceitos no contexto de programas Java totalmente funcionais, em vez do uso de trechos de código. Cada exemplo de código é imediatamente seguido por uma ou mais execuções de exemplo. Todo o código-fonte está disponível em www.deitel.com/books/jhtp8/ e www.prenhall.com/deitel_br. Na Deitel & Associates, escrevemos livros universitários e livros profissionais sobre linguagens de programação para a Pearson/Prentice Hall, ministramos cursos de treinamento corporativo no mundo todo e desenvolvemos negócios na Internet baseados na Web 2.0. Atualizamos a edição anterior deste livro com base nas modificações recentes na linguagem Java e na evolução das maneiras preferidas de ensino e aprendizagem de programação. Todos os capítulos foram significativamente aprimorados.

Recursos novos e atualizados Aqui estão as atualizações que fizemos neste livro: • O livro tem um novo design interno que organiza, esclarece e destaca graficamente as informações e aprimora sua pedagogia. • Atualizamos o livro inteiro de acordo com a Java Standard Edition 6 Update 11 e auditamos e comparamos cuidadosamente o manuscrito com a Java Language Specification. • Adicionamos o conjunto de exercícios “Fazendo a diferença”: os estudantes querem fazer a diferença. Nós os encorajamos a associar os computadores e a Internet à solução de problemas que realmente importam para pessoas, comunidades, países e para o mundo. Esperamos que os novos exercícios estimulem os estudantes a pensar por conta própria à medida que eles exploram questões sociais complexas. Esses exercícios não visam a fazer uma declaração política. Eles foram concebidos para aumentar a conscientização em relação a questões importantes que o mundo enfrenta. Os estudantes devem abordar essas questões no contexto dos seus próprios valores, política e crenças. Muitos dos novos exercícios exigem que os estudantes façam pesquisas na Web — e transformem os resultados em um processo de solução de problemas próprio. Eis uma lista dos 33 novos exercícios de “Fazendo a diferença”:  Test drive: Calculadora de emissão de carbono  Test drive: Calculadora de índice de massa corpórea  Atributos dos veículos híbridos  Neutralidade de sexos  Calculadora de índice de massa corpórea  Crescimento demográfico mundial  Perguntas sobre fatos relacionados ao aquecimento global  Alternativas de planejamento tributário; o “imposto justo”  Instrução auxiliada por computador  Instrução auxiliada por computador: Redução da fadiga do aluno  Instrução auxiliada por computador: Monitoramento do desempenho estudantil  Instrução auxiliada por computador: Níveis de dificuldade  Instrução auxiliada por computador: Variação dos tipos de problemas

xviii

Prefácio

Enquetes Controle de tráfego aéreo Interface de emissão de carbono: Polimorfismo Calculadora do crescimento demográfico mundial Calculadora da economia do transporte solidário Calculadora da taxa de frequência cardíaca Computadorização dos registros de saúde Impondo privacidade com criptografia Ecofont Professor de digitação: Aprimorando uma habilidade crucial na era da informática Telas com fontes grandes para pessoas com problemas de visão Cozinhando com ingredientes mais saudáveis Scanner de spam Scanner de phishing Projeto de acessibilidade: Síntese da fala Projeto de acessibilidade: Reconhecimento da fala Projeto: Simulador robótico Simbad Serviço Web de scanner de spam Serviço Web de SMS Serviço Web de neutralidade de sexos

• Ajustamos o estudo de caso opcional do projeto orientado a objetos/UML 2 do caixa eletrônico (ATM) e o reorganizamos em dois capítulos opcionais (12 e 13) que apresentam o projeto do caixa eletrônico e a implementação completa do código. O ATM é um bom exemplo de negócio com o qual os alunos podem se relacionar. Na nossa experiência, ensinar esses dois capítulos como uma unidade ajuda os estudantes a associar muitos dos conceitos orientados a objetos que eles aprendem nos capítulos 1–10. Um conceito-chave na programação orientada a objetos é a interação entre objetos. Na maioria dos livros escolares de programação, os exemplos de código criam e utilizam um ou dois objetos. O ATM dá aos estudantes a oportunidade de estudar interações de muitos objetos que fornecem a funcionalidade de um sistema substancial. Os capítulos 12 e 13 fornecem soluções completas para todos os exercícios relacionados. Na edição anterior, o estudo de caso foi distribuído ao longo dos capítulos 2–8, 10 e um apêndice. Para professores que desejam abordar o estudo de caso de uma maneira distribuída, para cada seção nos capítulos 12 e 13, indicamos depois de qual capítulo anterior a seção pode ser abordada. • Reforçamos nossa pedagogia de introdução antecipada de classes e objetos dando bastante atenção à orientação dos professores universitários nas nossas equipes de revisão para assegurar que alcançamos o nível conceitual certo. O tratamento da POO (Programação Orientada a Objetos) é claro e acessível. Introduzimos os conceitos básicos e a terminologia da tecnologia de objetos no Capítulo 1. Os estudantes desenvolvem suas primeiras classes e objetos personalizados no Capítulo 3. Apresentar objetos e classes, logo no início, faz com que os estudantes “pensem sobre objetos” imediatamente e dominem esses conceitos mais a fundo. • Reordenamos nossa apresentação das estruturas de dados. Agora começamos com a classe genérica ArrayList no Capítulo 7. Como os estudantes entenderão os conceitos genéricos básicos bem no início do livro, nossas discussões posteriores sobre estruturas de dados fornecem um tratamento mais profundo das coleções genéricas — mostrando como utilizar as coleções internas da API do Java. Mostramos, então, como implementar métodos e classes genéricos. Por fim, mostramos como construir estruturas de dados genéricas personalizadas. • Adicionamos a cobertura do Java Web Start e Java Network Launch Protocol ( JNLP), que permitem que applets e aplicativos sejam carregados por um navegador Web. Além disso, o usuário pode instalá-los como atalhos na área de trabalho para executá-los, no futuro, sem revisitar o site Web. Os programas também podem solicitar a permissão do usuário para acessar recursos do sistema local, tais como arquivos — permitindo que você desenvolva applets e aplicativos mais robustos, executados de uma maneira segura utilizando o modelo de segurança de caixa de areia do Java, que se aplica ao código baixado da Internet. • Reordenamos vários capítulos para facilitar o ensino do livro em módulos. O fluxograma de dependências (página xxiii) foi atualizado para refletir a nova modularização. • Adicionamos muitos links à documentação on-line, para que os estudantes possam aprender mais sobre uma classe ou um tópico, e adicionamos muitos links para os Resource Centers relacionados ao Java da Deitel, disponíveis em www.deitel.com/ResourceCenters.html. • O Capítulo 7 agora abrange a classe Arrays — que contém métodos para realizar manipulações de array comuns — e a classe Ar­ rayList — que implementa uma estrutura de dados parecida a um array dinamicamente redimensionável. Isso segue a nossa filosofia de utilizar classes existentes antes de aprender como definir suas próprias classes. • Agora, introduzimos a classe BigInteger para valores inteiros, arbitrariamente grandes, no Capítulo 18, “Recursão”.



Prefácio

xix

• Ajustamos cuidadosamente todos os capítulos com ênfase maior na clareza e simplicidade, eliminando redundâncias, reduzindo o número de páginas (esta nova edição tem 90 páginas a menos do que a anterior) e aprimorando a pedagogia e a organização modular. • Substituímos todos os usos da StringTokenizer pelo método recomendável String split por todo o livro. A classe Tokenizer ainda é discutida, principalmente para compatibilidade com versões anteriores com código legado.

String­

• Incluímos uma lista em ordem alfabética dos termos importantes definidos em cada capítulo com o número de página da ocorrência que define o termo. Ocorrências de definições também são destacadas no índice com um número de página em vermelho escuro. Tudo isso foi cuidadosamente revisado por 24 eminentes acadêmicos e desenvolvedores do setor que trabalharam conosco neste livro. Acreditamos que este livro e seu material de suporte propiciarão a estudantes e profissionais uma experiência educacional em Java informativa, interessante, desafiadora e divertida. Fornecemos um conjunto de materiais subsidiário que ajudará os professores a maximizar a experiência de aprendizagem dos seus alunos. Ao ler o livro, se tiver dúvidas, envie um e-mail para [email protected]; responderemos prontamente. Para atualizações deste livro e o status de todo o software Java de suporte, e para as notícias mais recentes sobre todas as publicações e serviços Deitel, visite www.deitel. com. Registre-se em www.deitel.com/newsletter/subscribe.html para receber por e-mail o boletim informativo gratuito Deitel® Buzz On-line (em inglês) e conheça nossa crescente lista sobre Java e recursos relacionados do Resource Centers em www.deitel.com/ ResourceCenters.html. Toda semana anunciamos nossos mais recentes recursos no boletim informativo.

Outros recursos Outros recursos desta oitava edição: • Alinhamos a apresentação com as recomendações curriculares da ACM/IEEE e do Computer Science Advanced Placement Examination. • A apresentação antecipada de classes e objetos fornece estudos de caso sobre as classes Time, Employee e GradeBook discutidas em várias seções e capítulos, introduzindo gradualmente conceitos OO mais profundos. • Professores que ministram cursos introdutórios têm uma ampla escolha quanto ao volume de material sobre GUIs e elementos fluxogramas a abranger — desde nenhuma sequência introdutória, ou dez breves seções, até um tratamento detalhado nos capítulos 14, 15 e 25 e no Apêndice I. • Nossa programação orientada a objetos e as apresentações de design utilizam a UML™ (Unified Modeling Language™) — a linguagem gráfica padrão do setor para modelagem de sistemas orientados a objetos. • Fornecemos vários estudos de caso substanciais de programação Web orientada a objetos. • O Capítulo 28, “Acesso a bancos de dados com o JDBC”, abrange o JDBC 4 e utiliza os sistemas de gerenciamento de bancos de dados Java DB/Apache Derby e MySQL. O capítulo apresenta um estudo de caso OO sobre o desenvolvimento de um catálogo de endereços voltado a banco de dados que demonstra instruções preparadas e a descoberta automática de drivers do JDBC 4. • O Capítulo 29, “Aplicativos Web JavaServer™ Faces” e o Capítulo 30, “Aplicativos Web JavaServer™ Faces compatíveis com Ajax”, introduzem a tecnologia JavaServer Faces ( JSF) e utiliza-a com o NetBeans 6.5 para construir aplicativos Web rápida e facilmente. O Capítulo 29 inclui exemplos da criação de GUIs de aplicativos Web, tratamento de eventos, formulários de validação e rastreamento de sessão. O Capítulo 30 discute o desenvolvimento de aplicativos Web compatíveis com o Ajax, utilizando a tecnologia JavaServer Faces. Ele apresenta um aplicativo Web multicamada do tipo catálogo de endereços baseado em banco de dados que permite aos usuários adicionar e pesquisar contatos. Esse aplicativo compatível com o Ajax dá ao leitor uma boa ideia do desenvolvimento de softwares baseado na Web 2.0. O aplicativo utiliza componentes JSF compatíveis com o Ajax para sugerir nomes de contato enquanto o usuário digita um nome a localizar. • O Capítulo 31, “Serviços Web”, utiliza uma abordagem baseada em ferramentas para criar e consumir serviços Web baseados em SOAP e REST. Os estudos de caso incluem o desenvolvimento de serviços Web de reservas aéreas e do jogo Vinte-e-um. • Utilizamos uma nova abordagem baseada em ferramentas para desenvolver rapidamente aplicativos Web; todas as ferramentas estão disponíveis gratuitamente para download. • Fornecemos o 100+ Resource Centers (www.deitel.com/resourcecenters.html) para dar suporte aos nossos leitores acadêmicos e profissionais. Seus tópicos incluem Java SE 6, Java, avaliação e certificação Java, padrões de design Java, Java EE 5, sistemas de pesquisa de código e sites de código, programação de jogos, projetos de programação e muito mais. Para receber por e-mail o boletim informativo gratuito Deitel® Buzz On-line, registre-se em www.deitel.com/newsletter/subscribe.html — toda semana anunciamos nossos Resource Centers mais recentes e incluímos outros itens de interesse para nossos leitores. • Discutimos conceitos-chave da comunidade de engenharia de software, como Web 2.0, Ajax, SaaS (Software as a Service), serviços Web, software de código-fonte aberto, padrões de design, mashups, refatoração, desenvolvimento ágil de softwares, prototipagem rápida etc. • Reformulamos completamente o Capítulo 26, “Multithreading” [agradecemos especialmente a Brian Goetz e Joseph Bowbeer — coautores do Java Concurrency in Practice, Addison-Wesley, 2006].

xx

Prefácio

Programação orientada a objetos

Fluxograma de dependências entre os capítulos [Nota: Setas que apontam para um capítulo indicam as dependências do capítulo. Alguns capítulos têm múltiplas dependências.]

Introdução 1 Introdução aos computadores, à Internet e à World Wide Web

Introdução à programação, classes e objetos 2 Introdução a aplicativos Java 3 Introdução a classes e objetos

(Opcional) GUI & rastreamento gráfico 3.9 Utilizando caixas de diálogo 4.14 Criando desenhos simples

5.10 Desenhando retângulos e ovais

6.13 Cores e formas preenchidas

Programação orientada a objetos

Instruções de controle, métodos e arrays

7.15 Desenhando arcos

8 Classes e objetos: uma visão mais aprofundada

4 Instruções de controle: Parte 1

8.16 Utilizando objetos com imagens gráficas

5 Instruções de controle: Parte 2 9 Programação orientada a objetos: herança 10 Programação orientada a objetos: polimorfismo

6 Métodos: uma visão mais aprofundada 7 Arrays e ArrayLists

11 Tratamento de exceções

Design orientado a objetos com UML 12 Design orientado a objetos (opcional) com UML

Strings e arquivos 16 Strings, caracteres e expressões regulares 17 Arquivos, fluxos e serialização de objetos

Estruturas de dados

Multithreading e redes

18 Recursão1

26 Multithreading2 27 Redes3

19 Pesquisa, ordenação e Big O

21 Genéricos 22 Estruturas de dados genéricas personalizadas

10.8 Desenhando com polimorfismo

GUI, imagens gráficas, applets e multimídia 14 Componentes GUI: Parte 1 15 Imagens gráficas e Java2D 23 Miniaplicativos e Java Web Start

13 Implementação (opcional) de um design orientado a objetos

20 Coleções genéricas

9.8 Exibindo texto e imagens utilizando rótulos

Área de trabalho baseada em banco de dados e desenvolvimento Web 28 JDBC4

29 Aplicativos Web JSF 30 Aplicativos Web JSF compatíveis com o Ajax 31 Serviços Web

24 Multimídia: applets e aplicativos 25 Componentes GUI: Parte 2

1. O Capítulo 18 depende dos capítulos 14 e 15 quanto à GUI e elementos gráficos utilizados em um exemplo. 2. O Capítulo 26 depende do Capítulo 14 quanto à GUI utilizada em um exemplo e dos capítulos 20–21 quanto a um exemplo. 3. O Capítulo 27 depende do Capítulo 23 quanto a um exemplo que utiliza um applet. O grande estudo de caso no final desse capítulo depende do Capítulo 25 para GUI e do Capítulo 26 para multithreading. 4. O Capítulo 28 depende do Capítulo 14 devido à GUI usada em um exemplo.



Prefácio

xxi

• Discutimos a classe SwingWorker para desenvolver interfaces com o usuário baseadas em multithreading. • Discutimos o gerenciador de layout GroupLayout no contexto da ferramenta de design de GUI no IDE do NetBeans. • Apresentamos capacidades de ordenação e filtragem do JTable que permitem ao usuário reordenar os dados. • Discutimos a classe StringBuilder, que tem um desempenho melhor do que StringBuffer em aplicativos sem threading. • Apresentamos anotações, que reduzem significativamente a quantidade de código que você tem de escrever para construir aplicativos.

Estudo de caso opcional Utilizando UML 2 para desenvolver um design de ATM orientado a objetos A UML 2 tornou-se a linguagem de modelagem gráfica preferida para projetar sistemas orientados a objetos. Utilizamos diagramas de atividade UML (em vez de fluxogramas) para demonstrar o fluxo de controle em cada uma das instruções de controle Java e utilizamos diagramas de classe UML para representar visualmente classes e seus relacionamentos de herança. Incluímos um estudo de caso opcional (mas altamente recomendável) sobre o projeto orientado a objetos utilizando a UML. O estudo de caso foi revisado por meio de várias edições, por profissionais do setor e uma equipe eminente de acadêmicos OOD/UML, incluindo líderes no segmento da Rational (os criadores da UML) e do Object Management Group (responsável pela evolução da UML). No estudo de caso, projetamos e implementamos integralmente o software para um sistema de caixa eletrônico (Automatic Teller Machine — ATM) simples. O estudo de caso opcional de engenharia de softwares nos capítulos 12 e 13 apresenta uma introdução cuidadosamente compassada ao projeto orientado a objetos utilizando a UML. Introduzimos um subconjunto simples e conciso da UML 2 e então orientamos o leitor para uma primeira experiência de design destinada a principiantes. O estudo de caso não é um exercício; em vez disso, é uma experiência de aprendizagem de ponta a ponta que termina com uma revisão detalhada do código Java completo. Os capítulos 12 e 13 ajudam os alunos a desenvolver um projeto orientado a objetos para complementar os conceitos da programação orientada a objetos que eles aprenderam nos capítulos de 1–11. No final do Capítulo 1, introduzimos conceitos básicos e terminologia relacionada ao OOD. No Capítulo 12, consideramos questões mais substanciais, à medida que empreendemos um problema desafiador com as técnicas OOD. Analisamos um documento de requisitos típicos que especifica um sistema a ser construído, determinamos os objetos necessários para implementar esse sistema, determinamos os atributos que esses objetos precisam ter, determinamos os comportamentos que esses objetos precisam exibir e especificamos como os objetos devem interagir um com o outro para atender aos requisitos do sistema. No Capítulo 13, incluímos uma implementação de código Java completa do sistema orientado a objetos que projetamos no Capítulo 12. Esse estudo de caso ajuda a preparar os alunos para os tipos de projetos substanciais que eles encontrarão na indústria. Empregamos um processo de projeto orientado a objetos incremental cuidadosamente desenvolvido para produzir um modelo de UML 2 para nosso sistema ATM. A partir desse projeto, criamos uma rica implementação Java funcional utilizando noções-chave da programação orientada a objeto, incluindo classes, objetos, encapsulamento, visibilidade, composição, herança e polimorfismo.

Fluxograma de dependências O fluxograma da página anterior mostra as dependências entre os capítulos para ajudar os professores a planejar seus planos de estudos. Este livro é apropriado para vários cursos de programação em vários níveis, mais notavelmente cursos e sequências de cursos de Ciência da Computação 1 e 2 nas disciplinas relacionadas. O livro tem uma organização claramente delineada e modular. Os capítulos 1–11 e 14–17 formam uma sequência de programação elementar acessível com uma sólida introdução à programação orientada a objetos. Os capítulos 12–13 opcionais formam uma introdução acessível ao projeto orientado a objetos com a UML. Os tópicos sobre GUI e rastreamento gráfico junto com os capítulos 14, 15, 23, 24 e 25 formam uma sequência substancial sobre GUI, imagens gráficas e multimídia. Os capítulos 18–22 formam uma boa sequência de estruturas de dados. Os capítulos 26–27 formam uma sólida introdução a multithreading e redes Internet. Os capítulos 28–31 formam uma sequência clara do desenvolvimento Web que usa intensamente banco de dados.

Ajuda para planos de estudo Teremos toda satisfação em ajudar os professores a criar planos de estudos baseados neste livro. Você pode entrar em contato conosco por e-mail ([email protected]).

A abordagem de ensino Este livro contém uma rica coleção de exemplos. O livro concentra-se nos princípios da boa engenharia de software e destaca principalmente a clareza da programação. Ensinamos por meio de exemplos. Somos educadores que ensinam linguagens de programação de ponta e tópicos relacionados a softwares nos setores governamental, industrial, militar e acadêmico no mundo todo.

xxii

Prefácio

Abordagem Live-Code. Este livro está repleto de exemplos de códigos funcionais. Isso significa que cada novo conceito é apresentado no contexto de um aplicativo Java totalmente funcional, seguido imediatamente por uma ou mais execuções reais que mostram as entradas e saídas do programa. Sintaxe colorida. Por questões de legibilidade, utilizamos uma sintaxe colorida para todos os códigos Java, semelhante à maneira da maioria dos ambientes de desenvolvimento integrado Java e editores de código, que utilizam cores nos códigos. Nossas convenções para cores de sintaxe incluem: Comentários aparecem em verde Palavras­chave aparecem em azul­escuro Erros aparecem em vermelho Constantes e valores literais aparecem em azul claro Todos os outros códigos aparecem em preto

Destaque de código. Realçamos em amarelo os segmentos de código mais importantes. Uso de fontes para ênfase. Inserimos os termos-chave e a referência de página do índice para cada ocorrência definidora em texto em negrito em vermelho escuro para facilitar a referência. Enfatizamos os componentes na tela com a fonte Helvetica em negrito (por exemplo, o menu File) e enfatizamos o texto do programa Java na fonte Lucida (por exemplo, int x = 5;). Acesso Web. Todos os exemplos de código-fonte deste livro estão disponíveis para download em: www.pearson.com.br/deitel www.prehall.com/deitel_br

Objetivos. Cada capítulo inicia com uma apresentação de objetivos. Citações. Os objetivos da aprendizagem são seguidos por citações. Esperamos que você aprecie relacionar esses exemplos ao material do capítulo. Ilustrações/figuras. Inúmeros gráficos, tabelas, desenhos a traço, programas e saída de programa foram incluídos. Modelamos o fluxo de controle em instruções de controle com diagramas de atividade UML. Os diagramas de classes UML modelam os campos, construtores e métodos das classes. Fazemos uso extenso de seis tipos de diagrama UML principais no estudo de caso opcional do ATM, baseado no OOD/UML 2. Dicas de programação. Incluímos dicas de programação para ajudá-lo a focalizar aspectos importantes do desenvolvimento do programa. Essas dicas e práticas representam o melhor que reunimos a partir de sete décadas combinadas de programação e experiência pedagógica.

Boa prática de programação As Boas práticas de programação chamam atenção a técnicas que irão ajudá-lo a criar programas que são mais claros, mais compreensíveis e mais fáceis de manter.

Erro de programação comum Indicar esses erros de programação comuns reduz a probabilidade de que eles aconteçam.

Dica de prevenção de erro Essas dicas contêm sugestões para expor bugs e removê-los dos seus programas; muitos descrevem aspectos do Java que evitam que bugs apareçam nos programas.

Dica de desempenho Essas dicas destacam oportunidades para fazer seus programas executarem mais rapidamente ou minimizar a quantidade de memória que eles ocupam.

Dica de portabilidade As dicas de portabilidade ajudam a escrever código que poderá ser executado em diferentes plataformas.

Observação de engenharia de software As Observações de engenharia de software destacam questões arquitetônicas e de projeto que afetam a construção de sistemas de software, especialmente sistemas de larga escala.



Prefácio

xxiii

Observação sobre a aparência e funcionamento Observações sobre a aparência e funcionamento destacam as convenções da interface gráfica com o usuário. Essas observações ajudam a criar interfaces gráficas atraentes e amigáveis ao usuário que seguem as normas da indústria.

Seção de resumo. Cada capítulo termina com uma seção de resumo que recapitula seu conteúdo e as transições para o próximo capítulo. Lista de tópicos de resumo. Cada capítulo termina com dispositivos pedagógicos adicionais. Apresentamos um resumo do capítulo, sessão por sessão, no estilo de lista itemizada. Terminologia. Incluímos uma lista em ordem alfabética dos termos importantes definidos em cada capítulo com o número de página da ocorrência que define o termo. Ocorrências de definições também são destacadas no índice com um número de página em vermelho escuro. Exercícios e respostas de autorrevisão. Extensos exercícios e respostas de autorrevisão são incluídos para autoaprendizagem. Todos os exercícios no estudo de caso opcional do ATM estão totalmente resolvidos. Exercícios. Cada capítulo termina com um conjunto substancial de exercícios, incluindo recapitular resumidamente a terminologia e conceitos importantes; identificar erros nos exemplos de código; escrever instruções de programa individuais; escrever pequenas partes dos métodos e classes Java; escrever métodos e classes e programas Java completos; e criar projetos de conclusão de curso. Os instrutores podem utilizar esses exercícios para formar deveres de casa, pequenos questionários, exames importantes e projetos de conclusão. [NOTA: Não nos escreva solicitando acesso ao Resource Center da Pearson Instructor. O acesso é limitado estritamente a professores universitários que utilizam o livro em suas aulas. Professores só podem obter acesso por meio dos seus representantes na Pearson.] Certifique-se de verificar em nosso Programming Projects Resource Center (http://www.deitel.com/ProgrammingProjects/) vários exercícios adicionais e possibilidades de projeto. Milhares de entradas de índice. Incluímos um índice extenso, que é especialmente útil ao utilizar o livro como uma referência.

Recursos para o aluno incluídos neste livro Há muitas ferramentas de desenvolvimento Java disponíveis para compra, mas você não precisa de nenhuma delas para começar a trabalhar com o Java. Para sistemas Windows, todos os softwares que você precisará para este livro estão disponíveis gratuitamente para o download na Web ou no CD que o acompanha. Para outras plataformas, todos os softwares que você precisará para este livro estão, na sua maioria, disponíveis gratuitamente para download a partir da Web. Escrevemos a maioria dos exemplos neste livro utilizando o Java Standard Edition Development Kit ( JDK) 6. A versão atual do JDK (e separadamente sua documentação) pode ser baixada do site Web Java da Sun, java.sun.com/javase/downloads/index.jsp. Os usuários do Mac OS X podem fazer o download do Java em developer. apple.com/java. Em vários capítulos, também utilizamos o IDE do NetBeans. O NetBeans está disponível como um pacote com o JDK do site Web Java da Sun precedente, ou você pode fazer o download dele separadamente em www.netbeans.org/downloads/index.html. O Eclipse pode ser baixado de www.eclipse.org/downloads/. O MySQL pode ser baixado de http://dev.mysql.com/downloads/, e MySQL Connector/J em http://dev.mysql.com/downloads/connector/j/5.1.html. Você pode encontrar recursos adicionais e downloads de softwares no nosso Resource Center do Java SE 6 em: www.deitel.com/JavaSE6Mustang/

O CD que acompanha este livro contém versões dos seguintes pacotes de software para uso no Microsoft® Windows®: • O Java™ SE Development Kit ( JDK) 6 Update 11 — que foi utilizado para criar e testar todos os programas no livro. • O IDE do Eclipse para desenvolvedores Java EE versão 3.4.1. • NetBeans™ IDE Version 6.5 All Bundle. • MySQL® 5.0 Community Server, versão 5.0.67. • MySQL® Connector/J, versão 5.1.7. O NetBeans e o Eclipse são ambientes de desenvolvimento integrado (IDEs) para desenvolver todos os tipos de aplicativos Java. O MySQL e o Conector/J MySQL são fornecidos para os aplicativos de banco de dados nos capítulos 28–31. Todas essas ferramentas também podem ser baixadas para outras plataformas, como discutido em “Antes de você começar”, depois deste Prefácio. O CD também contém uma página Web com links para o site Web da Deitel & Associates, Inc. e para o site Web da Pearson. Essa página da Web pode ser carregada em um navegador da Web para acesso rápido a todos os recursos.

xxiv

Prefácio

Companion Website O site de apoio desta oitava edição brasileira (www.pearson.com.br/deitel www.prenhall.com/deitel_br) oferece: • Para estudantes  exercícios de múltipla escolha  código-fonte dos exemplos apresentados no livro (em inglês) • Para professores1  manual de soluções (em inglês)  apresentações em PowerPoint

Cursos avançados de ciência da computação Este livro é adequado para ensino avançado de ciência da computação e também para preparar os alunos para realizar os exames avançados.1

Boletim Deitel® Buzz On-line gratuito via e-mail Toda semana, o Deitel® Buzz On-line anuncia o(s) nosso(s) Resource Center(s) mais recente(s) e inclui comentários sobre tendências e desenvolvimentos do setor, links para artigos gratuitos e recursos oferecidos por nossos livros publicados e futuras publicações, agendas de lançamento de produtos, erratas, desafios, depoimentos de leitores e usuários, informações sobre nossos cursos corporativos voltados a professores e muitos mais. Também é um bom modo de manter-se atualizado sobre este livro. Para cadastrar-se, visite: www.deitel.com/newsletter/subscribe.html

Deitel on-line Resource Centers O site Web www.deitel.com fornece mais de 100 Resource Centers (em inglês) sobre vários temas, incluindo linguagens de programação, desenvolvimento de software, Web 2.0, negócios on-line e projetos de código-fonte aberto — veja a lista dos Resource Centers em www. deitel.com/ResourceCenters.html. Os Resource Centers evoluíram a partir de pesquisas que fazemos para dar suporte aos nossos livros e empreendimentos comerciais. Encontramos muitos recursos excepcionais on-line, incluindo tutoriais, documentação, downloads de software, artigos, blogs, podcasts, vídeos, exemplos de código, livros, e-books e mais — a maioria deles é gratuita. Toda semana anunciamos nossos Resource Centers no nosso boletim informativo, o Deitel® Buzz On-line (www.deitel.com/newsletter/subscribe.html). Alguns dos Resource Centers que você pode achar útil ao estudar este livro são Java SE 6, Java, avaliação e certificação em Java, padrões de design Java, Java EE 5, sistemas de pesquisa de código e sites de código, de jogos, programação de jogos, programação em geral e muitos outros.

Agradecimentos É um prazer reconhecer os esforços das pessoas cujos nomes não aparecem na capa, mas cujo trabalho árduo, cooperação, amizade e compreensão foram cruciais para a produção do livro. Muitas pessoas na Deitel & Associates, Inc., dedicaram longas horas a este projeto — agradecemos especialmente a Abbey Deitel e Barbara Deitel. Também gostaríamos de agradecer aos participantes do nosso Honors Internship Program que contribuíram para esta nova edição — Nicholas Doiron, bacharel em engenharia elétrica e de computação pela Carnegie Mellon University; e Matthew Pearson, bacharel em ciência da computação pela Cornell University. Tivemos a felicidade de trabalhar neste projeto com uma equipe talentosa e dedicada de profissionais de publicação da Pearson. Apreciamos os esforços extraordinários de Marcia Horton, diretora editorial de engenharia e ciência da computação da Pearson. Carole Snyder e Dolores Mars fizeram um trabalho extraordinário na contratação da equipe de revisão do livro e gerenciamento do processo de revisão. Francesco Santalucia (um artista independente) e Kristine Carney, da Pearson, fizeram um trabalho maravilhoso na criação da capa do livro — fornecemos o conceito e eles o tornaram uma realidade. Scott Disanno e Robert Engelhardt fizeram um maravilhoso trabalho no gerenciamento da produção do livro. Erin Davis, nosso gerente de marketing, e Margaret Waples, sua chefe, fizeram um excelente trabalho para o marketing do livro em canais acadêmicos e profissionais.

Revisores desta edição Queremos reconhecer os esforços dos nossos revisores. Seguindo uma agenda apertada, eles escrutinaram o texto e os programas, e forneceram inúmeras sugestões para aprimorar a exatidão e a completa abrangência da apresentação: 1

Este material é de uso exclusivo de professores e protegido por senha. Para adquirir sua senha, contate seu representante ou envie um e-mail para o endereço [email protected]. [email protected]

Prefácio

• • • • • •

Revisores da Microsystems Lance Andersen Soundararajan Angusamy Lawrence Prem Kumar Simon Ritter Sang Shin Alexander Zuev

• • • • • • • • • • • •

Revisores acadêmicos William E. Duncan (Louisiana State University) Diana Franklin (University of California, Santa Barbara) Edward F. Gehringer (North Carolina State University) Ric Heishman (George Mason University) Patty Kraft (San Diego State University) Manjeet Rege, Ph.D. (Rochester Institute of Technology) Tim Margush (University of Akron) Sue McFarland Metzger (Villanova University) Shyamal Mitra (The University of Texas at Austin) Susan Rodger (Duke University) Amr Sabry (Indiana University) Monica Sweat (Georgia Tech)

• • • • • •

Revisores do setor Joseph Bowbeer (Consultor) Peter Pilgrim (Lloyds TSB) José Antonio González Seco (Parlamento de Andalusia) S. Sivakumar (Astra Infotech Private Limited) Raghavan “Rags” Srinivas (Intuit) S. Sivakumar (Astra Infotech Private Limited)

xxv

Bem, aí está! O Java é uma linguagem de programação poderosa que ajuda a escrever programas de maneira rápida e eficiente. É perfeitamente adequado ao âmbito do desenvolvimento de sistemas corporativos para ajudar organizações a construir seus sistemas de informação de negócios e missões cruciais. À medida que você ler este livro, apreciaríamos sinceramente seus comentários, críticas, correções e sugestões para melhorar o texto. Envie qualquer correspondência para: [email protected] [email protected]

Postaremos correções e esclarecimentos (em inglês) em: www.deitel.com/books/jHTP8/

Esperamos que você aprecie ler este livro tanto quanto apreciamos escrevê-lo! Paul J. Deitel Dr. Harvey M. Deitel

Sobre os autores Paul J. Deitel, executivo-chefe de tecnologia da Deitel & Associates, Inc., é graduado pela Sloan School of Management do MIT, onde estudou Tecnologia da Informação. Ele tem as certificações Java Certified Programmer e Java Certified Developer e foi nomeado pela Sun Microsystems como um Java Champion. Por meio da Deitel & Associates, ministrou cursos de programação sobre Java, C, C++, C#, Visual Basic e Internet para clientes da indústria, incluindo Cisco, IBM, Sun Microsystems, Dell, Lucent Technologies, Fidelity, NASA no Kennedy Space Center, National Severe Storm Laboratory, White Sands Missile Range, Rogue Wave Software, Boeing, Stratus, Cambridge Technology Partners, Open Environment Corporation, One Wave, Hyperion Software, Adra Systems, Entergy, Cable-Data Systems, Nortel Networks, Puma, iRobot, Invensys e muitos mais. Deu palestras sobre Java e C++ para o Boston Chapter da Association for Computing Machinery. Ele e seu coautor, Dr. Harvey M. Deitel, são os autores de livros-texto sobre linguagem de computação líderes de vendas no mundo todo.

xxvi

Prefácio

Dr. Harvey M. Deitel, executivo-chefe da Deitel & Associates, Inc., tem 48 anos de experiência acadêmica e empresarial no campo da computação. O Dr. Deitel obteve os graus de B.S e M.S. do MIT e um Ph.D. da Boston University. Ele tem 20 anos de experiência em ensino universitário, incluindo nomeação definitiva e servindo como o chefe do Departamento de Ciência da Computação no Boston College antes de fundar a Deitel & Associates, Inc. com seu filho, Paul J. Deitel. Ele e Paul são os coautores de dezenas de livros e pacotes de multimídia e atualmente estão escrevendo vários outros. Com traduções publicadas em chinês tradicional, chinês simplificado, japonês, alemão, russo, espanhol, coreano, francês, polonês, italiano, português, grego, urdu e turco, os textos da Deitel ganharam reconhecimento internacional. O Dr. Deitel proferiu centenas de seminários profissionais para importantes corporações, instituições acadêmicas, organizações governamentais e órgãos militares.

Sobre a Deitel & Associates, Inc. A Deitel & Associates, Inc. é internacionalmente reconhecida como uma organização corporativa na área de treinamento e autoria especializada em linguagens de programação de computador, tecnologia de software para Internet e Web, treinamento em tecnologia orientada a objetos e desenvolvimento de negócios na Internet por meio da sua Web 2.0 Internet Business Initiative. A empresa fornece cursos voltados a professores nas principais linguagens de programação e plataformas, tais como Java™, C++, C, Visual C#®, Visual Basic®, Visual C++®, XML®, Python®, tecnologia orientada a objetos, programação via Internet e Web e uma crescente lista de cursos adicionais relacionados à programação e desenvolvimento de softwares. Os fundadores da Deitel & Associates, Inc. são Paul J. Deitel e o Dr. Harvey M. Deitel. Os clientes da empresa incluem muitas das maiores empresas de computador do mundo, agências governamentais, áreas do serviço militar e instituições acadêmicas. Por meio da sua parceria de 33 anos na área de publicação com a Prentice Hall/Pearson, a Deitel & Associates, Inc. publica livros universitários e livros profissionais de ponta na área de programação, os cursos Cyber Classrooms com multimídia interativa, os cursos Live-Lessons baseados em DVD, cursos em vídeo baseados na Web e conteúdo eletrônico para os mais populares sistemas de gerenciamento de cursos. A Deitel & Associates e os autores podem ser contatados via e-mail em: [email protected]

Para aprender mais sobre a Deitel & Associates, Inc., suas publicações e seu programa Dive Into® Series Corporate Training ministrado nas premissas dos clientes em todo o mundo, visite: www.deitel.com/training/

e inscreva-se para receber o boletim Deitel® Buzz On-line gratuito via e-mail em: www.deitel.com/newsletter/subscribe.html

Conheça a crescente lista dos Deitel Resource Centers em: www.deitel.com/resourcecenters.html

Para compra de publicações Deitel, visite: www.deitel.com/books/index.html

Antes de você começar

Esta seção contém informações que você deve saber antes de utilizar este livro e instruções para assegurar que seu computador esteja configurado apropriadamente para o uso com este livro. Postaremos atualizações (se houver alguma) para a seção “Antes de você começar” no site Web do livro (em inglês): www.deitel.com/books/jhtp8/

Convenções de fontes e nomes Utilizamos fontes para separar componentes na tela (como nomes de menu e itens de menu) e código ou comandos Java. Nossa convenção é enfatizar componentes na tela utilizando a fonte Helvetica em negrito sem serifas (por exemplo, menu File) e enfatizar código e comandos Java com uma fonte Lucida sem serifas (por exemplo, System.out.println()).

Requisitos de sistema de software e hardware A interface para o conteúdo do CD que acompanha este livro é projetada para iniciar automaticamente por meio do arquivo AUTORUN.EXE (o CD é para uso nos sistemas Microsoft® Windows®). Se uma tela de abertura não aparecer quando você inserir o CD no computador, dê um clique duplo no arquivo welcome.htm para abrir a interface do CD Student, ou consulte o arquivo readme.txt no CD. Na página welcome. htm, clique no link Software na parte inferior da página para examinar os requisitos de sistema de hardware e software. Leia esses requisitos cuidadosamente antes de instalar o software do CD. O CD que acompanha este livro contém versões dos seguintes pacotes de software para uso no Microsoft® Windows®: • O Java™ SE Development Kit ( JDK) 6 Update 11 — que foi utilizado para criar e testar todos os programas no livro. • O IDE do Eclipse para desenvolvedores Java EE versão 3.4.1. • NetBeans™ IDE Version 6.5 All Bundle. • MySQL® 5.0 Community Server/v5.0.67. • MySQL® Connector/J, versão 5.1.7. O Netbeans e o Eclipse são ambientes de desenvolvimento integrado (IDEs) para desenvolver todos os tipos de aplicativos Java. O MySQL e o MySQL Connector/J são fornecidos para os aplicativos de banco de dados nos capítulos 28–31. Versões para outras plataformas de todas essas ferramentas também estão disponíveis para download, o que será discutido na próxima seção.

Instalando o software O CD inclui o software requerido para compilar e executar os exemplos neste livro na plataforma Windows. Para outras plataformas, você pode baixar o software. Usuários do Mac OS X podem aprender como utilizar o Java em um Mac em developer.apple.com/java/

Os usuários do Linux podem obter o Java SE Development Kit mais recente em java.sun.com/javase/downloads/index.jsp

xxviii

Antes de você começar

Instruções de instalação e instaladores No lado esquerdo da página dos requisitos de software e hardware do CD há links para as instruções de instalação para cada pacote de software para o sistema operacional Windows. Cada página de instruções da instalação contém um link para instalador do pacote de software correspondente. Você também pode abrir esses instaladores diretamente da pasta software do CD. Siga as instruções de instalação à risca para cada pacote de software. Antes de executar os aplicativos discutidos neste livro ou construir seus próprios aplicativos, você deve instalar o Java Standard Edition Development Kit ( JDK) 6 ou uma ferramenta de desenvolvimento Java, como o Netbeans (que exige o JDK) ou os ambientes de desenvolvimento integrado (IDEs) do Eclipse. O Netbeans é necessário para os capítulos 29–31. O Eclipse é opcional para uso com este livro. Fornecemos vídeos para ajudá-lo a começar a trabalhar com o Netbeans e Eclipse em: www.prenhall.com/deitel_br

Esses vídeos discutem: • Como criar programas de único arquivo. • Como criar programas de múltiplos arquivos. • Como os IDEs gerenciam arquivos, diretórios e pacotes Java. • Como utilizar o depurador. • Como utilizar bibliotecas Java de terceiros.

Downloads para outras plataformas O software no CD também está disponível para outras plataformas: • O Netbeans com o pacote JDK: java.sun.com/javase/downloads/index.jsp. • O Netbeans sem o pacote JDK: www.netbeans.org/downloads/index.html. • Eclipse: www.eclipse.org/downloads/. • MySQL Community Edition: dev.mysql.com/downloads/. • MySQL Connector/J: dev.mysql.com/downloads/connector/j/5.1.html.

Obtendo os exemplos de código Os exemplos deste livro estão disponíveis para download em: www.prenhall.com/deitel_br

Se você ainda não se registrou no nosso site Web em inglês, acesse www.deitel.com e clique no link Register abaixo do nosso logotipo no canto superior esquerdo da página. Preencha o formulário com suas informações. Não há nenhum custo para registrar-se e não compartilhamos suas informações com ninguém. Enviamos para você apenas e-mails de gerenciamento de conta a menos que você se registre separadamente para receber por e-mail nosso boletim informativo gratuito Deitel® Buzz Online em www.deitel.com/newsletter/ subscribe.html. Depois de registrar-se, você receberá um e-mail de confirmação com seu código de verificação. Você precisará desse código para cadastrar-se em www.deitel.com pela primeira vez. Configure seu cliente de e-mail para permitir e-mails do deitel.com a fim de assegurar que o e-mail de confirmação não seja filtrado como lixo eletrônico. Em seguida, acesse www.deitel.com e cadastre-se utilizando o link Login abaixo do nosso logotipo no canto superior esquerdo da página. Acesse www.deitel.com/books/jhtp8/. Clique no link Examples para fazer o download do arquivo Examples.zip. Anote o local onde você quer salvar o arquivo no seu computador. Esta é outra forma de obter acesso aos exemplos. Supomos que os exemplos estejam localizados em C:\Examples no seu computador. Extraia o conteúdo do Examples.zip utilizando uma ferramenta como o WinZip (www.winzip.com) ou os recursos internos do Windows XP e Windows Vista (ou uma ferramenta semelhante em outras plataformas).

Configurando a variável de ambiente PATH A variável de ambiente PATH no seu computador especifica em quais diretórios o computador pesquisa ao procurar aplicativos, como os aplicativos que permitem compilar e executar seus aplicativos Java (chamados javac e java, respectivamente). Siga atentamente as instruções de instalação para o Java na sua plataforma a fim de certificar-se de que você configurou a variável de ambiente PATH corretamente. Se você não configurar a variável PATH corretamente, ao utilizar as ferramentas do JDK, você receberá uma mensagem como: 'java' is not recognized as an internal or external command, operable program or batch file.



Configurando a variável de ambiente CLASSPATH

xxix

Nesse caso, volte às instruções de instalação para configurar a variável PATH e verifique novamente seus passos. Se baixou uma versão mais recente do JDK, talvez seja necessário mudar o nome do diretório de instalação do JDK na variável PATH.

Configurando a variável de ambiente CLASSPATH Se tentar executar um programa Java e receber uma mensagem como: Exception in thread "main" java.lang. NoClassDefFoundError: NomeDaClasse

então seu sistema tem um variável de ambiente CLASSPATH que deve ser modificada. Para corrigir esse problema, siga os passos da configuração da variável de ambiente PATH, localize a variável CLASSPATH, e então edite o valor da variável para incluir o diretório local — tipicamente representado por um ponto (.). No Windows adicione: .;

ao início do valor CLASSPATH (sem espaços antes ou depois desses caracteres). Em outras plataformas, substitua o ponto-e-vírgula pelos caracteres separadores de caminho apropriados — muitas vezes um dois-pontos (:):

A nova interface Nimbus do Java A partir da atualização 10 do Java SE 6, o Java é distribuído com uma interface nova, elegante e compatível com várias plataformas conhecida como Nimbus. Para programas com interfaces gráficas com o usuário, configuramos nossos sistemas para utilizar o Nimbus como a interface padrão. Para configurar o Nimbus como o padrão para todos os aplicativos Java, você precisa criar um arquivo de texto chamado swing. properties na pasta lib tanto da sua pasta de instalação do JDK como da sua pasta de instalação do JRE. Insira a seguinte linha do código no arquivo: swing.defaultlaf=com.sun.java.swing.plafnimbus.NimbusLookAndFeel

Para mais informações sobre a localização dessas pastas de instalação, visite java.sun.com/javase/6/webnotes/install/index.html. [Nota: além do JRE autônomo, há um JRE aninhado na pasta de instalação do seu JDK. Se estiver utilizando um IDE que depende do JDK (por exemplo, Netbeans), talvez você também precise inserir o arquivo swing.properties na pasta lib aninhada na pasta jre.] Agora você está pronto para começar seus estudos do Java com este livro. Esperamos que você goste dele!

Nossa vida é desperdiçada em detalhes... Simplifique, simplifique. — Henry David Thoreau

O principal mérito da língua é a clareza. — Galeno

My object all sublime I shall achieve in time. (Devo alcançar a tempo meu sublime objetivo.) — W. S. Gilbert

Ele tinha um talento maravilhoso para empacotar bem os pensamentos e torná-los portáteis. — Thomas B. Macaulay

Meu Deus, entre os dois, acho que o mais difícil de entender é o intérprete! — Richard Brinsley Sheridan

O homem ainda é o computador mais extraordinário. — John F. Kennedy

1

Introdução aos computadores, à Internet e à World Wide Web Objetivos Neste capítulo, você aprenderá: 

Os conceitos básicos de rede, hardware e software de computador.



Os conceitos básicos de tecnologia de objeto, como classes, objetos, atributos, comportamentos, encapsulamento e herança.



Os diferentes tipos de linguagens de programação e quais linguagens são mais amplamente utilizadas.



Um ambiente de desenvolvimento de programa Java típico.



O papel do Java no desenvolvimento de aplicativos cliente/servidor distribuídas para a Internet e a Web.



A história da UML — a linguagem de design orientado a objetos padrão no setor.



A história da Internet e da World Wide Web até a Web 2.0.



A fazer test-drive de aplicativos Java.



Algumas das principais tecnologias de software mais recentes.

2

Capítulo 1

Sumário

1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 1.10

Introdução aos computadores, à Internet e à World Wide Web

Introdução Computadores: hardware e software Organização do computador Primeiros sistemas operacionais Computação pessoal distribuída e computação cliente/servidor A Internet e a World Wide Web Linguagens de máquina, linguagens assembly e linguagens de alto nível História do C e do C++ História do Java Bibliotecas de classe do Java

1.11 Fortran, Cobol, Pascal e Ada 1.12 Basic, Visual Basic, Visual C++, C# e .NET 1.13 Ambiente típico de desenvolvimento Java 1.14 Notas sobre o Java e este livro 1.15 Testando um aplicativo Java 1.16 Estudo de caso de engenharia de software: introdução à tecnologia de objetos e à UML 1.17 Web 2.0 1.18 Tecnologias de software 1.19 Conclusão 1.20 Recursos da Web

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios | Fazendo a diferença

1.1 Introdução Bem vindo ao Java — uma poderosa linguagem de programação de computador que é divertida para iniciantes aprenderem e adequada para programadores experientes utilizarem na construção de sistemas de informações empresariais importantes. A oitava edição de Java — Como programar é uma ferramenta de aprendizagem efetiva para cada uma dessas audiências.

Pedagogia O livro enfatiza basicamente como alcançar a clareza do programa pelas técnicas comprovadas da programação orientada a objetos. Os não programadores aprenderão programação da maneira certa desde o início. A apresentação é clara, simples e ricamente ilustrada. Inclui centenas de programas Java e mostra as saídas produzidas quando eles são executados em um computador. Ensinamos recursos de Java no contexto de programas funcionais completos — chamamos isso de abordagem live-code. Os programas de exemplo podem ser baixados de www.deitel.com/books/jhtp8/ ou www.prenhall.com/deitel_br (consulte a Seção “Antes de você começar” depois do Prefácio). Fundamentos Os primeiros capítulos introduzem princípios básicos de informática, programação de computadores e a linguagem de programação Java, fornecendo uma base sólida para o tratamento mais profundo de Java nos capítulos posteriores. Programadores experientes tendem a ler os primeiros capítulos rapidamente e a considerar rigoroso e desafiante o tratamento de Java nos capítulos seguintes. A maioria das pessoas está familiarizada com as extraordinárias tarefas que os computadores realizam. Utilizando este manual, você aprenderá a escrever instruções que fazem com que os computadores realizem essas tarefas. Software (isto é, as instruções que você escreve); hardware (isto é, os computadores). O Java, desenvolvido pela Sun Microsystems, é uma das linguagens mais populares da atualidade para o desenvolvimento de software. Evolução da computação e da programação O uso de computadores está aumentando em quase todos os campos de trabalho. Os custos da computação estão caindo drasticamente, devido aos rápidos avanços nas tecnologias de hardware e software. Os computadores que ocupavam grandes salas e custavam milhões de dólares algumas décadas atrás agora podem ser gravados em chips de silício menores que uma unha, ao custo de apenas alguns poucos dólares. Felizmente, o silício é um dos materiais mais abundantes na Terra — é um ingrediente da areia comum. A tecnologia do chip de silício tornou a computação tão econômica que mais de um bilhão de computadores de uso geral estão em uso em todo o mundo, auxiliando pessoas no comércio, na indústria, no governo e na vida pessoal. O número pode facilmente dobrar nos próximos poucos anos. Ao longo da sua formação acadêmica e profissional, muitos programadores aprenderam uma metodologia conhecida como programação estruturada. Você aprenderá a programação estruturada e uma metodologia empolgante e mais recente, a programação orientada a objetos. Por que ensinamos ambas? Atualmente, a orientação a objeto é a principal metodologia de programação utilizada pelos programadores. Você irá criar e trabalhar com muitos objetos de software neste texto. Mas descobrirá que a estrutura interna deles costuma ser construída com técnicas de programação estruturada. Além disso, a lógica de manipular objetos é ocasionalmente expressa com programação estruturada.

1.2 Computadores: hardware e software

3

Linguagem preferida para aplicativos em rede O Java tornou-se a linguagem preferida para implementar aplicativos baseados na Internet e software para dispositivos que se comunicam por uma rede. Equipamentos de som estéreo e outros dispositivos domésticos muitas vezes são conectados em rede pela tecnologia Java. Atualmente, existem bilhões de celulares e dispositivos portáteis compatíveis com Java! O Java é a linguagem preferida para atender às necessidades de programação de muitas organizações. O Java evoluiu tão rapidamente que esta oitava edição do livro — baseada na Java Standard Edition ( Java SE) 6, atualização 11 — foi publicada exatamente 13 anos depois da primeira edição. O Java é utilizado para um espectro de aplicações tão amplo que ele tem duas outras versões. A Java Enterprise Edition ( Java EE) é adequada para desenvolver aplicativos distribuídos em rede em larga escala e aplicativos baseados na Web — discutiremos vários recursos do Java EE mais adiante. A Java Micro Edition ( Java ME) é voltada para o desenvolvimento de aplicativos de pequenos dispositivos com limitações de memória, como telefones celulares, pagers e PDAs. Como entrar em contato com os autores Se você quiser se comunicar conosco, envie um e-mail para [email protected]. Responderemos prontamente. Para manter-se atualizado com o desenvolvimento do Java na Deitel & Associates, registre-se em nosso boletim gratuito distribuído por e-mail, The Deitel® Buzz Online, em: www.deitel.com/newsletter/subscribe.html

Uma grande quantidade de material adicional sobre o Java pode ser encontrada em nossos Java Resource Centers, uma lista de recursos Java que cresce continuamente em www.deitel.com/ResourceCenters.html. Esperamos que você goste de aprender com esta nova edição de Java — Como programar.

1.2 Computadores: hardware e software Um computador é um dispositivo que pode realizar cálculos e tomar decisões lógicas fenomenalmente mais rápido do que os seres humanos. Boa parte dos computadores pessoais de hoje em dia podem realizar bilhões de cálculos em um segundo. Uma pessoa operando uma calculadora não pode realizar esse número de cálculos em uma vida inteira. (Questões a ponderar: como você saberia se a pessoa somou todos esses números corretamente? Como você saberia se o computador somou os números corretamente?) Supercomputadores já realizam milhares de trilhões (quatrilhões) de instruções por segundo! Para colocar isso em perspectiva, um computador de quatrilhões de instruções por segundo pode realizar mais de 100.000 cálculos por segundo para cada pessoa no planeta! Os computadores processam dados sob o controle de conjuntos de instruções chamados programas de computador. Esses programas orientam o computador por meio de conjuntos ordenados de ações especificadas por pessoas chamadas programadores de computador. Um computador consiste em vários dispositivos conhecidos como hardware (por exemplo, o teclado, tela, mouse, discos, memória, unidades de DVD, CD-ROM e de processamento). Os programas que executam em um computador são referidos como software. Os custos de hardware têm caído significativamente nos últimos anos, a ponto de os computadores pessoais terem se tornado um bem de consumo popular. Neste livro, você aprenderá metodologias comprovadas que podem reduzir custos de desenvolvimento de software — a programação orientada a objetos e (em nosso estudo de caso opcional de engenharia de software nos capítulos 12–13) o design orientado a objetos.

1.3 Organização do computador Independentemente das diferenças na aparência física, praticamente todos os computadores podem ser divididos em várias unidades lógicas ou seções: 1. Unidade de entrada. Essa seção de “recebimento” obtém informações (dados e programas de computador) de dispositivos de entrada e as coloca à disposição de outras unidades para serem processadas. A maioria das informações é inserida em computadores por meio de dispositivos de entrada, como teclados e mouse. Informações também podem ser inseridas de muitos outros modos, incluindo falar com um computador, digitalização de imagens e leitura de códigos de barra, leitura a partir de dispositivos de armazenamento secundário (como unidades de disco rígido, unidades de CD, unidades de DVD e unidades USB) e os computadores também recebem informações da Internet (por exemplo, ao fazer o download de vídeos do YouTube™, e-books da Amazon etc). 2. Unidade de saída. Essa seção de “entrega” pega as informações que o computador processou e as coloca em vários dispositivos de saída para torná-las disponíveis para serem utilizadas fora do computador. Hoje, a maioria das informações produzidas a partir dos computadores é exibida em telas, impressas em papel, reproduzidas em players de áudio (como os populares iPods da Apple), ou utilizadas para controlar outros dispositivos. Os computadores também podem gerar saída de suas informações para redes, como a Internet. 3. Unidade de memória. Essa seção de “armazenamento” de acesso rápido e relativamente baixa capacidade retém as informações que foram inseridas por meio da unidade de entrada, tornando-as imediatamente disponíveis para processamento quando necessário. A unidade de memória também retém informações processadas até que elas possam ser colocadas em dispositivos de saída pela unidade de saída. As informações na unidade de memória são voláteis — em geral, são perdidas quando o computador é desligado. A unidade de memória costuma ser chamada de memória ou memória principal.

4

Capítulo 1

Introdução aos computadores, à Internet e à World Wide Web

4. Unidade de aritmética e lógica (Arithmetic and Logic Unit — ALU). Essa seção de “produção” realiza cálculos, como adição,

subtração, multiplicação e divisão. Essa seção também contém os mecanismos de decisão que permitem ao computador, por exemplo, comparar dois itens da unidade de memória para determinar se são iguais ou não. Nos sistemas atuais, a ALU normalmente é implementada como parte da unidade lógica, a CPU. 5. Unidade central de processamento (Central Processing Unit — CPU). Essa “seção administrativa” coordena e supervisiona a operação das outras seções. A CPU instrui a unidade de entrada sobre quando as informações devem ser lidas e transferidas para a unidade de memória, informa à ALU quando as informações da unidade de memória devem ser utilizadas em cálculos e instrui a unidade de saída sobre quando enviar as informações da unidade de memória para certos dispositivos de saída. Muitos computadores de hoje têm múltiplas CPUs e, portanto, podem realizar muitas operações simultaneamente — esses computadores são chamados de multiprocessadores. Um processador de múltiplos núcleos (ou multi-core) implementa o multiprocessamento em um único chip de circuito integrado — por exemplo um processador de dois núcleos (ou dual-core) tem duas CPUs e um processador de quatro núcleos (ou quad-core) tem quatro. 6. Unidade de armazenamento secundária. Essa é a seção de “armazenamento” de longo prazo e alta capacidade. Programas ou dados que não são utilizados ativamente pelas outras unidades, em geral, são colocados em dispositivos de armazenamento secundário (por exemplo, unidades de disco) até que sejam necessários, possivelmente horas, dias, meses ou até mesmo anos mais tarde. Portanto, diz-se que as informações nos dispositivos de armazenamento secundário são persistentes — são preservadas mesmo quando a energia elétrica é desligada. As informações de armazenamento secundário exigem muito mais tempo para serem acessadas do que as informações na memória principal, mas o custo por unidade de armazenamento secundário é muito menor que o da memória principal. Exemplos de dispositivos de armazenamento secundário incluem CDs, DVDs e unidades flash (às vezes chamadas cartões de memória), que podem armazenar centenas de milhões a bilhões de caracteres.

1.4 Primeiros sistemas operacionais Os primeiros computadores podiam realizar apenas um trabalho ou tarefa por vez. Isso muitas vezes é chamado processamento em lotes de um único usuário e era a regra na década de 1950. O computador executava um único programa ao processar dados em grupos ou lotes. Os usuários geralmente submetiam seus trabalhos a um centro de processamento de dados em unidades de cartões perfurados e muitas vezes esperavam horas ou mesmo dias antes que as listagens estivessem prontas. Sistemas operacionais de software foram desenvolvidos para tornar o uso dos computadores mais conveniente. Os primeiros sistemas operacionais facilitavam e aceleravam a transição entre trabalhos, aumentando a quantidade de trabalho, ou throughput, que os computadores poderiam processar em um dado tempo. Quando os computadores tornaram-se mais poderosos, tornou-se evidente que o processamento em lotes de um único usuário era ineficiente, porque uma grande quantidade de tempo era gasta esperando dispositivos de entrada/saída lentos completarem suas tarefas. A ideia foi concebida para que muitos trabalhos ou tarefas pudessem compartilhar os recursos do computador para alcançar melhor utilização. Isso é chamado de multiprogramação. A multiprogramação envolve a operação simultânea de muitos trabalhos que competem para compartilhar os recursos do computador. Na década de 1960, vários grupos na indústria e universidades foram os pioneiros dos sistemas operacionais de compartilhamento de tempo. O compartilhamento de tempo é um caso especial da multiprogramação em que usuários acessam o computador por meio de terminais, em geral dispositivos com teclados e telas. Dúzias ou até mesmo centenas de usuários compartilham o computador de uma vez. Na verdade, o computador não executa todos os trabalhos simultaneamente. Em vez disso, ele executa uma pequena parte do trabalho de um usuário e, em seguida, atende o próximo usuário, talvez fornecendo serviço para cada usuário várias vezes por segundo. Portanto, os programas dos usuários parecem estar executando simultaneamente. Uma vantagem do compartilhamento de tempo é que as solicitações de usuário recebem respostas quase imediatas.

1.5 Computação pessoal distribuída e computação cliente/servidor Em 1977, a Apple Computer popularizou a computação pessoal. Os computadores tornaram-se tão econômicos que as pessoas podiam comprá-los para utilização pessoal ou profissional. Em 1981, a IBM, o maior fornecedor de computadores do mundo, lançou o IBM Personal Computer. Isso legitimou rapidamente a computação pessoal em negócios, indústria e organizações governamentais. Esses computadores eram unidades “independentes” — as pessoas transportavam discos de um lado para o outro entre si para o compartilhamento de informações (um modo de transferência frequentemente chamado de “rede peão”). Embora computadores pessoais antigos não fossem suficientemente poderosos para suportar vários usuários simultâneos, essas máquinas podiam ser interconectadas em redes de computadores, às vezes por linhas telefônicas e às vezes em redes locais (Local Area Networks — LANs) dentro de uma organização. Isso levou ao fenômeno da computação distribuída, em que a computação de uma organização, em vez de ser realizada somente em alguma instalação central, é distribuída por redes para os locais em que o trabalho da organização é realizado. Os computadores pessoais eram poderosos o bastante para tratar os requisitos de computação de usuários individuais bem como as tarefas básicas de comunicação de passar eletronicamente informações entre computadores. Hoje os computadores pessoais são tão poderosos quanto as máquinas de milhões de dólares de apenas poucas décadas atrás. As máquinas desktop mais poderosas — chamadas estações de trabalho — fornecem a usuários individuais capacidades enormes. As informações

1.6 A Internet e a World Wide Web

5

são facilmente compartilhadas por redes de computadores, onde computadores chamados servidores armazenam dados que podem ser utilizados por computadores clientes distribuídos por toda a rede — daí o termo computação cliente/servidor. O Java tornou-se amplamente utilizado para escrever software de rede de computador e aplicativos distribuídos cliente/servidor. Os sistemas operacionais populares de hoje, como o Linux, o Mac OS X (pronuncia-se “O-S dez”) da Apple e o Microsoft Windows, fornecem os tipos de capacidades necessárias atualmente.

1.6 A Internet e a World Wide Web A Internet — uma rede global de computadores — tem suas raízes na década de1960, quando o Departamento de Defesa dos Estados Unidos disponibilizou recursos financeiros. Originalmente projetada para conectar os principais sistemas de computadores de cerca de uma dúzia de universidades e organizações de pesquisa, a Internet atualmente é acessível a bilhões de computadores e dispositivos controlados por computadores no mundo todo. Com o surgimento da World Wide Web — que permite aos usuários de computador localizar e visualizar documentos baseados em multimídia sobre quase qualquer assunto pela Internet — a Internet explodiu, tornando-se um dos principais mecanismos de comunicação do mundo. Antigamente, a maioria dos aplicativos de computador executava em computadores que não se comunicavam entre si. Hoje em dia, é possível escrever aplicativos que permitem que os computadores se comuniquem por todo o mundo. A Internet, combinando tecnologias de computação e comunicação, torna o trabalho das pessoas mais fácil. Ela torna informações acessíveis mundialmente de forma instantânea e conveniente. Permite aos indivíduos e empresas de pequeno porte local obter exposição mundial. Ela está mudando a maneira como os negócios são feitos. As pessoas podem procurar os melhores preços em praticamente todos os produtos ou serviços. Comunidades de interesse especial podem permanecer em contato entre si. Os pesquisadores podem tornar-se instantaneamente cientes dos últimos avanços científicos. Java — Como programar apresenta técnicas de programação que permitem aos aplicativos Java utilizar a Internet e a Web para interagir com outros aplicativos. Essas capacidades e outras permitem desenvolver o tipo de aplicativos distribuídos de nível corporativo utilizados no setor hoje em dia. Os aplicativos Java podem ser escritos para executar portatilmente em todo tipo de computador importante, reduzindo significativamente o tempo e custo de desenvolvimento de sistemas.

1.7 Linguagens de máquina, linguagens assembly e linguagens de alto nível Os programadores escrevem instruções em várias linguagens de programação, algumas diretamente compreensíveis por computadores e, outras, requerendo passos intermediários de tradução. Centenas dessas linguagens estão em uso atualmente. Essas linguagens podem ser divididas em três tipos gerais: 1. linguagens de máquina 2. linguagens assembly 3. linguagens de alto nível Qualquer computador pode entender diretamente somente sua própria linguagem de máquina. Essa é a “linguagem natural” do computador, definida pelo seu design de hardware. As linguagens de máquina consistem geralmente em strings de números (em última instância reduzidas a 1s e 0s) que instruem os computadores a realizar suas operações mais elementares uma de cada vez. As linguagens de máquina são dependentes de máquina (uma linguagem particular de máquina pode ser utilizada apenas em um tipo de computador). Essas linguagens são complicadas para seres humanos. Por exemplo, eis uma seção de um primeiro programa de linguagem de máquina que adiciona o pagamento de horas extras à base de pagamentos e armazena o resultado como salário bruto: +1300042774 +1400593419 +1200274027

A programação na linguagem de máquina era um processo muito lento e tedioso para a maioria dos programadores. Em vez de utilizar as strings de números que os computadores poderiam entender diretamente, os programadores começaram a utilizar abreviações em inglês para representar operações elementares. Essas abreviações formaram a base de linguagens assembly. Programas tradutores chamados assemblers foram desenvolvidos para converter os primeiros programas de linguagem assembly em linguagem de máquina a velocidades de computador. A seção a seguir de um programa de linguagem assembly também soma os ganhos em horas extras ao salário de base e armazena o resultado no salário bruto: load add store

basepay overpay grosspay

Embora tal código seja mais claro para humanos, ele é incompreensível para computadores até ser traduzido em linguagem de máquina. O uso de computador aumentou rapidamente com o advento de linguagens assembly, mas os programadores ainda tinham de utilizar muitas instruções para realizar até as tarefas mais simples. Para acelerar o processo de programação, foram desenvolvidas linguagens de alto nível em que instruções únicas poderiam ser escritas para realizar tarefas substanciais. Os programas tradutores chamados compila-

6

Capítulo 1

Introdução aos computadores, à Internet e à World Wide Web

dores convertem os programas de linguagem de alto nível em linguagem de máquina. Linguagens de alto nível permitem aos programadores escrever instruções que se parecem com o inglês cotidiano e contêm notações matemáticas comumente utilizadas. Um programa de folha de pagamento escrito em uma linguagem de alto nível poderia conter uma instrução assim: grossPay = basePay + overTimePay

Do ponto de vista do programador, as linguagens de alto nível são preferíveis às linguagens de máquina e às linguagens assembly. O C, C++, as linguagens .NET da Microsoft (por exemplo, Visual Basic, Visual C++ e C#) estão entre as linguagens de programação de alto nível mais usadas; o Java é de longe a linguagem mais amplamente utilizada. O processo de compilação de um programa de linguagem de alto nível em linguagem de máquina pode consumir uma quantidade considerável de tempo do computador. Os programas interpretadores foram desenvolvidos para executar programas de linguagem de alto nível diretamente (sem o tempo de espera da compilação), embora mais lentos do que a execução de programas compilados. Discutiremos mais sobre como os interpretadores funcionam na Seção 1.13, onde você aprenderá que o Java utiliza uma combinação inteligente de compilação e interpretação para executar programas. Os exercícios 7.35–7.37 (na Seção especial “Construindo seu próprio computador”) orientam pelo processo da construção de um programa interpretador.

1.8 História do C e do C++ O Java evoluiu a partir do C++, que evoluiu do C, que evoluiu a partir do BCPL e do B. O BCPL foi desenvolvido em 1967 por Martin Richards como uma linguagem para escrever software de sistemas operacionais e compiladores. Ken Thompson modelou muitos recursos em sua linguagem B com base nas suas contrapartes em BCPL, utilizando o B para criar versões anteriores do sistema operacional UNIX na Bell Laboratories em 1970. A linguagem C — originalmente implementada em 1972 — foi desenvolvida a partir da linguagem B criada por Dennis Ritchie nos Bell Laboratories. Ela inicialmente tornou-se amplamente conhecida como a linguagem de desenvolvimento do sistema operacional UNIX. Hoje, a maior parte do código para sistemas operacionais de uso geral (por exemplo, aqueles encontrados em notebooks, áreas de trabalho, estações de trabalho e pequenos servidores) é escrita em C ou C++. O C++, uma extensão do C, foi desenvolvido por Bjarne Stroustrup no início da década de 1980 na Bell Laboratories. O C++ fornece vários recursos que “sofisticam” a linguagem C, mas, sobretudo, fornece capacidades para a programação orientada a objetos (discutida em mais detalhes na Seção 1.16 e em todo este livro). O C ++ é uma linguagem híbrida — é possível programar tanto no estilo C como no estilo orientado a objetos, ou em ambos. Construir software de maneira rápida, correta e econômica continua sendo um objetivo difícil de alcançar numa época em que a demanda por novos e mais poderosos programas de software cresce vertiginosamente. Objetos, ou mais precisamente — como veremos na Seção 1.16 — as classes a partir das quais os objetos se originam, são componentes de software essencialmente reutilizáveis. Há objetos data, objetos tempo, objetos áudio, objetos vídeo, objetos automóveis, objetos pessoas e assim por diante. De fato, praticamente todos os substantivos podem ser representados como um objeto de software em termos de atributos (por exemplo, nome, cor e tamanho) e comportamentos (por exemplo, calcular, mover e comunicar). Os desenvolvedores de software estão descobrindo que utilizar uma abordagem de implementação de design orientado a objetos modular pode tornar grupos de desenvolvimento de software muito mais produtivos do que era possível com as primeiras técnicas populares, como a programação estruturada. Programas orientados a objetos são frequentemente mais fáceis de entender, corrigir e modificar. O Java é a linguagem de programação orientada a objetos mais amplamente utilizada do mundo.

1.9 História do Java A contribuição mais importante da revolução do microprocessador até essa data é que ele tornou possível o desenvolvimento de computadores pessoais, que agora contam com mais de um bilhão em todo o mundo. Os computadores pessoais afetaram profundamente a vida das pessoas e a maneira que as organizações conduzem e gerenciam seus negócios. Os microprocessadores têm um impacto profundo em dispositivos eletrônicos inteligentes de consumo popular. Reconhecendo isso, a Sun Microsystems, em 1991, financiou um projeto de pesquisa corporativa interna que resultou em uma linguagem baseada em C++ que seu criador, James Gosling, chamou de Oak em homenagem a uma árvore de carvalho vista por sua janela na Sun. Descobriu-se mais tarde que já havia uma linguagem de computador com esse nome. Quando uma equipe da Sun visitou uma cafeteria local, o nome Java (cidade de origem de um tipo de café importado) foi sugerido; e o nome pegou. O projeto de pesquisa passou por algumas dificuldades. O mercado para dispositivos eletrônicos inteligentes destinados ao consumidor final não estava se desenvolvendo tão rapidamente como a Sun tinha previsto. Por uma feliz casualidade, a Web explodiu em popularidade em 1993 e a Sun viu o potencial de utilizar o Java para adicionar conteúdo dinâmico, como interatividade e animações, às páginas da Web. Isso deu nova vida ao projeto. A Sun anunciou o Java formalmente em uma conferência do setor em maio de 1995. O Java chamou a atenção da comunidade de negócios por causa do enorme interesse na Web. O Java é agora utilizado para desenvolver aplicativos corporativos de grande porte, aprimorar a funcionalidade de servidores da Web (os computadores que fornecem o conteúdo que vemos em nossos navegadores da Web), fornecer aplicativos para dispositivos voltados para o consumo popular (como telefones celulares, pagers e PDAs) e para muitos outros propósitos.

1.10 Bibliotecas de classe do Java

7

1.10 Bibliotecas de classe do Java Programas Java consistem em partes chamadas classes. As classes incluem partes chamadas métodos que realizam tarefas e retornam informações quando as tarefas são concluídas. Você pode criar cada parte necessária para formar seus programas Java. Entretanto, a maioria dos programadores Java tira proveito das ricas coleções de classes existentes nas bibliotecas de classe Java, que também são conhecidas como Java APIs (Application Programming Interfaces). Portanto, na realidade há dois aspectos para aprender o “mundo” do Java. O primeiro aspecto é a própria linguagem Java, de modo que você possa programar suas próprias classes, o segundo são as classes nas extensas bibliotecas de classe Java. Por todo este livro discutimos muitas classes da Java API preexistentes. Para baixar a documentação da Java API, acesse java.sun.com/javase/downloads/, role para baixo até a Seção Additional Resources e clique no botão Download à direita de Java SE 6 Documentation.

Observação de engenharia de software 1.1

Utilize uma abordagem de bloco de construção para criar seus programas. Evite reinventar a roda — utilize partes existentes onde quer que possível. Essa reutilização de software é um benefício fundamental da programação orientada a objetos.

Incluímos muitas Observações de engenharia de software por todo o livro para explicar conceitos que afetam e aprimoram a arquitetura total e a qualidade de sistemas de software. Também destacamos outros tipos de dicas, incluindo: • Boas práticas de programação (para ajudá-lo a escrever programas que são mais claros, mais compreensíveis, mais fáceis de manter e mais fáceis de testar e depurar — isto é, remover erros de programação). • Erros de programação comuns (problemas a observar e evitar). • Dicas de desempenho (técnicas para escrever programas que executam mais rapidamente e utilizam menos memória). • Dicas de portabilidade (técnicas para ajudá-lo a escrever programas que podem executar, com pouca ou nenhuma modificação, em diferentes computadores — essas dicas também incluem observações gerais de como o Java alcança seu alto grau de portabilidade). • Dicas de prevenção de erros (técnicas para remover bugs dos seus programas e, mais importante, antes de tudo, técnicas para escrever programas sem bugs). • Observações sobre a interface (técnicas para ajudá-lo a projetar a “aparência” e o “comportamento” das interfaces com o usuário dos seus aplicativos em termos da aparência e facilidade de uso). Muitas dessas técnicas são apenas diretrizes. Sem dúvida, você desenvolverá seu próprio estilo de programação preferido.

Observação de engenharia de software 1.2

Ao programar em Java, você geralmente utilizará os seguintes blocos de construção: classes e métodos de bibliotecas de classe, classes e métodos que você mesmo cria e classes e métodos que outros criam e tornam disponíveis para você.

A vantagem de criar suas próprias classes e métodos é que você sabe exatamente como eles funcionam e pode examinar o código Java. A desvantagem é o consumo de tempo e o esforço potencialmente complexo que é necessário.

Dica de desempenho 1.1

Usar as classes e métodos da Java API em vez de escrever suas próprias versões pode melhorar o desempenho de programa, porque eles são cuidadosamente escritos para executar de modo eficiente. Essa técnica também diminui o tempo de desenvolvimento de programa.

Dica de portabilidade 1.1

Utilizar as classes e métodos da Java API em vez de escrever suas próprias versões melhora a portabilidade de programa, porque esses são incluídos em cada implementação Java.

1.11 Fortran, Cobol, Pascal e Ada Centenas de linguagens de alto nível foram desenvolvidas, mas somente algumas alcançaram ampla aceitação. O Fortran (FORmula TRANslator) foi uma linguagem desenvolvida pela IBM Corporation em meados da década de 1950 para ser utilizada em aplicativos científicos de engenharia que exigem complexos cálculos matemáticos. O Fortran ainda é amplamente utilizado em aplicativos de engenharia. O Cobol (COmmon Business Oriented Language) foi desenvolvido no final da década de 1950 por fabricantes de computadores e usuários de computadores do governo norte-americano e da indústria. O Cobol é utilizado para aplicativos comerciais que exigem manipulação precisa e eficiente de grandes quantidades de dados. Grande parte do software utilizado em grandes empresas varejistas ainda é programada em Cobol.

8

Capítulo 1

Introdução aos computadores, à Internet e à World Wide Web

Durante a década de 1960, muitos grandes esforços no desenvolvimento de software encontraram diversas dificuldades. Em geral, os prazos de entrega de software atrasavam, os custos excediam enormemente os orçamentos e os produtos finais não eram confiáveis. As pessoas começaram a perceber que o desenvolvimento de software era uma atividade muito mais complexa do que haviam imaginado. Uma pesquisa feita na década de 1960 resultou na evolução da programação estruturada — uma abordagem disciplinada para escrever programas mais claros, mais fáceis de testar e depurar e mais fáceis de modificar do que os grandes programas produzidos com a técnica anterior. Um dos resultados mais tangíveis dessa pesquisa foi o desenvolvimento da linguagem de programação Pascal pelo professor Nicklaus Wirth em 1971. Batizada em homenagem a Blaise Pascal, matemático e filósofo do século XVII, essa linguagem foi adotada para ensino de programação estruturada em ambientes acadêmicos e rapidamente se tornou a linguagem de programação preferida na maioria das universidades. A linguagem de programação Ada foi desenvolvida sob o patrocínio do Departamento de Defesa dos Estados Unidos durante a década de 1970 e o início da década de 1980. O DOD queria que uma única linguagem atendesse a maioria de suas necessidades. A linguagem foi batizada como Ada, em homenagem a Lady Ada Lovelace, filha do poeta Lord Byron. Lady Lovelace é considerada a primeira pessoa a escrever um programa de computador do mundo no início do século XIX (para o dispositivo mecânico de computação conhecido como Máquina Analítica, projetado por Charles Babbage). Uma capacidade importante da linguagem Ada, chamada multitarefa, permite que os programadores especifiquem quantas atividades em um programa devem ocorrer paralelamente. O Java, por meio de uma técnica chamada multithreading (discutida no Capítulo 26), também permite aos programadores escrever programas com atividades paralelas.

1.12 Basic, Visual Basic, Visual C++, C# e .NET A linguagem de programação Basic (Beginner’s All-Purpose Symbolic Instruction Code) foi desenvolvida em meados da década de 1960, no Dartmouth College, como um meio de escrever programas simples. O principal propósito de Basic foi familiarizar os iniciantes com as técnicas de programação. A linguagem Visual Basic da Microsoft foi introduzida no início de 1990 para simplificar o desenvolvimento de aplicativos Microsoft Windows e é uma das linguagens de programação mais populares do mundo. As últimas ferramentas de desenvolvimento da Microsoft fazem parte de sua estratégia de escopo corporativo para integrar a Internet e a Web em aplicativos de computador. Essa estratégia é implementada na plataforma .NET da Microsoft, que fornece aos desenvolvedores as capacidades necessárias para criar e executar aplicativos de computador que possam executar em computadores distribuídos através da Internet. As três principais linguagens de programação da Microsoft são Visual Basic (baseada no Basic original), Visual C++ (baseada no C++) e C# (baseada no C++ e no Java, e desenvolvida especificamente para a plataforma .NET). Os desenvolvedores que utilizam .NET podem escrever componentes de software na linguagem com que se sentem à vontade, então formar aplicativos combinando esses componentes com outros escritos em qualquer linguagem de .NET.

1.13 Ambiente típico de desenvolvimento Java Agora explicamos os passos comumente seguidos na criação e execução de um aplicativo Java que utiliza um ambiente de desenvolvimento Java (ilustrado na Figura 1.1). Em geral, programas Java passam por cinco fases — edição, compilação, carregamento, verificação e execução. Discutimos essas fases no contexto do Java SE Development Kit 6 ( JDK6) da Sun Microsystems, Inc. Você pode baixar o JDK mais recente e sua documentação de java.sun.com/javase/downloads/. Siga atentamente as instruções de instalação do JDK fornecidas na Seção “Antes de você começar” deste livro a fim de garantir a correta configuração do computador para compilar e executar programas Java. Também é recomendável visitar o New to Java Center da Sun em: java.sun.com/new2java/

[Nota: esse site da Web fornece instruções de instalação para Windows, Linux e Mac OS X. Se você não estiver utilizando um desses sistemas operacionais, consulte a documentação do ambiente Java do seu sistema ou pergunte a seu instrutor como realizar essas tarefas com base no sistema operacional do seu computador. Se tiver problemas com esse link ou com quaisquer outros referidos neste livro, verifique erratas e nos informe por e-mail em [email protected].]

Fase 1: criando um programa A Fase 1 consiste em editar um arquivo com um programa editor (em geral, conhecido simplesmente como um editor). Você digita um programa Java (em geral referido como código-fonte) utilizando o editor, faz quaisquer correções necessárias e salva o programa em um dispositivo de armazenamento secundário, como sua unidade de disco. Um nome de arquivo que termina com a extensão .java indica que o arquivo contém o código-fonte Java. Supomos que você saiba como editar um arquivo. Dois editores amplamente utilizados em sistemas Linux são o vi e o emacs. No Windows, o Notepad será suficiente. Também há muitos editores freeware e shareware disponíveis online, incluindo EditPlus (www.editplus.com), TextPad (www.textpad.com) e jEdit (www.jedit.org).



1.13   Ambiente típico de desenvolvimento Java

Fase 1: Edita Editor

Fase 2: Compila

Fase 3: Carrega

Compilador

Disco

O programa é criado em um editor e armazenado em disco em um arquivo cujo nome termina com .java.

O compilador cria bytecodes e os armazena em disco em um arquivo cujo nome termina com .class.

Memória primária Carregador de classe

...

Disk

Fase 4: Verifica

Disco

9

O carregdor de classe lê os arquivos .class que contêm bytecodes a partir do disco e coloca esses bytecodes na memória.

Memória primária Verificador de bytecode O verificador de bytecode confirma que todos os bytecodes são válidos e não violam restrições de segurança do Java. ...

Fase 5: Executa

Memória primária Java Virtual Machine (JVM)

...

Para executar o programa, a JVM lê os bytecodes e os compila (isto é, traduz) no momento certo (ou Just-In-Time — JIT) para uma linguagem que o computador possa entender.

Figura 1.1  |  Ambiente típico de desenvolvimento Java.

Para organizações que desenvolvem sistemas de informações substanciais, ambientes de desenvolvimento integrado (Integrated Development Environments — IDEs) estão disponíveis a partir de muitos fornecedores de software importantes. IDEs fornecem ferramentas que suportam o processo de desenvolvimento de software, incluindo editores para escrever e editar programas e depuradores para localizar erros de lógica — erros que fazem os programas executar incorretamente. IDEs populares são Eclipse (www.eclipse.org), NetBeans (www.netbeans.org), JBuilder (www.codegear.com), JCreator (www.jcreator.com), BlueJ (www.blueJ.org) e jGRASP (www.jgrasp.org).

10

Capítulo 1  Introdução aos computadores, à Internet e à World Wide Web

Fase 2: compilando um programa Java em bytecodes Na Fase 2, utilize o comando (o compilador Java) para compilar um programa. Por exemplo, para compilar um programa chamado Welcome.java, você digitaria: javac Welcome.java

na janela de comando do seu sistema (isto é, o Prompt de Comando no Windows, o prompt de shell no Linux ou aplicativo Terminal no Mac OS X). Se o programa compilar, o compilador produz um arquivo .class chamado Welcome.class que contém a versão compilada do programa. O compilador Java converte o código-fonte Java em bytecodes que representam as tarefas a serem executadas na fase de execução (Fase 5). Os bytecodes são executados pela Java Virtual Machine (JVM) — uma parte do JDK e a base da plataforma Java. A máquina virtual (Virtual Machine — VM) é um aplicativo de software que simula um computador, mas oculta o sistema operacional e o hardware subjacentes dos programas que interagem com ela. Se a mesma VM for implementada nas várias plataformas de computador, os aplicativos que ela executa podem ser utilizados em todas essas plataformas. A JVM é uma das máquinas virtuais mais amplamente utilizadas. O .NET da Microsoft utiliza uma arquitetura de máquina virtual semelhante. Ao contrário da linguagem de máquina, que é dependente do hardware específico de computador, os bytecodes são independentes de plataforma — eles não dependem de uma plataforma de hardware particular. Portanto, os bytecodes do Java são portáveis — sem recompilar o código-fonte, os mesmos bytecodes podem executar em qualquer plataforma contendo uma JVM que entende a versão do Java em que os bytecodes foram compilados. A JVM é invocada pelo comando java. Por exemplo, para executar um aplicativo Java chamado Welcome, você digitaria o comando: java Welcome

em uma janela de comando para invocar a JVM, que então iniciaria os passos necessários para executar o aplicativo. Isso inicia a Fase 3.

Fase 3: carregando um programa na memória Na Fase 3, a JVM armazena o programa na memória para executá-lo — isso é conhecido como carregamento. O carregador de classe da JVM pega os arquivos .class que contêm os bytecodes do programa e transfere-os para a memória primária. O carregador de classe também carrega qualquer arquivo .class fornecido pelo Java que seu programa utiliza. Os arquivos .class podem ser carregados a partir de um disco em seu sistema ou em uma rede (por exemplo, sua unidade local ou rede corporativa ou a Internet). Fase 4: verificação de bytecode Na Fase 4, enquanto as classes são carregadas, o verificador de bytecode examina seus bytecodes para assegurar que eles são válidos e não violam restrições de segurança do Java. O Java impõe uma forte segurança para certificar-se de que os programas Java que chegam pela rede não danificam os arquivos ou o sistema (como vírus e vermes de computador). Fase 5: execução Na Fase 5, a JVM executa os bytecodes do programa, realizando assim as ações especificadas pelo programa. Nas primeiras versões do Java, a JVM era simplesmente um interpretador para bytecodes Java. Isso fazia com que a maioria dos programas Java executasse lentamente porque a JVM interpretava e executava um bytecode por vez. Em geral, as JVMs atuais executam bytecodes utilizando uma combinação de interpretação e a chamada compilação Just-In-Time ( JIT). Nesse processo, a JVM analisa os bytecodes à medida que eles são interpretados, procurando hot spots (pontos ativos) — partes dos bytecodes que executam com frequência. Para essas partes, um compilador Just-In-Time (JIT) — conhecido como compilador Java HotSpot — traduz os bytecodes para a linguagem de máquina de um computador subjacente. Quando a JVM encontra novamente essas partes compiladas, o código de linguagem de máquina mais rápido é executado. Portanto, os programas Java na realidade passam por duas fases de compilação — uma em que código-fonte é traduzido em bytecodes (para a portabilidade entre JVMs em diferentes plataformas de computador) e uma segunda em que, durante a execução, os bytecodes são traduzidos em linguagem de máquina para o computador real em que o programa é executado. Problemas que podem ocorrer em tempo de execução Os programas podem não funcionar na primeira tentativa. Cada uma das fases anteriores pode falhar por causa de vários erros que discutiremos por todo este livro. Por exemplo, um programa executável talvez tente realizar uma operação de divisão por zero (uma operação ilegal para a aritmética de número inteiro em Java). Isso faria o programa Java imprimir uma mensagem de erro. Se isso ocorresse, você teria de retornar à fase de edição, fazer as correções necessárias e passar novamente pelas demais fases para determinar se as correções resolveram o(s) problema(s). [Nota: a maioria dos programas Java realiza entrada ou saída de dados. Quando dizemos que um programa exibe uma mensagem, normalmente queremos dizer que ele exibe essa mensagem na tela do computador. As mensagens e outros dados podem ser enviados para outros dispositivos de saída, como discos e impressoras ou até mesmo uma rede para transmissão para outros computadores.]

1.14 Notas sobre o Java e este livro

11

Erro comum de programação 1.1 Erros como divisão por zero ocorrem durante a execução de um programa, por isso são chamados de erros de runtime ou erros de tempo de execução. Erros de tempo de execução fatais fazem com que os programas sejam imediatamente encerrados sem terem realizado seu trabalho com sucesso. Erros de tempo de execução não fatais permitem que os programas executem até sua conclusão, produzindo frequentemente resultados incorretos.

1.14 Notas sobre o Java e este livro O Java é uma poderosa linguagem de programação. Os programadores experientes às vezes sentem-se orgulhosos por criarem algum uso exótico, distorcido e complexo de uma linguagem. Essa é uma prática de programação pobre. Ela torna programas mais difíceis de ler, mais propensos a se comportar estranhamente, mais difíceis de testar e depurar e mais difíceis de adaptar em caso de alteração de requisitos. Este livro enfatiza a clareza. Eis a seguir a nossa primeira dica de Boa prática de programação.

Boa prática de programação 1.1 Escreva seus programas Java de uma maneira simples e direta. Isso é às vezes chamado KIS (“Keep It Simple”, ou seja, “mantenha a coisa simples”). Não “estenda” a linguagem tentando usos bizarros.

Você viu antes que o Java é uma linguagem portável e que programas escritos em Java podem executar em muitos computadores diferentes. Em geral, a portabilidade é um objetivo ilusório.

Dica de portabilidade 1.2

Embora seja mais fácil escrever programas portáveis em Java do que na maioria das outras linguagens de programação, diferenças entre compiladores, JVMs e computadores podem tornar a portabilidade difícil de alcançar. Simplesmente escrever programas em Java não garante portabilidade.

Dica de prevenção de erro 1.1

Sempre teste os programas Java em todos os sistemas em que você quiser executá-los, para assegurar que funcionarão corretamente para o público-alvo.

Você pode encontrar uma versão baseada na Web da documentação da Java API em java.sun.com/javase/6/docs/api/index. ou fazer o download dessa documentação no seu computador em java.sun.com/javase/downloads/. Para detalhes técnicos adicionais sobre muitos aspectos do desenvolvimento Java, visite java.sun.com/reference/docs/index.html.

html,

Boa prática de programação 1.2

Leia a documentação da versão do Java que você está utilizando. Consulte-a frequentemente para certificar-se de que você está ciente da rica coleção de recursos Java e de que está utilizando esses recursos corretamente.

1.15 Testando um aplicativo Java Nesta seção, você irá executar e interagir com seu primeiro aplicativo Java. Você começará executando um aplicativo ATM (Automatic Teller Machine — caixa eletrônico), que simula as transações que acontecem quando você utiliza um caixa eletrônico (por exemplo, para fazer saques e depósitos e obter saldos de sua conta bancária). Você aprenderá como construir esse aplicativo no estudo de caso opcional orientado a objetos incluído nos capítulos 12–13. A Figura 1.10 no final desta seção sugere outros aplicativos interessantes com os quais você também poderia querer fazer um test-drive. Para o objetivo desta seção, supomos que você esteja executando o Microsoft Windows.1 Nos próximos passos, você executará o aplicativo e realizará várias transações. Os elementos e a funcionalidade que você vê aqui são típicos daquilo que você aprenderá a programar neste livro. [Nota: usamos fontes para distinguir entre os aspectos que você vê em uma tela (por exemplo, o Command Prompt) e elementos que não estão diretamente relacionados a uma tela. Nossa convenção é enfatizar os recursos de tela como títulos e menus (por exemplo, o menu File) em uma fonte Helvetica sem serifa e seminegrito e, para enfatizar os elementos de 1

Em www.deitel.com/books/jhtp8/ fornecemos uma versão do Linux para esse test-drive. Também fornecemos links para vídeos que o ajudam a começar a trabalhar com vários ambientes de desenvolvimento integrados populares (IDEs), incluindo o Eclipse e NetBeans.

12

Capítulo 1  Introdução aos computadores, à Internet e à World Wide Web

não tela, como nomes de arquivo ou entrada (por exemplo, ProgramName.java), em uma fonte Lucida sem serifa. Como você já percebeu, a ocorrência que define cada termo-chave no texto é especificada em vermelho escuro. Nas figuras desta seção, destacamos em amarelo a entrada do usuário requerida por cada passo e indicamos as partes significativas do aplicativo. Para tornar esses recursos mais visíveis, alteramos a cor do fundo das janelas Prompt de Comando para branco e a cor do primeiro plano para preto.] 1. Verificando sua configuração. Leia a Seção “Antes de você começar” do livro para confirmar se você configurou o Java corretamente no seu computador e se copiou os exemplos do livro para sua unidade de disco rígido. 2. Localizando o aplicativo concluído. Abra a janela Prompt de Comando. Mude para o diretório do aplicativo ATM digitando cd C:\examples\ch01\ATM e, em seguida, pressionando Enter (Figura 1.2). O comando cd é utilizado para mudar de diretório. Usando o comando cd para mudar de diretório

Localização dos arquivos do aplicativo de ATM

Figura 1.2  |  Abrindo um Prompt de Comando e mudando de diretório.

3. Executando o aplicativo ATM. Digite o comando java ATMCaseStudy (Figura 1.3) e pressione Enter. Lembre-se de que o comando java, seguido pelo nome do arquivo .class do aplicativo (nesse caso, ATMCaseStudy), executa o aplicativo. Especificando a extensão .class ao utilizar o comando java resulta em um erro. [Nota: comandos Java diferenciam letras maiúsculas de minúsculas. É importante digitar o nome desse aplicativo com as letras A, T e M maiúsculas em “ATM”, a letra C maiúscula em “Case” e um S maiúsculo em “Study”. Caso contrário, o aplicativo não executará.] Se receber a mensagem de erro “Exception in thread "main" java. lang.NoClass-DefFoundError: ATMCaseStudy”, seu sistema tem um problema de CLASSPATH. Consulte na Seção “Antes de você começar” do livro instruções para ajudá-lo a corrigir esse problema.

Figura 1.3  |  Utilizando o comando java para executar o aplicativo ATM.

4. Inserindo o número de uma conta. Quando o aplicativo executa pela primeira vez, ele exibe uma saudação "Welcome!" e um prompt pedindo o número de uma conta. Digite 12345 no prompt "Please enter your account number:" (Figura 1.4) e pressione Enter. Mensagens de boas-vindas do aplicativo de ATM

Prompt para inserir o número da conta

Figura 1.4  |  Solicitando o número de uma conta ao usuário.

5. Inserindo um PIN. Depois que o número de uma conta válida é inserido, o aplicativo exibe o prompt "Enter your PIN:". Digite "54321" como seu PIN (Personal Identification Number) válido e pressione Enter. O menu principal do ATM contendo uma lista de opções será exibido (Figura 1.5).



1.15  Testando um aplicativo Java Insira um PIN válido

13

Menu principal do ATM

Figura 1.5 | Inserindo um número de PIN válido e exibindo o menu principal do aplicativo ATM.

6. Visualizando o saldo da conta. Selecione a opção 1, "View

my balance", no menu ATM (Figura 1.6). O aplicativo exibe então dois números — o Available balance ($1,000.00) e o Total balance ($1,200.00). O saldo disponível é a quantia máxima de dinheiro na sua conta que está disponível para saque em uma dada hora. Em alguns casos, certos fundos, como depósitos recentes, não estão imediatamente disponíveis para o usuário retirar, então o saldo disponível pode ser menor do que o saldo total, como é mostrado aqui. Depois que as informações do saldo da conta são mostradas, o menu principal do aplicativo é exibido novamente. Informações sobre o saldo da conta

Figura 1.6  |  O aplicativo ATM exibindo as informações de saldo da conta do usuário.

7. Retirando dinheiro da conta. Selecione opção 2, "Withdraw cash", no menu do aplicativo. Em seguida, uma lista das quantias em dólar (Figura 1.7) é apresentada (por exemplo, 20, 40, 60, 100 e 200). Você também tem a opção de cancelar a transação e retornar ao menu principal. Retire US$ 100 selecionando a opção 4. O aplicativo exibe “Please take your cash now.” e retorna ao menu principal. [Nota: infelizmente, esse aplicativo apenas simula o comportamento de um ATM real e, portanto, não libera realmente dinheiro.] Menu de saque do ATM

Figura 1.7  |  Retirando dinheiro da conta e retornando ao menu principal.

8. Confirmando que as informações de conta foram atualizadas. A partir do menu principal, selecione novamente a opção 1 para visualizar o saldo atual da sua conta (Figura 1.8). Observe que tanto o saldo disponível como o saldo total foram atualizados para refletir a transação de retirada.

14

Capítulo 1  Introdução aos computadores, à Internet e à World Wide Web Confirmando as informações de atualização do saldo da conta após transação de saque

Figura 1.8  |  Verificando o novo saldo.

9. Concluindo a transação. Para concluir a sessão ATM atual, selecione opção 4, “Exit”, no menu principal (Figura 1.9). O ATM sairá do sistema e exibirá uma mensagem de despedida para o usuário. O aplicativo então retornará ao seu prompt original pedindo o número de conta do próximo usuário. Mensagem de despedida do ATM

Prompt de número da conta para o próximo usuário

Figura 1.9  |  Concluindo uma sessão de transação de um ATM.

10. Saindo do aplicativo ATM e fechando a janela Command Prompt. A maioria dos aplicativos fornece uma opção para sair e retornar ao diretório Command Prompt a partir do qual o aplicativo foi executado. Um caixa eletrônico real não fornece ao usuário a opção de desligar a máquina. Em vez disso, quando um usuário tiver concluído todas as transações desejadas e escolher a opção de menu para sair, o caixa eletrônico reinicia e exibe um prompt solicitando o número de conta do próximo usuário. Como a Figura 1.9 ilustra, o aplicativo ATM aqui se comporta de maneira semelhante. Escolher a opção de menu de sair encerra somente a sessão de ATM do usuário atual, não o aplicativo ATM inteiro. Para sair realmente do aplicativo ATM, clique no botão close (x) no canto superior direito da janela Command Prompt. Fechar a janela faz com que o aplicativo em execução termine.

Aplicativos sugeridos para um test-drive neste livro A Figura 1.10 lista algumas das centenas de aplicativos encontrados nos exemplos e exercícios do livro. Esses programas introduzem muitos dos recursos poderosos e divertidos do Java. Execute esses programas para ver um número maior dos tipos de aplicativos que você aprenderá a construir neste livro. A pasta de exemplos deste capítulo contém todos os arquivos necessários para executar cada aplicativo. Simplesmente digite os comandos listados na Figura 1.10 em uma janela Prompt de Comando. Nome do aplicativo

Localização do capítulo

Comandos a serem executados

Tic-Tac-Toe (jogo da velha)

Capítulo 8

cd C:\examples\ch01\Tic-Tac-Toe java TicTacToeTest

Jogo de adivinhação

Capítulo 14

cd C:\examples\ch01\GuessGame java GuessGame

Animador de logotipo

Capítulo 24

cd C:\examples\ch01\LogoAnimator java LogoAnimator

Bola que rebate

Capítulo 26

cd C:\examples\ch01\BouncingBall java BouncingBall

Figura 1.10  |  Alguns dos aplicativos sugeridos para um test-drive neste livro.

1.16 Estudo de caso de engenharia de software: introdução à tecnologia de objetos e à UML

15

1.16 Estudo de caso de engenharia de software: introdução à tecnologia de objetos e à UML Agora vamos começar nossa primeira introdução à orientação a objetos, uma maneira natural de pensar o mundo e escrever programas de computador. Os capítulos 12–13 apresentam uma introdução cuidadosamente compassada ao design orientado a objetos. Nosso objetivo aqui é ajudá-lo a desenvolver uma maneira de pensar orientada a objetos e o introduzir a Unified Modeling Language™ (UML™) — uma linguagem gráfica que permite que as pessoas projetem sistemas de software para utilizar uma notação padrão da indústria para representá-las. Nesta única seção necessária, introduzimos a terminologia e os conceitos orientados a objetos. As seções opcionais nos capítulos 12–13 apresentam um design orientado a objetos e a implementação do software de um caixa eletrônico simples. As seções 12.2–12.7 do estudo de caso de engenharia de software: • analisam um típico documento de requisitos que descreve um sistema de software (o ATM) a ser construído; • determinam os objetos necessários para implementar o sistema; • determinam os atributos que os objetos terão; • determinam os comportamentos que esses objetos exibirão; • especificam como os objetos interagem para atender os requisitos de sistema. As seções 13.2–13.3 modificam e aprimoram o design apresentado nas seções 12.2–12.7. A Seção 13.4 contém uma implementação funcional do Java completa do sistema de software do ATM orientado a objetos. Você experimentará uma introdução concisa e sólida ao design orientado a objetos com a UML. Além disso, você aperfeiçoará suas habilidades de leitura de código passeando pela completa implementação Java do ATM cuidadosamente escrita e bem documentada na Seção 13.4.

Conceitos básicos da tecnologia de objetos Iniciamos nossa introdução à orientação a objetos com uma terminologia-chave. Onde quer que você olhe no mundo real, você vê objetos — pessoas, animais, plantas, carros, aviões, edifícios, computadores e assim por diante. Os humanos pensam em termos de objetos. Telefones, residências, sinais de trânsito, micro-ondas e refrigeradores são alguns outros objetos. Programas de computador, como os programas Java que você verá neste livro e aqueles que você escreverá, são compostos de muitos objetos de software interativos. Às vezes dividimos os objetos em duas categorias: animados e inanimados. Os animados são, em certo sentido, objetos “vivos” — eles se movem e fazem coisas. Por outro lado, os objetos inanimados não se movem por conta própria. Objetos de ambos os tipos, porém, têm algumas coisas em comum. Todos eles têm atributos (por exemplo, tamanho, forma, cor e peso) e todos exibem comportamentos (por exemplo, uma bola rola, rebate, infla e murcha; o bebê chora, dorme, engatinha, anda e pisca; um carro acelera, freia e desvia; uma toalha absorve água). Estudaremos os tipos de atributos e comportamentos dos objetos de software. Os humanos aprendem sobre os objetos existentes estudando seus atributos e observando seus comportamentos. Diferentes objetos podem ter atributos semelhantes e podem exibir comportamentos semelhantes. É possível fazer comparações, por exemplo, entre bebês e adultos, e entre humanos e chimpanzés. O design orientado a objetos (Object-Oriented Design — OOD) modela software em termos semelhantes àqueles que as pessoas utilizam para descrever objetos do mundo real. Ele tira proveito de relacionamentos de classe, em que os objetos de certa classe, como uma classe de veículos, têm as mesmas características — carros, caminhões, patins têm muito em comum. O OOD também tira proveito dos relacionamentos de herança, dos quais as classes de objetos novas são derivadas absorvendo-se características de classes existentes e adicionando-se características únicas dessas mesmas classes. Um objeto de classe “conversível” certamente tem as características da classe mais geral “automóvel”, mas, mais especificamente, seu capô sobe e desce. O design orientado a objetos fornece uma maneira natural e intuitiva de visualizar o processo de design de software — a saber, modelar objetos por seus atributos e comportamentos assim como descrevemos objetos do mundo real. A OOD também modela a comunicação entre objetos. Assim como as pessoas trocam mensagens entre si (por exemplo, um sargento manda um soldado permanecer em atenção), os objetos também se comunicam via mensagens. Um objeto conta bancária pode receber uma mensagem para reduzir seu saldo em certa quantia porque o cliente retirou essa quantia em dinheiro. O OOD encapsula (isto é, empacota) atributos e operações (comportamentos) em objetos — os atributos e operações de um objeto estão intimamente ligados. Os objetos têm a propriedade de ocultamento de informações. Isso significa que podem saber como se comunicar com outros por meio de interfaces bem definidas, mas normalmente eles não têm permissão para saber como os outros objetos são implementados — os detalhes de implementação são ocultados dentro dos próprios objetos. Na verdade podemos dirigir um carro, por exemplo, sem conhecer os detalhes de como motores, transmissões, freios e sistemas de escapamento funcionam internamente — contanto que saibamos utilizar o acelerador, o freio, a roda e assim por diante. O ocultamento de informações, como veremos, é crucial à boa engenharia de software.

16

Capítulo 1  Introdução aos computadores, à Internet e à World Wide Web

Linguagens como Java são linguagens orientadas a objeto. A programação nessa linguagem, chamada programação orientada a objetos (Object-Oriented Programming — OOP), permite-lhe implementar um design orientado a objetos como um sistema funcional. Linguagens como o C, por outro lado, são procedurais, então a programação tende a ser orientada para a ação. No C, a unidade de programação é a função. Grupos de ações que realizam alguma tarefa comum são reunidos em funções e as funções são agrupadas para formar programas. No Java, a unidade de programação é a classe a partir da qual os objetos por fim são instanciados (criados). Classes Java contêm métodos (que implementam operações e são semelhantes a funções na linguagem C) bem como campos (que implementam atributos). Programadores em Java concentram-se na criação de classes. Cada classe contém campos e o conjunto de métodos que manipulam os campos e fornecem serviços aos clientes (isto é, outras classes que utilizam a classe). O programador utiliza classes existentes como blocos de construção para construir novas classes. As classes estão para os objetos assim como as plantas arquitetônicas estão para as casas. Assim como podemos construir muitas casas a partir de uma planta, podemos instanciar (criar) muitos objetos a partir de uma classe. Você não pode fazer refeições na cozinha de uma planta; isso só é possível em uma cozinha real. As classes podem ter relacionamentos com outras classes. Por exemplo, em um design orientado a objetos de um banco, a classe “caixa de banco” precisa se relacionar com a classe “cliente”, a classe “gaveta de dinheiro”, a classe “cofre” etc. Esses relacionamentos são chamados de associações. Empacotar software como classes possibilita que os sistemas de software futuros reutilizem as classes. Grupos de classes relacionadas são frequentemente empacotados como componentes reutilizáveis. Assim como corretores de imóveis costumam dizer que os três fatores mais importantes que afetam o preço dos imóveis são “localização, localização e localização”, as pessoas na comunidade de software costumam dizer que os três fatores mais importantes que afetam o futuro do desenvolvimento de software são “reutilização, reutilização e reutilização”. A reutilização de classes existentes ao construir novas classes e programas economiza tempo e esforço. A reutilização também ajuda-lhe a construir sistemas mais confiáveis e eficientes, porque classes e componentes existentes costumam passar por extensos testes, depuração e ajuste de desempenho. Com a tecnologia de objetos, você pode construir grande parte do software necessário combinando classes, exatamente como fabricantes de automóveis combinam partes intercambiáveis. Cada nova classe que você criar terá o potencial de se tornar um “ativo de software” que você e outros programadores podem utilizar para acelerar e aprimorar a qualidade de seus esforços futuros no desenvolvimento de software.

Introdução à análise e design orientado a objetos (Object-Oriented Analysis and Design — OOAD) Logo você estará escrevendo programas em Java. Como criar o código para seus programas? Talvez, como muitos programadores, você simplesmente ligará seu computador e começará a digitar. Essa abordagem pode funcionar para pequenos programas (como os apresentados nos primeiros capítulos do livro), mas e se você fosse contratado para criar um sistema de software para controlar milhares de caixas eletrônicos para um banco importante? Ou se você fosse trabalhar em uma equipe de 1.000 desenvolvedores de software para construir o próximo sistema de controle de tráfego aéreo dos Estados Unidos? Para projetos tão grandes e complexos, você não sentaria e simplesmente começaria a escrever programas. Para criar as melhores soluções, você deve seguir um processo detalhado para analisar os requisitos do seu projeto (isto é, determinar o que o sistema deve fazer) e desenvolver um design que atenda esses requisitos (isto é, decidir como o sistema deve fazê-lo). Idealmente, você passaria por esse processo e revisaria cuidadosamente o projeto (ou teria seu projeto revisado por outros profissionais de software) antes de escrever qualquer código. Se esse processo envolve analisar e projetar o sistema de um ponto de vista orientado a objetos, ele é chamado de processo de análise e design orientado a objetos (Object-Oriented Analysis and Design — OOAD). Programadores experientes sabem que a análise e o design podem poupar muitas horas, ajudando a evitar uma abordagem de desenvolvimento de sistemas precariamente planejada que teria de ser abandonada no meio do caminho da implementação, possivelmente desperdiçando tempo, dinheiro e esforço consideráveis. O OOAD é o termo genérico para o processo de análise de um problema e desenvolvimento de uma abordagem para resolvê-lo. Pequenos problemas, como os discutidos nesses primeiros poucos capítulos, não exigem um processo exaustivo de OOAD. Talvez seja suficiente escrever o pseudocódigo antes de começarmos a escreve o código Java. O pseudocódigo é um meio informal de expressar a lógica do programa. Na realidade, o pseudocódigo não é uma linguagem de programação, mas pode ser utilizado como um esboço de orientação ao escrever o código. Introduzimos o pseudocódigo no Capítulo 4. Uma vez que os problemas e os grupos de pessoas que os resolvem aumentam em tamanho, os métodos OOAD tornam-se mais adequados do que o pseudocódigo. Idealmente, um grupo deve estabelecer um acordo comum sobre um processo rigorosamente definido para resolver seu problema e sobre uma maneira uniforme de comunicar os resultados desse processo para os outros. Embora existam muitos processos OOAD diferentes, uma única linguagem gráfica para comunicar os resultados de qualquer processo OOAD veio a ser amplamente utilizada. Essa linguagem, conhecida como Unified Modeling Language (UML), foi desenvolvida em meados da década de 1990 sob a direção inicial de três metodologistas de software — Grady Booch, James Rumbaugh e Ivar Jacobson. História da UML Na década de 1980, um número crescente de organizações começou a utilizar OOP para construir seus aplicativos e desenvolveu-se a necessidade de um processo OOAD padrão. Muitos metodologistas — incluindo Booch, Rumbaugh e Jacobson — produziram e promoveram individualmente processos separados para satisfazer essa necessidade. Cada processo tinha sua própria notação ou “linguagem” (na forma de diagramas gráficos) para transportar os resultados de análise e design.

1.17 Web 2.0

17

Por volta do início da década de 1990, diferentes organizações e até divisões dentro de uma mesma organização estavam utilizando seus próprios processos e notações. Ao mesmo tempo, essas organizações também queriam utilizar ferramentas de software que suportassem seus processos particulares. Os fornecedores de software acharam difícil e custoso fornecer ferramentas para tantos processos. Uma notação padrão e processos padrão eram necessários. Em 1994, James Rumbaugh associou-se a Grady Booch na Rational Software Corporation (agora uma divisão da IBM) e os dois começaram a trabalhar para unificar seus populares processos. Eles logo se associaram a Ivar Jacobson. Em 1996, o grupo liberou as primeiras versões da UML para a comunidade de engenharia de software e feedback solicitado. Mais ou menos na mesma época, uma organização conhecida como Object Management Group™ (OMG™) solicitou ideias e sugestões para uma linguagem de modelagem comum. O OMG (www.omg.org) é uma organização sem fins lucrativos que promove a padronização de tecnologias orientadas a objetos publicando diretrizes e especificações, como a UML. Várias corporações — entre elas HP, IBM, Microsoft, Oracle e Rational Software — já haviam reconhecido a necessidade de uma linguagem de modelagem comum. Em resposta à solicitação de propostas do OMG, essas empresas formaram o UML Partners — o consórcio que desenvolveu a UML versão 1.1 e a sugeriu para o OMG. Este aceitou a proposta e, em 1997, assumiu a responsabilidade pela manutenção e revisão constantes da UML. Apresentamos a terminologia e notação da UML 2 por todo este livro.

O que é a UML? A UML é agora o esquema de representação gráfica mais amplamente utilizado para modelar sistemas orientados a objetos. Ela de fato unificou os vários esquemas de notação populares. Aqueles que projetam sistemas utilizam a linguagem (na forma de diagramas) para modelar seus sistemas. Um recurso atraente da UML é sua flexibilidade. Ela é extensível (isto é, capaz de ser aprimorada com novos recursos) e é independente de qualquer processo OOAD particular. Os modeladores de UML são livres para utilizar vários processos para projetar sistemas, mas agora todos os desenvolvedores podem expressar seus designs com um conjunto padrão de notações gráficas. A UML é uma linguagem gráfica complexa, rica em recursos. Apresentamos nossos primeiros diagramas UML nos capítulo 3–4. No nosso estudo de caso (opcional) de engenharia de software do ATM nos capítulos 12–13, apresentamos um subconjunto simples e conciso dos recursos da UML. Então utilizamos esse subconjunto para guiá-lo pela primeira experiência de design com a UML destinada a programadores iniciantes em tecnologia orientada a objetos em cursos de programação do primeiro ou segundo semestre. Para informações adicionais sobre a UML, visite nosso UML Resource Center em www.deitel.com/UML/. Seção 1.16 Exercícios de autorrevisão 1.1

Liste três exemplos de objetos do mundo real que não mencionamos. Para cada objeto, liste vários atributos e comportamentos.

1.2

Pseudocódigo é ________. a) outro termo para OOAD b) uma linguagem de programação utilizada para exibir diagramas de UML c) uma maneira informal de expressar a lógica do programa d) um esquema de representação gráfica para modelar sistemas orientados a objetos

1.3

A UML é utilizada principalmente para ________. a) testar sistemas orientados a objetos b) projetar sistemas orientados a objetos c) implementar sistemas orientados a objetos d) a e b são alternativas corretas

Respostas da Seção 1.16 Exercícios de autorrevisão 1.1

1.2 1.3

[Nota: as respostas podem variar.] a) Os atributos de uma televisão incluem o tamanho da tela, o número de cores que ela pode exibir, o canal atual e o volume atual. Uma televisão liga e desliga, muda de canal, exibe vídeo e reproduz sons. b) Os atributos de uma cafeteira incluem o volume máximo de água que ela pode conter, o tempo necessário para preparar um bule de café e a temperatura da chapa de aquecimento sob o bule de café. Uma cafeteira liga e desliga, prepara o café e o mantém aquecido. c) Os atributos de uma tartaruga incluem sua idade, o tamanho do casco e o peso. Uma tartaruga caminha, protege-se dentro seu casco, sai de seu casco e alimenta-se de vegetais. c. b.

1.17 Web 2.0 A Web literalmente explodiu entre a metade e o final dos anos 1990, mas o fracasso econômico do “ponto com” trouxe tempos difíceis no início dos anos 2000. O ressurgimento que começou mais ou menos em 2004 foi chamado Web 2.0. A primeira conferência Web 2.0 aconteceu em 2004. Um ano depois, o termo “Web 2.0” acumulou aproximadamente 10 milhões de hits no sistema de pesquisa Google, aumentado para 60 milhões um ano mais tarde. O Google é amplamente considerado como a empresa símbolo da Web 2.0. Outras são: Craigslist (listas de classificados gratuitos), Flickr (compartilhamento de fotos), del.icio.us (social bookmarking), YouTube (compartilhamento de vídeos),

18

Capítulo 1

Introdução aos computadores, à Internet e à World Wide Web

MySpace e FaceBook (serviços de rede social), Salesforce (software profissional oferecido como serviços online), Second Life (um mundo virtual), Skype (telefonia via Internet) e Wikipedia (uma enciclopédia online gratuita). Na Deitel & Associates, lançamos nossa Internet Business Initiative baseada na Web 2.0 em 2005. Pesquisamos as tecnologias-chave da Web 2.0 e a utilizamos para construir negócios na Internet. Compartilhamos nossas pesquisas na forma de Resource Centers em www.deitel.com/resourcecenters.html. A cada semana, anunciamos os Resource Centers mais recentes no nosso boletim informativo, Deitel® Buzz Online (www.deitel.com/newsletter/subscribe.html). Cada um lista vários links para principalmente conteúdo e softwares gratuitos na Internet. Incluímos neste livro um tratamento substancial dos serviços Web (Capítulo 31) e introduzimos a nova metodologia de desenvolvimento de aplicativos dos mashups nos quais você pode desenvolver rapidamente aplicativos poderosos e intrigantes combinando serviços Web complementares e outras formas de feeds de informações de duas ou mais organizações. Um mashup popular é www.housingmaps.com, que combina as listagens de classificados de imóveis fornecidas por www.craigslist.org com as capacidades de mapeamento do Google Maps para oferecer mapas que mostram as localizações de apartamentos para locação em uma dada área. O Ajax é uma das principais tecnologias da Web 2.0. Embora o uso do termo tenha explodido em 2005, ele apenas identifica um grupo de tecnologias e técnicas de programação em uso desde o final dos anos 1990. O Ajax ajuda aplicativos baseados na Internet a ter um desempenho comparável ao dos aplicativos desktop — uma tarefa difícil, dado que esses aplicativos sofrem atrasos de transmissão à medida que os dados são transmitidos entre seu computador e outros computadores na Internet. Utilizando o Ajax, aplicativos como o Google Maps alcançaram um excelente desempenho e se aproximaram da aparência e do funcionamento dos aplicativos desktop. Embora não discutamos a programação Ajax “bruta” (que é bastante complexa) neste texto, mostramos no Capítulo 30 como construir aplicativos compatíveis com o Ajax utilizando componentes JavaServer Faces ( JSF) compatíveis com o Ajax. Blogs são sites Web (atualmente mais de 100 milhões) que se parecem com diários online, com as entradas mais recentes aparecendo primeiro. Bloggers postam rapidamente suas opiniões sobre notícias, lançamentos de produtos, candidatos políticos, questões controversas e praticamente tudo mais. A coleção de todos os blogs e a comunidade de criação de blogs é chamada blogosfera e está se tornando cada vez mais influente. Technorati e Google Blog Search são dois dos sistemas de pesquisa de blog populares. RSS feeds permitem que sites incluam informações aos assinantes. Um uso comum de RSS feeds é disponibilizar as postagens mais recentes nos blogs para assinantes. Os fluxos de informação RSS na Internet estão crescendo exponencialmente. A Web 3.0 é outro nome para a próxima geração da Web, também chamada Web semântica. A Web 1.0 era quase puramente baseada em HTML. A Web 2.0 cada vez mais utiliza a XML, especialmente em tecnologias como RSS feeds e serviços Web. A Web 3.0 utilizará intensamente a XML, criando “uma teia de significados”. Se você é um estudante procurando um artigo de pesquisa ou tema de tese, ou um empresário procurando oportunidades de negócios, verifique nosso Web 3.0 Resource Center. Para acompanhar os últimos avanços na Web 2.0, leia www.techcrunch.com e www.slashdot.org e verifique a lista crescente dos Resource Centers relacionados à Internet e Web em www.deitel.com/resourcecenters.html.

1.18 Tecnologias de software Nesta seção, discutimos alguns jargões da área de engenharia de software que você ouvirá na comunidade de desenvolvimento de softwares. Criamos Resource Centers sobre a maioria desses tópicos, com mais a caminho. O Agile Software Development é um grupo de metodologias que tenta implementar softwares rapidamente utilizando menos recursos do que as metodologias anteriores. Verifique a Agile Alliance (www.agilealliance.org) e o Agile Manifesto (www.agilemanifesto.org). A refatoração envolve a reformulação do código para torná-lo mais claro e mais fácil de manter e, ao mesmo tempo, preservar sua funcionalidade. Ela é amplamente empregada com metodologias de desenvolvimento ágeis. Há muitas ferramentas de refatoração disponíveis para criar as partes principais da reformulação automaticamente. Padrões de projeto (design patterns) são arquiteturas testadas para construir softwares orientados a objetos flexíveis e que podem ser mantidos (ver o Apêndice Q). O campo dos padrões de projeto tenta enumerar aqueles padrões recorrentes, encorajando os designers de software a reutilizá-los para desenvolver softwares de melhor qualidade em menos tempo, dinheiro e esforço. A programação de jogos é uma carreira interessante e desafiadora para desenvolvedores de software. O negócio de jogos de computador já é maior do que o negócio de filmes inéditos. Cursos universitários e até disciplinas agora dedicam-se às técnicas sofisticadas utilizadas na programação de jogos. Verifique nossos Resource Centers de projetos de programação e programação de jogos. Softwares de código-fonte aberto são um estilo de desenvolvimento de softwares que se diferencia do desenvolvimento proprietário que dominou os primeiros anos dos softwares. Com o desenvolvimento de softwares de código-fonte aberto, pessoas e empresas contribuem com seus esforços para desenvolver, manter e evoluir os softwares em troca do direito de utilizar esses softwares para objetivos próprios, em geral gratuitamente. Software de código-fonte aberto normalmente é realizado por um grupo de pessoas bem maior do que softwares proprietários, assim erros muitas vezes são removidos mais rapidamente. Código-fonte aberto também estimula mais inovações. A Sun transformou sua implementação do kit de desenvolvimento Java em código-fonte aberto. Algumas organizações sobre as quais você ouvirá muito na comunidade de código-fonte aberto são a Eclipse Foundation (o IDE Eclipse é popular para o desenvolvimento de softwares Java), a Mozilla Foundation (criadores do navegador Web Firefox), a Apache Software Foundation (criadores do servidor Web Apache) e SourceForge (que fornece as ferramentas para gerenciar projetos de código-fonte aberto e atualmente tem mais de 100.000 projetos de código-fonte aberto em desenvolvimento).

1.19 Conclusão

19

O Linux é um sistema operacional de código-fonte aberto e um dos maiores sucessos do movimento de código-fonte aberto. O MySQL é um sistema de gerenciamento de bancos de dados de código-fonte aberto. O PHP é a linguagem Internet para criação de scripts de código-fonte aberto do lado do servidor mais popular para desenvolver aplicativos baseados na Internet. LAMP é um acrônimo para o conjunto de tecnologias de código-fonte aberto que muitos desenvolvedores utilizavam para construir aplicativos Web — ele significa Linux, Apache, MySQL e PHP (ou Perl ou Python — duas outras linguagens utilizadas para objetivos semelhantes). O Ruby on Rails combina a linguagem de criação de scripts Ruby com a estrutura de aplicativo Web do Rails desenvolvida pela empresa 37Signals. Seu livro, Getting Real, é uma leitura obrigatória para os atuais desenvolvedores de aplicativo Web; leia-o gratuitamente em gettingreal.37signals.com/toc.php. Muitos desenvolvedores Ruby on Rails informaram ganhos de produtividade significativos em relação ao uso de outras linguagens ao desenvolver aplicativos Web que utilizam intensamente banco de dados. Softwares geralmente são vistos como um produto; a maioria dos softwares ainda é oferecida dessa forma. Se quiser executar um aplicativo, você compra um pacote de software de um fornecedor de softwares. Você então instala esse software no computador e executa-o conforme necessário. À medida que novas versões do software aparecem, você faz uma atualização do seu software, frequentemente com custos significativos. Esse processo pode tornar-se difícil para organizações com dezenas de milhares de sistemas que devem ser mantidos em um conjunto diverso de equipamentos de informática. Com o Software As A Service (SAAS) os softwares executam em servidores em outros locais na Internet. Quando esse servidor é atualizado, todos os clientes no mundo inteiro veem as novas capacidades; nenhuma instalação local é necessária. Você acessa o serviço por um navegador. Navegadores são bem portáteis, portanto você pode executar os mesmos aplicativos em diferentes tipos de computadores a partir de qualquer lugar no mundo. Salesforce.com, Google e o Office Live e Windows Live da Microsoft oferecem SAAS.

1.19 Conclusão Este capítulo introduziu os conceitos básicos de hardware, software e comunicação e os conceitos básicos da tecnologia orientada a objetos, incluindo classes, objetos, atributos, comportamentos, encapsulamento e herança. Discutimos os diferentes tipos de linguagens de programação e quais linguagens específicas são mais amplamente utilizadas. Você aprendeu os passos para criar e executar um aplicativo Java utilizando o JDK 6 da Sun. O capítulo explorou a história da Internet e da World Wide Web até o fenômeno Web 2.0, e o papel do Java para desenvolver aplicativos cliente/servidor distribuídos para a Internet e Web. Você também aprendeu a história e o propósito da UML — a linguagem gráfica padrão da indústria para modelar sistemas de software orientados a objetos. Você fez um test-drive de um ou mais aplicativos Java de exemplo semelhantes àqueles que você aprenderá a programar neste livro. Discutimos algumas importantes tecnologias de software recentes. No Capítulo 2, você criará seus primeiros aplicativos Java. Você verá exemplos que mostram como os programas exibem mensagens e obtêm informações do usuário para processamento. Você utilizará tipos de dados primitivos e operadores aritméticos do Java. Você utilizará operadores relacionais e de igualdade do Java para escrever instruções simples de tomada de decisão. Analisaremos e explicaremos cada exemplo para ajudar a facilitar a introdução à programação de Java.

1.20 Recursos da Web Esta seção lista muitos sites úteis à medida que você aprende o Java. Os sites incluem recursos do Java, ferramentas de desenvolvimento em Java para alunos e profissionais e, além disso, nossos próprios sites da Web em que você pode encontrar downloads e recursos associados com este livro.

Deitel & Associates Websites www.deitel.com

Contém atualizações, correções e recursos adicionais para todas as publicações Deitel. www.deitel.com/newsletter/subscribe.html

Assine e receba gratuitamente por e-mail o boletim informativo Deitel® Buzz Online para acompanhar o programa de publicação da Deitel & Associates, incluindo atualizações e erratas para este livro. www.deitel.com/books/jhtp8/ e www.prenhall.com/deitel_br

O primeio é o site da Deitel & Associates (em inglês) e o segundo é o site da Prentice Hall (em português) para este livro. Aqui você encontrará links para os exemplos do livro e outros recursos.

Centros de Recursos Relacionados a Java da Deitel & Associates www.deitel.com/Java/

Nosso Java Resource Center focaliza uma quantidade enorme de conteúdo gratuito sobre o Java disponível online. Inicie sua pesquisa aqui para ferramentas, ambientes de desenvolvimento integrados (IDEs), recursos, downloads, tutoriais, documentação, livros, e-books, periódicos, artigos, blogs e mais que irão ajudá-lo a desenvolver aplicativos Java.

20

Capítulo 1  Introdução aos computadores, à Internet e à World Wide Web

www.deitel.com/JavaSE6Mustang/

Nosso Java SE 6 (Mustang) Resource Center é seu guia para a versão mais recente do Java. O site inclui os melhores recursos que encontramos on-line para ajudá-lo a começar a trabalhar com o desenvolvimento Java SE 6. www.deitel.com/CodeSearchEngines/

Nossos Code Search Engines e Code Sites Resource Center incluem o uso de recursos que os desenvolvedores utilizam para localizar código-fonte online. www.deitel.com/ProgrammingProjects/

Nosso Programming Projects Resource Center é seu guia online para excelentes ideias relacionadas a projetos de programação de alunos.

Editores e ambientes de desenvolvimento integrado www.eclipse.org

O ambiente de desenvolvimento Eclipse pode ser utilizado para desenvolver código em qualquer linguagem de programação. Você pode fazer o download do ambiente e vários plug-ins Java para desenvolver seus programas Java. www.netbeans.org

O IDE do NetBeans é uma das ferramentas de desenvolvimento Java livremente distribuídas mais amplamente utilizada. www.blueJ.org

BlueJ é uma ferramenta gratuita projetada a fim de ajudar a ensinar o Java orientado a objetos para novos programadores. www.jgrasp.org

jGRASP é um ambiente de desenvolvimento integrado “mais leve” desenvolvido na Auburn University. A ferramenta também exibe representações visuais dos programas Java para auxiliar a compreensão. www.jcreator.com

JCreator é um IDE do Java popular. O JCreator Lite Edition está disponível como um download gratuito. Também há uma versão de avaliação de 30 dias do Creator Pro Edition. www.textpad.com

TextPad é um editor que também permite compilar e executar seus programas Java. www.editplus.com

EditPlus é um editor que pode ser configurado para compilar e executar seus programas Java. www.jedit.org

jEdit é um editor escrito em Java e pode ser utilizado com muitas linguagens de programação.

Resumo Seção 1.1  Introdução • O Java tornou-se a linguagem preferida para implementar aplicativos baseados na Internet e software para dispositivos que se comunicam em uma rede. • O Java Enterprise Edition ( Java EE) é adequado para desenvolver aplicativos em rede distribuídos e em grande escala e também aplicativos baseados na Web. • A Java Micro Edition ( Java ME) é voltada para o desenvolvimento de aplicativos de pequenos dispositivos com limitações de memória, como telefones celulares, pagers e PDAs.

Seção 1.2  Computadores: hardware e software • Um computador é um dispositivo que pode realizar cálculos e tomar decisões lógicas fenomenalmente mais rápido do que os seres humanos. • Os computadores processam dados sob o controle de conjuntos de instruções chamados programas de computador. Os programas guiam os computadores ao longo das ações especificadas por pessoas chamadas programadores. • Um computador consiste em vários dispositivos conhecidos como hardware. Os programas que executam em um computador são chamados de software.



Resumo

21

Seção 1.3  Organização do computador • Praticamente cada computador pode ser visto como sendo dividido em seis unidades lógicas ou seções. • A unidade de entrada obtém informações a partir dos dispositivos de entrada e disponibiliza essas informações a outras unidades para que possam ser processadas. • A unidade de saída pega as informações que o computador processou e as coloca em vários dispositivos de saída para tornar as informações disponíveis para serem utilizadas fora do computador. • A unidade de memória é a seção de armazenamento de relativamente baixa capacidade e acesso rápido do computador. Ela retém as informações que foram inseridas por meio da unidade de entrada, tornando-as imediatamente disponíveis para processamento quando necessário. Ela também retém informações processadas até que elas possam ser colocadas em dispositivos de saída pela unidade de saída. • A unidade aritmética e lógica (ALU) é responsável por realizar cálculos (tais como adição, subtração, multiplicação e divisão) e tomar decisões. • A unidade central de processamento (CPU) coordena e supervisiona a operação das outras seções. A CPU diz à unidade de entrada quando as informações devem ser lidas e transferidas para a unidade de memória, informa à ALU quando as informações da unidade de memória devem ser utilizadas em cálculos e instrui a unidade de saída sobre quando enviar as informações da unidade de memória para certos dispositivos de saída. • Os multiprocessadores têm múltiplas CPUs e, consequentemente, podem realizar várias operações simultaneamente. • Um processador de múltiplos núcleos (ou multi-core) implementa o multiprocessamento em um único chip de circuito integrado — por exemplo um processador de dois núcleos (ou dual-core) tem duas CPUs e um processador de quatro núcleos (ou quad-core) tem quatro. • A unidade de armazenamento secundário é a seção de armazenamento de alta capacidade e longo prazo do computador. Programas ou dados não ativamente usados por outras unidades normalmente são colocados em dispositivos de armazenamento secundário até que sejam necessários.

Seção 1.4  Primeiros sistemas operacionais • Os primeiros computadores podiam realizar apenas um trabalho ou tarefa por vez. • Os sistemas operacionais foram desenvolvidos para tornar o uso dos computadores mais conveniente. • A multiprogramação envolve a operação simultânea de muitos trabalhos. • Com o compartilhamento de tempo, o computador executa uma pequena parte do trabalho de um usuário, e, em seguida, atende o próximo usuário, talvez fornecendo serviço para cada usuário várias vezes por segundo.

Seção 1.5  Computação pessoal distribuída e computação cliente/servidor • Em 1977, a Apple Computer popularizou a computação pessoal. • Em 1981, a IBM, o maior fornecedor de computadores no mundo, introduziu o computador pessoal IBM, que rapidamente legitimou a computação pessoal nos negócios, indústrias e governos. • Na computação distribuída, em vez de ser executada apenas em um computador central, a computação é distribuída ao longo de redes até os sites onde o trabalho da organização é realizado. • Os servidores armazenam dados que podem ser utilizados por computadores clientes distribuídos por toda a rede, daí o termo computação cliente/ servidor. • O Java tornou-se amplamente utilizado para escrever software de rede de computador e aplicativos distribuídos cliente/servidor.

Seção 1.6  A Internet e a World Wide Web • A Internet é acessível por mais de um bilhão de computadores e dispositivos controlados por computador. • Com a introdução da World Wide Web, a Internet tornou-se um dos principais mecanismos de comunicação do mundo.

Seção 1.7  Linguagens de máquina, linguagens assembly e linguagens de alto nível • Qualquer computador pode entender diretamente somente sua própria linguagem de máquina. • A linguagem de máquina é a “linguagem natural” de um computador. • As linguagens de máquina consistem geralmente em strings de números (em última instância reduzidas a 1s e 0s) que instruem os computadores a realizar suas operações mais elementares uma de cada vez. • Qualquer computador pode entender diretamente apenas sua própria linguagem de máquina. • Os programadores começaram a utilizar abreviações como aquelas do idioma inglês para representar operações elementares. Essas abreviações formaram a base de linguagens assembly. • Programas tradutores chamados assemblers foram desenvolvidos para converter os primeiros programas de linguagem assembly em linguagem de máquina a velocidades de computador. • Para acelerar o processo de programação, foram desenvolvidas linguagens de alto nível em que instruções únicas poderiam ser escritas para realizar tarefas substanciais. • Os programas tradutores chamados compiladores convertem os programas de linguagem de alto nível em linguagem de máquina.

22

Capítulo 1  Introdução aos computadores, à Internet e à World Wide Web

• Linguagens de alto nível permitem aos programadores escrever instruções que se parecem com o inglês cotidiano e contêm notações matemáticas comumente utilizadas. • O Java é de longe a linguagem de alto nível mais amplamente usada. • O processo de compilação de um programa de linguagem de alto nível em linguagem de máquina pode consumir uma quantidade considerável de tempo do computador. Os programas interpretadores foram desenvolvidos para executar programas de linguagem de alto nível diretamente (sem o tempo de espera da compilação), embora mais lentos do que a execução de programas compilados.

Seção 1.8  História do C e do C++ • O Java evoluiu do C++, que evoluiu do C, que evoluiu do BCPL e do B. • A linguagem C foi desenvolvida por Dennis Ritchie na Bell Laboratories. Inicialmente, ela tornou-se muito conhecida como a linguagem de desenvolvimento do sistema operacional UNIX. • O C++, uma extensão do C, foi desenvolvido por Bjarne Stroustrup no início da década de 1980 na Bell Laboratories. O C++ fornece alguns recursos que sofisticam a linguagem C e a capacidade para a programação orientada a objetos.

Seção 1.9  História do Java • O Java é usado para desenvolver aplicativos corporativos de grande porte, aprimorar a funcionalidade de servidores da Web, fornecer aplicativos para dispositivos de consumo popular e para muitos outros fins. • Programas Java consistem em partes chamadas classes. As classes incluem partes chamadas métodos que realizam tarefas e retornam informações quando as tarefas são concluídas.

Seção 1.10  Bibliotecas de classe do Java • A maioria dos programadores Java tira proveito das ricas coleções de classes existentes nas bibliotecas de classe Java, também conhecidas como Java APIs (Application Programming Interfaces). • A vantagem de criar suas próprias classes e métodos é que você sabe como eles funcionam e pode examinar o código. A desvantagem é o esforço demorado e potencialmente complexo.

Seção 1.11  Fortran, Cobol, Pascal e Ada • A linguagem Fortran (FORmula TRANslator) foi desenvolvida pela IBM Corporation em meados da década de 1950 para utilização em aplicativos científicos e de engenharia que exigem complexos cálculos matemáticos. • O Cobol (COmmon Business Oriented Language) é utilizado para aplicativos comerciais que exigem manipulação precisa e eficiente de grandes quantidades de dados. • Pesquisas nos anos 1960 resultaram na programação estruturada — uma abordagem para escrever programas que são mais claros e mais fáceis de testar, depurar e modificar do que aqueles produzidos com as técnicas anteriores. • O Pascal foi projetado para ensinar a programação estruturada em ambientes acadêmicos e rapidamente tornou-se a linguagem de programação preferida na maioria das universidades. • A linguagem de programação Ada foi desenvolvida com o patrocínio do U.S. Department Of Defense (DOD) para atender à maioria das suas necessidades. Uma capacidade do Ada chamada multitarefa permite aos programadores especificar que atividades devem ocorrer em paralelo. O Java, por meio de uma técnica chamada multithreading, também permite que os programadores escrevam programas com atividades paralelas.

Seção 1.12  Basic, Visual Basic, Visual C++, C# e .NET • O Basic foi desenvolvido em meados dos anos 60 para escrever programas simples. • A linguagem Visual Basic da Microsoft simplifica o desenvolvimento de aplicativos Windows. • A plataforma .NET da Microsoft integra a Internet e a Web a aplicativos de computador.

Seção 1.13  Ambiente típico de desenvolvimento Java • Os programas Java normalmente passam por cinco fases — edição, compilação, carga, verificação e execução. • A fase 1 consiste na edição de um arquivo com um editor. Você digita um programa utilizando o editor, faz correções e salva o programa em um dispositivo de armazenamento secundário, como sua unidade de disco rígido. • Um nome de arquivo que termina com a extensão .java indica que o arquivo contém o código-fonte Java. • Ambientes de desenvolvimento integrados (IDEs) fornecem ferramentas que dão suporte ao desenvolvimento de softwares, incluindo editores para escrever e editar programas e depuradores para localizar erros de lógica. • Na Fase 2, o programador usa o comando javac para compilar um programa. • Se um programa compilar, o compilador produz um arquivo .class que contém o programa compilado. • O compilador Java converte o código-fonte Java em bytecodes que representam as tarefas a ser executadas. Bytecodes são executados pela Java Virtual Machine ( JVM).



Resumo

23

• Na Fase 3, carregamento, o carregador de classe seleciona os arquivos .class que contêm os bytecodes do programa e transfere-os para a memória primária. • Na Fase 4, enquanto as classes são carregadas, o verificador de bytecode examina seus bytecodes para assegurar que eles são válidos e não violam restrições de segurança do Java. • Na Fase 5, a JVM executa os bytecodes do programa.

Seção 1.14  Notas sobre o Java e este livro • Portabilidade é um objetivo vago. Simplesmente escrever programas em Java não garante portabilidade.

Seção 1.15  Testando um aplicativo Java • Nesta seção, você executou e interagiu com seu primeiro aplicativo Java.

Seção 1.16  Estudo de caso de engenharia de software: introdução à tecnologia de objetos e à UML • A Unified Modeling Language (UML) é uma linguagem gráfica que permite que as pessoas construam sistemas para representar seus designs orientados a objetos em uma notação comum. • O design orientado a objetos (Object-Oriented Design — OOD) modela componentes de software em termos de objetos do mundo real. • Objetos têm a propriedade de ocultamento de informações — os objetos de uma classe normalmente não sabem como os objetos de outras classes são implementados. • A Programação Orientada a Objetos (POO) implementa designs orientados a objetos. • Os programadores Java concentram-se em criar seus próprios tipos definidos pelo usuário chamados classes. Cada classe contém dados bem como métodos que manipulam esses dados e fornecem serviços aos clientes. • Os componentes de dados de uma classe são atributos ou campos; os componentes de operação são métodos. • Classes podem ter relacionamentos com outras classes; esses relacionamentos são chamados associações. • Empacotar software como classes possibilita que os sistemas de software futuros reutilizem as classes. • Uma instância de uma classe é chamada de objeto. • O processo de analisar e projetar um sistema a partir de um ponto de vista orientado a objetos é chamado análise orientada a objetos e design (ObjectOriented Analysis and Design — OOAD).

Seção 1.17  Web 2.0 • O ressurgimento da Web, que começou mais ou menos em 2004, foi chamado Web 2.0. • Mashups permitem desenvolver rapidamente aplicativos poderosos e intrigantes combinando serviços Web complementares e outras formas de feeds de informações de duas ou mais organizações. • O Ajax ajuda aplicativos baseados na Internet a ter um desempenho comparável ao dos aplicativos desktop — uma tarefa difícil, dado que esses aplicativos sofrem atrasos de transmissão à medida que os dados são transmitidos entre seu computador e outros computadores na Internet. • Blogs são sites Web que se parecem com diários online, com as entradas mais recentes aparecendo primeiro. • RSS feeds permitem que sites incluam informações aos assinantes. • A Web 3.0 é outro nome para a próxima geração da Web, também chamada Web semântica. A Web 3.0 utilizará intensamente a XML, criando “uma teia de significados”.

Seção 1.18  Tecnologias de software • O Agile Software Development é um grupo de metodologias que tenta implementar softwares rapidamente utilizando menos recursos do que as metodologias anteriores. • A refatoração envolve a reformulação do código para torná-lo mais claro e mais fácil de manter e, ao mesmo tempo, preservar sua funcionalidade. • Padrões de design são arquiteturas testadas para construir softwares orientados a objetos flexíveis e que podem ser mantidos. • A programação de jogos é uma carreira interessante e desafiadora para desenvolvedores de software. Cursos universitários e mesmo especializações dedicam-se às técnicas de software sofisticadas utilizadas na programação de jogos. • Com o desenvolvimento de softwares de código-fonte aberto, pessoas e empresas contribuem com seus esforços para desenvolver, manter e evoluir os softwares em troca do direito de utilizar esses softwares para objetivos próprios, em geral gratuitamente. Código de código-fonte aberto geralmente é realizado por um grupo de pessoas bem maior do que softwares proprietários, assim erros muitas vezes são removidos mais rapidamente. Código de código-fonte aberto também estimula mais inovações. • O Linux é um sistema operacional de código-fonte aberto e um dos maiores sucessos do movimento de código-fonte aberto. O MySQL é um sistema de gerenciamento de bancos de dados de código-fonte aberto. O PHP é a linguagem Internet para criação de scripts de código-fonte aberto do lado do servidor mais popular para desenvolver aplicativos baseados na Internet. LAMP é um acrônimo para o conjunto de tecnologias de código-fonte aberto que muitos desenvolvedores utilizavam para construir aplicativos Web — ele significa Linux, Apache, MySQL e PHP (ou Perl ou Python — duas outras linguagens utilizadas para objetivos semelhantes).

24

Capítulo 1  Introdução aos computadores, à Internet e à World Wide Web

• O Ruby on Rails combina a linguagem de criação de scripts Ruby com a estrutura de aplicativo Web do Rails desenvolvida pela empresa 37Signals. Muitos desenvolvedores Ruby in Rails informaram ganhos de produtividade significativos em relação ao uso de outras linguagens ao desenvolver aplicativos Web que utilizam intensamente banco de dados. • Com o Software As A Service (SAAS), os softwares executam em servidores em outros locais na Internet. Você acessa o serviço por um navegador. Navegadores são bem portáteis, portanto você pode executar os mesmos aplicativos em diferentes tipos de computadores a partir de qualquer lugar no mundo.

Terminologia Ada, linguagem de programação,  8 Agile Software Development,  18 Ajax (Asynchronous JavaScript and XML),  18 ALU (Arithmetic and Logic Unit),  4 ambiente de desenvolvimento integrado,  9 analisar os requisitos de um projeto,  16 análise e design orientado a objetos (Object-Oriented Analysis and Design — OOAD),  16 aplicativo de terminal (Mac OS X),  10 assembler,  5 associação,  16 associação na UML,  16 Basic (Beginner's All-Purpose Symbolic Instruction Code),  8 blog,  18 blogosfera,  18 boa prática de programação,  7 Bytecode,  10 campo,  16 carregador de classe,  10 carregando,  10 .class, arquivo,  10 classe,  7, 15 C#, linguagem de programação,  8 Cobol (COmmon Business Oriented Language),  7 código-fonte,  8 código-fonte aberto, software,  18 Command Prompt,  10 compartilhamento de tempo,  4 compilador,  5 compilar um programa,  10 componente,  6, 16 componente na UML,  16 computação cliente/servidor,  5 computação distribuída,  4 computação pessoal,  4 computador,  2 computador cliente,  5 conteúdo dinâmico,  6 CPU,  4 dados,  3 Deitel® Buzz Online, boletim,  19 dependente de máquina,  5 depurar,  7 design orientado a objetos (Object-Oriented Design — OOD),  15 Dica de desempenho,  7 Dica de portabilidade,  7 Dica de prevenção de erro,  7 dispositivos de entrada,  3 dispositivos de saída,  3 editor,  8 encapsulamento,  15 erro de lógica,  9 Erro de programação comum,  7

erro de tempo de execução,  11 erro fatal,  11 erro não fatal de tempo de execução,  11 estação de trabalho,  4 Fortran (FORmula TRANslator),  7 função,  16 Google,  17 hardware,  3, 5 herança,  15 IDE,  9 informações voláteis,  3 instanciar,  16 interface,  15 interface de programas aplicativos (Applications Programming Interface — API),  7 Internet,  5 interpretador,  6 java, comando,  10, 12 Java Application Programming Interface ( Java API),  7 Java, biblioteca de classe,  7 Java, compilador,  10 Java Enterprise Edition ( Java EE),  3 .java, extensão,  10 Java HotSpot, compilador,  10 Java Micro Edition ( Java ME),  3 Java Standard Edition ( Java SE),  3 Java Virtual Machine ( JVM),  10, 11 JIT, compilador,  10 just-in-time, compilação,  10 Just-In-Time ( JIT), compilador,  10 LAMP,  19 linguagem assembly,  5 linguagem de alto nível,  5 linguagem de máquina,  5 linguagem de programação procedural,  16 linguagem extensível,  17 linguagem orientada a objetos,  16 Linux,  5, 8, 19 live-code, abordagem,  2 máquina virtual (VM),  10 mashups,  18 memória,  3 memória principal,  3 método,  7, 16 multiprocessador,  4 multiprogramação,  4 multitarefa,  8 MySQL,  19 Object Management Group (OMG),  17 Object Management Group (www.omg.org),  17 Observações de engenharia de software,  7 Observações sobre a aparência e comportamento,  7 ocultamento de informações,  15

OOD (Object-Oriented Design),  15 OOP (Object-Oriented Programming),  16 Operação de um objeto,  15 orientado para a ação,  16 padrões de projeto (design patterns),  18 persistente,  4 PHP,  19 plataforma .NET,  8 pontos ativos no bytecode,  10 portável,  10 processador de múltiplos núcleos,  4 processamento em lote,  4 processo de análise,  16 Programação de jogos,  18 programação estruturada,  2, 3, 8 programa de computador,  3 programador,  3 programador de computador,  3 programa tradutor,  5 pseudocódigo,  16 rede local (Local Area Network — LAN),  4 refatorando,  18 requisitos,  16 RSS feeds,  18 Ruby on Rails,  19 serviços Web,  18 servidor,  5 shell, prompt no Linux,  10 sistema operacional,  4 Software As A Service (SAAS),  19 supercomputador,  3 tarefa,  4 Technorati,  18 throughput,  4 trabalho,  4 tradução,  5 UML, parceiros,  17 UML (Unified Modeling Language),  15 unidade central de processamento (Central Processing Unit — CPU),  4 Unidade de Aritmética e Lógica (ALU),  4 unidade de armazenamento secundária,  4 unidade de entrada,  3 unidade de memória,  3 unidade de saída,  3 unidade lógica,  4 Unified Modeling Language (UML),  15, 16 verificador de bytecode,  10 Visual Basic, linguagem de programação,  8 Visual C#, linguagem de programação,  8 Visual C++, linguagem de programação,  8 Web 2.0,  17 Web 3.0,  18 Web semântica,  18 World Wide Web (WWW),  5



Exercícios de autorrevisão

25

Exercícios de autorrevisão 1.1

Preencha as lacunas em cada uma das seguintes afirmações: a) A empresa que popularizou a computação pessoal foi ________. b) O computador que tornou computação pessoal legítima nos negócios e na indústria foi ________. c) Os computadores processam dados sob o controle de conjuntos de instruções chamados ________. d) As principais unidades lógicas do computador são ________, ________, ________, ________, ________ e ________. e) Os três tipos de linguagens discutidas no capítulo são ________, ________ e ________. f) Os programas que traduzem programas de linguagem de alto nível em linguagem de máquina são chamados ________. g) O ________ permite aos usuários de computador localizar e visualizar documentos baseados em multimídia sobre aproximadamente qualquer assunto pela Internet. h) ________ permite que um programa Java realize múltiplas atividades paralelamente.

1.2

Preencha as lacunas em cada uma das seguintes frases sobre o ambiente Java: a) O comando ________ do JDK executa um aplicativo Java. b) O comando ________ do JDK compila um programa Java. c) Um arquivo de programa Java deve terminar com a extensão de arquivo ________. d) Quando um programa Java é compilado, o arquivo produzido pelo compilador termina com a extensão de arquivo ________. e) O arquivo produzido pelo compilador Java contém ________ que são executados pela Java Virtual Machine.

1.3

Preencha as lacunas de cada uma das sentenças a seguir (com base na Seção 1.16): a) Os objetos têm a propriedade de ________ — embora os objetos possam saber comunicar entre si por interfaces bem definidas, normalmente não têm permissão de saber como outros objetos são implementados. b) Os programadores Java concentram-se na criação de ________, que contém campos e o conjunto de métodos que manipulam esses campos e fornecem serviços para clientes. c) As classes podem ter relacionamentos chamados ________ com outras classes. d) O processo de analisar e projetar um sistema de um ponto de vista orientado a objetos é chamado ________. e) O OOD tira proveito de relacionamentos de ________, nos quais novas classes de objetos são derivadas absorvendo características de classes existentes e, em seguida, adicionando características únicas dessas mesmas classes. f) ________ é uma linguagem gráfica que permite que as pessoas que projetam sistemas de software utilizem uma notação padrão da indústria para representá-las. g) O tamanho, forma, cor e peso de um objeto são considerados ________ da classe do objeto.

Respostas dos exercícios de autorrevisão 1.1 1.2 1.3

a) Apple. b) computador pessoal IBM. c) programas. d) unidade de entrada, unidade de saída, unidade de memória, unidade central de processamento, unidade aritmética e lógica e unidade de armazenamento secundário. e) linguagens de máquina, linguagens assembly e linguagens de alto nível. f) compiladores. g) World Wide Web. h) Multithreading. a) java. b) javac. c) .java. d) .class. e) bytecodes. a) ocultamento de informações. b) classes. c) associações. d) análise orientada a objetos e design (Object-Oriented Analysis and Design — OOAD). e) herança. f) Unified Modeling Language (UML). g) atributos.

Exercícios 1.4

Categorize cada um dos itens seguintes como hardware ou software: a) CPU d) unidade de entrada b) Java, compilador e) editor c) JVM

1.5

Preencha as lacunas em cada uma das seguintes afirmações: a) A unidade lógica do computador que recebe informações de fora do computador para utilização pelo computador é a ________. b) O processo de instrução do computador para resolver um problema específico é chamado ________. c) ________ é um tipo de linguagem de computador que utiliza abreviações em inglês para instruções de linguagem de máquina. d) ________ é uma unidade lógica do computador que envia informações que já foram processadas pelo computador para vários dispositivos de modo que possam ser utilizadas fora do computador. e) ________ e ________ são unidades lógicas do computador que retêm informações. f) ________ é uma unidade lógica do computador que realiza cálculos. g) ________ é uma unidade lógica do computador que toma decisões lógicas.

26

Capítulo 1  Introdução aos computadores, à Internet e à World Wide Web h) As linguagens mais convenientes para que programador escreva programas rápida e facilmente são as linguagens ________. i) A única linguagem que um computador pode entender diretamente é a ________ do computador. j) ________ é uma unidade lógica do computador que coordena as atividades de todas as outras unidades lógicas.

1.6

Qual é a diferença entre erros fatais e erros não fatais? Por que você poderia preferir que o programa sofresse um erro fatal em vez de um erro não fatal?

1.7

Preencha as lacunas em cada uma das seguintes afirmações: a) ________ é agora utilizado para desenvolver aplicativos corporativos de grande porte, aprimorar a funcionalidade de servidores da Web, fornecer aplicativos para dispositivos de consumo popular e para muitos outros propósitos. b) ________ foi projetada especificamente para a plataforma .NET para permitir aos programadores migrar facilmente para .NET. c) Inicialmente, o ________ tornou-se muito conhecido como a linguagem de desenvolvimento do sistema operacional UNIX. d) ________ foi desenvolvida no Dartmouth College em meados da década de 1960 como um meio de escrever programas simples. e) ________ foi desenvolvida pela IBM Corporation em meados da década de 1950 para ser utilizada para aplicativos científicos e de engenharia que requerem complexos cálculos matemáticos. f) ________ é utilizada para aplicativos comerciais que exigem manipulação precisa e eficiente de grandes quantidades de dados. g) A linguagem de programação ________ foi desenvolvida por Bjarne Stroustrup no início dos anos 80 na Bell Laboratories.

1.8

Preencha as lacunas em cada uma das seguintes afirmações: a) Os programas Java normalmente passam por cinco fases: ________, ________, ________, ________ e ________. b) Um(a) ________ fornece muitas ferramentas que suportam o processo de desenvolvimento de software, como editores para escrever e editar programas, depuradores para localizar erros de lógica em programas e muitos outros recursos. c) O comando java invoca ________, que executa programas Java. d) Uma ________ é um aplicativo de software que simula um computador, mas oculta o sistema operacional e o hardware subjacentes dos programas que interagem com ela. e) Um programa ________ pode executar em múltiplas plataformas. f) O ________ transfere os arquivos .class contendo os bytecodes do programa para a memória principal. g) O ________ examina bytecodes para assegurar que eles são válidos.

1.9

Explique as duas fases de compilação de programas Java.

1.10

Provavelmente você esteja usando no pulso um dos tipos mais comuns de objetos do mundo — um relógio. Discuta como cada um dos seguintes termos e conceitos se aplicam à noção de um relógio: objeto, atributos, comportamentos, classe, herança (considere, por exemplo, o alarme de um relógio), abstração, modelagem, mensagens, encapsulamento, interface e ocultamento de informações.

Fazendo a diferença 1.11 (Test-drive: Calculadora de emissão de carbono) Alguns cientistas acreditam que as emissões de carbono, especialmente da queima de combustíveis fósseis, contribuem significativamente para o aquecimento global e que isso pode ser combatido se as pessoas tomarem medidas para limitar o uso de combustíveis baseados no carbono. As organizações e pessoas estão cada vez mais preocupadas com suas “emissões de carbono”. Sites Web, como o TerraPass: www.terrapass.com/carbon-footprint-calculator/



e o Carbon Footprint: www.carbonfootprint.com/calculator.aspx



fornecem calculadoras de emissão de carbono. Teste essas calculadoras para determinar as sua emissões de carbono. Os exercícios nos próximos capítulos solicitarão que você programe sua própria calculadora. Para preparar-se para isso, pesquise as fórmulas para calcular as emissões de carbono.

1.12 (Test-drive: Calculadora de índice de massa corpórea) De acordo com estimativas recentes, dois terços dos norte-americanos estão acima do peso e aproximadamente metade destes são obesos. Isso causa aumentos significativos de doenças como diabetes e doenças cardíacas. Para determinar se uma pessoa está acima do peso ou obesa, você pode utilizar uma medida chamada índice de massa corpórea (IMC). Os departamentos de assistência social e de saúde norte-americanos fornecem uma calculadora do IMC em www.nhlbisupport.com/bmi/. Utilize-o para calcular seu próprio IMC. Um exercício no Capítulo 2 solicitará que você programe sua própria calculadora de IMC. Para preparar-se para isso, pesquise as fórmulas para calcular o IMC.

1.13 (Atributos dos veículos híbridos) Neste capítulo você aprendeu os conceitos básicos das classes. Agora você começará a detalhar os aspectos da classe chamada “Veículo híbrido”. Veículos híbridos estão se tornando cada vez mais populares, porque muitas vezes eles têm um desempenho por litro de combustível muito melhor do que veículos que só utilizam gasolina. Navegue pela Web e estude os recursos de quatro ou cinco dos atuais carros híbridos mais populares e então liste o maior número possível dos atributos relacionados a veículos híbridos. Por exemplo, atributos comuns incluem consumo urbano por litro de combustível e consumo em estradas. Também liste os atributos das baterias (tipo, peso etc.).



Fazendo a diferença

27

1.14 (Neutralidade de sexo) Muitas pessoas querem eliminar o machismo em todas as formas de comunicação. Você foi solicitado a criar um pro-

grama que pode processar um parágrafo do texto e substituir palavras de sexo específico por outras neutras. Supondo que você recebeu uma lista de palavras de sexo específico e suas substituições neutras em relação a ele (por exemplo, substitua “esposa” por “cônjuge”, “homem” por “pessoa”, “filha” por “criança” etc.), explique o procedimento que você utilizaria para ler um parágrafo do início ao fim do texto e fazer manualmente essas substituições. Pensando no idioma inglês, como seu procedimento poderia gerar um termo estranho como “woperchild”, que de fato está listado no Urban Dictionary (www.urbandictionary.com)? No Capítulo 4, você aprenderá que um termo mais formal para “procedimento” é “algoritmo”, e que um algoritmo especifica os passos a serem executados e a ordem para executá-los.

2

Que há em um simples nome? O que chamamos rosa, sob outra designação teria igual perfume. — William Shakespeare

Quando preciso tomar uma decisão, sempre pergunto, “O que seria mais divertido?” — Peggy Walker

“Tome mais chá”, a Lebre de Março disse para Alice, muito sinceramente. “Eu ainda não bebi nada”, Alice respondeu em um tom ofendido: “não posso tomar mais”. “Você quis dizer que não podes tomar menos”, disse Leirão: “É muito fácil tomar mais do que não tomar nada”. — Lewis Carroll

Introdução aos aplicativos Java

Objetivos Neste capítulo, você aprenderá: 

A escrever aplicativos Java simples.



A utilizar instruções de entrada e saída.



Os tipos primitivos do Java.



Os conceitos básicos de memória.



A utilizar operadores aritméticos.



A precedência dos operadores aritméticos.



A escrever instruções de tomada de decisão.



A utilizar operadores relacionais de igualdade.

Sumário

2.1 Introdução

2.1 Introdução

2.6 Conceitos de memória

2.2 Nosso primeiro programa Java: imprimindo uma linha de texto

2.7 Aritmética

2.3 Modificando nosso primeiro programa Java

2.8 Tomada de decisão: operadores de igualdade e operadores relacionais

2.4 Exibindo texto com printf

2.9 Conclusão

29

2.5 Outro aplicativo: adicionando inteiros Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios | Fazendo a diferença

2.1 Introdução Neste capítulo, apresentamos a programação de aplicativos Java com programas simples para uma introdução à criação de programas Java. Começamos com vários exemplos que exibem mensagens na tela e, então, demonstramos um programa que obtém dois números de um usuário, calcula sua soma e exibe o resultado. Você aprenderá a realizar cálculos aritméticos e salvar os resultados para utilização posterior. O último exemplo demonstra a tomada de decisão mostrando como comparar números e, em seguida, exibir mensagens baseadas nos resultados de comparação. Este capítulo utiliza as ferramentas do JDK para compilar e executar programas. Além disso, para ajudá-lo a começar a usar os populares ambientes de desenvolvimento integrados Eclipse e NetBeans, postamos vídeos em www.deitel.com/books/jhtp8/.

2.2 Nosso primeiro programa Java: imprimindo uma linha de texto Um aplicativo Java é um programa de computador que é executado quando você utiliza o comando java para carregar a Java Virtual Machine ( JVM). Vamos examinar um aplicativo simples que exibe uma linha de texto. (Mais adiante, nesta seção, discutiremos como compilar e executar um aplicativo.) O programa e sua saída são mostrados na Figura 2.1. A saída aparece no quadro no fim do programa. O programa ilustra vários recursos importantes da linguagem Java. Todo programa que apresentamos tem números de linha, que não fazem parte dos programas Java reais. Mais adiante, veremos que a linha 9 faz o trabalho real do programa — exibir a frase Welcome to Java Programming! na tela. 1 2 3 4 5 6 7 8 9 10 11

// Figura 2.1: Welcome1.java // Programa de impressão de texto. public class Welcome1 { // método principal inicia a execução do aplicativo Java public static void main( String[] args ) { System.out.println( "Welcome to Java Programming!" ); } // fim do método main } // fim da classe Welcome1

Welcome to Java Programming!

Figura 2.1 | Programa de impressão de texto.

Comentando programas A linha 1 // Figura 2.1: Welcome1.java

começa com //, que indica que a linha é um comentário. Você insere comentários para documentar programas e aprimorar sua legibilidade. O compilador Java ignora os comentários, portanto eles não fazem o computador realizar nenhuma ação quando o programa é executado. Por convenção, iniciamos cada programa com um comentário indicando o número da figura e o nome do arquivo. Um comentário que começa com // é um comentário de fim de linha — ele termina no fim da linha em que aparece. Um comentário de fim de linha também pode iniciar no meio de uma linha e continua até o fim dessa linha (como nas linhas 10–11). Outro tipo de comentário, chamado de comentário tradicional, pode ser distribuído por várias linhas como em:

30

Capítulo 2  Introdução aos aplicativos Java /* Isso é um comentário tradicional. Ele pode ser dividido em várias linhas */

Esse tipo de comentário começa com /* e termina com */. Todo o texto entre os delimitadores é ignorado pelo compilador. O Java incorporou comentários tradicionais e comentários de fim de linha das linguagens de programação C e C++, respectivamente. Neste livro, utilizamos somente comentários //. O Java também fornece comentários no estilo Javadoc que são delimitados por /** e */. Como ocorre com comentários tradicionais, todo o texto entre os delimitadores de comentário no estilo Javadoc é ignorado pelo compilador. Os comentários no estilo Javadoc permitem-lhe incorporar a documentação do programa diretamente aos seus programas. Esses comentários são o formato de documentação Java preferido na indústria. O programa utilitário javadoc (parte do Java SE Development Kit) lê comentários no estilo Javadoc e utiliza-os para preparar a documentação do seu programa no formato HTML. Demonstramos comentários de Javadoc e o utilitário javadoc no Apêndice M, “Criando documentação com o javadoc”. A linha 2 // Programa de impressão de texto.

é um comentário que descreve o propósito do programa.

Erro comum de programação 2.1 Esquecer um dos delimitadores de um comentário tradicional no estilo Javadoc causa um erro de sintaxe. A sintaxe de uma linguagem de programação especifica as regras para criar programas apropriados nessa linguagem, assim como as regras de gramática de uma língua natural especificam a estrutura da frase. Um erro de sintaxe ocorre quando o compilador encontra o código que viola as regras da linguagem do Java (isto é, sua sintaxe). Nesse caso, o compilador emite uma mensagem de erro e impede o programa de compilar. Erros de sintaxe também são chamados erros de compilador, erros em tempo de compilação ou erros de compilação, porque o compilador detecta-os durante a fase de compilação.

Boa prática de programação 2.1 Algumas organizações exigem que todo programa comece com um comentário que informa o objetivo e o autor do programa, a data e a hora em que o programa foi modificado pela última vez.

Utilizando linhas em branco A linha 3 é uma linha em branco. As linhas em branco e caracteres de espaço tornam os programas mais fáceis de ler. Juntos, as linhas em branco, os espaços e as tabulações são conhecidos como espaço em branco. Espaços em branco são ignorados pelo compilador. Boa prática de programação 2.2 Utilize linhas e espaços em branco para aprimorar a legibilidade do programa.

Declarando uma classe A linha 4 public class Welcome1

inicia uma declaração de classe da classe Welcome1. Todo programa Java consiste em pelo menos uma classe que você (o programador) define. Essas são conhecidas como classes definidas pelo programador. A palavra-chave class introduz uma declaração de classe e é imediatamente seguida pelo nome de classe (Welcome1). Palavras-chave (às vezes chamadas de palavras reservadas) são reservadas para uso pelo Java e sempre escritas com todas as letras minúsculas. A lista completa de palavras-chave está no Apêndice C. Por convenção, os nomes de classes iniciam com uma letra maiúscula e apresentam a letra inicial de cada palavra que eles incluem em maiúscula (por exemplo, SampleClassName). O nome de uma classe é um identificador — uma série de caracteres que consiste em letras, dígitos, sublinhados ( _ ) e sinais de cifrão ($) que não iniciam com um dígito e não contêm espaços. Alguns identificadores válidos são Bemvindo1, $valor, _valor, m_campoDeEntrada1 e botao7. O nome 7button não é um identificador válido porque inicia com um dígito; e o nome input field não é um identificador válido porque contém um espaço. Normalmente, um identificador que não inicia com uma letra maiúscula não é um nome de classe. O Java faz distinção entre letras maiúsculas e minúsculas — letras maiúsculas e letras minúsculas são diferentes — assim, a1 e A1 são identificadores diferentes (mas ambos válidos).



2.2  Nosso primeiro programa Java: imprimindo uma linha de texto

31

Boa prática de programação 2.3 Por convenção, inicie o identificador de cada nome de classe com uma letra maiúscula e inicie cada palavra subsequente do identificador com uma letra maiúscula.

Erro comum de programação 2.2 O Java diferencia letras maiúsculas de minúsculas. O uso incorreto de letras maiúsculas e minúsculas para um identificador normalmente causa um erro de compilação.

Nos capítulos 2–7, cada classe que definimos inicia com a palavra-chave public. Por enquanto, simplesmente exigiremos essa palavra-chave. Para nosso aplicativo, o nome de arquivo é Welcome1.java. Você aprenderá mais sobre as classes public e não public no Capítulo 8.

Erro comum de programação 2.3 Um classe public deve ser colocada em um arquivo que tenha o mesmo nome da classe (tanto na ortografia como no uso de maiúsculas e minúsculas) mais a extensão .java; caso contrário, ocorre um erro de compilação. Por exemplo, a classe public Welcome deve ser colocada em um arquivo chamado Welcome.java.

Uma chave esquerda (na linha 5 desse programa), {, inicia o corpo de cada declaração de classe. Uma chave direita , }, correspondente (na linha 11) deve terminar cada declaração de classe. Observe que as linhas 6–10 estão recuadas. Esse recuo é uma das convenções de espaçamento mencionadas anteriormente.

Dica de prevenção de erro 2.1 Quando você digitar uma chave de abertura, ou chave esquerda, {, imediatamente digite a chave de fechamento, ou chave direita, }, e, então reposicione o cursor entre as chaves e dê um recuo para começar a digitação do corpo. Essa prática ajuda a evitar erros devidos à ausência das chaves. Muitos IDEs inserem os colchetes para você.

Boa prática de programação 2.4 Recue o corpo inteiro de cada declaração de classe por um “nível” entre a chave esquerda e a chave direita que delimitam o corpo da classe. Esse formato enfatiza a estrutura da declaração de classe e torna mais fácil sua leitura.

Boa prática de programação 2.5 Muitos IDEs inserem recuos ou indentações em todos os lugares certos. A tecla Tab também pode ser utilizada para criar recuos, mas as paradas de tabulação variam entre editores de textos. Recomendamos a utilização de três espaços para formar um nível de recuo.

Erro comum de programação 2.4 É um erro de sintaxe se chaves não ocorrerem em pares correspondentes.

Declarando um método A linha 6 // método principal inicia a execução do aplicativo Java

é um comentário de fim de linha indicando o propósito das linhas 7–10 do programa. A linha 7: public static void main( String[] args )

é o ponto de partida de cada aplicativo Java. Os parênteses depois do identificador main indicam que ele é um bloco de construção do programa chamado método. Declarações de classe Java normalmente contêm um ou mais métodos. Para um aplicativo Java, um dos métodos deve ser chamado main e deve ser definido como mostrado na linha 7; caso contrário, a JVM não executará o aplicativo. Os métodos realizam tarefas e podem retornar informações quando completam suas tarefas. A palavra-chave void indica que esse método não devolverá nenhuma informação. Mais tarde, veremos como um método pode retornar informações. Por enquanto, basta simular a primeira linha de main nos aplicativos Java. Na linha 7, a String[] args entre parênteses é uma parte requerida da declaração do método main. Discutiremos isso no Capítulo 7.

32

Capítulo 2  Introdução aos aplicativos Java

A chave esquerda, {, na linha 8 inicia o corpo da definição de método. Uma chave direita correspondente deve terminar o corpo da declaração do método (linha 10). Note que a linha 9 no corpo do método está recuada entre as chaves.

Boa prática de programação 2.6

Recue o corpo inteiro de cada declaração de método um “nível” entre as chaves que definem o corpo do método. Isso faz com que a estrutura do método se destaque, tornando a declaração do método mais fácil de ler.

Gerando saída com System.out.println A linha 9 System.out.println( "Welcome to Java Programming!" );

instrui o computador a realizar uma ação — a saber, imprimir a string de caracteres contida entre as aspas duplas (mas não as próprias aspas duplas). Uma string às vezes é chamada de string de caractere ou string literal. Os caracteres de espaço em branco em strings não são ignorados pelo compilador. As strings não podem distribuir várias linhas de código. O objeto System.out é conhecido como o objeto de saída padrão. Ele permite que aplicativos Java exibam strings na janela de comando a partir da qual o aplicativo de Java executa. Em versões recentes do Microsoft Windows, a janela de comando chama-se Prompt de Comando. No UNIX/Linux/Mac OS X, a janela de comando é chamada janela terminal ou shell. Muitos programadores chamam a janela de comando simplesmente de linha de comando. O método System.out.println exibe (ou imprime) uma linha de texto na janela de comando. A string entre parênteses na linha 9 é o argumento para o método. Quando System.out.println completa sua tarefa, ele posiciona o cursor de saída (o local em que o próximo caractere será exibido) no começo da linha seguinte na janela de comando. (Esse movimento do cursor é semelhante ao pressionamento da tecla Enter ao digitar em um editor de textos — o cursor aparece no começo da próxima linha do documento.) A linha 9 inteira, incluindo System.out.println, o argumento "Welcome to Java Programming!" entre parênteses e o ponto-e-vírgula (;), é uma instrução. A maioria das instruções acaba com um ponto-e-vírgula. Quando a instrução na linha 9 executa, ela exibe Welcome to Java Programming! na janela de comando. Em geral, um método contém uma ou várias instruções que realizam sua tarefa.

Erro comum de programação 2.5 Quando um ponto-e-vírgula é necessário para acabar uma instrução, omitir o ponto-e-vírgula é um erro de sintaxe.

Dica de prevenção de erro 2.2 Ao aprender a programar, às vezes é útil “quebrar” um programa funcional para você poder familiarizar-se com as mensagens de erro de sintaxe do compilador. Essas mensagens nem sempre declaram o problema exato no código. Quando encontrar essas mensagens de erro de sintaxe, você terá uma ideia do que causou o erro. [Tente remover um ponto-e-vírgula ou chave do programa da Figura 2.1 e, então, recompile o programa para ver as mensagens de erro geradas pela omissão.]

42

Dica de prevenção de erro 2.3 Quando o compilador informa um erro de sintaxe, o erro pode não estar na linha indicada pela mensagem de erro. Primeiro, verifique a linha em que o erro foi informado. Se essa linha não contiver erros de sintaxe, verifique as várias linhas anteriores.

Utilizando comentários de fim de linha em chaves de fechamento para melhorar a legibilidade Incluímos um comentário de fim de linha depois de uma chave de fechamento que termina uma declaração de método e depois de uma chave de fechamento que termina uma declaração de classe. Por exemplo, a linha 10 } // fim do método main

indica a chave de fechamento do método main, e a linha 11 } // fim da classe Welcome1

indica a chave de fechamento da classe Welcome1. Cada comentário indica o método ou classe que a chave direita termina.

Boa prática de programação 2.7

Para aprimorar a legibilidade de programa, coloque depois da chave de fechamento do corpo de uma declaração de método ou de classe um comentário que indica a declaração de método ou de classe à qual a chave pertence.



2.2  Nosso primeiro programa Java: imprimindo uma linha de texto

33

Compilando e executando seu primeiro aplicativo Java Estamos agora prontos para compilar e executar nosso programa. Supomos que você esteja usando o Java SE Development Kit ( JDK) 6 Update 10 da Sun Microsystems, não um IDE. Nossos Java Resource Centers em www.deitel.com/ResourceCenters.html fornecem links para tutoriais que o ajudam a começar a usar várias ferramentas de desenvolvimento Java populares, incluindo NetBeans™ e Eclipse™. Também postamos vídeos NetBeans e Eclipse em www.deitel.com/books/jhtp8/ para ajudá-lo a começar a usar esses IDEs populares. Para preparar-se para compilar o programa, abra uma janela de comando e vá para o diretório onde o programa está armazenado. Muitos sistemas operacionais utilizam o comando cd para mudar de diretório. Por exemplo, cd c:\examples\ch02\fig02_01

muda para o diretório fig02_01 no Windows. O comando: cd ~/examples/ch02/fig02_01

muda para o diretório fig02_01 no UNIX/Linux/Max OS X. Para compilar o programa, digite: javac Welcome1.java

Se o programa não contiver nenhum erro de sintaxe, o comando anterior cria um novo arquivo chamado Welcome1.class (conhecido como o arquivo de classe para Welcome1) que contém os bytecodes Java independentes de plataforma que representam nosso aplicativo. Quando utilizamos o comando java para executar o aplicativo em uma dada plataforma, esses bytecodes serão traduzidos pela JVM em instruções que são entendidas pelo sistema operacional subjacente.

Dica de prevenção de erro 2.4 Ao tentar compilar um programa, se receber uma mensagem como “bad command or filename,” “javac: command not found” or “‘javac’ is not recognized as an internal or external command, operable program or batch file”, sua instalação do software Java não foi completada corretamente. No JDK, isso é um sinal de que a variável de ambiente PATH do sistema não foi configurada corretamente. Revise cuidadosamente as instruções de instalação na Seção “Antes de você começar” deste livro. Em alguns sistemas, depois de corrigir o PATH, é necessário reinicializar o computador ou abrir uma nova janela de comando para efetuar as configurações.

Dica de prevenção de erro 2.5 Toda mensagem de erro de sintaxe contém o nome do arquivo e número da linha em que o erro ocorreu. Por exemplo, Welcome1. java:6 indica que um erro ocorreu no arquivo Welcome1.java na linha 6. O restante da mensagem de erro fornece as informações

sobre o erro de sintaxe.

Dica de prevenção de erro 2.6 A mensagem de erro do compilador “class Welcome1 is public, should be declared in a file named Welcome1.java” indica que o nome de arquivo não corresponde exatamente ao nome da classe public no arquivo ou que você digitou o nome de classe incorretamente ao compilar a classe.

A Figura 2.2 mostra o programa da Figura 2.1 executando em uma janela Prompt de Comando do Microsoft® Windows Vista® . Para executar o programa, digite java Welcome1. Isso carrega a JVM, que carrega o arquivo “.class” para a classe Welcome1. Observe que a extensão do nome de arquivo “.class” é omitida do comando precedente; caso contrário a JVM não executará o programa. A JVM chama o método main. Em seguida, a instrução na linha 9 de main exibe "Welcome to Java Programming!" [Nota: muitos ambientes mostram prompts de comando com fundos pretos e texto na cor branca. Ajustamos essas configurações no nosso ambiente para tornar nossas capturas de tela mais legíveis.] Você digita este comando para executar o aplicativo O programa então imprime na tela Welcome to Java Programming!

Figura 2.2  |  Executando Welcome1 a partir do Prompt de Comando.

34

Capítulo 2

Introdução aos aplicativos Java

Dica de prevenção de erro 2.7 Ao tentar executar um programa Java, se receber uma mensagem como “Exception in thread "main" java.lang.NoClassDsua variável de ambiente CLASSPATH não foi configurada corretamente. Revise cuidadosamente as instruções de instalação na Seção “Antes de você começar” deste livro. Em alguns sistemas, talvez seja necessário reinicializar seu computador ou abrir uma nova janela de comando depois de configurar a CLASSPATH.

efFoundError: Welcome1”,

2.3 Modificando nosso primeiro programa Java Nesta seção, modificamos o exemplo na Figura 2.1 para imprimir texto em uma linha utilizando várias instruções e imprimir texto em várias linhas utilizando uma única instrução.

Exibindo uma linha de texto com múltiplas instruções Welcome to Java Programming! pode ser exibido de várias maneiras. A classe Welcome2, mostrada na Figura 2.3, utiliza duas instruções para produzir a mesma saída mostrada na Figura 2.1. A partir desse ponto em diante, destacamos os novos e principais recursos em cada listagem de código, como mostrado nas linhas 9–10 desse programa. O programa é parecido com Figura 2.1, portanto, aqui discutiremos somente as alterações. A linha 2 // Imprimindo uma linha de texto com múltiplas instruções.

é um comentário de fim de linha declarando o propósito desse programa. A linha 4 inicia a declaração da classe Welcome2. As linhas 9–10 do método main: System.out.print( "Welcome to " ); System.out.println( "Java Programming!" );

exibem uma linha de texto na janela de comando. A primeira instrução utiliza o método print de System.out para exibir uma string. Diferente de println, depois de exibir seu argumento, print não posiciona o cursor de saída no início da próxima linha na janela de comando — o próximo caractere que o programa exibe aparecerá imediatamente depois do último caractere que print exibe. Portanto, a linha 10 posiciona o primeiro caractere no seu argumento (a letra “J”) imediatamente depois do último caractere que a linha 9 exibe (o caractere de espaço em branco antes da aspa dupla de fechamento da string). Cada instrução print ou println retoma a exibição dos caracteres a partir de onde a última instrução print ou println parou de exibir os caracteres. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura 2.3: Welcome2.java // Imprimindo uma linha de texto com múltiplas instruções. public class Welcome2 { // método principal inicia a execução do aplicativo Java public static void main( String[] args ) { System.out.print( "Welcome to " ); System.out.println( "Java Programming!" ); } // fim do método main } // fim da classe Welcome2

Welcome to Java Programming!

Figura 2.3 | Imprimindo uma linha de texto com múltiplas instruções.

Exibindo múltiplas linhas de texto com uma única instrução Uma única instrução pode exibir múltiplas linhas utilizando caracteres de nova linha, os quais indicam aos métodos print e println de System.out quando posicionar o cursor de saída no começo da próxima linha na janela de comando. Como ocorre com linhas em branco, caracteres de espaço em branco e caracteres de tabulação, os caracteres de nova linha são caracteres de espaço em branco. A Figura 2.4 exibe quatro linhas de texto, utilizando caracteres de nova linha para determinar quando iniciar cada nova linha. A maior parte do programa é idêntica àquele das Figura 2.1 e Figura 2.3, portanto, aqui discutiremos somente as alterações. 1 2 3

// Figura 2.4: Welcome3.java // Imprimindo múltiplas linhas de texto com uma única instrução.

2.4 Exibindo texto com printf 4 5 6 7 8 9 10 11

35

public class Welcome3 { // método principal inicia a execução do aplicativo Java public static void main( String[] args ) { System.out.println( "Welcome\n to \n Java\n Programming!" ); } // fim do método main } // fim da classe Welcome3

Welcome to Java Programming!

Figura 2.4 | Imprimindo múltiplas linhas de texto com uma única instrução.

A linha 2 // Imprimindo múltiplas linhas de texto com uma única instrução.

é um comentário que declara o objetivo do programa. A linha 4 inicia a declaração da classe Welcome3. A linha 9 System.out.println( "Welcome\nto\nJava\nProgramming!" );

exibe quatro linhas separadas de texto na janela de comando. Normalmente, os caracteres em uma string são exibidos exatamente como aparecem entre as aspas duplas. Observe, porém, que os dois caracteres \ e n (repetidos três vezes na instrução) não aparecem na tela. A barra invertida (\) é chamada caractere de escape. Ela indica para os métodos print e println de System.out que a saída de um “caractere especial” deve ser gerada. Quando aparece uma barra invertida em uma string de caracteres, o Java combina o próximo caractere com as barras invertidas para formar uma sequência de escape. A sequência de escape \n representa o caractere de nova linha. Quando um caractere de nova linha aparece em uma string sendo enviada para saída com System.out, o caractere de nova linha faz o cursor de saída na tela mover-se para o começo da próxima linha na janela de comando. A Figura 2.5 lista várias sequências de escape comuns e descreve como elas afetam a exibição de caracteres na janela de comando. Para obter a lista completa de sequências de escape, visite java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.10.6. Sequência de escape

Descrição

\n

Nova linha. Posiciona o cursor de tela no início da próxima linha. Tabulação horizontal. Move o cursor de tela para a próxima parada de tabulação. Retorno de carro. Posiciona o cursor da tela no início da linha atual — não avança para a próxima linha. Qualquer saída de caracteres depois do retorno de carro sobrescreve a saída de caracteres anteriormente gerados na linha atual. Barras invertidas. Utilizada para imprimir um caractere de barra invertida. Aspas duplas. Utilizada para imprimir um caractere de aspas duplas. Por exemplo,

\t \r

\\ \"

System.out.println( "\"in quotes\"" );

exibe "in quotes"

Figura 2.5 | Algumas sequências de escape comuns.

2.4 Exibindo texto com printf O método System.out.printf (f significa “formatted”) exibe os dados formatados. A Figura 2.6 utiliza esse método para gerar a saída das strings "Welcome to" e "Java Programming!". As linhas 9–10: System.out.printf( "%s\n%s\n", "Welcome to", "Java Programming!" );

36

Capítulo 2

Introdução aos aplicativos Java

chamam o método System.out.printf para exibir a saída do programa. A chamada de método especifica três argumentos. Quando um método exige múltiplos argumentos, estes são colocados em uma lista separada por vírgulas. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura 2.6: Welcome4.java // Exibindo múltiplas linhas com o método System.out.printf. public class Welcome4 { // método principal inicia a execução do aplicativo Java public static void main( String[] args ) { System.out.printf( "%s\n%s\n", "Welcome to", "Java Programming!" ); } // fim do método main } // fim da classe Welcome4

Welcome to Java Programming!

Figura 2.6 | Exibindo múltiplas linhas com o método System.out.printf.

Boa prática de programação 2.8 Coloque um espaço depois de cada vírgula (,) em uma lista de argumentos para tornar os programas mais legíveis.

As linhas de 9–10 representam somente uma instrução. O Java permite que instruções grandes sejam divididas em muitas linhas. Recuamos a linha 10 para indicar que é uma continuação da linha 9. Observe que você não pode dividir uma instrução no meio de um identificador ou no meio de uma string.

Erro comum de programação 2.6 Dividir uma instrução no meio de um identificador ou de uma string é um erro de sintaxe.

O primeiro argumento do método printf é uma string de formato que pode consistir em texto fixo e especificadores de formato. A saída do texto fixo é gerada por printf exatamente como seria gerada por print ou println. Cada especificador de formato é um marcador de lugar para um valor e especifica o tipo da saída de dados. Especificadores de formato também podem incluir informações opcionais de formatação. Especificadores de formato iniciam com um sinal de porcentagem (%) e são seguidos por um caractere que representa o tipo de dados. Por exemplo, o especificador de formato %s é um marcador de lugar para uma string. A string de formato na linha 9 especifica que printf deve gerar a saída de duas strings, cada uma seguida por um caractere de nova linha. Na primeira posição do especificador de formato, printf substitui o valor do primeiro argumento depois da string de formato. Em cada posição subsequente, printf substitui o valor do próximo argumento na lista de argumentos. Portanto, esse exemplo coloca "Welcome to" no lugar do primeiro %s e "Java Programming!" no lugar do segundo %s. A saída mostra que duas linhas de texto são exibidas. Introduzimos vários recursos de formatação uma vez que eles são necessários nos nossos exemplos. O apêndice G apresenta os detalhes da formatação da saída com printf.

2.5 Outro aplicativo: somando inteiros Nosso próximo aplicativo lê (ou insere) dois inteiros (números integrais, como –22, 7, 0 e 1024) digitados por um usuário no teclado, calcula a soma dos valores e exibe o resultado. Esse programa deve manter um registro dos números fornecidos pelo usuário para o cálculo mais tarde no programa. Os programas lembram-se dos números e outros dados na memória do computador e acessam esses dados por meio de elementos de programa chamados variáveis. O programa da Figura 2.7 demonstra esses conceitos. Na saída de exemplo, usamos o texto em negrito para identificar a entrada do usuário (isto é, 45 e 72). 1 2 3 4 5

// Figura 2.7: Addition.java // Programa de adição que exibe a soma de dois números. import java.util.Scanner; // programa utiliza a classe Scanner public class Addition



2.5  Outro aplicativo: somando inteiros

6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

37

{ // método principal inicia a execução do aplicativo Java public static void main( String[] args ) { // cria um Scanner para obter entrada da janela de comando Scanner input = new Scanner( System.in ); int number1; // primeiro número a adicionar int number2; // segundo número a adicionar int sum; // soma de number1 e number2 System.out.print( "Enter first integer: " ); // prompt number1 = input.nextInt(); // lê primeiro o número fornecido pelo usuário System.out.print( "Enter second integer: " ); // prompt number2 = input.nextInt(); // lê o segundo número fornecido pelo usuário sum = number1 + number2; // soma os números, depois armazena o total em sum System.out.printf( "Sum is %d\n", sum ); // exibe a soma } // fim do método main } // fim da classe Addition

Enter first integer: 45 Enter second integer: 72 Sum is 117

Figura 2.7  |  O programa de adição que exibe a soma de dois números.

As linhas 1–2 // Figura 2.7: Addition.java // Programa de adição que exibe a soma de dois números.

declaram o número da figura, o nome do arquivo e o propósito do programa. A linha 3 import java.util.Scanner; // programa utiliza a classe Scanner

é uma declaração import que ajuda o compilador a localizar uma classe utilizada nesse programa. Um dos pontos fortes do Java é seu rico conjunto de classes predefinidas que você pode reutilizar em vez de “reinventar a roda”. Essas classes são agrupadas em pacotes — chamados de grupos de classes relacionadas — e, coletivamente, são chamadas de biblioteca de classes Java, ou Java Application Programming Interface (Java API). Utilize declarações import para identificar as classes predefinidas utilizadas em um programa Java. A declaração import na linha 3 indica que esse exemplo usa a classe Scanner predefinida do Java (discutida a seguir) do pacote java.util.

Erro comum de programação 2.7

Todas as declarações import devem aparecer antes da primeira declaração da classe no arquivo. Colocar uma declaração import dentro do corpo de uma declaração de classe ou depois de uma declaração de classe é um erro de sintaxe.

Dica de prevenção de erro 2.8

Em geral, esquecer-se de incluir uma declaração import para uma classe utilizada no seu programa resulta em um erro de compilação contendo uma mensagem como “cannot find symbol”. Quando isso ocorre, verifique se você forneceu as declarações import adequadas e que os nomes nas declarações import estão escritos corretamente, incluindo a utilização adequada de letras maiúsculas e minúsculas.

A linha 5 public class Addition

inicia a declaração da classe Addition. O nome de arquivo para essa classe public deve ser Addition.java. Lembre-se de que o corpo de cada declaração de classe inicia com uma chave esquerda de abertura (linha 6) e termina com uma chave direita de fechamento (linha 27). O aplicativo inicia a execução com o método main (linhas 8–26). A chave esquerda (linha 9) marca o início do corpo de main e a correspondente direita (linha 26) marca o final do corpo de main. Observe que o método main está recuado um nível no corpo da classe Addition e que o código no corpo de main está recuado um outro nível para legibilidade. A linha 11 Scanner input = new Scanner( System.in );

38

Capítulo 2  Introdução aos aplicativos Java

é uma instrução de declaração de variável que especifica o nome (input) e o tipo (Scanner) de uma variável que é usada nesse programa. Uma variável é uma posição na memória do computador onde um valor pode ser armazenado para uso posterior em um programa. Todas as variáveis Java devem ser declaradas com um nome e um tipo antes que elas possam ser utilizadas. O nome de uma variável permite que o programa acesse o valor da variável na memória. O nome de uma variável pode ser qualquer identificador válido. (Os requisitos de atribuição de nomes de identificadores são fornecidos na Seção 2.2.) O tipo de uma variável especifica o tipo de informações armazenado nessa posição na memória. Como ocorre com outras instruções, as instruções de declaração terminam com um ponto-e-vírgula (;). A declaração na linha 11 especifica que a variável nomeada input seja do tipo Scanner. Um Scanner permite a um programa ler os dados (por exemplo, números) para utilização em um programa. Os dados podem ser provenientes de várias origens, como os digitados pelo usuário ou um arquivo do disco. Antes de utilizar um Scanner, você deve criá-lo e especificar a origem dos dados. O sinal de igual (=) na linha 11 indica que a variável Scanner input deve ser inicializada (isto é, preparada para utilização no programa) na sua declaração com o resultado da expressão new Scanner(System.in) à direita do sinal de igual. Essa expressão utiliza a palavra-chave new para criar um objeto Scanner que lê caracteres digitados pelo usuário no teclado. O objeto de entrada padrão, System.in, permite que aplicativos leiam bytes de informações digitadas pelo usuário. O objeto Scanner traduz esses bytes em tipos (como ints) que podem ser utilizados em um programa. As instruções de declaração de variável nas linhas 13–15: int number1; // primeiro número a adicionar int number2; // segundo número a adicionar int sum; // soma de number1 e number2

declaram que as variáveis number1, number2 e sum armazenam dados do tipo int — elas podem armazenar valores inteiros (números inteiros como 72, –1127 e 0). Essas variáveis ainda não são inicializadas. O intervalo de valores para um int é –2.147.483.648 a +2.147.483.647. [Nota: valores int reais podem não conter vírgulas.] A seguir, discutiremos os tipos float e double, para armazenar números reais, e o tipo char, para armazenar dados de caracteres. Os números reais contêm pontos decimais, como 3.4, 0.0 e –11.19. Variáveis do tipo char representam caracteres individuais, como uma letra maiúscula (por exemplo, A), um dígito (por exemplo, 7), um caractere especial (por exemplo, * ou %) ou uma sequência de escape (por exemplo, o caractere de nova linha, \n). Os tipos int, float, double e char são chamados de tipos primitivos. Os nomes dos tipos primitivos são palavras-chave e devem aparecer em letras minúsculas. O Apêndice D resume as características dos oito tipos primitivos (boolean, byte, char, short, int, long, float e double). Diversas variáveis do mesmo tipo podem ser declaradas em uma declaração com os nomes de variável separados por vírgulas (isto é, uma lista separada por vírgulas de nomes de variáveis). Por exemplo, as linhas 13–15 também podem ser escritas como uma única instrução: int number1, // primeiro número a adicionar number2, // segundo número a adicionar sum; // soma de number1 e number2

Boa prática de programação 2.9

Declare cada variável em uma linha separada. Esse formato permite que um comentário descritivo seja facilmente inserido ao lado de cada declaração.

Boa prática de programação 2.10

Escolher nomes de variáveis significativos ajuda um programa ser autodocumentado (isto é, pode-se entender o programa simplesmente lendo-o em vez de ler manuais ou visualizar um número excessivo de comentários).

Boa prática de programação 2.11

Por convenção, identificadores de nomes de variáveis iniciam com uma letra minúscula e cada palavra no nome depois da primeira palavra inicia com uma letra maiúscula. Por exemplo, o identificador de nome da variável firstNumber inicia a sua segunda palavra, Number, com uma letra N maiúscula.

A linha 17 System.out.print( "Enter first integer: " ); // prompt

utiliza System.out.print para exibir a mensagem "Enter first integer:". Essa mensagem é chamada prompt porque direciona o usuário para uma ação específica. Utilizamos o método print aqui em vez de println para que a entrada do usuário apareça na mesma linha que o prompt. Lembre-se de que na Seção 2.2 esses identificadores que iniciam com letras maiúsculas representam em geral nomes de classe. Portanto, System é uma classe. A classe System faz parte do pacote java.lang. Observe que a classe System não é importada com uma declaração import no começo do programa.



2.5  Outro aplicativo: somando inteiros

39

Observação de engenharia de software 2.1

Por padrão, o pacote java.lang é importado em cada programa Java; portanto, java.lang é o único na Java API que não requer uma declaração import-.

A linha 18 number1 = input.nextInt(); // lê o primeiro número fornecido pelo usuário

utiliza o método nextInt do valor de input do objeto Scanner para obter um inteiro digitado pelo usuário. Nesse momento o programa espera que o usuário digite o número e pressione a tecla Enter para submeter o número para o programa. Nosso programa assume que o usuário insere um valor válido de inteiro. Se não for válido, um erro de lógica em tempo de execução ocorrerá e o programa terminará. O Capítulo 11 discute como tornar seus programas mais robustos permitindo que tratem esses erros. Isso também é conhecido como tornar seu programa tolerante a falhas. Na linha 18, o resultado da chamada ao método nextInt (um valor int) é colocado na variável number1 utilizando o operador de atribuição, =. A instrução exibe “number1 gets the value of input.nextInt().” O operador = é chamado de operador binário, porque tem dois operandos — number1 e o resultado da chamada de método input.nextInt(). Essa instrução inteira é chamada instrução de atribuição porque atribui um valor a uma variável. Tudo à direita do operador de atribuição, =, sempre é avaliado antes de a atribuição ser realizada.

Boa prática de programação 2.12 Colocar espaços em qualquer um dos lados de um operador binário faz com que eles se destaquem e torna o programa mais legível.

A linha 20 System.out.print( "Enter second integer: " ); // prompt

pede para o usuário inserir o segundo inteiro. A linha 21 number2 = input.nextInt(); // lê o segundo número fornecido pelo usuário

lê o segundo inteiro e o atribui à variável number2. A linha 23 sum = number1 + number2; // soma os números e, então, armazena o total em sum

é uma instrução de atribuição que calcula a soma das variáveis number1 e number2 e, então, atribui o resultado à variável sum utilizando operador de atribuição, =. A instrução é lida como “sum obtém o valor de number1 + number2”. Em geral, os cálculos são realizados em instruções de atribuição. Ao encontrar a operação de adição, o programa usa os valores armazenados nas variáveis number1 e number2 para fazer o cálculo. Na instrução anterior, o operador de adição é um operador binário — seus dois operandos são as variáveis number1 e number2. As partes das instruções que contêm cálculos são chamadas de expressões. De fato, uma expressão é qualquer parte de uma instrução que tem um valor associado com ela. Por exemplo, o valor da expressão number1 + number2 é a soma dos números. Da mesma forma, o valor da expressão input.nextInt() é um inteiro digitado pelo usuário. Depois que o cálculo foi realizado, a linha 25 System.out.printf( "Sum is %d\n", sum ); // exibe a soma

utiliza o método System.out.printf para exibir a sum. O especificador de formato %d é um marcador de lugar para um valor int (nesse caso o valor de sum) — a letra d significa “inteiro decimal”. Observe que além do especificador de formato %d, todos os caracteres restantes na string de formato são texto fixo. Portanto, o método printf exibe "Sum is ", seguido pelo valor de sum (na posição do especificador de formato %d) e por uma nova linha. Observe que os cálculos também podem ser realizados dentro de instruções printf. Poderíamos ter combinado as instruções nas linhas 23 e 25 na instrução: System.out.printf( "Sum is %d\n", ( number1 + number2 ) );

Os parênteses em torno da expressão number1 + number2 não são necessários — são incluídos para enfatizar que o valor da saída da expressão inteira é gerado na posição do especificador de formato %d.

Documentação da Java API Para cada nova classe da Java API que usamos, indicamos o pacote em que ela está localizada. Essas informações ajudam a localizar descrições de cada pacote e classe na documentação da Java API. Uma versão baseada na Web dessa documentação pode ser obtida em:

40

Capítulo 2

Introdução aos aplicativos Java

java.sun.com/javase/6/docs/api/

Você pode fazer download dessa documentação em: java.sun.com/javase/downloads

O Apêndice E mostra como utilizar essa documentação.

2.6 Conceitos de memória Os nomes de variável como number1, number2 e sum realmente correspondem às posições na memória do computador. Cada variável tem um nome, um tipo, um tamanho (em bytes) e um valor. No programa de adição da Figura 2.7, quando a instrução seguinte (linha 18) executa: number1 = input.nextInt(); // lê o primeiro número fornecido pelo usuário

o número digitado pelo usuário é colocado em uma localização de memória correspondente ao nome number1. Suponha que o usuário insira 45. O computador coloca esse valor do tipo inteiro na localização number1 (Figura 2.8), substituindo o valor anterior (se houver algum) nessa localização. O valor anterior é perdido. number1

45

Figura 2.8 | Posição da memória mostrando o nome e valor da variável number1.

Quando a instrução (linha 21), number2 = input.nextInt(); // lê o segundo número fornecido pelo usuário

é executada, suponha que o usuário insira 72. O computador coloca esse valor do tipo inteiro na localização number2. A memória agora aparece como mostrado na Figura 2.9. number1

45

number2

72

Figura 2.9 | As posições de memória depois de armazenar os valores para number1 e number2.

Depois que o programa da Figura 2.7 obtém os valores para number1 e number2, ele soma os valores e coloca o total na variável sum. A instrução (linha 23), sum = number1 + number2; // soma os números, depois armazena o total em sum

realiza a soma e então substitui qualquer valor anterior de sum. Depois que a variável sum foi calculada, a memória aparece conforme mostrado na Figura 2.10. Observe que os valores de number1 e number2 aparecem exatamente como apareceram antes de serem utilizados no cálculo de sum. Esses valores foram utilizados, mas não destruídos, enquanto o computador realizou o cálculo. Portanto, quando um valor é lido de uma posição da memória, o processo é do tipo não destrutivo. number1

45

number2

72

sum

117

Figura 2.10 | As posições da memória depois de armazenar a soma de number1 e number2.

2.7 Aritmética

41

2.7 Aritmética A maioria dos programas realiza cálculos aritméticos. Os operadores aritméticos são resumidos na Figura 2.11. Note o uso de vários símbolos especiais não utilizados na álgebra. O asterisco (*) indica multiplicação, e o sinal de porcentagem (%) é o operador de resto (módulo), que discutiremos a seguir. Os operadores aritméticos na Figura 2.11 são operadores binários porque cada um deles opera em dois operandos. Por exemplo, a expressão f + 7 contém o operador binário + e os dois operandos f e 7. Operação Java

Operador

Expressão algébrica

Expressão Java

Adição

+

f+7

f + 7

Subtração



p–c

p – c

Multiplicação

*

bm

b * m

Divisão

/

x / y

Resto

%

x/y ou –x ou x ÷ y y r mod s

r % s

Figura 2.11 | Operadores aritméticos.

A divisão de inteiros produz um quociente do tipo inteiro; por exemplo, a expressão 7 / 4 é avaliada como 1 e a expressão 17 / 5 é avaliada como 3. Qualquer parte fracionária na divisão de inteiros é simplesmente descartada (isto é, truncada) — nenhum arredondamento ocorre. O Java fornece o operador de módulo, %, que fornece o resto depois da divisão. A expressão x % y produz o restante depois que x é dividido por y. Portanto, 7 % 4 produz 3 e 17 % 5 produz 2. Esse operador é mais comumente utilizado com operandos inteiros, mas também pode ser utilizado com outros tipos de aritmética. Nos exercícios deste capítulo e nos capítulos posteriores, vamos examinar vários aplicativos interessantes do operador módulo, como determinar se um número é um múltiplo de outro.

Expressões aritméticas na forma de linha reta As expressões aritméticas em Java devem ser escritas na forma de linha reta para facilitar a inserção de programas no computador. Portanto, as expressões como “a dividido por b” devem ser escritas como a / b, de modo que todas as constantes, variáveis e operadores apareçam em uma linha reta. A seguinte notação algébrica geralmente não é aceitável para compiladores: a b

Parênteses para agrupar subexpressões Os parênteses são utilizados para agrupar termos em expressões Java da mesma maneira como em expressões algébricas. Por exemplo, para multiplicar a vezes a quantidade b + c escrevemos: a * ( b + c )

Se uma expressão contiver parênteses aninhados, como: ( ( a + b ) * c )

a expressão no conjunto de parênteses mais interno (a + b nesse caso) é avaliada primeiro.

Regras de precedência de operadores O Java aplica os operadores em expressões aritméticas em uma sequência precisa determinada pelas seguintes regras de precedência de operadores, que são geralmente as mesmas seguidas em álgebra (Figura 2.12): 1. Operações de multiplicação, divisão e módulo são aplicadas primeiro. Se uma expressão contiver várias dessas operações, elas serão aplicadas da esquerda para a direita. Os operadores de multiplicação, divisão e módulo têm o mesmo nível de precedência. 2. As operações de adição e subtração são aplicadas em seguida. Se uma expressão contiver várias dessas operações, os operadores serão aplicados da esquerda para a direita. Os operadores de adição e subtração têm o mesmo nível de precedência. Essas regras permitem ao Java aplicar operadores na ordem correta.1 Quando dizemos que os operadores são aplicados da esquerda para a direita, estamos nos referindo à sua associatividade. Alguns operadores associam da direita para a esquerda. A Figura 2.12 resume essas regras de precedência de operador. Um gráfico completo de precedência está incluído no Apêndice A. 1

Usamos exemplos simples para explicar a ordem da avaliação das expressões. Questões sutis que ocorrem em expressões mais complexas serão vistas mais adiante no livro. Para mais informações sobre a ordem de avaliação, veja o Capítulo 15 da Java™ Language Specification (java.sun.com/docs/books/jls/).

42

Capítulo 2  Introdução aos aplicativos Java

Operador

Operação

Ordem de avaliação (precedência)

* / %

Multiplicação Divisão Resto

Avaliado primeiro. Se houver vários operadores desse tipo, eles são avaliados da esquerda para a direita.

+ –

Adição Subtração

Avaliado em seguida. Se houver vários operadores desse tipo, eles são avaliados da esquerda para a direita.

=

Atribuição

Avaliado por último.

Figura 2.12  |  Precedência de operadores aritméticos.

Exemplo de expressões algébricas e Java Agora vamos considerar várias expressões à luz das regras de precedência de operador. Cada exemplo lista uma expressão algébrica e seu equivalente Java. O seguinte é um exemplo de uma média aritmética de cinco termos: Álgebra:

m= a+b+c+d+e 5

Java:

m = ( a + b + c + d + e ) / 5;

Os parênteses são exigidos porque a divisão tem precedência mais alta que a adição. A soma total (a + b + c + d + e) será dividida por 5. Se os parênteses são omitidos erroneamente, obtemos a + b + c + d + e / 5, que é avaliado como a+b+c+d+ e 5

Eis um exemplo da equação de uma linha reta: Álgebra: Java:

y = mx + b y = m * x + b;

Nenhum parêntese é requerido. O operador de multiplicação é aplicada primeiro porque a multiplicação tem uma precedência mais alta que a adição. A atribuição ocorre por último porque ela tem uma precedência mais baixa que a multiplicação ou adição. O seguinte exemplo contém operações de resto (%), multiplicação, divisão, adição e subtração: Álgebra: Java:

z = pr%q + w/x – y z = p *

r

%

1

6

q

+

2

4

w

/ 3

x



y;

5

Os números dentro de círculos sob a instrução indicam a ordem em que o Java aplica os operadores. As operações de multiplicação (*), resto (%) e divisão (/) são avaliadas primeiro na ordem da esquerda para a direita (isto é, elas associam-se da esquerda para a direita), porque têm precedência mais alta que adição (+) e subtração (–). As operações + e – são avaliadas a seguir. Essas operações também são aplicadas da esquerda para a direita. A operação (=) de atribuição é avaliada por último.

Avaliação de um polinômio de segundo grau Para entender melhor as regras de precedência de operadores, considere a avaliação de uma expressão de atribuição que inclui um polinômio de segundo grau y = ax2 + bx + c: y

= 6

a

* 1

x

* 2

x

+ 4

b

* 3

x

+

c;

5

Nessa instrução, as operações de multiplicação são avaliadas primeiro na ordem da esquerda para a direita (isto é, elas são associadas da esquerda para a direita), porque têm uma precedência mais alta que a adição. As operações de adição são avaliadas em seguida e são aplicadas da esquerda para a direita. Não há nenhum operador aritmético para exponenciação em Java, portanto x2 é representado como x * x. A Seção 5.4 demonstra uma alternativa para realizar a exponenciação. Suponha que a, b, c e x no polinômio de segundo grau anterior seja inicializado (valores dados) como a seguir: a = 2, b = 3, c = 7 e x = 5. A Figura 2.13 ilustra a ordem em que os operadores são aplicados.

2.8 Tomada de decisão: operadores de igualdade e operadores relacionais Passo 1

43

y = 2 * 5 * 5 + 3 * 5 + 7; (Multiplicação mais à esquerda) 2 * 5 é 10

Passo 2

y = 10 * 5 + 3 * 5 + 7;

(Multiplicação mais à esquerda)

10 * 5 é 50 Passo 3 y = 50 + 3 * 5 + 7;

(Multiplicação antes da adição)

3 * 5 é 15 Passo 4

(Adição mais à esquerda)

y = 50 + 15 + 7; 50 + 15 é 65

Passo 5

(Última adição)

y = 65 + 7; 65 + 7 é 72 Passo 6

(Última operação — coloca 72 em y)

y = 72

Figura 2.13 | Ordem em que um polinômio de segundo grau é avaliado.

Como na álgebra é aceitável colocar parênteses redundantes (parênteses desnecessários) em uma expressão para tornar a expressão mais clara. Por exemplo, a instrução precedente poderia ser posta entre parênteses como mostrado a seguir: y = ( a * x * x ) + ( b * x ) + c;

Boa prática de programação 2.13 Utilizar parênteses redundantes em expressões aritméticas complexas pode torná-las mais fáceis de ler.

2.8 Tomada de decisão: operadores de igualdade e operadores relacionais Uma condição é uma expressão que pode ser true ou false. Esta seção apresenta a instrução de seleção if do Java que permite a um programa tomar uma decisão com base no valor de uma condição. Por exemplo, a condição “nota é maior ou igual a 60” determina se um aluno passou em um teste. Se a condição em uma estrutura if for verdadeira, o corpo da estrutura if é executada. Se a condição for falsa, o corpo não é executado. Veremos um exemplo brevemente. As condições nas instruções if podem ser formadas utilizando os operadores de igualdade (== e !=) e operadores relacionais (>, = e =

x >= y

x é maior que ou igual a y



= 1000

Figura 2.15  |  Compare números inteiros usando instruções if, operadores relacionais e operadores de igualdade (Parte 2 de 2).

A declaração da classe Comparison inicia na linha 6: public class Comparison

O método main da classe (linhas 9–40) inicia a execução do programa. A linha 12 Scanner input = new Scanner( System.in );

declara a variável Scanner input e lhe atribui um Scanner que insere dados a partir da entrada padrão (isto é, o teclado). As linhas 14–15 int number1; // primeiro número a comparar int number2; // segundo número a comparar

declaram as variáveis int utilizadas para armazenar a entrada dos valores digitados pelo usuário. As linhas 17–18 System.out.print( "Enter first integer: " ); // prompt number1 = input.nextInt(); // lê o primeiro número fornecido pelo usuário

solicitam que o usuário digite o primeiro inteiro e insira o valor, respectivamente. O valor de entrada é armazenado na variável number1. As linhas 20–21 System.out.print( "Enter second integer: " ); // prompt number2 = input.nextInt(); // lê o segundo número fornecido pelo usuário

solicitam que o usuário digite o segundo inteiro e insira o valor, respectivamente. O valor de entrada é armazenado na variável number2. As linhas 23–24 if ( number1 == number2 ) System.out.printf( "%d == %d\n", number1, number2 );

comparam os valores de number1 e number2 para determinar se eles são iguais. Uma estrutura if sempre inicia com a palavra-chave if, seguida por uma condição entre parênteses. Uma instrução if espera uma instrução em seu corpo, mas pode conter múltiplas instruções se elas estiverem entre parênteses ({}). O recuo da instrução no corpo mostrado aqui não é exigido, mas melhora a legibilidade do programa enfatizando que a instrução na linha 24 faz parte da estrutura if que inicia na linha 23. A linha 24 executa somente se os números armazenados nas variáveis number1 e number2 forem iguais (isto é, se a condição for verdadeira). As instruções if nas linhas 26–27, 29–30, 32–33, 35–36 e 38–39 comparam number1 e number2 com os operadores !=, , =, respectivamente. Se a condição em uma das instruções if for verdadeira, a instrução no corpo correspondente é executada.

Erro comum de programação 2.8 Esquecer o parêntese esquerdo e/ou direito para a condição em uma estrutura requeridos.

if

é um erro de sintaxe — os parênteses são

Erro comum de programação 2.9 Confundir o operador de igualdade, ==, com o operador de atribuição, =, pode causar um erro de lógica ou um erro de sintaxe. O operador de igualdade deve ser lido como “igual a”, e o operador de atribuição deve ser lido como “obtém” ou “obtém o valor de”. Para evitar confusão, algumas pessoas leem o operador de igualdade como “duplo igual” ou “igual igual”.

Boa prática de programação 2.14 Colocar apenas uma instrução por linha em um programa aprimora a legibilidade do programa.

46

Capítulo 2  Introdução aos aplicativos Java

Observe que não há ponto-e-vírgula (;) no final da primeira linha de cada instrução if. Esse ponto-e-vírgula resultaria em um erro de lógica em tempo de execução. Por exemplo, if ( number1 == number2 ); // erro de lógica System.out.printf( "%d == %d\n", number1, number2 );

na realidade seria interpretado pelo Java como: if ( number1 == number2 ) ; // estrutura vazia System.out.printf( "%d == %d\n", number1, number2 );

onde o ponto-e-vírgula na linha sozinho — chamado instrução vazia — é a instrução a executar se a condição na instrução if for verdadeira. Quando a instrução vazia executa, nenhuma tarefa é realizada no programa. O programa então continua com a instrução de saída, que sempre é executada, independentemente de a condição ser verdadeira ou falsa, porque a instrução de saída não faz parte da estrutura if.

Erro comum de programação 2.10 Colocar um ponto-e-vírgula imediatamente depois do parêntese direito da condição em uma instrução if é normalmente um erro de lógica.

Note o uso do espaço em branco na Figura 2.15. Lembre-se de que, em geral, o espaço em branco é ignorado pelo compilador. Então, as instruções podem ser divididas em várias linhas e podem ser espaçadas de acordo com as suas preferências sem afetar o significado de um programa. Não é correto dividir identificadores e strings. Idealmente, as instruções devem ser mantidas pequenas, mas isso nem sempre é possível.

Boa prática de programação 2.15 Uma instrução longa pode se estender por várias linhas. Se uma única instrução deve ser dividida em várias linhas, escolha dividi-la em pontos que fazem sentido, como depois de uma vírgula em uma lista separada por vírgulas ou depois de um operador em uma expressão longa. Se uma instrução for dividida em duas ou mais linhas, recue todas as linhas subsequentes até o fim da instrução.

A Figura 2.16 mostra os operadores discutidos até agora em ordem decrescente de precedência. Todos esses operadores, com a exceção do operador de atribuição, =, associam da esquerda para a direita. A adição associa da esquerda para a direita, então uma expressão como x + y + z é avaliada como se tivesse sido escrita (x + y) + z. O operador de atribuição, = , associa da direita para a esquerda, assim uma expressão como x = y = 0 é avaliada como se tivesse sido escrita x = (y = 0), que, primeiro, atribui o valor 0 à variável y e, então, atribui o resultado dessa atribuição, 0, a x. Operadores *

/

+



= == =

!=

%

Associatividade

Tipo

da esquerda para a direita

multiplicativo

da esquerda para a direita

aditivo

da esquerda para a direita

relacional

da esquerda para a direita

igualdade

da direita para a esquerda

atribuição

Figura 2.16  |  Precedência e associatividade dos operadores discutidos.

Boa prática de programação 2.16 Consulte o gráfico de operador de precedência (Apêndice A) ao escrever expressões contendo muitos operadores. Confirme se as operações na expressão são realizados na ordem em que você espera. Se você não tiver certeza sobre a ordem de avaliação em uma expressão complexa, utilize parênteses para forçar a ordem, exatamente como faria em expressões algébricas.

2.9 Conclusão

47

2.9 Conclusão Você aprendeu muitos recursos importantes do Java neste capítulo, incluindo exibir dados na tela em uma Prompt de Command, inserir dados pelo teclado, realizar cálculos e tomar decisões. Os aplicativos apresentados aqui foram concebidos como uma introdução aos conceitos básicos de programação. Como verá no Capítulo 3, em geral aplicativos Java contêm apenas algumas linhas de código no método main — essas instruções normalmente criam os objetos que realizam o trabalho do aplicativo. No Capítulo 3, você aprenderá a implementar suas próprias classes e a utilizar objetos dessas classes nos aplicativos.

Resumo Seção 2.2 • • • • • • • • • • • • • • • •

Nosso primeiro programa Java: imprimindo uma linha de texto

Um aplicativo Java é um programa de computador que é executado quando você utiliza o comando java para carregar a JVM. Os comentários documentam programas e aprimoram sua legibilidade. Eles são ignorados pelo compilador. Um comentário que começa com // é chamado de comentário de fim de linha — ele termina no fim da linha em que aparece. Comentários tradicionais podem se estender por várias linhas e são delimitados por /* e */. Os comentários da Javadoc, delimitados por /** e */, permitem que você incorpore a documentação do programa no código. O programa utilitário javadoc gera páginas em HTML com base nesses comentários. Um erro de sintaxe (também chamado erro de compilador, erro em tempo de compilação ou erro de compilação) ocorre quando o compilador encontra um código que viola as regras da linguagem do Java. Isso é semelhante a um erro de gramática em uma língua natural. Linhas em branco, caracteres de espaço em branco e caracteres de tabulação são conhecidos como espaço em branco. O espaço em branco torna os programas mais fáceis de ler e não é ignorado pelo compilador. Todo programa que você cria consiste em pelo menos uma classe definida pelo programador. As palavras-chave são reservadas para uso pelo Java e sempre são escritas com todas as letras minúsculas. A palavra-chave class introduz uma declaração de classe e é imediatamente seguida pelo nome da classe. Por convenção, todos os nomes de classes em Java iniciam com uma letra maiúscula e apresentam a letra inicial de cada palavra que eles incluem em maiúscula (por exemplo, SampleClassName). O nome de uma classe Java é um identificador — uma série de caracteres que consiste em letras, dígitos, sublinhados ( _ ) e sinais de cifrão ($) que não iniciem com um dígito e não contenham espaços. O Java diferencia entre letras maiúsculas e minúsculas — isto é, letras maiúsculas e minúsculas são diferentes. O corpo de cada declaração de classe é delimitado por chaves, { e }. Uma declaração da classe public deve ser salva em um arquivo com o mesmo nome da classe seguido pela extensão de nome de arquivo “.java”. O método main é o ponto de partida de cada aplicativo Java e deve iniciar com: public static void main( String[] args )

• • • • • • •

Caso contrário, a JVM não executará o aplicativo. Os métodos realizam tarefas e informações de retorno quando eles completam suas tarefas. A palavra-chave void indica que um método realizará uma tarefa, mas não retornará nenhuma informação. As instruções fazem o computador realizar ações. Uma string entre aspas duplas é às vezes chamada de string de caracteres ou string literal. O objeto de saída padrão (System.out) exibe caracteres na janela de comando. O método System.out.println exibe seu argumento na janela de comando seguido por um caractere de nova linha para posicionar o cursor de saída no começo da próxima linha. Você compila um programa com o comando javac. Se o programa não contiver nenhum erro de sintaxe, um arquivo de classe contendo os bytecodes Java que representam o aplicativo é criado. Esses bytecodes são interpretados pela JVM quando executamos o programa. Para executar um aplicativo, digite java seguido pelo nome da classe que contém main.

Seção 2.3

Modificando nosso primeiro programa Java

• System.out.print exibe seu argumento e posiciona o cursor de saída imediatamente após o último caractere exibido. • Uma barra invertida (\) em uma string é um caractere de escape. O Java combina o próximo caractere com as barras invertidas para formar uma sequência de escape. A sequência de escape \n representa o caractere de nova linha.

48

Capítulo 2  Introdução aos aplicativos Java

Seção 2.4  Exibindo texto com printf • O método System.out.printf (f significa “formatado”) exibe os dados formatados. • O primeiro argumento do método printf é uma string de formato que pode consistir em texto fixo e especificadores de formato. Cada especificador de formato é um marcador de lugar para um valor e especifica o tipo da saída de dados. • Especificadores de formato iniciam com um sinal de porcentagem (%) e são seguidos por um caractere que representa o tipo de dados. Os especificadores de formato %s é um marcador de lugar para uma string. • Cada especificador de formato é substituído pelo valor do argumento correspondente que aparece depois da string de formato.

Seção 2.5  Outro aplicativo: somando inteiros • Uma declaração import ajuda o compilador a localizar uma classe utilizada em um programa. • O rico conjunto do Java de classes predefinidas é agrupado em pacotes — chamados de grupos de classes. • Coletivamente, os pacotes do Java são chamados de biblioteca de classes Java ou Java Application Programming Interface ( Java API). • Uma variável é uma posição na memória do computador onde um valor pode ser armazenado para utilização posterior em um programa. Todas as variáveis devem ser declaradas com um nome e um tipo antes que elas possam ser utilizadas. • O nome de uma variável permite que o programa acesse o valor da variável na memória. • Um Scanner (pacote java.util) permite a um programa ler os dados para uso em um programa. Antes de um Scanner poder ser utilizado, o programa deve criá-lo e especificar a origem dos dados. • Variáveis devem ser inicializadas para prepará-las para uso em um programa. • A expressão new Scanner (System.in) cria um Scanner que lê o objeto de entrada padrão (System.in) — normalmente o teclado. • O tipo de dados int é utilizado para declarar variáveis que conterão valores de inteiro. O intervalo de valores para um int é –2.147.483.648 a +2.147.483.647. • Os tipos float e double especificam números reais com pontos decimais, como 3.4 e –11.19. • Variáveis do tipo char representam caracteres individuais, como uma letra maiúscula (por exemplo, A), um dígito (por exemplo, 7), um caractere especial (por exemplo, * ou %) ou uma sequência de escape (por exemplo, nova linha, \n). • Tipos como int, float, double e char são tipos primitivos. Os nomes dos tipos primitivos são palavras-chave; portanto, todos devem aparecer em letras minúsculas. • Um prompt direciona o usuário a tomar uma ação específica. • O método Scanner nextInt obtém um inteiro para uso em um programa. • O operador de atribuição, =, permite ao programa atribuir um valor a uma variável. O operador = é chamado operador binário porque tem dois operandos. • Partes das instruções que contêm valores são chamadas expressões. • O especificador de formato %d é um marcador de lugar para um valor int.

Seção 2.6  Conceitos de memória • Os nomes de variável correspondem a posições na memória do computador. Cada variável tem um nome, um tipo, um tamanho e um valor. • Um valor que é colocado em uma posição de memória substitui o valor anterior da posição, que é perdido.

Seção 2.7  Aritmética • Os operadores aritméticos são + (adição), – (subtração), * (multiplicação), / (divisão) e % (resto). • A divisão de inteiros produz um quociente inteiro. • O operador de módulo, %, fornece o resto depois da divisão. • As expressões aritméticas devem ser escritas em sequência direta. • Se uma expressão contiver parênteses aninhados, o conjunto mais interno dentro dos parênteses é avaliado primeiro. • O Java aplica os operadores a expressões aritméticas em uma sequência precisa determinada pelas regras de precedência de operadores. • Quando dizemos que operadores são aplicados da esquerda para a direita, estamos nos referindo a sua associatividade. Alguns operadores associam da direita para a esquerda. • Os parênteses redundantes em uma expressão podem tornar uma expressão mais clara.

Seção 2.8  Tomada de decisão: operadores de igualdade e operadores relacionais • A instrução if toma uma decisão baseada no valor de uma condição (verdadeiro ou falso). • As condições em instruções if podem ser formadas utilizando-se os operadores de igualdade (== e !=) e relacionais (>, = e O operador relacional => é incorreto. Correção: altere => para >=.



Exercícios

2.5

2.6

1

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

51

a) // Calcula o produto de três inteiros b) Scanner input = new Scanner( System.in ); c) int x, y, z, result; ou int x; int y; int z; int result; d) System.out.print( "Enter first integer: " ); e) x = input.nextInt(); f) System.out.print( "Enter second integer: " ); g) y = input.nextInt(); h) System.out.print( "Enter third integer: " ); i) z = input.nextInt(); j) result = x * y * z; k) System.out.printf( "Product is %d\n", result ); A solução para o Exercício de autorrevisão 2.6 é a seguinte: // Ex. 2.6: Product.Java // Calcula o produto de três inteiros. import java.util.Scanner; // programa utiliza Scanner public class Product { public static void main( String[] args ) { // cria Scanner para obter entrada a partir da janela de comando Scanner input = new Scanner( System.in ); int int int int

x; // primeiro número inserido pelo usuário y; // segundo número inserido pelo usuário z; // terceiro número inserido pelo usuário result; // produto dos números

System.out.print( "Enter first integer: " ); // solicita entrada x = input.nextInt(); // lê o primeiro inteiro System.out.print( "Enter second integer: " ); // solicita entrada y = input.nextInt(); // lê o segundo inteiro System.out.print( "Enter third integer: " ); // solicita entrada z = input.nextInt(); // lê o terceiro inteiro result = x * y * z; // calcula o produto dos números System.out.printf( "Product is %d\n", result ); } // fim do método main } // fim da classe Product

Enter first integer: 10 Enter second integer: 20 Enter third integer: 30 Product is 6000

Exercícios 2.7

Preencha as lacunas em cada uma das seguintes afirmações: a) ________ são utilizados para documentar um programa e aprimorar sua legibilidade. b) Uma decisão pode ser tomada em um programa Java com um(a) ________. c) Os cálculos normalmente são realizados pelas instruções ________. d) Os operadores aritméticos com a mesma precedência da multiplicação são ________ e ________. e) Quando parênteses em uma expressão aritmética estão aninhados, o conjunto de parênteses ________ é avaliado primeiro. f) Uma posição na memória do computador que pode conter valores diferentes várias vezes ao longo da execução de um programa é chamada ________.

52 2.8

Capítulo 2  Introdução aos aplicativos Java Escreva instruções Java que fazem cada uma das seguintes tarefas: a) Exiba a mensagem "Enter an integer: ", deixando o cursor na mesma linha. b) Atribui o produto de variáveis b e c para a variável a. c) Declara que um programa realiza um cálculo de folha de pagamento de exemplo (isto é, usa texto que ajuda a documentar um programa).

2.9

Determine se cada um dos seguintes é verdadeiro ou falso. Se falso, explique por quê. a) Operadores Java são avaliados da esquerda para a direta. b) Os seguintes são todos nomes de variável válidos: _under_bar_, m928134, t5, j7, her_sales$,

his_$account_total, a, b$, c, z e z2.

c) Uma expressão aritmética Java válida sem parênteses é avaliada da esquerda para a direta. d) Os seguintes são todos nomes de variável inválidos: 3g, 87, 67h2, h22 e 2h.

2.10 Supondo que x = 2 e y = 3, o que cada um destas instruções exibe? a) System.out.printf(

"x = %d\n", x );

b) System.out.printf(

"Value of %d + %d is %d\n", x, x, ( x + x ) );

c) System.out.printf(

"x =" );

d) System.out.printf(

"%d = %d\n", ( x + y ), ( y + x ) );

2.11 Quais instruções Java a seguir contêm variáveis cujos valores são modificados? a) p

= i + j + k + 7;

b) System.out.println(

"variables whose values are modified" );

c) System.out.println(

"a = 5" );

d) value

= input.nextInt();

2.12 Dado que y = ax3 + 7, quais das seguintes são instruções Java corretas para essa equação? a) y

= a * x * x * x + 7;

b) y

= a * x * x * ( x + 7 );

c) y

= ( a * x ) * x * ( x + 7 );

d) y

= ( a * x ) * x * x + 7;

e) y

= a * ( x * x * x ) + 7;

f)

y = a * x * ( x * x + 7 );

2.13 Declare a ordem de avaliação dos operadores em cada uma das seguintes instruções Java e mostre o valor de x após cada instrução ser realizada: a) x

= 7 + 3 * 6 / 2



1;

b) x

= 2 % 2 + 2 * 2



2 / 2;

c) x

= ( 3 * 9 * ( 3 + ( 9 * 3 / ( 3 ) ) ) );

2.14 Escreva um aplicativo que exibe os números 1 a 4 na mesma linha, com cada par de números adjacentes separados por um espaço. Escreva o programa utilizando as técnicas a seguir: a) Utilize uma instrução System.out.println. b) Utilize quatro instruções System.out.print. c) Utilize uma instrução System.out.printf.

2.15 (Aritmética) Escreva um aplicativo que solicita ao usuário inserir dois inteiros, obtém do usuário esses números e imprime sua soma, produto, diferença e quociente (divisão). Utilize as técnicas mostradas na Figura 2.7.

2.16 (Comparando inteiros) Escreva um aplicativo que solicita ao usuário inserir dois inteiros, obtém do usuário esses números e exibe o número maior seguido pelas palavras "is larger". Se os números forem iguais, imprime a mensagem "These numbers are mostradas na Figura 2.15.

equal". Utilize as técnicas

2.17 (Aritmética, menor e maior) Escreva um aplicativo que insere três inteiros digitados pelo usuário e exibe a soma, média, produto e os números menores e maiores. Utilize as técnicas mostradas na Figura 2.15. [Nota: o cálculo da média neste exercício deve resultar em uma representação de inteiro da média. Assim, se a soma dos valores for 7, a média deverá ser 2, não 2,3333….]

2.18 (Exibindo formas com asteriscos) Escreva um aplicativo que exibe uma caixa, uma oval, uma seta e um losango utilizando asteriscos (*), como segue:



Exercícios

********* * * * * * * * * * * * * * * *********

*

***

* * * * *

*

*

* * * * *

***

*

* *** ***** * * * * * *

53

* * *

*

*

*

*

*

*

*

*

*

*

* * *

2.19 O que o seguinte código imprime? System.out.println( "*\n**\n***\n****\n*****" );

2.20 O que o seguinte código imprime? System.out.println( System.out.println( System.out.println( System.out.println( System.out.println(

"*" ); "***" ); "*****" ); "****" ); "**" );

2.21 O que o seguinte código imprime? System.out.print( "*" ); System.out.print( "***" ); System.out.print( "*****" ); System.out.print( "****" ); System.out.println( "**" );

2.22 O que o seguinte código imprime? System.out.print( "*" ); System.out.println( "***" ); System.out.println( "*****" ); System.out.print( "****" ); System.out.println( "**" );

2.23 O que o seguinte código imprime? System.out.printf( "%s\n%s\n%s\n", "*", "***", "*****" );

2.24 (Maiores e menores inteiros) Escreva um aplicativo que lê cinco inteiros, determina e imprime o maior e o menor inteiro no grupo. Utilize somente as técnicas de programação que você aprendeu neste capítulo.

2.25 (Ímpar ou par) Escreva um aplicativo que lê um inteiro, determina e imprime se ele é ímpar ou par. [Dica: utilize o operador de módulo. Um número par é um múltiplo de 2. Qualquer múltiplo de 2 deixa um resto 0 quando dividido por 2.]

2.26 (Múltiplos) Escreva um aplicativo que lê dois inteiros, determina se o primeiro é um múltiplo do segundo e imprime o resultado. [Dica: utilize o operador de módulo].

2.27 (Padrão de tabuleiro de damas de asteriscos) Escreva um aplicativo que exibe um padrão de tabuleiro de damas, conforme mostrado a seguir:

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

2.28 (Diâmetro, circunferência e área de um círculo) Eis uma prévia do que veremos mais adiante. Neste capítulo, você aprendeu sobre inteiros

e o tipo int. O Java também pode representar números de pontos flutuantes que contêm pontos de fração decimal, como 3.14159. Escreva um aplicativo que lê a entrada a partir do usuário do raio de um círculo como um inteiro e imprime o diâmetro do círculo, circunferência e área utilizando o valor do ponto flutuante 3.14159 para π. Utilize as técnicas mostradas na Figura 2.7 [Nota: você também pode utilizar a constante Math.PI predefinida para o valor de π. Essa constante é mais precisa que o valor 3,14159. A classe Math é definida no pacote java.lang. As classes nesse pacote são importadas automaticamente, portanto, você não precisa importar a classe Math para utilizá-la.] Utilize as seguintes fórmulas (r é o raio):



diameter = 2r circumference = 2πr area = πr2



Não armazene os resultados de cada cálculo em uma variável. Em vez disso, especifique cada cálculo como o valor de saída em uma instrução System.out.printf. Observe que os valores produzidos pela circunferência e os cálculos da área são números de ponto flutuante. A saída desses

54

Capítulo 2  Introdução aos aplicativos Java valores pode ser gerada com o especificador de formato %f em uma instrução System.out.printf. Você aprenderá mais sobre números de pontos flutuantes no Capítulo 3.

2.29 (O valor inteiro de um caractere) Eis outra prévia do que virá adiante. Neste capítulo, você aprendeu sobre inteiros e o tipo int. O Java também pode representar letras maiúsculas, minúsculas e uma variedade considerável de símbolos especiais. Cada caractere tem uma representação correspondente de inteiro. O conjunto de caracteres que um computador utiliza junto com as correspondentes representações na forma de inteiro desses caracteres é chamado conjunto de caracteres desse computador. Você pode indicar um valor de caractere em um programa simplesmente incluindo esse caractere entre aspas simples, como em 'A’. Você pode determinar o equivalente em inteiro de um caractere precedendo esse caractere (com int), como em:

(int) 'A’

Essa forma é chamada operador de coerção. (Você aprenderá sobre os operadores de coerção no Capítulo 4.) A instrução a seguir gera saída de um caractere e seu equivalente de inteiro: System.out.printf( "The character %c has the value %d\n", 'A’, ( (int) 'A’ ) );

Quando a instrução precedente executa, ela exibe o caractere A e o valor 65 (do conjunto de caracteres Unicode®) como parte da string. Observe que o especificador de formato %c é um marcador de lugar para um caractere (nesse caso, o caractere 'A’). Utilizando instruções semelhantes àquela mostrada anteriormente neste exercício, escreva um aplicativo que exibe os equivalentes inteiros de algumas letras maiúsculas, letras minúsculas, dígitos e símbolos especiais. Exiba os equivalentes inteiros do seguinte: A B C a b c 0 1 2 $ * + / e o caractere em branco.

2.30 (Separando os dígitos em um inteiro) Escreva um aplicativo que insere um número consistindo em cinco dígitos do usuário, separa o número em seus dígitos individuais e imprime os dígitos separados uns dos outros por três espaços cada. Por exemplo, se o usuário digitar o número 42339, o programa deve imprimir: 4

2

3

3

9

Suponha que o usuário insira o número correto de dígitos. O que acontece quando você executa o programa e digita um número com mais de cinco dígitos? O que acontece quando você executa o programa e digita um número com menos de cinco dígitos? [Dica: é possível fazer esse exercício com as técnicas que você aprendeu neste capítulo. Você precisará utilizar tanto as operações de divisão como as de resto para “selecionar” cada dígito.]

2.31 (Tabela de quadrados e cubos) Utilizando apenas as técnicas de programação que aprendeu neste capítulo, escreva um aplicativo que calcule os quadrados e cubos dos números de 0 a 10 e imprime os valores resultantes no formato de tabela como a seguir: [Nota: esse programa não requer nenhuma entrada do usuário.] number 0 1 2 3 4 5 6 7 8 9 10

square 0 1 4 9 16 25 36 49 64 81 100

cube 0 1 8 27 64 125 216 343 512 729 1000

2.32 (Valores negativos, positivos e zero) Escreva um programa que insere cinco números e determina e imprime quantos números negativos, quantos números positivos e quantos zeros foram inseridos.

Fazendo a diferença 2.33 (Calculadora de índice de massa corporal) Introduzimos a calculadora de índice de massa corporal (IMC) no Exercício 1.12. As fórmulas para calcular o IMC são × 703 IMC = ------------pesoEmQuilogramas ----------------------------------------------------------------------alturaEmMetros × alturaEmMetros

ou --------------------------------------------------------------------IMC = ------------------pesoEmQuilogramas alturaEmMetros × alturaEmMetros Crie um aplicativo de calculadora IMC que lê o peso do usuário em libras e a altura em polegadas (ou, se preferir, o peso em quilogramas e a altura em metros) e, então, calcula e exibe o índice de massa corporal do usuário. Além disso, o aplicativo deve exibir as seguintes informações do Department of Health and Human Services/National Institutes of Health, portanto o usuário pode avaliar o seu IMC:

Fazendo a diferença

BMI VALUES Underweight: Normal: Overweight: Obese:

55

less than 18.5 between 18.5 and 24.9 between 25 and 29.9 30 or greater

[Nota: neste capítulo, você aprendeu a utilizar o tipo int para representar números inteiros. Os cálculos de IMC quando feitos com valores produzirão ambos os resultados de número inteiro. No Capítulo 3, você aprenderá a utilizar o tipo double para representar números com pontos decimais. Quando os cálculos de IMC são realizados com doubles, eles produzirão ambos os números com pontos decimais — esses são chamados de números de “ponto flutuante”.] int

2.34 (Calculadora de crescimento demográfico mundial) Utilize a Web para determinar a população mundial atual e a taxa de crescimento

demográfica mundial anual. Escreva um aplicativo que introduza esses valores e, então, que exiba a população mundial estimada depois um, dois, três, quatro e cinco anos.

2.35 (Calculadora de economia da faixa solidária) Pesquise vários sites Web de faixa solidária (ou “faixa 2”) . Crie um aplicativo que calcule o custo diário de dirigir, para que possa estimar quanto dinheiro pode ser economizado com o uso da faixa solidária, que também tem outras vantagens, como reduzir emissões de carbono e congestionamento de tráfego. O aplicativo deve introduzir as seguintes informações e exibir o custo do usuário por dia de dirigir para o trabalho: a) Milhas totais dirigidas por dia. b) Preço por galão de gasolina. c) Milhas médias por galão. d) Taxas de estacionamento por dia. e) Pedágio por dia.

3

Você verá uma coisa nova. Duas coisas. E eu as chamo Coisa Um e Coisa Dois. — Dr. Theodor Seuss Geisel

Nada pode ter valor sem ser um objeto útil. — Karl Marx

Seus servidores públicos prestam-lhe bons serviços. — Adlai E. Stevenson

Saber como responder àquele que fala, para responder àquele que envia uma mensagem. — Amenemope

Introdução a classes e objetos

Objetivos Neste capítulo, você aprenderá: 

O que são classes, objetos, métodos e variáveis de instância.



Como declarar uma classe e utilizá-la para criar um objeto.



Como declarar métodos em uma classe para implementar os comportamentos da classe.



Como declarar variáveis de instância em uma classe para implementar os atributos da classe.



Como chamar os métodos de um objeto para fazer esses métodos realizarem suas tarefas.



As diferenças entre variáveis de instância de uma classe e variáveis locais de um método.



Como utilizar um construtor para assegurar que os dados de um objeto sejam inicializados quando o objeto for criado.



As diferenças entre tipos por referência primitivos.

Sumário

3.1 Introdução

3.1 Introdução 3.2 Classes, objetos, métodos e variáveis de instância 3.3 Declarando uma classe com um método e instanciando um objeto de uma classe 3.4 Declarando um método com um parâmetro 3.5 Variáveis de instância, métodos set e get

57

3.6 3.7 3.8 3.9

Tipos primitivos versus tipos por referência Inicializando objetos com construtores Números de ponto flutuante e tipo double (Opcional) Estudo de caso de GUI e imagens gráficas: utilizando caixas de diálogo 3.10 Conclusão

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios | Fazendo a diferença

3.1 Introdução Introduzimos a terminologia e os conceitos básicos da programação orientada a objetos na Seção 1.16. Em geral, os aplicativos que você desenvolve neste livro consistirão em duas ou mais classes, cada uma contendo um ou mais métodos. Se você se tornar parte de uma equipe de desenvolvimento na indústria, você poderá trabalhar em aplicativos com centenas, ou até milhares, de classes. Neste capítulo, apresentamos um framework [estrutura, arcabouço] simples para organizar aplicativos orientados a objetos em Java. Primeiro, motivamos a noção de classes com um exemplo do mundo real. Em seguida, apresentamos cinco aplicativos funcionais completos para demonstrar a criação e utilização de suas próprias classes. Os quatro primeiros exemplos iniciam nosso estudo de caso sobre desenvolvimento de uma classe livro de notas que instrutores podem utilizar para manter as notas de teste do aluno. Esse estudo de caso é aprimorado nos capítulos 4, 5 e 7. O último exemplo no capítulo introduz números de ponto flutuante — isto é, números que contêm pontos decimais — no contexto de uma classe de conta bancária que mantém o saldo de um cliente.

3.2 Classes, objetos, métodos e variáveis de instância Iniciemos com uma analogia simples para ajudar a entender classes e seu conteúdo. Suponha que você queira guiar um carro e fazê-lo andar mais rápido pisando no pedal acelerador. O que deve acontecer antes que você possa fazer isso? Bem, antes de poder dirigir um carro, alguém tem de projetá-lo. Em geral, um carro inicia com os desenhos de engenharia, semelhantes às plantas utilizadas para projetar uma casa. Esses desenhos de engenharia incluem o projeto de um pedal acelerador para aumentar a velocidade do carro. O pedal “oculta” do motorista os complexos mecanismos que realmente fazem o carro ir mais rápido, assim como o pedal de freio “oculta” os mecanismos que diminuem a velocidade do carro e a direção “oculta” os mecanismos que mudam a direção do carro. Isso permite que as pessoas com pouco ou nenhum conhecimento de como os motores funcionam dirijam um carro facilmente. Infelizmente, você não pode determinar o projeto de um carro. Antes de poder guiar um carro, ele deve ser construído a partir dos desenhos de engenharia que o descrevem. Um carro pronto tem um pedal de acelerador real para fazer o carro andar mais rápido, mas até isso não é suficiente — o carro não acelerará por conta própria, então o motorista deve pressionar o pedal do acelerador. Agora vamos utilizar nosso exemplo do carro para introduzir os conceitos-chave de programação desta seção. Para realizar uma tarefa em um programa é necessário um método. O método descreve os mecanismos que realmente realizam suas tarefas. O método oculta de seu usuário as tarefas complexas que ele realiza, assim como o pedal acelerador de um carro oculta do motorista os complexos mecanismos que fazem o carro andar mais rápido. Em Java, primeiro criamos uma unidade de programa chamada classe para abrigar um método, assim como os desenhos de engenharia do carro abrigam o projeto de um pedal acelerador. Em uma classe, você fornece um ou mais métodos que são projetados para realizar as tarefas da classe. Por exemplo, uma classe que representa uma conta bancária poderia conter um método para fazer depósitos de dinheiro em uma conta, outro para fazer saques e um terceiro para perguntar qual é o saldo atual. Assim como você não pode dirigir um desenho de engenharia de um carro, você não pode “dirigir” uma classe. Assim como alguém tem de construir um carro a partir de seus desenhos de engenharia antes de poder realmente guiar o carro, você deve construir um objeto de uma classe antes de fazer um programa realizar as tarefas que a classe descreve como fazer. Essa é uma razão de o Java ser conhecido como uma linguagem de programação orientada a objetos. Ao dirigir um carro, o ato de pressionar o acelerador envia uma mensagem para o carro realizar uma tarefa — isto é, fazer o carro andar mais rápido. De maneira semelhante, você envia mensagens para um objeto — cada mensagem é implementada como uma chamada de método que instrui um método do objeto a realizar sua tarefa. Até aqui, utilizamos a analogia do carro para introduzir classes, objetos e métodos. Um carro, além de suas capacidades, também tem muitos atributos, como cor, o número de portas, a quantidade de gasolina no tanque, a velocidade atual e o total de quilômetros percorridos (isto é, a leitura do hodômetro). Como as capacidades do carro, esses atributos são representados como parte do projeto de um carro em seus diagramas de engenharia. Quando você dirige um carro, esses atributos estão sempre associados com o carro. Cada carro mantém seus próprios atributos. Por exemplo, cada carro sabe a quantidade de gasolina que há no seu tanque, mas não sabe quanto há no tanque de outros carros. Um objeto tem atributos que são carregados com o objeto quando ele é utilizado em um programa. Esses atributos são especificados

58

Capítulo 3

Introdução a classes e objetos

como parte da classe do objeto. Por exemplo, um objeto conta bancária tem um atributo ‘saldo’ que representa a quantidade de dinheiro na conta. Cada objeto ‘conta bancária’ sabe o saldo da conta que ele representa, mas não sabe os saldos de outras contas no banco. Os atributos são especificados pelas variáveis de instância da classe.

Resumo dos exemplos deste capítulo O restante deste capítulo apresenta exemplos que demonstram os conceitos que introduzimos no contexto da analogia do carro. Os quatro primeiros exemplos constroem incrementalmente uma classe GradeBook para demonstrar esses conceitos: 1. O primeiro exemplo apresenta uma classe GradeBook com um método que simplesmente exibe uma mensagem de boas-vindas quando é chamado. Então mostramos como criar um objeto dessa classe e chamar o método para ele exibir a mensagem de boas-vindas. 2. O segundo exemplo aprimora o primeiro para permitir ao método receber o nome de um curso como argumento e exibir o nome como parte da mensagem de boas-vindas. 3. O terceiro exemplo mostra como armazenar o nome do curso em um objeto GradeBook. Para essa versão da classe, nós também mostramos como utilizar métodos para configurar o nome do curso e obter seu nome. 4. O quarto exemplo demonstra como os dados em um objeto GradeBook podem ser inicializados quando o objeto é criado. O último exemplo apresenta uma classe Account que reforça os conceitos apresentados nos quatro primeiros exemplos e introduz números de ponto flutuante. A classe Account representa uma conta bancária e mantém seu saldo como um número de ponto flutuante. A classe contém dois métodos — um que credita um depósito à conta, aumentando assim o saldo, e outro número que recupera o saldo. O construtor da classe permite que o saldo de cada objeto Account seja inicializado quando o objeto é criado. Criamos dois objetos Account e fizemos depósitos em cada um para mostrar que cada objeto mantém seu próprio saldo. O exemplo também demonstra como inserir e exibir números de ponto flutuante.

3.3 Declarando uma classe com um método e instanciando um objeto de uma classe Nas seções 2.5 e 2.8, você criou um objeto da classe existente Scanner e, então, utilizou esse objeto para ler dados a partir do teclado. Nesta seção, você criará uma nova classe e irá usá-la para criar um objeto. Iniciamos com um exemplo que consiste em classes GradeBook (Figura 3.1) e GradeBookTest (Figura 3.2). A classe GradeBook (declarada no arquivo GradeBook.java) será utilizada para exibir uma mensagem na tela (Figura 3.2) que dá boas-vindas ao instrutor do aplicativo livro de notas. A classe GradeBookTest (declarada no arquivo GradeBookTest.java) é uma classe de aplicativos na qual o método main utilizará um objeto da classe GradeBook. Cada declaração de classe que inicia com a palavra-chave public deve ser armazenada em um arquivo que tenha o mesmo nome da classe e terminar com a extensão de nome de arquivo .java. Dessa forma, as classes GradeBook e GradeBookTest devem ser declaradas em arquivos separados, porque cada classe é declarada public. Como discutiremos, iniciando no Capítulo 8, há outros modos de declarar classes.

Erro comum de programação 3.1 Declarar mais de uma classe public no mesmo arquivo é um erro de compilação.

A classe GradeBook A declaração de classe GradeBook (Figura 3.1) contém um método displayMessage (linhas 7–10) que exibe uma mensagem na tela. Lembre-se de que uma classe é como uma planta arquitetônica — precisaremos fazer um objeto dessa classe e chamar seu método para executar a linha 9 e exibir a mensagem. 1 2 3 4 5 6 7 8 9 10 11

// Figura 3.1: GradeBook.java // Declaração de classe com um método. public class GradeBook { // exibe uma mensagem de boas­vindas para o usuário GradeBook public void displayMessage() { System.out.println( “Welcome to the Grade Book!” ); } // fim do método displayMessage } // fim da classe GradeBook

Figura 3.1 | Declaração de classe com um método.



3.3  Declarando uma classe com um método e instanciando um objeto de uma classe

59

A declaração de classe inicia na linha 4. A palavra-chave public é um modificador de acesso. Por enquanto, simplesmente declaramos toda classe public. Cada declaração de classe contém a palavra-chave class seguida imediatamente do nome da classe. O corpo de cada classe está entre as chaves esquerda e direita ({ e }), como nas linhas 5–11 da classe GradeBook. No Capítulo 2, cada classe que declaramos tinha um método chamado main. A classe GradeBook também tem um método — displayMessage (linhas 7–10). Lembre-se de que main é um método especial que será sempre chamado automaticamente pela Java Virtual Machine quando você executar um aplicativo. A maioria dos métodos não é chamada automaticamente. Como logo verá, você deve chamar o método displayMessage para instruí-lo explicitamente a realizar sua tarefa. A declaração do método começa com palavra-chave public para indicar que o método está “disponível para o público” — ele pode ser chamado a partir de métodos de outras classes. Em seguida, vem o tipo de retorno do método, que especifica o tipo de dados que o método retorna depois de realizar sua tarefa. O tipo de retorno void indica que esse método realizará uma tarefa mas não retornará (isto é, devolverá) nenhuma informação para seu método chamador ao completar sua tarefa. Você já usou métodos que retornam informações — por exemplo, no Capítulo 2 você usou o método Scanner nextInt para inserir um inteiro digitado pelo usuário no teclado. Quando nextInt lê um valor, ele retorna esse valor para uso no programa. O nome do método, displayMessage, segue o tipo de retorno. Por convenção, os nomes de método iniciam com a primeira letra minúscula e as palavras subsequentes do nome iniciam com uma maiúscula. Os parênteses depois do nome do método indicam que isso é um método. Os parênteses vazios, como na linha 7, indicam que esse método não requer informações adicionais para realizar sua tarefa. A linha 7 é comumente referida como o cabeçalho de método. O corpo de cada método é delimitado pelas chaves esquerda e direita ({ e }), como nas linhas 8–10. O corpo de um método contém uma ou mais instruções que realizam a tarefa do método. Nesse caso, o método contém uma instrução (linha 9) que exibe a mensagem "Welcome to the GradeBook!" seguida por caractere de nova linha na janela de comando. Depois que essa instrução executar, o método terá concluído sua tarefa. Em seguida, gostaríamos de utilizar a classe GradeBook em um aplicativo. Como você aprendeu no Capítulo 2, o método main começa a execução de cada aplicativo. Uma classe que contém o método main inicia a execução de um aplicativo Java. A classe GradeBook não é um aplicativo porque não contém main. Portanto, se tentar executar GradeBook digitando java GradeBook na janela de comando, você obterá uma mensagem de erro como essa: Exception in thread "main" java.lang.NoSuchMethodError: main

Isso não era um problema no Capítulo 2, porque cada classe que você declarava tinha um método main. Para corrigir esse problema, devemos declarar uma classe separada que contenha um método main ou colocar um método main na classe GradeBook. Para ajudá-lo a se preparar para os programas maiores encontrados mais adiante neste livro e na indústria, utilizamos uma classe separada (GradeBookTest nesse exemplo) que contém o método main para testar cada classe nova criada neste capítulo. Alguns programadores tratam essa classe como uma classe driver.

A classe GradeBookTest A declaração de classe GradeBookTest (Figura 3.2) contém o método main que controlará a execução do nosso aplicativo. A declaração de classe GradeBookTest inicia na linha 4 e termina na linha 15. A classe contém somente um método GradeBookTest, que é típico de muitas classes que iniciam a execução de um aplicativo. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

// Figura 3.2: GradeBookTest.java // Criando um objeto GradeBook e chamando seu método displayMessage. public class GradeBookTest { // método main inicia a execução de programa public static void main( String[] args ) { // cria um objeto GradeBook e o atribui a myGradeBook GradeBook myGradeBook = new GradeBook(); // chama método displayMessage de myGradeBook myGradeBook.displayMessage(); } // fim de main } // fim da classe GradeBookTest

Welcome to the Grade Book!

Figura 3.2  |  Criando um objeto GradeBook e chamando seu método displayMessage.

60

Capítulo 3  Introdução a classes e objetos

As linhas 7–14 declaram o método main. Uma parte-chave para permitir à JVM localizar e chamar o método main para iniciar a execução do aplicativo é a palavra-chave static (linha 7), que indica que main é um método static. Um método static é especial, porque pode ser chamado sem primeiro criar um objeto da classe em que o método é declarado. Explicamos completamente os métodos static no Capítulo 6, “Métodos: uma visão mais aprofundada”. Nesse aplicativo, gostaríamos de chamar o método displayMessage da classe GradeBook para exibir a mensagem de boas-vindas na janela de comando. Em geral, você não pode chamar um método que pertence a outra classe até criar um objeto dessa classe, como mostrado na linha 10. Iniciamos com a declaração da variável myGradeBook. Observe que o tipo da variável é GradeBook — a classe que declaramos na Figura 3.1. Cada nova classe que você cria torna-se um novo tipo que pode ser utilizado para declarar variáveis e criar objetos. Você pode declarar novos tipos de classe conforme necessário; essa é uma razão pela qual o Java é conhecido como uma linguagem extensível. A variável myGradeBook é inicializada com o resultado da expressão de criação de instância de classe new GradeBook(). A palavra-chave new cria um novo objeto da classe especificada à direita da palavra-chave (isto é, GradeBook). Os parênteses à direita de GradeBook são necessários. Como você aprenderá na Seção 3.7, esses parênteses em combinação com um nome de classe representam uma chamada para um construtor, que é semelhante a um método, mas é utilizado na hora em que um objeto é criado para inicializar os dados do objeto. Nessa seção você verá que os dados podem ser colocados entre parênteses para especificar os valores iniciais para os dados do objeto. Por enquanto, simplesmente não incluímos nada entre os parênteses. Assim como podemos usar o objeto System.out para chamar os métodos print, printf e println, também podemos usar o objeto myGradeBook para chamar o método displayMessage. A linha 13 chama o método displayMessage (as linhas 7–10 da Figura 3.1) usando myGradeBook seguido por um ponto separador (.), o nome do método displayMessage e um conjunto de parênteses vazio. Essa chamada faz com que o método displayMessage realize sua tarefa. Essa chamada de método difere daquelas do Capítulo 2 que exibiam informações em uma janela de comando — todas aquelas chamadas de método forneciam argumentos que especificavam os dados a serem exibidos. No começo da linha 13, myGradeBook. indica que main deve utilizar o objeto myGradeBook que foi criado na linha 10. A linha 7 da Figura 3.1 indica que o método displayMessage tem uma lista de parâmetros vazia — isto é, displayMessage não requer informações adicionais para realizar sua tarefa. Por isso, a chamada de método (linha 13 da Figura 3.2) especifica um conjunto de parênteses vazio depois do nome do método para indicar que nenhum argumento está sendo passado ao método displayMessage. Quando o método displayMessage completa sua tarefa, o método main continua a executar na linha 14. Esse é o fim do método main, portanto, o programa termina. Observe que qualquer classe pode conter um método main. O JVM invoca o método main somente na classe utilizada para executar o aplicativo. Se um aplicativo tiver múltiplas classes que contêm main, então aquele que é invocado é aquele na classe nomeada no comando java.

Compilando um aplicativo com múltiplas classes Você deve compilar as classes na Figura 3.1 e Figura 3.2 antes de poder executar o aplicativo. Primeiro, mude para o diretório que contém os arquivos de código-fonte do aplicativo. Em seguida, digite o comando: javac GradeBook.java GradeBookTest.java

para compilar ambas as classes de uma só vez. Se o diretório que contém o aplicativo incluir somente os arquivos desse aplicativo, você pode compilar todas as classes no diretório com o comando: javac *.java

O asterisco (*) em *.java indica que todos os arquivos no diretório atual que têm a extensão .java devem ser compilados.

Diagrama de classes UML para a classe GradeBook A Figura 3.3 apresenta um diagrama de classes UML para a classe GradeBook da Figura 3.1. Lembre-se da Seção 1.16 que a UML é uma linguagem gráfica utilizada pelos programadores para representar sistemas orientados a objetos de uma maneira padronizada. Na UML, cada classe é modelada em um diagrama de classe como um retângulo com três compartimentos. O superior contém o nome da classe centralizado horizontalmente no tipo negrito. O compartimento do meio contém os atributos da classe, que correspondem às variáveis de instância (discutidas na Seção 3.5) em Java. Na Figura 3.3, o compartimento do meio está vazio, porque essa classe GradeBook não tem nenhum atributo. O compartimento inferior contém as operações da classe, que correspondem aos métodos em Java. A UML modela operações listando o nome da operação precedido por um modificador de acesso (nesse caso +) e seguido por um conjunto de parênteses. A classe GradeBook tem um método, displayMessage, então o compartimento inferior da Figura 3.3 lista uma operação com esse nome. O método displayMessage não requer informações adicionais para executar suas tarefas, portanto os parênteses depois do nome do método no diagrama de classe estão vazios, da mesma maneira como estavam na declaração do método na linha 7 da Figura 3.1. O sinal de adição (+) na frente do nome da operação indica que displayMessage é uma operação public na UML (isto é, um método public no Java). Utilizaremos com frequência os diagramas de classe UML para resumir os atributos e as operações de uma classe.

3.4 Declarando um método com um parâmetro

61

GradeBook + displayMessage( ) Figura 3.3 | Diagrama de classe UML indicando que a classe GradeBook tem uma operação public

displayMessage.

3.4 Declarando um método com um parâmetro Em nossa analogia do carro da Seção 3.2, discutimos o fato de que pressionar o acelerador de um carro envia uma mensagem para ele realizar uma tarefa — fazer o carro andar mais rápido. Mas quanto o carro deve acelerar? Como você sabe, quanto mais pressionar o pedal, mais rápido o carro irá acelerar. Então a mensagem para o carro na verdade inclui a tarefa a ser realizada e informações adicionais que ajudam o carro a executar essa tarefa. Essas informações adicionais são conhecidas como parâmetro — o valor do parâmetro ajuda o carro a determinar com que rapidez acelerar. De maneira semelhante, um método pode exigir um ou mais parâmetros que representam informações adicionais necessárias para realizar a tarefa. Os parâmetros são definidos em uma lista de parâmetros separados por vírgula, que está localizada nos parênteses depois do nome do método. Todo parâmetro deve especificar um tipo e um identificador. A lista de parâmetros pode conter qualquer número de parâmetros, inclusive nenhum parâmetro. Os parênteses vazios que se seguem ao nome de método (como na Figura 3.1, linha 7) indicam que um método não requer nenhum parâmetro. Uma chamada de método fornece valores — chamados argumentos — para cada um dos parâmetros do método. Por exemplo, o método System.out.println exige um argumento que especifica que dados enviar para a saída em uma janela de comando. De maneira semelhante, para fazer um depósito em uma conta bancária, um método deposit especifica um parâmetro que representa a quantidade de depósito. Quando o método deposit é chamado, um valor de argumento que representa a quantidade de depósito é atribuído ao parâmetro do método. O método então faz um depósito dessa quantia. Nosso próximo exemplo declara a classe GradeBook (Figura 3.4) com um método displayMessage que exibe o nome do curso como parte da mensagem de boas-vindas. (Ver a execução de exemplo na Figura 3.5.) O novo método displayMessage requer um parâmetro que representa o nome do curso a ser enviado para a saída. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura 3.4: GradeBook.java // Declaração de classe com um método que tem um parâmetro. public class GradeBook { // exibe uma mensagem de boas­vindas para o usuário GradeBook public void displayMessage( String courseName ) { System.out.printf( “Welcome to the GradeBook for\n%s!\n”, courseName ); } // fim do método displayMessage } // fim da classe GradeBook

Figura 3.4 | Declaração de classe com um método que tem um parâmetro.

1 2 3 4 5 6 7 8 9 10 11 12 13

// Figura 3.5: GradeBookTest.java // Cria objeto GradeBook e passa uma String para // seu método displayMessage. import java.util.Scanner; // programa utiliza Scanner public class GradeBookTest { // método main inicia a execução de programa public static void main( String[] args ) { // cria Scanner para obter entrada a partir da janela de comando Scanner input = new Scanner( System.in );

62 14 15 16 17 18 19 20 21 22 23 24 25 26

Capítulo 3  Introdução a classes e objetos // cria um objeto GradeBook e o atribui a myGradeBook GradeBook myGradeBook = new GradeBook(); // prompt para entrada do nome do curso System.out.println( "Please enter the course name:" ); String nameOfCourse = input.nextLine(); // lê uma linha de texto System.out.println(); // gera saída de uma linha em branco // chama método displayMessage de myGradeBook // e passa nameOfCourse como um argumento myGradeBook.displayMessage( nameOfCourse ); } // fim de main } // fim da classe GradeBookTest

Please enter the course name: CS101 Introduction to Java Programming Welcome to the GradeBook for CS101 Introduction to Java Programming!

Figura 3.5  |  Criando um objeto GradeBook e passando uma String ao seu método displayMessage.

Antes de discutir os novos recursos da classe GradeBook, vejamos como a nova classe é utilizada a partir do método main da classe GradeBookTest (Figura 3.5). A linha 12 cria um Scanner chamado input para ler o nome do curso fornecido pelo usuário. A linha 15 cria um objeto da classe GradeBook e a atribui à variável myGradeBook. A linha 18 exibe um prompt que pede para o usuário inserir o nome de um curso. A linha 19 mostra o nome do usuário e o atribui à variável nameOfCourse, utilizando método Scanner nextLine para realizar a entrada. O usuário digita o nome do curso e pressiona Enter para enviar o nome do curso ao programa. Observe que pressionar Enter insere um caractere de nova linha no final dos caracteres digitados pelo usuário. O método nextLine lê os caracteres digitados pelo usuário até o caractere de nova linha ser encontrado, depois retorna uma String que contém o caractere até, mas não incluindo, o caractere de nova linha. O caractere de nova linha é descartado. A classe Scanner também fornece um método semelhante — next — que lê palavras específicas. Quando o usuário pressiona Enter depois de digitar a entrada, o método next lê os caracteres até que um caractere de espaço em branco (como um espaço, tabulação ou nova linha) seja encontrado e retorna uma String contendo os caracteres até, mas sem incluir, o caractere de espaço em branco (que é descartado). Nenhuma informação depois do primeiro caractere de espaço em branco é perdida — essas informações podem ser lidas por outras instruções que chamam os métodos de Scanner posteriormente no programa. A linha 20 gera a saída de uma linha em branco. A linha 24 chama o método displayMessage de myGradeBooks. A variável nameOfCourse entre parênteses é o argumento que é passado ao método displayMessage para o método poder realizar sua tarefa. O valor da variável nameOfCourse em main torna-se o valor do parâmetro courseName do método displayMessage na linha 7 da Figura 3.4. Quando você executa esse aplicativo, observe que o método displayMessage gera a saída do nome que você digita como parte da mensagem de boas-vindas (Figura 3.5).

Mais sobre argumentos e parâmetros Na Figura  3.4, a lista de parâmetros de displayMessage (linha 7) declara um parâmetro indicando que o método requer uma String para executar sua tarefa. Quando o método é chamado, o valor de argumento na chamada é atribuído ao parâmetro correspondente (nesse caso, courseName) no cabeçalho do método. Em seguida, o corpo do método utiliza o valor do parâmetro courseName. As linhas 9–10 da Figura 3.4 exibem o valor do parâmetro courseName, utilizando o especificador de formato %s na string de formato do printf. Observe que o nome da variável de parâmetro (courseName na Figura 3.4, linha 7) pode ser o mesmo nome da variável de argumento ou um nome diferente (courseName na Figura 3.5, linha 24). O número de argumentos em uma chamada de método deve corresponder ao número de parâmetros na lista de parâmetros da declaração do método. Além disso, os tipos de argumento na chamada do método devem ser “consistentes com” os tipos dos parâmetros correspondentes na declaração do método. (Como você aprenderá nos capítulos subsequentes, nem sempre é requerido que um tipo do argumento e o tipo de seu parâmetro correspondente sejam idênticos.) Em nosso exemplo, a chamada de método passa um argumento do tipo String (nameOfCourse é declarada como uma String na linha 19 da Figura 3.5) e a declaração de método especifica um parâmetro de tipo String (courseName é declarado como uma String na linha 7 da Figura 3.4). Portanto, nesse exemplo, o tipo do argumento na chamada de método corresponde exatamente ao tipo do parâmetro no cabeçalho do método. Erro comum de programação 3.2

Ocorrerá um erro de compilação se o número de argumentos em uma chamada de método não corresponder ao número de parâmetros na declaração do método.

3.5 Variáveis de instância, métodos set e get

63

Erro comum de programação 3.3 Ocorrerá um erro de compilação se o tipo de qualquer argumento em uma chamada de método não for consistente com o tipo do parâmetro correspondente na declaração do método.

Diagrama da classe UML atualizado para a classe GradeBook O diagrama de classes UML da Figura 3.6 modela a classe GradeBook da Figura 3.4. Como a Figura 3.1, essa classe GradeBook contém displayMessage, uma operação public. Mas essa versão de displayMessage tem um parâmetro. A UML modela um parâmetro de um modo pouco diferente do Java listando o nome de parâmetro, seguido por dois-pontos e pelo tipo de parâmetro nos parênteses que seguem o nome da operação. A UML tem seus próprios tipos de dados semelhantes àqueles do Java (mas, como você verá, todos os tipos de dados UML têm os mesmos nomes dos tipos de Java correspondentes). O tipo UML String corresponde ao tipo do Java String. displayMessage do método GradeBook (Figura 3.4) tem um parâmetro String chamado courseName, portanto a Figura 3.6 lista courseName: String entre os parênteses depois de displayMessage. GradeBook + displayMessage( courseName : String )

Figura 3.6 | Diagrama de classe UML indicando que a classe GradeBook tem uma operação displayMessage com um parâmetro courseName de tipo UML String.

Notas sobre declarações import Note a declaração import na Figura 3.5 (linha 4). Essa declaração indica ao compilador que o programa utiliza a classe Scanner. Por que precisamos importar a classe Scanner, mas não as classes System, String ou GradeBook? As classes System e String estão no pacote java.lang, que é implicitamente importado para cada programa Java, assim todos os programas podem utilizar as classes desse pacote sem importá-las explicitamente. A maioria das outras classes que você utilizará nos programas Java precisa ser importada explicitamente. Há um relacionamento especial entre as classes que são compiladas no mesmo diretório no disco, como as classes GradeBook e GradeBookTest. Por padrão, essas classes são consideradas pertencentes ao mesmo pacote — conhecido como o pacote padrão. As classes do mesmo pacote são importadas implicitamente para os arquivos de código-fonte de outras classes do mesmo pacote. Assim, uma declaração import não é necessária quando uma classe em um pacote utiliza outra no mesmo pacote — por exemplo, quando a classe GradeBookTest utiliza a classe GradeBook. A declaração import na linha 4 não é requerida se sempre referenciarmos a classe Scanner como java.util.Scanner, o que inclui o nome inteiro do pacote e o nome de classe. Isto é conhecido como nome de classe completamente qualificado da classe. Por exemplo, a linha 12 poderia ser escrita como: java.util.Scanner input = new java.util.Scanner( System.in );

Observação de engenharia de software 3.1

O compilador Java não exigirá as declarações import em um arquivo de código-fonte Java se o nome de classe completamente qualificado for especificado toda vez que um nome de classe for utilizado no código-fonte. A maioria dos programadores Java prefere utilizar declarações import.

3.5 Variáveis de instância, métodos set e get No Capítulo 2, declaramos todas as variáveis de um aplicativo no método main do aplicativo. As variáveis declaradas no corpo de um método particular são conhecidas como variáveis locais e só podem ser utilizadas nesse método. Quando esse método terminar, os valores de suas variáveis locais são perdidos. A partir da Seção 3.2, lembre-se de que um objeto tem atributos que são carregados com o objeto quando ele é utilizado em um programa. Esses atributos existem antes de um método ser chamado em um objeto e depois de o método completar a execução. Uma classe normalmente consiste em um ou mais métodos que manipulam os atributos que pertencem a um objeto particular da classe. Os atributos são representados como variáveis em uma declaração de classe. Essas variáveis são chamadas campos e estão declaradas dentro de uma declaração de classe, mas fora do corpo das declarações de método da classe. Quando cada objeto de uma classe mantém sua própria cópia de um atributo, o campo que representa o atributo também é conhecido como uma variável de instância — cada objeto (instância) da classe tem uma instância separada da variável na memória. O exemplo nesta seção demonstra uma classe GradeBook que contém uma variável de instância courseName para representar o nome do curso de um objeto GradeBook particular.

64

Capítulo 3  Introdução a classes e objetos

Classe GradeBook com uma variável de instância, um método set e um método get Em nosso próximo aplicativo (figuras 3.7 e 3.8), a classe GradeBook (Figura 3.7) mantém o nome do curso como uma variável de instância para que ele possa ser utilizado ou modificado a qualquer hora durante a execução de um aplicativo. A classe contém três métodos — setCourseName, getCourseName e displayMessage. O método setCourseName armazena o nome de um curso de um GradeBook. O método getCourseName obtém o nome do curso de uma GradeBook. O método displayMessage, que agora não especifica nenhum parâmetro, ainda exibe uma mensagem de boas-vindas que inclui o nome do curso; como você verá, o método agora obtém o nome do curso chamando outro método na mesma classe — getCourseName. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

// Figura 3.7: GradeBook.java // classe GradeBook que contém uma variável de instância // courseName e métodos para configurar e obter seu valor. public class GradeBook { private String courseName; // nome do curso para esse GradeBook // método para configurar o nome do curso public void setCourseName( String name ) { courseName = name; // armazena o nome do curso } // fim do método setCourseName // método para recuperar o nome do curso public String getCourseName() { return courseName; } // fim do método getCourseName // exibe uma mensagem de boas-vindas para o usuário GradeBook public void displayMessage() { // chama getCourseName para obter o nome do // o curso que essa GradeBook representa System.out.printf( "Welcome to the GradeBook for\n%s!\n", getCourseName() ); } // fim do método displayMessage } // fim da classe GradeBook

Figura 3.7  |  A classe GradeBook que contém uma variável de instância courseName e métodos para configurar e obter seu valor.

Um instrutor típico dá mais de um curso, cada um com seu próprio nome. A linha 7 declara que courseName é uma variável de tipo String. Como a variável é declarada no corpo da classe, mas fora dos corpos dos métodos da classe (linhas 10–13, 16–19 e 22–28), a linha

7 é uma declaração para uma variável de instância. Toda instância (isto é, objeto) de classe GradeBook contém uma cópia de cada variável de instância. Por exemplo, se houver dois objetos GradeBook, cada objeto terá uma cópia própria de courseName (uma por objeto). Um benefício de tornar courseName uma variável de instância é que todos os métodos da classe (nesse caso, GradeBook) podem manipular qualquer variável de instância que aparece na classe (nesse caso, courseName).

Modificadores de acesso public e private A maioria das declarações de variável de instância é precedida pela palavra-chave private (como na linha 7). Como public, a palavra-chave private é um modificador de acesso. As variáveis ou métodos declarados com o modificador de acesso private só são acessíveis a métodos da classe em que são declarados. Dessa forma, a variável courseName só pode ser utilizada nos métodos setCourseName, getCourseName e displayMessage da (cada objeto da) classe GradeBook. A declaração de variáveis de instância com o modificador de acesso private é conhecida como ocultamento de dados ou ocultamento de informações. Quando um programa cria (instancia) um objeto de classe GradeBook, a variável courseName é encapsulada (ocultada) no objeto e pode ser acessada apenas por métodos da classe do objeto. Isso evita que courseName seja modificado acidentalmente por uma classe em outra parte do programa. Na classe GradeBook, os métodos setCourseName e getCourseName manipulam a variável de instância courseName.



3.5  Variáveis de instância, métodos set e get

65

Observação de engenharia de software 3.2 Anteceda cada campo e declaração de método com um modificador de acesso. Geralmente, as variáveis de instância devem ser declaradas private e os métodos public. (Veremos que é apropriado declarar certos métodos private, se eles forem acessados apenas por outros métodos da classe.)

Boa prática de programação 3.1 Preferimos listar os campos de uma classe primeiro, de modo que, à medida que você leia o código, também veja os nomes e tipos das variáveis antes que sejam utilizados nos métodos da classe. Você pode listar os campos da classe em qualquer lugar na classe fora de suas declarações de método, mas sua dispersão tende a resultar em um código de difícil leitura.

Boa prática de programação 3.2 Coloque uma linha em branco entre as declarações de método para separar os métodos e aprimorar a legibilidade do programa.

Métodos setCourseName e getCourseName O método setCourseName (linhas 10–13) não retorna quaisquer dados quando ele completa sua tarefa, portanto, seu tipo de retorno é void. O método recebe um parâmetro — name — que representa o nome do curso que será passado para o método como um argumento. A linha 12 atribui name à variável de instância courseName. O método getCourseName (linhas 16–19) devolve um objeto courseName específico de GradeBook. O método tem uma lista vazia de parâmetros, então não exige informações adicionais para realizar sua tarefa. O método especifica que ele retorna uma String — esse é o tipo de retorno do método. Quando um método que especifica um tipo de retorno diferente de void for chamado e completar sua tarefa, o método retornará um resultado para seu método chamador. Por exemplo, ao utilizar um caixa eletrônico (Automated Teller Machine — ATM) e solicitar o saldo da sua conta, você espera o ATM devolver um valor que representa seu saldo. De maneira semelhante, quando uma instrução chama o método getCourseName em um objeto GradeBook, a instrução espera receber o nome do curso do GradeBook (nesse caso, um String, como especificado no tipo de retorno da declaração de método). A instrução return na linha 18 passa o valor da variável de instância courseName de volta à instrução que chama o método getCourseName. Considere, a linha 27 do método displayMessage, que chama o método getCourseName. Quando o valor é retornado, a instrução nas linhas 26–27 utiliza esse valor para gerar o nome do curso. De modo semelhante, se você tiver um método square que retorna o quadrado de seu argumento, você esperaria que a instrução: int result = square( 2 );

retornasse 4 a partir do método square e atribuísse 4 à variável result. Se tiver um método maximum que retorna o maior de três argumentos do tipo inteiro, você esperaria que a instrução: int biggest = maximum( 27, 114, 51 );

retornasse 114 a partir do método maximum e atribuísse 114 à variável biggest. Observe que todas as instruções nas linhas 12 e 18 utilizam courseName, embora ele não tenha sido declarado em nenhum dos métodos. Podemos utilizar courseName em métodos GradeBook porque courseName é uma variável de instância da classe. Observe também que a ordem em que os métodos são declarados em uma classe não determina quando eles são chamados em tempo de execução. Então o método getCourseName poderia ser declarado antes de método setCourseName.

O método displayMessage O método displayMessage (linhas 22–28) não retorna quaisquer dados quando ele completa sua tarefa, portanto, seu tipo de retorno é void. O método não recebe parâmetros, então, a lista de parâmetros está vazia. As linhas 26–27 geram saída de uma mensagem de boas-vindas que inclui o valor da variável de instância courseName, que é retornado pela chamada ao método getCourseName na linha 27. Note que um método de uma classe (displayMessage nesse caso) pode chamar outro método da mesma classe utilizando apenas o nome do método (nesse caso, getCourseName). Mais uma vez, precisamos criar um objeto de classe GradeBook e chamar seus métodos antes que a mensagem de boas-vindas possa ser exibida. A classe GradeBookTest que demonstra a classe GradeBook A classe GradeBookTest (Figura 3.8) cria um objeto da classe GradeBook e demonstra seus métodos. A linha 14 cria um objeto GradeBook e o atribui à variável local myGradeBook de tipo GradeBook. As linhas 17–18 exibem o nome do curso inicial que chama o método getCourseName do objeto. Observe que a primeira linha da saída mostra o nome “null”. Diferentemente das variáveis locais, que não são automaticamente inicializadas, todo campo tem um valor inicial padrão — um valor fornecido pelo Java quando você não especifica o valor inicial do campo. Portanto, não é exigido que os campos sejam explicitamente inicializados antes de serem utilizados em um programa — a menos que eles devam ser inicializados para valores diferentes de seus valores padrão. O valor padrão de um campo do tipo String (como courseName nesse exemplo) é null, sobre o qual discutiremos mais na Seção 3.6.

66

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

Capítulo 3  Introdução a classes e objetos

// Figura 3.8: GradeBookTest.java // Criando e manipulando um objeto GradeBook. import java.util.Scanner; // programa utiliza Scanner public class GradeBookTest { // método main inicia a execução de programa public static void main( String[] args ) { // cria Scanner para obter entrada a partir da janela de comando Scanner input = new Scanner( System.in ); // cria um objeto GradeBook e o atribui a myGradeBook GradeBook myGradeBook = new GradeBook(); // exibe valor inicial de courseName System.out.printf( "Initial course name is: %s\n\n", myGradeBook.getCourseName() ); // solicita e lê o nome do curso System.out.println( "Please enter the course name:" ); String theName = input.nextLine(); // lê uma linha de texto myGradeBook.setCourseName( theName ); // configura o nome do curso System.out.println(); // gera saída de uma linha em branco // exibe mensagem de boas-vindas depois de especificar o // nome do curso myGradeBook.displayMessage(); } // fim de main } // fim da classe GradeBookTest

Initial course name is: null Please enter the course name: CS101 Introduction to Java Programming Welcome to the GradeBook for CS101 Introduction to Java Programming!

Figura 3.8  |  Criando e manipulando um objeto GradeBook.

A linha 21 exibe um prompt que pede para usuário inserir o nome de um curso. A variável String theName local (declarada na linha 22) é inicializada com o nome do curso inserido pelo usuário, que é retornado pela chamada ao método nextLine do objeto Scanner input. A linha 23 chama o método setCourseName do objeto myGradeBook e armazena theName como o argumento do método. Quando o método é chamado, o valor do argumento é atribuído ao parâmetro name (linha 10, Figura 3.7) do método setCourseName (linhas 10–13, Figura 3.7). Então o valor do parâmetro é atribuído à variável de instância courseName (linha 12, Figura 3.7). A linha 24 (Figura 3.8) pula uma linha na saída e então a linha 27 chama o método displayMessage do objeto myGradeBook para exibir a mensagem de boas-vindas contendo o nome do curso.

Métodos set e get Os campos private de uma classe só podem ser manipulados pelos métodos de classe. Portanto, um cliente de um objeto — isto é, qualquer classe que chama os métodos do objeto — chama métodos public da classe para manipular os campos private de um objeto da classe. É por isso que as instruções no método main (Figura 3.8) chamam os métodos setCourseName, getCourseName e displayMessage em um objeto GradeBook. As classes costumam fornecer métodos public para permitir a clientes configurar (set, isto é, atribuir valores a) ou obter (get, isto é, obter os valores de) variáveis de instância private. Os nomes desses métodos não precisam começar com set ou get, mas essa convenção de atribuição de nomes é recomendada e requerida para componentes de software Java especiais, denominados JavaBeans, que podem simplificar a programação em muitos ambientes de desenvolvimento integrado Java (Integrated Development Environments — IDEs). O método que configura (set) a variável de instância courseName nesse exemplo chama-se setCourseName e o método que obtém (get) o valor de courseName chama-se getCourseName. Diagrama de classe UML do GradeBook com uma variável de instância e os métodos set e get A Figura 3.9 contém um diagrama de classes UML atualizado para a versão da classe GradeBook na Figura 3.7. Esse diagrama modela a variável de instância courseName da classe GradeBook como um atributo no compartimento no meio da classe. A UML representa as

3.6 Tipos primitivos versus tipos por referência

67

variáveis de instância como atributos listando o nome do atributo, seguido por um caractere de dois-pontos e o tipo de atributo. O tipo UML do atributo courseName é String. A variável de instância courseName é private em Java, portanto, o diagrama de classe lista um modificador de acesso com o sinal de subtração (–) na frente do nome do atributo correspondente. A classe GradeBook contém três métodos public, então o diagrama de classe lista três operações no terceiro compartimento. Lembre-se de que o sinal de adição (+) antes de cada nome de operação indica que a operação é public. A operação setCourseName tem um parâmetro String chamado name. A UML indica o tipo de retorno de uma operação colocando um dois-pontos e o tipo de retorno depois dos parênteses que se seguem ao nome da operação. O método getCourseName da classe GradeBook (Figura 3.7) tem um tipo de retorno String em Java, então o diagrama de classe mostra um tipo de retorno String na UML. Observe que as operações setCourseName e displayMessage não retornam valores (isto é, elas retornam void em Java), portanto, o diagrama de classe UML não especifica um tipo de retorno depois dos parênteses dessas operações. GradeBook – courseName : String + setCourseName( name : String ) + getCourseName( ) : String + displayMessage( )

Figura 3.9 | O diagrama de classes UML que indica que a classe GradeBook tem um atributo courseName privado do tipo UML String e três operações públicas — setCourseName (com um parâmetro name do tipo UML String), getCourseName (que retorna o tipo UML String) e displayMessage.

3.6 Tipos primitivos versus tipos por referência Os tipos do Java são divididos em tipos primitivos e tipos por referência. Os tipos primitivos são boolean, byte, char, short, int, long, float e double. Todos os tipos não primitivos são tipos por referência, portanto, as classes, que especificam os tipos de objeto, são tipos por referência. Uma variável de tipo primitivo pode armazenar exatamente um valor de seu tipo declarado por vez. Por exemplo, uma variável int pode armazenar um número inteiro (como 7) por vez. Quando outro valor for atribuído a essa variável, seu valor inicial será substituído. As variáveis de instância de tipo primitivo são inicializadas por padrão — as variáveis dos tipos byte, char, short, int, long, float e double são inicializadas como 0, e as variáveis do tipo boolean são inicializadas como false. Você pode especificar seu próprio valor inicial para uma variável do tipo primitivo atribuindo à variável um valor na sua declaração. Lembre-se de que as variáveis locais não são inicializadas por padrão.

Dica de prevenção de erro 3.1 Uma tentativa de utilizar uma variável local não inicializada causa um erro de compilação.

Os programas utilizam as variáveis de tipos por referência (normalmente chamados referências) para armazenar as localizações de objetos na memória do computador. Diz-se que tal variável referencia um objeto no programa. Os objetos que são referenciados podem todos conter muitas variáveis de instância e métodos. A linha 14 da Figura 3.8 cria um objeto de classe GradeBook e a variável myGradeBook contém uma referência a esse objeto GradeBook. As variáveis de instância de tipo por referência são inicializadas por padrão com o valor null — uma palavra reservada que representa uma “referência a nada”. Essa é a razão por que a primeira chamada a getCourseName na linha 18 da Figura 3.8 retornou null — o valor de courseName não foi configurado, assim o valor inicial padrão null foi retornado. A lista completa das palavras reservadas e palavras-chave está listada no Apêndice C. Ao utilizar um objeto de outra classe, uma referência ao objeto deve invocar (isto é, chamar) seus métodos. No aplicativo da Figura 3.8, as instruções no método main utilizam a variável myGradeBook para enviar as mensagens para o objeto GradeBook. Essas mensagens são chamadas para os métodos (como setCourseName e getCourseName) que permitem ao programa interagir com o objeto GradeBook. Por exemplo, a instrução na linha 23 utiliza myGradeBook para enviar a mensagem setCourseName ao objeto GradeBook. A mensagem inclui o argumento que setCourseName exige para realizar sua tarefa. O objeto GradeBook utiliza essas informações para configurar a variável de instância courseName. Observe que as variáveis de tipo primitivo não referenciam objetos, então essas variáveis não podem ser utilizadas para invocar métodos.

Observação de engenharia de software 3.3

O tipo declarado de uma variável ( por exemplo, int, double ou GradeBook) indica se a variável é de um tipo primitivo ou tipo por referência. Se o tipo de uma variável não for um dos oito tipos primitivos, então ele é um tipo por referência.

68

Capítulo 3

Introdução a classes e objetos

3.7 Inicializando objetos com construtores Como mencionado na Seção 3.5, quando um objeto de classe GradeBook (Figura 3.7) é criado, sua variável de instância courseName é inicializada como null por padrão. E se você quiser fornecer o nome de um curso quando criar um objeto GradeBook? Cada classe que você declara pode fornecer um método especial chamado construtor que pode ser utilizado para inicializar um objeto de uma classe quando o objeto for criado. De fato, o Java requer uma chamada de construtor para todo objeto que é criado. A palavra-chave new solicita memória do sistema para armazenar um objeto e então chama o construtor da classe correspondente para inicializar o objeto. A chamada é indicada pelos parênteses depois do nome da classe. Um construtor deve ter o mesmo nome que a classe. Por exemplo, a linha 14 da Figura 3.8 utiliza primeiro new para criar um objeto GradeBook. Os parênteses vazios depois de “new GradeBook” indicam uma chamada ao construtor da classe sem argumentos. Por padrão, o compilador fornece um construtor padrão sem parâmetros em qualquer classe que não inclui explicitamente um construtor. Quando uma classe tem somente o construtor padrão, suas variáveis de instância são inicializadas de acordo com seus valores padrões. Ao declarar uma classe, você pode fornecer seu próprio construtor a fim de especificar a inicialização personalizada para objetos de sua classe. Por exemplo, talvez você queira especificar o nome de um curso para um objeto GradeBook quando o objeto é criado, como em: GradeBook myGradeBook = new GradeBook( "CS101 Introduction to Java Programming" );

Nesse caso, o argumento "CS101 Introduction to Java Programming" é passado para o construtor do objeto GradeBook e utilizado para inicializar courseName. A instrução anterior exige que a classe forneça um construtor com um parâmetro String. A Figura 3.10 contém uma classe GradeBook modificada com esse construtor. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

// Figura 3.10: GradeBook.java // Classe GradeBook com um construtor para inicializar o // nome de um curso. public class GradeBook { private String courseName; // nome do curso para esse GradeBook // o construtor inicializa courseName com o argumento String public GradeBook( String name ) { courseName = name; // inicializa courseName } // fim do construtor // método para configurar o nome do curso public void setCourseName( String name ) { courseName = name; // armazena o nome do curso } // fim do método setCourseName // método para recuperar o nome do curso public String getCourseName() { return courseName; } // fim do método getCourseName // exibe uma mensagem de boas­vindas para o usuário GradeBook public void displayMessage() { // essa instrução chama getCourseName para obter o // nome do curso que esse GradeBook representa System.out.printf( "Welcome to the GradeBook for\n%s!\n", getCourseName() ); } // fim do método displayMessage } // fim da classe GradeBook

Figura 3.10 | A classe GradeBook com um construtor para inicializar o nome do curso.

As linhas 9–12 declaram o construtor de GradeBook. Como um método, a lista de parâmetros de um construtor especifica os dados que ele requer para realizar sua tarefa. Ao criar um novo objeto (como faremos na Figura 3.11), esses dados são colocados nos parênteses depois do nome da classe. A linha 9 indica que o construtor tem um parâmetro String chamado name. O name passado para o construtor é atribuído à variável de instância courseName na linha 11.



3.7  Inicializando objetos com construtores

69

A Figura 3.11 inicializa os objetos GradeBook utilizando o construtor. As linhas 11–12 criam e inicializam um objeto GradeBook gradeBook1. O construtor de GradeBook é chamado com o argumento "CS101 Introduction to Java Programming" para inicializar

o nome do curso. A expressão de criação de instância de classe nas linhas 11–12 retorna uma referência ao novo objeto, que é atribuído à variável gradeBook1. As linhas 13–14 repetem esse processo, dessa vez passando o argumento "CS102 Data Structures in Java" para inicializar o nome do curso de gradeBook2. As linhas 17–20 utilizam o método getCourseName de cada objeto para obter os nomes de curso e mostrar que eles foram inicializados quando os objetos foram criados. A saída confirma que cada GradeBook mantém sua própria cópia da variável de instância courseName. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

// Figura 3.11: GradeBookTest.java // construtor GradeBook utilizado para especificar o nome // do curso na hora em que cada objeto GradeBook é criado. public class GradeBookTest { // método main inicia a execução de programa public static void main( String[] args ) { // cria objeto GradeBook GradeBook gradeBook1 = new GradeBook( “CS101 Introduction to Java Programming” ); GradeBook gradeBook2 = new GradeBook( “CS102 Data Structures in Java” ); // exibe valor inicial de courseName para cada GradeBook System.out.printf( "gradeBook1 course name is: %s\n", gradeBook1.getCourseName() ); System.out.printf( "gradeBook2 course name is: %s\n", gradeBook2.getCourseName() ); } // fim de main } // fim da classe GradeBookTest

gradeBook1 course name is: CS101 Introduction to Java Programming gradeBook2 course name is: CS102 Data Structures in Java

Figura 3.11  |  O construtor de GradeBook usado para especificar o nome do curso no momento em que cada objeto GradeBook é criado.

Uma diferença importante entre construtores e métodos é que os construtores não podem retornar valores, portanto, não podem especificar um tipo de retorno (nem mesmo void). Normalmente, os construtores são declarados public. Se uma classe não incluir um construtor, as variáveis de instância da classe são inicializadas como seus valores padrão. Se você declarar qualquer construtor para uma classe, o compilador Java não criará um construtor padrão para essa classe. Assim, não é mais possível criar um objeto GradeBook com new GradeBook() como fizemos nos exemplos anteriores.

Dica de prevenção de erro 3.2

A menos que a inicialização padrão de variáveis de instância de sua classe seja aceitável, forneça um construtor para assegurar que as variáveis de instância da sua classe sejam adequadamente inicializadas com valores significativos quando cada novo objeto de sua classe for criado.

Adicionando o construtor ao diagrama de classe UML da classe GradeBook O diagrama de classe UML da Figura 3.12 modela a classe GradeBook da Figura 3.10, que tem um construtor que possui um parâmetro name de tipo String. Assim como operações, a UML modela construtores no terceiro compartimento de uma classe em um diagrama de classe. Para distinguir entre um construtor e operações de uma classe, a UML requer que a palavra “constructor” seja colocada entre aspas francesas (« e ») antes do nome do construtor. É habitual listar construtores antes de outras operações no terceiro compartimento. GradeBook – courseName : String «constructor» GradeBook( name : String ) + setCourseName( name : String ) + getCourseName( ) : String + displayMessage( )

Figura 3.12  |  Diagrama de classe UML que indica que a classe GradeBook tem um construtor com um parâmetro name de tipo UML String.

70

Capítulo 3

Introdução a classes e objetos

Construtores com múltiplos parâmetros Às vezes é recomendável inicializar objetos com múltiplos dados. Por exemplo, o Exercício 3.11 solicita que você armazene o nome do curso e o nome do instrutor em um objeto GradeBook. Nesse caso, o construtor de GradeBook seria modificado para receber duas Strings, como em: public GradeBook( String courseName, String instructorName )

e você chamaria o construtor de GradeBook desta maneira: GradeBook gradeBook = new GradeBook( "CS101 Introduction to Java Programming", "Sue Green" );

3.8 Números de ponto flutuante e tipo double Agora, temporariamente deixamos de lado nosso estudo de caso GradeBook para declarar uma classe Account que mantém o saldo de uma conta bancária. A maioria dos saldos de conta não é constituída de números inteiros (por exemplo, 0, –22 e 1.024). Por essa razão, a classe Account representa o saldo de conta como um número de ponto flutuante (isto é, um número com um ponto de fração decimal, como 7,33, 0,0975 ou 1.000,12345). O Java fornece dois tipos primitivos para armazenar números de ponto flutuante na memória — float e double. A principal diferença entre eles é que as variáveis double podem armazenar números com maior magnitude e mais detalhes (isto é, mais dígitos à direita do ponto de fração decimal — também conhecido como a precisão do número) do que as variáveis float.

Precisão de número de ponto flutuante e requisitos de memória As variáveis do tipo float representam números de ponto flutuante de precisão simples e podem representar até sete dígitos significativos. As variáveis do tipo double representam números de ponto flutuante de precisão dupla. Essas requerem duas vezes a quantidade de memória das variáveis float e fornecem 15 dígitos significativos — aproximadamente o dobro da precisão de variáveis float. Para o intervalo de valores requerido pela maioria dos programas, as variáveis de tipo float devem bastar, mas você pode utilizar double para “trabalhar com segurança”. Em alguns aplicativos, mesmo as variáveis do tipo double serão inadequadas. A maioria dos programadores representa números de ponto flutuante com o tipo double. De fato, o Java trata todos os números de ponto flutuante que você digita no código-fonte de um programa (como 7,33 e 0,0975) como valores double por padrão. Esses valores no código-fonte são conhecidos como literais de ponto flutuante. Veja o Apêndice D, Tipos primitivos, para informações sobre os intervalos de valores de floats e doubles. Embora os números de ponto flutuante não sejam sempre 100% precisos, eles têm numerosas aplicações. Por exemplo, quando falamos de uma temperatura “normal” de corpo de 98,6 ºF não precisamos ser tão precisos a ponto de envolver um grande número de dígitos. Quando lemos a temperatura de 98,6 ºF em um termômetro, ela pode, de fato, ser 98,5999473210643 ºF. Chamar simplesmente esse número de 98,6 ºF serve para a maioria dos aplicativos que medem temperaturas de corpo. Devido à natureza imprecisa dos números de ponto flutuante, o tipo double é preferido ao tipo float, porque as variáveis double podem representar números de ponto flutuante com mais exatidão. Por essa razão, utilizamos principalmente o tipo double por todo o livro. Para números de ponto flutuante precisos, o Java fornece a classe BigDecimal (pacote java.math). Os números de ponto flutuante também surgem como resultado de divisão. Na aritmética convencional, quando dividimos 10 por 3, o resultado é 3,3333333…, com a sequência de 3 repetindo-se infinitamente. O computador aloca apenas uma quantidade fixa de espaço para armazenar tal valor, portanto evidentemente o valor de ponto flutuante armazenado somente pode ser uma aproximação. Erro comum de programação 3.4

Utilizar números de ponto flutuante de uma maneira que assuma que eles são representados precisamente pode levar a resultados incorretos.

A classe Account com uma variável de instância do tipo double Nosso próximo aplicativo (figuras 3.13–3.14) contém uma classe chamada Account (Figura 3.13) que mantém o saldo de uma conta bancária. Um banco típico atende muitas contas, cada uma com um saldo próprio, portanto a linha 7 declara uma variável de instância chamada balance do tipo double. A variável balance é uma variável de instância porque é declarada no corpo da classe, mas fora das declarações de método da classe (linhas 10–16, 19–22 e 25–28). Cada instância (isto é, objeto) de classe Account contém sua própria cópia de balance. 1 2 3 4 5 6

// Figura 3.13: Account.java // classe Account com um construtor para validar e // inicializa a variável de instância balance do tipo double. public class Account {

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

3.8  Números de ponto flutuante e tipo double

71

private double balance; // variável de instância que armazena o saldo // construtor public Account( double initialBalance ) { // valida que initialBalance é maior que 0,0; // se não, o saldo é inicializado como o valor padrão 0.0 if ( initialBalance > 0.0 ) balance = initialBalance; } // fim do construtor Account // credita (adiciona) uma quantia à conta public void credit( double amount ) { balance = balance + amount; // adiciona quantia ao saldo } // fim do método credit // retorna o saldo da conta public double getBalance() { return balance; // fornece o valor de saldo ao método chamador } // fim do método getBalance } // fim da classe Account

Figura 3.13  |  A classe account com um construtor para validar e inicializar a variável de instância balance do tipo double.

A classe tem um construtor e dois métodos. É comum que alguém que abre uma conta deposite o dinheiro imediatamente, assim o construtor (linhas 10–16) recebe um parâmetro initialBalance do tipo double que representa o saldo inicial. As linhas 14–15 asseguram que initialBalance seja maior do que 0.0. Se for, o valor de initialBalance é atribuído à variável de instância balance. Caso contrário, balance permanece em 0.0 — seu valor inicial padrão. O método credit (linhas 19–22) não retorna quaisquer dados quando ele completa sua tarefa, portanto, seu tipo de retorno é void. O método recebe um parâmetro chamado amount — um valor double que será adicionado ao saldo. A linha 21 adiciona amount ao valor atual de balance, então atribui o resultado ao balance (substituindo assim a quantia do saldo anterior). O método getBalance (linhas 25–28) permite aos clientes da classe (isto é, outras classes que utilizam essa classe) obter o valor do balance de um objeto Account particular. O método especifica o tipo de retorno double e uma lista vazia de parâmetros. Mais uma vez, observe que as instruções nas linhas 15, 21 e 27 utilizam a variável de instância balance mesmo se ela não tiver sido declarada em nenhum dos métodos. Podemos utilizar balance nesses métodos porque ele é uma variável de instância da classe.

A classe AccountTest para utilizar a classe Account A classe AccountTest (Figura 3.14) cria dois objetos Account (linhas 10–11) e os inicializa com 50.00 e –7.53, respectivamente. As linhas 14–17 geram a saída do saldo em cada Account chamando o método getBalance da Account. Quando o método getBalance é chamado por account1 a partir da linha 15, o valor do saldo da account1 é retornado da linha 27 da Figura 3.13 e exibido pela instrução System.out.printf (Figura 3.14, linhas 14–15). De maneira semelhante, quando o método getBalance for chamado por account2 da linha 17, o valor do saldo da account2 é retornado da linha 27 da Figura 3.13 e exibido pela instrução System.out.printf (Figura 3.14, linhas 16–17). Observe que o saldo de account2 é 0.00, porque o construtor assegurou que a conta não poderia iniciar com um saldo negativo. A saída do valor é gerada por printf com o especificador de formato %.2f. O especificador de formato %f é utilizado para gerar saída de valores de tipo float ou double. O .2 entre % e f representa o número de casas decimais (2) que devem ser enviadas para a saída à direita do ponto de fração decimal no número de ponto flutuante — também conhecido como a precisão do número. Qualquer saída de valor de ponto flutuante com %.2f será arredondada para a casa decimal dos centésimos — por exemplo, 123,457 seria arredondado para 123,46; 27,333 seria arredondado para 27,33 e 123,455 para 123,46. 1 2 3 4 5 6 7 8 9 10 11

// Figura 3.14: AccountTest.Java // Entrada e saída de números de ponto flutuante com objetos Account. import java.util.Scanner; public class AccountTest { // método main inicia a execução do aplicativo Java public static void main( String[] args ) { Account account1 = new Account( 50.00 ); // cria o objeto Account Account account2 = new Account( -7.53 ); // cria o objeto Account

72 12 13 14 15 16 17 18 19 20 21 22 23

Capítulo 3  Introdução a classes e objetos

// exibe o saldo inicial de cada objeto System.out.printf( "account1 balance: $%.2f \n", account1.getBalance() ); System.out.printf( "account2 balance: $%.2f \n\n", account2.getBalance() ); // cria Scanner para obter entrada a partir da janela de comando Scanner input = new Scanner( System.in ); double depositAmount; // quantia de depósito lida a partir do usuário System.out.print( "Enter deposit amount for account1: " ); // prompt

24

depositAmount = input.nextDouble(); // entrada do usuário

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

System.out.printf( "\nadding %.2f to account1 balance\n\n", depositAmount ); account1.credit( depositAmount ); // adiciona o saldo de account1 // exibe os saldos System.out.printf( "account1 balance: $%.2f \n", account1.getBalance() ); System.out.printf( "account2 balance: $%.2f \n\n", account2.getBalance() ); // prompt System.out.print( "Enter deposit amount for account2: " ); depositAmount = input.nextDouble(); // entrada do usuário System.out.printf( "\nadding %.2f to account2 balance\n\n", depositAmount ); account2.credit( depositAmount ); // adiciona ao saldo // de account2 // exibe os saldos System.out.printf( "account1 balance: $%.2f \n", account1.getBalance() ); System.out.printf( "account2 balance: $%.2f \n", account2.getBalance() ); } // fim de main } // fim da classe AccountTest

account1 balance: $50.00 account2 balance: $0.00 Enter deposit amount for account1: 25.53 adding 25.53 to account1 balance account1 balance: $75.53 account2 balance: $0.00 Enter deposit amount for account2: 123.45 adding 123.45 to account2 balance account1 balance: $75.53 account2 balance: $123.45

Figura 3.14  |  Entrada e saída de números de ponto flutuante com objetos Account.

A linha 21 declara a variável local depositAmount para armazenar cada quantia de depósito inserida pelo usuário. Ao contrário da variável de instância balance na classe Account, a variável local depositAmount em main não é inicializada como 0.0 por padrão. Entretanto, essa variável não precisa ser inicializada aqui porque seu valor será determinado pela entrada do usuário. A linha 23 pede ao usuário para inserir uma quantia de depósito para account1. A linha 24 obtém a entrada do usuário chamando o método nextDouble do objeto input de Scanner, que retorna um valor double inserido pelo usuário. As linhas 25–26 exibem o quanto foi depositado. A linha 27 chama o método credit do objeto account1 e armazena depositAmount como o argumento do método. Quando o método é chamado, o valor do argumento é atribuído ao parâmetro amount (linha 19 da Figura 3.13) do método credit (linhas 19–22 da Figura 3.13), então o método credit adiciona esse valor ao balance (linha 21 da Figura 3.13). As linhas 30–33 (Figura 3.14) geram a saída dos saldos de ambas as Accounts para mostrar novamente que apenas o saldo de account1 mudou. A linha 35 pede ao usuário para inserir uma quantia de depósito para account2. A linha 36 obtém a entrada do usuário chamando o método nextDouble do objeto Scanner input. As linhas 37–38 exibem o quanto foi depositado. A linha 39 chama o método credit

3.9 (Opcional) Estudo de caso de GUI e imagens gráficas: utilizando caixas de diálogo

73

do objeto account2 e armazena depositAmount como o argumento do método; em seguida, o método credit adiciona esse valor ao saldo. Por fim, as linhas 42–45 geram a saída dos saldos de ambas as Accounts para mostrar novamente que somente o saldo de account2 mudou.

Diagrama de classes UML para a classe Account O diagrama de classes UML na Figura 3.15 modela a classe Account da Figura 3.13. O diagrama modela o atributo private balance com o tipo UML Double para corresponder à variável de instância balance da classe do tipo Java double. O diagrama modela o construtor da classe Account com um parâmetro initialBalance do tipo UML Double no terceiro compartimento da classe. Os dois métodos public da classe também são modelados como operações no terceiro compartimento. O diagrama modela a operação credit com um parâmetro amount do tipo UML Double (porque o método correspondente tem um parâmetro amount do tipo Java double) e a operação getBalance com um tipo de retorno de Double (porque o método Java correspondente retorna um valor double). Account – balance : Double «constructor» Account( initialBalance : Double ) + credit( amount : Double ) + getBalance( ) : Double

Figura 3.15 | O diagrama de classes UML indicando que a classe Account tem um atributo private balance do tipo UML Double, um construtor (com um parâmetro do tipo UML Double) e duas operações public — credit (com um parâmetro amount do tipo UML Double) e getBalance (retorna o tipo UML Double).

3.9 (Opcional) Estudo de caso de GUI e imagens gráficas: utilizando caixas de diálogo Esse estudo de caso opcional é projetado para aqueles que querem começar a aprender as poderosas capacidades do Java para criar interfaces gráficas com o usuário (Graphical User Interfaces — GUIs) e as imagens gráficas iniciais do livro, antes das principais discussões desses tópicos no Capítulo 14, “Componentes GUI: Parte 1”, Capítulo 15, “Imagens gráficas e Java2D”, e Capítulo 25, “Componentes GUI: Parte 2”. O estudo de caso sobre GUI e imagens gráficas aparece em 10 seções curtas (ver Figura 3.16). Cada seção introduz vários novos conceitos e fornece exemplos de capturas de tela que mostram interações e resultados de exemplo. Nas poucas primeiras seções, você criará seus primeiros aplicativos gráficos. Nas próximas seções, você utilizará conceitos da programação orientada a objetos para criar um aplicativo que desenha várias formas. Ao introduzirmos as GUIs formalmente no Capítulo 14, usamos o mouse para escolher exatamente que formas desenhar e onde as desenhar. No Capítulo 15, adicionamos capacidades gráficas da Java 2 API para desenhar as formas com diferentes espessuras de linha e preenchimentos. Esperamos que esse estudo de caso seja informativo e divertido para você. Posição

Título — Exercício(s)

Seção 3.9

Utilizando caixas de diálogo — Entrada e saída básicas com caixas de diálogo

Seção 4.14

Criando desenhos simples — exibindo e desenhando linhas na tela

Seção 5.10

Desenhando retângulos e ovais — Utilizando formas para representar dados

Seção 6.13

Cores e formas preenchidas — Desenhando um alvo e imagens gráficas aleatórias

Seção 7.15

Desenhando arcos — desenhando espirais com arcos

Seção 8.16

Utilizando objetos com elementos gráficos — armazenando formas como objetos

Seção 9.8

Exibindo texto e imagens utilizando rótulos — Fornecendo informações de status

Seção 10.8

Desenhando com polimorfismo — identificando as semelhanças entre as formas

Exercícios 14.17

Expandindo a interface — Utilizando componentes GUI e tratamento de evento

Exercícios 15.31

Adicionando Java 2D — Utilizando a API Java 2D para aprimorar desenhos

Figura 3.16 | Resumo dos estudos de caso de GUI e imagens gráficas em cada capítulo.

74

Capítulo 3  Introdução a classes e objetos

Exibindo texto em uma caixa de diálogo Os programas apresentados até agora exibem a saída na janela de comando. Muitos aplicativos utilizam janelas ou caixas de diálogo (também chamadas diálogos) para exibir a saída. Navegadores Web, como o Mozilla Firefox e o Microsoft Internet Explorer exibem páginas Web em janelas separadas. Os programas de correio eletrônico permitem digitar e ler mensagens em uma janela. Tipicamente, as caixas de diálogo são janelas nas quais os programas exibem mensagens importantes aos usuários. A classe JOptionPane fornece caixas de diálogo pré-construídas que permitem aos programas exibir janelas que contêm mensagens — essas janelas são chamadas de diálogos de mensagem. A Figura 3.17 exibe a String "Welcome \nto\nJava" em uma caixa de diálogo de mensagem. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura 3.17: Dialog1.java // Imprimindo múltiplas linhas na caixa de diálogo. import javax.swing.JOptionPane; // importa classe JOptionPane public class Dialog1 { public static void main( String[] args ) { // exibe um diálogo com uma mensagem JOptionPane.showMessageDialog( null, “Welcome\nto\nJava” ); } // fim de main } // fim da classe Dialog1

Figura 3.17  |  Utilizando JOptionPane para exibir múltiplas linhas em uma caixa de diálogo.

A linha 3 indica que o programa utiliza a classe JOptionPane do pacote javax.swing. Esse pacote contém muitas classes que ajudam-lhe a criar interfaces gráficas com o usuário (Graphical User Interfaces — GUIs) para aplicativos. Componentes GUI facilitam a entrada de dados pelo usuário de um programa e apresentação das saídas ao usuário. A linha 10 chama o método JOptionPane showMessageDialog para exibir uma caixa de diálogo que contém uma mensagem. O método requer dois argumentos. O primeiro ajuda o aplicativo Java a determinar onde posicionar a caixa de diálogo. Um diálogo é tipicamente exibido a partir de um aplicativo GUI em uma janela própria. O primeiro argumento refere-se a essa janela (conhecida como a janela pai) e faz o diálogo aparecer centralizado na janela do aplicativo. Se o primeiro argumento for null, a caixa de diálogo será exibida no centro da tela. O segundo argumento é a String a ser exibida na caixa de diálogo.

Introduzindo métodos static O método JOptionPane showMessageDialog é o chamado de método static. Esses métodos muitas vezes definem tarefas frequentemente utilizadas. Por exemplo, muitos programas exibem caixas de diálogo, e o código para fazer isso sempre é o mesmo. Em vez de exigir que você “reinvente a roda” e crie o código para exibir uma caixa de diálogo, os projetistas da classe JOptionPane declararam um método static que realiza essa tarefa para você. Em geral, um método static é chamado utilizando-se seu nome de classe seguido por um ponto (.) e o nome de método, como em: NomeDaClasse.nomeDoMétodo( argumentos )

Note que você não cria um objeto da classe JOptionPane para utilizar seu método static métodos static mais detalhadamente no Capítulo 6.

showMessageDialog.

Discutiremos

Inserindo texto em uma caixa de diálogo A Figura 3.18 utiliza outra caixa de diálogo JOptionPane predefinida chamada caixa de diálogo de entrada que permite ao usuário inserir dados em um programa. O programa solicita o nome do usuário e responde com uma caixa de diálogo de mensagem que contém uma saudação e o nome que o usuário inseriu.

3.10 Conclusão

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

75

// Figura 3.18: NameDialog.Java // Entrada básica com uma caixa de diálogo. import javax.swing.JOptionPane; public class NameDialog { public static void main( String[] args ) { // pede para o usuário inserir seu nome String name = JOptionPane.showInputDialog( “What is your name?” ); // cria a mensagem String message = String.format( “Welcome, %s, to Java Programming!”, name ); // exibe a mensagem para cumprimentar o usuário pelo nome JOptionPane.showMessageDialog( null, message ); } // fim de main } // termina NameDialog

Figura 3.18 | Obtendo a entrada de usuário a partir de um diálogo.

As linhas 10–11 usam o método showInputDialog de JOptionPane para exibir uma caixa de diálogo de entrada que contém um prompt e um campo (conhecido como campo de texto) no qual o usuário pode inserir o texto. O argumento do método showInputDialog é o prompt que indica o nome que o usuário deve inserir. O usuário digita caracteres no campo de texto, depois clica no botão OK ou pressiona a tecla Enter para retornar a String para o programa. O método showInputDialog (linha 11) retorna uma String contendo os caracteres digitados pelo usuário. Armazenamos a String na variável name (linha 10). [Nota: se você pressionar o botão do diálogo Cancel ou pressionar a tecla Esc, o método retornará null e o programa exibirá a palavra “null” como nome.] As linhas 14–15 usam o método static String format para retornar uma String que contém uma saudação com o nome do usuário. O método format funciona como o método System.out.printf, exceto que format retorna a String formatada em vez de exibi-la em uma janela de comando. A linha 18 exibe a saudação em uma caixa de diálogo de mensagem, assim como fizemos na Figura 3.17.

Exercício do Estudo de caso GUI e imagens gráficas 3.1

Modifique o programa de adição na Figura 2.7 para utilizar entrada e saída baseadas em caixas de diálogo com os métodos da classe JOptionPane. Uma vez que o método showInputDialog retorna uma String, você deve converter a String que o usuário insere em um int para utilização em cálculos. O método static parseInt da classe Integer recebe um argumento String que representa um número inteiro (por exemplo, o resultado de JOptionPane.showInputDialog) e retorna o valor como um int. O método parseInt é um método static da classe Integer (do pacote java.lang). Observe que, se a String não contiver um inteiro válido, o programa terminará com um erro.

3.10 Conclusão Neste capítulo, você aprendeu os conceitos básicos de classes, objetos, métodos e variáveis de instância — esses conceitos serão utilizados na maioria dos aplicativos Java que você criar. Em particular, você aprendeu a declarar variáveis de instância de uma classe para manter os dados para cada objeto da classe e aprendeu a declarar métodos que operam sobre esses dados. Você aprendeu a chamar um método para instruí-lo a executar a tarefa e a passar informações para métodos como argumentos. Você aprendeu a diferença entre variável local de um método e variável de instância de uma classe e que apenas variáveis de instância são inicializadas automaticamente. Você também aprendeu a utilizar um construtor da classe para especificar os valores iniciais para as variáveis de instância de um objeto. Por todo o capítulo, você viu como a UML pode ser utilizada para criar diagramas de classe que modelam os construtores, métodos e atributos de classes. Por fim, você aprendeu a lidar com os números de ponto flutuante — como armazená-los com variáveis de tipo primitivo double, como inseri-los com um objeto Scanner, e como formatá-los com printf e o especificador %f para propósitos de exibição. No próximo capítulo iniciamos nossa introdução às instruções de controle, que especificam a ordem em que as ações de um programa são realizadas. Você as utilizará em seus métodos para especificar como eles devem realizar suas tarefas.

76

Capítulo 3  Introdução a classes e objetos

Resumo Seção 3.2  Classes, objetos, métodos e variáveis de instância • Para realizar uma tarefa em um programa é necessário um método. Dentro do método você insere os mecanismos para que ele faça suas tarefas. • A unidade de programa que abriga um método é chamada de classe. Uma classe pode conter um ou mais métodos que são projetados para realizar as tarefas da classe. • Um método pode realizar uma tarefa e retornar um resultado. • Uma classe pode ser utilizada para criar uma instância da classe chamada de objeto. • Uma chamada de método instrui um método a executar sua tarefa. • Todo método pode especificar parâmetros que representam informações adicionais que o método exige para realizar corretamente sua tarefa. Uma chamada de método fornece argumentos aos parâmetros do método. • Um objeto tem atributos que são carregados com o objeto quando ele é utilizado em um programa. Esses atributos são especificados como parte da classe do objeto. Os atributos são especificados em classes por campos.

Seção 3.3  Declarando uma classe com um método e instanciando um objeto de uma classe • Toda declaração de classe que inicia com o modificador de acesso public deve ser armazenada em um arquivo que tem exatamente o mesmo nome que a classe e termina com a extensão de nome do arquivo .java. • Cada declaração de classe contém a palavra-chave class seguida imediatamente do nome da classe. • Uma declaração de método que começa com a palavra-chave public indica que o método pode ser chamado por outras classes declaradas fora da declaração de classe. • A palavra-chave void indica que um método realizará uma tarefa, mas não retornará nenhuma informação. • Por convenção, os nomes de método iniciam com a primeira letra minúscula e todas as palavras subsequentes no nome iniciam com a primeira letra maiúscula. • Os parênteses vazios que seguem um nome de método indicam que o método não requer nenhum parâmetro para realizar sua tarefa. • O corpo de todos os métodos é delimitado pelas chaves esquerda e direita ({ e }). • O corpo do método contém instruções que realizam a tarefa do método. Depois que as instruções executam, o método completou sua tarefa. • Quando você tentar executar uma classe, o Java procura o método main da classe para iniciar a execução. • Em geral, você não pode chamar um método de outra classe até criar um objeto dessa classe. • As expressões de criação de instância de classe que iniciam com palavra-chave new criam novos objetos. • Para chamar um método de um objeto, o nome da variável deve ser seguido de um separador ponto (.), do nome de método e de um conjunto de parênteses que contém os argumentos do método. • Na UML, cada classe é modelada em um diagrama de classe como um retângulo com três compartimentos. Aquele na parte superior contém o nome da classe centralizado horizontalmente em negrito. O do meio contém os atributos da classe, que correspondem a campos em Java. O inferior contém as operações da classe, que correspondem a métodos e construtores em Java. • A UML modela operações listando o nome da operação seguido por um conjunto de parênteses. Um sinal de adição (+) na frente do nome da operação indica que é uma operação public na UML (isto é, um método public em Java).

Seção 3.4  Declarando um método com um parâmetro • Os métodos costumam exigir informações adicionais para realizar suas tarefas. Essas informações adicionais são fornecidas para os métodos via argumentos em chamadas de método. • O método Scanner nextLine lê os caracteres até um caractere de nova linha ser encontrado, depois retorna os caracteres como um método String. • O método Scanner next lê os caracteres até um caractere de espaço em branco ser encontrado, então retorna os caracteres como uma String. • Um método que requer dados para realizar sua tarefa deve especificar isso em sua declaração colocando informações adicionais na lista de parâmetros do método. • Todo parâmetro deve especificar um tipo e um identificador. • Na hora em que um método é chamado, seus argumentos são atribuídos a seus parâmetros. Então o corpo de método utiliza as variáveis de parâmetro para acessar os valores de argumento. • Um método especifica múltiplos parâmetros em uma lista separada por vírgula. • O número de argumentos na chamada de método deve corresponder ao número de parâmetros na lista de parâmetros da declaração do método. Além disso, os tipos de argumento na chamada de método devem ser consistentes com os tipos dos parâmetros correspondentes na declaração do método. • A classe String está no pacote java.lang, que é importado implicitamente em todos os arquivos de código-fonte. • Há um relacionamento especial entre as classes que são compiladas no mesmo diretório no disco. Por padrão, essas classes são consideradas como estando no mesmo pacote. As classes do mesmo pacote são importadas implicitamente para os arquivos de código-fonte de outras classes do mesmo pacote.



Resumo

77

• Declarações import não são necessárias se você sempre utilizar nomes de classe totalmente qualificados. • A UML modela um parâmetro de uma operação listando o nome do parâmetro, seguido por um caractere de dois-pontos e o tipo de parâmetro entre os parênteses depois do nome de operação. • A UML tem seus próprios tipos de dados semelhantes aos do Java. Nem todos os tipos de dados UML têm os mesmos nomes que os tipos Java correspondentes. • O tipo String da UML corresponde ao tipo String do Java.

Seção 3.5  Variáveis de instância, métodos set e get • As variáveis declaradas no corpo de um método são variáveis locais e só podem ser utilizadas nesse método. • Normalmente, uma classe consiste em um ou mais métodos que manipulam os atributos (dados) que pertencem a um objeto particular da classe. Essas variáveis são chamadas campos e estão declaradas dentro de uma declaração de classe, mas fora do corpo das declarações de método da classe. • Quando cada objeto de uma classe mantém sua própria cópia de um atributo, o campo correspondente é conhecido como uma variável de instância. • As variáveis ou métodos declarados com o modificador de acesso private só são acessíveis a métodos da classe em que são declarados. • A declaração de variáveis de instância com o modificador de acesso private é conhecida como ocultamento de dados. • Um benefício dos campos é que todos os métodos da classe podem usar os campos. Outra distinção entre um campo e uma variável local é que o campo tem um valor inicial padrão fornecido pelo Java quando você não especifica o valor inicial do campo, mas uma variável local não. • O valor padrão de um campo do tipo String (ou qualquer outro tipo por referência) é null. • Quando um método que especifica um tipo de retorno for chamado e completar sua tarefa, o método retornará um resultado para seu método chamador. • As classes costumam fornecer métodos public para permitir aos clientes da classe configurar (set) ou obter (get) variáveis de instância private. Os nomes desses métodos não precisam iniciar com set ou get, mas essa convenção de atribuição de nomes é recomendada e requerida para componentes de software Java especiais chamados JavaBeans. • A UML representa variáveis de instância como um nome de atributo, seguido por um dois-pontos e o tipo. • Os atributos privados são precedidos por um sinal de subtração (–) na UML. • A UML indica o tipo de retorno de uma operação colocando um dois-pontos e o tipo de retorno depois dos parênteses que se seguem ao nome da operação. • Os diagramas de classe UML não especificam tipos de retorno para operações que não retornam valores.

Seção 3.6  Tipos primitivos versus tipos por referência • Tipos no Java são divididos em duas categorias — tipos primitivos e tipos por referência. Os tipos primitivos são boolean, byte, char, short, int, long, float e double. Todos os outros são tipos por referência; portanto, classes, que especificam os tipos de objetos, são tipos por referência. • Uma variável de tipo primitivo pode armazenar exatamente um valor de seu tipo declarado por vez. • As variáveis de instância de tipo primitivo são inicializadas por padrão. Variáveis dos tipos byt, char, short, int, long, float e double são inicializadas como 0. As variáveis de tipo boolean são inicializadas como false. • Os programas utilizam as variáveis de tipos por referência (chamadas de referências) armazenam a localização de um objeto na memória do computador. Essas variáveis referenciam objetos no programa. O objeto que é referenciado pode conter muitas variáveis de instância e métodos. • Os campos de tipo por referência são inicializados por padrão como o valor null. • Para invocar métodos de instância de um objeto é necessária uma referência a um objeto. Uma variável de tipo primitivo não referencia um objeto e, portanto, não pode ser utilizada para invocar um método.

Seção 3.7  Inicializando objetos com construtores • A palavra-chave new solicita memória do sistema para armazenar um objeto e então chama o construtor da classe correspondente para inicializar o objeto. • Um construtor pode ser utilizado para inicializar o objeto de uma classe quando o objeto é criado. • Os construtores podem especificar parâmetros mas não podem especificar tipos de retorno. • Se uma classe não definir construtores, o compilador fornecerá um construtor padrão sem parâmetros, e as variáveis de instância da classe serão inicializadas com seus valores padrão. • Assim como as operações, a UML modela construtores no terceiro compartimento de um diagrama de classe. Para distinguir entre um construtor e operações de uma classe, a UML coloca a palavra “constructor” entre aspas francesas (« e ») antes do nome do construtor.

Seção 3.8  Números de ponto flutuante e tipo double • Número de ponto flutuante é um número com um ponto de fração decimal, como 7,33, 0,0975 ou 1.000,12345. O Java fornece dois tipos primitivos para armazenar números de ponto flutuante na memória — float e double. A principal diferença entre esses tipos é que as variáveis double podem armazenar números com maior magnitude e maior detalhe (conhecida como precisão do número) do que as variáveis float. • As variáveis de tipo float representam números de ponto flutuante de precisão simples e têm sete dígitos significativos. As variáveis de tipo double representam números de ponto flutuante de dupla precisão. Essas requerem duas vezes a quantidade de memória das variáveis float e fornecem 15 dígitos significativos — aproximadamente o dobro da precisão de variáveis float.

78

Capítulo 3  Introdução a classes e objetos

• Por padrão, esses literais de ponto flutuante são do tipo double. • O método scanner nextDouble retorna um valor double. • O especificador de formato %f é utilizado para gerar saída de valores de tipo float ou double. O especificador de formato %.2f especifica que dois dígitos da precisão devem ser gerados à direita do ponto decimal no número de ponto flutuante. • O valor padrão de um campo de tipo double é 0.0, e o valor padrão de um campo de tipo int é 0.

Terminologia %f, especificador de formato, 71 ., ponto separador, 60

aspas francesas (« e ») na UML, 69 cabeçalho de método, 59 caixa de diálogo, 74 campo, 63 campo de texto, 75 chamada de método, 57 cliente de um objeto, 66 compartimento, 60 construtor, 60 construtor padrão, 68 declarar um método de uma classe, 58 diagrama de classes na UML, 60 diálogo, 74 diálogo de entrada, 74 diálogo de mensagem, 74 double, tipo primitivo, 70 enviar uma mensagem para um objeto, 57 expressão de criação de instância de classe, 60 float, tipo primitivo, 70 get, método, 66

instanciar um objeto de uma classe, 58 interface gráfica com usuário (Graphical User Interface — GUI), 74 invocar um método, 67 javax.swing, pacote, 74 linguagem extensível, 60 lista de parâmetros, 61 literal de ponto flutuante, 70 método chamador, 59 modificador de acesso, 59 new, palavra-chave, 60 next, método da classe Scanner, 62 nextDouble, método da classe Scanner, 72 nextLine, método da classe Scanner, 62 nome de classe completamente qualificado, 63 null, palavra-chave, 67 número de ponto flutuante de precisão dupla, 70 número de ponto flutuante de precisão simples, 70 obter (get) um valor, 66 ocultamento de dados, 64 operação, 60

pacote padrão, 63 parâmetro, 61 ponto flutuante, número, 70 ponto separador (.), 60 precisão, 71 precisão de um número de ponto flutuante formatado, 71 private, palavra-chave, 64 public, palavra-chave, 64 referência, 67 referenciar um objeto, 67 return, palavra-chave, 65 set, método, 66 showInputDialog, método da classe JOptionPane, 75 showMessageDialog, método da classe JOptionPane, 74 tipo por referência, 67 valor inicial padrão, 65 valor padrão, 65 variável de instância, 58 variável local, 63

Exercícios de autorrevisão 3.1

Preencha as lacunas em cada um dos seguintes: a) Uma casa está para uma planta arquitetônica assim como um(a) ________ está para uma classe. b) Toda declaração de classe que inicia com a palavra-chave ________ deve ser armazenada em um arquivo que tem exatamente o mesmo nome que a classe e terminar com a extensão de nome do arquivo .java. c) A palavra-chave ________ em uma declaração de classe é imediatamente seguida pelo nome da classe. d) A palavra-chave ________ solicita memória do sistema para armazenar um objeto e então chama o construtor da classe correspondente para inicializar o objeto. e) Todo parâmetro deve especificar um(a) ________ e um(a) ________. f) Por padrão, as classes que são compiladas no mesmo diretório são consideradas como estando no mesmo pacote, conhecido como ________. g) Quando cada objeto de uma classe mantém sua própria cópia de um atributo, o campo que representa o atributo também é conhecido como um(a) ________. h) O Java fornece dois tipos primitivos para armazenar números de ponto flutuante na memória: ________ e ________. i) As variáveis de tipo double representam números de ponto flutuante ________. j) O método scanner ________ retorna um valor double. k) A palavra-chave public é um ________ de acesso. l) O tipo de retorno ________ indica que um método não retornará um valor. m) O método Scanner ________ lê os caracteres de até um caractere de nova linha ser encontrado e então retorna esses caracteres como uma String. n) A classe String está no pacote ________. o) Um(a) ________ não é requerido(a) se você sempre referenciar uma classe por meio do seu nome de classe completamente qualificado. p) Um(a) ________ é um número com um ponto de fração decimal, como 7,33, 0,0975 ou 1.000,12345. q) As variáveis de tipo float representam números de ponto flutuante ________. r) O especificador de formato ________ é utilizado para gerar saída de valores de tipo float ou double. s) Os tipos no Java são divididos em duas categorias — tipos ________ e tipos ________.



Respostas dos exercícios de autorrevisão

79

3.2

Determine se cada um dos seguintes é verdadeiro ou falso. Se falso, explique por quê. a) Por convenção, os nomes de método iniciam com a primeira letra maiúscula e todas as palavras subsequentes do nome iniciam com a primeira letra maiúscula. b) Uma declaração import não é necessária quando uma classe em um pacote utiliza outra no mesmo pacote. c) Parênteses vazios que se seguem a um nome de método em uma declaração de método indicam que o método não requer nenhum parâmetro para realizar sua tarefa. d) As variáveis ou métodos declarados com o modificador de acesso private só são acessíveis a métodos da classe em que são declarados. e) Uma variável de tipo primitivo pode ser utilizada para invocar um método. f) As variáveis declaradas no corpo de um método particular são conhecidas como variáveis de instância e podem ser utilizadas em todos os métodos da classe. g) O corpo de todos os métodos é delimitado pelas chaves esquerda e direita ({ e }). h) As variáveis locais de tipo primitivo são inicializadas por padrão. i) As variáveis de instância de tipo por referência são inicializadas por padrão para o valor null. j) Qualquer classe que contém public static void main (String [] args) pode ser utilizada para executar um aplicativo. k) O número de argumentos na chamada de método deve corresponder ao número de parâmetros na lista de parâmetros da declaração do método. l) Os valores de ponto flutuante que aparecem no código-fonte são conhecidos como literais de ponto flutuante e são tipos float por padrão.

3.3

Qual diferença entre uma variável local e um campo?

3.4

Explique o propósito de um parâmetro de método. Qual a diferença entre um parâmetro e um argumento?

Respostas dos exercícios de autorrevisão 3.1

a) objeto. b) public. c) class. d) new. e) tipo, nome. f) pacote padrão. g) variável de instância. h) float, double. i) precisão dupla. j) next­ Double. k) modificador. l) void. m) nextLine. n) java.lang. o) declaração import. p) número de ponto flutuante. q) precisão única. r) %f. s) primitivo, referência. 3.2 a) Falso. Por convenção, os nomes de método iniciam com a primeira letra minúscula e todas as palavras subsequentes no nome iniciam com a primeira letra maiúscula. b) Verdadeiro. c) Verdadeiro. d) Verdadeiro. e) Falso. Uma variável de tipo primitivo não pode ser utilizada para invocar um método — uma referência a um objeto é necessária para que os métodos do objeto possam ser invocados. f) Falso. Essas variáveis são chamadas de variáveis locais e podem ser utilizadas somente no método em que são declaradas. g) Verdadeiro. h) Falso. As variáveis de instância de tipo primitivo são inicializadas por padrão. Deve-se atribuir um valor explicitamente a cada variável local . i) Verdadeiro. j) Verdadeiro. k) Verdadeiro. l) Falso. Esses literais são de tipo double por padrão. 3.3 Uma variável local é declarada no corpo de um método e só pode ser utilizada do ponto em que ela é declarada até o fim da declaração do método. Um campo é declarado em uma classe, mas não no corpo de qualquer um dos métodos da classe. Além disso, os campos são acessíveis a todos os métodos da classe. (Veremos uma exceção a isso no Capítulo 8.) 3.4 Um parâmetro representa informações adicionais que um método requer para realizar sua tarefa. Cada parâmetro requerido por um método é especificado na declaração do método. Um argumento é o valor real de um parâmetro de método. Quando um método é chamado, os valores de argumento são passados para os parâmetros correspondentes do método para que ele possa realizar sua tarefa.

Exercícios 3.5

Qual é o propósito da palavra-chave new? Explique o que acontece quando você a utiliza.

3.6

O que é um construtor padrão? Como as variáveis de instância de um objeto são inicializadas se uma classe tiver somente um construtor padrão?

3.7

Explique o propósito de uma variável de instância.

3.8

A maioria de classes precisa ser importada antes de poder ser utilizada em um aplicativo. Por que cada aplicativo pode utilizar as classes System e String sem antes importá-las?

3.9

Explique como um programa pode utilizar a classe Scanner sem importar a classe.

3.10 Explique por que uma classe pode fornecer um método set e um método get para uma variável de instância. 3.11 (Classe GradeBook modificada) Modifique a classe GradeBook (Figura 3.10) como a seguir: a) Inclua uma variável de instância String que representa o nome do instrutor do curso. b) Forneça um método set para alterar o nome do instrutor e um método get para recuperá-lo. c) Modifique o construtor para especificar dois parâmetros — um para o nome do curso e um para o nome do instrutor. d) Modifique o método displayMessage para gerar saída da mensagem de boas-vindas e do nome do curso, seguido por "This  course  is  presented by:" e o nome do professor.

Utilize sua classe modificada em um aplicativo de teste que demonstra as novas capacidades da classe. 3.12 (Classe Account modificada) Modifique a classe Account (Figura 3.13) para fornecer um método chamado debit que retira dinheiro de uma Account. Assegure

que a quantidade de débito não exceda o saldo de Account. Se exceder, o saldo deve ser deixado inalterado e o método deve

80

Capítulo 3  Introdução a classes e objetos imprimir uma mensagem que indica "Debit amount exceeded account balance" Modifique a classe AccountTest (Figura 3.14) para testar o método debit.

[Quantia de débito excedeu o saldo da conta].

3.13 (Classe Invoice) Crie uma classe chamada Invoice para que uma loja de suprimentos de informática possa utilizá-la para representar

uma fatura de um item vendido na loja. Uma Invoice (fatura) deve incluir quatro partes das informações como variáveis de instância — o número (tipo String), a descrição (tipo String), a quantidade comprada de um item (tipo int) e o preço por item (double). Sua classe deve ter um construtor que inicializa as quatro variáveis de instância. Forneça um método set e um get para cada variável de instância. Além disso, forneça um método chamado getInvoiceAmount que calcula a quantidade de fatura (isto é, multiplica a quantidade pelo preço por item), e depois retorna a quantidade como um valor double. Se a quantidade não for positiva, ela deve ser configurada como 0. Se o preço por item não for positivo, ele deve ser configurado como 0.0. Escreva um aplicativo de teste chamado InvoiceTest que demonstra as capacidades da classe Invoice.

3.14 (Classe Employee) Crie uma classe chamada Employee que inclua três variáveis de instância — um primeiro nome (tipo String), um sobre-

nome (tipo String) e um salário mensal (double). Forneça um construtor que inicializa as três variáveis de instância. Forneça um método set e um get para cada variável de instância. Se o salário mensal não for positivo, não configure seu valor. Escreva um aplicativo de teste chamado EmployeeTest que demonstra as capacidades da classe Employee. Crie dois objetos Employee e exiba o salário anual de cada objeto. Então dê a cada Employee um aumento de 10% e exiba novamente o salário anual de cada Employee.

3.15 (Classe Date) Crie uma classe chamada Date que inclua três variáveis de instância — mês (tipo int), dia (tipo int) e ano (tipo int). Forneça

um construtor que inicializa as três variáveis de instância e suponha que os valores fornecidos estejam corretos. Forneça um método set e um get para cada variável de instância. Forneça um método displayDate que exibe o mês, o dia e o ano separados por barras normais (/). Escreva um aplicativo de teste chamado DateTest que demonstra as capacidades da classe Date.

Fazendo a diferença 3.16 (Calculadora de frequência cardíaca-alvo) Ao realizar exercícios físicos, você pode utilizar um monitor de frequência cardíaca para ver

se sua frequência permanece dentro de um intervalo seguro, sugerido pelos seus treinadores e médicos. Segundo a American Heart Association (AHA) (www.americanheart.org/presenter.jhtml?identifier=4736), a fórmula para calcular a frequência cardíaca máxima por minuto é 220 menos a idade. Sua frequência cardíaca alvo é um intervalo entre 50–85% da frequência cardíaca máxima. [Nota: essas fórmulas são estimativas fornecidas pela AHA. As frequências cardíacas máximas e o alvo podem variar com base na saúde, capacidade física e sexo da pessoa. Sempre consulte um médico ou profissional de saúde qualificado antes de começar ou modificar um programa de exercícios físicos.] Crie uma classe chamada HeartRates. Os atributos da classe devem incluir o nome, sobrenome e data de nascimento da pessoa (consistindo em atributos separados para o mês, dia e ano de nascimento). Sua classe deve ter um construtor que recebe esses dados como parâmetros. Para cada atributo forneça métodos set e get. A classe também deve incluir um método que calcula e retorna a idade da pessoa (em anos), um método que calcula e retorna a frequência cardíaca máxima da pessoa e um método que calcula e retorna a frequência cardíaca-alvo da pessoa. Escreva um aplicativo Java que solicite as informações da pessoa, instancie um objeto da classe HeartRates e imprime as informações a partir desse objeto — incluindo o nome, sobrenome e data de nascimento da pessoa — calcule e imprima a idade da pessoa (em anos), intervalo de frequência cardíaca máxima e frequência cardíaca-alvo.

3.17 (Computadorização dos registros de saúde) Uma questão relacionada à assistência médica discutida ultimamente nos veículos de comu-

nicação é a computadorização dos registros de saúde. Essa possibilidade está sendo abordada cautelosamente por causa de preocupações quanto à privacidade e segurança de dados sigilosos, entre outros. [Iremos discutir essas preocupações nos exercícios mais adiante.] A computadorização dos registros de saúde pode tornar mais fácil que pacientes compartilhem seus perfis e históricos de saúde entre vários profissionais de saúde. Isso pode aprimorar a qualidade da assistência médica, ajudar a evitar conflitos de medicamentos e prescrições de medicamentos errados, reduzir custos em ambulatórios e poderia salvar vidas. Nesse exercício, você projetará uma classe HealthProfile “inicial” para uma pessoa. Os atributos da classe devem incluir o nome, sobrenome, sexo, data de nascimento (consistindo em atributos separados para o mês, dia e ano de nascimento), altura (em polegadas) e peso (em libras) da pessoa. Sua classe deve ter um construtor que recebe esses dados. Para cada atributo, forneça métodos set e get. A classe também deve incluir métodos que calculem e retornem a idade do usuário em anos, intervalo de frequência cardíaca máxima e frequência cardíaca-alvo (ver Exercício 3.16) e índice de massa corporal (IMC; ver Exercício 2.33). Escreva um aplicativo Java que solicite as informações da pessoa, instancie um objeto da classe HealthProfile para essa pessoa e imprime as informações a partir desse objeto — incluindo o nome, sobrenome, sexo, data de nascimento, altura e peso da pessoa — e, então, calcule e imprima a idade da pessoa em anos, IMC, intervalo de frequência cardíaca máxima e frequência cardíaca-alvo. Ele também deve exibir o gráfico de “valores IMC” do Exercício 2.33.

Vamos todos dar um passo à frente. — Lewis Carroll

A roda já deu uma volta completa. — William Shakespeare

Quantas maçãs não caíram na cabeça de Newton antes de ele ter entendido a pista! — Robert Frost

Toda a evolução que conhecemos procede do vago para o definido. — Charles Sanders Peirce

4

Instruções de controle: Parte I

Objetivos Neste capítulo, você aprenderá : 

A utilizar técnicas básicas de solução de problemas.



A desenvolver algoritmos por meio do processo de refinamento passo a passo de cima para baixo utilizando pseudocódigo.



A utilizar instruções de seleção if e if...else para escolher entre ações alternativas.



A utilizar a instrução de repetição while para executar instruções em um programa repetidamente.



A utilizar repetição controlada por contador e repetição controlada por sentinela.



A utilizar os operadores compostos de atribuição, incremento e decremento.



A portabilidade dos tipos de dados primitivos.

82

Capítulo 4

Instruções de controle: Parte I

4.1 Introdução

Sumário

4.2 Algoritmos 4.3 Pseudocódigo 4.4 Estruturas de controle 4.5 A instrução de seleção única if 4.6 A instrução de seleção dupla if...else 4.7 A instrução de repetição while 4.8 Formulando algoritmos: repetição controlada por contador

4.9 Formulando algoritmos: repetição controlada por sentinela 4.10 Formulando algoritmos: instruções de controle aninhadas 4.11 Operadores de atribuição composta 4.12 Operadores de incremento e decremento 4.13 Tipos primitivos 4.14 (Opcional) Estudo de caso de GUI e imagens gráficas: criando desenhos simples 4.15 Conclusão

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios | Fazendo a diferença

4.1 Introdução Antes de escrever um programa para resolver um problema, você deve ter um entendimento completo do problema e uma abordagem cuidadosamente planejada para resolvê-lo. Ao escrever um programa, você também deve entender os tipos de blocos de construção que estão disponíveis e empregar técnicas de construção do programa comprovadas. Neste capítulo e no Capítulo 5, “Instruções de controle: Parte 2”, discutimos essas questões na nossa apresentação da teoria e princípios da programação estruturada. Os conceitos apresentados aqui são cruciais na construção de classes e manipulação de objetos. Neste capítulo, introduzimos as instruções if, if...else e while do Java, três dos blocos de construção que permitem-lhe especificar a lógica requerida para que os métodos realizem suas tarefas. Dedicamos uma parte deste capítulo (e os capítulos 5 e 7) para desenvolver em mais detalhes a classe GradeBook introduzida no Capítulo 3. Em particular, adicionamos um método à classe GradeBook que utiliza as instruções de controle para calcular a média de um conjunto de notas de alunos. Um outro exemplo demonstra maneiras adicionais de combinar instruções de controle para resolver um problema semelhante. Apresentamos os operadores de atribuição composta, operadores de incremento e decremento do Java. Por fim, discutimos a portabilidade dos tipos primitivos do Java.

4.2 Algoritmos Qualquer problema de computação pode ser resolvido executando uma série de ações em uma ordem específica. Um procedimento para resolver um problema em termos 1. das ações a executar e 2. da ordem em que essas ações executam chama-se algoritmo. O exemplo a seguir demonstra que é importante especificar corretamente a ordem em que as ações executam. Considere o “algoritmo cresça e brilhe” seguido por um executivo para sair da cama e ir trabalhar: (1) Levantar da cama; (2) tirar o pijama; (3) tomar banho; (4) vestir-se; (5) tomar café da manhã; (6) dirigir o carro até o trabalho. Essa rotina leva o executivo para trabalhar bem preparado para tomar decisões críticas. Suponha que os mesmos passos sejam seguidos em uma ordem um pouco diferente: (1) Levantar da cama; (2) tirar o pijama; (3) vestir-se; (4) tomar banho; (5) tomar café da manhã; (6) dirigir o carro até o trabalho. Nesse caso, nosso executivo chegará ao trabalho completamente molhado. Especificar a ordem em que as instruções (ações) são executadas em um programa é chamado controle de programa. Este capítulo investiga o controle de programa utilizando as instruções de controle do Java.

4.3 Pseudocódigo Pseudocódigo é uma linguagem informal que ajuda a desenvolver algoritmos sem a preocupação com os estritos detalhes da sintaxe da linguagem Java. O pseudocódigo que apresentamos é particularmente útil para desenvolver algoritmos que serão convertidos em partes estruturadas de programas Java. O pseudocódigo é similar à língua cotidiana — é conveniente e amigável ao usuário, mas não é uma linguagem de programação de computador real. Você verá um algoritmo escrito em pseudocódigo na Figura 4.5. O pseudocódigo não é executado nos computadores. Mais exatamente, ele ajuda você a “estudar” um programa antes de tentar escrevê-lo em uma linguagem de programação como Java. Este capítulo fornece vários exemplos de como utilizar o pseudocódigo para desenvolver programas Java. O estilo de pseudocódigo que apresentamos consiste puramente em caracteres, para que você possa digitar o pseudocódigo convenientemente, utilizando um programa editor de textos qualquer. Um programa de pseudocódigo cuidadosamente preparado pode ser facilmente convertido em um programa Java correspondente.

4.4 Estruturas de controle

83

Normalmente, o pseudocódigo só descreve as instruções que representam as ações que ocorrem depois que você converte um programa no pseudocódigo em Java e depois de o programa ser executado em um computador. Essas ações poderiam incluir entrada, saída ou cálculos. Em geral, não incluímos declarações de variáveis em nosso pseudocódigo, mas alguns programadores escolhem listar variáveis e mencionar seus objetivos no início do pseudocódigo.

4.4 Estruturas de controle Normalmente, instruções em um programa são executadas uma após a outra na ordem em que são escritas. Esse processo é chamado execução sequencial. Várias instruções Java, que discutiremos mais adiante, permitirão que você especifique que a próxima instrução a executar não é necessariamente a próxima na sequência. Isso é chamado de transferência de controle. Durante a década de 1960, tornou-se claro que a utilização indiscriminada de transferências de controle era a raiz de muita dificuldade experimentada por grupos de desenvolvimento de software. A culpa disso é a instrução goto (utilizada na maioria das linguagens de programação atuais), que permite ao programador especificar uma transferência de controle para um entre vários destinos em um programa. O termo programação estruturada tornou-se quase sinônimo de “eliminação de goto”. [Nota: O Java não contém uma instrução goto; entretanto, a palavra goto é reservada pelo Java e não deve ser utilizada como um identificador em programas]. A pesquisa de Bohm e Jacopini1 demonstrou ser possível escrever programas sem nenhuma instrução goto. O desafio dos programadores na época era mudar seus estilos para “programação sem goto”. Foi somente em meados da década de 1970 que os programadores começaram a levar a sério a programação estruturada. Os resultados foram impressionantes. Grupos de desenvolvimento de software informaram tempos de desenvolvimento menores, mais frequente cumprimento dos prazos de entrega dos sistemas e mais frequente conclusão dentro do orçamento dos projetos de software. A chave para esses sucessos era, para começar, que programas estruturados eram mais claros, mais fáceis de depurar e modificar e menos propensos a conterem bugs. O trabalho de Bohm e Jacopini demonstrou que todos os programas poderiam ser escritos em termos de somente três estruturas de controle — a estrutura de sequência, a estrutura de seleção e a estrutura de repetição. Ao introduzirmos as implementações das estruturas de controle do Java, na terminologia da especificação da linguagem Java, nós as chamamos “instruções de controle”.

Estrutura de sequência em Java A estrutura de sequência está incorporada ao Java. A não ser que seja instruído de outra forma, o computador executa as instruções Java uma depois da outra na ordem em que elas são escritas — isto é, em sequência. O diagrama de atividades na Figura 4.1 ilustra uma estrutura de sequência típica em que dois cálculos são realizados na ordem. O Java lhe permite ter o número de ações que quiser em uma estrutura de sequência. Como logo veremos, uma única ação pode ser colocada em qualquer lugar; podemos colocar várias ações em sequência. Um diagrama de atividades UML modela o fluxo de trabalho (também chamado atividade) de uma parte de um sistema de software. Esses fluxos de trabalho podem incluir uma parte de um algoritmo, como a estrutura de sequência na Figura 4.1. Os diagramas de atividade são compostos de símbolos, como símbolos de estado de ação (retângulos com os lados esquerdo e direito substituídos por arcos curvados para fora), losangos e pequenos círculos. Esses símbolos são conectados por setas de transição, que representam o fluxo da atividade — isto é, a ordem em que as ações devem ocorrer.

adiciona grade a total

adiciona 1 a counter

Instrução Java correspondente: total = total + grade;

Instrução Java correspondente: counter = counter + 1;

Figura 4.1 | Diagrama de atividades da estrutura de sequência.

Como ocorre com o pseudocódigo, os diagramas de atividades ajudam-lhe a desenvolver e representar algoritmos, embora muitos programadores prefiram o pseudocódigo. Os diagramas de atividades mostram claramente como operam as estruturas de controle. Utilizamos a UML neste capítulo e no Capítulo 5 para mostrar o fluxo de controle em instruções de controle. Nos capítulos 12–13, utilizamos a UML em um estudo de caso de caixa eletrônico real. 1

BOHM, C.; JACOPINI, G. "Flow Diagrams, Turing Machines, and Languages with Only Two Formation Rules", Communications of the ACM, v. 9, n. 5, maio 1966, p. 336–371.

84

Capítulo 4

Instruções de controle: Parte I

Considere o diagrama de atividade de estrutura de sequência na Figura 4.1. Ele contém dois estados de ação que representam ações a ser realizadas. Cada estado da ação contém uma expressão de ação — por exemplo, “adicionar grade a total” ou “adicionar 1 a counter” — que especifica uma ação particular a ser realizada. Outras ações poderiam incluir cálculos ou operações de entrada e saída. As setas no diagrama de atividade representam transições, as quais indicam a ordem em que as ações representadas pelos estados da ação ocorrem. O programa que implementa as atividades ilustradas pelo diagrama na Figura 4.1 primeiro adiciona grade a total e então adiciona 1 a counter. O círculo sólido na parte superior do diagrama de atividade representa o estado inicial — o começo do fluxo de trabalho antes do programa realizar as ações modeladas. O círculo sólido cercado por um círculo vazio que aparece na parte inferior do diagrama representa o estado final — o fim do fluxo de trabalho depois que o programa realiza suas ações. A Figura 4.1 também inclui retângulos com os cantos superiores direitos dobrados. Estes são notas na UML (como comentários em Java) — observações explanatórias que descrevem o propósito dos símbolos no diagrama. A Figura 4.1 utiliza notas da UML para mostrar o código de Java associado com cada estado de ação. Uma linha pontilhada conecta cada nota com o elemento que ele descreve. Os diagramas de atividades normalmente não mostram o código Java que implementa a atividade. Fazemos isso aqui para ilustrar como o diagrama se relaciona ao código Java. Para obter mais informações sobre a UML, consulte o nosso estudo de caso opcional (capítulos 12–13) ou visite www.uml.org.

Instruções de seleção em Java O Java contém três tipos de instruções de seleção (discutidos neste capítulo e no Capítulo 5). A instrução if realiza uma ação (seleciona) se uma condição for verdadeira ou pula a ação se a condição for falsa. A instrução if...else realiza uma ação se uma condição for verdadeira e realiza uma ação diferente se a condição for falsa. A instrução de seleção switch (Capítulo 5) realiza uma de muitas ações diferentes, dependendo do valor de uma expressão. A instrução if é uma instrução de seleção única porque seleciona ou ignora uma única ação (ou, como veremos a seguir, um único grupo de ações). A instrução if...else é chamada instrução de seleção dupla porque seleciona entre duas ações diferentes (ou grupos de ações). A instrução switch é chamada de instrução de seleção múltipla pois seleciona entre muitas ações diferentes (ou grupos de ações). Instruções de repetição em Java O Java fornece três instruções de repetição (também chamadas instruções de loop) que permitem que programas executem instruções repetidamente contanto que uma condição (chamada condição de continuação do loop) permaneça verdadeira. As instruções de repetição são as instruções while, do...while e for. (O Capítulo 5 apresenta as instruções do...while e for.) As instruções while e for realizam a ação (ou grupo de ações) no seu corpo zero ou mais vezes — se a condição de continuação de loop for inicialmente falsa, a ação (ou grupo de ações) não será executada. A instrução do...while realiza a ação (ou grupo de ações) no seu corpo uma ou mais vezes. As palavras if, else, switch, while, do e for são palavras-chave Java. Uma lista completa das palavras-chave Java é apresentada no Apêndice C. Resumo das instruções de controle em Java O Java contém somente três tipos de estruturas de controle, que daqui para a frente chamaremos de instruções de controle: a instrução de sequência, instruções de seleção (três tipos) e instruções de repetição (três tipos). Cada programa é formado combinando a quantidade de instruções apropriada para o algoritmo que o programa implementa. Podemos modelar cada instrução de controle como um diagrama de atividade. Como na Figura 4.1, cada diagrama contém um estado inicial e um estado final que representa um ponto de entrada e um ponto de saída da instrução de controle, respectivamente. As instruções de controle de entrada única/saída única facilitam a construção de programas — basta conectarmos o ponto de saída de uma instrução ao ponto de entrada da instrução seguinte. Chamamos isso de empilhamento de instruções de controle. Aprenderemos que existe apenas outra maneira de conectar instruções de controle — aninhamento de instruções de controle — em que uma instrução de controle aparece dentro da outra. Portanto, algoritmos nos programas Java são construídos a partir de apenas três tipos de instruções de controle, combinadas apenas de duas maneiras. Isso é a essência da simplicidade.

4.5 A instrução de seleção única if Os programas utilizam instruções de seleção para escolher entre cursos alternativos de ações. Por exemplo, suponha que a nota de aprovação de um exame seja 60. A instrução em pseudocódigo: Se a nota do aluno for maior que ou igual a 60 Imprima “Aprovado”

determina se a condição “nota do aluno é maior que ou igual a 60” é verdadeira. Nesse caso “Aprovado” é impresso, e a próxima instrução de pseudocódigo é “realizada”. (Lembre-se de que o pseudocódigo não é uma linguagem de programação real.) Se a condição for falsa, a instrução Imprima é ignorada e a próxima instrução de pseudocódigo na sequência é realizada. O recuo da segunda linha dessa instrução de seleção é opcional, mas recomendável, porque enfatiza a estrutura inerente dos programas estruturados.

4.6 A instrução de seleção dupla if...else

85

A instrução de pseudocódigo If precedente pode ser escrita em Java como: if ( studentGrade >= 60 ) System.out.println( "Passed" );

Observe que o código Java tem uma correspondência precisa com o pseudocódigo. Essa é uma das propriedades do pseudocódigo que torna essa ferramenta de desenvolvimento de programas tão útil. A Figura 4.2 ilustra a instrução de seleção única if. Essa figura contém o símbolo mais importante em um diagrama de atividade — o losango, ou símbolo de decisão, que indica que uma decisão deve ser tomada. O fluxo de trabalho continua ao longo de um caminho determinado pelas condições de guarda do símbolo associado, que podem ser verdadeiras ou falsas. Cada seta de transição que sai de um símbolo de decisão tem uma condição de guarda (especificada entre colchetes ao lado da seta). Se uma condição de guarda for verdadeira, o fluxo de trabalho entra no estado de ação para o qual a seta de transição aponta. Na Figura 4.2, se a nota for maior ou igual a 60, o programa imprime “Aprovado” e então se dirige para o estado final da atividade. Se a nota for menor que 60, o programa se dirige imediatamente para o estado final sem exibir uma mensagem.

[grade >= 60]

imprime “Aprovado”

[grade < 60]

Figura 4.2 | Diagrama de atividades UML de uma instrução de seleção única if.

A instrução if é uma instrução de controle de uma única entrada e uma única saída. Veremos que os diagramas de atividades para as instruções de controle restantes também contêm estados iniciais, setas de transição, estados de ação que indicam ações a realizar, símbolos de decisão (com condições de guarda associadas) que indicam decisões a serem tomadas e estados finais. Pense em sete bins, cada um contendo somente um tipo de instrução de controle Java. As instruções de controle estão todas vazias. Sua tarefa é montar um programa com o número necessário de cada tipo de instrução de controle que o algoritmo demanda, combiná-las somente de duas maneiras possíveis (empilhamento ou aninhamento) e, então, preencher os estados e decisões da ação com expressões e condições de guarda apropriadas para o algoritmo. Discutiremos agora as várias maneiras como as ações e decisões podem ser escritas.

4.6 A instrução de seleção dupla if...else A instrução if de seleção única realiza uma ação indicada somente quando a condição é true; caso contrário, a ação é pulada. A instrução de seleção dupla if...else permite especificar uma ação a realizar quando a condição é verdadeira e uma ação diferente quando a condição é falsa. Por exemplo, este pseudocódigo: Se a nota do aluno for maior que ou igual a 60 Imprima “Aprovado” Caso contrário (Else) Imprima “Reprovado”

imprime “Aprovado” se a nota do aluno for maior ou igual a 60, mas imprime “Reprovado” se for menor que 60. Em qualquer um dos casos, depois que impressão ocorre, a próxima instrução do pseudocódigo na sequência é “realizada”. A instrução If...Else no pseudocódigo anterior pode ser escrita em Java assim: if ( studentGrade >= 60 ) System.out.println( "Passed" ); else System.out.println( "Failed" );

Observe que o corpo do else também é recuado. Qualquer que seja a convenção de recuo escolhida, você deve aplicá-la consistentemente por todos os seus programas.

Boa prática de programação 4.1 Recue as duas instruções do corpo de uma instrução if...else.

86

Capítulo 4  Instruções de controle: Parte I

Boa prática de programação 4.2 Se existem vários níveis de recuo, cada nível deve ser recuado pela mesma quantidade adicional de espaço.

A Figura 4.3 ilustra o fluxo de controle na instrução if...else. Mais uma vez, os símbolos no diagrama de atividades UML (além do estado inicial, setas de transição e estado final) representam os estados e decisões da ação.

imprime “Reprovado”

[grade < 60]

[grade >= 60]

imprime “Aprovado”

Figura 4.3  |  Diagrama de atividades da UML de instrução de seleção dupla if...else.

Operador condicional (?:) O Java fornece o operador condicional (?:) que pode ser utilizado no lugar de uma instrução if...else. Esse é o único operador ternário do Java (operador que recebe três operandos). Juntos, os operandos e o símbolo ?: formam uma expressão condicional. O primeiro operando (à esquerda do ?) é uma expressão boolean (isto é, uma condição que é avaliada como um valor boolean — true ou false), o segundo operando (entre o ? e :) é o valor da expressão condicional se a expressão boolean for true e o terceiro operando (à direita do :) é o valor da expressão condicional se a expressão boolean for avaliada como false. Por exemplo, a instrução: System.out.printIn ( studentGrade>=60 ? "Passed" : "Failed" );

imprime o valor do argumento da expressão condicional de println. A expressão condicional nessa instrução é avaliada para a string "Passed" se a expressão boolean studentGrade >= 60 for verdadeira e para a string "Failed" se a expressão boolean for falsa. Portanto, essa instrução com o operador condicional realiza essencialmente a mesma função da instrução if...else mostrada anteriormente nesta seção. A precedência do operador condicional é baixa, então a expressão condicional inteira normalmente é colocada entre parênteses. Veremos que as expressões condicionais podem ser utilizadas em algumas situações nas quais as instruções if...else não podem.

Boa prática de programação 4.3 As expressões condicionais são mais difíceis de ler que as instruções if...else e devem ser utilizadas para substituir somente instruções if...else simples que escolhem entre dois valores.

Instruções if...else aninhadas Um programa pode testar múltiplos casos colocando instruções if...else dentro de outras instruções if...else para criar instruções if...else aninhadas. Por exemplo, o pseudocódigo a seguir representa uma if...else aninhada que imprime A para notas de exame maiores que ou igual a 90, B para notas de 80 a 89, C para notas de 70 a 79, D para notas de 60 a 69 e F para todas as outras notas: Se a nota do aluno for maior que ou igual a 90 I mprima “A” Caso contrário Se a nota do aluno for maior que ou igual a 80 Imprima “B” Caso contrário Se a nota do aluno for maior que ou igual a 70 Imprima “C” Caso contrário Se a nota do aluno for maior que ou igual a 60 Imprima “D” Caso contrário Imprima “F”



4.6  A instrução de seleção dupla if...else

87

Esse pseudocódigo pode ser escrito em Java como: if ( studentGrade >= 90 ) System.out.println( "A" ); else if ( studentGrade >= 80 ) System.out.println( "B" ); else if ( studentGrade >= 70 ) System.out.println( "C" ); else if ( studentGrade >= 60 ) System.out.println( "D" ); else System.out.println( "F" );

Se a variável studentGrade for maior ou igual a 90, as quatro primeiras condições na instrução if...else aninhada serão verdadeiras, mas somente a instrução na parte if da primeira instrução if...else será executada. Depois que essa instrução for executada, a parte else da instrução if...else “mais externa” é pulada. A maioria dos programadores Java prefere escrever a instrução if...else anterior deste modo: if ( studentGrade >= 90 ) System.out.println( "A" ); else if ( studentGrade >= 80 ) System.out.println( "B" ); else if ( studentGrade >= 70 ) System.out.println( "C" ); else if ( studentGrade >= 60 ) System.out.println( "D" ); else System.out.println( "F" );

As duas formas são idênticas, exceto pelo espaçamento e recuo, que o compilador ignora. A última forma é popular porque evita grande recuo de código para a direta. Tal entrada muitas vezes deixa pouco espaço em uma linha de código-fonte, forçando a divisão de linhas.

O problema do else oscilante O compilador Java sempre associa um else à instrução if imediatamente anterior, a menos que instruído de outro modo pela colocação de chaves ({ e }). Esse comportamento pode levar àquilo que é chamado do problema do else oscilante. Por exemplo: if ( x > 5 ) if ( y > 5 ) System.out.println( "x and y are > 5" ); else System.out.println( "x is 5" é enviada para a saída. Caso contrário, parece que se x não for maior que 5, a parte else do if...else imprime a string "x is  5" ); else System.out.println( "x is 5" — é exibida. Mas, se a segunda condição for falsa, a string "x is 5" ); } else System.out.println( "x is >>" ); // cria botão de cópia copyJButton.addActionListener( new ActionListener() // classe interna anônima { // trata evento de botão public void actionPerformed( ActionEvent event ) { // coloca valores selecionados na copyJList copyJList.setListData( colorJList.getSelectedValues() ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener add( copyJButton ); // adiciona botão de cópia ao JFrame copyJList = new JList(); // cria lista para armazenar nomes de cor copiados copyJList.setVisibleRowCount( 5 ); // mostra 5 linhas copyJList.setFixedCellWidth( 100 ); // configura a largura copyJList.setFixedCellHeight( 15 ); // configura a altura copyJList.setSelectionMode( ListSelectionModel.SINGLE_INTERVAL_SELECTION ); add( new JScrollPane( copyJList ) ); // adiciona lista com scrollpane } // fim do construtor MultipleSelectionFrame } // fim da classe MultipleSelectionFrame

Figura 14.25  |  JList que permite múltiplas seleções.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

// Figura 14.26: MultipleSelectionTest.java // Testando MultipleSelectionFrame. import javax.swing.JFrame; public class MultipleSelectionTest { public static void main( String[] args ) { MultipleSelectionFrame multipleSelectionFrame = new MultipleSelectionFrame(); multipleSelectionFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); multipleSelectionFrame.setSize( 350, 150 ); // configura o tamanho do frame multipleSelectionFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe MultipleSelectionTest

Figura 14.26  |  Teste a classe MultipleSelectionFrame.

14.14 Tratamento de evento de mouse

449

A linha 27 da Figura 14.25 cria JList colorJList e a inicializa com as Strings no array colorNames. A linha 28 configura o número de linhas visíveis em colorJList como 5. As linhas 29–30 especificam que colorJList é uma lista MULTIPLE_INTERVAL_ SELECTION. A linha 31 adiciona um novo JScrollPane contendo colorJList ao JFrame. As linhas 49–55 executam tarefas semelhantes para a copyJList, que é declarada como uma lista SINGLE_INTERVAL_SELECTION. Se uma JList não contiver itens ela não será exibida em um FlowLayout. Por essa razão, as linhas 51–52 utilizam os métodos JList setFixedCellWidth e setFixedCellHeight para configurar a largura da copyJList como 100 pixels e a altura de cada item na JList como 15 pixels, respectivamente. Não há nenhum evento para indicar que um usuário fez múltiplas seleções em uma lista de seleção múltipla. Normalmente, um evento gerado por outro componente GUI (conhecido como um evento externo) especifica quando as múltiplas seleções em uma JList devem ser processadas. Nesse exemplo, o usuário clica no JButton chamado copyJButton para desencadear o evento que copia os itens selecionados em colorJList para copyJList. As linhas 34–45 declaram, criam e registram um ActionListener para o copyJButton. Quando o usuário clica em copyJButton, o método actionPerformed (linhas 39–43) utiliza o método JList setListData para configurar os itens exibidos em copyJList. A linha 42 chama o método getSelectedValues da colorJList, que retorna um array de Objects para representar os itens selecionados em colorJList. Nesse exemplo, o array retornado é passado como o argumento para o método setListData de copyJList. Você poderia estar se perguntando por que copyJList pode ser utilizada na linha 42 embora o aplicativo não crie o objeto referenciado por copyJList até a linha 49. Lembre-se de que o método actionPerformed (linhas 39–43) não executa até que o usuário pressione copyJButton, o que não pode ocorrer até que o construtor complete a execução e o aplicativo exiba a GUI. Nesse ponto na execução do aplicativo, copyJList já é inicializado com um novo objeto Jlist.

14.14 Tratamento de evento de mouse Esta seção apresenta as interfaces ouvintes de evento MouseListener e MouseMotionListener para tratar eventos de mouse. Os eventos de mouse podem ser capturados por qualquer componente GUI que deriva de java.awt.Component. Os métodos de interfaces MouseListener e MouseMotionListener são resumidos na Figura 14.27 O pacote javax.swing.event contém a interface MouseInputListener, que estende as interfaces MouseListener e MouseMotionListener para criar uma única interface que contém todos os métodos MouseListener e MouseMotionListener. Os métodos MouseListener e MouseMotionListener são chamados quando o mouse interage com um Component se objetos listeners de evento apropriados forem registrados para esse Component. Métodos de interface MouseListener e MouseMotionListener

Métodos de interface MouseListener public void mousePressed( MouseEvent event )

Chamado quando um botão do mouse é pressionado enquanto o cursor do mouse estiver sobre um componente. public void mouseClicked( MouseEvent event )

Chamado quando um botão do mouse é pressionado e liberado enquanto o cursor do mouse pairar sobre um componente. Esse evento é sempre precedido por uma chamada para mousePressed. public void mouseReleased( MouseEvent event )

Chamado quando um botão do mouse é liberado depois de ser pressionado. Esse evento sempre é precedido por uma chamada para mousePressed e uma ou mais chamadas para mouseDragged. public void mouseEntered( MouseEvent event )

Chamado quando o cursor do mouse entra nos limites de um componente. public void mouseExited( MouseEvent event )

Chamado quando o cursor do mouse deixa os limites de um componente. Métodos de interface MouseMotionListener public void mouseDragged( MouseEvent event )

Chamado quando o botão do mouse é pressionado enquanto o cursor do mouse estiver sobre um componente e o mouse é movido enquanto o botão do mouse permanecer pressionado. Esse evento é sempre precedido por uma chamada para mousePressed. Todos os eventos de arrastar são enviados para o componente em que o usuário começou a arrastar o mouse. public void mouseMoved( MouseEvent event )

Chamado quando o mouse é movido (sem pressionamentos de botões do mouse) quando o cursor do mouse está sobre um componente. Todos os eventos de movimento são enviados para o componente sobre o qual o mouse atualmente está posicionado. Figura 14.27 | Métodos de interface MouseListener e MouseMotionListener.

450

Capítulo 14  Componentes GUI: Parte 1

Cada um dos métodos de tratamento de evento de mouse recebe como um argumento um objeto MouseEvent que contém informações sobre o evento de mouse que ocorreu, incluindo as coordenadas x e y da localização em que ocorreu o evento. Essas coordenadas são medidas do canto superior esquerdo do componente GUI em que o evento ocorreu. As coordenadas x iniciam em 0 e aumentam da esquerda para a direita. As coordenadas y iniciam em 0 e aumentam de cima para baixo. Os métodos e as constantes de classe InputEvent (superclasse de MouseEvent) permitem-lhe determinar o botão do mouse em que o usuário clicou.

Observação de engenharia de software 14.7 As chamadas de método para mouseDragged são enviadas para o MouseMotionListener do Component em que a operação de arrastar o mouse se iniciou. De maneira semelhante, a chamada de método mouseReleased no fim de uma operação de arrastar é enviada para o MouseListener do Component em que a operação de arrastar iniciou.

O Java também fornece a interface MouseWheelListener para permitir que aplicativos respondam à rotação da roda de um mouse. Essa interface declara o método mouseWheelMoved, que recebe um MouseWheelEvent como seu argumento. A classe MouseWheel­Event (uma subclasse de MouseEvent) contém métodos que permitem que o handler de evento obtenha as informações sobre a quantidade de rotação de roda.

Monitorando eventos de mouse em um JPanel O aplicativo MouseTracker (figuras 14.28–14.29) demonstra os métodos de interface MouseListener e MouseMotionListener. A classe de handler de evento (linhas 36–90) implementa ambas as interfaces. Observe que você deve declarar os sete métodos dessas duas interfaces quando a classe implementar as duas interfaces. Cada evento de mouse nesse exemplo exibe uma String no JLabel chamada statusBar que é anexada à parte inferior da janela. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

// Figura 14.28: MouseTrackerFrame.java // Demonstrando os eventos de mouse. import java.awt.Color; import java.awt.BorderLayout; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseEvent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; public class MouseTrackerFrame extends JFrame { private JPanel mousePanel; // painel em que os eventos de mouse ocorrerão private JLabel statusBar; // rótulo que exibe informações de evento // construtor MouseTrackerFrame configura GUI e // registra handlers de evento de mouse public MouseTrackerFrame() { super( "Demonstrating Mouse Events" ); mousePanel = new JPanel(); // cria painel mousePanel.setBackground( Color.WHITE ); // configura cor de fundo add( mousePanel, BorderLayout.CENTER ); // adiciona painel ao JFrame statusBar = new JLabel( "Mouse outside JPanel" ); add( statusBar, BorderLayout.SOUTH ); // adiciona rótulo ao JFrame // cria e registra listener para mouse e eventos de movimento de mouse MouseHandler handler = new MouseHandler(); mousePanel.addMouseListener( handler ); mousePanel.addMouseMotionListener( handler ); } // construtor de MouseTrackerFrame de fim private class MouseHandler implements MouseListener, MouseMotisnListener { // Handlers de evento de MouseListener // trata evento quando o mouse é liberado imediatamente depois de ter sido pressionado public void mouseClicked( MouseEvent event )

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

14.14  Tratamento de evento de mouse { statusBar.setText( String.format( "Clicked at [%d, %d]", event.getX(),event.getY() ) ); } // fim do método mouseClicked // trata evento quando mouse é pressionado public void mousePressed( MouseEvent event ) { statusBar.setText( String.format( "Pressed at [%d, %d]", event.getX(),event.getY() ) ); } // fim do método mousePressed // trata o evento quando o mouse é liberado public void mouseReleased( MouseEvent event ) { statusBar.setText( String.format( "Released at [%d, %d]", event.getX(),event.getY() ) ); } // fim do método mouseReleased // trata evento quando mouse entra na área public void mouseEntered( MouseEvent event ) { statusBar.setText( String.format( "Mouse entered at [%d, %d]", event.getX(),event.getY() ) ); mousePanel.setBackground( Color.GREEN ); } // fim do método mouseEntered // trata evento quando mouse sai da área public void mouseExited( MouseEvent event ) { statusBar.setText( "Mouse outside JPanel" ); mousePanel.setBackground( Color.WHITE ); } // mouseExited fim do método // Handlers de evento de MouseMotionListener // trata evento quando usuário arrasta o mouse com o botão pressionado public void mouseDragged( MouseEvent event ) { statusBar.setText( String.format( "Dragged at [%d, %d]", event.getX(),event.getY() ) ); } // fim do método mouseDragged // trata evento quando usuário move o mouse public void mouseMoved( MouseEvent event ) { statusBar.setText( String.format( "Moved at [%d, %d]", event.getX(),event.getY() ) ); } // fim do método mouseMoved } // fim da classe interna MouseHandler } // fim da classe MouseTrackerFrame

Figura 14.28  |  Tratamento de evento de mouse. 1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 14.29: MouseTrackerFrame.java // Testando MouseTrackerFrame. import javax.swing.JFrame; public class MouseTracker { public static void main( String[] args ) { MouseTrackerFrame mouseTrackerFrame = new MouseTrackerFrame(); mouseTrackerFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); mouseTrackerFrame.setSize( 300, 100 ); // configura o tamanho do frame mouseTrackerFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe MouseTracker

451

452

Capítulo 14

Componentes GUI: Parte 1

Figura 14.29 | Teste a classe MouseTrackerFrame.

A linha 23 na Figura 14.28 cria JPanel mousePanel. Os eventos de mouse desse JPanel serão monitorados pelo aplicativo. A linha 24 configura a cor de fundo do mousePanel como branco. Quando o usuário mover o mouse no mousePanel, o aplicativo irá mudar a cor de fundo do mousePanel para verde. Quando o usuário mover o mouse fora do mousePanel, o aplicativo mudará a cor de fundo para branco. A linha 25 anexa o mousePanel ao JFrame. Como você aprendeu na Seção 14.5, geralmente deve-se especificar o layout dos componentes GUI em um JFrame. Nessa seção, introduzimos o gerenciador de layout FlowLayout. Aqui usamos o layout padrão do painel de conteúdo de um JFRAME — BorderLayout. Esse gerenciador de layout organiza componentes em cinco regiões: NORTH, SOUTH, EAST, WEST e CENTER. NORTH corresponde à parte superior do contêiner. Esse exemplo utiliza as regiões CENTER e SOUTH. A linha 25 utiliza uma versão de dois argumentos do método add para colocar mousePanel na região CENTER. O BorderLayout dimensiona o componente no CENTER automaticamente para utilizar todo o espaço em JFrame que não é ocupado pelos componentes nas outras regiões. A Seção 14.18.2 discute BorderLayout mais detalhadamente. As linhas 27–28 no construtor declaram JLabel statusBar e o anexam à região SOUTH de JFrame. Esse JLabel ocupa a largura do JFrame. A altura da região é determinada pelo JLabel. A linha 31 cria uma instância da classe interna MouseHandler (linhas 36–90) chamada handler que responde aos eventos de mouse. As linhas 32–33 registram handler como o ouvinte de evento de mouse do mousePanel. Os métodos addMouseListener e addMouseMotionListener são herdados indiretamente da classe Component e podem ser usados para registrar MouseListeners e MouseMotionListeners, respectivamente. Um objeto MouseHandler é um MouseListener e é um MouseMotionListener porque a classe implementa ambas as interfaces. [Observe que escolhemos implementar ambas as interfaces aqui para demonstrar uma classe que implementa mais de uma interface, mas poderíamos ter implementado a interface MouseInputListener em vez disso.] Quando o mouse entrar e sair da área de mousePanel, os métodos mouseEntered (linhas 62–67) e mouseExited (linhas 70–74) são chamados, respectivamente. O método mouseEntered exibe uma mensagem no statusBar que indica que o mouse entrou em JPanel e muda a cor de fundo para verde. O método mouseExited exibe uma mensagem no statusBar que indica que o mouse está fora do JPanel (ver a primeira janela de saída de exemplo) e muda a cor de fundo para branco. Quando qualquer um dos outros cinco eventos ocorrer, ele exibe uma mensagem no statusBar que inclui uma string contendo o evento e as coordenadas em que ele ocorreu. Os métodos MouseEvent getX e getY retornam as coordenadas x e y, respectivamente, do mouse no momento em que o evento ocorreu.

14.15 Classes adaptadoras Muitas interfaces listener de eventos, como MouseListener e MouseMotionListener, contêm múltiplos métodos. Não é sempre desejável declarar cada método em uma interface ouvinte de evento. Por exemplo, um aplicativo pode precisar apenas da rotina de tratamento de evento mouseClicked a partir do MouseListener ou da rotina mouseDragged a partir do MouseMotionListener. A interface WindowListener especifica sete métodos de tratamento de evento de janela. Para muitas das interfaces listener que têm vários métodos, os pacotes java.awt.event e javax.swing.event fornecem classes adaptadoras listeners de evento. Uma classe adaptadora implementa uma interface e fornece uma implementação padrão (com o corpo de um método vazio) de cada método na interface. A Figura 14.30 mostra várias classes adaptadoras java.awt.event e as interfaces que elas implementam. Você pode estender uma classe adaptadora para herdar a implementação padrão de cada método e, subsequentemente, sobrescrever somente o(s) método(s) necessário(s) para o tratamento de evento.

Observação de engenharia de software 14.8 Quando uma classe implementar uma interface, a classe terá um relacionamento é um com essa interface. Todas as subclasses diretas e indiretas dessa classe herdam essa interface. Portanto, um objeto de uma classe que estende uma classe adaptadora de evento é um objeto do tipo ouvinte de evento correspondente ( por exemplo, um objeto de uma subclasse de MouseAdapter é um MouseListener).



14.15  Classes adaptadoras

Classe adaptadora de evento no java.awt.event

Implementa a interface

ComponentAdapter ContainerAdapter FocusAdapter KeyAdapter MouseAdapter MouseMotionAdapter WindowAdapter

ComponentListener ContainerListener FocusListener KeyListener MouseListener MouseMotionListener WindowListener

453

Figura 14.30  |  Classes adaptadoras de evento e as interfaces que elas implementam no pacote java.awt.event.

Estendendo MouseAdapter O aplicativo das figuras 14.31 e 14.32 demonstra como determinar o número de cliques de mouse (isto é, a contagem de cliques) e como distinguir entre os diferentes botões do mouse. O ouvinte de evento nesse aplicativo é um objeto da classe interna MouseClickHandler (linhas 25–45) que estende MouseAdapter, então podemos declarar somente o método mouseClicked de que precisamos nesse exemplo. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

// Figura 14.31: MouseDetailsFrame.java // Demonstrando cliques de mouse e distinguindo entre botões do mouse. import ons.awt.BorderLayout; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.Jframe; import javax.swing.Jlabel; public class MouseDetailsFrame extends Jframe { private String details; // A string que é exibida na barra de status private Jlabel statusBar; // Jlabel que aparece na parte inferior da janela // construtor configura String de barra de título e registra o listener de mouse public MouseDetailsFrame() { super( "Mouse clicks and buttons" ); statusBar = new Jlabel( "Click the mouse" ); add( statusBar, BorderLayout.SOUTH ); addMouseListener( new MouseClickHandler() ); // adiciona handler } // fim do construtor MouseDetailsFrame // classe interna para tratar eventos de mouse private class MouseClickHandler extends MouseAdapter { // trata evento de clique de mouse e determina qual botão foi pressionado public void mouseClicked( MouseEvent event ) { int xPos = event.getX(); // obtém a posição x do mouse int yPos = event.getY(); // obtém a posição y do mouse details = String.format( "Clicked %d time(s)", event.getClickCount() ); if (event.isMetaDown() ) // botão direito do mouse details += " with right mouse button"; else if (event.isAltDown() ) // botão do meio do mouse details += " with center mouse button"; else // botão esquerdo do mouse details += " with left mouse button"; statusBar.setText( details ); // exibe mensagem em statusBar } // fim do método mouseClicked } // fim da classe interna private MouseClickHandler } // fim da classe MouseDetailsFrame

Figura 14.31  |  Clique dos botões esquerdo, central e direito do mouse.

454

1 2 3 4 5 6 7 8 9 10 11 12 13 14

Capítulo 14  Componentes GUI: Parte 1

// Figura 14.32: MouseDetails.java // Testando MouseDetailsFrame. import javax.swing.Jframe; public class MouseDetails { public static void main( String[] args ) { MouseDetailsFrame mouseDetailsFrame = new MouseDetailsFrame(); mouseDetailsFrame.setDefaultCloseOperation( Jframe.EXIT_ON_CLOSE ); mouseDetailsFrame.setSize( 400, 150 ); // configura o tamanho do frame mouseDetailsFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe MouseDetails

Figura 14.32  |  Teste a classe MouseDetailsFrame.

Erro comum de programação 14.4 Se você estender uma classe adaptadora e digitar incorretamente o nome do método que você está sobrescrevendo, o método simplesmente torna-se outro método na classe. Esse é um erro de lógica difícil de ser detectado, visto que o programa chamará a versão vazia do método herdado da classe adaptadora.

Um usuário de um programa Java pode estar em um sistema com um, dois ou três botões do mouse. O Java fornece um mecanismo para distinguir os botões do mouse. A classe MouseEvent herda vários métodos da classe InputEvent que podem distinguir entre o botão do mouse em um mouse de múltiplos botões ou podem simular um mouse de múltiplos botões com uma combinação de um clique do teclado e um clique de botão do mouse. A Figura 14.33 mostra os métodos InputEvent utilizados para distinguir os cliques do botão do mouse. O Java assume que cada mouse contém um botão do mouse esquerdo. Portanto, é simples testar um clique com o botão esquerdo do mouse. Entretanto, os usuários com mouse de um ou dois botões devem utilizar uma combinação de pressionamentos de tecla e cliques do botão

Método InputEvent

Descrição

isMetaDown()

Retorna true quando o usuário clica no botão direito do mouse em um mouse com dois ou três botões. Para simular um clique de botão direito com um mouse de um botão, o usuário pode manter pressionada a tecla Meta no teclado e clicar no botão do mouse.

isAltDown()

Retorna true quando o usuário clica no botão do mouse do meio em um mouse com três botões. Para simular um clique com o botão do meio do mouse em um mouse com um ou dois botões, o usuário pode pressionar a tecla Alt e clicar no único botão ou no botão esquerdo do mouse, respectivamente.

Figura 14.33  |  Métodos InputEvent que ajudam a distinguir entre os cliques do botão esquerdo, do centro e direito do mouse.

14.16 Subclasse Jpanel para desenhar com o mouse

455

do mouse ao mesmo tempo para simular os botões ausentes no mouse. No caso de um mouse com um ou dois botões, um aplicativo Java assume que o botão do meio do mouse é clicado se o usuário mantém pressionada a tecla Alt e clica no botão esquerdo em um mouse de dois botões ou no único botão em um mouse de um único botão. Em caso de um mouse com um único botão, um aplicativo Java supõe que o botão do mouse direito é clicado se o usuário mantiver a tecla Meta pressionada (às vezes chamada de tecla Command ou tecla “Maçã” em um Mac) e clicar no botão do mouse. A linha 21 da Figura 14.31 registra um MouseListener para o MouseDetailsFrame. O ouvinte de evento é um objeto da classe MouseClickHandler, que estende MouseAdapter. Isso permite declarar apenas o método mouseClicked (linhas 28–44). Esse método primeiro captura as coordenadas em que o evento ocorreu e as armazena nas variáveis locais xPos e yPos (linhas 30–31). As linhas 33–34 criam uma String chamada details contendo o número de cliques consecutivos de mouse, que é retornado pelo método MouseEvent getClickCount na linha 34. As linhas 36–41 utilizam os métodos isMetaDown e isAltDown para determinar o botão do mouse em que o usuário clicou e acrescentar uma String adequada aos details em cada caso. A String resultante é exibida em statusBar. A classe MouseDetails (Figura 14.32) contém o método main que executa o aplicativo. Tente clicar repetidamente com cada um dos botões do mouse para ver o incremento de contagem de cliques.

14.16 Subclasse Jpanel para desenhar com o mouse A Seção 14.14 mostrou como monitorar eventos de mouse em um Jpanel. Nesta seção, usamos um Jpanel como uma área dedicada de desenho em que o usuário pode desenhar arrastando o mouse. Além disso, esta seção demonstra um ouvinte de evento que estende uma classe adaptadora.

Método paintComponent Os componentes Swing leves que estendem a classe Jcomponent (como Jpanel) contêm o método paintComponent, que é chamado quando um componente Swing leve é exibido. Por sobrescrever esse método, você pode especificar como desenhar formas utilizando capacidades de imagens gráficas do Java. Ao personalizar um Jpanel para uso como uma área dedicada de desenho, a subclasse deve sobrescrever o método paintComponent e chamar a versão de superclasse de paintComponent como a primeira instrução no corpo do método sobrescrito para assegurar que o componente será exibido corretamente. A razão é que as subclasses de Jcomponent suportam a transparência. Para exibir um componente corretamente, o programa deve determinar se o componente é transparente. O código que determina isso está na implementação paintComponent da superclasse Jcomponent. Quando um componente for transparente, paintComponent não limpará seu fundo quando o programa exibir o componente. Quando um componente for opaco, paintComponent limpará o fundo do componente antes de o componente ser exibido. Se a versão de superclasse de paintComponent não for chamada, geralmente um componente GUI opaco não será exibido corretamente na interface com o usuário. Além disso, se a versão de superclasse é chamada depois de realizar as instruções de desenho personalizadas, os resultados geralmente serão apagados. A transparência de um componente Swing leve pode ser configurada com o método setOpaque (um argumento false indica que o componente é transparente). Observações sobre a aparência e comportamento 14.12 A maioria dos componentes Swing GUI pode ser transparente ou opaca. Se um componente Swing GUI for opaco, seu fundo será limpo quando seu método paintComponent for chamado. Apenas os componentes opacos podem ser exibidos com uma cor de fundo personalizada. Os objetos Jpanel são opacos por padrão.

Dica de prevenção de erro 14.1 No método paintComponent de uma subclasse Jcomponent, a primeira instrução deve sempre chamar o método da superclasse paintComponent a fim de assegurar que um objeto da subclasse seja exibido corretamente.

Erro comum de programação 14.5 Se um método paintComponent sobrescrito não chamar a versão da superclasse, o componente de subclasse pode não ser exibido adequadamente. Se um método paintComponent sobrescrito chamar a versão da superclasse depois que outro desenho for realizado, o desenho será apagado.

Definindo a área personalizada de desenho O aplicativo Painter das figuras 14.34 e 14.35 demonstra uma subclasse personalizada de Jpanel que é utilizada para criar uma área dedicada de desenho. O aplicativo utiliza o handler de evento mouseDragged para criar um aplicativo de desenho simples. O usuário pode desenhar figuras arrastando o mouse sobre o Jpanel. Esse exemplo não utiliza o método mouseMoved, então nossa classe ouvinte de evento (a classe interna anônima nas linhas 22–34) estende MouseMotionAdapter. Visto que essa classe já declara tanto mouseMoved como mouseDragged, podemos simplesmente sobrescrever mouseDragged para fornecer o tratamento de evento requerido por esse aplicativo.

456

Capítulo 14  Componentes GUI: Parte 1

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

// Figura 14.34: PaintPanel.java // Utilizando a classe MouseMotionAdapter. import ons.awt.Point; import ons.awt.Graphics; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import javax.swing.Jpanel; public class PaintPanel extends Jpanel { private int pointCount = 0; // número de contagem de pontos // array de 10000 referências ons.awt.Point private Point[] points = new Point[ 10000 ]; // configura GUI e registra handler de evento de mouse public PaintPanel() { // trata evento de movimento de mouse do frame addMouseMotionListener( new MouseMotionAdapter() // classe interna anônima { // armazena coordenadas de arrastar e repinta public void mouseDragged( MouseEvent event ) { if ( pointCount < points.length ) { points[ pointCount ] = event.getPoint(); // localiza o ponto pointCount++; // incrementa número de pontos em array repaint(); // repinta Jframe } // fim do if } // fim do método mouseDragged } // fim da classe interna anônima ); // fim da chamada para addMouseMotionListener } // fim do construtor PaintPanel // desenha ovais em um quadro delimitador de 4 por 4 nas localizações especificadas na janela public void paintComponent( Graphics g ) { super.paintComponent( g ); // limpa a área de desenho // desenha todos os pontos no array for ( int i = 0; i < pointCount; i++ ) g.fillOval(points[ i ].x, points[ i ].y, 4, 4 ); } // fim do método paintComponent } // fim da classe PaintPanel

Figura 14.34  |  Classe adaptadora utilizada para implementar handlers de evento.

A classe PaintPanel (Figura 14.34) estende Jpanel para criar a área dedicada de desenho. As linhas 3–7 importam as classes utilizadas na classe PaintPanel. A classe Point (pacote java.awt) representa uma coordenada x-y. Utilizamos objetos dessa classe para armazenar as coordenadas de cada evento de arrastar com o mouse. A classe Graphics é utilizada para desenhar. Nesse exemplo, utilizamos um array de 10.000 Points (linha 14) para armazenar a localização em que cada evento de arrastar com o mouse ocorre. Como você verá, o método paintComponent utiliza esses Points para desenhar. A variável de instância pointCount (linha 11) mantém o número total de Points capturados a partir dos eventos de arrastar com o mouse até agora. As linhas 20–35 registram um MouseMotionListener para ouvir os eventos de movimento do mouse PaintPanel. As linhas 22–34 criam um objeto de uma classe interna anônima que estende a classe adaptadora MouseMotionAdapter. Lembre-se de que MouseMotionAdapter implementa MouseMotionListener, portanto o objeto de classe interna anônima é um MouseMotionListener. A classe interna anônima herda as implementações padrão mouseMoved e mouseDragged, portanto já implementa métodos de toda a interface. Entretanto, os métodos padrão não fazem nada quando são chamados. Portanto, sobrescrevemos o método mouseDragged nas linhas 25–33 para capturar as coordenadas de um evento de arrastar com o mouse e as armazenamos como um objeto Point. A linha 27 assegura que armazenamos as coordenadas do evento somente se ainda houver elementos vazios no array. Se houver, a linha 29 invoca o método g­ etPoint de MouseEvent para obter Point em que o evento ocorreu e armazená-lo no array no índice pointCount. A linha 30 incrementa o pointCount e a linha 31 chama o método repaint (herdado indiretamente da classe Component) para indicar que o PaintPanel deve ser atualizado na tela o mais rápido possível com uma chamada para o método paintComponent de PaintPanel.



14.16  Subclasse Jpanel para desenhar com o mouse

457

O método paintComponent (linhas 39–46), que recebe um parâmetro Graphics, é chamado automaticamente a qualquer hora que PaintPanel precisar ser exibido na tela (como quando a GUI é inicialmente exibida) ou atualizado na tela (como quando o método repaint é chamado ou quando o componente GUI foi oculto por outra janela na tela e subsequentemente torna-se visível novamente).

Observações sobre a aparência e comportamento 14.13 Chamar repaint para um componente Swing GUI indica que o componente deve ser atualizado na tela o mais rápido possível. O fundo do componente GUI é limpo somente se o componente for opaco. Para o método Jcomponent setOpaque pode ser passado um argumento boolean que indica se o componente é opaco (true) ou transparente (false).

A linha 41 invoca a versão de superclasse de paintComponent para limpar o fundo de PaintPanel (Jpanels são opacos por padrão). As linhas 44–45 desenham uma oval na localização especificada por cada Point no array (até o pointCount). O método Graphics fillOval desenha uma oval sólida. Os quatro parâmetros do método representam uma área retangular (chamada de quadro delimitador) em que a oval é exibida. Os dois primeiros parâmetros são a coordenada x superior esquerda e a coordenada y superior esquerda da área retangular. As duas últimas coordenadas representam a largura e altura da área retangular. O método fillOval desenha a oval de tal modo que ela toque no meio de cada lado da área retangular. Na linha 45, os dois primeiros argumentos são especificados utilizando as duas variáveis de instância public da classe Point — x e y. O loop termina quando os pontos pointCount tiverem sido exibidos. Você aprenderá mais sobre os recursos Graphics no Capítulo 15.

Observações sobre a aparência e comportamento 14.14 O desenho em qualquer componente GUI é realizado com as coordenadas que são medidas a partir do canto superior esquerdo (0, 0) desse componente GUI, não o canto superior esquerdo da tela.

Utilizando o Jpanel personalizado em um aplicativo A classe Painter (Figura 14.35) contém o método main que executa esse aplicativo. A linha 14 cria um objeto PaintPanel em que o usuário pode arrastar o mouse para desenhar. A linha 15 anexa o PaintPanel ao Jframe. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

// Figura 14.35: Painter.java // Testando o PaintPanel. import ons.awt.BorderLayout; import javax.swing.Jframe; import javax.swing.Jlabel; public class Painter { public static void main( String[] args ) { // cria o Jframe Jframe application = new Jframe( "A simple paint program" ); PaintPanel paintPanel = new PaintPanel(); // cria o painel de pintura application.add( paintPanel, BorderLayout.CENTER ); // no centro // cria um rótulo e o coloca em SOUTH do BorderLayout application.add( new Jlabel( "Drag the mouse to draw" ), BorderLayout.SOUTH ); application.setDefaultCloseOperation( Jframe.EXIT_ON_CLOSE ); application.setSize( 400, 200 ); // configura o tamanho do frame application.setVisible( true ); // exibe o frame } // fim de main } // fim da classe Painter

Figura 14.35  |  Classe de teste para PaintFrame.

458

Capítulo 14

Componentes GUI: Parte 1

14.17 Tratamento de evento-chave Esta seção apresenta a interface KeyListener para tratar eventos de teclado. Eventos de teclado são gerados quando as teclas no teclado são pressionadas e liberadas. Uma classe que implementa KeyListener deve fornecer declarações para métodos keyPressed, keyReleased e keyTyped, cada um dos quais recebe um KeyEvent como seu argumento. A classe KeyEvent é uma subclasse de InputEvent. O método keyPressed é chamado em resposta ao pressionamento de qualquer tecla. O método keyTyped é chamado em resposta ao pressionamento de qualquer tecla que não seja uma tecla de ação. (As teclas de ação são qualquer tecla de seta, Home, End, Page Up, Page Down, qualquer tecla de função etc.) O método keyReleased é chamado quando a tecla é lançada depois de qualquer evento keyPressed ou keyTyped. O aplicativo das figuras 14.36–14.37 demonstra os métodos KeyListener. A classe KeyDemoFrame implementa a interface Key­ Listener, então todos os três métodos são definidos no aplicativo. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

// Figura 14.36: KeyDemoFrame.java // Demonstrando os eventos de pressionamento de tecla. import ons.awt.Color; import java.awt.event.KeyListener; import java.awt.event.KeyEvent; import javax.swing.Jframe; import javax.swing.JtextArea; public class KeyDemoFrame extends Jframe implements KeyListener { private String line1 = ""; // primeira linha de textarea private String line2 = ""; // segunda linha de textarea private String line3 = ""; // terceira linha de textarea private JtextArea textArea; // textarea a exibir saída // construtor KeyDemoFrame public KeyDemoFrame() { super( "Demonstrating Keystroke Events" ); textArea = new JtextArea( 10, 15 ); // configura JtextArea textArea.setText( "Press any key on the keyboard…" ); textArea.setEnabled( false ); // desativa textarea textArea.setDisabledTextColor( Color.BLACK ); // configura a cor do texto add( textArea ); // adiciona textarea ao Jframe addKeyListener( this ); // permite que o frame processe os eventos de teclado } // fim do construtor KeyDemoFrame // trata pressionamento de qualquer tecla public void keyPressed( KeyEvent event ) { line1 = String.format( "Key pressed: %s", KeyEvent.getKeyText( event.getKeyCode() ) ); // mostra a tecla pressionada setLines2and3( event ); // configura a saída das linhas dois e três } // fim do método keyPressed // trata liberação de qualquer tecla public void keyReleased( KeyEvent event ) { line1 = String.format( "Key released: %s", KeyEvent.getKeyText( event.getKeyCode() ) ); // mostra a tecla liberada setLines2and3( event ); // configura a saída das linhas dois e três } // fim do método keyReleased // trata pressionamento de uma tecla de ação public void keyTyped( KeyEvent event ) { line1 = String.format( "Key typed: %s", event.getKeyChar() ); setLines2and3( event ); // configura a saída das linhas dois e três } // fim do método keyTyped // configura segunda e terceira linhas de saída private void setLines2and3( KeyEvent event )



14.17  Tratamento de evento-chave

55 56 57 58 59 60 61 62 63 64 65 66 67

459

{ line2 = String.format( "This key is %san action key", (event.isActionKey() ? "" : "not " ) ); String temp = KeyEvent.getKeyModifiersText( event.getModifiers() ); line3 = String.format( "Modifier keys pressed: %s", ( temp.equals( "" ) ? "none" : temp ) ); // modificadores de saída textArea.setText( String.format( "%s\n%s\n%s\n", line1, line2, line3 ) ); // gera saída de três linhas de texto } // fim do método setLines2and3 } // fim da classe KeyDemoFrame

Figura 14.36  |  Tratamento de evento de teclado.

1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 14.37: KeyDemo.java // Testando KeyDemoFrame. import javax.swing.Jframe; public class KeyDemo { public static void main( String[] args ) { KeyDemoFrame keyDemoFrame = new KeyDemoFrame(); keyDemoFrame.setDefaultCloseOperation( Jframe.EXIT_ON_CLOSE ); keyDemoFrame.setSize( 350, 100 ); // configura o tamanho do frame keyDemoFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe KeyDemo

Figura 14.37  |  Classe de teste para KeyDemoFrame.

O construtor (Figura 14.36, linhas 17–28) registra o aplicativo para tratar seus próprios eventos de teclado utilizando o método addKeyListener na linha 27. O método addKeyListener é declarado na classe Component, então cada subclasse de Component pode

notificar objetos KeyListener de eventos de teclado desse Component. Na linha 25, o construtor adiciona JtextArea textArea (onde a saída do aplicativo é exibida) ao Jframe. Uma JtextArea é uma área com várias linhas em que você pode exibir o texto. (Discutimos JtextAreas mais detalhadamente na Seção 14.20.) Note nas capturas de tela que textArea ocupa a janela inteira. Isso é devido ao BorderLayout padrão de Jframe (discutido na Seção 14.18.2 e demonstrado na Figura 14.41). Quando um único Component é adicionado a um BorderLayout, o Component ocupa o Container inteiro. A linha 23

460

Capítulo 14

Componentes GUI: Parte 1

desativa JtextArea, portanto, o usuário não pode digitar nela. Isso faz com que o texto na JtextArea torne-se cinza. A linha 24 utiliza o método setDisabledTextColor para mudar a cor do texto na JtextArea para preto a fim de melhorar a legibilidade. Os métodos keyPressed (linhas 31–36) e keyReleased (linhas 39–44) utilizam o método KeyEvent getKeyCode para obter o código de tecla virtual da tecla pressionada. A classe KeyEvent contém constantes de código de tecla virtuais que representam cada tecla no teclado. Essas constantes podem ser comparadas com o valor de retorno de getKeyCode para testar as teclas individuais no teclado. O valor retornado por getKeyCode é passado para o método static KeyEvent getKeyText, que retorna uma string contendo o nome da tecla que foi pressionada. Para uma lista completa de constantes de tecla virtual, veja a documentação on-line para a classe KeyEvent (pacote java.awt.event). O método keyTyped (linhas 47–51) utiliza o método KeyEvent getKeyChar (que retorna um char) para obter o valor Unicode do caractere digitado. Todos os três métodos de tratamento de evento terminam chamando o método setLines2and3 (linhas 54–66) e passando para ele o objeto KeyEvent. Esse método utiliza o método KeyEvent isActionKey (linha 57) para determinar se a tecla no evento é uma tecla de ação. Além disso, o método InputEvent getModifiers é chamado (linha 59) para determinar se alguma tecla modificadora (como Shift, Alt e Ctrl) foi pressionada quando o evento de teclado ocorreu. O resultado desse método é passado para o método static KeyEvent getKeyModifiersText, que produz uma string contendo os nomes das teclas modificadoras pressionadas. [Nota: se você precisar testar uma tecla específica no teclado, a classe KeyEvent fornece uma tecla constante para cada uma delas. Essas constantes podem ser utilizadas a partir dos handlers de evento de teclado para determinar se uma tecla particular foi pressionada. Além disso, para determinar se as teclas Alt, Ctrl, Meta e Shift são pressionadas individualmente, os métodos InputEvent isAltDown, isControlDown, isMetaDown e isShiftDown retornam um boolean indicando se a tecla particular foi pressionada durante o evento de teclado.]

14.18 Introdução a gerenciadores de layout Os gerenciadores de layout organizam componentes GUI em um contêiner para propósitos de apresentação. Você pode utilizar os gerenciadores de layout para obter as capacidades de layout básicas em vez de determinar a posição e o tamanho exatos de cada componente GUI. Essa funcionalidade permite que você se concentre na aparência e comportamento básicos e deixa os gerenciadores de layout processarem a maioria dos detalhes de layout. Todos os gerenciadores de layout implementam a interface LayoutManager (no pacote java. awt). O método setLayout da classe Container aceita um objeto que implementa a interface LayoutManager como um argumento. Há basicamente três maneiras de organizar componentes em uma GUI: 1. Posicionamento absoluto: esse fornece o maior nível de controle sobre a aparência de uma GUI. Configurando o layout de um Container como null, você

pode especificar a posição absoluta de cada componente GUI em relação ao canto superior esquerdo do Container usando métodos Component setSize e setLocation ou setBounds. Se fizer isso, você também deve especificar o tamanho de cada componente GUI. A programação de uma GUI com posicionamento absoluto pode ser tediosa a menos que você tenha um ambiente de desenvolvimento integrado (IDE) que pode gerar o código para você.

2. Gerenciadores de layout: utilizar os gerenciadores de layout para posicionar elementos pode ser mais simples e mais rápido que criar uma

GUI com posicionamento absoluto, mas você perde algum controle sobre o tamanho e o posicionamento precisos de componentes GUI. 3. Programação visual em um IDE: os IDEs fornecem ferramentas que facilitam a criação de GUIs. Em geral, todo IDE fornece uma ferra-

menta de design GUI que permite arrastar e soltar componentes GUI de uma caixa de ferramenta em uma área de desenho. Você então pode posicionar, dimensionar e alinhar componentes GUI como quiser. O IDE gera o código Java que cria a GUI. Além disso, em geral, você pode adicionar o código de tratamento de evento de um componente particular dando um clique duplo no componente. Algumas ferramentas de desenho também permitem utilizar os gerenciadores de layout descritos neste capítulo e no Capítulo 25.

Observações sobre a aparência e comportamento 14.15 A maioria dos IDEs Java fornece ferramentas de projeto para projetar visualmente uma GUI; as ferramentas então escrevem o código Java que cria a GUI. Essas ferramentas costumam fornecer maior controle sobre o tamanho, posição e alinhamento de componentes GUI do que os gerenciadores de layouts integrados.

Observações sobre a aparência e comportamento 14.16 É possível configurar o layout de um Container como null, que indica que nenhum gerenciador de layout deve ser utilizado. Em um Container sem gerenciador de layout, você deve posicionar e dimensionar os componentes no contêiner dado e tomar o cuidado de que, em eventos de redimensionamento, todos os componentes sejam reposicionados conforme necessário. Os eventos de redimensionamento de um componente podem ser processados por um ComponentListener.

A Figura 14.38 resume os gerenciadores de layout apresentados neste capítulo. Outros são discutidos no Capítulo 25 e o poderoso gerenciador de layout GroupLayout é discutido no Apêndice I.



14.18  Introdução a gerenciadores de layout

461

Gerenciador de layout

Descrição

FlowLayout

Padrão para javax.swing.Jpanel. Coloca os componentes sequencialmente (da esquerda para a direita) na ordem em que foram adicionados. Também é possível especificar a ordem dos componentes utilizando o método Container add que aceita um Component e uma posição de índice do tipo inteiro como argumentos.

BorderLayout

Padrão para JFrames (e outras janelas). Organiza os componentes em cinco áreas: NORTH, SOUTH, EAST, WEST e CENTER.

GridLayout

Organiza os componentes nas linhas e colunas.

Figura 14.38  |  Gerenciadores de layout.

14.18.1   FlowLayout FlowLayout é o gerenciador de layout mais simples. Os componentes GUI são colocados em um contêiner da esquerda para a direita na ordem em que são adicionados ao contêiner. Quando a borda do contêiner for alcançada, os componentes continuarão a ser exibidos na próxima linha. A classe FlowLayout permite aos componentes GUI ser alinhados à esquerda, centralizados (o padrão) e alinhados à direita. O aplicativo das figuras 14.39–14.40 cria três objetos JButton e os adiciona ao aplicativo, usando um gerenciador de layout Flow­ Layout. Os componentes são centralizados por padrão. Quando o usuário clica em Left, o alinhamento do gerenciador de layout muda para um FlowLayout alinhado à esquerda. Quando o usuário clica em Right, o alinhamento do gerenciador de layout muda para um FlowLayout alinhado à direita. Quando o usuário clica em Center, o alinhamento do gerenciador de layout muda para um FlowLayout alinhado ao centro. Cada botão tem seu próprio handler de evento que é declarado com uma classe interna anônima que implementa ActionListener. As janelas de saída de exemplo mostram cada um dos alinhamentos FlowLayout. Além disso, a última janela de saída de exemplo mostra o alinhamento centralizado depois que a janela foi redimensionada para uma largura menor. Observe que o botão Right flui em uma nova linha. Como visto anteriormente, um layout do contêiner é configurado com o método setLayout da classe Container. A linha 25 configura o gerenciador de layout como o FlowLayout declarado na linha 23. Normalmente, o layout é configurado antes de qualquer componente GUI ser adicionado a um contêiner.

Observações sobre a aparência e comportamento 14.17 Cada contêiner individual pode ter apenas um gerenciador de layout, mas vários contêineres no mesmo aplicativo podem utilizar, cada um, gerenciadores de layout diferentes.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

// Figura 14.39: FlowLayoutFrame.java // Demonstrando os alinhamentos FlowLayout. import java.awt.FlowLayout; import java.awt.Container; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.JFrame; import javax.swing.JButton; public class FlowLayoutFrame extends JFrame { private JButton leftJButton; // botão para configurar alinhamento à esquerda private JButton centerJButton; // botão para configurar alinhamento centralizado private JButton rightJButton; // botão para configurar alinhamento à direita private FlowLayout layout; // objeto de layout private Container container; // contêiner para configurar layout // configura GUI e registra listeners de botão public FlowLayoutFrame() { super( "FlowLayout Demo" ); layout = new FlowLayout(); // cria FlowLayout container = getContentPane(); // obtém contêiner para layout

462 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81

Capítulo 14  Componentes GUI: Parte 1 setLayout( layout ); // configura o layout de frame // configura leftJButton e registra listener leftJButton = new JButton( "Left" ); // cria botão Left add( leftJButton ); // adiciona o botão Left ao frame leftJButton.addActionListener( new ActionListener() // classe interna anônima { // processa o evento leftJButton public void actionPerformed( ActionEvent event ) { layout.setAlignment( FlowLayout.LEFT ); // realinha os componentes anexados layout.layoutContainer( container ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener // configura centerJButton e registra o listener centerJButton = new JButton( "Center" ); // cria botão Center add( centerJButton ); // adiciona botão Center ao frame centerJButton.addActionListener( new ActionListener() // classe interna anônima { // processa evento centerJButton public void actionPerformed( ActionEvent event ) { layout.setAlignment( FlowLayout.CENTER ); // realinha os componentes anexados layout.layoutContainer( container ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener // configura rightJButton e registra o listener rightJButton = new JButton( "Right" ); // cria botão Right add( rightJButton ); // adiciona botão Right ao frame rightJButton.addActionListener( new ActionListener() // classe interna anônima { // processo evento rightJButton public void actionPerformed( ActionEvent event ) { layout.setAlignment( FlowLayout.RIGHT ); // realinha os componentes anexados layout.layoutContainer( container ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener } // fim do construtor FlowLayoutFrame } // FlowLayoutFrame fim da classe

Figura 14.39  |  FlowLayout permite que os componentes fluam sobre múltiplas linhas.



1 2 3 4 5 6 7 8 9

// Figura 14.40: FlowLayoutDemo.java // Testando FlowLayoutFrame. import javax.swing.JFrame; public class FlowLayoutDemo { public static void main( String[] args ) { FlowLayoutFrame flowLayoutFrame = new FlowLayoutFrame();

10 11 12 13 14

14.18  Introdução a gerenciadores de layout

463

flowLayoutFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); flowLayoutFrame.setSize( 300, 75 ); // configura o tamanho do frame flowLayoutFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe FlowLayoutDemo

Figura 14.40  |  Classe de teste para FlowLayoutFrame.

Observe que a rotina de tratamento de evento de cada botão é especificada com um objeto de classe interna anônima separado (Figura 14.39, linhas 30–43, 48–61 e 66–79, respectivamente) e o método actionPerformed executa duas instruções em cada caso. Por exemplo, a linha 37 no handler de eventos para leftJButton utiliza o método FlowLayout setAlignment para mudar o alinhamento do FlowLayout para um FlowLayout alinhado à esquerda (FlowLayout.LEFT). A linha 40 utiliza o método layoutContainer (que é herdado por todos os gerenciadores de layout) da interface LayoutManager para especificar que o JFrame deve ser reorganizado com base no layout ajustado. Dependendo do botão clicado, o método actionPerformed de cada botão configura o alinhamento de FlowLayout como FlowLayout.LEFT (linha 37), FlowLayout.CENTER (linha 55) ou FlowLayout.RIGHT (linha 73).

14.18.2   BorderLayout O gerenciador de layout BorderLayout (o gerenciador de layout padrão de um JFrame) organiza componentes em cinco regiões: e CENTER. NORTH corresponde à parte superior do contêiner. A classe BorderLayout estende Object e implementa a interface LayoutManager2 (uma subinterface de LayoutManager que adiciona vários métodos para obter um processamento de layout aprimorado). Um BorderLayout limita um Container a conter no máximo cinco componentes — um em cada região. O componente colocado em cada região pode ser um contêiner ao qual os outros componentes são anexados. Os componentes colocados nas regiões NORTH e SOUTH estendem-se horizontalmente para os lados do contêiner e têm a mesma altura que o componente mais alto colocado nessas regiões. As regiões EAST e WEST se expandem verticalmente entre NORTH e SOUTH e são tão largas quanto os componentes colocados nessas regiões. O componente colocado na região CENTER se expande para preencher todo o espaço restante no layout (que é a razão de JTextArea na Figura 14.37 ocupar a janela inteira). Se todas as cinco regiões são ocupadas, o espaço do contêiner inteiro é coberto por componentes GUI. Se a região NORTH ou SOUTH não for ocupada, os componentes GUI nas regiões EAST, CENTER e WEST se expandem verticalmente para preencher o espaço restante. Se a região EAST ou WEST não for ocupada, o componente GUI na região CENTER expande horizontalmente para preencher o espaço restante. Se a região CENTER não for ocupada, a área é deixada vazia — os outros componentes GUI não se expandem para preencher o espaço restante. O aplicativo das figuras 14.41–14.42 demonstra o gerenciador de layout BorderLayout utilizando cinco JButtons. NORTH, SOUTH, EAST, WEST

1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 14.41: BorderLayoutFrame.java // Demonstrando BorderLayout. import java.awt.BorderLayout; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.JFrame; import javax.swing.JButton; public class BorderLayoutFrame extends JFrame implements ActionListener { private JButton[] buttons; // array de botões para ocultar partes private static final String[] names = { "Hide North", "Hide South", "Hide East", "Hide West", "Hide Center" }; private BorderLayout layout; // objeto borderlayout

464

Capítulo 14  Componentes GUI: Parte 1

15 16

// configura GUI e tratamento de evento

17

public BorderLayoutFrame()

18

{

19

super( "BorderLayout Demo" );

20 21

layout = new BorderLayout( 5, 5 ); // espaços de 5 pixels

22

setLayout( layout ); // configura o layout de frame

23

buttons = new JButton[ names.length ]; // configura o tamanho do array

24 25

// cria JButtons e registra ouvintes para eles

26

for ( int count = 0; count < names.length; count++ )

27

{

28

buttons[ count ] = new JButton( names[ count ] );

29

buttons[ count ].addActionListener( this );

30

} // for final

31 32

add( buttons[ 0 ], BorderLayout.NORTH ); // adiciona botão para o norte

33

add( buttons[ 1 ], BorderLayout.SOUTH ); // adiciona botão para o sul

34

add( buttons[ 2 ], BorderLayout.EAST ); // adiciona botão para o leste

35

add( buttons[ 3 ], BorderLayout.WEST ); // adiciona botão para o oeste

36

add( buttons[ 4 ], BorderLayout.CENTER ); // adiciona botão para o centro

37

} // fim do construtor BorderLayoutFrame

38 39

// trata os eventos de botão

40

public void actionPerformed( ActionEvent event )

41

{

42

// verifica a origem de evento e o painel de conteúdo de layout correspondentemente

43

for ( JButton button : buttons )

44

{

45

if ( event.getSource() == button )

46

button.setVisible( false ); // oculta o botão clicado

47

else

48

button.setVisible( true ); // mostra outros botões

49

} // for final

50 51

layout.layoutContainer( getContentPane() ); // faz o layout do painel de conteúdo

52 53

} // fim do método actionPerformed } // fim da classe BorderLayoutFrame

Figura 14.41  |  BorderLayout que contém cinco botões.

1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 14.42: BorderLayoutDemo.java



// Testando BorderLayoutFrame. import javax.swing.JFrame; public class BorderLayoutDemo { public static void main( String[] args ) { BorderLayoutFrame borderLayoutFrame = new BorderLayoutFrame(); borderLayoutFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); borderLayoutFrame.setSize( 300, 200 ); // configura o tamanho do frame borderLayoutFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe BorderLayoutDemo



14.18  Introdução a gerenciadores de layout lacuna horizontal

465

lacuna vertical

Figura 14.42  |  Teste a classe BorderLayoutFrame.

A linha 21 da Figura 14.41 cria um BorderLayout. Os argumentos de construtor especificam o número de pixels entre componentes que estão organizados horizontalmente (espaçamento horizontal) e entre componentes que são organizados verticalmente (espaçamento vertical), respectivamente. O padrão tem horizontal e verticalmente um pixel de espaçamento. A linha 22 utiliza o método setLayout para configurar o layout do painel de conteúdo como layout. Adicionamos Components a um BorderLayout com outra versão do método Container add que aceita dois argumentos — o ­Component para adicionar e a região em que o Component deva aparecer. Por exemplo, a linha 32 especifica que buttons[ 0 ] deve aparecer na região NORTH. Os componentes podem ser adicionados em qualquer ordem, mas apenas um componente deve ser adicionado a cada região.

Observações sobre a aparência e comportamento 14.18 Se nenhuma região for especificada ao se adicionar um Component a um BorderLayout, o gerenciador de layout assume que o Component deve ser adicionado à região BorderLayout.CENTER.

Erro comum de programação 14.6 Quando mais de um componente for adicionado a uma região em um BorderLayout, somente o último componente adicionado a essa região será exibido. Não há nenhum erro que indica esse problema.

Observe que a classe BorderLayoutFrame implementa ActionListener diretamente nesse exemplo, então BorderLayoutFrame tratará os eventos de JButtons. Por essa razão, a linha 29 passa a referência this para o método addActionListener de cada Jbutton. Quando o usuário clica em um JButton particular no layout, o método actionPerformed (linhas 40–52) é executado. A instrução for aprimorada nas linhas 43–49 utiliza um if…else para ocultar o JButton particular que gerou o evento. O método setVisible (herdado em JButton da classe Component) é chamado com um argumento false (linha 46) para ocultar JButton. Se o JButton atual no array não é o que gerou o evento, o método setVisible é chamado com um argumento true (linha 48) para assegurar que o JButton é exibido na tela. A linha 51 utiliza o método LayoutManager layoutContainer para recalcular o layout do painel de conteúdo. Note nas capturas de tela da Figura 14.42 que certas regiões no BorderLayout alteram a forma quando os JButtons são ocultados e exibidos em

466

Capítulo 14  Componentes GUI: Parte 1

outras regiões. Tente redimensionar a janela do aplicativo para ver como as várias regiões redimensionam com base na largura e altura da janela. Para layouts mais complexos, agrupe componentes em JPanels, cada um com gerenciador de layout separado. Coloque os JPanels no JFrame utilizando o padrão BorderLayout ou algum outro layout.

14.18.3  GridLayout O gerenciador de layout GridLayout divide o contêiner em uma grade de modo que os componentes podem ser colocados nas linhas e colunas. A classe GridLayout estende diretamente a classe Object e implementa a interface LayoutManager. Cada Component em um GridLayout tem a mesma largura e altura. Os componentes são adicionados a um GridLayout iniciando a célula na parte superior esquerda da grade e prosseguindo da esquerda para a direita até a linha estar cheia. Então o processo continua da esquerda para a direita na próxima linha da grade e assim por diante. O aplicativo das figuras 14.43 e 14.44 demonstra o gerenciador de layout GridLayout utilizando seis JButtons. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

// Figura 14.43: GridLayoutFrame.java // Demonstrando GridLayout. import java.awt.GridLayout; import java.awt.Container; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.JFrame; import javax.swing.JButton; public class GridLayoutFrame extends JFrame implements ActionListener { private JButton[] buttons; // array de botões private static final String[] names = { "one", "two", "three", "four", "five", "six" }; private boolean toggle = true; // alterna entre dois layouts private Container container; // contêiner do frame private GridLayout gridLayout1; // primeiro gridlayout private GridLayout gridLayout2; // segundo gridlayout // construtor sem argumentos public GridLayoutFrame() { super( "GridLayout Demo" ); gridLayout1 = new GridLayout( 2, 3, 5, 5 ); // 2 por 3; lacunas de 5 gridLayout2 = new GridLayout( 3, 2 ); // 3 por 2; nenhuma lacuna container = getContentPane(); // obtém painel de conteúdo setLayout( gridLayout1 ); // configura o layout JFrame buttons = new JButton[ names.length ]; // cria array de JButtons for ( int count = 0; count < names.length; count++ ) { buttons[ count ] = new JButton( names[ count ] ); buttons[ count ].addActionListener( this ); // ouvinte registrado add( buttons[ count ] ); // adiciona o botão ao JFrame } // for final } // fim do construtor GridLayoutFrame // trata eventos de botão alternando entre layouts public void actionPerformed( ActionEvent event ) { if ( toggle ) container.setLayout( gridLayout2 ); // configura layout como segundo else container.setLayout( gridLayout1 ); // configura layout como primeiro toggle = !toggle; // alterna para valor oposto container.validate(); // refaz o layout do contêiner } // fim do método actionPerformed } // fim da classe GridLayoutFrame

Figura 14.43  |  GridLayout que contém seis botões.

14.19 Utilizando painéis para gerenciar layouts mais complexos

467

As linhas 24–25 criam dois objetos GridLayout. O construtor GridLayout utilizado na linha 24 especifica um GridLayout com 2 linhas, 3 colunas, 5 pixels de espaçamento horizontal entre os Components na grade e 5 pixels de espaçamento vertical entre Components na grade. O construtor GridLayout utilizado na linha 25 especifica um GridLayout com 3 linhas e 2 colunas que utiliza o espaçamento padrão (1 pixel). Os objetos JButton nesse exemplo são inicialmente organizados utilizando-se gridLayout1 (configura para o painel de conteúdo na linha 27 com o método setLayout). O primeiro componente é adicionado à primeira coluna da primeira linha. O próximo componente é adicionado à segunda coluna da primeira linha e assim por diante. Quando um JButton é pressionado, o método actionPerformed (linhas 39–48) é chamado. Toda chamada para actionPerformed alterna o layout entre gridLayout2 e gridLayout1, utilizando a variável boolean toggle para determinar o próximo layout a ser configurado. A linha 47 mostra outra maneira de reformatar um contêiner cujo layout foi alterado. O método Container validate recalcula o layout do contêiner com base no gerenciador de layout atual para o Container e o conjunto atual de componentes GUI exibidos. 1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 14.44: GridLayoutDemo.java // Testando GridLayoutFrame. import javax.swing.JFrame; public class GridLayoutDemo { public static void main( String[] args ) { GridLayoutFrame gridLayoutFrame = new GridLayoutFrame(); gridLayoutFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); gridLayoutFrame.setSize( 300, 200 ); // configura o tamanho do frame gridLayoutFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe GridLayoutDemo

Figura 14.44 | Classe de teste para GridLayoutFrame.

14.19 Utilizando painéis para gerenciar layouts mais complexos GUIs complexas (como a Figura 14.1) exigem que cada componente seja colocado em uma localização exata. Elas frequentemente consistem em múltiplos painéis, com os componentes de cada painel organizados em um layout específico. A classe JPanel estende JComponent e JComponent estende a classe Container, então todo JPanel é um Container. Assim, todo JPanel pode ter componentes, incluindo outros painéis, anexados, com o método Container add. O aplicativo das figuras 14.45–14.46 demonstra como um JPanel pode ser utilizado para criar um layout mais complexo em que vários JButtons são colocados na região SOUTH de um BorderLayout. Depois de o JPanel buttonJPanel ser declarado (linha 11) e criado (linha 19), a linha 20 configura o layout de buttonJPanel como um GridLayout de uma linha e cinco colunas (há cinco JButtons no array buttons). As linhas 23–27 adicionam JButtons no array ao JPanel. A linha 26 adiciona os botões diretamente ao JPanel — a classe JPanel não tem um painel de conteúdo, ao contrário de um JFrame. A linha 29 utiliza o padrão BorderLayout de JFrame para adicionar buttonJPanel à região SOUTH. Observe que a região SOUTH é tão alta quanto os botões no buttonJPanel. Um JPanel é dimensionado aos componentes que ele contém. À medida que mais componentes são adicionados, o JPanel cresce (de acordo com as restrições de seu gerenciador de layout) para acomodar os componentes. Redimensione a janela para ver como o gerenciador de layout afeta o tamanho dos JButtons. 1 2 3 4 5 6 7 8

// Figura 14.45: PanelFrame.java // Utilizando um JPanel para ajudar a fazer o layout dos componentes. import java.awt.GridLayout; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JButton;

468 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

Capítulo 14

public class PanelFrame extends JFrame { private JPanel buttonJPanel; // painel para armazenar botões private JButton[] buttons; // array de botões // construtor sem argumentos public PanelFrame() { super( "Panel Demo" ); buttons = new JButton[ 5 ]; // cria botões de array buttonJPanel = new JPanel(); // configura painel buttonJPanel.setLayout( new GridLayout( 1, buttons.length ) ); // cria e adiciona botões for ( int count = 0; count < buttons.length; count++ ) { buttons[ count ] = new JButton( "Button " + ( count + 1 ) ); buttonJPanel.add( buttons[ count ] ); // adiciona botão ao painel } // for final add( buttonJPanel, BorderLayout.SOUTH ); // adiciona painel ao JFrame } // fim do construtor PanelFrame } // fim da classe PanelFrame

Figura 14.45 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14

Componentes GUI: Parte 1

JPanel

com cinco JButtons anexados à região SOUTH de um BorderLayout.

// Figura 14.46: PanelDemo.java // Testando PanelFrame. import javax.swing.JFrame; public class PanelDemo extends JFrame { public static void main( String[] args ) { PanelFrame panelFrame = new PanelFrame(); panelFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); panelFrame.setSize( 450, 200 ); // configura o tamanho do frame panelFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe PanelDemo

Figura 14.46 | Classe de teste para PanelFrame.

14.20 JTextArea JTextArea fornece uma área para manipular múltiplas linhas de texto. Como a classe JTextField, JTextArea é uma subclasse de JTextComponent, que declara métodos comuns aos JTextFields, JTextAreas e vários outros componentes GUI baseados em texto.

O aplicativo nas figuras 14.47–14.48 demonstra as JTextAreas. Uma JTextArea exibe texto que o usuário pode selecionar. A outra é não editável e é usada para exibir o texto que o usuário selecionou na primeira JTextArea. Diferentemente de JTextFields, JTextAreas não têm eventos de ação — quando você pressiona Enter ao digitar uma JTextArea, o cursor simplesmente se move para a linha seguinte. Como com JLists de seleção múltipla (Seção 14.13), um evento externo de outro componente GUI indica quando processar o texto em uma JTextArea. Por exemplo, ao digitar uma mensagem de correio eletrônico, você normalmente clica em um botão Send para enviar o texto da mensagem para o destinatário. De maneira semelhante, ao editar um documento em um processador de texto, você normalmente salva o arquivo selecionando o item Save ou Save As… de um menu. Nesse programa, o botão Copy >>> gera o evento externo que copia o texto selecionado na JTextArea à esquerda e o exibe na JtextArea à direita.



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

14.20  JTextArea

// Figura 14.47: TextAreaFrame.java // Copiando texto selecionado de uma textarea para outra. import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.Box; import javax.swing.JFrame; import javax.swing.JTextArea; import javax.swing.JButton; import javax.swing.JScrollPane; public class TextAreaFrame extends { private JTextArea textArea1; // private JTextArea textArea2; // private JButton copyJButton; //

JFrame exibe a string demo texto destacado é copiado aqui começa a copiar o texto

// construtor sem argumentos public TextAreaFrame() { super( "TextArea Demo" ); Box box = Box.createHorizontalBox(); // cria box String demo = "This is a demo string to\n" + "illustrate copying text\nfrom one textarea to \n" + "another textarea using an\nexternal event\n"; textArea1 = new JTextArea( demo, 10, 15 ); // cria textarea1 box.add( new JScrollPane( textArea1 ) ); // adiciona scrollpane copyJButton = new JButton( "Copy >>>" ); // cria botão de cópia box.add( copyJButton ); // adiciona o botão de cópia à box copyJButton.addActionListener( new ActionListener() // classe interna anônima { // configura texto em textArea2 como texto selecionado de textArea1 public void actionPerformed( ActionEvent event ) { textArea2.setText( textArea1.getSelectedText() ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener textArea2 = new JTextArea( 10, 15 ); // cria segunda textarea textArea2.setEditable( false ); // desativa a edição box.add( new JScrollPane( textArea2 ) ); // adiciona scrollpane add( box ); // adiciona box ao frame } // fim do construtor TextAreaFrame } // fim da classe TextAreaFrame

Figura 14.47  |  Copiando texto selecionado de uma JTextArea para outra. 1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 14.48: TextAreaDemo.java // Copiando texto selecionado de uma textarea para outra. import javax.swing.JFrame; public class TextAreaDemo { public static void main( String[] args ) { TextAreaFrame textAreaFrame = new TextAreaFrame(); textAreaFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); textAreaFrame.setSize( 425, 200 ); // configura o tamanho do frame textAreaFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe TextAreaDemo

469

470

Capítulo 14  Componentes GUI: Parte 1

Figura 14.48  |  Classe de teste para TextAreaFrame.

No construtor (linhas 18–48), a linha 21 cria um contêiner Box (pacote javax.swing) para organizar os componentes GUI. Box é uma subclasse de Container que utiliza um gerenciador de layout BoxLayout (discutido em detalhes na Seção 25.9) para organizar os componentes GUI horizontal ou verticalmente. O método static createHorizontalBox de Box cria uma Box que organiza componentes da esquerda para a direita na ordem que eles são anexados. As linhas 26–43 criam as JTextAreas textArea1 e textArea2. A linha 26 utiliza o construtor de três argumentos de JTextArea, que aceita uma String que representa o texto inicial e dois ints para especificar que a JTextArea tem 10 linhas e 15 colunas. A linha 43 utiliza o construtor de dois argumentos da JTextArea, especificando que a JTextArea tem 10 linhas e 15 colunas. A linha 26 especifica que demo deve ser exibido como o conteúdo JTextArea padrão. Uma JTextArea não fornece barras de rolagem se ela não puder exibir seu conteúdo completo. Desse modo, a linha 27 cria um objeto JScrollPane, inicializa-o com textArea1 e o anexa ao contêiner box. Por padrão, as barras de rolagem horizontais e verticais aparecem conforme necessário em um JscrollPane. As linhas 29–41 criam objeto JButton copyJButton com o rótulo "Copy >>>", adicionam copyJButton ao contêiner box e registram o handler de evento ao ActionEvent de copyJButton. Esse botão fornece o evento externo que determina quando o programa deve copiar o texto selecionado na textArea1 para textArea2. Quando o usuário clicar em copyJButton, a linha 38 em actionPerformed indica que o método getSelectedText (herdado em JTextArea de JTextComponent) deve retornar o texto selecionado de textArea1. O usuário seleciona texto arrastando o mouse sobre o texto desejado para destacá-lo. O método setText muda o texto em textArea2 para a string retornada por getSelectedText. As linhas 43–45 criam textArea2, configuram sua propriedade editável como false e adicionam essa propriedade ao contêiner box. A linha 47 adiciona label3 a JFrame. A partir da Seção 14.18, lembre-se de que o layout padrão de um JFrame é um BorderLayout e que o método add por padrão anexa seu argumento ao CENTER do BorderLayout. Quando o texto alcançar o canto direito de uma JTextArea, o texto pode recorrer para a próxima linha. Isso é referido como mudança de linha automática. Por padrão, JTextArea não muda de linha automaticamente.

Observações sobre a aparência e comportamento 14.19 Para fornecer a funcionalidade de mudança de linha automática para uma neWrap com um argumento true.

JTextArea,

invoque o método

JTextArea

setLi-

Diretivas de barra de rolagem JScrollPane Esse exemplo utiliza um JScrollPane para fornecer rolagem para uma JTextArea. Por padrão, JScrollPane exibe apenas barras de rolagem se elas forem necessárias. As diretivas de barra de rolagem horizontal e vertical de um JScrollPane podem ser configuradas quando ele for construído. Se um programa tiver uma referência a um JScrollPane, o programa pode utilizar os métodos JScrollPane setHorizontalScrollBarPolicy e setVerticalScrollBarPolicy para alterar as diretivas de barra de rolagem a qualquer hora. A classe JScrollPane declara as constantes JScrollPane.VERTICAL_SCROLLBAR_ALWAYS JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS

para indicar que uma barra de rolagem sempre deve aparecer, as constantes

Respostas dos exercícios de autorrevisão

471

JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED

para indicar que uma barra de rolagem deve aparecer somente se necessário (os padrões) e as constantes JScrollPane.VERTICAL_SCROLLBAR_NEVER JScrollPane.HORIZONTAL_SCROLLBAR_NEVER

para indicar que uma barra de rolagem nunca deve aparecer. Se a diretiva de barra de rolagem horizontal for configurada como JScroll­ Pane.HORIZONTAL_SCROLLBAR_NEVER, uma JTextArea anexada ao JScrollPane mudará automaticamente de linhas.

14.21 Conclusão Neste capítulo, você aprendeu muitos componentes GUI e a implementar o tratamento de evento. Você também aprendeu sobre as classes aninhadas, classes internas e classes internas anônimas. Você viu o relacionamento especial entre um objeto de classe interna e um objeto de sua classe de primeiro nível. Você aprendeu a utilizar diálogos de JOptionPane para obter entrada de texto do usuário e a exibir mensagens para o usuário. Você também aprendeu a criar aplicativos que são executados em suas próprias janelas. Discutimos a classe JFrame e componentes que permitem ao usuário interagir com um aplicativo. Também mostramos como exibir texto e imagens para o usuário. Você aprendeu a personalizar Jpanels para criar áreas de desenho personalizadas, que serão extensamente utilizadas no próximo capítulo. Viu como organizar componentes em uma janela utilizando gerenciadores de layout e como criar GUIs mais complexas utilizando JPanels para organizar componentes. Por fim, você aprendeu sobre o componente JTextArea em que um usuário pode inserir texto e um aplicativo pode exibir texto. No Capítulo 25, você aprenderá sobre os componentes GUI mais avançados, como controles deslizantes, menus e gerenciadores de layout mais complexos. No próximo capítulo, você aprenderá a adicionar imagens gráficas ao aplicativo GUI. Os recursos gráficos permitem desenhar formas e texto com cores e estilos.

Resumo Seção 14.1 Introdução • Uma interface gráfica com usuário (Graphical User Interface — GUI) apresenta um mecanismo amigável ao usuário para interagir com um aplicativo. Uma GUI fornece a um aplicativo uma “aparência” e “comportamento” distintos. • Fornecer diferentes aplicativos com componentes de interface com usuário consistentes e intuitivos dá aos usuários uma sensação de familiaridade com um novo aplicativo, para que possam aprendê-lo mais rapidamente. • As GUIs são construídas a partir de componentes GUI — às vezes chamados controles ou widgets.

Seção 14.2 A nova interface Nimbus do Java • Desde a atualização 10 do Java SE 6, o Java vem com uma interface nova, elegante e compatível com várias plataformas conhecidas como Nimbus. • Para configurar o Nimbus como o padrão de todos os aplicativos Java, você deve criar um arquivo de texto swing.properties na pasta lib tanto da pasta de instalação JDK como da pasta de instalação JRE. Insira a seguinte linha do código no arquivo: swing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel

• Para selecionar o Nimbus individualmente por aplicativo, coloque o seguinte argumento de linha de comando depois do comando java e antes do nome do aplicativo quando você executar o aplicativo: ­Dswing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel

Seção 14.3 Entrada/saída baseada em GUI simples com JOptionPane • A maioria dos aplicativos utiliza janelas ou caixas de diálogo (também chamadas de diálogos) para interagir com o usuário. • A classe JOptionPane (pacote javax.swing) fornece caixas de diálogo predefinidas tanto para entrada como para saída. O método JOptionPane static showInputDialog exibe um diálogo de entrada. • Em geral, um prompt utiliza maiúsculas e minúsculas no estilo de frases — empregando a maiúscula inicial apenas na primeira palavra da frase a menos que a palavra seja um nome próprio. • Um diálogo de entrada só pode inserir Strings de entrada. Isso é típico da maioria dos componentes GUI. • O método JOptionPane static showMessageDialog exibe um diálogo de mensagem.

Seção 14.4 Visão geral de componentes Swing • A maioria dos componentes Swing GUI está localizada no pacote javax.swing.

472

Capítulo 14  Componentes GUI: Parte 1

• Juntas, a aparência e a maneira como o usuário interage com o aplicativo são conhecidas como a aparência e comportamento desse aplicativo. Os componentes Swing GUI permitem especificar uniformemente a aparência e comportamento para o aplicativo em todas as plataformas ou utilizar a aparência e comportamento personalizados de cada plataforma. • Os componentes Swing leves não são amarrados aos componentes GUI reais suportados pela plataforma subjacente em que um aplicativo é executado. • Vários componentes Swing são componentes pesados que exigem interação direta com o sistema de janela local, que pode restringir sua aparência e funcionalidades. • A classe Component (pacote java.awt) declara muitos dos atributos e comportamentos comuns aos componentes GUI em pacotes java.awt e javax. swing. • A classe Container (pacote java.awt) é uma subclasse de Component. Components são anexados a Containers, desse modo, os Components podem ser organizados e exibidos na tela. • A classe JComponent (pacote javax.swing) é uma subclasse de Container. JComponent é a superclasse de todos os componentes Swing leves e declara seus atributos e comportamentos comuns. • Alguns recursos de JComponent comuns incluem uma aparência e comportamento plugáveis, teclas de atalho chamadas de mnemônicas, dicas de ferramenta, suporte para tecnologias de apoio a deficientes e suporte para localização de interface com o usuário.

Seção 14.5  Exibição de texto e imagens em uma janela • A classe JFrame fornece os atributos e comportamentos básicos de uma janela. • Um JLabel exibe texto somente de leitura, uma imagem ou tanto texto como imagem. Normalmente, o texto em um JLabel emprega maiúsculas e minúsculas no estilo de frases. • Cada componente GUI deve ser anexado a um contêiner, como uma janela criada com um JFrame. • Muitos IDEs fornecem ferramentas de design de GUI em que você pode especificar o tamanho e localização exata de um componente utilizando o mouse; então, o IDE gerará o código GUI para você. • O método JComponent setToolTipText especifica a dica de ferramenta que é exibida quando o usuário posiciona o cursor do mouse sobre um componente leve. • O método Container add anexa um componente GUI a um Container. • A classe ImageIcon suporta vários formatos de imagem, incluindo GIF, PNG e JPEG. • O método getClass (da classe Object) recupera uma referência ao objeto Class que representa a declaração de classe do objeto em que o método é chamado. • O método Class getResource retorna a localização de seu argumento como um URL. O método getResource utiliza o carregador de classe do objeto Class para determinar a localização do recurso. • A interface SwingConstants (pacote javax.swing) declara constantes de inteiro comuns que são utilizadas com muitos componentes Swing. • Os alinhamentos horizontais e verticais de um Alignment, respectivamente.

JLabel

podem ser configurados com os métodos

setHorizontalAlignment

e

setVertical­

• O método JLabel setText configura o texto exibido em um rótulo. O método correspondente getText recupera o texto atual exibido em um rótulo. • O método JLabel setIcon especifica o Icon a ser exibido em um rótulo. Um método correspondente getIcon recupera o Icon atual exibido em um rótulo. • Os métodos JLabel setHorizontalTextPosition e setVerticalTextPosition especificam a posição de texto no rótulo. • O método JFrame setDefaultCloseOperation com a constante JFrame.EXIT_ON_CLOSE como o argumento indica o que o programa deve terminar quando a janela é fechada pelo usuário. • O método Component setSize especifica a largura e altura de um componente. • O método Component setVisible com o argumento true exibe um JFrame na tela.

Seção 14.6  Campos de texto e uma introdução ao tratamento de evento com classes aninhadas • As GUIs são baseadas em evento — quando o usuário interage com um componente GUI, os eventos guiam o programa para realizar tarefas. • O código que realiza uma tarefa em resposta a um evento é chamado handler de evento e o processo total de responder a eventos é conhecido como tratamento de evento. • A classe JTextField estende a classe JTextComponent (pacote javax.swing.text), que fornece recursos de componentes baseados em texto comuns. A classe JPasswordField estende JTextField e adiciona vários métodos que são específicos ao processamento de senhas. • Um JPasswordField mostra que os caracteres estão sendo digitados à medida que o usuário os insere, mas oculta os caracteres reais com caracteres de eco. • Um componente recebe o foco quando o usuário clica no componente. • O método JTextComponent setEditable pode ser utilizado para tornar um campo de texto não editável.



Resumo

473

• Para responder a um evento de determinado componente GUI, você deve: 1) Criar uma classe que representa o handler de evento. 2) Implementar uma interface apropriada de ouvinte de evento, na classe do Passo 1. 3) Indicar que um objeto da classe dos Passos 1 e 2 deve ser notificado quando ocorrer o evento. Isso é conhecido como registrar handler de evento. • As classes aninhadas podem ser static ou não static. As classes não static aninhadas são chamadas de classes internas e são frequentemente utilizadas para tratamento de evento. • Um objeto de uma classe interna não static deve ser criado por um objeto da classe de nível superior que contém a classe interna. • Um objeto de classe interna pode acessar diretamente as variáveis de instância e métodos de sua classe de primeiro nível. • Uma classe aninhada que é static não exige um objeto de sua classe de primeiro nível e não tem implicitamente uma referência a um objeto da classe de primeiro nível. • Pressionar Enter em um JTextField ou JPasswordField gera um ActionEvent (do pacote java.awt.event) que pode ser tratado por um ­ActionListener (pacote java.awt.event). • O método JTextField addActionListener registra a rotina de tratamento de evento para esse ActionEvent de um campo de texto. Esse método recebe como seu argumento um objeto ActionListener. • O componente GUI com o qual o usuário interage é a origem de evento. • Um objeto ActionEvent contém informações sobre o evento que acabou de ocorrer, como a origem de evento e o texto no campo de texto. • O método ActionEvent getSource retorna uma referência à origem de evento. O método ActionEvent getActionCommand retorna o texto que o usuário digitou em um campo de texto ou o rótulo em um JButton. • O método JPasswordField getPassword retorna a senha digitada pelo usuário.

Seção 14.7  Tipos comuns de eventos GUI e interfaces ouvintes • Todo tipo de objeto de evento costuma ter uma interface de ouvinte do evento correspondente que especifica um ou vários métodos de tratamento de evento que devem ser declarados na classe que implementa a interface.

Seção 14.8  Como o tratamento de evento funciona • Quando um evento ocorre, o componente GUI com o qual o usuário interagiu notifica seus ouvintes registrados chamando o método de tratamento de evento apropriado de cada ouvinte. • Cada JComponent tem uma EventListenerList (pacote javax.swing.event) na qual as referências a ouvintes registrados são armazenadas. • Cada componente GUI suporta vários tipos de evento. Quando um evento ocorre, o evento é despachado apenas para os ouvintes de evento do tipo apropriado. O componente GUI recebe um evento ID único que especifica o tipo de evento, que ele utiliza para decidir o tipo de listener para o qual o evento deve ser despachado e qual método chamar em cada objeto listener.

Seção 14.9  JButton • Um botão é um componente em que o usuário clica para acionar uma ação. Todos os tipos de botão são subclasses de AbstractButton (pacote javax. swing). Em geral, os rótulos de botões utilizam letras maiúsculas e minúsculas no estilo de título de livro. • Os botões de comando são criados com a classe JButton. • Um JButton pode exibir um Icon. Um JButton também pode ter um Icon rollover — um Icon que é exibido quando o usuário posiciona o mouse sobre o botão. • O método setRolloverIcon (da classe AbstractButton) especifica a imagem exibida em um botão quando o usuário posiciona o mouse sobre ele.

Seção 14.10  Botões que mantêm o estado • Há três tipos de botão de estado Swing — JToggleButton, JCheckBox e JRadioButton. • As classes JCheckBox e JRadioButton são subclasses de JToggleButton. • O método Component setFont configura a fonte do componente como um novo objeto Font (pacote java.awt). • Clicar um JCheckBox causa ItemEvent que pode ser tratado por um objeto ItemListener (que deve implementar o método ItemStateChanged). O método addItemListener registra o ouvinte para o ItemEvent de um JCheckBox ou objeto JRadioButton. • O método JCheckBox isSelected determina se um JCheckBox está selecionado. • Os JRadioButtons são semelhantes às JCheckBoxes porque eles têm dois estados — selecionado e não selecionado. Entretanto, os botões de opção normalmente aparecem como um grupo em que apenas um botão pode ser selecionado por vez. Selecionar um botão de opção diferente força a remoção da seleção de todos os outros que estão selecionados. • JRadioButtons são utilizados para representar opções mutuamente exclusivas. • A relação lógica entre JRadioButtons é mantida por um objeto ButtonGroup. • O método ButtonGroup add associa cada JRadioButton com um ButtonGroup. Se mais de um objeto JRadioButton selecionado for adicionado a um grupo, aquele selecionado que foi adicionado primeiro será selecionado quando a GUI for exibida. • Um segundo argumento true indica que o JRadioButton deve aparecer selecionado quando exibido.

474

Capítulo 14  Componentes GUI: Parte 1

Seção 14.11  JComboBox e uso de uma classe interna anônima para tratamento de evento • Uma JComboBox fornece uma lista de itens em que o usuário pode fazer uma única seleção. JComboBoxes geram ItemEvents. • Todo item em uma JComboBox tem um índice. O primeiro item adicionado a uma JComboBox aparece como o item atualmente selecionado quando a JComboBox é exibida. Outros itens são selecionados clicando na JComboBox, que se expande em uma lista a partir da qual o usuário pode fazer uma seleção. • O método JcomboBox setMaximumRowCount configura o número máximo de elementos que é exibido quando o usuário clica na JComboBox. Se houver mais itens, a JComboBox fornece uma barra de rolagem que permite que o usuário role por todos os elementos na lista. • Uma classe interna anônima é uma forma especial de classe interna que é declarada sem nome e, em geral, aparece dentro de uma declaração de método. Como uma classe interna anônima não tem nome, deve-se criar um objeto da classe interna anônima no ponto em que a classe é declarada. • O método JComboBox getSelectedIndex retorna o índice do item selecionado.

Seção 14.12  JList • Uma JList exibe uma série de itens da qual o usuário pode selecionar um ou mais itens. A classe JList suporta listas de uma única seleção e listas de seleção múltipla. • Quando o usuário clicar em um item de uma JList, um ListSelectionEvent ocorre. O método JList addListSelectionListener registra um ListSelectionListener para eventos de seleção de uma JList. Um ListSelectionListener (pacote javax.swing.event) deve implementar o método valueChanged. • O método JList setVisibleRowCount especifica o número de itens que são visíveis na lista. • O método JList setSelectionMode especifica o modo de seleção de uma lista. • Uma JList não fornece automaticamente uma barra de rolagem se houver mais itens na lista que o número de linhas visíveis. Nesse caso, um objeto JScrollPane pode ser utilizado para fornecer a capacidade de rolagem. • O método JFrame getContentPane retorna uma referência ao painel de conteúdo de JFrame em que os componentes GUI são exibidos. • O método JList getSelectedIndex retorna o índice selecionado do item.

Seção 14.13  Listas de seleção múltipla • Uma lista de seleção múltipla permite ao usuário selecionar muitos itens de uma JList. • O método JList setFixedCellWidth configura a largura de uma JList. O método setFixedCellHeight configura a altura de cada item em uma JList. • Não há nenhum evento para indicar que um usuário fez múltiplas seleções em uma lista de seleção múltipla. Normalmente, um evento externo gerado por outro componente GUI (como um JButton) especifica quando as múltiplas seleções em uma JList devem ser processadas. • O método JList setListData configura os itens exibidos em uma JList. O método JList getSelectedValues retorna um array de Objects para representar os itens selecionados em uma JList.

Seção 14.14  Tratamento de evento de mouse • As interfaces listener de eventos MouseListener e MouseMotionListener são utilizadas para tratar eventos de mouse. Os eventos de mouse podem ser interrompidos por qualquer componente GUI que estenda Component. • A interface MouseInputListener (pacote javax.swing.event) estende as interfaces MouseListener e MouseMotionListener para criar uma interface simples que contém todos os seus métodos. • Todo método de tratamento do evento de mouse recebe um objeto MouseEvent que contém informações sobre o evento, incluindo as coordenadas x e y em que o evento ocorreu. As coordenadas são medidas a partir do canto superior esquerdo do componente GUI em que o evento ocorreu. • Os métodos e as constantes de classe InputEvent (superclasse de MouseEvent) permitem que um aplicativo determine o botão do mouse em que o usuário clicou. • A interface MouseWheelListener permite aos aplicativos responder à rotação da roda de um mouse.

Seção 14.15  Classes adaptadoras • Muitas interfaces listener de eventos contêm múltiplos métodos. Para muitas dessas interfaces, os pacotes java.awt.event e javax.swing.event fornecem classes adaptadoras listeners de evento. Uma classe adaptadora implementa uma interface e fornece implementações padrão de seus métodos. Estenda uma classe adaptadora para herdar as implementações de método padrão e, então, sobrescreva o(s) método(s) de que você precisa. • O método MouseEvent getClickCount retorna o número de cliques consecutivos de botão do mouse. Os métodos isMetaDown e isAltDown determinam em que botão do mouse o usuário clicou.

Seção 14.16  Subclasse JPanel para desenhar com o mouse • Os componentes Swing leves que estendem a classe JComponent contêm o método paintComponent, que é chamado quando um componente Swing leve é exibido. Por sobrescrever esse método, você pode especificar como desenhar formas utilizando capacidades de imagens gráficas do Java.



Resumo

475

• Ao personalizar um JPanel para desenhar, a subclasse deve anular o método paintComponent e chamar a versão de superclasse como a primeira instrução no corpo do método sobrescrito. • As subclasses de JComponent suportam transparência. Quando um componente for opaco, paintComponent limpará o fundo do componente antes de o componente ser exibido. • A transparência de um componente Swing leve pode ser configurada com o método setOpaque (um argumento false indica que o componente é transparente). • A classe Point (pacote java.awt) representa uma coordenada x-y. • A classe Graphics é utilizada para desenhar. • O método MouseEvent getPoint obtém o Point em que ocorreu um evento de mouse. • O método repaint (herdado indiretamente de classe Component) indica que um componente deve ser atualizado na tela o mais rápido possível. • O método paintComponent recebe um parâmetro Graphics e é chamado automaticamente a qualquer hora em que um componente leve precisar ser exibido na tela. • O método Graphics fillOval desenha uma oval sólida. Os dois primeiros argumentos são a coordenada x superior esquerda e a coordenada y superior esquerda do quadro delimitador. Os dois últimos argumentos representam a largura e a altura do quadro delimitador.

Seção 14.17  Tratamento de evento chave • A interface KeyListener é utilizada para tratar eventos de teclado que são gerados quando as teclas são pressionadas e liberadas. O método addKey­ Listener da classe Component registra um KeyListener. • O método KeyEvent getKeyCode obtém o código de tecla virtual da tecla que foi pressionada. A classe KeyEvent mantém um conjunto de código de tecla virtual constante que representa cada tecla no teclado. • O método KeyEvent getKeyText retorna uma string que contém o nome da tecla que foi pressionada. • O método KeyEvent getKeyChar obtém o valor Unicode do caractere digitado. • O método KeyEvent isActionKey determina se a tecla em um evento era uma tecla de ação. • O método InputEvent getModifiers determina se alguma tecla modificadora (como Shift, Alt e Ctrl) foi pressionada quando o evento de teclado ocorreu. • O método KeyEvent getKeyModifiersText produz uma string que contém os nomes das teclas modificadoras pressionadas.

Seção 14.18  Introdução a gerenciadores de layout • Os gerenciadores de layout organizam componentes GUI em um contêiner para propósitos de apresentação. • Todos os gerenciadores de layout implementam a interface LayoutManager (pacote java.awt). • O método container setLayout especifica o layout de um contêiner. • FlowLayout coloca componentes da esquerda para a direita na ordem em que são adicionados ao contêiner. Quando o canto do contêiner é alcançado, os componentes continuam a exibir na linha seguinte. FlowLayout permite que os componentes GUI sejam alinhados à esquerda, centrados (o padrão) e alinhados à direita. • O método FlowLayout setAlignment muda o alinhamento para um FlowLayout. • BorderLayout (o padrão para um JFrame) organiza componentes em cinco regiões: NORTE, SOUTH, EAST, WEST e CENTER. NORTH corresponde à parte superior do contêiner. • Um BorderLayout limita um Container a conter no máximo cinco componentes — um em cada região. • GridLayout divide um contêiner em uma grade de linhas e colunas. • O método Container validate recalcula o layout de um contêiner com base no gerenciador de layout atual para o Container e para o conjunto atual de componentes GUI exibidos.

Seção 14.19  Utilizando painéis para gerenciar layouts mais complexos • GUIs complexas frequentemente consistem em múltiplos painéis, com os componentes de cada painel organizados em um layout específico. Todo JPanel pode ter componentes, incluindo outros painéis, anexados a ele com o método Container add.

Seção 14.20  JTextArea • Uma JTextArea fornece uma área para manipular múltiplas linhas do texto. JTextArea é uma subclasse de JTextComponent. • A classe Box é uma subclasse de Container que utiliza um gerenciador de layout BoxLayout para organizar os componentes GUI horizontal ou verticalmente. • O método Box static createHorizontalBox cria um Box que organiza componentes da esquerda para a direita na ordem em que eles são anexados. • O método getSelectedText retorna o texto selecionado de uma JTextArea. • Você pode configurar as diretivas de barra de rolagem horizontal e vertical de um JScrollPane quando ele é construído. Os métodos JScrollPane setHorizontalScrollBarPolicy e setVerticalScrollBarPolicy podem ser utilizados para mudar as diretivas de barra de rolagem a qualquer hora.

476

Capítulo 14  Componentes GUI: Parte 1

Terminologia AbstractButton, classe, 435 Abstract Window Toolkit (AWT), 423 ActionEvent, classe, 432 ActionListener, interface, 432 addActionListener, método da classe JTextField, 432 add, método da classe ButtonGroup, 442 add, método da classe JFrame, 427 addItemListener, método da classe AbstractButton, 440 addKeyListener, método da classe Component, 459 addListSelectionListener, método da classe JList, 447 addMouseListener, método da classe Component, 452 addMouseMotionListener, método da classe Component, 452 aparência e funcionamento plugável (Pluggable Look-And-Feel — PLAF), 425 área dedicada de desenho, 455 AWT (Abstract Window Toolkit), 423 barra de menus, 419 barra de rolagem, 444 barra de título, 419 baseado em evento, 428 BorderLayout, classe, 452 botão, 419 botão de alternação, 435 botão de comando, 435 botão de estado, 438 botão de opção, 435 botão de opção, grupo, 440 botão, rótulo, 436 Box, classe, 470 BoxLayout, classe, 470 ButtonGroup, classe, 440 caixa de combinação, 419 caixa de diálogo, 421 caixa de diálogo modal, 422 caixa de rolagem, 444 caixa de seleção, 435 caixa de seleção, rótulo, 440 CENTER, constante da classe BorderLayout, 452 CENTER, constante da classe FlowLayout, 463 classe adaptadora, 452 classe aninhada, 431 classe de primeiro nível, 431 classe interior anônima, 444 classe interna, 431 código de tecla virtual, 460 Component, classe, 424 componente GUI de peso leve, 424 componente opaco, 455 componente transparente, 455 componentes de peso leve, 424 componentes de peso pesado, 424 componentes GUI Swing, 423 consumir um evento, 432 Container, classe, 424 controles, 419 createHorizontalBox, método da classe Box, 470 diálogo, 421 diálogo de entrada, 421 diálogo de mensagem, 421

dicas de ferramenta, 425 diretivas de barra de rolagem, 470 EAST, constante da classe BorderLayout, 452 echo, caractere da classe JPasswordField, 429 eco, caractere, 429 espaçamento horizontal, 465 espaçamento vertical, 465 EventListenerList, classe, 434 evento, 428 evento de mouse, 435 evento externo, 449 fechar (ocultar) um diálogo, 422 ferramenta de design, 460 fillOval, método da classe Graphics, 457 FlowLayout, classe, 427 foco, 429 Font, classe, 440 fonte de evento, 432 gerenciador de layout, 427 getActionCommand, método da classe ActionEvent, 433 getClass, método da classe Object, 427 getClickCount, método da classe MouseEvent, 455 getContentPane, método da classe JFrame, 447 getIcon, método da classe JLabel, 428 getKeyChar, método da classe KeyEvent, 460 getKeyCode, método da classe KeyEvent, 460 getKeyModifiersText, método da classe KeyEvent, 460 getKeyText, método da classe KeyEvent, 460 getModifiers, método da classe InputEvent, 460 getPassword, método da classe JPasswordField, 433 getPoint, método da classe MouseEvent, 456 getResource, método da classe Class, 427 getSelectedIndex, método da classe JComboBox, 445 getSelectedIndex, método da classe JList, 447 getSelectedText, método da classe JTextComponent, 470 getSelectedValues, método da classe JList, 449 getStateChange, método da classe ItemEvent, 445 getText, método da classe JLabel, 428 getX, método da classe MouseEvent, 452 getY, método da classe MouseEvent, 452 Graphics, classe, 456 Graphics Interchange Format (GIF), 427 GridLayout, classe, 466 GUI, componente, 419 handler de evento, 428 HORIZONTAL_SCROLLBAR_ALWAYS, constante da classe JScrollPane, 470 HORIZONTAL_SCROLLBAR_AS_NEEDED, constante da classe JScrollPane, 471 HORIZONTAL_SCROLLBAR_NEVER, constante da classe JScrollPane, 471 Icon, interface, 427

ID de evento, 435 classe, 427 implementar múltiplas interfaces, 449 índice de um JComboBox, 444 InputEvent, classe, 450 interface gráfica com usuário (Graphical User Interface — GUI), 419 interface ouvinte de evento, 431 isActionKey, método da classe KeyEvent, 460 isAltDown, método da classe InputEvent, 460 isControlDown, método da classe InputEvent, 460 isMetaDown, método da classe InputEvent, 460 isShiftDown, método da classe InputEvent, 460 ItemEvent, classe, 440 ItemListener, interface, 440 janelas, sistema, 424 java.awt, pacote, 423 Java Foundation Classes (JFC), 423 javax.swing, pacote, 423 javax.swing.event, pacote, 433 JButton, classe, 436 JCheckBox, classe, 438 JComboBox, classe, 443 JFrame.EXIT_ON_CLOSE, 428 JLabel, classe, 425 Joint Photographic Experts Group (JPEG), 427 JOptionPane, classe, 421 JPasswordField, classe, 429 JRadioButton, classe, 438 JTextArea, classe, 468 JTextComponent, classe, 429 JTextField, classe, 429 JToggleButton, classe, 438 KeyEvent, classe, 435 KeyListener, interface, 435 keyPressed, método da interface KeyListener, 458 keyReleased, método da interface KeyListener, 458 keyTyped, método da interface KeyListener, 458 layoutContainer, método da interface LayoutManager, 463 LayoutManager, interface, 460 LayoutManager2, interface, 463 LEFT, constante da classe FlowLayout, 463 lista de seleção múltipla, 445 lista de seleção única, 445 lista drop-down, 443 ListSelectionEvent, classe, 445 ListSelectionListener, interface, 447 ListSelectionModel, classe, 447 ListSelectionModel, interface, 447 localização, 425 menu, 419 mnemônico, 425 modelo de evento de delegação, 434 modos de seleção, 447 MouseEvent, classe, 435 MouseInputListener, interface, 449 MouseListener, interface, 435 MouseMotionListener, interface, 435 ImageIcon,



Exercícios de autorrevisão

MouseWheelEvent, classe, 450 MouseWheelListener, interface, 450 mouseWheelMoved, método da interface MouseWheelListener, 450

Nimbus, aparência e comportamento, 420 NORTH, constante da classe BorderLayout, 452 opções mutuamente exclusivas, 440 ouvir eventos, 431 paintComponent, método da classe JComponent, 455 PLAIN_MESSAGE, constante da classe JOptionPane, 422 Point, classe, 456 Portable Network Graphics (PNG), 427 quebra de linha, 470 registrar um handler de evento, 431 repaint, método da classe Component, 456 RIGHT, constante da classe FlowLayout, 463 rollover Icon, 437 rótulo, 425 seta de rolagem, 444 setAlignment, método da classe FlowLayout, 463 setBackground, método da classe Component, 447 setDefaultCloseOperation, método da classe JFrame, 428 setDisabledTextColor, método da classe JTextComponent, 460 setEditable, método da classe JTextComponent, 431 setFixedCellHeight, método da classe JList, 449

setFixedCellWidth, JList, 449 setFont, método da

método da classe classe Component,

440 setHorizontalAlignment, método da classe JLabel, 428 setHorizontalScrollBarPolicy, método da classe JScrollPane, 470 setHorizontalTextPosition, método da classe JLabel, 428 setIcon, método da classe JLabel, 427 setLayout, método da classe Container,

427 setLineWrap, método JTextArea, 470 setListData, método

da classe da classe JList,

449 setMaximumRowCount, método da classe JComboBox, 444 setOpaque, método da classe JComponent,

455 setRolloverIcon, método da classe AbstractButton, 438 setSelectionMode, método da classe JList, 447 setSize, método da classe JFrame, 428 setText, método da classe JLabel, 428 setToolTipText, método da classe JComponent, 427 setVerticalAlignment, método da classe JLabel, 428 setVerticalScrollBarPolicy, método da classe JScrollPane, 470 setVerticalTextPosition, método da classe JLabel, 428 setVisible, método da classe Component,

428 setVisibleRowCount, JList, 446

método da classe

showInputDialog, método JOptionPane, 422

477

da classe

showMessageDialog, método JOptionPane, 422

da classe

SINGLE_INTERVAL_SELECTION, constante de interface ListSelectionModel, 447 SINGLE_SELECTION, constante de ListSelectionModel, 447

interface

SOUTH,

constante da classe BorderLayout, 452 string vazia, 433 SwingConstants, interface, 428 tecla de ação, 458 teclado, evento, 435 transparência de JComponent, 455 tratamento de evento, 428 uso de letras maiúsculas e minúsculas no estilo de frases, 422 uso de letras maiúsculas e minúsculas no estilo título de livro, 422 validate, método da classe Container, 467 valueChanged, método da interface ListSelectionListener, 447 VERTICAL_SCROLLBAR_ALWAYS, constante da classe JScrollPane, 470 VERTICAL_SCROLLBAR_AS_NEEDED, constante da classe JScrollPane, 471 VERTICAL_SCROLLBAR_NEVER, constante da classe JScrollPane, 471 WEST, constante da classe BorderLayout, 452 widgets, 419

Exercícios de autorrevisão 14.1 Preencha as lacunas em cada uma das seguintes afirmações: a) O método ________ é chamado quando o mouse é movido sem pressionamento de botões e um ouvinte de evento é registrado para tratar o evento. b) O texto que não pode ser modificado pelo usuário é chamado texto ________. c) Um(a) ________ organiza os componentes GUI em um Container. d) O método add para anexar componentes GUI é um método da classe ________. e) GUI é um acrônimo de ________. f) O método ________ é utilizado para especificar o gerenciador de layout para um contêiner. g) Uma chamada de método mouseDragged é precedida por uma chamada de método ________ e seguida por uma chamada de método ________. h) A classe ________ contém métodos que exibem diálogos de mensagem e diálogos de entrada. i) Um diálogo de entrada capaz de receber entrada do usuário é exibido com o método ________ da classe ________. j) Um diálogo capaz de exibir uma mensagem para o usuário é exibido com o método ________ da classe ________. k) Tanto JTextFields como JTextAreas estendem diretamente a classe ________.

14.2 Determine se cada sentença é verdadeira ou falsa. Se falsa, explique por quê. a) BorderLayout é o gerenciador de layout padrão do painel de conteúdo de um JFrame. b) Quando o cursor do mouse é movido nos limites de um componente GUI, o método mouseOver é chamado. c) Um JPanel não pode ser adicionado a outro JPanel. d) Em um BorderLayout, dois botões adicionados à região NORTH serão colocados lado a lado. e) Um máximo de cinco componentes pode ser adicionado a um BorderLayout. f) As classes internas não têm permissão de acessar os membros da classe que os envolve. g) Um texto da JTextArea é sempre de leitura (read-only). h) A classe JTextArea é uma subclasse direta da classe Component.

478

Capítulo 14  Componentes GUI: Parte 1

14.3 Localize o(s) erro(s) em cada uma das seguintes instruções e explique como corrigi-lo(s). a) buttonName = JButton( "Caption" ); b) JLabel aLabel, JLabel; // cria referências c) txtField = new JTextField( 50, "Default Text" d) setLayout( new BorderLayout() );

);

button1 = new JButton( "North Star" ); button2 = new JButton( "South Pole" ); add( button1 ); add( button2 );

Respostas dos exercícios de autorrevisão 14.1 a)

mouseMoved. b) não editável (de leitura). c) gerenciador de layout. d) Container. e) interface gráfica com o usuário. f) setLayout. g) mousePressed, mouseReleased. h) JOptionPane. i) showInputDialog, JOptionPane. j) showMessageDialog, JOptionPane. k) JTextComponent. 14.2 a) Verdadeira. b) Falsa. O método mouseEntered é chamado. c) Falsa. Um JPanel pode ser adicionado ao outro JPanel, porque JPanel é uma subclasse indireta de Component. Desse modo, um JPanel é um Component. Qualquer Component pode ser adicionado a um Container. d) Falsa. Apenas o último botão adicionado será exibido. Lembre-se de que apenas um componente deve ser adicionado a cada região em um BorderLayout. e) Verdadeira. [Nota: os painéis que contêm vários componentes podem ser adicionados a cada região.] f) Falsa. As classes internas têm acesso a todos os membros da declaração de classe que os envolve. g) Falsa. JTextAreas são editáveis por padrão. h) Falsa. JTextArea deriva da classe JTextComponent. 14.3 a) new é necessário para criar um objeto. b) JLabel é um nome de classe e não pode ser utilizado como um nome de variável. c) Os argumentos passados para o construtor estão invertidos. A String deve ser passada primeiro. d) BorderLayout foi configurado e os componentes que estão sendo adicionados sem especificar a região são ambos adicionados à região centro. As instruções add adequadas podem ser add( button1, BorderLayout.NORTH ); add( button2, BorderLayout.SOUTH );

Exercícios 14.4 Preencha as lacunas em cada uma das seguintes afirmações: a) A classe JTextField estende diretamente a classe ________. b) O método Container ________ anexa um componente GUI a um contêiner. c) O método ________ é chamado quando um botão de mouse é liberado (sem mover o mouse). d) A classe ________ é utilizada para criar um grupo de JRadioButtons.

14.5 Determine se cada sentença é verdadeira ou falsa . Se falsa, explique por quê. a) Apenas um gerenciador de layout pode ser utilizado por Container. b) Os componentes GUI podem ser adicionados a um Container em qualquer ordem em um BorderLayout. c) JRadioButtons fornecem uma série de opções mutuamente exclusivas (isto é, apenas uma pode ser true por vez). d) O método Graphics setFont é utilizado para configurar a fonte para campos de texto. e) Uma JList exibe uma barra de rolagem se houver mais itens na lista do que podem ser exibidos. f) Um objeto Mouse tem um método chamado mouseDragged.

14.6 Determine se cada sentença é verdadeira ou falsa. Se falsa, explique por quê. a) Um JPanel é um JComponent. b) Um JPanel é um Component. c) Um JLabel é um Container. d) Um JList é um JPanel. e) Um AbstractButton é um JButton. f) Um JTextField é um Object. g) ButtonGroup é uma subclasse de JComponent.



Exercícios

479

14.7 Localize qualquer erro em cada uma das seguintes linhas de código e explique como corrigi-lo. a) import javax.swing.JFrame b) panelObject.GridLayout( 8, 8 ); // configura GridLayout c) container.setLayout( new FlowLayout( FlowLayout.DEFAULT d) container.add( eastButton, EAST ); // BorderLayout

) );

14.8 Crie a seguinte GUI. Você não precisa fornecer funcionalidades.

14.9 Crie a seguinte GUI. Você não precisa fornecer funcionalidades.

14.10 Crie a seguinte GUI. Você não precisa fornecer funcionalidades.

14.11 Crie a seguinte GUI. Você não precisa fornecer funcionalidades.

14.12 (Conversão de temperatura) Escreva um aplicativo de conversão de temperatura que converte de Fahrenheit para Celsius. A temperatura de

Fahrenheit deve ser inserida pelo teclado (via um JTextField). Um JLabel deve ser utilizado para exibir a temperatura convertida. Utilize a seguinte fórmula para a conversão: Celsius =  5 × ( Fahrenheit – 32 ) 9 14.13 (Modificação de conversão de temperatura) Aprimore o aplicativo de conversão de temperatura do Exercício 14.12 adicionando a escala de temperatura Kelvin. O aplicativo também deve permitir ao usuário fazer conversões entre quaisquer duas escalas. Utilize a seguinte fórmula para a conversão entre Kelvin e Celsius (além da fórmula no Exercício 14.12): Kelvin = Celsius + 273,15

14.14 (Jogo Adivinhe o número) Escreva um aplicativo que joga o “Adivinhe o número” como mostrado a seguir: Seu aplicativo escolhe o número a ser adivinhado selecionando um inteiro aleatoriamente no intervalo 1–1000. O aplicativo então exibe o seguinte em um rótulo:

I have a number between 1 and 1000. Can you guess my number? Please enter your first guess.

Um JTextField deve ser usado para entrar a suposição. Conforme cada suposição é inserida, a cor de fundo deve mudar para vermelho ou azul. O vermelho indica que o usuário está ficando “mais quente”, e o azul, “mais frio”. Um JLabel deve exibir "Too High" ou "Too Low" para ajudar o usuário a zerar. Quando o usuário obtiver a resposta correta, "Correct!" deve ser exibido, e o JTextField usado para a entrada deve ser mudado para ser não editável. Um JButton deve ser fornecido para permitir ao usuário jogar de novo. Quando o JButton for clicado, um novo número aleatório deverá ser gerado e a entrada JTextField deve ser alterada para o estado editável.

14.15 (Exibindo eventos) Frequentemente, é útil exibir os eventos que ocorrem durante a execução de um aplicativo. Isso pode ajudá-lo a entender quando os eventos ocorrem e como eles são gerados. Escreva um aplicativo que permite ao usuário gerar e processar cada evento discutido

480

Capítulo 14  Componentes GUI: Parte 1 neste capítulo. O aplicativo deve fornecer os métodos das interfaces ActionListener, ItemListener, ListSelectionListener, MouseListener, MouseMotionListener e KeyListener para exibir as mensagens quando os eventos ocorrem. Utilize o método toString para converter os objetos de evento recebidos em cada handler de evento para Strings que possam ser exibidas. O método toString cria uma String contendo todas as informações no objeto de evento.

14.16 (Jogo de dados baseado em GUI) Modifique o aplicativo da Seção 6.10 para fornecer uma GUI que permite ao usuário clicar em um JBut­ ton para lançar os dados. O aplicativo também deve exibir quatro JLabels e quatro JTextFields, com um JLabel para cada JTextField. Os

JTextFields devem ser usados para exibir os valores de cada dado e a soma dos dados depois de cada lançamento. O ponto deve ser exibido no quarto JTextField quando o usuário não ganhar ou perder no primeiro lançamento e deve continuar a ser exibido até que o jogo seja perdido.

(Opcional) Exercício de Estudo de caso de GUI e imagens gráficas: expandindo a interface 14.17 (Aplicativo de desenho interativo) Neste exercício, você implementará um aplicativo GUI que utiliza a hierarquia MyShape do Exercício 10.2

do estudo de caso sobre GUI para criar um aplicativo de desenho interativo. Você criará duas classes para a GUI e fornecerá uma classe de teste que carrega o aplicativo. As classes da hierarquia MyShape não exigem nenhuma alteração adicional. A primeira classe a ser criada é uma subclasse de JPanel chamada DrawPanel, que representa a área em que o usuário desenha as formas. A classe DrawPanel deve ter as seguintes variáveis de instância: a) Um array shapes do tipo MyShape que armazenará todas as formas que o usuário desenhar. b) Um inteiro shapeCount que conta o número de formas no array. c) Um inteiro shapeType que determina o tipo de forma a ser desenhado. d) Um MyShape currentShape que representa a forma atual que o usuário está desenhando. e) Um Color currentColor que representa a cor atual de desenho. f) Um booleano filledShape que determina se desenhar ou não uma forma preenchida. g) Um JLabel statusLabel que representa a barra de status. A barra de status exibirá as coordenadas da posição atual do mouse. A classe DrawPanel também deve declarar os seguintes métodos: a) Método paintComponent sobrescrito que desenha as formas no array. Utilize a variável de instância shapeCount para determinar quantas formas desenhar. O método paintComponent também deve chamar o método draw de currentShape, contanto que currentShape não seja null. b) Configure métodos para shapeType, currentColor e filledShape. c) O método clearLastShape deve eliminar a última forma desenhada decrementando a variável de instância shapeCount. Assegure que shapeCount nunca é menor que zero. d) O método clearDrawing deve remover todas as formas no desenho atual configurando shapeCount como zero. Os métodos clearLastShape e clearDrawing devem chamar o método repaint (herdado de JPanel) para atualizar o desenho no Draw­ Panel indicando que o sistema deve chamar o método paintComponent. A classe DrawPanel também deve fornecer tratamento de evento para permitir ao usuário desenhar com o mouse. Crie uma única classe interna que tanto estenda MouseAdapter como implemente MouseMotionListener para tratar todos os eventos de mouse em uma classe. Na classe interna, sobrescreva o método mousePressed para que ele atribua a shapeType uma nova forma do tipo especificado por currentShape e inicialize ambos os pontos como a posição do mouse. Em seguida, sobrescreva o método mouseReleased para terminar de desenhar a forma atual e colocá-la no array. Configure o segundo ponto de currentShape como a posição atual do mouse e adicione currentShape ao array. A variável de instância shapeCount determina o índice de inserção. Configure currentShape como null e chame o método repaint para atualizar o desenho com a nova forma. Sobrescreva o método mouseMoved para configurar o texto do statusLabel de modo que ele exiba as coordenadas de mouse — isso atualizará o rótulo com as coordenadas toda vez que o usuário mover (mas não arrastar) o mouse dentro do DrawPanel. Em seguida, sobrescreva o método mouseDragged de modo que ele configure o segundo ponto do currentShape como a posição de mouse atual e chame o método repaint. Isso permitirá ao usuário ver a forma ao arrastar o mouse. Além disso, atualize o JLabel em mouseDragged com a posição atual do mouse. Crie um construtor para DrawPanel que tem um único parâmetro JLabel. No construtor, inicialize statusLabel com o valor passado para o parâmetro. Também inicialize o array shapes com 100 entradas, shapeCount como 0, shapeType como o valor que representa uma linha, cur­ rentShape como null e currentColor como Color.BLACK . O construtor então deve configurar a cor de fundo do DrawPanel como .WHITE e registrar o MouseListener e MouseMotionListener para que o JPanel trate adequadamente os eventos de mouse. Em seguida, crie uma subclasse JFrame chamada DrawFrame que forneça uma GUI para permitir ao usuário controlar vários aspectos de desenho. Para o layout do DrawFrame, recomendamos um BorderLayout, com os componentes na região NORTH , o principal painel de desenho na região CENTER e uma barra de status na região SOUTH, como na Figura 14.49. No painel superior, crie os componentes listados abaixo. O handler de evento de cada componente deve chamar o método adequado na classe DrawPanel. a) Um botão para desfazer a última forma desenhada. b) Um botão para eliminar todas as formas do desenho. c) Uma caixa de combinação para selecionar a partir das 13 cores predefinidas. d) Uma caixa de combinação para selecionar a forma desenhar. e) Uma caixa de seleção que especifica se uma forma deve ou não ter preenchimento. Declare e crie os componentes de interface no construtor de DrawFrame. Você precisará criar a barra de status JLabel antes de criar o Draw­ Panel, para que possa passar o JLabel como um argumento para o construtor do DrawPanel. Por fim, crie uma classe de teste que inicialize e exiba o DrawFrame para executar o aplicativo.



Fazendo a diferença

481

Figura 14.49  |  Interface para desenhar formas. 14.18 (Versão baseada em GUI do Estudo de Caso de ATM) Reimplemente o Estudo de Caso de ATM dos capítulos 12–13 como um aplicativo baseado em GUI. Utilize componentes GUI para aproximar-se da interface com o usuário ATM mostrada na Figura 12.1. Para o dispensador de dinheiro e o slot de depósito, utilize JButtons rotulados de Remove Cash e Insert Envelope. Isso permitirá que o aplicativo receba eventos que indicam quando o usuário saca o dinheiro e quando insere um envelope de depósito, respectivamente.

Fazendo a diferença 14.19 (Ecofont) Ecofont (www.ecofont.eu/ecofont_en.html) — desenvolvida pela Spranq (uma empresa situada na Holanda) — é uma fonte

de computador gratuita, distribuída sob o regime Open Source e projetada para reduzir 20% da quantidade de tinta empregada na impressão, reduzindo assim também o número de cartuchos de tinta usados e o impacto ambiental dos processos de fabricação e entrega (utilizando menos energia, menos combustível para despacho, e assim por diante). A fonte, baseada em Verdana sem serifa, tem pequenos “furos” circulares que não são visíveis em menores tamanhos — como os tamanhos 9 ou 10 pontos mais utilizados. Baixe a Ecofont e instale o arquivo de fontes Spranq_ eco_sans_regular.ttf utilizando as instruções do site Ecofont. Depois, desenvolva um programa baseado em GUI que permite digitar uma linha de texto com a Ecofont. Crie os botões Increase Font Size e Decrease Font Size que permitem aumentar e diminuir o tamanho da fonte um ponto por vez. Comece com um tamanho padrão de 9 pontos. À medida que aumentar o tamanho, você será capaz de ver mais claramente os furos nas letras. À medida que você reduz, os furos serão menos aparentes. Qual é o menor tamanho de fonte em que você começa a notar os furos?

14.20 (Professor de digitação: aprimorando uma habilidade crucial na era dos computadores) Digitar rápida e corretamente é uma habili-

dade essencial para trabalhar efetivamente com computadores e Internet. Neste exercício, você construirá um aplicativo GUI que pode ajudar usuários a aprender a digitar corretamente sem olhar para o teclado. O aplicativo deve exibir um teclado virtual (Figura 14.50) e permitir ao usuário observar o que ele está digitando na tela sem olhar para o teclado real. Utilize JButtons para representar as teclas. À medida que o usuário pressiona cada tecla, o aplicativo destaca o JButton correspondente na GUI e adiciona o caractere a uma JTextArea que mostra o que o usuário digitou até o momento. [Dica: para destacar um JButton, utilize seu método setBackground para mudar a cor de fundo. Quando a tecla é liberada, reinicialize a cor de fundo original. Você pode obter a cor de fundo original do JBUTTON com o método getBackground antes de mudar sua cor.]

Figura 14.50  |  Professor de digitação.

482

Capítulo 14  Componentes GUI: Parte 1 Você pode testar o seu programa digitando um pangrama — uma frase em que são usadas todas as letras do alfabeto pelo menos uma vez — como “The quick brown fox jumped over lazy dogs”. Você pode encontrar outros pangramas na Web. Para tornar o programa mais interessante, você poderia monitorar a exatidão do usuário. Poderia fazer o usuário digitar frases específicas que você teria pré-armazenado no programa e exibir na tela acima do teclado virtual. Poderia monitorar quantas teclas o usuário pressiona corretamente e quantas incorretamente. Você também poderia monitorar as teclas com que o usuário tem dificuldades e exibir um relatório que mostre essas teclas.

Uma imagem vale dez mil palavras. — Provérbio chinês

Trate a natureza em termos de cilindros, esferas, cones, tudo em perspectiva. — Paul Cézanne

As cores, como a expressão facial, mudam conforme as emoções. — Pablo Picasso

Nada se torna real até ser experimentado – mesmo um provérbio não significa nada para você até sua vida ilustrá-lo. — John Keats

15

Imagens gráficas e Java 2D™

Objetivos Neste capítulo, você aprenderá: 

A entender contextos gráficos e objetos gráficos.



A manipular cores.



A manipular fontes.



A utilizar métodos da classe Graphics para desenhar linhas, retângulos, retângulos com cantos arredondados, retângulos tridimensionais, ovais, arcos e polígonos.



A utilizar métodos da classe Graphics2D da Java 2D API para desenhar linhas, retângulos, retângulos com cantos arredondados, elipses, arcos e caminhos gerais.



A especificar as características de Paint e Stroke das formas exibidas com Graphics2D.

484

Sumário

Capítulo 15

Imagens gráficas e Java 2D™

15.1 Introdução

15.6 Desenhando arcos

15.2 Contextos gráficos e objetos gráficos

15.7 Desenhando polígonos e polilinhas

15.3 Controle de cor

15.8 Java 2D API

15.4 Manipulando fontes

15.9 Conclusão

15.5 Desenhando linhas, retângulos e ovais Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios | Fazendo a diferença

15.1 Introdução Neste capítulo, oferecemos uma visão geral de várias capacidades do Java para desenhar formas bidimensionais, controlar cores e controlar fontes. Um dos atrativos iniciais do Java foi seu suporte a imagens gráficas que permitia aos programadores aprimorar visualmente seus aplicativos. O Java agora contém muitas capacidades mais sofisticadas de desenho como parte da Java 2D™ API. Este capítulo inicia com uma introdução a muitas capacidades originais de desenho do Java. Em seguida, apresentamos várias capacidades mais poderosas da Java 2D, como controlar o estilo das linhas utilizado para desenhar formas e a maneira como formas são preenchidas com cores e padrões. A Figura 15.1 mostra uma parte da hierarquia de classe Java que inclui várias das classes gráficas básicas e várias das classes e interfaces Java 2D API abordadas neste capítulo. A classe Color contém métodos e constantes para manipular cores. A classe JComponent contém o método paintComponent, que é utilizado para desenhar imagens gráficas em um componente. A classe Font contém métodos e constantes para manipular fontes. A classe FontMetrics contém métodos para obter informações de fonte. A classe Graphics contém métodos para desenhar strings, linhas, retângulos e outras formas. A classe Graphics2D, que estende a classe Graphics, é utilizada para desenhar com a Java 2D API. A classe Polygon contém métodos para criar polígonos. A metade inferior da figura lista várias classes e interfaces da Java 2D API. A classe BasicStroke ajuda a especificar as características do desenho de linhas. As classes GradientPaint e TexturePaint ajudam a especificar as características para preencher formas com cores ou padrões. As classes GeneralPath, Line2D, Arc2D, Ellipse2D, Rectangle2D e RoundRectangle2D representam várias formas 2D do Java. [Nota: começamos discutindo as capacidades gráficas originais do Java e, então, avançamos para a Java 2D API. As classes que eram parte das capacidades gráficas originais do Java são agora consideradas parte da API 2D do Java.] Para começar a desenhar em Java, devemos primeiro entender o sistema de coordenadas do Java (Figura 15.2), que é um esquema para identificar cada ponto na tela. Por padrão, o canto superior esquerdo de um componente GUI (por exemplo, uma janela) tem as coordenadas (0, 0). Um par de coordenadas é composto de uma coordenada x (a coordenada horizontal) e uma coordenada y (a coordenada vertical). A coordenada x é a distância horizontal que vai do lado direito ao lado esquerdo da tela. A coordenada y é a distância vertical de baixo para cima da tela. O eixo x descreve cada coordenada horizontal e o eixo y, cada coordenada vertical. As coordenadas são utilizadas para indicar onde as imagens gráficas devem ser exibidas em uma tela. As unidades coordenadas são medidas em pixels (que significa “picture element”). Um pixel é a menor unidade de exibição de resolução do monitor.

Dica de portabilidade 15.1 Monitores diferentes têm resoluções diferentes (isto é, a densidade dos pixels varia). Isso pode fazer as imagens gráficas aparecerem com tamanhos diferentes em diferentes monitores ou no mesmo monitor com diferentes configurações.

15.2 Contextos gráficos e objetos gráficos Um contexto gráfico permite desenhar na tela. Um objeto Graphics gerencia um contexto gráfico e desenha pixels na tela que representam texto e outros objetos gráficos (por exemplo, linhas, elipses, retângulos e outros polígonos). Objetos Graphics contêm métodos para desenhar, manipular fontes, manipular cores e coisas do tipo. A classe Graphics é uma classe abstract (isto é, objetos Graphics não podem ser instanciados). Isso contribui para a portabilidade do Java. Como o desenho é realizado de várias maneiras em cada plataforma que suporta o Java, não poderia haver somente uma implementação das capacidades de desenho em todos os sistemas. Por exemplo, as capacidades gráficas que permitem que um PC, executando o Windows Microsoft, desenhe um retângulo são diferentes daquelas que permitem a uma estação de trabalho Linux desenhar um retângulo — e ambas são diferentes das capacidades gráficas que permitem ao Macintosh desenhar um retângulo. Quando o Java é implementado em cada plataforma, é criada uma subclasse de Graphics que implementa as capacidades de desenho. Essa implementação permanece oculta pela classe Graphics, que fornece a interface que permite utilizar imagens gráficas de uma maneira independente de plataforma.



15.2  Contextos gráficos e objetos gráficos

485

java.lang.Object java.awt.Color java.awt.Component

java.awt.Container

javax.swing.JComponent

java.awt.Font java.awt.FontMetrics java.awt.Graphics

java.awt.Graphics2D

java.awt.Polygon «interface» java.awt.Paint

java.awt.BasicStroke

«interface» java.awt.Shape

java.awt.GradientPaint java.awt.TexturePaint

«interface» java.awt.Stroke

java.awt.geom.GeneralPath java.awt.geom.Line2D java.awt.geom.RectangularShape

java.awt.geom.Arc2D java.awt.geom.Ellipse2D java.awt.geom.Rectangle2D java.awt.geom.RoundRectangle2D

Figura 15.1  |  As classes e interfaces utilizadas neste capítulo são provenientes das capacidades gráficas originais do Java e da Java 2D API.

+x

(0, 0)

(x, y)

+y eixo y

Figura 15.2  |  Sistema de coordenadas Java. As unidades são medidas em pixels.

eixo x

486

Capítulo 15

Imagens gráficas e Java 2D™

Lembre-se do Capítulo 14 de que a classe Component é a superclasse para muitas das classes no pacote java.awt. A classe Jcomponent (pacote javax.swing), que herda indiretamente da classe Component, contém um método paintComponent que pode ser utilizado para desenhar imagens gráficas. O método paintComponent recebe um objeto Graphics como um argumento. Esse objeto é passado para o método paintComponent pelo sistema quando um componente Swing, de peso leve, precisa ser repintado. O cabeçalho para o método paintComponent é public void paintComponent( Graphics g )

O parâmetro g recebe uma referência a uma instância da subclasse específica de sistema que a Graphics estende. O cabeçalho do método anterior deve parecer familiar para você — é o mesmo que utilizamos em alguns aplicativos no Capítulo 14. Na verdade, a classe JComponent é uma superclasse de JPanel. Muitas capacidades da classe JPanel são herdadas de classe JComponent. Você raramente chama o método paintComponent diretamente, porque desenhar elementos gráficos é um processo baseado em eventos. Como mencionamos no Capítulo 11, o Java utiliza um modelo multithread para a execução de programas. Cada thread é uma atividade paralela. Cada programa pode ter muitas threads. Ao criar um aplicativo baseado na GUI, uma dessas threads é conhecida como a thread de despacho do evento (EDT) e é utilizada para processar todos os eventos GUI. Todo o desenho e a manipulação dos componentes GUI devem ser realizados nessa thread. Quando um aplicativo GUI é executado, o contêiner de aplicativo chama o método paintComponent (na thread de despacho do evento) para cada componente leve à medida que a GUI é exibida. Para paintComponent ser chamado novamente, deve ocorrer um evento (como cobrir e descobrir o componente com uma outra janela). Se precisar de paintComponent para executar (isto é, se quiser atualizar os elementos gráficos desenhados em um componente Swing), você poderá chamar o método repaint, que é herdado por todos os JComponents indiretamente da classe Component (pacote java.awt). O método repaint é frequentemente chamado para solicitar uma chamada ao método paintComponent. O cabeçalho para repaint é public void repaint()

15.3 Controle de cor A classe Color declara métodos e constantes para manipular cores em um programa Java. As constantes color pré-declaradas estão resumidas na Figura 15.3, e vários construtores e métodos color estão resumidos na Figura 15.4. Observe que dois dos métodos na Figura 15.4 são métodos Graphics específicos para cores. Constante Color

Valor RGB

public final static Color RED

255, 0, 0

public final static Color GREEN

0, 255, 0

public final static Color BLUE

0, 0, 255

public final static Color ORANGE

255, 200, 0

public final static Color PINK

255, 175, 175

public final static Color CYAN

0, 255, 255

public final static Color MAGENTA

255, 0, 255

public final static Color YELLOW

255, 255, 0

public final static Color BLACK

0, 0, 0

public final static Color WHITE

255, 255, 255

public final static Color GRAY

128, 128, 128

public final static Color LIGHT_GRAY

192, 192, 192

public final static Color DARK_GRAY

64, 64, 64

Figura 15.3 | Constantes Color e seus valores de RGB.

Cada cor é criada a partir de um componente vermelho, um verde e um azul. Juntos, esses componentes são chamados valores RGB. Todos os três componentes RGB podem ser inteiros no intervalo de 0 a 255 ou podem ser valores de ponto flutuante nos intervalo de 0,0 a 1,0. O primeiro componente RGB especifica o valor de vermelho; o segundo, o valor de verde; e o terceiro, o valor de azul. Quanto maior o valor de RGB, maior a quantidade dessa cor particular. O Java permite escolher entre 256 × 256 × 256 cores (aproximadamente 16,7 milhões). Nem todos os computadores são capazes de exibir todas essas cores. A tela de computador exibira a cor mais próxima possível.



15.3  Controle de cor

Método

487

Descrição

Construtores e métodos Color public Color( int r, int g, int b )

Cria uma cor com base nos componentes azul, verde, vermelho expressos como valores de ponto flutuante de 0,0 a 1,0.

public Color( float r, float g, float b )

Retorna um valor entre 0 e 255 representando o componente vermelho.

public int getRed()

Retorna um valor entre 0 e 255 representando o conteúdo de vermelho.

public int getGreen()

Retorna um valor entre 0 e 255 representando o conteúdo de verde.

public int getBlue()

Retorna um valor entre 0 e 255 representando o conteúdo de azul.

Métodos Graphics para manipular Colors public Color getColor()

Retorna o objeto Color que representa as cores atuais no contexto gráfico.

public void setColor( Color c )

Configura a cor atual para desenho com o contexto gráfico.

Figura 15.4  |  Métodos Color e métodos Graphics relacionados com cor.

Dois construtores da classe Color são mostrados na Figura 15.4 — um que recebe três argumentos int e outro que recebe três argumentos float, com cada argumento especificando a quantidade de vermelho, verde e azul. Os valores int devem estar no intervalo de 0 a 255 e os valores float devem estar no intervalo de 0,0 a 1,0. O novo objeto Color terá as quantidades de vermelho, verde e azul especificadas. Os métodos getRed, getGreen e getBlue retornam valores inteiros de 0 a 255 representando as quantidades de vermelho, verde e azul, respectivamente. O método Graphics getColor retorna um objeto Color representando a cor de desenho atual. O método Graphics setColor configura a cor de desenho atual.

Desenhando em cores diferentes As figuras 15.5 e 15.6 demonstram vários métodos da Figura 15.4 desenhando retângulos preenchidos e Strings em várias diferentes cores. Quando o aplicativo inicia a execução, o método paintComponent da classe ColorJPanel (linhas 10–37 da Figura 15.5) é chamado para pintar a janela. A linha 17 utiliza o método Graphics setColor para configurar as cores atuais de desenho. O método setColor recebe um objeto Color. A expressão new Color( 255, 0, 0 ) cria um novo objeto Color que representa vermelho (valor vermelho 255 e 0 para os valores de azul e verde). A linha 18 utiliza o método Graphics fillRect para desenhar um retângulo preenchido com a cor atual. O método fillRect desenha um retângulo baseado em seus quatro argumentos. Os dois primeiros valores de inteiro representam a coordenada x superior esquerda e a coordenada y superior esquerda onde o objeto Graphics começa a desenhar o retângulo. O terceiro e quarto argumentos são números inteiros não negativos que representam a largura e a altura do retângulo em pixels, respectivamente. Um retângulo desenhado com o método fillRect é preenchido pela cor atual do objeto Graphics. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

// Figura 15.5: ColorJPanel.java // Demonstrando Colors. import java.awt.Graphics; import java.awt.Color; import javax.swing.JPanel; public class ColorJPanel extends JPanel { // desenha retângulos e Strings em cores diferentes public void paintComponent( Graphics g ) { super.paintComponent( g ); // chama o paintComponent da superclasse this.setBackground( Color.WHITE ); // nova cor de desenho configurada utiliza inteiros g.setColor( new Color( 255, 0, 0 ) ); g.fillRect( 15, 25, 100, 20 ); g.drawString( "Current RGB: " + g.getColor(), 130, 40 ); // nova cor de desenho configurada utiliza floats g.setColor( new Color( 0.50f, 0.75f, 0.0f ) ); g.fillRect( 15, 50, 100, 20 ); g.drawString( "Current RGB: " + g.getColor(), 130, 65 );

488 25 26 27 28 29 30 31 32 33 34 35 36 37 38

Capítulo 15  Imagens gráficas e Java 2D™

// nova cor de desenho configurada usa objetos Color estáticos g.setColor( Color.BLUE ); g.fillRect( 15, 75, 100, 20 ); g.drawString( "Current RGB: " + g.getColor(), 130, 90 ); // exibe valores individuais de RGB Color color = Color.MAGENTA; g.setColor( color ); g.fillRect( 15, 100, 100, 20 ); g.drawString( "RGB values: " + color.getRed() + ", " + color.getGreen() + “, “ + color.getBlue(), 130, 115 ); } // fim do método paintComponent } // fim da classe ColorJPanel

Figura 15.5  |  Exemplo de alteração de Color em um desenho.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// Figura 15.6: ShowColors.java // Demonstrando Colors. import javax.swing.JFrame; public class ShowColors { // executa o aplicativo public static void main( String[] args ) { // cria o frame para ColorJPanel JFrame frame = new JFrame( "Using colors" ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); ColorJPanel colorJPanel = new ColorJPanel(); // cria ColorJPanel frame.add( colorJPanel ); // adiciona colorJPanel ao frame frame.setSize( 400, 180 ); // configura o tamanho do frame frame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe ShowColors

Figura 15.6  |  Criando um JFrame para exibir cores no JPanel.

A linha 19 utiliza o método Graphics drawString para desenhar uma String com a cor atual. A expressão g.getColor() recupera a cor atual do objeto Graphics. Então, concatenamos a Color com a string "Current RGB: ", resultando em uma chamada implícita ao método toString da classe Color. A representação String de um Color contém o nome da classe e pacote (java.awt.Color) e os valores de vermelho, verde e azul.

Observações sobre a aparência e comportamento 15.1 Cada um percebe as cores de uma maneira diferente. Escolha suas cores cuidadosamente para assegurar que seu aplicativo é legível, tanto para pessoas que podem perceber as cores como para aquelas que são daltônicas. Tente evitar utilizar várias cores diferentes com valores muito próximos.



15.3  Controle de cor

489

As linhas 22–24 e 27–29 realizam as mesmas tarefas novamente. A linha 22 utiliza o construtor Color com três argumentos float para criar uma cor verde escura (0,50f para vermelho, 0,75f para verde e 0,0f para azul). Observe a sintaxe dos valores. A letra f acrescentada a um literal de ponto flutuante indica que o literal deve ser tratado como tipo float. Lembre-se de que, por padrão, literais de ponto flutuante são tratados como um tipo double. A linha 27 configura a cor atual do desenho como uma das constantes Color (Color.BLUE) pré-declaradas. As constantes Color são static, assim elas são criadas quando a classe Color é carregada na memória em tempo de execução. A instrução nas linhas 35–36 faz chamadas aos métodos Color getRed, getGreen e getBlue na constante Color.MAGENTA pré-declarada. O método main da classe ShowColors (linhas 8–18 da Figura 15.6) cria a JFrame que conterá um objeto ColorJPanel onde as cores serão exibidas.

Observação de engenharia de software 15.1 Para alterar as cores, você deve criar um novo objeto Color (ou utilizar uma das constantes Color pré-declaradas). Como ocorre com objetos String, objetos Color são imutáveis (não modificáveis).

O pacote javax.swing fornece o componente GUI JColorChooser que permite aos usuários do aplicativo selecionar cores. O aplicativo das figuras 15.7 e 15.8 demonstra uma caixa de diálogo JColorChooser. Quando você clica no botão Change Color, um diálogo JColor­ Chooser aparece. Quando você seleciona uma cor e pressiona o botão OK do diálogo, a cor de fundo da janela do aplicativo muda. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

// Figura 15.7: ShowColors2JFrame.Java // Escolhendo cores com JColorChooser. import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JColorChooser; import javax.swing.JPanel; public class ShowColors2JFrame extends JFrame { private JButton changeColorJButton; private Color color = Color.LIGHT_GRAY; private JPanel colorJPanel; // configura a GUI public ShowColors2JFrame() { super( "Using JColorChooser" ); // cria JPanel para exibir as cores colorJPanel = new JPanel(); colorJPanel.setBackground( color ); // configura changeColorJButton e registra seu handler de evento changeColorJButton = new JButton( "Change Color" ); changeColorJButton.addActionListener( new ActionListener() // classe interna anônima { // exibe JColorChooser quando o usuário clica no botão public void actionPerformed( ActionEvent event ) { color = JColorChooser.showDialog( ShowColors2JFrame.this, “Choose a color”, color ); // configura a cor padrão, se nenhuma cor for retornada if ( color == null ) color = Color.LIGHT_GRAY; // muda a cor de fundo do painel de conteúdo colorJPanel.setBackground( color ); } // fim do método actionPerformed } // fim da classe interna anônima

490 47 48 49 50 51 52 53 54 55

Capítulo 15  Imagens gráficas e Java 2D™ ); // fim da chamada para addActionListener add( colorJPanel, BorderLayout.CENTER ); // adiciona colorJPanel add( changeColorJButton, BorderLayout.SOUTH ); // adiciona botão setSize( 400, 130 ); // configura o tamanho do frame setVisible( true ); // exibe o frame } // fim do construtor ShowColor2JFrame } // fim da classe ShowColors2JFrame

Figura 15.7  |  Caixa de diálogo JColorChooser.

1 2 3 4 5 6 7 8 9 10 11 12 13

// Figura 15.8: ShowColors2.java // Escolhendo cores com JColorChooser. import javax.swing.JFrame; public class ShowColors2 { // executa o aplicativo public static void main( String[] args ) { ShowColors2JFrame application = new ShowColors2JFrame(); application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); } // fim de main } // fim da classe ShowColors2

(a) Janela inicial do aplicativo.

(b) Janela JColorChooser .

Selecione uma das amostras de cor

(c) Janela do aplicativo depois de alterar a cor de fundo de JPanel

Figura 15.8  |  Escolhendo cores com JColorChooser.

A classe JColorChooser fornece um método static showDialog, que cria um objeto JColorChooser, anexa-o a uma caixa de diálogo e exibe o diálogo. As linhas 36–37 da Figura 15.7 invocam esse método para exibir o diálogo do seletor de cores. O método show­ Dialog retorna o objeto Color selecionado, ou null se o usuário pressionar Cancel ou fechar o diálogo sem pressionar OK. O método aceita três argumentos — uma referência ao seu Component pai, uma String para exibir na barra de título do diálogo e a Color inicial selecionada para o diálogo. O componente pai é uma referência à janela a partir da qual o diálogo é exibido (nesse caso JFrame, com o nome de referência frame). A caixa de diálogo será centralizada no pai. Se o pai for null, o diálogo é centralizado na tela. Enquanto o diálogo de seleção de cor está na tela, o usuário não pode interagir com o componente pai até que o diálogo seja liberado. Esse tipo de diálogo é chamado diálogo modal. Depois de o usuário selecionar uma cor, as linhas 40–41 determinam se color é null e, nesse caso, configuram color como Color. LIGHT_GRAY. A linha 44 invoca o método setBackground para alterar a cor de fundo de JPanel. O método setBackground é um dos muitos métodos Component que podem ser utilizados na maioria dos componentes GUI. Observe que o usuário pode continuar a utilizar o botão Change Color para alterar a cor de fundo do aplicativo. A Figura 15.8 contém o método main que executa o programa.

15.4 Manipulando fontes

491

A Figura 15.8(b) mostra a caixa de diálogo JColorChooser padrão que permite ao usuário selecionar uma cor a partir de várias amostras de cores. Observe que na verdade há três guias ao longo da parte superior da caixa de diálogo — Swatches, HSB e RGB. Esses representam três maneiras diferentes de selecionar uma cor. A guia HSB permite selecionar uma cor com base no tom, saturação e brilho — os valores utilizados para definir a quantidade de luz em uma cor. Não discutimos valores de HSB. Para informações adicionais sobre eles, visite whatis.techtarget.com/definition/0,,sid9_gci212262,00.html. A guia RGB permite selecionar uma cor utilizando controles deslizantes para selecionar os componentes vermelhos, verdes e azuis. As guias HSB e RGB são mostradas na Figura 15.9.

Controles deslizantes para selecionar as cores componentes (vermelho, verde e azul)

Figura 15.9 | As guias HSB e RGB da caixa de diálogo JColorChooser.

15.4 Manipulando fontes Esta seção introduz métodos e constantes para manipular fontes. A maioria dos métodos de fonte e das constantes de fonte é parte da classe Font. Alguns métodos da classe Font e da classe Graphics estão resumidos na Figura 15.10. Método ou constante

Descrição

Constantes, construtores e métodos de Font public final static int PLAIN

Uma constante representando um estilo de fonte simples.

public final static int BOLD

Uma constante representando um estilo de fonte negrito.

public final static int ITALIC

Uma constante representando um estilo de fonte itálico.

public Font( String name, int style,

Cria um objeto Font com o nome, o estilo e o tamanho de fonte especificados.

int size ) public int getStyle()

Retorna um int indicando o estilo da fonte atual.

public int getSize()

Retorna um int indicando o tamanho da fonte atual.

public String getName()

Retorna o nome da fonte atual como uma string.

public String getFamily()

Retorna o nome da família de fontes como uma string.

492

Capítulo 15  Imagens gráficas e Java 2D™

Método ou constante

Descrição

public boolean isPlain()

Retorna true se a fonte for simples, caso contrário false.

public boolean isBold()

Retorna true se a fonte for negrito, caso contrário false.

public boolean isItalic()

Retorna true se a fonte for itálica, caso contrário false.

Métodos Graphics para manipular Fonts public Font getFont()

Retorna uma referência de objeto Font representando a fonte atual.

public void setFont( Font f )

Configura a fonte atual como a fonte, o estilo e o tamanho especificados pela referência de objeto Font f.

Figura 15.10  |  Métodos e constantes relacionados com Font.

O construtor da classe Font aceita três argumentos — nome da fonte, estilo da fonte e tamanho da fonte. O nome da fonte é qualquer fonte atualmente suportada pelo sistema em que o programa está executando, como as fontes padrão Java Monospaced, SansSerif e Serif. O estilo de fonte é Font.PLAIN, Font.ITALIC ou Font.BOLD (cada um é um campo static da classe Font). Os estilos de fonte podem ser utilizados em combinação (por exemplo, Font.ITALIC + Font.BOLD). O tamanho da fonte é medido em pontos. Um ponto é 1/72 de uma polegada. O método Graphics setFont configura a fonte atual de desenho — a fonte em que o texto será exibido — com seu argumento Font.

Dica de portabilidade 15.2 O número de fontes varia de um sistema para outro. O Java fornece cinco nomes de fonte — Serif, Monospaced, SansSerif, Dialog e DialogInput — que podem ser utilizados em todas as plataformas Java. O ambiente de tempo de execução Java (Java Runtime Environment — JRE) em cada plataforma mapeia esses nomes lógicos de fonte para fontes reais instaladas na plataforma. As fontes reais utilizadas podem variar entre plataformas.

O aplicativo das figuras 15.11 e 15.12 exibe texto em quatro fontes diferentes, com cada fonte em um tamanho diferente. A Figura 15.11 usa o construtor Font para inicializar objetos Font (nas linhas 16, 20, 24 e 29) que são passados para o método Graphics setFont a fim de alterar a fonte do desenho. Cada chamada ao construtor Font passa o nome de uma fonte (Serif, Monospaced ou SansSerif) como uma string, um estilo de fonte (Font.PLAIN, Font.ITALIC ou Font.BOLD) e um tamanho de fonte. Uma vez que o método Graphics setFont é invocado, todo texto exibido após a chamada aparecerá na nova fonte até que a fonte seja alterada. Informações de cada fonte são exibidas nas linhas 17, 21, 25, 30 e 31 com o método drawString. Note que a coordenada passada para drawString corresponde ao canto inferior esquerdo da linha de base da fonte. A linha 28 altera a cor do desenho para vermelho a fim de que a próxima string exibida apareça em vermelho. As linhas 30–31 exibem informações sobre o objeto Font final. O método getFont da classe Graphics retorna um objeto Font para representar a fonte atual. O método getName retorna o nome atual da fonte como uma string. O método getSize retorna o tamanho da fonte em pontos. A Figura 15.12 contém o método main, que cria um JFrame. Adicionamos um objeto FontJPanel a esse JFrame (linha 15), que exibe as imagens gráficas criadas na Figura 15.11.

Observação de engenharia de software 15.2 Para alterar a fonte, você deve criar um novo objeto Font. Objetos Font são imutáveis — a classe Font não tem nenhum método set para alterar as características da fonte atual.

1 2 3 4 5 6 7 8 9 10 11 12 13

// Figura 15.11: FontJPanel.java // exibe strings em diferentes fontes e cores. import java.awt.Font; import java.awt.Color; import java.awt.Graphics; import javax.swing.JPanel; public class FontJPanel extends JPanel { // exibe Strings em diferentes fontes e cores public void paintComponent( Graphics g ) { super.paintComponent( g ); // chama o paintComponent da superclasse

14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

15.4  Manipulando fontes

493

// fonte configurada como Serifa (Times), negrito, 12 pt e desenha uma string g.setFont( new Font( “Serif”, Font.BOLD, 12 ) ); g.drawString( "Serif 12 point bold.", 20, 30 ); // configura font como Monospaced (Courier), itálico, 24 pt e desenha uma string g.setFont( new Font( “Monospaced”, Font.ITALIC, 24 ) ); g.drawString( "Monospaced 24 point italic.", 20, 50 ); // configura font como SansSerif (Helvetica), simples, 14 pt e desenha uma string g.setFont( new Font( “SansSerif”, Font.PLAIN, 14 ) ); g.drawString( "SansSerif 14 point plain.", 20, 70 ); // configura fonte como Serifa (Times), 18 pt negrito/itálico e desenha uma string g.setColor( Color.RED ); g.setFont( new Font( “Serif”, Font.BOLD + Font.ITALIC, 18 ) ); g.drawString(g.getFont().getName() + “ “ + g.getFont().getSize() + " point bold italic.", 20, 90 ); } // fim do método paintComponent } // fim da classe FontJPanel

Figura 15.11  |  O método Graphics setFont altera a fonte de desenho. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// Figura 15.12: Fonts.java // Utilizando fontes. import javax.swing.JFrame; public class Fonts { // executa o aplicativo public static void main( String[] args ) { // cria frame para FontJPanel JFrame frame = new JFrame( "Using fonts" ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); FontJPanel fontJPanel = new FontJPanel(); // cria FontJPanel frame.add( fontJPanel ); // adiciona fontJPanel ao frame frame.setSize( 420, 150 ); // configura o tamanho do frame frame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe Fonts

Figura 15.12  |  Criando um JFrame para exibir fontes.

Métrica de fontes Às vezes é necessário obter informações sobre a fonte atual do desenho, como seu nome, estilo e tamanho. Vários métodos Font utilizados para obter informações sobre fontes estão resumidos na Figura 15.10. O método getStyle retorna um valor inteiro que representa o estilo atual. O valor de inteiro retornado é Font.PLAIN, Font.ITALIC, Font.BOLD ou a combinação de Font.ITALIC e Font.BOLD. O método getFamily retorna o nome da família de fontes a qual a fonte atual pertence. O nome da família de fontes é específico à plataforma. Os métodos Font também estão disponíveis para testar o estilo da fonte atual, e estes também estão resumidos na Figura 15.10. Os métodos isPlain, isBold e isItalic retornam true se o estilo da fonte atual for simples, negrito ou itálico, respectivamente. A Figura 15.13 ilustra alguns casos de métrica de fonte comum, que fornece informações precisas sobre uma fonte, como altura, descendente (quanto um caractere desce abaixo da linha de base), ascendente (quanto um caractere se eleva acima da linha de base) e entrelinha (a diferença entre a ascendente de uma linha de texto e a descendente da linha de texto embaixo dela — isto é, o espaçamento entre linhas).

494

Capítulo 15  Imagens gráficas e Java 2D™ entrelinha altura

ascendente linha de base descendente

Figura 15.13  |  Medidas de fonte.

A classe FontMetrics declara vários métodos para obter a métrica de fonte. Esses métodos e o método Graphics getFontMetrics estão resumidos na Figura 15.14. O aplicativo das figuras 15.15 e 15.16 utiliza os métodos da Figura 15.14 para obter informações sobre a métrica da fonte para duas fontes. Método

Descrição

Métodos FontMetrics public int getAscent() public int getDescent() public int getLeading() public int getHeight()

Retorna a ascendente de uma fonte em pontos. Retorna a descendente de uma fonte em pontos. Retorna a entrelinha de uma fonte em pontos. Retorna a altura de uma fonte em pontos.

Métodos Graphics para obter FontMetrics de uma Font public FontMetrics getFontMetrics() public FontMetrics getFontMetrics( Font f )

Retorna o objeto FontMetrics para o argumento Font especificado. Retorna o objeto FontMetrics para o argumento Font especificado.

Figura 15.14  |  Os métodos FontMetrics e Graphics para obter medidas de fonte. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

// Figura 15.15: MetricsJPanel.java // Métodos FontMetrics e Graphics úteis para obter a métrica de fontes. import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import javax.swing.JPanel; public class MetricsJPanel extends JPanel { // exibe a métrica de fontes. public void paintComponent( Graphics g ) { super.paintComponent( g ); // chama o paintComponent da superclasse g.setFont( new Font( "SansSerif", Font.BOLD, 12 ) ); FontMetrics metrics = g.getFontMetrics(); g.drawString( "Current font: " + g.getFont(), 10, 30 ); g.drawString( "Ascent: " + metrics.getAscent(), 10, 45 ); g.drawString( "Descent: " + metrics.getDescent(), 10, 60 ); g.drawString( "Height: " + metrics.getHeight(), 10, 75 ); g.drawString( "Leading: " + metrics.getLeading(), 10, 90 ); Font font = new Font( "Serif", Font.ITALIC, 14 ); metrics = g.getFontMetrics( font ); g.setFont( font ); g.drawString( "Current font: " + font, 10, 120 ); g.drawString( "Ascent: " + metrics.getAscent(), 10, 135 ); g.drawString( "Descent: " + metrics.getDescent(), 10, 150 ); g.drawString( "Height: " + metrics.getHeight(), 10, 165 ); g.drawString( "Leading: " + metrics.getLeading(), 10, 180 ); } // fim do método paintComponent } // fim da classe MetricsJPanel

Figura 15.15  |  Medidas de fonte.

15.5 Desenhando linhas, retângulos e ovais

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

495

// Figura 15.16: Metrics.java // Exibindo a métrica de fonte. import javax.swing.JFrame; public class Metrics { // executa o aplicativo public static void main( String[] args ) { // criar frame para MetricsJPanel JFrame frame = new JFrame( "Demonstrating FontMetrics" ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); MetricsJPanel metricsJPanel = new MetricsJPanel(); frame.add( metricsJPanel ); // adiciona metricsJPanel ao frame frame.setSize( 510, 240 ); // configura o tamanho do frame frame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe Metrics

Figura 15.16 | Criando JFrame para exibir informações da métrica de fonte.

A linha 15 da Figura 15.15 cria e configura a fonte atual do desenho como uma fonte SansSerif, negrito, de 12 pontos. A linha 16 utiliza o método Graphics getFontMetrics para obter o objeto FontMetrics para a fonte atual. A linha 17 gera a saída da representação String da Font retornada por g.getFont(). As linhas 18–21 utilizam os métodos FontMetric para obter a ascendente, a descendente, a altura e a entrelinha da fonte. A linha 23 cria uma nova fonte Serif, de 14 pontos em itálico. A linha 24 utiliza uma segunda versão do método Graphics get­ FontMetrics, que aceita um argumento Font e retorna um objeto FontMetrics correspondente. As linhas 27–30 obtêm a ascendente, descendente, altura e entrelinha da fonte. Observe que as medidas de fonte são ligeiramente diferentes para as duas fontes.

15.5 Desenhando linhas, retângulos e ovais Esta seção apresenta os métodos Graphics para desenhar linhas, retângulos e ovais. Os métodos e seus parâmetros estão resumidos na Figura 15.17. Para cada método de desenho que requer um parâmetro width e height, o width e height devem ser valores não negativos. Caso contrário, a forma não será exibida. Método

Descrição

public void drawLine( int x1, int y1, int x2, int y2 )

Desenha uma linha entre o ponto (x1, y1) e o ponto (x2, y2). public void drawRect( int x, int y, int width, int height )

Desenha um retângulo com a largura e a altura especificadas. O canto superior esquerdo do retângulo está localizado em (x, y). Somente o contorno do retângulo é desenhado utilizando a cor do objeto de Graphics — o corpo do retângulo não é preenchido com essa cor. public void fillRect( int x, int y, int width, int height )

Desenha um retângulo preenchido na cor atual com a largura e a altura especificadas. O canto superior esquerdo do retângulo está localizado em (x, y).

496

Capítulo 15  Imagens gráficas e Java 2D™

Método

Descrição

public void clearRect( int x, int y, int width, int height )

Desenha um retângulo preenchido com a largura e a altura especificadas na cor de fundo atual. O canto superior esquerdo do retângulo está localizado em (x, y). Esse método é útil se você quiser remover uma parte de uma imagem. public void drawRoundRect( int x, int y, int width, int height, int arcWidth, int arcHeight )

Desenha um retângulo com cantos arredondados na cor atual com a largura e a altura especificadas. A arcWidth e a arcHeight determinam o arredondamento dos cantos (veja Figura 15.20). Somente o contorno da forma é desenhado. public void fillRoundRect( int x, int y, int width, int height, int arcWidth, int arcHeight )

Desenha um retângulo preenchido na cor atual com cantos arredondados com a largura e a altura especificadas. A arcWidth e a arcHeight determinam o arredondamento dos cantos (veja Figura 15.20). public void draw3DRect( int x, int y, int width, int height, boolean b )

Desenha um retângulo tridimensional na cor atual com a largura e a altura especificadas. O canto superior esquerdo do retângulo está localizado em (x, y). O retângulo parece em alto relevo quando b é verdadeiro e em baixo relevo quando b é falso. Somente o contorno da forma é desenhado. public void fill3DRect( int x, int y, int width, int height, boolean b )

Desenha um retângulo tridimensional preenchido na cor atual com a largura e a altura especificadas. O canto superior esquerdo do retângulo está localizado em (x, y). O retângulo parece em alto relevo quando b é verdadeiro e em baixo relevo quando b é falso. public void drawOval( int x, int y, int width, int height )

Desenha uma oval na cor atual com a largura e a altura especificadas. O canto superior esquerdo do retângulo delimitado está localizado em (x, y). A oval toca todos os quatro lados do retângulo associado no centro de cada lado (veja Figura 15.21). Somente o contorno da forma é desenhado. public void fillOval( int x, int y, int width, int height )

Desenha uma oval preenchida na cor atual com a largura e a altura especificadas. O canto superior esquerdo do retângulo delimitado está localizado em (x, y). A oval toca o centro dos quatro lados do retângulo delimitador (ver Figura 15.21). Figura 15.17  |  Métodos Graphics que desenham linhas, retângulos e ovais.

O aplicativo das figuras 15.18 e 15.19 demonstra o desenho de diversas linhas, retângulos, retângulos tridimensionais, retângulos arredondados e ovais. Na Figura 15.18, a linha 17 desenha uma linha vermelha, a linha 20 desenha um retângulo azul vazio e a linha 21 desenha um retângulo preenchido com azul. Os métodos fillRoundRect (linha 24) e drawRoundRect (linha 25) desenham retângulos com cantos arredondados. Seus primeiros dois argumentos especificam as coordenadas do canto superior esquerdo do retângulo delimitador — a área em que o retângulo arredondado será desenhado. Observe que as coordenadas do canto superior esquerdo não são a borda do retângulo arredondado, mas as coordenadas em que a borda estaria se o retângulo tivesse cantos quadrados. O terceiro e quarto argumentos especificam a largura e a altura do retângulo. Os dois últimos argumentos determinam os diâmetros horizontais e verticais do arco (isto é, a largura do arco e a altura do arco) utilizados para representar os cantos. 1 2 3 4 5 6 7 8 9 10

// Figura 15.18: LinesRectsOvalsJPanel.java // Desenhando linhas, retângulos e ovais. import java.awt.Color; import java.awt.Graphics; import javax.swing.JPanel; public class LinesRectsOvalsJPanel extends JPanel { // exibe várias linhas, retângulos e elipses public void paintComponent( Graphics g )

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

15.5  Desenhando linhas, retângulos e ovais { super.paintComponent( g ); // chama o método paint da superclasse this.setBackground( Color.WHITE ); g.setColor( Color.RED ); g.drawLine( 5, 30, 380, 30 ); g.setColor( Color.BLUE ); g.drawRect( 5, 40, 90, 55 ); g.fillRect( 100, 40, 90, 55 ); g.setColor( Color.CYAN ); g.fillRoundRect( 195, 40, 90, 55, 50, 50 ); g.drawRoundRect( 290, 40, 90, 55, 20, 20 ); g.setColor( Color.GREEN ); g.draw3DRect( 5, 100, 90, 55, true ); g.fill3DRect( 100, 100, 90, 55, false ); g.setColor( Color.MAGENTA ); g.drawOval( 195, 100, 90, 55 ); g.fillOval( 290, 100, 90, 55 ); } // fim do método paintComponent } // fim da classe LinesRectsOvalsJPanel

Figura 15.18  |  Desenhando linhas, retângulos e ovais. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

// Figura 15.19: LinesRectsOvals.java // Desenhando linhas, retângulos e ovais. import java.awt.Color; import javax.swing.JFrame; public class LinesRectsOvals { // executa o aplicativo public static void main( String[] args ) { // criar frame para LinesRectsOvalsJPanel JFrame frame = new JFrame( "Drawing lines, rectangles and ovals" ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); LinesRectsOvalsJPanel linesRectsOvalsJPanel = new LinesRectsOvalsJPanel(); linesRectsOvalsJPanel.setBackground( Color.WHITE ); frame.add( linesRectsOvalsJPanel ); // adiciona painel ao frame frame.setSize( 400, 210 ); // configura o tamanho do frame frame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe LinesRectsOvals

drawLine

fillRoundRect

drawRect

drawRoundRect

fillRect

drawOval

draw3DRect

fillOval

fill3DRect

Figura 15.19  |  Criando JFrame para exibir linhas, retângulos e ovais.

497

498

Capítulo 15

Imagens gráficas e Java 2D™

A Figura 15.20 rotula a largura e a altura de um arco e a largura e a altura de um retângulo arredondado. Utilizar o mesmo valor para a largura e a altura do arco produz o quarto de um círculo em cada um dos cantos. Se a largura do arco, altura do arco, largura e altura tiverem os mesmos valores, o resultado será um círculo. Se os valores para width (largura) e height (altura) forem os mesmos e os valores de arcWidth e arcHeight forem 0, o resultado será um quadrado. (x, y)

altura do arco largura do arco altura

largura

Figura 15.20 | A largura do arco e altura do arco para retângulos arredondados.

Os métodos draw3DRect (linha 28) e fill3DRect (linha 29) aceitam os mesmos argumentos. Os dois primeiros especificam o canto superior esquerdo do retângulo. Os próximos dois argumentos especificam a largura e altura do retângulo, respectivamente. O último argumento determina se o retângulo está em alto relevo (true) ou baixo relevo (false). O efeito tridimensional de draw3DRect aparece como duas bordas do retângulo na cor original e duas bordas em uma cor ligeiramente mais escura. O efeito tridimensional de fill3DRect aparece como duas bordas do retângulo na cor de desenho original e o preenche e as outras duas bordas em uma cor ligeiramente mais escura. Retângulos em alto relevo têm a cor de desenho original nas bordas superior e esquerda. Retângulos em baixo relevo têm a cor de desenho original nas bordas inferior e direita. O efeito tridimensional é difícil de ver em algumas cores. Os métodos drawOval e fillOval (Figura 15.18, linhas 32–33) recebem os mesmos quatro argumentos. Os primeiros dois argumentos especificam a coordenada do canto superior esquerdo do retângulo delimitador que contém a oval. Os dois últimos argumentos especificam a largura e altura do retângulo delimitador, respectivamente. A Figura 15.21 mostra uma oval delimitada por um retângulo. Observe que a oval toca o centro de todos os quatro lados do retângulo delimitador. (O retângulo delimitador não é exibido na tela.) (x,y)

altura

largura

Figura 15.21 | Oval unida por um retângulo.

15.6 Desenhando arcos Um arco é desenhado como uma parte de uma oval. Uma oval delimitada por um retângulo. Os arcos varrem (isto é, se movem ao longo de uma curva) a partir de um ângulo inicial até o número de graus especificado pelos seus ângulos de arco. Os ângulos de arco são medidos em graus. Os arcos varrem a partir de um ângulo inicial o número de graus especificado por seu ângulo de arco. O ângulo inicial indica em graus onde o arco começa. O conjunto esquerdo de eixos mostra uma varredura de arco de zero grau a aproximadamente 110 graus. Os arcos que varrem no sentido anti-horário são medidos em graus positivos. O conjunto de eixos à direita mostra um arco que varre de zero grau a aproximadamente –110 graus. Os arcos que varrem em sentido horário são medidos em graus negativos. Observe as caixas tracejadas em torno dos arcos na Figura 15.22. Ao desenhar um arco, especificamos um retângulo delimitador para uma oval. O arco varrerá parte da oval. Os métodos Graphics drawArc e fillArc para desenhar arcos são resumidos na Figura 15.23.



15.6  Desenhando arcos

Ângulos positivos 90º

180º

499

Ângulos negativos 90º



270º

180º



270º

Figura 15.22  |  Ângulos de arco positivo e negativo.

Método

Descrição

public void drawArc( int x, int y, int width, int height, int startAngle, int arcAngle )

Desenha um arco em relação ao canto superior esquerdo do retângulo delimitador e coordenadas x e y com a largura e altura especificadas. Métodos Graphics para desenhar arcos. public void fillArc( int x, int y, int width, int height, int startAngle, int arcAngle )

Desenha um arco preenchido (isto é, um setor) em relação às coordenadas x e y do canto superior esquerdo do retângulo delimitador com a largura e altura especificadas. Métodos Graphics para desenhar arcos. Figura 15.23  |  Métodos Graphics para desenhar arcos.

As figuras 15.24 e 12.25 demonstram os métodos arc da Figura 15.23. O aplicativo desenha seis arcos (três não preenchidos e três preenchidos). Para ilustrar o retângulo delimitador que ajuda a determinar onde o arco aparece, os primeiros três arcos são exibidos dentro de um retângulo amarelo que tem os mesmos argumentos x, y, largura e altura que os arcos. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

// Figura 15.24: ArcsJPanel.java // Desenhando arcos. import java.awt.Color; import java.awt.Graphics; import javax.swing.JPanel; public class ArcsJPanel extends JPanel { // desenha retângulos e arcos public void paintComponent( Graphics g ) { super.paintComponent( g ); // chama o paintComponent da superclasse // inicia em 0 e varre 360 graus g.setColor( Color.RED ); g.drawRect( 15, 35, 80, 80 ); g.setColor( Color.BLACK ); g.drawArc( 15, 35, 80, 80, 0, 360 ); // inicia em 0 e varre 110 graus g.setColor( Color.RED ); g.drawRect( 100, 35, 80, 80 ); g.setColor( Color.BLACK ); g.drawArc( 100, 35, 80, 80, 0, 110 );

500 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

Capítulo 15

Imagens gráficas e Java 2D™

// inicia em 0 e varre ­270 graus g.setColor( Color.RED ); g.drawRect( 185, 35, 80, 80 ); g.setColor( Color.BLACK ); g.drawArc( 185, 35, 80, 80, 0, ­270 ); // inicia em 0 e varre 360 graus g.fillArc( 15, 120, 80, 40, 0, 360 ); // inicia em 270 e varre ­90 graus g.fillArc( 100, 120, 80, 40, 270, ­90 ); // inicia em 0 e varre ­270 graus g.fillArc( 185, 120, 80, 40, 0, ­270 ); } // fim do método paintComponent } // fim da classe ArcsJPanel

Figura 15.24 | Arcos exibidos com drawArc e fillArc.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// Figura 15.25: DrawArcs.java // Desenhando arcos. import javax.swing.JFrame; public class DrawArcs { // executa o aplicativo public static void main( String[] args ) { // cria frame para ArcsJPanel JFrame frame = new JFrame( "Drawing Arcs" ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); ArcsJPanel arcsJPanel = new ArcsJPanel(); // criar ArcsJPanel frame.add( arcsJPanel ); // adiciona arcsJPanel ao frame frame.setSize( 300, 210 ); // configura o tamanho do frame frame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe DrawArcs

Figura 15.25 | Criando JFrame para exibir arcos.

15.7 Desenhando polígonos e polilinhas Polígonos são formas de múltiplos lados compostas de segmentos de linhas retas. Polilinhas são sequências de pontos conectados. A Figura 15.26 discute métodos para desenhar polígonos e polilinhas. Observe que alguns métodos requerem um objeto Polygon (pacote java.awt). Os construtores da classe Polygon também são descritos na Figura 15.26. O aplicativo das figuras 15.27 e 15.28 desenha polígonos e polilinhas.



15.7  Desenhando polígonos e polilinhas

Método

501

Descrição

Métodos Graphics para desenhar polígonos public void drawPolygon( int[] xPoints, int[] yPoints, int points )

Desenha um polígono. A coordenada x de cada ponto é especificada no array xPoints e a coordenada y de cada ponto é especificada no array yPoints. O último argumento especifica o número de points. Esse método desenha um polígono. Se o último ponto for diferente do primeiro, o polígono é fechado por uma linha que conecta o último ponto ao primeiro. public void drawPolyline( int[] xPoints, int[] yPoints, int points )

Desenha uma sequência de linhas conectadas. A coordenada x de cada ponto é especificada no array xPoints e a coordenada y de cada ponto é especificada no array yPoints. O último argumento especifica o número de points. Se o último ponto for diferente do primeiro, a polilinha não é fechada. public void drawPolygon( Polygon p )

Desenha o polígono especificado. public void fillPolygon( int[] xPoints, int[] yPoints, int points )

Desenha um polígono preenchido. A coordenada x de cada ponto é especificada no array xPoints e a coordenada y de cada ponto é especificada no array yPoints. O último argumento especifica o número de points. Esse método desenha um polígono. Se o último ponto for diferente do primeiro, o polígono é fechado por uma linha que conecta o último ponto ao primeiro. public void fillPolygon( Polygon p )

Desenha o polígono preenchido especificado. O polígono é fechado. Construtores e métodos

Polygon

public Polygon()

Constrói um novo objeto de polígono. O polígono não contém nenhum ponto. public void fillPolygon( int[] xPoints, int[] yPoints, int points )

Constrói um novo objeto de polígono. O polígono tem numberOfPoints lados, com cada ponto consistindo em uma coordenada x de xValues e uma coordenada y de yValues. public void addPoint( int x, int y )

Adiciona pares das coordenadas x e y ao Polygon. Figura 15.26  |  Métodos Graphics para polígonos e métodos Polygon da classe.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

// Figura 15.27: PolygonsJPanel.java // Desenhando polígonos. import java.awt.Graphics; import java.awt.Polygon; import javax.swing.JPanel; public class PolygonsJPanel extends JPanel { // desenha polígonos e polilinhas public void paintComponent( Graphics g ) { super.paintComponent( g ); // chama o paintComponent da superclasse // desenha o polígono com objeto Polygon int[] xValues = { 20, 40, 50, 30, 20, 15 }; int[] yValues = { 50, 50, 60, 80, 80, 60 }; Polygon polygon1 = new Polygon( xValues, yValues, 6 ); g.drawPolygon( polygon1 );

502 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

Capítulo 15  Imagens gráficas e Java 2D™

// desenha polilinhas com dois arrays int[] xValues2 = { 70, 90, 100, 80, 70, 65, 60 }; int[] yValues2 = { 100, 100, 110, 110, 130, 110, 90 }; g.drawPolyline( xValues2, yValues2, 7 ); // preenche o polígono com dois arrays int[] xValues3 = { 120, 140, 150, 190 }; int[] yValues3 = { 40, 70, 80, 60 }; g.fillPolygon( xValues3, yValues3, 4 ); // desenha o polígono preenchido com objeto Polygon Polygon polygon2 = new Polygon(); polygon2.addPoint( 165, 135 ); polygon2.addPoint( 175, 150 ); polygon2.addPoint( 270, 200 ); polygon2.addPoint( 200, 220 ); polygon2.addPoint( 130, 180 ); g.fillPolygon( polygon2 ); } // fim do método paintComponent } // fim da classe PolygonsJPanel

Figura 15.27  |  Polígonos exibidos com drawPolygon e fillPolygon.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// Figura 15.28: DrawPolygons.java // Desenhando polígonos. import javax.swing.JFrame; public class DrawPolygons { // executa o aplicativo public static void main( String[] args ) { // cria frame para PolygonsJPanel JFrame frame = new JFrame( "Drawing Polygons" ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); PolygonsJPanel polygonsJPanel = new PolygonsJPanel(); frame.add( polygonsJPanel ); // adiciona polygonsJPanel ao frame frame.setSize( 280, 270 ); // configura o tamanho do frame frame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe DrawPolygons

Resultado da linha 18 Resultado da linha 23

Resultado da linha 28

Resultado da linha 37

Figura 15.28  |  Criando JFrame para exibir polígonos.

As linhas 15–16 da Figura 15.27 criam dois arrays int e os utilizam para especificar os pontos para Polygon polygon1. A chamada do construtor Polygon na linha 17 recebe o array xValues, que contém a coordenada x de cada ponto, o array yValues, que contém a coordenada y de cada ponto e 6 (o número de pontos no polígono). A linha 18 exibe polygon1 passando-o como um argumento para o método Graphics drawPolygon.

15.8 Java 2D API

503

As linhas 21–22 criam dois arrays int e os utilizam para especificar os pontos para uma série de linhas conectadas. O array xValues2 contém a coordenada x de cada ponto e o array yValues2, a coordenada y de cada ponto. A linha 23 utiliza o método Graphics drawPolyline para exibir uma série de linhas conectadas especificadas com os argumentos xValues2, yValues2 e 7 (o número de pontos). As linhas 26–27 criam dois arrays int e os utilizam para especificar os pontos de um polígono. O array xValues3 contém a coordenada x de cada ponto e o array yValues3, a coordenada y de cada ponto. A linha 28 exibe um polígono passando para o método Graphics fillPolygon os dois arrays (xValues3 e yValues3) e o número de pontos a desenhar (4).

Erro comum de programação 15.1 Uma ArrayIndexOutOfBoundsException é lançada se o número de pontos especificado no terceiro argumento para o método drawPolygon ou o método fillPolygon for maior que o número de elementos nos arrays de coordenadas que especificam o polígono a exibir.

A linha 31 cria Polygon polygon2 sem pontos. As linhas 32–36 utilizam o método Polygon addPoint para adicionar pares de coordenadas x e y a Polygon. A linha 37 exibe Polygon polygon2 passando-o para o método Graphics fillPolygon.

15.8 Java 2D API A Java 2D API fornece capacidades gráficas bidimensionais avançadas para programadores que requerem manipulações gráficas complexas e detalhadas. A API inclui recursos para processar arte a traço, texto e imagens nos pacotes java.awt, java.awt.image, java. awt.color, java.awt.font, java.awt.geom, java.awt.print e java.awt.image.renderable. As capacidades da API são muito amplas para abranger neste texto. Para uma visão geral dessas capacidades, consulte a demo Java 2D (discutida no Capítulo 23, “Applets e Java Web Start”) ou visite java.sun.com/javase/6/docs/technotes/guides/2d. Nesta seção, apresentamos uma visão geral das várias capacidades de Java 2D. Desenhar com a Java 2D API é realizado com uma referência Graphics2D (pacote java.awt). A Graphics2D é uma subclasse abstrata da classe Graphics, portanto ela contém todas as capacidades gráficas demonstradas anteriormente neste capítulo. De fato, o objeto real utilizado para desenhar em cada método paintComponent é uma instância de uma subclasse de Graphics2D que é passada para o método paintComponent e acessada via superclasse Graphics. Para acessar as capacidades de Graphics2D, devemos fazer a coerção da referência Graphics (g) passada a paintComponent para uma referência Graphics2D com uma instrução como Graphics2D g2d = ( Graphics2D ) g;

Os dois exemplos a seguir utilizam essa técnica.

Linhas, retângulos, retângulos arredondados, arcos e elipses Esse exemplo demonstra as várias formas de Java 2D no pacote java.awt.geom, incluindo Line2D.Double, Rectangle2D.Double, RoundRectangle2D.Double, Arc2D.Double e Ellipse2D.Double. Observe a sintaxe do nome de cada classe. Cada classe representa uma forma com dimensões especificadas como valores double. Há uma versão separada de cada uma representada com valores float (por exemplo, Ellipse2D.Float). Em cada caso, Double é uma classe public static aninhada da classe especificada à esquerda do ponto (por exemplo, Ellipse2D). Para utilizar a classe static aninhada, simplesmente qualificamos seu nome com o nome de classe externa. Nas figuras 15.29 e 15.30, desenhamos formas de Java 2D e modificamos suas características de desenho, como mudar a espessura de linha, preencher formas com padrões e desenhar linhas tracejadas. Essas são apenas algumas das muitas capacidades fornecidas pelo Java 2D. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// Figura 15.29: ShapesJPanel.java // Demonstrando algumas formas 2D Java. import java.awt.Color; import java.awt.Graphics; import java.awt.BasicStroke; import java.awt.GradientPaint; import java.awt.TexturePaint; import java.awt.Rectangle; import java.awt.Graphics2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.awt.geom.Arc2D; import java.awt.geom.Line2D; import java.awt.image.BufferedImage; import javax.swing.JPanel; public class ShapesJPanel extends JPanel {

504 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75

Capítulo 15  Imagens gráficas e Java 2D™ // desenha formas com Java 2D API public void paintComponent( Graphics g ) { super.paintComponent( g ); // chama o paintComponent da superclasse Graphics2D g2d = ( Graphics2D ) g; // faz uma coerção em g para Graphics2D // desenha elipse 2D preenchida com um gradiente azul-amarelo g2d.setPaint( new GradientPaint( 5, 30, Color.BLUE, 35, 100, Color.YELLOW, true ) ); g2d.fill( new Ellipse2D.Double( 5, 30, 65, 100 ) ); // desenha retângulo 2D em vermelho g2d.setPaint( Color.RED ); g2d.setStroke( new BasicStroke( 10.0f ) ); g2d.draw( new Rectangle2D.Double( 80, 30, 65, 100 ) ); // desenha retâng. arred. 2D com um fundo armazenado em buffer BufferedImage buffImage = new BufferedImage( 10, 10, BufferedImage.TYPE_INT_RGB ); // obtém Graphics2D de buffImage e desenha nela Graphics2D gg = buffImage.createGraphics(); gg.setColor( Color.YELLOW ); // desenha em amarelo gg.fillRect( 0, 0, 10, 10 ); // desenha um retângulo preenchido gg.setColor( Color.BLACK ); // desenha em preto gg.drawRect( 1, 1, 6, 6 ); // desenha um retângulo gg.setColor( Color.BLUE ); // desenha em azul gg.fillRect( 1, 1, 3, 3 ); // desenha um retângulo preenchido gg.setColor( Color.RED ); // desenha em vermelho gg.fillRect( 4, 4, 3, 3 ); // desenha um retângulo preenchido // pinta buffImage sobre o JFrame g2d.setPaint( new TexturePaint( buffImage, new Rectangle( 10, 10 ) ) ); g2d.fill( new RoundRectangle2D.Double( 155, 30, 75, 100, 50, 50 ) ); // Desenha arco 2D em forma de torta em branco g2d.setPaint( Color.WHITE ); g2d.setStroke( new BasicStroke( 6.0f ) ); g2d.draw( new Arc2D.Double( 240, 30, 75, 100, 0, 270, Arc2D.PIE ) ); // Desenha linhas 2D em verde e amarelo g2d.setPaint( Color.GREEN ); g2d.draw( new Line2D.Double( 395, 30, 320, 150 ) ); // desenha uma linha em 2D utilizando traço float[] dashes = { 10 }; // especifica padrão de traço g2d.setPaint( Color.YELLOW ); g2d.setStroke( new BasicStroke( 4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10, dashes, 0 ) ); g2d.draw( new Line2D.Double( 320, 30, 395, 150 ) ); } // fim do método paintComponent } // fim da classe ShapesJPanel

Figura 15.29  |  Formas 2D Java.



1 2 3 4 5 6 7

// Figura 15.30: Shapes.java // Demonstrando algumas formas 2D Java. import javax.swing.JFrame; public class Shapes { // executa o aplicativo

8 9 10 11 12 13 14 15 16 17 18 19 20 21

15.8  Java 2D API

505

public static void main( String[] args ) { // cria frame para ShapesJPanel JFrame frame = new JFrame( "Drawing 2D shapes" ); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); // cria ShapesJPanel ShapesJPanel shapesJPanel = new ShapesJPanel(); frame.add( shapesJPanel ); // adiciona shapesJPanel ao frame frame.setSize( 425, 200 ); // configura o tamanho do frame frame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe Shapes

Figura 15.30  |  Criando JFrame para exibir formas.

A linha 25 da Figura 15.29 faz coerção da referência Graphics recebida pelo paintComponent para uma referência Graphics2D e a atribui a g2d a fim de permitir acesso aos recursos do Java 2D.

Ovais e preenchimentos em degradê e objetos Paint A primeira forma que desenhamos é uma oval preenchida com cores que mudam gradualmente. As linhas 28–29 invocam o método Graphics2D setPaint para configurar o objeto Paint que determina as cores exibidas pela forma. Um objeto Paint implementa a interface java.awt.Paint. Ele pode ser algo tão simples quanto um dos objetos Color pré-declarados introduzidos na Seção 15.3 (a classe Color implementa Paint) ou pode ser uma instância das classes GradientPaint, SystemColor, TexturePaint, LinearGradientPaint ou RadialGradientPaint da Java 2D API. Nesse caso, utilizamos um objeto GradientPaint. A classe GradientPaint ajuda a desenhar uma forma em cores que mudam gradualmente — o que é chamado gradiente (ou degradê). O construtor GradientPaint utilizado aqui requer sete argumentos. Os dois primeiros especificam a coordenada inicial do gradiente (ou degradê). O terceiro especifica a Color inicial do gradiente. O quarto e o quinto argumentos especificam a coordenada final para o gradiente. O sexto especifica a Color de término do gradiente. O último argumento especifica se o gradiente é cíclico (true) ou acíclico (false). Os dois conjuntos de coordenadas determinam a direção do gradiente. Como a segunda coordenada (35, 100) está abaixo e à direita da primeira coordenada (5, 30), o gradiente segue para baixo e para a direita em um ângulo. Como esse gradiente é cíclico (true), a cor inicia com azul, gradualmente torna-se amarelo, então gradualmente retorna para azul. Se o gradiente fosse acíclico, a transição de cor seria da primeira cor especificada (por exemplo, azul) para a segunda cor (por exemplo, amarelo). A linha 30 utiliza o método Graphics2D fill para desenhar um objeto Shape preenchido — um objeto que implementa a interface Shape (pacote java.awt). Nesse caso, exibimos um objeto Ellipse2D.Double. O construtor Ellipse2D.Double recebe quatro argumentos que especificam o retângulo delimitador para a elipse exibir. Retângulos, Strokes Em seguida, desenhamos um retângulo vermelho com uma borda grossa. A linha 33 invoca setPaint para configurar o objeto Paint como Color.RED. A linha 34 utiliza o método Graphics2D setStroke para configurar as características da borda do retângulo (ou as linhas para qualquer outra forma). O método setStroke requer como seu argumento um objeto que implementa a interface Stroke (pacote java.awt). Nesse caso, utilizamos uma instância da classe BasicStroke. A classe BasicStroke fornece diversos construtores para especificar a largura da linha, como as linhas terminam (chamadas terminações de linha), como as linhas se juntam (chamadas junções de linha) e os atributos de traço da linha (se for uma linha tracejada). O construtor aqui especifica que a linha deve ter 10 pixels de largura. A linha 35 utiliza o método Graphics2D draw para desenhar um objeto Shape — nesse caso, um Rectangle2D.Double. O construtor Rectangle2D.Double recebe argumentos que especificam a coordenada x superior esquerda, a coordenada y superior esquerda, a largura e a altura.

506

Capítulo 15  Imagens gráficas e Java 2D™

Retângulos arredondados, objetos BufferedImage e TexturePaint Em seguida, desenhamos um retângulo arredondado preenchido com um padrão criado em um objeto BufferedImage (pacote java.awt.image). As linhas 38–39 criam o objeto BufferedImage. A classe BufferedImage pode ser utilizada para criar imagens coloridas e na escala de cinza. Essa BufferedImage particular tem 10 pixels de largura e 10 pixels de altura (como especificado pelos dois primeiros argumentos do construtor). O terceiro argumento BufferedImage.TYPE_INT_RGB indica que a imagem é armazenada em cores utilizando o esquema de cores RGB. Para criar o padrão de preenchimento do retângulo arredondado, devemos primeiro desenhar no BufferedImage. A linha 42 cria um objeto Graphics2D (chamando o método createGraphics BufferedImage) que pode ser utilizado para desenhar no BufferedImage. As linhas 43–50 utilizam os métodos setColor, fillRect e drawRect (discutidos anteriormente neste capítulo) para criar o padrão. As linhas 53–54 configuram o objeto Paint como um novo objeto TexturePaint (pacote java.awt). Um objeto TexturePaint utiliza a imagem armazenada na sua BufferedImage associada (o primeiro argumento de construtor) como a textura de preenchimento para uma forma preenchida. O segundo argumento especifica a área Rectangle da BufferedImage que será replicada à textura. Nesse caso, Rectangle tem o mesmo tamanho que a BufferedImage. Mas uma parte menor da BufferedImage pode ser utilizada. As linhas 55–56 utilizam o método Graphics2D fill para desenhar um objeto Shape preenchido — nesse caso, um RoundRectan­ gle2D.Double. O construtor da classe RoundRectangle2D.Double recebe seis argumentos que especificam as dimensões do retângulo e a largura e a altura de arco utilizadas para determinar o arredondamento dos cantos. Arcos Em seguida desenhamos um arco em forma de torta com uma linha branca espessa. A linha 59 configura o objeto Paint como Color.WHITE. A linha 60 configura o objeto Stroke de um novo BasicStroke como uma linha de 6 pixels de largura. As linhas 61–62 usam o método Graphics2D draw para desenhar um objeto Shape — nesse caso, um Arc2D.Double. Os primeiros quatro argumentos do construtor Arc2D.Double especificam a coordenada x superior esquerda, a coordenada y superior esquerda, a largura e a altura do retângulo delimitador para o arco. O quinto argumento especifica o ângulo inicial. O sexto argumento especifica o ângulo do arco. O último argumento especifica como o arco é fechado. A constante Arc2D.PIE indica que o arco é fechado desenhando duas linhas — uma linha a partir do ponto inicial do arco para o centro do retângulo delimitador e uma linha a partir do centro do retângulo delimitador para o ponto terminal. A classe Arc2D fornece duas outras constantes static para especificar como o arco é fechado. A constante Arc2D.CHORD desenha uma linha do ponto inicial ao ponto final. A constante Arc2D.OPEN especifica que o arco não deve ser fechado. Linhas Por fim, desenhamos duas linhas utilizando objetos Line2D — uma sólida e uma tracejada. A linha 65 configura o objeto Paint como Color.GREEN. A linha 66 utiliza o método Graphics2D draw para desenhar um objeto Shape — nesse caso, uma instância da classe Line2D.Double. Os argumentos do construtor Line2D.Double especificam as coordenadas iniciais e coordenadas finais da linha. A linha 69 declara o array float de um elemento contendo o valor 10. Esse array descreve os traços na linha tracejada. Nesse caso, cada traço terá 10 pixels de comprimento. Para criar traços de comprimentos diferentes em um padrão, simplesmente forneça o comprimento de cada traço como um elemento no array. A linha 70 configura o objeto Paint como Color.YELLOW. As linhas 71–72 configuram o objeto Stroke como um novo BasicStroke. A linha terá 4 pixels de largura e terá terminações arredondadas (BasicStroke.CAP_ROUND). Se as linhas se juntam (como em um vértice de um retângulo), as linhas de junção serão arredondadas (BasicStroke.JOIN_ROUND). O argumento dashes especifica o comprimento do traço da linha. O último argumento indica o índice inicial no array dashes para o primeiro traço no padrão. A linha 73 então desenha uma linha com o atual Stroke. Criando suas próprias formas com caminhos gerais Em seguida, apresentaremos um caminho geral — uma forma construída a partir de linhas retas e curvas complexas. Um caminho geral é representado com um objeto da classe GeneralPath (pacote java.awt.geom). O aplicativo das figuras 15.31 e 15.32 demonstra como desenhar um caminho geral na forma de uma estrela de cinco pontas. 1 2 3 4 5 6 7 8 9 10 11

// Figura 15.31: Shapes2JPanel.java // Demonstrando um caminho geral. import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.GeneralPath; import java.util.Random; import javax.swing.JPanel; public class Shapes2JPanel extends JPanel {

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

15.8  Java 2D API // desenha caminhos gerais public void paintComponent( Graphics g ) { super.paintComponent( g ); // chama o paintComponent da superclasse Random random = new Random(); // obtém o gerador de números aleatórios int[] xPoints = { 55, 67, 109, 73, 83, 55, 27, 37, 1, 43 }; int[] yPoints = { 0, 36, 36, 54, 96, 72, 96, 54, 36, 36 }; Graphics2D g2d = ( Graphics2D ) g; GeneralPath star = new GeneralPath(); // cria o objeto GeneralPath // configura a coordenada inicial do General Path star.moveTo( xPoints[ 0 ], yPoints[ 0 ] ); // cria a estrela -- isso não desenha a estrela for ( int count = 1; count < xPoints.length; count++ ) star.lineTo( xPoints[ count ], yPoints[ count ] ); star.closePath(); // fecha a forma g2d.translate( 150, 150 ); // traduz a origem para (150, 150) // gira em torno da origem e desenha estrelas em cores aleatórias for ( int count = 1; count = 0; count-- ) System.out.printf( "%c ", s1.charAt( count ) ); // copia caracteres a partir de string para charArray s1.getChars( 0, 5, charArray, 0 ); System.out.print( "\nThe character array is: " ); for ( char character : charArray ) System.out.print( character ); System.out.println(); } // fim de main } // fim da classe StringMiscellaneous

s1: hello there Length of s1: 11 The string reversed is: e r e h t The character array is: hello

o l l e h

Figura 16.2  |  Os métodos de manipulação de caractere de classe String.

A linha 15 utiliza o método String length para determinar o número de caracteres em string s1. Como os arrays, as strings conhecem seu próprio comprimento. Entretanto, diferentemente dos arrays, você não pode acessar o comprimento de uma String via um campo length — em vez disso você deve chamar o método length de String. As linhas 20–21 imprimem os caracteres da String s1 em ordem inversa (e separados por espaços). O método String charAt (linha 21) retorna o caractere em uma posição específica na String. O método charAt recebe um argumento inteiro que é utilizado como o índice e retorna o caractere nessa posição. Como os arrays, o primeiro elemento de uma String está na posição 0. A linha 24 utiliza o método String getChars para copiar os caracteres de uma String em um array de caracteres. O primeiro argumento é o índice inicial na String a partir da qual os caracteres devem ser copiados. O segundo argumento é o índice que está um além do



16.3  Classe String

519

último caractere que será copiado da String. O terceiro argumento é o array de caracteres em que eles devem ser copiados. O último argumento é o índice inicial no qual os caracteres copiados são colocados no array de caracteres-alvo. Em seguida, as linhas 27–28 imprimem o conteúdo do array char um caractere por vez.

16.3.3  Comparando Strings O Capítulo 19 discute a classificação e pesquisa de arrays. Frequentemente, as informações a serem classificadas ou pesquisadas consistem em Strings que devem ser comparadas para colocá-las em ordem ou determinar se uma string aparece em um array (ou em outra coleção). A classe String fornece vários métodos para comparar strings, como os demonstrados nos dois exemplos a seguir. Para entender o que significa uma string ser “maior que” ou “menor que” outra, considere o processo de alfabetar uma série de sobrenomes. Sem dúvida, você colocaria “Jones” antes de “Smith”, pois a primeira letra de “Jones” vem antes da primeira letra de “Smith” no alfabeto. Mas o alfabeto é mais do que uma mera lista de 26 letras — é um conjunto ordenado de caracteres. Cada letra ocorre em uma posição específica dentro do conjunto. O “z” é mais do que apenas uma letra do alfabeto — ele é especificamente a vigésima sexta letra do alfabeto. Como o computador sabe que uma letra vem antes de outra? Todos os caracteres são representados no computador como códigos numéricos (ver o Apêndice B). Quando o computador compara strings, ele realmente compara os códigos numéricos dos caracteres nas strings. A Figura 16.3 demonstra os métodos String equals, equalsIgnoreCase, compareTo e regionMatches e a utilização do operador de igualdade == para comparar objetos String. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

// Figura 16.3: StringCompare.java // Métodos String equals, equalsIgnoreCase, compareTo e regionMatches. public class StringCompare { public static void main( String[] args ) { String s1 = new String( "hello" ); // s1 é uma cópia de "hello" String s2 = "goodbye"; String s3 = "Happy Birthday"; String s4 = "happy birthday"; System.out.printf( "s1 = %s\ns2 = %s\ns3 = %s\ns4 = %s\n\n", s1, s2, s3, s4 ); // teste para igualdade if (s1.equals( "hello" ) ) // true System.out.println( "s1 equals \"hello\"" ); else System.out.println( "s1 does not equal \"hello\"" ); // testa quanto à igualdade com == if (s1 == "hello" ) // false; eles não são os mesmos objetos System.out.println( "s1 is the same object as \"hello\"" ); else System.out.println( "s1 is not the same object as \"hello\"" ); // testa quanto à igualdade (ignora if (s3.equalsIgnoreCase( s4 ) ) // System.out.printf( "%s equals %s else System.out.println( "s3 does not

maiúsculas e minúsculas) true with case ignored\n", s3, s4 ); equal s4" );

// testa compareTo System.out.printf( "\ns1.compareTo( s2 ) is %d", s1.compareTo( s2 ) ); System.out.printf( "\ns2.compareTo( System.out.printf( "\ns1.compareTo( System.out.printf( "\ns3.compareTo( System.out.printf( "\ns4.compareTo(

s1 ) is %d", s2.compareTo( s1 ) ); s1 ) is %d", s1.compareTo( s1 ) ); s4 ) is %d", s3.compareTo( s4 ) ); s3 ) is %d\n\n", s4.compareTo( s3 ) );

// testa regionMatches (distingue maiúsculas e minúsculas) if (s3.regionMatches( 0, s4, 0, 5 ) ) System.out.println( "First 5 characters of s3 and s4 match" ); else

520

Capítulo 16  Strings, caracteres e expressões regulares

50 51 52 53 54 55 56 57 58 59 60 61 s1 s2 s3 s4

System.out.println( "First 5 characters of s3 and s4 do not match" ); // testa regionMatches (ignora maiúsculas e minúsculas) if (s3.regionMatches( true, 0, s4, 0, 5 ) ) System.out.println( "First 5 characters of s3 and s4 match with case ignored" ); else System.out.println( "First 5 characters of s3 and s4 do not match" ); } // fim de main } // fim da classe StringCompare = = = =

hello goodbye Happy Birthday happy birthday

s1 equals "hello" s1 is not the same object as "hello" Happy Birthday equals happy birthday with case ignored s1.compareTo( s2.compareTo( s1.compareTo( s3.compareTo( s4.compareTo(

s2 s1 s1 s4 s3

) ) ) ) )

is is is is is

1 -1 0 -32 32

First 5 characters of s3 and s4 do not match First 5 characters of s3 and s4 match with case ignored

Figura 16.3  |  Métodos string

equals, equalsIgnoreCase, compareTo

e regionMatches.

A condição na linha 17 utiliza o método equals para comparar String s1 e o literal da String "hello" quanto à igualdade. O método equals (um método da classe Object sobrescrito em String) testa dois objetos quaisquer quanto à igualdade — as strings contidas nos dois objetos são idênticas. O método retorna true se os conteúdos dos objetos forem iguais e false, caso contrário. A condição anterior é true porque String s1 foi inicializada com o literal de string "hello". O método equals utiliza uma comparação lexicográfica — compara os valores inteiros Unicode (ver Apêndice L, para informações adicionais) que representam cada caractere em cada String. Portanto, se a String "hello" é comparada com a string "HELLO", o resultado é false, pois a representação de inteiro de uma letra minúscula é diferente daquela da letra maiúscula correspondente. A condição na linha 23 utiliza o operador de igualdade == para comparar a igualdade entre String s1 e o literal da String "hello". Quando valores de tipo de dados primitivo são comparados com ==, o resultado é true se ambos os valores forem idênticos. Quando referências são comparadas com ==, o resultado é true se ambas as referências referenciam o mesmo objeto na memória. Para comparar o conteúdo real (ou informações de estado) de objetos quanto à igualdade, um método deve ser invocado. No caso de Strings, esse método é equals. A condição anterior é avaliada como false na linha 23 porque a referência s1 foi inicializada com a instrução s1 = new String( "hello" );

que cria um novo objeto String com uma cópia de literal da string "hello" e atribui o novo objeto à variável s1. Se s1 tivesse sido inicializada com a instrução s1 = "hello";

que atribui diretamente o literal de string "hello" à variável s1, a condição seria true. Lembre-se de que o Java trata todos os objetos de literal string com o mesmo conteúdo como um objeto String ao qual há muitas referências. Portanto, as linhas 8, 17 e 23 todas referenciam o mesmo objeto String "hello" na memória.

Erro comum de programação 16.2 Comparar referências com == pode levar a erros de lógica, porque == compara as referências a fim de determinar se elas referenciam o mesmo objeto, não se dois objetos têm o mesmo conteúdo. Quando dois objetos idênticos (mas separados) são comparados com ==, o resultado será false. Ao comparar objetos para determinar se eles têm o mesmo conteúdo, utilize o método equals.

Se estiver classificando Strings, você pode compará-las quanto à igualdade com o método equals-IgnoreCase , que ignora se as letras em cada String são maiúsculas ou minúsculas ao realizar a comparação. Assim, "hello" e "HELLO" são consideradas iguais. A linha



16.3  Classe String

521

29 utiliza o método String equalsIgnoreCase para comparar String s3 — Happy Birthday — quanto à igualdade com String s4 — happy birthday. O resultado dessa comparação é true porque a comparação ignora distinção entre maiúsculas e minúsculas. As linhas 35–44 utilizam o método compareTo para comparar Strings. O método compareTo é declarado na interface Comparable e implementado na classe String. A linha 36 compara String s1 com String s2. O método compareTo retorna 0 se as Strings forem iguais, um número negativo se a String que invoca compareTo for menor que a String que é passada como um argumento e um número positivo se a String que invoca compareTo for maior que a String que é passada como um argumento. O método compareTo utiliza uma comparação lexicográfica — ele compara os valores numéricos de caracteres correspondentes em cada String. (Para obter mais informações sobre o valor exato retornado pelo método compareTo, consulte java.sun.com/javase/6/docs/api/java/lang/String.html.) A condição na linha 47 utiliza o método String regionMatches para comparar a igualdade entre partes de duas Strings. O primeiro argumento é o índice inicial na String que invoca o método. O segundo argumento é uma comparação de String. O terceiro argumento é o índice inicial na comparação de String. O último argumento é o número de caracteres a comparar entre as duas Strings. O método retorna true apenas se o número especificado de caracteres for lexicograficamente igual. Por fim, a condição na linha 54 utiliza uma versão de cinco argumentos do método String regionMatches para comparar a igualdade de partes de duas Strings. Quando o primeiro argumento é true, o método ignora maiúsculas e minúsculas dos caracteres sendo comparados. Os argumentos restantes são idênticos àqueles descritos para o método de quatro argumentos regionMatches. O próximo exemplo (Figura 16.4) demonstra os métodos String startsWith e endsWith. O método main cria o array strings que contém "started", "starting", "ended" e "ending". O restante do método main consiste em três instruções for que testam os elementos do array para determinar se eles iniciam ou terminam com um conjunto particular de caracteres. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

// Figura 16.4: StringStartEnd.java // métodos String StartsWith e endsWith. public class StringStartEnd { public static void main( String[] args ) { String[] strings = { "started", "starting", "ended", "ending" }; // testa o método startsWith for ( String string : strings ) { if (string.startsWith( "st" ) ) System.out.printf( "\"%s\" starts with \"st\"\n", string ); } // for final System.out.println(); // testa o método startsWith iniciando da posição 2 de string for ( String string : strings ) { if (string.startsWith( "art", 2 ) ) System.out.printf( "\"%s\" starts with \"art\" at position 2\n", string ); } // for final System.out.println(); // testa o método endsWith for ( String string : strings ) { if (string.endsWith( "ed" ) ) System.out.printf( "\"%s\" ends with \"ed\"\n", string ); } // for final } // fim de main } // fim da classe StringStartEnd

"started" starts with "st" "starting" starts with "st" "started" starts with "art" at position 2 "starting" starts with "art" at position 2 "started" ends with "ed" "ended" ends with "ed"

Figura 16.4  |  Métodos String startsWith e endsWith.

522

Capítulo 16  Strings, caracteres e expressões regulares

As linhas 11–15 utilizam a versão do método startsWith que aceita um argumento String. A condição na instrução if (linha 13) determina se cada String no array inicia com os caracteres "st". Se iniciar, o método retorna true e o aplicativo imprime essa String. Caso contrário, o método retorna false e nada acontece. As linhas 20–25 utilizam o método startsWith que aceita uma String e um número inteiro como argumentos. O inteiro especifica o índice em que a comparação deve iniciar na String. A condição na instrução if (linha 22) determina se cada String no array tem os caracteres "art" começando com o terceiro caractere em cada String. Se tiver, o método retorna true e o aplicativo imprime a String. A terceira instrução for (linhas 30–34) utiliza o método endsWith, que aceita um argumento String. A condição na linha 32 determina se cada String no array termina com os caracteres "ed". Se tiver, o método retorna true e o aplicativo imprime a String.

16.3.4  Localizando caracteres e substrings em strings Costuma ser útil pesquisar uma string para um caractere ou conjunto de caracteres. Por exemplo, se estiver criando seu próprio processador de texto, você poderia querer fornecer a capacidade de pesquisar por documentos. A Figura 16.5 demonstra as muitas versões dos métodos String indexOf e lastIndexOf que procuram um caractere especificado ou substring em uma String. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

// Figura 16.5: StringIndexMethods.java // Métodos de pesquisa de String indexOf e lastIndexOf. public class StringIndexMethods { public static void main( String[] args ) { String letters = "abcdefghijklmabcdefghijklm"; // testa indexOf para System.out.printf( "'c' is located at System.out.printf( "'a' is located at System.out.printf( "'$' is located at

localizar um caractere em uma string index %d\n", letters.indexOf( 'c' ) ); index %d\n", letters.indexOf( 'a', 1 ) ); index %d\n\n", letters.indexOf( '$' ) );

// testa lastIndexOf para localizar um caractere System.out.printf( "Last 'c' is located at index letters.lastIndexOf( 'c' ) ); System.out.printf( "Last 'a' is located at index letters.lastIndexOf( 'a', 25 ) ); System.out.printf( "Last '$' is located at index letters.lastIndexOf( '$' ) );

em uma string %d\n", %d\n", %d\n\n",

// testa indexOf para localizar uma substring em uma string System.out.printf( "\"def\" is located at index %d\n", letters.indexOf( "def" ) ); System.out.printf( "\"def\" is located at index %d\n", letters.indexOf( "def", 7 ) ); System.out.printf( "\"hello\" is located at index %d\n\n", letters.indexOf( "hello" ) ); // testa lastIndexOf para localizar uma substring em uma string System.out.printf( "Last \"def\" is located at index %d\n", letters.lastIndexOf( "def" ) ); System.out.printf( "Last \"def\" is located at index %d\n", letters.lastIndexOf( "def", 25 ) ); System.out.printf( "Last \"hello\" is located at index %d\n", letters.lastIndexOf( "hello" ) ); } // fim de main } // fim da classe StringIndexMethods

'c' is located at index 2 'a' is located at index 13 '$' is located at index -1 Last 'c' is located at index 15 Last 'a' is located at index 13 Last '$' is located at index -1



16.3  Classe String

523

"def" is located at index 3 "def" is located at index 16 "hello" is located at index -1 Last "def" is located at index 16 Last "def" is located at index 16 Last "hello" is located at index -1

Figura 16.5  |  Métodos de pesquisa de string indexOf e lastIndexOf.

Todas as pesquisas nesse exemplo são realizadas em String letters (inicializadas com "abcdefghijklmabcdefghijklm"). As linhas 11–16 utilizam o método indexOf para localizar a primeira ocorrência de um caractere em uma String. Se o método localizar o caractere, ele retorna o índice do caractere na String — caso contrário, retorna –1. Há duas versões de indexOf que procuram caracteres em uma String. A expressão na linha 12 utiliza a versão do método indexOf que aceita uma representação de inteiro do caractere a localizar. A expressão na linha 14 utiliza outra versão do método indexOf, que aceita dois argumentos inteiros — o caractere e o índice inicial em que a pesquisa da String deve iniciar. As linhas 19–24 utilizam o método lastIndexOf para localizar a última ocorrência de um caractere em uma String. O método procura do fim da String em direção ao começo. Se localizar o caractere, ele retorna o índice do caractere na String — caso contrário, ele retorna –1. Há duas versões de lastIndexOf que pesquisam por caracteres em uma String. A expressão na linha 20 utiliza a versão que aceita a representação de inteiro do caractere. A expressão na linha 22 utiliza a versão que aceita dois argumentos inteiros — a representação de inteiro do caractere e o índice a partir do qual iniciar a pesquisa de trás para frente. As linhas 27–40 demonstram versões dos métodos indexOf e lastIndexOf que aceitam uma String como o primeiro argumento. Essas versões trabalham identicamente àquelas descritas anteriormente exceto que procuram sequências de caracteres (ou substrings) que são especificadas por seus argumentos String. Se a substring for localizada, esses métodos retornam o índice na String do primeiro caractere na substring.

16.3.5  Extraindo substrings de strings A classe String fornece dois métodos substring para permitir que um novo objeto String seja criado copiando parte de um objeto String existente. Cada método retorna um novo objeto String. Ambos os métodos são demonstrados na Figura 16.6. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

// Figura 16.6: SubString.java // métodos substring da classe String. public class SubString { public static void main( String[] args ) { String letters = "abcdefghijklmabcdefghijklm"; // testa métodos substring System.out.printf( "Substring from index 20 to end is \"%s\"\n", letters.substring( 20 ) ); System.out.printf( "%s \"%s\"\n", "Substring from index 3 up to, but not including 6 is", letters.substring( 3, 6 ) ); } // fim de main } // fim da classe Substring

Substring from index 20 to end is "hijklm" Substring from index 3 up to, but not including 6 is "def"

Figura 16.6  |  Métodos substring da classe String.

A expressão letters.substring(20) na linha 12 utiliza o método substring que aceita um argumento inteiro. O argumento especifica o índice inicial na String letters original a partir da qual os caracteres devem ser copiados. A substring retornada contém uma cópia dos caracteres desde o índice inicial até o final da String. Especificar um índice fora dos limites da String causa uma String­ IndexOutOfBoundsException. A linha 15 utiliza o método substring que aceita dois argumentos do tipo inteiro — o índice inicial a partir do qual copiar caracteres na String original e o índice um além do último caractere que será copiado (isto é, copiar até, mas não incluindo, esse índice na String). A substring retornada contém uma cópia dos caracteres especificados da String original. Um índice fora dos limites da String causa uma StringIndexOutOfBoundsException.

524

Capítulo 16  Strings, caracteres e expressões regulares

16.3.6  Concatenando strings O método String concat (Figura 16.7) concatena dois objetos String e retorna um novo objeto String que contém os caracteres de ambas as Strings originais. A expressão s1.concat(s2) na linha 13 forma uma String acrescentando os caracteres em s2 aos caracteres em s1. As Strings originais que referenciam s1 e s2 não são modificadas. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

// Figura 16.7: StringConcatenation.java // Método string concat. public class StringConcatenation { public static void main( String[] args ) { String s1 = "Happy "; String s2 = "Birthday"; System.out.printf( "s1 = %s\ns2 = %s\n\n",s1, s2 ); System.out.printf( "Result of s1.concat( s2 ) = %s\n", s1.concat( s2 ) ); System.out.printf( "s1 after concatenation = %s\n", s1 ); } // fim de main } // fim da classe StringConcatenation

s1 = Happy s2 = Birthday Result of s1.concat( s2 ) = Happy Birthday s1 after concatenation = Happy

Figura 16.7  |  Método String concat.

16.3.7  Métodos de String diversos A classe String fornece vários métodos que retornam cópias modificadas de Strings ou que retornam arrays de caracteres. Esses métodos são demonstrados no aplicativo na Figura 16.8. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

// Figura 16.8: StringMiscellaneous2.java // Métodos String replace, toLowerCase, toUpperCase, trim e toCharArray. public class StringMiscellaneous2 { public static void main( String[] args ) { String s1 = "hello"; String s2 = "GOODBYE"; String s3 = " spaces "; System.out.printf( "s1 = %s\ns2 = %s\ns3 = %s\n\n", s1, s2, s3 ); // testa o método replace System.out.printf( "Replace 'l' with 'L' in s1: %s\n\n", s1.replace( 'l', 'L' ) ); // testa o toLowerCase e toUpperCase System.out.printf( "s1.toUpperCase() = %s\n", s1.toUpperCase() ); System.out.printf( "s2.toLowerCase() = %s\n\n", s2.toLowerCase() ); // testa o método trim System.out.printf( "s3 after trim = \"%s\"\n\n", s3.trim() ); // testa o método toCharArray char[] charArray = s1.toCharArray(); System.out.print( "s1 as a character array = " );



16.3  Classe String

29 30 31 32 33 34

525

for ( char character : charArray ) System.out.print( character ); System.out.println(); } // fim de main } // fim da classe StringMiscellaneous2

s1 = hello s2 = GOODBYE s3 = spaces Replace 'l' with 'L' in s1: heLLo s1.toUpperCase() = HELLO s2.toLowerCase() = goodbye s3 after trim = "spaces" s1 as a character array = hello

Figura 16.8  |  Métodos String

replace, toLowerCase, toUpperCase, trim

e toCharArray.

A linha 16 utiliza o método String replace para retornar um novo objeto String em que cada ocorrência na s1 do caractere 'l' (letras minúsculas el) é substituída pelo caractere 'L'. O método replace deixa a String original inalterada. Se não houver nenhuma ocorrência do primeiro argumento na String, o método replace retorna a String original. Uma versão sobrecarregada do método re­ place permite substituir substrings em vez de caracteres individuais. A linha 19 utiliza o método String toUpperCase para gerar uma nova String com letras maiúsculas na qual letras minúsculas correspondentes existem em s1. O método retorna um novo objeto String contendo a String convertida e deixa a String original inalterada. Se não houver caractere para converter, o método toUpperCase retorna a String original. A linha 20 utiliza o método String toLowerCase para retornar um novo objeto String com letras minúsculas no qual há letras maiúsculas correspondentes em s2. A String original permanece inalterada. Se não houver caracteres na String original para serem convertidos, toLowerCase retorna a String original. A linha 23 utiliza o método String trim para gerar um novo objeto String que remove todos os caracteres de espaço em branco que aparecem no início ou no fim da String em que trim opera. O método retorna um novo objeto String contendo a String sem espaço em branco inicial ou final. A String original permanece inalterada. A linha 26 utiliza o método String toCharArray para criar um novo array de caractere que contém uma cópia dos caracteres em s1. As linhas 29–30 geram saída de cada char no array.

16.3.8  Método String valueOf Como vimos, cada objeto em Java tem um método toString que permite que um programa obtenha a representação de string do objeto. Infelizmente, essa técnica não pode ser utilizada com tipos primitivos porque eles não têm métodos. A classe String fornece os métodos static que aceitam um argumento de qualquer tipo e o convertem em um objeto String. A Figura 16.9 demonstra os métodos valueOf da classe String. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

// Figura 16.9: StringValueOf.java // Métodos valueOf de String. public class StringValueOf { public static void main( String[] args ) { char[] charArray = { 'a', 'b', 'c', 'd', 'e', 'f' }; boolean booleanValue = true; char characterValue = 'Z'; int integerValue = 7; long longValue = 10000000000L; // sufixo L indica tipo long float floatValue = 2.5f; // f indica que 2.5 é um tipo float double doubleValue = 33.333; // sem sufixo, tipo double é padrão Object objectRef = "hello"; // atribui string a uma referência Object System.out.printf( "char array = %s\n", String.valueOf( charArray ) );

526 19 20 21 22 23 24 25 26 27 28 29 30 31 32

Capítulo 16

Strings, caracteres e expressões regulares

System.out.printf( "part of char array = %s\n", String.valueOf( charArray, 3, 3 ) ); System.out.printf( "boolean = %s\n", String.valueOf( booleanValue ) ); System.out.printf( "char = %s\n", String.valueOf( characterValue ) ); System.out.printf( "int = %s\n", String.valueOf( integerValue System.out.printf( "long = %s\n", String.valueOf( longValue ) System.out.printf( "float = %s\n", String.valueOf( floatValue ) System.out.printf( "double = %s\n", String.valueOf( doubleValue ) ); System.out.printf( "Object = %s", String.valueOf( objectRef ) } // fim de main } // fim da classe da StringValueOf

) ); ); );

);

char array = abcdef part of char array = def boolean = true char = Z int = 7 long = 10000000000 float = 2.5 double = 33.333 Object = hello

Figura 16.9 | Métodos String valueOf.

A expressão String.valueOf(charArray) na linha 18 utiliza o array de caractere charArray para criar um novo objeto String. A expressão String.valueOf(charArray, 3, 3) na linha 20 utiliza uma parte do array de caracteres charArray para criar um novo objeto String. O segundo argumento especifica o índice inicial a partir do qual os caracteres são utilizados. O terceiro argumento especifica o número de caracteres a ser utilizado. Há sete outras versões do método valueOf, que aceitam argumentos do tipo boolean, char, int, long, float, double e Object, respectivamente. Esses são demonstrados nas linhas 21–30. Observe que a versão de valueOf que aceita um Object como um argumento pode fazer isso porque todos os Objects podem ser convertidos em Strings com o método toString. [Nota: As linhas 12–13 utilizam os valores literais 10000000000L e 2.5f como os valores iniciais da variável long longValue e variável float floatValue, respectivamente. Por padrão, o Java trata os literais de inteiro como o tipo int e os literais de ponto flutuante como o tipo double. Acrescentar a letra L ao literal 10000000000 e letra f ao literal 2.5 indica para o compilador que 10000000000 deve ser tratado como um long e que 2.5 deve ser tratado como um float. Um L maiúsculo ou l minúsculo pode ser utilizado para denotar uma variável de tipo long e um F maiúsculo ou f minúsculo pode ser utilizado para denotar uma variável de tipo float].

16.4 Classe StringBuilder Discutimos agora os recursos da classe StringBuilder para criar e manipular informações de string dinâmica — isto é, strings modificáveis. Cada StringBuilder é capaz de armazenar um número de caracteres especificado pela sua capacidade. Se a capacidade de um StringBuilder for excedida, a capacidade se expande para acomodar os caracteres adicionais.

Dica de desempenho 16.2 O Java pode realizar certas otimizações que envolvem objetos String (como referenciar um objeto String a partir de múltiplas variáveis) porque ele sabe que esses objetos não se alterarão. Strings (não StringBuilders) devem ser utilizadas se os dados não se alterarem.

Dica de desempenho 16.3 Em programas que frequentemente realizam a concatenação de strings, ou outras modificações de strings, em geral, é mais eficiente implementar as modificações com a classe StringBuilder.

Observação de engenharia de software 16.2 StringBuilders

não são seguros para thread. Se múltiplos threads exigirem acesso às mesmas informações de string dinâmicas, utilize a classe StringBuffer no seu código. As classes StringBuilder e StringBuffer fornecem capacidades idênticas, mas a classe StringBuffer é segura para threads. Para obter mais detalhes sobre threading, consulte o Capítulo 26.



16.4  Classe StringBuilder

527

16.4.1  Construtores StringBuilder A classe StringBuilder fornece quatro construtores. Demonstramos três deles na Figura 16.10. A linha 8 utiliza o construtor sem argumento StringBuilder para criar um StringBuilder sem caracteres e uma capacidade inicial de 16 caracteres (o padrão para um StringBuilder). A linha 9 utiliza o construtor StringBuilder que aceita um argumento inteiro para criar um StringBuilder sem caracteres e a capacidade inicial especificada pelo argumento inteiro (isto é, 10). A linha 10 utiliza o construtor StringBuilder que aceita um argumento String para criar um StringBuilder contendo o caractere no argumento String. A capacidade inicial é o número de caracteres no argumento String mais 16. As linhas 12–14 utilizam o método toString da classe StringBuilder para gerar saída de StringBuilders com o método printf. Na Seção 16.4.4, discutimos como o Java utiliza objetos StringBuilder para implementar os operadores + e += para concatenação de strings. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

// Figura 16.10: StringBuilderConstructors.java // Construtores StringBuilder. public class StringBuilderConstructors { public static void main( String[] args ) { StringBuilder buffer1 = new StringBuilder(); StringBuilder buffer2 = new StringBuilder( 10 ); StringBuilder buffer3 = new StringBuilder( "hello" ); System.out.printf( "buffer1 = \"%s\"\n", buffer1.toString() ); System.out.printf( "buffer2 = \"%s\"\n", buffer2.toString() ); System.out.printf( "buffer3 = \"%s\"\n", buffer3.toString() ); } // fim de main } // fim da classe StringBuilderConstructors

buffer1 = "" buffer2 = "" buffer3 = "hello"

Figura 16.10  |  Construtores StringBuilder.

16.4.2  Métodos StringBuilder length, capacity, setLength e ensureCapacity A classe StringBuilder fornece os métodos length e capacity para retornar o número de caracteres atualmente em um String­ e o número de caracteres que pode ser armazenado em um StringBuilder sem alocar mais memória, respectivamente. O método ensureCapacity garante que um StringBuilder tenha pelo menos a capacidade especificada. O método setLength aumenta ou diminui o comprimento de uma StringBuilder. A Figura 16.11 demonstra esses métodos.

Builder

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

// Figura 16.11: StringBuilderCapLen.java // Métodos StringBuilder length, setLength, capacity e ensureCapacity.

17 18 19 20

System.out.printf( "New length = %d\nbuffer = %s\n", buffer.length(), buffer.toString() ); } // fim de main } // fim da classe StringBuilderCapLen

public class StringBuilderCapLen { public static void main( String[] args ) { StringBuilder buffer = new StringBuilder( "Hello, how are you?" ); System.out.printf( "buffer = %s\nlength = %d\ncapacity = %d\n\n", buffer.toString(),buffer.length(),buffer.capacity() ); buffer.ensureCapacity( 75 ); System.out.printf( "New capacity = %d\n\n", buffer.capacity() ); buffer.setLength( 10 );

528

Capítulo 16  Strings, caracteres e expressões regulares

buffer = Hello, how are you? length = 19 capacity = 35 New capacity = 75 New length = 10 buffer = Hello, how

Figura 16.11  |  Métodos StringBuilder length, setLength, capacity e ensureCapacity.

O aplicativo contém um StringBuilder chamado buffer. A linha 8 utiliza o construtor StringBuilder que aceita um argumento String para inicializar o StringBuilder com "Hello, how are you?". As linhas 10–11 imprimem o conteúdo, o comprimento e a capacidade do StringBuilder. Note na janela de saída que a capacidade do StringBuilder é inicialmente 35. Lembre-se de que o construtor StringBuilder que aceita um argumento String inicializa a capacidade com o comprimento da string passada como um argumento mais 16. A linha 13 utiliza o método ensureCapacity para expandir a capacidade do StringBuilder a um mínimo de 75 caracteres. Na verdade, se a capacidade original for menor que o argumento, o método assegura uma capacidade que é o maior do número especificado como um argumento e duas vezes a capacidade original mais 2. A capacidade atual do StringBuilder permanece inalterada se ela for maior do que a capacidade especificada.

Dica de desempenho 16.4 Aumentar a capacidade de um StringBuilder dinamicamente pode exigir um tempo relativamente longo. Executar um grande número dessas operações pode degradar o desempenho de um aplicativo. Se o tamanho de um StringBuilder vai aumentar significativamente, possivelmente múltiplas vezes, configurar sua capacidade alta no início aumentará o desempenho.

A linha 16 utiliza o método setLength para configurar o comprimento do StringBuilder como 10. Se o comprimento especificado é menor que o número atual de caracteres no StringBuilder, o buffer é truncado para o comprimento especificado (isto é, os caracteres na StringBuilder depois do comprimento especificado são descartados). Se o comprimento especificado for maior que o número de caracteres atualmente no StringBuilder, caracteres nulos (caracteres com a representação numérica 0) são acrescentados até que o número total de caracteres em StringBuilder seja igual ao comprimento especificado.

16.4.3  Métodos StringBuilder, charAt, setCharAt, getChars e reverse A classe StringBuilder fornece os métodos charAt, setCharAt, getChars e reverse para manipular os caracteres em um StringBuilder(Figura 16.12). O método charAt (linha 12) aceita um argumento inteiro e retorna o caractere no StringBuilder nesse índice. O método getChars (linha 15) copia caracteres de um StringBuilder no array de caractere passado como um argumento. Esse método aceita quatro argumentos — o índice inicial a partir do qual caracteres devem ser copiados na StringBuilder, o índice um além do último caractere que será copiado a partir do StringBuilder, o array de caracteres em que os caracteres serão copiados e a localização inicial no array de caracteres em que o primeiro caractere deve ser colocado. O método setCharAt (linhas 21 e 22) aceita um argumento inteiro e um argumento caractere e configura o caractere na posição especificada no StringBuilder como o argumento caractere. O método reverse (linha 25) inverte o conteúdo do StringBuilder.

Erro comum de programação 16.3 Tentar acessar um caractere que está além dos limites de uma StringBuilder (isto é, com um índice menor que 0 ou um índice maior que ou igual ao comprimento da StringBuilder) resulta em uma StringIndexOutOfBoundsException.

1 2 3 4 5 6 7 8 9 10 11

// Figura 16.12: StringBuilderChars.java // Métodos StringBuilder charAt, setCharAt, getChars e reverse. public class StringBuilderChars { public static void main( String[] args ) { StringBuilder buffer = new StringBuilder( "hello there" ); System.out.printf( "buffer = %s\n", buffer.toString() ); System.out.printf( "Character at 0: %s\nCharacter at 4: %s\n\n",



16.4  Classe StringBuilder

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

529

buffer.charAt( 0 ), buffer.charAt( 4 ) ); char[] charArray = new char[ buffer.length() ]; buffer.getChars( 0, buffer.length(), charArray, 0 ); System.out.print( "The characters are: " ); for ( char character : charArray ) System.out.print( character ); buffer.setCharAt( 0, 'H' ); buffer.setCharAt( 6, 'T' ); System.out.printf( "\n\nbuffer = %s", buffer.toString() ); buffer.reverse(); System.out.printf( "\n\nbuffer = %s\n", buffer.toString() ); } // fim de main } // fim da classe StringBuilderChars

buffer = hello there Character at 0: h Character at 4: o The characters are: hello there buffer = Hello There buffer = erehT olleH

Figura 16.12  |  Os métodos StringBuilder charAt, setCharAt, getChars e reverse.

16.4.4  Métodos StringBuilder append A classe StringBuilder fornece os métodos append sobrecarregados (Figura 16.13) para permitir que valores de vários tipos sejam acrescentados no fim de um StringBuilder. Versões são fornecidas para cada um dos tipos primitivos, e para arrays de caractere, Strings, Objects e mais. (Lembre-se de que o método toString produz uma representação de string de qualquer Object.) Cada método recebe seu argumento, converte-o em uma string e a acrescenta a StringBuilder. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

// Figura 16.13: StringBuilderAppend.java // Métodos append de StringBuilder public class StringBuilderAppend { public static void main( String[] args ) { Object objectRef = "hello"; String string = "goodbye"; char[] charArray = { 'a', 'b', 'c', 'd', 'e', 'f' }; boolean booleanValue = true; char characterValue = 'Z'; int integerValue = 7; long longValue = 10000000000L; float floatValue = 2.5f; double doubleValue = 33.333; StringBuilder lastBuffer = new StringBuilder( "last buffer" ); StringBuilder buffer = new StringBuilder(); buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append(

objectRef ); "\n" ); string ); "\n" ); charArray ); "\n" ); charArray, 0, 3 ); "\n" ); booleanValue ); "\n" ); characterValue );

530 32 33 34 35 36 37 38 39 40 41 42 43 44 45

Capítulo 16  Strings, caracteres e expressões regulares buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append( buffer.append(

"\n" ); integerValue ); "\n" ); longValue ); "\n" ); floatValue ); "\n" ); doubleValue ); "\n" ); lastBuffer );

System.out.printf( "buffer contains %s\n", buffer.toString() ); } // fim de main } // termina StringBuilderAppend

buffer contains hello goodbye abcdef abc true Z 7 10000000000 2.5 33.333 last buffer

Figura 16.13  |  Métodos StringBuilder append.

De fato, um compilador pode utilizar os métodos StringBuilder (ou StringBuffer) e append para implementar os operadores de concatenação de String + e += . Por exemplo, considerando as declarações String string1 = "hello"; String string2 = "BC"; int value = 22;

a instrução String s = string1 + string2 + value;

concatena "hello", "BC" e 22. A concatenação pode ser realizada como mostrado a seguir: String s = new StringBuilder().append( "hello" ).append( "BC" ). append( 22 ).toString();

Em primeiro lugar, a instrução anterior cria um StringBuilder vazio, depois acrescenta-lhe as strings "hello" e "BC" e o número inteiro 22. Em seguida, o método toString de StringBuilder converte o objeto StringBuilder em uma String a ser atribuída à String s. A instrução s += "!";

pode ser realizada desta maneira: s = new StringBuilder().append( s ).append( "!" ).toString();

Primeiro, a instrução anterior cria um StringBuilder vazio, depois acrescenta-lhe o conteúdo atual de s seguido por "!". Em seguida, o método toString de StringBuilder converte o StringBuilder em uma representação de string e o resultado é atribuído à s.

16.4.5  Métodos de inserção e exclusão de StringBuilder A classe StringBuilder fornece os métodos insert sobrecarregados para inserir valores de vários tipos em qualquer posição em um StringBuilder. As versões são oferecidas para os tipos primitivos e para os arrays de caracteres, Strings, Objects e CharSequences. Cada método aceita seu segundo argumento, converte-o em uma String e o insere no índice especificado pelo primeiro argumento. Se o primeiro argumento menor que 0 ou maior que o comprimento StringBuilder, uma StringIndexOutOfBoundsException ocorre. A classe StringBuilder também fornece métodos delete e deleteCharAt para excluir caracteres em qualquer posição em um String­ Builder. O método delete — aceita dois argumentos — o índice inicial e o índice um além do fim dos caracteres a excluir. Todos os caracteres que começam no índice inicial, mas não incluindo o índice final, são excluídos. O método deleteCharAt aceita um argumento — o índice do caractere a excluir. Os índices inválidos fazem com que ambos os métodos lancem uma StringIndexOutOfBoundsException. A Figura 16.14 demonstra os métodos insert, delete e deleteCharAt.

16.5 Classe Character

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

531

// Figura 16.14: StringBuilderInsertDelete.java // Métodos StringBuilder insert, delete e deleteCharAt. public class StringBuilderInsertDelete { public static void main( String[] args ) { Object objectRef = "hello"; String string = "goodbye"; char[] charArray = { 'a', 'b', 'c', 'd', 'e', 'f' }; boolean booleanValue = true; char characterValue = 'K'; int integerValue = 7; long longValue = 10000000; float floatValue = 2.5f; // o sufixo f indica que 2.5 é um tipo float double doubleValue = 33.333; StringBuilder buffer = new StringBuilder(); buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert( buffer.insert(

0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

objectRef ); " " ); // cada um desses contém dois espaços string ); " " ); charArray ); " " ); charArray, 3, 3 ); " " ); booleanValue ); " " ); characterValue ); " " ); integerValue ); " " ); longValue ); " " ); floatValue ); " " ); doubleValue );

System.out.printf( "buffer after inserts:\n%s\n\n", buffer.toString() ); buffer.deleteCharAt( 10 ); // exclui 5 em 2.5 buffer.delete( 2, 6 ); // exclui .333 in 33.333 System.out.printf( "buffer after deletes:\n%s\n", buffer.toString() ); } // fim de main } // fim da classe StringBuilderInsertDelete

buffer after inserts: 33.333 2.5 10000000 buffer after deletes: 33 2. 10000000 7 K

7

K

true

true

def

def

abcdef

abcdef

Figura 16.14 | Os métodos StringBuilder

goodbye

goodbye

insert, delete

hello

hello

e deleteCharAt.

16.5 Classe Character O Java fornece oito classes do tipo wrapper — Boolean, Character, Double, Float, Byte, Short, Integer e Long — que permite que valores de tipo primitivo sejam tratados como objetos. Nesta seção, apresentamos a classe Character — a classe do tipo wrapper para o tipo primitivo char. A maioria dos métodos Character são métodos static projetados por uma questão de conveniência no processamento de valores char individuais. Esses métodos aceitam pelo menos um argumento caractere e realizam um teste ou uma manipulação do caractere. A classe Character também contém um construtor que recebe um argumento char para inicializar um objeto Character. A maioria dos

532

Capítulo 16  Strings, caracteres e expressões regulares

métodos da classe Character é apresentada nos próximos três exemplos. Para mais informações sobre a classe Character (e todas as classes empacotadoras de tipo), veja o pacote java.lang na documentação do Java API. A Figura 16.15 demonstra métodos static que testam caracteres para determinar se eles são um tipo específico de caractere e os métodos static que realizam as conversões de caracteres em letras maiúsculas/minúsculas. Você pode inserir qualquer caractere e aplicar os métodos ao caractere. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

// Figura 16.15: StaticCharMethods.java // Métodos Character static para testar caracteres e converter letras maiúsculas e minúsculas. import java.util.Scanner;

26 27 28 29 30 31 32 33

System.out.printf( "is upper case: %b\n", Character.isUpperCase( c ) ); System.out.printf( "to upper case: %s\n", Character.toUpperCase( c ) ); System.out.printf( "to lower case: %s\n", Character.toLowerCase( c ) ); } // fim de main } // StaticCharMethods fim da classe

public class StaticCharMethods { public static void main( String[] args ) { Scanner scanner = new Scanner( System.in ); // cria scanner System.out.println( "Enter a character and press Enter" ); String input = scanner.next(); char c = input.charAt( 0 ); // obtém caractere de entrada // exibe informações de caractere de System.out.printf( "is defined: %b\n", Character.isDefined( c ) ); System.out.printf( "is digit: %b\n", Character.isDigit( c ) ); System.out.printf( "is first character in a Java identifier: %b\n", Character.isJavaIdentifierStart( c ) ); System.out.printf( "is part of a Java identifier: %b\n", Character.isJavaIdentifierPart( c ) ); System.out.printf( "is letter: %b\n", Character.isLetter( c ) ); System.out.printf( "is letter or digit: %b\n", Character.isLetterOrDigit( c ) ); System.out.printf( "is lower case: %b\n", Character.isLowerCase( c ) );

Enter a character and press Enter A is defined: true is digit: false is first character in a Java identifier: true is part of a Java identifier: true is letter: true is letter or digit: true is lower case: false is upper case: true to upper case: A to lower case: a

Enter a character and press Enter 8 is defined: true is digit: true is first character in a Java identifier: false is part of a Java identifier: true is letter: false is letter or digit: true is lower case: false is upper case: false to upper case: 8 to lower case: 8



16.5  Classe Character

533

Enter a character and press Enter $ is defined: true is digit: false is first character in a Java identifier: true is part of a Java identifier: true is letter: false is letter or digit: false is lower case: false is upper case: false to upper case: $ to lower case: $

Figura 16.15  |  Métodos Character static para testar caracteres e converter caracteres maiúsculos e minúsculos.

A linha 15 utiliza o método Character isDefined para determinar se o caractere c está definido no conjunto de caracteres Unicode. Se estiver, o método retorna true; caso contrário, retorna false. A linha 16 utiliza o método Character isDigit para determinar se o caractere c é um dígito Unicode definido. Se for, o método retorna true e caso contrário, false. A linha 18 utiliza o método Character isJavaIdentifierStart para determinar se c é um caractere que pode ser o primeiro caractere de um identificador em Java — isto é, uma letra, um sublinhado (_) ou um sinal de cifrão ($). Se for, o método retorna true e caso contrário, false. A linha 20 utiliza o método Character isJavaIdentifierPart para determinar se o caractere c é um caractere que pode ser usado em um identificador em Java — isto é, um dígito, uma letra, um sublinhado (_) ou um sinal de cifrão ($). Se for, o método retorna true e caso contrário, false. A linha 21 utiliza o método Character isLetter para determinar se o caractere c é uma letra. Se for, o método retorna true e caso contrário, false. A linha 23 utiliza o método Character isLetterOrDigit para determinar se o caractere c é uma letra ou um dígito. Se for, o método retorna true e caso contrário, false. A linha 25 utiliza o método Character isLowerCase para determinar se o caractere c é uma letra minúscula. Se for, o método retorna true e caso contrário, false. A linha 27 utiliza o método Character isUpperCase para determinar se o caractere c é uma letra maiúscula. Se for, o método retorna true e caso contrário, false. A linha 29 utiliza o método Character toUpperCase para converter o caractere c em seu equivalente em letras maiúsculas. O método retorna o caractere convertido se o caractere tiver um equivalente em letras maiúsculas; caso contrário, o método retorna seu argumento original. A linha 31 utiliza o método Character toLowerCase para converter o caractere c em seu equivalente em letras minúsculas. O método retorna o caractere convertido se o caractere tem um equivalente em letras minúsculas e, caso contrário, o método retorna seu argumento original. A Figura 16.16 demonstra os métodos static Character digit e forDigit, que convertem os caracteres em dígitos e dígitos em caracteres, respectivamente, em diferentes sistemas de números. Sistemas comuns de número incluem decimal (base 10), octal (base 8), hexadecimal (base 16) e binário (base 2). A base de um número também é conhecida como sua radical. Para mais informações sobre conversões entre sistemas de números, veja o Apêndice H. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

// Figura 16.16: StaticCharMethods2.java // Métodos de conversão static da classe Character. import java.util.Scanner; public class StaticCharMethods2 { // executa o aplicativo public static void main( String[] args ) { Scanner scanner = new Scanner( System.in ); // obtém radical System.out.println( "Please enter a radix:" ); int radix = scanner.nextInt(); // obtém escolha de usuário System.out.printf( "Please choose one:\n1 -- %s\n2 -- %s\n", "Convert digit to character", "Convert character to digit" ); int choice = scanner.nextInt(); // processa solicitação switch ( choice ) {

534 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

Capítulo 16  Strings, caracteres e expressões regulares case 1: // converte dígito em caractere System.out.println( "Enter a digit:" ); int digit = scanner.nextInt(); System.out.printf( "Convert digit to character: %s\n", Character.forDigit( digit, radix ) ); break; case 2: // converte caractere em dígito System.out.println( "Enter a character:" ); char character = scanner.next().charAt( 0 ); System.out.printf( "Convert character to digit: %s\n", Character.digit( character, radix ) ); break; } // fim do switch } // fim de main } // fim da classe StaticCharMethods2

Please enter a radix: 16 Please choose one: 1 -- Convert digit to character 2 -- Convert character to digit 2 Enter a character: A Convert character to digit: 10

Please enter a radix: 16 Please choose one: 1 -- Convert digit to character 2 -- Convert character to digit 1 Enter a digit: 13 Convert digit to character: d

Figura 16.16  |  Métodos de conversão static da classe Character.

A linha 28 método utiliza forDigit para converter o inteiro digit em um caractere no sistema de números especificado pelo inteiro (a base do número). Por exemplo, o inteiro decimal 13 na base 16 (o radix) tem o valor de caractere 'd'. Letras minúsculas e maiúsculas representam o mesmo valor em sistemas numéricos. A linha 35 utiliza o método digit para converter a variável character em um inteiro no sistema numérico especificado pelo inteiro radix (a base do número). Por exemplo, o caractere 'A' é a representação base 16 (o radix) do valor 10 de base 10. O radical deve estar entre 2 e 36, inclusive. A Figura 16.17 demonstra o construtor e vários métodos não static da classe Character — charValue, toString e equals. As linhas 7–8 instanciam dois objetos Character atribuindo as constantes de caractere 'A' e 'a', respectivamente, às variáveis Character. O Java converte automaticamente esses literais char em objetos Character — um processo conhecido como autoboxing que discutimos mais detalhadamente na Seção 20.4. A linha 11 utiliza o método Character charValue para retornar o valor char armazenado no objeto Character c1. A linha 11 retorna uma representação de string do objeto Character c2 utilizando o método toString. A condição na linha 13 utiliza o método equals para determinar se o objeto c1 tem o mesmo conteúdo que o objeto c2 (isto é, os caracteres dentro de cada objeto são iguais). radix



1 2 3 4 5 6 7 8 9

// Figura 16.17: OtherCharMethods.java // Métodos não static da classe Character. public class OtherCharMethods { public static void main( String[] args ) { Character c1 = 'A'; Character c2 = 'a';

16.6 Tokenização de Strings 10 11 12 13 14 15 16 17 18

535

System.out.printf( "c1 = %s\nc2 = %s\n\n", c1.charValue(),c2.toString() ); if (c1.equals( c2 ) ) System.out.println( "c1 and c2 are equal\n" ); else System.out.println( "c1 and c2 are not equal\n" ); } // fim de main } // fim da classe OtherCharMethods

c1 = A c2 = a c1 and c2 are not equal

Figura 16.17 | Método não static da classe Character.

16.6 Tokenização de Strings Quando você lê uma frase, sua mente a divide em tokens — palavras individuais e sinais de pontuação que lhe transmitem significado. Os compiladores também realizam "tokenização". Eles dividem instruções em pedaços individuais, como palavras-chave, identificadores, operadores e outros elementos de linguagem de programação. Agora estudaremos o método split da classe String, que divide uma String em seus tokens componentes. Os tokens são separados entre si por delimitadores, em geral caracteres de espaçamento como espaço, tabulação, nova linha e retorno de carro. Outros caracteres também podem ser utilizados como delimitadores para separar tokens. O aplicativo na Figura 16.18 demonstra o método split de String. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

// Figura 16.18: TokenTest.java // Objeto StringTokenizer utilizado para tokenizar strings. import java.util.Scanner; import java.util.StringTokenizer; public class TokenTest { // executa o aplicativo public static void main( String[] args ) { // obtém a frase Scanner scanner = new Scanner( System.in ); System.out.println( "Enter a sentence and press Enter" ); String sentence = scanner.nextLine(); // processa a frase do usuário String[] tokens = sentence.split( " " ); System.out.printf( "Number of elements: %d\nThe tokens are:\n", tokens.length ); for ( String token : tokens ) System.out.println( token ); } // fim de main } // fim da classe TokenTest

Enter a sentence and press Enter This is a sentence with seven tokens Number of elements: 7 The tokens are: This is a sentence with seven tokens

Figura 16.18 | O objeto StringTokenizer utilizado para tokenizar strings.

536

Capítulo 16

Strings, caracteres e expressões regulares

Quando o usuário pressiona a tecla Enter, a frase de entrada é armazenada na variável sentence. A linha 17 invoca o método String split com o argumento String " ", que retorna um array de Strings. O caractere de espaço no argumento String é o delimitador que

o método split utiliza para localizar os tokens na String. Como você aprenderá na próxima seção, o argumento para o método split pode ser uma expressão regular para tokenização mais complexa. A linha 19 exibe o tamanho do array tokens — isto é, o número de tokens em sentence. As linhas 21–22 geram saída de cada token em uma linha separada.

16.7 Expressões regulares, classe Pattern e classe Matcher Uma expressão regular é uma String especialmente formatada que descreve um padrão de pesquisa para correspondência de caracteres em outras Strings. Elas são úteis para validar a entrada e assegurar que os dados estão em determinado formato. Por exemplo, um CEP deve consistir em cinco dígitos e um sobrenome deve conter somente letras, espaços, apóstrofos e hífens. Um aplicativo de expressões regulares serve para facilitar a construção de um compilador. Frequentemente, uma expressão regular grande e complexa é utilizada para validar a sintaxe de um programa. Se o código do programa não localizar expressão regular, o compilador sabe que há um erro de sintaxe dentro do código. A classe String fornece vários métodos para realizar operações de expressão regular, das quais a mais simples é a operação de correspondência. O método String matches recebe uma String que especifica a expressão regular e localiza o conteúdo do objeto String em que ele é chamado na expressão regular. O método retorna um boolean indicando se a correspondência foi ou não bem-sucedida. Uma expressão regular consiste em caracteres literais e símbolos especiais. A Figura 16.19 especifica algumas classes de caractere predefinidas que podem ser utilizadas com expressões regulares. Uma classe de caracteres é uma sequência de escape que representa um grupo de caracteres. Um dígito é qualquer caractere numérico. Um caractere de palavra é qualquer letra (em letras maiúsculas ou minúsculas), qualquer dígito ou o caractere sublinhado. Um caractere de espaço em branco é um espaço, uma tabulação, um retorno de carro, um caractere de nova linha ou um avanço de formulário. Cada classe de caracteres localiza a um único caractere na String que estamos tentando localizar com a expressão regular. Caractere

Correspondências

Caractere

Correspondências

\d

qualquer dígito

\D

qualquer não dígito

\w

qualquer caractere de palavra

\W

qualquer caractere não palavra

\s

qualquer caractere de espaço em branco

\S

qualquer caractere não espaço em branco

Figura 16.19 | Classes predefinidas de caractere.

As expressões regulares não estão limitadas a essas classes predefinidas de caractere. As expressões empregam vários operadores e outras formas de notação para localizar padrões complexos. Examinamos várias dessas técnicas no aplicativo das figuras 16.20 e 16.21 que valida a entrada de usuário por meio de expressões regulares. [Nota: Esse aplicativo não é projetado para localizar todas as possíveis entradas de usuário válidas]. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

// Figura 16.20: ValidateInput.java // Valida informações de usuário utilizando expressões regulares. public class ValidateInput { // valida o primeiro nome public static boolean validateFirstName( String firstName ) { return firstName.matches( "[A­Z][a­zA­Z]*" ); } // fim do método validateFirstName // valida o sobrenome public static boolean validateLastName( String lastName ) { return lastName.matches( "[a­zA­z]+([ '­][a­zA­Z]+)*" ); } // fim do método validateLastName // valida o endereço public static boolean validateAddress( String address ) { return address.matches(

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48

16.7  Expressões regulares, classe Pattern e classe Matcher "\\d+\\s+([a-zA-Z]+|[a-zA-Z]+\\s[a-zA-Z]+)" ); } // fim do método validateAddress // valida cidade public static boolean validateCity( String city ) { return city.matches( "([a-zA-Z]+|[a-zA-Z]+\\s[a-zA-Z]+)" ); } // fim do método validateCity // valida estado public static boolean validateState( String state ) { return state.matches( "([a-zA-Z]+|[a-zA-Z]+\\s[a-zA-Z]+)" ) ; } // fim do método validateState // valida CEP public static boolean validateZip( String zip ) { return zip.matches( "\\d{5}" ); } // fim do método validateZip // valida telefone public static boolean validatePhone( String phone ) { return phone.matches( "[1-9]\\d{2}-[1-9]\\d{2}-\\d{4}" ); } // fim do método validatePhone } // fim da classe ValidateInput

Figura 16.20  |  Validando informações de usuário com expressões regulares.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

// Figura 16.21: Validate.java // Valida informações de usuário utilizando expressões regulares. import java.util.Scanner; public class Validate { public static void main( String[] args ) { // obtém entrada de usuário Scanner scanner = new Scanner( System.in ); System.out.println( "Please enter first name:" ); String firstName = scanner.nextLine(); System.out.println( "Please enter last name:" ); String lastName = scanner.nextLine(); System.out.println( "Please enter address:" ); String address = scanner.nextLine(); System.out.println( "Please enter city:" ); String city = scanner.nextLine(); System.out.println( "Please enter state:" ); String state = scanner.nextLine(); System.out.println( "Please enter zip:" ); String zip = scanner.nextLine(); System.out.println( "Please enter phone:" ); String phone = scanner.nextLine(); // valida entrada de usuário e exibe mensagem de erro System.out.println( "\nValidate Result:" ); if ( !ValidateInput.validateFirstName( firstName ) ) System.out.println( "Invalid first name" ); else if ( !ValidateInput.validateLastName( lastName ) ) System.out.println( "Invalid last name" ); else if ( !ValidateInput.validateAddress( address ) ) System.out.println( "Invalid address" ); else if ( !ValidateInput.validateCity( city ) ) System.out.println( "Invalid city" );

537

538 37 38 39 40 41 42 43 44 45 46

Capítulo 16  Strings, caracteres e expressões regulares else if ( !ValidateInput.validateState( state ) ) System.out.println( "Invalid state" ); else if ( !ValidateInput.validateZip( zip ) ) System.out.println( "Invalid zip code" ); else if ( !ValidateInput.validatePhone( phone ) ) System.out.println( "Invalid phone number" ); else System.out.println( "Valid input. Thank you." ); } // fim de main } // fim da classe Validate

Please enter first name: Jane Please enter last name: Doe Please enter address: 123 Some Street Please enter city: Some City Please enter state: SS Please enter zip: 123 Please enter phone: 123-456-7890 Validate Result: Invalid zip code

Please enter first name: Jane Please enter last name: Doe Please enter address: 123 Some Street Please enter city: Some City Please enter state: SS Please enter zip: 12345 Please enter phone: 123-456-7890 Validate Result: Valid input. Thank you.

Figura 16.21  |  Insere e valida os dados de usuário utilizando a classe ValidateInput.

A Figura 16.20 valida a entrada de usuário. A linha 9 valida o primeiro nome. Para localizar um conjunto de caracteres que não tem uma classe de caracteres predefinida, utilize os colchetes, []. Por exemplo, o padrão "[aeiou]" localiza um único caractere que é uma vogal. Os intervalos de caractere são representados colocando um traço (-) entre dois caracteres. No exemplo, "[A-Z]" identifica uma única letra maiúscula. Se o primeiro caractere entre colchetes for "^", a expressão aceitará qualquer caractere diferente desses indicados. Entretanto, é importante observar que "[^Z]" não é o mesmo que "[A-Y]", que localiza todas as letras maiúsculas no intervalo A–Y — "[^Z]" localiza qualquer caractere diferente da letra Z maiúscula, incluindo as letras minúsculas e não letras como o caractere de nova linha. Os intervalos nas classes de caractere são determinados pelos valores inteiros das letras. Neste exemplo, "[A-Za-z]" localiza todas as letras maiúsculas e minúsculas. O intervalo "[A-z]" localiza todas as letras e também aqueles caracteres (como [ e \) com um valor inteiro entre o Z maiúsculo e o a minúsculo (para obter informações adicionais sobre valores inteiros de caracteres, consulte o Apêndice B). Como as classes predefinidas de caractere, as classes de caractere delimitadas por colchetes localizam um único caractere no objeto de pesquisa.



16.7  Expressões regulares, classe Pattern e classe Matcher

539

Na linha 9, o asterisco depois da segunda classe de caracteres indica que pode haver correspondência com qualquer número de letras. Em geral, quando o operador de expressão regular "*" aparece em uma expressão regular, o aplicativo tenta localizar a zero ou mais ocorrências da subexpressão imediatamente anterior a "*". O operador "+" tenta identificar uma ou mais ocorrências da subexpressão imediatamente anterior "+". Assim tanto "A*" como "A+" localizará "AAA" ou "A", mas apenas "A*" identificará uma string vazia. Se o método validateFirstName retorna true (linha 29 da Figura 16.21), o aplicativo tenta validar o sobrenome (linha 31) chamando validateLastName (linhas 13–16 da Figura 16.20). A expressão regular para validar o sobrenome localiza qualquer número de letras separadas por espaços, apóstrofos ou hífens. A linha 33 da Figura 16.21 chama o método validateAddress (linhas 19–23 da Figura 16.20) para validar o endereço. A primeira classe de caracteres localiza qualquer dígito uma ou mais vezes (\\d+). Observe que os dois caracteres \ são utilizados, pois \ normalmente inicia uma sequência de escape em uma string. Assim \\ Em seguida, pesquisamos um ou mais caracteres de espaço em branco (\\s+). O caractere "|" localiza a expressão à sua esquerda ou direita. Por exemplo, "Hi (John|Jane)" localiza tanto a "Hi John" como "Hi Jane". Os parênteses são utilizados para agrupar partes da expressão regular. Neste exemplo, o lado esquerdo de | localiza uma única palavra e, o direito, a duas palavras separadas por qualquer quantidade de espaço em branco. Assim o endereço deve conter um número seguido por uma ou duas palavras. Portanto, "10 Broadway" e "10 Main Street" são ambos endereços válidos nesse exemplo. Os métodos city (linhas 26–29 da Figura 16.20) e state (linhas 32–35 da Figura 16.20) também procuram qualquer palavra de pelo menos um caractere ou, alternativamente, quaisquer duas palavras de pelo menos um caractere se as palavras forem separadas por um único espaço, assim ambos Waltham e West Newton devem corresponder.

Quantificadores Os sinais de asterisco (*) e de adição (+) são formalmente chamados de quantificadores. A Figura 16.22 lista todos os quantificadores. Já discutimos como os quantificadores de asterisco (*) e sinal de adição (+) funcionam. Todos os quantificadores afetam apenas a subexpressão imediatamente anterior ao quantificador. O ponto de interrogação quantificador (?) localiza zero ou uma ocorrência da expressão que ele quantifica. Um conjunto de chaves contendo um número ({n}) localiza exatamente n ocorrências da expressão que ele quantifica. Demonstramos esse quantificador para validar o CEP da Figura 16.20 na linha 40. Incluir uma vírgula depois do número incluído entre chaves localiza chaves pelo menos n ocorrências da expressão quantificada. O conjunto de chaves que contém dois números ({n,m}), localiza entre n e m ocorrências da expressão que ele qualifica. Os quantificadores podem ser aplicados a padrões entre parênteses para criar expressões regulares mais complexas. Quantificador

Correspondências

*

Localiza a zero ou mais ocorrências do padrão.

+

Localiza a uma ou mais ocorrências do padrão.

?

Localiza a zero ou uma ocorrência do padrão.

{n}

Localiza exatamente n ocorrências.

{n,}

Localiza pelo menos n ocorrências.

{n,

Localiza entre n e m (inclusive) ocorrências.

m}

Figura 16.22  |  Quantificadores utilizados em expressões regulares.

Todos os quantificadores são gananciosos. Isso significa que eles identificarão quantas ocorrências eles puderem contanto que a correspondência ainda seja bem-sucedida. Entretanto, se qualquer um desses quantificadores for seguido por um ponto de interrogação (?), o quantificador se tornará relutante (às vezes chamado de preguiçoso). Então ele reconhecerá o mínimo de ocorrências possível contanto que a correspondência ainda seja bem-sucedida. O CEP (linha 40 na Figura 16.20) localiza cinco vezes um dígito. Essa expressão regular utiliza a classe de caracteres de dígito e um quantificador com o dígito 5 entre chaves. O número de telefone (linha 46 na Figura 16.20) combina com três dígitos (o primeiro não pode ser zero) seguidos por um traço seguido por mais três dígitos (novamente o primeiro não pode ser zero) seguido por mais quatro dígitos. O método String matches verifica se uma string inteira se adapta a uma expressão regular. Por exemplo, queremos aceitar "Smith" como um sobrenome, mas não "9@Smith#". Se ao menos uma substring corresponder à expressão regular, o método matches retorna false.

540

Capítulo 16  Strings, caracteres e expressões regulares

Substituindo substrings e dividindo strings Às vezes é útil substituir partes de uma string ou dividir uma string em partes. Para esse propósito, a classe String fornece os métodos replaceAll, replaceFirst e split. Esses métodos são demonstrados na Figura 16.23. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

// Figura 16.23: RegexSubstitution.Java // Métodos string ReplaceFirst, replaceAll e split. import java.util.Arrays; public class RegexSubstitution { public static void main( String[] args ) { String firstString = "This sentence ends in 5 stars *****"; String secondString = "1, 2, 3, 4, 5, 6, 7, 8"; System.out.printf( "Original String 1: %s\n", firstString ); // substitui '*' por '^' firstString = firstString.replaceAll( "\\*", "^" ); System.out.printf( "^ substituted for *: %s\n", firstString ); // substitui asteriscos por circunflexos firstString = firstString.replaceAll( "stars", "carets" ); System.out.printf( "\"carets\" substituted for \"stars\": %s\n", firstString ); // substitui palavras por 'palavra' System.out.printf( "Every word replaced by \"word\": %s\n\n", firstString.replaceAll( "\\w+", "word" ) ); System.out.printf( "Original String 2: %s\n", secondString ); // substitui primeiros três dígitos pelo 'dígito' for ( int i = 0; i < 3; i++ ) secondString = secondString.replaceFirst( "\\d", "digit" ); System.out.printf( "First 3 digits replaced by \"digit\" : %s\n", secondString ); System.out.print( "String split at commas: " ); String[] results = secondString.split( ",\\s*" ); // divide em vírgulas System.out.println( Arrays.toString( results ) ); } // fim de main } // fim da classe RegexSubstitution

Original String 1: This sentence ends in 5 stars ***** ^ substituted for *: This sentence ends in 5 stars ^^^^^ "carets" substituted for "stars": This sentence ends in 5 carets ^^^^^ Every word replaced by "word": word word word word word word ^^^^^ Original String 2: 1, 2, 3, 4, 5, 6, 7, 8 First 3 digits replaced by "digit" : digit, digit, digit, 4, 5, 6, 7, 8 String split at commas: ["digit", "digit", "digit", "4", "5", "6", "7", "8"]

Figura 16.23  |  Métodos String

replaceFirst, replaceAll

e split.

O método replaceAll substitui texto em uma String com texto novo (o segundo argumento) onde quer que a string original localize uma expressão regular (o primeiro argumento). A linha 15 substitui cada instância de "*" em firstString por "^". Observe que a expressão regular ("\\*") precede o caractere * com duas barras invertidas. Normalmente, * é um quantificador que indica que uma expressão regular deve corresponder a qualquer número de ocorrências de um padrão anterior. Entretanto, na linha 15, queremos combinar todas as ocorrências do caractere literal * — para fazer isso, devemos escapar o caractere * pelo caractere \. Escapar um caractere de expressão regular especial com \ instrui o mecanismo de correspondência a localizar o caractere real. Visto que a expressão é armazenada em uma String Java e \ é um caractere especial em Strings Java, devemos incluir um \adicional. Então a String "\\*" do Java representa



16.7  Expressões regulares, classe Pattern e classe Matcher

541

o padrão de expressão regular \* que identifica um único caractere * na string de pesquisa. Na linha 20, cada correspondência da expressão regular "stars" em firstString é substituída por "carets". A linha 27 utiliza replaceAll para substituir todas as palavras na string por "word". O método replaceFirst (linha 33) substitui a primeira ocorrência de uma correspondência de padrão. Strings do Java são imutáveis, portanto o método replaceFirst retorna uma nova String em que os caracteres apropriados foram substituídos. Essa linha aceita a String original e a substitui pela String retornada por replaceFirst. Iterando três vezes substituímos as três primeiras instâncias de um dígito (\d) em secondString pelo texto "digit". O método split divide uma String em várias substrings. A original é dividida em qualquer localização que corresponde a uma expressão regular especificada. O método split retorna um array de Strings que contém as substrings entre as correspondências da expressão regular. Na linha 39, utilizamos o método split para tokenizar uma String de inteiros separados por vírgulas. O argumento é a expressão regular que corresponde ao delimitador. Nesse caso, utilizamos a expressão regular ",\\s*" para separar as substrings onde quer que ocorra uma vírgula. Correspondendo quaisquer caracteres espaço em branco, eliminamos os espaços extras das substrings resultantes. Observe que as vírgulas e os caracteres de espaço em branco não são retornados como parte das substrings. Novamente, observe que a String ",\\s*" do Java representa a expressão regular ,\s*. A linha 40 utiliza o método Arrays toString para exibir os conteúdos do array results em colchetes e separado por vírgulas.

Classes Pattern e Matcher Além das capacidades de expressão regular da classe String, o Java fornece outras classes no pacote java.util.regex que ajudam os desenvolvedores a manipular expressões regulares. A classe Pattern representa uma expressão regular. A classe Matcher contém tanto um padrão de expressão regular como uma CharSequence na qual procurar o padrão. CharSequence (pacote java.lang) é uma interface que permite acesso de leitura a uma sequência de caracteres. A interface exige que os métodos charAt, length, subSequence e toString sejam declarados. Tanto String como StringBuilder implementam a interface CharSequence, portanto uma instância de qualquer dessas classes pode ser utilizada com a classe Matcher. Erro comum de programação 16.4 Uma expressão regular pode ser testada contra um objeto de qualquer classe que implemente a interface CharSequence, mas a expressão regular deve ser uma String. Tentar criar uma expressão regular como um StringBuilder é um erro.

Se uma expressão regular vai ser utilizada apenas uma vez, o método static Pattern matches pode ser utilizado. Esse método aceita uma String que especifica a expressão regular e um CharSequence em que realiza a correspondência. Esse método retorna um boolean que indica se o objeto de pesquisa (o segundo argumento) corresponde à expressão regular. Se uma expressão regular vai ser utilizada mais de uma vez (em um loop por exemplo), é mais eficiente utilizar o método static Pattern compile para criar um objeto Pattern específico para essa expressão regular. Esse método recebe uma String que representa o padrão e retorna um novo objeto Pattern, que então pode ser utilizado para chamar o método matcher. Esse método recebe um Char­ Sequence para procurar e retornar um objeto Matcher. Matcher fornece método matches, que realiza a mesma tarefa que o método Pattern matches, mas não recebe nenhum argumento — o padrão de pesquisa e o objeto de pesquisa são encapsulados no objeto Matcher. A classe Matcher fornece outros métodos, incluindo find, lookingAt-, replaceFirst e replaceAll. A Figura 16.24 apresenta um exemplo simples que emprega expressões regulares. Esse programa identifica aniversários em uma expressão regular. A expressão identifica somente aniversários que não ocorram em abril e que pertençam às pessoas cujos nomes iniciam com "J". As linhas 11–12 criam um Pattern invocando o método static Pattern compile. O caractere de ponto "." na expressão regular (linha 12) procura qualquer caractere único exceto um caractere de nova linha. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

// Figura 16.24: RegexMatches.java // as classes Pattern e Matcher. import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegexMatches { public static void main( String[] args ) { // cria expressão regular Pattern expression = Pattern.compile( "J.*\\d[0-35-9]-\\d\\d-\\d\\d" ); String string1 = "Jane's Birthday is 05-12-75\n" + "Dave's Birthday is 11-04-68\n" +

542 16 17 18 19 20 21 22 23 24 25

Capítulo 16

Strings, caracteres e expressões regulares

"John's Birthday is 04­28­73\n" + "Joe's Birthday is 12­17­77"; // corresponde expressão regular à string e imprime as correspondências Matcher matcher = expression.matcher( string1 ); while ( matcher.find() ) System.out.println( matcher.group() ); } // fim de main } // fim da classe RegexMatches

Jane's Birthday is 05­12­75 Joe's Birthday is 12­17­77

Figura 16.24 | Classes Pattern e Matcher.

A linha 20 cria o objeto Matcher para a expressão regular compilada e a sequência de correspondência (string1). As linhas 22–23 utilizam um loop while para iterar pela String. A linha 22 utiliza o método Matcher find para tentar corresponder uma parte do objeto de pesquisa ao padrão de pesquisa. Cada chamada para esse método inicia no ponto em que a última chamada terminou, então múltiplas correspondências podem ser localizadas. O método Matcher lookingAt executa da mesma maneira, exceto que sempre desde o início do objeto de pesquisa e sempre localizará a primeira correspondência se houver uma.

Erro comum de programação 16.5 O método matches (da classe String, Pattern ou Matcher) retornará true somente se o objeto de pesquisa inteiro corresponder à expressão regular. Os métodos find e lookingAt (da classe Matcher) retornarão true se uma parte do objeto de pesquisa corresponder à expressão regular.

A linha 23 utiliza o método Matcher group, que retorna a String do objeto de pesquisa que corresponde ao padrão de pesquisa. A String que é retornada é aquela que correspondeu da última vez por uma chamada a find ou lookingAt. A saída na Figura 16.24 mostra as duas correspondências que foram encontradas em string1. Para obter mais informações sobre expressões regulares, visite o nosso Regular Expressions Resource Center em www.deitel.com/ regularexpressions/.

16.8 Conclusão Neste capítulo, você aprendeu mais sobre os métodos String para selecionar partes de Strings e manipular Strings. Você também aprendeu sobre a classe Character e alguns métodos que ela declara para tratar chars. O capítulo também discutiu as capacidades da classe StringBuilder de criar Strings. O fim do capítulo discutiu as expressões regulares, as quais fornecem uma capacidade poderosa de pesquisar e corresponder partes de Strings que se ajustam a um padrão particular. No próximo capítulo, você aprenderá sobre o processamento de arquivo, incluindo como os dados persistentes são armazenados e recuperados.

Resumo Seção 16.2 Fundamentos de caracteres e strings • O valor de um literal de caractere é seu valor de tipo inteiro no Unicode. As strings podem incluir letras, dígitos e caracteres especiais como +, –, *, / e $. Uma string no Java é um objeto da classe String. Os literais de String são frequentemente referidos como objetos e escritos entre aspas duplas em um programa.

Seção 16.3 Classe String • • • •

Objetos String são imutáveis — depois que são criados, seu conteúdo de caracteres não pode ser alterado. O método string length retorna o número de caracteres em uma String. O método String charAt retorna o caractere em uma posição específica. O método string equals testa a igualdade. O método retorna true se o conteúdo das Strings forem iguais, false, caso contrário. O método equals utiliza uma comparação lexicográfica para Strings. • Quando valores de tipo de dados primitivo são comparados com ==, o resultado é true se ambos os valores forem idênticos. Quando referências são comparadas com ==, o resultado será true se ambas referenciam o mesmo objeto. • O Java trata todos os literais de string com o mesmo conteúdo como um único objeto String.



Resumo

543

• O método String equalsIgnoreCase realiza uma comparação de string que não diferencia maiúsculas e minúsculas. • O método string compareTo utiliza uma comparação lexicográfica e retorna 0 se as Strings forem iguais, um número negativo se a string que chama compareTo for menor do que o argumento String e um número positivo se a string que chama compareTo for maior do que o argumento String. • O método String regionMatches compara partes de duas strings quanto à igualdade. • O método String startsWith determina se uma string inicia com os caracteres especificados. O método String endsWith determina se uma string termina com os caracteres especificados. • O método String indexOf localiza a primeira ocorrência de um caractere ou uma substring em uma string. O método String lastIndexOf localiza a última ocorrência de um caractere ou uma substring em uma string. • O método String substring copia e retorna parte de um objeto string existente. • O método String concat concatena dois objetos string e retorna um novo objeto string. • O método String replace retorna um novo objeto string que substitui cada ocorrência em uma String do seu primeiro argumento de caractere pelo seu segundo argumento de caractere. • String toUpperCase retorna uma nova string com letras maiúsculas nas posições em que a string original tinha letras minúsculas. O método String toLowerCase retorna uma nova string com letras minúsculas nas posições em que a string original tinha letras maiúsculas. • O método String trim retorna um novo objeto string em que todos os caracteres de espaço em branco (por exemplo, espaços, nova linha e tabulações) foram removidos do início ao fim de uma string. • O método String toCharArray retorna um array char contendo uma cópia dos caracteres da string. • O método static valueOf da classe String retorna seu argumento convertido em uma string.

Seção 16.4  Classe StringBuilder • A classe StringBuilder fornece construtores que permitem que StringBuilders sejam inicializados sem caracteres e tenham uma capacidade inicial de 16 caracteres, sem caracteres e uma capacidade inicial especificada no argumento de inteiro; ou com uma cópia dos caracteres do argumento String e uma capacidade inicial que é o número de caracteres no argumento de String mais 16. • O método StringBuilder length retorna o número de caracteres atualmente armazenado em um StringBuilder. O método StringBuilder capacity retorna o número de caracteres que pode ser armazenado em um StringBuilder sem alocar mais memória. • O método StringBuilder ensureCapacity assegura que um StringBuilder tem pelo menos a capacidade especificada. O método StringBuilder setLength aumenta ou diminui o comprimento de um StringBuilder. • O método StringBuilder charAt retorna o caractere no índice especificado. O método StringBuilder setCharAt configura o caractere na posição especificada. O método StringBuilder getChars copia caracteres do StringBuilder no array de caractere passado como um argumento. • Os métodos append de StringBuilder sobrecarregados adicionam o array de caracteres de tipo primitivo, valores String, Object ou CharSequence ao fim de um StringBuilder. • Os métodos insert sobrecarregados de StringBuilder inserem tipo primitivo, array de caracteres e valores String, Object ou CharSequence em qualquer posição em um StringBuilder.

Seção 16.5  Classe Character • O método Character isDefined determina se um caractere está no conjunto de caracteres Unicode. • O método Character isDigit determina se um caractere é um dígito Unicode definido. • O método caractere isJavaIdentifierStart determina se um caractere pode ser utilizado como o primeiro caractere de um identificador Java. O método Character isJavaIdentifierPart determina se um caractere pode ser utilizado em um identificador. • O método caractere isLetter determina se um caractere é uma letra. O método Character isLetterOrDigit determina se um caractere é uma letra ou um dígito. • O método Character isLowerCase determina se um caractere é uma letra minúscula. O método Character isUpperCase determina se um caractere é uma letra maiúscula. • O método Character toUpperCase converte um caractere em seu equivalente em letras maiúsculas. O método Character toLowerCase converte um caractere em seu equivalente em letras minúsculas. • O método Character digit converte seu argumento caractere em um inteiro no sistema de números especificado por seu argumento inteiro radix. O método Character forDigit converte seu argumento inteiro digit em um caractere no sistema de números especificado por seu argumento inteiro radix. • O método caractere charValue retorna o char armazenado em um objeto Character. O método Character toString retorna uma representação String de um Character.

Seção 16.6  Tokenização de Strings • O método split da classe String tokeniza uma String com base no delimitador especificado como um argumento e retorna um array de Strings que contém o token.

544

Capítulo 16  Strings, caracteres e expressões regulares

Seção 16.7  Expressões regulares, classe Pattern e classe Matcher • Expressões regulares são sequências de caracteres e símbolos que definem um conjunto de strings. Elas são úteis para validar entrada e assegurar que os dados estão em um formato particular. • O método String matches recebe uma string que especifica a expressão regular e corresponde ao conteúdo do objeto String em que é chamado com a expressão regular. O método retorna um boolean indicando se a correspondência foi ou não bem-sucedida. • Uma classe de caracteres é uma sequência de escape que representa um grupo de caracteres. Cada classe de caracteres localiza um único caractere na string a que estamos tentando localizar com a expressão regular. • Um caractere de palavra (\w) é qualquer letra (em maiúsculas ou minúsculas), qualquer dígito ou o caractere sublinhado. • Um caractere espaço em branco (\s) é um espaço, uma tabulação, um retorno de carro, uma nova linha ou um avanço de formulário. • Um dígito (\d) é qualquer caractere numérico. • Para localizar um conjunto de caracteres que não tem uma classe de caracteres predefinida, utilize os colchetes, []. Os intervalos podem ser representados colocando-se um traço (-) entre dois caracteres. Se o primeiro caractere entre colchetes for "^", a expressão aceitará qualquer caractere diferente desses indicados. • Quando o operador de expressão regular "*" aparece em uma expressão regular, o programa tenta combinar zero ou mais ocorrências da subexpressão que imediatamente precede o "*". • O operador "+" tenta localizar uma ou mais ocorrências da subexpressão que o precede. • O caractere "|" permite uma correspondência da expressão a sua esquerda ou direita. • Parênteses ( ) são utilizados para agrupar partes da expressão regular. • Os sinais de asterisco (*) e de adição (+) são formalmente chamados de quantificadores. • Um quantificador afeta apenas a subexpressão que imediatamente o precede. • O ponto de interrogação quantificador (?) localiza zero ou uma ocorrência da expressão que ele quantifica. • Um conjunto de chaves contendo um número ({n}) localiza exatamente n ocorrências da expressão que ele quantifica. Incluir uma vírgula depois do número entre chaves corresponde pelo menos a n ocorrências. • Um conjunto de chaves que contém dois números ({n,m}), localiza entre n e m ocorrências da expressão que ele qualifica. • Os quantificadores são gulosos — eles localizarão quantas ocorrências puderem contanto que a correspondência seja bem-sucedida. Se um quantificador for seguido por um ponto de interrogação (?), o quantificador torna-se relutante, identificando o menor número de ocorrências possível contanto que a correspondência seja bem-sucedida. • O método String replaceAll substitui o texto em uma string pelo novo texto (o segundo argumento) onde quer que a string original coincida com uma expressão regular (o primeiro argumento). • Escapar um caractere especial de expressão regular com uma \ instrui o mecanismo de correspondência de expressão regular a localizar o caractere real, em oposição ao que ele representa em uma expressão regular. • O método string replaceFirst substitui a primeira ocorrência de uma correspondência de padrão e retorna uma nova string na qual os caracteres apropriados foram substituídos. • O método string split divide uma string em uma substring em qualquer localização que corresponda a uma expressão regular especificada e retorna um array das substrings. • A classe Pattern representa uma expressão regular. • A classe Matcher contém um padrão de expressão regular e um CharSequence na qual pesquisar. • CharSequence é uma interface que permite acesso de leitura a uma sequência de caracteres. Tanto a classe String como StringBuilder implementam essa interface, portanto, podem ser utilizadas com a classe Matcher. • Se uma expressão regular vai ser utilizada apenas uma vez, o método Pattern matches estático aceita uma string que especifica a expressão regular e uma CharSequence na qual realizar a correspondência. Esse método retorna um boolean que indica se o objeto de pesquisa corresponde à expressão regular. • Se uma expressão regular vai ser utilizada mais de uma vez, é mais eficiente utilizar o método Pattern compile para criar um objeto Pattern específico para essa expressão regular. Esse método recebe uma string que representa o padrão e retorna um novo objeto Pattern. • O método Pattern matcher recebe um CharSequence para procurar e retornar um objeto Matcher. O método Matcher matches realiza a mesma tarefa que método Pattern matches, mas sem argumentos. • O método Matcher find tenta localizar uma parte objeto de pesquisa para o padrão de pesquisa. Cada chamada para esse método inicia no ponto em que a última chamada terminou, assim múltiplas correspondências podem ser localizadas. • O método Matcher lookingAt executa o mesmo que o find, exceto que sempre inicia desde o início do objeto de pesquisa e sempre localizará a primeira correspondência se houver alguma. • O método Matcher group retorna a string do objeto de pesquisa que reconhece o padrão de pesquisa. A string retornada é aquela que correspondeu da última vez por uma chamada a find ou lookingAt.



Terminologia

545

Terminologia append, método da classe StringBuilder, 529

isDefined, método da classe Character, 533

capacity, método da classe StringBuilder,

isDigit, método da classe Character, 533

527 caractere especial, 516 caractere palavra (expressões regulares), 536 charAt, método da classe String, 518 charAt, método da classe StringBuilder, 528 CharSequence, interface, 541 charValue, método da classe Character, 534 classe de caractere predefinida (expressões regulares), 536 comparação lexicográfica, 520 compile, método da classe Pattern, 541 concat, método da classe String, 524 delete, método da classe StringBuilder, 530 deleteCharAt, método da classe StringBuilder, 530 delimitador para tokens, 535 digit, método da classe Character, 533 endsWith, método da classe String, 521 ensureCapacity, método da classe StringBuilder, 527 expressão regular, 536 find, método da classe Matcher, 541 forDigit, método da classe Character, 533 getChars, método da classe String, 518 getChars, método da classe StringBuilder, 528 group, método da classe Matcher, 542 indexOf, método da classe String, 522 insert, método da classe StringBuilder, 530

isJavaIdentifierPart, método da classe Character, 533 isJavaIdentifierStart, método da classe Character, 533 isLetter, método da classe Character, 533 isLetterOrDigit, método da classe Character, 533 isLowerCase, método da classe Character,

533

isUpperCase, método da classe Character,

533

lastIndexOf, método da classe String, 522 length, método da classe String, 518 length, método da classe StringBuilder, 527 literal de caractere, 516 literal de string, 516 lookingAt, método da classe Matcher, 541 Matcher, classe, 541 matcher, método da classe Pattern, 541 matches, método da classe Matcher, 541 matches, método da classe Pattern, 541 matches, método da classe String, 536 Pattern, classe, 541 quantificadores utilizados em expressões regulares, 539 quantificador ganancioso (expressões regulares, 539 quantificador preguiçoso (expressões regulares), 539

quantificador relutante (expressões regulares), 539 radical (base) de um número, 533, 534 regionMatches, método da classe String, 519 replaceAll, método da classe Matcher, 541 replaceAll, método da classe String, 540 replaceFirst, método da classe Matcher, 541 replaceFirst, método da classe String, 540 reverse, método da classe StringBuilder, 528 setCharAt, método da classe StringBuilder, 528 split, método da classe String, 535 startsWith, método da classe String, 521 String, objeto, imutável, 517 StringBuffer, classe, 526 StringBuilder, classe, 526 StringIndexOutOfBoundsException, classe, 523 string vazia, 517 toCharArray, método da classe String, 525 token de uma String, 535 toLowerCase, método da classe Character, 533 toLowerCase, método da classe String, 525 toUpperCase, método da classe Character, 533 toUpperCase, método da classe String, 525 trim, método da classe String, 525 Unicode, conjunto de caracteres, 516 valueOf, método da classe String, 525

Exercícios de autorrevisão 16.1 Determine se cada uma das seguintes afirmações é verdadeira ou falsa. Se falsa, explique por quê. a) Quando os objetos String são comparados utilizando ==, o resultado é true se as Strings contiverem os mesmos valores. b) Uma String pode ser modificada depois de criada.

16.2 Para cada um dos seguintes, escreva uma única instrução que realiza a tarefa indicada: a) Compare a string em s1 com string em s2 quanto à igualdade de conteúdo. b) Acrescente a string s2 à string s1, utilizando +=. c) Determine o comprimento da string em s1.

Respostas dos exercícios de autorrevisão 16.1

a) Falsa. Os objetos string são comparados utilizando o operador == para determinar se eles são o mesmo objeto na memória. b) Falsa. Objetos String são imutáveis e não podem ser modificados depois de criados. Objetos StringBuilder podem ser modificados depois de criados.

16.2 a) s1.equals( b) s1 c)

s2 )

+= s2;

s1.length()

Exercícios 16.3 (Comparando Strings) Escreva um aplicativo que utiliza o método String compareTo para comparar duas entradas de strings pelo usuário. Crie uma saída informando se a primeira string é menor que, igual a ou maior que a segunda.

546

Capítulo 16  Strings, caracteres e expressões regulares

16.4 (Comparando partes de Strings) Escreva um aplicativo que utiliza o método String regionMatches para comparar duas entradas de strings pelo usuário. O aplicativo deve inserir o número de caracteres que será comparado e o índice inicial da comparação. O aplicativo deve declarar se as strings são iguais. Ignore a distinção entre maiúsculas e minúsculas dos caracteres ao realizar a comparação.

16.5 (Sentenças aleatórias) Escreva um aplicativo que utiliza geração de números aleatórios para criar frases. Utilize quatro arrays de strings

chamados article, noun, verb e preposition. Crie uma frase selecionando uma palavra aleatoriamente de cada array na seguinte ordem:

article, noun, verb, preposition, article e noun. À medida que cada palavra for selecionada, concatene-a as primeiras palavras na frase. As

palavras devem ser separadas por espaços. Quando a frase final for enviada para saída, ela deve iniciar com uma letra maiúscula e terminar com um ponto. O aplicativo deve gerar e exibir 20 frases.



O array de artigos deve conter os artigos "the", "a", "one", "some" e "any"; o array de substantivos deve conter os substantivos "boy", "girl",

"dog", "town" e "car"; o array de verbos deve conter os verbos "drove", "jumped", "ran", "walked" e "skipped"; o array de preposições deve

conter as preposições "to", "from", "over", "under" e "on".

16.6 (Limericks) Um limerick é um poema humorístico de cinco versos em que a primeira e a segunda linha rimam com a quinta, e a terceira linha

rima com a quarta. Utilizando técnicas semelhantes àquelas desenvolvidas no Exercício 16.5, escreva um aplicativo Java que produz limericks aleatórios. Polir esse aplicativo para produzir bons limericks é um problema desafiador, mas o resultado vale o esforço!

16.7 (Latim de porco) Escreva um aplicativo que codifica frases da língua inglesa em latim de porco. O Pig Latin é uma forma de linguagem codificada. Há muitos métodos diferentes para formar frases em Pig Latin. Para simplificar, utilize o seguinte algoritmo:



Para formar uma frase em latim de porco a partir de uma frase em inglês, tokenize a frase em palavras com o método String split. Para traduzir cada palavra inglesa em uma palavra do latim de porco, coloque a primeira letra da palavra inglesa no fim da palavra e adicione as letras “ay”. Assim, a palavra “jump” torna-se “umpjay,” a palavra “the” torna-se “hetay,” e a palavra “computer” torna-se “omputercay.” Os espaços entre as palavras permanecem iguais. Assuma o seguinte: a frase inglesa consiste em palavras separadas por espaços, não há nenhuma marcação de pontuação e todas as palavras têm duas ou mais letras. O método printLatinWord deve exibir cada palavra. Cada token é passado para o método printLatinWord para imprimir a palavra em latim de porco (Pig Latin). Permita que o usuário insira a frase. Continue exibindo todas as frases convertidas em uma área de texto.

16.8 (Tokenizando números de telefone) Escreva um aplicativo que insere um número de telefone como uma string na forma (555) 555-5555. O aplicativo deve utilizar o método String split para extrair o código de área como um token, os três primeiros dígitos do número de telefone como um segundo token e os últimos quatro dígitos do número de telefone como um terceiro token. Os sete dígitos do número de telefone devem ser concatenados em uma string. O código de área e o número de telefone devem ser impressos. Lembre-se de que você terá de alterar caracteres delimitadores durante o processo de tokenização.

16.9 (Exibindo uma oração com suas palavras invertidas) Escreva um aplicativo que introduz uma linha do texto, tokeniza a linha com o método String split e gera saída do token em ordem inversa. Utilize caracteres espaço em branco como delimitadores.

16.10 (Exibindo Strings em letras maiúsculas e minúsculas) Escreva um aplicativo que insere uma linha de texto e gera duas vezes a saída do texto — uma vez em letras maiúsculas e uma vez em letras minúsculas.

16.11 (Pesquisando Strings) Escreva um aplicativo que insere uma linha de texto e um caractere de pesquisa e utiliza o método String indexOf para determinar o número de ocorrências do caractere no texto.

16.12 (Pesquisando Strings) Escreva um aplicativo com base no aplicativo do Exercício 16.11 que insere uma linha de texto e utiliza o método String indexOf para determinar o número total de ocorrências de cada letra do alfabeto no texto. As letras minúsculas e maiúsculas devem

ser contadas juntas. Armazene os totais para cada letra em um array e imprima os valores em formato tabular depois que os totais foram determinados.

16.13 (Tokenizando e comparando Strings) Escreva um aplicativo que lê uma linha de texto, tokeniza essa linha utilizando caracteres de espaço em branco como delimitadores e gera a saída apenas daquelas palavras que iniciam com a letra “b”.

16.14 (Tokenizando e comparando Strings) Escreva um aplicativo que lê uma linha de texto, tokeniza essa linha utilizando caracteres de espaço em branco como delimitadores e gera a saída apenas daquelas palavras que terminem com as letras “ED”.

16.15 (Convertendo valores int em caracteres) Escreva um aplicativo que introduz um código inteiro de um caractere e exibe o caractere correspondente. Modifique esse aplicativo de modo que ele gere todos os possíveis códigos de três dígitos no intervalo de 000 a 255 e tente imprimir os caracteres correspondentes.

16.16 (Definindo seus próprios métodos String) Escreva suas próprias versões de métodos de pesquisa String indexOf e lastIndexOf. 16.17 (Criando Strings de três letras de uma palavra de cinco letras) Escreva um aplicativo que lê uma palavra de cinco letras do usuário e

produz cada possível string de três letras que pode ser derivada das letras dessa palavra. Por exemplo, as palavras de três letras produzidas a partir da palavra “bathe” incluem “ate,” “bat,” “bet,” “tab,” “hat,” “the” e “tea”.

Seção especial: exercícios de manipulação avançada de string

Os exercícios precedentes são voltados para o texto e para projetados para testar seu entendimento de conceitos fundamentais de manipulação de string. Esta seção inclui uma coleção de exercícios de manipulação de string avançados e intermediários. Você deve achar esses problemas desafiadores, mas divertidos. Os problemas variam consideravelmente em dificuldade. Alguns requerem uma hora ou duas para escrever e implementar o aplicativo. Outros são úteis para atribuições de laboratório que talvez requeiram duas ou três semanas de estudo e implementação. Alguns são projetos de conclusão de curso desafiadores.



Seção especial: exercícios de manipulação avançada de string

547

16.18 (Análise de texto) A disponibilidade de computadores com capacidades de manipulação de string resultou em algumas abordagens bastante interessantes para analisar textos de grandes autores. Muita atenção foi dada à polêmica de que William Shakespeare não teria existido de fato. Alguns acadêmicos acreditam haver evidências substanciais que indicam que Christopher Marlowe realmente escreveu as obras-primas atribuídas a Shakespeare. Os pesquisadores têm utilizado computadores para encontrar semelhanças na escrita desses dois autores. Esse exercício examina três métodos para analisar textos com um computador. a) Escreva um aplicativo que lê uma linha de texto do teclado e imprime uma tabela que indica o número de ocorrências de cada letra do alfabeto no texto. Por exemplo, a frase

To be, or not to be: that is the question:

contém um “a,” dois “b”, nenhum “c,” e assim por diante. b) Escreva um aplicativo que lê uma linha de texto e imprime uma tabela que indique o número de palavras de uma letra, palavras de duas letras, palavras de três letras e assim por diante, aparecer no texto. Por exemplo, a Figura 16.25 mostra as contagens para a frase

Whether 'tis nobler in the mind to suffer

Comprimento de palavra

Ocorrências

1

0

2

2

3

1

4

2 (incluindo

5

0

6

2

7

1

Figura 16.25  |  Contagens de comprimento de palavra da string "Whether

'tis)

'tis nobler in the mind to suffer".

c) Escreva um aplicativo que lê uma linha de texto e imprime uma tabela que indica o número de ocorrências de cada palavra diferente no texto. O aplicativo deve incluir as palavras na tabela na mesma ordem em que elas aparecem no texto. Por exemplo, as linhas

To be, or not to be: that is the question: Whether 'tis nobler in the mind to suffer

contém a palavra “to” três vezes, a palavra “be” duas vezes, a palavra “or” uma vez etc.

16.19 (Imprimindo datas em vários formatos) As datas são impressas em vários formatos comuns. Dois dos formatos mais comuns em inglês são



04/25/1955 and April 25, 1955

Escreva um aplicativo que lê uma data no primeiro formato e imprime no segundo formato.

16.20 (Proteção de cheque) Os computadores são frequentemente empregados em sistemas de verificação de escrita como aplicativos de folha de pagamento e contas a pagar. Ouvimos muitas histórias estranhas relacionadas a cheques de pagamento semanal que são impressos (por engano) com quantias de mais de US$ 1 milhão. Quantidades incorretas são impressas por sistemas computadorizados de preenchimento de cheque por causa de erro humano ou falha de máquina. Os projetistas de sistemas embutem controles em seus sistemas para evitar a emissão desses cheques errados.

Outro problema sério é a alteração intencional do valor de um cheque por alguém que planeja receber um cheque fraudulentamente. Para evitar que uma quantia monetária seja alterada, alguns sistemas computadorizados de preenchimento de cheque empregam uma técnica chamada proteção de cheque. Cheques projetados para imprimir por computador contém um número fixo de espaços em que o computador pode imprimir uma quantia. Suponha que um cheque de pagamento contenha oito espaços em branco em que o computador deve imprimir a quantidade de um cheque de pagamento semanal. Se o valor for alto, então todos os oito espaços serão preenchidos. Por exemplo, 1,230.60 (check amount) ------- 12345678 (position numbers)

Por outro lado, se a quantidade for menor que US$ 1000, então vários dos espaços seriam comumente deixados em branco. Por exemplo, 99.87 ------- 12345678

contém três espaços em branco. Se um cheque for impresso com espaços em branco, é mais fácil para alguém alterar a quantia. Para impedir a alteração, muitos sistemas de preenchimento de cheque inserem asteriscos iniciais para proteger a quantia como mostrado a seguir: ***99.87 ------- 12345678

548

Capítulo 16  Strings, caracteres e expressões regulares Escreva um aplicativo que insere uma quantia monetária que será impressa em um cheque e então imprime o valor em formato de cheque protegido com asteriscos iniciais se necessário. Assuma que nove espaços estão disponíveis para imprimir o valor.

16.21 (Escrevendo o valor de um cheque por extenso) Continuando a discussão do Exercício 16.20, reiteramos a importância de se projetar

sistemas de preenchimento de cheques para impedir a alteração de valores do cheque. Um método comum de segurança requer que o valor seja escrito em números e “por extenso” também. Mesmo se alguém for capaz de alterar o valor numérico do cheque, é extremamente difícil alterar o valor por extenso. Escreva um aplicativo que insere um valor numérico de cheque menor do que $1000 e escreve o valor por extenso em inglês. Por exemplo, o valor 112,43 deve ser escrito assim



ONE hundred TWELVE and 43/100

16.22 (Código Morse) Talvez o mais famoso de todos os esquemas de codificação seja o código Morse, desenvolvido por Samuel Morse em 1832 para

utilização com o sistema de telégrafo. O código Morse atribui uma série de pontos e traços para cada letra do alfabeto, para cada dígito e alguns caracteres especiais (como ponto, vírgula, dois-pontos e ponto-e-vírgula). Em sistemas orientados para áudio, o ponto representa um som curto e o traço representa um som longo. Outras representações de pontos e traços são utilizadas com sistemas baseados em sinais luminosos e sistemas baseados em sinais de bandeira. A separação entre palavras é indicada por um espaço, ou, simplesmente, a ausência de um ponto ou traço. Em um sistema orientado a som, um espaço é indicado por um tempo curto durante o qual nenhum som é transmitido. A versão internacional do código Morse aparece na Figura 16.26 Escreva um aplicativo que lê uma frase em inglês e a codifica em código Morse. Escreva também um aplicativo que lê uma frase em código Morse e a converte no equivalente em inglês. Utilize um espaço em branco entre cada letra codificada em Morse e três espaços em branco entre cada palavra codificada em Morse.

Caractere

Código

Caractere

Código

A

.-

T

-

B

-...

U

..-

C

-.-.

V

...-

D

-..

W

.--

E

.

X

-..-

F

..-.

Y

-.--

G

--.

Z

--..

H

....

I

..

Dígitos

J

.---

1

.----

K

-.-

2

..---

L

.-..

3

...--

M

--

4

....-

N

-.

5

.....

O

---

6

-....

P

.--.

7

--...

Q

--.-

8

---..

R

.-.

9

----.

S

...

0

-----

Figura 16.26  |  Letras e dígitos como expressos em código Morse internacional. 16.23 (Conversões métricas) Escreva um aplicativo que auxiliará o usuário com conversões métricas. Seu aplicativo deve permitir que o usuário especifique os nomes das unidades como strings (isto é, centímetros, litros, gramas etc. para o sistema métrico e polegadas, quartos, libras etc. para o sistema inglês) e deve responder a perguntas simples como





Seu aplicativo deve reconhecer conversões inválidas. Por exemplo, a pergunta



"How many inches are in 2 meters?" "How many liters are in 10 quarts?"

"How many feet are in 5 kilograms?"

não é significativo porque "feet" é uma unidade de comprimento, enquanto "kilograms" é uma unidade de massa.



Seção especial: projetos de manipulação de string desafiadores

549

Seção especial: projetos de manipulação de string desafiadores 16.24 (Projeto: Um corretor ortográfico) Muitos pacotes populares de software processador de texto têm verificadores ortográficos integrados. Nesse projeto, exige-se que você desenvolva seu próprio utilitário de verificação ortográfica. Fazemos sugestões para ajudá-lo a começar. Você então deve considerar a adição de mais capacidades. Utilize um dicionário computadorizado (se tiver acesso a um) como uma fonte de palavras.



Por que digitamos tantas palavras com ortografia incorreta? Em alguns casos, isso é porque simplesmente não conhecemos a ortografia correta, então fazemos nossa “melhor suposição”. Em alguns casos, é porque transpomos duas letras (por exemplo, “pardão” em vez de “padrão)”. Ocasionalmente digitamos duas vezes uma letra acidentalmente (por exemplo, “canssado” em vez de “cansado)”. Às vezes digitamos uma tecla próxima em vez daquela pretendida (por exemplo, “amiverário” em vez de “aniversário”) e assim por diante.



Projete e implemente um aplicativo de verificador ortográfico em Java. Seu aplicativo deve manter um array wordList de strings. Permita que o usuário insira essas strings. [Nota: no Capítulo 17, introduzimos processamento de arquivo. Com essa capacidade, você pode obter as palavras para o verificador ortográfico de um dicionário computadorizado armazenado em um arquivo].



Seu aplicativo deve solicitar que um usuário insira uma palavra. O aplicativo deve então pesquisar essa palavra no array wordList. Se a palavra estiver no array, seu aplicativo deve imprimir "Word is spelled correctly." Se a palavra não estiver no array, seu aplicativo deve imprimir "Word is not spelled correctly." Então o aplicativo deve tentar localizar outras palavras em wordList que podem ser a palavra que o usuário pretendia digitar. Por exemplo, você pode tentar todas as transposições possíveis simples de letras adjacentes para descobrir que a palavra “default” é uma correspondência direta com uma palavra na wordList. Naturalmente, isso implica que seu aplicativo verificará todas as outras transposições simples, como “edfault”, “dfeault”, “deafult”, “defalut”, e “defautl”. Quando você encontrar uma nova palavra que localiza uma palavra na wordList, imprima essa palavra em uma mensagem, como



Did you mean "default"?

Implemente outros testes, como substituir cada letra dupla por uma única letra e algum outro teste que você pode desenvolver para aprimorar o valor de seu verificador ortográfico.

16.25 (Projeto: Um gerador de palavras cruzadas) A maioria das pessoas já brincou de palavras cruzadas, mas poucos tentaram gerar um jogo de palavras cruzadas. Gerar um jogo de palavras cruzadas é sugerido aqui como um projeto de manipulação de string que requer bastante sofisticação e esforço.



Há muitas questões que o programador deve resolver para que até mesmo o mais simples aplicativo gerador de palavras cruzadas funcione. Por exemplo, como a grade das palavras cruzadas é representada dentro do computador? Você deve utilizar uma série de strings ou arrays bidimensionais?



O programador precisa de uma fonte de palavras (isto é, um dicionário computadorizado) que possa ser referenciado diretamente pelo aplicativo. De que forma essas palavras devem ser armazenadas para facilitar as complexas manipulações requeridas pelo aplicativo?



Se você for realmente ambicioso, vai querer gerar a parte de pistas do quebra-cabeça, em que breves dicas para palavras na horizontal e na vertical são impressas. Meramente imprimir uma versão da parte em branco do jogo não é um problema simples.

Fazendo a diferença 16.26 (Cozinhando com ingredientes mais saudáveis) A obesidade nos Estados Unidos está aumentando a uma taxa alarmante. Consulte o mapa

dos Centers for Disease Control and Prevention (CDC) em www.cdc.gov/nccdphp/dnpa/Obesity/trend/maps/index.htm, que mostra tendências de obesidade nos Estados Unidos nos últimos 20 anos. À medida que a obesidade aumenta, crescem também as ocorrências de problemas relacionados (por exemplo, doença de coração, hipertensão, colesterol alto, diabetes tipo 2). Escreva um programa que ajude usuários a escolher ingredientes mais saudáveis ao cozinhar e ajude usuários alérgicos a encontrar substitutos para certos alimentos (por exemplo, castanhas, glúten). O programa deve ler uma receita de uma JTextArea e sugerir substituições mais saudáveis para alguns ingredientes. Por uma questão de simplicidade, ele deve supor que a receita não tem abreviações para medidas como colheres de chá, xícaras e colheres de sopa, e utilizar dígitos numéricos para as quantidades (por exemplo, 1 ovo, 2 xícaras) em vez de escrevê-los (um ovo, duas xícaras). Algumas substituições comuns são mostradas na Figura 16.27. O programa deve exibir um aviso como: “Sempre consulte o médico antes de fazer mudanças significativas na sua dieta”.

Ingrediente

Substituto

1 xícara de creme de leite

1 xícara de iogurte

1 xícara de leite

½ xícara de leite evaporado e ½ xícara de água

1 colher de chá de suco de limão

½ de chá de vinagre

1 xícara de açúcar

½ xícara de mel, 1 xícara de melado ou ¼ de xícara de néctar de agave

1 xícara de manteiga

1 xícara de margarina ou iogurte

1 xícara de farinha

1 xícara de farinha de centeio ou arroz

1 xícara de maionese

1 xícara de queijo minas ou 1/8 de xícara de iogurte e 7/8 de xícara de iogurte

550

Capítulo 16  Strings, caracteres e expressões regulares

Ingrediente

Substituto

1 ovo

2 de colheres de sopa de amido de milho, farinha de araruta ou fécula de batata ou 2 claras de ovo ou 1/2 banana grande (amassada)

1 xícara de leite

1 xícara de leite de soja

¼ de xícara de óleo

¼ de xícara de suco de maçã

pão branco

pão integral

Figura 16.27  |  Substitutos de ingredientes típicos.

O programa deve levar em conta que as substituições nem sempre são um para um. Por exemplo, se uma receita de bolo tem três ovos, em vez disso, ela poderia utilizar razoavelmente seis claras. Dados de conversão para medidas e substitutos podem ser obtidos em sites como:



chinesefood.about.com/od/recipeconversionfaqs/f/usmetricrecipes.htm www.pioneerthinking.com/eggsub.html www.gourmetsleuth.com/conversions.htm

O programa deve considerar as preocupações de saúde do usuário, como colesterol alto, hipertensão, perda de peso, alergia a glúten e assim por diante. Para o colesterol alto, o programa deve sugerir substitutos para ovos e laticínios; se o usuário quiser perder peso, substitutos de baixa caloria para ingredientes como açúcar devem ser sugeridos.

16.27 (Scanner de Spam) O spam (ou junk email) custa bilhões de dólares às organizações dos Estados Unidos por ano em software de prevenção

do spam, equipamento, recursos de rede, largura de banda e produtividade perdida. Pesquise on-line algumas das mensagens e palavras de e-mail de spam mais comuns, e verifique sua própria pasta de lixo eletrônico. Crie uma lista das 30 palavras e frases mais encontradas nas mensagens de spam. Escreva um aplicativo no qual o usuário insere uma mensagem de e-mail em uma JTextArea. Em seguida, varra a mensagem de cada uma das 30 palavras-chave ou frases. Para cada ocorrência de uma dessas dentro da mensagem, adicione um ponto ao “escore de spam” da mensagem. Depois, avalie a probabilidade de a mensagem ser um spam, com base no número de pontos que ela recebeu.

16.28 (Linguagem SMS) O SMS (Short Message Service) é um serviço de comunicação que permite enviar mensagens de texto de 160 ou menos ca-

racteres entre telefones celulares. Com a proliferação do uso do celular no mundo inteiro, o SMS está sendo utilizado em muitos países em desenvolvimento com objetivos políticos (por exemplo, expressar opiniões e fazer oposição), reportagens sobre catástrofes naturais, e assim por diante. Por exemplo, consulte comunica.org/radio2.0/archives/87. Como o tamanho das mensagens de SMS é limitado, linguagem SMS — as abreviações de palavras e frases comuns em mensagens mobile text, e-mails, mensagens instantâneas etc. — são muito usadas. Por exemplo, “in my opinion” é “imo” na linguagem SMS. Pesquise on-line o termo SMS Language. Escreva um aplicativo GUI em que o usuário pode inserir uma mensagem utilizando linguagem SMS, depois clicar em um botão para traduzi-lo para o inglês (ou para sua própria língua). Também forneça um mecanismo para traduzir o texto escrito para o inglês (ou para sua própria língua) na linguagem SMS. Um problema potencial é que uma abreviação de SMS poderia ter vários significados. Por exemplo, IMO (como utilizado acima) também pode significar “International Maritime Organization” “in memory of” etc.

Só posso assumir que um documento marcado como “Não Arquivar” está arquivado como “Não Arquivar”. — Senador Frank Church, Depoimento ao Subcomitê de Inteligência do Senado, 1975

A consciência … em si não aparece dividida em pedaços [bits]. Um “rio” ou um “fluxo” são as metáforas com que ela é mais naturalmente descrita. — William James

Li parte dele até o fim. — Samuel Goldwyn

Uma memória excelente não faz um filósofo, assim como um dicionário não pode ser chamado de gramática. — John Henry, Cardeal Newman

17

Arquivos, fluxos e serialização de objetos Objetivos Neste capítulo, você aprenderá: 

O que são arquivos e como eles são usados para reter dados de aplicativo entre sucessivas execuções.



A criar, ler, gravar e atualizar arquivos.



A utilizar a classe File para recuperar informações sobre arquivos e diretórios.



A hierarquia de classes para fluxo de entrada/saída do Java.



As diferenças entre arquivos de texto e arquivos binários.



O processamento de arquivo de acesso sequencial.



A utilizar as classes Scanner e Formatter para processar arquivos de texto.



A utilizar as classes FileInputStream e FileOutputStream para ler e gravar arquivos.



A utilizar as classes ObjectInputStream e ObjectOutputStream para ler e gravar objetos em arquivos.



A utilizar um diálogo de JFileChooser.

552

Capítulo 17

Arquivos, fluxos e serialização de objetos

17.1 Introdução 17.2 Hierarquia de dados 17.3 Arquivos e fluxos 17.4 Classe File

Sumário

17.5 Arquivos de texto de acesso sequencial 17.5.1 Criando um arquivo de texto de acesso sequencial 17.5.2 Lendo dados a partir de um arquivo de texto de acesso sequencial

17.6 Serialização de objeto 17.6.1 Criando um arquivo de acesso sequencial com a serialização de objeto 17.6.2 Lendo e desserializando dados a partir de um arquivo de acesso sequencial

17.7 Classes java.io adicionais 17.7.1 Interfaces e classes para entrada e saída baseadas em bytes 17.7.2 Interfaces e classes para entrada e saída baseadas em caracteres

17.5.3 Estudo de caso: um programa de consulta de crédito

17.8 Abrindo arquivos com JFileChooser

17.5.4 Atualizando arquivos de acesso sequencial

17.9 Conclusão

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios | Fazendo a diferença

17.1 Introdução Dados armazenados em variáveis e arrays são temporários — eles são perdidos quando uma variável local “sai do escopo” ou quando o programa termina. Para retenção de longo prazo dos dados, mesmo depois de os programas que criam os dados terminarem, os computadores utilizam arquivos. Você utiliza arquivos diariamente para tarefas como escrever um e-mail ou criar uma planilha. Os computadores armazenam arquivos em dispositivos de armazenamento secundários, como discos rígidos, discos ópticos e fitas magnéticas. Os dados mantidos nos arquivos são dados persistentes porque existem além da duração da execução do programa. Neste capítulo, explicaremos como programas Java criam, atualizam e processam arquivos. Uma linguagem de programação deve ser capaz de processar arquivos para suportar aplicativos comerciais, que tipicamente processam quantidades maciças de dados persistentes. Este capítulo discute o processamento de arquivos do Java e os recursos de entrada/saída de fluxo. O termo "fluxo" refere-se a dados ordenados que são lidos ou gravados em um arquivo. Discutiremos os fluxos em detalhes na Seção 17.3. O processamento de arquivos é um subconjunto das capacidades de processamento de fluxos do Java, que incluem ler e gravar dados de memória, arquivos e conexões de rede. Neste capítulo, introduziremos os conceitos do processamento de arquivos (fazendo com que você se sinta mais à vontade com o uso de arquivos programaticamente) e forneceremos capacidades de processamento de fluxo suficientes para suportar os recursos em rede introduzidos no Capítulo 27, “Redes”. Discutimos duas formas do processamento de arquivos aqui — processamento de arquivo de texto e serialização de objetos. Iniciamos discutindo a hierarquia de dados contida em arquivos. Então abrangeremos a arquitetura do Java para tratar arquivos programaticamente — discutimos as várias classes do pacote java.io. Em seguida, explicamos que os dados podem ser armazenados em arquivos de texto e arquivos binários — e cobrimos as diferenças entre eles. Demonstramos como recuperar informações sobre arquivos ou diretórios usando a classe File e, então, dedicamos várias seções aos diferentes mecanismos para gravar e ler dados de arquivos. Ensinamos como criar e manipular arquivos de texto de acesso sequencial. Trabalhar com arquivos de texto permite que o leitor comece a manipular arquivos rápida e facilmente. Como você aprenderá, porém, é difícil ler dados a partir de arquivos de texto e de volta na forma de objetos. Felizmente, várias linguagens orientadas a objetos (incluindo Java) fornecem maneiras de gravar e ler objetos em arquivos (conhecido como serialização e desserialização de objetos). Para demonstrar isso, recriamos alguns de nossos programas de acesso sequencial que utilizaram arquivos de texto, dessa vez armazenando os objetos em arquivos binários.

17.2 Hierarquia de dados Em última instância, um computador processa os itens de dados como combinações de zeros e uns, porque é simples e econômico para os engenheiros construírem dispositivos eletrônicos que podem assumir dois estados estáveis — um representando 0 e o outro representando 1. É notável que as impressionantes funções realizadas pelos computadores envolvam somente as manipulações mais fundamentais de 0s e 1s.

Bits O menor item de dados em um computador pode assumir o valor 0 ou o valor 1. Esse item de dados é chamado de bit (abreviação de “binary digit” — um dígito que pode assumir um de dois valores). O circuito elétrico dos computadores executa várias manipulações de bits simples, como examinar o valor de um bit, configurar o valor de um bit e inverter o valor de um bit (de 1 a 0 ou de 0 a 1).



17.2  Hierarquia de dados

553

Caracteres É incômodo para os programadores trabalharem com dados na forma de baixo nível de bits. Em vez disso, eles preferem trabalhar com dígitos decimais (0–9), letras (A–Z e a–z) e símbolos especiais (por exemplo, $, @, %, &, *, (,), –, +, ", :, ? e / ). Dígitos, letras e símbolos especiais são conhecidos como caracteres. O conjunto de caracteres do computador é o conjunto de todos os caracteres utilizados para escrever programas e representar itens de dados. Os computadores processam somente 1s e 0s, assim o conjunto de caracteres de um computador representa cada caractere como um padrão de 1s e 0s. O Java utiliza caracteres Unicode compostos de dois bytes, cada um composto de oito bits. O tipo byte do Java pode ser utilizado para representar dados de byte. O Unicode contém caracteres para muitos idiomas do mundo. Consulte o Apêndice L para informações adicionais sobre esse conjunto de caracteres. Consulte o Apêndice B, para informações adicionais sobre o conjunto de caracteres ASCII (American Standard Code for Information Interchange), um subconjunto popular do Unicode que representa letras maiúsculas e minúsculas, dígitos e vários caracteres especiais comuns. Campos Assim como caracteres são compostos de bits, campos são compostos de caracteres ou bytes. Um campo é um grupo de caracteres ou bytes que transmitem um significado. Por exemplo, um campo consistindo em letras minúsculas em letras maiúsculas ser utilizado para representar um nome da pessoa. Itens de dados processados pelos computadores formam uma hierarquia de dados que se torna, com relação à estrutura, maior e mais complexa à medida que progredimos de bits para caracteres, campos e assim por diante. Registros e arquivos Em geral, vários campos compõem um registro (implementado como uma classe em Java). Por exemplo, em um sistema de folha de pagamento o registro para um funcionário poderia consistir nos seguintes campos (possíveis tipos para esses campos são mostrados entre parênteses): • número de identificação do funcionário (int) • nome (String) • endereço (String) • salário-hora (double) • número de isenções reivindicadas (int) • rendimentos no ano até a presente data (int ou double) • total de impostos retidos (int ou double) Portanto, um registro é um grupo de campos relacionados. No exemplo anterior, todos os campos pertencem ao mesmo funcionário. Uma empresa poderia ter vários funcionários e assim ter um registro de folha de pagamentos para cada um. Um arquivo é um grupo de registros relacionados. [Nota: De maneira mais geral, um arquivo contém dados arbitrários em formatos arbitrários. Em alguns sistemas operacionais, um arquivo é visualizado como nada além de uma coleção de bytes — qualquer organização dos bytes em um arquivo (por exemplo, organizar os dados em registros) é uma visualização criada pelo programador do aplicativo]. O arquivo de folha de pagamento de uma empresa normalmente contém um registro por empregado. Portanto, o arquivo de folha de pagamento de uma pequena empresa poderia conter apenas 22 registros, enquanto o de uma grande empresa poderia conter milhares. Não é incomum uma empresa ter muitos arquivos, que contêm alguns bilhões, ou mesmo trilhões, de caracteres de informações. A Figura 17.1 ilustra uma parte da hierarquia de dados.

Chaves de registro Para facilitar a recuperação de registros específicos de um arquivo, pelo menos um campo em cada registro é escolhido como uma chave de registro. Uma chave de registro identifica um registro como pertencente a uma pessoa ou empresa em particular e é única para cada registro. Esse campo em geral é utilizado para pesquisar e classificar registros. No registro de folha de pagamento descrito previamente, o número de identificação de empregado normalmente seria escolhido como a chave de registro. Arquivos sequenciais Há muitas maneiras de organizar registros em um arquivo. O mais comum é chamado arquivo sequencial, em que registros são armazenados na ordem pelo campo-chave de registro. Por exemplo, um arquivo de folha de pagamentos comumente insere os registros em ordem crescente pelo número de identificação dos funcionários. Bancos de dados A maioria das organizações armazena dados em vários arquivos diferentes. Empresas podem ter arquivos de folha de pagamento, arquivos de contas a receber (listagem de dinheiro devido por clientes), arquivo de contas a pagar (listagem de dinheiro devido a fornecedores), arquivo de inventário (listagem de fatos sobre todos os itens abrangidos pelo negócio) e muitos outros. Um grupo de arquivos relacionados costuma ser chamado de banco de dados. Uma coleção de programas projetados para criar e gerenciar bancos de dados é chamada sistema de gerenciamento de bancos de dados (DataBase Management System — DBMS). Discutiremos esse tópico no Capítulo 28, “Acesso a banco de dados com JDBC”.

554

Capítulo 17

Arquivos, fluxos e serialização de objetos

Judy

Sally

Black

Tom

Blue

Judy

Green

Iris

Orange

Randy

Red

Registro

Green

J u d y

Campo Caractere Unicode J

00000000 01001010

1

Arquivo

Bit

Figura 17.1 | Hierarquia de dados.

17.3 Arquivos e fluxos O Java vê cada arquivo como um fluxo sequencial de bytes (Figura 17.2). Cada sistema operacional fornece um mecanismo para determinar o término de um arquivo, como um marcador de fim de arquivo ou uma contagem do total de bytes no arquivo que é registrado nos dados mantidos na estrutura do sistema administrativo. Um programa Java que processa um fluxo de bytes simplesmente recebe uma indicação do sistema operacional quando ele alcança o fim do fluxo — o programa não precisa saber como a plataforma subjacente representa arquivos ou fluxos. Em alguns casos, a indicação de fim de arquivo ocorre como uma exceção. Em outros casos, a indicação é um valor de retorno de um método invocado sobre um objeto que processa o fluxo. 0

1

2

3

4

5

6

7

8

9

... ...

n-1

marcador de fim de arquivo

Figura 17.2 | Visualização do Java de um arquivo de n bytes.

Fluxos baseados em bytes e fluxos baseados em caracteres Fluxos de arquivos podem ser utilizados para entrada e saída de dados como bytes ou caracteres. Os fluxos de entrada e saída de bytes são conhecidos como fluxos baseados em bytes, representando os dados no seu formato binário. Os fluxos de entrada e saída de caracteres para arquivos são conhecidos como fluxos baseados em caracteres, representando os dados como uma sequência de caracteres. Por exemplo, se o valor 5 fosse armazenado utilizando um fluxo baseado bytes, ele seria armazenado no formato binário do valor numérico 5, ou 101. Se o valor 5 fosse armazenado usando um fluxo baseado em caracteres, ele seria armazenado no formato binário do caractere 5, ou 00000000 00110101 (essa é a representação binária para o valor numérico 53, que indica o caractere 5 no conjunto de caracteres Unicode). A diferença entre as duas formas é que o valor numérico pode ser utilizado como um número inteiro nos cálculos, enquanto o caractere 5 é simplesmente um caractere que pode ser utilizado em uma string do texto, como em "Sarah Miller is 15 years old". Arquivos criados usando fluxos baseados em bytes são chamados arquivos binários e arquivos criados usando fluxos baseados em caracteres são chamados arquivos de texto. Arquivos de texto podem ser lidos por editores de textos, enquanto arquivos binários são lidos por programas que entendem o conteúdo específico do arquivo e ordenamento desse conteúdo. Entrada padrão, saída padrão e fluxos de erros padrão Um programa Java abre um arquivo criando e associando um objeto ao fluxo de bytes ou de caracteres. As classes usadas para criar esses objetos são discutidas mais adiante. O Java também pode associar fluxos a diferentes dispositivos. De fato, o Java cria três objetos de fluxo que são associados a dispositivos quando um programa Java inicia a execução — System.in, System.out e System.err. System.in (o objeto de fluxo de entrada padrão) normalmente permite a um programa inserir bytes a partir do teclado; o objeto System.out (o objeto de fluxo de saída padrão) normalmente permite a um programa enviar a saída dos dados de caractere para a tela; e o objeto System.err (o

17.4 Classe File

555

objeto fluxo de erro padrão) normalmente permite a um programa gerar a saída de mensagens de erros baseados em caractere na tela. Cada um desses fluxos pode ser redirecionado. Para System.in, essa capacidade permite ao programa ler bytes a partir de uma origem diferente. Para System.out e System.err, essa capacidade permite que a saída seja enviada para um local diferente, como a um arquivo em disco. A classe System fornece os métodos setIn, setOut e setErr para redirecionar os fluxos de entrada, saída e erro padrão, respectivamente.

O pacote java.io Os programas Java realizam o processamento do arquivo utilizando classes do pacote java.io. Esse pacote inclui definições para classes de fluxo, como FileInputStream (para entrada baseada em bytes de um arquivo), FileOutputStream (para saída baseada em bytes de um arquivo), FileReader (para entrada baseada em caractere de um arquivo) e FileWriter (para saída baseada em caractere de um arquivo), que herda das classes InputStream, OutputStream, Reader e Writer, respectivamente (essas classes serão discutidas mais tarde neste capítulo). Assim, os métodos das classes stream também podem ser aplicados a fluxos de arquivo. Você abre um arquivo criando um objeto de uma dessas classes stream. O construtor do objeto interage com o sistema operacional para abrir o arquivo. O Java contém classes que permitem realizar a entrada e saída de objetos ou variáveis de tipos de dados primitivos. Os dados ainda serão armazenados como bytes ou caracteres nos bastidores, permitindo que você leia ou grave dados na forma de ints, Strings ou outros tipos sem ter de se preocupar com os detalhes da conversão desses valores em formato de bytes. Para realizar essa entrada e saída, os objetos das classes ObjectInputStream e ObjectOutputStream podem ser utilizados juntos com as classes de arquivos baseados em fluxos de bytes FileInputStream e FileOutputStream (essas classes serão discutidas em mais detalhes a seguir). A hierarquia completa das classes no pacote java.io poder ser visualizada na documentação on-line em java.sun.com/javase/6/docs/api/java/io/package­tree.html

Cada classe é identada sob sua superclasse. Por exemplo, a classe InputStream é uma subclasse de Object. Para visualizar os detalhes de uma classe, clique no seu nome na hierarquia. Como você pode ver na hierarquia, o Java oferece muitas classes para realizar operações de entrada/saída. Utilizamos várias dessas classes neste capítulo para implementar programas de processamento de arquivos que criam e manipulam arquivos de acesso sequencial. Também incluímos um exemplo detalhado da classe File, que é útil para obter as informações sobre arquivos e diretórios. No Capítulo 27, utilizamos extensamente classes de fluxo para implementar aplicativos de rede. Várias outras classes no pacote java.io que não utilizamos neste capítulo serão discutidas brevemente na Seção 17.7. Além das classes java.io, a entrada e saída baseadas em caracteres podem ser executadas com as classes Scanner e Formatter. A classe Scanner é usada extensamente para a entrada de dados a partir do teclado. Essa classe também pode ler dados de um arquivo. A classe Formatter permite que os dados formatados sejam impressos em qualquer fluxo baseado em texto de uma maneira semelhante ao método System.out.printf. O Apêndice G apresenta os detalhes da saída formatada com printf. Todos esses recursos também podem ser utilizados para formatar arquivos de texto.

17.4 Classe File Esta seção apresenta a classe File, particularmente útil para recuperar informações sobre arquivos ou diretórios em disco. Os objetos da classe File não abrem arquivos nem fornecem quaisquer capacidades de processamento de arquivos. Entretanto, os objetos File são utilizados frequentemente com objetos de outras classes java.io para especificar arquivos ou diretórios a manipular.

Criando objetos File A classe File fornece quatro construtores. Aquele com um argumento String especifica o name de um arquivo ou diretório para associar com o objeto File. O name pode conter as informações de caminho bem como um nome de arquivo ou diretório. Um caminho de arquivo ou diretório especifica sua localização em disco. O caminho inclui alguns ou os principais diretórios para o arquivo ou diretório. Um caminho absoluto contém todos os diretórios, desde o diretório-raiz, que levam a um arquivo ou diretório específico. Cada arquivo ou diretório em uma unidade de disco particular tem o mesmo diretório-raiz em seu caminho. Um caminho relativo normalmente inicia a partir do diretório no qual o aplicativo começou a executar e, portanto, é “relativo” ao diretório atual. O construtor com dois argumentos String especifica um caminho absoluto ou relativo como o primeiro argumento e o arquivo ou diretório a associar com o objeto File como o segundo argumento. O construtor com os argumentos File e String utiliza um objeto File existente que especifica o diretório pai do arquivo ou diretório especificado pelo argumento String. O quarto construtor utiliza um objeto URI para localizar o arquivo. Um Uniform Resource Identifier (URI) é uma forma mais geral dos Uniform Resource Locators (URLs) que são utilizados para localizar sites na Web. Por exemplo, http://www.deitel.com/ é o URL do site Web Deitel & Associates. URIs para localizar arquivos variam em diferentes sistemas operacionais. Em plataformas Windows, o URI file://C:/data.txt

identifica o arquivo data.txt armazenado no diretório-raiz da unidade C: unidade. Em plataformas UNIX/Linux, o URI file:/home/student/data.txt

identifica o arquivo data.txt armazenado no diretório home do usuário student.

556

Capítulo 17  Arquivos, fluxos e serialização de objetos

Dica de prevenção de erro 17.1 O método File usa isFile para determinar se um objeto File representa um arquivo (não um diretório) antes de tentar abri-lo.

A Figura 17.3 lista alguns métodos File comuns. A relação completa pode ser vista em java.sun.com/javase/6/docs/api/java/ io/File.html.

Método

Descrição

boolean canRead()

Retorna true se um arquivo for legível pelo aplicativo atual; false caso contrário.

boolean canWrite()

Retorna true se um arquivo for gravável pelo aplicativo atual; false caso contrário.

boolean exists()

Retorna true se o arquivo ou diretório representado pelo objeto File existir; false do contrário.

boolean isFile()

Retorna true se o nome especificou como o argumento para o File o construtor é um arquivo; caso contrário, false.

boolean isDirectory()

Retorna true se o nome especificado como o argumento para o construtor File é um diretório; caso contrário, false.

boolean isAbsolute()

Retorna true se os argumentos especificados para o construtor File indicam um caminho absoluto para um arquivo ou diretório; caso contrário, false.

String getAbsolutePath()

Retorna uma String com o caminho absoluto do arquivo ou diretório.

String getName()

Retorna uma String com o nome do arquivo ou diretório.

String getPath()

Retorna uma String com o caminho do arquivo ou diretório.

String getParent()

Retorna uma String com o diretório pai do arquivo ou diretório (isto é, o diretório em que o arquivo ou diretório está localizado).

long length()

Retorna o comprimento do arquivo, em bytes. Se o objeto File representa um diretório, um valor não especificado é retornado.

long lastModified()

Retorna uma representação dependente de plataforma da data/hora em que o arquivo ou diretório for modificado pela última vez. O valor retornado é útil somente para a comparação com outros valores retornados por esse método.

String[] list()

Retorna um array de Strings que representam o conteúdo de um diretório. Retorna null se o objeto File não representar um diretório.

Figura 17.3  |  Métodos File.

Demonstrando a classe File A Figura 17.4 solicita que o usuário digite o nome de um arquivo ou diretório, utiliza então a classe File para imprimir as informações sobre o arquivo ou diretório. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// Figura 17.4: FileDemonstration.java // A classe File utilizada para obter informações de arquivo e de diretório. import java.io.File; import java.util.Scanner; public class FileDemonstration { public static void main( String[] args ) { Scanner input = new Scanner( System.in ); System.out.print( "Enter file or directory name: " ); analyzePath( input.nextLine() ); } // fim de main // exibe informações sobre o arquivo que o usuário especifica public static void analyzePath( String path ) { // cria o objeto File com base na entrada de usuário



17.4  Classe File

20 21

File name = new File( path );

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

if (name.exists() ) // se o nome existir, gera saída das informações sobre ele { // exibe informações sobre o arquivo (ou diretório) System.out.printf( "%s%s\n%s\n%s\n%s\n%s%s\n%s%s\n%s%s\n%s%s\n%s%s", name.getName(), " exists", (name.isFile() ? "is a file" : "is not a file" ), (name.isDirectory() ? "is a directory" : "is not a directory" ), (name.isAbsolute() ? "is absolute path" : "is not absolute path" ), "Last modified: ", name.lastModified(), “Length: “, name.length(), "Path: ", name.getPath(), "Absolute path: ", name.getAbsolutePath(), “Parent: “, name.getParent() ); if (name.isDirectory() ) // listagem de diretório de saída { String[] directory = name.list(); System.out.println( "\n\nDirectory contents:\n" ); for ( String directoryName : directory ) System.out.println( directoryName ); } // fim do if } // fim do if externo else // não for arquivo ou diretório, gera saída da mensagem de erro { System.out.printf( "%s %s", path, "does not exist." ); } // fim de else } // fim do método analyzePath } // fim da classe FileDemonstration

Enter file or directory name: E:\Program Files\Java\jdk1.6.0_11\demo\jfc jfc exists is not a file is a directory is absolute path Last modified: 1228404395024 Length: 4096 Path: E:\Program Files\Java\jdk1.6.0_11\demo\jfc Absolute path: E:\Program Files\Java\jdk1.6.0_11\demo\jfc Parent: E:\Program Files\Java\jdk1.6.0_11\demo Directory contents: CodePointIM FileChooserDemo Font2DTest Java2D Laffy Metalworks Notepad SampleTree Stylepad SwingApplet SwingSet2 SwingSet3 Enter file or directory name: C:\Program Files\Java\jdk1.6.0_11\demo\jfc \Java2D\README.txt README.txt exists is a file is not a directory is absolute path Last modified: 1228404384270 Length: 7518 Path: E:\Program Files\Java\jdk1.6.0_11\demo\jfc\Java2D\README.txt Absolute path: E:\Program Files\Java\jdk1.6.0_11\demo\jfc\Java2D\README.txt Parent: E:\Program Files\Java\jdk1.6.0_11\demo\jfc\Java2D

Figura 17.4  |  A classe File utilizada para obter informações de arquivo e de diretório.

557

558

Capítulo 17

Arquivos, fluxos e serialização de objetos

O programa começa solicitando ao usuário um arquivo ou diretório (linha 12). A linha 13 gera as entradas do nome de arquivo ou nome de diretório e passa-o para o método analyzePath (linhas 17–50). O método cria um novo objeto File (linha 20) e atribui sua referência a name. A linha 22 invoca o método File exists para determinar se o nome inserido pelo usuário existe (como um arquivo ou diretório) no disco. Se o nome não existir, o controle prosseguirá para as linhas 46–49 e exibirá uma mensagem na tela contendo o nome que o usuário digitou, seguido por “does not exist”. Caso contrário, a instrução if (linhas 22–45) executa. O programa envia para a saída o nome do arquivo ou diretório (linha 27), seguido pelos resultados do teste do objeto File com isFile (linha 28), isDirectory (linha 29) e isAbsolute (linha 31). Em seguida, o programa exibe os valores retornandos por lastModified (linha 33), length (linha 33), getPath (linha 34), getAbsolutePath (linha 35) e getParent (linha 35). Se o objeto File representar um diretório (linha 37), o programa obterá uma lista do conteúdo do diretório como um array de Strings utilizando o método File list (linha 39) e exibirá a lista na tela. A primeira saída desse programa demonstra um objeto File associado com o diretório jfc do JDK. A segunda saída demonstra um objeto File associado com o arquivo README.txt do exemplo do Java 2D que vem com o JDK. Em ambos os casos, especificamos um caminho absoluto em nosso computador. Um caractere separador é utilizado para separar diretórios e arquivos no caminho. Em um computador Windows, o caractere separador é uma barra invertida (\). Em um sistema UNIX, é uma barra (/). O Java processa esses dois caracteres de maneira idêntica em um nome de caminho. Por exemplo, se fôssemos utilizar o caminho c:\Program Files\Java\jdk1.6.0_11\demo/jfc

que emprega cada caractere separador, ainda assim o Java processaria o caminho adequadamente. Ao criar Strings que representam informações de caminho, utilize File.separator para obter caractere de separador adequado do computador local, em vez de utilizar explicitamente /.ou \. Essa constante retorna uma String que consiste em um caractere — o separador adequado para o sistema.

Erro comum de programação 17.1 Utilizar \ como um separador de diretório em vez de \\ em uma literal de string é um erro de lógica. Uma \ simples indica que a \ seguida pelo próximo caractere representa uma sequência de escape. Utilize \\ para inserir um \ em uma literal de string.

17.5 Arquivos de texto de acesso sequencial Em seguida, criamos e manipulamos arquivos de acesso sequencial nos quais os registros são armazenados na ordem pelo campo de chave de registro. Iniciamos com arquivos de texto, permitindo que o leitor crie e edite rapidamente arquivos legíveis por seres humanos. Discutimos como criar, gravar dados, ler dados e atualizar arquivos de texto de acesso sequencial. Também incluímos um programa de consulta de crédito que recupera dados específicos em um arquivo.

17.5.1 Criando um arquivo de texto de acesso sequencial O Java não impõe nenhuma estrutura a um arquivo — noções, como registros, não fazem parte da linguagem Java. Portanto, você deve estruturar os arquivos para satisfazer os requisitos dos seus aplicativos. No exemplo a seguir, veremos como impor uma estrutura de registro chaveado a um arquivo. O programa nas figuras 17.5, 17.6 e 17.8 cria um arquivo simples de acesso sequencial que poderia ser utilizado em um sistema de contas a receber para monitorar os valores devidos pelos seus clientes a uma empresa. Para cada cliente, o programa obtém do usuário um número de conta e o nome além do saldo do cliente (isto é, o valor que o cliente deve à empresa por bens e serviços recebidos). Os dados de cada cliente constituem “um registro” para esse cliente. Esse aplicativo utiliza o número de conta como a chave de registro — o arquivo será criado e mantido na ordem do número de conta. O programa assume que o usuário insere os registros em ordem de número de conta. Em um sistema abrangente de contas a receber (baseado em arquivos de acesso sequencial), seria fornecido um recurso de classificação de modo que o usuário pudesse inserir o registro em qualquer ordem. Os registros seriam então classificados e gravados no arquivo.

Classe AccountRecord A classe AccountRecord (Figura 17.5) encapsula as informações sobre o registro do cliente utilizadas pelos exemplos neste capítulo. AccountRecord é declarada no pacote com.deitel.ch17 (linha 3) para que possa ser importado em vários exemplos deste capítulo para reutilização. (A Seção 8.14 fornece informações sobre como compilar e utilizar seus próprios pacotes.) A classe AccountRecord contém as variáveis de instância private account, firstName, lastName e balance (linhas 7–10) e os métodos set e get para acessar esses campos. Embora os métodos set não validem os dados nesse exemplo, eles devem fazer isso em um sistema de força industrial. 1 2 3 4 5

// Figura 17.5: AccountRecord.java // A classe AccountRecord mantém informações para uma conta. package com.deitel.ch17; // empacotada para reutilização public class AccountRecord

6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

17.5  Arquivos de texto de acesso sequencial { private private private private

int account; String firstName; String lastName; double balance;

// construtor sem argumentos chama outro construtor com valores padrão public AccountRecord() { this( 0, "", "", 0.0 ); // chama o construtor com quatro argumentos } // fim do construtor de AccountRecord sem argumentos // inicializa um registro public AccountRecord( int acct, String first, String last, double bal ) { setAccount( acct ); setFirstName( first ); setLastName( last ); setBalance( bal ); } // fim do construtor de AccountRecord com quatro argumentos // configura o número de conta public void setAccount( int acct ) { account = acct; } // fim do método setAccount // obtém número de conta public int getAccount() { return account; } // fim do método getAccount // configura o nome public void setFirstName( String first ) { firstName = first; } // fim do método setFirstName // obtém o primeiro nome public String getFirstName() { return firstName; } // fim do método getFirstName // configura o sobrenome public void setLastName( String last ) { lastName = last; } // fim do método setLastName // obtém o ultimo nome public String getLastName() { return lastName; } // fim do método getLastName // configura saldo public void setBalance( double bal ) { balance = bal; } // fim do método setBalance // obtém saldo public double getBalance() { return balance; } // fim do método getBalance } // fim da classe AccountRecord

Figura 17.5  |  A classe AccountRecord mantém informações de uma conta.

559

560

Capítulo 17  Arquivos, fluxos e serialização de objetos

Para compilar a classe AccountRecord, abra uma janela de comando, altere os diretórios para o diretório fig17_05 deste capítulo (que contém AccountRecord.java) e, então, digite: javac -d .. AccountRecord.java

Isso insere AccountRecord.class na estrutura de diretórios do pacote e insere o pacote na pasta ch17 que contém todos os exemplos deste capítulo. Ao compilar a classe AccountRecord (ou quaisquer outras classes que serão reutilizadas neste capítulo), você deve colocá-las em um diretório comum. Ao compilar ou executar as classes que utilizam a classe AccountRecord (por exemplo, CreateTextFile na Figura 17.6), você deve especificar o argumento de linha de comando -classpath para javac e java, como em javac -classpath .;c:\examples\ch17 CreateTextFile.java java -classpath .;c:\examples\ch17 CreateTextFile

Observe que o diretório atual (especificado com.) foi incluído no classpath para assegurar que o compilador possa localizar outras classes no mesmo diretório que o da classe sendo compilada. O separador de caminho utilizado nos comandos precedentes deve ser apropriado à sua plataforma — um ponto-e-vírgula (;) no Windows e um dois-pontos (:) no UNIX/Linux/Mac OS X. Os comandos precedentes supõem que o pacote que contém AccountRecord esteja localizado no diretório C:\examples\ch17 em um computador Windows.

Classe CreateTextFile Agora vamos examinar a classe CreateTextFile (Figura 17.6). A linha 14 declara a variável Formatter output. Como discutido na Seção 17.3, um objeto Formatter gera saída para Strings formatadas, usando as mesmas capacidades de formato do método System. out.printf. Um objeto Formatter pode gerar saída para vários locais, tela ou arquivo, como ocorre aqui. O objeto Formatter é instanciado na linha 21 no método openFile (linhas 17–34). O construtor utilizado na linha 21 recebe um argumento — uma String contendo o nome do arquivo, incluindo seu caminho. Se um caminho não for especificado, como é o caso aqui, a JVM assume que o arquivo está no diretório a partir do qual o programa foi executado. Para arquivos de texto, utilizamos a extensão de arquivo .txt. Se o arquivo não existir, ele será criado. Se um arquivo existente for aberto, seu conteúdo será truncado — todos os dados no arquivo serão descartados. Nesse ponto, o arquivo é aberto para gravação e o objeto Formatter resultante pode ser utilizado para gravar dados no arquivo. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

// Figura 17.6: CreateTextFile.java // Gravando dados em um arquivo de texto sequencial com a classe Formatter. import java.io.FileNotFoundException; import java.lang.SecurityException; import java.util.Formatter; import java.util.FormatterClosedException; import java.util.NoSuchElementException; import java.util.Scanner; import com.deitel.ch17.AccountRecord; public class CreateTextFile { private Formatter output; // objeto utilizado para gerar saída de texto no arquivo // permite ao usuário abrir o arquivo public void openFile() { try { output = new Formatter( “clients.txt” ); // abre o arquivo } // fim do try catch ( SecurityException securityException ) { System.err.println( "You do not have write access to this file." ); System.exit( 1 ); // termina o programa } // fim do catch catch ( FileNotFoundException fileNotFoundException ) { System.err.println( "Error opening or creating file." ); System.exit( 1 ); // termina o programa } // fim do catch } // fim do método openFile // adiciona registros ao arquivo public void addRecords()

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

17.5  Arquivos de texto de acesso sequencial

561

{ // objeto a ser gravado no arquivo AccountRecord record = new AccountRecord(); Scanner input = new Scanner( System.in ); System.out.printf( "%s\n%s\n%s\n%s\n\n", "To terminate input, type the end-of-file indicator ", "when you are prompted to enter input.", "On UNIX/Linux/Mac OS X type d then press Enter", "On Windows type z then press Enter" ); System.out.printf( "%s\n%s", "Enter account number (> 0), first name, last name and balance.", "? " ); while (input.hasNext() ) // faz um loop até o indicador de fim de arquivo { try // gera saída dos valores para o arquivo { // recupera os dados para saída record.setAccount( input.nextInt() ); // lê o número de conta record.setFirstName( input.next() ); // lê o primeiro nome record.setLastName( input.next() ); // lê o sobrenome record.setBalance( input.nextDouble() ); // lê o saldo if ( record.getAccount() > 0 ) { // grava um novo registro output.format( “%d %s %s %.2f\n”, record.getAccount(), record.getFirstName(), record.getLastName(), record.getBalance() ); } // fim do if else { System.out.println( "Account number must be greater than 0." ); } // fim de else } // fim do try catch ( FormatterClosedException formatterClosedException ) { System.err.println( "Error writing to file." ); return; } // fim do catch catch ( NoSuchElementException elementException ) { System.err.println( "Invalid input. Please try again." ); input.nextLine(); // descarta a entrada para que o usuário possa tentar novamente } // fim do catch System.out.printf( "%s %s\n%s", "Enter account number (>0),", "first name, last name and balance.", "? " ); } // fim do while } // fim do método addRecords // fecha o arquivo public void closeFile() { if ( output != null ) output.close(); } // fim do método closeFile } // fim da classe CreateTextFile

Figura 17.6  |  Gravando dados em um arquivo de texto sequencial com a classe Formatter.

As linhas 23–28 tratam da SecurityException, que ocorre se o usuário não tiver permissão de gravar dados no arquivo. As linhas 29–33 tratam da FileNotFoundException, que ocorre se o arquivo não existir e um novo arquivo não puder ser criado. Essa exceção também pode ocorrer se houver um erro ao abrir o arquivo. Observe que nos dois handlers de exceção, chamamos o método static System.exit e passamos o valor 1. Esse método termina o aplicativo. Um argumento de 0 para o método exit indica terminação bem-

562

Capítulo 17  Arquivos, fluxos e serialização de objetos

-sucedida do programa. Um valor de não zero, como 1 nesse exemplo, normalmente indica que ocorreu um erro. Esse valor é passado à janela de comando que executou o programa. O argumento é útil se o programa for executado de um arquivo em lote em sistemas Windows ou de um script de shell nos sistemas UNIX/Linux/Mac OS X. Os arquivos em lote e scripts de shell oferecem uma maneira conveniente de executar vários programas em sequência. Quando o primeiro programa termina, o próximo programa inicia a execução. É possível utilizar o argumento para o método exit em um arquivo em lote ou script de shell a fim de determinar se outros programas devem ser executados. Para mais informações sobre arquivos de lote ou scripts de shell, veja a documentação do seu sistema operacional. O método addRecords (linhas 37–91) pede que o usuário insira os vários campos para cada registro ou inserir a sequência e teclas de fim de arquivo quando a entrada de dados estiver completa. A Figura 17.7 lista as combinações de teclas para inserir o fim de arquivo para vários sistemas de computador. Sistema operacional

Combinação de teclas

UNIX/Linux/Mac OS X

d

Windows

z

Figura 17.7  |  Combinações de chaves de fim de arquivo.

A linha 40 cria um objeto AccountRecord, que será utilizado para armazenar os valores do registro atual inseridos pelo usuário. A linha 42 cria um objeto Scanner para ler a entrada de usuário a partir do teclado. As linhas 44–48 e 50–52 solicitam a entrada do usuário. A linha 54 utiliza o método Scanner hasNext para determinar se a combinação de teclas de fim de arquivo foi inserida. O loop executa até que hasNext encontre o fim de arquivo. As linhas 59–62 leem os dados do usuário, armazenando informações de registro no objeto AccountRecord. Cada instrução lança uma NoSuchElementException (tratada nas linhas 82–86) se os dados estiverem no formato errado (por exemplo, uma String quando um int é esperado) ou se não houver mais dados para saída. Se o número de conta for maior do que 0 (linha 64), as informações do registro são gravadas em clients.txt (linhas 67–69) empregando o método format, que pode executar formatação idêntica ao método System. out.printf utilizado extensivamente nos capítulos anteriores. O método format gera saída para uma String formatada ao destino de saída do objeto Formatter — o arquivo clients.txt. A string de formato "%d %s %s %.2f\n" indica que o registro atual será armazenado como um inteiro (o número de conta) seguido por uma String (o primeiro nome), outra String (o sobrenome) e um valor de ponto flutuante (o saldo). Cada informação é separada da seguinte por um espaço e a saída do valor de double (o saldo) é gerada com dois dígitos à direita do ponto de fração decimal (como indicado pelo .2 em %.2f). Os dados no arquivo de texto podem ser visualizados com um editor de textos ou recuperados mais tarde por um programa projetado para ler o arquivo (Seção 17.5.2). Quando as linhas 67–69 executam, se o objeto Formatter for fechado, uma FormatterClosedException será lançada. Essa exceção é tratada nas linhas 77–81. [Nota: você também pode gerar saída de dados para um arquivo de texto utilizando a classe java. io.PrintWriter, que fornece os métodos format e printf para gerar saída de dados formatados.] As linhas 94–98 declaram o método closeFile, que fecha o Formatter e o arquivo de saída subjacente. A linha 97 fecha o objeto simplesmente chamando o método close. Se o método close não for chamado explicitamente, o sistema operacional normalmente fechará o arquivo quando a execução do programa termina — esse é um exemplo da “faxina” feita pelo sistema operacional. Mas você sempre deve fechar explicitamente um arquivo quando ele não for mais necessário.

Caracteres separadores de linha específicos à plataforma As linhas 67–69 geram saída de uma linha do texto seguida por uma nova linha (\n). Se utilizar um editor de textos para abrir o arquivo clients.txt produzido, cada registro talvez não seja exibido em uma linha separada. Por exemplo, no Notepad (Microsoft Windows), os usuários verão uma linha contínua do texto. Isso ocorre porque plataformas distintas utilizam diferentes caracteres separadores de linha. No UNIX/Linux/Mac OS X, o separador de linha é uma nova linha (\n). No Windows, é uma combinação de um retorno de carro e um feed de linha — representado como \r\n. Você pode utilizar o especificador de formato %n em uma string de controle de formato para imprimir um separador de linha específico à plataforma, assegurando assim que o arquivo de texto possa ser aberto e visualizado corretamente em um editor de textos para a plataforma na qual o arquivo foi criado. Observe que o método System.out.println gera saída a um separador de linha específico à plataforma depois do seu argumento. Além disso, observe que independentemente do separador de linha utilizado em um arquivo de texto, um programa Java ainda pode reconhecer as linhas do texto e lê-las. Classe CreateTextFileTest A Figura 17.8 executa o programa. A linha 8 cria um objeto CreateTextFile, que é então utilizado para abrir, adicionar registros e fechar o arquivo (linhas 10–12). Os dados de exemplo para esse aplicativo são mostrados na Figura 17.9. Na execução de exemplo desse programa, o usuário insere informações para cinco contas e depois insere o fim de arquivo para sinalizar que a entrada de dados está completa. A execução de exemplo não mostra como os registros de dados na verdade aparecem no arquivo. Na próxima seção, para verificar se o arquivo foi criado com sucesso, apresentamos um programa que lê o arquivo e imprime seu conteúdo. Como é um arquivo de texto, você também pode verificar as informações abrindo o arquivo em um editor de textos.



17.5  Arquivos de texto de acesso sequencial

1 2 3 4 5 6 7 8 9 10 11 12 13 14

563

// Figura 17.8: CreateTextFileTest.java // Testando a classe CreateTextFile. public class CreateTextFileTest { public static void main( String[] args ) { CreateTextFile application = new CreateTextFile(); application.openFile(); application.addRecords(); application.closeFile(); } // fim de main } // fim da classe CreateTextFileTest

To terminate input, type the end-of-file indicator when you are prompted to enter input. On UNIX/Linux/Mac OS X type d then press Enter On Windows type z then press Enter Enter account number (> 0), first name, last name and balance. ? 100 Bob Jones 24.98 Enter account number (> 0), first name, last name and balance. ? 200 Steve Doe -345.67 Enter account number (> 0), first name, last name and balance. ? 300 Pam White 0.00 Enter account number (> 0), first name, last name and balance. ? 400 Sam Stone -42.16 Enter account number (> 0), first name, last name and balance. ? 500 Sue Rich 224.62 Enter account number (> 0), first name, last name and balance. ? ^Z

Figura 17.8  |  Testando a classe CreateTextFile.

Dados de exemplo 100

Bob

Jones

24.98

200

Steve

Doe

-345.67

300

Pam

White

0.00

400

Sam

Stone

-42.16

500

Sue

Rich

224.62

Figura 17.9  |  Dados de exemplo para o programa nas Figuras 17.6–17.8.

17.5.2  Lendo dados a partir de um arquivo de texto de acesso sequencial Os dados são armazenados em arquivos de modo que possam ser recuperados para processamento quando necessário. A Seção 17.5.1 demonstrou como criar um arquivo de acesso sequencial. Esta seção mostra como ler dados sequencialmente em um arquivo de texto. Demonstramos como a classe Scanner pode ser utilizada para inserir dados a partir de um arquivo em vez de utilizar o teclado. O aplicativo nas figuras 17.10 e 17.11 lê os registros no arquivo "clients.txt" criados pelo aplicativo da Seção 17.5.1 e exibe o conteúdo do registro. A linha 13 da Figura 17.10 declara um Scanner que será utilizado para recuperar a entrada no arquivo.

1 2 3 4 5 6

// Figura 17.10: ReadTextFile.java // Esse programa lê um arquivo de texto e exibe cada registro. import java.io.File; import java.io.FileNotFoundException; import java.lang.IllegalStateException; import java.util.NoSuchElementException;

564 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

Capítulo 17  Arquivos, fluxos e serialização de objetos import java.util.Scanner; import com.deitel.ch17.AccountRecord; public class ReadTextFile { private Scanner input; // permite ao usuário abrir o arquivo public void openFile() { try { input = new Scanner( new File( “clients.txt” ) ); } // fim do try catch ( FileNotFoundException fileNotFoundException ) { System.err.println( "Error opening file." ); System.exit( 1 ); } // fim do catch } // fim do método openFile // lê o registro no arquivo public void readRecords() { // objeto a ser gravado na tela AccountRecord record = new AccountRecord(); System.out.printf( "%-10s%-12s%-12s%10s\n", "Account", "First Name", "Last Name", "Balance" ); try // lê os registros no arquivo utilizando o objeto Scanner { while (input.hasNext() ) { record.setAccount( input.nextInt() ); // lê o número de conta record.setFirstName( input.next() ); // lê o primeiro nome record.setLastName( input.next() ); // lê o sobrenome record.setBalance( input.nextDouble() ); // lê o saldo // exibe o conteúdo de registro System.out.printf( “%-10d%-12s%-12s%10.2f\n”, record.getAccount(), record.getFirstName(), record.getLastName(), record.getBalance() ); } // fim do while } // fim do try catch ( NoSuchElementException elementException ) { System.err.println( "File improperly formed." ); input.close(); System.exit( 1 ); } // fim do catch catch ( IllegalStateException stateException ) { System.err.println( "Error reading from file." ); System.exit( 1 ); } // fim do catch } // fim do método readRecords // fecha o arquivo e termina o aplicativo public void closeFile() { if ( input != null ) input.close(); // fecha o arquivo } // fim do método closeFile } // fim da classe ReadTextFile

Figura 17.10  |  Leitura de arquivo sequencial utilizando um Scanner.



17.5  Arquivos de texto de acesso sequencial

1 2 3 4 5 6 7 8 9 10 11 12 13 14

565

// Figura 17.11: ReadTextFileTest.java // Testando a classe ReadTextFile. public class ReadTextFileTest { public static void main( String[] args ) { ReadTextFile application = new ReadTextFile(); application.openFile(); application.readRecords(); application.closeFile(); } // fim de main } // fim da classe ReadTextFileTest

Account 100 200 300 400 500

First Name Bob Steve Pam Sam Sue

Last Name Jones Doe White Stone Rich

Balance 24.98 -345.67 0.00 -42.16 224.62

Figura 17.11  |  Testando a classe ReadTextFile.

O método openFile (linhas 16–27) abre o arquivo para leitura instanciando um objeto Scanner na linha 20. Passamos um objeto File para o construtor, o qual especifica que o objeto Scanner vai ler do arquivo "clients.txt" localizado no diretório em que o aplicativo é executado. Se o arquivo não puder ser localizado, ocorrerá uma FileNotFoundException. O tratamento de exceções nas linhas 22–26. O método readRecords (linhas 30–64) lê e exibe os registros no arquivo. A linha 33 cria objeto AccountRecord record para armazenar informações do registro atual. As linhas 35–36 exibem os cabeçalhos das colunas na saída do aplicativo. As linhas 40–51 leem os dados no arquivo até que o marcador de fim de arquivo seja alcançado (em que caso, o método hasNext retornará false na linha 40). As linhas 42–45 utilizam os métodos Scanner nextInt, next e nextDouble para inserir um int (o número de conta), duas Strings (os nomes e sobrenomes) e um valor double (o saldo). Cada registro é uma linha de dados no arquivo. Os valores são armazenados no objeto record. Se as informações no arquivo não estiverem adequadamente formatadas (por exemplo, há um sobrenome no qual deveria haver um saldo), ocorrerá uma NoSuchElementException quando o registro é inserido. Essa exceção é tratada nas linhas 53–58. Se Scanner foi fechado antes de os dados serem inseridos, uma IllegalStateException ocorrerá (tratada nas linhas 59–63). Se nenhuma exceção ocorrer, as informações do registro são exibidas na tela (linhas 48–50). Observe na string de formato na linha 48 que o número da conta, o primeiro nome e o sobrenome são alinhados à esquerda, enquanto o saldo é justificado à direita e enviado para a saída com dois dígitos de precisão. Cada iteração do loop insere uma linha de texto no arquivo de texto, que representa um registro. As linhas 67–71 definem o método closeFile, que fecha o Scanner. O método main é definido na Figura 17.11, nas linhas 6–13. A linha 8 cria um objeto ReadTextFile, que é então utilizado para abrir, adicionar registros e fechar o arquivo (linhas 10–12).

17.5.3  Estudo de caso: um programa de consulta de crédito Para recuperar dados sequencialmente de um arquivo, os programas começam no início do arquivo e leem todos os dados consecutivamente até que as informações desejadas sejam encontradas. Talvez seja necessário processar o arquivo várias vezes sequencialmente (a partir do início do arquivo) durante a execução de um programa. A classe Scanner não permite reposicionamento no início do arquivo. Se for necessário ler o arquivo novamente, o programa deverá fechar e reabrir o arquivo. O programa nas figuras 17.12–17.14 permite que um gerente de crédito obtenha listas de clientes com saldo zero (isto é, clientes que não devem nada à empresa), clientes com saldos credores (isto é, os clientes aos quais a empresa deve dinheiro) e clientes com saldos devedores (isto é, os clientes que devem à empresa por bens e serviços recebidos). Um saldo credor é um valor monetário negativo e um saldo devedor, um valor positivo.

Enumeração MenuOption Iniciamos criando um tipo enum (Figura 17.12) para definir as diferentes opções de menu que o usuário terá. As opções e seus valores são listados nas linhas 7–10. O método getValue (linhas 19–22) recupera o valor de uma constante enum específica.

566

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

Capítulo 17  Arquivos, fluxos e serialização de objetos

// Figura 17.12: MenuOption.Java // Enumeração para as opções do programa de consulta de crédito. public enum MenuOption { // declara o conteúdo do tipo enum ZERO_BALANCE( 1 ), CREDIT_BALANCE( 2 ), DEBIT_BALANCE( 3 ), END( 4 ); private final int value; // opção atual de menu MenuOption( int valueOption ) { value = valueOption; } // fim do construtor do enum de MenuOptions public int getValue() { return value; } // fim do método getValue } // fim do enum de MenuOption

Figura 17.12  |  Enumeração das opções de menu do programa de consulta de crédito.

Classe CreditInquiry A Figura 17.13 contém a funcionalidade para o programa de consulta de crédito e a Figura 17.14 contém o método main que executa o programa. O programa exibe um menu de texto e permite ao gerente de crédito inserir uma de três opções para obter informações de crédito. A opção 1 (ZERO_BALANCE) exibe as contas com saldos zero. A opção 2 (CREDIT_BALANCE) exibe as contas com saldos positivos. A opção 3 (DEBIT_BALANCE) exibe as contas com saldos negativos. A opção 4 (END) termina a execução do programa. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

// Figura 17.13: CreditInquiry.java // Esse programa lê um arquivo sequencialmente e exibe o // conteúdo com base no tipo de conta que o usuário solicita // (saldo credor, saldo devedor ou saldo zero). import java.io.File; import java.io.FileNotFoundException; import java.lang.IllegalStateException; import java.util.NoSuchElementException; import java.util.Scanner; import com.deitel.ch17.AccountRecord; public class CreditInquiry { private MenuOption accountType; private Scanner input; private final static MenuOption[] choices = { MenuOption.ZERO_BALANCE, MenuOption.CREDIT_BALANCE, MenuOption.DEBIT_BALANCE, MenuOption.END }; // lê registros de arquivo e exibe somente os registros do tipo apropriado private void readRecords() { // objeto para armazenar dados que serão gravados no arquivo AccountRecord record = new AccountRecord(); try // lê registros { // abre o arquivo para leitura a partir do início input = new Scanner( new File( “clients.txt” ) ); while (input.hasNext() ) // insere os valores do arquivo {

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106

17.5  Arquivos de texto de acesso sequencial record.setAccount( input.nextInt() ); // lê o número de conta record.setFirstName( input.next() ); // lê o primeiro nome record.setLastName( input.next() ); // lê o sobrenome record.setBalance( input.nextDouble() ); // lê o saldo // se o tipo for a conta adequada, exibe o registro if ( shouldDisplay( record.getBalance() ) ) System.out.printf( “%-10d%-12s%-12s%10.2f\n”, record.getAccount(), record.getFirstName(), record.getLastName(), record.getBalance() ); } // fim do while } // fim do try catch ( NoSuchElementException elementException ) { System.err.println( "File improperly formed." ); input.close(); System.exit( 1 ); } // fim do catch catch ( IllegalStateException stateException ) { System.err.println( "Error reading from file." ); System.exit( 1 ); } // fim do catch catch ( FileNotFoundException fileNotFoundException ) { System.err.println( "File cannot be found." ); System.exit( 1 ); } // fim do catch finally { if ( input != null ) input.close(); // fecha o Scanner e o arquivo } // fim de finally } // fim do método readRecords // utiliza o tipo de registro para determinar se registro deve ser exibido private boolean shouldDisplay( double balance ) { if ( ( accountType == MenuOption.CREDIT_BALANCE ) && ( balance < 0 ) ) return true; else if ( ( accountType == MenuOption.DEBIT_BALANCE ) && ( balance > 0 ) ) return true; else if ( ( accountType == MenuOption.ZERO_BALANCE ) && ( balance == 0 ) ) return true; return false; } // fim do método shouldDisplay // obtém a solicitação do usuário private MenuOption getRequest() { Scanner textIn = new Scanner( System.in ); int request = 1; // exibe opções de solicitação System.out.printf( "\n%s\n%s\n%s\n%s\n%s\n", "Enter request", " 1 - List accounts with zero balances", " 2 - List accounts with credit balances", " 3 - List accounts with debit balances", " 4 - End of run" ); try // tenta inserir a escolha de menu { do // insere a solicitação de usuário { System.out.print( "\n? " ); request = textIn.nextInt(); } while ( ( request < 1 ) || ( request > 4 ) ); } // fim do try

567

568 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140

Capítulo 17  Arquivos, fluxos e serialização de objetos catch ( NoSuchElementException elementException ) { System.err.println( "Invalid input." ); System.exit( 1 ); } // fim do catch return choices[ request - 1 ]; // retorna o valor enum da opção } // fim do método getRequest public void processRequests() { // obtém a solicitação do usuário (por exemplo, saldo zero, credor ou devedor) accountType = getRequest(); while ( accountType != MenuOption.END ) { switch ( accountType ) { case ZERO_BALANCE: System.out.println( "\nAccounts with zero balances:\n" ); break; case CREDIT_BALANCE: System.out.println( "\nAccounts with credit balances:\n" ); break; case DEBIT_BALANCE: System.out.println( "\nAccounts with debit balances:\n" ); break; } // fim do switch readRecords(); accountType = getRequest(); } // fim do while } // fim do método processRequests } // fim da classe CreditInquiry

Figura 17.13  |  Programa de consulta de crédito. 1 2 3 4 5 6 7 8 9 10 11

// Figura 17.14: CreditInquiryTest.java // Esse programa testa a classe CreditInquiry. public class CreditInquiryTest { public static void main( String[] args ) { CreditInquiry application = new CreditInquiry(); application.processRequests(); } // fim de main } // fim da classe CreditInquiryTest

Figura 17.14  |  Testando a classe CreditInquiry. Enter request 1 - List accounts with zero balances 2 - List accounts with credit balances 3 - List accounts with debit balances 4 - End of run ? 1 Accounts with zero balances: 300

Pam

White

Enter request 1 - List accounts with zero balances 2 - List accounts with credit balances 3 - List accounts with debit balances 4 - End of run

0.00

17.6 Serialização de objetos

569

? 2 Accounts with credit balances: 200 Steve Doe 400 Sam Stone

­345.67 ­42.16

Enter request 1 ­ List accounts with zero balances 2 ­ List accounts with credit balances 3 ­ List accounts with debit balances 4 ­ End of run ? 3 Accounts with debit balances: 100 Bob Jones 500 Sue Rich

24.98 224.62

? 4

Figura 17.15 | A saída de exemplo do programa de consulta de crédito na Figura 17.14.

As informações sobre o registro são coletadas lendo o arquivo do princípio ao fim e determinando se cada registro satisfaz os critérios para o tipo de conta selecionado. O método processRequests (linhas 116–139 da Figura 17.13) chama o método getRequest para exibir as opções de menu (linha 119), converte o número digitado pelo usuário. As linhas 121–138 fazem um loop até que o usuário especifique que o programa deve terminar. As linhas 123–134 exibem um cabeçalho para o conjunto atual de registros a ser impresso na tela. A linha 136 chama o método readRecords (linhas 22–67), que faz o loop pelo arquivo e lê cada registro. A linha 30 do método readRecords abre o arquivo para ler com um Scanner. Observe que o arquivo será aberto para leitura com um novo objeto Scanner toda vez que esse método é chamado de modo que possamos ler novamente do início do arquivo. As linhas 34–37 leem um registro. A linha 40 chama o método shouldDisplay (linhas 70–85) para determinar se o registro atual satisfaz o tipo de conta solicitado. Se shouldDisplay retornar true, o programa exibirá as informações de conta. Quando o marcador de fim de arquivo for alcançado, o loop termina e a linha 65 chama o método close de Scanner para fechar Scanner e o arquivo. Observe que isso ocorre em um bloco finally, que será executado independentemente de o arquivo ter sido lido ou não com sucesso. Depois que todos os registros foram lidos, o controle retorna ao método processRequests e getRequest é novamente chamado (linha 137) para recuperar a próxima opção de menu do usuário. A Figura 17.14 contém o método main e chama o método processRequests na linha 9.

17.5.4 Atualizando arquivos de acesso sequencial Os dados em muitos arquivos sequenciais não podem ser modificados sem o risco de destruir outros dados no arquivo. Por exemplo, se for necessário alterar o nome “White” para “Worthington”, o nome antigo simplesmente não poderá ser sobrescrito, porque o novo nome requer mais espaço. O registro para White foi gravado no arquivo como 300 Pam White 0.00

Se o registro fosse regravado começando no mesmo local no arquivo utilizando o novo nome, o registro seria 300 Pam Worthington 0.00

O novo registro é maior (tem mais caracteres) que o registro original. Os caracteres além do segundo “o” em “Worthington” sobrescreverá o começo do próximo registro sequencial no arquivo. O problema aqui é que os campos em um arquivo de texto — e, consequentemente, registros — podem variar de tamanho. Por exemplo, 7, 14, –117, 2074 e 27383 são todos ints armazenados no mesmo número de bytes (4) internamente, mas são campos com diferentes tamanhos quando exibidos na tela ou gravados em um arquivo como texto. Portanto, registros em um arquivo de acesso sequencial normalmente não são atualizados no lugar. Em vez disso, o arquivo inteiro é normalmente regravado. Para fazer a alteração no nome anterior, os registros antes de 300 Pam White 0.00 seriam copiados para um novo arquivo, o novo registro (que pode ter um tamanho diferente daquele que o substitui) seria gravado e os registros depois de 300 Pam White 0.00 seriam copiados para o novo arquivo. Reescrever todo o arquivo não é uma boa ideia para atualizar um único registro, mas razoável se um número substancial de registros tiver de ser atualizado.

17.6 Serialização de objetos Na Seção 17.5, demonstramos como gravar os campos individuais de um objeto AccountRecord em um arquivo como texto e ler esses campos de um arquivo e colocar seus valores em um objeto AccountRecord na memória. Nos exemplos, AccountRecord foi utilizado para agregar as informações a um registro. Quando a saída das variáveis de instância para um AccountRecord fosse gerada para um ar-

570

Capítulo 17  Arquivos, fluxos e serialização de objetos

quivo em disco, certas informações seriam perdidas, como o tipo de cada valor. Por exemplo, se o valor "3" for lido a partir de um arquivo, não haverá como informar se ele veio de um int, um String ou um double. Temos apenas dados, não informações de tipo, em um disco. Se o programa que fosse ler esses dados “soubesse” a que tipo de objeto os dados correspondem, os dados então seriam simplesmente lidos e transferidos para objetos desse tipo. Por exemplo, na Seção 17.5.2, sabemos que estamos inserindo um int (o número de conta), seguido por duas Strings (o nome e sobrenome) e um double (o saldo). Também sabemos que esses valores são separados por espaços, com somente um registro em cada linha. Às vezes, nós saberemos exatamente como os dados serão armazenados em um arquivo. Nesses casos, queremos ler ou gravar um objeto inteiro a partir de um arquivo. O Java fornece esse mecanismo, chamado serialização de objetos. Um objeto serializado é um objeto representado como uma sequência de bytes que inclui os dados do objeto bem como as informações sobre o tipo do objeto e os tipos dos dados armazenados no objeto. Depois que um objeto serializado foi gravado em um arquivo, ele pode ser lido a partir do arquivo e desserializado, isto é, as informações dos tipos e bytes que representam o objeto e seus dados podem ser utilizadas para recriar o objeto na memória.

Observação de engenharia de software 17. 1 O mecanismo de serialização cria cópias exatas dos objetos. Isso simplificará a maneira de clonar objetos sem a necessidade de sobrescrever o método Object clone.

Classes ObjectInputStream e ObjectOutputStream As classes ObjectInputStream e ObjectOutputStream que, respectivamente, implementam as interfaces ObjectInput e ObjectOutput, permitem que objetos inteiros sejam lidos ou gravados em um fluxo (possivelmente um arquivo). Para utilizar a serialização com arquivos, inicializamos os objetos ObjectInputStream e ObjectOutputStream com objetos de fluxo que leem de e gravam em arquivos — os objetos das classes FileInputStream e FileOutputStream, respectivamente. Esse tipo de inicialização de objetos de fluxo com outros objetos de fluxo é chamado, às vezes, de empacotamento — o novo objeto de fluxo a ser criado empacota o objeto de fluxo especificado como um argumento do construtor. Por exemplo, para empacotar um FileInputStream em um ObjectInputStream, passamos o objeto FileInputStream para o construtor de ObjectInputStream. Interfaces ObjectOutput e ObjectInput A interface ObjectOutput contém o método writeObject, que recebe um Object como um argumento e grava suas informações em um OutputStream. Uma classe que implementa a interface ObjectOuput (como ObjectOutputStream) declara esse método e assegura que o objeto sendo gerado implementa a interface Serializable (discutida mais adiante). Correspondentemente, a interface ObjectInput contém o método readObject, que lê e retorna uma referência a um Object a partir de um InputStream. Depois que um objeto foi lido, podemos fazer uma coerção da sua referência para o tipo real do objeto. Como você verá no Capítulo 27, aplicativos que se comunicam via uma rede, como a Internet, também podem transmitir objetos inteiros pela rede.

17.6.1  Criando um arquivo de acesso sequencial com a serialização de objeto Esta seção e a Seção 17.6.2 criam e manipulam arquivos de acesso sequencial utilizando a serialização de objeto. A serialização de objetos que mostramos aqui é realizada com fluxos baseados em bytes, assim arquivos sequenciais criados e manipulados serão arquivos binários. Lembre-se de que, em geral, arquivos binários não podem ser visualizados nos editores de textos padrão. Por essa razão, escrevemos um aplicativo separado que sabe ler e exibir objetos serializados. Iniciamos criando e gravando objetos serializados em um arquivo de acesso sequencial. O exemplo é semelhante àquele na Seção 17.5, portanto focalizamos apenas os novos recursos.

Definindo a classe AccountRecordSerializable Vamos começar modificando nossa classe AccountRecord para que objetos dessa classe possam ser serializados. A classe AccountRe­ cordSerializable (Figura 17.16) implementa a interface Serializable (linha 7), que permite aos objetos de AccountRecordSeria­ lizable ser serializados e desserializados com ObjectOutputStreams e ObjectInputStreams, respectivamente. A interface Serializable é uma interface de tags. Essa interface não contém nenhum método. Uma classe que implementa Serializable é marcada com tags como um objeto Serializable. Isso é importante, porque um ObjectOutputStream não enviará para a saída um objeto a menos que ele seja um objeto Serializable, que é o caso para qualquer objeto de uma classe que implementa Serializable.

1 2 3 4 5 6 7 8 9

// Figura 17.16: AccountRecordSerializable.java // Classe AccountRecordSerializable para objetos serializáveis. package com.deitel.ch17; // empacotada para reutilização import java.io.Serializable; public class AccountRecordSerializable implements Serializable { private int account;

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

17.6  Serialização de objetos private String firstName; private String lastName; private double balance; // construtor sem argumentos chama outro construtor com valores padrão public AccountRecordSerializable() { this( 0, "", "", 0.0 ); } // fim do construtor sem argumentos AccountRecordSerializable // construtor com quatro argumentos inicializa um registro public AccountRecordSerializable( int acct, String first, String last, double bal ) { setAccount( acct ); setFirstName( first ); setLastName( last ); setBalance( bal ); } // fim do construtor de AccountRecordSerializable com quatro argumentos // configura o número de conta public void setAccount( int acct ) { account = acct; } // fim do método setAccount // obtém o número de conta public int getAccount() { return account; } // fim do método getAccount // configura o nome public void setFirstName( String first ) { firstName = first; } // fim do método setFirstName // obtém o primeiro nome public String getFirstName() { return firstName; } // fim do método getFirstName // configura o sobrenome public void setLastName( String last ) { lastName = last; } // fim do método setLastName // obtém o último nome public String getLastName() { return lastName; } // fim do método getLastName // configura o saldo public void setBalance( double bal ) { balance = bal; } // fim do método setBalance // obtém o saldo public double getBalance() { return balance; } // fim do método getBalance } // fim da classe AccountRecordSerializable

Figura 17.16  |  Classe AccountRecordSerializable para objetos serializáveis.

571

572

Capítulo 17  Arquivos, fluxos e serialização de objetos

Em uma classe que implementa Serializable, o programador deve assegurar que cada variável de instância seja um tipo Serializable. Do contrário, deve ser declarado transient para indicar que não é Serializable e deve ser ignorado durante o processo de seria-

lização. Por padrão, todas as variáveis de tipo primitivo são serializáveis. Para variáveis de tipo por referência, você deve verificar a documentação da classe (e possivelmente suas superclasses) para assegurar que o tipo é Serializable. Por exemplo, Strings são Serializable. Por padrão, os array são serializáveis; mas, em um array de tipo por referência, os objetos referenciados podem ou não ser serializáveis. A classe AccountRecordSerializable contém membros de dados private account, firstName, lastName e balance — os quais são Serializable. Essa classe também fornece os métodos public get e set para acessar os campos private.

Gravando objetos serializados em um arquivo de acesso sequencial Agora vamos discutir o código que cria o arquivo de acesso sequencial (figuras 17.17–17.18). Aqui, vamos nos concentrar apenas nos novos conceitos. Como afirmado na Seção 17.3, um programa pode abrir um arquivo criando um objeto da classe de fluxo FileInput­ Stream ou FileOutputStream. Nesse exemplo, o arquivo deve ser aberto para saída, assim o programa cria um FileOutputStream (linha 21 da Figura 17.17). O argumento de String passado para o construtor de FileOutputStream representa o nome e caminho do arquivo a ser aberto. Arquivos existentes que são abertos para saída dessa maneira são truncados. Note que é usada a extensão de arquivo .ser — utilizamos essa extensão de arquivo para arquivos binários que contêm objetos serializados. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

// Figura 17.17: CreateSequentialFile.java // Gravando objetos sequencialmente em um arquivo com a classe ObjectOutputStream. import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.NoSuchElementException; import java.util.Scanner; import com.deitel.ch17.AccountRecordSerializable; public class CreateSequentialFile { private ObjectOutputStream output; // gera saída dos dados no arquivo // permite que o usuário especifique o nome do arquivo public void openFile() { try // abre o arquivo { output = new ObjectOutputStream( new FileOutputStream( “clients.ser” ) ); } // fim do try catch ( IOException ioException ) { System.err.println( "Error opening file." ); } // fim do catch } // fim do método openFile // adiciona registros ao arquivo public void addRecords() { AccountRecordSerializable record; // objeto a ser gravado no arquivo int accountNumber = 0; // número da conta para o objeto de registro String firstName; // primeiro nome para o objeto de registro String lastName; // sobrenome para o objeto de registro double balance; // saldo para o objeto de registro Scanner input = new Scanner( System.in ); System.out.printf( "%s\n%s\n%s\n%s\n\n", "To terminate input, type the end-of-file indicator ", "when you are prompted to enter input.", "On UNIX/Linux/Mac OS X type d then press Enter", "On Windows type z then press Enter" ); System.out.printf( "%s\n%s", "Enter account number (> 0), first name, last name and balance.", "? " ); while ( input.hasNext() ) // faz um loop até o indicador de fim de arquivo { try // gera saída dos valores para o arquivo

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

17.6  Serialização de objetos { accountNumber = input.nextInt(); firstName = input.next(); // lê o lastName = input.next(); // lê o balance = input.nextDouble(); //

// lê o número de conta primeiro nome sobrenome lê o saldo

if ( accountNumber > 0 ) { // cria um novo registro record = new AccountRecordSerializable( accountNumber, firstName, lastName, balance ); output.writeObject( record ); // gera a saída de registro } // fim do if else { System.out.println( "Account number must be greater than 0." ); } // fim de else } // fim do try catch ( IOException ioException ) { System.err.println( "Error writing to file." ); return; } // fim do catch catch ( NoSuchElementException elementException ) { System.err.println( "Invalid input. Please try again." ); input.nextLine(); // descarta a entrada para que o usuário possa tentar novamente } // fim do catch System.out.printf( "%s %s\n%s", "Enter account number (>0),", "first name, last name and balance.", "? " ); } // fim do while } // fim do método addRecords // fecha o arquivo e termina o aplicativo public void closeFile() { try // fecha o arquivo { if ( output != null ) output.close(); } // fim do try catch ( IOException ioException ) { System.err.println( "Error closing file." ); System.exit( 1 ); } // fim do catch } // fim do método closeFile } // fim da classe CreateSequentialFile

Figura 17.17  |  Arquivo sequencial criado com ObjectOutputStream. 1 2 3 4 5 6 7 8 9 10 11 12 13 14

573

// Figura 17.18: CreateSequentialFileTest.java // Testando a classe CreateSequentialFile. public class CreateSequentialFileTest { public static void main( String[] args ) { CreateSequentialFile application = new CreateSequentialFile(); application.openFile(); application.addRecords(); application.closeFile(); } // fim de main } // fim da classe CreateSequentialFileTest

574

Capítulo 17  Arquivos, fluxos e serialização de objetos

To terminate input, type the end-of-file indicator when you are prompted to enter input. On UNIX/Linux/Mac OS X type d then press Enter On Windows type z then press Enter Enter ? 100 Enter ? 200 Enter ? 300 Enter ? 400 Enter ? 500 Enter ? ^Z

account number (> Bob Jones 24.98 account number (> Steve Doe -345.67 account number (> Pam White 0.00 account number (> Sam Stone -42.16 account number (> Sue Rich 224.62 account number (>

0), first name, last name and balance. 0), first name, last name and balance. 0), first name, last name and balance. 0), first name, last name and balance. 0), first name, last name and balance. 0), first name, last name and balance.

Figura 17.18  |  Testando a classe CreateSequentialFile.

Erro comum de programação 17.2 É um erro de lógica abrir um arquivo existente para saída quando, de fato, você deseja preservar o arquivo. A classe FileOutputStream fornece um construtor sobrecarregado que permite abrir um arquivo e acrescentar dados ao fim do arquivo. Isso preservará

o conteúdo do arquivo.

A classe FileOutputStream fornece métodos para gravar array de byte e byte individuais em um arquivo, mas queremos gravar objetos em um arquivo. Por essa razão, empacotamos um FileOutputStream em um ObjectOutputStream passando o novo objeto FileOutputStream para o construtor de ObjectOutputStream (linhas 20–21). O objeto ObjectOutputStream utiliza o objeto FileOutputStream para gravar objetos no arquivo. As linhas 20–21 podem lançar uma IOException se um problema ocorrer ao abrir o arquivo (por exemplo, quando um arquivo é aberto para gravação em uma unidade com espaço insuficiente ou quando um arquivo de leitura é aberto para gravação). Se isso ocorrer, o programa exibirá uma mensagem de erro (linhas 23–26). Se nenhuma exceção ocorrer, o arquivo é aberto e a variável output pode ser utilizada para gravar objetos nela. Esse programa supõe que os dados foram inseridos corretamente e na ordem de número de registro adequada. O método addRecords (linhas 30–86) realiza a operação de gravação. As linhas 62–63 criam um objeto AccountRecordSerializable a partir dos dados inseridos pelo usuário. A linha 64 chama o método ObjectOutputStream write-Object para gravar o objeto record no arquivo de saída. Observe que somente uma instrução é requerida para gravar todo o objeto. O método closeFile (linhas 89–101) chama o método ObjectOutputStream close em output para fechar tanto o ObjectOut­ putStream como seu FileOutputStream subjacente (linha 94). Observe que a chamada ao método close está contida em um bloco try. O método close lança uma IOException se o arquivo não puder ser fechado adequadamente. Nesse caso, é importante notificar o usuário de que as informações no arquivo talvez estejam corrompidas. Ao utilizar fluxos empacotados, fechar o fluxo mais externo também fecha o arquivo subjacente. Na execução de exemplo do programa na Figura 17.18, inserimos informações para cinco contas — as mesmas informações mostradas na Figura 17.9. O programa, na verdade, não mostra como os registros dos dados aparecem no arquivo. Lembre-se de que agora estamos utilizando arquivos binários, que não são humanamente legíveis. Para verificar se o arquivo foi criado com sucesso, a próxima seção apresenta um programa para ler o conteúdo do arquivo.

17.6.2  Lendo e desserializando dados a partir de um arquivo de acesso sequencial A seção anterior mostrou como criar um arquivo de acesso sequencial utilizando a serialização de objetos. Nesta seção, discutimos como ler dados serializados sequencialmente a partir de um arquivo. O programa nas figuras 17.19–17.20 lê registros de um arquivo criado pelo programa na Seção 17.6.1 e exibe o conteúdo. O programa abre o arquivo para entrada criando um objeto FileInputStream (linha 21). O nome do arquivo a ser aberto é especificado como um argumento para o construtor FileInputStream. Na Figura 17.17, os objetos no arquivo foram gravados com um objeto ObjectOutput­ Stream. Os dados devem ser lidos do arquivo no mesmo formato em que foram gravados. Portanto, utilizamos um ObjectInputStream empacotado em um FileInputStream nesse programa (linhas 20–21). Se nenhuma exceção ocorrer ao abrir o arquivo, a variável input poderá ser utilizada para ler objetos a partir do arquivo.



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

17.6  Serialização de objetos

// Figura 17.19: ReadSequentialFile.java // Lendo um arquivo de objetos sequencialmente com ObjectInputStream // e exibe cada registro. import java.io.EOFException; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import com.deitel.ch17.AccountRecordSerializable; public class ReadSequentialFile { private ObjectInputStream input; // permite que o usuário selecione o arquivo a abrir public void openFile() { try // abre o arquivo { input = new ObjectInputStream( new FileInputStream( “clients.ser” ) ); } // fim do try catch ( IOException ioException ) { System.err.println( "Error opening file." ); } // fim do catch } // fim do método openFile // lê o registro no arquivo public void readRecords() { AccountRecordSerializable record; System.out.printf( "%-10s%-12s%-12s%10s\n", "Account", "First Name", "Last Name", "Balance" ); try // insere os valores do arquivo { while ( true ) { record = ( AccountRecordSerializable ) input.readObject(); // exibe o conteúdo de registro System.out.printf( “%-10d%-12s%-12s%10.2f\n”, record.getAccount(), record.getFirstName(), record.getLastName(), record.getBalance() ); } // fim do while } // fim do try catch ( EOFException endOfFileException ) { return; // fim do arquivo foi alcançado } // fim do catch catch ( ClassNotFoundException classNotFoundException ) { System.err.println( "Unable to create object." ); } // fim do catch catch ( IOException ioException ) { System.err.println( "Error during read from file." ); } // fim do catch } // fim do método readRecords // fecha o arquivo e termina o aplicativo public void closeFile() { try // fecha o arquivo e encerra { if ( input != null ) input.close();

575

576 69 70 71 72 73 74 75 76

Capítulo 17

Arquivos, fluxos e serialização de objetos

} // fim do try catch ( IOException ioException ) { System.err.println( "Error closing file." ); System.exit( 1 ); } // fim do catch } // fim do método closeFile } // fim da classe ReadSequentialFile

Figura 17.19 | Lendo um arquivo de objetos sequencialmente com ObjectInputStream e exibindo cada registro.

O programa lê registros do arquivo no método readRecords (linhas 30–60). A linha 40 chama o método ObjectInputStream para ler um Object a partir do arquivo. Para utilizar os métodos específicos a AccountRecordSerializable, fazemos um downcast do Object retornado para o tipo AccountRecordSerializable. O método readObject lança uma EOFException (processada nas linhas 48–51) se ocorrer uma tentativa de leitura depois do fim do arquivo. O método readObject lança uma ClassNot­ FoundException se a classe para o objeto sendo lido não puder ser localizada. Isso pode ocorrer se o arquivo acessado em um computador não tiver essa classe. A Figura 17.20 contém o método main (linhas 6–13), que abre o arquivo, chama o método readRecords e fecha o arquivo. readObject

1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 17.20: ReadSequentialFileTest.java // Testando a classe ReadSequentialFile. public class ReadSequentialFileTest { public static void main( String[] args ) { ReadSequentialFile application = new ReadSequentialFile(); application.openFile(); application.readRecords(); application.closeFile(); } // fim de main } // fim da classe ReadSequentialFileTest

Account 100 200 300 400 500

First Name Bob Steve Pam Sam Sue

Last Name Jones Doe White Stone Rich

Balance 24.98 ­345.67 0.00 ­42.16 224.62

Figura 17.20 | Testando a classe ReadSequentialFile.

17.7 Classes java.io adicionais Essa seção apresenta uma visão geral das interfaces e classes adicionais (do pacote java.io) para fluxos de entrada e saída baseados em bytes e fluxos de entrada e saída baseados em caracteres.

17.7.1 Interfaces e classes para entrada e saída baseadas em bytes e OutputStream são classes abstract que declaram os métodos para realizar entrada e saída baseadas em bytes, respectivamente. Utilizamos várias subclasses concretas FileInputStream InputStream e OutputStream para manipular arquivos neste capítulo. InputStream

Fluxos de pipe Pipes são canais de comunicação sincronizados entre threads. Discutiremos threads no Capítulo 26, “Multithreading”. O Java fornece PipedOutputStream (uma subclasse de OutputStream) e PipedInputStream (uma subclasse de InputStream) para estabelecer pipes entre duas threads em um programa. Um thread envia dados a outro gravando em um PipedOutputStream. A thread-alvo lê informações do pipe via um PipedInputStream.



17.7  Classes java.io adicionais

577

Fluxos de filtro Um FilterInputStream filtra um InputStream e um FilterOutputStream filtra um OutputStream. A filtragem significa simplesmente que o fluxo do filtro fornece funcionalidades adicionais, como agregar bytes de dados a unidades de tipos primitivos significativas. FilterInputStream e FilterOutputStream são geralmente estendidas, assim algumas das suas capacidades de filtragem são fornecidas pelas suas subclasses. Um PrintStream (uma subclasse de FilterOutputStream) realiza a saída de texto para o fluxo especificado. Na verdade, já utilizamos a saída PrintStream por todo o texto até esse ponto — System.out e System.err são objetos PrintStream. Fluxos de dados Ler dados como bytes brutos é rápido, mas grosseiro. Normalmente, os programas leem os dados como agregado de bytes que formam ints, floats, doubles e assim por diante. Programas Java podem utilizar várias classes para inserir e gerar saída de dados na forma agregada. A interface DataInput descreve os métodos para ler tipos primitivos a partir de um fluxo de entrada. As classes DataInputStream e RandomAccessFile implementam essa interface para ler conjuntos de bytes e visualizá-los como valores de tipos primitivos. A interface DataInput inclui métodos como readBoolean, readByte, readChar, readDouble, readFloat, readFully (para arrays byte), readInt, readLong, readShort, readUnsignedByte, readUnsignedShort, readUTF (para ler caracteres Unicode codificados pelo Java — discutimos a codificação UTF no Apêndice L) e skipBytes. A interface DataOutput descreve o conjunto de métodos para gravar tipos primitivos em um fluxo de saída. As classes DataOutputStream (uma subclasse de FilterOutputStream) e RandomAccessFile implementam essa interface para gravar valores de tipos primitivos como bytes. A interface DataOutput inclui versões sobrecarregadas do método write (para um byte ou para um array byte) e os métodos writeBoolean, writeByte, writeBytes, writeChar, writeChars (para Strings Unicode), writeDouble, writeFloat, writeInt, writeLong, writeShort e writeUTF (para imprimir texto modificado para o Unicode). Fluxos armazenados em buffer Armazenamento em buffer (buffering) é uma técnica de aprimoramento do desempenho de E/S. Com um BufferedOutputStream (uma subclasse da classe FilterOutputStream), cada instrução de saída não necessariamente resulta em uma transferência física real de dados para o dispositivo de saída (uma operação lenta se comparada com as velocidades do processador e da memória principal). Em vez disso, cada operação de saída é dirigida para uma região na memória chamada buffer que é suficientemente grande para armazenar os dados de muitas operações de saída. Então a transferência real para o dispositivo de saída é realizada em uma grande operação física de saída toda vez que o buffer se enche. As operações de saída dirigidas para o buffer de saída na memória são frequentemente chamadas operações lógicas de saída. Com um BufferedOutputStream, um buffer parcialmente preenchido pode ser forçado a enviar para o dispositivo a qualquer momento invocando o método flush do objeto de fluxo. Utilizar o armazenamento em buffer pode aumentar significativamente o desempenho de um aplicativo. Operações típicas de E/S são extremamente lentas se comparadas à velocidade de acesso aos dados na memória do computador. O armazenamento em buffer reduz o número de operações de E/S combinando primeiro saídas menores na memória. O número de operações físicas reais de E/S é pequeno se comparado ao número de solicitações de E/S emitidas pelo programa. Portanto, o programa que utiliza armazenamento em buffer é mais eficiente. Dica de desempenho 17.1 E/S armazenada em buffer produz melhorias significativas de desempenho em relação a E/S não armazenada em buffer.

Com um BufferedInputStream (uma subclasse de classe FilterInputStream) muitos fragmentos ou trechos “lógicos” de dados de um arquivo são lidos como uma grande operação física de entrada em um buffer de memória. À medida que o programa solicita novos fragmentos de dados, eles são selecionados do buffer. (Esse procedimento é às vezes chamado de operação lógica de entrada.) Quando o buffer está vazio, a próxima operação física real de entrada do dispositivo de entrada é realizada para ler (read) o próximo grupo de trechos “lógicos” de dados. Portanto, o número de operações físicas reais de entrada é pequeno comparado ao número de solicitações de leitura emitido pelo programa.

Fluxos de array byte baseados em memória O fluxo de E/S do Java inclui capacidades para entrada de arrays de byte na memória e saída de arrays de byte na memória. Um ByteArrayInputStream (uma subclasse de InputStream) lê de um array byte na memória. Um ByteArrayOutputStream (uma subclasse de OutputStream) gera saída a um array byte na memória. Um dos usos de E/S de array de byte é a validação de dados. Um programa pode inserir uma linha inteira por vez do fluxo de entrada em um array de byte. Então uma rotina de validação pode escrutinar o conteúdo do array de byte e corrigir os dados se necessário. Por fim, o programa pode prosseguir para inserir do array de byte, “sabendo” que os dados de entrada estão no formato adequado. Dar saída para um array de byte é uma boa maneira de tirar proveito das poderosas capacidades de formatação de fluxos de saída do Java. Por exemplo, os dados podem ser armazenados em um array de byte, utilizando

578

Capítulo 17

Arquivos, fluxos e serialização de objetos

a mesma formatação que será exibida em um momento posterior; podemos, então, gerar a saída do array de byte em um arquivo para preservar a formatação.

Sequenciando entrada a partir de múltiplos fluxos Um SequenceInputStream (uma subclasse de InputStream) concatena logicamente vários InputStreams — o programa vê o grupo como um InputStream contínuo. Quando o programa alcança o fim de um fluxo de entrada, esse fluxo se fecha e o próximo fluxo na sequência se abre.

17.7.2 As interfaces e classes para entrada e saída baseada em caracteres Além dos fluxos baseados em bytes, o Java fornece as classes abstract Reader e Writer, que são fluxos baseados em caracteres de dois bytes Unicode. A maioria dos fluxos baseados em bytes tem classes Reader ou Writer concretas correspondentes baseadas em caracteres.

Leitores e gravadores para buffer baseados em caracteres As classes BufferedReader (uma subclasse da classe abstract Reader) e BufferedWriter (uma subclasse da classe abstract Writer) permitem armazenamento em buffer para fluxos baseados em caractere. Lembre-se de que fluxos baseados em caracteres utilizam caracteres Unicode — esses fluxos podem processar dados em qualquer idioma que o conjunto de caracteres Unicode representa. Leitores e gravadores de array char baseados na memória As classes CharArrayReader e CharArrayWriter leem e gravam, respectivamente, um fluxo de caracteres em um array de char. Um LineNumberReader (uma subclasse de Buffered­Reader) é um fluxo de caracteres armazenado em buffer que monitora o número de linhas lidas — novas linhas, combinações de quebra de linha e e retorno de carro incrementam a contagem de linhas. Monitorar os números da linha pode ser útil se o programa precisar informar o leitor sobre um erro em uma linha específica. Leitores e gravadores de arquivos baseados em caracteres, pipes e strings Um InputStream pode ser convertido para um Reader via classe InputStreamReader. De maneira semelhante, um OuputStream pode ser convertido para OutputStreamWriter. A classe FileReader (uma subclasse de InputStreamReader) e a classe FileWriter (uma subclasse de OutputStreamWriter) leem e gravam caracteres em um arquivo, respectivamente. As classes PipedReader e PipedWriter implementam fluxos de caracteres baseados em pipe para transferir dados entre threads. A classe StringReader e StringWriter leem e gravam caracteres em Strings, respectivamente. Uma PrintWriter grava caracteres em um fluxo.

17.8 Abrindo arquivos com JFileChooser A classe JFileChooser exibe uma caixa de diálogo (conhecida como caixa de diálogo JFileChooser) que permite ao usuário selecionar facilmente arquivos ou diretórios. Para demonstrar o diálogo de JFileChooser, aprimoramos o exemplo na Seção 17.4, como mostrado nas Figuras 17.21–17.22. O exemplo agora contém uma interface gráfica com o usuário, mas continua a exibir os mesmos dados como anteriormente. O construtor chama o método analyzePath na linha 34. Esse método chama então o método getFile na linha 68 para recuperar o objeto File. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

// Figura 17.21: FileDemonstration.java Demonstrando JFileChooser. import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; public class FileDemonstration extends JFrame { private JTextArea outputArea; // utilizado para saída private JScrollPane scrollPane; // utilizado para fornecer rolagem para saída // configura a GUI public FileDemonstration() { super( "Testing class File" );

23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

17.8  Abrindo arquivos com JFileChooser

outputArea = new JTextArea(); // adiciona outputArea a scrollPane scrollPane = new JScrollPane( outputArea ); add( scrollPane, BorderLayout.CENTER ); // adiciona scrollPane a GUI setSize( 400, 400 ); // configura o tamanho da GUI setVisible( true ); // exibe a GUI analyzePath(); // cria e analisa o objeto File } // fim do construtor de FileDemonstration // permite que o usuário especifique o nome de arquivo ou diretório private File getFileOrDirectory() { // exibe o diálogo de arquivo para que o usuário possa escolher o arquivo a abrir JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode( JFileChooser.FILES_AND_DIRECTORIES ); int result = fileChooser.showOpenDialog( this ); // se o usuário clicou no botão Cancel no diálogo, retorna if (result == JFileChooser.CANCEL_OPTION ) System.exit( 1 ); File fileName = fileChooser.getSelectedFile(); // obtém o arquivo // exibe erro se inválido if ( ( fileName == null ) || ( fileName.getName().equals( "" ) ) ) { JOptionPane.showMessageDialog( this, "Invalid Name", "Invalid Name", JOptionPane.ERROR_MESSAGE ); System.exit( 1 ); } // fim do if return fileName; } // fim do método getFile // exibe informações sobre o arquivo ou diretório que o usuário especifica public void analyzePath() { // cria o objeto File com base na entrada de usuário File name = getFileOrDirectory(); if ( name.exists() ) // se o nome existir, gera saída das informações sobre ele { // exibe informações sobre o arquivo (ou diretório) outputArea.setText( String.format( "%s%s\n%s\n%s\n%s\n%s%s\n%s%s\n%s%s\n%s%s\n%s%s", name.getName(), " exists", ( name.isFile() ? "is a file" : "is not a file" ), ( name.isDirectory() ? "is a directory" : "is not a directory" ), ( name.isAbsolute() ? "is absolute path" : "is not absolute path" ), "Last modified: ", name.lastModified(), "Length: ", name.length(), "Path: ", name.getPath(), "Absolute path: ", name.getAbsolutePath(), "Parent: ", name.getParent() ) ); if ( name.isDirectory() ) // listagem de diretório de saída { String[] directory = name.list(); outputArea.append( "\n\nDirectory contents:\n" ); for ( String directoryName : directory ) outputArea.append( directoryName + "\n" );

579

580 92 93 94 95 96 97 98 99 100

Capítulo 17  Arquivos, fluxos e serialização de objetos } // fim de else } // fim do if mais externo else // não se trata de um arquivo nem de um diretório, então imprime uma mensagem de erro { JOptionPane.showMessageDialog( this, name + " does not exist.", "ERROR", JOptionPane.ERROR_MESSAGE ); } // fim do else } // fim do método analyzePath } // fim da classe FileDemonstration

Figura 17.21  |  Demonstrando JFileChooser.

1 2 3 4 5 6 7 8 9 10 11 12

// Figura 17.22: FileDemonstrationTest.java // Testando a classe FileDemonstration. import javax.swing.JFrame; public class FileDemonstrationTest { public static void main( String[] args ) { FileDemonstration application = new FileDemonstration(); application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); } // fim de main } // fim da classe FileDemonstrationTest

Selecione a localização do arquivo ou diretório aqui

Arquivos e diretórios são exibidos aqui

Clique em Open para enviar o nome do arquivo ou diretório para o programa

Figura 17.22  |  Testando a classe FileDemonstration.

O método getFile é definido nas linhas 38–62 da Figura 17.21. A linha 41 cria um JFileChooser e atribui sua referência a fileChooser. As linhas 42–43 chamam o método setFileSelectionMode para especificar o que o usuário pode selecionar no fileChooser.

Para esse programa, utilizamos a constante JFileChooser static FILES_AND_DIRECTORIES para indicar que arquivos e diretórios podem ser selecionados. Outras constantes static incluem FILES_ONLY (o padrão) e DIRECTORIES_ONLY.

17.9 Conclusão

581

A linha 45 chama o método de showOpenDialog para exibir o diálogo de JFileChooser intitulado Open. O argumento this especifica a janela pai do diálogo de JFileChooser, que determina a posição do diálogo na tela. Se null for passado, o diálogo será exibido no centro da tela — caso contrário, o diálogo é centralizado na janela do aplicativo (especificado pelo argumento this). Um diálogo JFi­ leChooser é um diálogo modal que não permite ao usuário interagir com nenhuma outra janela no programa até que o usuário feche o JFileChooser clicando no botão Open ou Cancel. O usuário seleciona a unidade, diretório ou nome de arquivo e, então, clica em Open. O método showOpenDialog retorna um inteiro especificando em qual botão (Open ou Cancel) o usuário clicou para fechar o diálogo. A linha 48 testa se o usuário clicou em Cancel comparando o resultado com a constante static CANCEL_OPTION. Se forem iguais, o programa termina. A linha 51 recupera o arquivo que o usuário selecionou chamando o método getSelectedFile de JFileChooser. Então o programa exibe as informações sobre o arquivo ou diretório selecionado.

17.9 Conclusão Neste capítulo, você aprendeu a utilizar o processamento de arquivos para manipular dados persistentes. Você aprendeu que os dados são armazenados em computadores como 0s e 1s e que combinações desses valores são utilizadas para formar bytes, campos, registros e arquivos. Comparamos fluxos baseados em caracteres e em bytes, e introduzimos várias classes de processamento de arquivo fornecidas pelo pacote java.io. Você utilizou a classe File para recuperar informações sobre um arquivo ou diretório. Você utilizou o processamento de arquivo de acesso sequencial para manipular registros que são armazenados na ordem pelo campo de chave de registro. Você aprendeu as diferenças entre o processamento de arquivos de texto e a serialização de objetos e utilizou a serialização para armazenar e recuperar objetos inteiros. O capítulo concluiu com uma visão geral de outras classes fornecida no pacote java.io um pequeno exemplo da utilização de um diálogo de JFileChooser para permitir que os usuários possam selecionar facilmente arquivos em uma GUI. No próximo capítulo, você aprenderá o conceito da recursão — métodos que chamam a eles mesmos. Definir métodos dessa maneira pode resultar em programas mais intuitivos.

Resumo Seção 17.1 Introdução • Computadores utilizam arquivos para armazenamento de longo prazo de grandes volumes de dados persistentes, mesmo depois dos programas que criaram os dados terminarem. • Os computadores armazenam arquivos em dispositivos de armazenamento secundários, como discos rígidos.

Seção 17.2 Hierarquia de dados • O menor item de dados em um computador pode assumir o valor 0 ou o valor 1 e é chamado bit. Em última instância, um computador processa todos os itens de dados como combinações de zeros e uns. • O conjunto de caracteres do computador é o conjunto de todos os caracteres utilizados para escrever programas e representar dados. • Os caracteres em Java são caracteres Unicode compostos de dois bytes, cada um composto de oito bits. • Um campo é um grupo de caracteres ou bytes que transmitem um significado. • Itens de dados processados pelos computadores formam uma hierarquia de dados que se torna, com relação à estrutura, maior e mais complexa à medida que progredimos de bits para caracteres, campos e assim por diante. • Em geral, vários campos compõem um registro (implementado como uma classe em Java). • Um registro é um grupo de campos relacionados. Um arquivo é um grupo de registros relacionados. • Uma chave de registro identifica um registro como pertencente a uma pessoa ou empresa em particular e é única para cada registro. Chaves de registro facilitam a recuperação de registros específicos a partir de um arquivo. • Há muitas maneiras de organizar registros em um arquivo. O mais comum é chamado arquivo sequencial, em que registros são armazenados na ordem pelo campo-chave de registro. • Um grupo de arquivos relacionados costuma ser chamado de banco de dados. Uma coleção de programas projetados para criar e gerenciar bancos de dados é chamada sistema de gerenciamento de bancos de dados (DataBase Management System — DBMS).

Seção 17.3 Arquivos e fluxos • O Java vê cada arquivo como um fluxo sequencial de bytes. • Cada sistema operacional fornece um mecanismo para determinar o fim de um arquivo, como um marcador de fim de arquivo ou uma contagem dos bytes totais no arquivo. • Os fluxos baseados em bytes representam dados no formato binário. • Os fluxos baseados em caracteres representam dados como sequências de caracteres.

582

Capítulo 17  Arquivos, fluxos e serialização de objetos

• Os arquivos criados utilizando os fluxos baseados em bytes são arquivos binários. Os arquivos criados utilizando os fluxos baseados em caracteres são arquivos de texto. Os arquivos de texto podem ser lidos por editores de textos, enquanto arquivos binários são lidos por um programa que converte os dados em um formato legível por humanos. • O Java também pode associar fluxos a diferentes dispositivos. Três objetos stream estão associados com dispositivos quando um programa Java começa a executar — System.in, System.out e System.err. • O pacote java.io inclui classes de fluxo, como FileInputStream (para entrada baseada em bytes em um arquivo), FileOutputStream (para saída baseada em bytes para um arquivo), FileReader (para entrada baseada em caracteres em um arquivo) e FileWriter (para saída baseada em caracteres para um arquivo). Os arquivos são abertos criando objetos dessas classes de fluxo.

Seção 17.4  Classe File • A classe File é utilizada para obter informações sobre arquivos e diretórios. • A entrada e saída baseada em caracteres pode ser realizada com as classes Scanner e Formatter. • A classe Formatter permite que a saída de dados formatados na tela ou para um arquivo de uma maneira semelhante a System.out.printf. • Um caminho de arquivo ou diretório especifica sua localização em disco. • Um caminho absoluto contém todos os diretórios desde o diretório-raiz que levam a um arquivo ou diretório específico. Cada arquivo ou diretório em uma unidade de disco tem o mesmo diretório-raiz em seu caminho. • Um caminho relativo normalmente inicia do diretório em que o aplicativo começou a execução. • Um caractere separador é utilizado para separar diretórios e arquivos no caminho.

Seção 17.5  Arquivos de texto de acesso sequencial • O Java não impõe nenhuma estrutura a um arquivo. Você deve estruturar os arquivos para satisfazer as necessidades do seu aplicativo. • Para recuperar dados sequencialmente de um arquivo, os programas normalmente começam a ler a partir do início do arquivo e leem todos os dados consecutivamente até que as informações desejadas sejam encontradas. • Os dados em muitos arquivos sequenciais não podem ser modificados sem o risco de destruir outros dados no arquivo. Portanto, registros em um arquivo de acesso sequencial normalmente não são atualizados no lugar. Em vez disso, o arquivo inteiro é normalmente regravado.

Seção 17.6  Serialização de objeto • O Java fornece um mecanismo chamado serialização de objetos que permite a objetos inteiros serem gravados ou lidos de um fluxo. • Um objeto serializado é representado como uma sequência de bytes que inclui os dados do objeto bem como as informações sobre o tipo de objeto e os tipos de dados armazenados no objeto. • Depois que um objeto serializado foi gravado em um arquivo, ele pode ser lido a partir do arquivo e desserializado para recriar o objeto na memória. • As classes ObjectInputStream e ObjectOutputStream permitem que objetos inteiros sejam lidos de ou gravados em um fluxo (possivelmente um arquivo). • Somente as classes que implementam a interface Serializable podem ser serializadas e desserializadas. • A interface ObjectOutput contém o método writeObject, que recebe um Object como um argumento e grava suas informações em um Output­ Stream. Uma classe que implementa essa interface, como ObjectOutputStream, asseguraria que o Object é Serializable. • A interface ObjectInput contém o método readObject, que lê e retorna uma referência a um Object a partir de um InputStream. Depois que um objeto foi lido, podemos fazer uma coerção da sua referência para o tipo real do objeto.

Seção 17.7  Classes java.io adicionais • InputStream e OutputStream são classes abstract para executar a entrada e saída baseada em bytes. • As classes FileInputStream (uma subclasse de InputStream) e FileOutputStream (uma subclasse de OutputStream) manipulam arquivos. • Pipes são canais de comunicação sincronizados entre threads. Um thread envia dados a outro gravando em um PipedOutputStream. O thread alvo lê informações do pipe via um PipedInputStream. • Um fluxo do filtro fornece funcionalidades adicionais, como agregar bytes de dados a unidades de tipos primitivos significativas. FilterInputStream e FilterOutputStream são, em geral, classes estendidas, assim algumas das suas capacidades de filtragem são fornecidas pelas suas subclasses concretas. • Um PrintStream executa a saída de texto. System.out e System.err são objetos PrintStream. • A interface DataInput descreve os métodos para ler tipos primitivos a partir de um fluxo de entrada. As classes DataInputStream e RandomAccess­ File implementam essa interface. • A interface DataOutput descreve os métodos para gravar tipos primitivos em um fluxo de saída. As classes DataOutputStream e RandomAccessFile implementam essa interface. • Armazenamento em buffer (buffering) é uma técnica de aprimoramento do desempenho de E/S. Operações típicas de E/S são extremamente lentas se comparadas ao acesso dos dados na memória do computador. O armazenamento em buffer reduz o número de operações de E/S combinando saídas menores na memória. O número de operações físicas reais de E/S é muito menor do que o número de solicitações de E/S emitidas pelo programa.



Terminologia

583

• Com um BufferedOutputStream cada operação de saída é direcionada a um buffer suficientemente grande para conter os dados de muitas operações de saída. A transferência real para o dispositivo de saída é realizada em uma grande operação física de saída toda vez que o buffer é preenchido. Um buffer parcialmente preenchido pode ser forçado a enviar para o dispositivo a qualquer momento invocando o método flush do objeto de fluxo. • Com um BufferedInputStream, muitos fragmentos ou trechos “lógicos” de dados de um arquivo são lidos como uma grande operação física de entrada em um buffer de memória. À medida que um programa solicita dados, ele é selecionado do buffer. Quando o buffer está vazio, a próxima operação de entrada física real é executada. • Um ByteArrayInputStream lê a partir de um array byte na memória. Um ByteArrayOutputStream gera saída de um array de bytes na memória. • Um SequenceInputStream concatena vários InputStreams. Quando o programa alcança o fim de um fluxo de entrada, esse fluxo se fecha e o próximo fluxo na sequência se abre. • As classes Reader e Writer abstract são fluxos baseados em caracteres Unicode. A maioria dos fluxos baseados em bytes tem classes Reader ou Writer concretas correspondentes baseadas em caracteres • As classes BufferedReader e BufferedWriter permitem o armazenamento em buffer de fluxos baseados em caracteres. • As classes CharArrayReader e CharArrayWriter manipulam um array de chars na memória. • Um LineNumberReader é um fluxo de caracteres armazenado em buffer que monitora o número de linhas lido. • As classes FileReader e FileWriter leem caracteres de e gravam caracteres em um arquivo. • A classe PipedReader e a classe PipedWriter implementam fluxo de caracteres baseados em pipe para transferir dados entre threads. • As classes StringReader e StringWriter leem e gravam caracteres em Strings, respectivamente. Uma PrintWriter grava caracteres em um fluxo.

Seção 17.8  Abrindo arquivos com JFileChooser • A classe JFileChooser é utilizada para exibir um diálogo que permite aos usuários de um programa selecionar facilmente arquivos ou diretórios em uma GUI.

Terminologia %n, formatar o especificador (separador de

linha), 562 abrir um arquivo, 554 arquivo, 552 arquivo binário, 554 arquivo de acesso sequencial, 553 arquivo de texto, 554 ASCII (American Standard Code for Information Interchange), conjunto de caracteres, 553 banco de dados, 553 bit (dígito binário), 552 buffer, 577 BufferedInputStream, classe, 577 BufferedOutputStream, classe, 577 BufferedReader, classe, 578 BufferedWriter, classe, 578 byte, 553 byte, tipo primitivo, 553 caminho absoluto, 555 caminho relativo, 555 campo, 553 CANCEL_OPTION, constante de JFileChooser, 581 caractere, 553 caractere separador, 558 CharArrayReader, classe, 578 CharArrayWriter, classe, 578 close, método de Formatter, 562 close, método de ObjectOutputStream, 574 conjunto de caracteres, 553 dados persistentes, 552 DataInputStream, classe, 577 DataOutputStream, classe, 577 desserializar um objeto, 570 dígito binário (bit), 552

dígito decimal, 553 DIRECTORIES_ONLY, constante de JFileChooser, 580

diretório-raiz, 555 dispositivo de armazenamento secundário, 552 empacotando objetos fluxo, 570 EOFException, classe, 576 E/S armazenada em buffer, 577 exit, método da classe System, 561 File, classe, 555 FileInputStream, classe, 555 FileNotFoundException, classe, 561 FileOutputStream, classe, 555 FileReader, classe, 555 FILES_AND_DIRECTORIES, constante de JFileChooser, 580 FILES_ONLY, constante de JFileChooser, 580 FileWriter, classe, 555 FilterInputStream, classe, 577 FilterOutputStream, classe, 577 filtrar um fluxo, 577 flush, método da classe BufferedOutputStream, 577 fluxo baseado em bytes, 554 fluxo baseado em caracteres, 554 fluxo de bytes, 554 format, método da classe Formatter, 562 Formatter, classe, 555 FormatterClosedException, classe, 562 getSelectedFile, método da classe JFileChooser, 581 IllegalStateException, classe, 565 informações de caminho, 555 InputStream, classe, 576 InputStreamReader, classe, 578

interface de tags, 570 IOException, classe, 574 isFile, método de File, 556 java.io, pacote, 555 JFileChooser, classe, 578 letra, 553 LineNumberReader, classe, 578 lote, arquivo, 562 marcador de fim de arquivo, 554 NoSuchElementException, classe, 562 ObjectInput, interface, 570 ObjectInputStream, classe, 555 ObjectOutput, interface, 570 ObjectOutputStream, classe, 555 objeto desserializado, 570 objeto serializado, 570 operação física de entrada, 577 operação física de saída, 577 operações lógicas de entrada, 577 operações lógicas de saída, 577 OutputStream, classe, 576 OutputStreamWriter, classe, 578 pipe, 576 PipedInputStream, classe, 576 PipedOutputStream, classe, 576 PipedReader, classe, 578 PipedWriter, classe, 578 PrintStream, classe, 577 PrintWriter, classe, 562 Reader, classe, 578 readObject, método de ObjectInput, 570 redirecionar um fluxo padrão, 555 registro, 553 registro, chave, 553

584

Capítulo 17  Arquivos, fluxos e serialização de objetos

script de shell, 562 SecurityException, classe, 561 Serializable, interface, 570 serialização de objeto, 570 setErr, método da classe System, 555 setFileSelectionMode, método da classe JFileChooser, 580 setIn, método da classe System, 555 setOut, método de System, 555

showOpenDialog, método da classe JFileChooser, 581

símbolos especiais, 553 sistema de gerenciamento de bancos de dados (DataBase Management System — DBMS), 553 StringReader, classe, 578 StringWriter, classe, 578 transient, palavra-chave, 572 truncar o conteúdo de um arquivo, 560

Unicode, conjunto de caracteres, 553 Uniform Resource Identifier (URI), 555 Uniform Resource Locator (URL), 555 writeObject, método da interface ObjectOutput, 570 Writer, classe, 578

Exercícios de autorrevisão 17.1 Preencha as lacunas em cada uma das seguintes afirmações: a) Em última instância, todos os itens de dados processados por um computador são reduzidos a combinações de ________ e ________. b) O menor item de dados que um computador pode processar é chamado de ________. c) Um ________ às vezes pode ser visualizado como um grupo de registros relacionados. d) Dígitos, letras e símbolos especiais são referidos como ________. e) Um banco de dados é um grupo de ________ relacionados. f) O objeto ________ normalmente permite a um programa gerar saída de mensagens de erros na tela.

17.2 Determine se cada uma das sentenças é verdadeira ou falsa. Se falsa, explique por quê. a) Você deve criar explicitamente os objetos de fluxo System.in, System.out e System.err. b) Ao ler dados de um arquivo utilizando a classe Scanner, se você quiser ler dados no arquivo múltiplas vezes, o arquivo deve ser fechado e reaberto para ler a partir do início do arquivo. c) O método exists da classe File retorna true se o nome especificado como o argumento para o construtor File for um arquivo ou diretório no caminho especificado. d) Arquivos binários são legíveis por seres humanos em um editor de textos. e) Um caminho absoluto contém todos os diretórios desde o diretório-raiz que levam a um arquivo ou diretório específico. f) A classe Formatter contém o método printf, que permite gerar a saída de dados formatados na tela ou para um arquivo.

17.3 Complete as seguintes tarefas, supondo que cada uma se aplica ao mesmo programa: a) Escreva uma instrução que abre o arquivo "oldmast.txt" para entrada — utilize a variável Scanner inOldMaster. b) Escreva uma instrução que abre o arquivo "trans.txt" para entrada — utilize a variável Scanner inTransaction. c) Escreva uma instrução que abre arquivo "newmast.txt" para saída (e criação) — utilize a variável formatter de outNewMaster. d) Escreva as instruções necessárias para ler um registro do arquivo "oldmast.txt". Utilize os dados para criar um objeto da classe AccountRe­ cord — use a variável Scanner inOldMaster. Suponha que a classe AccountRecord é idêntica à classe AccountRecord na Figura 17.5. e) Escreva as instruções necessárias para ler um registro do arquivo "trans.txt". O registro é um objeto da classe TransactionRecord — use a variável Scanner inTransaction. Suponha que a classe TransactionRecord contenha o método setAccount (que recebe um int) para configurar o número de conta e o método setAmount (que recebe um double) para configurar o valor monetário da transação. f) Escreva uma instrução que gera a saída de um registro para o arquivo "newmast.txt". O registro é um objeto do tipo AccountRecord — use a variável Formatter outNewMaster.

17.4 Complete as seguintes tarefas, supondo que cada uma se aplica ao mesmo programa: a) Escreva uma instrução que abre o arquivo "oldmast.ser" para entrada — use a variável ObjectInputStream inOldMaster para empacotar um objeto FileInputStream. b) Escreva uma instrução que abre o arquivo "trans.ser" para entrada — use a variável ObjectInputStream inTransaction para empacotar um objeto FileInputStream. c) Escreva uma instrução que abre arquivo "newmast.ser" para saída (e criação) — utilize variável Object-OutputStream outNewMaster para empacotar um FileOutputStream. d) Escreva uma instrução que lê um registro no arquivo "oldmast.ser". O registro é um objeto da classe AccountRecordSerializable — use a variável ObjectInputStream inOldMaster. Assuma que a classe AccountRecordSerializable é a mesma que a classe Account­ Record-Serializable na Figura 17.16 e) Escreva uma instrução que lê um registro no arquivo "trans.ser". O registro é um objeto da classe TransactionRecord — use a variável ObjectInputStream inTransaction. f) Escreva uma instrução que gera saída de um registro do tipo AccountRecordSerializable para o arquivo "newmast.ser" — use a variável ObjectOutputStream outNewMaster.



Respostas dos exercícios de autorrevisão

585

17.5 Encontre o erro em cada bloco de código e mostre como corrigi-lo. a) Assuma que account, company e amount são declarados.

ObjectOutputStream outputStream; outputStream.writeInt( account ); outputStream.writeChars( company ); outputStream.writeDouble( amount );

b) As seguintes instruções devem ler um registro do arquivo "payables.txt". A variável inPayable de Scanner deve ser usada para referir-se a esse arquivo.

Scanner inPayable = new Scanner( new File( "payables.txt" ) ); PayablesRecord record = ( PayablesRecord ) inPayable.readObject();

Respostas dos exercícios de autorrevisão 17.1 17.2

17.3

a) uns, zeros. b) bit. c) arquivo. d) caracteres. e) arquivos. f) System.err. a) Falsa. Esses três fluxos são criados para você quando um aplicativo Java inicia a execução. b) Verdadeira. c) Verdadeira. d) Falsa. Arquivos de texto são legíveis por seres humanos em um editor de textos. Arquivos binários podem ser legíveis por seres humanos, mas apenas se os bytes no arquivo representarem caracteres ASCII e) Verdadeira. f) Falsa. A classe Formatter contém o método format, que permite gerar a saída de dados formatados na tela ou para um arquivo. a) Scanner inOldMaster = new Scanner( new File ( "oldmast.txt" ) ); b) Scanner inTransaction = new Scanner( new File( "trans.txt" ) ); c) Formatter outNewMaster = new Formatter( "newmast.txt" ); d) AccountRecord account = new AccountRecord(); account.setAccount( inOldMaster.nextInt() ); account.setFirstName( inOldMaster.next() ); account.setLastName( inOldMaster.next() ); account.setBalance( inOldMaster.nextDouble() );

17.4

e)

TransactionRecord transaction = new Transaction(); transaction.setAccount( inTransaction.nextInt() ); transaction.setAmount( inTransaction.nextDouble() );

f)

outNewMaster.format( "%d %s %s %.2f\n", account.getAccount(), account.getFirstName(), account.getLastName(), account.getBalance() );

a) ObjectInputStream inOldMaster = new ObjectInputStream( new FileInputStream( "oldmast.ser" ) ); b) ObjectInputStream inTransaction = new ObjectInputStream( new FileInputStream( "trans.ser" ) );

c)

ObjectOutputStream outNewMaster = new ObjectOutputStream( new FileOutputStream( "newmast.ser" ) );

d) accountRecord = ( AccountRecordSerializable ) inOldMaster.readObject();

e) transactionRecord = ( TransactionRecord ) inTransaction.readObject(); f) outNewMaster.writeObject( newAccountRecord ); 17.5 a) Erro: O arquivo não foi aberto antes da tentativa de gerar a saída dos dados para o fluxo. Correção: Abra um arquivo para saída criando um novo objeto de ObjectOutputStream que empacota um objeto de FileOutputStream. b) Erro: Esse exemplo utiliza arquivos de texto com um Scanner; não há nenhuma serialização de objeto. Como resultado, o método readOb­ ject não pode ser utilizado para ler esses dados do arquivo. Cada fragmento de dados deve ser lido separadamente e então utilizado para criar um objeto de PayablesRecord. Correção: Utilize os métodos de inPayable para ler cada parte do objeto PayablesRecord.

Exercícios 17.6 Preencha as lacunas em cada uma das seguintes afirmações: a) Os computadores armazenam quantidades grandes de dados em dispositivos de armazenamento secundários como ________. b) O ________ é composto de vários campos. c) Para facilitar a recuperação de registros específicos de um arquivo, um campo em cada registro é escolhido como uma ________. d) Os arquivos criados utilizando fluxos baseados em bytes são chamados arquivos________ , enquanto arquivos criados utilizando fluxos baseados em caracteres são chamados arquivos ________. e) Os objetos padrão de fluxo são ________, ________ e ________.

586

Capítulo 17  Arquivos, fluxos e serialização de objetos

17.7 Determine quais das seguintes afirmações são verdadeiras e quais são falsas. Se falso, explique por quê. a) As impressionantes funções realizadas pelos computadores envolvem essencialmente a manipulação de zeros e uns. b) As pessoas especificam programas e itens de dados como caracteres. Os computadores então manipulam e processam esses caracteres como grupos de zeros e uns. c) Os itens de dados representados em computadores assumem uma hierarquia de dados em que itens de dados tornam-se cada vez maiores e mais complexos à medida que progredimos de campos para caracteres, então para bits e assim por diante. d) Uma chave de registro identifica um registro como pertencente a um campo particular. e) As empresas armazenam todas suas informações em um único arquivo para facilitar o processamento dessas informações pelo computador. Quando um programa cria um arquivo, o arquivo é retido pelo computador para referência futura.

17.8 (Correspondência de arquivos) O Exercício de autorrevisão 17.3 pede que você escreva uma série de instruções únicas. De fato, essas instru-

ções formam o núcleo de um importante tipo de programa processador de arquivo, a saber, um programa de correspondência de arquivo (filematching program). Em processamento de dados comercial, é comum ter vários arquivos em cada sistema de aplicativo. Em um sistema de contas a receber, por exemplo, há em geral um arquivo-mestre contendo informações detalhadas sobre cada cliente, como seu nome, endereço, número de telefone, saldo, limite de crédito, termos de desconto, arranjos de contrato e possivelmente um histórico condensado de compras recentes e pagamentos de conta. À medida que as transações ocorrem (isto é, são feitas vendas e pagamentos chegam pelo correio), as informações sobre elas são inseridas em um arquivo. No fim de cada período de negócios (um mês para algumas empresas, uma semana para outras e um dia em alguns casos), o arquivo de transações (chamado "trans.txt") é aplicado ao arquivo-mestre (chamado "oldmast.txt") para atualizar o registro de compra e pagamento de cada conta. Durante uma atualização, o arquivo-mestre é regravado como o arquivo "newmast.txt", que é então utilizado no fim do próximo período de negócios para começar o processo de atualização novamente. Programas de correspondência de arquivo devem lidar com certos problemas que não surgem em programas de um único arquivo. Por exemplo, nem sempre ocorre uma correspondência. Se um cliente no arquivo-mestre não fez nenhuma compra ou pagamentos à vista no período de negócios atual, nenhum registro para esse cliente aparecerá no arquivo de transações. De maneira semelhante, um cliente que fez alguma compra ou pagamento em dinheiro poderia apenas ter mudado para essa comunidade e a empresa pode não ter tido uma oportunidade de criar um registro-mestre para esse cliente. Escreva um programa completo de correspondência de arquivos de contas a receber. Utilize o número de conta em cada arquivo como a chave de registro para propósitos de correspondência. Assuma que cada arquivo é um arquivo de texto sequencial com registros armazenados em ordem de número de conta crescente. a) Defina a classe TransactionRecord. Os objetos dessa classe contêm um número de conta e o valor monetário para a transação. Forneça métodos para modificar e recuperar esses valores monetários. b) Modifique a classe AccountRecord na Figura 17.5 para incluir o método combine, que recebe um objeto TransactionRecord e combina o saldo do objeto AccountRecord e o valor da quantidade monetária do objeto TransactionRecord. c) Escreva um programa para criar dados a fim de testar o programa. Utilize os dados da conta de exemplo nas figuras 17.23 e 17.24. Execute o programa para criar os arquivos trans.txt e oldmast.txt a serem utilizados pelo seu programa de correspondência de arquivos.

Número de conta de arquivo-mestre

Nome

Saldo

100

Alan Jones

348.17

300

Mary Smith

27.19

500

Sam Sharp

0.00

700

Suzy Green

–14.22

Figura 17.23  |  Dados de exemplo para o arquivo-mestre.

Número de conta de arquivo de transação

Quantia da transação

100

27.14

300

62.11

400

100.56

900

82.17

Figura 17.24  |  Dados de exemplo para o arquivo de transações. d) Crie a classe FileMatch para realizar a funcionalidade de correspondência de arquivos. A classe deve conter métodos que leem oldmast. txt e trans.txt. Quando uma correspondência ocorre (isto é, registros com o mesmo número de conta aparecem tanto no arquivo-mestre como no arquivo de transações), adicione o valor monetário no registro de transação ao saldo atual no registro-mestre e grave o registro em "newmast.txt". (Suponha que compras são indicadas por valores monetários positivos no arquivo de transações e os pagamentos por valores



Exercícios

587

monetários negativos). Quando houver um registro-mestre para uma conta particular, mas nenhum correspondente registro de transação, simplesmente grave o registro mestre em "newmast.txt". Se houver um registro de transação, mas nenhum registro-mestre correspondente, imprima a mensagem "Unmatched transaction record for account number…" [Registro de transação não correspondido para o número da conta] em um arquivo de log (preencha o número da conta a partir do registro de transação). O arquivo de log deve ser um arquivo de texto chamado "log.txt".

17.9 (Correspondência de arquivos com múltiplas transações) É possível (e, na verdade, comum) ter vários registros de transações com a mesma

chave de registro. Essa situação ocorre, por exemplo, quando um cliente faz várias compras e pagamentos à vista durante um período de negócios. Reescreva seu programa de correspondência de arquivo de contas a receber do Exercício 17.8 para oferecer a possibilidade de tratamento de vários registros de transação com a mesma chave de registro. Modifique os dados de teste de CreateData.java para incluir registros de transações adicionais na Figura 17.25.

Número de conta

Quantia em dólar

300

83.89

700

80.78

700

1.53

Figura 17.25  |  Registros de transações adicionais. 17.10 (Correspondência de arquivos com serialização de objetos) Recrie sua solução para o Exercício 17.9 utilizando a serialização de objetos. Utilize as instruções do Exercício 17.4 como sua base para esse programa. Talvez você queira criar aplicativos para ler os dados armazenados nos arquivos .ser — o código na Seção 17.6.2 pode ser modificado para esse propósito.

17.11 (Gerador de palavra de número de telefone) Os teclados de telefone padrão contêm os dígitos de zero a nove. Os números 2 a 9 têm três

letras associadas a cada número (Figura 17.26). Muitas pessoas acham difícil memorizar números de telefone, então utilizam a correspondência entre dígitos e letras para criar palavras de sete letras que correspondem com seus números de telefone. Por exemplo, uma pessoa cujo número de telefone é 686-2377 talvez utilize a correspondência indicada na Figura 17.26 para desenvolver a palavra de sete letras “NUMBERS”. Cada palavra de sete letras corresponde a exatamente um número de telefone de sete dígitos. Um restaurante que deseja aumentar seu negócio de entregas em domicílio (“takeout”, em inglês) seguramente poderia fazer isso com o número 825-3688 (isto é, “TAKEOUT)”.

Dígito

Letras

Dígito

Letras

Dígito

Letras

2

A B C

5

J K L

8

T U V

3

D E F

6

M N O

9

W X Y

4

G H I

7

P R S

Figura 17.26  |  Dígitos e letras do teclado do telefone. Cada número de telefone de sete letras corresponde a muitas diferentes palavras de sete letras, mas a maioria dessas palavras representa justaposições irreconhecíveis das letras. É possível, porém, que o proprietário de um salão de cabeleireiro ficasse contente em saber que o número de telefone de seu salão, 424-7288, corresponde a “HAIRCUT” (corte de cabelo, em inglês). Um veterinário com o número de telefone 738-2273 ficaria satisfeito em saber que seu número corresponde à palavra de sete letras “PETCARE” (cuidado de animais de estimação). Um vendedor de automóvel ficaria satisfeito em saber que o número de telefone de sua loja, 639-2277, corresponde a “NEWCARS”. Escreva um programa que, dado um número de sete dígitos, utiliza um objeto PrintStream para gravar em um arquivo cada possível combinação de palavras de sete letras correspondente a esse número. Há 2.187 (37) dessas combinações. Evite números de telefone com os dígitos 0 e 1.

17.12 (Pesquisa entre alunos) A Figura 7.8 contém um array de respostas a uma pesquisa que é codificado diretamente no programa. Suponha que

queremos processar os resultados dessa pesquisa que são armazenados em um arquivo. Este exercício requer dois programas separados. Primeiro, crie um aplicativo que solicita ao usuário respostas à pesquisa e gera a saída de cada resposta para um arquivo. Utilize um Formatter para criar um arquivo chamado numbers.txt. Cada inteiro deve ser escrito com o método format. Então modifique o programa na Figura 7.8 para ler as respostas à pesquisa a partir de numbers.txt. As respostas devem ser lidas do arquivo utilizando um Scanner. Utilize o método nextInt para gerar saída de um número inteiro em um dado momento a partir do arquivo. O programa deve continuar a ler respostas até alcançar o fim do arquivo. A saída dos resultados deve ser gerada no arquivo de texto "output.txt".

17.13 (Adicionando serialização de objetos ao aplicativo de desenho MyShape) Modifique o Exercício 14.17 para permitir ao usuário salvar

um desenho em um arquivo ou carregar um desenho prévio a partir de um arquivo utilizando serialização de objetos. Adicione botões Load (para ler objetos de um arquivo) e Save (para gravar objetos em um arquivo). Utilize um ObjectOutputStream para gravar dados no arquivo e um ObjectInputStream para ler dados do arquivo. Escreva o array de objetos MyShape utilizando o método writeObject (classe ObjectOutput­ Stream) e leia o array utilizando o método readObject (ObjectInputStream). Observe que o mecanismo de serialização de objeto pode ler ou gravar arrays inteiros — não é necessário manipular cada elemento do array de objetos MyShape individualmente. Simplesmente é exigido que todas as formas sejam Serializable. Para os dois botões Load e Save, utilize um JFileChooser para permitir que o usuário selecione o arqui-

588

Capítulo 17  Arquivos, fluxos e serialização de objetos vo em que as formas serão armazenadas ou do qual elas serão lidas. Quando o usuário executa o programa pela primeira vez, nenhuma forma deve ser exibida na tela. O usuário pode exibir formas abrindo um arquivo anteriormente salvo ou desenhando novas formas. Havendo formas na tela, os usuários podem salvá-las em um arquivo utilizando o botão Save.

Fazendo a diferença 17.14 (Scanner de phishing) Phishing é uma forma de furto de identidade em que, em um e-mail, um remetente, que se faz passar por uma fonte

confiável, tenta adquirir informações privadas, como nomes de usuário, senhas, números de cartão de crédito e cadastros de pessoas físicas. Falsos e-mails de conhecidos bancos, empresas de cartão do crédito, sites de leilão, redes sociais e serviços de pagamento on-line podem parecer bastante autênticos. Essas mensagens fraudulentas muitas vezes fornecem links para sites Web falsos nos quais você é solicitado a inserir informações confidenciais. Visite McAfee® (www.mcafee.com/us/threat_center/anti_phishing/phishing_top10.html), Security Extra (www.securityex­ tra.com/), www.snopes.com e outros sites Web para localizar listas das principais fraudes relacionadas ao phishing. Também verifique o AntiPhishing Working Group (www.antiphishing.org/), e o site Web Cyber Investigations do FBI (www.fbi.gov/cyberinvest/cyberhome. htm), nos quais você localizará informações sobre as fraudes mais recentes e como se proteger. Crie uma lista de 30 palavras, frases e nomes de empresas comumente encontrados nas mensagens de phishing. Atribua um valor em pontos a cada um com base na sua estimativa da probabilidade de a mensagem ser um esquema de phishing (por exemplo, um ponto se for mais ou menos provável, dois pontos se moderadamente provável ou três pontos se altamente provável). Escreva um aplicativo que procura esses termos e frases em um arquivo de texto. Para cada ocorrência de uma frase ou palavra-chave dentro do arquivo de texto, adicione o valor em ponto atribuído aos pontos totais para essa palavra ou frase. Para cada frase ou palavra-chave encontrada, imprima uma linha com a palavra ou frase, o número de ocorrências e o total em pontos. Mostre então o total em pontos da mensagem inteira. Seu programa atribui um total alto em pontos a alguns e-mails de phishing reais que você recebeu? Ele atribui um total alto em pontos a alguns e-mails legítimos que você recebeu?

Devemos aprender a explorar todas as opções e possibilidades que nos confrontam em um mundo complexo e em constante e rápida mudança. — James William Fulbright

O! thou hast damnable iteration, and art indeed able to corrupt a saint. [Fazes sempre citações execráveis; és capaz de corromper um santo.] — William Shakespeare

É um tipo pobre de memória que só funciona para trás. — Lewis Carroll

A vida só pode ser compreendida olhando-se para trás; mas só pode ser vivida olhando-se para frente. — Soren Kierkegaard

Prossiga — continue andando. — Thomas Morton

18

Recursão

Objetivos Neste capítulo, você aprenderá: 

O conceito de recursão.



Como escrever e utilizar métodos recursivos.



Como determinar o caso básico e o passo de recursão em um algoritmo recursivo.



Como chamadas de método recursivo são tratadas pelo sistema.



As diferenças entre recursão e iteração, e quando é apropriado utilizar cada uma.



O que são formas geométricas chamadas fractais e como desenhá-las utilizando a recursão.



O que é reversão recursiva (recursive backtracking) e por que é uma técnica efetiva para a resolução de problemas.

Sumário

590

Capítulo 18

Recursão

18.1 Introdução

18.6 Recursão vs. Iteração

18.2 Conceitos de recursão

18.7 Torres de Hanói

18.3 Exemplo que utiliza recursão: fatoriais

18.8 Fractais

18.4 Exemplo que utiliza recursão: série de Fibonacci

18.9 Retorno recursivo

18.5 Recursão e a pilha de chamadas de método

18.10 Conclusão

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios

18.1 Introdução Os programas que discutimos até aqui geralmente são estruturados como métodos que chamam uns aos outros de uma maneira hierárquica. Para alguns problemas, é útil ter uma chamada de método própria. Um método que faz isso é conhecido como método recursivo. Um método recursivo pode chamar a si próprio direta ou indiretamente por outro método. A recursão é um tópico importante discutido demoradamente em cursos de ciência da computação de nível superior. Neste capítulo, consideramos a recursão conceitualmente, então apresentamos vários programas que contêm métodos recursivos. A Figura 18.1 resume os exemplos e os exercícios de recursão deste livro. Capítulo

Exemplos de recursão e exercícios neste livro

18

Método fatorial (figuras 18.3 e 18.4) Método de Fibonacci (Figura 18.5) Torres do Hanói (Figura 18.11) Fractais (figuras 18.18 e 18.19) O que faz esse código? (Exercício 18.7, Exercício 18.12 e Exercício 18.13) Localize o erro no seguinte código (Exercício 18.8) Elevando um inteiro à potência de um inteiro (Exercício 18.9) Visualizando a recursão (Exercício 18.10) Máximo divisor comum (Exercício 18.11) Determine se uma string é um palíndromo (Exercício 18.14) Oito rainhas (Exercício 18.15) Imprima um array (Exercício 18.16) Imprima um array de trás para frente (Exercício 18.17) Valor mínimo em um array (Exercício 18.18) Estrela fractal (Exercício 18.19) Percorrendo um labirinto com a reversão recursiva (Exercício 18.20) Gerando labirintos aleatoriamente (Exercício 18.21) Labirintos de qualquer tamanho (Exercício 18.22) Tempo necessário para calcular um número de Fibonacci (Exercício 18.23)

19

Classificação por intercalação (figuras 19.10 e 19.11) Pesquisa linear (Exercício 19.8) Pesquisa binária (Exercício 19.9) Classificação rápida (Quicksort) (Exercício 19.10)

22

Inserção de árvore binária (Figura 22.17) Percorrendo uma árvore binária na pré-ordem (Figura 22.17) Percorrendo uma árvore binária na ordem (Figura 22.17) Percorrendo uma árvore binária na pós-ordem (Figura 22.17) Impressão de uma lista vinculada de trás para frente (Exercício 22.20) Pesquisa em uma lista vinculada (Exercício 22.21)

Figura 18.1 | Resumo dos exemplos e exercícios de recursão neste texto.

18.2 Conceitos de recursão

591

18.2 Conceitos de recursão Abordagens de solução de problemas de recursão têm um número de elementos em comum. Quando um método recursivo é chamado para resolver um problema, na verdade, ele é capaz de resolver somente o(s) caso(s) mais simples(s), ou caso(s) básico(s). Se o método é chamado com um caso básico, ele retorna um resultado. Se o método for chamado com um problema mais complexo, em geral, ele divide o problema em duas partes conceituais — uma parte que o método sabe como fazer e uma parte que ele não sabe. Para tornar a recursão realizável, a última parte deve assemelhar-se ao problema original, mas ser uma versão ligeiramente mais simples ou menor dele. Como esse novo problema é parecido com o problema original, o método chama uma cópia nova dele próprio para trabalhar no problema menor — isso é referido como chamada recursiva e também é denominado passo de recursão. O passo de recursão normalmente inclui a instrução return uma vez que seu resultado será combinado com a parte do problema que o método sabia como resolver para formar um resultado que será passado de volta para o chamador original. Esse conceito de separar o problema em duas partes menores é uma forma da abordagem de dividir para conquistar introduzida no Capítulo 6. O passo de recursão executa enquanto a chamada de método original ainda está aberta (isto é, não terminou de executar). Ele pode resultar em muitas outras chamadas recursivas à medida que o método divide cada novo subproblema em duas partes conceituais. Para a recursão por fim terminar, toda vez que o método chamar a si próprio com uma versão mais simples do problema original, a sequência de problemas cada vez menores deve convergir para um caso básico. Quando o método reconhece o caso básico, ele retorna um resultado para a cópia anterior do método. Uma sequência de retornos segue até a chamada de método original retornar o resultado final para o chamador. Um método recursivo pode chamar outro método, que, por sua vez, pode fazer uma chamada de volta ao método recursivo. Isso é conhecido como uma chamada recursiva indireta ou recursão indireta. Por exemplo, o método A chama o método B, que faz uma chamada de volta ao método A. Isso ainda é recursão, porque a segunda chamada para o método A é feita enquanto a primeira chamada ao método A está ativa, isto é, a primeira chamada ao método A ainda não concluiu sua execução (porque está esperando o método B retornar um resultado para ela) e não retornou ao chamador original do método A. Para entender melhor o conceito de recursão, veremos um exemplo que é bem comum aos usuários de computador — a definição recursiva de um diretório em um computador. Um computador normalmente armazena arquivos relacionados em um diretório. Um diretório pode estar vazio, pode conter arquivos e/ou conter outros diretórios (normalmente conhecidos como subdiretórios). Cada um desses subdiretórios, por sua vez, também pode conter tanto arquivos como diretórios. Se quisermos listar cada arquivo em um diretório (incluindo todos os arquivos nos subdiretórios do diretório), precisamos criar um método que primeiro lista os arquivos do diretório inicial e, então, faz chamadas recursivas para listar os arquivos em todos os subdiretórios desse diretório. O caso básico ocorre quando um diretório que é alcançado não contém nenhum subdiretório. Nesse ponto, todos os arquivos no diretório original foram listados e nenhuma recursão adicional é necessária.

18.3 Exemplo que utiliza recursão: fatoriais Vamos escrever um programa recursivo para efetuar um cálculo matemático popular. Considere o fatorial de um inteiro positivo n, escrito n! (pronuncia-se “n fatorial”), que é o produto n · (n – 1) · (n – 2) · … · 1

com 1! igual a 1 e 0! definido como 1. Por exemplo, 5! é o produto 5 · 4 · 3 · 2 · 1, que é igual a 120. O fatorial de inteiro number (onde number ≥ 0) pode ser calculado iterativamente (não recursivamente) utilizando-se uma instrução for como a seguinte: factorial = 1; for ( int counter = number; counter >= 1; counter­­ ) factorial *= counter;

Chega-se a uma declaração recursiva do método fatorial observando o seguinte relacionamento: n! = n · (n – 1)!

Por exemplo, 5! é claramente igual a 5 · 4!, como mostrado pelas seguintes equações: 5! = 5 · 4 · 3 · 2 · 1 5! = 5 · (4 · 3 · 2 · 1) 5! = 5 · (4!)

A avaliação de 5! prosseguiria como mostrado na Figura 18.2. A Figura 18.2(a) mostra como a sucessão de chamadas recursivas prossegue até 1! (o caso básico) é avaliado como 1, que termina a recursão. A Figura 18.2(b) mostra os valores retornados de cada chamada recursiva para seu chamador até que o valor final seja calculado e retornado.

592

Capítulo 18  Recursão Valor final = 120 5!

5!

5! = 5 * 24 = 120 é retornado 5 * 4!

5 * 4!

4! = 4 * 6 = 24 é retornado # 4 * 3! # . # 3 * 2! # # # 2 * 1! # # # 1 # (a) Sequência de chamadas recursivas. #

# . . # . # . # . # . #

# . # # . # . . . # . #

# . . . . # # # . # . #

# # # # # 4 # *# 3! # # . . . . . . # 3! = 3 * 2 = 6 é retornado # . # # # # . # # . . . . # . # 3 * 2! . # # # . # . . 2! = 2 * 1 = 2 é retornado . # . # . # . # . # . # . # . # 2 * 1! . # . # . # . # 1 é retornado . . . . . # . # # # . # # # . # 1 . . . # . . . # #(b) # Valores # # #retornados # # # de cada chamada recursiva.

Figura 18.2  |  Avaliação recursiva de 5!

A Figura 18.3 utiliza a recursão para calcular e imprimir os fatoriais dos inteiros de 0–21. O método recursivo factorial (linhas 7–13) primeiro testa para determinar se uma condição de término (linha 9) é true. Se number for menor que ou igual a 1 (o caso básico), factorial retorna 1, nenhuma recursão adicional é necessária e o método retorna. Se number for maior que 1, a linha 12 expressa o problema como o produto de number e uma chamada recursiva para factorial avaliando o fatorial de number - 1, que é um problema ligeiramente menor que o cálculo original, factorial( number ). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

// Figura 18.3: FactorialCalculator.java // Método fatorial recursivo. public class FactorialCalculator { // método recursivo fatorial (assume que seu parâmetro é >= 0) public long factorial( long number ) { if ( number ­­> ­­> ­­> ­­> ­­>

3 2 2 3 1 3 3

Figura 18.11 | Solução do problema das Torres de Hanói com um método recursivo.

18.8 Fractais Um fractal é uma figura geométrica que pode ser gerada de um padrão repetido recursivamente (Figura 18.12). A figura é modificada aplicando o padrão a cada segmento da figura original. Examinaremos algumas dessas aproximações nesta seção. [Nota: iremos nos referir às nossas figuras geométricas como fractais, ainda que sejam aproximações]. Embora essas figuras tenham sido estudadas antes do século XX, foi o matemático Benoit Mandelbrot que introduziu o termo “fractal” na década de 1970, com as especificidades de como um fractal é criado e de suas aplicações práticas. A geometria fractal de Mandelbrot fornece modelos matemáticos para muitas formas complexas encontradas na natureza, como montanhas, nuvens e litorais. Os fractais têm muitos usos na matemática e ciência. Eles podem ser utilizados para entender melhor os sistemas ou padrões que aparecem na natureza (por exemplo, ecossistemas), no corpo humano (por exemplo, nas circunvoluções cerebrais) ou no universo (por exemplo, grupos de galáxias). Nem todos os fractais se parecem com objetos na natureza. Desenhar fractais tornou-se uma forma popular de arte. Os fractais têm uma propriedade autossimilar — quando subdivididos em partes, cada parte parece uma cópia de tamanho reduzido do total. Muitos fractais produzem uma cópia exata do original quando uma parte do fractal é ampliada — diz-se que um fractal é estritamente autossimilar. Consulte nosso Recursion Resource Center (www.deitel.com) para sites web que demonstram fractais. (a) Nível 0

(b) Nível 1

(c) Nível 2

(d) Nível 3

(e) Nível 4

(f) Nível 5

Figura 18.12 | Fractal Curva de Koch.



18.8  Fractais

601

Como exemplo, vejamos o fractal Curva de Koch estritamente autossimilar (Figura 18.12). Ele é formado removendo o terço médio de cada linha no desenho e substituindo-o por duas linhas que formam um ponto, de tal modo que se esse terço médio permanecesse no meio da linha original, um triângulo equilateral seria formado. As formas para criar fractais costumam envolver a remoção de toda ou parte da imagem fractal anterior. Esse padrão já foi determinado para esse fractal — focalizamos aqui como usar essas fórmulas em uma solução recursiva. Iniciamos com uma linha reta (Figura 18.12(a)) e aplicamos o padrão, criando um triângulo a partir do terço médio (Figura 18.12(b)). Então, aplicamos novamente o padrão a cada linha reta, resultando na Figura 18.12(c). Toda vez que o padrão for aplicado, dizemos que o fractal está em um novo nível, ou profundidade (às vezes, o termo ordem também é utilizado). Fractais podem ser exibidos em muitos níveis — por exemplo, um fractal no nível 3 teve três iterações do padrão aplicado (Figura 18.12(d)). Depois de apenas algumas iterações, esse fractal começa a parecer com uma parte de um floco de neve (Figura 18.12(e) e (f)). Visto que esse é um fractal estritamente autossimilar, cada parte dele contém uma cópia exata do fractal. Na Figura 18.12(f), por exemplo, destacamos uma parte do fractal com uma caixa vermelha tracejada. Se aumentássemos o tamanho da imagem nessa caixa, ela seria exatamente semelhante ao fractal inteiro da parte (f). Um fractal semelhante, o Floco de neve de Koch, é o mesmo da Curva de Koch, mas inicia com um triângulo em vez de iniciar com uma linha. O mesmo padrão é aplicado a cada lado do triângulo, resultando em uma imagem semelhante a um floco de neve fechado. Para simplificar, escolhemos focalizar a Curva de Koch. Para aprender mais sobre a Curva de Koch e Floco de neve de Koch, consulte os links no nosso Recursion Resource Center (www.deitel.com).

“Fractal de Lo” Agora demonstramos o uso da recursão para desenhar fractais escrevendo um programa para criar um fractal estritamente autossimilar. Chamamos esse fractal de “fractal de Lo,” em homenagem a Sin Han Lo, um colega da Deitel & Associates que o criou. O fractal por fim será parecido com uma metade de uma pena (ver a saída na Figura 18.19). O caso básico, ou nível fractal de 0, inicia como uma linha entre dois pontos, A e B (Figura 18.13). Para criar o próximo nível mais alto, localizamos o ponto intermediário (C) da linha. Para calcular a localização do ponto C, utilizamos a seguinte fórmula: xC = (xA + xB) / 2; yC = (yA + yB) / 2;

[Nota: o x e y à esquerda de cada letra referem-se à coordenada x e à coordenada y desse ponto, respectivamente. Por exemplo, xA refere-se à coordenada x do ponto A, enquanto yC refere-se à coordenada y do ponto C. Em nossos diagramas denotamos o ponto por sua letra, seguido por dois números que representam as coordenadas x e y]. Para criar esse fractal, também devemos localizar um ponto D que resida à esquerda de segmento AC e cria um triângulo isósceles reto ADC. Para calcular a localização do ponto D, utilize as seguintes fórmulas: xD = xA + (xC - xA) / 2 - (yC - yA) / 2; yD = yA + (yC - yA) / 2 + (xC - xA) / 2;

A (6, 5)

B (30, 5)

Origem (0, 0)

Figura 18.13  |  “Fractal de Lo” no nível 0.

Agora nos movemos do nível 0 para o nível 1 como mostrado a seguir: Inicialmente, adicionamos os pontos C e D (como na Figura 18.14). Então, removemos a linha original e adicionamos os segmentos DA, DC e DB. As linhas restantes se curvarão em um ângulo, fazendo com que nosso fractal se pareça com uma pena. Para o próximo nível do fractal, esse algoritmo é repetido em cada uma das três linhas no nível 1. Para cada linha, as fórmulas acima são aplicadas, nas quais o primeiro ponto D agora é considerado o ponto A, enquanto a outra extremidade de cada linha é considerada o ponto B. A Figura 18.15 contém a linha do nível 0 (agora uma linha tracejada) e três linhas adicionais do nível 1. Mudamos o ponto D para o ponto A, e os pontos originais A, C e B para B1, B2 e B3, respectivamente. As fórmulas precedentes foram utilizadas para localizar os novos pontos C e D em cada linha. Esses pontos também são numerados 1–3 para monitorar que ponto está associado com cada linha. Os

602

Capítulo 18  Recursão

pontos C1 e D1, por exemplo, representam os pontos C e D associados com a linha formada a partir do ponto A para o ponto B1. Para alcançar o nível 2, as três linhas na Figura 18.15 são removidas e substituídas pelas novas linhas dos pontos C e D que acabaram de ser adicionados. A Figura 18.16 mostra as novas linhas (as linhas do nível 2 são mostradas como linhas tracejadas para sua conveniência). A Figura 18.17 mostra o nível 2 sem as linhas tracejadas do nível 1. Uma vez que esse processo foi repetido várias vezes, o fractal criado começará a parecer-se com metade de uma pena, como mostrado na saída da Figura 18.19. Apresentaremos o código para esse aplicativo em breve.

D (12, 11)

A (6, 5)

C (18, 5)

B (30, 5)

Origem (0, 0)

Figura 18.14  |  Determinando os pontos C e D para o nível 1 do “Fractal de Lo”.

D3 (18, 14) A (12, 11) C1 (9, 8) D1 (12, 8) B1 (6, 5)

D2 (15, 11) C2 (15, 8)

B2 (18, 5)

C3 (21, 8)

B3 (30, 5)

Origem (0, 0)

Figura 18.15  |  “Fractal de Lo” no nível 1, com os pontos C e D determinados para o nível 2. [Nota: o fractal no nível 0 está incluído como uma linha tracejada como um lembrete de onde a linha foi localizada em relação ao fractal atual.]

Origem (0, 0)

Figura 18.16  |  “Fractal de Lo” no nível 2, com linhas tracejadas do nível 1 fornecido.



18.8  Fractais

603

Origem (0, 0)

Figura 18.17  |  “Fractal de Lo” no nível 2.

O aplicativo na Figura 18.18 define a interface com o usuário para desenhar esse fractal (mostrado no fim da Figura 18.19). A interface consiste em três botões — um para o usuário alterar a cor do fractal, um para aumentar o nível de recursão e um para diminuir o nível de recursão. Um JLabel monitora o nível atual de recursão, que é modificado chamando-se o método setLevel, a ser discutido em breve. As linhas 15–16 especificam que as constantes WIDTH e HEIGHT são 400 e 480 respectivamente, para o tamanho do JFrame. O usuário provoca um ActionEvent clicando no botão Color. O handler de evento para esse botão é registrado nas linhas 37–53. O método action­ Performed exibe um JColorChooser. Esse diálogo retorna o objeto Color selecionado ou azul (se o usuário pressionar Cancel ou fechar o diálogo sem pressionar OK). A linha 50 chama o método setColor na classe FractalJPanel para atualizar a cor. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

// Figura 18.18: Fractal.java // Interface com o usuário do fractal. import java.awt.Color; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JColorChooser; public class Fractal extends JFrame { private static final int WIDTH = 400; // define a largura de GUI private static final int HEIGHT = 480; // define a altura de GUI private static final int MIN_LEVEL = 0, MAX_LEVEL = 15; private JButton changeColorJButton, increaseLevelJButton, decreaseLevelJButton; private JLabel levelJLabel; private FractalJPanel drawSpace; private JPanel mainJPanel, controlJPanel; // configura a GUI public Fractal() { super( "Fractal" ); // configura o painel de controle controlJPanel = new JPanel(); controlJPanel.setLayout( new FlowLayout() ); // configura o botão de cor e registra o ouvinte changeColorJButton = new JButton( "Color" ); controlJPanel.add( changeColorJButton ); changeColorJButton.addActionListener( new ActionListener() // classe interna anônima { // processa o evento changeColorJButton

604 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

Capítulo 18  Recursão public void actionPerformed( ActionEvent event ) { Color color = JColorChooser.showDialog( Fractal.this, "Choose a color", Color.BLUE ); // configura a cor padrão, se nenhuma cor for retornada if ( color == null ) color = Color.BLUE; drawSpace.setColor( color ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim de addActionListener // configura o botão decrease level para adicionar o painel de controle // ouvinte registrado decreaseLevelJButton = new JButton( "Decrease Level" ); controlJPanel.add( decreaseLevelJButton ); decreaseLevelJButton.addActionListener( new ActionListener() // classe interna anônima { // processa o evento decreaseLevelJButton public void actionPerformed( ActionEvent event ) { int level = drawSpace.getLevel(); --level; // diminui o nível por um // modifica o nível se possível if ( ( level >= MIN_LEVEL ) ) && ( level = MIN_LEVEL ) ) && ( level list1 = new LinkedList< String >(); for ( String color : colors ) list1.add( color ); // adiciona elementos colors2 à list2 String[] colors2 = { "gold", "white", "brown", "blue", "gray", "silver" }; List< String > list2 = new LinkedList< String >(); for ( String color : colors2 ) list2.add( color ); list1.addAll( list2 ); // concatena as listas list2 = null; // libera recursos printList( list1 ); // imprime elementos list1 convertToUppercaseStrings( list1 ); // converte para string maiúscula printList( list1 ); // imprime elementos list1 System.out.print( "\nDeleting elements 4 to 6..." ); removeItems( list1, 4, 7 ); // remove itens 4-6 da lista printList( list1 ); // imprime elementos list1 printReversedList( list1 ); // imprime lista na ordem inversa } // fim de main // gera saída do conteúdo de List private static void printList(List< String > list ) { System.out.println( "\nlist: " ); for ( String color : list ) System.out.printf( "%s ", color ); System.out.println(); } // fim do método printList // localiza objetos String e converte em letras maiúsculas private static void convertToUppercaseStrings(List< String > list ) { ListIterator< String > iterator = list.listIterator(); while (iterator.hasNext() ) { String color = iterator.next(); // obtém o item iterator.set( color.toUpperCase() ); // converte em letras maiúsculas } // fim do while } // fim do método convertToUppercaseStrings // obtém sublista e utiliza método clear para excluir itens da sublista private static void removeItems(List< String > list, int start, int end ) { list.subList( start, end ).clear(); // remove os itens } // fim do método removeItems // imprime lista invertida private static void printReversedList(List< String > list ) { ListIterator< String > iterator = list.listIterator( list.size() );



20.6  Listas

75 76 77 78 79 80 81

643

System.out.println( "\nReversed List:" ); // imprime lista na ordem inversa while (iterator.hasPrevious() ) System.out.printf( "%s ", iterator.previous() ); } // fim do método printReversedList } // fim da classe ListTest

list: black yellow green blue violet silver gold white brown blue gray silver list: BLACK YELLOW GREEN BLUE VIOLET SILVER GOLD WHITE BROWN BLUE GRAY SILVER Deleting elements 4 to 6... list: BLACK YELLOW GREEN BLUE WHITE BROWN BLUE GRAY SILVER Reversed List: SILVER GRAY BLUE BROWN WHITE BLUE GREEN YELLOW BLACK

Figura 20.3  |  Lists, LinkedLists e ListIterators.

As linhas 14 e 22 criam as LinkedLists list1 e list2 do tipo String. LinkedList é uma classe genérica que tem um único parâmetro de tipo para o qual especificamos o argumento de tipo String nesse exemplo. As linhas 16–17 e 24–25 chamam o método List add para acrescentar elementos dos arrays colors e colors2 no fim de list1 e list2, respectivamente. A linha 27 chama o método List addAll para acrescentar todos os elementos de list2 ao final de list1. A linha 28 configura list2 como null, então LinkedList que list2 referenciou pode sofrer coleta de lixo. A linha 29 chama o método printList (linhas 41–49) para gerar saída do conteúdo de list1. A linha 31 chama o método convertToUppercaseStrings (linhas 52–61) para converter cada elemento String em letras maiúsculas, então a linha 32 chama novamente printList para exibir as Strings modificadas. A linha 35 chama o método removeItems (linhas 64–68) para remover os elementos que iniciam no índice 4 até, mas não incluindo, o índice 7 da lista. A linha 37 chama o método printReversedList (linhas 71–80) para imprimir a lista em ordem inversa.

Método convertToUppercaseStrings O método convertToUppercaseStrings (linhas 52–61) converte de letras minúsculas em letras maiúsculas os elementos String do seu argumento List. A linha 54 chama o método List listIterator para obter o iterador bidirecional da List (isto é, um que pode percorrer um List para trás ou para frente). ListIterator também é uma classe genérica. Nesse exemplo, o List-Iterator referencia objetos String, porque o método listIterator é chamado em uma List de Strings. A linha 56 chama o método hasNext para determinar se a List contém outro elemento. A linha 58 obtém a próxima String na List. A linha 59 chama o método String toUpperCase para obter uma versão em letras maiúsculas da String e chama o método ListIterator set para substituir a String atual que iterator referencia pela String retornada pelo método toUpperCase. Como o método toUpperCase, o método String toLowerCase retorna uma versão em letras minúsculas da String. Método removeItems O método removeItems (linhas 64–68) remove um intervalo de itens da lista. A linha 67 chama o método List subList para obter uma parte da List (chamada de sublista). Esse é chamado método de visualização de intervalo, que permite ao programa examinar uma parte da lista. A sublista é simplesmente uma visualização na List em que subList é chamada. O método subList aceita como argumentos o índice inicial e o índice final para a sublista. O índice final não faz parte do intervalo da sublista. Nesse exemplo, a linha 35 passa 4 para o índice inicial e 7 para o índice final da subList. A sublista retornada é o conjunto de elementos com os índices de 4 a 6. Em seguida, o programa chama o método List clear na sublista para remover os elementos da sublista da List. Qualquer alteração feita em uma sublista também será feita na List original. Método printReversedList O método printReversedList (linhas 71–80) imprime a lista de trás para frente. A linha 73 chama o método List listItera­ tor com a posição inicial como um argumento (no nosso caso, o último elemento na lista) para obter um iterador bidirecional para a lista. O método List size retorna o número de itens na List. A condição while (linha 78) chama o método hasPrevious de ListIterator para determinar se há mais elementos ao percorrer a lista de trás para frente. A linha 79 chama o método previous de ListIterator a fim de obter o elemento anterior da lista e o envia para o fluxo de saída padrão. Visualizações em coleções e o método Arrays asList Um recurso importante da estrutura de coleções é a capacidade de manipular os elementos de um tipo de coleção (como um conjunto) por um tipo diferente de coleção (como uma lista), independentemente da implementação interna da coleção. O conjunto de métodos public pelo qual as coleções são manipuladas é chamado de visualização.

644

Capítulo 20  Coleções genéricas

A classe Arrays fornece método static asList para examinar um array (às vezes chamado array de apoio) como uma coleção List. Uma visualização List permite manipular o array como se ele fosse uma lista. Isso é útil para adicionar os elementos em um array

a uma coleção (por exemplo, um LinkedList) e para classificar elementos de array. O próximo exemplo demonstra como criar uma LinkedList com uma visualização List de um array, porque não podemos passar o array para um construtor LinkedList. A classifica-

ção de elementos de array com uma visualização List é demonstrada na Figura 20.7. Qualquer modificação feita pela visualização List altera o array, e qualquer modificação feita no array altera a visualização List. A única operação permitida na visualização retornada por asList é set, o que altera o valor da visualização e o array de apoio. Qualquer outra tentativa de alterar a visualização (como adicionar ou remover elementos) resulta em uma UnsupportedOperationException.

Visualizando arrays como Lists e convertendo Lists em arrays A Figura 20.4 utiliza método Arrays asList para visualizar um array como uma List e utiliza o método List toArray para obter um array de uma coleção LinkedList. O programa chama o método asList para criar uma visualização List de um array, que é utilizada para inicializar um objeto LinkedList, então adiciona uma série de strings a uma LinkedList e chama o método toArray para obter um array contendo referências às Strings. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

// Figura 20.4: UsingToArray.java // Visualizando arrays como Lists e convertendo Lists em arrays. import java.util.LinkedList; import java.util.Arrays; public class UsingToArray { // cria uma LinkedList, adiciona elementos e converte em array public static void main( String[] args ) { String[] colors = { "black", "blue", "yellow" }; LinkedList< String > links = new LinkedList< String >( Arrays.asList( colors ) ); links.addLast( “red” ); // adiciona como o último item links.add( “pink” ); // adiciona ao final links.add( 3, “green” ); // adiciona no terceiro índice links.addFirst( “cyan” ); // adiciona como primeiro item // obtém elementos LinkedList como um array colors = links.toArray( new String[ links.size() ] ); System.out.println( "colors: " ); for ( String color : colors ) System.out.println( color ); } // fim de main } // fim da classe UsingToArray

colors: cyan black blue yellow green red pink

Figura 20.4  |  Visualizando arrays como Lists e convertendo Lists em arrays.

As linhas 13–14 constroem uma LinkedList de Strings contendo os elementos do array colors. A linha 14 utiliza o método Ar­ rays asList para retornar uma visualização List do array, em seguida utiliza isso para inicializar a LinkedList com seu construtor que recebe um Collection como um argumento (uma List é uma Collection). A linha 16 chama o método LinkedList addLast para adicionar "red" ao fim de links. As linhas 17–18 chamam o método LinkedList add para adicionar "pink" como o último elemento e "green" como o elemento no índice 3 (isto é, o quarto elemento). O método addLast (linha 16) funciona de modo idêntico ao método add (linha 17). A linha 19 chama o método LinkedList addFirst para adicionar "cyan" como o novo primeiro item na LinkedList. As operações add são permitidas porque operam no objeto LinkedList, não na visualização retornada por asList. [Nota: Quando "cyan" é adicionado como o primeiro elemento, "green" torna-se o quinto elemento na LinkedList.]

20.7 Métodos de coleções

645

A linha 22 chama o método toArray da interface List para obter um array String a partir de links. O array é uma cópia dos elementos da lista — modificar o conteúdo do array não modifica a lista. O array passado para o método toArray é do mesmo tipo que você gostaria que o método toArray retornasse. Se o número de elementos no array for maior ou igual ao número de elementos na LinkedList, toArray copia os elementos da lista em seu argumento de array e retorna esse array. Se a LinkedList tiver mais elementos que o número de elementos no array passado para toArray, toArray aloca um novo array do mesmo tipo que ele recebe como um argumento, copia os elementos da lista no novo array e retorna o novo array.

Erro comum de programação 20.2 Passar um array que contém dados para toArray pode causar erros de lógica. Se o número de elementos no array for menor que o número de elementos na lista em que toArray é chamado, um novo array é alocado para armazenar os elementos da lista — sem preservar os elementos do argumento de array. Se o número de elementos no array for maior que o número de elementos na lista, os elementos do array (iniciando no índice zero) serão sobrescritos pelos elementos da lista. Os elementos do array que não são sobrescritos retêm seus valores.

20.7 Métodos de coleções A classe Collection fornece vários algoritmos de alto desempenho para manipular elementos de coleção. Os algoritmos (Figura 20.5) são implementados como métodos static. Os métodos sort, binarySearch, reverse, shuffle, fill e copy operam em Lists. Os métodos min, max, addAll, frequency e disjoint operam em Collections. Método

Descrição

sort

Classifica os elementos de uma List.

binarySearch

Localiza um objeto em uma List.

reverse

Inverte os elementos de uma List.

shuffle

Ordena aleatoriamente os elementos de uma List.

fill

Configura todo elemento List para referir-se a um objeto especificado.

copy

Copia referências de uma List em outra.

min

Retorna o menor elemento em uma Collection.

max

Retorna o maior elemento em uma Collection.

addAll

Acrescenta todos os elementos em um array a uma Collection.

frequency

Calcula quantos elementos da coleção são iguais ao elemento especificado.

disjoint

Determina se duas coleções não têm nenhum elemento em comum.

Figura 20.5 | Métodos Collections.

Observação de engenharia de software 20.4 Os métodos da estrutura de coleções são polimórficos. Isto é, cada um deles pode operar em objetos que implementam interfaces específicas, independentemente da implementação subjacente.

20.7.1 Método sort O método sort classifica os elementos de uma List, que deve implementar a interface Comparable. A ordem é determinada pela ordem natural do tipo dos elementos como implementado por um método compareTo. O método compareTo é declarado na interface Comparable e às vezes é chamado método natural de comparação. A chamada sort pode especificar como um segundo argumento um objeto Comparator que determina uma ordem alternativa dos elementos.

Classificando na ordem crescente A Figura 20.6 utiliza o método Collections sort para ordenar os elementos de uma List em ordem crescente (linha 17). Lembre-se de que List é um tipo genérico e aceita como argumento o tipo de elemento da lista — a linha 14 cria list como uma List de Strings. Observe que as linhas 15 e 20 utilizam, cada uma, uma chamada implícita para o método toString da list para gerar saída do conteúdo da lista no formato mostrado na segunda e quarta linhas da saída.

646

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

Capítulo 20  Coleções genéricas

// Figura 20.6: Sort1.java // Método Collections sort. import java.util.List; import java.util.Arrays; import java.util.Collections; public class Sort1 { public static void main( String[] args ) { String[] suits = { "Hearts", "Diamonds", "Clubs", "Spades" }; // Cria e exibe uma lista que contém os elementos do array de ternos List< String > list = Arrays.asList( suits ); // cria List System.out.printf( "Unsorted array elements: %s\n", list ); Collections.sort( list ); // classifica ArrayList // gera a saída da lista System.out.printf( "Sorted array elements: %s\n", list ); } // fim de main } // fim da classe Sort1

Unsorted array elements: [Hearts, Diamonds, Clubs, Spades] Sorted array elements: [Clubs, Diamonds, Hearts, Spades]

Figura 20.6  |  Método Collections

sort.

Classificando em ordem decrescente A Figura 20.7 classifica a mesma lista das strings utilizadas na Figura 20.6 em ordem decrescente. O exemplo introduz a interface Comparator, que é utilizado para classificar elementos de uma Collection em uma ordem diferente. A linha 18 chama o método sort de Collections para ordenar a List em ordem decrescente. O método static Collections reverseOrder retorna um objeto Com­ parator que ordena os elementos da coleção na ordem inversa. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

// Figura 20.7: Sort2.java // Utilizando um objeto Comparator com o método sort. import java.util.List; import java.util.Arrays; import java.util.Collections; public class Sort2 { public static void main( String[] args ) { String[] suits = { "Hearts", "Diamonds", "Clubs", "Spades" }; // Cria e exibe uma lista que contém os elementos do array de ternos List< String > list = Arrays.asList( suits ); // cria List System.out.printf( "Unsorted array elements: %s\n", list ); // classifica em ordem decrescente utilizando um comparador Collections.sort( list, Collections.reverseOrder() ); // gera a saída de elementos List System.out.printf( "Sorted list elements: %s\n", list ); } // fim de main } // fim da classe Sort2

Unsorted array elements: [Hearts, Diamonds, Clubs, Spades] Sorted list elements: [Spades, Hearts, Diamonds, Clubs]

Figura 20.7  |  O método Collections

sort

com um objeto Comparator.



20.7  Métodos de coleções

647

Classificando com um Comparator A Figura 20.8 cria uma classe Comparator personalizada, chamada TimeComparator, que implementa a interface Comparator para comparar dois objetos Time2. A classe Time2, declarada na Figura 8.5, representa o tempo com horas, minutos e segundos. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

// Figura 20.8: TimeComparator.java // Classe Comparator personalizada que compara dois objetos Time2. import java.util.Comparator; public class TimeComparator implements Comparator< Time2 > { public int compare(Time2 time1, Time2 time2 ) { int hourCompare = time1.getHour() - time2.getHour(); // compara hora // testa a primeira hora if ( hourCompare != 0 ) return hourCompare; int minuteCompare = time1.getMinute() - time2.getMinute(); // compara minuto // então testa o minuto if ( minuteCompare != 0 ) return minuteCompare; int secondCompare = time1.getSecond() - time2.getSecond(); // compara segundo return secondCompare; // retorna o resultado da comparação de segundos } // fim do método compare } // fim da classe TimeComparator

Figura 20.8  |  Classe Comparator personalizada que compara dois objetos Time2.

A classe TimeComparator implementa a interface Comparator, um tipo genérico que aceita um argumento (nesse caso Time2). Uma classe que implementa Comparator deve declarar um método compare que recebe dois argumentos e retorna um número inteiro negativo se o primeiro argumento for menor que o segundo, 0 se os argumentos forem iguais ou um número inteiro positivo se o primeiro argumento for maior do que o segundo. O método compare (linhas 7–26) realiza comparações entre objetos Time2. A linha 9 compara as duas horas dos objetos Time2. Se as horas forem diferentes (linha 12), então retornamos esse valor. Se esse valor for positivo, então a primeira hora é maior que a segunda e o primeiro tempo é maior que o segundo. Se esse valor for negativo, então a primeira hora é menor que a segunda e o primeiro tempo é menor que o segundo. Se esse valor for zero, as horas serão as mesmas e devemos testar os minutos (e talvez os segundos) para determinar que tempo é maior. A Figura 20.9 classifica uma lista que utiliza a classe Comparator TimeComparator personalizada. A linha 11 cria uma ArrayList de objetos Time2. Lembre-se de que ArrayList e List são tipos genéricos e aceitam um argumento de tipo que especifica o tipo de elemento da coleção. As linhas 13–17 criam cinco objetos Time2 e os adicionam a essa lista. A linha 23 chama o método sort, passando para ele um objeto de nossa classe TimeComparator (Figura 20.8). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

// Figura 20.9: Sort3.java // Método Collections sort com um objeto Comparator personalizado. import java.util.List; import java.util.ArrayList; import java.util.Collections; public class Sort3 { public static void main( String[] args ) { List< Time2 > list = new ArrayList< Time2 >(); // cria List list.add( new Time2( 6, 24, 34 ) ); list.add( new Time2( 18, 14, 58 ) ); list.add( new Time2( 6, 05, 34 ) );

648 16 17 18 19 20 21 22 23 24 25 26 27 28

Capítulo 20  Coleções genéricas list.add( new Time2( 12, 14, 58 ) ); list.add( new Time2( 6, 24, 22 ) ); // gera a saída de elementos List System.out.printf( "Unsorted array elements:\n%s\n", list ); // classifica em ordem utilizando um comparador Collections.sort( list, new TimeComparator() ); // gera a saída de elementos List System.out.printf( "Sorted list elements:\n%s\n", list ); } // fim de main } // fim da classe Sort3

Unsorted array elements: [6:24:34 AM, 6:14:58 PM, 6:05:34 AM, 12:14:58 PM, 6:24:22 AM] Sorted list elements: [6:05:34 AM, 6:24:22 AM, 6:24:34 AM, 12:14:58 PM, 6:14:58 PM]

Figura 20.9  |  Método Collections

sort

com um objeto Comparator personalizado.

20.7.2  Método shuffle O método shuffle ordena aleatoriamente os elementos de uma List. No Capítulo 7, apresentamos uma simulação do embaralhamento e distribuição de cartas que utiliza um loop para embaralhar as cartas do baralho. Na Figura 20.10, utilizamos o método shuffle para embaralhar as cartas do baralho de objetos Card que poderiam ser utilizados em um simulador de jogo de cartas. A classe Card (linhas 8–41) representa uma carta do baralho. Cada Card tem uma face e um naipe. As linhas 10–12 declaram dois tipos enum— Face e Suit — que representam a face e o naipe da carta, respectivamente. O método toString (linhas 37–40) retorna uma String que contém a face e o naipe da Card separados pela string "  of  ". Quando uma constante enum for convertida em uma string, o identificador da constante é utilizado como a representação de string. Normalmente utilizaríamos todas as letras maiúsculas para constantes enum. Nesse exemplo, escolhemos utilizar letras maiúsculas apenas para a letra inicial de cada constante enum porque queremos que a carta seja exibida com letras iniciais maiúsculas para a face e naipe (por exemplo, "Ace of Spades"). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

// Figura 20.10: DeckOfCards.java // Embaralhando e distribuindo cartas com o método Collections shuffle. import java.util.List; import java.util.Arrays; import java.util.Collections; // classe para representar uma carta de um baralho class Card { public static enum Face { Ace, Deuce, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King }; public static enum Suit { Clubs, Diamonds, Hearts, Spades }; private final Face face; // face da carta private final Suit suit; // naipe da carta // construtor de dois argumentos public Card( Face cardFace, Suit cardSuit ) { face = cardFace; // inicializa face da carta suit = cardSuit; // inicializa naipe da carta } // fim do construtor Card de dois argumentos // retorna a face da carta public Face getFace() { return face; } // fim do método getFace // retorna o naipe da carta public Suit getSuit()



20.7  Métodos de coleções

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82

{ return suit; } // fim do método getSuit // retorna a representação String de Card public String toString() { return String.format( "%s of %s", face, suit ); } // fim do método toString } // fim da classe Card // declaração da classe DeckOfCards public class DeckOfCards { private List< Card > list; // declara List que armazenará cartas // configura o baralho de cartas e embaralha public DeckOfCards() { Card[] deck = new Card[ 52 ]; int count = 0; // número de cartas // preenche baralho com objetos Card for (Card.Suit suit : Card.Suit.values() ) { for (Card.Face face : Card.Face.values() ) { deck[ count ] = new Card( face, suit ); ++count; } // for final } // for final list = Arrays.asList( deck ); // obtém List Collections.shuffle( list ); // embaralha as cartas } // fim do construtor DeckOfCards // gera a saída de baralho public void printCards() { // exibe 52 cartas em duas colunas for ( int i = 0; i < list.size(); i++ ) System.out.printf( "%-19s%s", list.get( i ), ( ( i + 1 ) % 4 == 0 ) ? "\n" : "" ); } // fim do método printCards public static void main( String[] args ) { DeckOfCards cards = new DeckOfCards(); cards.printCards(); } // fim de main } // fim da classe DeckOfCards

Deuce of Clubs Three of Diamonds Three of Spades Ten of Spades Nine of Clubs Ten of Clubs Queen of Diamonds Ace of Spades Seven of Diamonds Seven of Spades Eight of Clubs Six of Clubs Five of Spades

Six of Spades Five of Clubs Six of Diamonds King of Diamonds Ten of Diamonds Five of Hearts Ace of Diamonds Deuce of Spades Three of Hearts King of Hearts Three of Clubs Nine of Spades King of Spades

Nine of Diamonds Deuce of Diamonds King of Clubs Eight of Spades Eight of Diamonds Ace of Clubs Four of Clubs Ace of Hearts Four of Spades Seven of Hearts Queen of Clubs Four of Hearts Jack of Spades

Ten of Hearts Seven of Clubs Jack of Hearts Six of Hearts Eight of Hearts Deuce of Hearts Nine of Hearts Jack of Diamonds Four of Diamonds Five of Diamonds Queen of Spades Jack of Clubs Queen of Hearts

Figura 20.10  |  Embaralhamento e distribuição de cartas com o método Collections shuffle.

649

650

Capítulo 20  Coleções genéricas

As linhas 55–62 preenchem o array deck com cartas que têm combinações únicas de face e de naipe. Tanto Face como Suit são os tipos public static enum da classe Card. Para utilizar esses tipos enum fora da classe Card, você deve qualificar o nome de tipo de cada enum com o nome da classe em que ele reside (isto é, Card) e um ponto (.) separador. Por isso, as linhas 55 e 57 utilizam Card.Suit e Card.Face para declarar as variáveis de controle das instruções for. Lembre-se de que o método values de um tipo enum retorna um array que contém todas as constantes do tipo enum. As linhas 55–62 utilizam instruções for aprimoradas para construir 52 novas Cards. O embaralhamento ocorre na linha 65, que chama o método static shuffle da classe Collections- para embaralhar os elementos do array. O método shuffle exige um argumento List, assim, devemos obter uma visualização List do array antes que possamos embaralhá-lo. A linha 64 invoca o método static asList da classe Arrays para obter uma visualização List do array deck. O método printCards (linhas 69–75) exibe o baralho de cartas em quatro colunas. Em cada iteração do loop, as linhas 73–74 geram saída de uma carta alinhada à esquerda em um campo de 19 caracteres seguido por um caractere de nova linha ou uma string vazia com base no número de cartas enviado para saída até agora. Se o número de cartas for divisível por 4, o programa imprime uma nova linha; caso contrário, uma string vazia.

20.7.3  Métodos reverse, fill, copy, max e min A classe Collections fornece métodos para inverter, preencher e copiar Lists. O método Collections reverse inverte a ordem dos elementos em uma List e o método fill sobrescreve elementos em uma List com um valor especificado. A operação fill é útil para reinicializar uma List. O método copy recebe dois argumentos — uma List de destino e uma List de origem. Cada elemento da List de origem é copiado para a List de destino. A List de destino deve ser pelo menos tão longa quanto a List de origem; caso contrário, uma IndexOutOfBoundsException ocorre. Se a List de destino é mais longa, os elementos não sobrescritos permanecem inalterados. Todos os métodos que vimos até aqui operam em Lists. Os métodos min e max, cada um, operam em qualquer Collection. O método min retorna o menor elemento. Esses dois métodos podem ser chamados com um objeto Comparator como um segundo argumento para realizar comparações personalizadas de objetos, como o TimeComparator na Figura 20.9. A Figura 20.11 demonstra os métodos reverse, fill, copy, min e max. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

// Figura 20.11: Algorithms1.java // Métodos Collections reverse, fill, copy, max e min. import java.util.List; import java.util.Arrays; import java.util.Collections; public class Algorithms1 { public static void main( String[] args ) { // cria e exibe uma List< Character > Character[] letters = { 'P', 'C', 'M' }; List< Character > list = Arrays.asList( letters ); // obtém List System.out.println( "list contains: " ); output( list ); // inverte e exibe a List< Character > Collections.reverse( list ); // inverte a ordem dos elementos System.out.println( "\nAfter calling reverse, list contains: " ); output( list ); // cria copyList de um array de 3 caracteres Character[] lettersCopy = new Character[ 3 ]; List< Character > copyList = Arrays.asList( lettersCopy ); // copia os conteúdos de list para copyList Collections.copy( copyList, list ); System.out.println( "\nAfter copying, copyList contains: " ); output( copyList ); // preenche a lista com Rs Collections.fill( list, ‘R’ ); System.out.println( "\nAfter calling fill, list contains: " ); output( list ); } // fim de main // envia informações de List para saída private static void output( List< Character > listRef ) {



20.7  Métodos de coleções

40 41 42 43 44 45 46 47 48

651

System.out.print( "The list is: " ); for ( Character element : listRef ) System.out.printf( "%s ", element ); System.out.printf( "\nMax: %s", Collections.max( listRef ) ); System.out.printf( " Min: %s\n", Collections.min( listRef ) ); } // fim do método output } // fim da classe Algorithms1

list contains: The list is: P C M Max: P Min: C After calling reverse, list contains: The list is: M C P Max: P Min: C After copying, copyList contains: The list is: M C P Max: P Min: C After calling fill, list contains: The list is: R R R Max: R Min: R

Figura 20.11  |  Os métodos Collections

reverse, fill, copy, max

e min.

A linha 13 cria a variável List list e a inicializa com uma visualização List do array Character letters. As linhas 14–15 imprimem o conteúdo atual da List. A linha 18 chama o método Collections reverse para inverter a ordem de list. O método reverse aceita um argumento List. Como list é uma visualização List do array letters, os elementos do array estão agora em ordem inversa. A saída do conteúdo invertido é gerada nas linhas 19–20. A linha 27 utiliza o método Collections copy para copiar os elementos de list para copyList. As alterações na copyList não alteram letters, porque copyList é uma List separada que não é uma visualização List do array letters. O método copy exige dois argumentos List— a List de destino e a List de origem. A linha 32 chama o método Collections fill para colocar o caractere 'R' em cada elemento list. Como list é uma visualização List do array letters, essa operação transforma cada elemento em letters para 'R'. O método fill exige uma List do primeiro argumento e um Object para o segundo argumento — nesse caso, o Object é a versão do caractere 'R' após a operação de boxing. As linhas 45–46 chamam os métodos Collections max e min para localizar o maior e o menor elemento de uma Collection, respectivamente. Lembre-se de que a interface List estende a interface Collection, portanto, uma List é uma Collection.

20.7.4  Método binarySearch Na Seção 19.2.2, estudamos o algoritmo de pesquisa binária de alta velocidade. Esse algoritmo é incorporado na estrutura de coleções do Java como um método static Collections binarySearch, que localiza um objeto em uma List (por exemplo, uma LinkedList ou uma ArrayList). Se o objeto for encontrado, seu índice é retornado. Se o objeto não for localizado, binarySearch retorna um valor negativo. O método binarySearch determina esse valor negativo primeiro calculando o ponto de inserção e tornando seu sinal negativo. Então, binarySearch subtrai 1 do ponto de inserção para obter o valor de retorno, que garante que o método binarySearch retorna números positivos (>=0) se e somente se o objeto for localizado. Se múltiplos elementos na lista corresponderem à chave de pesquisa, não é garantido que um será localizado primeiro. A Figura 20.12 utiliza o método binarySearch para procurar uma série de strings em uma ArrayList. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura 20.12: BinarySearchTest.java // Método Collections binarySearch. import java.util.List; import java.util.Arrays; import java.util.Collections; import java.util.ArrayList; public class BinarySearchTest { public static void main( String[] args ) { // cria um ArrayList< String > do conteúdo do array colors

652 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

Capítulo 20  Coleções genéricas String[] colors = { "red", "white", "blue", "black", "yellow", "purple", "tan", "pink" }; List< String > list = new ArrayList< String >( Arrays.asList( colors ) ); Collections.sort( list ); // classifica a ArrayList System.out.printf( "Sorted ArrayList: %s\n", list ); // pesquisa vários valores na lista printSearchResults( list, colors[ 3 printSearchResults( list, colors[ 0 printSearchResults( list, colors[ 7 printSearchResults( list, "aqua" ); printSearchResults( list, "gray" ); printSearchResults( list, "teal” ); } // fim de main

] ); // primeiro item ] ); // item do meio ] ); // último item // abaixo do mais baixo // não existe // não existe

// realiza pesquisa e exibe o resultado private static void printSearchResults( List< String > list, String key ) { int result = 0; System.out.printf( "\nSearching for: %s\n", key ); result = Collections.binarySearch( list, key ); if ( result >= 0 ) System.out.printf( "Found at index %d\n", result ); else System.out.printf( "Not Found (%d)\n",result ); } // fim do método printSearchResults } // fim da classe BinarySearchTest

Sorted ArrayList: [black, blue, pink, purple, red, tan, white, yellow] Searching for: black Found at index 0 Searching for: red Found at index 4 Searching for: pink Found at index 2

Searching for: aqua Not Found (-1) Searching for: gray Not Found (-3) Searching for: teal Not Found (-7)

Figura 20.12  |  Método Collections

binarySearch.

As linhas 15–16 inicializam list com uma ArrayList contendo uma cópia dos elementos no array colors. O método Collec­ tions binarySearch espera que os elementos do argumento List sejam classificados em ordem crescente, então, a linha 18 utiliza o método Collections sort para classificar a lista. Se os elementos do argumento List não forem classificados, o resultado de utilizar binarySearch é indefinido. A linha 19 gera saída da lista classificada. As linhas 22–27 chamam o método printSearchResults (linhas 31–43) para realizar pesquisas e gerar saída dos resultados. A linha 37 chama o método Collections binarySearch para procurar pela key especificada em list. O método binarySearch aceita uma List como o primeiro argumento e um Object como o segundo argumento. As linhas 39–42 geram saída dos resultados da pesquisa. Uma versão sobrecarregada de binarySearch aceita um objeto Compara­ tor como seu terceiro argumento, que especifica como binarySearch deve comparar a chave de pesquisa com os elementos da List

20.7.5  Métodos addAll, frequency e disjoint A classe Collections também fornece os métodos addAll, frequency e disjoint. O método Collections addAll aceita dois argumentos — uma Collection na qual inserir o(s) novo(s) elemento(s) e um array que fornece elementos a ser inseridos. O método



20.7  Métodos de coleções

653

aceita dois argumentos — um Collection a ser pesquisado e um Object a ser procurado na coleção. O método frequency retorna o número de vezes que o segundo argumento aparece na coleção. O método Collections disjoint aceita duas Collections e retorna true se elas não tiverem nenhum elemento em comum. A Figura 20.13 demonstra o uso de métodos addAll, frequency e disjoint. Collections frequency

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

// Figura 20.13: Algorithms2.java // Métodos Collections addAll, frequency e disjoint. import java.util.ArrayList; import java.util.List; import java.util.Arrays; import java.util.Collections; public class Algorithms2 { public static void main( String[] args ) { // inicializa list1 e list2 String[] colors = { "red", "white", "yellow", "blue" }; List< String > list1 = Arrays.asList( colors ); ArrayList< String > list2 = new ArrayList< String >(); list2.add( "black" ); // adiciona "black" ao fim de list2 list2.add( "red" ); // adiciona "red" ao fim de list2 list2.add( "green" ); // adiciona "green" ao fim de list2 System.out.print( "Before addAll, list2 contains: " ); // exibe elementos em list2 for ( String s : list2 ) System.out.printf( "%s ", s ); Collections.addAll( list2, colors ); // adiciona Strings colors a list2 System.out.print( "\nAfter addAll, list2 contains: " ); // exibe elementos em list2 for ( String s : list2 ) System.out.printf( "%s ", s ); // obtém frequência de "red" int frequency = Collections.frequency( list2, “red” ); System.out.printf( “\nFrequency of red in list2: %d\n”, frequency ); // verifica se list1 e list2 têm elementos em comum boolean disjoint = Collections.disjoint( list1, list2 ); System.out.printf( "list1 and list2 %s elements in common\n", ( disjoint ? "do not have" : "have" ) ); } // fim de main } // fim da classe Algorithms2

Before addAll, list2 contains: black red green After addAll, list2 contains: black red green red white yellow blue Frequency of red in list2: 2 list1 and list2 have elements in common

Figura 20.13  |  Métodos Collections

addAll, frequency

e disjoint.

A linha 14 inicializa list1 com elementos no array colors, e as linhas 17–19 adiciona Strings "black", "red" e "green" à A linha 27 invoca o método addAll para adicionar elementos no array colors para list2. A linha 36 obtém a frequência de String "red" em list2 para utilizar o método frequency. A linha 41 invoca o método disjoint para testar se Collections list1 e list2 têm elementos em comum, o que, nesse exemplo, elas apresentam. list2.

654

Capítulo 20

Coleções genéricas

20.8 Classe Stack do pacote java.util Introduzimos o conceito de uma pilha na Seção 6.6 quando discutimos a pilha de chamada do método. No Capítulo 22, Estruturas de dados genéricos personalizados, aprenderemos como construir estruturas de dados, incluindo listas encadeadas, pilhas, filas e árvores. Em um mundo de reutilização de software, em vez de construir estruturas de dados à medida que precisamos delas, costumamos tirar proveito de estruturas de dados existentes. Nesta seção, investigamos a classe Stack do pacote de utilitários Java (java.util). A classe Stack estende a classe Vector para implementar uma estrutura de dados de pilha. Como a classe Stack estende a classe Vec­ tor, a interface public inteira da classe Vector está disponível para clientes da classe Stack. A Figura 20.14 demonstra vários métodos Stack. Para os detalhes da classe Stack, visite java.sun.com/javase/6/docs/api/java/util/Stack.html.

Dica de prevenção de erro 20.1 Como Stack estende Vector, todos os métodos public Vector podem ser chamados em objetos Stack, mesmo se os métodos não representarem operações de pilha convencionais. Por exemplo, o método Vector add pode ser utilizado para inserir um elemento em qualquer lugar em uma pilha — uma operação que poderia “corromper” a pilha. Ao manipular uma Stack, somente os métodos push e pop devem ser utilizados para adicionar elementos à Stack e remover elementos dela, respectivamente.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

// Figura 20.14: Stacktest.java // Classe Stack do pacote java.util. import java.util.Stack; import java.util.EmptyStackException; public class StackTest { public static void main( String[] args ) { Stack< Number > stack = new Stack< Number >(); // cria uma pilha // utiliza método push stack.push( 12L ); // adiciona valor long 12L System.out.println( "Pushed 12L" ); printStack( stack ); stack.push( 34567 ); // adiciona valor int 34567 System.out.println( "Pushed 34567" ); printStack( stack ); stack.push( 1.0F ); // adiciona valor float 1.0F System.out.println( "Pushed 1.0F" ); printStack( stack ); stack.push( 1234.5678 ); // adiciona valor double 1234.5678 System.out.println( "Pushed 1234.5678 " ); printStack( stack ); // remove itens de pilha try { Number removedObject = null; // remove elementos da pilha while ( true ) { removedObject = stack.pop(); // utiliza método pop System.out.printf( "Popped %s\n", removedObject ); printStack( stack ); } // fim do while } // fim do try catch ( EmptyStackException emptyStackException ) { emptyStackException.printStackTrace(); } // fim do catch } // fim de main // exibe o conteúdo de Stack private static void printStack( Stack< Number > stack ) {

20.9 Classe PriorityQueue e interface Queue 48 49 50 51 52 53

655

if (stack.isEmpty() ) System.out.println( "stack is empty\n" ); // a pilha está vazia else // a pilha não está vazia System.out.printf( "stack contains: %s (top)\n", stack ); } // fim do método printStack } // fim da classe StackTest

Pushed 12L stack contains: [12] Pushed 34567 stack contains: [12, Pushed 1.0F stack contains: [12, Pushed 1234.5678 stack contains: [12, Popped 1234.5678 stack contains: [12, Popped 1.0 stack contains: [12, Popped 34567 stack contains: [12] Popped 12 stack is empty

(top) 34567] (top) 34567, 1.0] (top) 34567, 1.0, 1234.5678] (top) 34567, 1.0] (top) 34567] (top) (top)

java.util.EmptyStackException at java.util.Stack.peek(Unknown Source) at java.util.Stack.pop(Unknown Source) at StackTest.main(StackTest.java:34)

Figura 20.14 | Classe Stack do pacote java.util.

A linha 10 cria uma Stack vazia de Numbers. A classe Number (no pacote java.lang) é a superclasse das classes empacotadoras de tipo de tipos numéricos primitivos (por exemplo, Integer, Double). Criando uma Stack de Numbers, os objetos de qualquer classe que estenda Number podem ser adicionados à Stack. As linhas 13, 16, 19 e 22 chamam o método Stack push para adicionar um objeto Number na parte superior da pilha. Observe os literais 12L (linha 13) e 1.0F (linha 19). Qualquer literal inteiro que tenha o sufixo L é um valor long. Um literal de inteiro sem sufixo é um valor int. De maneira semelhante, qualquer literal de ponto flutuante que tenha o sufixo F é um valor float. Um literal de ponto flutuante sem sufixo é um valor double. Você pode aprender mais sobre literais numéricos na Java Language Specification em java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.8.1. Um loop infinito (linhas 32–37) chama o método Stack pop para remover o elemento na parte superior da pilha. O método retorna uma referência Number ao elemento removido. Se não houver nenhum elemento na Stack, o método pop lança uma EmptyStackException, que termina o loop. A classe Stack também declara o método peek. Esse método retorna o elemento na parte superior da pilha sem remover o elemento da pilha. O método printStack (linhas 46–52) exibe o conteúdo da pilha. A parte superior atual da pilha (o último valor adicionado à pilha) é o primeiro valor impresso. A linha 48 chama o método Stack isEmpty (herdado por Stack da classe Vector) para determinar se a pilha está vazia. Se estiver vazia, o método retorna true; caso contrário, false.

20.9 Classe PriorityQueue e interface Queue Lembre-se de que uma fila é uma coleção que representa uma fila de espera — em geral, as inserções são feitas na parte posterior da fila e as exclusões na da frente. Na Seção 22.6, discutiremos e implementaremos uma estrutura de dados de fila. Nesta seção, investigamos a interface Queue e a classe PriorityQueue do Java do pacote java.util. A interface Queue estende a interface Collection e fornece operações adicionais para inserir, remover e inspecionar elementos em uma fila. PriorityQueue, que implementa a interface Queue, ordena elementos por sua ordem natural como especificado pelo método compareTo dos elementos Comparable ou por um objeto Comparator que é fornecido pelo construtor. A classe PriorityQueue fornece funcionalidades que permitem inserções na ordem de classificação na estrutura de dados subjacente e exclusões a partir da frente da estrutura de dados subjacente. Ao adicionar elementos a uma PriorityQueue, os elementos são inseridos na ordem de prioridade de tal modo que o elemento de maior prioridade (isto é, o maior valor) será o primeiro elemento removido da PriorityQueue. As operações PriorityQueue comuns são offer, para inserir um elemento na posição apropriada com base na ordem de prioridade, poll para remover o elemento de mais alta prioridade da fila de prioridade (isto é, a cabeça da fila), peek para obter uma referência ao

656

Capítulo 20

Coleções genéricas

elemento de mais alta prioridade da fila de prioridade (sem remover esse elemento), clear para remover todos os elementos da fila de prioridade e size, para obter o número de elementos da fila de prioridade. A Figura 20.15 demonstra a classe PriorityQueue. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

// Figura 20.15: PriorityQueueTest.java // Programa de teste PriorityQueue. import java.util.PriorityQueue; public class PriorityQueueTest { public static void main( String[] args ) { // fila de capacidade 11 PriorityQueue< Double > queue = new PriorityQueue< Double >(); // insere elementos na fila queue.offer( 3.2 ); queue.offer( 9.8 ); queue.offer( 5.4 ); System.out.print( "Polling from queue: " ); // exibe elementos na fila while (queue.size() > 0 ) { System.out.printf( "%.1f ", queue.peek() ); // visualiza elemento superior queue.poll(); // remove elemento superior } // fim do while } // fim de main } // fim da classe PriorityQueueTest

Polling from queue: 3.2 5.4 9.8

Figura 20.15 | Programa de teste PriorityQueue.

A linha 10 cria uma PriorityQueue que armazena Doubles com uma capacidade inicial de 11 elementos e os ordena de acordo com a ordem natural do objeto (os padrões para uma PriorityQueue). Observe que PriorityQueue é uma classe genérica e que a linha 10 instancia uma PriorityQueue com um argumento de tipo Double. A classe PriorityQueue fornece cinco construtores adicionais. Um desses construtores aceita um int e um objeto Comparator para criar uma PriorityQueue com a capacidade inicial especificada pelo int e a ordem pelo Comparator. As linhas 13–15 utilizam o método offer para adicionar elementos à fila de prioridade. O método offer lança uma NullPointerException se o programa tentar adicionar um objeto null à fila. O loop nas linhas 20–24 utiliza o método size para determinar se a fila de prioridade está vazia (linha 20). Enquanto houver mais elementos, a linha 22 utiliza o método PriorityQueue peek para recuperar o elemento de prioridade mais alta na fila para saída (sem realmente remover o elemento da fila). A linha 23 remove o elemento de prioridade mais alta na fila com o método poll, que retorna o elemento removido.

20.10 Conjuntos Um Set é uma Collection não ordenada de elementos únicos (isto é, sem elementos duplicados). A estrutura de coleções contém diversas implementações de Set, incluindo HashSet e TreeSet. HashSet armazena seus elementos em uma tabela de hash; e TreeSet armazena seus elementos em uma árvore. As tabelas de hash são apresentadas na Seção 20.11. As árvores são discutidas na Seção 22.7. A Figura 20.16 utiliza um HashSet para remover strings duplicadas de uma List. Lembre-se de que tanto List como Collection são tipos genéricos, assim, a linha 16 cria uma List que contém objetos String e a linha 20 passa uma Collection de Strings para o método printNonDuplicates. 1 2 3 4 5 6 7 8 9

// Figura 20.16: SetTest.java // HashSet utilizado para remover valores duplicados do array de strings. import java.util.List; import java.util.Arrays; import java.util.HashSet; import java.util.Set; import java.util.Collection; public class SetTest



20.10  Conjuntos

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

657

{ public static void main( String[] args ) { // cria e exibe uma List< String > String[] colors = { "red", "white", "blue", "green", "gray", "orange", "tan", "white", "cyan", "peach", "gray", "orange" }; List< String > list = Arrays.asList( colors ); System.out.printf( "List: %s\n", list ); // elimina duplicatas e imprime valores únicos printNonDuplicates( list ); } // fim de main // cria um Set a partir de uma Collection para eliminar duplicatas private static void printNonDuplicates( Collection< String > values ) { // cria um HashSet Set< String > set = new HashSet< String >( values ); System.out.print( "\nNonduplicates are: " ); for ( String value : set ) System.out.printf( "%s ", value ); System.out.println(); } // fim do método printNonDuplicates } // fim da classe SetTest

List: [red, white, blue, green, gray, orange, tan, white, cyan, peach, gray, orange] Nonduplicates are: orange green white peach gray cyan red blue tan

Figura 20.16  |  HashSet utilizado para remover valores duplicados de um array de strings.

O método printNonDuplicates (linhas 24–35) aceita um argumento Collection. A linha 27 constrói um HashSet a partir do argumento Collection. Por definição, Sets não contêm duplicata, então quando o HashSet é construído, ele remove qualquer duplicata da Collection. As linhas 31–32 imprimem os elementos em Set.

Conjuntos classificados A estrutura de coleções também inclui a interface SortedSet (que estende Set) para conjuntos que mantêm seus elementos em ordem de classificação — a ordem natural dos elementos (por exemplo, números estão em ordem crescente) ou uma ordem especificada por um Comparator. A classe TreeSet implementa SortedSet. O programa na Figura 20.17 coloca strings em um TreeSet. As strings são classificadas à medida que são adicionadas ao TreeSet. Esse exemplo também demonstra métodos de visualização de intervalo, que permitem que um programa visualize uma parte de uma coleção. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

// Figura 20.17: SortedSetTest.java // Utilizando SortedSets e TreeSets. import java.util.Arrays; import java.util.SortedSet; import java.util.TreeSet; public class SortedSetTest { public static void main( String[] args ) { // cria TreeSet a partir do array colors String[] colors = { "yellow", "green", "black", "tan", "grey", "white", "orange", "red", "green" }; SortedSet< String > tree = new TreeSet< String >( Arrays.asList( colors ) ); System.out.print( "sorted set: " ); printSet( tree ); // conteúdo de saída de árvore // obtém headSet com base em "orange"

658 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

Capítulo 20

Coleções genéricas

System.out.print( "headSet (\"orange\"): printSet(tree.headSet( “orange” ) ); // obtém tailSet baseado em "orange" System.out.print( "tailSet (\"orange\"): printSet(tree.tailSet( “orange” ) );

" );

" );

// obtém o primeiro e o último elementos System.out.printf( "first: %s\n", tree.first() ); System.out.printf( "last : %s\n", tree.last() ); } // fim de main // imprime a SortedSet utilizando a instrução for aprimorada private static void printSet( SortedSet< String > set ) { for ( String s : set ) System.out.printf( "%s ", s ); System.out.println(); } // fim do método printSet } // fim da classe SortedSetTest

sorted set: black green grey orange red tan white yellow headSet ("orange"): black green grey tailSet ("orange"): orange red tan white yellow first: black last : yellow

Figura 20.17 | Utilizando SortedSets e TreeSets.

As linhas 14–15 criam um TreeSet que contém os elementos do array colors, então atribuem a nova variável à variável SortedSet tree. A linha 18 gera saída do conjunto inicial de strings utilizando o método printSet (linhas 34–40), que discutiremos em breve. A linha 22 chama o método TreeSet headSet para obter um subconjunto do TreeSet em que cada elemento é menor que "orange". A visualização retornada de headSet é então enviada para saída com printSet. Se o subconjunto sofrer alguma alteração, o TreeSet original também será alterado, pois o subconjunto retornado por headSet é uma visualização do TreeSet. A linha 26 chama o método TreeSet tailSet para obter um subconjunto em que cada elemento seja maior ou igual a "orange", e então gera o resultado. Qualquer alteração feita pela visualização tailSet ocorre no TreeSet original. As linhas 29–30 chamam os métodos SortedSet first e last para obter os menores e os maiores elementos do conjunto, respectivamente. O método printSet (linhas 34–40) aceita um SortedSet como um argumento e o imprime. As linhas 36–37 imprimem cada elemento do SortedSet utilizando a instrução for aprimorada. TreeSet

20.11 Mapas Os maps associam chaves a valores. As chaves em um Map devem ser únicas, mas não os valores associados. Se um Map contiver tanto chaves como valores únicos, diz-se que ele implementa um mapeamento um para um. Se apenas as chaves forem únicas, diz-se que o Map implementa um mapeamento de “muitos para um” — muitas chaves podem mapear para um valor. Os mapas diferem de Sets no sentido de que Maps contêm chaves e valores, enquanto Sets contêm apenas valores. Três das várias classes que implementam a interface Map são Hashtable, HashMap e TreeMap. Hashtables e HashMaps armazenam elementos em tabelas de hash e TreeMaps armazenam elementos em árvores. Esta seção discute as tabelas de hash e fornece um exemplo que utiliza um Hash­ Map para armazenar pares chave/valor. A interface SortedMap estende Map e mantém suas chaves em ordem de classificação — na ordem natural dos elementos ou em uma ordem especificada por uma implementação Comparator. A classe TreeMap implementa SortedMap.

Implementação Map com tabelas de hash As linguagens de programação orientada a objetos facilitam a criação de novos tipos. Quando um programa cria objetos de tipos novos ou existentes, ele pode precisar armazená-los e recuperá-los eficientemente. Armazenar e recuperar informações com arrays é eficiente se algum aspecto de seus dados corresponder diretamente com o valor numérico e se essas chaves forem únicas e fortemente empacotadas. Se você tiver 100 funcionários com CPF de nove dígitos e quiser armazenar e recuperar dados sobre os funcionários usando o CPF como chave, a tarefa exigirá um array com mais de 700 milhões de elementos, porque os CPFs de nove dígitos devem iniciar com 001-733 de acordo com o site Social Security Administration. www.socialsecurity.gov/employer/stateweb.htm



20.11  Mapas

659

Isso é complicado para praticamente todos os aplicativos que utilizam números do CPF como chaves. Um programa que tivesse um array tão grande assim poderia alcançar alto desempenho tanto para armazenar como para recuperar registros de empregados simplesmente utilizando o número do CPF como o índice de array. Numerosos aplicativos têm esse problema, ou seja, as chaves são tanto do tipo errado (por exemplo, inteiros não positivos que correspondem a subscritos de array) como chaves do tipo certo, mas estão escassamente espalhadas em um enorme intervalo. O que é necessário é um esquema de alta velocidade para converter chaves, como números do CPF, códigos de produtos em estoque e muitos outros em índices de array únicos. Então, quando um aplicativo precisasse armazenar algo, o esquema poderia converter a chave do aplicativo rapidamente em um índice e o registro poderia ser armazenado nessa posição do array. A recuperação é realizada da mesma maneira: Uma vez que o aplicativo tem uma chave pela qual ele quer recuperar um registro de dados, o aplicativo simplesmente aplica a conversão à chave — isso produz o índice de array em que os dados são armazenados e recuperados. O esquema que descrevemos aqui é a base de uma técnica chamada hashing. Por que esse nome? Quando convertemos uma chave em um índice de array, literalmente embaralhamos os bits, formando um tipo de número “confusamente misturado,” ou hasheado. O número realmente não tem nenhuma importância real além de sua utilidade em armazenar e recuperar um registro de dados particulares. Um glitch (falha, em geral de pequena gravidade) no esquema é chamado de colisão — esta ocorre quando duas chaves diferentes produzem hash para a mesma célula (ou elemento) no array. Não podemos armazenar dois valores no mesmo espaço, então precisamos localizar uma posição alternativa para todos os valores depois do primeiro que produz hash para um índice de array particular. Há muitos esquemas para fazer isso. Um é um “novo hash” (isto é, aplicar a transformação de hashing à chave para fornecer uma próxima célula candidata no array). O processo de hashing é projetado para distribuir os valores por toda a tabela; desse modo, a suposição é de que uma célula disponível será localizada com apenas alguns hashes. Outro esquema utiliza um hash para localizar a primeira célula candidata. Se essa célula estiver ocupada, sucessivas células são pesquisadas em ordem até que uma célula disponível seja localizada. A recuperação funciona da mesma maneira: A chave sofre hash uma vez para determinar a localização inicial e verificar se ela contém os dados desejados. Se ela contiver, a pesquisa é concluída. Se não contiver, células sucessivas são pesquisadas linearmente até que os dados desejados sejam localizados. A solução mais popular para colisões de tabela de hash é fazer cada célula da tabela ser um “bucket” de hash, em geral uma lista vinculada de todos os pares chave/valor que sofrem hash para essa célula. Essa é a solução que as classes Hashtable e HashMap do Java (do pacote java.util) implementam. Tanto Hashtable como HashMap implementam a interface Map. As principais diferenças entre eles são que HashMap é não sincronizado (múltiplas threads não devem modificar um HashMap concorrentemente) e permite chaves null e valores null. O fator de carga de uma tabela de hash afeta o desempenho de esquemas de hashing. O fator de carga é a relação do número de células ocupadas na tabela de hash com o número total de células na tabela de hash. Quanto mais a proporção se aproximar de 1,0, maior a chance de colisões.

Dica de desempenho 20.2 O fator de carga em uma tabela de hash é um exemplo clássico de uma troca entre espaço de memória e tempo de execução: aumentando o fator de carga, melhoramos a utilização da memória, mas o programa executa mais lentamente por causa do aumento das colisões de hashing. Diminuindo o fator de carga, melhoramos a velocidade do programa, em virtude da redução das colisões de hashing, mas fazemos uma pobre utilização da memória porque uma parte maior da tabela de hash permanece vazia.

As tabelas de hash são complexas de programar. Os alunos de ciência da computação estudam esquemas de hashing em cursos chamados “Estruturas de dados” e “Algoritmos.” As classes Hashtable e HashMap permitem o uso de hashing sem a necessidade de implementar mecanismos de tabela de hash. Esse conceito é extremamente importante em nosso estudo de programação orientada a objetos. Como discutido nos capítulos anteriores, as classes encapsulam e ocultam a complexidade (isto é, detalhes de implementação) e oferecem interfaces amigáveis ao usuário. Criar adequadamente as classes para exibir esse comportamento é uma das habilidades mais estimadas no campo da programação orientada a objetos. A Figura 20.18 utiliza um HashMap para contar o número de ocorrências de cada palavra em uma string. 1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 20.18: WordTypeCount.java // O programa conta o número de ocorrências de cada palavra em uma String. import java.util.Map; import java.util.HashMap; import java.util.Set; import java.util.TreeSet; import java.util.Scanner; public class WordTypeCount { public static void main( String[] args ) { // cria HashMap para armazenar chaves de String e valores Integer Map< String, Integer > myMap = new HashMap< String, Integer >();

660

Capítulo 20  Coleções genéricas

15 16

createMap( myMap ); // cria mapa com base na entrada do usuário

17 18

displayMap( myMap ); // exibe o conteúdo do mapa } // fim de main

19 20

// cria mapa de entrada do usuário

21

private static void createMap( Map< String, Integer > map )

22

{

23

Scanner scanner = new Scanner( System.in ); // cria o scanner

24

System.out.println( "Enter a string:" ); // solicita a entrada do usuário

25

String input = scanner.nextLine();

26 27

// tokeniza a entrada

28

String[] tokens = input.split( " " );

29 30

// processamento de texto de entrada

31

for ( String token : tokens )

32

{

33

String word = token.toLowerCase(); // obtém palavra em letras minúsculas

34 35

// se o mapa contiver a palavra

36

if (map.containsKey( word ) ) // is word in map

37

{

38

int count = map.get( word ); // obtém a contagem atual

39

map.put( word, count + 1 ); // incrementa a contagem

40

} // fim do if

41

else

42

map.put( word, 1 ); // adiciona nova palavra com uma contagem de 1 para mapa

43 44

} // fim do for } // fim do método createMap

45 46

// exibe o conteúdo do mapa

47

private static void displayMap( Map< String, Integer > map )

48

{

49

Set< String > keys = map.keySet(); //obtém chaves

50 51

// classifica as chaves

52

TreeSet< String > sortedKeys = new TreeSet< String >( keys );

53 54

System.out.println( "\nMap contains:\nKey\t\tValue" );

55 56

// gera a saída de cada chave no mapa

57

for ( String key : sortedKeys )

58

System.out.printf( "%-10s%10s\n", key, map.get( key ) );

59 60 61

System.out.printf( "\nsize: %d\nisEmpty: %b\n", map.size(), map.isEmpty() );

62

} // fim do método displayMap

63

} // fim da classe WordTypeCount

Enter a string: this is a sample sentence with several words this is another sample sentence with several different words

20.12 Classe Properties

Map contains: Key a another different is sample sentence several this with words

661

Value 1 1 1 2 2 2 2 2 2 2

size: 10 isEmpty: false

Figura 20.18 | O programa conta o número de ocorrências de cada palavra em uma String.

A linha 14 cria um HashMap vazio com uma capacidade inicial padrão (16 elementos) e um fator de carga padrão (0,75) — esses padrões são criados na implementação de HashMap. Quando o número de posições ocupadas no HashMap tornar-se maior que a capacidade vezes o fator de carga, a capacidade é automaticamente dobrada. Note que HashMap é uma classe genérica que aceita dois argumentos de tipo — o tipo da chave (isto é, String) e o tipo do valor (isto é, Integer). Lembre-se de que os argumentos de tipo passados para uma classe genérica devem ser tipos por referência, daí o segundo argumento de tipo ser Integer, não int. A linha 16 chama o método createMap (linhas 21–44), que utiliza um map para armazenar o número de ocorrências de cada palavra na frase. A linha 25 obtém a entrada de usuário, e a linha 28 a tokeniza. O loop nas linhas 31–43 converte o próximo token em letra minúscula (linha 33) e chama o método Map containsKey (linha 36) para determinar se a palavra está no mapa (e assim ocorreu anteriormente na string). Se Map não contiver um mapeamento para a palavra, a linha 42 utiliza o método Map put para criar uma nova entrada no mapa, com a palavra como a chave e um objeto Integer que contém 1 como o valor. O autoboxing ocorre quando o programa passa o inteiro 1 para o método put, porque o mapa armazena o número de ocorrências da palavra como um Integer. Se a palavra existir no mapa, a linha 38 utiliza o método Map get para obter o valor associado (a contagem) da chave no mapa. A linha 39 incrementa esse valor e utiliza put para substituir o valor associado da chave no mapa. O método put retorna o valor anterior associado da chave, ou null se a chave não estiver no mapa. O método displayMap (linhas 47–62) exibe todas as entradas no mapa. Ele utiliza o método HashMap keySet (linha 49) para obter um conjunto das chaves. As chaves têm o tipo String no map, então o método keySet retorna um tipo genérico Set com o parâmetro de tipo especificado para ser String. A linha 52 cria um TreeSet das chaves, em que as chaves são classificadas. O loop nas linhas 57–58 acessa cada chave e seu valor no mapa. A linha 58 exibe cada chave e seu valor utilizando o especificador de formato %­10s para alinhar à esquerda cada chave e formatar o especificador %10s à direita de cada valor. Observe que as chaves são exibidas em ordem crescente. A linha 61 chama o método Map size para obter o número de pares chave/valor no Map. A linha 61 também chama o método Map isEmpty, que retorna um boolean que indica se o Map está vazio.

20.12 Classe Properties Um objeto Properties é uma Hashtable persistente que normalmente armazena pares chave/valor de strings — assumindo que você utiliza os métodos setProperty e getProperty para manipular a tabela em vez dos métodos Hashtable put e get herdados. Por “persistente”, queremos dizer que o objeto Properties pode ser gravado em um fluxo de saída (possivelmente um arquivo) e lido de volta por um fluxo de entrada (input stream). Um uso comum de objetos Properties em versões anteriores do Java era manter os dados de configuração de aplicativo ou preferências de usuário de aplicativos. [Nota: A Preferences API (pacote java.util.prefs) foi projetada para substituir esse uso particular da classe Properties, mas está além do escopo deste livro. Para aprender mais, visite java.sun.com/ javase/6/docs/technotes/guides/preferences/index.html.] A classe Properties estende a classe Hashtable. A Figura 20.19 demonstra vários métodos da classe Properties. 1 2 3 4 5 6 7 8 9

// Figura 20.19: PropertiesTest.java // Demonstra classe Properties do pacote java.util. import java.io.FileOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.util.Properties; import java.util.Set; public class PropertiesTest

662 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80

Capítulo 20  Coleções genéricas { public static void main( String[] args ) { Properties table = new Properties(); // cria tabela Properties // configura propriedades table.setProperty( “color”, “blue” ); table.setProperty( “width”, “200” ); System.out.println( "After setting properties" ); listProperties( table ); // exibe os valores da propriedade // substitui o valor de propriedade table.setProperty( “color”, “red” ); System.out.println( "After replacing properties" ); listProperties( table ); // exibe os valores da propriedade saveProperties( table ); // salva as propriedades table.clear(); // tabela vazia System.out.println( "After clearing properties" ); listProperties( table ); // exibe os valores da propriedade loadProperties( table ); // carrega propriedades // obtém valor de cor da propriedade Object value = table.getProperty( “color” ); // verifica se o valor está na tabela if ( value != null ) System.out.printf( "Property color's value is %s\n", value ); else System.out.println( "Property color is not in table" ); } // fim de main // salva as propriedades para um arquivo private static void saveProperties( Properties props ) { // salva o conteúdo da tabela try { FileOutputStream output = new FileOutputStream( "props.dat" ); props.store( output, “Sample Properties” ); // salva as propriedades output.close(); System.out.println( "After saving properties" ); listProperties( props ); // exibe os valores da propriedade } // fim do try catch ( IOException ioException ) { ioException.printStackTrace(); } // fim do catch } // fim do método saveProperties // carrega as propriedades de um arquivo private static void loadProperties( Properties props ) { // carrega o conteúdo da tabela try { FileInputStream input = new FileInputStream( "props.dat" ); props.load( input ); // carrega propriedades input.close(); System.out.println( "After loading properties" ); listProperties( props ); // exibe os valores da propriedade } // fim do try catch ( IOException ioException ) { ioException.printStackTrace(); } // fim do catch

20.13 Coleções sincronizadas 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95

663

} // fim do método loadProperties // gera saída de valores de propriedade private static void listProperties( Properties props ) { Set< Object > keys = props.keySet(); // obtém nomes de propriedade // gera saída de pares nome/valor for ( Object key : keys ) System.out.printf( "%s\t%s\n", key, props.getProperty( ( String ) key ) ); System.out.println(); } // fim do método listProperties } // fim da classe PropertiesTest

After setting properties color blue width 200 After replacing properties color red width 200 After saving properties color red width 200 After clearing properties After loading properties color red width 200 Property color's value is red

Figura 20.19 | Classe Properties do pacote java.util.

A linha 13 utiliza o construtor sem argumento para criar uma Properties table vazia sem propriedades padrão. A classe Properties também fornece um construtor sobrecarregado que recebe uma referência a um objeto Properties que contém os valores de propriedade padrão. As linhas 16 e 17 chamam o método Properties setProperty para armazenar um valor para a chave especificada. Se a chave não existir em table, setProperty retorna null; caso contrário, ela retorna o valor anterior dessa chave. A linha 38 chama o método Properties getProperty para localizar o valor associado com a chave especificada. Se a chave não for localizada nesse objeto Properties, getProperty retorna null. Uma versão sobrecarregada desse método recebe um segundo argumento que especifica o valor padrão a retornar se getProperty não puder localizar a chave. A linha 54 chama o método Properties store para salvar o conteúdo do objeto Properties no OutputStream especificado como o primeiro argumento (nesse caso, um FileOutputStream). O segundo argumento, uma String, é uma descrição no arquivo. O método Properties list, que aceita um argumento PrintStream, é útil para exibir a lista de propriedades. A linha 72 chama o método Properties load para restaurar o conteúdo do objeto Properties do InputStream especificado como o primeiro argumento (nesse caso, um FileInputStream). A linha 86 chama o método Properties keySet para obter um Set dos nomes da propriedade. A linha 91 obtém o valor de uma propriedade passando uma chave para o método getProperty.

20.13 Coleções sincronizadas No Capítulo 26, discutimos multithreading. Exceto para Vector e Hashtable, as coleções na estrutura de coleções são não sincronizadas por padrão, portanto, podem operar eficientemente quando o multithreading não é necessário. Mas como essas coleções são não sincronizadas, o acesso concorrente por múltiplas threads a uma Collection poderia causar resultados indeterminados ou erros fatais. Para evitar potenciais problemas de encadeamento, os empacotadores de sincronização são utilizados para coleções que poderiam ser acessadas por múltiplas threads. Um objeto empacotador (wrapper) recebe chamadas de método, adiciona sincronização de thread (para evitar acesso simultâneo à coleção) e delega as chamadas para o objeto de coleção empacotado. A API Collections fornece um conjunto de métodos static para empacotar coleções como versões sincronizadas. Cabeçalhos de método para os empacotadores de sincronização são listados na Figura 20.20. Os detalhes sobre esses métodos estão disponíveis em java.sun.com/javase/6/docs/api/java/util/Col­ lections.html. Todos esses métodos aceitam um tipo genérico e retornam uma visualização sincronizada do tipo genérico. Por exemplo, o seguinte código cria uma List sincronizada (list2) que armazena objetos String: List< String > list1 = new ArrayList< String >(); List< String > list2 = Collections.synchronizedList( list1 );

664

Capítulo 20

Coleções genéricas

Cabeçalhos de método public

static

< T > Collection< T > synchronizedCollection( Collection< T > c ) < T > List< T > synchronizedList( List< T > aList ) < T > Set< T > synchronizedSet( Set< T > s ) < T > SortedSet< T > synchronizedSortedSet( SortedSet< T > s ) < K, V > Map< K, V > synchronizedMap( Map< K, V > m ) < K, V > SortedMap< K, V > synchronizedSortedMap( SortedMap< K, V > m )

Figura 20.20 | Métodos empacotadores de sincronização.

20.14 Coleções não modificáveis A classe Collections fornece um conjunto de métodos static que criam empacotadores não modificáveis para coleções. Empacotadores não modificáveis lançam UnsupportedOperationExceptions se forem feitas tentativas de modificar a coleção. Os cabeçalhos desses métodos são listados na Figura 20.21. Os detalhes sobre esses eles estão disponíveis em java.sun.com/javase/6/docs/api/ java/util/Collections.html. Todos esses métodos aceitam um tipo genérico e retornam uma visualização não modificável do tipo genérico. Por exemplo, o seguinte código cria uma List (list2) não modificável que armazena objetos String: List< String > list1 = new ArrayList< String >(); List< String > list2 = Collections.unmodifiableList( list1 );

Cabeçalhos de método public

static

< T > Collection< T > unmodifiableCollection( Collection< T > c ) < T > List< T > unmodifiableList( List< T > aList ) < T > Set< T > unmodifiableSet( Set< T > s ) < T > SortedSet< T > unmodifiableSortedSet( SortedSet< T > s ) < K, V > Map< K, V > unmodifiableMap( Map< K, V > m ) < K, V > SortedMap< K, V > unmodifiableSortedMap( SortedMap< K, V > m )

Figura 20.21 | Métodos empacotadores não modificáveis.

Observação de engenharia de software 20.5 Você pode utilizar um empacotador não modificável para criar uma coleção que oferece acesso de leitura às outras pessoas enquanto permite o acesso de leitura e gravação para você. Você faz isso simplesmente dando às outras pessoas uma referência ao empacotador não modificável ao mesmo tempo em que retém para você uma referência à coleção original.

20.15 Implementações abstratas A estrutura de coleções fornece várias implementações abstratas de interfaces Collection a partir das quais você pode implementar rapidamente aplicativos personalizados completos. Essas implementações abstratas incluem uma implementação magra de Collection chamada AbstractCollection, uma implementação de List que permite acesso aleatório a seus elementos chamada AbstractList, uma implementação de Map chamada AbstractMap, uma implementação de List que permite acesso sequencial a seus elementos chamada AbstractSequentialList, uma implementação de Set chamada AbstractSet e uma implementação de Queue chamada AbstractQueue. Você pode aprender mais sobre essas classes em java.sun.com/javase/6/docs/api/java/ util/package­summary.html. Para escrever uma implementação personalizada, você pode estender a implementação abstrata que melhor atender às suas necessidades e implementar cada um dos métodos abstract da classe. Então, se sua coleção precisa ser modificável, anule qualquer método concreto que impeça a modificação.

20.16 Conclusão

665

20.16 Conclusão Este capítulo introduziu a estrutura de coleções do Java. Você aprendeu a hierarquia de coleção e a maneira de utilizar as interfaces de estrutura de coleções para programar com coleções polimorficamente. Você utilizou as classes ArrayList e LinkedList, que implementam a interface List. Apresentamos as interfaces e classes predefinidas do Java para manipular pilhas e filas. Você utilizou vários métodos predefinidos para manipular coleções. Em seguida, você aprendeu a utilizar a interface Set e a classe HashSet para manipular uma coleção não ordenada de valores únicos. Continuamos nossa apresentação de conjuntos com a interface SortedSet e a classe TreeSet para manipular uma coleção classificada de valores únicos. Em seguida, você aprendeu sobre as interfaces e classes do Java para manipular pares de chave/valor — Map, SortedMap, Hashtable, HashMap e TreeMap. Então discutimos a classe Properties especializada para manipular pares de chave/valor Strings que podem ser armazenadas em um arquivo e recuperadas de um arquivo. Por fim, discutimos os métodos static da classe Collections para obter visualizações não modificáveis e sincronizadas de coleções. O Capítulo 21 demonstra como utilizar as capacidades dos genéricos do Java para que você implemente seus próprios métodos e classes genéricos.

Resumo Seção 20.1 Introdução • A estrutura de coleções Java fornece acesso a estruturas de dados predefinidas bem como a métodos para manipulá-las.

Seção 20.2 Visão geral das coleções • Uma coleção é um objeto que pode armazenar referências a outros objetos. • As classes e interfaces da estrutura de coleções estão no pacote java­.util.

Seção 20.3 Classes empacotadoras de tipo para tipos primitivos • As classes empacotadoras de tipo (por exemplo, Integer, Double, Boolean) permitem aos programadores manipular valores de tipo primitivo como objetos. Os objetos dessas classes podem ser utilizados em coleções.

Seção 20.4 Autoboxing e auto-unboxing • O boxing converte um valor primitivo em um objeto da classe empacotadora de tipo correspondente. O unboxing converte um objeto empacotador de tipo no valor primitivo correspondente. • O Java realiza conversões boxing e unboxing automaticamente.

Seção 20.5 Interface Collection e classe Collections • A interface Collection é a interface-raiz na hierarquia de coleções. As interfaces Set e List estendem Collection. A interface Collection contém operações de adição, limpeza, comparação e retenção de objetos em uma coleção, e o método iterator para obter uma coleção Iterator. • A classe Collections fornece os métodos static para manipular coleções.

Seção 20.6 Listas • Uma List é uma Collection ordenada que pode conter elementos duplicados. • A interface List é implementada pelas classes ArrayList, LinkedList e Vector. ArrayList é uma implementação de array redimensionável de uma List. LinkedList é uma implementação de lista encadeada de uma List. • O método iterador hasNext determina se uma Collection contém outro elemento. O método next retorna uma referência ao próximo objeto na Collection e avança o Iterator. • O método subList retorna uma visualização em uma List. Qualquer alteração feita nessa visualização também será feita na List. • O método clear remove elementos de um List. • O método toArray retorna o conteúdo de uma coleção como um array.

Seção 20.7 Métodos de coleções • Os algoritmos sort, binarySearch, reverse, shuffle, fill, copy, addAll, frequency e disjoint operam em operam em Collections.

Lists.

Os algoritmos min e max

• O algoritmo addAll acrescenta todos os elementos de um array a uma coleção, frequency calcula quantos elementos da coleção são iguais ao elemento especificado e disjoint determina se as duas coleções têm elementos em comum. • Os algoritmos min e max localizam os menores e maiores itens em uma coleção. • A interface Comparator fornece um meio de classificar elementos de uma Collection em uma ordem diferente de sua ordem natural.

666

Capítulo 20  Coleções genéricas

• O método Collections reverseOrder retorna um objeto Comparator que pode ser utilizado com sort para classificar elementos de uma coleção na ordem inversa. • O algoritmo shuffle ordena aleatoriamente os elementos de uma List. • O algoritmo binarySearch localiza um Object em uma List classificada.

Seção 20.8  Classe Stack do pacote java.util • A classe Stack estende Vector. O método Stack push adiciona seu argumento na parte superior da pilha. O método pop remove o elemento superior da pilha. O método peek retorna uma referência ao elemento na parte superior da pilha sem removê-lo. O método Stack isEmpty determina se a pilha está vazia.

Seção 20.9  Classe PriorityQueue e interface Queue • A interface Queue estende a interface Collection e fornece operações adicionais para inserir, remover e inspecionar elementos em uma fila. • PriorityQueue implementa a interface Queue e ordena elementos pela sua ordem natural ou por um objeto Comparator que é fornecido para o construtor. • O método PriorityQueue offer insere um elemento na localização adequada com base na ordem de prioridade. O método poll remove o elemento da mais alta prioridade da fila de prioridade. O método peek obtém uma referência ao elemento de prioridade mais alta da fila de prioridades. O método clear remove todos os elementos da fila de prioridades. O método size obtém o número de elementos na fila de prioridades.

Seção 20.10  Conjuntos • Um Set é uma Collection não ordenada que não contém nenhum elemento duplicado. HashSet armazena seus elementos em uma tabela de hash. TreeSet armazena seus elementos em uma árvore. • A interface SortedSet estende Set e representa um conjunto que mantém seus elementos na ordem de classificação. A classe TreeSet implementa SortedSet. • O método TreeSet headSet obtém uma visualização TreeSet que contém elementos que são menores do que um elemento especificado. O método tailSet obtém uma visualização TreeSet que contém elementos que são maiores que ou iguais a um elemento especificado. Quaisquer alterações feitas nessas visualizações ocorrerão no TreeSet original.

Seção 20.11  Mapas • Maps armazenam pares de chave/valor e não podem conter chaves duplicadas. HashMaps e Hashtables armazenam elementos em uma tabela de hash, e TreeMaps armazenam elementos em uma árvore. • HashMap aceita dois argumentos de tipo — o tipo de chave e o tipo do valor. • O método HashMap put adiciona uma chave e um valor em um HashMap. O método get localiza o valor associado com a chave especificada. O método isEmpty determina se o mapa está vazio. • O método HashMap keySet retorna o conjunto de chaves. O método Map size retorna o número de pares chave/valor no Map. • A interface SortedMap estende Map e representa um mapa que mantém suas chaves em ordem de classificação. A classe TreeMap implementa SortedMap.

Seção 20.12  Classe Properties • Um objeto Properties é um objeto Hashtable persistente. A classe Properties estende Hashtable. • O construtor sem argumentos Properties cria uma tabela Properties vazia. Um construtor sobrecarregado recebe um objeto Properties contendo valores de propriedade padrão. • O método Properties setProperty especifica o valor associado com o argumento de chave. O método getProperty localiza o valor da chave especificada como um argumento. O método store salva o conteúdo de um objeto Properties no OutputStream especificado. O método load restaura o conteúdo de um objeto Properties a partir do InputStream especificado.

Seção 20.13  Coleções sincronizadas • As coleções da estrutura de coleções são não sincronizadas. Os empacotadores de sincronização são oferecidos para coleções que podem ser acessadas por múltiplas threads simultaneamente.

Seção 20.14  Coleções não modificáveis • A classe Collections fornece um conjunto de métodos public static para converter coleções em versões não modificáveis. Empacotadores não modificáveis lançam UnsupportedOperation-Exceptions se forem feitas tentativas de modificar a coleção.

Seção 20.15  Implementações abstratas • A estrutura de coleções fornece várias implementações abstratas de interfaces de coleção a partir das quais você pode concretizar rapidamente implementações personalizadas completas.



Terminologia

667

Terminologia AbstractCollection, classe, 664 AbstractList, classe, 664 AbstractMap, classe, 664 AbstractQueue, classe, 664 AbstractSequentialList, classe, 664 AbstractSet, classe, 664 addAll, método da classe Collections, 652 addAll, método de interfaceList, 643 add, método da classe LinkedList, 644 add, método de interface List, 641 addFirst, método da classe LinkedList, 644 addLast, método da classe LinkedList, 644 API de preferências, 661 array de apoio, 644 ArrayList, classe, 639 asList, método da classe Arrays, 644 autoboxing, 638 auto-unboxing, 638 binarySearch, método da classe Collections, 651 Boolean, classe, 638 boxing, conversão, 638 Byte, classe, 638 Character, classe, 638 classes empacotadoras de tipo, 638 clear, método da classe List, 643 clear, método da classe PriorityQueue, 656 coleções, 637 colisão em uma tabela de hash, 659 Collection, interface, 639 Collections, classe, 639 Comparable, interface, 645 Comparator, interface, 645 compare, método de interface Comparator, 647 contains, método de interface Collection, 641 containsKey, método de interface Map, 661 conversão unboxing, 638 copy, método da classe Collections, 650 disjoint, método da classe Collections, 653 Double, classe, 638 empacotador de sincronização, 663 empacotador não modificável, 664 EmptyStackException, classe, 655 estrutura de coleções, 637

F, sufixo para literais float, 655 fator de carga, 659 fill, método da classe Collections, 650 first, método de interface SortedSet, 658 Float, classe, 638 frequency, método da classe Collections, 653 get, método de interface List, 641 get, método de interface Map, 661 getProperty, método da classe Properties, 661 hashing, 659 HashMap, classe, 658 HashSet, classe, 656 Hashtable, classe, 658 hasNext, método de interface Iterator, 641 hasPrevious, método de interface ListIterator, 643 headSet, método da classe TreeSet, 658 Integer, classe, 638 isEmpty, método da classe Stack, 655 isEmpty, método de interface Map, 661 iterador, 637 iterador bidirecional, 643 Iterator, interface, 639 iterator, método de interface Collection, 641 java.util.prefs, pacote, 661 keySet, método da classe HashMap, 661 last, método da classe SortedSet, 658 L, sufixo para long literais, 655 LinkedList, classe, 639 List, interface, 643 list, método da classe Properties, 663 ListIterator, interface, 639 listIterator, método de interface List, 643 load, método da classe Properties, 663 Long, classe, 638 Map, interface, 658 max, método da classe Collections, 650 método de comparação natural, 645 métodos de visualização de intervalo, 643 métodos empacotadores da classe Collections, 639 min, método da classe Collections, 650 muitos para um, mapeamento, 658 next, método de interface Iterator, 641

objeto empacotador (coleções), 663 offer, método da classe PriorityQueue, 655 peek, método da classe PriorityQueue, 655 peek, método da classe Stack, 655 poll, método da classe PriorityQueue, 655 pop, método da classe Stack, 655 previous, método de interface ListIterator, 643 PriorityQueue, classe, 655 Properties, classe, 661 push, método da classe Stack, 655 put, método de interface Map, 661 Queue, interface, 639 remove, método de interface Iterator, 641 reverse, método da classe Collections, 650 reverseOrder, método da classe Collections, 646 sequência, 639 Set, interface, 639 set, método de interface ListIterator, 643 setProperty, método da classe Properties, 661 Short, classe, 638 shuffle, método da classe Collections, 648 size, método da classe PriorityQueue, 656 size, método de interface List, 641 size, método de interface Map, 661 sort, método da classe Collections, 645 SortedMap, interface, 658 SortedSet, interface, 657 Stack, classe, 654 store, método da classe Properties, 663 sublista, 643 subList, método de interface List, 643 tailSet, método da classe TreeSet, 658 toArray, método de interface List, 644 toLowerCase, método da classe String, 643 toUpperCase, método da classe String, 643 TreeMap, classe, 658 TreeSet, classe, 656 um para um, mapeamento, 658 UnsupportedOperationException, classe, 644 Vector, classe, 639 visualização em uma coleção, 643 volume, operação de, 639

Exercícios de autorrevisão 20.1 Preencha as lacunas em cada uma das seguintes afirmações: a) Um(a) ________ é utilizado(a) para iterar por uma coleção e pode remover elementos da coleção durante a iteração. b) Um elemento em uma List pode ser acessado utilizando o(a) ________ do elemento. c) Assumindo que myArray contém referências aos objetos Double, ________ ocorre quando a instrução “myArray[ 0 ] = 1.25;” executa. d) As classes Java ________ e ________ fornecem as capacidades de estruturas de dados no estilo array que podem redimensionar a si mesmas dinamicamente. e) Se você não especificar um incremento de capacidade, o sistema irá ________ o tamanho do Vector toda vez que capacidade adicional for necessária. f) Você pode utilizar um(a) ________ para criar uma coleção que oferece acesso de leitura para outras pessoas ao mesmo tempo em que permite que você tenha acesso de gravação e leitura.

668

Capítulo 20  Coleções genéricas g) Assumindo que myArray contém referências aos objetos Double, ________ ocorre quando a instrução “double number = myArray[ 0 ];” executa. h) O algoritmo ________ de Collections determina se duas coleções têm elementos em comum.

20.2 Determine se cada sentença é verdadeira ou falsa. Se falso, explique por quê. a) Os valores de tipos primitivos podem ser armazenados diretamente em uma coleção. b) Uma Set pode conter valores duplicados. c) Um Map pode conter chaves duplicatas. d) Uma LinkedList pode conter valores duplicados. e) Collections é uma interface. f) Iterators podem remover elementos. g) Com hashing, enquanto o fator de carga aumenta, a chance de colisões diminui. h) Uma PriorityQueue permite elementos null.

Respostas dos exercícios de autorrevisão 20.1 a) Iterator. b) índice. c) autoboxing. d) ArrayList, Vector. e) dobrar. f) empacotador não modificável. g) auto-unboxing. h) disjoint. 20.2 a) Falsa. O autoboxing ocorre ao adicionar um tipo primitivo a uma coleção, o que significa que o tipo primitivo é convertido em sua classe empacotadora de tipo correspondente. b) Falsa. Um Set não pode conter valores duplicados. c) Falsa. Um Map não pode conter chaves duplicadas. d) Verdadeira. e) Falsa. Collections é uma class; e Collection é uma interface. f) Verdadeira. g) Falsa. À medida que o fator de carga aumenta, menos posições estão disponíveis em relação ao seu número total, portanto, a possibilidade de colisões aumenta. h) Falsa. Tentar inserir um elemento null causa uma NullPointerException.

Exercícios 20.3 Defina cada um dos seguintes termos: a) Collection b) Collections c) Comparator d) List e) fator de carga f) colisão g) relação de troca espaço/tempo em hashing h) HashMap

20.4 Explique brevemente a operação de cada um dos seguintes métodos da classe Vector: a) add b) set c) remove d) removeAllElements e) removeElementAt f) firstElement g) lastElement h) contains i) indexOf j) size k) capacity

20.5 Explique por que inserir elementos adicionais em um objeto Vector cujo tamanho atual é menor que sua capacidade é uma operação relati-

vamente rápida e por que inserir elementos adicionais em um objeto Vector cujo tamanho atual está dentro da capacidade é uma operação relativamente lenta.



Exercícios

669

20.6 Estendendo a classe Vector, os projetistas do Java foram capazes de criar a classe Stack rapidamente. Quais são os aspectos negativos dessa utilização de herança, particularmente para a classe Stack?

20.7 Responda resumidamente às seguintes perguntas: a) Qual é a principal diferença entre um Set e um Map? b) O que acontece quando você adiciona um valor de tipo primitivo (por exemplo, double) a uma coleção? c) Você pode imprimir todos os elementos em uma coleção sem utilizar um Iterator? Se puder, como você os imprime?

20.8 Explique a principal operação de cada um dos seguintes métodos relacionados com Iterator: a) iterator b) hasNext c) next

20.9 Explique brevemente a operação de cada um dos seguintes métodos da classe HashMap: a) put b) get c) isEmpty d) containsKey e) keySet

20.10 Determine se cada uma das sentenças é verdadeira ou falsa. Se falsa, explique por quê. a) Os elementos em uma Collection devem ser classificados em ordem crescente antes que uma binarySearch- possa ser realizada. b) O método first obtém o primeiro elemento em uma TreeSet. c) Uma List criada com o método Arrays asList é redimensionável.

20.11 Explique a operação de cada um dos seguintes métodos da classe Properties: a) load b) store c) getProperty d) list

20.12 Reescreva as linhas 16–25 na Figura 20.3 para que sejam mais concisas utilizando o método asList e o construtor LinkedList que aceita um argumento Collection.

20.13 (Eliminação de duplicatas) Escreva um programa que leia em uma série de nomes e elimine os duplicados armazenando-os em um Set. Permita que o usuário procure um primeiro nome.

20.14 (Contando letras) Modifique o programa da Figura 20.18 para contar o número de ocorrências de cada letra em vez do número de cada palavra. Por exemplo, a string "HELLO

THERE" contém dois Hs, três Es, dois Ls, um O, um T e um R. Exiba os resultados.

20.15 (Seletor de cor) Utilize uma HashMap para criar uma classe reutilizável a fim de escolher uma das 13 cores predefinidas na classe Color. Os

nomes das cores devem ser utilizados como chaves; e os objetos Color predefinidos devem ser utilizados como valores. Coloque essa classe em um pacote que pode ser importado em qualquer programa Java. Utilize sua nova classe em um aplicativo que permita ao usuário selecionar uma cor e desenhar uma forma nessa cor.

20.16 (Contando palavras duplicadas) Escreva um programa que determine e imprima o número de palavras duplicadas em uma frase. Trate da mesma maneira letras minúsculas e letras maiúsculas. Ignore a pontuação.

20.17 (Inserindo elementos em uma LinkedList na ordem classificada) Escreva um programa que insira 25 números inteiros aleatórios de 0

a 100 em um objeto LinkedList. O programa deve classificar os elementos e, então, calcular a soma dos elementos e a média de ponto flutuante dos elementos.

20.18 (Copiando e invertendo

LinkedLists)

Escreva um programa que cria um objeto

LinkedList

LinkedList que contenha uma cópia da primeira lista, mas na ordem inversa.

de 10 caracteres e um segundo objeto

20.19 (Números primos e fatores primos) Escreva um programa que aceite como entrada um número inteiro e determine se é ou não um número pri-

mo. Se o número não for primo, exiba seus fatores primos únicos. Lembre-se de que os fatores de um número primo são somente 1 e o próprio número primo. Cada número que não é primo tem uma fatoração em primos única. Por exemplo, considere o número 54. Os fatores primos de 54 são 2, 3, 3 e 3. Quando os valores são multiplicados, o resultado é 54. Para o número 54, a saída dos fatores primos deve ser 2 e 3. Utilize Sets como parte de sua solução.

20.20 (Classificando palavras com um TreeSet) Escreva um programa que utilize um método String

split para tokenizar uma linha de texto inserida pelo usuário e coloque cada token em um TreeSet. Imprima os elementos do TreeSet. [Nota: Isso deve fazer com que os elementos sejam impressos na ordem de classificação ascendente.]

20.21 (Alterando a ordem de classificação de uma PriorityQueue) A saída da Figura 20.15 (PriorityQueueTest) mostra que Priority­

Queue ordena elementos Double em ordem crescente. Reescreva a Figura 20.15 de modo que ela ordene os elementos Double em ordem decres-

cente (isto é, 9.8 deve ser o elemento de maior prioridade em vez de 3.2).

21

Todo gênio vê o mundo de um ângulo diferente de seus pares. — Havelock Ellis

… nossa individualidade especial, enquanto distinta da nossa humanidade genérica. — Oliver Wendell Holmes, Sr.

Born under one law, to another bound. [Nascido sob uma lei, subjugado a outra.] — Lorde Brooke

Você lida com o material bruto da opinião, e, se minhas convicções têm alguma validade, em última instância a opinião governa o mundo. — Woodrow Wilson

Classes e métodos genéricos

Objetivos Neste capítulo, você aprenderá: 

A criar métodos genéricos que realizam tarefas idênticas em argumentos de diferentes tipos.



A criar uma classe Stack genérica que pode ser utilizada para armazenar objetos de qualquer tipo de classe ou interface.



A entender como sobrecarregar métodos genéricos com métodos não genéricos ou com outros métodos genéricos.



A entender tipos brutos e como eles ajudam a alcançar a retrocompatibilidade.



A utilizar curingas quando informações precisas de tipo sobre um parâmetro não são requeridas no corpo de método.



A identificar o relacionamento entre herança e genéricos.

Sumário

21.1 Introdução

21.1 Introdução 21.2 Motivação para métodos genéricos 21.3 Métodos genéricos: implementação e tradução em tempo de compilação 21.4 Questões adicionais da tradução em tempo de compilação: métodos que utilizam um parâmetro de tipo como o tipo de retorno

671

21.5 21.6 21.7 21.8

Sobrecarregando métodos genéricos Classes genéricas Tipos brutos Curingas em métodos que aceitam parâmetros de tipo 21.9 Genéricos e herança: notas 21.10 Conclusão

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios

21.1 Introdução Você já utilizou as classes e os métodos genéricos existentes nos capítulos 7 e 20. Neste capítulo, aprenderá a escrever seus próprios. Também aprenderá os relacionamentos entre os genéricos e outros recursos Java, como sobrecarga e herança. Seria conveniente se pudéssemos escrever um único método sort para classificar os elementos em um array de Integer, em um array de String ou em um array de qualquer tipo que suporte ordenamento (isto é, seus elementos podem ser comparados). Também seria conveniente se pudéssemos escrever uma única classe Stack que seria utilizada como uma Stack de inteiros, uma Stack de números de ponto flutuante, uma Stack de Strings ou uma Stack de qualquer outro tipo. Seria ainda mais conveniente se pudéssemos detectar não correspondências de tipos em tempo de compilação — conhecida como segurança de tipo em tempo de compilação. Por exemplo, se uma Stack armazenasse somente inteiros, ao tentar inserir uma String nessa Stack deveria emitir um erro em tempo de compilação. Este capítulo discute genéricos, que fornecem significado para criar os modelos gerais já mencionados. Métodos genéricos e classes genéricas (e interfaces) permitem especificar, com uma única declaração de método, um conjunto de métodos relacionados ou, com uma única declaração de classe, um conjunto de tipos relacionados, respectivamente. Os genéricos também fornecem segurança de tipo em tempo de compilação que permite capturar tipos inválidos em tempo de compilação. Poderíamos escrever um método genérico para classificar um array de objetos e então invocar o método genérico com arrays de Integers, arrays de Doubles, arrays de Strings e assim por diante, para classificar os elementos no array. O compilador realizaria uma verificação de tipo para assegurar que o array passado para o método de classificação contenha elementos do mesmo tipo. Poderíamos escrever uma única classe Stack genérica que manipulasse uma pilha de objetos e então instanciasse objetos Stack em uma pilha de Integers, uma pilha de Doubles, uma pilha de Strings e assim por diante. O compilador realizaria a verificação de tipo para assegurar que a Stack armazena elementos do mesmo tipo.

Observação de engenharia de software 21.1 Métodos e classes genéricas estão entre as capacidades mais poderosas do Java para reutilização de software com segurança de tipo em tempo de compilação.

21.2 Motivação para métodos genéricos Métodos sobrecarregados são frequentemente utilizados para realizar operações semelhantes em tipos diferentes de dados. Para encorajar o uso dos métodos genéricos, vamos começar com um exemplo (Figura 21.1) contendo os métodos printArray sobrecarregados (linhas 21–28, linhas 31–38 e linhas 41–48) que imprimem representações String dos elementos de um array Integer, um array Double e um array Character, respectivamente. Poderíamos ter utilizado array de tipos primitivos int, double e char. Utilizamos arrays das classes empacotadoras de tipo para configurar nosso exemplo de método genérico, porque apenas tipos por referência podem ser utilizados com classes e métodos genéricos. 1 2 3 4 5 6 7 8 9

// Figura 21.1: OverloadedMethods.java // Imprimindo elementos do array com métodos sobrecarregados. public class OverloadedMethods { public static void main( String[] args ) { // cria arrays de Integer, Double e Character Integer[] integerArray = { 1, 2, 3, 4, 5, 6 }; Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };

672 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

Capítulo 21  Classes e métodos genéricos Character[] characterArray = { 'H', 'E', 'L', 'L', 'O' }; System.out.println( "Array integerArray contains:" ); printArray( integerArray ); // passa um array de Integer System.out.println( "\nArray doubleArray contains:" ); printArray( doubleArray ); // passa um array Double System.out.println( "\nArray characterArray contains:" ); printArray( characterArray ); // passa um array de Character } // fim de main // método printArray para imprimir um array de Integer public static void printArray( Integer[] inputArray ) { // exibe elementos do array for ( Integer element : inputArray ) System.out.printf( "%s ", element ); System.out.println(); } // fim do método printArray // método printArray para imprimir um array de Double public static void printArray( Double[] inputArray ) { // exibe elementos do array for ( Double element : inputArray ) System.out.printf( "%s ", element ); System.out.println(); } // fim do método printArray // método printArray para imprimir um array de Character public static void printArray( Character[] inputArray ) { // exibe elementos do array for ( Character element : inputArray ) System.out.printf( "%s ", element ); System.out.println(); } // fim do método printArray } // fim da classe OverloadedMethods

Array integerArray contains: 1 2 3 4 5 6 Array doubleArray contains: 1.1 2.2 3.3 4.4 5.5 6.6 7.7 Array characterArray contains: H E L L O

Figura 21.1  |  Imprimindo elementos do array com métodos sobrecarregados.

O programa começa declarando e inicializando três arrays — array Integer integerArray de seis elementos (linha 8), array de sete elementos (linha 9) e array Character characterArray de cinco elementos (linha 10). Em seguida, as linhas 12–17 exibem o conteúdo de cada array. Quando o compilador encontra uma chamada de método, ele tenta localizar uma declaração de método com o mesmo nome e parâmetros que correspondam aos tipos de argumentos da chamada. Nesse exemplo, cada chamada a printArray corresponde a uma das declarações do método printArray. Por exemplo, a linha 13 chama printArray com integerArray como seu argumento. O compilador determina o tipo do argumento (isto é, Integer[]) e tenta localizar um método printArray que especifica um parâmetro Integer[] (linhas 21–28), e então configura uma chamada a esse método. De maneira semelhante, quando o compilador encontra a chamada na linha 15, ele determina o tipo do argumento (isto é, Double[]), tenta localizar um método printArray que especifica um parâmetro Double[] (linhas 31–38) e, então, configura uma chamada a esse método. Por fim, quando o compilador encontra a chamada a print­ Array na linha 17, ele determina o tipo do argumento (isto é, Character[]), tenta localizar um método printArray que especifica um parâmetro Character[] (linhas 41–48) e, então, configura uma chamada a esse método. Estude cada método printArray. Observe que o tipo de elemento do array de tipos aparece no cabeçalho de cada método (linhas 21, 31 e 41) e no cabeçalho da instrução for (linhas 24, 34 e 44). Se fôssemos substituir os tipos de elemento em cada método por um nome genérico — T por convenção — os três métodos se pareceriam com aquele na Figura 21.2. Parece que se fosse possível substituir o tipo de elemento do array em cada um dos três métodos por um tipo genérico único, seríamos então capazes de declarar um método printArray Array doubleArray

21.3 Métodos genéricos: implementação e tradução em tempo de compilação

673

que pode exibir as representações String dos elementos de qualquer array que contém objetos. O método na Figura 21.2 é semelhante à declaração do método printArray genérico que discutimos na Seção 21.3. 1 2 3 4 5 6 7 8

public static void printArray( T[] inputArray ) { // exibe elementos do array for ( T element : inputArray) System.out.printf( "%s ", element ); System.out.println(); } // fim do método printArray

Figura 21.2 | Método printArray em que nomes de tipos reais são substituídos pelo nome genérico T por convenção.

21.3 Métodos genéricos: implementação e tradução em tempo de compilação Se as operações realizadas por vários métodos sobrecarregados forem idênticas para cada tipo de argumento, os métodos sobrecarregados podem ser codificados mais compacta e convenientemente com um método genérico. Você pode escrever uma única declaração de método genérico que pode ser chamada com argumentos de tipos diferentes. Com base nos tipos dos argumentos passados para o método genérico, o compilador trata cada chamada de método apropriadamente. A Figura 21.3 reimplementa o aplicativo da Figura 21.1 utilizando um método printArray genérico (linhas 22–29). Observe que as chamadas do método printArray nas linhas 14, 16 e 18 são idênticas àquelas da Figura 21.1 (linhas 14, 16 e 18) e que as saídas dos dois aplicativos são idênticas. Isso demonstra radicalmente o poder expressivo dos genéricos. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

// Figura 21.3: GenericMethodTest.java // Imprimindo elementos do array com o método genérico printArray. public class GenericMethodTest { public static void main( String[] args ) { // cria arrays de Integer, Double e Character Integer[] intArray = { 1, 2, 3, 4, 5, 6 }; Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 }; Character[] charArray = { 'H', 'E', 'L', 'L', 'O' }; System.out.println( "Array integerArray contains:" ); printArray( integerArray ); // passa um array de Integer System.out.println( "\nArray doubleArray contains:" ); printArray( doubleArray ); // passa um array Double System.out.println( "\nArray characterArray contains:" ); printArray( characterArray ); // passa um array de Character } // fim de main // método genérico printArray public static < T > void printArray( T[] inputArray ) { // exibe elementos do array for ( T element : inputArray ) System.out.printf( “%s “, element ); System.out.println(); } // fim do método printArray } // fim da classe GenericMethodTest

Array integerArray contains: 1 2 3 4 5 6 Array doubleArray contains: 1.1 2.2 3.3 4.4 5.5 6.6 7.7 Array characterArray contains: H E L L O

Figura 21.3 | Imprimindo elementos do array com o método genérico printArray.

674

Capítulo 21  Classes e métodos genéricos

A linha 22 inicia a declaração do método printArray. Todas as declarações de métodos genéricos contêm uma seção de parâmetro de tipo delimitado por colchetes angulares (< e >) que precede o tipo de retorno do método (< T > nesse exemplo). Cada seção de parâmetro de tipo contém um ou mais parâmetros de tipos (também chamados parâmetros de tipo formais), separados por vírgulas. Um parâmetro de tipo, também conhecido como uma variável de tipo, é um identificador que especifica um nome genérico do tipo. Os parâmetros de tipo podem ser utilizados para declarar o tipo de retorno, tipos de parâmetros e tipos de variáveis locais em uma declaração de método genérico e atuam como marcadores de lugar para os tipos dos argumentos passados ao método genérico, conhecidos como argumentos de tipos reais. O corpo de um método genérico é declarado como o de qualquer outro método. Observe que parâmetros de tipos podem representar somente tipos por referência — não tipos primitivos (como int, double e char). Também observe que os nomes dos parâmetros de tipos por toda a declaração de método devem corresponder aqueles declarados na seção de parâmetro de tipo. Por exemplo, a linha 25 declara element como tipo T, que corresponde ao parâmetro de tipo (T) declarado na linha 22. Além disso, um parâmetro de tipo pode ser declarado somente uma vez na seção de parâmetro de tipo, mas pode aparecer mais de uma vez na lista de parâmetros do método. Por exemplo, o nome do parâmetro de tipo T aparece duas vezes na seguinte lista de parâmetros do método: public static void printTwoArrays( T[] array1, T[] array2 )

Os nomes de parâmetro de tipo não precisam ser únicos entre diferentes métodos genéricos.

Erro comum de programação 21.1 Ao declarar um método genérico, não conseguir colocar uma seção de parâmetro de tipo antes do tipo de retorno de um método é um erro de sintaxe — o compilador não entenderá os nomes do parâmetro de tipo quando eles forem encontrados no método.

A seção de parâmetro de tipo do método printArray declara o parâmetro do tipo T como o marcador de lugar para o tipo de elemento do array que printArray enviará para a saída. Observe que T aparece na lista de parâmetros como o tipo de elemento do array (linha 22). O cabeçalho da instrução for (linha 25) também utiliza T como o tipo de elemento. Estas são as duas localizações exatas em que os métodos printArray sobrecarregados da Figura 21.1 especificaram Integer, Double ou Character como o tipo de elemento do array. O restante do printArray é idêntico às versões apresentadas na Figura 21.1.

Boa prática de programação 21.1 É recomendável que parâmetros de tipos sejam especificados como letras maiúsculas individuais. Tipicamente, um parâmetro de tipo que representa o tipo de um elemento do array (ou outra coleção) é nomeado T.

Como na Figura 21.1, o programa começa declarando e inicializando o array Integer integerArray de seis elementos (linha 9), o array Double doubleArray de sete elementos (linha 10) e o array Character characterArray de cinco elementos (linha 11). O programa então gera uma saída para cada array chamando printArray (linhas 14, 16 e 18) — uma vez com o argumento integerArray, uma vez com o argumento doubleArray e uma vez com o argumento characterArray. Quando o compilador encontra a linha 14, ele primeiro determina o tipo do argumento integerArray (isto é, Integer[]) e tenta localizar um método chamado printArray que especifica um único parâmetro Integer[]. Não há tal método nesse exemplo. Em seguida, o compilador determina se há um método genérico chamado printArray que especifica um parâmetro de array individual e utiliza um parâmetro de tipo para representar o tipo de elemento do array. O compilador determina que o printArray (linhas 22–29) é uma correspondência e configura uma chamada ao método. O mesmo processo é repetido para chamadas ao método printArray nas linhas 16 e 18.

Erro comum de programação 21.2 Se o compilador não puder encontrar uma correspondência entre uma chamada de método e uma declaração de método genérico ou não genérico, ocorrerá um erro de compilação.

Erro comum de programação 21.3 Se o compilador não encontrar uma declaração de método que corresponda exatamente a uma chamada de método, mas encontrar dois ou mais métodos genéricos que podem satisfazer a chamada de método, ocorrerá um erro de compilação.

Além de configurar as chamadas de método, o compilador também determina se as operações no corpo do método podem ser aplicadas a elementos do tipo armazenado no argumento do array. A única operação executada nos elementos do array nesse exemplo é gerar saída para sua representação String. A linha 26 realiza uma chamada a toString implícita em cada element. Para trabalhar com genéricos, todo elemento do array deve ser um objeto de um tipo de classe ou interface. Como todos os objetos têm um método toString, o compilador fica satisfeito com fato de que a linha 26 realiza uma operação válida para qualquer objeto no argumento do array do printArray. Os métodos toString das classes Integer, Double e Character retornam a representação de String dos valores subjacentes do valor int, double ou char, respectivamente.

21.4 Métodos que utilizam um parâmetro de tipo como tipo de retorno

675

Erasure em tempo de compilação Quando o compilador traduz o método genérico printArray em bytecodes Java, ele remove a seção de parâmetro de tipo e substitui os parâmetros de tipo por tipos reais. Esse processo é conhecido como erasure. Por padrão, todos os tipos genéricos são substituídos pelo tipo Object. Assim, a versão compilada do método printArray aparece como mostrada na Figura 21.4 — há somente uma cópia desse código utilizada para todas as chamadas a printArray no exemplo. Isso é bem diferente de outros mecanismos semelhantes, como templates do C++ em que uma cópia separada do código-fonte é gerada e compilada para cada tipo passado como um argumento para o método. Como veremos na Seção 21.4, a tradução e compilação dos genéricos é um pouco mais complicada do que aquilo que foi discutido nesta seção. Declarando printArray como um método genérico na Figura 21.3, eliminamos a necessidade dos métodos sobrecarregados da Figura 21.1, poupando 19 linhas de código e criando um método reutilizável que pode gerar saída das representações de String dos elementos em qualquer array que contém objetos. Entretanto, esse exemplo em particular poderia simplesmente ter declarado o método printArray como mostrado na Figura 21.4, utilizando um array Object como o parâmetro. Isso produziria os mesmos resultados, pois qualquer Object pode ser enviado para a saída como uma String. Em um método genérico, os benefícios tornam-se aparentes quando o método também utiliza um parâmetro de tipo, o tipo de retorno do método, como demonstraremos na próxima seção. 1 2 3 4 5 6 7 8

public static void printArray(Object [] inputArray ) { // exibe elementos do array for (Object element : inputArray ) System.out.printf( "%s ", element ); System.out.println(); } // fim do método printArray

Figura 21.4 | O método genérico printArray depois de a erasure ser realizada pelo compilador.

21.4 Questões adicionais da tradução em tempo de compilação: métodos que utilizam um parâmetro de tipo como tipo de retorno Vamos considerar o exemplo de um método genérico em que parâmetros de tipo são utilizados no tipo de retorno e na lista de parâmetros (Figura 21.5). O aplicativo utiliza um método genérico maximum para determinar e retornar o maior dos seus três argumentos do mesmo tipo. Infelizmente, o operador relacional > não pode ser utilizado com tipos por referência. Entretanto, é possível comparar dois objetos da mesma classe se essa classe implementar a interface genérica Comparable (pacote java.lang). Todas as classes empacotadoras de tipo para tipos primitivos implementam essa interface. Como ocorre com as classes genéricas, as interfaces genéricas permitem especificar, com uma única declaração de interface, um conjunto de tipos relacionados. Objetos Comparable têm um método compareTo. Por exemplo, se houver dois objetos Integer, integer1 e integer2, eles poderão ser comparados com a expressão: integer1.compareTo( integer2 )

É sua responsabilidade ao declarar uma classe que implementa Comparable para declarar o método compareTo a fim de ele comparar o conteúdo dos dois objetos dessa classe e retornar os resultados da comparação. O método deve voltar 0 se os objetos forem iguais, um número inteiro negativo se object1 for menor que object2 ou um número inteiro positivo se object1 for maior que object2. Por exemplo, o método compareTo da classe Integer compara os valores de int armazenados em dois objetos Integer. Um benefício da implementação da interface Comparable é que objetos Comparable podem ser utilizados com os métodos de classificação e pesquisa da classe Collections (pacote java.util). Discutimos esses métodos no Capítulo 20, “Coleções genéricas”. Neste exemplo, utilizaremos o método compareTo no método maximum para ajudar a determinar o maior valor. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura 21.5: MaximumTest.java // O método genérico maximum retorna o maior dos três objetos. public class MaximumTest { public static void main( String[] args ) { System.out.printf( "Maximum of %d, %d and %d is %d\n\n", 3, 4, 5, maximum( 3, 4, 5 ) ); System.out.printf( "Maximum of %.1f, %.1f and %.1f is %.1f\n\n", 6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) ); System.out.printf( "Maximum of %s, %s and %s is %s\n", "pear",

676 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

Capítulo 21  Classes e métodos genéricos "apple", "orange", maximum( “pear”, “apple”, “orange” ) ); } // fim de main // determina o maior dos três objetos Comparable public static < T extends Comparable< T > > T maximum( T x, T y, T z ) { T max = x; // supõe que x é inicialmente o maior if ( y.compareTo( max ) > 0 ) max = y; // y é o maior até agora if ( z.compareTo( max ) > 0 ) max = z; // z é o maior return max; // retorna o maior objeto } // fim do método Maximum } // fim da classe MaximumTest

Maximum of 3, 4 and 5 is 5 Maximum of 6.6, 8.8 and 7.7 is 8.8 Maximum of pear, apple and orange is pear

Figura 21.5  |  Método genérico maximum com um limite superior no seu parâmetro de tipo.

Método genérico maximum O método genérico maximum (linhas 17–28) utiliza o parâmetro de tipo T como o tipo de retorno do método (linha 17), como o tipo dos parâmetros do método de x, y e z (linha 17) e como o tipo da variável local max (linha 19). A seção de parâmetro de tipo especifica que T estende Comparable — somente objetos das classes que implementam a interface Comparable podem ser utilizados com esse método. Nesse caso, Comparable é conhecido como o limite superior do parâmetro de tipo. Por padrão, Object é o limite superior. Observe que declarações do parâmetro de tipo que limitam o parâmetro sempre utilizam a palavra-chave extends independentemente de o parâmetro de tipo estender uma classe ou implementar uma interface. Esse parâmetro de tipo é mais restritivo do que aquele especificado para printArray na Figura 21.3, o qual foi capaz de gerar a saída de arrays contendo qualquer tipo de objeto. A restrição à utilização de objetos Comparable é importante, pois nem todos os objetos podem ser comparados. Entretanto, é garantido que objetos Comparable terão um método compareTo. O método maximum utiliza o mesmo algoritmo utilizado na Seção 6.4 para determinar o maior dos seus três argumentos. O método supõe que seu primeiro argumento (x) é o maior e o atribui à variável local max (linha 19). Em seguida, a instrução if nas linhas 21–22 determina se y é maior que max. A condição invoca o método compareTo de y com a expressão y.compareTo(max), que retorna um número inteiro negativo, 0 ou número inteiro positivo, para determinar o relacionamento com max de y. Se o valor de retorno de compareTo for maior que 0, então y é maior e será atribuído à variável max. De maneira semelhante, a instrução if nas linhas 24–25 determina se z é maior que max. Se for, a linha 25 atribui z a max. Então, a linha 27 retorna max ao chamador. Chamando o método maximum Em main (linhas 6–14), a linha 9 chama maximum com os inteiros 3, 4 e 5. Quando o compilador encontra essa chamada, ele primeiro procura um método maximum que recebe três argumentos do tipo int. Não há tal método, assim o compilador procura um método genérico que possa ser utilizado e encontra o método genérico maximum. Entretanto, lembre-se de que os argumentos para um método genérico devem ser de um tipo por referência. O compilador assim converte por autoboxing os três valores de int em objetos Integer e especifica que os três objetos Integer serão passados para maximum. Observe que a classe Integer (pacote java.lang) implementa a interface Comparable de tal maneira que o método compareTo compara os valores int em dois objetos Integer. Portanto, Integers são argumentos válidos para o método maximum. Quando o Integer representando o valor máximo é retornado, tentamos enviá-lo para a saída com o especificador de formato %d, que gera a saída de um valor de tipo primitivo int. Dessa forma, o valor de retorno de maximum é impresso como um valor int. Um processo semelhante ocorre para os três argumentos double passados para maximum na linha 11. Cada double é convertido por autoboxing em um objeto Double e passado para maximum. Novamente, isso é permitido porque a classe Double (pacote java.lang) implementa a interface Comparable. O Double retornado por maximum é gerado com o especificador de formato %.1f, que envia para a saída um valor de tipo primitivo double. Portanto, o valor de retorno de maximum é convertido por auto-unboxing e enviado para a saída como um double. A chamada a maximum na linha 13 recebe três Strings, que também são objetos Comparable. Observe que colocamos intencionalmente o maior valor em uma posição diferente em cada chamada de método (linhas 9, 11 e 13) para mostrar que o método genérico sempre encontra o valor máximo, independentemente da sua posição na lista de argumentos.

21.5 Sobrecarregando métodos genéricos

677

Limite superior de um parâmetro de tipo Quando o compilador traduz o método genérico maximum em bytecodes Java, ele utiliza a técnica de erasure (apresentada na Seção 21.3) para substituir os parâmetros de tipo por tipos reais. Na Figura 21.3, todos os tipos genéricos foram substituídos pelo tipo Object. Na verdade, todos os parâmetros de tipo são substituídos por aquilo que se denomina limite superior do parâmetro de tipo, que é especificado na seção do parâmetro de tipo. Para indicar o limite superior, depois do nome do parâmetro de tipo posicione a palavra-chave extends e o nome da classe ou interface que representa o limite superior, ou uma lista separada por vírgula dos tipos que representam o limite superior. A lista pode conter zero ou uma classe e zero ou mais interfaces. Por exemplo, a seção do parâmetro de tipo do método maximum (Figura 21.5), especificamos o limite superior do parâmetro de tipo T como o tipo Comparable desta maneira: T extends Comparable< T >

Assim, somente objetos Comparable podem ser passados como argumentos para maximum — qualquer coisa além de um Comparable resultará em erros de compilação. A menos que especificado de outro modo, Object é o limite superior padrão. A Figura 21.6 simula a erasure de tipos do método maximum, mostrando o código-fonte do método depois de a seção de parâmetro de tipo ter sido removida e o parâmetro de tipo T ter sido substituído pelo limite superior, Comparable, por toda a declaração de método. Observe que a erasure de Comparable é simplesmente Comparable. 1 2 3 4 5 6 7 8 9 10 11 12

public static Comparable maximum(Comparable x, Comparable y, Comparable z) { Comparable max = x; // supõe que x é inicialmente o maior if ( y.compareTo( max ) > 0 ) max = y; // y é o maior até agora if ( z.compareTo( max ) > 0 ) max = z; // z é o maior return max; // retorna o maior objeto } // fim do método Maximum

Figura 21.6 | O método genérico maximum depois de a erasure ser realizada pelo compilador.

Depois da erasure, a versão compilada do método maximum especifica que ele retorna o tipo Comparable. Entretanto, o método chamador não espera receber um Comparable. Ele espera receber um objeto do mesmo tipo que foi passado para maximum como um argumento — Integer, Double ou String nesse exemplo. Quando o compilador substitui as informações do parâmetro de tipo pelo tipo do limite superior na declaração do método, ele também insere operações explícitas de coerção na frente de cada chamada de método para assegurar que o valor retornado é do tipo esperado pelo chamador. Portanto, a chamada a maximum na linha 9 (Figura 21.5) é precedida por uma coerção para Integer, como em (Integer) maximum( 3, 4, 5 )

a chamada a maximum na linha 11 é precedida por uma coerção em Double, como em (Double) maximum( 6.6, 8.8, 7.7 )

e a chamada a maximum na linha 13 é precedida por uma coerção em String, como em (String) maximum( "pear", "apple", "orange" )

Em cada caso, o tipo da coerção para o valor de retorno é inferido a partir dos tipos dos argumentos de método na chamada de método particular porque, de acordo com a declaração do método, o tipo de retorno e os tipos de argumento correspondem.

Possíveis ClassCastExceptions Nesse exemplo, você não pode utilizar um método que aceita Objetos, porque a classe Object só fornece uma comparação de igualdade. Além disso, sem genéricos, você seria responsável por implementar a operação de coerção. O uso de genéricos assegura que a coerção inserida nunca lançará uma ClassCastException, supondo que genéricos sejam utilizados por todo seu código (isto é, você não misturou código antigo com código novo de genéricos).

21.5 Sobrecarregando métodos genéricos Um método genérico pode ser sobrecarregado. Uma classe pode fornecer dois ou mais métodos genéricos que especificam o mesmo nome de método, mas diferentes parâmetros de método. Por exemplo, o método genérico printArray da Figura 21.3 poderia ser sobrecarregado por outro método genérico printArray com os parâmetros adicionais lowSubscript e highSubscript para especificar a parte do array que será enviada para a saída (ver Exercício 21.5).

678

Capítulo 21

Classes e métodos genéricos

Um método genérico também pode ser sobrecarregado por métodos não genéricos. Quando o compilador encontra uma chamada de método, ele procura a declaração de método que corresponde mais precisamente ao nome de método e aos tipos de argumentos especificados na chamada. Por exemplo, o método genérico printArray da Figura 21.3 poderia ser sobrecarregado por uma versão específica para Strings, que gera a saída de Strings em um formato tabular elegante (ver Exercício 21.6). Quando o compilador encontra uma chamada de método, ele realiza um processo de correspondência para determinar qual método invocar. O compilador tenta encontrar e utilizar uma correspondência precisa em que o nome do método e os tipos de argumento da chamada de método correspondam àqueles da declaração de um método específico. Se não houver um método desse, o compilador tenta localizar um método com tipos compatíveis ou um método genérico correspondente.

21.6 Classes genéricas O conceito de uma estrutura de dados, como uma pilha, pode ser entendido independentemente do tipo de elemento que ela manipula. Classes genéricas fornecem um meio de descrever o conceito de uma pilha (ou de qualquer outra classe) de uma maneira independente do tipo. Podemos então instanciar objetos específicos de tipo da classe genérica. Essa capacidade fornece uma excelente oportunidade de reutilização de software. Como visto no Capítulo 20, uma vez que tenha uma classe genérica, você pode utilizar uma notação concisa e simples para indicar o(s) tipo(s) real(is) que deve(m) ser utilizado(s) no lugar do(s) parâmetro(s) de tipo da classe. Em tempo de compilação, o compilador Java garante a segurança de tipo do seu código e utiliza as técnicas de erasure descritas nas seções 21.3–21.4 para permitir que o código do seu cliente interaja com a classe genérica. Uma classe Stack genérica, por exemplo, poderia ser a base para criar muitas classes Stack lógicas (por exemplo, “Stack de Double”, “Stack de Integer”, “Stack de Character”, “Stack de Employee”). Essas classes são conhecidas como classes parametrizadas ou tipos parametrizados porque aceitam um ou mais parâmetros. Lembre-se de que parâmetros de tipo só representam tipos por referência, o que significa que a classe Stack genérica não pode ser instanciada com tipos primitivos. Entretanto, é possível instanciar uma Stack que armazena objetos das classes empacotadoras de tipo do Java e permitir que o Java utilize o autoboxing para converter os valores primitivos em objetos. Lembre-se de que o autoboxing ocorre quando o valor de um tipo primitivo (por exemplo, int) é inserido em uma Stack que contém objetos da classe empacotadora (por exemplo, Integer). O auto-unboxing ocorre quando um objeto da classe empacotadora é removido da Stack e atribuído a uma variável de tipo primitivo.

Implementando uma classe Stack genérica A Figura 21.7 apresenta uma declaração da classe Stack genérica. Uma declaração de classe genérica se parece com uma não genérica, mas o nome da classe é seguido por uma seção do parâmetro de tipo (linha 5). Nesse caso, o parâmetro de tipo T representa o tipo de elemento que a Stack manipulará. Como ocorre com métodos genéricos, a seção de parâmetro de tipo de uma classe genérica pode ter um ou mais parâmetros de tipo separado por vírgulas. (Você criará uma classe genérica com dois parâmetros de tipo no Exercício 21.8.) O parâmetro de tipo T é utilizado por toda a declaração de classe Stack para representar o tipo de elemento. [Nota: Esse exemplo implementa uma Stack como uma ArrayList.] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

// Figura 21.7: Stack.java // Empilha a declaração de classe genérica. import java.util.ArrayList; public class Stack< T > { private ArrayList< T > elements; // ArrayList armazena elementos de pilha // construtor sem argumento cria uma pilha do tamanho padrão public Stack() { this( 10 ); // tamanho padrão da pilha } // fim do construtor sem argumentos da classe Stack // construtor cria uma pilha com o número especificado de elementos public Stack( int capacity ) { int initCapacity = capacity > 0 ? capacity : 10; // valida elements = new ArrayList< T >( initCapacity ); // cria a ArrayList } // fim do construtor Stack de um argumento // insere o elemento na pilha public void push( T pushValue ) { elements.add( pushValue ); // insere pushValue na Stack

26 27 28 29 30 31 32 33 34 35 36 37

21.6  Classes genéricas

679

} // fim do método push // retorna o elemento superior se não estiver vazia; do contrário lança uma EmptyStackException public T pop() { if ( elements.isEmpty() ) // se a pilha estiver vazia throw new EmptyStackException( "Stack is empty, cannot pop" ); // remove e retorna o elemento superior da Stack return elements.remove( elements.size() - 1 ); } // fim do método pop } // fim da classe Stack < T >

Figura 21.7  |  Declaração da classe genérica Stack.

A classe Stack declara a variável elements como um ArrayList (linha 7). Essa ArrayList armazenará os elementos da Como você sabe, uma ArrayList pode crescer dinamicamente, assim objetos da nossa classe Stack também podem crescer dinamicamente. O construtor sem argumento da classe Stack (linhas 10–13) invoca o construtor de um argumento (linhas 16–20) para criar uma Stack no qual a ArrayList subjacente tem uma capacidade de 10 elementos. O construtor de um argumento também pode ser chamado diretamente para criar uma Stack com uma capacidade inicial especificada. A linha 18 valida o argumento do construtor. A linha 19 cria a ArrayList com a capacidade especificada (ou 10 se a capacidade for inválida). O método push (linhas 23–26) utiliza o método add ArrayList para acrescentar o item inserido ao final de ArrayList elements. O último elemento em ArrayList representa a parte superior da pilha. O método pop (linhas 29–36) primeiro determina se está sendo feita uma tentativa de remover um elemento de uma Stack vazia. Nesse caso, a linha 32 lança uma EmptyStackException (declarada na Figura 21.8). Do contrário, a linha 35 retorna o elemento na parte superior da Stack removendo o último elemento na ArrayList subjacente. A classe EmptyStackException (Figura 21.8) fornece um construtor sem argumento e um construtor de um argumento. O construtor sem argumentos configura a mensagem de erro padrão e o construtor de um argumento configura uma mensagem de erro personalizada. stack.

1 2 3 4 5 6 7 8 9 10

// Figura 21.8: EmptyStackException.java // Declaração da classe EmptyStackException. public class EmptyStackException extends RuntimeException { // construtor sem argumento public EmptyStackException() { this( "Stack is empty" ); } // fim do construtor sem argumentos de EmptyStackException

11 12 13 14 15 16

// construtor de um argumento public EmptyStackException( String message ) { super( message ); } // fim do construtor de EmptyStackException de um argumento } // fim da classe EmptyStackException

Figura 21.8  |  Declaração de classe EmptyStackException.

Como ocorre com métodos genéricos, quando uma classe genérica é compilada, o compilador executa a técnica de erasure nos parâmetros de tipo da classe e os substitui pelos seus limites superiores. Para a classe Stack (Figura 21.7), nenhum limite superior é especificado, portanto o limite superior padrão, Object, é utilizado. O escopo do parâmetro de tipo de uma classe genérica é a classe inteira. Entretanto, parâmetros de tipo não podem ser utilizados nas declarações de variáveis static de uma classe.

Testando a classe Stack genérica da Figura 21.7 Agora, vamos considerar o aplicativo (Figura 21.9) que utiliza a classe genérica Stack (Figura 21.7). As linhas 12–13 criam e inicializam variáveis do tipo Stack (pronuncia-se “Stack de Double”) e Stack (pronuncia-se “Stack de Integer”). Os tipos Double e Integer são conhecidos como argumentos de tipo Stack. Eles são utilizados pelo compilador para substituir os parâmetros de tipo de modo que o compilador possa executar a verificação de tipo e inserir operações de coerção conforme necessário. Discutiremos as operações de coerção em mais detalhes a seguir. As linhas 12–13 instanciam doubleStack com uma capacidade de 5 e integerStack com uma capacidade de 10 (o padrão). As linhas 16–17 e 20–21 chamam os métodos testPushDouble (linhas 25–36), testPopDouble (linhas 39–59), testPushInteger (linhas 62–73) e testPopInteger (linhas 76–96), respectivamente, para demonstrar as duas Stacks nesse exemplo.

680

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

Capítulo 21  Classes e métodos genéricos

// Figura 21.9: Stacktest.java // Programa de teste da classe genérica Stack. public class StackTest { public static void main( String[] args ) { double[] doubleElements = { 1.1, 2.2, 3.3, 4.4, 5.5 }; int[] integerElements = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // Cria uma Stack< Double > e uma Stack< Integer Stack< Double > doubleStack = new Stack< Double >( 5 ); Stack< Integer > integerStack = new Stack< Integer >(); // insere os elementos de doubleElements em doubleStack testPushDouble( doubleStack, doubleElements ); testPopDouble( doubleStack ); // remove de doubleStack // insere os elementos de integerElements em integerStack testPushInteger( integerStack, integerElements ); testPopInteger( integerStack ); // remove de integerStack } // fim de main // testa o método push com a pilha de doubles private static void testPushDouble( Stack< Double > stack, double[] values ) { System.out.println( "\nPushing elements onto doubleStack" ); // insere elementos na Stack for ( double value : values ) { System.out.printf( "%.1f ", value ); stack.push( value ); // insere em doubleStack } // for final } // fim do método testPushDouble // testa o método pop com a pilha de doubles private static void testPopDouble( Stack< Double > stack ) { // remove elementos da pilha try { System.out.println( "\nPopping elements from doubleStack" ); double popValue; // armazena o elemento removido da pilha // remove todos os elementos da Stack while ( true ) { popValue = stack.pop(); // remove de doubleStack System.out.printf( "%.1f ", popValue ); } // fim do while } // fim do try catch( EmptyStackException emptyStackException ) { System.err.println(); emptyStackException.printStackTrace(); } // fim da captura de EmptyStackException } // fim do método testPopDouble // testa o método push com a pilha de integers private static void testPushInteger( Stack< Integer > stack, int[] values ) { System.out.println( "\nPushing elements onto integerStack" ); // insere elementos na Stack for ( int value : values )



21.6  Classes genéricas

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

681

{ System.out.printf( "%d ", value ); stack.push( value ); // insere em integerStack } // for final } // fim do método testPushInteger // testa o método pop com a pilha de integers private static void testPopInteger( Stack< Integer > stack ) { // remove os elementos da pilha try { System.out.println( "\nPopping elements from integerStack" ); int popValue; // armazena o elemento removido da pilha // remove todos os elementos da Stack while ( true ) { popValue = stack.pop(); // remove de intStack System.out.printf( "%d ", popValue ); } // fim do while } // fim do try catch( EmptyStackException emptyStackException ) { System.err.println(); emptyStackException.printStackTrace(); } // fim da captura de EmptyStackException } // fim do método testPopInteger } // fim da classe StackTest

Pushing elements onto doubleStack 1.1 2.2 3.3 4.4 5.5 Popping elements from doubleStack 5.5 4.4 3.3 2.2 1.1 EmptyStackException: Stack is empty, cannot pop at Stack.pop(Stack.java:32) at StackTest.testPopDouble(StackTest.java:50) at StackTest.main(StackTest.java:17) Pushing elements onto integerStack 1 2 3 4 5 6 7 8 9 10 Popping elements from integerStack 10 9 8 7 6 5 4 3 2 1 EmptyStackException: Stack is empty, cannot pop at Stack.pop(Stack.java:32) at StackTest.testPopInteger(StackTest.java:87) at StackTest.main(StackTest.java:21)

Figura 21.9  |  Programa de teste da classe genérica Stack.

Métodos testPushDouble e testPopDouble O método testPushDouble (linhas 25–36) invoca o método push (linha 34) para inserir os valores double 1.1, 2.2, 3.3, 4.4 e 5.5 do array doubleElements em doubleStack. Observe que o autoboxing ocorre na linha 34 quando o programa tenta inserir um valor de tipo primitivo double em doubleStack, que armazena somente referências a objetos Double. O método testPopDouble (linhas 39–59) invoca o método Stack pop (linha 50) em um loop while infinito (linhas 48–52) para remover todos os valores da pilha. Observe na saída que os valores são de fato removidos na ordem “último a entrar, primeiro a sair” (a característica definidora das pilhas). Quando o loop tenta utilizar pop para remover um sexto valor, a doubleStack estará vazia, portanto pop lança uma EmptyStackException, o que faz o programa prosseguir para o bloco catch (linhas 54–58) a fim de tratar a exceção. O rastreamento da pilha indica a exceção que ocorreu e mostra que o método Stack pop gerou a exceção na linha 32 do arquivo Stack. java (Figura 21.7). O rastreamento também mostra que o método pop foi chamado pelo método StackTest testPopDouble na linha 50 de StackTest.java e que o método testPopDouble foi chamado a partir do método main na linha 17 de StackTest.java. Essas informações permitem determinar os métodos que estavam na pilha de chamadas de métodos no momento em que a exceção ocorreu. Como o programa captura a exceção, ela é considerada como tendo sido tratada e o programa pode continuar a executar.

682

Capítulo 21  Classes e métodos genéricos

O auto-unboxing ocorre na linha 50 quando o programa atribui o objeto Double removido da pilha a uma variável primitiva double. A partir da Seção 21.4, lembre-se de que o compilador insere coerções para assegurar que os tipos adequados sejam retornados a partir de métodos genéricos. Depois da erasure, o método pop Stack retorna o tipo Object, mas o código cliente em testPopDouble espera receber um double quando o método pop retorna. Assim, o compilador insere uma coerção Double, como em popValue = ( Double ) stack.pop();

O valor atribuído a popValue passará pelo unboxing do objeto Double retornado por pop.

Métodos testPushInteger e testPopInteger O método testPushInteger (linhas 62–73) invoca o método push Stack para inserir valores em integerStack até que ela esteja cheia. O método testPopInteger (linhas 76–96) invoca o método pop Stack para remover valores de integerStack. Mais uma vez, observe que os valores são removidos na ordem “último a entrar, primeiro a sair”. Durante o processo de erasure, o compilador reconhece que o código de cliente no método testPopInteger espera receber um int quando o método pop retorna. Dessa forma, o compilador insere uma coerção em Integer, como em popValue = ( Integer ) stack.pop();

O valor atribuído a popValue passará pelo unboxing do objeto Integer retornado por pop.

Criando métodos genéricos para testar a classe Stack Observe que o código nos métodos testPushDouble e testPushInteger é quase idêntico a inserir valores em uma Stack ou em uma Stack, respectivamente, e o código nos métodos testPopDouble e testPopInteger é quase idêntico a remover valores de uma Stack ou de uma Stack, respectivamente. Isso apresenta outra oportunidade de utilizar métodos genéricos. A Figura 21.10 declara o método genérico testPush (linhas 24–35) para realizar as mesmas tarefas, como testPushDouble e testPushInteger na Figura 21.9 — isto é, inserir (push) valores em uma Stack. De maneira semelhante, o método genérico testPop (linhas 38–58) executa as mesmas tarefas que testPopDouble e testPopInteger na Figura 21.9 — isto é, utiliza pop para remover valores de uma Stack. Observe que a saída da Figura 21.10 corresponde precisamente àquela da Figura 21.9. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

// Figura 21.10: StackTest2.java // Passando objetos Stack genéricos para métodos genéricos. public class StackTest2 { public static void main( String[] args ) { Double [] doubleElements = { 1.1, 2.2, 3.3, 4.4, 5.5 }; Integer [] integerElements = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // Cria uma Stack< Double > e uma Stack< Integer Stack< Double > doubleStack = new Stack< Double >( 5 ); Stack< Integer > integerStack = new Stack< Integer >(); // insere os elementos de doubleElements em doubleStack testPush( “doubleStack”, doubleStack, doubleElements ); testPop( “doubleStack”, doubleStack ); // remove de doubleStack // insere os elementos de integerElements em integerStack testPush( “integerStack”, integerStack, integerElements ); testPop( “integerStack”, integerStack ); // remove de integerStack } // fim de main // método genérico testPush insere elementos em uma Stack public static < T > void testPush( String name , Stack< T > stack, T[] elements ) { System.out.printf( "\nPushing elements onto %s\n", name ); // insere elementos na Stack for ( T element : elements ) { System.out.printf( "%s ", element ); stack.push( element ); // insere o elemento na pilha } // for final } // fim do método testPush

21.7 Tipos brutos 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

683

// método genérico testPop remove elementos de uma Stack public static < T > void testPop( String name, Stack< T > stack ) { // remove elementos da pilha try { System.out.printf( "\nPopping elements from %s\n", name ); T popValue; // armazena o elemento removido da pilha // remove todos os elementos da Stack while ( true ) { popValue = stack.pop(); System.out.printf( "%s ", popValue ); } // fim do while } // fim do try catch( EmptyStackException emptyStackException ) { System.out.println(); emptyStackException.printStackTrace(); } // fim da captura de EmptyStackException } // fim do método testPop } // fim da classe StackTest2

Pushing elements onto doubleStack 1.1 2.2 3.3 4.4 5.5 Popping elements from doubleStack 5.5 4.4 3.3 2.2 1.1 EmptyStackException: Stack is empty, cannot pop at Stack.pop(Stack.java:32) at StackTest2.testPop(StackTest2.java:50) at StackTest2.main(StackTest2.java:17) Pushing elements onto integerStack 1 2 3 4 5 6 7 8 9 10 Popping elements from integerStack 10 9 8 7 6 5 4 3 2 1 EmptyStackException: Stack is empty, cannot pop at Stack.pop(Stack.java:32) at StackTest2.testPop(StackTest2.java:50) at StackTest2.main(StackTest2.java:21

Figura 21.10 | Passando objetos Stack genéricos para métodos genéricos.

As linhas 11–12 criam os objetos Stack e Stack, respectivamente. As linhas 15–16 e 19–20 invocam os métodos genéricos testPush e testPop para testar os objetos Stack. Lembre-se de que parâmetros de tipo só podem representar tipos por referência. Portanto, para ser capaz de passar arrays doubleElements e integerElements para o método genérico testPush, os arrays declarados nas linhas 7–8 devem ser declarados com os tipos empacotadores Double e Integer. Quando esses arrays são inicializados com valores de tipos primitivos, o compilador autoempacota cada valor de tipo primitivo. O método genérico testPush (linhas 24–35) utiliza o parâmetro de tipo T (especificado na linha 24) para representar o tipo de dados armazenado na Stack. O método genérico recebe três argumentos — uma String que representa o nome do objeto < Stack para propósitos de saída, uma referência a um objeto de tipo Stack e um array de tipo T — o tipo de elementos que será colocado em Stack. Observe que o compilador impõe uma consistência entre o tipo da Stack e os elementos que serão colocados na Stack quando o método push é invocado, que é o valor real da chamada do método genérico. O método genérico testPop (linhas 38–58) recebe dois argumentos — uma String que representa o nome do objeto Stack para propósitos de saída e uma referência a um objeto do tipo Stack.

21.7 Tipos brutos Os programas de teste para a classe genérica Stack na Seção 21.6 instanciam Stacks com argumentos do tipo Double e Integer. Também há a possibilidade de instanciar uma classe genérica Stack sem especificar um argumento de tipo, como a seguir: Stack objectStack = new Stack( 5 ); // nenhum argumento de tipo especificado

684

Capítulo 21  Classes e métodos genéricos

Nesse caso, diz-se que o objectStack tem um tipo bruto, o que significa que o compilador utiliza implicitamente o tipo Object por toda a classe genérica para cada argumento de tipo. Assim, a instrução precedente cria uma Stack que pode armazenar objetos de qualquer tipo. Isso é importante para retrocompatibilidade com versões anteriores do Java. Por exemplo, todas as estruturas de dados do Java Collections Framework (ver Capítulo 20, Coleções genéricas) armazenavam referências a Objects, mas agora são implementadas como tipos genéricos. Uma variável Stack de tipo bruto pode ser atribuída a uma Stack que especifica um argumento de tipo, como um objeto Stack , como a seguir: Stack rawTypeStack2 = new Stack< Double >( 5 );

porque o tipo Double é uma subclasse de Object. Essa atribuição é permitida porque os elementos em uma Stack (isto é, objetos Double) são certamente objetos — a classe Double é uma subclasse indireta de Object. De maneira semelhante, uma variável Stack que especifica um argumento de tipo na sua declaração pode ser atribuída a um tipo bruto do objeto Stack, como em: Stack< Integer > integerStack = new Stack( 10 );

Embora essa atribuição seja permitida, é perigosa porque uma Stack do tipo bruto armazenaria outros tipos além de Integer. Nesse caso, o compilador emite uma mensagem de alerta que indica a atribuição insegura.

Utilizando tipos brutos com a classe genérica Stack O programa de teste da Figura 21.11 utiliza a noção de tipos brutos. A linha 11 instancia a classe genérica Stack com o tipo bruto, o que indica que rawTypeStack1 pode conter objetos de qualquer tipo. A linha 14 atribui uma Stack à variável rawTypeStack2, que é declarada como uma Stack do tipo bruto. A linha 17 atribui uma Stack do tipo bruto à variável Stack , o que é válido, mas faz com que o compilador emita uma mensagem de alerta (Figura 21.12) indicando uma atribuição potencialmente insegura — mais uma vez, isso ocorre porque uma Stack do tipo bruto armazenaria outros tipos além de Integer. Além disso, as chamadas aos métodos genéricos testPush e testPop nas linhas 19–22 resulta em mensagens de alerta do compilador (Figura 21.12). Isso ocorre porque rawTypeStack1 e rawTypeStack2 são declaradas como Stacks do tipo bruto, mas cada um dos métodos testPush e testPop espera um segundo argumento que é uma Stack com um argumento de tipo específico. Esses alertas indicam que o compilador não pode garantir o fato de que os tipos manipulados pelas pilhas são os tipos corretos, na medida em que não fornecemos uma variável declarada com um argumento de tipo. Os métodos testPush (linhas 28–39) e testPop (linhas 42–62) são o mesmo como na Figura 21.10. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

// Figura 21.11: RawTypeTest.java // Programa de teste de tipos brutos. public class RawTypeTest { public static void main( String[] args ) { Double[] doubleElements = { 1.1, 2.2, 3.3, 4.4, 5.5 }; Integer[] integerElements = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; // Pilha de tipos brutos atribuídos à classe Stack da variável de tipos brutos Stack rawTypeStack1 = new Stack( 5 ); // Stack atribuído à Stack da variável de tipos brutos Stack rawTypeStack2 = new Stack< Double >( 5 ); // Pilha de tipos crus atribuídos à variável Stack Stack< Integer > integerStack = new Stack( 10 ); testPush( "rawTypeStack1", rawTypeStack1, doubleElements ); testPop( "rawTypeStack1", rawTypeStack1 ); testPush( "rawTypeStack2", rawTypeStack2, doubleElements ); testPop( "rawTypeStack2", rawTypeStack2 ); testPush( "integerStack", integerStack, integerElements ); testPop( "integerStack", integerStack ); } // fim de main // método genérico insere elementos na pilha public static < T > void testPush( String name, Stack< T > stack, T[] elements )



21.7  Tipos brutos

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

{ System.out.printf( "\nPushing elements onto %s\n", name ); // insere elementos na Stack for ( T element : elements ) { System.out.printf( "%s ", element ); stack.push( element ); // insere o elemento na pilha } // for final } // fim do método testPush // método genérico testPop remove elementos da pilha public static < T > void testPop( String name, Stack< T > stack ) { // remove elementos da pilha try { System.out.printf( "\nPopping elements from %s\n", name ); T popValue; // armazena o elemento removido da pilha // remove elementos da Stack while ( true ) { popValue = stack.pop(); // remove da pilha System.out.printf( "%s ", popValue ); } // fim do while } // fim do try catch( EmptyStackException emptyStackException ) { System.out.println(); emptyStackException.printStackTrace(); } // fim da captura de EmptyStackException } // fim do método testPop } // fim da classe RawTypeTest

Pushing elements onto rawTypeStack1 1.1 2.2 3.3 4.4 5.5 Popping elements from rawTypeStack1 5.5 4.4 3.3 2.2 1.1 EmptyStackException: Stack is empty, cannot pop at Stack.pop(Stack.java:32) at RawTypeTest.testPop(RawTypeTest.java:53) at RawTypeTest.main(RawTypeTest.java:20) Pushing elements onto rawTypeStack2 1.1 2.2 3.3 4.4 5.5 Popping elements from rawTypeStack2 5.5 4.4 3.3 2.2 1.1 EmptyStackException: Stack is empty, cannot pop at Stack.pop(Stack.java:32) at RawTypeTest.testPop(RawTypeTest.java:53) at RawTypeTest.main(RawTypeTest.java:22) Pushing elements onto integerStack 1 2 3 4 5 6 7 8 9 10 Popping elements from integerStack 10 9 8 7 6 5 4 3 2 1 EmptyStackException: Stack is empty, cannot pop at Stack.pop(Stack.java:32) at RawTypeTest.testPop(RawTypeTest.java:53) at RawTypeTest.main(RawTypeTest.java:24)

Figura 21.11  |  Programa de teste de tipos brutos.

685

686

Capítulo 21  Classes e métodos genéricos

A Figura 21.12 mostra as mensagens de alerta geradas pelo compilador quando o arquivo RawTypeTest.java (Figura  21.11) é compilado com a opção -Xlint:unchecked, que fornece informações adicionais sobre operações potencialmente perigosas no código que utiliza genéricos. O primeiro alerta é gerado para a linha 17, que atribuiu um tipo bruto de Stack a uma variável Stack — o compilador não pode assegurar que todos os objetos na Stack serão objetos Integer. O próximo alerta ocorre na linha 19. O compilador determina o argumento de tipo do método testPush a partir do array Double passado como o terceiro argumento, porque o segundo argumento do método é uma variável Stack do tipo bruto. Nesse caso, Double é o argumento de tipo, portanto o compilador espera uma Stack como o segundo argumento. O alerta ocorre porque o compilador não pode assegurar que o tipo bruto Stack só contém Doubles. O alerta na linha 21 ocorre pela mesma razão, embora a Stack real que rawTypeStack2 referencia seja uma Stack. O compilador não pode garantir que a variável sempre irá se referir ao mesmo objeto Stack, assim ele deve utilizar o tipo declarado da variável para realizar todas as verificações de tipos. As linhas 20 e 22 geram alertas porque o método testPop espera como um argumento uma Stack à qual foi especificado um argumento de tipo. Entretanto, em cada chamada a testPop, passamos uma variável Stack de tipo bruto. Portanto, o compilador indica um alerta uma vez que não pode verificar os tipos utilizados no corpo do método.

RawTypeTest.java:17: warning: [unchecked] unchecked conversion found : Stack required: Stack Stack< Integer > integerStack = new Stack( 10 ); ^ RawTypeTest.java:19: warning: [unchecked] unchecked conversion found : Stack required: Stack testPush( "rawTypeStack1", rawTypeStack1, doubleElements ); ^ RawTypeTest.java:19: warning: [unchecked] unchecked method invocation: String,Stack,T[]) in RawTypeTest is applied to (java.lang.String,Stack,java.lang.Double[]) testPush( "rawTypeStack1", rawTypeStack1, doubleElements ); ^ RawTypeTest.java:20: warning: [unchecked] unchecked conversion found : Stack required: Stack testPop( "rawTypeStack1", rawTypeStack1 ); ^ RawTypeTest.java:20: warning: [unchecked] unchecked method invocation: String,Stack) in RawTypeTest is applied to (java.lang.String,Stack) testPop( "rawTypeStack1", rawTypeStack1 ); ^ RawTypeTest.java:21: warning: [unchecked] unchecked conversion found : Stack required: Stack testPush( "rawTypeStack2", rawTypeStack2, doubleElements ); ^ RawTypeTest.java:21: warning: [unchecked] unchecked method invocation: String,Stack,T[]) in RawTypeTest is applied to (java.lang.String,Stack,java.lang.Double[]) testPush( "rawTypeStack2", rawTypeStack2, doubleElements ); ^ RawTypeTest.java:22: warning: [unchecked] unchecked conversion found : Stack required: Stack testPop( "rawTypeStack2", rawTypeStack2 ); ^ RawTypeTest.java:22: warning: [unchecked] unchecked method invocation: String,Stack) in RawTypeTest is applied to (java.lang.String,Stack) testPop( "rawTypeStack2", rawTypeStack2 ); ^ 9 warnings

Figura 21.12  |  Mensagens de alerta do compilador.

testPush(java.lang.

testPop(java.lang.

testPush(java.lang.

testPop(java.lang.

21.8 Curingas em métodos que aceitam parâmetros de tipo

687

21.8 Curingas em métodos que aceitam parâmetros de tipo Nesta seção, apresentamos um conceito poderoso sobre genéricos conhecido como curingas. Para esse propósito, também apresentaremos uma nova estrutura de dados do pacote java.util. No Capítulo 20, discutimos o Java Collections Framework, que fornece várias estruturas e algoritmos de dados genéricos que manipulam os elementos dessas estruturas de dados. Talvez a mais simples dessas estruturas de dados seja a classe ArrayList — uma estrutura de dados dinamicamente redimensionável como de um array. Como parte dessa discussão, você aprenderá a criar uma ArrayList, adicionar elementos a ela e percorrer esses elementos utilizando uma instrução for aprimorada. Antes de apresentarmos os curingas, vamos considerar um exemplo que nos ajuda a motivar a utilização deles. Suponha que você quer implementar um método genérico sum que soma os números em uma coleção, como uma ArrayList. Começaria inserindo os números na coleção. Como se sabe, classes genéricas só podem ser utilizadas com tipos de classe ou interface, portanto os números passariam por um autoboxing como objetos das classes empacotadoras de tipo. Por exemplo, qualquer valor int seria autoempacotado como um objeto Integer e qualquer valor double seria convertido por autoboxing em um objeto Double. Queremos ser capazes de somar todos os números na ArrayList independentemente dos seus tipos. Por essa razão, declararemos a ArrayList com o argumento de tipo Number, que é a superclasse de Integer e Double. Além disso, o método sum receberá um parâmetro do tipo ArrayList e somará seus elementos. A Figura 21.13 demonstra a somatória dos elementos de uma ArrayList de Numbers. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

// Figura 21.13: TotalNumbers.java // Somando os números em uma ArrayList. import java.util.ArrayList; public class TotalNumbers { public static void main( String[] args ) { // cria, inicializa e gera saída de ArrayList de números contendo // tanto Integers como Doubles e então exibe o total dos elementos Number[] numbers = { 1, 2.4, 3, 4.1 }; // Integers and Doubles ArrayList< Number > numberList = new ArrayList< Number >(); for ( Number element : numbers ) numberList.add( element ); // insere cada número na numberList System.out.printf( "numberList contains: %s\n", numberList ); System.out.printf( "Total of the elements in numberList: %.1f\n", sum( numberList ) ); } // fim de main // calcula o total de elementos em ArrayList public static double sum( ArrayList< Number > list ) { double total = 0; // inicializa o total // calcula a soma for ( Number element : list ) total += element.doubleValue(); return total; } // fim do método sum } // fim da classe TotalNumbers

numberList contains: [1, 2.4, 3, 4.1] Total of the elements in numberList: 10.5

Figura 21.13 | Somando os números em uma ArrayList.

A linha 11 declara e inicializa um array de Numbers. Como os inicializadores são valores primitivos, o Java autoempacota cada valor de tipo primitivo como um objeto do seu tipo empacotador correspondente. Os valores int 1 e 3 passam por um autoboxing como objetos Integer, e os valores double 2.4 e 4.1 passam por um autoboxing como objetos Double. A linha 12 declara e cria um objeto ArrayList que armazena Numbers e lhe atribui à variável numberList. Observe que não temos de especificar o tamanho do ArrayList porque ele aumentará automaticamente à medida que inserimos objetos.

688

Capítulo 21  Classes e métodos genéricos

As linhas 14–15 percorrem o array numbers e colocam cada elemento em numberList. A linha 17 gera saída do conteúdo do Array­ List como uma String. Essa instrução invoca implicitamente o método toString da ArrayList , que retorna uma String na forma "[elements]" , em que elementos é uma lista separada por vírgulas das representações da String dos elementos. As linhas 18–19 exibem

a soma dos elementos que é retornada pela chamada ao método sum. O método sum (linhas 23–32) recebe um ArrayList de Numbers e calcula o total do Numbers na coleção. O método utiliza valores double para executar os cálculos e retorna o resultado como um double. As linhas 28–29 utilizam a instrução for aprimorada, projetada para funcionar tanto em coleções como arrays do Collections Framework, para somar os elementos da ArrayList. A instrução for atribui cada Number do ArrayList à variável element e, então, utiliza o método Number doubleValue para obter o valor do tipo primitivo subjacente do Number como um valor double. O resultado é adicionado a total. Quando o loop termina, o método retorna o total.

Implementando o método sum com um argumento de tipo de curinga no seu parâmetro Lembre-se de que o propósito do método sum na Figura 21.13 era somar qualquer tipo de Numbers armazenado em uma ArrayList. Criamos uma ArrayList de Numbers que continham tanto objetos Integer como Double. A saída da Figura 21.13 demonstra que o método sum funcionou adequadamente. Dado o fato de que esse método sum pode somar os elementos de uma ArrayList de Numbers, talvez você esperasse que o método também funcionasse para ArrayLists que contêm elementos de somente um tipo numérico, como ArrayList. Assim, modificamos a classe TotalNumbers para criar uma ArrayList de Integers e passá-la para o método sum. Ao compilar o programa, o compilador emite a mensagem de erro a seguir: sum(java.util.ArrayList) in TotalNumbersErrors cannot be applied to (java.util.ArrayList)

Embora Number seja a superclasse de Integer, o compilador não considera o tipo parametrizado ArrayList uma superclasse de ArrayList. Se fosse, cada operação que podemos realizar em ArrayList também funcionaria em uma ArrayList. Considere o fato de que você pode adicionar um objeto Double a uma ArrayList porque um Double é um Number, mas você não pode adicionar um objeto Double a um ArrayList porque um Double não é um Integer. Portanto, o relacionamento de subtipos não se aplica. Como é possível criar uma versão mais flexível do método sum que possa somar os elementos de qualquer ArrayList que contém elementos de qualquer subclasse de Number? É aí que os argumentos de tipo curingas são importantes. Os curingas permitem especificar parâmetros de método, valores de retorno, variáveis ou campos e assim por diante, que atuam como supertipos ou subtipos de tipos parametrizados. Na Figura 21.14, o parâmetro do método sum é declarado na linha 50 com o tipo: ArrayList< ? extends Number >

Um argumento do tipo curinga é denotado pelo ponto de interrogação (?), que representa por si mesmo um “tipo desconhecido”. Nesse caso, o curinga estende a classe Number, o que significa que o curinga tem um limite superior de Number. Portanto, o argumento de tipo desconhecido deve ser Number ou uma subclasse de Number. Com o tipo de parâmetro mostrado aqui, o método sum pode receber um argumento ArrayList que contém qualquer tipo de Number, como ArrayList (linha 20), ArrayList (linha 33) ou ArrayList (linha 46). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

// Figura 21.14: WildcardTest.java // Programa de teste de curinga. import java.util.ArrayList; public class WildcardTest { public static void main( String[] args ) { // cria, inicializa e gera saída de ArrayList de Integers, então // exibe o total dos elementos Integer[] integers = { 1, 2, 3, 4, 5 }; ArrayList< Integer > integerList = new ArrayList< Integer >(); // insere elementos na integerList for ( Integer element : integers ) integerList.add( element ); System.out.printf( "integerList contains: %s\n", integerList ); System.out.printf( "Total of the elements in integerList: %.0f\n\n", sum( integerList ) ); // cria, inicializa e gera saída do ArrayList de Doubles, então // exibe o total dos elementos Double[] doubles = { 1.1, 3.3, 5.5 };



21.8  Curingas em métodos que aceitam parâmetros de tipo

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

689

ArrayList< Double > doubleList = new ArrayList< Double >(); // insere elementos na doubleList for ( Double element : doubles ) doubleList.add( element ); System.out.printf( "doubleList contains: %s\n", doubleList ); System.out.printf( "Total of the elements in doubleList: %.1f\n\n", sum( doubleList ) ); // cria, inicializa e gera saída de ArrayList de números contendo // tanto Integers como Doubles e então exibe o total dos elementos Number[] numbers = { 1, 2.4, 3, 4.1 }; // Integers and Doubles ArrayList< Number > numberList = new ArrayList< Number >(); // insere elementos na numberList for ( Number element : numbers ) numberList.add( element ); System.out.printf( "numberList contains: %s\n", numberList ); System.out.printf( "Total of the elements in numberList: %.1f\n", sum( numberList ) ); } // fim de main // soma os elementos; utilizando um curinga no parâmetro ArrayList public static double sum( ArrayList< ? extends Number > list ) { double total = 0; // inicializa o total // calcula a soma for ( Number element : list ) total += element.doubleValue(); return total; } // fim do método sum } // fim da classe WildcardTest

integerList contains: [1, 2, 3, 4, 5] Total of the elements in integerList: 15 doubleList contains: [1.1, 3.3, 5.5] Total of the elements in doubleList: 9.9 numberList contains: [1, 2.4, 3, 4.1] Total of the elements in numberList: 10.5

Figura 21.14  |  Programa de teste de curinga genérico.

As linhas 11–20 criam e inicializam um ArrayList, geram a saída dos seus elementos e somam seus elementos chamando o método sum (linha 20). As linhas 24–33 realizam as mesmas operações para uma ArrayList. As linhas 37–46 realizam as mesmas operações para uma ArrayList que contém Integers e Doubles. No método sum (linhas 50–59), embora os tipos de elemento do argumento de ArrayList não sejam conhecidos diretamente pelo método, eles são conhecidos como pelo menos do tipo Number , porque o curinga foi especificado com o limite superior Number. Por essa razão, a linha 56 é permitida, porque todos os objetos Number têm um método doubleValue. Embora curingas forneçam flexibilidade ao passar tipos parametrizados para um método, eles também têm algumas desvantagens. Como o curinga (?) no cabeçalho do método (linha 50) não especifica um nome de parâmetro de tipo, você não pode utilizá-lo como um nome de tipo por todo o corpo do método (isto é, não pode substituir Number por ? na linha 55). Você pode, porém, declarar o método sum desta maneira: public static double sum( ArrayList< T > list )

o que permite ao método receber uma ArrayList que contém elementos de qualquer subclasse Number. Você pode então utilizar o parâmetro de tipo T por todo o corpo do método. Se o curinga for especificado sem um limite superior, somente os métodos do tipo Object podem ser invocados nos valores do tipo curinga. Além disso, métodos que utilizam curingas nos seus argumentos de tipo do parâmetro não podem ser utilizados para adicionar elementos a uma coleção referenciada pelo parâmetro.

690

Capítulo 21

Classes e métodos genéricos

Erro comum de programação 21.4 Utilizar um curinga na seção de parâmetro de tipo de um método ou utilizar um curinga como um tipo explícito de uma variável no corpo do método é um erro de sintaxe.

21.9 Genéricos e herança: notas Os genéricos podem ser utilizados com a herança de várias maneiras: • Uma classe genérica pode ser derivada de uma classe não genérica. Por exemplo, a classe Object é uma superclasse direta ou indireta de cada classe genérica. • Uma classe genérica pode ser derivada de outra classe genérica. Por exemplo, a classe genérica Stack (no pacote java.util) é uma subclasse da classe genérica Vector (no pacote java.util). Discutimos essas classes no Capítulo 20. • Uma classe não genérica pode ser derivada de uma classe genérica. Por exemplo, a classe não genérica Properties (no pacote java. util) é uma subclasse da classe genérica Hashtable (no pacote java.util). Também discutimos essas classes no Capítulo 20. • Por fim, um método genérico em uma subclasse pode sobrescrever um método genérico em uma superclasse se os dois métodos tiverem a mesma assinatura.

21.10 Conclusão Este capítulo introduziu genéricos. Você aprendeu a declarar métodos e classes genéricas. Discutimos como a compatibilidade com versões anteriores é alcançada via tipos brutos. Também aprendeu a utilizar curingas em um método genérico ou classe genérica. No Capítulo 22, você aprenderá a implementar suas próprias estruturas de dados dinâmicas personalizadas que podem aumentar ou reduzir de tamanho em tempo de execução. Em particular, implementará essas estruturas de dados utilizando as capacidades dos genéricos que aprendeu neste capítulo. Para obter informações adicionais sobre genéricos, visite nosso Java Resource Center em www.deitel.com/Java/ e clique no tópico Java Generics sob o título Resource Center Contents.

Resumo Seção 21.1 Introdução • Os métodos genéricos permitem especificar, com uma única declaração de método, um conjunto de métodos relacionados. • Classes e interfaces genéricas permitem especificar conjuntos de tipos relacionados.

Seção 21.2 Motivação para métodos genéricos • Métodos sobrecarregados são frequentemente utilizados para realizar operações semelhantes em tipos diferentes de dados. • Quando o compilador encontra uma chamada de método, ele tenta localizar uma declaração de método com o mesmo nome de método e parâmetros que sejam compatíveis com os tipos de argumentos na chamada de método.

Seção 21.3 Métodos genéricos: implementação e tradução em tempo de compilação • Se as operações realizadas por vários métodos sobrecarregados forem idênticas para cada tipo de argumento, elas podem ser codificadas mais compacta e convenientemente com um método genérico. Uma única declaração de método genérico pode ser chamada com argumentos de diferentes tipos de dados. Com base nos tipos dos argumentos passados para um método genérico, o compilador trata cada chamada de método apropriadamente. • Todas as declarações de métodos genéricos contêm uma seção de parâmetro de tipo delimitado por colchetes angulares (< e >) que precede o tipo de retorno do método. • Uma seção do parâmetro de tipo contém um ou mais parâmetros de tipo separados por vírgulas. • Um parâmetro de tipo é um identificador que especifica um nome de tipo genérico. Os parâmetros de tipo podem ser utilizados como o tipo de retorno, tipos de parâmetro e tipos de variáveis locais em uma declaração de método genérico e atuam como marcadores de lugar para os tipos dos argumentos passados para o método genérico, conhecidos como argumentos de tipo reais. Os parâmetros de tipo podem representar somente tipos por referência. • Nomes de parâmetro de tipo utilizados por toda uma declaração de método devem corresponder àqueles declarados na seção do parâmetro de tipo. O nome de um parâmetro de tipo pode ser declarado somente uma vez na seção de parâmetro de tipo, mas pode aparecer mais de uma vez na lista de parâmetros do método.



Resumo

691

• Quando o compilador encontra uma chamada de método, ele determina os tipos de argumento e tenta localizar um método com o mesmo nome e parâmetros que correspondem aos tipos de argumento. Se não houver um desses métodos, o compilador procura métodos com o mesmo nome, parâmetros compatíveis e métodos genéricos correspondentes. • Objetos de uma classe que implementa a interface genérica Comparable podem ser comparados com o método compareTo, que retorna 0 se os objetos forem iguais, um número inteiro negativo se o primeiro objeto for menor que o segundo ou um número inteiro positivo se o primeiro objeto for maior do que o segundo. • Todas as classes empacotadoras de tipos para tipos primitivos implementam a interface Comparable. • Objetos Comparable podem ser utilizados com os métodos de pesquisa e classificação da classe Collections. • Quando um método genérico é compilado, o compilador remove a seção do parâmetro de tipo e substitui os parâmetros de tipo por tipos reais. Esse processo é conhecido como erasure. Por padrão cada parâmetro de tipo é substituído pelo seu limite superior, que é Object a menos que especificado de outro modo.

Seção 21.4  Questões adicionais da tradução em tempo de compilação: métodos que utilizam um parâmetro de tipo como o tipo de retorno • Quando a erasure é executada em um método que retorna uma variável de tipo, coerções explícitas são inseridas na frente de cada chamada de método para assegurar que o valor retornado tem o tipo esperado pelo chamador.

Seção 21.5  Sobrecarregando métodos genéricos • Um método genérico pode ser sobrecarregado com outros métodos genéricos ou com métodos não genéricos.

Seção 21.6  Classes genéricas • As classes genéricas fornecem um meio de descrever uma classe de uma maneira independente de tipo. Podemos então instanciar objetos específicos de tipo da classe genérica. • Uma declaração de classe genérica se parece com a declaração de uma classe não genérica, exceto que o nome de classe é seguido por uma seção de parâmetro de tipo. A seção do parâmetro de tipo de uma classe genérica pode ter um ou mais parâmetros de tipo separados por vírgulas. • Quando uma classe genérica é compilada, o compilador realiza a erasure nos parâmetros de tipo da classe e os substitui pelos seus limites superiores. • Os parâmetros de tipo não podem ser utilizados nas declarações de uma classe static. • Ao instanciar um objeto de uma classe genérica, os tipos especificados dentro de colchetes angulares depois do nome de classe são conhecidos como argumentos de tipo. O compilador utiliza-os para substituir os parâmetros de tipo a fim de que ele possa executar a verificação dos tipos e inserir operações de coerção conforme necessário.

Seção 21.7  Tipos brutos • É possível instanciar uma classe genérica sem especificar um argumento de tipo. Nesse caso, diz-se que o novo objeto da classe tem um tipo bruto — o compilador utiliza implicitamente o tipo Object (ou o limite superior do parâmetro de tipo) por toda a classe genérica para cada argumento de tipo.

Seção 21.8  Curingas em métodos que aceitam parâmetros de tipo • A classe Number é a superclasse tanto de Integer como Double. • O método Number doubleValue obtém o valor do tipo primitivo subjacente de Number como um valor de double. • Os argumentos do tipo curinga permitem especificar parâmetros de método, valores de retorno, variáveis e assim por diante, que atuam como supertipos dos tipos parametrizados. Um argumento do tipo curinga é denotado pelo ponto de interrogação (?), que representa um “tipo desconhecido”. Um curinga também pode ter um limite superior. • Como um curinga (?) não é um nome do parâmetro de tipo, você não pode utilizá-lo como um nome de tipo por todo o corpo de um método. • Se um curinga for especificado sem um limite superior, somente os métodos do tipo Object podem ser invocados nos valores do tipo de curinga. • Métodos que utilizam curingas como argumentos de tipo não podem ser utilizados para adicionar elementos a uma coleção referenciada pelo parâmetro.

Seção 21.9  Genéricos e herança: Notas • Uma classe genérica pode ser derivada de uma classe não genérica. Por exemplo, Object- é uma superclasse direta ou indireta de cada classe genérica. • Uma classe genérica pode ser derivada de outra classe genérica. • Uma classe não genérica pode ser derivada de uma classe genérica. • Um método genérico em uma subclasse pode sobrescrever um método genérico em uma superclasse se os dois métodos tiverem as mesmas assinaturas.

692

Capítulo 21  Classes e métodos genéricos

Terminologia ? (argumento de tipo curinga), 688 argumento de tipo, 679 argumento de tipo curinga, 688 argumentos reais de tipo, 674 classe parametrizada, 678 classes genéricas, 671 colchetes angulares (< e >), 674 Comparable, interface, 675

compareTo, método de Comparable, 675

curinga em um parâmetro de tipo genérico, 687 doubleValue, método de Number, 688 erasure, 675 genéricos, 671 interface genérica, 675 limite superior de um parâmetro de tipo, 676 método genérico, 671

parâmetro formal de tipo, 674 seção de parâmetro de tipo, 674 segurança de tipo em tempo de compilação, 671 sinais de maior e menor (< e >), 674 tipo, parâmetro, 674 tipo, variável, 674 tipo bruto, 684 tipo parametrizado, 678

Exercícios de autorrevisão 21.1 Determine se cada uma das seguintes afirmações é verdadeira ou falsa. Se falsa, explique por quê. a) Um método genérico não pode ter o mesmo nome de método de um método não genérico. b) Todas as declarações de métodos genéricos têm uma seção de parâmetro de tipo que precede imediatamente o nome de método. c) Um método genérico pode ser sobrecarregado por outro método genérico com o mesmo nome do método, mas diferentes parâmetros de método. d) Um parâmetro de tipo pode ser declarado somente uma vez na seção de parâmetro de tipo, mas pode aparecer mais de uma vez na lista de parâmetros do método. e) Os nomes dos parâmetros de tipo entre diferentes métodos genéricos devem ser únicos. f) O escopo de um parâmetro de tipo da classe genérico é a classe inteira, exceto seus membros static.

21.2 Preencha as lacunas em cada uma das sentenças: a) ________ e ________ permitem especificar, com uma única declaração de método, um conjunto de métodos relacionados ou, com uma única declaração de classe, um conjunto de tipos relacionados, respectivamente. b) Uma seção de parâmetro de tipo é delimitada por ________. c) O ________ de um método genérico pode ser utilizado para especificar os tipos de argumento do método, especificar o tipo de retorno do método e declarar variáveis dentro do método. d) A instrução “Stack objectStack = new Stack();” indica que objectStack armazena ________. e) Na declaração de uma classe genérica, o nome da classe é seguido por um(a) ________. f) A sintaxe ________ especifica que o limite superior de um curinga é o tipo T.

Respostas dos exercícios de autorrevisão 21.1

21.2

a) Falsa. Métodos genéricos e não genéricos podem ter o mesmo nome de método. Um método genérico pode sobrecarregar outro método genérico com o mesmo nome do método, mas diferentes parâmetros de método. Um método genérico também pode ser sobrecarregado fornecendo métodos não genéricos com o mesmo nome de método e número de argumentos. b) Falsa. Todas as declarações de métodos genéricos têm uma seção de parâmetro de tipo que precede imediatamente o tipo de retorno do método. c) Verdadeira. d) Verdadeira. e) Falsa. Os nomes de parâmetro de tipo entre diferentes métodos genéricos não precisam ser únicos. f) Verdadeira. a) Métodos genéricos, classes genéricas. b) colchetes angulares (< e >). c) parâmetros de tipo. d) um tipo bruto. e) seção de parâmetro de tipo. f) ? extends T.

Exercícios 21.3 Explique o uso da seguinte notação em um programa Java: public class Array< T > { }

21.4 Escreva um método genérico selectionSort com base no programa de classificação das Figuras 19.6–19.7. Escreva um programa de teste que insere, classifica e gera saída de um array Integer e de um array Float. [Dica: Utilize na seção do parâmetro de tipo do método selectionSort, de modo que possa utilizar o método compareTo para comparar os objetos do tipo que T representa.]

21.5 Sobrecarregue o método genérico printArray da Figura 21.3 de modo que ele aceite dois argumentos adicionais de inteiros, lowSubscript e

highSubscript. Uma chamada a esse método imprime somente a parte especificada do array. Valide lowSubscript e highSubscript. Se um

estiver fora do intervalo, o método printArray sobrecarregado deverá lançar uma InvalidSubscriptException; caso contrário, printAr­ ray deve retornar o número de elementos impresso. Modifique então main para praticar as duas versões de printArray nos arrays integerAr­ ray, doubleArray e characterArray. Teste todas as capacidades das duas versões de printArray.

21.6 Sobrecarregue o método genérico printArray da Figura 21.3 com uma versão não genérica que imprime especificamente um array de Strings em formato tabular elegante, como mostrado na saída do exemplo a seguir:



Exercícios

693

Array stringArray contains: one

two

three

four

five

six

seven

eight

21.7 Escreva uma versão genérica simples do método isEqualTo que compara seus dois argumentos com o método equals e retorna true se forem

iguais e false caso contrário. Utilize esse método genérico em um programa que chama isEqualTo com uma variedade de tipos predefinidos, como Object ou Integer. Qual resultado você obtém ao tentar executar esse programa?

21.8 Escreva uma classe genérica Pair que tem dois parâmetros de tipo — F e S — cada um representando o tipo do primeiro e segundo elemento do par, respectivamente. Adicione os métodos get e set ao primeiro e ao segundo elemento do par. [Dica: O cabeçalho de classe deve ser public class Pair.]

21.9 Como métodos genéricos podem ser sobrecarregados? 21.10 O compilador realiza um processo de correspondência para determinar qual método chamar quando um método é invocado. Sob quais circunstâncias uma tentativa de fazer uma correspondência resulta em um erro em tempo de compilação?

21.11 Explique por que um programa Java utilizaria a instrução ArrayList< Employee > workerList = new ArrayList< Employee >();

22

Much that I bound, I could not free; Much that I freed returned to me. [Muito do que eu prendi, não pude libertar; Muito que eu libertei voltou para mim.] — Lee Wilson Dodd

‘Will you walk a little faster?’ said a whiting to a snail, ‘There’s a porpoise close behind us, and he’s treading on my tail.’ [‘Não dá pra ir mais rápido?’ disse a enchova para o caracol ‘Tem um delfim atrás de mim, e ele está me empurrando.’] — Lewis Carroll

Há sempre espaço no ponto mais alto. — Daniel Webster

Prossiga — continue andando. — Thomas Morton

Virarei uma nova folha. — Miguel de Cervantes

Estruturas de dados genéricas personalizadas Objetivos Neste capítulo, você aprenderá: 

A formar estruturas de dados encadeadas utilizando referências, classes autorreferenciais, recursão e genéricos.



A criar e manipular estruturas de dados dinâmicas, como listas encadeadas, filas, pilhas e árvores binárias.



Vários aplicativos importantes de estruturas de dados encadeadas.



Como criar estruturas de dados reutilizáveis com classes, herança e composição.

Sumário

22.1 Introdução

22.1 Introdução

22.5 Pilhas

22.2 Classes autorreferenciais

22.6 Filas

22.3 Alocação dinâmica de memória

22.7 Árvores

22.4 Listas vinculadas

22.8 Conclusão

695

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios | Seção especial: construindo seu próprio compilador

22.1 Introdução Este capítulo demonstra como construir estruturas de dados dinâmicas que crescem e encolhem em tempo de execução. As listas encadeadas são coleções de itens de dados “vinculados em uma cadeia”; as inserções e exclusões podem ser feitas em qualquer lugar de uma lista encadeada. As pilhas são importantes em compiladores e sistemas operacionais; as inserções e as exclusões são feitas somente no fim de uma pilha — sua parte superior. As filas representam filas de espera; as inserções são feitas na parte de atrás (também referida como cauda) de uma fila e as exclusões são feitas na parte da frente da fila (também referida como cabeça). As árvores binárias facilitam a pesquisa e classificação de dados em alta velocidade, a eliminação eficiente de itens de dados duplicados, a representação de diretórios de sistema de arquivos, a compilação de expressões em linguagem de máquina e muitas outras aplicações interessantes. Discutimos cada um desses principais tipos de estrutura de dados e implementamos programas que os criam e manipulam. Utilizamos classes, herança e composição para criá-las e empacotá-las a fim de implementar as capacidades de reutilização e manutenção. Em geral, você utilizaria uma das classes de coleção predefinidas que discutimos no Capítulo 20. Contudo, as técnicas que apresentamos aqui podem ser utilizadas sempre que você precisar construir suas próprias coleções personalizadas. Por uma questão de simplicidade, os exemplos deste capítulo manipulam valores primitivos. Contudo, as implementações de estrutura de dados neste capítulo podem armazenar objetos da maioria dos tipos. (O exemplo de árvore binária requer objetos que implementem a interface Comparable.) Se sentir-se confiante, pode tentar o projeto principal descrito na seção especial intitulada Building Your Own Compiler (Construindo seu próprio compilador), que postamos on-line em www.deitel.com/books/jhtp8/. Você vem utilizando um compilador Java para traduzir seus programas Java em bytecodes a fim de poder executar esses programas em seu computador. Nesse projeto, realmente construirá seu próprio compilador. Essa seção apresentará instruções escritas em uma linguagem de alto nível simples, mas poderosa e semelhante às primeiras versões da popular linguagem Basic e as converterá em instruções SML (Simpletron Machine Language) — SML é a linguagem que você aprendeu na seção especial do Capítulo 7, Construindo seu próprio computador. Seu programa Simpletron Simulator então executará o programa SML produzido por seu compilador! A implementação desse projeto utilizando uma abordagem orientada a objetos lhe fornecerá uma excelente oportunidade para praticar grande parte do que aprendeu neste livro. A seção especial o orienta cuidadosamente ao longo das especificações da linguagem de alto nível e descreve os algoritmos que você precisará para converter cada instrução de linguagem de alto nível em instruções de linguagem de máquina. Se gosta de desafios, pode tentar os muitos aprimoramentos no compilador e no Simpletron Simulator sugeridos nos exercícios.

22.2 Classes autorreferenciais Uma classe autorreferencial contém uma variável de instância que referencia outro objeto do mesmo tipo de classe. Por exemplo, a declaração de classe genérica class Node< T > { private T data; private Node< T > nextNode; // referência ao próximo nó vinculado public Node( T data ) { /* corpo do construtor */ } public void setData( T data ) { /* corpo do método */ } public T getData() { /* corpo do método */ } public void setNext( Node< T > next ) { /* corpo do método */ } public Node< T > getNext() { /* corpo do método */ } } // fim da classe Node< T >

696

Capítulo 22

Estruturas de dados genéricas personalizadas

declara a classe Node, que tem duas variáveis de instância private — a variável data (do tipo genérico T) e Node nextNode. A variável nextNode referencia um objeto Node, um objeto da mesma classe que está sendo declarada aqui, daí o termo “classe autorreferencial”. O campo nextNode é um link — ele “vincula” um objeto do tipo Node a outro objeto do mesmo tipo. O tipo Node também tem cinco métodos: um construtor que recebe um valor para inicializar data, um método setData para configurar o valor de data, um método getData para retornar o valor de data, um método setNext para configurar o valor de nextNode e um método get­ Next para retornar uma referência ao próximo nó. Os programas podem vincular objetos autorreferenciais para formar estruturas de dados tão úteis quanto listas, filas, pilhas e árvores. A Figura 22.1 ilustra dois objetos autorreferenciais vinculados entre si para formar uma lista. Uma barra invertida — representando uma referência null — é colocada no membro de link do segundo objeto autorreferencial para indicar que o link não referencia outro objeto. Note que a barra invertida é ilustrativa; ela não corresponde ao caractere de barra invertida no Java. Utilizamos a referência null para indicar o fim de uma estrutura de dados. 15

10

Figura 22.1 | Objetos de classe autorreferencial vinculados entre si.

22.3 Alocação dinâmica de memória Criar e manter estruturas de dados dinâmicas requer alocação dinâmica de memória — permissão para que um programa obtenha mais espaço de memória em tempo de execução para armazenar novos nós e liberar espaço não mais necessário. Lembre-se de que os programas Java não explicitamente liberam memória alocada dinamicamente. Em vez disso, o Java realiza coleta de lixo automática de objetos que não são mais referenciados em um programa. O limite para alocação dinâmica de memória pode ser tão grande quanto a quantidade de memória física disponível no computador ou a quantidade de espaço em disco disponível em um sistema de memória virtual. Frequentemente, os limites são muito menores, porque a memória disponível do computador deve ser compartilhada entre muitos aplicativos. A criação da declaração e expressão de instância de classe // 10 são os dados de nodeToAdd Node< Integer > nodeToAdd = new Node< Integer >( 10 );

aloca a memória para armazenar um objeto Node e retornar uma referência ao objeto, que é atribuído a nodeToAdd. Se a memória insuficiente estiver disponível, a expressão lança um OutOfMemoryError. As seções a seguir discutem que listas, pilhas, filas e árvores utilizam alocação dinâmica de memória e classes autorreferenciais para criar estruturas de dados dinâmicas.

22.4 Listas vinculadas Uma lista vinculada é uma coleção linear (isto é, uma sequência) de objetos autorreferenciais de classe, chamados nós, conectados por links de referência — daí o termo lista “vinculada”. Em geral, um programa acessa uma lista encadeada via uma referência ao primeiro nó. O programa acessa cada nó subsequente via a referência de link armazenado no nó anterior. Por convenção, a referência de vínculo no último nó da lista é configurada como null. Os dados são armazenados em uma lista encadeada dinamicamente — o programa cria cada nó conforme necessário. As pilhas e filas são estruturas de dados também lineares e, como veremos, são versões limitadas de listas vinculadas. As árvores são estruturas de dados não lineares. As listas de dados podem ser armazenadas em arrays, mas as listas vinculadas fornecem várias vantagens. Uma lista vinculada é apropriada quando o número de elementos de dados a ser representado na estrutura de dados é imprevisível. As listas encadeadas são dinâmicas, portanto, o comprimento de uma lista pode aumentar ou diminuir de acordo com a necessidade, enquanto o tamanho de um array Java “convencional” não pode ser alterado — ele é fixado quando o programa cria o array. Os arrays “convencionais” podem tornar-se cheios. As listas vinculadas tornam-se cheias apenas quando o sistema tem memória insuficiente para satisfazer solicitações de alocação de armazenamento dinâmico. O pacote java.util contém a classe LinkedList (discutida no Capítulo 20) para implementar e manipular listas encadeadas que crescem e encolhem durante a execução de programa.

Dica de desempenho 22.1 Um array pode ser declarado para conter mais elementos do que o número de itens esperado, mas isso desperdiça memória. Nessas situações, as listas encadeadas fornecem melhor uso de memória permitindo ao programa adaptar-se às necessidades de armazenamento em tempo de execução.



22.4  Listas vinculadas

697

Dica de desempenho 22.2 A inserção em uma lista encadeada é rápida — somente duas referências precisam ser modificadas (depois de localizar ponto de inserção). Todos os objetos existentes de nó permanecem em suas localizações atuais na memória.

As listas vinculadas podem ser mantidas em ordem de classificação simplesmente inserindo cada novo elemento no ponto adequado na lista. (Sem dúvida, realmente, leva tempo para localizar o ponto de inserção adequado.) Os elementos existentes da lista não precisam ser movidos.

Dica de desempenho 22.3 A inserção e a exclusão em um array classificado podem consumir muito tempo — todos os elementos que se seguem ao elemento inserido ou excluído devem ser deslocados apropriadamente.

Listas encadeadas individualmente Nós de lista vinculada normalmente não são armazenados contiguamente na memória. Em vez disso, são logicamente contíguos. A Figura 22.2 ilustra uma lista vinculada com vários nós. Esse diagrama apresenta uma lista encadeada individualmente — cada nó contém uma referência ao próximo nó da lista. Em geral, as listas encadeadas são implementadas como listas duplamente encadeadas — cada nó contém uma referência ao próximo nó na lista e uma referência ao anterior. A classe LinkedList do Java é a implementação de uma lista duplamente encadeada. firstNode

H

lastNode

D

...

Q

Figura 22.2  |  Representação gráfica de lista encadeada.

Dica de desempenho 22.4 Normalmente, os elementos de um array são contíguos na memória. Isso permite acesso imediato a qualquer elemento do array, porque seu endereço pode ser calculado diretamente como seu deslocamento a partir do início do array. As listas encadeadas não têm recursos para tal acesso imediato — um elemento só pode ser acessado percorrendo a lista da parte da frente (ou da parte da trás em uma lista duplamente encadeada).

Implementando uma classe List genérica O programa das figuras 22.3–22.5 utiliza um objeto de nossa classe genérica List para manipular uma lista de objetos variados. O programa consiste em quatro classes — ListNode (Figura 22.3, linhas 6–37), List (Figura 22.3, linhas 40–147), EmptyListException (Figura 22.4) e ListTest (Figura 22.5). As classes List, ListNode e EmptyListException encontram-se no pacote com.deitel. ch22, portanto, podem ser reutilizadas por todo este capítulo. Encapsulada em cada objeto List está uma lista vinculada de objetos List­ Node. [Nota: Muitas das classes neste capítulo são declaradas no pacote com.deitel.ch22. Toda classe assim deve ser compilada com a opção de linha de comando -d para javac. Ao compilar as classes que não estão nesse pacote e ao executar os programas, não deixe de utilizar a opção -classpath com javac e java, respectivamente.] 1 2 3 4 5 6 7 8 9 10 11

// Figura 22.3: List.java // Declarações da classe ListNode e List. package com.deitel.ch22; // classe para representar um nó em uma lista class ListNode< T > { // membros de acesso de pacote; List pode acessar esses diretamente T data; // dados para esse nó ListNode< T > nextNode; // referência para o próximo nó na lista

698

Capítulo 22  Estruturas de dados genéricas personalizadas

12 13 14 15 16 17

// construtor cria uma ListNode que referencia o objeto ListNode( T object ) { this( object, null ); } // fim do construtor de um argumento ListNode

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80

// construtor cria ListNode que referencia o objeto // especificado e a próxima ListNode ListNode( T object, ListNode< T > node ) { data = object; nextNode = node; } // fim do construtor de dois argumentos ListNode // retorna referência aos dados no nó T getData() { return data; // retorna o item nesse nó } // fim do método getData // retorna referência ao próximo nó na lista ListNode< T > getNext() { return nextNode; // obtém próximo nó } // fim do método getNext } // fim da classe ListNode< T > // definição da classe List public class List< T > { private ListNode< T > firstNode; private ListNode< T > lastNode; private String name; // string como "lista" usada na impressão // construtor cria List vazia com "list" como o nome public List() { this( "list" ); } // fim do construtor sem argumentos List // construtor cria uma List vazia com um nome public List( String listName ) { name = listName; firstNode = lastNode = null; } // fim do construtor de um argumento List // insere o item na frente de List public void insertAtFront( T insertItem ) { if ( isEmpty() ) // firstNode e lastNode referenciam o mesmo objeto firstNode = lastNode = new ListNode< T >( insertItem ); else // firstNode referenciam o novo nó firstNode = new ListNode< T >( insertItem, firstNode ); } // fim do método insertAtFront // insere o item no fim de List public void insertAtBack( T insertItem ) { if ( isEmpty() ) // firstNode e lastNode referenciam o mesmo objeto firstNode = lastNode = new ListNode< T >( insertItem ); else // nextNode do lastNode referencia o novo nó lastNode = lastNode.nextNode = new ListNode< T >( insertItem ); } // fim do método insertAtBack // remove o primeiro nó de List public T removeFromFront() throws EmptyListException { if ( isEmpty() ) // lança exceção se List estiver vazia

81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

22.4  Listas vinculadas throw new EmptyListException( name ); T removedItem = firstNode.data; // recupera dados sendo removidos // atualiza referências firstNode e lastNode if ( firstNode == lastNode ) firstNode = lastNode = null; else firstNode = firstNode.nextNode; return removedItem; // retorna dados de nó removidos } // fim do método removeFromFront // remove o último nó de List public T removeFromBack() throws EmptyListException { if ( isEmpty() ) // lança exceção se List estiver vazia throw new EmptyListException( name ); T removedItem = lastNode.data; // recupera dados sendo removidos // atualiza referências firstNode e lastNode if ( firstNode == lastNode ) firstNode = lastNode = null; else // localiza o novo último nó { ListNode< T > current = firstNode; // faz loop enquanto nó atual não referencia lastNode while ( current.nextNode != lastNode ) current = current.nextNode; lastNode = current; // atual é novo lastNode current.nextNode = null; } // fim de else return removedItem; // retorna dados de nó removidos } // fim do método removeFromBack // determina se a lista estiver vazia public boolean isEmpty() { return firstNode == null; // retorna true se a lista estiver vazia } // fim do método isEmpty // gera saída do conteúdo da lista public void print() { if ( isEmpty() ) { System.out.printf( "Empty %s\n", name ); return; } // fim do if System.out.printf( "The %s is: ", name ); ListNode< T > current = firstNode; // enquanto não estiver no fim de lista, gera saída dos dados do nó atual while ( current != null ) { System.out.printf( "%s ", current.data ); current = current.nextNode; } // fim do while System.out.println( "\n" ); } // fim do método print } // fim da classe List< T >

Figura 22.3  |  Declarações de classe ListNode e List.

699

700 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Capítulo 22  Estruturas de dados genéricas personalizadas

// Figura 22.4: EmptyListException.java // declaração da classe EmptyListException. package com.deitel.ch22; public class EmptyListException extends RuntimeException { // construtor sem argumento public EmptyListException() { this( "List" ); // chama outro construtor de EmptyListException } // fim do construtor sem argumento EmptyListException // construtor de um argumento public EmptyListException( String name ) { super( name + " is empty" ); // chama construtor de superclasse } // fim do construtor de um argumento EmptyListException } // fim da classe EmptyListException

Figura 22.4  |  Declaração da classe EmptyListException. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

// Figura 22.5: ListTest.java // Classe ListTest para demonstrar capacidades de List. import com.deitel.ch22.List; import com.deitel.ch22.EmptyListException; public class ListTest { public static void main( String[] args ) { List< Integer > list = new List< Integer >(); // cria uma List // insere inteiros na lista list.insertAtFront( -1 ); list.print(); list.insertAtFront( 0 ); list.print(); list.insertAtBack( 1 ); list.print(); list.insertAtBack( 5 ); list.print(); // remove objetos da lista; imprime depois de cada remoção try { int removedItem = list.removeFromFront(); System.out.printf( "\n%d removed\n", removedItem ); list.print(); removedItem = list.removeFromFront(); System.out.printf( "\n%d removed\n", removedItem ); list.print(); removedItem = list.removeFromBack(); System.out.printf( "\n%d removed\n", removedItem ); list.print(); removedItem = list.removeFromBack(); System.out.printf( "\n%d removed\n", removedItem ); list.print(); } // fim do try catch ( EmptyListException emptyListException ) { emptyListException.printStackTrace(); } // fim do catch } // fim de main } // fim da classe ListTest



22.4  Listas vinculadas

The The The The

list list list list

is: is: is: is:

701

-1 0 -1 0 -1 1 0 -1 1 5

0 removed The list is: -1 1 5 -1 removed The list is: 1 5 5 removed The list is: 1 1 removed Empty list

Figura 22.5  |  Classe ListTest para demonstrar capacidades List.

Classes genéricas ListNode e List A classe genérica ListNode (Figura 22.3, linhas 6–37) declara campos de acesso de pacote data e nextNode. O campo data é uma referência do tipo T, portanto, seu tipo será determinado quando o código de cliente criar o objeto List correspondente. A variável next­ Node armazena uma referência ao próximo objeto ListNode na lista encadeada (ou null se o nó for o último na lista). As linhas 42–43 da classe List (Figura 22.3, linhas 40–47) declaram referências ao primeiro e último ListNodes em uma List (firstNode e lastNode, respectivamente). Os construtores (linhas 47–50 e 53–57) inicializam ambas as referências como null. Os métodos mais importantes da classe List são insertAtFront (linhas 60–66), insertAtBack (linhas 69–75), removeFromFront (linhas 78–92) e removeFromBack (linhas 95–118). O método isEmpty (linhas 121–124) é um método predicado que determina se a lista está vazia (isto é, a referência ao primeiro nó da lista é null). Os métodos predicados em geral testam uma condição e não modificam o objeto em que eles são chamados. Se a lista estiver vazia, o método isEmpty retorna true; caso contrário, retorna false. O método print (linhas 127–146) exibe o conteúdo da lista. Discutimos os métodos da classe List mais detalhadamente depois de discutirmos a classe ListTest. Classe ListTest O método main da classe ListTest (Figura 22.5) cria um objeto List, (linha 10), insere objetos no início da lista utilizando o método insertAtFront, insere objetos no fim da lista utilizando o método insertAtBack , exclui objetos na frente da lista utilizando o método removeFromFront e exclui objetos do fim da lista utilizando o método removeFromBack. Depois que cada operação de inserção e remoção, ListTest chama o método List print para exibir o conteúdo da lista atual. Se uma tentativa de remover um item de uma lista vazia for feita, uma EmptyListException (Figura 22.4) é lançada, então as chamadas de método para removeFromFront e removeFromBack são colocadas em um bloco try que é seguido por um handler de exceção apropriado. Observe nas linhas 13, 15, 17 e 19 que o aplicativo passa os valores primitivos literais int para os métodos insertAtFront e insertAtBack. Cada um desses métodos foi declarado com um parâmetro do tipo genérico T (Figura 22.3, linhas 60 e 69). Como esse exemplo manipula um List, o tipo T representa a classe de empacotador do tipo Integer. Nesse caso, a JVM converte (autoboxing) cada valor literal em um objeto Integer e esse objeto é realmente inserido na lista. Método List insertAtFront Agora discutimos cada método de classe List (Figura 22.3) em detalhes e fornecemos diagramas que mostram as manipulações de referência realizadas pelos métodos insertAtFront, insertAtBack, removeFromFront e removeFromBack. O método insertAt­ Front (linhas 60–66 da Figura 22.3) coloca um novo nó na frente da lista. Os passos são: 1 . Chamar isEmpty para determinar se a lista está vazia (linha 62). 2. Se a lista for vazia, atribua a firstNode e lastNode o novo ListNode inicializado com insertItem (linha 63). (Lembre-se de que os operadores de atribuição avaliam da direita para a esquerda.) O construtor ListNode nas linhas 13–16 chama o construtor ListNode nas linhas 20–24 para configurar a variável de instância data para referenciar o insertItem passado como um argumento e para configurar a referência nextNode como null, porque esse é o primeiro e último nó na lista. 3. Se a lista não estiver vazia, o novo nó é “vinculado” na lista configurando firstNode como um novo objeto ListNode e inicializando esse objeto com insertItem e firstNode (linha 65). Quando o construtor ListNode (linhas 20–24) executar, ele configura a variável de instância data para referenciar o insertItem passado como um argumento e realiza a inserção configurando a referência next­ Node do novo nó como o ListNode passado como um argumento, que era anteriormente o primeiro nó. Na Figura 22.6, a parte (a) mostra uma lista e um novo nó durante a operação insertAtFront e antes de o programa vincular o novo nó na lista. As setas pontilhadas na parte (b) ilustram o Passo 3 da operação insertAtFront que permite que o nó que contém 12 torne-se o novo primeiro nó na lista.

702

Capítulo 22  Estruturas de dados genéricas personalizadas (a)

firstNode 7

11

new ListNode 12

(b)

firstNode 7

11

new ListNode 12

Figura 22.6  |  Representação gráfica da operação insertAtFront.

Método List insertAtBack O método insertAtBack (linhas 69–75 da Figura 22.3) coloca um novo nó na parte de trás da lista. Os passos são: 1 . Chamar isEmpty para determinar se a lista está vazia (linha 71). 2. Se a lista for vazia, atribua a firstNode e lastNode o novo ListNode inicializado com insertItem (linha 72). O construtor ListNode nas linhas 13–16 chama o construtor nas linhas 20–24 para configurar a variável de instância data a fim de referenciar o insertItem passado como um argumento e configurar a referência nextNode como null. 3 . Se a lista não estiver vazia, a linha 74 vincula o novo nó na lista atribuindo a lastNode e lastNode.nextNode a referência ao novo ListNode que foi inicializado com insertItem. O construtor do ListNode (linhas 13–16), configura a variável de instância data para referenciar o insertItem passado como um argumento e configura a referência nextNode como null, pois esse é o último nó na lista. Na Figura 22.7, a parte (a) mostra uma lista e um novo nó durante a operação insertAtBack e antes de vincular o novo nó à lista. As setas pontilhadas na parte (b) ilustram o Passo 3 do método insertAtBack, que adiciona o novo nó ao fim de uma lista que não estiver vazia. (a)

12

(b)

lastNode

firstNode

7

lastNode

firstNode

12

11

7

11

new Listnode

5

new Listnode

5

Figura 22.7  |  Representação gráfica da operação insertAtBack.

Método List removeFromFront O método removeFromFront (linhas 78–92 da Figura 22.3) remove o primeiro nó da lista e retorna uma referência aos dados removidos. Se a lista estiver vazia quando o programa chamar esse método, o método lançará uma EmptyListException (linhas 80–81). Caso contrário, o método retorna uma referência aos dados removidos. Os passos são:



22.4  Listas vinculadas

703

1. Atribua firstNode.data (os dados sendo removidos) a removedItem (linha 83). 2. Se firstNode e lastNode referenciarem o mesmo objeto (linha 86), a lista terá apenas um elemento nessa hora. Então, o método configura firstNode e lastNode como null (linha 87) para remover o nó da lista (deixando a lista vazia). 3 . Se a lista tiver mais de um nó, então o método deixa a referência lastNode como está e atribui o valor de firstNode.nextNode ao firstNode (linha 89). Portanto, firstNode referencia o nó que, anteriormente, era o segundo nó na lista.

4. Retornar a referência removedItem (linha 91). Na Figura 22.8, parte (a) ilustra a lista antes da operação de remoção. As linhas tracejadas e setas na parte (b) mostram as manipulações de referência.

Método List removeFromBack O método removeFromBack (linhas 95–118 da Figura 22.3) remove o último nó de uma lista e retorna uma referência aos dados removidos. O método lança uma EmptyListException (linhas 97–98) se a lista estiver vazia quando o programa chamar esse método. Os passos são: 1 . Atribuir lastNode.data (os dados sendo removidos da lista) a removedItem (linha 100). (a)

firstNode

12

lastNode

7

11

(b) firstNode

12

5

lastNode

7

11

5

removeItem

Figura 22.8  |  Representação gráfica da operação removeFromFront.

2. Se firstNode e lastNode referenciarem o mesmo objeto (linha 103), a lista terá apenas um elemento nessa hora. Então, a linha 104 configura firstNode e lastNode como null para remover esse nó da lista (deixando a lista vazia). 3 . Se a lista tiver mais de um nó, crie a referência ListNode current e a atribua firstNode (linha 107).

4. Agora “percorrer a lista” com current até ela referenciar o nó antes do último nó. O loop while (linhas 110–111) atribui current. nextNode a current contanto que current.nextNode (o próximo nó na lista) não seja lastNode. 5. Depois de localizar o penúltimo nó, atribuir current a lastNode (linha 113) para atualizar qual o nó é o último na lista. 6. Configurar o current.nextNode como null (linha 114) para remover o último nó da lista e termina a lista no nó atual. 7 . Retornar a referência removedItem (linha 117).

Na Figura 22.9, parte (a) ilustra a lista antes da operação de remoção. As linhas tracejadas e setas na parte (b) mostram as manipulações de referência.

Método List print O método print (linhas 127–146) primeiro determina se a lista está vazia (linhas 129–133). Se estiver, print exibe uma mensagem que indica que a lista está vazia e o controle retorna o método chamador. Caso contrário, print gera saída dos dados da lista. A linha 136 cria ListNode current e a inicializa com firstNode. Enquanto current não estiver null, há mais itens na lista. Portanto, a linha 141 gera saída de uma representação de string de current.data. A linha 142 muda para o próximo nó na lista atribuindo o valor de referência current.nextNode a current. Esse algoritmo de impressão é idêntico para listas vinculadas, pilhas e filas.

704

Capítulo 22

Estruturas de dados genéricas personalizadas (a)

firstNode

12

(b)

lastNode

7

current

firstNode

12

11

7

5

lastNode

11

5

removeItem

Figura 22.9 | Representação gráfica da operação removeFromBack.

22.5 Pilhas Uma pilha é uma versão limitada de uma lista — novos nós podem ser inseridos e removidos de uma pilha apenas na parte superior. Por essa razão, uma pilha é referida como uma estrutura de dados primeiro a entrar, primeiro a sair (Last-In, First-Out — LIFO). O membro link no último nó é configurado como null para indicar a parte inferior da pilha. Observe que não é necessário que uma pilha seja implementada como uma lista encadeada — ela também pode ser implementada utilizando um array. Os principais métodos para manipular uma pilha são push e pop, que adicionam um novo nó à superior da pilha e removem um nó da superior da pilha, respectivamente. O método pop também retorna os dados do nó removido. As pilhas têm muitas aplicações interessantes. Por exemplo, quando um programa chama um método, o método chamado deve saber retornar ao seu chamador, assim o endereço de retorno do método chamador é inserido na pilha de execução do programa (discutido na Seção 6.6). Se uma série de chamadas de método ocorre, os sucessivos endereços de retorno são empilhados na ordem primeiro a entrar, primeiro a sair de modo que cada método possa retornar para seu chamador. As pilhas suportam as chamadas de método recursivo da mesma maneira que as chamadas de método não recursivo convencionais. A pilha de execução de programa também contém a memória criada para variáveis locais a cada invocação de um método durante a execução de um programa. Quando o método retorna para seu chamador, a memória para variáveis locais desse método é removida da pilha e essas variáveis não são mais conhecidas para o programa. Se a variável local for uma referência e o objeto referenciado não tiver nenhuma outra variável referenciando-o, o objeto pode sofrer coleta de lixo. Os compiladores utilizam pilhas para avaliar expressões aritméticas e gerar o código de linguagem de máquina para processá-las. Os exercícios neste capítulo exploram várias aplicações de pilhas, incluindo seu uso para desenvolver um compilador completo. Além disso, o pacote java.util contém a classe Stack (ver Capítulo 20) para implementar e manipular pilhas que podem crescer e encolher durante a execução do programa. Nesta seção, tiramos proveito do íntimo relacionamento entre lista e pilha para implementar uma classe de pilha reutilizando a classe List da Figura 22.3. Demonstramos duas formas diferentes da capacidade de reutilização. Em primeiro lugar, implementamos a classe de pilha estendendo a classe List. Em seguida, implementamos uma classe de pilha de execução idêntica por meio da composição incluindo uma referência a um objeto List como um variável de instância private. As estruturas de dados de lista, pilha e fila neste capítulo são implementadas para armazenar referências a objetos de qualquer tipo a fim de encorajar mais a capacidade de reutilização.

Classe de pilha que herda de List As figuras 22.10–22.11 criam e manipulam uma classe de pilha que estende a classe List da Figura 22.3. Queremos que a pilha tenha os métodos push, pop, isEmpty e print. Essencialmente, esses são os métodos List insertAtFront, removeFromFront, isEmpty e print. Naturalmente, a classe List contém outros métodos (como insertAtBack e removeFromBack) que não tornaríamos acessível pela interface public à classe de pilha. É importante lembrar que todos os métodos na interface de classe public da List também são métodos public da subclasse StackInheritance (Figura 22.10). Cada método de StackInheritance chama o método List apropriado — por exemplo, método push chama insertAtFront e o método pop chama removeFrom­ Front. Clientes StackInheritance podem chamar os métodos isEmpty e print porque eles são herdados de List. A classe StackInheritance é declarada no pacote com.deitel.ch22 (linha 3) para a reutilização. Observe que StackInheritance não importa List — as classes estão no mesmo pacote.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

22.5  Pilhas

705

// Figura 22.10: StackInheritance.java // StackInheritance estende a classe List. package com.deitel.ch22; public class StackInheritance< T > extends List< T > { // construtor sem argumento public StackInheritance() { super( "stack" ); } // fim do construtor sem argumento StackInheritance // adiciona objeto à pilha public void push( T object ) { insertAtFront( object ); } // fim do método push // remove objeto da pilha public T pop() throws EmptyListException { return removeFromFront(); } // fim do método pop } // fim da classe StackInheritance

Figura 22.10  |  StackInheritance estende a classe List.

O método main da classe StackInheritanceTest (Figura  22.11) cria um objeto da classe StackInheritance chamado (linhas 10–11). O programa adiciona inteiros na pilha (linhas 14, 16, 18 e 20). O autoboxing é utilizado aqui para inserir objetos Integer na estrutura de dados. As linhas 28–33 removem os objetos da pilha em um loop while infinito. Se o método pop for invocado em uma pilha vazia, o método lança uma EmptyListException. Nesse caso, o programa exibe o rastreamento de pilha da exceção, que mostra os métodos na pilha de execução do programa no momento em que a exceção ocorreu. Observe que o programa utiliza o método print (herdado de List) para gerar a saída do conteúdo da pilha. stack

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

// Figura 22.11: StackInheritanceTest.java // Programa de manipulação de pilha. import com.deitel.ch22.StackInheritance; import com.deitel.ch22.EmptyListException; public class StackInheritanceTest { public static void main( String[] args ) { StackInheritance< Integer > stack = new StackInheritance< Integer >(); // utiliza método push stack.push( -1 ); stack.print(); stack.push( 0 ); stack.print(); stack.push( 1 ); stack.print(); stack.push( 5 ); stack.print(); // remove itens de pilha try { int removedItem; while ( true ) { removedItem = stack.pop(); // utiliza método pop System.out.printf( "\n%d popped\n", removedItem );

706 32 33 34 35 36 37 38 39 40 The The The The

Capítulo 22  Estruturas de dados genéricas personalizadas stack.print(); } // fim do while } // fim do try catch ( EmptyListException emptyListException ) { emptyListException.printStackTrace(); } // fim do catch } // fim de main } // fim da classe StackInheritanceTest stack stack stack stack

is: is: is: is:

-1 0 -1 1 0 -1 5 1 0 -1

5 popped The stack is: 1 0 -1 1 popped The stack is: 0 -1 0 popped The stack is: -1 -1 popped Empty stack com.deitel.ch22.EmptyListException: stack is empty at com.deitel.ch22.List.removeFromFront(List.java:81) at com.deitel.ch22.StackInheritance.pop(StackInheritance.java:22) at StackInheritanceTest.main(StackInheritanceTest.java:30)

Figura 22.11  |  Programa de manipulação de pilha.

Classe de pilha que contém uma referência a uma List Você também pode implementar uma classe reutilizando uma classe de lista por composição. A Figura 22.12 utiliza uma private List (linha 7) na declaração de classe da StackComposition. A composição permite que ocultemos os métodos List que não devem estar na interface public da nossa pilha. Fornecemos os métodos da interface public que utilizam somente os métodos List necessários. A implementação de cada método de pilha como uma chamada para um método List chama-se delegação — o método de pilha invocado delega a chamada para o método List apropriado. Em particular, StackComposition delega chamadas para os métodos List insertAtFront, removeFromFront, isEmpty e print. Nesse exemplo, não mostramos a classe StackCompositionTest, porque a única diferença é que alteramos o tipo da pilha de StackInheritance para StackComposition (linhas 3 e 10–11 da Figura 22.11). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

// Figura 22.12: StackComposition.java // StackComposition utiliza um objeto List composto. package com.deitel.ch22; public class StackComposition< T > { private List< T > stackList; // construtor sem argumento public StackComposition() { stackList = new List< T >( "stack" ); } // fim do construtor sem argumento StackComposition // adiciona objeto à pilha public void push( T object ) { stackList.insertAtFront( object ); } // fim do método push // remove o objeto da pilha public T pop() throws EmptyListException

22.6 Filas 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

707

{ return stackList.removeFromFront(); } // fim do método pop // determina se a pilha está vazia public boolean isEmpty() { return stackList.isEmpty(); } // fim do método isEmpty // gera saída do conteúdo de pilha public void print() { stackList.print(); } // fim do método print } // fim da classe StackComposition

Figura 22.12 |

StackComposition

utiliza um objeto List composto.

22.6 Filas Outra estrutura de dados comumente utilizada é a fila. Uma fila é semelhante a uma fila de caixa de um supermercado — o caixa atende a primeira pessoa da fila. Outros clientes entram no fim da fila e esperam ser atendidos. Os nós de fila são removidos apenas a partir da cabeça (ou no início) da fila e são inseridos apenas na cauda (ou no fim). Por essa razão, uma fila é uma estrutura de dados primeiro a entrar, primeiro a sair (First-In, First-Out — FIFO). As operações de inserção e remoção são conhecidas como enqueue e dequeue. As filas têm muitas utilizações em sistemas de computador. Cada CPU em um computador pode servir somente um aplicativo por vez. Todo aplicativo que requer tempo de processador é colocado em uma fila. O aplicativo na frente da fila é o próximo a receber o serviço. Cada aplicativo avança gradualmente para frente à medida que os aplicativos antes dele recebem o serviço. As filas também são utilizadas para suportar spooling de impressão. Por exemplo, uma única impressora talvez seja compartilhada por todos os usuários de uma rede. Muitos usuários podem enviar trabalhos de impressão à impressora, mesmo quando a impressora já estiver ocupada. Esses trabalhos de impressão são colocados em uma fila até a impressora ficar disponível. Um programa chamado spooler gerencia a fila para assegurar que, à medida que cada trabalho de impressão seja concluído, o próximo trabalho de impressão seja enviado à impressora. Os pacotes de informações também esperam em filas em redes de computadores. Toda vez que um pacote chegar a um nó de rede, ele deve ser roteado para o próximo nó ao longo do caminho para o destino final do pacote. O nó de roteamento roteia um pacote por vez, então pacotes adicionais são enfileirados até o roteador conseguir roteá-los. Um servidor de arquivos em uma rede de computadores trata as solicitações de acesso a arquivo de muitos clientes por toda a rede. Os servidores têm uma capacidade limitada para servir solicitações de clientes. Quando essa capacidade é excedida, as solicitações dos clientes esperam em filas. A Figura 22.13 cria uma classe Queue que contém um objeto List (Figura 22.3) e fornece os métodos enqueue, dequeue, isEmpty e print. A classe List contém alguns métodos (por exemplo, insertAtFront e removeFromBack) que preferimos não tornar acessíveis por meio da interface public da classe Queue . Utilizar a composição permite que ocultemos outros métodos public da classe List a partir de clientes da classe Queue. Cada método Queue chama um método List apropriado — o método enqueue chama o método List insertAtBack, o método dequeue chama o método List removeFromFront, o método isEmpty chama o método List isEmpty e o método print chama o método List print. Para a reutilização, a classe Queue é declarada no pacote com.deitel.ch22. 1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 22.13: Queue.java // Queue utiliza a classe List. package com.deitel.ch22; public class Queue { private List< T > queueList; // construtor sem argumento public Queue() { queueList = new List< T >( "queue" ); } // fim do construtor sem argumento Queue

708 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

Capítulo 22  Estruturas de dados genéricas personalizadas // adiciona o objeto à fila public void enqueue( T object ) { queueList.insertAtBack( object ); } // fim do método enqueue // remove o objeto da fila public T dequeue() throws EmptyListException { return queueList.removeFromFront(); } // fim do método dequeue // determina se a fila está vazia public boolean isEmpty() { return queueList.isEmpty(); } // fim do método isEmpty // gera o conteúdo da fila public void print() { queueList.print(); } // fim do método print } // fim da classe Queue

Figura 22.13  |  Queue utiliza a classe List.

O método main da classe QueueTest (Figura 22.14) cria e inicializa a variável Queue queue (linha 10). As linhas 13, 15, 17 e 19 enfileiram quatro inteiros, tirando proveito de autoboxing para inserir objetos Integer na fila. As linhas 27–32 utilizam um loop infinito para desenfileirar os objetos na ordem primeiro a entrar, primeiro a sair. Quando a fila está vazia, o método dequeue lança uma Empty­ ListException e o programa exibe o rastreamento de pilha da exceção. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

// Figura 22.14: QueueTest.java // Classe QueueTest. import com.deitel.ch22.Queue; import com.deitel.ch22.EmptyListException; public class QueueTest { public static void main( String[] args ) { Queue< Integer > queue = new Queue< Integer >(); // utiliza o método enqueue queue.enqueue( -1 ); queue.print(); queue.enqueue( 0 ); queue.print(); queue.enqueue( 1 ); queue.print(); queue.enqueue( 5 ); queue.print(); // remove os objetos da fila try { int removedItem; while ( true ) { removedItem = queue.dequeue(); // utiliza método dequeue System.out.printf( "\n%d dequeued\n", removedItem ); queue.print(); } // fim do while } // fim do try catch ( EmptyListException emptyListException ) {

22.7 Árvores 36 37 38 39 The The The The

709

emptyListException.printStackTrace(); } // fim do catch } // fim de main } // fim da classe QueueTest queue queue queue queue

is: is: is: is:

­1 ­1 0 ­1 0 1 ­1 0 1 5

­1 dequeued The queue is: 0 1 5 0 dequeued The queue is: 1 5 1 dequeued The queue is: 5 5 dequeued Empty queue com.deitel.ch22.EmptyListException: queue is empty at com.deitel.ch22.List.removeFromFront(List.java:81) at com.deitel.ch22.Queue.dequeue(Queue.java:24) at QueueTest.main(QueueTest.java:29)

Figura 22.14 | Programa de processamento de fila.

22.7 Árvores Listas, pilhas e filas são estruturas de dados lineares (isto é, sequências). Uma árvore é uma estrutura de dados bidimensional, não linear, com propriedades especiais. Os nós da árvore contêm dois ou mais links. Esta seção discute as árvores binárias (Figura 22.15) — árvores cujos nós contêm, cada um, dois links (nenhum, um ou ambos os quais podem ser null). O nó raiz é o primeiro nó em uma árvore. Cada link no nó raiz referencia um filho. O filho esquerdo é o primeiro nó na subárvore esquerda (também conhecido como o nó raiz da subárvore esquerda) e o filho direito é o primeiro nó na subárvore direita (também conhecido como o nó raiz da subárvore direita). Os filhos de um nó específico são chamados irmãos. Um nó sem filhos é chamado de nó de folha. Os cientistas da computação normalmente desenham árvores indo do nó raiz para baixo — o oposto da maneira como a maioria das árvores cresce na natureza.

B

A

D

C

Figura 22.15 | Representação gráfica da árvore binária.

Em nosso exemplo, criamos uma árvore binária especial chamada árvore de pesquisa binária. Uma árvore de pesquisa binária (sem valores de nó duplicados) tem a característica de que os valores em qualquer subárvore esquerda são menores que o valor no nó pai dessa subárvore e os valores em qualquer subárvore direita são maiores que o valor no nó pai dessa subárvore. A Figura 22.16 ilustra uma árvore de pesquisa binária com 12 valores de inteiro. Observe que a forma da árvore de pesquisa binária que corresponde a um conjunto de dados pode variar, dependendo da ordem em que os valores são inseridos na árvore. As figuras 22.17–22.18 criam uma classe de árvore de pesquisa binária genérica e a utilizam para manipular uma árvore de números inteiros. O aplicativo na Figura 22.18 percorre a árvore (isto é, passa por todos os seus nós) de três maneiras — utilizando percursos recur-

710

Capítulo 22  Estruturas de dados genéricas personalizadas

sivos na ordem, na pré-ordem e na pós-ordem. O programa gera 10 números aleatórios e insere cada um na árvore. A classe Tree é declarada no pacote com.deitel.ch22 para a reutilização. 47 25

77

11 7

43 17

31

65 44

93 68

Figura 22.16  |  Árvore de pesquisa binária que contém 12 valores.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

// Figura 22.17: Tree.java // Declarações de classe TreeNode e Tree para uma árvore de pesquisa binária. package com.deitel.ch22; // definição da classe TreeNode class TreeNode< T extends Comparable< T > > { // membros de acesso de pacote TreeNode< T > leftNode; // nó esquerdo T data; // valor do nó TreeNode< T > rightNode; // nó direito // construtor inicializa os dados e os torna um nó de folha public TreeNode( T nodeData ) { data = nodeData; leftNode = rightNode = null; // o nó não tem nenhum filho } // fim do construtor TreeNode // localiza ponto de inserção e insere novo nó; ignora os valores duplicados public void insert( T insertValue ) { // insere na subárvore esquerda if ( insertValue.compareTo( data ) < 0 ) { // insere novo TreeNode if ( leftNode == null ) leftNode = new TreeNode< T >( insertValue ); else // continua percorrendo a subárvore esquerda recursivamente leftNode.insert( insertValue ); } // fim do if // insere na subárvore direita else if ( insertValue.compareTo( data ) > 0 ) { // insere novo TreeNode if ( rightNode == null ) rightNode = new TreeNode< T >( insertValue ); else // continua percorrendo a subárvore direita recursivamente rightNode.insert( insertValue ); } // fim de else if } // fim do método insert } // fim da classe TreeNode // definição da classe Tree public class Tree< T extends Comparable< T > > { private TreeNode< T > root; // construtor inicializa uma Tree de inteiros vazia public Tree() {

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

22.7  Árvores root = null; } // fim do construtor sem argumento Tree // insere um novo nó na árvore de pesquisa binária public void insertNode( T insertValue ) { if ( root == null ) root = new TreeNode< T >( insertValue ); // cria o nó raiz else root.insert( insertValue ); // chama o método insert } // fim do método insertNode // inicia percurso na pré-ordem public void preorderTraversal() { preorderHelper( root ); } // fim do método preorderTraversal // método recursivo para realizar percurso na pré-ordem private void preorderHelper( TreeNode< T > node ) { if ( node == null ) return; System.out.printf( "%s ", node.data ); // gera saída de dados do nó preorderHelper( node.leftNode ); // percorre subárvore esquerda preorderHelper( node.rightNode ); // percorre subárvore direita } // fim do método preorderHelper // inicia percurso na ordem public void inorderTraversal() { inorderHelper( root ); } // fim do método inorderTraversal // método recursivo para realizar percurso na ordem private void inorderHelper( TreeNode< T > node ) { if ( node == null ) return; inorderHelper( node.leftNode ); // percorre subárvore esquerda System.out.printf( "%s ", node.data ); // gera saída de dados do nó inorderHelper( node.rightNode ); // percorre subárvore direita } // fim do método inorderHelper // inicia percurso na pós-ordem public void postorderTraversal() { postorderHelper( root ); } // fim do método postorderTraversal // método recursivo para realizar percurso na pós-ordem private void postorderHelper( TreeNode< T > node ) { if ( node == null ) return; postorderHelper( node.leftNode ); // percorre subárvore esquerda postorderHelper( node.rightNode ); // percorre subárvore direita System.out.printf( "%s ", node.data ); // gera saída de dados do nó } // fim do método postorderHelper } // fim da classe Tree

Figura 22.17  |  Declarações da classe TreeNode e Tree para uma árvore de pesquisa binária.

711

712

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

Capítulo 22  Estruturas de dados genéricas personalizadas

// Figura 22.18: TreeTest.java // Programa de teste da árvore binária. import java.util.Random; import com.deitel.ch22.Tree; public class TreeTest { public static void main( String[] args ) { Tree< Integer > tree = new Tree< Integer >(); int value; Random randomNumber = new Random(); System.out.println( "Inserting the following values: " ); // insere 10 inteiros aleatórios de 0-99 na árvore for ( int i = 1; i 1.000. Pesquisar em uma árvore de pesquisa binária (fortemente empacotada) de 1.000.000 elementos requer no máximo 20 comparações, pois 220 > 1.000.000. Os exercícios do capítulo apresentam algoritmos para várias outras operações de árvore binária, como excluir um item de uma árvore binária, imprimir uma árvore binária em um formato de árvore bidimensional e realizar um percurso na ordem de nível de uma árvore binária. O percurso na ordem de nível visita os nós da árvore linha por linha, iniciando no nível do nó raiz. Em cada nível da árvore, um percurso na ordem de nível visita os nós da esquerda para direita. Outros exercícios de árvore binária incluem permitir uma árvore de pesquisa binária conter valores duplicados, inserir valores de string em uma árvore binária e determinar quantos níveis estão contidos em uma árvore binária.

22.8 Conclusão Este capítulo completa nossa apresentação de estruturas de dados. Nós a iniciamos no Capítulo 20 com uma introdução às coleções predefinidas da estrutura de coleções Java e continuamos no Capítulo 21 mostrando como implementar coleções e métodos genéricos. Neste capítulo, você aprendeu a construir estruturas de dados dinâmicas genéricas que crescem e encolhem em tempo de execução. Aprendeu que as listas encadeadas são coleções de itens de dados que são “vinculados em uma cadeia.” Também viu que um aplicativo pode realizar inserções e exclusões no começo e no fim de uma lista encadeada. Você aprendeu que as estruturas de dados pilha e fila são versões limitadas de listas. Quanto às pilhas, vimos que as inserções e exclusões só podem ser feitas na parte superior. Quanto às filas, que representam filas de espera, você viu que as inserções são feitas na cauda (na parte de trás) e as exclusões são feitas na cabeça (na parte da frente). Também aprendeu a estrutura de dados da árvore binária. Você viu uma árvore de pesquisa binária que facilitou a pesquisa e a classificação de dados em alta velocidade e a eliminação eficiente de itens de dados duplicados. Por todo o capítulo, aprendeu a criar e a empacotar essas estruturas de dados para capacidade de reutilização e de manutenção. Depois, introduzimos os applets Java — programas Java que, em geral, executam em um navegador. Apresentamos uma visão geral dos applets de exemplo do JDK e, em seguida, mostramos como escrever e executar os seus próprios applets. Depois introduzimos as capacidades Java Web Start para carregar um applet e instalar um atalho de área de trabalho a fim de recarregar futuramente o applet sem a necessidade de revisitar o site do applet.

Resumo Seção 22.1 Introdução • As estruturas de dados dinâmicas podem crescer e encolher em tempo de execução. • As listas encadeadas são coleções de itens de dados “vinculados em uma cadeia” — as inserções e exclusões podem ser feitas em qualquer lugar de uma lista encadeada. • As pilhas são importantes em compiladores e sistemas operacionais — as inserções e as exclusões são feitas somente na parte superior de uma pilha. • Em uma fila, as inserções são feitas na parte de trás (cauda), e as exclusões, na parte da frente (cabeça). • As árvores binárias facilitam a pesquisa e a classificação de dados em alta velocidade, a eliminação eficiente de itens de dados duplicados, a representação de diretórios de sistema de arquivos e a compilação de expressões em linguagem de máquina.

Seção 22.2 Classes autorreferenciais • Uma classe autorreferencial contém uma referência que referencia outro objeto do mesmo tipo de classe. Objetos autorreferenciais podem ser vinculados para formar estruturas de dados dinâmicas.

Seção 22.3 Alocação dinâmica de memória • O limite para alocação dinâmica de memória pode ser tão grande quanto a memória física disponível no computador ou o espaço em disco disponível em um sistema de memória virtual. Frequentemente, os limites são bem menores, porque a memória disponível do computador deve ser compartilhada entre muitos usuários. • Se nenhuma memória estiver disponível, um OutOfMemoryError é lançado.



Resumo

715

Seção 22.4  Listas vinculadas • Uma lista vinculada é acessada via uma referência ao primeiro nó da lista. Cada nó subsequente é acessado via o membro de referência de link armazenado no nó anterior. • Por convenção, a referência de link no último nó de uma lista é configurada como null para marcar o fim da lista. • Um nó pode conter dados de qualquer tipo, incluindo objetos de outras classes. • Uma lista encadeada é apropriada quando o número de elementos de dados a ser armazenado for imprevisível. As listas vinculadas são dinâmicas, portanto o comprimento de uma lista pode aumentar ou diminuir conforme necessário. • O tamanho de um array Java “convencional” não pode ser alterado — ele é fixado no momento da criação. • Os nós de lista normalmente não são armazenados na memória contígua. Em vez disso, são logicamente contíguos.

Seção 22.5  Pilhas • Uma pilha é uma estrutura de dados do tipo último a entrar, primeiro a sair (Last-In, First-Out — LIFO). Os principais métodos utilizados para manipular uma pilha são push e pop, que adicionam um novo nó à parte superior da pilha e removem um nó da parte superior da pilha, respectivamente. O método pop retorna os dados do nó removido. • Quando uma chamada de método é feita, o método chamado deve saber retornar para seu chamador, assim o endereço de retorno é inserido na pilha de execução do programa. Se uma série de chamadas de método ocorre, os valores sucessivos de retorno são inseridos na pilha na ordem primeiro a entrar, primeiro a sair de modo que cada método possa retornar para seu chamador. • A pilha de execução do programa contém o espaço criado para variáveis locais a cada invocação de um método. Quando o método retorna para seu chamador, o espaço para variáveis locais desse método é removido da pilha e essas variáveis não estão mais disponíveis para o programa. • As pilhas são utilizadas por compiladores para avaliar expressões aritméticas e gerar código de linguagem de máquina para processar as expressões. • A técnica de implementar cada método de pilha como uma chamada a um método List é chamada de delegação — o método de pilha invocado delega a chamada ao método List apropriado.

Seção 22.6  Filas • Uma fila é semelhante a uma fila de caixa em um supermercado — a primeira pessoa na fila é servida primeiro e os outros clientes entram na fila apenas no fim e esperam ser atendidos. • Os nós da fila só são removidos a partir da cabeça da fila e só são inseridos na cauda. Por essa razão, uma fila é referida como uma estrutura de dados primeiro a entrar, primeiro a sair (First-In, First-Out — FIFO). • As operações de inserção e remoção são conhecidas como enqueue e dequeue. • As filas têm muitas utilizações em sistemas de computador. A maioria dos computadores tem apenas um único processador, então somente um usuário por vez pode ser servido. As entradas para os outros aplicativos são colocadas em uma fila. A entrada na frente da fila é a próxima a receber o serviço. Cada entrada avança gradualmente para a frente da fila quando os aplicativos recebem o serviço.

Seção 22.7  Árvores • Uma árvore é uma estrutura de dados bidimensional não linear. Os nós da árvore contêm dois ou mais links. • Os nós de árvore contêm dois ou mais links. O nó raiz é o primeiro nó em uma árvore. • Cada link no nó raiz referencia um filho. O filho esquerdo é o primeiro nó na subárvore esquerda e o filho direito é o primeiro nó na subárvore direita. • Os filhos de um nó são chamados irmãos. Um nó sem filhos é chamado nó de folha. • Em uma árvore de pesquisa binária sem valores duplicados, os valores em qualquer subárvore esquerda são menores que o valor no nó pai da subárvore e os valores em qualquer subárvore direita são maiores que o valor no nó pai da subárvore. Um nó pode ser inserido apenas como um nó de folha em uma árvore de pesquisa binária. • Um percurso na ordem de uma árvore de pesquisa binária processa os valores de nó na ordem crescente. • Em um percurso na pré-ordem, o valor em cada nó é processado quando o nó é percorrido. Então os valores na subárvore esquerda são processados e, em seguida, os valores na subárvore direita. • Em um percurso na pós-ordem, o valor em cada nó é processado depois dos valores de seus filhos. • A árvore de pesquisa binária facilita a eliminação de duplicatas. Quando a árvore é criada, as tentativas de inserir um valor duplicado são reconhecidas porque uma duplicata segue as mesmas decisões “siga para a esquerda” ou “siga para a direita” em cada comparação que o valor original fez. Portanto, a duplicata acaba sendo comparada com um nó contendo o mesmo valor. O valor duplicado pode ser descartado nesse ponto. • Em uma árvore fortemente empacotada, cada nível contém aproximadamente duas vezes o número de elementos que o anterior. Então uma árvore de pesquisa binária fortemente empacotada com n elementos tem log2 n níveis e, portanto, no máximo log2 n comparações teriam de ser feitas para localizar uma correspondência ou para determinar que não existe nenhuma correspondência. Pesquisar em uma árvore de pesquisa binária (fortemente empacotada) de 1.000 elementos requer no máximo 10 comparações, pois 210 > 1.000. Pesquisar em uma árvore de pesquisa binária (fortemente empacotada) de 1.000.000 elementos requer no máximo 20 comparações, pois 220 > 1.000.000.

716

Capítulo 22  Estruturas de dados genéricas personalizadas

Terminologia adicionar a operação de pilha (push), 704 alocação dinâmica de memória, 696 árvore binária, 695 árvore binária, classificação, 713 árvore de pesquisa binária, 709 árvore fortemente empacotada, 714 árvore fortemente equilibrada, 714 cabeça de uma fila, 695 classe autorreferencial, 695 delegar uma chamada de método, 706 eliminação de duplicatas, 713 enqueue, operação de fila, 707 estrutura de dados dinâmica, 695 estrutura de dados linear, 709 estrutura de dados, primeiro a entrar, primeiro a sair (First-In, First-Out — FIFO), 707

estrutura de dados último a entrar, primeiro a sair (Last-In-First-Out — LIFO), 704 fila, 695 filho direito, 709 filho esquerdo, 709 final de uma fila, 695 lista vinculada, 695 lista vinculada individualmente, 697 nó de folha, 709 nó em uma lista, 696 nó filho, 709 nó raiz, 709 nós irmãos, 709 operação para desenfileiramento de fila, 707 operação para enfileiramento de fila, 707 parte superior de uma pilha, 695

percurso em uma árvore binária em ordem de nível, 714 percurso na ordem, 709 percurso na pós-ordem, 710 percurso na pré-ordem, 710 pilha, 695 remover a operação de pilha (pop), 704 sequência, 709 spooler, 707 spooling de impressão, 707 subárvore direita, 709 subárvore esquerda, 709 vincular a outro nó (link), 696

Exercícios de autorrevisão 22.1 Preencha as lacunas em cada uma das seguintes afirmações: a) Uma classe auto________ é utilizada para formar estruturas de dados dinâmicas que podem crescer e encolher em tempo de execução. b) Um(a) ________ é uma versão limitada de uma lista encadeada em que nós podem ser inseridos e excluídos somente a partir do início da lista. c) Um método que não altera uma lista encadeada, mas simplesmente a examina para determinar se ela está vazia, é referido como um método ________. d) Uma fila é referida como uma estrutura de dados ________ porque os primeiros nós inseridos são os primeiros nós removidos. e) A referência ao próximo nó em uma lista vinculada é referida como ________. f) Reivindicar automaticamente memória alocada dinamicamente em Java é chamado de ________. g) Um(a) ________ é uma versão limitada de uma lista vinculada em que os nós podem ser inseridos apenas no fim da lista e excluídos apenas do início da lista. h) Um(a) ________ é uma estrutura de dados bidimensional não linear que contém nós com dois ou mais links. i) Uma pilha é referida como uma estrutura de dados ________ porque o último nó inserido é o primeiro nó removido. j) Os nós de uma árvore ________ contém dois membros de link. k) O primeiro nó de uma árvore é o nó de ________. l) Cada link em um nó de árvore refere-se a um(a) ________ ou ________ desse nó. m) Um nó de árvore que não tem filhos é chamado de nó ________. n) Os três algoritmos de percorrer que mencionamos no texto para árvores de pesquisa binária são ________, ________ e ________.

22.2 Quais são as diferenças entre uma lista vinculada e uma pilha? 22.3 Quais são as diferenças entre uma pilha e uma fila? 22.4 Talvez um título mais apropriado para este capítulo fosse "Estruturas de dados reutilizáveis". Comente como cada uma das seguintes entidades ou conceitos contribuem para a capacidade de reutilização das estruturas de dados: a) classes b) herança c) composição

22.5 Forneça manualmente os percursos na ordem, pré-ordem e pós-ordem da árvore de pesquisa binária da Figura 22.20 49 28

83

18 11

40 19

Figura 22.20  |  Árvore de pesquisa binária com 15 nós.

32

71 44

69

97 72

92

99



Respostas dos exercícios de autorrevisão

717

Respostas dos exercícios de autorrevisão 22.1 22.2 22.3 22.4

22.5

a) referencial. b) pilha. c) predicado. d) primeiro a entrar, primeiro a sair (first in, first out — FIFO). e) link. f) coleta de lixo. g) fila. h) árvore i) último a entrar, primeiro a sair (LIFO). j) binário. k) raiz. l) filho ou subárvore. m) folha. n) na ordem, pré-ordem, pós-ordem. É possível inserir um nó em qualquer lugar de uma lista vinculada e remover um nó de qualquer lugar de uma lista vinculada. Os nós em uma pilha podem ser inseridos somente na parte superior da pilha e removidos somente a partir da parte superior. Uma fila é uma estrutura de dados FIFO que tem referências tanto para sua cabeça como para sua cauda, de modo que os nós podem ser inseridos na cauda e excluídos da cabeça. Uma pilha é uma estrutura de dados LIFO que tem uma única referência ao topo da pilha, no qual a inserção e a exclusão são realizadas. a) As classes nos permitem instanciar quantos objetos de estrutura de dados de certo tipo (isto é, classe) quisermos. b) A herança permite que uma subclasse reutilize as funcionalidades de uma superclasse. Os métodos públicos e protegidos de uma superclasse podem ser acessados por uma subclasse para eliminar a lógica duplicada. c) A composição permite que uma classe reutilize o código armazenando uma referência a uma instância de outra classe em um campo. Os métodos públicos da instância podem ser chamados pelos métodos na classe que contém a referência. O percurso na ordem é

11 18 19 28 32 40 44 49 69 71 72 83 92 97 99

O percurso na pré-ordem é

49 28 18 11 19 40 32 44 83 71 69 72 97 92 99

O percurso na pós-ordem é

11 19 18 32 44 40 28 69 72 71 92 99 97 83 49

Exercícios 22.6 (Concatenando listas) Escreva um programa que concatene dois objetos de lista vinculada de caracteres. A classe ListConcatenate deve incluir um método static concatenate que aceite referências tanto para objetos de lista como para argumentos e concatene a segunda lista com a primeira lista.

22.7 (Inserindo em uma lista ordenada) Escreva um programa que insira 25 inteiros aleatórios de 0 a 100 na ordem em um objeto de lista vinculada. Para esse exercício, você precisará modificar a classe List (Figura 22.3) para manter uma lista ordenada. Nomeie a nova versão da classe SortedList.

22.8 (Mesclando listas ordenadas) Modifique a classe SortedList do Exercício 22.7 para incluir um método merge que possa mesclar a Sort­ edList que ele recebe como um argumento com a SortedList que chama o método. Escreva um aplicativo para testar o método merge.

22.9 (Copiando uma lista de trás para frente) Escreva um método static reverseCopy que recebe uma List como um argumento e retorna uma cópia dessa List com seus elementos invertidos. Teste esse método em um aplicativo.

22.10 (Imprimindo uma frase na ordem inversa usando uma pilha) Escreva um programa que insere uma linha de texto e utiliza uma pilha para exibir as palavras da linha em ordem reversa.

22.11 (Testador de palíndromo) Escreva um programa que utilize uma pilha para determinar se uma string é um palíndromo (isto é, a string é escrita identicamente de trás para frente). O programa deve ignorar espaços e pontuação.

22.12 (Conversor de infixo para pós-fixo) Pilhas são utilizadas por compiladores para ajudar no processo de avaliar expressões e gerar código de

linguagem de máquina. Nesse e no próximo exercício, investigamos como os compiladores avaliam expressões aritméticas que consistem apenas de constantes, operadores e parênteses. Os humanos geralmente escrevem expressões como 3 + 4 e 7 / 9 em que o operador (+ ou / aqui) é escrito entre seus operandos — isso é chamado notação infixa. Os computadores “preferem” notação pós-fixa, na qual o operador é escrito à direita de seus dois operandos. As expressões infixas precedentes apareceriam na notação pós-fixa como 3 4 + e 7 9 /, respectivamente. Para avaliar uma expressão infixa complexa, um compilador primeiro converteria a expressão em notação pós-fixa e avaliaria a versão. Cada um desses algoritmos requer apenas uma única passagem da esquerda para a direita pela expressão. Cada algoritmo utiliza um objeto pilha em suporte de sua operação, mas cada um utiliza a pilha para um propósito diferente. Nesse exercício, você escreverá uma versão Java do algoritmo de conversão de infixo para pós-fixo. No próximo exercício, escreverá uma versão Java do algoritmo de avaliação da expressão pós-fixa. Em um exercício posterior, você descobrirá que o código que escrever nesse exercício pode ajudá-lo a implementar um compilador completo. Escreva a classe InfixToPostfixConverter para converter uma expressão aritmética infixa comum (assuma que uma expressão válida foi inserida) com inteiros de único dígito como (6 + 2) * 5 - 8 / 4

para uma expressão pós-fixa. A versão pós-fixa da expressão infixa precedente é (observe que nenhum parêntese é necessário) 6 2 + 5 * 8 4 / -

O programa deve ler a expressão no StringBuffer infix e utilizar uma das classes de pilha implementadas neste capítulo para ajudar a criar a expressão pós-fixa no StringBuffer postfix. O algoritmo para criar uma expressão pós-fixa é o seguinte: a) Adicionar um parêntese esquerdo '(' à pilha. b) Acrescentar um parêntese direito ')' ao final de infix.

718

Capítulo 22  Estruturas de dados genéricas personalizadas c) Enquanto a pilha não estiver vazia, leia infix da esquerda para a direita e faça o seguinte: Se o caractere atual em infix for um dígito, acrescente-o a postfix. Se o caractere atual em infix for um parêntese esquerdo, adicione-o à pilha. Se o caractere atual em infix for o operador: Remova os operadores (se houver um) na parte superior da pilha enquanto eles tiverem precedência igual ou mais alta que o operador atual e acrescente os operadores removidos a postfix. Adicione o caractere atual a infix na pilha. Se o caractere atual em infix for um parêntese direito: Remova operadores da parte superior da pilha e acrescente-os a postfix até que um parêntese esquerdo esteja na parte superior da pilha. Remova (e descartar) o parêntese esquerdo da pilha. As seguintes operações aritméticas são permitidas em uma expressão: +  adição –  subtração *  multiplicação / divisão ^  exponenciação % resto A pilha deve ser mantida com nós de pilha que cada um contém uma variável de instância e uma referência ao próximo nó de pilha. Alguns métodos que você pode querer fornecer são apresentados a seguir: a) O método convertToPostfix, que converte a expressão infixa em notação pós-fixa. b) O método isOperator, que determina se c é o operador. c) O método precedence, que determina se a precedência do operator1 (da expressão infixa) é menor, igual ou maior que a do operator2 (da pilha). O método retorna true se operator1 tiver precedência mais baixa que operator2. Caso contrário, false é retornado. d) O método peek (que deve ser adicionado à classe de pilha), que retorna o valor superior da pilha sem estourar a pilha.

22.13 (Avaliador de pós-fixo) Escreva a classe PostfixEvaluator que avalia uma expressão pós-fixa como 6 2 + 5 * 8 4 / -

O programa deve ler uma expressão pós-fixa consistindo em dígitos e operadores em um StringBuffer. Utilizando versões modificadas dos métodos de pilha implementados anteriormente neste capítulo, o programa deve varrer a expressão e avaliá-la (supõe que ela seja válida). O algoritmo é como segue: a) Acrescente um parêntese direito ')' no fim da expressão pós-fixa. Quando o caractere do parêntese direito for encontrado, mais nenhum processamento é necessário. b) Enquanto o caractere do parêntese direito não for encontrado, leia a expressão da esquerda para a direta. Se o caractere atual for um dígito, faça o seguinte: Adicione seu valor de inteiro à pilha (o valor de inteiro de um caractere de dígito é seu valor no conjunto de caracteres Unicode menos o valor '0' em Unicode). Caso contrário, se o caractere atual for um operador: Remova os dois elementos superiores da pilha para variáveis x e y. Calcule y operador x. Adicione o resultado do cálculo à pilha. c) Quando o parêntese direito for encontrado na expressão, remova o valor da parte superior da pilha. Esse é o resultado da expressão pós-fixa. [Nota: Em (b) acima (com base na expressão de exemplo no início deste exercício), se o operador for '/', o topo da pilha é 4 e o próximo elemento na pilha é 40, então remova 4 para x, remova 40 para y, avalie 40 / 4 e adicione o resultado, 10, de volta à pilha. Essa nota também se aplica ao operador '-'.] As operações aritméticas permitidas em uma expressão são: +  adição –  subtração *  multiplicação / divisão ^ exponenciação % resto A pilha deve ser mantida com uma das classes de pilha introduzidas neste capítulo. Você pode querer fornecer os seguintes métodos: a) O método evaluatePostfixExpression, que avalia a expressão pós-fixa. b) O método calculate, que avalia a expressão op1 operator op2.

22.14 (Modificação do avaliador de pós-fixo) Modifique o programa avaliador de pós-fixo do Exercício 22.13 de modo que ele possa processar os operandos de inteiros maiores que 9.

22.15 (Simulação de Supermercado) Escreva um programa que simula uma fila de caixa em um supermercado. A fila é um objeto fila. Os clientes

(isto é, os objetos cliente) chegam em intervalos aleatórios inteiros de 1 a 4 minutos. Além disso, cada cliente é atendido em intervalos aleatórios



Exercícios

719

inteiros de 1 a 4 minutos. Obviamente, as taxas precisam ser equilibradas. Se a taxa média de chegada for maior que a taxa média de atendimento, a fila crescerá infinitamente. Mesmo com taxas “equilibradas”, a aleatoriedade ainda pode provocar filas longas. Execute a simulação de supermercado para um dia de 12 horas (720 minutos) utilizando o seguinte algoritmo: a) Escolha um inteiro aleatório entre 1 e 4 para determinar o minuto em que o primeiro cliente chega. b) Na hora da chegada do primeiro cliente, faça o seguinte: Determine o tempo de atendimento do serviço ao cliente (inteiro aleatório de 1 a 4). Comece atendendo o cliente. Agenda a hora de chegada do próximo cliente (inteiro aleatório de 1 a 4 adicionado à hora atual). c) Para cada minuto simulado do dia, considere o seguinte: Se o próximo cliente chegar, prosseguir da seguinte maneira: Expresse isso. O enfileiramento do cliente. Agende a hora de chegada do próximo cliente. Se o atendimento do último cliente tiver sido concluído, faça o seguinte: Expresse isso. Desenfileire o próximo cliente a ser atendido. Determine o tempo de atendimento do cliente (inteiro aleatório de 1 a 4 adicionado à hora atual). Agora execute sua simulação para 720 minutos e responda a cada uma das seguintes perguntas: a) Qual é o número máximo de clientes na fila a qualquer hora? b) Qual é a espera mais longa que qualquer cliente experimenta? c) O que acontece se o intervalo de chegada é alterado de 1 a 4 minutos para 1 a 3 minutos?

22.16 (Permitindo duplicatas em uma árvore binária) Modifique as figuras 22.17–22.18 para permitir que a árvore binária contenha valores duplicados.

22.17 (Processando uma árvore de pesquisa binária de Strings) Escreva um programa com base no programa das figuras 22.17–22.18 que

insira uma linha do texto, tokenize (divida) a frase em palavras separadas, insira as palavras em uma árvore de pesquisa binária e imprima os percursos na ordem, pré-ordem e pós-ordem da árvore.

22.18 (Eliminação de duplicata) Neste capítulo, vimos que a eliminação de duplicata é simples e direta quando se cria uma árvore de pesquisa biná-

ria. Descreva como você realizaria a eliminação de duplicatas ao utilizar apenas um array unidimensional. Compare o desempenho da eliminação de duplicata baseada em array com o desempenho da eliminação de duplicata baseada na pesquisa de árvore binária.

22.19 (Profundidade de uma árvore binária) Modifique as figuras 22.17–22.18 para que a classe Tree forneça um método getDepth que determina quantos níveis estão na árvore. Teste o método em um aplicativo que insere 20 números inteiros aleatórios em uma Tree.

22.20 (Imprimir recursivamente uma lista de trás para frente) Modifique a classe List da Figura 22.3 para incluir o método printList­ Backward que gera recursivamente saída dos itens em um objeto de lista encadeada na ordem inversa. Escreva um programa de teste que crie uma lista de inteiros e imprima a lista em ordem inversa.

22.21 (Pesquisar recursivamente uma lista) Modifique a classe List da Figura 22.3 para incluir o método search que pesquisa recursiva-

mente um valor especificado em um objeto de lista encadeada. O método deve retornar uma referência ao valor se ele for localizada; caso contrário, ele deve retornar null. Utilize seu método em um programa de teste que crie uma lista de inteiros. O programa deve solicitar ao usuário um valor para localizar na lista.

22.22 (Exclusão de árvore binária) Neste exercício, discutimos a exclusão de itens de árvores de pesquisa binária. O algoritmo de exclusão não é tão

simples e direto quanto o algoritmo de inserção. Três casos são encontrados ao excluir-se um item — o item está contido em um nó de folha (isto é, não tem filhos), o item está contido em um nó que tem um filho ou está em um nó que tem dois filhos. Se o item a ser excluído está contido em um nó de folha, o nó é excluído e a referência no nó pai é configurada como nulo. Se o item a ser excluído está contido em um nó com um filho, a referência no nó pai é configurada para referenciar o nó filho e o nó contendo o item de dados é excluído. Isso faz com que o nó filho tome o lugar do nó excluído na árvore. O último caso é o mais difícil. Quando um nó com dois filhos é excluído, outro nó na árvore deve tomar seu lugar. Entretanto, a referência no nó pai simplesmente não pode ser atribuída para referenciar um dos filhos do nó a ser excluído. Na maioria dos casos, a árvore de pesquisa binária resultante não incorporaria seguinte característica das árvores de pesquisa binária (sem valores duplicados): Os valores em qualquer subárvore esquerda são menores que o valor no nó pai e os valores em qualquer subárvore direita são maiores que o valor no nó pai. Qual é o nó utilizado como um nó substituto para manter essa característica? É o nó contendo o maior valor na árvore menor que o valor no nó que está sendo excluído, ou o nó contendo o menor valor na árvore maior que o valor no nó que está sendo excluído? Vamos considerar o nó com o menor valor. Em uma árvore de pesquisa binária, o valor maior que um valor do pai encontra-se na subárvore esquerda do nó pai e seguramente estará contido no nó mais à direita da subárvore. Esse nó é encontrado percorrendo a subárvore esquerda pela direita até que a referência ao filho direito do nó atual seja nula. Agora estamos referenciando o nó substituto que é um nó de folha ou um nó com um filho à sua esquerda. Se o nó substituto for um nó de folha, os passos para realizar a exclusão são os seguintes: a) Armazene a referência ao nó a ser excluído em uma variável de referência temporária. b) Configure a referência no pai do nó sendo excluído para referenciar o nó substituto. c) Configure a referência no pai do nó substituto como null. d) Configure a referência como a subárvore direita no nó substituto para referenciar a subárvore direita do nó a ser excluído. e) Configure a referência como a subárvore esquerda no nó substituto para referenciar a subárvore esquerda do nó a ser excluído.

720

Capítulo 22  Estruturas de dados genéricas personalizadas Os passos de exclusão para um nó substituto com um filho esquerdo são semelhantes àqueles para um nó substituto sem filhos, mas o algoritmo também deve mover o filho para a posição do nó substituto na árvore. Se o nó substituto for um nó com um filho esquerdo, os passos a realizar a exclusão são como segue: a) Armazene a referência ao nó a ser excluído em uma variável de referência temporária. b) Configure a referência no pai do nó sendo excluído para referenciar o nó substituto. c) Configure a referência no pai do nó substituto para referenciar o filho esquerdo do nó substituto. d) Configure a referência como a subárvore direita no nó substituto para referenciar a subárvore direita do nó a ser excluído. e) Configure a referência como a subárvore esquerda no nó substituto para referenciar a subárvore esquerda do nó a ser excluído. Escreva o método deleteNode, que aceita como seu argumento o valor a ser excluído. O método deleteNode deve localizar na árvore o nó que contém o valor a ser excluído e utilizar os algoritmos discutidos aqui para excluir o nó. Se o valor não for localizado na árvore, o método deve exibir uma mensagem informando isso. Modifique o programa das figuras 22.17–22.18 para utilizar esse método. Depois de excluir um item, chame os métodos inorderTraversal, preorderTraversal e postorderTraversal para confirmar que a operação de exclusão foi realizada corretamente.

22.23 (Pesquisa de árvore binária) Modifique a classe Tree da Figura 22.17 para incluir o método contains, que tenta localizar um valor especi-

ficado em um objeto de árvore da pesquisa binária. O método deve aceitar como um argumento uma chave de pesquisa a ser localizada. Se o nó contendo a chave de pesquisa for localizado, o método deve retornar uma referência aos dados desse nó; caso contrário, deve retornar null.

22.24 (Travessia na ordem de nível de árvore binária) O programa das figuras 22.17–22.18 ilustrou os três métodos recursivos de atravessar uma

árvore binária — travessias na ordem, pré-ordem e pós-ordem. Esse exercício apresenta o percurso na ordem de nível de uma árvore binária, em que os valores de nó são impressos nível por nível, iniciando no nível do nó raiz. Os nós em cada nível são impressos da esquerda para a direta. O percurso na ordem de nível não é um algoritmo recursivo. Ela utiliza um objeto fila para controlar a saída dos nós. O algoritmo é como segue: a) Insira o nó raiz na fila. b) Enquanto houver nós esquerdos na fila, faça o seguinte: Obtenha o próximo nó na fila. Imprima o valor do nó. Se a referência ao filho esquerdo do nó não for nula: Insira o nó filho esquerdo na fila. Se a referência ao filho direito do nó não é nula: Insira o nó filho direito na fila. Escreva o método levelOrder para realizar um percurso na ordem de nível de um objeto de árvore binária. Modifique o programa das figuras 22.17–22.18 para utilizar esse método. [Nota: Você também precisará utilizar os métodos de processamento de fila da Figura 22.13 nesse programa.

22.25 (Imprimindo árvores) Modifique a classe Tree da Figura 22.17 para incluir um método outputTree recursivo para exibir um objeto de árvore binária. O método deve gerar saída da árvore linha por linha com o topo da árvore na parte esquerda da tela e a parte inferior da árvore em direção à parte direita da tela. Cada linha é enviada para a saída verticalmente. Por exemplo, a árvore binária ilustrada na Figura 22.20 é enviada para a saída como mostrado na Figura 22.21.

               99           97                92      83                72           71                69 49                44           40                32      28                19           18                11

Figura 22.21  |  Saída de exemplo do método recursivo outputTree. O nó mais à direita da folha aparece na parte superior da saída na coluna mais à direita e o nó raiz aparece à esquerda da saída. Cada coluna inicia cinco espaços à direita da coluna precedente. O método outputTree deve receber um argumento totalSpaces para representar o número de espaços que precedem o valor a ser enviado para a saída. (Essa variável deve iniciar em zero de modo que o nó raiz seja enviado para a saída à esquerda da tela.) O método utiliza uma travessia na ordem modificada para dar a saída à árvore — ele inicia no nó mais à direita na árvore e segue para a esquerda. O algoritmo é como segue:

Enquanto a referência ao nó atual não for nula, faça o seguinte: Chame recursivamente outputTree com a subárvore direita do nó atual e totalSpaces + . Utilize uma estrutura for para contar de 1 a totalSpaces e envie os espaços para saída. Envie para a saída o valor no nó atual. Configure a referência ao nó atual para referenciar a subárvore esquerda do nó atual. Incremente totalSpaces por 5.



Seção especial: construindo seu próprio compilador

721

22.26 (Inserção/Exclusão em qualquer lugar de uma lista vinculada) Nossa classe de lista vinculada permitiu inserções e exclusões no início

e no fim da lista vinculada. Essas capacidades foram convenientes para nós quando utilizamos herança ou composição para produzir uma classe de pilha e uma classe de fila com uma quantidade mínima de código simplesmente reutilizando a classe de lista. As listas vinculadas são normalmente mais gerais que aquelas que fornecemos. Modifique a classe da lista vinculada que desenvolvemos neste capítulo para tratar inserções e exclusões em qualquer lugar da lista.

22.27 (Listas e filas sem referências de fim) Nossa implementação de uma lista vinculada (Figura 22.3) utilizou tanto firstNode como lastNode. O lastNode foi útil para os métodos insertAtBack e removeFromBack da classe List. O método insertAtBack equivale ao método enqueue da classe Queue. Reescreva a classe List de modo que ela não utilize um lastNode. Portanto, quaisquer operações no fim de uma lista devem começar pesquisando no início da lista. Isso afeta nossa implementação da classe Queue (Figura 22.13)?

22.28 (Desempenho da classificação e da pesquisa de árvore binária) Um problema com a classificação de árvore binária é que a ordem em

que os dados são inseridos afeta a forma da árvore — para a mesma coleção de dados, ordens diferentes podem produzir árvores binárias de formas significativamente diferentes. O desempenho dos algoritmos de classificação e pesquisa de árvore binária é sensível à forma da árvore binária. Que forma teria uma árvore binária se seus dados fossem inseridos na ordem crescente? na ordem decrescente? Que forma a árvore deveria ter para alcançar desempenho máximo de pesquisa?

22.29 (Listas indexadas) Como apresentado no texto, as listas vinculadas devem ser pesquisadas sequencialmente. Para listas grandes, isso pode

resultar em desempenho pobre. Uma técnica comum para aprimorar o desempenho de pesquisa de lista é criar e manter um índice para a lista. Um índice é um conjunto de referências para lugares-chave na lista. Por exemplo, um aplicativo que pesquisa uma lista grande de nomes pode aprimorar seu desempenho criando um índice com 26 entradas — uma para cada letra do alfabeto. Uma operação de pesquisa de um sobrenome iniciando com ‘Y’ iria primeiro pesquisar o índice para determinar onde as entradas ‘Y’ iniciaram e, então, “saltaria” na lista nesse ponto e pesquisaria linearmente até que o nome desejado fosse localizado. Isso seria muito mais rápido que pesquisar a lista vinculada desde o início. Utilize a classe List da Figura 22.3 como a base de uma classe IndexedList. Escreva um programa que demonstre a operação de listas indexadas. Certifique-se de incluir os métodos insertInIndexedList, search­ IndexedList e deleteFromIndexedList.

22.30 (Classe de pilha que herda de uma classe List) Na Seção 22.5, criamos uma classe de pilha da classe List com a herança (Figura 22.10)

e a composição (Figura 22.12). Na Seção 22.6, criamos uma classe queue a partir da classe List com composição (Figura 22.13). Crie uma classe queue herdando da classe List. Quais as diferenças entre essa classe e aquela criada com a composição?

Seção especial: construindo seu próprio compilador Nos exercícios 7.35–7.37, introduzimos a Simpletron Machine Language (SML) e implementamos um simulador de computador Simpletron para executar programas SML. Nos exercícios 22.31–22.35, construímos um compilador que converte programas escritos em uma linguagem de programação de alto nível em SML. Esta seção “amarra” o processo de programação inteiro. Você escreverá programas nessa nova linguagem de alto nível, compilará esses programas no compilador que construir e os executará no simulador construído no Exercício 7.36. Você deve se esforçar o máximo possível para implementar seu compilador de uma maneira orientada a objetos. [Nota: Por causa do tamanho das descrições dos exercícios 22.31–22.35, nós as postamos em um documento PDF em inglês localizado em www.deitel.com/books/jhtp8/.]

23

Aja com prudência, a ocasião é o fator mais importante entre todos. — Hesíodo

A pintura é apenas uma ponte ligando a mente do pintor com a do observador. — Eugene Delacroix

A direção em que a educação conduz um homem determinará sua vida futura. — Platão

Applets e Java Web Start

Objetivos Neste capítulo, você aprenderá: 

O que são applets e como eles são utilizados em páginas Web.



A observar alguns das fantásticas capacidades do Java por meio de applets de demonstração do JDK.



A escrever applets simples.



A escrever um documento XHTML (Extensible HyperText Markup Language) simples para carregar um applet em um contêiner de applets e executar o applet.



Os cinco métodos chamados automaticamente por um contêiner de applets durante o ciclo de vida de um applet.



O que é o modelo de segurança de caixa de areia e como ele permite executar de uma maneira segura código baixado.



O que é o Java Web Start e como utilizá-lo para fazer o download, instalar e executar applets fora do navegador Web.

23.1 Introdução

23.1 Introdução

Sumário

23.2 Applets de exemplo fornecidas com o JDK 23.3 Applet Java simples: desenhando uma string

23.6 Modelo de segurança da caixa de areia 23.7 Java Web Start e o Java Network Launch Protocol (JNLP)

23.3.1 Executando WelcomeApplet no appletviewer

23.7.1 Empacotando o applet DrawTest para uso

23.3.2 Executando um applet em um navegador da Web

com Java Web Start

23.4 Métodos de ciclo de vida de applet 23.5 Inicializando uma variável de instância com o método init

723

23.7.2 Documento JNLP para o applet DrawTest

23.8 Conclusão

Resumo | Terminologia | Exercício de autorrevisão | Respostas do exercício de autorrevisão | Exercícios

23.1 Introdução [Nota: Este capítulo é intencionalmente pequeno e simples para leitores que desejam estudar applets depois de ler apenas os primeiros capítulos do livro. Apresentamos applets mais complexos no Capítulo 24, Multimídia: applets e aplicativos, e no Capítulo 27, Redes. Além disso, os exemplos neste capítulo exigem um pouco de conhecimento de XHTML para criar uma página Web que carrega um applet. Com cada exemplo fornecemos documentos XHTML de exemplo que você pode modificar para seus próprios objetivos. Para aprender mais sobre a XHTML, acesse o link XMTML Tutorials no nosso XHTML Resource Center (www.deitel.com/xhtml/).] Este capítulo introduz applets — programas Java que são tipicamente incorporados a documentos XHTML (Extensible HyperText Markup Language) — também chamados páginas Web. Quando um navegador Web compatível com Java carrega uma página Web que contém um applet, o applet é baixado no navegador e executa.

Contêineres de applet O aplicativo no qual um applet executa é conhecido como o contêiner de applets. É responsabilidade do contêiner de applets carregar a(s) classe(s) do applet, criar uma instância do applet e gerenciar seu ciclo de vida (que discutimos mais detalhadamente na Seção 23.4). O Java Development Kit ( JDK) inclui um chamado appletviewer para testar applets à medida que você os desenvolve e antes de incorporá-los a páginas Web. Demonstramos applets que utilizam tanto o appletviewer como navegadores Web, que executam applets Java via o Plug-in Java. Alguns navegadores não vêm com o plug-in por padrão. Você pode visitar java.com para verificar se seu navegador está pronto para executar applets Java. Se não estiverem, clique no botão Free Java Download para instalar o Java no seu navegador. Muitos navegadores populares são utilizados. Testamos nossos applets no Firefox 3 da Mozilla, Internet Explorer da Microsoft 7, Chrome do Google, Opera 9 e Safari da Apple 3. Java Web Start e o Java Network Launch Protocol (JNLP) Este capítulo termina com uma introdução ao Java Web Start e ao Java Network Launch Protocol ( JNLP). Juntos, eles permitem empacotar seus applets e aplicativos para que possam ser instalados na área de trabalho do usuário. Como veremos no Capítulo 24, o Java Web Start também permite dar ao usuário controle sobre se um applet ou aplicativo baixado da Web pode ter acesso limitado aos recursos no sistema de arquivos local. Por exemplo, se você criar em Java um programa editor de textos que pode ser baixado, é provável que os usuários talvez queiram armazenar os documentos nos seus próprios computadores.

23.2 Applets de exemplo fornecidas com o JDK Antes de discutirmos nossos applets, vamos considerar vários applets de exemplo fornecidos no JDK. Cada applet de exemplo vem com o código-fonte. Alguns programadores acham interessante ler esse código-fonte para aprender os fantásticos recursos Java. Os programas de demonstração fornecidos no JDK estão localizados em um diretório chamado demo. Para o Windows, a localização padrão do diretório demo do JDK 6.0 é C:\Program Files\Java\jdk1.6.0_##\demo

onde _## representa o número da atualização do JDK. No UNIX/Linux, a localização padrão é o diretório em que você instala o JDK seguido por jdk1.6.0_##/demo — por exemplo, /usr/local/jdk1.6.0_##/demo

724

Capítulo 23  Applets e Java Web Start

Para outras plataformas, haverá uma estrutura semelhante de diretório (ou pasta). Este capítulo supõe que o JDK esteja instalado em C:\Program Files \Java\jdk1.6.0_11 no Windows ou no diretório inicial em ~/jdk1.6.0_11 no UNIX/Linux. Talvez seja necessário

atualizar as localizações especificadas aqui para que elas correspondam ao seu diretório de instalação e unidade de disco selecionados, ou uma versão diferente do JDK. Se estiver utilizando uma ferramenta de desenvolvimento Java que não é distribuída com as demos do Sun Java, baixe o JDK atual em java.sun.com/javase/downloads/index.jsp. Usuários do Mac OS X devem acessar developer.apple.com/java para obter informações sobre o Java no Mac, ou utilizar um software de virtualização para executar as versões do Windows ou Linux do Java em uma máquina virtual.

Visão geral dos applets de demonstração Abra uma janela de comando e utilize o comando cd para mudar para o diretório demo do JDK. O diretório demo contém vários subdiretórios. Você pode listá-los emitindo o comando dir no Windows ou o comando ls no UNIX/Linux/Mac OS X. Discutimos programas de exemplo nos subdiretórios applets e jfc. O diretório applets contém applets de demonstração. O diretório jfc ( Java Foundation Classes) contém applets e aplicativos que demonstram os recursos gráficos e as capacidades GUI do Java. Mude para o diretório applets e liste seu conteúdo para ver os nomes dos diretórios para os applets de demonstração. A Figura 23.1 fornece uma breve descrição de cada um. Se seu navegador suportar Java, você poderá testar um applet abrindo o respectivo documento XHTML no diretório do applet. Demonstraremos três desses applets utilizando o comando appletviewer em uma janela de comando. Exemplo

Descrição

Animator

Realiza uma de quatro animações separadas.

ArcTest

Demonstra como desenhar arcos. Você pode interagir com o applet para alterar atributos do arco que é exibido.

BarChart

Desenha um gráfico de barras simples.

Blink

Exibe texto intermitente em diferentes cores.

CardTest

Demonstra vários componentes GUI e layouts.

Clock

Desenha um relógio com ponteiros giratórios, a data e a hora atuais. O relógio é atualizado uma vez por segundo.

DitherTest

Demonstra desenhos com uma técnica gráfica conhecida como pontilhamento que permite uma transformação gradual de uma cor para outra.

DrawTest

Permite ao usuário arrastar o mouse para desenhar linhas e pontos em diferentes cores.

Fractal

Desenha um fractal. Os fractais em geral requerem cálculos complexos para determinar como eles são exibidos. Discutimos fractais na Seção 18.8.

GraphicsTest

Desenha formas para ilustrar as capacidades gráficas.

GraphLayout

Desenha um gráfico que consiste em muitos nós (representados como retângulos) conectados por linhas. Arraste um nó para ver os outros nós no gráfico se ajustarem na tela e para demonstrar interações gráficas complexas.

ImageMap

Demonstra uma imagem com pontos ativos. Posicionar o ponteiro do mouse sobre certas áreas da imagem destaca essa área e uma mensagem é exibida no canto esquerdo inferior da janela do contêiner de applets. Posicione sobre a boca na imagem para ouvir o applet dizer “hi”.

JumpingBox

Move um retângulo aleatoriamente pela tela. Tente pegá-lo clicando nele com o mouse!

MoleculeViewer

Apresenta uma visualização tridimensional de várias moléculas químicas. Arraste o mouse para ver a molécula de ângulos diferentes.

NervousText

Desenha texto que se movimenta pela applet.

SimpleGraph

Desenha uma curva complexa.

SortDemo

Compara três técnicas de classificação. A classificação (descrita no Capítulo 19) organiza as informações em ordem — como palavras em ordem alfabética. Ao executar esse exemplo com o appletviewer, três janelas aparecem. Ao executá-lo em um navegador, as três demos aparecem lado a lado. Clique em cada demo para iniciar a classificação. Observe que todas as classificações operam em diferentes velocidades.



23.2  Applets de exemplo fornecidas com o JDK

Exemplo

Descrição

SpreadSheet

Demonstra uma planilha simples de linhas e colunas.

TicTacToe

Permite ao usuário jogar o jogo da velha contra o computador.

WireFrame

Desenha uma forma tridimensional como um aramado. Arraste o mouse para ver a forma de ângulos diferentes.

725

Figura 23.1  |  Exemplos do diretório applets.

Applet TicTacToe Esse applet de demonstração TicTacToe permite jogar o jogo da velha contra o computador. Mude para o subdiretório TicTacToe, no qual você localizará o arquivo example1.html que carrega o applet. Na janela de comando, digite o comando appletviewer example1.html

e pressione Enter. Isso executa o contêiner de applets appletviewer, o qual carrega o documento XHTML example1.html especificado como seu argumento de linha de comando. O appletviewer determina no documento qual applet será carregado e executado. A Figura 23.2 mostra as várias capturas de tela do jogo Tic-Tac-Toe (jogo da velha) com esse applet. Você pode abrir o documento XHTML no navegador para executar o applet nele.

Figura 23.2  |  Execução de exemplo do applet TicTacToe.

Você é o jogador X. Para interagir com o applet, aponte o mouse para o quadrado em que você quer colocar um X e clique com o botão do mouse. O applet emite um som e coloca um X no quadrado, se ele estiver aberto. Se o quadrado estiver ocupado, esse é um movimento inválido e o applet emite um som diferente indicando que você não fez o movimento especificado. Depois de fazer um movimento válido, o applet responde fazendo seu próprio movimento. Para reproduzir novamente, clique no menu Applet do appletviewer e selecione o item Reload no menu (Figura 23.3), ou clique no applet novamente quando o jogo terminar. Para terminar o appletviewer, clique no menu Applet do appletviewer e selecione o item de menu Quit.

Selecione Reload para executar o applet novamente

Selecione Quit para terminar o appletviewer

Figura 23.3  |  Menu Applet no appletviewer.

.

726

Capítulo 23  Applets e Java Web Start

Applet DrawTest O applet DrawTest permite desenhar linhas e pontos em cores diferentes. Na janela de comando, mude para o diretório applets e então para subdiretório DrawTest. Você pode mover-se para cima na árvore de diretórios incrementalmente em direção a demo emitindo o comando “cd ..” na janela de comando. O diretório DrawTest contém o documento example1.html que é utilizado para executar o applet. Na janela de comando, digite o comando appletviewer example1.html

e pressione Enter. O appletviewer carrega example1.html, determina a partir do documento o applet que deve carregar e o executa. A Figura 23.4 mostra uma captura de tela depois que algumas linhas e pontos foram desenhados. Por padrão, o applet permite desenhar linhas pretas arrastando o mouse pelo applet. Ao arrastar o mouse, observe que o ponto de partida da linha sempre permanece no mesmo lugar e sua extremidade segue o ponteiro do mouse na applet. A linha não será permanente até você liberar o botão do mouse.

Arraste o mouse para a área branca a fim de desenhar

Selecione a a cor do desenho clicando em um dos botões de opções.

Selecione Lines ou Points na caixa de combinação para para especificar o que será arrastado quando arrastar o mouse.

Figura 23.4  |  Execução de exemplo do applet DrawTest.

Selecione uma cor clicando em um dos botões de opção na parte inferior do applet. Você pode selecionar vermelho, verde, azul, cor-de-rosa, alaranjado e preto. Altere a forma para desenhar de Lines para Points selecionando Points na caixa de combinação. Para iniciar um novo desenho, selecione Reload do menu Applet do appletviewer.

Applet Jav2D O applet Java2D demonstra muitos recursos da Java 2D API (que introduzimos no Capítulo 15). Mude para o diretório jfc no diretório demo do JDK, e depois para o diretório Java2D. Na janela de comando, digite o comando appletviewer Java2Demo.html

e pressione Enter. O appletviewer carrega Java2Demo.html, determina a partir do documento o applet que deve carregar e o executa. A Figura 23.5 mostra uma captura de tela de uma das muitas demonstrações desse applet das capacidades gráficas bidimensionais do Java. Na parte superior do applet estão guias que parecem pastas suspensas em um armário de arquivos. Essa demonstração fornece 12 guias, cada uma delas demonstrando os recursos da Java 2D API. Para mudar para uma parte diferente da demo, simplesmente clique em uma guia diferente. Além disso, tente alterar as opções no canto superior direito do applet. Algumas delas afetam a velocidade com que o applet desenha as imagens gráficas. Por exemplo, clique na caixa de seleção à esquerda da palavra Anti Aliasing para desativar a suavização, ou o antialiasing (uma técnica gráfica para produzir imagens mais suaves na tela em que as bordas da imagem são desfocadas). Formas que não são suavizadas (antialiased) são menos complexas de desenhar. Consequentemente, quando o recurso antialiasing está desativado, a velocidade de animação aumenta para as formas animadas na parte inferior da demo (Figura 23.5).

23.3 Applet Java simples: desenhando uma string Clique em uma guia para selecionar uma demonstração gráfica bidimensional.

727

Tente alterar as opções para ver seu efeito na demonstração.

Figura 23.5 | Execução de exemplo do applet Java2D.

23.3 Applet Java simples: desenhando uma string Cada applet Java é uma interface gráfica com o usuário em que você pode colocar componentes GUI utilizando as técnicas apresentadas no Capítulo 14 ou desenhar utilizando as técnicas demonstradas no Capítulo 15. Neste capítulo, demonstraremos como desenhar em um applet. Os exemplos nos capítulos 24 e 27 demonstram como criar uma interface gráfica com o usuário do applet. Agora vamos criar nosso próprio applet. Iniciamos com um applet simples (Figura 23.6) que desenha "Welcome to Java Program­ ming!" no applet. Mostramos esse applet em execução em dois contêineres de applet — o appletviewer e o navegador Web Mozilla Firefox. No fim desta seção, você aprenderá a executar o applet em um navegador da Web. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

// Figura 23.6: WelcomeApplet.java // Applet que desenha uma String. import TML.awt.Graphics; // o programa utiliza a classe Graphics import javax.swing.JApplet; // o programa utiliza a classe JApplet public class WelcomeApplet extends JApplet { // desenha texto sobre o fundo do applet public void paint( Graphics g ) { // chama a versão da superclasse do método paint super.paint( g ); // desenha uma String nas coordenadas x 25 e y 25 g.drawString( "Welcome to Java Programming!", 25, 25 ); } // fim do método paint } // fim da classe WelcomeApplet

728

Capítulo 23  Applets e Java Web Start WelcomeApplet executando no appletviewer

eixo x eixo y O canto superior de desenho é a área de localização (0, 0). A área de desenho estende-se da parte inferior do menu do Applet para acima da barra de status. A coordenada x aumenta da esquerda para a direita. A coordenada y aumenta de cima para baixo.

Menu do Applet A barra de status simula o que seria exibido na barra de status do carregador à medida que o applet carrega e começa a executar. Coordenada de pixels (25, 25) em que a string é exibida WelcomeApplet executando no Mozilla Firefox

Canto superior esquerdo da área de desenho Coordenada de pixels (25, 25)

Figura 23.6  |  Applet que desenha uma String.

Criando a classe Applet A linha 3 importa a classe Graphics para permitir ao applet desenhar imagens gráficas como linhas, retângulos, ovais e strings de caracteres. A classe JApplet (importada na linha 4) do pacote javax.swing é utilizada para criar applets. Como com aplicativos, cada applet Java contém pelo menos uma declaração de classe public. Um contêiner de applets só pode criar objetos de classes que são public e JApplet estendidas (ou sua superclasse Applet). Por essa razão, a classe WelcomeApplet (linhas 6–17) estende a classe JApplet. Um contêiner de applets espera que cada applet Java tenha os métodos init, start, paint, stop e destroy, cada um dos quais é declarado na classe JApplet. Cada nova classe de applet que você cria herda as implementações padrão desses métodos da classe JApplet. Esses métodos podem ser sobrescritos (redefinidos) para realizar tarefas específicas ao seu applet. A Seção 23.4 discute cada um desses métodos em mais detalhes. Quando um contêiner de applets carrega a classe WelcomeApplet, o contêiner cria um objeto WelcomeApplet, então chama seus métodos init, start e paint na sequência. Se você não declarar esses métodos no seu applet, o contêiner de applets chamará as versões herdadas. Os métodos init e start da superclasse têm corpos vazios, portanto eles não realizam nenhuma tarefa. O método paint da superclasse não desenha nada no applet. Talvez você pergunte por que é necessário herdar os métodos init, start e paint se suas implementações padrão não realizam tarefas. Alguns applets não utilizam todos esses três métodos. Entretanto, o contêiner de applets não sabe disso. Portanto, ele espera que cada applet tenha esses métodos, assim pode fornecer uma sequência inicial consistente. Isso é semelhante ao fato de os aplicativos sempre começarem a executar com main. Herdar as versões “padrão” desses métodos garante que o contêiner de applets pode executar cada applet uniformemente. Além disso, herdar implementações padrão desses métodos permite que o programador se concentre apenas na definição dos métodos requeridos por um applet particular. Sobrescrevendo o método paint para desenhar Para permitir que nosso applet desenhe, a classe WelcomeApplet sobrescreve o método paint (linhas 9–16) colocando instruções no corpo do paint que desenha uma mensagem na tela. O método paint recebe um parâmetro do tipo Graphics (chamado g por convenção), que é utilizado para desenhar imagens gráficas no applet. Você não chama o método paint explicitamente em um applet. Em vez disso, o contêiner de applets chama paint para dizer ao applet quando desenhar e o contêiner de applet é responsável por passar um objeto de Graphics como um argumento. A linha 12 chama a versão da superclasse do método paint que foi herdado de JApplet. Essa deve ser a primeira instrução no método paint de cada applet. Omiti-lo pode causar erros sutis de desenho em applets que combinam componentes de desenho e GUIs. A linha 15 utiliza o método Graphics drawString para desenhar Welcome to Java Programming! No applet. O método recebe como argumentos a String a desenhar e as coordenadas x e y em que o canto inferior esquerdo da String deve aparecer na área de desenho. Quando a linha 15 executa, ela desenha a String sobre o applet nas coordenadas 25 e 25.



23.3  Applet Java simples: desenhando uma string

729

23.3.1  Executando WelcomeApplet no appletviewer Depois de criar a classe WelcomeApplet e salvá-la no arquivo WelcomeApplet.java, abra uma janela de comando, mude para o diretório em que você salvou a declaração da classe de applet e compile a classe WelcomeApplet. Lembre-se de que applets são incorporados a páginas Web para execução em um contêiner de applets (appletviewer ou um navegador). Para poder executar o applet, antes você deve criar um documento XHTML que especifica qual applet executar do contêiner de applets. Em geral, um documento XHTML termina com uma extensão de arquivo .html ou .htm. A Figura 23.7 mostra um documento XHTML simples — WelcomeApplet.html — que carrega o applet definido na Figura 23.6 em um contêiner de applets. A maioria dos elementos XHTML é delimitada por tags — por exemplo, as linhas 1 e 6 delimitam o início e o fim do documento XHTML, respectivamente. Cada tag é colocado entre colchetes angulares (< e >). As linhas 2–5 especificam o elemento do elemento body do documento — isso representa os elementos que serão exibidos na página Web. As linhas especificam um elemento applet que instrui o contêiner de applets a carregar um applet específico e define o tamanho de sua área de exibição (sua largura e altura em pixels) no contêiner de applets.

1 2 3 4 5 6





Figura 23.7  |  WelcomeApplet.html carrega WelcomeApplet (Figura 23.6) em um contêiner de applets.

O applet e seu documento XHTML correspondente normalmente são armazenados no mesmo diretório do disco. Em geral, um navegador carrega um documento XHTML de um computador (além do seu) conectado à Internet. Entretanto, documentos XHTML também podem residir no seu computador (como visto na Seção 23.2). Quando um contêiner de applets encontra um elemento applet em um documento XHTML, ele carrega o arquivo (ou arquivos) .class do applet a partir da mesma localização que contém o documento XHTML. O elemento applet tem vários atributos. O primeiro atributo na linha 3, code = "WelcomeApplet.class", indica que o arquivo WelcomeApplet.class contém a classe compilada de applet. O segundo e terceiro atributos na linha 3 indicam a largura, ou width, (300) e a altura, ou height, (45) do applet em pixels. O tag (linha 4) termina o elemento applet que iniciou na linha 2. O tag (linha 6) termina o documento XHTML.

Observações sobre a aparência e comportamento 23.1 Geralmente, um applet deve ter menos de 1.024 pixels de largura e 768 pixels de altura — dimensões suportadas pela maioria das telas de computador.

Erro comum de programação 23.1 Esquecer o tag de fechamento impede que o applet seja executado em alguns contêineres de applets. O appletviewer termina sem indicar um erro. Alguns navegadores da Web simplesmente ignoram o elemento applet incompleto.

Dica de prevenção de erro 23.1 Se você receber uma mensagem de erro MissingResourceException ao carregar um applet no appletviewer ou em um navegador, verifique cuidadosamente erros de sintaxe no tag do documento XHTML, como vírgulas (,) entre os atributos.

O appletviewer só entende os tags XHTML e e ignora todos os outros tags do documento. O appletviewer é um lugar ideal para testar um applet e assegurar que ele execute adequadamente. Depois que a execução do applet for verificada, você pode adicionar os tags XHTML a uma página Web para que outras pessoas possam visualizar nos seus navegadores da Web. Para executar WelcomeApplet no appletviewer, abra uma janela de comando, mude para o diretório contendo seu applet e o documento XHTML e, então, digite appletviewer WelcomeApplet.html

Dica de prevenção de erro 23.2 Teste seus applets no appletviewer antes de executá-los em um navegador da Web. Frequentemente, os navegadores salvam uma cópia de um applet na memória até que todas as janelas do navegador tenham sido fechadas. Se alterar um applet, recompile e então recarregue-o no seu navegador. O navegador talvez continue a executar a versão original do applet.

730

Capítulo 23

Applets e Java Web Start

Dica de prevenção de erro 23.3 Teste seus applets em cada navegador da Web em que eles serão executados para assegurar que eles operam corretamente.

23.3.2 Executando um applet em um navegador da Web As execuções do programa de exemplo na Figura 23.6 demonstram WelcomeApplet executando no appletviewer e no navegador da Web Mozilla Firefox. Para executar um applet no Firefox, siga os passos a seguir: 1. Selecione Open File no menu File. 2. Na caixa de diálogo que aparece, localize o diretório contendo o documento XHTML do applet que você deseja executar. 3. Selecione o documento XHTML. 4. Clique no botão Open. Os passos para executar applets em outros navegadores da Web são semelhantes. Na maioria dos navegadores, você pode simplesmente pressionar Ctrl + O para abrir uma caixa de diálogo que permite selecionar um documento XHTML a partir do disco rígido local.

Dica de prevenção de erro 23.4 Se seu applet executar no appletviewer, mas não executar no seu navegador da Web, o Java não poderá ser instalado e configurado no seu navegador. Nesse caso, visite o site Web Java.com e clique no botão Free Java Download para instalar o Java no seu navegador.

23.4 Métodos de ciclo de vida de applet Agora que você criou um applet, vamos considerar os cinco métodos de applet que são chamados pelo contêiner de applets a partir do momento em que o applet é carregado no navegador até que ele seja encerrado pelo navegador. Esses métodos correspondem a vários aspectos do ciclo de vida de um applet. A Figura 23.8 lista esses métodos, que são herdados para suas classes de applet da classe JApplet. A tabela especifica quando cada método é chamado e explica o seu propósito. Além do método paint, os corpos desses métodos permanecem vazios por padrão. Se quiser declarar qualquer um deles nos seus applets para que sejam chamados pelo contêiner de applets, você deverá utilizar os cabeçalhos de método mostrados na Figura 23.8. Método

Descrição

public void init()

Chamado uma vez pelo contêiner de applets quando um applet é carregado para execução. Esse método inicializa um applet. Ações típicas realizadas aqui são: inicializar campos, criar componentes GUI, carregar sons para tocar, carregar e exibir imagens (ver Capítulo 24) e criar threads (ver Capítulo 26). public void start()

Chamado pelo contêiner de applets depois de o método init completar a execução. Além disso, se o usuário for para outro site da Web e depois retornar à página XHTML do applet, o método start é chamado novamente. O método realiza todas as tarefas que devem ser completadas quando o applet é carregado pela primeira vez e isso deve ser feito todas as vezes que a página XHTML do applet for revisitada. As ações realizadas aqui poderiam incluir: iniciar uma animação ou iniciar outros threads da execução. public void paint( Graphics g )

Chamado pelo contêiner de applets depois dos métodos init e start. O método paint também é chamado quando o applet precisa ser repintado. Por exemplo, se o usuário encobrir o applet com outra janela aberta na tela e mais tarde exibi-lo, o método paint será chamado. Ações típicas realizadas aqui envolvem desenhar com o objeto g de Graphics que é passado para o método paint pelo contêiner de applets. public void stop()

Esse método é chamado pelo contêiner de applets quando o usuário sai da página Web do applet indo para outra página Web. Como é possível que o usuário retorne à página Web contendo o applet, o método stop realiza as tarefas que seriam necessárias para suspender a execução do applet, de modo que o applet não utilize o tempo de processamento do computador quando não é exibido na tela. Ações típicas realizadas aqui interromperiam a execução de animações e threads.

23.5 Inicializando uma variável de instância com o método init

Método

731

Descrição

public void destroy()

Esse método é chamado pelo contêiner de applets quando o applet é removido da memória. Isso ocorre quando o usuário encerra a sessão de navegação fechando todas as janelas do navegador e também pode ocorrer sem que o navegador saiba quando o usuário tiver ido para outras páginas Web. O método realiza quaisquer tarefas necessárias para limpar recursos alocados ao applet. Figura 23.8 | Métodos de ciclo de vida do JApplet chamados por um contêiner de applets durante a execução de um applet.

Erro comum de programação 23.2 Declarar os métodos init, start, paint, stop ou destroy com cabeçalhos de método que diferem daqueles mostrados na Figura 23.8 resulta em métodos que não serão chamados pelo contêiner de applets. O código especificado nas suas versões dos métodos não os executará. A notação @Override pode ser aplicada a cada método para evitar esse problema.

23.5 Inicializando uma variável de instância com o método init Nosso próximo applet (Figura 23.9) calcula a soma dos dois valores inseridos em diálogos de entrada pelo usuário e exibe o resultado desenhando uma String dentro de um retângulo no applet. A soma é armazenada em uma variável de instância da classe Addition­ Applet, de modo que possa ser utilizada no método init e no método paint. O documento XHTML que você pode utilizar para carregar esse applet em um contêiner de applets (isto é, o appletviewer ou um navegador Web) é mostrado na Figura 23.10. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

// Figura 23.9: AdditionApplet.java // Applet que adiciona dois valores double inseridos via caixas de diálogo de entrada. import java.awt.Graphics; // o programa utiliza a classe Graphics import javax.swing.JApplet; // o programa utiliza a classe JApplet import javax.swing.JOptionPane; // o programa utiliza a classe JOptionPane public class AdditionApplet extends JApplet { private double sum; // soma dos valores inseridos pelo usuário // inicializa um applet obtendo os valores inseridos pelo usuário public void init() { // obtém do usuário o primeiro número String firstNumber = JOptionPane.showInputDialog( "Enter first floating­point value" ); // obtém do usuário o segundo número String secondNumber = JOptionPane.showInputDialog( "Enter second floating­point value" ); // converte os números de tipo String para tipo duplo double number1 = Double.parseDouble( firstNumber ); double number2 = Double.parseDouble( secondNumber ); sum = number1 + number2; // soma os números } // fim do método init // desenha os resultados em um retângulo sobre o fundo do applet public void paint( Graphics g ) { super.paint( g ); // chama a versão da superclasse do método paint // desenha um retângulo iniciando em (15, 10) que tem 270 // pixels de largura e 20 pixels de altura g.drawRect( 15, 10, 270, 20 ); // desenha os resultados como uma String em (25, 25)

732 39 40 41

Capítulo 23

Applets e Java Web Start

g.drawString( "The sum is " + sum, 25, 25 ); } // fim do método paint } // fim da classe AdditionApplet

Figura 23.9 | Applet que adiciona dois valores double inseridos via caixas de diálogo de entrada.

1 2 3 4 5 6





Figura 23.10 |

AdditionApplet.html

carrega a classe AdditionApplet da Figura 23.9 em um contêiner de applets.

O applet solicita que o usuário insira dois números de ponto flutuante. Na Figura 23.9, a linha 9 declara a variável de instância sum do tipo double. O applet contém dois métodos — init (linhas 12–27) e paint (linhas 30–40). Quando um contêiner de applets carrega esse applet, o contêiner cria uma instância da classe AdditionApplet e chama seu método init — isso ocorre apenas uma vez durante a execução de um applet. O método init normalmente inicializa os campos do applet (se precisarem ser inicializados com valores além dos valores padrão) e realiza outras tarefas que devem ocorrer apenas uma vez quando o applet inicia a execução. A primeira linha do init sempre aparece como mostrado na linha 12, a qual indica que init é um método public que não recebe nenhum argumento e não retorna nenhuma informação depois de completar. As linhas 15–24 declaram as variáveis para armazenar os valores inseridos pelo usuário, obtém a entrada do usuário e converte as Strings inseridas pelo usuário em valores double. A linha 26 adiciona os valores armazenados nas variáveis number1 e number2, e atribui o resultado à variável de instância sum. Nesse ponto, o método init do applet retorna o controle do programa ao contêiner de applets, que então chama o método start do applet. Não declaramos start nesse applet, portanto aquele herdado da classe JApplet é chamado aqui. Em seguida, o contêiner de applets chama o método paint do applet, que desenha um retângulo (linha 36) em que o resultado da adição aparecerá. A linha 39 chama o método drawString do objeto Graphics para exibir os resultados. A instrução concatena o valor da variável de instância sum da String "The sum is " e exibe a String concatenada.

Observação de engenharia de software 23.1 As únicas instruções que devem ser colocadas no método init do applet são aquelas que devem ser executadas somente uma vez quando o applet é inicializado.

23.6 Modelo de segurança da caixa de areia Por razões de segurança, geralmente é considerado perigoso permitir que applets ou qualquer outro programa que você executa a partir de um navegador Web acessem seu computador local. Desse modo, você deve decidir se confia na fonte. Por exemplo, se optar por fazer o download de uma nova versão do navegador Web Firefox a partir do site do Mozilla, firefox.com, você deverá decidir se confia no site do Mozilla. Afinal de contas, o programa instalador irá modificar seu sistema e inserir os arquivos para executar o Firefox no seu computador. Depois de instalado, o Firefox poderá acessar arquivos e outros recursos locais. A maioria das coisas que você faz via navegadores Web — como compras, navegação na Web e download de softwares — exige que você confie nos sites que você visita e também nas organizações que mantêm esses sites. Se você não for cuidadoso, um programa mal-intencionado baixado poderá ganhar controle sobre o computador,

23.7 Java Web Start e o Java Network Launch Protocol (JNLP)

733

acessar informações pessoais armazenadas aí, corromper seus dados e possivelmente até ser utilizado para atacar outros computadores na Internet — assim como muitas vezes acontece com vírus de computador hoje em dia.

Evitando applets mal-intencionados Applets costumam ser descarregados da Internet. O que aconteceria se você fizesse o download de um applet mal-intencionado? Considere o fato de que um navegador baixa e executa um applet Java automaticamente — o usuário não é solicitado a dar sua aprovação. Na verdade, um applet é baixado sem o conhecimento do usuário — é apenas outro elemento da página Web que o usuário acessa. Os projetistas do Java consideraram essa questão a fundo, uma vez que o Java foi concebido para uso em ambientes em rede. Para combater códigos mal-intencionados, a plataforma Java utiliza o chamado modelo de segurança de caixa de areia que fornece um mecanismo para executar código descarregado de uma maneira segura. Esse código executa na “caixa de areia” e não tem permissão de “rodar fora da caixa de areia”. Por padrão, o código descarregado não pode acessar recursos do sistema local, e um applet só pode interagir com o servidor a partir do qual o applet foi descarregado. Applets assinados digitalmente Infelizmente, a execução em uma caixa de areia dificulta para os applets realizar tarefas úteis. É possível, porém, para uma organização que deseja criar applets com acesso ao sistema local obter um certificado de segurança (também chamado certificado digital) de uma entre várias autoridades certificadoras (ver en.wikipedia.org/wiki/Certificate_Authority para uma lista de autoridades e informações adicionais sobre autoridades certificadoras). A organização então pode utilizar as ferramentas fornecidas com o JDK para “assinar digitalmente” um applet que requer acesso aos recursos do sistema local. Quando um usuário baixa um applet digitalmente assinado, uma caixa de diálogo pergunta se o usuário confia na origem do applet. Nessa caixa de diálogo, o usuário pode visualizar o certificado de segurança da organização e ver qual autoridade certificadora o emitiu. Se o usuário indicar que ele confia na fonte, só então o applet será capaz de acessar os recursos do computador local. Na próxima seção, introduzimos o Java Web Start e o Java Network Launch Protocol ( JNLP). Essas tecnologias permitem a applets ou aplicativos interagir com o usuário para solicitar acesso a recursos específicos do sistema local. Com a permissão do usuário, isso permite a programadores Java estender a caixa de areia, mas ele não dá acesso aos programas a todos os recursos locais do usuário — assim os princípios da caixa de areia ainda estão em efeito. Por exemplo, seria útil para um programa de editor de textos descarregável armazenar os arquivos do usuário em uma pasta no computador do usuário. O editor de textos pode solicitar que o usuário dê permissão para fazer isso. Se o usuário der permissão de acesso a um diretório específico no disco, o programa então só poderá acessar esse diretório local e seus subdiretórios. Para obter informações adicionais sobre applets digitalmente assinados, visite java.sun.com/developer/onlineTraining/Pro­ gramming/JDCBook/signed.html. Para informações sobre o modelo de segurança da Java Platform, visite java.sun.com/javase/6/ docs/technotes/guides/security/.

23.7 Java Web Start e o Java Network Launch Protocol (JNLP) O Java Web Start é uma framework para executar applets e aplicativos descarregados fora do navegador. Tipicamente, esses programas são armazenados em um servidor Web para acesso via Internet, mas eles também podem ser armazenados na rede de uma organização para distribuição interna, ou até mesmo em CDs, DVDs ou outra mídia. Como você aprenderá no Capítulo 24, o Java Web Start permite que você pergunte ao usuário se um programa descarregado pode ter acesso aos recursos do computador do usuário.

Recursos do Java Web Start Alguns recursos-chave do Java Web Start incluem: • Integração com a área de trabalho: os usuários podem inicializar applets e aplicativos robustos clicando em um hiperlink em uma página Web e podem instalar rápida e facilmente os programas nos computadores. O Java Web Start pode ser configurado para perguntar ao usuário se um ícone na área de trabalho deve ser criado para que o usuário possa inicializar o programa diretamente da área de trabalho. Programas descarregados também podem ter um “modo off-line” para a execução se o computador não estiver conectado à Internet. • Atualização automática: ao executar um programa via Java Web Start, o programa é descarregado e armazenado no cache (área de armazenamento temporário) no computador do usuário. Na próxima vez que o usuário executar esse programa, o Java Web Start o inicializa a partir do cache. Se o programa foi atualizado desde a última vez em que foi carregado, o Java Web Start pode baixar automaticamente as atualizações para que um usuário sempre tenha a versão mais recente. Isso torna simples e contínua a instalação e atualização do software para o usuário. • Applets arrastáveis: esse é um novo recurso do Java SE 6 Update 10. Com uma pequena modificação no elemento applet que invoca um applet a partir de um documento XHTML, você pode permitir que os usuários executem um applet em uma janela própria mantendo pressionada a tecla Alt e arrastando o applet para fora do navegador Web. O applet continua a executar mesmo depois que o navegador Web fecha.

734

Capítulo 23  Applets e Java Web Start

Java Network Launch Protocol (JNLP) Um documento Java Network Launch Protocol ( JNLP) fornece as informações que o Java Web Start precisa para fazer o download e executar um programa. Além disso, você deve empacotar seu programa em um ou mais repositórios de arquivo Java ( JAR) que contêm o código e os recursos do programa e (por exemplo, imagens, arquivos de mídia, arquivos de texto). Por padrão, programas inicializados via Java Web Start são executados utilizando o modelo de segurança de caixa de areia. Se o usuário der permissão, esses programas podem acessar o sistema de arquivos local, a área de transferência e outros serviços via JNLP APIs do pacote javax.jnlp. Discutiremos alguns desses recursos no Capítulo 24. Programas digitalmente assinados podem ter maior acesso ao sistema local se o usuário confiar na fonte.

23.7.1  Empacotando o applet DrawTest para uso com Java Web Start Vamos empacotar o applet DrawTest de demonstração do JDK (discutido na Seção 23.2) para que você possa executá-lo via Java Web Start. Para fazer isso, você deve primeiro empacotar os arquivos e recursos .class do applet que ele utiliza (se houver algum) em um repositório de arquivos Java ( JAR). Em uma janela de comando, mude para o diretório DrawTest, como você fez na Seção 23.2. Nessa pasta, execute o seguinte comando: jar cvf DrawTest.jar *.class

que cria um arquivo JAR no diretório atual nomeado DrawTest.jar contendo os arquivos .class do applet — DrawControls.class, DrawPanel.class e DrawTest.class. Se o programa tivesse outros recursos, você simplesmente adicionaria os nomes de arquivo ou os nomes de pasta em que esses recursos são armazenados no fim do comando precedente. As letras cvf são opções de linha de comando para o comando jar. A opção c indica que o comando deve criar um novo arquivo JAR. A opção v indica que o comando deve produzir uma saída detalhada para que você possa ver a lista de arquivos e diretórios que estão sendo incluídos no arquivo JAR. A opção f indica que o próximo argumento na linha de comando (DrawTest.jar) é o novo nome do arquivo JAR. A Figura 23.11 mostra a saída detalhada do comando precedente, que mostra os arquivos que foram inseridos no JAR. added manifest adding: DrawControls.class(in = 2611) (out= 1488)(deflated 43%) adding: DrawPanel.class(in = 2703) (out= 1406)(deflated 47%) adding: DrawTest.class(in = 1170) (out= 706)(deflated 39%)

Figura 23.11  |  Saída do comando jar.

23.7.2  Documento JNLP para o applet DrawTest Em seguida, você deve criar um documento JNLP que descreve o conteúdo do arquivo JAR e especifica que arquivo no JAR é o chamado main-classe que inicia a execução do programa. Para um applet, o main-class é aquele que estende JApplet (isto é, DrawTest nesse exemplo). Para um aplicativo, o main-class é aquele que contém o método main. Um documento JNLP básico para o applet DrawTest é mostrado na Figura 23.12. Descreveremos os elementos desse documento mais adiante. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19



DrawTest Applet Sun Microsystems, Inc.









23.7  Java Web Start e o Java Network Launch Protocol (JNLP)

20 21 22 23 24 25 26

735



Figura 23.12  |  Documento DrawTest.jnlp para inicializar o applet DrawTest.

Visão geral da XML Os documentos de JNLP são escritos na Extensible Markup Language (XML) — um padrão amplamente suportado para descrever dados. A XML é comumente utilizada na troca de dados entre aplicativos pela Internet, e muitos aplicativos agora utilizam a XML para também especificar informações sobre a configuração — como é o caso com documentos JNLP para o Java Web Start. A XML permite criar marcações para praticamente qualquer tipo de informações. Isso permite criar linguagens de marcação inteiramente novas para descrever qualquer tipo de dados, como fórmulas matemáticas, instruções de configuração de software, estruturas químicas moleculares, músicas, notícias, receitas e relatórios financeiros. A XML descreve dados de uma maneira que tanto os seres humanos como os computadores podem entender. O JNLP é um vocabulário XML que descreve as informações que o Java Web Start precisa para inicializar um programa. Documentos XML contêm elementos que especificam a estrutura do documento, como title (linha 7), e texto que representa o conteúdo (isto é, dados), como DrawTest Applet (linha 7). Documentos XML delimitam os elementos com tags de abertura e fechamento. Um tag de abertura consiste no nome do elemento entre colchetes angulares (por exemplo, e vendor> nas linhas 7 e 8). Tags de abertura também podem conter atributos da forma nome=valor — por exemplo, o tag de início jnlp contém o atributo href= "DrawTest.jnlp". Um tag de fechamento consiste no nome do elemento precedido por uma barra (/) entre colchetes angulares (por exemplo, e nas linhas 7–8). Os tags de abertura e fechamento de um elemento cercam o texto que representa um dado (por exemplo, o vendor (fabricante) do programa — Sun Microsystems, Inc. — na linha 8, que é cercado pelo tag de abertura e pelo tag de fechamento ) ou outros elementos (por exemplo, os elementos title, vendor, shortcut e offline-allowed no elemento information das linhas 6–13). Cada documento XML deve ter exatamente um único elemento-raiz que contém todos os outros elementos. No Figura 23.12, o elemento jnlp (linhas 2–26) é o elemento-raiz. Documento JNLP: Elemento jnlp O tag de abertura do elemento jnlp (linhas 2–4) tem dois atributos — codebase e href. O valor do atributo codebase é um URL que especifica o caminho em que o documento JNLP e o arquivo JAR estão armazenados — isso é especificado na Figura 23.12 como CaminhoParaOArquivoJNLP, uma vez que esse valor depende da localização a partir da qual o applet é carregado. O atributo href especifica o arquivo JNLP que inicializa o programa. Salvamos o arquivo JNLP e o arquivo JAR no diretório do applet de demonstração DrawTest dentro da estrutura de diretórios do JDK. Utilizamos o seguinte URL do sistema de arquivos local como o codebase: file:.

que indica que o código está no diretório atual (.). Em geral, o codebase refere-se a um diretório em um servidor Web com um URL http://. Se quiser disponibilizar seu applet ou aplicativo a partir de um servidor Web para que os usuários possam acessá-lo on-line, você precisará configurar o servidor Web corretamente, como descrito em java.sun.com/javase/6/docs/technotes/guides/javaws/ developersguide/setup.html.

Documento JNLP: Elemento information O elemento information (linhas 6–13) fornece detalhes sobre o programa. O elemento title especifica um título para o programa. O elemento vendor especifica quem criou o programa. Os valores desses elementos aparecem nos erros e alertas de segurança do Java Web Start e são apresentados ao usuário. O valor de title também aparece na barra de título da janela em que o programa executa. O elemento desktop, aninhado no elemento shortcut (linhas 9–11) instrui o Java Web Start a perguntar se o usuário deseja instalar um atalho na área de trabalho. Se o usuário aceitar, um ícone aparecerá na área de trabalho. O usuário pode então inicializar o programa em janela própria dando um clique duplo no ícone na área de trabalho. Observe a sintaxe do elemento — elemento XML vazio. Quando nada aparece entre os tags de abertura e fechamento de um elemento, o elemento pode ser escrito utilizando um tag de fechamento que termina com />. O elemento offline-allowed (linha 12) indica que depois de o programa ser instalado no computador do usuário, ele pode ser inicializado via Java Web Start — mesmo quando o computador não está conectado à Internet. Isso é especialmente útil para qualquer programa que pode ser utilizado com arquivos armazenados no computador do usuário.

736

Capítulo 23  Applets e Java Web Start

Documento JNLP: elemento resources O elemento resources (linhas 15–18) contém dois elementos aninhados. O elemento java lista a versão mínima do Java necessária para executar o programa (linha 16) e o elemento jar (linha 17) especifica a localização do arquivo JAR que contém o programa e se esse arquivo JAR contém a classe que inicializa o programa. Pode haver múltiplos elementos jar, como veremos no próximo capítulo. Documento JNLP: elemento applet-desc O elemento applet-desc (linhas 20–25) é semelhante ao elemento applet na XHTML. O atributo name especifica o nome do applet. O atributo main-class especifica a classe de applet principal (aquela que estende JApplet). Os atributos width e height especificam a largura e altura em pixels, respectivamente, da janela em que o applet executará. O Capítulo 24 discute um elemento semelhante para aplicativos — application-desc. Inicializando o applet com o Java Web Start Você agora está pronto para inicializar o applet via Java Web Start. Há várias maneiras de fazer isso. Você pode utilizar o comando javaws em uma janela de comando a partir da pasta que contém o documento JNLP, como em javaws DrawTest.jnlp

Você também pode utilizar o gerenciador de arquivos do seu sistema operacional para localizar o JNLP no computador e dar um clique duplo no nome de arquivo. Normalmente, o arquivo JNLP é referenciado a partir de uma página Web via um hiperlink. O documento Draw­ TestWebPage.html na Figura 23.13 (que foi salvo no mesmo diretório que o arquivo JNLP) contém um elemento âncora (a) (linha 4), que é vinculado ao arquivo DrawTest.jnlp. Clicar nesse hiperlink na página Web descarrega o arquivo JNLP (nesse caso, ele é carregado do sistema de arquivos local) e executa o applet correspondente.

1 2 3 4 5 6

DrawTest Launcher Page

Launch DrawTest via Java Web Start

Hiperlink para DrawTest.jnlp

Figura 23.13  |  O documento XHTML que inicializa o applet DrawTest quando o usuário clica no link.

Ao executar o applet pela primeira vez via Java Web Start, você verá uma caixa de diálogo como a da Figura 23.14. Essa caixa de diálogo permite que o usuário decida se um ícone será instalado na área de trabalho. Se o usuário clicar em OK, um novo ícone rotulado com o título especificado no documento JNLP aparece na área de trabalho do usuário. O applet também é armazenado em cache para uso futuro. Depois de o usuário clicar em OK ou Skip nessa caixa de diálogo, o programa executa (Figura 23.15).

Figura 23.14  |  A caixa de diálogo pergunta se o usuário deseja instalar um atalho na área de trabalho.

23.8 Conclusão

737

Figura 23.15 | Applet DrawTest em execução e com o Java Web Start.

Visualizando programas instalados via Java Web Star Você pode visualizar os programas instalados via Java Web Start no Java Cache Viewer digitando o seguinte comando em uma janela de comandos: javaws ­viewer

Isso exibe a janela na Figura 23.16. O Java Cache Viewer permite gerenciar os programas Java Web Start no seu sistema. Você pode executar um programa selecionado, criar um atalho na área de trabalho para um programa (se não houver um ainda), excluir programas instalados etc. Para obter informações adicionais sobre o Java Web Start , visite java.sun.com/javase/6/docs/technotes/guides/javaws/. Esse site fornece uma visão geral do Java Web Start e inclui links para o Developer’s Guide, um FAQ, a JNLP Specification e a documentação da API para o pacote javax.jnlp. i

Executa o aplicativo selecionado

Cria atalho para a área de trabalho

Remove itens selecionados

Figura 23.16 | Visualizando programas instalados via Java Web Start no Java Cache Viewer.

23.8 Conclusão Neste capítulo, você aprendeu os fundamentos dos applets Java e do Java Web Start. Conheceu os conceitos básicos da XHTML para incorporar um applet a uma página Web e executá-lo em um contêiner de applets como o appletviewer ou um navegador Web. Além disso, elencamos os cinco métodos que são chamados automaticamente pelo contêiner de applets durante o ciclo de vida de um applet. Discutimos o modelo de segurança de caixa de areia do Java para executar código descarregado. Introduzimos então o Java Web Start e o Java Network Launch Protocol ( JNLP). Você aprendeu a empacotar um programa em um arquivo JAR para que ele possa ser executado via Java Web Start. Também apontamos os elementos básicos de um documento JNLP. No próximo capítulo, você verá vários applets adicionais à medida que apresentamos os recursos de multimídia básicos. Também aprenderá mais recursos do Java Web Start e JNLP. No Capítulo 27, demonstraremos como personalizar um applet via parâmetros que são especificados em um elemento XHTML applet.

738

Capítulo 23  Applets e Java Web Start

Resumo Seção 23.1  Introdução • Applets são programas Java que costumam ser incorporados a documentos XHTML (Extensible HyperText Markup Language) — também chamados páginas Web. Quando um navegador Web compatível com Java carrega uma página Web que contém um applet, o applet é baixado no navegador e executa. • O aplicativo no qual um applet executa é conhecido como contêiner de applets. É responsabilidade do contêiner de applets inicializar a(s) classe(s) do applet, criar uma instância do applet e gerenciar seu ciclo de vida. O JDK inclui um contêiner de applets chamado appletviewer para testar applets à medida que você os desenvolve e antes de incorporá-los a páginas Web. • Navegadores Web executam applets Java via Plug-in Java.

Seção 23.2  Applets de exemplo fornecidos com o JDK • Para reexecutar um applet no appletviewer, clique no menu Applet do appletviewer e selecione o item de menu Reload. • Para encerrar o appletviewer, selecione Quit no menu Applet do appletviewer.

Seção 23.3  Applet Java simples: desenhando uma string • Cada applet Java é uma interface gráfica com o usuário em que você pode desenhar ou colocar componentes GUI. • A classe JApplet do pacote javax.swing é utilizada para criar applets. • Um contêiner de applets pode criar apenas objetos de classes public e estender a JApplet (ou a classe Applet nas versões anteriores do Java). • Um contêiner de applets espera que cada applet Java tenha os métodos init, start, paint, stop e destroy, cada um dos quais é declarado na classe JApplet. Cada nova classe de applet que você cria herda as implementações padrão desses métodos da classe JApplet. • Quando um contêiner de applets carrega um applet, ele cria um objeto do tipo de applet e, então, chama os métodos init, start e paint do applet. Se você não declarar esses métodos no seu applet, o contêiner de applets chamará as versões herdadas. • Os métodos init e start da superclasse têm corpos vazios, portanto eles não realizam nenhuma tarefa. O método paint da superclasse não desenha nada no applet. • Para permitir que um applet desenhe, sobrescreva o método paint. Você não chama o método paint explicitamente em um applet. Em vez disso, o contêiner de applets chama paint para dizer ao applet quando desenhar, e o contêiner de applets é responsável por passar um objeto Graphics como um argumento. • A primeira instrução no método paint deve ser uma chamada ao método paint da superclasse. Omitir isso pode causar erros sutis de desenho em applets que combinam desenho e componentes GUI. • Para poder executar um applet, antes você deve criar um documento XHTML (Extensible Hypertext Markup Language) que especifica qual applet executar no contêiner de applets. Em geral, um documento XHTML termina com uma extensão de nome de arquivo “.html” ou “.htm”. • A maioria dos elementos XHTML é delimitada por pares de tags. Todos os tags XHTML iniciam com um sinal de menor, . • Um elemento applet instrui o contêiner de applets a carregar um applet específico e define sua área de exibição (sua largura e altura em pixels) no contêiner de applets. • Normalmente, um applet e seu documento XHTML correspondente são armazenados no mesmo diretório. • Quando um contêiner de applets encontra um documento XHTML que contém um applet, o contêiner de applets carrega automaticamente o(s) arquivo(s) .class do applet do mesmo diretório no computador em que o documento XHTML reside. • O appletviewer só entende os tags XHTML e e ignora todos os outros tags do documento.

Seção 23.4  Métodos de ciclo de vida de applet • Cinco métodos do ciclo de vida de um applet são chamados pelo contêiner de applets entre o momento em que o applet é carregado no navegador e o momento em que ele é fechado pelo navegador. • O método init é chamado uma vez pelo contêiner de applets para inicializar um applet quando ele é carregado. • O método start é chamado pelo contêiner de applets depois de o método init completar a execução. Além disso, se o usuário for para outro site da Web e depois retornar à página XHTML do applet, o método start é chamado novamente. • O método paint é chamado pelo contêiner de applets depois dos métodos init e start. O método paint também é chamado quando o applet precisa ser repintado. • O método stop é chamado pelo contêiner de applets quando o usuário sai da página Web do applet indo para outra página Web. • O método destroy é chamado pelo contêiner de applets quando o applet é removido da memória. Isso ocorre quando o usuário encerra a sessão de navegação fechando todas as janelas do navegador e também pode ocorrer sem que o navegador saiba quando o usuário tiver ido para outras páginas Web.



Resumo

739

Seção 23.5  Inicializando uma variável de instância com o método init • O método Graphics drawString desenha uma String em uma localização especificada. • O método Graphics drawRect desenha um retângulo no canto superior esquerdo com a largura e altura especificadas.

Seção 23.6  Modelo de segurança da caixa de areia • É considerado perigoso permitir que applets ou outros programas que você executa a partir de um navegador Web acessem seu computador local. Você deve decidir se confia na fonte. • Um navegador baixa um applet sem o conhecimento do usuário — é apenas outro elemento da página Web que o usuário acessa. • Para combater códigos mal-intencionados, a plataforma Java utiliza um modelo de segurança de caixa de areia que fornece um mecanismo para executar códigos descarregados de uma maneira segura. O código baixado não pode acessar recursos do sistema local, e um applet só pode interagir com o servidor de onde foi baixado. • Você pode “assinar digitalmente” um applet que requer acesso aos recursos do sistema local. Se o usuário indicar que ele confia na origem do applet, só então o applet será capaz de acessar os recursos do computador local.

Seção 23.7  Java Web Start e o Java Network Launch Protocol (JNLP) • O Java Web Start é um framework para executar programas descarregados fora do navegador. Em geral, esses programas são armazenados em um servidor Web, mas eles também podem ser armazenados na rede de uma organização para distribuição interna, ou até mesmo em CDs, DVDs ou outra mídia. • Os usuários podem inicializar applets e aplicativos robustos clicando em um hiperlink em uma página Web, e podem instalar rápida e facilmente os programas nos computadores. • O Java Web Start pode ser configurado para perguntar ao usuário se um ícone na área de trabalho deve ser criado para que o usuário possa inicializar o programa diretamente da área de trabalho. Programas descarregados também podem ter um “modo off-line” para a execução se o computador não estiver conectado à Internet. • Ao executar um programa via Java Web Start, o programa é descarregado e armazenado no cache (área de armazenamento temporário) no computador do usuário. Na próxima vez que o usuário executar esse programa, o Java Web Start o inicializa a partir do cache. • Se o programa foi atualizado desde a última vez em que foi carregado, o Java Web Start pode baixar automaticamente as atualizações para que um usuário sempre tenha a versão mais recente. • Um documento Java Network Launch Protocol ( JNLP) fornece as informações de que o Java Web Start precisa para baixar e executar um programa. • Programas inicializados via o Java Web Start são executados utilizando o modelo de segurança de caixa de areia. O usuário pode permitir acesso ao sistema de arquivos local, à área de transferência e a outros serviços via JNLP APIs.

Seção 23.7.1 Empacotando o applet DrawTest para uso com o Java Web Start • O comando jar é utilizado para criar arquivos JAR. A opção c indica que o comando deve criar um novo arquivo JAR. A opção v indica que o comando deve produzir uma saída prolixa. A opção f indica que o próximo argumento na linha de comandos é o novo nome do arquivo JAR.

Seção 23.7.2 Documento JNLP para o applet DrawTest • Um documento JNLP descreve o conteúdo do arquivo JAR e especifica qual arquivo no JAR é o assim chamado main-class que inicia a execução do programa. • Os documentos de JNLP são escritos na Extensible Markup Language (XML) — um padrão amplamente suportado para descrever dados. • O JNLP é um vocabulário XML que descreve as informações que o Java Web Start precisa para inicializar um programa. • Documentos XML contêm elementos que especificam a estrutura do documento. Documentos XML delimitam os elementos com tags de abertura e fechamento. Um tag de abertura consiste no nome do elemento entre colchetes angulares. Um tag de abertura também pode conter atributos da forma nome=valor. Um tag de fechamento consiste no nome do elemento precedido por uma barra (/) entre colchetes angulares. • Os tags de abertura e fechamento de um elemento cercam o texto que representa um dado ou outros elementos. • Cada documento XML deve ter exatamente um único elemento-raiz que contém todos os outros elementos. • O atributo codebase do elemento jnlp especifica o caminho em que o documento JNLP e o arquivo JAR são armazenados. O atributo href especifica o arquivo JNLP que inicializa o programa. • Em geral, o codebase refere-se a um diretório em um servidor Web com um URL http://. • O elemento information fornece detalhes sobre o programa. • O elemento title especifica um título para o programa. • O elemento vendor especifica quem criou o programa. • O elemento desktop, aninhado no elemento shortcut, instrui o Java Web Start a perguntar aos usuários se eles desejam instalar um atalho na área de trabalho. • O elemento offline-allowed indica que um programa pode ser inicializado via Java Web Start mesmo quando o computador não está conectado à Internet.

740

Capítulo 23  Applets e Java Web Start

• O elemento resources contém um elemento java que lista a versão mínima do Java necessária para executar o programa e um elemento jar que especifica a localização do arquivo JAR. • O atributo name do elemento applet-desc especifica o nome do applet. O atributo main-class especifica a classe de applet principal. Os atributos width e height especificam a largura e altura em pixels, respectivamente, da janela em que o applet executará. • Para inicializar o applet via Java Web Start, utilize o comando javaws. Você também pode utilizar o gerenciador de arquivos do seu sistema operacional para localizar o JNLP no computador e dar um clique duplo no nome de arquivo. Normalmente, um arquivo JNLP é referenciado a partir de uma página Web via um hiperlink. • Ao executar um applet via Java Web Start pela primeira vez e o documento JNLP especifica que um ícone deve ser instalado na área de trabalho, você verá uma caixa de diálogo que permite decidir se é preciso instalar o ícone na área de trabalho. Se clicar em OK, um novo ícone rotulado com o título especificado no documento JNLP aparece na área de trabalho. • Você pode visualizar os programas instalados via Java Web Start no Java Cache Viewer digitando o comando javaws -viewer em uma janela de comandos: • O Java Cache Viewer permite gerenciar os programas instalados via Java Web Start. Você pode executar um programa selecionado, criar um atalho na área de trabalho, excluir programas instalados etc.

Terminologia /, barras em tags de fechamento, 735 , colchetes angulares para elementos XML,

735 .htm, extensão de nome de arquivo, 729 .html, extensão de nome de arquivo, 729 applet, 723 applet-desc, elemento de um documento JNLP, 736 applet, elemento XHTML, 729 appletviewer, contêiner de applets, 723 atributo de um elemento XHTML, 729 body, elemento XHTML, 729 c, opção do comando jar, 734 caractere de barra (/) em tags de fechamento, 735 colchete angular () para elementos XML, 735 contêiner de applets, 723 desktop, elemento de um documento JNLP, 735 destroy, método da classe JApplet, 728 elemento-raiz (XML), 735

elemento (XML), 735 Extensible Markup Language (XML), 735 f, opção do comando jar, 734 height de um applet em pixels, 729 information, elemento de um documento JNLP, 735 init, método da classe JApplet, 728 JApplet, classe, 728 jar, comando, 734 jar, elemento de um documento JNLP, 736 java, elemento de um documento JNLP, 736 Java Network Launch Protocol ( JNLP), 734 Java Plug-in, 723 Java Web Start, 733 javaws, comando, 736 jnlp, elemento de um documento JNLP, 735 main-class especificada em um documento JNLP, 734 modelo de segurança de caixa de areia, 733

offline-allowed, elemento de um documento

JNLP, 735

paint, método da classe JApplet, 728 resources, elemento de um documento JNLP,

736

shortcut, elemento de um documento JNLP,

735 sinais de maior e menor () para elementos XML, 735 start, método da classe JApplet, 728 stop, método da classe JApplet, 728 tag de abertura, 735 tag de fechamento, 735 tag (em um documento de XHTML), 729 title, elemento de um documento JNLP, 735 v, opção do comando jar, 734 vendor, elemento de um documento JNLP, 735 width de um applet em pixels, 729 XHTML (Extensible HyperText Markup Language), documento, 723

Exercício de autorrevisão 23.1 Preencha as lacunas em cada uma das seguintes sentenças: a) Os applets Java começam a execução com uma série de três chamadas de método: ________, ________ e ________. b) O método ________ é invocado para um applet toda vez que o usuário de um navegador deixa a página XHTML em que o applet reside. c) Cada applet deve estender a classe ________. d) O(A) ________ ou um navegador pode ser utilizado para executar um applet Java. e) O método ________ é chamado toda vez que o usuário de um navegador revisita a página XHTML em que um applet reside. f) Para carregar um applet em um navegador você deve primeiro definir um arquivo ________. g) O método ________ é chamado uma vez quando um applet inicia a execução. h) O método ________ é invocado para desenhar em um applet. i) O método ________ é invocado para um applet quando o navegador o remove da memória. j) Os tags XHTML ________ e ________ especificam que um applet deve ser carregado em um contêiner de applets e executado. k) ________ é uma estrutura para executar programas baixado fora do navegador. l) Um documento ________ fornece as informações que a Java Web Start precisa para baixar e executar um programa. m) O ________ permite gerenciar os programas Java Web Start no seu sistema.



Respostas do exercício de autorrevisão

741

Respostas do exercício de autorrevisão 23.1 a)init, start, paint. b)  stop. c)  JApplet (ou Applet). d)  appletviewer. e)  start. f) XHTML. g)  init. h)  paint. i)  destroy. j) , . k) Java Web Start. l) Java Network Launch Protocol ( JNLP). m) Java Cache Viewer.

Exercícios 23.2 Escreva um applet que pede que o usuário insira dois números de ponto flutuante, obtém do usuário os dois números e desenha sua soma, produto (multiplicação), diferença e quociente (divisão). Utilize as técnicas mostradas na Figura 23.9.

23.3 Escreva um applet que pede que o usuário insira dois números de ponto flutuante, obtém do usuário os números e exibe os dois números e, então, exibe o maior número seguido pelas palavras "is larger" como uma string no applet. Se os números forem iguais, o applet deve imprimir a mensagem "These numbers are equal". Utilize as técnicas mostradas na Figura 23.9.

23.4 Escreva um applet que insere três números de ponto flutuante do usuário e exibe a soma, média, produto, maior e menor desses números como strings no applet. Utilize as técnicas mostradas na Figura 23.9.

23.5 Escreva um applet que pede que o usuário insira o raio de um círculo como um número de ponto flutuante e desenha o diâmetro do círculo, circunferência e área. Utilize o valor 3.14159 para π. Utilize as técnicas mostradas na Figura 23.9. [Nota: Você também pode utilizar a constante Math.PI predefinida para o valor de π. Essa constante é mais precisa que o valor 3,14159. A classe Math é definida no pacote java.lang, então você não precisa importá-la.] Utilize as seguintes fórmulas (r é o raio): diâmetro = 2r circunferência = 2πr área = πr2

23.6 Escreva um applet que lê cinco inteiros, determina e imprime o maior e o menor inteiro no grupo. Desenhe os resultados no applet. 23.7 Escreva um applet que desenha um padrão de tabuleiro de damas como segue: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

23.8 Escreva um applet que desenha retângulos de diferentes tamanhos e localizações. 23.9 Escreva  um applet que permite ao usuário inserir os quatro valores para os argumentos requeridos pelo método drawRect , então desenhe um retângulo utilizando os quatro valores de entrada.

23.10 A classe Graphics contém o método drawOval , que recebe como argumentos os mesmos quatro argumentos do método drawRect. Os argumentos do método drawOval especificam o “quadro delimitador” da oval — os lados do quadro delimitador são os limites da oval. Escreva um applet Java que desenha uma oval e um retângulo com os mesmos quatro argumentos. A oval tocará o retângulo no centro de cada lado.

23.11 Modifique a solução do Exercício 23.10 para gerar a saída de ovais de diferentes formas e tamanhos. 23.12 Escreva um applet que permite ao usuário inserir os quatro argumentos requeridos pelo método draw-Oval , então desenhe uma oval utilizando os quatro valores de entrada.

23.13 Empacote o applet de demonstração TicTacToe do JDK (discutido na Seção 23.2) para uso com o Java Web Start, copie então o documento JNLP na Figura 23.12 e modifique-o para que ele inicialize o applet TicTacToe .

24

A roda que faz mais barulho ... é a que obtém a graxa. — John Billings (Henry Wheeler Shaw)

Utilizaremos um sinal que eu testei e considerei de grande alcance e fácil de gritar. Uaa-huu! — Zane Grey

Há uma dança natural no movimento de um peixe de aquário. — Walt Disney

Entre o movimento e o ato está a sombra. — Thomas Stearns Eliot

Multimídia: applets e aplicativos

Objetivos Neste capítulo, você aprenderá: 

A obter, exibir e redimensionar imagens.



A criar animações de sequências de imagens.



A criar mapas de imagem.



A obter, reproduzir, repetir (loop) e interromper sons, utilizando um AudioClip.



A reproduzir vídeo utilizando a interface Player.

Sumário

24.1 Introdução

24.1 Introdução 24.2 Carregando, exibindo e dimensionando imagens 24.3 Animação de uma série de imagens 24.4 Mapas de imagem

743

24.5 Carregando e reproduzindo clipes de áudio 24.6 Reproduzindo vídeo e outros tipos de mídia com o Java Media Framework 24.7 Conclusão 24.8 Recursos da Web

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios | Seção especial: projetos de multimídia desafiadores | Fazendo a diferença

24.1 Introdução A multimídia — o uso de som, imagens, elementos gráficos, animação e vídeo — faz aplicativos “ganharem vida”. Embora a maior parte da multimídia em aplicativos Java seja bidimensional, você pode utilizar a Java 3D API para criar aplicativos gráficos 3D (java.sun. com/javase/technologies/desktop/java3d/). A maioria dos computadores novos vendidos atualmente é multimídia, pois já são equipados com unidades de DVD e capacidades de reprodução de áudio e vídeo. Os computadores desktop e laptop são tão poderosos que podem armazenar e reproduzir som e vídeo com qualidade de DVD (e muitas vezes, com qualidade de HD). Entre os usuários que querem imagens gráficas, muitos agora querem imagens gráficas tridimensionais coloridas e de alta resolução. Verdadeiras imagens tridimensionais já estão disponíveis. Espera-se que a televisão tridimensional e de alta resolução com a experiência de um “teatro arena” (em que a audiência circunda o palco) acabe se tornando comum. Eventos esportivos e de entretenimento parecerão acontecer dentro da sua sala! Estudantes de medicina em todo o mundo verão operações sendo realizadas a milhares de milhas de distância, como se estivessem ocorrendo no mesmo lugar. As pessoas aprenderão como guiar com simuladores de direção incrivelmente realistas em suas casas antes de pegarem no volante. As possibilidades são infinitas e empolgantes. A multimídia demanda um extraordinário poder de computação. Até recentemente, computadores economicamente acessíveis com esse tipo de poder não estavam amplamente disponíveis. Os processadores ultrapoderosos atuais tornam a multimídia efetivamente econômica. Os usuários estão ávidos por processadores mais poderosos, mais memória e canais de comunicação mais rápidos que suportem os exigentes aplicativos multimídia. Ironicamente, essas capacidades aprimoradas podem não custar mais — a concorrência feroz continua abaixando os preços. As Java APIs fornecem recursos multimídia que permitem começarmos a desenvolver imediatamente poderosos aplicativos multimídia. Este capítulo apresenta vários exemplos, incluindo: 1. Princípios básicos de manipulação de imagens. 2. Criação de animações suaves. 3. Reprodução de arquivos de áudio com a interface AudioClip. 4. Criação de mapas de imagem que podem perceber quando o cursor está sobre eles, mesmo sem o clique de mouse. 5. Reprodução de arquivos de vídeo com a interface Player. O capítulo também introduz recursos de JNLP adicionais que, com a permissão do usuário, permitem que um applet ou aplicativo acesse arquivos no computador local do usuário. Os exercícios sugerem dezenas de projetos desafiadores e interessantes. Quando estávamos escrevendo-os, as ideias simplesmente não paravam de fluir. A multimídia estimula a criatividade de uma maneira que não experimentamos com as capacidades de computadores “convencionais”. [Nota: As capacidades multimídia do Java vão bem além das apresentadas neste capítulo. Elas incluem a Java Media Framework (JMF) API (para adicionar mídia de áudio e vídeo a um aplicativo), a Java Sound API (para reproduzir, gravar e modificar áudio), Java 3D API (para criar e modificar imagens gráficas 3D), a Java Advanced Imaging API (para capacidades de processamento de imagens, como cortar e redimensionar), a Java Speech API (para inserir comandos de voz do usuário ou gerar saída de comandos de voz para o usuário), a Java 2D API (para criar e modificar imagens gráficas 2D, discutidas no Capítulo 15) e a Java Image I/O API (para ler e gerar a saída de imagens para arquivos). A Seção 24.8 fornece links Web para essas APIs.]

24.2 Carregando, exibindo e dimensionando imagens Começamos nossa discussão com imagens. Utilizaremos várias imagens diferentes neste capítulo. Você pode criar suas próprias imagens com softwares, como Adobe® Photoshop®, Corel® Paint Shop Pro®, Microsoft® Paint e G.I.M.P. (gimp.org). O applet da Figura 24.1 utiliza o Java Web Start e o JNLP FileOpenService (pacote javax.jnlp) para permitir ao usuário selecionar uma imagem, exibi-la e redimensioná-la. Depois de o usuário selecionar uma imagem, o applet obtém os bytes do arquivo e os passa para o

744

Capítulo 24  Multimídia: applets e aplicativos

construtor ImageIcon (pacote javax.swing) para criar a imagem que será exibida. Os construtores da classe ImageIcon podem receber argumentos de vários formatos diferentes, incluindo um array byte que contém os bytes de uma imagem, uma Image (pacote java.awt) já carregada na memória, ou uma String ou um URL representando a localização da imagem. O Java suporta vários formatos de imagem, incluindo Graphics Interchange Format (GIF), Joint Photographic Experts Group (JPEG) e Portable Network Graphics (PNG). Os nomes de arquivo desses tipos geralmente terminam com a extensão .gif, .jpg (ou .jpeg) e .png, respectivamente. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62

// Figura 24.1: LoadImageAndScale.java // Carregando, exibindo e redimensionando uma imagem em um applet. import java.awt.BorderLayout; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.jnlp.FileContents; import javax.jnlp.FileOpenService; import javax.jnlp.ServiceManager; import javax.swing.ImageIcon; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; public class LoadImageAndScale extends JApplet { private ImageIcon image; // referencia imagem para exibição private JPanel scaleJPanel; // JPanel contendo o selecionador de escala private JLabel percentJLabel; // rótulo para JTextField private JTextField scaleInputJTextField; // obtém a entrada de usuário private JButton scaleChangeJButton; // inicia o redimensionamento de imagem private double scaleValue = 1.0; // redimensiona porcentagem para imagem // carrega a imagem quando o applet é carregado public void init() { scaleJPanel = new JPanel(); percentJLabel = new JLabel( "scale percent:" ); scaleInputJTextField = new JTextField( "100" ); scaleChangeJButton = new JButton( "Set Scale" ); // adiciona componentes e coloca scaleJPanel na região NORTH do applet scaleJPanel.add( percentJLabel ); scaleJPanel.add( scaleInputJTextField ); scaleJPanel.add( scaleChangeJButton ); add( scaleJPanel, BorderLayout.NORTH ); // registra a rotina de tratamento de evento para scaleChangeJButton scaleChangeJButton.addActionListener( new ActionListener() { // quando JButton é pressionado, configura scaleValue e redesenha public void actionPerformed( ActionEvent e ) { scaleValue = Double.parseDouble( scaleInputJTextField.getText() ) / 100.0; repaint(); // faz com que a imagem seja reexibida na nova escala } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener // utiliza serviços JNLP para abrir um arquivo de imagem que o usuário seleciona try { // obtém uma referência ao FileOpenService FileOpenService fileOpenService = (FileOpenService) ServiceManager.lookup( "javax.jnlp.FileOpenService" );

63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103

24.2  Carregando, exibindo e dimensionando imagens

// obtém conteúdo de arquivo a partir do FileOpenService FileContents contents = fileOpenService.openFileDialog( null, null ); // array de bytes para armazenar dados da imagem byte[] imageData = new byte[ (int) contents.getLength() ]; contents.getInputStream().read( imageData ); // lê bytes da imagem image = new ImageIcon( imageData ); // cria a imagem // se a imagem for carregada com sucesso, cria e adiciona DrawJPanel add( new DrawJPanel(), BorderLayout.CENTER ); } // fim do try catch( Exception e ) { e.printStackTrace(); } // fim do catch } // fim do método init // DrawJPanel utilizado para exibir a imagem carregada private class DrawJPanel extends JPanel { // exibe a imagem public void paintComponent( Graphics g ) { super.paintComponent( g ); // os seguintes valores são utilizados para centralizar a imagem double spareWidth = getWidth() - scaleValue * image.getIconWidth(); double spareHeight = getHeight() - scaleValue * image.getIconHeight(); // desenha imagem com a largura e a altura redimensionada g.drawImage( image.getImage(), (int) ( spareWidth ) / 2, (int) ( spareHeight ) / 2, (int) ( image.getIconWidth() * scaleValue ), (int) ( image.getIconHeight() * scaleValue ), this ); } // fim do método paint } // fim da classe DrawJPanel } // fim da classe LoadImageAndScale

(a) Diálogo de segurança do Java Web Start que aparece porque esse applet está pedindo acesso a um arquivo no computador local.

(b) Diálogo Open que aparece se o usuário clicar em OK no diálogo de segurança

745

746

Capítulo 24  Multimídia: applets e aplicativos (c) Dimensionando a imagem.

Figura 24.1  |  Carregando, exibindo e dimensionando uma imagem em um applet.

Configurando a GUI e a rotina de tratamento de evento JBUTTON O método init do applet (linhas 29–80) configura a GUI e uma rotina de tratamento de evento. Ele também utiliza serviços JNLP para permitir ao usuário selecionar uma imagem para exibição a partir do computador local. A linha 31 cria o JPanel que conterá o JLabel, JTextField e JButton criados nas linhas 32–34. As linhas 37–39 adicionam esses componentes ao FlowLayout padrão do JPANEL. A linha 40 coloca esse JPanel na região NORTH do BorderLayout padrão do JApplet. As linhas 43–54 criam a rotina de tratamento de evento para scaleChangeJButton. Quando o usuário clica nesse JButton, as linhas 49–50 obtêm a entrada do usuário do scaleInputJTextField e a dividem por 100.0 a fim de calcular a porcentagem de escala e atribuir o resultado a scaleValue. Esse valor será utilizado em cálculos posteriores para redimensionar a imagem. Por exemplo, se o usuário inserir 50, o valor da escala será 0.5 e a imagem será exibida com a metade do tamanho original. A linha 51 então redesenha o applet para exibir a imagem em sua nova escala. Abrindo o arquivo de imagem com FileOpenService do JNLP Como mencionamos na Seção 23.7, com a permissão do usuário, os programas Java Web Start podem acessar o sistema de arquivos local por meio das JNLP APIs do pacote javax.jnlp. Nesse exemplo, gostaríamos que o usuário selecionasse uma imagem do computador local para exibir no applet. (Fornecemos duas imagens no diretório desse exemplo com o código-fonte.) Você pode utilizar o FileOpenService do JNLP para solicitar acesso limitado ao sistema de arquivos local. As linhas 7–9 importam as interfaces e a classe de que precisamos para utilizar o FileOpenService. As linhas 60–62 utilizam o método static lookup da classe JNLP ServiceManager para obter uma referência ao FileOpenService. O JNLP fornece vários serviços, por isso esse método retorna uma referência Object, da qual você deve fazer coerção (converter) para o tipo apropriado. Em seguida, as linhas 65–66 utilizam o método openFileDialog do FileOpenService para exibir um diálogo de seleção de arquivo. O Java Web Start abre um prompt para o usuário (Figura 24.1 (a)) permitir a solicitação do applet de obter acesso ao sistema de arquivos local. Se o usuário permitir, o diálogo Open (Figura 24.1 (b)) é exibido. O método openFileDialog tem dois parâmetros — uma String para sugerir que um diretório seja aberto e um array String de extensões de arquivo aceitáveis (como "png" e "jpg"). Para simplificar, esse programa não faz o uso de nenhum desses parâmetros, em vez disso passa valores null, que exibem um diálogo de seleção de arquivo para abrir o diretório padrão do usuário e permitir que qualquer tipo de arquivo seja selecionado. Quando o usuário seleciona um arquivo de imagem e clica no botão Open do diálogo, o método openFileDialog retorna um objeto FileContents, que, por razões de segurança não dá ao programa acesso à localização exata do arquivo no disco. Em vez disso, o programa pode obter um InputStream e ler os bytes do arquivo. A linha 69 cria um array byte no qual os dados da imagem serão armazenados. O método FileContents getLength retorna o número de bytes (como um long) no arquivo. A linha 70 obtém o InputStream, depois invoca o seu método read para preencher o array imageData byte. A linha 71 cria um ImageIcon utilizando o array byte como a fonte dos dados da imagem. Por fim, a linha 74 adiciona um novo DrawJPanel ao CENTER do BorderLayout do applet. Quando o applet é exibido, os métodos paintComponent de seus componentes são chamados, o que faz com que o DrawJPanel exiba a imagem. Você pode aprender mais sobre as JNLP APIs em java.sun.com/javase/6/docs/jre/api/javaws/jnlp. Exibindo a imagem com o método paintComponent da classe DrawJPanel Para separar a GUI da área em que a imagem é exibida, utilizamos uma subclasse de JPanel chamada DrawJPanel (linhas 83–102). Seu método paintComponent (linhas 86–101) exibe a imagem. Gostaríamos de centralizar a imagem em DrawJPanel, portanto, as linhas 91–94 calculam a diferença entre a largura do DrawJPanel e a da imagem redimensionada e, depois, a altura do DrawJPanel e a da



24.2  Carregando, exibindo e dimensionando imagens

747

imagem redimensionada. Os métodos getWidth e getHeight da subclasse DrawJPanel (herdados indiretamente da classe Component) retornam a largura e a altura de DrawJPanel, respectivamente. Os métodos getIconWidth e getIconHeight retornam a altura e a largura da imagem, respectivamente. O scaleValue é configurado como 1.0 por padrão (linha 26), e é alterado quando o usuário clica no JButton Set Scale. As linhas 97–100 utilizam um dos métodos drawImage sobrecarregados da classe Graphics para exibir uma versão redimensionada do ImageIcon. O primeiro argumento invoca o método getImage do ImageIcon para obter a Image a ser desenhada. O segundo e terceiro argumentos representam as coordenadas do canto superior esquerdo da Image em relação ao canto superior esquerdo do DrawJPanel. O quarto e quinto argumentos especificam a largura e a altura da Image, respectivamente. A linha 99 redimensiona a largura da imagem invocando o método getIconWidth do ImageIcon e multiplicando seu valor de retorno pelo scaleValue. De modo semelhante, a linha 100 redimensiona a altura da imagem invocando o método getIconHeight da classe ImageIcon e multiplicando seu valor de retorno pelo scaleValue. O último argumento é uma referência a um ImageObserver — uma interface implementada pela classe Component. Como a classe DrawJPanel estende Component indiretamente, um DrawJPanel é um ImageObserver. Esse argumento é importante ao exibir imagens grandes que exigem bastante tempo para carregar (ou download da Internet). É possível que um programa tente exibir a imagem antes que tenha sido carregada (ou feito o seu download) completamente. Enquanto Image é carregada, ImageObserver recebe notificações e atualiza a imagem na tela conforme necessário. Nesse exemplo, as imagens estão sendo carregadas a partir do computador do usuário, portanto, é provável que a imagem inteira seja imediatamente exibida.

Compilando o applet Compilar e executar esse applet requer o arquivo jnlp.jar que contém as JNLP APIs. Esse arquivo pode ser encontrado no diretório de instalação do JDK sob os diretórios sample jnlp servlet

Para compilar o applet, utilize o seguinte comando: javac -classpath CaminhoParaOArquivoJnlpJar LoadImageAndScale.java

onde CaminhoParaOArquivoJnlpJar inclui tanto o caminho como o nome de arquivo jnlp.jar. Por exemplo, no nosso computador Windows Vista, o CaminhoParaOArquivoJnlpJar é "C:\Program Files\Java\jdk1.6.0_11\sample\jnlp\servlet\jnlp.jar"

Empacotando o applet para usar com Java Web Start Para empacotar o applet para uso com o Java Web Start, você deve criar um arquivo JAR que contenha o código do applet e o arquivo jnlp.jar. Para fazer isso, utilize o comando jar cvf LoadImageAndScale.jar *.class CaminhoParaOArquivoJnlpJar

onde CaminhoParaOArquivoJnlpJar inclui tanto o caminho como o nome de arquivo jnlp.jar. Isso colocará todos os arquivos .class do applet e uma cópia do arquivo jnlp.jar no novo arquivo JAR LoadImageAndScale.jar.

Documento JNLP para o applet LoadImageAndScale O documento JNLP na Figura 24.2 é semelhante ao introduzido na Figura 23.12. O único recurso novo nesse documento é que o elemento resources (linhas 10–14) contém um segundo elemento jar (linha 13) que referencia o arquivo jnlp.jar, que está incorporado no arquivo LoadImageAndScale.jar. 1 2 3 4 5 6 7 8 9 10 11 12 13 14



LoadImageAndScale Applet Deitel





748 15 16 17 18 19 20 21 22

Capítulo 24

Multimídia: applets e aplicativos



Figura 24.2 | Documento JNLP para o applet LoadImageAndScale.

Tornando o applet arrastável fora da janela do navegador O documento XHTML na Figura 24.3 carrega o applet em um navegador da Web. Nesse exemplo, utilizamos um elemento applet para especificar a classe do applet e fornecer dois elementos param entre os tags do elemento applet. O primeiro (linha 4) especifica que esse applet deve ser arrastável. Isto é, o usuário pode manter a tecla Alt e utilizar o mouse para arrastar o applet fora da janela do navegador. O applet então continuará executando, mesmo se o navegador for fechado. Clicar na caixa de fechamento do applet quando ele estiver executando fora do navegador faz com que o applet volte para a janela de navegador se ela ainda estiver aberta, ou termine, caso contrário. O segundo elemento param mostra uma maneira alternativa de especificar o arquivo JNLP que carrega um applet. Discutiremos parâmetros de applets em mais detalhes na Seção 27.2. 1 2 3 4 5 6 7 8







Figura 24.3 | O documento XHTML para carregar o applet LoadImageAndScale o torna arrastável para fora da janela de navegador.

24.3 Animação de uma série de imagens A seguir, animamos uma série de imagens que estão armazenadas em um array de ImageIcons. Nesse exemplo, utilizamos o JNLP FileOpenService para permitir ao usuário escolher um grupo de imagens que serão animadas exibindo uma imagem por vez em inter-

valos de 50 milissegundos. A animação apresentada nas figuras 24.4–24.5 é implementada utilizando uma subclasse de JPanel chamada LogoAnimatorJPanel (Figura 24.4) que pode ser anexada a uma janela de aplicativo ou um JApplet. A classe LogoAnimator (Figura 24.5) declara um método main (linhas 8–20 da Figura 24.5) para executar a animação como um aplicativo. O método main declara uma instância da classe JFrame e anexa um objeto LogoAnimatorJPanel ao JFrame para exibir a animação. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// Figura 24.4: LogoAnimatorJPanel.java // Animando uma série de imagens. import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.Graphics; import javax.jnlp.FileContents; import javax.jnlp.FileOpenService; import javax.jnlp.ServiceManager; import javax.swing.ImageIcon; import javax.swing.JPanel; import javax.swing.Timer; public class LogoAnimatorJPanel extends JPanel { protected ImageIcon images[]; // array de imagens private int currentImage = 0; // índice de imagem atual private final int ANIMATION_DELAY = 50; // retardo em milissegundos private int width; // largura da imagem

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89

24.3  Animação de uma série de imagens private int height; // altura da imagem private Timer animationTimer; // O timer guia a animação // construtor inicializa LogoAnimatorJPanel carregando imagens public LogoAnimatorJPanel() { try { // obtém a referência para FileOpenService FileOpenService fileOpenService = (FileOpenService) ServiceManager.lookup( "javax.jnlp.FileOpenService" ); // exibe diálogo que permite que o usuário selecione vários arquivos FileContents[] contents = fileOpenService.openMultiFileDialog( null, null ); // cria o array para armazenar referências de ImageIcons images = new ImageIcon[ contents.length ]; // carrega as imagens selecionadas for ( int count = 0; count < images.length; count++ ) { // cria o array de bytes para armazenar dados de uma imagem byte[] imageData = new byte[ (int) contents[ count ].getLength() ]; // obtém dados da imagem e cria a imagem contents[ count ].getInputStream().read( imageData ); images[ count ] = new ImageIcon( imageData ); } // for final // esse exemplo assume que todas as imagens têm a mesma largura e altura width = images[ 0 ].getIconWidth(); // obtém a largura de ícone height = images[ 0 ].getIconHeight(); // obtém a altura de ícone } // fim do try catch( Exception e ) { e.printStackTrace(); } // fim do catch } // fim do construtor LogoAnimatorJPanel // exibe a imagem atual public void paintComponent( Graphics g ) { super.paintComponent( g ); // chama a superclasse paintComponent images[ currentImage ].paintIcon( this, g, 0, 0 ); // configura a próxima imagem a ser desenhada apenas se o timer estiver executando if (animationTimer.isRunning() ) currentImage = ( currentImage + 1 ) % images.length; } // fim do método paintComponent // inicia a animação ou reinicia se a janela for reexibida public void startAnimation() { if ( animationTimer == null ) { currentImage = 0; // exibe a primeira imagem // cria o timer animationTimer = new Timer( ANIMATION_DELAY, new TimerHandler() ); animationTimer.start(); // inicia o timer } // fim do if else // animationTimer já existe, reinicia animação {

749

750 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122

Capítulo 24  Multimídia: applets e aplicativos if ( ! animationTimer.isRunning() ) animationTimer.restart(); } // fim de else } // fim do método startAnimation // para o timer da animação public void stopAnimation() { animationTimer.stop(); } // fim do método stopAnimation // retorna o tamanho mínimo de animação public Dimension getMinimumSize() { return getPreferredSize(); } // fim do método getMinimumSize // retorna tamanho preferido da animação public Dimension getPreferredSize() { return new Dimension( width, height ); } // fim do método getPreferredSize // classe interna para tratar eventos de ação do Timer private class TimerHandler implements ActionListener { // responde ao evento do Timer public void actionPerformed( ActionEvent actionEvent ) { repaint(); // pinta o animator novamente } // fim do método actionPerformed } // fim da classe TimerHandler } // fim da classe LogoAnimatorJPanel

Figura 24.4  |  Animando uma série de imagens. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

// Figura 24.5: LogoAnimator.java // Exibindo imagens animadas em um JFrame. import javax.swing.JFrame; public class LogoAnimator { // executa a animação em um JFrame public static void main( String args[] ) { LogoAnimatorJPanel animation = new LogoAnimatorJPanel(); JFrame window = new JFrame( "Animator test" ); // configura a janela window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); window.add( animation ); // adiciona painel ao frame window.pack(); // aumenta a janela apenas o suficiente para sua GUI window.setVisible( true ); // exibe a janela animation.startAnimation(); } // fim de main } // fim da classe LogoAnimator

// inicia a animação

Figura 24.5  |  Exibindo imagens animadas em um JFrame.



24.3  Animação de uma série de imagens

751

Classe LogoAnimatorPanel A classe LogoAnimatorJPanel (Figura 24.4) mantém um array de ImageIcons (declarados na linha 16) que são carregados no construtor (linhas 25–61). O construtor começa utilizando o método openMultiFileDialog do FileOpenService JNLP para exibir um diálogo de seleção de arquivo que permite ao usuário selecionar vários arquivos ao mesmo tempo. Nomeamos nossas imagens de exemplo de tal modo que todas tenham o mesmo nome básico (“deitel”) seguido por um número de dois dígitos de 00-29. Isso assegura que as imagens estão na ordem adequada para a animação. Como no primeiro exemplo deste capítulo, primeiro o usuário é solicitado a dar a permissão, e então o diálogo Open aparece se a permissão tiver sido concedida. O método FileOpenService openMultiFileDialog aceita os mesmos argumentos do método openFileDialog, mas retorna um array de objetos FileContents representando o conjunto de arquivos selecionados pelo usuário. Ao executar esse aplicativo, navegue até a pasta que contém as imagens que você deseja utilizar e selecione-as. Se quiser, você pode utilizar as 30 imagens que fornecemos no subdiretório chamado images desse exemplo. A linha 39 cria o array de ImageIcons, então as linhas 42–51 preenchem o array criando um array byte (linhas 45–46) para os dados da imagem atual, lendo os bytes da imagem no array (linha 49) e criando um objeto ImageIcon do array byte. As linhas 54–55 determinam a largura e a altura da animação a partir do tamanho da primeira imagem no array images — supomos que todas as imagens tenham a mesma largura e altura. Método startAnimation Depois que o construtor LogoAnimatorJPanel carrega as imagens, o método main da Figura 24.5 configura a janela em que a animação aparecerá (linhas 12–17) e a linha 19 chama o método startAnimation do LogoAnimatorJPanel (declarado nas linhas 76–93 da Figura 24.4). Esse método inicia a animação do programa pela primeira vez ou reinicia a animação que o programa parou anteriormente. A animação é controlada por uma instância da classe Timer (do pacote javax.swing). Quando o programa é executado pela primeira vez, o método startAnimation é chamado para começar a animação. Embora forneçamos as funcionalidades para esse método reiniciar a animação se ela tiver sido parada, o exemplo não chama o método para esse propósito. Mas adicionamos as funcionalidades para o caso de o leitor escolher se acrescenta componentes GUI que permitam ao usuário iniciar e parar a animação. Um Timer gera ActionEvents a um intervalo fixo em milissegundos (normalmente especificado como um argumento para o construtor do Timer) e notifica todos seus ActionListeners sempre que ocorrer um ActionEvent. A linha 78 determina se a referência animationTimer de Timer é null. Se for, o método startAnimation está sendo chamado pela primeira vez, e um Timer precisa ser criado para que a animação possa começar. A linha 80 configura currentImage como 0, o que indica que a animação deve iniciar com o primeiro elemento do array images. As linhas 83–84 atribuem um novo objeto Timer a animationTimer. O construtor Timer recebe dois argumentos — o retardo em milissegundos (ANIMATION_DELAY é 50, como especificado na linha 18) e o ActionListener que responderá aos ActionEvents do Timer. Quanto ao segundo argumento, um objeto da classe TimerHandler é criado. Essa classe, que implementa ActionListener, é declarada nas linhas 114–121. A linha 86 chama o método start do objeto Timer para iniciar o Timer. Uma vez iniciado, animationTimer gerará um ActionEvent a cada 50 milissegundos e chamará a rotina de tratamento de evento actionPer­ formed de Timer (linhas 117–120). A linha 119 chama o método repaint de LogoAnimatorJPanel para agendar uma chamada para o método paintComponent de LogoAnimatorJPanel (linhas 64–73). Lembre-se de que qualquer subclasse de JComponent que desenhe deve fazer isso em seu método paintComponent. Lembre-se de que a primeira instrução em qualquer método paintComponent deve ser uma chamada ao método paintComponent da superclasse, para assegurar que os componentes Swing sejam exibidos corretamente. Se a animação já tiver iniciado antes, nosso Timer foi criado e a condição na linha 78 é avaliada como false. O programa continua com as linhas 90–91, que reiniciam a animação que o programa parou anteriormente. A condição if na linha 90 utiliza o método Timer isRunning para determinar se o Timer está executando (isto é, gerando eventos). Se não estiver executando, a linha 91 chama o método Timer restart para indicar que Timer deve começar a gerar eventos novamente. Uma vez que isso ocorre, o método actionPerformed (o handler de evento do Timer) é novamente chamado em intervalos regulares. Método paintComponent A linha 68 chama o método paintIcon do ImageIcon para exibir a imagem armazenada no elemento currentImage no array. Os argumentos representam o Component em que desenhar (this), o objeto Graphics que realiza o desenho (g) e as coordenadas do canto superior esquerdo da imagem. As linhas 71–72 determinam se o animationTimer está executando e, se estiver, preparam a próxima imagem a ser exibida incrementando currentImage por 1. O cálculo restante assegura que o valor de currentImage está configurado como 0 (para repetir a sequência de animação) quando ele é incrementado depois do último índice de elemento no array. A instrução if assegura que a mesma imagem seja exibida se paintComponent for chamado enquanto o Timer é parado. Isso pode ser útil se for fornecida uma GUI que permita ao usuário iniciar e parar a animação. Por exemplo, se a animação for interrompida e o usuário cobri-la com outra janela, e depois descobri-la, o método paintComponent será chamado. Nesse caso, não queremos que a animação exiba a próxima imagem (porque a animação foi interrompida). Simplesmente queremos que a janela exiba a mesma imagem até a animação ser reiniciada.

752

Capítulo 24  Multimídia: applets e aplicativos

Método stopAnimation O método stopAnimation (linhas 96–99) para a animação chamando o método Timer stop para indicar que o Timer deve parar de gerar os eventos. Isso impede que actionPerformed chame repaint para iniciar a pintura da próxima imagem no array. Assim como com o exemplo de reiniciar a animação, esse exemplo define, mas não utiliza, o método stopAnimation. Fornecemos esse método para propósitos de demonstração, ou para permitir ao usuário modificar esse exemplo a fim de parar e reiniciar a animação.

Observação de engenharia de software 24.1 Ao criar uma animação para uso em um applet, forneça um mecanismo para desativar a animação quando o usuário navegar para uma nova página Web diferente daquela em que o applet de animação reside.

Métodos getPreferredSize e getMinimumSize Estendendo a classe JPanel, estamos criando um novo componente GUI. Portanto, devemos assegurar que ele funciona como os outros componentes para propósitos de layout. Em geral, os gerenciadores de layout utilizam o método getPreferredSize de um componente (herdado da classe java.awt.Component) para determinar a largura e a altura preferidas do componente. Se um novo componente tiver uma largura e altura preferidas, ele deve anular o método getPreferredSize (linhas 108–111) para retornar essa largura e altura como um objeto da classe Dimension (pacote java.awt). A classe Dimension representa a largura e altura de um componente GUI. Nesse exemplo, as imagens que fornecemos têm 160 pixels de largura e 80 pixels de altura, portanto, o método getPreferredSize retornaria um objeto Dimension contendo os números 160 e 80 (se você utilizasse essas imagens).

Observações sobre a aparência e comportamento 24.1 O tamanho padrão de um objeto JPanel é de 10 pixels de largura e 10 pixels de altura.

Observações sobre a aparência e comportamento 24.2 Ao subclassificar JPanel (ou qualquer outro JComponent), anule o método getPreferredSize se o novo componente precisar ter uma largura e uma altura específicas preferidas.

As linhas 102–105 sobrescrevem o método getMinimumSize. Esse método determina a largura e altura mínima do componente. Como com o método getPreferredSize, novos componentes devem sobrescrever o método getMinimumSize (também herdado da classe Component). O método getMinimumSize simplesmente chama getPreferredSize (uma prática de programação comum) para indicar que o tamanho mínimo e o preferido são os mesmos. Alguns gerenciadores de layout ignoram as dimensões especificadas por esses métodos. Por exemplo, as regiões NORTH e SOUTH de um BorderLayout utilizam apenas a altura preferida do componente.

Observações sobre a aparência e comportamento 24.3 Se um novo componente GUI tiver uma largura e altura mínimas (isto é, se as pequenas dimensões resultarem em um componente ineficiente na tela), sobrescreva o método getMinimumSize para retornar a largura e a altura mínimas como uma instância da classe Dimension.

Observações sobre a aparência e comportamento 24.4 Para muitos componentes GUI o método getMinimumSize é implementado para retornar o resultado de uma chamada ao método getPreferredSize desse componente.

Compilando o aplicativo A compilação e a execução desse aplicativo requerem o arquivo jnlp.jar que contém as JNLP APIs. Para compilar o aplicativo, utilize o seguinte comando: javac -classpath CaminhoParaOArquivoJnlpJar *.java

onde CaminhoParaOArquivoJnlpJar inclui tanto o caminho como o nome de arquivo jnlp.jar.

24.4 Mapas de imagem

753

Empacotando o aplicativo para uso com Java Web Start Para empacotar o aplicativo para uso com Java Web Start, você deve criar um arquivo JAR que contém o código e o arquivo jnlp.jar do applet. Para fazer isso, utilize o comando jar cvf LogoAnimator.jar *.class CaminhoParaOArquivoJnlpJar

onde CaminhoParaOArquivoJnlpJar inclui tanto o caminho como o nome de arquivo jnlp.jar.

Documento JNLP para o applet LoadImageAndScale O documento JNLP da Figura 24.6 é semelhante ao da Figura 24.2. O único recurso novo nesse documento é o elemento application­ desc (linhas 16–19), que especifica o nome do aplicativo e sua classe principal. Para executar esse aplicativo, utilize o comando javaws LogoAnimator.jnlp

Lembre-se de que você também pode executar aplicativos Java Web Start via um link em uma página Web, como mostramos na Figura 23.13. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20



LogoAnimator Deitel







Figura 24.6 | Documento JNLP para o applet LoadImageAndScale.

24.4 Mapas de imagem Os mapas de imagem são comumente utilizados para criar páginas Web interativas. Um mapa de imagem é uma imagem com áreas ativas (hot areas) em que o usuário pode clicar para realizar uma tarefa, como carregar uma página Web diferente em um navegador. Quando o usuário posiciona o ponteiro do mouse sobre uma área ativa, normalmente uma mensagem descritiva aparece na área de status do navegador ou em uma dica de ferramenta. A Figura 24.7 carrega uma imagem contendo vários dos ícones de dica de programação utilizados neste livro. O programa permite ao usuário posicionar o ponteiro do mouse sobre um ícone para exibir uma mensagem descritiva associada com ele. O handler de evento mouseMoved (linhas 39–43) aceita as coordenadas do mouse e as passa para o método translateLocation (linhas 58–69). O método translateLocation testa as coordenadas para determinar o ícone sobre o qual o mouse foi posicionado quando o evento mouseMoved ocorreu — o método então retorna uma mensagem que indica o que o ícone representa. Essa mensagem é exibida na barra de status do contêiner de applets utilizando o método showStatus da classe Applet. 1 2 3 4 5 6 7

// Figura 24.7: ImageMap.java // Mapa de imagem. import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.Graphics; import javax.swing.ImageIcon;

754 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

Capítulo 24  Multimídia: applets e aplicativos import javax.swing.JApplet; public class ImageMap extends JApplet { private ImageIcon mapImage; private static final String captions[] = { "Common Programming Error", "Good Programming Practice", "Look-and-Feel Observation", "Performance Tip", "Portability Tip", "Software Engineering Observation", "Error-Prevention Tip" }; // configura ouvintes de mouse public void init() { addMouseListener( new MouseAdapter() // classe interna anônima { // indica quando o ponteiro do mouse sai da área do applet public void mouseExited( MouseEvent event ) { showStatus( "Pointer outside applet" ); } // mouseExited fim do método } // fim da classe interna anônima ); // fim da chamada para addMouseListener addMouseMotionListener( new MouseMotionAdapter() // classe interna anônima { // determina ícone sobre o qual o mouse aparece public void mouseMoved( MouseEvent event ) { showStatus( translateLocation( event.getX(), event.getY() ) ); } // fim do método mouseMoved } // fim da classe interna anônima ); // fim da chamada para addMouseMotionListener mapImage = new ImageIcon( "icons.png" ); // obtém a imagem } // fim do método init // exibe mapImage public void paint( Graphics g ) { super.paint( g ); mapImage.paintIcon( this, g, 0, 0 ); } // fim do método paint // retorna legenda de dica com base nas coordenadas do mouse public String translateLocation( int x, int y ) { // se coordenar fora da imagem, retorna imediatamente if ( x >= mapImage.getIconWidth() || y >= mapImage.getIconHeight() ) return ""; // determina o número de ícone (0 - 6) double iconWidth = ( double ) mapImage.getIconWidth() / 7.0; int iconNumber = ( int )( ( double ) x / iconWidth ); return captions[ iconNumber ]; // retorno à legenda de ícone apropriada } // fim do método translateLocation } // fim da classe ImageMap



24.4  Mapas de imagem

755

Figura 24.7  |  Mapa de imagem.

Clicar no applet da Figura 24.7 não produzirá nenhuma ação. No Capítulo 27, Rede, discutimos as técnicas para carregar outra página Web em um navegador via URLs e a interface AppletContext. Utilizando essas técnicas, esse applet poderia associar cada ícone com um URL que o navegador exibiria quando o usuário clicasse no ícone.

756

Capítulo 24

Multimídia: applets e aplicativos

24.5 Carregando e reproduzindo clipes de áudio Programas Java podem manipular e reproduzir clipes de áudio. Os usuários podem capturar seus próprios clipes de áudio e muitos clipes estão disponíveis em produtos de software e Internet. Seu sistema precisa ser equipado com hardware de áudio (alto-falantes e uma placa de som) para ser capaz de reproduzir os clipes de áudio. O Java fornece vários mecanismos para reproduzir sons em um applet. Os dois mais simples são o método play de Applet e o método play da interface AudioClip. As capacidades adicionais de áudio estão disponíveis nas APIs Java Media Framework e Java Sound. Se você quiser reproduzir um som uma vez em um programa, o método Applet play carrega o som e o reproduz uma vez, depois o som é marcado para coleta de lixo. O método Applet play tem duas versões: public void play( URL location, String soundFileName ); public void play( URL soundURL );

A primeira versão carrega o clipe de áudio armazenado no arquivo soundFileName a partir da location e reproduz o som. O primeiro argumento é normalmente uma chamada ao método getDocumentBase ou getCodeBase do applet. O método getDocumentBase retorna a localização do arquivo HTML que carregou o applet. (Se o applet estiver em um pacote, o método retorna a localização do pacote ou o arquivo JAR que contém o pacote.) O método getCodeBase indica a localização do arquivo .class do applet. A segunda versão de método play aceita um URL que contém a localização e o nome de arquivo do clipe de áudio. A instrução play( getDocumentBase(), "hi.au" );

carrega o clipe de áudio no arquivo hi.au e reproduz o clipe uma vez. O mecanismo de som que reproduz os clipes de áudio suporta vários formatos de arquivo de áudio, incluindo formato de arquivo Sun Audio (extensão .au), formato de arquivo Windows Wave (extensão .wav), formato de arquivo Macintosh AIFF (extensões .aif ou .AIFF) e o formato de arquivo Musical Instrument Digital Interface (MIDI) (extensões .mid ou .rmi). A API Java Media Framework ( JMF) e a Java Sound API suportam formatos adicionais. O programa da Figura 24.8 demonstra carregamento e reprodução de um AudioClip (pacote java.applet). Essa técnica é mais flexível que o método Applet play. Um applet pode utilizar um AudioClip para armazenar o áudio para uso repetido em toda execução de um programa. O método Applet getAudioClip tem duas formas que aceitam os mesmos argumentos que o método play descrito anteriormente. O método getAudioClip retorna uma referência para um AudioClip. Um AudioClip tem três métodos — play, loop e stop. Como mencionado anteriormente, o método play reproduz o clipe de áudio uma vez. O método loop faz loops continuamente pelo clipe de áudio no segundo plano. O método stop termina um clipe de áudio que atualmente está reproduzindo. No programa, cada um desses métodos é associado com um botão no applet. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

// Figura 24.8: LoadAudioAndPlay.java // Carregando e executando um AudioClip. import java.applet.AudioClip; import java.awt.event.ItemListener; import java.awt.event.ItemEvent; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.FlowLayout; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JComboBox; public class LoadAudioAndPlay extends JApplet { private AudioClip sound1, sound2, currentSound; private JButton playJButton, loopJButton, stopJButton; private JComboBox soundJComboBox; // carrega a imagem quando o applet começa a executar public void init() { setLayout( new FlowLayout() ); String choices[] = { "Welcome", "Hi" }; soundJComboBox = new JComboBox( choices ); // cria JComboBox soundJComboBox.addItemListener( new ItemListener() // classe interna anônima {



24.5  Carregando e reproduzindo clipes de áudio

31

// interrompe o som e muda para o som selecionado pelo usuário

32

public void itemStateChanged( ItemEvent e )

33

{

34

currentSound.stop();

35

currentSound = soundJComboBox.getSelectedIndex() == 0 ?

36

sound1 : sound2;

37

} // fim do método itemStateChanged

38

} // fim da classe interna anônima

39

); // fim da chamada de método addItemListener

40 41

add( soundJComboBox ); // adiciona JComboBox o applet

42 43

// configura o handler de evento de botão e botões

44

ButtonHandler handler = new ButtonHandler();

45 46

// cria o JButton Play

47

playJButton = new JButton( "Play" );

48

playJButton.addActionListener( handler );

49

add( playJButton );

50 51

// cria o JButton Loop

52

loopJButton = new JButton( "Loop" );

53

loopJButton.addActionListener( handler );

54

add( loopJButton );

55 56

// cria o JButton Stop

57

stopJButton = new JButton( "Stop" );

58

stopJButton.addActionListener( handler );

59

add( stopJButton );

60 61

// carrega sons e configura currentSound

62

sound1 = getAudioClip( getDocumentBase(), "welcome.wav" );

63

sound2 = getAudioClip( getDocumentBase(), "hi.au" );

64 65

currentSound = sound1; } // fim do método init

66 67

// para o som quando o usuário muda de página Web

68

public void stop()

69

{

70 71

currentSound.stop(); // interrompe o AudioClip } // fim do método stop

72 73

// classe interna private para tratar eventos de botão

74

private class ButtonHandler implements ActionListener

75

{

76

// processa, reproduz, faz loop e interrompe eventos de botão

77

public void actionPerformed( ActionEvent actionEvent )

78

{

79 80 81 82 83 84 85

if ( actionEvent.getSource() == playJButton ) currentSound.play(); // reproduz o AudioClip uma vez else if ( actionEvent.getSource() == loopJButton ) currentSound.loop(); // reproduz o AudioClip continuamente else if ( actionEvent.getSource() == stopJButton ) currentSound.stop(); // interrompe o AudioClip } // fim do método actionPerformed

86

} // fim da classe ButtonHandler

87

} // fim da classe LoadAudioAndPlay

757

758

Capítulo 24

Multimídia: applets e aplicativos

Figura 24.8 | Carregamento e reproduzir um AudioClip.

As linhas 62–63 no applet init utilizam o método getAudioClip para carregar dois arquivos de áudio — um Windows Wave (welcome.wav) e um Sun Audio (hi.au). O usuário pode selecionar qual clipe de áudio reproduzir a partir do JComboBox soundJCom­ boBox. Observe que o método stop do applet é sobrescrito nas linhas 68–71. Quando o usuário alterna páginas Web, o contêiner de applets chama o método stop do applet. Isso permite que o applet pare de reproduzir o clipe de áudio. Caso contrário, continua a reproduzir no segundo plano — mesmo se o applet não for exibido no navegador. Isso não é necessariamente um problema, mas pode irritar o usuário se o clipe de áudio estiver fazendo um loop. O método stop é fornecido aqui como uma conveniência para o usuário.

Observações sobre a aparência e comportamento 24.5 Ao reproduzir clipes de áudio em um applet ou aplicativo, forneça um mecanismo para o usuário desativar o áudio.

24.6 Reproduzindo vídeo e outros tipos de mídia com o Java Media Framework Um vídeo simples pode concisa e efetivamente transportar muitas informações. Reconhecendo o valor de introduzir capacidades multimídia extensíveis ao Java, a Sun Microsystems, a Intel e a Silicon Graphics se uniram para produzir o aplicativo Java Media Framework ( JMF). Utilizando a JMF API, os programadores podem criar aplicativos Java que reproduzem, editam, transmitem por stream e capturam muitos tipos de mídia populares. Os recursos do JMF são bem extensos. Esta seção apresenta resumidamente alguns formatos de mídia populares e demonstra a reprodução de vídeo utilizando a JMF API. A IBM e a Sun desenvolveram a última especificação JMF — a versão 2.0. A Sun também fornece uma implementação de referência da especificação JMF — JMF 2.1.1e — que suporta tipos de arquivo de mídia como Microsoft Audio/Video Interleave (.avi), filmes Macromedia Flash 2 (.swf), Future Splash (.spl), MPEG Layer 3 Audio (.mp3), Musical Instrument Digital Interface (MIDI; extensões .mid ou .rmi), MPEG-1 videos (.mpeg.mpg), QuickTime.mov, formato de arquivo Sun Audio (extensão .au) e formato de arquivo AIFF da Macintosh (extensões .aif ou .aiff). Você já viu alguns desses tipos de arquivo. Atualmente, a JMF está disponível como uma extensão separada do Java 2 Software Development Kit. O download da implementação JMF mais recente (2.1.1e) pode ser feito a partir de: java.sun.com/javase/technologies/desktop/media/jmf/2.1.1/ download.html

[Nota: Preste atenção ao local em que você instala o Java Media Framework no computador. Para compilar e executar esse aplicativo, você deve incluir no caminho de classe o arquivo jmf.jar que é instalado com o Java Media Framework. Lembre-se de que você pode especificar o caminho de classe tanto com o comando javac quanto com o comando java por meio da opção de linha de comando ­classpath.] O site Web da JMF fornece versões da JMF que tiram proveito dos recursos de desempenho de certas plataformas. Por exemplo, o JMF Windows Performance Pack fornece extensa mídia e suporte de dispositivo para programas Java que executam em plataformas Microsoft Windows. O site do JMF (java.sun.com/javase/technologies/desktop/media/jmf/) tem informações e recursos para programadores JMF.

Criando um Media Player simples A JMF oferece vários mecanismos para reproduzir a mídia. O mais simples é utilizar objetos que implementam a interface Player declarada no pacote javax.media. O pacote javax.media e seus subpacotes contêm as classes que compõem o Java Media Framework. Para reproduzir um clipe de mídia, você deve primeiro criar um objeto URL que o referencia. Então passe o URL como um argumento para o método static createRealizedPlayer da classe Manager a fim de obter um Player para o clipe de mídia. A classe Manager declara os métodos de utilitários para acessar os recursos do sistema a fim de reproduzir e manipular mídia. A Figura 24.9 declara um JPanel que demonstra alguns desses métodos.



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

24.6  Reproduzindo vídeo e outros tipos de mídia com o Java Media Framework

759

// Figura 24.9: MediaPanel.java // JPanel que reproduz um arquivo de mídia de um URL. import java.awt.BorderLayout; import java.awt.Component; import java.io.IOException; import java.net.URL; import javax.media.CannotRealizeException; import javax.media.Manager; import javax.media.NoPlayerException; import javax.media.Player; import javax.swing.JPanel; public class MediaPanel extends JPanel { public MediaPanel( URL mediaURL ) { setLayout( new BorderLayout() ); // utiliza um BorderLayout // Utiliza componentes leves para compatibilidade Swing Manager.setHint( Manager.LIGHTWEIGHT_RENDERER, true ); try { // cria um player para reproduzir a mídia especificada no URL Player mediaPlayer = Manager.createRealizedPlayer( mediaURL ); // obtém os componentes para o vídeo e os controles de reprodução Component video = mediaPlayer.getVisualComponent(); Component controls = mediaPlayer.getControlPanelComponent(); if ( video != null ) add( video, BorderLayout.CENTER ); // adiciona o componente de vídeo if ( controls != null ) add( controls, BorderLayout.SOUTH ); // adiciona os controles mediaPlayer.start(); // inicia a reprodução do clipe de mídia } // fim do try catch ( NoPlayerException noPlayerException ) { System.err.println( "No media player found" ); } // fim do catch catch ( CannotRealizeException cannotRealizeException ) { System.err.println( "Could not realize media player" ); } // fim do catch catch ( IOException iOException ) { System.err.println( "Error reading from the source" ); } // fim do catch } // fim do construtor MediaPanel } // fim da classe MediaPanel

Figura 24.9  |  JPanel que reproduz um arquivo de mídia de um URL.

O construtor (linhas 15–51) configura o JPanel para reproduzir o arquivo de mídia especificado por um parâmetro URL do construtor. MediaPanel utiliza um BorderLayout (linha 17). A linha 20 invoca o método static setHint para configurar o flag Manager.LIGHTWEIGHT_RENDERER como true. Isso instrui o Manager a utilizar um renderizador de peso leve que é compatível com os componentes Swing leves, em oposição ao renderizador pesado padrão. No bloco try (linhas 22–38), a linha 25 invoca o método static createRealizedPlayer da classe Manager para criar e realizar um Player que reproduz o arquivo de mídia. Quando um Player é realizado, ele identifica os recursos do sistema de que ele precisa para reproduzir a mídia. Dependendo do arquivo, realizar pode ser um processo que consome recursos e tempo. O método createRealizedPlayer lança três exceções verificadas, NoPlayerException, CannotRealizeException e IOException. Uma NoPlayerException indica que o sistema não pôde localizar um player que pudesse reproduzir o formato de arquivo. Uma CannotRealizeException indica que o sistema não pôde identificar adequadamente os recursos que o arquivo de mídia precisa. Uma IOException indica que houve um erro durante a leitura do arquivo. Essas exceções são tratadas no bloco catch nas linhas 39–50.

760

Capítulo 24  Multimídia: applets e aplicativos

A linha 28 invoca o método getVisualComponent de Player para obter um Component que exibe o aspecto visual (geralmente vídeo) do arquivo de mídia. A linha 29 invoca o método getControlPanelComponent de Player para obter um Component que fornece controles de reprodução e mídia. Esses componentes são atribuídos às variáveis locais video e controls, respectivamente. As instruções if nas linhas 31–32 e linhas 34–35 adicionam o video e os controls se eles existirem. O video Component é adicionado à região CENTER (linha 32), então ele preenche qualquer espaço disponível no JPanel. O controls Component, que é adicionado à região SOUTH, em geral fornece os seguintes controles: 1 . Um controle deslizante de posicionamento para pular para certos pontos no clipe de mídia. 2. Um botão de pausa. 3. Um botão de volume que fornece controle de volume clicando-se à direita e uma função de mudo clicando-se à esquerda. 4. Um botão de propriedades de mídia que fornece informações detalhadas de mídia clicando-se à esquerda e controle de velocidade de projeção clicando-se à direita. A linha 37 chama o método Player start para iniciar a reprodução do arquivo de mídia. As linhas 39–50 tratam as várias exceções que o createRealizedPlayer lança. O aplicativo na Figura 24.10 exibe um diálogo JFileChooser para o usuário escolher um arquivo de mídia. Ele, então, cria um Me­ diaPanel que executa o arquivo selecionado e cria um JFrame para exibir o MediaPanel. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

// Figura 24.10: MediaTest.java // Aplicativo de teste que cria um MediaPanel de um arquivo selecionado pelo usuário. import java.io.File; import java.net.MalformedURLException; import java.net.URL; import javax.swing.JFileChooser; import javax.swing.JFrame; public class MediaTest { // carrega o aplicativo public static void main( String args[] ) { // cria um chooser de arquivo JFileChooser fileChooser = new JFileChooser(); // mostra diálogo de arquivo aberto int result = fileChooser.showOpenDialog( null ); if ( result == JFileChooser.APPROVE_OPTION ) // usuário escolheu um arquivo { URL mediaURL = null; try { // obtém o arquivo como URL mediaURL = fileChooser.getSelectedFile().toURI().toURL(); } // fim do try catch ( MalformedURLException malformedURLException ) { System.err.println( "Could not create URL for the file" ); } // fim do catch if ( mediaURL != null ) // exibe somente se houver um URL válido { JFrame mediaTest = new JFrame( "Media Tester" ); mediaTest.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); MediaPanel mediaPanel = new MediaPanel( mediaURL ); mediaTest.add( mediaPanel );

} } // } // fim } // fim da

mediaTest.setSize( 300, 300 ); mediaTest.setVisible( true ); // fim do if interno fim do if externo de main classe MediaTest

24.7 Conclusão

761

Figura 24.10 | Aplicativo de teste que cria um MediaPanel de um arquivo selecionado pelo usuário.

O método main (linhas 12–46) atribui um novo JFileChooser à variável local fileChooser (linha 15), mostra um diálogo de arquivo aberto (linha 18) e atribui o valor de retorno ao result. A linha 20 verifica result para determinar se o usuário escolheu um arquivo. Para criar um Player a fim de reproduzir o arquivo selecionado de mídia, você deve converter o objeto File retornado pelo JFile­ Chooser em um objeto URL. O método toURI da classe File retorna um URI que aponta para o File no sistema. Então, invocamos o método toURL da classe URI para obter o arquivo URL. A instrução try (linhas 24–32) cria um URL para o arquivo selecionado e o atribui à mediaURL. A instrução if nas linhas 34–44 verifica se mediaURL não é null e cria os componentes GUI para reproduzir a mídia.

24.7 Conclusão Neste capítulo, você aprendeu a fazer aplicativos mais interessantes incluindo som, imagens, imagens gráficas e vídeo. Introduzimos as capacidades multimídia do Java, incluindo a Java Media Framework API e a Java Sound API. Utilizou a classe ImageIcon para exibir e manipular imagens armazenadas em arquivos e aprendeu sobre os diferentes formatos de imagem suportados pelo Java. Usou o JNLP Fi­ leOpenService para permitir ao usuário de um aplicativo Java Web Start selecionar arquivos do sistema de arquivos local e utilizou fluxos para carregar o conteúdo desses arquivos para uso nos seus programas. Criou a animação exibindo uma série de imagens em uma ordem específica. Empregou mapas de imagem para tornar um aplicativo mais interativo. Em seguida, aprendeu a carregar clipes de áudio e a reproduzi-los uma vez ou em um loop contínuo. O capítulo concluiu com uma demonstração de carregar e reproduzir vídeo. No próximo capítulo, você continuará o estudo de conceitos GUI, baseando-se nas técnicas aprendidas no Capítulo 14.

24.8 Recursos da Web www.nasa.gov/multimedia/highlights/index.html

A NASA Multimedia Gallery contém uma grande variedade de imagens, clipes de áudio e videoclipes que você pode descarregar e utilizar para testar seus programas de multimídia Java. commons.wikimedia.org/wiki/Main_Page

O site Wikimedia Commons fornece acesso a milhões de arquivos de mídia. www.anbg.gov.au/anbg/index.html

O site da Web Australian National Botanic Gardens fornece links para muitos sons de animais. Experimente, por exemplo, o link Common Birds na seção “Animals in the Gardens”.

762

Capítulo 24  Multimídia: applets e aplicativos

www.thefreesite.com

Esse site tem links para sons e clipes de arte gratuitos. www.soundcentral.com

SoundCentral fornece clipes de áudio em formatos WAV, AU, AIFF e MIDI. www.animationfactory.com

Animation Factory fornece milhares de animações GIF gratuitamente para uso pessoal. www.clipart.com

Esse site é um serviço baseado em assinatura para imagens e sons. java.sun.com/developer/techDocs/hi/repository/

O Java look-and-feel Graphics Repository fornece imagens projetadas para utilização em uma Swing GUI, incluindo imagens de botão de barra de ferramentas. www.freebyte.com/graphicprograms/

Este guia contém links para vários programas de softwares de imagens gráficas gratuitos. O software pode ser utilizado para modificar imagens e desenhar imagens gráficas. graphicssoft.about.com/od/pixelbasedfreewin/

Esse site fornece links para programas gratuitos de imagens gráficas projetados para utilização em máquinas Windows.

Referências de API multimídia do Java java.sun.com/javase/technologies/desktop/media/

A página principal das Java Media APIs. java.sun.com/products/java-media/sound/

A home page da Java Sound API. A API Java Sound fornece recursos para reproduzir e gravar áudio. java3d.dev.java.net/

A home page da Java 3D API. Essa API pode ser utilizada para produzir imagens tridimensionais típicas dos videogames de hoje. java.sun.com/products/java-media/speech/

A Java Speech API permite aos programas realizar síntese e reconhecimento de fala. freetts.sourceforge.net/docs/index.php

FreeTTS é uma implementação da Java Speech API.

Resumo Seção 24.2  Carregando, exibindo e dimensionando imagens • Os construtores da classe ImageIcon podem receber argumentos de vários formatos diferentes, incluindo um array de bytes que contém os bytes de uma imagem, uma Image (pacote java.awt) já carregada na memória, ou uma String ou um URL representando a localização da imagem. • O Java suporta vários formatos de imagem, incluindo Graphics Interchange Format (GIF), Joint Photographic Experts Group ( JPEG) e Portable Network Graphics (PNG). Em geral, os nomes de arquivo desses tipos terminam em .gif, .jpg (ou .jpeg) e .png, respectivamente. • Os programas Java Web Start podem acessar o sistema de arquivos local via JNLP APIs do pacote javax.jnlp. Você pode utilizar o FileOpenService do JNLP para solicitar acesso limitado ao sistema de arquivos local. • O método static lookup da classe JNLP ServiceManager obtém uma referência ao FileOpenService. Como outros serviços são fornecidos pelo JNLP, esse método retorna uma referência Object, que você deve fazer uma coerção para o tipo apropriado. • O método openFileDialog do método FileOpenService exibe um diálogo de seleção do arquivo. Isso faz o Java Web Start pedir ao usuário que aprove a solicitação de acesso de sistema de arquivos local do programa. Se o usuário der a permissão, o diálogo Open é exibido. O método openFileDialog tem dois parâmetros — uma String para sugerir um diretório a ser aberto e um array String de extensões de arquivo aceitáveis.



Resumo

763

• Quando o usuário selecionar um arquivo de imagem e clicar no botão Open do diálogo, o método openFileDialog retornará um objeto FileCon­ tents, que por razões de segurança não fornece ao programa acesso ao local exato do arquivo no disco. Em vez disso, o programa pode obter um InputStream e ler os bytes do arquivo. • O método FileContents getLength retorna o número de bytes no arquivo. • Os métodos Component getWidth e getHeight retornam a largura e a altura de um Component, respectivamente. • Os métodos ImageIcon getIconWidth e getIconHeight retornam a largura e a altura de uma imagem, respectivamente. • A classe Graphics fornece métodos drawImage sobrecarregados, um do qual exibe uma versão redimensionada de um Image. O primeiro argumento é a Image a ser desenhada. O segundo e terceiro argumentos representam as coordenadas de canto superior esquerdo da Image. O quarto e quinto argumentos especificam a largura e a altura da Image, respectivamente. O último argumento é uma referência a um ImageObserver — uma interface implementada pela classe Component. • É possível que um programa tente exibir uma imagem antes de ela ter sido completamente carregada (ou de o download ter sido concluído). Enquanto uma Image é carregada, o ImageObserver recebe notificações e atualiza a imagem na tela de acordo com a necessidade. • Um applet arrastável pode ser arrastado para fora da janela de navegador mantendo a tecla Alt pressionada e arrastando o applet com o mouse. O applet então continuará executando, mesmo se o navegador for fechado. Clicar na caixa de fechamento do applet quando ele estiver executando fora do navegador faz com que o applet volte para a janela de navegador se ela ainda estiver aberta, ou termine, caso contrário.

Seção 24.3  Animação de uma série de imagens • O método openMultiFileDialog do FileOpenService exibe um diálogo de seleção de arquivo que permite ao usuário selecionar múltiplos arquivos ao mesmo tempo. O método FileOpenService openMultiFileDialog aceita os mesmos argumentos que o método openFileDialog, mas retorna um array de objetos FileContents representando o conjunto de arquivos selecionados pelo usuário. • Objetos Timer geram ActionEvents a intervalos fixos de milissegundos. O construtor Timer recebe um retardo em milissegundos e um ActionLis­ tener. O método Timer start inicia o Timer. O método stop indica que o Timer deve parar de gerar eventos. O método restart indica que o Timer deve começar a gerar eventos novamente. • O método ImageIcon paintIcon exibe a imagem ImageIcon. O método requer quatro argumentos — uma referência para o Component em que a imagem será exibida, uma referência para o objeto Graphics utilizado para renderizar a imagem, a coordenada x do canto superior esquerdo da imagem e a coordenada y do canto superior esquerdo da imagem.

Seção 24.4  Mapas de imagem • Um mapa de imagem é uma imagem que tem áreas ativas (hot areas) em que o usuário pode clicar para realizar uma tarefa, como carregar uma página Web diferente em um navegador.

Seção 24.5  Carregando e reproduzindo clipes de áudio • O método applet play tem duas formas: public void play( URL location, String soundFileName ); public void play( URL soundURL );

Uma versão carrega o clipe de áudio armazenado no arquivo soundFileName da location e reproduz o som. A outra versão aceita um URL que contém a localização e o nome de arquivo do clipe de áudio. • O método Applet getDocumentBase indica a localização do arquivo de HTML que carregou o applet. O método getCodeBase indica para um applet onde está localizado o arquivo .class. • O dispositivo de som que reproduz clipes de áudio suporta vários formatos de arquivo de áudio, incluindo formato de arquivo Sun Audio (extensão .au), formato de arquivo Wave do Windows (extensão.wav), formato de arquivo AIFF da Macintosh (extensões .aif ou .aiff) e formato de arquivo Musical Instrument Digital Interface (MIDI) (extensões.mid ou .rmi). O Java Media Framework ( JMF) suporta formatos adicionais. • O método applet getAudioClip tem duas formas que aceitam os mesmos argumentos que o método play. O método getAudioClip retorna uma referência a um AudioClip. AudioClips têm três métodos — play, loop e stop. O método play reproduz o clipe de áudio uma vez. O método loop faz um loop contínuo do clipe de áudio. O método stop termina um clipe de áudio que atualmente está reproduzindo.

Seção 24.6  Reproduzindo vídeo e outros tipos de mídia com o Java Media Framework • A Sun Microsystems, a Intel e a Silicon Graphics trabalharam em conjunto para produzir a Java Media Framework ( JMF). • O pacote javax.media e seus subpacotes contêm as classes Java Media Framework. • A classe Manager declara os métodos para acessar os recursos do sistema a fim de reproduzir e manipular mídia. • O método toURI da classe File retorna um URI que aponta para o File no sistema.

764

Capítulo 24  Multimídia: applets e aplicativos

Terminologia .aif, extensão de arquivo, 756, 758

getWidth, método da classe Component, 747

.aiff, extensão de arquivo, 756, 758

.gif, extensão de arquivo, 744

AIFF, formato de arquivo do Macintosh (extensões .aif ou .aiff), 756 applet arrastável, 748 área ativa, 753 .au, extensão de arquivo, 756, 758 AudioClip, interface, 756 .avi, extensão de arquivo, 758 CannotRealizePlayerException, exceção, 759 .class, extensão de arquivo, 756 clipe de áudio, 756 createRealizedPlayer, método da classe Manager, 758 Dimension, classe, 752 drawImage, método da classe Graphics, 747 FileOpenService, interface, 743 Future Splash (.spl), formato de arquivo, 758 getAudioClip, método da classe Applet, 756 getCodeBase, método da classe Applet, 756 getControlPanelComponent, método da interface Player, 760 getDelay, método da classe Timer, 765 getHeight, método da classe Component, 747 getIconHeight, método da classe ImageIcon, 747 getIconWidth, método da classe ImageIcon, 747 getImage, método da classe ImageIcon, 747 getLength, método da interface FileContents, 746 getMinimumSize, método da classe Component, 752 getPreferredSize, método da classe Component, 752 getVisualComponent, método da interface Player, 760

Graphics Interchange Format (GIF), 744 Image, classe, 744 ImageObserver, interface, 747 isRunning, método da classe Timer, 751 Java 3D API, 743 Java Advanced Imaging API, 743 Java Image I/O API, 743 Java Media Framework ( JMF), API, 743 Java Sound API, 743 Java Speech API, 743 javax.media, pacote, 758 Joint Photographic Experts Group ( JPEG), 744 .jpeg, extensão de arquivo, 744 .jpg, extensão de arquivo, 744 LIGHTWEIGHT_RENDERER, constante da classe Manager, 759 lookup, método da classe ServiceManager, 746 loop, método da interface AudioClip, 756 Macromedia Flash, filmes (.swf), 758 Manager, classe, 758 mapa de imagem, 753 Microsoft Audio/Video Interleave (.avi), formato de arquivo, 758 .mid, extensão de arquivo, 756, 758 .mov, extensão de arquivo, 758 .mp3, extensão de arquivo, 758 MPEG-1, vídeos (extensões .mpeg ou .mpg), 758 .mpeg, extensão de arquivo, 758 MPEG Layer 3 Audio (.mp3), formato de arquivo, 758 .mpg, extensão de arquivo, 758 multimídia, 743

Musical Instrument Digital Interface (MIDI), formato de arquivo (extensões .mid ou .rmi), 756 NoPlayerException, exceção, 759 openFileDialog, método da interface FileOpenService, 746 openMultiFileDialog, método da interface FileOpenService, 751 paintIcon, método da classe ImageIcon, 751 play, método da classe Applet, 756 play, método da interface AudioClip, 756 Player, interface, 758 .png, extensão de arquivo, 744 Portable Network Graphics (PNG), 744 QuickTime (.mov), formato de arquivo, 758 read, método da classe InputStream, 746 restart, método da classe Timer, 751 .rmi, extensão de arquivo, 756, 758 ServiceManager, classe, 746 setDelay, método da classe Timer, 765 setHint, método da classe Manager, 759 showStatus, método da classe Applet, 753 som, sistema, 756 .spl, extensão de arquivo, 758 start, método da classe Timer, 751 start, método da interface Player, 760 stop, método da classe Timer, 752 stop, método da interface AudioClip, 756 Sun Audio, formato de arquivo (extensão .au), 756 .swf, extensão de arquivo, 758 Timer, classe, 751 toURI, método da classe File, 761 toURL, método da classe URI, 761 .wav, extensão de arquivo, 756 Windows Wave, formato de arquivo (extensão .wav), 756

Exercícios de autorrevisão 24.1 Preencha as lacunas em cada uma das seguintes afirmações: a) O método Graphics ________ exibe uma imagem em um applet. b) O Java fornece dois mecanismos para reproduzir sons em um applet — o método play de Applet e o método play da interface ________. c) Um(a) ________ é uma imagem que tem áreas ativas em que o usuário pode clicar para realizar uma tarefa como carregar uma página Web. d) O método ________ da classe ImageIcon exibe a imagem do ImageIcon. e) O Java suporta vários formatos de imagem, incluindo ________, ________ e ________. f) O método static lookup da classe JNLP ________ obtém uma referência ao FileOpenService.

24.2 Determine se cada uma das sentenças é verdadeira ou falsa. Se falsa, explique por quê. a) Um arquivo de áudio é marcado para a coleta de lixo depois de ele ser reproduzido por meio do método play da classe Applet. b) A classe ImageIcon fornece construtores que permitem que um objeto ImageIcon seja inicializado somente com uma imagem do computador local. c) O método play da classe AudioClip faz loop de um clipe de áudio continuamente. d) A Java Image I/O API é utilizada para adicionar imagens gráficas 3D a um aplicativo Java. e) O método de Applet getDocumentBase retorna, como um objeto da classe URL, a localização na Internet do arquivo HTML que invocou o applet. f) Os métodos FileOpenService openFileDialog e openMultiFileDialog retornam um objeto FileContents e um array de objetos FileContents, respectivamente.



Respostas dos exercícios de autorrevisão

765

Respostas dos exercícios de autorrevisão 24.1 24.2

a) drawImage. b) AudioClip. c) mapa de imagem. d) paintIcon. e) Graphics Interchange Format (GIF), Joint Photographic Experts Group ( JPEG), Portable Network Graphics (PNG). f) ServiceManager. a) Verdadeira. b) Falsa. ImageIcon também pode carregar imagens da internet. c) Falsa. O método play da classe AudioClip reproduz um clipe de áudio uma vez. O método loop da classe AudioClip repete um clipe de áudio continuamente. d) Falsa. A Java 3D API é utilizada para criar e modificar imagens gráficas 3D. A Java Image I/O API é utilizada para ler e gerar a saída de imagens para arquivos. e) Verdadeira. f) Falsa.

Exercícios 24.3 Descreva como fazer uma animação “amigável ao navegador”. 24.4 Descreva os métodos Java para reproduzir e manipular clipes de áudio. 24.5 Expliquem como os mapas de imagem são utilizados. Liste vários exemplos de sua utilização. 24.6 (Apagando aleatoriamente uma imagem) Suponha que uma imagem seja exibida em uma área retangular de tela. Uma maneira de apagar

a imagem é simplesmente configurar cada pixel com a mesma cor imediatamente, mas o efeito visual será desagradável. Escreva um programa Java que exibe uma imagem e, então, a apaga utilizando a geração de números aleatórios para selecionar pixels individuais a serem apagados. Depois que a maior parte da imagem tiver sido apagada, apague todos os pixels restantes de uma vez. Você pode desenhar pixels individuais como uma linha que inicia e termina nas mesmas coordenadas. Você poderia tentar diversas variantes desse problema. Por exemplo, você talvez exiba linhas ou formas aleatoriamente para apagar regiões da tela.

24.7 (Flasher de texto) Crie um programa Java que faça um texto piscar repetidamente na tela. Faça isso alternando o texto com uma imagem

simples de cor do fundo. Permita que o usuário controle a “velocidade de piscamento” e a cor ou padrão de fundo. Você precisará utilizar métodos

getDelay e setDelay da classe Timer. Esses métodos são utilizados para recuperar e configurar o intervalo em milissegundos entre Actio­ nEvents, respectivamente

24.8 (Flasher de imagem) Crie um programa Java que faça uma imagem piscar repetidamente na tela. Faça isso alternando a imagem com uma imagem simples de cor do fundo.

24.9 (Relógio digital) Implemente um programa que exibe um relógio digital na tela. 24.10 (Chamando a atenção para uma imagem) Se quiser enfatizar uma imagem, você pode colocar uma fileira de lâmpadas simuladas em torno dela. Pode fazer com que todas as lâmpadas acendam ao mesmo tempo ou ligar e fazê-las acender e apagar em sequência uma após a outra.

24.11 (Ampliador de imagem) Crie um programa que permita ampliar e reduzir uma imagem. 24.12 (Modificação de LoadImageAndScale) Modifique o applet LoadImageAndScale (Figura 24.1) para fornecer um segundo botão que permite

ao usuário escolher uma nova imagem. A rotina de tratamento de evento de botão deve utilizar o JNLP FileOpenService a fim de exibir um diálogo Open, para que o usuário possa selecionar uma nova imagem.

24.13 (Visualizador de imagem) Utilizando as técnicas de JNLP que aprendeu nas seções 24.2–24.3, crie um aplicativo visualizador de imagem que

permite ao usuário selecionar um grupo de imagens para exibição. O aplicativo deve exibir uma JList contendo os nomes dos arquivos selecionados. Você pode obter o nome do arquivo representado por um objeto FileContents chamando seu método getName. Quando o usuário clicar no nome de uma imagem na JList, o aplicativo deve exibir a imagem na janela.

Seção especial: projetos de multimídia desafiadores

Os exercícios precedentes são voltados para o texto e projetados para testar seu entendimento de conceitos fundamentais de multimídia. Esta seção inclui uma coleção de projetos de multimídia avançados. Você deve achar esses problemas desafiadores, mas divertidos. Os problemas variam em dificuldade. Alguns requerem uma hora ou duas para escrever e implementar o programa. Outros são úteis para atribuições de laboratório que talvez requeiram duas ou três semanas de estudo e implementação. Alguns são projetos de conclusão de curso desafiadores. [Nota para instrutores: Não são fornecidas soluções para esses exercícios.]

24.14 (Animação) Crie um programa de animação Java de uso geral. Você deve permitir ao usuário especificar a sequência de quadros a ser exibida, a velocidade em que as imagens são exibidas, os áudios a ser reproduzidos enquanto a animação estiver executando e assim por diante.

24.15 (Limericks) Modifique o programa de escrever limericks que você escreveu no Exercício 16.6 para cantar os limericks que seu programa criou. 24.16 (Transição aleatória entre imagens) Esse exercício fornece um efeito visual muito elegante. Se você estiver exibindo uma imagem em

uma determinada área na tela e quiser fazer uma transição para outra imagem na mesma área, armazene a nova imagem de tela em um buffer fora da tela e copie pixels aleatoriamente dela para a área de exibição, sobrepondo os pixels que já estão nessas localizações. Quando a maioria dos pixels for copiada, copie a inteira imagem nova para a área de exibição para certificar-se de que você está exibindo a nova imagem completa. Para implementar esse programa você pode precisar utilizar as classes PixelGrabber e MemoryImageSource (veja a documentação do Java API para descrições dessas classes). Você poderia tentar diversas variantes desse problema. Por exemplo, selecione todos os pixels em uma linha reta ou em uma forma escolhida aleatoriamente na nova imagem e sobreponha-os às posições correspondentes da antiga imagem.

766

Capítulo 24  Multimídia: applets e aplicativos

24.17 (Áudio de fundo) Adicione áudio de fundo para um de seus aplicativos favoritos utilizando o método loop da classe AudioClip para reproduzir o som no fundo enquanto interage com seu aplicativo de maneira normal.

24.18 (Painel de caracteres rolantes) Crie um programa que rola caracteres da direita para a esquerda (ou da esquerda para a direita se isso for apropriado para seu idioma) ao longo de um painel de exibição. Como uma opção, exiba o texto em um loop contínuo, de modo que depois que o texto desaparecer em uma extremidade, ele reapareça na outra.

24.19 (Painel de imagens rolantes) Crie um programa que rola uma imagem ao longo de um painel. 24.20 (Relógio Analógico) Crie um programa que exibe um relógio analógico com ponteiros de hora, minutos e segundos que se movem apropriadamente conforme o tempo passa.

24.21 (Áudio dinâmico e caleidoscópio gráfico) Desenvolva um programa de caleidoscópio que exibe imagens gráficas refletidas para simular o popular brinquedo infantil. Incorpore efeitos de áudio que “reflitam” as imagens gráficas dinamicamente mutantes do seu programa.

24.22 (Gerador automático de quebra-cabeça) Crie um gerador e manipulador de quebra-cabeça. O usuário especifica uma imagem. O programa carrega e exibe a imagem, e depois a divide em formas selecionadas aleatoriamente e as embaralha. O usuário então utiliza o mouse para mover as peças para resolver o quebra-cabeça. Adicione sons de áudio de movimento e encaixe adequados. Você poderia controlar a posição de cada peça e então utilizar efeitos de áudio para ajudar o usuário a encaixar as peças nas posições corretas.

24.23 (Gerando e percorrendo o labirinto) Desenvolva um programa gerador de labirinto e de percurso do labirinto com base em multimídia uti-

lizando os programas de labirinto escritos nos exercícios 18.20–18.22. Deixe o usuário personalizar o labirinto especificando o número de linhas e colunas e indicando o nível de dificuldade. Crie uma animação de um ratinho percorrendo o labirinto. Utilize áudio para dramatizar o movimento de seu personagem ratinho.

24.24 (Caça-níqueis) Desenvolva uma simulação multimídia de um caça-níqueis. Crie três rodas giratórias. Coloque símbolos e imagens de várias frutas em cada roda. Utilize a geração de números aleatórios para simular a giro e a parada de cada roda em um símbolo.

24.25 (Corrida de cavalos) Crie uma simulação de uma corrida de cavalos. Permita múltiplos competidores. Utilize áudios para um apresentador

da corrida. Reproduza os áudios adequados para indicar o status correto de cada competidor por toda a corrida. Utilize áudios para anunciar os resultados finais. Você poderia tentar simular os tipos de jogo de corrida de cavalos que frequentemente ocorrem em parques de diversões. Os jogadores fazem turnos para usar o mouse e precisam realizar alguma manipulação baseada em suas habilidades com o mouse para avançar seus cavalos.

24.26 (Jogo de malha) Desenvolva uma simulação baseada em multimídia do jogo de malha. Utilize áudio e efeitos visuais apropriados. 24.27 (Jogo de bilhar) Crie uma simulação baseada em multimídia do jogo de bilhar. Cada jogador faz turnos para usar o mouse a fim de posicionar um taco de bilhar e acertá-lo contra uma bola no ângulo adequado para tentar fazer outras bolas caírem nos bolsos. Seu programa deve manter uma contagem.

24.28 (Artista) Projete um programa de arte que forneça a um artista uma grande variedade de recursos para desenhar, utilizar imagens e utilizar animações para criar uma exposição dinâmica de arte multimídia.

24.29 (Designer de fogos de artifício) Crie um programa em Java que alguém poderia utilizar para criar um show de fogos de artifício. Crie uma variedade de demonstrações de fogos de artifício. Então orquestre a queima dos fogos de artifício para obter um efeito máximo.

24.30 (Planejador de planta baixa) Desenvolva um programa que ajude alguém a organizar os móveis de uma casa. Adicione recursos que permitam que a pessoa alcance a melhor disposição possível.

24.31 (Palavras cruzadas) Palavras cruzadas estão entre os passatempos mais populares. Desenvolva um programa de palavras cruzadas baseado

em multimídia. Seu programa deve permitir que o jogador insira e apague palavras facilmente. Associe seu programa a um grande dicionário computadorizado. Seu programa também deve ser capaz de sugerir as palavras com base nas letras já preenchidas. Forneça outro recurso que facilite o trabalho do entusiasta de palavras cruzadas.

24.32 (Jogo do 15) Escreva um programa com base em multimídia que permite que o usuário jogue o jogo do 15. O jogo é um tabuleiro de 4 por 4 com

um total de 16 posições. Uma posição está vazia, as outras são ocupadas por 15 ladrilhos numerados de 1 a 15. O usuário pode mover qualquer ladrilho ao lado da posição atualmente vazia para aquela posição clicando no ladrilho. Seu programa deve criar o tabuleiro com os ladrilhos em ordem aleatória. O objetivo é organizar os ladrilhos na ordem sequencial linha por linha.

24.33 (Testador de precisão de reação/tempo de reação) Crie um programa que move uma forma criada aleatoriamente ao redor da tela. O usuário move o mouse para capturar e clicar na forma. A velocidade e o tamanho da forma podem ser variados. Mantenha estatísticas sobre quanto tempo geralmente o usuário leva para perceber uma forma de um determinado tamanho. O usuário provavelmente terá mais dificuldade para capturar mais rapidamente formas menores em movimento.

24.34 (Arquivo de calendário/lembretes) Utilizando tanto áudio como imagens, crie um arquivo de calendário e “lembretes” de uso geral. Por

exemplo, o programa deve cantar “Feliz Aniversário” quando você utilizá-lo em seu aniversário. Faça com que o programa exiba imagens e reproduza áudios associados com eventos importantes. Além disso, faça-o lembrá-lo com antecedência desses eventos importantes. Seria interessante, por exemplo, fazer o programa fornecer um aviso com uma semana de antecedência de modo que você possa enviar um cartão de felicitações apropriado para essa pessoa especial.

24.35 (Rotação de imagens) Crie um programa que permita rotacionar uma imagem por vários graus (até um máximo de 360 graus). O programa deve permitir especificar que você quer girar a imagem continuamente. Esse programa deve permitir ajustar dinamicamente a velocidade de rotação.



Fazendo a diferença

767

24.36 (Colorindo fotografias e imagens P&B) Crie um programa que permita colorir uma fotografia em preto e branco. Forneça uma paleta de cores para selecionar cores. Seu programa deve permitir a aplicação de diferentes cores a regiões distintas da imagem.

24.37 (Simulador Simpletron baseado em multimídia) Modifique o simulador Simpletron que você desenvolveu nos exercícios dos capítulos

anteriores (exercícios 7.35–7.37) para incluir recursos de multimídia. Adicione sons de computador para indicar que o Simpletron está executando instruções. Adicione um som de vidro quebrando quando um erro fatal ocorrer. Utilize luzes intermitentes para indicar as células de memória ou os registros que estão sendo manipulados atualmente. Utilize outras técnicas de multimídia conforme apropriado para tornar seu simulador Simpletron mais valioso para seus usuários como uma ferramenta educacional.

Fazendo a diferença 24.38 (Projeto de acessibilidade: Síntese de fala) Os computadores podem ajudar usuários cegos ou com problemas de visão fornecendo páginas

Web que falam, e-mails e outros documentos que utilizam “mecanismos” de TTS (text-to-speech) ou de síntese de fala. Da mesma forma, para ajudar pessoas que têm dificuldade de interagir com um computador via mouse e teclado, os mecanismos de reconhecimento da fala permitem a computadores reconhecerem comandos falados. Com a síntese e o reconhecimento de fala, os usuários podem “falar” com computadores. Neste exercício, você irá pesquisar e explorar a síntese de fala com a Java Speech API (java.sun.com/products/java-media/speech/). Baixe e instale o sintetizador de fala FreeTTS de código-fonte aberto (freetts.sourceforge.net/docs/index.php). Explore a documentação do FreeTTS, depois implemente um aplicativo no qual o usuário pode inserir o texto em uma JTextArea. Quando o usuário clicar no JButton Speak, o programa deve utilizar o FreeTTS para falar o texto em voz alta.

24.39 (Projeto de acessibilidade: Reconhecimento de fala) Neste exercício, você irá pesquisar e explorar o reconhecimento da fala com a

Java Speech API. Baixe o mecanismo de reconhecimento de fala Sphinx-4 de código-fonte aberto e instale-o (cmusphinx.sourceforge.net/ sphinx4/). Escreva um programa que permite que um usuário fale com o computador. Utilize as capacidades de reconhecimento da fala de exi-

bir o que o usuário diz em um JTextArea. Permita que o usuário salve o conteúdo do JTextArea em um arquivo no disco falando o comando “salvar”.

24.40 (Projeto: Simulador de robótica Simbad ) A robótica promete a grande tarefa de lidar com trabalhos perigosos para seres humanos, como

mineração de carvão; exploração de minas e exploração das profundezas do oceano e do espaço. Simbad (simbad.sourceforge.net) é um simulador de robótica 3D de código-fonte aberto com base em Java. De acordo com a página Web do projeto, ele suporta simulações de um ou vários robôs; sensores de visão, distância e contato; e muito mais. Você pode baixar o Simbad de simbad.sourceforge.net/index.php#download. Você também precisará baixar e instalar o Java 3D — nesse site são fornecidas instruções para Mac OS X, Windows e Linux. Assim que concluir o download do Simbad e a instalação do Java 3D, você pode experimentar o exemplo simples fornecido em simbad. sourceforge.net/example1.php. Depois de executar isso, leia o Simbad Programming Guide em simbad.sourceforge.net/guide.php e tente modificar o exemplo simples para realizar algumas tarefas diferentes. Se estiver interessado em explorar ainda mais a robótica, estude a documentação da API em simbad.sourceforge.net/doc/ e crie seu próprio programa de simulação de robôs utilizando o Simbad. Por exemplo, crie a simulação de um aspirador de pó robótico que se move para a direção para a qual ele está voltado até encontrar um obstáculo e, então, escolhe outra direção aleatoriamente.

25

Se um ator entra pela porta, você não tem nada. Mas se ele entrar pela janela, você terá algo especial. — Billy Wilder

...a força dos eventos desperta talentos adormecidos. — Edward Hoagland

Você e eu veríamos fotografias mais interessantes se eles parassem de se preocupar e, em vez disso, aplicassem senso comum ao problema do registro da aparência e comportamento de suas próprias eras. — Jessie Tarbox Beals

Componentes GUI: Parte 2

Objetivos Neste capítulo, você aprenderá: 

A criar e manipular controles deslizantes, menus, menus pop-up e janelas.



A alterar programaticamente a aparência e comportamento de uma GUI, utilizando a aparência e comportamento plugável do Swing.



A criar uma interface de múltiplos documentos com JDesktopPane e JInternalFrame.



A utilizar gerenciadores adicionais de layout.

Sumário

25.1 Introdução

25.1 Introdução

25.6 Aparência e comportamento plugável

25.2

25.7

JDesktopPane

25.8

JTabbedPane

JSlider

25.3 Windows: notas adicionais 25.4 Utilizando menus com frames 25.5

769

e JInternalFrame

25.9 Gerenciadores de layout: BoxLayout e GridBagLayout

25.10 Conclusão

JPopupMenu

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios

25.1 Introdução Neste capítulo, continuamos nosso estudo de GUIs. Discutimos componentes e gerenciadores de layout adicionais e projetamos a base para construir mais GUIs complexas. Começamos nossa discussão com controles deslizantes que permitem selecionar um intervalo de valores de números inteiros. Em seguida, discutimos alguns detalhes adicionais das janelas. Você aprenderá a utilizar menus que permitem ao usuário executar efetivamente tarefas no programa. A aparência e comportamento de uma GUI Swing pode ser uniforme em todas as plataformas em que um programa Java executa ou a GUI pode ser personalizada utilizando a aparência e comportamento plugável (Pluggable Look-And-Feel — PLAF) do Swing. Fornecemos um exemplo que ilustra como alterar entre a aparência e comportamento de metal padrão do Swing (que parece e se comporta da mesma maneira em diferentes plataformas), a nova aparência e comportamento Nimbus (introduzida no Capítulo 14), uma aparência e comportamento que simula o Motif (uma aparência e comportamento popular do UNIX) e uma que simula a aparência e comportamento do Windows da Microsoft. Muitos aplicativos de hoje em dia utilizam uma interface de múltiplos documentos (Multiple Document Interface — MDI) — uma janela principal (frequentemente chamada janela-pai) contendo outras janelas (frequentemente chamadas janelas-filhas) para gerenciar vários documentos abertos em paralelo. Por exemplo, muitos programas de correio eletrônico permitem abrir várias janelas de correio eletrônico ao mesmo tempo de modo que você possa compor ou ler múltiplas mensagens. Demonstramos as classes do Swing para criar interface de múltiplos documentos. O capítulo termina com uma série de exemplos que discute gerenciadores adicionais de layout para organizar interfaces gráficas com o usuário. O Swing é um assunto extenso e complexo. Há muito mais componentes GUI e capacidades do que o espaço neste livro permite apresentar. Muito outros componentes GUI Swing são introduzidos nos demais capítulos deste livro quando necessários.

25.2 JSlider O JSlider permite a um usuário selecionar a partir de um intervalo de valores inteiros. A classe JSlider herda de JComponent. A Figura 25.1 mostra um JSlider horizontal com marcas de medida (tick marks) e um marcador (thumb) que permitem a um usuário selecionar um valor. Os JSliders podem ser personalizados para exibir marcas de medida principais, marcas de medida secundárias e rótulos para as marcas de medida. Eles também suportam marcas de aderência, que fazem a miniatura, quando posicionada entre duas marcas, aderir à marca mais próxima.

Marcador

Marcas de medida

Figura 25.1 | Um componente JSlider com uma orientação horizontal.

A maioria dos componentes GUI Swing suporta interações do usuário pelo mouse e pelo teclado. Por exemplo, se um JSlider tem o foco (isto é, ele é o componente GUI atualmente selecionado na interface com o usuário), a tecla de seta para a esquerda e a tecla da seta para a direita fazem com que o marcador do JSlider diminua ou aumente por 1, respectivamente. A tecla da seta para baixo e a tecla da seta para cima também fazem com que a caixa de rolagem do JSlider diminua ou aumente por 1 tique, respectivamente. A tecla PgDn (page down) e a tecla PgUp (page up) fazem com que o marcador do JSlider diminua ou aumente por incrementos de bloco de um décimo do intervalo de valores, respectivamente. A tecla Home move a caixa de rolagem para o valor mínimo do JSlider, e a tecla End move a caixa de rolagem para o valor máximo do JSlider. Os JSliders têm uma orientação horizontal ou uma orientação vertical. Para um JSlider horizontal, o valor mínimo está na extrema esquerda do JSlider e o valor máximo está na extrema direita. Para um JSlider vertical, o valor mínimo está na parte inferior

770

Capítulo 25  Componentes GUI: Parte 2

e o valor máximo na parte superior. As posições de valor mínimo e máximo em um JSlider podem ser alternadas chamando o método JSlider setInverted com argumento boolean true. A posição relativa da caixa de rolagem indica o valor atual do JSlider. O programa nas figuras 25.2–25.4 permite ao usuário dimensionar um círculo desenhado em uma subclasse do JPanel chamada OvalPanel (Figura 25.2). O usuário especifica o diâmetro do círculo com um JSlider horizontal. A classe OvalPanel sabe desenhar um círculo em si mesma, utilizando sua própria variável de instância diameter para determinar o diâmetro do círculo — o diameter é utilizado como a largura e a altura do quadro delimitador em que o círculo será exibido. O valor diameter é configurado quando o usuário interage com o JSlider. O handler de evento chama o método setDiameter na classe OvalPanel para configurar o diameter e chama repaint para desenhar o novo círculo. A chamada a repaint resulta em uma chamada ao método paintComponent de OvalPanel. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

// Figura 25.2: OvalPanel.java // Uma classe personalizada de JPanel. import java.awt.Graphics; import java.awt.Dimension; import javax.swing.JPanel; public class OvalPanel extends JPanel { private int diameter = 10; // diâmetro padrão de 10 // desenha uma oval do diâmetro especificado public void paintComponent( Graphics g ) { super.paintComponent( g ); g.fillOval( 10, 10, diameter, diameter ); // desenha um círculo } // fim do método paintComponent // valida e configura o diâmetro e então repinta public void setDiameter( int newDiameter ) { // se diâmetro inválido, assume o padrão de 10 diameter = ( newDiameter >= 0 ? newDiameter : 10 ); repaint(); // repinta o painel } // fim do método setDiameter // utilizado pelo gerenciador de layout para determinar o tamanho preferido public Dimension getPreferredSize() { return new Dimension( 200, 200 ); } // fim do método getPreferredSize // utilizado pelo gerenciador de layout para determinar o tamanho mínimo public Dimension getMinimumSize() { return getPreferredSize(); } // fim do método getMinimumSize } // fim da classe OvalPanel

Figura 25.2  |  Subclasse JPanel para desenhar círculos de um diâmetro especificado.

1 2 3 4 5 6 7 8 9 10 11 12 13

// Figura 25.3: SliderFrame.java // Utilizando JSliders para dimensionar uma oval. import java.awt.BorderLayout; import java.awt.Color; import javax.swing.JFrame; import javax.swing.JSlider; import javax.swing.SwingConstants; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; public class SliderFrame extends JFrame { private JSlider diameterJSlider; // controle deslizante para selecionar o diâmetro

14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

25.2  JSlider private OvalPanel myPanel; // painel para desenhar um círculo // construtor sem argumento public SliderFrame() { super( "Slider Demo" ); myPanel = new OvalPanel(); // cria o painel para desenhar um círculo myPanel.setBackground( Color.YELLOW ); // configura o fundo como amarelo

// configura o JSlider para controlar o valor do diâmetro diameterJSlider = new JSlider( SwingConstants.HORIZONTAL, 0, 200, 10 ); diameterJSlider.setMajorTickSpacing( 10 ); // cria uma marca de medida a cada 10 diameterJSlider.setPaintTicks( true ); // pinta as marcas de medida no controle deslizante // registra o ouvinte de evento do JSlider diameterJSlider.addChangeListener( new ChangeListener() // classe interna anônima { // trata da alteração de valor do controle deslizante public void stateChanged( ChangeEvent e ) { myPanel.setDiameter( diameterJSlider.getValue() ); } // fim do método stateChanged } // fim da classe interna anônima ); // fim da chamada a addChangeListener add( add( } // fim } // fim da

diameterJSlider, BorderLayout.SOUTH ); // adiciona um controle deslizante ao quadro myPanel, BorderLayout.CENTER ); // adiciona painel ao frame do construtor de SliderFrame classe SliderFrame

Figura 25.3  |  Valor do JSlider utilizado para determinar o diâmetro de um círculo.

1 2 3 4 5 6 7 8 9 10 11 12 13 14

771

// Figura 25.4: SliderDemo.java // testando SliderFrame. import javax.swing.JFrame; public class SliderDemo { public static void main( String[] args ) { SliderFrame sliderFrame = new SliderFrame(); sliderFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); sliderFrame.setSize( 220, 270 ); // configura o tamanho do frame sliderFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe SliderDemo

Figura 25.4  |  Classe de teste para SliderFrame.

772

Capítulo 25

Componentes GUI: Parte 2

A classe OvalPanel (Figura 25.2) contém um método paintComponent (linhas 12–17) que desenha uma oval preenchida (nesse exemplo, um círculo), um método setDiameter (linhas 20–25) que altera o diameter do círculo e repaints a OvalPanel, um método getPreferredSize (linhas 28–31) que retorna a largura e altura preferidas de uma OvalPanel e um método getMinimumSize (linhas 34–37) que retorna a largura e altura mínimas de uma OvalPanel. A Seção 24.3 introduziu getPreferredSize e getMinimumSize, que são utilizados por alguns gerenciadores de layout para determinar o tamanho de um componente. A classe SliderFrame (Figura 25.3) cria o JSlider que controla o diâmetro do círculo. O construtor da classe SliderFrame (linhas 17–45) cria o objeto OvalPanel myPanel (linha 21) e configura sua cor de fundo (linha 22). As linhas 25–26 criam o objeto JSlider diameterSlider para controlar o diâmetro do círculo desenhado no OvalPanel. O construtor JSlider recebe quatro argumentos. O primeiro argumento especifica a orientação do diameterSlider, que é HORIZONTAL (uma constante na interface SwingConstants). O segundo e o terceiro argumentos indicam os valores inteiros mínimo e máximo no intervalo de valores para este JSlider. O último argumento indica que o valor inicial do JSlider (isto é, onde a caixa de rolagem é exibida) deve ser 10. As linhas 27–28 personalizam a aparência do JSlider. O método setMajorTickSpacing indica que cada marca de medida principal representa 10 valores no intervalo de valores suportados pelo JSlider. O método setPaintTicks com um argumento true indica que as marcas de medida devem ser exibidas (elas não são exibidas por padrão). Para outros métodos que são utilizados para personalizar a aparência de um JSLIDER, consulte a documentação on-line do JSlider (java.sun.com/javase/6/docs/api/javax/swing/ JSlider.html). Os JSliders geram ChangeEvents (pacote javax.swing.event) em resposta a interações de usuário. Um objeto de uma classe que implementa a interface ChangeListener (pacote javax.swing.event) e declara o método stateChanged pode responder a ChangeEvents. As linhas 31–41 registram um ChangeListener para tratar eventos do diameterSlider. Quando o método state­ Changed (linhas 36–39) é chamado em resposta a uma interação do usuário, a linha 38 chama o método setDiameter de myPanel e passa o valor atual do JSlider como um argumento. O método JSlider getValue retorna a posição atual do marcador.

25.3 Windows: notas adicionais Um JFrame é uma janela com uma barra de título e uma borda. A classe JFrame é uma subclasse de Frame (pacote java. que é uma subclasse de Window (pacote java.awt). Assim, JFrame é um dos componentes Swing GUI de maior peso. Ao exibir uma janela em um programa Java, a janela é fornecida pelo conjunto de ferramentas de janelas da plataforma local e, portanto, ela irá parecer qualquer outra janela exibida nessa plataforma. Quando um aplicativo Java executa em um Macintosh e exibe uma janela, a barra de título da janela e as bordas serão semelhantes àquelas de outros aplicativos do Macintosh. Quando um aplicativo Java é executado em um sistema Microsoft Windows e exibe uma janela, a barra de título e bordas da janela parecerão aquelas de outros aplicativos Microsoft Windows. E quando um aplicativo Java executar em uma plataforma UNIX e exibir uma janela, a barra de título da janela e as bordas serão semelhantes aos outros aplicativos UNIX dessa plataforma. Por padrão, quando o usuário fecha uma janela JFrame, ela se torna oculta (isto é, ela desaparece da tela), mas você pode controlar isso com método JFrame setDefaultCloseOperation. Interface WindowConstants (pacote javax.swing), que a classe JFrame implementa, declara três constantes — DISPOSE_ON_CLOSE, DO_NOTHING_ON_CLOSE e HIDE_ON_CLOSE (o padrão) — para o uso com esse método. Algumas plataformas permitem que apenas um número limitado de janelas seja exibido na tela. Portanto, uma janela é um recurso valioso que deve ser devolvido ao sistema quando não for mais necessária. A classe Window (uma superclasse indireta de JFrame) declara o método dispose para esse propósito. Quando uma janela não é mais necessária em um aplicativo, você deve descartá-la explicitamente. Isso pode ser feito chamando o método dispose de Window ou chamando o método setDefaultCloseOperation com o argumento WindowConstants.DISPOSE_ON_CLOSE. Terminar um aplicativo também retornará os recursos da janela ao sistema. Utilizar DO_NOTHING_ON_CLOSE indica que o programa determinará o que fazer quando o usuário tenta fechar a janela. Por exemplo, o programa talvez queira perguntar se é preciso salvar as modificações de um arquivo antes de fechar uma janela. awt),

Dica de desempenho 25.1 Uma janela é um recurso caro de sistema. Retorne-a ao sistema chamando seu método dispose quando a janela não mais é necessária.

Por padrão, uma janela não é exibida na tela até que o programa invoque o método setVisible da janela (herdado da classe com um argumento true. O tamanho de uma janela deve ser configurado com uma chamada ao método setSize (herdado da classe java.awt.Component). A posição de uma janela quando aparece na tela é especificada com o método setLocation (herdado da classe java.awt.Component). java.awt.Component)

Erro comum de programação 25.1 Esquecer de chamar o método setVisible em uma janela é um erro de lógica de tempo de execução — a janela não é exibida.

25.4 Utilizando menus com frames

773

Erro comum de programação 25.2 Esquecer de chamar o método setSize em uma janela é um erro de lógica em tempo de execução — somente a barra de título aparece.

Quando o usuário manipula a janela, essa ação gera eventos de janela. Os ouvintes de evento são registrados para eventos de janela com o método Window addWindowListener. A interface WindowListener fornece sete métodos de tratamento de eventos de janela — windowActivated (chamado quando o usuário torna uma janela ativa), windowClosed (chamado depois de a janela ser fechada), windowClosing (chamado quando o usuário inicia o fechamento da janela), windowDeactivated (chamado quando o usuário torna outra janela a janela ativa), windowDeiconified (chamado quando o usuário restaura uma janela que está sendo minimizada), windowIconified (chamado quando o usuário minimiza uma janela) e windowOpened (chamado quando um programa exibe uma janela na tela pela primeira vez).

25.4 Utilizando menus com frames Os menus são uma parte integrante das GUIs. Eles permitem que o usuário realize ações sem poluir desnecessariamente uma GUI com componentes extras. Em GUIs Swing, os menus podem ser anexados somente aos objetos das classes que fornecem o método setJMenuBar. Duas dessas classes são JFrame e JApplet. As classes utilizadas para declarar menus são JMenuBar, JMenu, JMenuItem, JCheckBox­ MenuItem e classe JRadioButtonMenuItem.

Observações sobre a aparência e o comportamento 25.1 Os menus simplificam as GUIs porque os componentes podem ser ocultos dentro deles. Esses componentes serão visíveis somente quando o usuário procurá-los selecionando no menu.

Resumo dos vários componentes relacionados a menu A classe JMenuBar (uma subclasse de JComponent) contém os métodos necessários para gerenciar uma barra de menus, que é um contêiner de menus. A classe JMenu (uma subclasse de javax.swing.JMenuItem) contém os métodos necessários para gerenciar menus. Os menus contêm itens de menu e são adicionados a barras de menus ou a outros menus, a exemplo dos submenus. Quando um menu é clicado, ele se expande para mostrar sua lista dos itens de menu. A classe JMenuItem (uma subclasse de javax.swing.AbstractButton) contém os métodos necessários para gerenciar itens de menu. Um item de menu é um componente GUI dentro de um menu que, quando selecionado, resulta em um evento de ação. Um item de menu pode ser utilizado para iniciar uma ação ou ser um submenu que fornece mais itens de menu a partir dos quais o usuário pode selecionar. Os submenus são úteis para agrupar itens de menu relacionados em um menu. A classe JCheckBoxMenuItem (uma subclasse de javax.swing.JMenuItem) contém os métodos necessários para gerenciar itens de menu que podem ser ativados ou desativados. Quando um JCheckBoxMenuItem é selecionado, uma marca de verificação aparece à esquerda do item de menu. Quando o JCheckBoxMenuItem é selecionado novamente, a marca é removida. A classe JRadioButtonMenuItem (uma subclasse de javax.swing.JMenuItem) contém os métodos necessários para gerenciar itens de menu que podem ser ativados ou desativados, como os JCheckBoxMenuItems. Quando múltiplos JRadioButtonMenuItems são mantidos como parte de um ButtonGroup, apenas um item do grupo pode ser selecionado de cada vez. Quando um JRadioButtonMenu­ Item é selecionado, um círculo preenchido aparece à esquerda do item de menu. Quando outro JRadioButtonMenuItem é selecionado, o círculo preenchido do item de menu anteriormente selecionado é removido. Utilizando menus em um aplicativo As figuras 25.5–25.6 demonstram vários itens de menu e como especificar caracteres especiais chamados mnemônicos que podem fornecer acesso rápido a um menu ou item de menu a partir do teclado. Os mnemônicos podem ser utilizados com todas as subclasses de javax.swing.AbstractButton. A classe MenuFrame (Figura 25.5) cria a GUI e trata os eventos de item de menu. A maior parte do código nesse aplicativo aparece no construtor da classe (linhas 34–151). 1 2 3 4 5 6 7 8 9

// Figura 25.5: MenuFrame.java // Demonstrando menus. import java.awt.Color; import java.awt.Font; import java.awt.BorderLayout; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.ItemListener; import java.awt.event.ItemEvent;

774 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

Capítulo 25  Componentes GUI: Parte 2 import import import import import import import import import import

javax.swing.JFrame; javax.swing.JRadioButtonMenuItem; javax.swing.JCheckBoxMenuItem; javax.swing.JOptionPane; javax.swing.JLabel; javax.swing.SwingConstants; javax.swing.ButtonGroup; javax.swing.JMenu; javax.swing.JMenuItem; javax.swing.JMenuBar;

public class MenuFrame extends JFrame { private final Color[] colorValues = { Color.BLACK, Color.BLUE, Color.RED, Color.GREEN }; private JRadioButtonMenuItem[] colorItems; // itens do menu Color private JRadioButtonMenuItem[] fonts; // itens do menu Font private JCheckBoxMenuItem[] styleItems; // itens do menu Font Style private JLabel displayJLabel; // exibe texto de exemplo private ButtonGroup fontButtonGroup; // gerencia itens do menu Font private ButtonGroup colorButtonGroup; // gerencia itens do menu Color private int style; // utilizado para criar estilos de fontes // construtor sem argumento para configurar a GUI public MenuFrame() { super( "Using JMenus" ); JMenu fileMenu = new JMenu( "File" ); // cria o menu File fileMenu.setMnemonic( 'F' ); // configura o mnemônico como F // cria item de menu About... JMenuItem aboutItem = new JMenuItem( "About..." ); aboutItem.setMnemonic( 'A' ); // configura o mnemônico com A fileMenu.add( aboutItem ); // adiciona o item about ao menu File aboutItem.addActionListener( new ActionListener() // classe interna anônima { // exibe um diálogo de mensagem quando o usuário seleciona About... public void actionPerformed( ActionEvent event ) { JOptionPane.showMessageDialog( MenuFrame.this, "This is an example\nof using menus", "About", JOptionPane.PLAIN_MESSAGE ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener JMenuItem exitItem = new JMenuItem( "Exit" ); // cria o item exit exitItem.setMnemonic( 'x’ ); // configura o mnemônico como x fileMenu.add( exitItem ); // adiciona o item exit ao menu File exitItem.addActionListener( new ActionListener() // classe interna anônima { // termina o aplicativo quando o usuário clica exitItem public void actionPerformed( ActionEvent event ) { System.exit( 0 ); // encerra o aplicativo } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener JMenuBar bar = new JMenuBar(); // cria a barra de menus setJMenuBar( bar ); // adiciona uma barra de menus ao aplicativo bar.add( fileMenu ); // adiciona o menu File à barra de menus

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145

25.4  Utilizando menus com frames JMenu formatMenu = new JMenu( "Format" ); // cria o menu Format formatMenu.setMnemonic( 'r' ); // configura o mnemônico como r // array listando cores de string String[] colors = { "Black", "Blue", "Red", "Green" }; JMenu colorMenu = new JMenu( "Color" ); // cria o menu Color colorMenu.setMnemonic( 'C' ); // configura o mnemônico como C // cria itens de menu de botão de opção para cores colorItems = new JRadioButtonMenuItem[ colors.length ]; colorButtonGroup = new ButtonGroup(); // gerencia cores ItemHandler itemHandler = new ItemHandler(); // handler para cores // cria itens do menu Color com botões de opção for ( int count = 0; count < colors.length; count++ ) { colorItems[ count ] = new JRadioButtonMenuItem( colors[ count ] ); // cria o item colorMenu.add( colorItems[ count ] ); // adiciona o item ao menu Color colorButtonGroup.add( colorItems[ count ] ); // adiciona ao grupo colorItems[ count ].addActionListener( itemHandler ); } // for final colorItems[ 0 ].setSelected( true ); // seleciona o primeiro item Color formatMenu.add( colorMenu ); // adiciona o menu Color ao menu Format formatMenu.addSeparator(); // adiciona um separador no menu // array listando nomes de fonte String[] fontNames = { "Serif", "Monospaced", "SansSerif" }; JMenu fontMenu = new JMenu( "Font" ); // cria a fonte do menu fontMenu.setMnemonic( 'n' ); // configura o mnemônico como n // cria itens do menu radio button para nomes de fonte fonts = new JRadioButtonMenuItem[ fontNames.length ]; fontButtonGroup = new ButtonGroup(); // gerencia os nomes das fontes // criar itens do menu Font com botões de opção for ( int count = 0; count < fonts.length; count++ ) { fonts[ count ] = new JRadioButtonMenuItem( fontNames[ count ] ); fontMenu.add( fonts[ count ] ); // adiciona fonte ao menu Font fontButtonGroup.add( fonts[ count ] ); // adiciona ao grupo de botões fonts[ count ].addActionListener( itemHandler ); // adiciona handler } // for final fonts[ 0 ].setSelected( true ); // seleciona o primeiro item do menu Font fontMenu.addSeparator(); // adiciona uma barra separadora ao menu Font String[] styleNames = { "Bold", "Italic" }; // nomes dos estilos styleItems = new JCheckBoxMenuItem[ styleNames.length ]; StyleHandler styleHandler = new StyleHandler(); // handler de estilo // criar itens do menu Style com caixas de seleção for ( int count = 0; count < styleNames.length; count++ ) { styleItems[ count ] = new JCheckBoxMenuItem( styleNames[ count ] ); // para estilo fontMenu.add( styleItems[ count ] ); // adiciona ao menu Font styleItems[ count ].addItemListener( styleHandler ); // handler } // for final formatMenu.add( fontMenu ); // adiciona o menu Font ao menu Format bar.add( formatMenu ); // adiciona o menu Format à barra de menus // configura o rótulo para exibir texto displayJLabel = new JLabel( "Sample Text", SwingConstants.CENTER );

775

776 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

Capítulo 25  Componentes GUI: Parte 2 displayJLabel.setForeground( colorValues[ 0 ] ); displayJLabel.setFont( new Font( "Serif", Font.PLAIN, 72 ) ); getContentPane().setBackground( Color.CYAN ); // configura o fundo add( displayJLabel, BorderLayout.CENTER ); // adiciona displayJLabel } // fim do construtor de MenuFrame // classe interna para tratar eventos de ação dos itens de menu private class ItemHandler implements ActionListener { // processa seleções de cor e fonte public void actionPerformed( ActionEvent event ) { // processa a seleção de cor for ( int count = 0; count < colorItems.length; count++ ) { if ( colorItems[ count ].isSelected() ) { displayJLabel.setForeground( colorValues[ count ] ); break; } // fim do if } // for final // processa a seleção de fonte for ( int count = 0; count < fonts.length; count++ ) { if ( event.getSource() == fonts[ count ] ) { displayJLabel.setFont( new Font( fonts[ count ].getText(), style, 72 ) ); } // fim do if } // for final repaint(); // redesenha o aplicativo } // fim do método actionPerformed } // fim da classe ItemHandler // classe interna para tratar eventos de itens dos itens de menu da caixa de verificação private class StyleHandler implements ItemListener { // processa seleções de estilo da fonte public void itemStateChanged( ItemEvent e ) { String name = displayJLabel.getFont().getName(); // Font atual Font font; // nova fonte baseada nas seleções de usuário // determina quais itens são verificados e cria Font if ( styleItems[ 0 ].isSelected() && styleItems[ 1 ].isSelected() ) font = new Font( name, Font.BOLD + Font.ITALIC, 72 ); else if ( styleItems[ 0 ].isSelected() ) font = new Font( name, Font.BOLD, 72 ); else if ( styleItems[ 1 ].isSelected() ) font = new Font( name, Font.ITALIC, 72 ); else font = new Font( name, Font.PLAIN, 72 ); displayJLabel.setFont( font ); repaint(); // redesenha o aplicativo } // fim do método itemStateChanged } // fim da classe StyleHandler } // fim da classe MenuFrame

Figura 25.5  |  JMenus e mnemônicos.

1 2 3 4 5 6 7 8 9 10 11 12 13 14

25.4  Utilizando menus com frames

777

// Figura 25.6: MenuTest.java // Testando MenuFrame. import javax.swing.JFrame; public class MenuTest { public static void main( String[] args ) { MenuFrame menuFrame = new MenuFrame(); // criar MenuFrame menuFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); menuFrame.setSize( 500, 200 ); // configura o tamanho do frame menuFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe MenuTest

Menu

Caracteres mnemônicos

Barra de menus

Submenu expandido Itens do menu Linha separadora

Figura 25.6  |  Classe de teste para MenuFrame.

Configurando o menu File As linhas 38–76 configuram o menu File e o anexam à barra de menus. O menu File contém um item de menu About… que exibe um diálogo de mensagem quando o item de menu é selecionado e um item de menu Exit que pode ser selecionado para terminar o aplicativo. A linha 38 cria um JMenu e passa para o construtor a string "File" como o nome do menu. A linha 39 utiliza o método JMenu s ­ etMnemonic (herdado da classe AbstractButton) para indicar que F é o mnemônico desse menu. Pressionar a tecla Alt e a letra F abre o menu da mesma maneira que clicar o nome de menu com o mouse abriria. Na GUI, o caractere de mnemônico no nome do menu é exibido com um sublinhado. (Ver capturas de tela na Figura 25.6.) Observações sobre a aparência e o comportamento 25.2 Os mnemônicos fornecem acesso rápido a comandos de menu e comandos de botão pelo teclado.

Observações sobre a aparência e o comportamento 25.3 Diferentes mnemônicos devem ser utilizados para cada botão ou item de menu. Normalmente, a primeira letra do rótulo no item de menu ou botão é utilizada como o mnemônico. Se diversos botões ou itens de menu iniciam com a mesma letra, escolha a próxima letra mais significativa do nome (por exemplo, x comumente é escolhido para um botão Exit ou um item do menu). Mnemônicos não fazem distinção entre maiúsculas e minúsculas.

As linhas 42–43 criam JMenuItem aboutItem com o texto “About... ” e configuram seu mnemônico como a letra A. Esse item de menu é adicionado a fileMenu na linha 44 com o método add JMenu. Para acessar o item de menu About... pelo teclado, pressione a tecla Alt e a letra F para abrir o menu File, então pressione A para selecionar o item de menu About.... As linhas 47–56 criam um ActionListener para processar o evento de ação de aboutItem. As linhas 52–54 exibem uma caixa de diálogo de mensagem. Na maioria das utilizações anteriores de showMessageDialog, o primeiro argumento era null. O propósito do primeiro argumento é especificar a janela-pai que ajuda a determinar onde a caixa de diálogo será exibida. Se a janela-pai for especificada como null, a caixa de diálogo aparecerá no centro da tela. Caso contrário, ela aparece centralizada na janela-pai especificada. Neste exemplo, o programa especifica a janela-pai com Menu­

778

Capítulo 25  Componentes GUI: Parte 2

Frame.this — a referência this do objeto MenuFrame. Ao utilizar a referência this em uma classe interna, especificar this sozinha faz referência ao objeto da classe interna. Para representar objetos da classe externa com a referência this, qualifique this com o nome da classe externa e um ponto (.). Em geral, caixas de diálogo são modais. Uma caixa de diálogo modal não permite qualquer outra janela do aplicativo ser acessada até que a caixa de diálogo seja fechada. Os diálogos exibidos com a classe JOptionPane são diálogos modais. A classe JDialog pode ser utilizada para criar seus próprios diálogos modais ou não modais. As linhas 59–72 criam o item de menu exitItem, configuram seus mnemônicos como x, adicionam a fileMenu e registram um ­ActionListener que termina o programa quando o usuário seleciona exit-Item. As linhas 74–76 criam o JMenuBar, o anexam à janela com o método setJMenuBar JFrame e usam o método add JMenuBar para anexar o fileMenu ao JMenuBar.

Erro comum de programação 25.3 Esquecer-se de configurar a barra de menus com o método JFrame setJMenuBar impede que a barra de menus seja exibida no JFrame.

Observações sobre a aparência e o comportamento 25.4 Os menus aparecem da esquerda para a direita na ordem em que eles são adicionados a um JMenuBar.

Configurando o menu Format As linhas 78–79 criam o menu formatMenu e configuram seu mnemônico como r. (F não é utilizado porque esse é o mnemônico do menu File). As linhas 84–85 criam o menu colorMenu (que será um submenu no menu Format) e configuram seu mnemônico como C. A linha 88 cria o array JRadioButtonMenuItem colorItems que fará referência aos itens do menu em colorMenu. A linha 89 cria o Button­ Group colorButtonGroup, que irá assegurar que somente um dos itens de menu no submenu Color seja selecionado de cada vez. A linha 90 cria uma instância da classe interna ItemHandler (declarada nas linhas 154–181) que responde a seleções nos submenus Color e Font (discutidos mais adiante). A instrução for nas linhas 93–100 cria cada JRadioButtonMenuItem no array colorItems, adiciona cada item de menu a colorMenu e a colorButtonGroup e registra o ActionListener para cada item de menu. A linha 102 invoca o método AbstractButton setSelected para selecionar o primeiro elemento no array colorItems. A linha 104 adiciona colorMenu como um submenu de formatMenu. A linha 105 invoca o método JMenu addSeparator para adicionar uma linha separadora horizontal ao menu. Observações sobre a aparência e o comportamento 25.5 Um submenu é criado adicionando um menu como um item de menu a outro menu. Quando o mouse é posicionado sobre um submenu (ou o mnemônico do submenu é pressionado), o submenu expande para mostrar seus itens de menu.

Observações sobre a aparência e o comportamento 25.6 Separadores podem ser adicionados a um menu para agrupar itens de menu logicamente.

Observações sobre a aparência e o comportamento 25.7 Qualquer componente GUI “leve” (isto é, um componente que é uma subclasse de JComponent) pode ser adicionado a um JMenu ou a um JMenuBar.

As linhas 108–126 criam o submenu Font, vários JRadioButtonMenuItems e selecionam o primeiro elemento do array JRadioBut­ linha 129 cria um array JCheckBoxMenuItem a fim de representar os itens de menu para especificar os estilos negrito e itálico para as fontes. A linha 130 cria uma instância da classe interna StyleHandler (declarada nas linhas 184–206) para responder aos eventos de JCheckBoxMenuItem. A estrutura for nas linhas 133–139 cria cada JCheckBoxMenuItem, adiciona cada item de menu ao fontMenu e registra o ItemListener para cada item de menu. A linha 141 adiciona fontMenu como um submenu de for­ matMenu. A linha 142 adiciona o formatMenu a bar (a barra de menus). tonMenuItem fonts. A

Criando o restante da GUI e definindo os handlers de evento As linhas 145–147 criam um JLabel para o qual os itens do menu Format controlam a fonte, cor de fonte e estilo de fonte. A cor inicial do primeiro plano é configurada como o primeiro elemento do array colorValues (Color.BLACK) invocando o método setForeground JComponent. A fonte inicial é configurada como Serif com o estilo PLAIN e tamanho em pontos 72. A linha 149 configura a cor de fundo do painel de conteúdo da janela como ciano (azul), e a linha 150 anexa o JLabel ao CENTER do painel de conteúdo BorderLayout.

25.5 JPopupMenu

779

O método actionPerformed da classe ItemHandler (linhas 157–180) utiliza duas instruções for para determinar qual item de menu de fonte ou de cor gerou o evento e configura a fonte ou a cor do JLabel displayLabel, respectivamente. A condição if na linha 162 utiliza o método AbstractButton isSelected para determinar o JRadioButtonMenuItem selecionado. A condição if na linha 172 invoca o objeto de evento do método getSource para obter uma referência ao JRadioButtonMenuItem que gerou o evento. A linha 175 invoca o método AbstractButton getText para obter o nome da fonte a partir do item de menu. O método StyleHandler itemStateChanged (linhas 187–205) é chamado se o usuário selecionar um JCheckBoxMenuItem no fontMenu. As linhas 193–201 determinam quais JCheckBoxMenuItem estão selecionados e utilizam seu estado combinado para determinar o novo estilo de fonte.

25.5 JPopupMenu Muitos dos aplicativos atuais de computador fornecem os chamados menus pop-up sensíveis ao contexto. No Swing, esses menus são criados com a classe JPopupMenu (uma subclasse de JComponent). Esses menus fornecem opções que são específicas do componente pelo qual o evento de gatilho pop-up foi gerado. Na maioria dos sistemas, o evento de acionamento de pop-up ocorre quando o usuário pressiona e libera o botão direito do mouse.

Observações sobre a aparência e o comportamento 25.8 O evento de acionamento do pop-up é específico da plataforma. Na maioria das plataformas que utilizam um mouse com múltiplos botões, o evento de acionamento do pop-up ocorre quando o usuário clica com o botão direito do mouse em um componente que suporta um menu pop-up.

O aplicativo nas figuras 25.7–25.8 cria um JPopupMenu que permite ao usuário selecionar uma de três cores e alterar a cor de fundo da janela. Quando o usuário clica com o botão direito do mouse sobre o fundo da janela PopupFrame, aparece um JPopupMenu que contêm cores. Se o usuário clicar em um JRadioButtonMenuItem de uma cor, o método ItemHandler actionPerformed altera a cor de fundo do painel de conteúdo da janela. A linha 25 do construtor PopupFrame (Figura 25.7, linhas 21–69) cria uma instância da classe ItemHandler (declarada nas linhas 72–87) que processará os eventos dos itens a partir dos itens de menu do menu pop-up. A linha 29 cria o JPopupMenu. A instrução for (linhas 33–39) cria um objeto JRadioButtonMenuItem (linha 35), adiciona-o a popupMenu (linha 36), adiciona-o a ButtonGroup colorGroup (linha 37) para manter um JRadioButtonMenuItem selecionado de cada vez e registra seu ActionListener (linha 38). A linha 41 configura o fundo inicial como branco invocando o método setBackground. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

// Figura 25.7: PopupFrame.java // Demonstrando JPopupMenus. import java.awt.Color; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import javax.swing.JFrame; import javax.swing.JRadioButtonMenuItem; import javax.swing.JPopupMenu; import javax.swing.ButtonGroup; public class PopupFrame extends JFrame { private JRadioButtonMenuItem[] items; // contém itens para cores private final Color[] colorValues = { Color.BLUE, Color.YELLOW, Color.RED }; // cores a serem utilizadas private JPopupMenu popupMenu; // permite que o usuário selecione a cor // construtor sem argumento configure a GUI public PopupFrame() { super( "Using JPopupMenus" ); ItemHandler handler = new ItemHandler(); // handler para itens de menu String[] colors = { "Blue", "Yellow", "Red" }; // array de cores ButtonGroup colorGroup = new ButtonGroup(); // gerencia itens de cor popupMenu = new JPopupMenu(); // cria menu pop­up items = new JRadioButtonMenuItem[ colors.length ]; // aplica cores aos itens

780 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88

Capítulo 25  Componentes GUI: Parte 2 // cria item de menu, adiciona-o ao menu pop-up, permite tratamento de eventos for ( int count = 0; count < items.length; count++ ) { items[ count ] = new JRadioButtonMenuItem( colors[ count ] ); popupMenu.add( items[ count ] ); // adiciona o item ao menu pop-up colorGroup.add( items[ count ] ); // adiciona o item ao grupo de botões items[ count ].addActionListener( handler ); // adiciona handler } // for final setBackground( Color.WHITE ); // configura o fundo como branco // declara um MouseListener para a janela a fim de exibir o menu pop-up addMouseListener( new MouseAdapter() // classe interna anônima { // trata eventos de pressionamento do mouse public void mousePressed( MouseEvent event ) { checkForTriggerEvent( event ); // verifica o acionamento } // fim do método mousePressed // trata eventos de liberação de botão do mouse public void mouseReleased( MouseEvent event ) { checkForTriggerEvent( event ); // verifica o acionamento } // fim do método mouseReleased // determina se o evento deve acionar o menu de pop-up private void checkForTriggerEvent( MouseEvent event ) { if ( event.isPopupTrigger() ) popupMenu.show( event.getComponent(), event.getX(), event.getY() ); } // fim do método checkForTriggerEvent } // fim da classe interna anônima ); // fim da chamada para addMouseListener } // fim do construtor PopupFrame // classe interna privada para tratar eventos de item de menu private class ItemHandler implements ActionListener { // processa seleções de itens de menu public void actionPerformed( ActionEvent event ) { // determina qual item de menu foi selecionado for ( int i = 0; i < items.length; i++ ) { if ( event.getSource() == items[ i ] ) { getContentPane().setBackground( colorValues[ i ] ); return; } // fim do if } // for final } // fim do método actionPerformed } // fim da classe interna privada ItemHandler } // fim da classe PopupFrame

Figura 25.7  |  JPopupMenu para selecionar cores.



1 2 3 4 5 6 7

// Figura 25.8: PopupTest.java // Testando PopupFrame. import javax.swing.JFrame; public class PopupTest { public static void main( String[] args )

25.6 Aparência e comportamento plugável 8 9 10 11 12 13 14

781

{ PopupFrame popupFrame = new PopupFrame(); // cria PopupFrame popupFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); popupFrame.setSize( 300, 200 ); // configura o tamanho do frame popupFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe PopupTest

Figura 25.8 | Classe de teste para PopupFrame.

As linhas 44–68 registram um MouseListener para tratar os eventos de mouse da janela de aplicativo. Os métodos mousePressed (linhas 49–52) e mouseReleased (linhas 55–58) verificam o evento de acionamento do pop-up. Cada método chama o método utilitário privado checkForTriggerEvent (linhas 61–66) para determinar se o evento de disparo de pop-up ocorreu. Se ocorreu, o método Mou­ seEvent isPopupTrigger retorna true e o método JPopupMenu show exibe o JPopupMenu. O primeiro argumento para o método show especifica o componente de origem, cuja posição ajuda a determinar onde o JPopupMenu aparecerá na tela. Os dois últimos argumentos são as coordenadas x e y (medidas do canto superior esquerdo do componente de origem) em que o JPopupMenu deve aparecer.

Observações sobre a aparência e o comportamento 25.9 Exibir um JPopupMenu para o evento de acionamento de pop-up de múltiplos componentes GUI requer o registro de handlers de eventos de mouse para cada um desses componentes GUI.

Quando o usuário seleciona um item no menu pop-up, o método actionPerformed da classe ItemHandler (linhas 75–86) determina qual JRadioButtonMenuItem o usuário selecionou e configura a cor de fundo do painel de conteúdo da janela.

25.6 Aparência e comportamento plugável Um programa que utiliza componentes AWT GUI do Java (pacote java.awt) assume a aparência e o comportamento da plataforma em que o programa executa. Um aplicativo Java em execução em um Mac OS X se parece com outros aplicativos Mac OS X. Um em execução no Microsoft Windows se parece com outros aplicativos Windows. Um em execução em uma plataforma UNIX se parece com outros aplicativos nessa plataforma UNIX. Às vezes, isso é desejável porque permite que usuários do aplicativo em cada plataforma utilizem componentes GUI com os quais eles já estão familiarizados. Mas isso também introduz questões interessantes de portabilidade.

Dica de portabilidade 25.1 Os componentes GUI têm uma aparência distinta em diferentes plataformas e talvez requeiram quantidades diferentes de espaço para serem exibidos. Isso poderia alterar os layouts e alinhamentos da GUI.

Dica de portabilidade 25.2 Os componentes GUI em plataformas distintas têm funcionalidade padrão diferente (por exemplo, algumas plataformas permitem que um botão com o foco seja “pressionado” com a barra de espaço e outras não).

Os componentes GUI leves do Swing eliminam muitas dessas questões fornecendo funcionalidades uniformes entre diferentes plataformas e definindo uma aparência e funcionamento uniformes entre várias plataformas. A partir do Java SE 6 Update 10, essa é a aparência e funcionamento do Nimbo que discutimos na Seção 14.2. As versões anteriores do Java utilizavam a aparência e funcionamento metal, que ainda é o padrão. O Swing também fornece a flexibilidade para personalizar a aparência e comportamento com um estilo Microsoft Windows (somente em sistemas Windows), um estilo Motif (UNIX) (em diferentes plataformas) ou um estilo Macintosh (somente em sistemas Mac). As figuras 25.9–25.10 demonstram um modo de alterar a aparência e funcionamento de uma GUI do Swing. Ele cria vários componentes GUI, assim você pode ver a modificação na aparência e funcionamento ao mesmo tempo. As janelas de saída mostram as aparências e funcionamentos do Metal, Nimbus, CDE/Motif, Windows e Windows Classic disponíveis nos sistemas Windows. As aparências e funcionamentos instalados irão variar entre uma e outra plataforma.

782 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

Capítulo 25  Componentes GUI: Parte 2

// Figura 25.9: LookAndFeelFrame.java // Alterando a aparência e o comportamento. import java.awt.GridLayout; import java.awt.BorderLayout; import java.awt.event.ItemListener; import java.awt.event.ItemEvent; import javax.swing.JFrame; import javax.swing.UIManager; import javax.swing.JRadioButton; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JComboBox; import javax.swing.JPanel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; public class LookAndFeelFrame extends JFrame { private UIManager.LookAndFeelInfo[] looks; // aparências e comportamentos private String[] lookNames; // nomes das aparências e comportamentos private JRadioButton[] radio; // botões de opção para selecionar a aparência e o funcionamento private ButtonGroup group; // grupo de botões de opção private JButton button; // exibe a aparência do botão private JLabel label; // exibe a aparência do rótulo private JComboBox comboBox; // exibe a aparência da caixa de combinação // configura a GUI public LookAndFeelFrame() { super( "Look and Feel Demo" ); // obtém as informações sobre a aparência e o comportamento instalados looks = UIManager.getInstalledLookAndFeels(); lookNames = new String[ looks.length ]; // obtém os nomes das aparências e funcionamentos instalados for ( int i = 0; i < looks.length; i++ ) lookNames[ i ] = looks[ i ].getName(); JPanel northPanel = new JPanel(); // cria o painel North northPanel.setLayout( new GridLayout( 3, 1, 0, 5 ) ); label = new JLabel( "This is a " + lookNames[0] + " look-and-feel", SwingConstants.CENTER ); // cria o rótulo northPanel.add( label ); // adiciona o rótulo ao painel button = new JButton( "JButton" ); // cria o botão northPanel.add( button ); // adiciona botão ao painel comboBox = new JComboBox( lookNames ); // cria a caixa de combinação northPanel.add( comboBox ); // adiciona a caixa de combinação ao painel // cria um array para botões de opção radio = new JRadioButton[ looks.length ]; JPanel southPanel = new JPanel(); // cria o painel South // utiliza um GridLayout com 3 botões em cada linha int rows = (int) Math.ceil( radio.length / 3.0 ); southPanel.setLayout( new GridLayout( rows, 3 ) ); group = new ButtonGroup(); // grupo de botões para aparências e comportamentos ItemHandler handler = new ItemHandler(); // handler de aparência e comportamento for ( int count = 0; count < radio.length; count++ ) { radio[ count ] = new JRadioButton( lookNames[ count ] );

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115

25.6  Aparência e comportamento plugável radio[ count ].addItemListener( handler ); // adiciona handler group.add( radio[ count ] ); // adiciona o botão de opção ao grupo southPanel.add( radio[ count ] ); // adiciona botão de opções ao painel } // for final add( northPanel, BorderLayout.NORTH ); // adiciona o painel North add( southPanel, BorderLayout.SOUTH ); // adiciona o painel South radio[ 0 ].setSelected( true ); // configura a seleção padrão } // fim do construtor LookAndFeelFrame // utiliza UIManager para alterar a aparência e o comportamento da GUI private void changeTheLookAndFeel( int value ) { try // muda a aparência e o comportamento { // configura a aparência e o comportamento para esse aplicativo UIManager.setLookAndFeel( looks[ value ].getClassName() ); // atualiza os componentes nesse aplicativo SwingUtilities.updateComponentTreeUI( this ); } // fim do try catch ( Exception exception ) { exception.printStackTrace(); } // fim do catch } // fim do método changeTheLookAndFeel

// classe interna private para tratar eventos de botão de opção private class ItemHandler implements ItemListener { // processa a seleção de aparência e comportamento feita pelo usuário public void itemStateChanged( ItemEvent event ) { for ( int count = 0; count < radio.length; count++ ) { if ( radio[ count ].isSelected() ) { label.setText( String.format( "This is a %s look-and-feel", lookNames[ count ] ) ); comboBox.setSelectedIndex( count ); // configura o índice da caixa de combinação changeTheLookAndFeel( count ); // muda a aparência e o comportamento } // fim do if } // for final } // fim do método itemStateChanged } // fim da classe interna privada ItemHandler } // fim da classe LookAndFeelFrame

Figura 25.9  |  Aparência e comportamento de uma GUI baseada no Swing. 1 2 3 4 5 6 7 8 9 10 11 12 13 14

783

// Figura 25.10: LookAndFeelDemo.java // Alterando a aparência e o comportamento. import javax.swing.JFrame; public class LookAndFeelDemo { public static void main( String[] args ) { LookAndFeelFrame lookAndFeelFrame = new LookAndFeelFrame(); lookAndFeelFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); lookAndFeelFrame.setSize( 400, 220 ); // configura o tamanho do frame lookAndFeelFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe LookAndFeelDemo

784

Capítulo 25

Componentes GUI: Parte 2

Figura 25.10 | Classe de teste para LookAndFeelFrame.

Cobrimos os componentes GUI e conceitos que tratam o evento nesse exemplo anteriormente, portanto aqui focalizamos o mecanismo para alterar a aparência e o funcionamento. A classe UIManager (pacote javax.swing) contém uma classe aninhada LookAndFeelInfo (uma classe public static) que mantém as informações sobre uma aparência e um comportamento. A linha 20 declara um array do tipo UIManager.LookAndFeelInfo (observe a sintaxe utilizada para identificar a classe interna static LookAndFeelInfo). A linha 34 utiliza o método getInstalledLookAndFeels de UIManager static para obter o array dos objetos UIManager.LookAndFeelInfo que descrevem cada aparência e comportamento disponível no seu sistema.

Dica de desempenho 25.2 Cada aparência e comportamento é representada por uma classe Java. O método UIManager getInstalledLookAndFeels não carrega cada classe. Em vez disso, fornece os nomes das classes de aparência e comportamento disponíveis de modo que uma escolha possa ser feita (presumivelmente uma vez na inicialização do programa). Isso reduz o overhead de ter de carregar todas as classes de aparência e comportamento mesmo se o programa não utilizar algumas delas.

Nosso método utilitário changeTheLookAndFeel (linhas 81–95) é chamado pelo handler de evento para os JRadioButtons na parte inferior da interface com o usuário. O handler de evento (declarado na classe interna private ItemHandler nas linhas 98–114) passa um inteiro que representa o elemento do array looks que deve ser utilizado para alterar a aparência e comportamento. A linha 86 invoca o método static setLookAndFeel de UIManager para alterar a aparência e comportamento. O método getClassName da classe UIManager. LookAndFeelInfo determina o nome da classe de aparência e comportamento que corresponde ao objeto UIManager.LookAndFeelInfo. Se a classe da aparência e comportamento ainda não estiver carregada, ela será carregada como parte da chamada a setLookAndFeel. A linha 89 invoca o método static updateComponentTreeUI da classe SwingUtilities (pacote javax.swing) para alterar a aparência e comportamento de cada componente GUI anexado ao seu argumento (instância this da nossa classe de aplicativo LookAndFeelFrame) para a nova aparência e comportamento.

25.7 JDesktopPane e JInternalFrame Muitos aplicativos atuais utilizam uma interface de múltiplos documentos (Multiple-Document Interface — MDI) — uma janela principal (chamada janela-pai) contendo outras janelas (chamadas janelas-filhas), para gerenciar vários documentos abertos que estão sendo processados em paralelo. Por exemplo, muitos programas de correio eletrônico permitem ter várias janelas abertas ao mesmo tempo, assim você pode compor ou ler múltiplas mensagens simultaneamente. De maneira semelhante, vários processadores de texto permitem que o usuário abra múltiplos documentos em janelas separadas dentro de uma janela principal, tornando possível alternar entre elas sem fechar uma e abrir outra. O aplicativo nas figuras 25.11–25.12 demonstra as classes JDesktopPane e JInternalFrame do Swing para implementar interfaces de múltiplos documentos.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

25.7  JDesktopPane e JInternalFrame

785

// Figura 25.11: DesktopFrame.java // Demonstrando JDesktopPane import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.util.Random; import javax.swing.JFrame; import javax.swing.JDesktopPane; import javax.swing.JMenuBar; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JInternalFrame; import javax.swing.JPanel; import javax.swing.ImageIcon; public class DesktopFrame extends JFrame { private JDesktopPane theDesktop; // configura a GUI public DesktopFrame() { super( "Using a JDesktopPane" ); JMenuBar bar = new JMenuBar(); // cria a barra de menus JMenu addMenu = new JMenu( "Add" ); // cria o menu Add JMenuItem newFrame = new JMenuItem( "Internal Frame" ); addMenu.add( newFrame ); // adiciona um novo item de quadro ao menu Add bar.add( addMenu ); // adiciona o menu Add à barra de menus setJMenuBar( bar ); // configura a barra de menus para esse aplicativo theDesktop = new JDesktopPane(); // cria o painel de área de trabalho add( theDesktop ); // adiciona painel de área de trabalho ao quadro // configura o ouvinte para o item de menu newFrame newFrame.addActionListener( new ActionListener() // classe interna anônima { // exibe a nova janela interna public void actionPerformed( ActionEvent event ) { // cria o quadro interno JInternalFrame frame = new JInternalFrame( "Internal Frame", true, true, true, true ); MyJPanel panel = new MyJPanel(); // cria um novo painel frame.add( panel, BorderLayout.CENTER ); // adiciona o painel frame.pack(); // configura o quadro interno de acordo com o tamanho do conteúdo theDesktop.add( frame ); // anexa o quadro interno frame.setVisible( true ); // mostra o quadro interno } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener } // fim do construtor DesktopFrame } // fim da classe DesktopFrame // classe para exibir um ImageIcon em um painel class MyJPanel extends JPanel { private static Random generator = new Random(); private ImageIcon picture; // imagem a ser exibida private final static String[] images = { "yellowflowers.png", "purpleflowers.png", "redflowers.png", "redflowers2.png",

786 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

Capítulo 25  Componentes GUI: Parte 2 "lavenderflowers.png" }; // carrega a imagem public MyJPanel() { int randomNumber = generator.nextInt( images.length ); picture = new ImageIcon( images[ randomNumber ] ); // configura o ícone } // fim do construtor MyJPanel // exibe imageIcon no painel public void paintComponent( Graphics g ) { super.paintComponent( g ); picture.paintIcon( this, g, 0, 0 ); // exibe o ícone } // fim do método paintComponent // retorna as dimensões da imagem public Dimension getPreferredSize() { return new Dimension( picture.getIconWidth(), picture.getIconHeight() ); } // fim do método getPreferredSize } // fim da classe MyJPanel

Figura 25.11  |  Interface de múltiplos documentos.

As linhas 12–33 definem um JMenuBar, um JMenu e um JMenuItem, para adicionar o JMenuItem a JMenu, adicionar o JMenu a JMenuBar e configurar o JMenuBar para a janela de aplicativo. Quando o usuário seleciona o JMenuItem newFrame, o aplicativo cria e exibe um novo objeto JInternalFrame contendo uma imagem. A linha 35 atribui a variável theDesktop JDesktopPane (pacote javax.swing) a um novo objeto JDesktopPane que será utilizado para gerenciar as janelas-filhas de JInternalFrame. A linha 36 adiciona o JDesktopPane ao JFrame. Por padrão, o JDesktop­ Pane é adicionado ao centro do painel de conteúdo BorderLayout e então o JDesktopPane se expande para preencher a janela inteira do aplicativo. As linhas 39–58 registram um ActionListener para tratar o evento quando o usuário seleciona o item de menu newFrame. Quando o evento ocorre, o método actionPerformed (linhas 44–56) cria um objeto JInternalFrame nas linhas 47–48. O construtor JInternal­ Frame aqui utilizado recebe cinco argumentos — uma String para a barra de título da janela interna, um boolean indicando se o frame interno pode ser redimensionado pelo usuário, um boolean indicando se o frame interno pode ser fechado pelo usuário, um boolean indicando se o frame interno pode ser maximizado pelo usuário e um boolean indicando se o frame interno pode ser minimizado pelo usuário. Para cada um dos argumentos boolean, um valor true indica que a operação deve ser permitida (como é o caso aqui). Como ocorre com JFrames e JApplets, um JInternalFrame tem um painel de conteúdo a que componentes GUI podem ser anexados. A linha 50 (Figura 25.11) cria uma instância da nossa classe MyJPanel (declarada nas linhas 63–91), que é adicionada ao JInter­ nalFrame na linha 51. 1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 25.12: DesktopTest.java // Demonstrando JDesktopPane. import javax.swing.JFrame; public class DesktopTest { public static void main( String[] args ) { DesktopFrame desktopFrame = new DesktopFrame(); desktopFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); desktopFrame.setSize( 600, 480 ); // configura o tamanho do frame desktopFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe DesktopTest

25.8 JTabbedPane

Frames internos

Frames internos minimizados

Minimizar

Maximizar

787

Fechar

Posicione o mouse sobre qualquer canto de uma janela-filha para redimensionar a janela (se for permitido redimensionar).

Frame interno maximizado

Figura 25.12 | Classe de teste para DeskTopFrame.

A linha 52 utiliza o método JInternalFrame pack para configurar o tamanho da janela-filha. O método pack utiliza os tamanhos preferidos dos componentes para determinar o tamanho da janela. A classe MyJPanel declara o método getPreferredSize (linhas 86–90) para especificar o tamanho preferido do painel para utilização pelo método pack. A linha 54 adiciona o JInternalFrame ao JDesktopPane, e a linha 55 exibe o JInternalFrame. As classes JInternalFrame e JDesktopPane fornecem muitos métodos para gerenciar janelas-filhas. Consulte a documentação on-line da API de JInternalFrame e JDesktopPane para listas completas desses métodos: java.sun.com/javase/6/docs/api/javax/swing/JInternalFrame.html java.sun.com/javase/6/docs/api/javax/swing/JDesktopPane.html

25.8 JTabbedPane Um JTabbedPane organiza componentes GUI em camadas, das quais somente uma é visível de cada vez. Os usuários acessam cada camada via uma guia — semelhante a pastas em um gabinete de arquivos. Quando o usuário clica em uma guia, a camada apropriada é exibida. As guias aparecem na parte superior por padrão, mas também podem ser posicionadas à esquerda, direita ou parte inferior do JTabbedPane. Qualquer componente pode ser posicionado em uma guia. Se o componente for um contêiner, como um painel, ele poderá utilizar qualquer gerenciador de layout para organizar vários componentes na guia. A classe JTabbedPane é uma subclasse de JCompo­ nent. O aplicativo nas figuras 25.13–25.14 cria um painel com guias com três guias. Cada guia exibe um dos JPanels — panel1, panel2 ou panel3.

788

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

Capítulo 25  Componentes GUI: Parte 2

// Figura 25.13: JTabbedPaneFrame.java // Demonstrando o JTabbedPane. import java.awt.BorderLayout; import java.awt.Color; import javax.swing.JFrame; import javax.swing.JTabbedPane; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JButton; import javax.swing.SwingConstants; public class JTabbedPaneFrame extends JFrame { // configura a GUI public JTabbedPaneFrame() { super( "JTabbedPane Demo " ); JTabbedPane tabbedPane = new JTabbedPane(); // cria o JTabbedPane // configura o pane11 e o adiciona ao JTabbedPane JLabel label1 = new JLabel( "panel one", SwingConstants.CENTER ); JPanel panel1 = new JPanel(); // cria o primeiro painel panel1.add( label1 ); // adiciona o rótulo ao painel tabbedPane.addTab( "Tab One", null, panel1, "First Panel" ); // configura o panel2 e o adiciona a JTabbedPane JLabel label2 = new JLabel( "panel two", SwingConstants.CENTER ); JPanel panel2 = new JPanel(); // cria o segundo painel panel2.setBackground( Color.YELLOW ); // configura o fundo como amarelo panel2.add( label2 ); // adiciona o rótulo ao painel tabbedPane.addTab( "Tab Two", null, panel2, "Second Panel" ); // configura o panel3 e o adiciona a JTabbedPane JLabel label3 = new JLabel( "panel three" ); JPanel panel3 = new JPanel(); // cria o terceiro painel panel3.setLayout( new BorderLayout() ); // utilize o borderlayout panel3.add( new JButton( "North" ), BorderLayout.NORTH ); panel3.add( new JButton( "West" ), BorderLayout.WEST ); panel3.add( new JButton( "East" ), BorderLayout.EAST ); panel3.add( new JButton( "South" ), BorderLayout.SOUTH ); panel3.add( label3, BorderLayout.CENTER ); tabbedPane.addTab( "Tab Three", null, panel3, "Third Panel" ); add( tabbedPane ); // adiciona o JTabbedPane ao quadro } // fim do construtor JTabbedPaneFrame } // fim da classe JTabbedPaneFrame

Figura 25.13  |  JTabbedPane utilizado para organizar componentes GUI.

1 2 3 4 5 6 7 8 9 10 11 12 13 14

// Figura 25.14: JTabbedPaneDemo.java // Demonstrando o JTabbedPane. import javax.swing.JFrame; public class JTabbedPaneDemo { public static void main( String[] args ) { JTabbedPaneFrame tabbedPaneFrame = new JTabbedPaneFrame(); tabbedPaneFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); tabbedPaneFrame.setSize( 250, 200 ); // configura o tamanho do frame tabbedPaneFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe JTabbedPaneDemo

25.9 Gerenciadores de layout: BoxLayout e GridBagLayout

789

Figura 25.14 | Classe de teste para JTabbedPaneFrame.

O construtor (linhas 15–46) constrói a GUI. A linha 19 cria um JTabbedPane vazio com as configurações padrão, isto é, guias ao longo da parte superior. Se as guias não se ajustarem em uma linha, elas serão empacotadas a fim de formar linhas adicionais das guias. Em seguida, o construtor cria os JPanels panel1, panel2 e panel3 e seus componentes GUI. À medida que configuramos cada painel, nós o adicionamos ao tabbedPane utilizando o método JTabbedPane addTab com quatro argumentos. O primeiro argumento é uma String que especifica o título da guia. O segundo argumento é uma referência Icon que especifica um ícone a ser exibido na guia. Se o Icon for uma referência null, nenhuma imagem será exibida. O terceiro argumento é uma referência Component que representa o componente GUI a ser exibido quando o usuário clica na guia. O último argumento é uma String que especifica a dica de ferramenta para a guia. Por exemplo, a linha 25 adiciona o JPanel panel1 ao tabbedPane com o título "Tab One" e a dica de ferramenta "First Panel". Os JPanels panel2 e panel3 são adicionados ao tabbedPane nas linhas 32 e 43. Para visualizar uma guia, clique nela com o mouse ou utilize as teclas de seta para alternar as guias.

25.9 Gerenciadores de layout: BoxLayout e GridBagLayout No Capítulo 14, introduzimos três gerenciadores de layout — FlowLayout, BorderLayout e GridLayout. Esta seção apresenta dois gerenciadores adicionais de layout (resumidos na Figura 25.15). Discutimos esses gerenciadores de layout nos exemplos que se seguem. Gerenciador de layout

Descrição

BoxLayout

Um gerenciador de layout que permite que os componentes GUI sejam organizados da esquerda para a direita ou de cima para baixo em um contêiner. A classe Box declara um contêiner com BoxLayout como seu gerenciador padrão de layout e fornece métodos static para criar um Box com um BoxLayout horizontal ou vertical.

GridBagLayout

Um gerenciador de layout semelhante a GridLayout, mas os componentes podem variar de tamanho e podem ser adicionados em qualquer ordem.

Figura 25.15 | Gerenciadores de layout adicionais.

Gerenciador de layout BoxLayout O gerenciador de layout BoxLayout (no pacote javax.swing) organiza os componentes GUI horizontalmente ao longo do eixo x ou verticalmente ao longo do eixo y de um contêiner. O aplicativo nas figuras 25.16–25.17 demonstra o BoxLayout e a classe contêiner Box, que utiliza o BoxLayout como seu gerenciador padrão de layout. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura 25.16: BoxLayoutFrame.java // Demonstrando BoxLayout. import java.awt.Dimension; import javax.swing.JFrame; import javax.swing.Box; import javax.swing.JButton; import javax.swing.BoxLayout; import javax.swing.JPanel; import javax.swing.JTabbedPane; public class BoxLayoutFrame extends JFrame {

790 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

Capítulo 25  Componentes GUI: Parte 2 // configura a GUI public BoxLayoutFrame() { super( "Demonstrating BoxLayout" ); // cria contêineres Box com BoxLayout Box horizontal1 = Box.createHorizontalBox(); Box vertical1 = Box.createVerticalBox(); Box horizontal2 = Box.createHorizontalBox(); Box vertical2 = Box.createVerticalBox(); final int SIZE = 3; // número de botões em cada Box // adiciona botões a Box horizontal1 for ( int count = 0; count < SIZE; count++ ) horizontal1.add( new JButton( "Button " + count ) ); // cria um suporte e adiciona botões a Box for ( int count = 0; count < SIZE; count++ { vertical1.add( Box.createVerticalStrut( vertical1.add( new JButton( "Button " + } // for final

vertical1 ) 25 ) ); count ) );

// cria a cola horizontal e adiciona botões a Box horizontal2 for ( int count = 0; count < SIZE; count++ ) { horizontal2.add( Box.createHorizontalGlue() ); horizontal2.add( new JButton( "Button " + count ) ); } // for final // cria uma área rígida e adiciona botões a Box vertical2 for ( int count = 0; count < SIZE; count++ ) { vertical2.add( Box.createRigidArea( new Dimension( 12, 8 ) ) ); vertical2.add( new JButton( "Button " + count ) ); } // for final // cria cola vertical e adiciona botões ao painel JPanel panel = new JPanel(); panel.setLayout(new BoxLayout( panel, BoxLayout.Y_AXIS ) ); for ( int count = 0; count < SIZE; count++ ) { panel.add( Box.createGlue() ); panel.add( new JButton( "Button " + count ) ); } // for final // cria um JTabbedPane JTabbedPane tabs = new JTabbedPane( JTabbedPane.TOP, JTabbedPane.SCROLL_TAB_LAYOUT ); // coloca cada contêiner no painel com guias tabs.addTab( "Horizontal Box", horizontal1 ); tabs.addTab( "Vertical Box with Struts", vertical1 ); tabs.addTab( "Horizontal Box with Glue", horizontal2 ); tabs.addTab( "Vertical Box with Rigid Areas", vertical2 ); tabs.addTab( "Vertical Box with Glue", panel ); add( tabs ); // coloca o painel com guias no quadro } // fim do construtor BoxLayoutFrame } // fim da classe BoxLayoutFrame

Figura 25.16  |  Gerenciador de layout BoxLayout.



25.9  Gerenciadores de layout: BoxLayout e GridBagLayout

1 2 3 4 5 6 7 8 9 10 11 12 13 14

791

// Figura 25.17: BoxLayoutDemo.java // Demonstrando BoxLayout. import javax.swing.JFrame; public class BoxLayoutDemo { public static void main( String[] args ) { BoxLayoutFrame boxLayoutFrame = new BoxLayoutFrame(); boxLayoutFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); boxLayoutFrame.setSize( 400, 220 ); // configura o tamanho do frame boxLayoutFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe BoxLayoutDemo Setas para alternar pelas guias

Figura 25.17  |  Classe de teste para BoxLayoutFrame.

As linhas 19–22 criam contêineres Box. As referências horizontal1 e horizontal2 são inicializadas com método static Box createHorizontalBox, que retorna um contêiner Box com um BoxLayout horizontal em que os componentes GUI são organizados da

esquerda para a direita. As variáveis vertical1 e vertical2 são inicializadas com o método static Box createVerticalBox, que retorna referências aos contêineres Box com um BoxLayout vertical, no qual os componentes GUI são organizados de cima para baixo. O loop nas linhas 27–28 adiciona três JButtons a horizontal1. A estrutura for nas linhas 31–35 adiciona três JButtons ao vertical1. Antes de adicionar cada botão, a linha 33 adiciona um suporte vertical ao contêiner com o método static Box createVerticalStrut. Uma estrutura vertical é um componente GUI invisível que tem uma altura fixa em pixels e é utilizado para garantir uma quantidade fixa de espaço entre os componentes GUI. O argumento int para o método createVerticalStrut determina a altura da estrutura em pixels. Quando o contêiner é redimensionado, a distância entre os componentes GUI separados por struts não muda. A classe Box também declara o método createHorizontalStrut para BoxLayouts horizontais. A estrutura for nas linhas 38–42 adiciona três JButtons ao horizontal2. Antes de adicionar cada botão, a linha 40 adiciona a cola horizontal ao contêiner com o método static Box createHorizontalGlue. A cola horizontal é um componente GUI invisível que pode ser utilizado entre componentes GUI de tamanho fixo para ocupar espaço adicional. Normalmente, o espaço extra aparece à direita do último componente GUI horizontal ou abaixo do último vertical em um BoxLayout. A cola permite que o espaço extra seja colocado entre componentes GUI. Quando o contêiner é redimensionado, componentes separados por componentes de cola permanecem no mesmo tamanho, mas a cola se expande ou se contrai para ocupar o espaço entre eles. A classe Box também declara o método createVerticalGlue para BoxLayouts verticais. A estrutura for nas linhas 45–49 adiciona três JButtons ao vertical2. Antes de cada botão ser adicionado, a linha 47 adiciona uma área rígida ao contêiner com o método static Box createRigidArea. Uma área rígida é um componente GUI invisível que sempre tem a largura e altura fixas em pixels. O argumento para o método createRigidArea é um objeto Dimension que especifica a largura e altura da área.

792

Capítulo 25  Componentes GUI: Parte 2

As linhas 52–53 criam um objeto JPanel e configuram seu layout como um BoxLayout da maneira convencional, utilizando o método Container setLayout. O construtor BoxLayout recebe uma referência ao contêiner cujo layout ele controla, e uma constante indicando se o layout é horizontal (BoxLayout.X_AXIS) ou vertical (BoxLayout.Y_AXIS). A estrutura for nas linhas 55–59 adiciona três JButtons ao panel. Antes de adicionar cada botão, a linha 57 adiciona um componente de cola ao contêiner com o método static Box createGlue. Esse componente expande ou contrai com base no tamanho da classe Box. As linhas 62–63 criam um JTabbedPane para exibir os cinco contêineres nesse programa. O argumento JTabbedPane.TOP enviado ao construtor indica que as guias devem aparecer na parte superior do JTabbedPane. O argumento JTabbedPane.SCROLL_TAB_LAYOUT especifica que as guias devem ser posicionadas em uma nova linha se houver muitas para que se ajustem em uma linha. Os contêineres Box e o JPanel são anexados ao JTabbedPane nas linhas 66–70. Tente executar o aplicativo. Quando a janela aparecer, redimensione a janela para ver como os componentes cola, estrutura e área rígida afetam o layout em cada guia.

Gerenciador de layout GridBagLayout Um dos gerenciadores de layout predefinidos mais poderosos é GridBagLayout (no pacote java.awt). Esse layout é semelhante ao GridLayout pelo fato de que ele organiza os componentes em uma grade. Entretanto, GridBagLayout é mais flexível. Os componentes podem variar em tamanho (isto é, eles podem ocupar múltiplas linhas e colunas) e podem ser adicionados em qualquer ordem. O primeiro passo na utilização de GridBagLayout é determinar a aparência da GUI. Para este passo você precisa somente de um pedaço de papel. Desenhe a GUI e, então, desenhe uma grade sobre ela, dividindo os componentes nas linhas e colunas. Os números iniciais de linhas e colunas devem ser 0, de modo que o gerenciador de layout GridBagLayout possa utilizar os números de linha e coluna para posicionar adequadamente os componentes na grade. A Figura 25.18 demonstra como desenhar linhas e colunas sobre uma GUI. Coluna 0

1

2

0 1 Linha

2 3

Figura 25.18  |  Projetando uma GUI que utilizará GridBagLayout.

Um objeto GridBagConstraints descreve como um componente é posicionado em um GridBagLayout. Vários campos GridBag­ Constraints estão resumidos na Figura 25.19.

Campo

Descrição

anchor

Especifica a posição relativa (NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST, CENTER) do componente em uma área que ele não preenche.

fill

Redimensiona o componente na direção especificada (NONE, HORIZONTAL, VERTICAL, BOTH) quando a área de exibição for maior que o componente.

gridx

A coluna em que o componente será colocado.

gridy

A linha em que o componente será colocado.

gridwidth

O número de colunas que componente ocupa.

gridheight

O número de linhas que o componente ocupa.

weightx

A quantidade de espaço extra a alocar horizontalmente. O componente na grade pode tornar-se mais largo se houver espaço extra disponível.

weighty

A quantidade de espaço extra a alocar verticalmente. O componente na grade pode tornar-se mais alto se houver espaço extra disponível.

Figura 25.19  |  Campos GridBagConstraints.



25.9  Gerenciadores de layout: BoxLayout e GridBagLayout

793

O campo GridBagConstraints anchor especifica a posição relativa do componente em uma área que ele não preenche. Atribui-se à variável anchor uma das seguintes constantes GridBagConstraints: NORTH, NORTHEAST, EAST, SOUTHEAST, SOUTH, SOUTHWEST, WEST, NORTHWEST ou CENTER. O valor padrão é CENTER. O campo GridBagConstraints fill define como o componente aumenta se a área em que pode ser exibido for maior que o componente. Atribui-se à variável fill uma das seguintes constantes GridBagConstraints: NONE, VERTICAL, HORIZONTAL ou BOTH. O valor padrão é NONE, o que indica que o componente não crescerá em nenhuma direção. VERTICAL indica que ele crescerá verticalmente. HORI­ ZONTAL indica que ele crescerá horizontalmente. BOTH indica que ele crescerá em ambas as direções. As variáveis gridx e gridy especificam onde o canto superior esquerdo do componente é posicionado na grade. A variável gridx corresponde à coluna, e a variável gridy corresponde à linha. Na Figura 25.18, o JComboBox (exibindo "Iron") tem um valor gridx de 1 e um valor gridy de 2. A variável gridwidth especifica o número de colunas que um componente ocupa. O JComboBox ocupa duas colunas. A variável gridheight especifica o número de linhas que um componente ocupa. O JTextArea no lado esquerdo da Figura 25.18 ocupa três linhas. A variável weightx especifica como distribuir o espaço horizontal extra para componentes na grade em um GridBagLayout quando o contêiner é redimensionado. Um valor zero indica que o componente na grade não aumenta horizontalmente por conta própria. Entretanto, se o componente distribui uma coluna contendo um componente com valor weightx diferente de zero, o componente com o valor weightx de zero crescerá horizontalmente na mesma proporção que o(s) outro(s) componente(s) dessa coluna. Isso ocorre porque cada componente deve ser mantido na mesma linha e coluna em que foi originalmente posicionado. A variável weighty especifica como distribuir espaço vertical extra para componentes na grade em um GridBagLayout quando o contêiner é redimensionado. Um valor zero indica que o componente na grade não aumenta verticalmente por conta própria. Entretanto, se o componente se distribui por uma linha contendo um componente com valor weighty diferente de zero, o componente com o valor weighty de zero cresce verticalmente na mesma proporção que o(s) outro(s) componente(s) na mesma linha. Na Figura 25.18, os efeitos de weighty e weightx não podem ser vistos facilmente até que o contêiner seja redimensionado e o espaço adicional torne-se disponível. Os componentes com valores de peso maiores ocupam mais do espaço adicional que aqueles com valores de peso menores. Os componentes devem receber valores de peso positivos diferentes de zero — caso contrário, eles “se amontoam” no meio do contêiner. A Figura 25.20 mostra a GUI da Figura 25.18 com todos os pesos configurados como zero.

Figura 25.20  |  GridBagLayout com os pesos configurados como zero.

O aplicativo nas figuras 25.21–25.22 utiliza o gerenciador de layout GridBagLayout para organizar os componentes da GUI na Figura 25.18. O aplicativo não faz nada, exceto demonstrar como utilizar GridBagLayout. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

// Figura 25.21: GridbagFrame.java // Demonstrando GridBagLayout import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Component; import javax.swing.JFrame; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JButton; import javax.swing.JComboBox; public class GridBagFrame extends JFrame { private GridBagLayout layout; // layout desse quadro private GridBagConstraints constraints; // restrições desse layout // configura a GUI

794 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79

Capítulo 25  Componentes GUI: Parte 2 public GridBagFrame() { super( "GridBagLayout" ); layout = new GridBagLayout(); setLayout( layout ); // configura o layout de frame constraints = new GridBagConstraints(); // instancia restrições // cria componentes GUI JTextArea textArea1 = new JTextArea( "TextArea1", 5, 10 ); JTextArea textArea2 = new JTextArea( "TextArea2", 2, 2 ); String[] names = { "Iron", "Steel", "Brass" }; JComboBox comboBox = new JComboBox( names ); JTextField textField = new JTextField( JButton button1 = new JButton( "Button JButton button2 = new JButton( "Button JButton button3 = new JButton( "Button

"TextField" ); 1" ); 2" ); 3" );

// weightx e weighty para textArea1 são 0: o padrão // anchor para todos os componentes CENTER: o padrão constraints.fill = GridBagConstraints.BOTH; addComponent( textArea1, 0, 0, 1, 3 ); // weightx e weighty para button1 são 0: o padrão constraints.fill = GridBagConstraints.HORIZONTAL; addComponent( button1, 0, 1, 2, 1 ); // weightx e weighty para comboBox são 0: o padrão // fill é HORIZONTAL addComponent( comboBox, 2, 1, 2, 1 ); // button2 constraints.weightx = 1000; // pode crescer na largura constraints.weighty = 1; // pode crescer na altura constraints.fill = GridBagConstraints.BOTH; addComponent( button2, 1, 1, 1, 1 ); // preenchimento é BOTH para button3 constraints.weightx = 0; constraints.weighty = 0; addComponent( button3, 1, 2, 1, 1 ); // weightx e weighty para textField são 0, preenchimento é BOTH addComponent( textField, 3, 0, 2, 1 ); // weightx e weighty para textArea2 são 0, preenchimento é BOTH addComponent( textArea2, 3, 2, 1, 1 ); } // fim do construtor GridBagFrame // método para configurar restrições em private void addComponent( Component component, int row, int column, int width, int height ) { constraints.gridx = column; // configura gridx constraints.gridy = row; // configura gridy constraints.gridwidth = width; // configura gridwidth constraints.gridheight = height; // configure gridheight layout.setConstraints( component, constraints ); // configura constraints add( component ); // adiciona componente } // fim do método addComponent } // fim da classe GridBagFrame

Figura 25.21  |  Gerenciador de layout GridBagLayout.



1 2 3 4 5 6 7 8 9 10 11 12 13 14

25.9  Gerenciadores de layout: BoxLayout e GridBagLayout

795

// Figura 25.22: GridBagDemo.java // Demonstrando GridBagLayout import javax.swing.JFrame; public class GridBagDemo { public static void main( String[] args ) { GridBagFrame gridBagFrame = new GridBagFrame(); gridBagFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); gridBagFrame.setSize( 300, 150 ); // configura o tamanho do frame gridBagFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe GridBagDemo

Figura 25.22  |  Classe de teste para GridBagFrame.

A GUI contém três JButtons, duas JTextAreas, um JComboBox e um JText-Field. O gerenciador de layout é GridBagLayout. As linhas 21–22 criam o objeto GridBagLayout e configuram o gerenciador de layout para o JFrame como layout. A linha 23 cria o objeto GridBagConstraints utilizado para determinar a localização e tamanho de cada componente na grade. As linhas 26–35 criam cada componente GUI que será adicionado ao painel de conteúdo. As linhas 39–40 configuram JTextArea textArea1 e o adicionam ao painel de conteúdo. Os valores para weightx e weighty não são especificados em constraints, portanto cada um tem o valor zero por padrão. Portanto, a JTextArea não redimensionará a si própria mesmo se espaço estiver disponível. Entretanto, ela se distribui por múltiplas linhas, então o tamanho vertical está sujeito aos valores weighty dos JButtons button2 e button3. Quando um botão é redimensionado verticalmente com base no seu valor weighty, a JTextArea também é redimensionada. A linha 39 configura a variável fill em constraints como GridBagConstraints.BOTH, fazendo com que a JTextArea sempre preencha toda sua área alocada na grade. Um valor anchor não é especificado em constraints, assim o padrão CENTER é utilizado. Não utilizamos a variável anchor nesse aplicativo, portanto todos os componentes utilizarão o padrão. A linha 40 chama nosso método utilitário addComponent (declarado nas linhas 69–78). O objeto JTextArea, a linha, a coluna, o número de colunas a distribuir e o número de linhas a distribuir são passados como argumentos. JButton button1 é o próximo componente adicionado (linhas 43–44). Por padrão, os valores de weightx e weighty ainda são zero. A variável fill é configurada como HORIZONTAL — o componente sempre ocupará sua área na direção horizontal. A direção vertical não é ocupada. Como o valor weighty é zero, o botão se tornará mais alto somente se outro componente na mesma linha tiver um valor weighty diferente de zero. JButton button1 está localizado na linha 0, coluna 1. Uma linha e duas colunas são ocupadas.

796

Capítulo 25  Componentes GUI: Parte 2

JComboBox comboBox é o próximo componente adicionado (linha 48). Por padrão, os valores weightx e weighty são zero, e a variável fill é configurada como HORIZONTAL. O botão JComboBox crescerá somente na direção horizontal. Observe que as variáveis weightx, weighty e fill retêm os valores configurados em constraints até eles serem alterados. O botão JComboBox é colocado na linha 2, coluna 1. Uma linha e duas colunas são ocupadas. JButton button2 é o próximo componente adicionado (linhas 51–54). É dado um valor de weightx de 1000 e um valor de weighty de 1. A área ocupada pelo botão é capaz de crescer nas direções horizontal e vertical. A variável fill é configurada como BOTH, que especifica que o botão sempre ocupará a área inteira. Quando a janela é redimensionada, button2 crescerá. O botão é colocado na linha 1, coluna 1. Uma linha e uma coluna são ocupadas. JButton button3 é adicionado a seguir (linhas 57–59). O valor weightx e o valor weighty são ambos configurados como zero e o valor de fill é BOTH. JButton button3 crescerá se a janela for redimensionada; ele é afetado pelos valores de peso de button2. Observe que o valor de weightx para button2 é muito maior do que para button3. Quando o redimensionamento ocorrer, button2 ocupará uma porcentagem maior do novo espaço. O botão é colocado na linha 1, coluna 2. Uma linha e uma coluna são ocupadas. Tanto o JTextField textField (linha 62) como a JTextArea textArea2 (linha 65) têm um valor weightx de 0 e um valor weighty de 0. O valor de fill é BOTH. O JTextField é posicionado na linha 3, coluna 0 e a JTextArea na linha 3, coluna 2. O JTextField ocupa uma linha e duas colunas, a JTextArea, uma linha e uma coluna. Os parâmetros do método addComponent são um component de referência Component e números inteiros row, column, width e height. As linhas 72–73 configuram as variáveis GridBagConstraints gridx e gridy. A variável gridx é atribuída à coluna em que o Component será posicionado e o valor gridy é atribuído à linha em que Component será posicionado. As linhas 74–75 configuram as variáveis GridBagConstraints gridwidth e gridheight. A variável gridwidth especifica o número de colunas que o Component estenderá na grade e a variável gridheight especifica o número de linhas que o Component estenderá na grade. A linha 76 configura as GridBagConstraints para um componente no GridBagLayout. O método setConstraints da classe GridBagLayout aceita um argumento Component e um argumento GridBagConstraints. A linha 77 adiciona o componente ao JFrame. Quando executar esse aplicativo, tente redimensionar a janela para ver como as limitações para cada componente GUI afetam sua posição e tamanho na janela.

Constantes GridBagConstraints RELATIVE e REMAINDER Em vez de gridx e gridy, uma variação de GridBagLayout utiliza as constantes GridBagConstraints RELATIVE e REMAINDER. RELATIVE especifica que o penúltimo componente em uma linha particular deve ser posicionado à direita do componente anterior na linha. REMAINDER especifica que um componente é o último componente em uma coluna. Qualquer componente que não seja penúltimo ou o último componente em uma linha deve especificar os valores para as variáveis GridbagConstraints gridwidth e gridheight. O aplicativo nas figuras 25.23–25.24 organiza os componentes no GridBagLayout, utilizando essas constantes. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

// Figura 25.23: GridBagFrame2.java // Demonstrando as constantes GridBagLayout. import java.awt.GridBagLayout; import java.awt.GridBagConstraints; import java.awt.Component; import javax.swing.JFrame; import javax.swing.JComboBox; import javax.swing.JTextField; import javax.swing.JList; import javax.swing.JButton; public class GridBagFrame2 extends JFrame { private GridBagLayout layout; // layout desse quadro private GridBagConstraints constraints; // restrições desse layout // configura a GUI public GridBagFrame2() { super( "GridBagLayout" ); layout = new GridBagLayout(); setLayout( layout ); // configura o layout de frame constraints = new GridBagConstraints(); // instancia restrições // cria componentes GUI String[] metals = { "Copper", "Aluminum", "Silver" };



25.9  Gerenciadores de layout: BoxLayout e GridBagLayout

27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

JComboBox comboBox = new JComboBox( metals ); JTextField textField = new JTextField( "TextField" ); String[] fonts = { "Serif", "Monospaced" }; JList list = new JList( fonts ); String[] names = { "zero", "one", "two", "three", "four" }; JButton[] buttons = new JButton[ names.length ]; for ( int count = 0; count < buttons.length; count++ ) buttons[ count ] = new JButton( names[ count ] ); // define restrições dos componentes GUI para textField constraints.weightx = 1; constraints.weighty = 1; constraints.fill = GridBagConstraints.BOTH; constraints.gridwidth = GridBagConstraints.REMAINDER; addComponent( textField ); // buttons[0] -- weightx e weighty são 1: fill é BOTH constraints.gridwidth = 1; addComponent( buttons[ 0 ] ); // buttons[1] -- weightx e weighty são 1: fill é BOTH constraints.gridwidth = GridBagConstraints.RELATIVE; addComponent( buttons[ 1 ] ); // buttons[2] -- weightx e weighty são 1: fill é BOTH constraints.gridwidth = GridBagConstraints.REMAINDER; addComponent( buttons[ 2 ] ); // comboBox -- weightx é 1: fill é BOTH constraints.weighty = 0; constraints.gridwidth = GridBagConstraints.REMAINDER; addComponent( comboBox ); // buttons[3] -- weightx é 1: fill é BOTH constraints.weighty = 1; constraints.gridwidth = GridBagConstraints.REMAINDER; addComponent( buttons[ 3 ] ); // buttons[4] -- weightx e weighty são 1: fill é BOTH constraints.gridwidth = GridBagConstraints.RELATIVE; addComponent( buttons[ 4 ] ); // list -- weightx e weighty são 1: fill é BOTH constraints.gridwidth = GridBagConstraints.REMAINDER; addComponent( list ); } // fim do construtor GridBagFrame2 // adiciona um componente ao contêiner private void addComponent( Component component ) { layout.setConstraints( component, constraints ); add( component ); // adiciona componente } // fim do método addComponent } // fim da classe GridBagFrame2

Figura 25.23  |  Constantes GridBagConstraints RELATIVE e REMAINDER.



1 2 3 4 5 6

// Figura 25.24: GridBagDemo2.java // Demonstrando as constantes GridBagLayout. import javax.swing.JFrame; public class GridBagDemo2 {

797

798 7 8 9 10 11 12 13 14

Capítulo 25

Componentes GUI: Parte 2

public static void main( String[] args ) { GridBagFrame2 gridBagFrame = new GridBagFrame2(); gridBagFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); gridBagFrame.setSize( 300, 200 ); // configura o tamanho do frame gridBagFrame.setVisible( true ); // exibe o frame } // fim de main } // fim da classe GridBagDemo2

Figura 25.24 | Classe de teste para GridBagDemo2.

As linhas 21–22 criam um GridBagLayout e o utilizam para configurar o gerenciador de layout do JFrame. Os componentes que são inseridos no GridBagLayout são criados nas linhas 27–38 — eles são um JComboBox, um JTextField, uma JList e cinco JButtons. O JTextField é adicionado primeiro (linhas 41–45). Os valores weightx e weighty são configurados como 1. A variável fill é configurada como BOTH. A linha 44 especifica que o JTextField é o último componente na linha. O JTextField é adicionado ao painel de conteúdo com uma chamada ao nosso método utilitário addComponent (declarado nas linhas 79–83). O método addComponent aceita um argumento Component e utiliza o método GridBagLayout setConstraints para configurar as limitações para o Component. O método add anexa o componente ao painel de conteúdo. JButton buttons[ 0 ] (linhas 48–49) tem valores weightx e weighty de 1. A variável fill é BOTH. Como buttons[ 0 ] não é um dos dois últimos componentes na linha, ele recebeu um gridwidth de 1 e assim ocupará uma coluna. O JButton é adicionado ao painel de conteúdo com uma chamada para o método utilitário addComponent. JButton buttons[ 1 ] (linhas 52–53) tem valores weightx e weighty de 1. A variável fill é BOTH. A linha 52 especifica que o JButton deve ser posicionado em relação ao componente anterior. O Button é adicionado ao JFrame com uma chamada a addComponent. JButton buttons[ 2 ] (linhas 56–57) tem valores weightx e weighty de 1. A variável fill é BOTH. O JButton é o último componente da linha, então REMAINDER é utilizado. O JButton é adicionado ao painel de conteúdo com uma chamada a addComponent. O JComboBox (linhas 60–62) tem um weightx de 1 e um weighty de 0. O JComboBox não crescerá verticalmente. O JComboBox é o único componente na linha, então REMAINDER é utilizado. O JComboBox é adicionado ao painel de conteúdo com uma chamada a addComponent. JButton buttons[ 3 ] (linhas 65–67) tem valores weightx e weighty de 1. A variável fill é BOTH. O JButton é o único componente na linha, então REMAINDER é utilizado. O JButton é adicionado ao painel de conteúdo com uma chamada a addComponent. JButton buttons[ 4 ] (linhas 70–71) tem valores weightx e weighty de 1. A variável fill é BOTH. Esse JButton é o único componente na linha, então RELATIVE é utilizado. O JButton é adicionado ao painel de conteúdo com uma chamada a addComponent. A JList (linhas 74–75) tem valores weightx e weighty de 1. A variável fill é BOTH. O JList é adicionado ao painel de conteúdo com uma chamada a addComponent.

25.10 Conclusão Este capítulo completa nossa introdução às GUIs. Nele, discutimos tópicos sobre GUI adicionais, como menus, controles deslizantes, menus pop-up, interfaces de múltiplos documentos, painéis com guias e aparência e funcionamento plugável do Java. Todos esses componentes podem ser adicionados a aplicativos existentes para torná-los mais fáceis de utilizar e entender. Também apresentamos gerenciadores de layout adicionais para organizar e dimensionar componentes GUI. No próximo capítulo, você aprenderá sobre o multithreading, uma capacidade poderosa que permite aos aplicativos utilizar threads para realizar múltiplas tarefas de uma vez.



Resumo

799

Resumo Seção 25.2  JSlider • Os JSliders permitem ao usuário selecionar a partir de um intervalo de valores inteiros. JSliders podem exibir marcas de medida principais, marcas de medida secundárias menores e rótulos para as marcas de medida. Eles também suportam aderência às marcas, na qual posicionar o marcador entre duas marcas de medida faz o marcador aderir à marca de medida mais próxima. • Se um JSlider tiver o foco, as teclas de seta esquerdas e direitas fazem com que marcador do JSlider- diminuir ou aumentar por 1. As teclas de seta que apontam para cima e para baixo também fazem com que o marcador do JSlider diminua ou aumente por 1, respectivamente. A tecla PgDn (page down) e a tecla PgUp (page up) fazem com que o marcador diminua ou aumente por incrementos de bloco de um décimo do intervalo de valores, respectivamente. A tecla Home move o marcador para o valor mínimo e a tecla End move-o para o valor máximo. • Os JSliders têm orientação horizontal ou vertical. Para um JSlider horizontal, o valor mínimo está na extrema esquerda e o valor máximo, na extrema direita. Para um JSlider vertical, o valor mínimo está na parte inferior extrema e o valor máximo, na parte superior extrema. A posição da caixa de rolagem indica o valor atual do JSlider. O método getValue da classe JSlider retorna a posição atual da caixa de rolagem. • O método JSlider setMajorTickSpacing configura o espaçamento para marcas de tique em um JSlider. O método setPaintTicks com um argumento true indica que as marcas de medida devem ser exibidas. • Os JSliders geram ChangeEvents quando o usuário interage com um JSlider. Um ChangeListener declara o método stateChanged que pode responder a ChangeEvents.

Seção 25.3  Windows: notas adicionais • Cada janela gera eventos de janela quando o usuário a manipula. A interface WindowListener fornece sete métodos de tratamento de evento de janela — windowActivated, windowClosed, windowClosing, windowDeactivated, windowDeiconified, windowIconified e windowOpened.

Seção 25.4  Utilizando menus com frames • Menus são uma parte integral das GUIs que permitem aos usuários realizar ações sem atravancar desnecessariamente uma GUI com componentes extras. Na GUI do Swing, os menus só podem ser anexados a objetos das classes com o método setJMenuBar (por exemplo, JFrame e JApplet). • As classes utilizadas para construir menus são JMenuBar, JMenuItem, JMenu, JCheckBoxMenuItem e JRadioButtonMenuItem. • Um JMenuBar é um contêiner para menus. Um JMenuItem aparece em um menu que, quando selecionado, faz uma ação ser executada. Um JMenu contém itens de menu e pode ser adicionado a um JMenuBar ou a outros JMenus como submenus. • Quando um menu é clicado, ele se expande para mostrar sua lista de itens de menu. O método JMenu addSeparator adiciona uma linha separadora a um menu. • Quando um JCheckBoxMenuItem é selecionado, uma marca de verificação aparece à esquerda do item de menu. Quando o JCheckBoxMenuItem é selecionado novamente, a marca é removida. • Quando múltiplos JRadioButtonMenuItems são mantidos como parte de um ButtonGroup, somente um pode ser selecionado em um dado momento. Quando um deles é selecionado, um círculo preenchido aparece à sua esquerda. Quando outro JRadioButtonMenuItem é selecionado, o círculo preenchido à esquerda do item previamente selecionado é removido. • O método AbstractButton setMnemonic especifica o mnemônico para um AbstractButton. Os caracteres mnemônicos normalmente são exibidos com um caractere de sublinhado. • Uma caixa de diálogo modal não permite acesso a qualquer outra janela no aplicativo até que o diálogo seja fechado. Os diálogos exibidos com a classe JOptionPane são diálogos modais. A classe JDialog pode ser utilizada para criar seus próprios diálogos modais ou não modais.

Seção 25.5  JPopupMenu • Menus pop-up sensíveis ao contexto são criados com a classe JPopupMenu. O evento de acionamento do pop-up normalmente ocorre quando o usuário pressiona e libera o botão direito do mouse. O método MouseEvent isPopupTrigger retorna true se o evento de acionamento do pop-up ocorreu. • O método

JPopupMenu show

exibe um

JPopupMenu.

O primeiro argumento especifica o componente de origem, que ajuda a determinar onde o

JPopupMenu aparecerá. Os dois últimos argumentos são as coordenadas no canto superior esquerdo do componente de origem, em que o JPopupMenu

aparece.

Seção 25.6  Aparência e comportamento plugável • A classe UIManager.LookAndFeelInfo mantém informações sobre uma aparência e um funcionamento. • O método UIManager static getInstalledLookAndFeels retorna um array de objetos UIManager.LookAndFeelInfo que descreve as aparências e funcionamentos disponíveis. • O método static setLookAndFeel da UIManager altera a aparência e o comportamento. O método static SwingUtilities updateComponent­ TreeUI altera a aparência e o comportamento de cada componente anexado ao seu argumento Component à nova aparência e comportamento.

800

Capítulo 25  Componentes GUI: Parte 2

Seção 25.7  JDesktopPane e JInternalFrame • Muitos aplicativos atuais utilizam uma interface de múltiplos documentos (Multiple Document Interface — MDI) para gerenciar vários documentos abertos que são processados em paralelo. As classes JDesktopPane e JInternalFrame do Swing fornecem suporte para criar interfaces de múltiplos documentos.

Seção 25.8  JTabbedPane • Um JTabbedPane organiza componentes GUI em camadas, das quais somente uma é visível de cada vez. Usuários acessam cada camada clicando na guia.

Seção 25.9  Gerenciadores de layout: BoxLayout e GridBagLayout • BoxLayout organiza os componentes GUI da esquerda para a direita ou de cima para baixo em um contêiner. • A classe Box representa um contêiner com BoxLayout como seu gerenciador padrão de layout e fornece métodos static para criar um Box com um BoxLayout horizontal ou vertical. • GridBagLayout é semelhante a GridLayout, mas o tamanho de cada componente pode variar e os componentes podem ser adicionados em qualquer ordem. • Um objeto GridBagConstraints especifica como um componente é inserido em um GridBagLayout. O método GridBagLayout setConstraints recebe um argumento Component e um argumento GridBagConstraints e configura as restrições do Component.

Terminologia add, método da classe JMenu, 777 add, método da classe JMenuBar, 778

getClassName, método da classe UIManager LookAndFeelInfo, 784

addTab, método da classe JTabbedPane, 789

getInstalledLookAndFeels, método da classe UIManager, 784

addWindowListener, método da classe Window, 773

GridBagConstraints, classe, 792

addSeparator, método da classe JMenu, 778

aderência às marcas para um JSlider, 769 anchor, campo da classe GridBagConstraints, 793 aparência e funcionamento plugável (Pluggable Look-And-Feel — PLAF), 769 área rígida de classe Box, 791 barra de menus, 773 barra de título de uma janela, 772 bloquear o incremento de um JSlider, 769 BOTH, constante da classe GridBagConstraints, 793 caixa de diálogo modal, 778 CENTER, constante da classe GridBagConstraints, 793 ChangeEvent, classe, 772 ChangeListener, interface, 772 cola horizontal, 791 componente de origem, 781 createGlue, método da classe Box, 792 createHorizontalGlue, método da classe Box, 791 createHorizontalStrut, método da classe Box, 791 createRigidArea, método da classe Box, 791 createVerticalBox, método da classe Box, 791 createVerticalGlue, método da classe Box, 791 createVerticalStrut, método da classe Box, 791 dispose, método da classe Window, 772 EAST, constante da classe GridBagConstraints, 793 estrutura vertical, 791 evento de gatilho pop-up, 779 evento de janela, 773

getValue, método da classe JSlider, 772 gridheight, campo da classe GridBagConstraints, 793 gridwidth, campo da classe GridBagConstraints, 793 gridx, campo da classe GridBagConstraints,

793

gridy, campo da classe GridBagConstraints,

793

HORIZONTAL, constante da classe GridBagConstraints, 793

indicador de classe JSlider, 769 interface de múltiplos documentos (Multiple Document Interface — MDI), 784 isPopupTrigger, método da classe MouseEvent, 781 isSelected, método da classe AbstractButton, 777 item de menu, 773 janela, 772 janela, eventos, 773 janela-filha em uma interface de múltiplos documentos, 784 janela-pai em uma interface de múltiplos documentos, 784 janela-pai para uma caixa de diálogo, 777 JDesktopPane, classe, 784 JDialog, classe, 778 JMenu, classe, 773 JMenuItem, classe, 773 JPopupMenu, classe, 779 JSlider, classe, 769 JTabbedPane, classe, 787 LookAndFeelInfo, classe aninhada da classe UIManager, 784 marcas de medida em um JSlider, 769 margem de uma janela, 772

MDI (Multiple Document Interface), 784 menu pop-up sensível ao contexto, 779 metal, aparência, 781 miniatura de um JSlider, 769 mnemônico, 773 Motif-style (UNIX), aparência e funcionamento, 769 NONE, constante da classe GridBagConstraints, 793 NORTH, constante da classe GridBagConstraints, 793 NORTHEAST, constante da classe GridBagConstraints, 793 NORTHWEST, constante da classe GridBagConstraints, 793 pack, método da classe Window, 787 RELATIVE, constante da classe GridBagConstraints, 796 REMAINDER, constante da classe GridBagConstraints, 796 SCROLL_TAB_LAYOUT, constante da classe JTabbedPane, 792 setConstraints, método da classe GridBagLayout, 796 setDefaultCloseOperation, método da classe JFrame, 772 setForeground, método da classe JComponent, 778 setInverted, método da classe JSlider, 770 setJMenuBar, método da classe JFrame, 778 setLocation, método da classe Component, 772 setLookAndFeel, método da classe UIManager, 784 setMajorTickSpacing, método da classe JSlider, 772 setMnemonic, método da classe AbstractButton, 777 setPaintTicks, método da classe JSlider, 772 setSelected, método da classe AbstractButton, 778



Exercícios de autorrevisão

show, método da classe JPopupMenu, 781 SOUTH, constante da classe GridBagConstraints, 793 SOUTHEAST, constante da classe GridBagConstraints, 793 SOUTHWEST, constante da classe GridBagConstraints, 793 stateChanged, método da interface ChangeListener, 772

submenu, 773 SwingUtilities, classe, 784 TOP, constante da classe JTabbedPane, 792 UIManager, classe, 784

updateComponentTreeUI, método da classe SwingUtilities, 784 VERTICAL, constante da classe GridBagConstraints, 793 weightx, campo da classe GridBagConstraints, 793 weighty, campo da classe GridBagConstraints, 793 WEST, constante da classe GridBagConstraints, 793

801

windowClosing, método da interface WindowListener, 773 WindowConstants, interface, 772 windowDeactivated, método da interface WindowListener, 773 windowDeiconified, método da interface WindowListener, 773 windowIconified, método da interface WindowListener, 773 WindowListener, interface, 773

windowActivated, método da interface WindowListener, 773

windowOpened, método da interface WindowListener, 773

windowClosed, método da interface WindowListener, 773

Y_AXIS, constante da classe Box, 792

X_AXIS, constante da classe Box, 792

Exercícios de autorrevisão 25.1 Preencha as lacunas em cada uma das seguintes afirmações: a) A classe ________ é utilizada para criar um objeto de menu. b) O método ________ da JMenu classe coloca uma barra separadora em um menu. c) Os eventos JSlider são tratados pelo método ________ da interface ________. d) A variável de instância GridBagConstraints ________ é configurada como CENTER por padrão.

25.2 Determine se cada uma das sentenças é verdadeira ou falsa. Se falsa, explique por quê. a) Quando o programador cria um JFrame, no mínimo um menu deve ser criado e adicionado ao JFrame. b) A variável fill pertence à classe GridBagLayout. c) O desenho em um componente GUI é realizado com relação à coordenada (0, 0) do canto superior esquerdo do componente. d) O layout padrão para um Box é BoxLayout.

25.3 Encontre o(s) erro(s) em cada um dos seguintes itens e explique como corrigir o(s) erro(s). a) JMenubar

b;

b) mySlider

= JSlider( 1000, 222, 100, 450 );

c) gbc.fill d) //

= GridBagConstraints.NORTHWEST;

// configura preenchimento

sobrescreve para pintar sobre um componente Swing personalizado

public void paintcomponent( Graphics g ) { g.drawString( "HELLO", 50, 50 ); } // fim do método paintComponent

e) //

cria e exibe um JFrame

JFrame f = new JFrame( "A Window" ); f.setVisible( true );

Respostas dos exercícios de autorrevisão 25.1 a) JMenu. b) addSeparator. c) stateChanged, ChangeListener. d) anchor. 25.2 a) Falsa. Um JFrame não requer nenhum menu. b) Falsa. A variável fill pertence à classe GridBagConstraints. c) Verdadeira. d) Verdadeira.

25.3 a) JMenubar deve ser JMenuBar. b) O primeiro argumento para o construtor deve ser um SwingConstants.HORIZONTAL ou SwingConstants.VERTICAL e a palavra-chave new deve ser utilizada depois do operador =. Além disso, o valor mínimo deve ser menor que o máximo e o valor inicial deve estar no intervalo. c) A constante deve ser BOTH, HORIZONTAL, VERTICAL ou NONE. d) paintcomponent deve ser paintComponent e o método deve chamar o super.paintComponent( g ) como sua primeira instrução. e) O método setSize do JFrame também deve ser chamado para estabelecer o tamanho da janela.

802

Capítulo 25  Componentes GUI: Parte 2

Exercícios 25.4 Preencha as lacunas em cada uma das seguintes afirmações: a) Um JMenuItem que é um JMenu é chamado ________. b) O método ________ anexa um JMenuBar a um JFrame. c) A classe contêiner ________ tem um BoxLayout padrão. d) Um(a) ________ gerencia um conjunto de janelas-filhas declarado com a classe JInternalFrame.

25.5 Determine se cada uma das seguintes afirmações é verdadeira ou falsa. Se falsa, explique por quê. a) Os menus requerem um objeto JMenuBar então podem ser anexados a um JFrame. b) BoxLayout é o gerenciador padrão de layout para um JFrame. c) JApplets podem conter menus.

25.6 Localize o(s) erro(s) em cada um dos seguintes itens. Explique como corrigir o erro (s). a) x.add( new JMenuItem( "Submenu Color" ) ); // b) container.setLayout( new GridbagLayout() );

cria submenu

25.7 Escreva um programa que exiba um círculo de tamanho aleatório e calcule e exiba a área, raio, diâmetro e circunferência. Utilize as seguintes

equações: diâmetro = 2 × raio, área = π × raio2, circunferência = 2 × π × raio. Utilize a constante Math.PI para pi (π). Todos os desenhos devem ser feitos em uma subclasse de JPanel e os resultados dos cálculos devem ser exibidos em um JtextArea de leitura.

25.8 Aprimore o programa no Exercício 25.7 permitindo ao usuário alterar o raio com um JSlider. O programa deve funcionar em todos os raios no intervalo de 100 a 200. À medida que o raio muda, o diâmetro, a área e a circunferência devem ser atualizados e exibidos. O raio inicial deve ser 150. Utilize as equações do Exercício 25.7. Todos os desenhos devem ser feitos em uma subclasse de JPanel e os resultados dos cálculos devem ser exibidos em um JtextArea de leitura.

25.9 Explore os efeitos da variação dos valores weightx e weighty do programa na Figura 25.21. O que acontece quando um slot tem um peso não zero, mas não tem permissão de preencher a área inteira (isto é, o valor fill não é BOTH)?

25.10 Escreva um programa que utilize o método paintComponent para desenhar o valor atual de um JSlider em uma subclasse de JPanel. Além

disso, forneça um JTextField em que um valor específico possa ser inserido. O JTextField deve exibir o valor atual do JSlider todas as vezes. Alterar o valor no JTextField também deve atualizar o JSlider. Um JLabel deve ser utilizado para identificar o JTextField. Os métodos JSlider setValue e getValue devem ser utilizados. [Nota: O método setValue é um método public que não retorna um valor e aceita um argumento do tipo inteiro, o valor JSlider, que determina a posição da caixa de rolagem.]

25.11 Declare uma subclasse de JPanel chamado MyColorChooser que forneça três objetos JSlider e três objetos JTextField. Cada JSlider

representa os valores de 0 a 255 para as partes de azul, verde e vermelha de uma cor. Utilize esses valores como os argumentos para o construtor a fim de criar um novo objeto Color. Exiba o valor atual de cada JSlider no correspondente JTextField. Quando o usuário altera o valor do JSlider, o JTextField deve ser alterado correspondentemente. Utilize seu novo componente GUI como parte de um aplicativo que exiba o valor Color atual desenhando um retângulo preenchido.

Color

25.12 Modifique a classe MyColorChooser do Exercício 25.11 para permitir ao usuário digitar um valor inteiro em um JTextField para configurar

o valor de vermelho, verde ou azul. Quando o usuário pressionar Enter no JTextField, o JSlider correspondente deve ser configurado com o valor apropriado.

25.13 Modifique o aplicativo no Exercício 25.12 para desenhar a cor atual como um retângulo em uma instância de uma subclasse do JPanel, que fornece seu próprio método paintComponent para desenhar o retângulo e fornece os métodos set para configurar os valores de vermelho, verde e azul para a cor atual. Quando um método set é invocado, o painel de desenho deve automaticamente repintar (repaint) a si próprio.

25.14 Modifique o aplicativo no Exercício 25.13 para permitir que usuário arraste o mouse pelo painel de desenho (uma subclasse do JPanel) para desenhar uma forma na cor atual. Permita ao usuário escolher que forma desenhar.

25.15 Modifique o aplicativo no Exercício 25.14 para fornecer ao usuário a capacidade de terminar o aplicativo clicando na caixa de fechamento na janela que é exibida e selecionando Exit em um menu

File. Utilize as técnicas mostradas na Figura 25.5.

25.16 (Aplicativo de desenho completo) Utilizando as técnicas desenvolvidas neste capítulo e no Capítulo 14, crie um aplicativo de desenho

completo. O programa deve utilizar os componentes GUI dos capítulos 14 e 25 para permitir que o usuário selecione as características de forma, cor e preenchimento. Cada forma deve ser armazenada em um array de objetos MyShape, onde MyShape é a superclasse na sua hierarquia das classes de forma. Utilize um JDesktopPane e JInternalFrames para permitir ao usuário criar múltiplos desenhos separados em janelas-filhas separadas. Crie a interface com o usuário como uma janela-filha separada contendo todo o componente GUI que permite ao usuário determinar as características da forma que será desenhada. O usuário então pode clicar em qualquer JInternalFrame para desenhar a forma.

A mais geral definição da beleza… Multiplicidade na Unidade. — Samuel Taylor Coleridge

Não bloqueie o modo de pesquisa. — Charles Sanders Peirce

Uma pessoa com um relógio sabe que horas são; uma pessoa com dois relógios nunca está segura. — Provérbio

Aprenda a trabalhar e a esperar. — Henry Wadsworth Longfellow

O mundo está mudando tão rápido hoje que um homem que diz que algo não pode ser feito, em geral, é interrompido por alguém fazendo esse algo. — Elbert Hubbard

26

Multithreading

Objetivos Neste capítulo, você aprenderá: 

O que são as threads e por que elas são úteis.



Como as threads permitem gerenciar atividades concorrentes.



O ciclo de vida de uma thread.



As prioridades e agendamento de threads.



A criar e executar Runnables.



A sincronização de threads.



O que são relacionamentos produtor/consumidor e como são implementados com multithreading.



A permitir que múltiplas threads atualizem componentes GUI Swing de maneira segura para threads.



Sobre as interfaces Callable e Future, que podem ser utilizadas com threading para executar tarefas que retornam resultados.

804

Capítulo 26

26.1 26.2 26.3 26.4

Multithreading

Introdução Estados de thread: ciclo de vida de uma thread Prioridades de thread e agendamento de thread Criando e executando threads 26.4.1 Runnables e a classe Thread 26.4.2 Gerenciamento de threads com o framework

Sumário

Executor

26.5 Sincronização de thread 26.5.1 Compartilhamento de dados não sincronizados 26.5.2 Compartilhamento de dados sincronizados — tornando operações atômicas

26.6 Relacionamento entre produtor e consumidor sem sincronização

26.7 Relacionamento de produtor/consumidor: ArrayBlockingQueue

26.8 Relacionamento entre produtor e consumidor com sincronização 26.9 Relacionamento de produtor/consumidor: buffers limitados 26.10 Relacionamento de produtor/consumidor: as interfaces Lock e Condition 26.11 Multithreading com GUI 26.11.1

Realizando cálculos em uma thread worker

26.11.2 Processando resultados intermediários com SwingWorker

26.12 Interfaces Callable e Future 26.13 Conclusão

Resumo | Terminologia | Exercícios de autorrevisão | Respostas aos exercícios de autorrevisão | Exercícios | Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios

26.1 Introdução Seria interessante se pudéssemos fazer uma coisa por vez, e fazê-la bem, mas em geral isso é difícil. O corpo humano realiza uma grande variedade de operações paralelamente — ou, como diremos por todo este capítulo, concorrentemente. A respiração, a circulação sanguínea, a digestão, o pensamento e a locomoção, por exemplo, podem ocorrer simultaneamente. Todos os sentidos — visão, tato, olftato, paladar e audição — podem ser empregados ao mesmo tempo. Os computadores, também, realizam operações concorrentemente. É comum aos computadores pessoais compilar um programa, enviar um arquivo para uma impressora e receber mensagens de correio eletrônico em uma rede concorrentemente. Apenas os computadores que têm múltiplos processadores podem, de fato, executar múltiplas instruções concorrentemente. Os sistemas operacionais em computadores de um único processador criam a ilusão da execução concorrente alternando rapidamente entre atividades, mas nesses computadores apenas uma instrução pode executar de cada vez. A maioria das linguagens de programação não permite especificar atividades concorrentes. Especialmente, as linguagens fornecem instruções de controle sequenciais que permitem especificar que apenas uma ação deve ser realizada por vez, com a execução avançando para a ação seguinte depois que a anterior tiver sido concluída. Historicamente, a concorrência foi implementada com os primitivos de sistemas operacionais disponíveis apenas para programadores de sistemas experientes. A linguagem de programação Ada, desenvolvida pelo Departamento de Defesa dos Estados Unidos, tornou primitivos de concorrência amplamente disponíveis para as empresas contratadas do Departamento de Defesa que estavam construindo sistemas de comando e controle militar. Entretanto, a tecnologia Ada não foi amplamente utilizada nas universidades e na indústria.

Concorrência no Java O Java disponibiliza a concorrência por meio da linguagem e das APIs. Você especifica que um aplicativo contém threads, ou linhas de execução separadas, nas quais cada uma tem sua própria pilha de chamadas de método e seu próprio contador de programa, permitindo a execução simultânea com outras threads ao compartilhar recursos no nível do aplicativo como a memória. Essa capacidade, chamada multithreading, não está disponível nas linguagens C e C++ básicas, mas já existem bibliotecas que fornecem essa funcionalidade. Dica de desempenho 26.1 Um problema com aplicativos de uma única thread que pode levar a uma fraca responsividade é que as atividades longas e demoradas devem ser concluídas antes de as outras poderem iniciar. Em um aplicativo com múltiplas threads, as threads podem ser distribuídas por múltiplos processadores (se disponíveis) de modo que múltiplas tarefas sejam mesmo executadas concorrentemente e o aplicativo possa operar de modo mais eficiente. O multithreading também pode aumentar o desempenho em sistemas de um único processador que simulam a concorrência — quando uma thread não puder avançar ( porque, por exemplo, está esperando o resultado de uma operação E/S), outra pode utilizar o processador.

Ao contrário das linguagens que não têm capacidades de multithreading integradas e, portanto, devem fazer chamadas não portáveis para primitivos de multithreading do sistema operacional, o Java inclui primitivos de multithreading como parte da própria linguagem e de suas bibliotecas. Isso facilita a manipulação de threads de maneira portável entre plataformas.

26.2 Estados de thread: ciclo de vida de uma thread

805

Usos da programação concorrente Discutiremos muitas aplicações da programação de processos concorrentes. Por exemplo, ao fazer download de um arquivo grande (como uma imagem, um clipe de áudio ou um videoclipe) na internet, o usuário pode não querer esperar até o download do clipe inteiro para iniciar a reprodução. Para resolver esse problema, podemos colocar múltiplas threads para trabalhar — uma delas faz o download do clipe e a outra o reproduz. Essas atividades prosseguem concorrentemente. Para evitar a reprodução instável, iremos sincronizar as threads de modo que a thread do player não inicie até que haja uma quantidade suficiente do clipe na memória para manter a thread do player ocupado. A Java Virtual Machine ( JVM) também utiliza threads. Ela cria threads para executar programas e threads para realizar tarefas de limpeza, como a coleta de lixo. A programação concorrente é difícil Escrever programas de múltiplas threads pode ser difícil. Embora a mente humana possa realizar funções simultaneamente, as pessoas acham difícil alternar entre linhas de pensamento paralelas. Para ver por que programas de múltiplas threads podem ser difíceis de escrever e de entender, tente a seguinte experiência: abra três livros na página 1 e tente ler os livros concorrentemente. Leia algumas palavras do primeiro livro e depois do segundo, do terceiro e, então, faça um loop e leia as próximas poucas palavras a seguir do primeiro livro e assim por diante. Depois dessa experiência, você apreciará os desafios da tecnologia multithreading — alternar entre os livros, ler brevemente, lembrar-se de onde parou em cada livro, aproximar ainda mais o livro que está lendo para poder vê-lo melhor e afastar os que não está lendo — e, no meio de todo esse caos, tentar compreender o conteúdo dos livros! Utilize APIs de concorrência predefinida sempre que possível É difícil e propenso a erros programar aplicativos concorrentes. Se precisar utilizar a sincronização em um programa, você deve seguir algumas diretrizes simples. Utilize classes existentes da Java API (como a classe ArrayBlockingQueue que discutimos na Seção 26.7) que gerenciam a sincronização para você. As classes da Java API são escritas por experts, foram inteiramente testadas e depuradas, operam com eficiência e ajudam a evitar interrupções e armadilhas comuns. Se precisar de capacidades ainda mais complexas, utilize as interfaces Lock e Condition que são introduzidas na Seção 26.10. As interfaces Lock e Condition só devem ser utilizadas por programadores avançados que conhecem as armadilhas comuns da programação concorrente. Explicamos esses tópicos neste capítulo por várias razões — eles fornecem uma base sólida para entender como os aplicativos concorrentes sincronizam o acesso à memória compartilhada; é importante entender os conceitos, mesmo que um aplicativo não utilize essas ferramentas explicitamente; e mostrando a complexidade envolvida no uso desses recursos de baixo nível, esperamos enfatizar a importância do uso das capacidades de concorrência predefinidas sempre que possível.

26.2 Estados de thread: ciclo de vida de uma thread A qualquer dado momento, diz-se que uma thread está em um dos vários estados de thread — ilustrados no diagrama de estado UML na Figura 26.1. Vários dos termos no diagrama são definidos nas seções posteriores. novo o programa inicia a thread

notify notifyAll

o intervalo expira

wait sleep

interrupt

terminado

de

adquire o bloqueio

espera cronometrada

a tarefa completa

espera

in

sy sere nc in hr str so on uç lici iz ão taç ed ão

E/S completa

executável fy l ti Al no ify t no it wa

E/S

bloqueado

Figura 26.1 | Diagrama de estado de ciclo e vida da thread.

Estados novo e executável Uma nova thread inicia seu ciclo de vida no estado novo. Ela permanece nesse estado até que o programa inicia a thread, o que a coloca no estado executável. Considera-se que uma thread no estado executável está executando sua tarefa.

806

Capítulo 26  Multithreading

Estado de espera Às vezes a thread executável transita para o estado de espera enquanto espera outra thread realizar uma tarefa. Uma thread de espera transita de volta para o estado executável apenas quando outra thread a notifica para continuar executando. Estado de espera sincronizada Uma thread executável pode entrar no estado de espera sincronizada por um intervalo especificado de tempo. Ela transita para o estado executável quando esse intervalo de tempo expira ou quando o evento pelo qual ela está esperando ocorre. As threads de espera sincronizada não podem utilizar um processador, mesmo se houver um disponível. Uma thread executável pode transitar para o estado de espera sincronizada se fornecer um intervalo de espera opcional quando ela estiver esperando outra thread realizar uma tarefa. Essa thread retorna ao estado executável quando é notificada por outra thread ou quando o intervalo sincronizado expira — o que ocorrer primeiro. Outra maneira de colocar uma thread no estado de espera sincronizada é colocar a thread executável para dormir. Uma thread adormecida permanece no estado de espera sincronizada por um período de tempo designado (chamado intervalo de adormecimento), depois do qual ela retorna ao estado executável. As threads dormem quando, por um breve período, não têm de realizar nenhuma tarefa. Por exemplo, um processador de texto pode conter uma thread que grave periodicamente backups (isto é, grava uma cópia) do documento atual no disco para fins de recuperação. Se a thread não dormisse entre os sucessivos backups, seria necessário um loop em que testaria continuamente se deve ou não gravar uma cópia do documento em disco. Esse loop consumiria tempo de processador sem realizar trabalho produtivo, reduzindo assim o desempenho de sistema. Nesse caso, é mais eficiente para a thread especificar um intervalo de adormecimento (igual ao período entre backups sucessivos) e entrar no estado de espera sincronizada. Essa thread é retornada ao estado executável quando seu intervalo de adormecimento expira, ponto em que ela grava uma cópia do documento no disco e entra novamente no estado de espera sincronizada. Estado bloqueado Uma thread executável transita para o estado bloqueado quando tenta realizar uma tarefa que não pode ser completada imediatamente e deve esperar temporariamente até que essa tarefa seja concluída. Por exemplo, quando uma thread emite uma solicitação de entrada/saída, o sistema operacional bloqueia a thread de executar até que essa solicitação de E/S se complete — nesse ponto, a thread bloqueada transita para o estado executável e, desse modo, pode retomar a execução. Uma thread bloqueada não pode utilizar um processador, mesmo se algum estiver disponível. Estado terminado Uma thread executável entra no estado terminado (às vezes chamado estado morto) quando completa sua tarefa com sucesso ou, de outro modo, a termina (talvez por causa de um erro). No diagrama de estado UML da Figura 26.1, o estado terminado é seguido pelo estado final da UML (símbolo do alvo) para indicar o fim das transições de estado. Visão do sistema operacional do estado executável No nível do sistema operacional, o estado executável do Java geralmente inclui dois estados separados (Figura 26.2). O sistema operacional oculta esses estados da Java Virtual Machine ( JVM), que vê apenas o estado executável. Quando uma thread entra pela primeira vez no estado executável a partir do estado novo, ela está no estado pronto. Uma thread pronta entra no estado de execução (isto é, começa a executar) quando o sistema operacional a atribui a um processador — também conhecido como despachar a thread. Na maioria dos sistemas operacionais, cada thread recebe uma pequena quantidade de tempo de processador — chamada de quantum ou fração de tempo — com a qual realiza sua tarefa. Decidir o tamanho que o quantum deve ter é um tópico-chave em cursos de sistemas operacionais. Quando seu quantum expira, a thread retorna ao estado pronto, e o sistema operacional atribui executável

sistema operacional despacha uma thread

pronto

executando o quantum expira

Figura 26.2  |  Visualização interna do sistema operacional do estado executável do Java.

outra thread ao processador (ver a Seção 26.3). As transições entre os estado pronto e executável são tratadas isoladamente pelo sistema operacional. A JVM não “vê” as transições — ela simplesmente visualiza a thread como executável e a deixa para o sistema operacional fazer a transição da thread entre os estados pronto e executável. O processo que utiliza um sistema operacional para determinar qual thread despachar é conhecido como agendamento de thread e depende das prioridades de thread (discutidas na próxima seção).

26.3 Prioridades de thread e agendamento de thread

807

26.3 Prioridades de thread e agendamento de thread Toda thread do Java tem uma prioridade de thread que ajuda a determinar a ordem em que são agendadas. As prioridades do Java variam entre MIN_PRIORITY (uma constante de 1) e MAX_PRIORITY (uma constante de 10). Por padrão, toda thread recebe a prioridade NORM_PRIORITY (uma constante de 5). Cada nova thread herda a prioridade da thread que o cria. Informalmente, as threads de prioridade mais alta são mais importantes para um programa e devem ser alocadas em tempo de processador antes das threads de prioridade mais baixa. Entretanto, as prioridades de thread não podem garantir a ordem em que elas são executadas. [Nota: As constantes MAX_PRIORITY, MIN_PRIORITY e NORM_PRIORITY são declaradas na classe Thread. Recomenda-se não criar e utilizar explicitamente objetos Thread para implementar a concorrência, mas, em vez disso, utilizar a interface Executor (que é descrita na Seção 26.4.2). A classe Thread contém alguns métodos static úteis, que discutimos mais adiante no capítulo.] A maioria dos sistemas operacionais suporta o fracionamento de tempo, o que permite que threads de igual prioridade compartilhem um processador. Sem o fracionamento de tempo, cada thread em um conjunto de threads de igual prioridade executa até sua conclusão (a menos que ela deixe o estado executável e entre no estado de espera ou espera sincronizada ou, ainda, seja interrompida por uma thread de prioridade mais alta) antes de outras threads de igual prioridade terem uma chance de executar. Com o fracionamento de tempo, mesmo se a thread não tiver concluído a execução quando seu quantum expirar, o processador é tirado da thread e recebe a próxima thread de igual prioridade, se houver alguma disponível. O agendador de threads (thread scheduler) de um sistema operacional determina que thread executa em seguida. Uma simples implementação do scheduler de thread mantém a thread de prioridade mais alta executando o tempo todo e, se houver mais de uma thread de prioridade mais alta, isso assegura que cada uma dessas threads executa por um quantum no estilo rodízio. A Figura 26.3 ilustra uma Threads prontas

Thread.MAX_Prioridade Thread.MAX_PRIORITY

Prioridade 10

A

Prioridade 9

C

B

Prioridade 8

Thread.NORM_PRIORITY

Prioridade 7

D

Prioridade 6

G

Prioridade 5

H

I

J

K

E

Prioridade 4

Prioridade 3

Prioridade 2

Thread.MIN_PRIORITY

Prioridade 1

Figura 26.3 | Agendamento de prioridade de threads.

F

808

Capítulo 26

Multithreading

fila de prioridades de múltiplos níveis para as threads. Na figura, supondo um computador de um único processador, as threads A e B executam por um quantum no esquema de rodízio até que ambas as threads completem a execução. Isso significa que A obtém um quantum de tempo para executar. Então B obtém um quantum. Então A obtém outro quantum. Então B obtém outro quantum. Isso continua até que uma thread complete. O processador então dedica toda sua energia à thread que resta (a menos que outra thread de prioridade 10 torne-se pronta). Em seguida, a thread C executa até sua conclusão (assumindo que nenhuma thread de prioridade mais alta ou igual chegará). Cada uma das threads D, E e F executa um quantum no esquema rodízio até que todas completem a execução (novamente supondo que nenhuma thread de prioridade mais alta ou igual chegará). Esse processo continua até que todos os threads executem até sua conclusão. Quando uma thread de prioridade mais alta entra no estado pronto, o sistema operacional geralmente faz preempção da thread atualmente em execução (uma operação conhecida como agendamento preemptivo). Dependendo do sistema operacional, as threads de prioridade mais alta poderiam adiar — possivelmente por um tempo indefinido — a execução de threads de prioridade mais baixa. Esse adiamento indefinido é às vezes referido, mais alegoricamente, como inanição. O Java fornece utilitários de concorrência de nível superior para ocultar um pouco dessa complexidade e tornar os programas de múltiplas threads menos propensos a erros. As prioridades de thread são utilizadas nos bastidores para interagir com o sistema operacional, mas a maioria dos programadores que utiliza o multithreading do Java não se preocupará com a configuração e o ajuste de prioridades de thread.

Dica de portabilidade 26.1 O agendamento de threads é dependente de plataforma — o comportamento de um programa de múltiplas threads pode variar nas diferentes implementações do Java.

Dica de portabilidade 26.2 Ao projetar programas de múltiplas threads, considere as capacidades de threading de todas as plataformas nas quais os programas executarão. Utilizar prioridades diferentes das prioridades padrão tornará a comportamento dos seus programas dependentes da plataforma. Se seu objetivo for a portabilidade, não ajuste as prioridades de thread.

26.4 Criando e executando threads A forma preferida de criar aplicativos Java de múltiplas threads é implementando a interface Runnable (do pacote java.lang). Um objeto Runnable representa uma “tarefa” que pode executar concorrentemente com outras tarefas. A interface Runnable declara o método run único, que contém o código que define a tarefa que um objeto Runnable deve realizar. Quando uma thread executando um Runnable é criada e iniciada, ela chama o método run do objeto Runnable, que executa na nova thread.

26.4.1 Runnables e a classe Thread A classe PrintTask (Figura 26.4) implementa Runnable (linha 5), de modo que múltiplos PrintTasks possam executar concorrentemente. A variável sleepTime (linha 7) armazena um valor do tipo inteiro aleatório de 0 a 5 segundos criado no construtor PrintTask (linha 16). Cada thread executando uma PrintTask adormece pelo período de tempo especificado por sleepTime e, então, gera saída do nome de sua tarefa e de uma mensagem indicando que isso foi feito no estado adormecido. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

// Figura 26.4: PrintTask.java // Classe PrintTask dorme por um tempo aleatório de 0 a 5 segundos import java.util.Random; public class PrintTask implements Runnable { private final int sleepTime; // tempo de adormecimento aleatório para a thread private final String taskName; // nome de tarefa private final static Random generator = new Random(); public PrintTask( String name ) { taskName = name; // configura o nome da tarefa // seleciona tempo de adormecimento aleatório entre 0 e 5 segundos sleepTime = generator.nextInt( 5000 ); // milissegundos } // fim do construtor PrintTask // método run contém o código que uma thread executará public void run() {



26.4  Criando e executando threads

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

809

try // coloca a thread para dormir pelo período de tempo sleepTime { System.out.printf( "%s going to sleep for %d milliseconds.\n", taskName, sleepTime ); Thread.sleep( sleepTime ); // coloca a thread para dormir } // fim do try catch ( InterruptedException exception ) { System.out.printf( "%s %s\n", taskName, "terminated prematurely due to interruption" ); } // fim do catch // imprime o nome da tarefa System.out.printf( "%s done sleeping\n", taskName ); } // fim do método run } // fim da classe PrintTask

Figura 26.4  |  A classe PrintTask dorme por um tempo aleatório de 0 a 5 segundos.

Uma PrintTask executa quando uma thread chama o método run da PrintTask. As linhas 24–25 exibem uma mensagem que indica o nome da tarefa em execução atualmente e que a tarefa vai dormir por sleepTime milissegundos. A linha 26 invoca o método static sleep da classe Thread para colocar a thread no estado espera sincronizada pelo período de tempo especificado. Nesse ponto, a thread perde o processador e o sistema permite que outra thread execute. Quando a thread acordar, ela entra novamente no estado executável. Quando a PrintTask for novamente atribuída a um processador, a linha 35 gera saída de uma mensagem que indica que a tarefa não está mais dormindo, depois o método run termina. Observe que catch nas linhas 28–32 é necessário porque o método sleep poderia lançar uma exceção verificada do tipo InterruptedException se o método interrupt da thread adormecida fosse chamado. A Figura 26.5 cria objetos Thread para executar PrintTasks. As linhas 12–14 criam três threads, cada uma especificando uma nova PrintTask a executar. As linhas 19–21 chamam o método Thread start em cada uma das threads, colocando cada uma delas no estado executável e configurando uma chamada para o correspondente método run de Runnable para executar a tarefa. A linha 23 gera saída de uma mensagem indicando que todas as tarefas das threads foram iniciadas e que o método main terminou de executar. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

// Figura 26.5: ThreadCreator.java // Criando e iniciando três threads para executar Runnables. import java.lang.Thread; public class ThreadCreator { public static void main( String[] args ) { System.out.println( "Creating threads" ); // cria cada thread com um novo runnable selecionado Thread thread1 = new Thread( new PrintTask( “task1” ) ); Thread thread2 = new Thread( new PrintTask( “task2” ) ); Thread thread3 = new Thread( new PrintTask( “task3” ) ); System.out.println( "Threads created, starting tasks." ); // inicia threads e thread1.start(); // thread2.start(); // thread3.start(); //

coloca invoca invoca invoca

no estado executável o método run de task1 o método run de task2 o método run de task3

System.out.println( "Tasks started, main ends.\n" ); } // fim de main } // fim da classe ThreadCreator

Creating threads Threads created, starting tasks Tasks started, main ends task3 going to sleep for 491 milliseconds

810

task2 task1 task2 task3 task1

Capítulo 26  Multithreading

going to sleep for 71 milliseconds going to sleep for 3549 milliseconds done sleeping done sleeping done sleeping

Creating threads Threads created, starting tasks task1 going to sleep for 4666 milliseconds task2 going to sleep for 48 milliseconds task3 going to sleep for 3924 milliseconds Tasks started, main ends task2 done sleeping task3 done sleeping task1 done sleeping

Figura 26.5  |  Criando e iniciando três threads para executar Runnables.

O código em main executa na thread principal, uma thread criada pela JVM. O código no método run de PrintTask (linhas 20–36 da Figura 26.4) executa nas threads criadas nas linhas 12–14 da Figura 26.5. Quando o método main terminar, o próprio programa continuará executando porque ainda haverá threads que estão ativas (isto é, quaisquer das três threads que criamos que ainda não alcançaram o estado terminado). O programa não terminará até que sua última thread complete a execução. As saídas de exemplo desse programa mostram o nome e tempo de adormecimento de cada tarefa quando a thread vai dormir. A thread com o tempo de adormecimento mais curto normalmente acorda primeiro, o que indica que ela não está mais dormindo e terminará. Na Seção 26.9, discutimos as questões de multithreading que poderiam impedir que a thread com o tempo mais curto de adormecimento acordasse primeiro. Na primeira saída, a thread principal termina antes de qualquer outra thread gerar saída de seus nomes e tempos de adormecimento. Isso mostra que a thread principal executa até a conclusão antes que qualquer uma das outras threads tenha uma chance de executar. Na segunda saída, todas as threads geram saída de seus nomes e períodos de adormecimento antes de a thread principal terminar. Isso mostra que outras threads executaram antes de a thread principal terminar. Esse é um exemplo do agendamento de rodízio discutido na Seção 26.3. Observe também que na primeira saída de exemplo, task3 vai dormir primeiro e task1 por último, embora iniciássemos a thread da task1 primeiro. Isso ilustra o fato de que não podemos prever a ordem em que as threads serão agendadas, mesmo se soubermos a ordem em que foram criadas e iniciadas.

26.4.2  Gerenciamento de threads com o framework Executor Embora seja possível criar threads explicitamente, como na Figura 26.5, recomenda-se utilizar a interface Executor para gerenciar a execução de objetos Runnable para você. Em geral, um objeto Executor cria e gerencia um grupo de threads chamado pool de threads para executar Runnables. O uso de um Executor é muito mais vantajoso do que você mesmo criar threads. Os Executors podem reutilizar threads existentes para eliminar o overhead que cria uma nova thread para cada tarefa e podem aprimorar o desempenho otimizando o número de threads a fim de assegurar que o processador permaneça ocupado, sem criar tantas threads que esgotam os recursos do aplicativo. A interface Executor declara um método único chamado execute que aceita um Runnable como um argumento. O Executor atribui cada Runnable passado para seu método execute a uma das threads disponíveis no pool de threads. Se não houver nenhuma thread disponível, o Executor cria uma nova thread ou espera uma thread tornar-se disponível e atribui a essa thread o Runnable que foi passado para o método execute. A interface ExecutorService (do pacote java.util.concurrent) estende Executor e declara muitos outros métodos para gerenciar o ciclo de vida de um Executor. Um objeto que implementa a interface ExecutorService pode ser criado utilizando métodos static declarados na classe Executors (do pacote java.util.concurrent). Utilizamos a interface ExecutorService e um método da classe Executors no aplicativo a seguir, que executa três tarefas. A Figura 26.6 utiliza um objeto ExecutorService para gerenciar threads que executam PrintTasks. O método main (linhas 8–29) cria e nomeia três objetos PrintTask (linhas 11–13). A linha 18 utiliza o método Executors newCachedThreadPool para obter um ExecutorService que cria novas threads conforme requerido pelo aplicativo. Essas threads são utilizadas por threadExecutor para executar os Runnables.



26.4  Criando e executando threads

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

811

// Figura 26.6: TaskExecutor.java // Utilizando um ExecutorService para executar Runnables. import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; public class TaskExecutor { public static void main( { // cria e nomeia cada PrintTask task1 = new PrintTask task2 = new PrintTask task3 = new

String[] args ) executável PrintTask( "task1" ); PrintTask( "task2" ); PrintTask( "task3" );

System.out.println( "Starting Executor" ); // cria ExecutorService para gerenciar threads ExecutorService threadExecutor = Executors.newCachedThreadPool(); // inicia threads e coloca no threadExecutor.execute( task1 threadExecutor.execute( task2 threadExecutor.execute( task3

estado executável ); // inicia task1 ); // inicia task2 ); // inicia task3

// encerra threads trabalhadoras quando suas tarefas terminarem threadExecutor.shutdown(); System.out.println( "Tasks started, main ends.\n" ); } // fim de main } // fim da classe TaskExecutor

Starting Executor Tasks started, main ends task1 task2 task3 task3 task2 task1

going to sleep for 4806 milliseconds going to sleep for 2513 milliseconds going to sleep for 1132 milliseconds done sleeping done sleeping done sleeping

Starting Executor task1 going to sleep for 1342 milliseconds task2 going to sleep for 277 milliseconds task3 going to sleep for 2737 milliseconds Tasks started, main ends task2 done sleeping task1 done sleeping task3 done sleeping

Figura 26.6  |  Utilizando um ExecutorService para executar Runnables.

As linhas 21–23 invocam o método execute do ExecutorService. Esse método executa o Runnable que lhe foi passado como um argumento (nesse caso um PrintTask) em algum momento no futuro. A tarefa especificada pode executar em uma das threads no pool de threads do ExecutorService, em uma nova thread criada para executá-la, ou na thread que chamou o método execute; o ExecutorService gerencia esses detalhes. O método execute retorna imediatamente de cada invocação — o programa não espera cada PrintTask terminar. A linha 26 chama o método ExecutorService shutdown, que notifica o ExecutorService para parar de aceitar novas tarefas, mas continua executando as tarefas que já foram enviadas. Uma vez que todos os Runnables enviados anteriormente forem completados, threadExecutor termina. A linha 28 gera saída de uma mensagem que indica que as tarefas foram iniciadas e que a thread principal está terminando sua execução. As saídas de exemplo desse programa são semelhantes àquelas do programa anterior e demonstram novamente o não determinismo do agendamento de threads.

812

Capítulo 26

Multithreading

26.5 Sincronização de thread Quando múltiplas threads compartilham um objeto e ele é modificado por uma ou várias delas, podem ocorrer resultados indeterminados (como veremos nos exemplos) a menos que o acesso ao objeto compartilhado seja gerenciado adequadamente. Se uma thread estiver no processo de atualização de um objeto compartilhado e outra thread também tentar atualizá-lo, não é claro a atualização de qual thread entra em vigor. Quando isso acontecer, não se pode confiar no comportamento do programa — às vezes, o programa produzirá resultados corretos e, outras vezes, não. Em qualquer caso, não haverá nenhuma indicação de que o objeto compartilhado foi manipulado incorretamente. O problema pode ser resolvido fornecendo a somente uma thread, por vez, o código de acesso exclusivo que manipula o objeto compartilhado. Durante esse tempo, outras threads que desejarem manipular o objeto são mantidas na espera. Quando a thread com acesso exclusivo ao objeto terminar de manipulá-lo, uma das threads que foi mantida na espera tem a permissão de prosseguir. Esse processo, chamado sincronização de threads, coordena o acesso a dados compartilhados por múltiplas threads concorrentes. Sincronizando threads dessa maneira, você pode assegurar que cada thread que acessa um objeto compartilhado exclui todas as outras threads de fazer isso simultaneamente — isso é chamado exclusão mútua.

Monitores Uma maneira comum de realizar a sincronização é utilizar os monitores predefinidos do Java. Todo objeto tem um monitor e um bloqueio de monitor (ou bloqueio intrínseco). O monitor assegura que o bloqueio de monitor do seu objeto é mantido por no máximo uma única thread em qualquer dado momento. Desse modo, monitores e bloqueios de monitor podem ser utilizados para forçar a exclusão mútua. Se uma operação exigir que a thread em execução mantenha um bloqueio enquanto a operação for realizada, uma thread deve adquirir o bloqueio antes de prosseguir com a operação. Outras threads tentando realizar uma operação que requer o mesmo bloqueio serão bloqueadas até que a primeira thread libere o bloqueio, ponto em que as threads bloqueadas podem tentar adquirir o bloqueio e prosseguir com a operação. Para especificar que uma thread deve manter um bloqueio de monitor para executar um bloco do código, o código deve ser colocado em uma instrução synchronized. Diz-se que o código está guardado pelo bloqueio de monitor; uma thread deve adquirir o bloqueio para executar as instruções synchronized. O monitor permite que apenas uma thread por vez execute instruções dentro de blocos syn­ chronized que bloqueiam o mesmo objeto, uma vez que somente uma thread por vez pode manter o bloqueio de monitor. As instruções synchronized são declaradas utilizando a palavra-chave synchronized: synchronized ( objeto ) {

instruções } // fim da instrução synchronized

onde objeto é o objeto cujo bloqueio de monitor será adquirido; objeto é normalmente this se for o objeto no qual a instrução synchro­ aparece. Se diversas instruções synchronized estiverem tentando executar em um objeto ao mesmo tempo, apenas uma delas poderá estar ativa no objeto por vez — todas as outras threads que tentarem entrar em uma instrução synchronized do mesmo objeto serão colocadas no estado bloqueado. Quando uma instrução synchronized termina de executar, o bloqueio de monitor do objeto é lançado e uma das threads bloqueadas tentando inserir uma instrução synchronized pode ganhar permissão para adquirir o bloqueio para prosseguir. O Java também permite métodos synchronized. Antes de executar, um método synchronized não static deve adquirir o bloqueio no objeto que é utilizado para chamar o método. De modo semelhante, um método synchronized static deve adquirir o bloqueio na classe que é utilizada para chamar o método. nized

26.5.1 Compartilhamento de dados não sincronizados Um exemplo aqui ilustrará os perigos de compartilhar um objeto entre threads sem sincronização apropriada. Nesse exemplo, dois Runnables mantêm referências a um único array do tipo inteiro. Cada Runnable grava cinco valores ao array e, então, termina. Isso pode

parecer inofensivo, mas pode resultar em erros se o array for manipulado sem sincronização.

Classe SimpleArray Um objeto SimpleArray (Figura 26.7) será compartilhado por múltiplas threads. SimpleArray permitirá que essas threads coloquem valores int no array (declarados na linha 8). A linha 9 inicializa a variável writeIndex, que será utilizada para determinar o elemento de array que deve ser gravado em seguida. O construtor (linhas 13–16) cria um array de inteiros do tamanho desejado. 1 2 3 4 5

// Figura 26.7: SimpleArray.java // A classe que gerencia um array de inteiros a ser compartilhado por múltiplas threads. import java.util.Arrays; import java.util.Random;



26.5  Sincronização de thread

6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47

813

public class SimpleArray // ATENÇÃO: NÃO SEGURO PARA THREADS! { private final int[] array; // o array de inteiros compartilhado private int writeIndex = 0; // o índice do próximo elemento a ser gravado private final static Random generator = new Random(); // constrói um SimpleArray de um dado tamanho public SimpleArray( int size ) { array = new int[ size ]; } // fim do construtor // adiciona um valor ao array compartilhado public void add( int value ) { int position = writeIndex; // armazena o índice de gravação try { // coloca a thread para dormir por 0-499 milissegundos Thread.sleep( generator.nextInt( 500 ) ); } // fim do try catch ( InterruptedException ex ) { ex.printStackTrace(); } // fim do catch // coloca valor no elemento correto array[ position ] = value; System.out.printf( "%s wrote %2d to element %d.\n", Thread.currentThread().getName(), value, position ); ++writeIndex; // incrementa índice do elemento a ser gravado depois System.out.printf( "Next write index: %d\n", writeIndex ); } // fim do método add // utilizado para gerar saída do conteúdo do array de inteiros compartilhado public String toString() { return "\nContents of SimpleArray:\n" + Arrays.toString( array ); } // fim do método toString } // fim da classe SimpleArray

Figura 26.7  |  A classe que gerencia um array de inteiros a ser compartilhado por múltiplas threads.

O método add (linhas 19–40) permite que novos valores sejam inseridos no fim do array. A linha 21 armazena o valor writeIndex atual. A linha 26 coloca a thread que invoca add para dormir por um intervalo aleatório de 0 a 499 milissegundos. Isso é feito para tornar mais óbvios os problemas associados com o acesso não sincronizado a dados compartilhados. Depois que a thread não estiver mais dormindo, a linha 34 insere o valor passado para add no array do elemento especificado por position. As linhas 35–36 geram saída de uma mensagem que indica o nome da thread em execução, o valor que foi inserido no array e onde ele foi inserido. A expressão Thread.currentThread. getName() (linha 36) primeiro obtém uma referência a Thread em execução atualmente, então utiliza o método getName dessa Thread para obter seu nome. A linha 38 incrementa writeIndex para que a chamada seguinte para add insira um valor no próximo elemento do array. As linhas 43–46 sobrescrevem o método toString para criar uma representação String do conteúdo do array.

Classe ArrayWriter A classe ArrayWriter (Figura 26.8) implementa a interface Runnable para definir uma tarefa para inserir valores em um objeto SimpleArray. O construtor (linhas 10–14) aceita dois argumentos — um número inteiro value, que é o primeiro valor que essa tarefa inserirá no objeto SimpleArray, e uma referência ao objeto SimpleArray. A linha 20 invoca o método add no objeto SimpleArray. A tarefa se completa depois que três números inteiros consecutivos que iniciam com startValue forem adicionados ao objeto SimpleArray.

1 2 3 4

// Figura 26.8: ArrayWriter.java // Adiciona números inteiros a um array compartilhado com outros Runnables import java.lang.Runnable;

814 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

Capítulo 26  Multithreading public class ArrayWriter implements Runnable { private final SimpleArray sharedSimpleArray; private final int startValue; public ArrayWriter( int value, SimpleArray array ) { startValue = value; sharedSimpleArray = array; } // fim do construtor public void run() { for ( int i = startValue; i < startValue + 3; i++ ) { sharedSimpleArray.add( i ); // adiciona um elemento ao array compartilhado } // for final } // fim do método run } // fim da classe ArrayWriter

Figura 26.8  |  Adiciona números inteiros a um array compartilhado com outros Runnables.

Classe SharedArrayTest A classe SharedArrayTest (Figura 26.9) executa duas tarefas ArrayWriter que adicionam valores a um objeto SimpleArray simples. A linha 12 constrói um objeto SimpleArray de seis elementos. As linhas 15–16 criam duas novas tarefas ArrayWriter, uma que coloca os valores 1–3 no objeto SimpleArray, e outra que coloca os valores 11–13. As linhas 19–21 criam um ExecutorService e executam os dois ArrayWriters. A linha 23 invoca o método shutDown do ExecutorService para impedir que tarefas adicionais iniciem e para permitir que o aplicativo termine quando as tarefas em execução atualmente completarem a execução. Lembre-se de que o método ExecutorService shutdown retorna imediatamente. Dessa maneira, qualquer código que apareça depois da chamada para o método ExecutorService shutdown na linha 23 continuará executando enquanto a thread main ainda estiver atribuída a um processador. Gostaríamos de gerar saída do objeto SimpleArray para mostrar os resultados depois de as threads completarem suas tarefas. Portanto, precisamos que o programa espere as threads completarem suas tarefas antes de main gerar saída do conteúdo do objeto SimpleArray. A interface ExecutorService fornece o método awaitTermination para esse propósito. Esse método retorna o controle para seu chamador quando todas as tarefas executando no ExecutorService se completarem ou quando o tempo-limite especificado expirar. Se todas as tarefas forem completadas antes de awaitTermination terminar, esse método retorna true; caso contrário, retorna false. Os dois argumentos para awaitTermination representam um valor de tempo-limite e uma unidade da medida especificada com uma constante da classe TimeUnit (nesse caso, TimeUnit.MINUTES). Nesse exemplo, se ambas as tarefas forem completadas antes de awaitTermination expirar, a linha 32 exibe o conteúdo do objeto SimpleArray. Caso contrário, as linhas 34–35 imprimem uma mensagem que indica que as tarefas não terminaram de executar antes de awaitTermination. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

// Figura 26.9: SharedArrayTest.java // Executa dois Runnables para adicionar elementos a um SimpleArray compartilhado. import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; public class SharedArrayTest { public static void main( String[] arg ) { // constrói o objeto compartilhado SimpleArray sharedSimpleArray = new SimpleArray( 6 ); // cria duas tarefas a serem gravadas no SimpleArray compartilhado ArrayWriter writer1 = new ArrayWriter( 1, sharedSimpleArray ); ArrayWriter writer2 = new ArrayWriter( 11, sharedSimpleArray ); // executa as tarefas com um ExecutorService ExecutorService executor = Executors.newCachedThreadPool(); executor.execute( writer1 ); executor.execute( writer2 ); executor.shutdown();



26.5  Sincronização de thread

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

815

try { // espera 1 minuto por ambos os gravadores terminarem a execução boolean tasksEnded = executor.awaitTermination( 1, TimeUnit.MINUTES ); if ( tasksEnded ) System.out.println( sharedSimpleArray ); // imprime o conteúdo else System.out.println( "Timed out while waiting for tasks to finish." ); } // fim do try catch ( InterruptedException ex ) { System.out.println( "Interrupted while waiting for tasks to finish." ); } // fim do catch } // fim de main } // fim da classe SharedArrayTest

pool-1-thread-1 wrote 1 to element Next write index: 1 pool-1-thread-1 wrote 2 to element Next write index: 2 pool-1-thread-1 wrote 3 to element Next write index: 3 pool-1-thread-2 wrote 11 to element Next write index: 4 pool-1-thread-2 wrote 12 to element Next write index: 5 pool-1-thread-2 wrote 13 to element Next write index: 6

0. 1. 2. 0.

O primeiro pool-1-thread-1 grava o valor 1 no elemento 0. Depois, pool-1-thread-2 grava o valor 11 no elemento 0, sobrescrevendo assim o valor previamente armazenado.

4. 5.

Contents of SimpleArray: [11, 2, 3, 0, 12, 13]

Figura 26.9  |  Executa dois Runnables para inserir valores em um array compartilhado.

A saída na Figura 26.9 demonstra os problemas (destacados na saída) que podem ser causados pela falha em sincronizar o acesso a dados compartilhados. O valor 1 foi gravado no elemento 0 e, posteriormente, sobrescrito pelo valor 11. Além disso, quando writeIndex foi incrementado por 3, nada foi gravado nesse elemento, conforme indicado por 0 nesse elemento do array impresso. Lembre-se de que adicionamos chamadas para o método Thread sleep entre operações nos dados compartilhados para enfatizar a imprevisibilidade do agendamento de threads e aumentar a probabilidade de produzir a saída incorreta. É importante notar que mesmo se essas operações tivessem permissão de prosseguir em seu ritmo normal, você ainda poderia ver erros na saída do programa. Contudo, os processadores modernos podem tratar as operações simples do método SimpleArray add com tanta rapidez que talvez não fosse possível ver os erros causados pelas duas threads executando esse método simultaneamente, mesmo se você testasse o programa dezenas de vezes. Um dos desafios da programação de múltiplas threads é indicar erros com precisão — eles podem ocorrer tão raramente que um programa com bugs não produz resultados incorretos durante os testes, criando a ilusão de que o programa está correto.

26.5.2  Compartilhamento de dados sincronizados — tornando operações atômicas Os erros de saída da Figura 26.9 podem ser atribuídos ao fato de que o objeto compartilhado, SimpleArray, não é seguro para threads — SimpleArray é suscetível a erros se acessado concorrentemente por múltiplas threads. O problema reside no método add, que armazena o valor de writeIndex, coloca um novo valor nesse elemento e, então, incrementa writeIndex. Esse método não apresentaria nenhum problema em programas de uma única thread. Contudo, se uma única thread obtiver o valor de writeIndex, não haverá garantia de que outra thread não poderá avançar e incrementar writeIndex antes de a primeira thread ter tido uma chance de colocar um valor no array. Se isso acontecer, a primeira thread estará gravando no array com base em um valor obsoleto de writeIndex — um valor que não é mais válido. Outra possibilidade é que uma thread pudesse obter o valor de writeIndex depois de outra thread adicionar um elemento ao array, mas antes de writeIndex ser incrementado. Nesse caso, também, a primeira thread gravaria no array com base em um valor inválido para writeIndex. SimpleArray não é seguro para thread porque permite que qualquer número de threads leia e modifique dados compartilhados concorrentemente, o que pode causar erros. Para tornar SimpleArray seguro para threads, devemos assegurar que duas threads não podem acessá-lo ao mesmo tempo. Também devemos assegurar que, enquanto uma thread estiver no processo de armazenar writeIndex,

816

Capítulo 26  Multithreading

adicionar um valor ao array e incrementar writeIndex, nenhuma outra thread pode ler ou alterar o valor de writeIndex ou modificar o conteúdo do array em nenhum momento durante essas três operações. Em outras palavras, queremos que essas três operações — armazenar writeIndex, gravar no array, incrementar writeIndex — sejam uma operação atômica, que não pode ser dividida em suboperações menores. Podemos simular a atomicidade assegurando que apenas uma thread executa as três operações por vez. Qualquer outra thread que precisar realizar a operação deve esperar até a primeira thread ter terminado a operação add em sua totalidade. A atomicidade pode ser alcançada utilizando a palavra-chave synchronized. Colocando nossas três suboperações em uma instrução synchronized ou método synchronized, permitimos que apenas uma thread por vez adquira o bloqueio e realize as operações. Quando essa thread tiver completado todas as operações no bloco synchronized e liberar o bloqueio, outra thread pode adquiri-lo e começar a executar as operações. Isso garante que uma thread que executa as operações verá os valores reais dos dados compartilhados e que esses valores não mudarão inesperadamente no meio das operações como resultado do fato de outra thread modificá-los.

Observação de engenharia de software 26.1 Coloque todos os acessos a dados mutáveis que podem ser compartilhados por múltiplas threads dentro de instruções synchronized ou métodos synchronized que sincronizam no mesmo bloqueio. Ao realizar múltiplas operações em dados compartilhados, mantenha o bloqueio pela totalidade da operação para assegurar que a operação seja efetivamente atômica.

Classe SimpleArray com sincronização A Figura 26.10 exibe a classe SimpleArray com a sincronização apropriada. Observe que é ela idêntica à classe SimpleArray da Figura 26.7, exceto que add é agora um método synchronized (linha 20). Desse modo, apenas uma thread por vez pode executar esse método. Reutilizamos as classes ArrayWriter (Figura 26.8) e SharedArrayTest (Figura 26.9) do exemplo anterior. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

// Figura 26.10: SimpleArray.java // A classe que gerencia um array de inteiros a ser compartilhado por múltiplas threads // threads with synchronization. import java.util.Arrays; import java.util.Random; public class SimpleArray { private final int[] array; // o array de inteiros compartilhado private int writeIndex = 0; // o índice do próximo elemento a ser gravado private final static Random generator = new Random(); // constrói um SimpleArray de um dado tamanho public SimpleArray( int size ) { array = new int[ size ]; } // fim do construtor // adiciona um valor ao array compartilhado public synchronized void add( int value ) { int position = writeIndex; // armazena o índice de gravação try { // coloca a thread para dormir por 0-499 milissegundos Thread.sleep( generator.nextInt( 500 ) ); } // fim do try catch ( InterruptedException ex ) { ex.printStackTrace(); } // fim do catch // coloca valor no elemento correto array[ position ] = value; System.out.printf( "%s wrote %2d to element %d.\n", Thread.currentThread().getName(), value, position ); ++writeIndex; // incrementa índice do elemento a ser gravado depois System.out.printf( "Next write index: %d\n", writeIndex ); } // fim do método add



26.5  Sincronização de thread

42 43 44 45 46 47 48

817

// utilizado para gerar saída do conteúdo do array de inteiros compartilhado public String toString() { return "\nContents of SimpleArray:\n" + Arrays.toString( array ); } // fim do método toString } // fim da classe SimpleArray

pool-1-thread-1 wrote 1 to element 0. Next write index: 1 pool-1-thread-2 wrote 11 to element 1. Next write index: 2 pool-1-thread-2 wrote 12 to element 2. Next write index: 3 pool-1-thread-2 wrote 13 to element 3. Next write index: 4 pool-1-thread-1 wrote 2 to element 4. Next write index: 5 pool-1-thread-1 wrote 3 to element 5. Next write index: 6 Contents of SimpleArray: 1 11 12 13 2 3

Figura 26.10  |  A classe que gerencia um array de inteiros a ser compartilhado por múltiplas threads com a sincronização.

A linha 20 declara o método como synchronized, fazendo todas as operações nesse método comportarem-se como uma operação simples, atômica. A linha 22 realiza a primeira suboperação — armazenar o valor de writeIndex. A linha 35 define a segunda suboperação, gravar um elemento no elemento do índice position. A linha 39 incrementa writeIndex. Quando o método termina a execução na linha 41, a thread em execução libera o bloqueio SimpleArray, possibilitando que a outra thread comece a executar o método add. No método synchronized add, imprimimos mensagens para o console indicando o progresso de threads enquanto elas executam esse método, além de realizar as operações reais necessárias para inserir um valor no array. Fazemos isso para que as mensagens sejam impressas na ordem correta, o que nos permite ver se o método está adequadamente sincronizado comparando essas saídas com as do exemplo anterior, não sincronizado. Continuamos a gerar saída de mensagens a partir de blocos synchronized nos exemplos posteriores por propósitos de demonstração, mas, em geral, a E/S não deve ser realizada em blocos synchronized, porque é importante minimizar o período de tempo que um objeto permanece “bloqueado”. Além disso, a linha 27 nesse exemplo chama o método Thread sleep para enfatizar a imprevisibilidade do agendamento de thread. Você nunca deve chamar sleep enquanto mantém um bloqueio em um aplicativo real.

Dica de desempenho 26.2 Mantenha a duração de instruções synchronized o mais curta possível mantendo ao mesmo tempo a sincronização necessária. Isso minimiza o tempo de espera por threads bloqueadas. Evite realizar E/S, cálculos longos e operações que não exigem a sincronização enquanto mantiver um bloqueio.

Outra nota sobre segurança de thread: Dissemos que é necessário sincronizar o acesso a todos os dados que podem ser compartilhados por múltiplas threads. De fato, essa sincronização é necessária somente para dados mutáveis, ou dados que podem mudar durante seu tempo de vida. Se os dados compartilhados não mudarem em um programa de múltiplas threads, então não é possível para uma thread ver valores antigos ou incorretos em consequência da manipulação desses dados por outra thread. Ao compartilhar dados imutáveis por threads, declare os campos de dados correspondentes final para indicar que os valores das variáveis não mudarão depois de eles serem inicializados. Isso impede a modificação acidental dos dados compartilhados posteriormente em um programa, o que poderia comprometer a segurança de thread. Rotular referências de objeto como final indica que a referência não mudará, mas não garante que o próprio objeto seja imutável — isso depende inteiramente das propriedades do objeto. Contudo, ainda é uma boa prática marcar referências que não mudarão como final, já que fazer isso força o construtor do objeto a ser atômico — o objeto será inteiramente construído com todos seus campos inicializados antes de o programa acessá-lo.

Boa prática de programação 26.1 Sempre declare como final campos de dados que você não espera que mudem. As variáveis primitivas que são declaradas como final podem ser seguramente compartilhadas pelas threads. Uma referência de objeto que é declarada como final assegura que o objeto que ela referencia será totalmente construído e inicializado antes de ser utilizado pelo programa, e impede que a referência aponte para outro objeto.

818

Capítulo 26

Multithreading

26.6 Relacionamento entre produtor e consumidor sem sincronização Em um relacionamento produtor/consumidor, a parte produtora de um aplicativo gera dados e os armazena em um objeto compartilhado e a parte consumidora do aplicativo lê os dados do objeto compartilhado. O relacionamento produtor/consumidor separa a tarefa de identificar o trabalho a ser feito a partir das tarefas envolvidas, de fato, na realização do trabalho. Um exemplo de relacionamento produtor/consumidor comum é o spooling de impressão. Embora uma impressora não pudesse estar disponível quando você quer imprimir a partir de um aplicativo (isto é, o produtor), você ainda pode “completar” a tarefa de impressão, uma vez que os dados são temporariamente colocados em disco até a impressora tornar-se disponível. De modo semelhante, quando a impressora (isto é, um consumidor) estiver disponível, ela não precisa esperar até que um usuário atual queira imprimir. Os trabalhos de impressão em spool podem ser impressos assim que a impressora se disponibilizar. Outro exemplo de relacionamento produtor/consumidor é um aplicativo que copia dados em CDs colocando os dados em um buffer de tamanho fixo, o qual é esvaziado à medida que a unidade de CD-RW grava os dados no CD. Em um relacionamento produtor/consumidor de múltiplas threads, uma thread produtora gera dados e os coloca em um objeto compartilhado chamado buffer. Uma thread consumidora lê dados do buffer. Esse relacionamento requer que a sincronização assegure que os valores sejam produzidos e consumidos corretamente. Todas as operações sobre dados mutáveis que são compartilhados pelas múltiplas threads (por exemplo, dados no buffer) devem ser guardadas com um bloqueio para impedir corrupção, como discutido na Seção 26.5. As operações nos dados de buffers compartilhados por uma thread consumidora e produtora são também dependentes de estado — as operações devem prosseguir somente se o buffer estiver no estado correto. Se o buffer estiver em um estado não cheio, o produtor pode produzir; se o buffer estiver em um estado não vazio, o consumidor pode consumir. Todas as operações que acessam o buffer devem utilizar a sincronização para assegurar que os dados são gravados no buffer ou lidos do buffer somente se o buffer estiver no estado adequado. Se o produtor que está tentando colocar os próximos dados no buffer determina que ele está cheio, a thread produtora deve esperar até haver espaço para gravar um novo valor. Se uma thread consumidora encontrar o buffer vazio ou determinar que os dados anteriores já foram lidos, a consumidora também deve esperar que novos dados sejam disponibilizados. Considere como os erros de lógica podem surgir se não sincronizarmos o acesso entre múltiplas threads que manipulam dados compartilhados. Nosso próximo exemplo (Figura 26.11–Figura 26.15) implementa um relacionamento produtor/consumidor sem a sincronização adequada. Uma thread produtora grava os números 1 a 10 em um buffer compartilhado — uma posição de memória única compartilhada entre duas threads (uma variável int simples chamada buffer na linha 6 da Figura 26.14 nesse exemplo). A thread consumidora lê esses dados do buffer compartilhado e os exibe. A saída do programa mostra os valores que a produtora grava (produz) no buffer compartilhado e os valores que a consumidora lê (consome) a partir do buffer compartilhado. Cada valor que a thread produtora gravar no buffer compartilhado deve ser consumido exatamente uma vez pela thread consumidora. Entretanto, as threads nesse exemplo erroneamente não estão sincronizadas. Portanto, os dados podem ser perdidos ou deturpados se a produtora colocar novos dados no buffer compartilhado antes de a consumidora ler os dados anteriores. Além disso, os dados podem ser duplicados incorretamente se a consumidora consumir os dados outra vez antes de a produtora produzir o próximo valor. Para mostrar essas possibilidades, a thread consumidora no exemplo a seguir mantém um total de todos os valores que ele lê. A thread produtora produz valores de 1 a 10. Se a consumidora ler cada valor produzido uma vez e apenas uma vez, o total será 55. Entretanto, se executar esse programa várias vezes, você verá que o total nem sempre é 55 (como mostrado nas saídas da Figura 26.15). Para enfatizar a questão, as threads produtoras e consumidoras no exemplo dormem por intervalos aleatórios de até três segundos entre a execução de suas tarefas. Portanto, não sabemos quando a thread produtora tentará gravar um novo valor ou quando a thread consumidora tentará ler um valor.

Implementando o relacionamento produtor/consumidor O programa consiste na interface Buffer (Figura 26.11) e nas classes Producer (Figura 26.12), Consumer (Figura 26.13), Unsyn­ chronizedBuffer (Figura 26.14) e SharedBufferTest (Figura 26.15). A interface Buffer (Figura 26.11) declara os métodos set (linha 6) e get (linha 9) que um Buffer (como UnsynchronizedBuffer) deve implementar para permitir que a thread Producer coloque um valor no Buffer e a thread Consumer recupere um valor do Buffer, respectivamente. Nos exemplos subsequentes, os métodos set e get chamarão os métodos que lançam InterruptedExceptions. Declaramos cada método com uma cláusula throws aqui para que não tenhamos de modificar essa interface para os exemplos posteriores. 1 2 3 4 5 6 7 8 9 10

// Figura 26.11: Buffer.java // Interface Buffer especifica métodos chamados por Producer e Consumer. public interface Buffer { // coloca o valor int no Buffer public void set( int value ) throws InterruptedException; // retorna o valor int a partir do Buffer public int get() throws InterruptedException; } // fim da interface Buffer

Figura 26.11 | A interface buffers especifica os métodos chamados por Producer e Consumer.



26.6  Relacionamento entre produtor e consumidor sem sincronização

819

A classe Producer (Figura 26.12) implementa a interface Runnable, permitindo que ela seja executada como uma tarefa em uma thread separada. O construtor (linhas 11–14) inicializa a referência Buffer sharedLocation com um objeto criado em main (linha 14 da Figura 26.15) e passado para o construtor. Como veremos, esse é um objeto UnsynchronizedBuffer que implementa a interface Buffer sem sincronizar acesso ao objeto compartilhado. A thread Producer nesse programa executa as tarefas especificadas no método run (linhas 17–39). Cada iteração do loop (linhas 21–35) invoca o método Thread sleep (linha 25) para colocar a thread Producer no estado de espera sincronizada de um intervalo aleatório de tempo entre 0 e 3 segundos. Quando a thread acordar, a linha 26 passa o valor da variável de controle count para o método set do objeto Buffer para configurar o valor do buffer compartilhado. As linhas 27–28 mantêm um total de todos os valores produzidos até aqui e gera saída desse valor. Quando o loop é concluído, as linhas 37–38 exibem uma mensagem indicando que Producer concluiu a produção de dados e está terminando. Em seguida, o método run termina, o que indica que Producer completou sua tarefa. Observe que qualquer método chamado a partir do método run do Runnable (por exemplo, o método Buffer set) executa como parte da thread de execução dessa tarefa. Esse fato se torna importante na Seção 26.7 quando adicionamos a sincronização ao relacionamento produtor/consumidor. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

// Figura 26.12: Producer.java // Producer com um método run que insere os valores de 1 a 10 no buffer. import java.util.Random; public class Producer implements Runnable { private final static Random generator = new Random(); private final Buffer sharedLocation; // referência a objeto compartilhado // construtor public Producer( Buffer shared ) { sharedLocation = shared; } // fim do construtor Producer // armazena os valores de 1 a 10 em sharedLocation public void run() { int sum = 0; for ( int count = 1; count { private final Random generator = new Random();

846 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

Capítulo 26  Multithreading private private private private private

final final final final final

JTextArea intermediateJTextArea; // exibe os primos localizados JButton getPrimesJButton; JButton cancelJButton; JLabel statusJLabel; // exibe o status do cálculo boolean[] primes; // array booleano para localizar primos

// construtor public PrimeCalculator( int max, JTextArea intermediate, JLabel status, JButton getPrimes, JButton cancel ) { intermediateJTextArea = intermediate; statusJLabel = status; getPrimesJButton = getPrimes; cancelJButton = cancel; primes = new boolean[ max ]; // inicializa todos os valores de array primos como verdadeiros for ( int i = 0; i < max; i ++ ) primes[ i ] = true; } // fim do construtor // localiza todos os primos até o máximo utilizando o Crivo de Eratóstenes public Integer doInBackground() { int count = 0; // o número de primos localizados // iniciando no terceiro valor, circula pelo array // falso como o valor de qualquer número maior que for ( int i = 2; i < primes.length; i++ ) { if ( isCancelled() ) // se o cálculo tiver sido return count; else { setProgress( 100 * ( i + 1 ) / primes.length

e coloca for um múltiplo

cancelado

);

try { Thread.sleep( generator.nextInt( 5 ) ); } // fim do try catch ( InterruptedException ex ) { statusJLabel.setText( "Worker thread interrupted" ); return count; } // fim do catch if ( primes[ i ] ) // i é primo { publish( i ); // disponibiliza para exibição na lista de primos ++count; for ( int j = i + i; j < primes.length; j += i ) primes[ j ] = false; // i não é primo } // fim do if } // fim de else } // for final return count; } // fim do método doInBackground // exibe valores publicados na lista de primos protected void process( List< Integer > publishedVals ) { for ( int i = 0; i < publishedVals.size(); i++ ) intermediateJTextArea.append( publishedVals.get( i ) + “\n” ); } // fim do método process // código para executar quando doInBackground se completa protected void done()

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

26.11  Multithreading com GUI

847

{ getPrimesJButton.setEnabled( true ); // ativa o botão Get Primes cancelJButton.setEnabled( false ); // desativa o botão Cancel int numPrimes; try { numPrimes = get(); // recupera o valor de retorno de doInBackground } // fim do try catch ( InterruptedException ex ) { statusJLabel.setText( "Interrupted while waiting for results." ); return; } // fim do catch catch ( ExecutionException ex ) { statusJLabel.setText( "Error performing computation." ); return; } // fim do catch catch ( CancellationException ex ) { statusJLabel.setText( "Cancelled." ); return; } // fim do catch statusJLabel.setText( "Found " + numPrimes + " primes." ); } // fim do método done } // fim da classe PrimeCalculator

Figura 26.27  |  Calcula os n primeiros primos, exibindo-os à medida que são localizados.

A classe PrimeCalculator estende SwingWorker (linha 12), com o primeiro parâmetro de tipo que indica o tipo de retorno do método doInBackground e o segundo que indica que o tipo de resultados intermediários passados entre os métodos publish e process. Nesse caso, ambos os parâmetros de tipo são Integers. O construtor (linhas 22–34) aceita como argumentos um número inteiro que indica o limite superior dos números primos a localizar, uma JTextArea utilizada para exibir primos na GUI, um JButton para iniciar um cálculo e um para cancelá-lo, e um JLabel utilizado para exibir o status do cálculo.

Crivo de Eratóstenes As linhas 32–33 inicializam os elementos do array boolean primes como true. PrimeCalculator utiliza esse array e o algoritmo de Crivo de Eratóstenes (descrito no Exercício 7.27) para localizar todos os primos menores que max. O Crivo de Eratóstenes aceita uma lista de números inteiros e, iniciando com o primeiro número primo, filtra todos os múltiplos desse primo. Ele então avança para o próximo primo, que será o próximo número que ainda não foi filtrado, e elimina todos seus múltiplos. Ele continua até que o fim da lista seja alcançado e todos os não primos tenham sido filtrados. Algoritmicamente, iniciamos com o elemento 2 do array boolean e configuramos as células correspondentes a todos os valores que são múltiplos de 2 como false para indicar que eles são divisíveis por 2 e, portanto, não primos. Então avançamos para o próximo elemento do array, verificamos se é true e, se for, configuramos todos os seus múltiplos como false para indicar que são divisíveis pelo índice atual. Quando o array de inteiros tiver sido percorrido desse modo, todos os índices que contêm true serão primos, uma vez que não têm divisores. Método doInBackground No método doInBackground (linhas 37–73), a variável de controle i do loop (linhas 43–70) controla o índice atual para implementar o Crivo de Eratóstenes. A linha 45 chama o método SwingWorker isCancelled herdado para determinar se o usuário clicou no botão Cancel. Se isCancelled retornar true, o método doInBackground retornará o número de primos localizados até esse momento (linha 46) sem terminar o cálculo. Se o cálculo não for cancelado, a linha 49 chamará setProgress para atualizar a porcentagem do array que foi percorrida até agora. A linha 53 coloca a thread em execução atualmente para dormir até 4 milissegundos. Discutimos a razão disso a seguir. A linha 61 testa se o elemento do array primes no índice atual é true (e, portanto, primo). Nesse caso, a linha 63 passa o índice para o método publish para que ele possa ser exibido como um resultado intermediário na GUI e a linha 64 incrementa o número de primos localizados. As linhas 66–67 configuram todos os múltiplos do índice atual como false para indicar que eles não são primos. Quando o array de inteiros tiver sido percorrido, a linha 72 retorna o número de primos localizados.

848

Capítulo 26  Multithreading

Método process As linhas 76–80 declaram o método process, que executa na thread de despacho de eventos e recebe seu argumento published­ Vals do método publish. A passagem de valores entre publish na thread trabalhadora e process na thread de despacho de eventos é assíncrona; process pode não ser invocado por toda chamada a publish. Todos Integers publicados desde a última chamada a process são recebidos como uma List pelo método process. As linhas 78–79 iteram por essa lista e exibem os valores publicados em uma JTextArea. Como o cálculo no método doInBackground avança rapidamente, publicando valores frequentemente, as atualizações para JTextArea podem acumular-se na thread de despacho de eventos, tornando a GUI lenta. De fato, ao procurar por um grande número de primos, a thread de despacho de eventos pode receber tantas solicitações em rápida sucessão para atualizar a JTextArea que ela fica sem memória em sua fila de eventos. É por isso que colocamos a thread trabalhadora para dormir por alguns milissegundos entre as chamadas a publish. A velocidade do cálculo é reduzida o suficiente para permitir que a thread de despacho de eventos acompanhe as solicitações de atualização da JTextArea com os novos primos, permitindo à GUI atualizar-se facilmente e permanecer responsiva. Método done As linhas 83–111 definem o método done. Quando o cálculo é concluído ou cancelado, o método done ativa o botão Get Primes e desativa o botão Cancel (linhas 85–86). A linha 92 obtém o valor de retorno — número de primos localizados — a partir do método doIn­ Background. As linhas 94–108 capturam as exceções lançadas pelo método get e exibem uma mensagem apropriada no statusJLabel. Se não ocorrer exceções, a linha 110 configura o statusJLabel para indicar o número de primos localizados. Classe FindPrimes A classe FindPrimes (Figura 26.28) exibe um JTextField que permite ao usuário inserir um número, um JButton para começar a localizar todos os primos menores que aquele número e uma JTextArea para exibir os primos. Um JButton permite que o usuário cancele o cálculo, e um JProgressBar indica o progresso do cálculo. O construtor FindPrimes (linhas 32–125) inicializa esses componentes e os exibe em um JFrame usando BorderLayout. As linhas 42–94 registram o handler de evento para o getPrimesJButton. Quando o usuário clica nesse JButton, as linhas 47–49 reinicializam a JProgressBar e limpam a displayPrimesJTextArea e a statusJLabel. As linhas 53–63 analisam o valor no JText­ Field e exibem uma mensagem de erro se o valor não for um número inteiro. As linhas 66–68 constroem um novo objeto PrimeCal­ culator, passando como argumentos o número inteiro inserido pelo usuário, a displayPrimesJTextArea para exibir os primos, o statusJLabel e os dois JButtons. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

// Figura 26.28: FindPrimes.java // Utilizando um SwingWorker para exibir números primos e atualizar um JProgressBar // enquanto os números primos estão sendo calculados. import javax.swing.JFrame; import javax.swing.JTextField; import javax.swing.JTextArea; import javax.swing.JButton; import javax.swing.JProgressBar; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ScrollPaneConstants; import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.util.concurrent.ExecutionException; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; public class FindPrimes extends JFrame { private final JTextField highestPrimeJTextField = new JTextField(); private final JButton getPrimesJButton = new JButton( "Get Primes" ); private final JTextArea displayPrimesJTextArea = new JTextArea(); private final JButton cancelJButton = new JButton( "Cancel" ); private final JProgressBar progressJProgressBar = new JProgressBar(); private final JLabel statusJLabel = new JLabel(); private PrimeCalculator calculator; // construtor public FindPrimes() {

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

26.11  Multithreading com GUI super( "Finding Primes with SwingWorker" ); setLayout( new BorderLayout() ); // inicializa o painel para obter um número do usuário JPanel northJPanel = new JPanel(); northJPanel.add( new JLabel( "Find primes less than: " ) ); highestPrimeJTextField.setColumns( 5 ); northJPanel.add( highestPrimeJTextField ); getPrimesJButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { progressJProgressBar.setValue( 0 ); // redefine JProgressBar displayPrimesJTextArea.setText( "" ); // limpa JTextArea statusJLabel.setText( "" ); // limpa JLabel int number; // pesquisa primos para cima até esse valor try { // obtém entrada de usuário number = Integer.parseInt( highestPrimeJTextField.getText() ); } // fim do try catch ( NumberFormatException ex ) { statusJLabel.setText( "Enter an integer." ); return; } // fim do catch // constrói um novo objeto PrimeCalculator calculator = new PrimeCalculator( number, displayPrimesJTextArea, statusJLabel, getPrimesJButton, cancelJButton ); // ouve alterações de propriedade na barra de progresso calculator.addPropertyChangeListener( new PropertyChangeListener() { public void propertyChange( PropertyChangeEvent e ) { // se a propriedade alterada for progress, // atualiza a barra de progresso if ( e.getPropertyName().equals( “progress” ) ) { int newValue = ( Integer ) e.getNewValue(); progressJProgressBar.setValue( newValue ); } // fim do if } // fim do método propertyChange } // fim da classe interna anônima ); // termina a chamada para addPropertyChangeListener // desativa o botão Get Primes e ativa o botão Cancel getPrimesJButton.setEnabled( false ); cancelJButton.setEnabled( true ); calculator.execute(); // executa o objeto PrimeCalculator } // fim do método ActionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener northJPanel.add( getPrimesJButton ); // adiciona uma JList rolável para exibir os resultados do cálculo displayPrimesJTextArea.setEditable( false ); add( new JScrollPane( displayPrimesJTextArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER ) );

849

850 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

Capítulo 26  Multithreading // inicializa um painel para exibir cancelJButton, // progressJProgressBar e statusJLabel JPanel southJPanel = new JPanel( new GridLayout( 1, 3, 10, 10 ) ); cancelJButton.setEnabled( false ); cancelJButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { calculator.cancel( true ); // cancela o cálculo } // fim do método ActionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener southJPanel.add( cancelJButton ); progressJProgressBar.setStringPainted( true ); southJPanel.add( progressJProgressBar ); southJPanel.add( statusJLabel ); add( northJPanel, BorderLayout.NORTH ); add( southJPanel, BorderLayout.SOUTH ); setSize( 350, 300 ); setVisible( true ); } // fim do construtor // método main inicia a execução de programa public static void main( String[] args ) { FindPrimes application = new FindPrimes(); application.setDefaultCloseOperation( EXIT_ON_CLOSE ); } // fim de main } // fim da classe FindPrimes

Figura 26.28  |  Utilizando um SwingWorker para exibir números primos e atualizar uma JProgressBar enquanto os números primos estão sendo calculados.

As linhas 71–85 registram um objeto PropertyChangeListener para o PrimeCalculator. PropertyChangeListener é uma interface do pacote java.beans que define um único método, propertyChange. Toda vez que o método setProgress é invocado em um PrimeCalculator, este gera um PropertyChangeEvent para indicar que a propriedade de progresso mudou. O método Property­ Change ouve esses eventos. A linha 78 testa se um dado PropertyChangeEvent indica uma alteração na propriedade de progresso. Nesse caso, a linha 80 obtém o novo valor da propriedade e a linha 81 atualiza a JProgressBar com o novo valor da propriedade de progresso. O JButton Get Primes é desativado (linha 88) para que somente um cálculo que atualiza a GUI possa executar por vez, e o JButton Cancel é ativado (linha 89) para permitir ao usuário parar o cálculo antes de ele ser completado. A linha 91 executa o PrimesCalculator para começar a localizar os primos. Se o usuário clicar no cancelJButton, o handler de evento registrado nas linhas 107–115 chama o método PrimeCalculator cancel (linha 112), que é herdado da classe SwingWorker, e o cálculo retorna mais cedo. O argumento true para o método cancel indica que a thread que realiza a tarefa deve ser interrompida em uma tentativa de cancelar a tarefa.

26.12 Interfaces Callable e Future

851

26.12 Interfaces Callable e Future A interface Runnable fornece apenas as funcionalidades mais básicas para a programação de múltiplas threads. De fato, essa interface tem várias limitações. Suponha que um Runnable encontre um problema e tente lançar uma exceção verificada. O método run não é declarado para lançar nenhuma exceção, desse modo, o problema deve ser tratado dentro do Runnable — a exceção não pode ser passada para a thread chamadora. Agora suponha que um Runnable esteja realizando um cálculo longo e o aplicativo queira recuperar o resultado desse cálculo. O método run não pode retornar um valor, então o aplicativo deve utilizar dados compartilhados para passar o valor de volta para a thread chamadora. Isso também envolve o overhead de sincronizar acesso aos dados. Os desenvolvedores das APIs de concorrência introduzidas no Java SE 5 reconheceram essas limitações e criaram uma nova interface para corrigi-las. A interface Callable (do pacote java.util. concurrent) declara um único método chamado call. Essa interface é projetada para ser semelhante à interface Runnable — permitindo que uma ação seja realizada concorrentemente em uma thread separada — mas o método call permite que a thread retorne um valor ou lance uma exceção verificada. Um aplicativo que cria uma Callable provavelmente quer executá-la concorrentemente com outras Runnables e Callables. A interface ExecutorService fornece o método submit, que executará uma Callable passada como seu argumento. O método submit retorna um objeto do tipo Future (do pacote java.util.concurrent), que é uma interface que representa a Callable em execução. A interface Future declara o método get para retornar o resultado da Callable e fornece outros métodos para gerenciar a execução de uma Callable.

26.13 Conclusão Neste capítulo, você aprendeu que a concorrência tem sido historicamente implementada com primitivos de sistema operacional disponíveis apenas a programadores de sistemas experientes, mas que o Java a disponibiliza para você por meio da linguagem e das APIs. Também aprendeu que a própria JVM cria threads para executar um programa, e que também pode criar threads para realizar tarefas de limpeza, como coleta de lixo. Discutimos o ciclo de vida de uma thread e os estados que uma thread pode ocupar durante seu tempo de vida. Também discutimos prioridades de threads do Java, que ajudam o sistema a agendar threads para execução. Você aprendeu que deve evitar manipular prioridades de thread do Java diretamente e conheceu os problemas associados com prioridades de thread, como o adiamento indefinido (às vezes chamado de inanição). Em seguida, apresentamos a interface Runnable, utilizada para especificar uma tarefa que pode executar concorrentemente com outras tarefas. O método run dessa interface é invocado pela thread que executa a tarefa. Mostramos como executar um objeto Runnable associando-o com um objeto da classe Thread. Depois mostramos como utilizar a interface Executor para gerenciar a execução de objetos Runnable via grupos de threads, que podem reutilizar threads existentes para eliminar o overhead de criar uma nova thread para cada tarefa e que podem aprimorar o desempenho otimizando o número de threads para assegurar que o processador permaneça ocupado. Você aprendeu que, quando múltiplas threads compartilham um objeto e uma ou várias delas modificam esse objeto, podem ocorrer resultados indeterminados a menos que o acesso ao objeto compartilhado seja gerenciado corretamente. Mostramos como resolver esse problema via sincronização de threads, que coordena o acesso a dados compartilhados por múltiplas threads concorrentes. Também aprendeu várias técnicas para realizar a sincronização — primeiro com a classe predefinida ArrayBlockingQueue (que trata todos os detalhes de sincronização para você), então com monitores predefinidos do Java e a palavra-chave synchronized e, por fim, com as interfaces Lock e Condition. Discutimos o fato de as GUI Swing não serem seguras para thread, portanto todas as interações com e as modificações ao GUI devem ser realizadas na thread de despacho de eventos. Também discutimos os problemas associados com a realização de cálculos muito extensos na thread de despacho de eventos. Mostramos então como é possível utilizar a classe SwingWorker do Java SE 6 para realizar cálculos muito longos em threads trabalhadoras. Você aprendeu a exibir os resultados de um SwingWorker em uma GUI quando o cálculo for completado e a exibir resultados intermediários enquanto o cálculo ainda está em andamento. Por fim, discutimos as interfaces Callable e Future, que permitem executar tarefas que retornam resultados e obter esses resultados, respectivamente. Utilizamos as técnicas de multithreading introduzidas aqui novamente no Capítulo 27, “Redes”, para ajudar a construir servidores de múltiplas threads que podem interagir com múltiplos clientes concorrentemente.

Resumo Seção 26.1 Introdução • Historicamente, a concorrência foi implementada com os primitivos de sistemas operacionais disponíveis apenas para programadores de sistemas experientes. • A linguagem de programação Ada tornou os primitivos de concorrência amplamente disponíveis. • O Java disponibiliza a concorrência por meio da linguagem e das APIs. • A JVM cria threads para executar um programa e para tarefas de faxina, a exemplo da coleta de lixo.

852

Capítulo 26  Multithreading

Seção 26.2  Estados de thread: ciclo de vida de uma thread • Uma nova thread inicia seu ciclo de vida no estado novo. Quando o programa inicia a thread, ela é colocada no estado executável. Considera-se que uma thread no estado executável está executando sua tarefa. • Uma thread executável entra no estado de espera aguardando outra thread realizar uma tarefa. Uma thread de espera transita para o estado executável quando outra thread a notifica para continuar em execução. • Uma thread executável pode entrar no estado de espera sincronizada por um intervalo de tempo especificado, retornando ao estado executável quando esse intervalo expirar ou quando o evento pelo qual ela estiver esperando ocorrer. • Uma thread executável pode transitar para o estado de espera sincronizada se fornecer um intervalo de espera opcional quando ela estiver esperando outra thread realizar uma tarefa. Essa thread voltará ao estado executável quando for notificada por outra thread ou quando o intervalo sincronizado expirar. • Uma thread adormecida permanece no estado de espera sincronizada por um período de tempo designado, depois do qual ela retorna ao estado executável. • Uma thread adormecida transita para o estado bloqueado quando tenta realizar uma tarefa que não pode ser completada imediatamente e a thread deve esperar temporariamente até essa tarefa ser concluída. Nesse ponto, a thread bloqueada transita para o estado executável, portanto pode retomar a execução. • Uma thread executável entra no estado terminado quando completa sua tarefa ou, caso contrário, termina (talvez por causa de um erro). • No nível de sistema operacional, o estado executável geralmente inclui dois estados separados. Quando uma thread entra pela primeira vez no estado executável a partir do estado novo, ela está no estado pronto. Uma thread pronta entra no estado executável quando o sistema operacional a despacha. • A maioria dos sistemas operacionais aloca um quantum ou fração de tempo no qual uma thread realiza sua tarefa. Quando isso expira, a thread retorna ao estado pronto e outra thread é atribuída ao processador. • O agendamento de threads determina qual thread despachar com base nas prioridades de thread.

Seção 26.3  Prioridades de thread e agendamento de thread • Toda thread do Java tem uma prioridade de thread (de MIN_PRIORITY a MAX_PRIORITY) que ajuda o sistema operacional a determinar a ordem em que as threads serão agendadas. • Por padrão, toda thread recebe a prioridade NORM_PRIORITY (uma constante de 5). Cada nova thread herda a prioridade da thread que a cria. • O trabalho de um scheduler de thread do sistema operacional é determinar a próxima thread que entrará em execução. • Quando uma thread de prioridade mais alta entra no estado pronto, o sistema operacional geralmente faz preempção da thread atualmente em execução (uma operação conhecida como agendamento preemptivo). • Dependendo do sistema operacional, as threads de prioridade mais alta poderiam adiar — possivelmente por um tempo indefinido — a execução de threads de prioridade mais baixa.

Seção 26.4  Criando e executando threads • Um objeto Runnable representa uma “tarefa” que pode executar concorrentemente com outras tarefas. • A interface Runnable declara o método run no qual você coloca o código que define a tarefa a ser realizada. A thread que executa um Runnable chama o método run para realizar a tarefa. • Um programa não terminará até que sua última thread complete a execução. • Você não pode prever a ordem em que as threads serão agendadas, mesmo se você souber a ordem na qual elas foram criadas e iniciadas. • Recomenda-se o uso da interface Executor para gerenciar a execução de objetos Runnable. Em geral, um objeto Executor cria e gerencia um grupo de threads — chamado pool de threads. • Os Executors podem reutilizar threads existentes (para eliminar o overhead de criar novas threads) e podem aprimorar o desempenho otimizando o número de threads para assegurar que o processador permaneça ocupado. • O método Executor execute recebe um Runnable e o atribui a uma thread disponível em um grupo de threads. Se não houver nenhuma, o Executor cria uma nova thread ou espera uma se tornar disponível. • A interface ExecutorService (do pacote java.util.concurrent) estende a interface Executor e declara outros métodos para gerenciar o ciclo de vida de um Executor. • Um objeto que implementa a interface ExecutorService pode ser criado com os métodos static declarados na classe Executors (do pacote java. util.concurrent). • O método Executors newCachedThreadPool retorna um ExecutorService que cria novas threads conforme a necessidade do aplicativo. • O método ExecutorService execute executa o seu Runnable algum momento no futuro. O método retorna imediatamente de cada invocação — o programa não espera cada tarefa para terminar. • O método ExecutorService shutdown notifica o ExecutorService para deixar de aceitar novas tarefas, mas continua executando tarefas existentes e termina quando essas tarefas terminam sua execução.



Resumo

853

Seção 26.5  Sincronização de thread • A sincronização de threads coordena o acesso a dados compartilhados por múltiplas threads concorrentes. • Sincronizando as threads, você pode assegurar que cada thread que acessa um objeto compartilhado exclui todas as outras threads de fazer isso simultaneamente — isso é chamado exclusão mútua. • Uma maneira comum de realizar a sincronização é utilizar os monitores predefinidos do Java. Cada objeto tem um monitor e um bloqueio de monitor. O monitor assegura que o bloqueio de monitor do seu objeto é mantido por no máximo uma thread em qualquer dado momento, e assim pode ser utilizado para impor a exclusão mútua. • Se uma operação exigir que a thread em execução mantenha um bloqueio enquanto a operação é realizada, uma thread deve adquirir o bloqueio antes de poder prosseguir com a operação. Quaisquer outras threads tentando prosseguir com uma operação que requer o mesmo bloqueio será bloqueada até que a primeira thread libere o bloqueio, ponto em que as threads bloqueadas podem tentar adquirir o bloqueio. • Para especificar que uma thread deve manter um bloqueio de monitor para executar um bloco do código, o código deve ser colocado em uma instrução synchronized. Diz-se que esse código deve ser guardado pelo bloqueio de monitor. • As instruções synchronized são declaradas utilizando a palavra-chave synchronized: synchronized ( objeto ) {

instruções } // fim da instrução synchronized

onde objeto é o objeto cujo bloqueio de monitor será adquirido; objeto é normalmente this se for o objeto no qual a instrução synchronized aparece. • O Java também permite métodos synchronized. Antes de executar, um método synchronized não static deve adquirir o bloqueio no objeto que é utilizado para chamar o método. De modo semelhante, um método synchronized static deve adquirir o bloqueio na classe que é utilizada para chamar o método. • O método ExecutorService awaitTermination impõe que um programa espere pelo término das threads. Ele retorna o controle à sua chamadora quando todas as tarefas executando no ExecutorService concluíram ou quando o tempo-limite especificado expirou. Se todas as tarefas forem concluídas antes de o tempo-limite expirar, o método retorna true; caso contrário, ele retorna false. • Você pode simular a atomicidade assegurando que apenas uma thread realize o conjunto de operações por vez. A atomicidade pode ser alcançada com instruções synchronized ou métodos synchronized. • Ao compartilhar dados imutáveis entre threads, você deve declarar os campos de dados final correspondentes para indicar que os valores das variáveis não mudarão depois que forem inicializados.

Seção 26.6  Relacionamento entre produtor e consumidor sem sincronização • Em um relacionamento produtor/consumidor de múltiplas threads, uma thread produtora gera dados e os coloca em um objeto compartilhado chamado buffer. Uma thread consumidora lê dados do buffer. • As operações em dados de um buffer compartilhado por um produtor e um consumidor devem prosseguir somente se o buffer estiver no estado correto. Se o buffer não estiver cheio, o produtor pode produzir; se o buffer não for vazio, o consumidor pode consumir. Se o buffer estiver cheio quando o produtor tentar gravar nele, o produtor deve esperar até que haja espaço. Se o buffer estiver vazio ou o valor anterior já tiver sido lido, o consumidor deve esperar os novos dados se tornarem disponíveis.

Seção 26.7  Relacionamento de produtor/consumidor: ArrayBlockingQueue • ArrayBlockingQueue é uma classe de buffer inteiramente implementada a partir do pacote java.util.concurrent que implementa a interface BlockingQueue. • Um ArrayBlockingQueue pode implementar um buffer compartilhado em um relacionamento de produtor/consumidor. O método put coloca um elemento no fim da BlockingQueue, esperando se a fila estiver cheia. O método take removerá um elemento da cabeça da BlockingQueue, esperando se a fila estiver vazia. • ArrayBlockingQueue armazena dados compartilhados em um array que é dimensionado com um argumento passado para o construtor. Uma vez criado, um ArrayBlockingQueue tem o tamanho fixado.

Seção 26.8  Relacionamento entre produtor/consumidor com sincronização • Você pode implementar um buffer compartilhado utiliza a palavra-chave synchronized e os métodos Object wait, notify e notifyAll. • Uma thread pode chamar o método Object wait para lançar o bloqueio de monitor de um objeto, e esperar no estado de espera enquanto outras threads tentam inserir a(s) instrução(ões) ou método(s) synchronized do objeto. • Quando uma thread que executa uma instrução (ou método) synchronized completa ou satisfaz a condição que outra thread pode estar esperando, ela pode chamar o método Object notify para permitir que uma thread em espera transite para o estado executável novamente. Nesse ponto, a thread que transitou do estado de espera para o estado bloqueado pode tentar readquirir o bloqueio de monitor do objeto. • Se uma thread chamar notifyAll, então todas as threads que esperam o bloqueio de monitor se tornarão elegíveis para readquirir o bloqueio (isto é, todas farão a transição para o estado executável).

854

Capítulo 26  Multithreading

Seção 26.9  Relacionamento de produtor/consumidor: buffers limitados • Você não pode fazer suposições sobre as velocidades relativas das threads concorrentes. • Um buffer limitado pode ser utilizado para minimizar o tempo de espera de threads que compartilham recursos e operam nas mesmas velocidades médias. Se a produtora produzir valores temporariamente mais rápidos do que a consumidora pode consumi-los, a produtora pode escrever valores adicionais no espaço extra de buffer (se algum estiver disponível). Se a consumidora consumir mais rápido do que a capacidade da produtora de produzir novos valores, a consumidora poderá ler valores adicionais (se houver algum) do buffer. • A chave para utilizar um buffer delimitado com uma produtora e uma consumidora que operam quase na mesma velocidade é fornecer ao buffer posições suficientes para tratar a produção “extra” antecipada. • O modo mais simples de implementar um buffer limitado é utilizar um ArrayBlockingQueue para o buffer para que todos os detalhes de sincronização sejam tratados por você.

Seção 26.10  Relacionamento de produtor/consumidor: as interfaces Lock e Condition • As interfaces Lock e Condition, introduzidas no Java SE 5, fornecem aos programadores controle mais preciso sobre a sincronização de threads, mas são mais complicadas de utilizar. • Qualquer objeto pode conter uma referência a um objeto que implementa a interface Lock (do pacote java.util.concurrent.locks). Uma thread chama o método lock de Lock para obter o bloqueio. Uma vez que um Lock foi obtido por uma thread, o objeto Lock não permitirá que outra thread obtenha o Lock até que a primeira thread libere o Lock (chamando o método unlock de Lock). • Se várias threads estiverem tentando chamar o método lock no mesmo objeto Lock ao mesmo tempo, apenas uma thread poderá obter o bloqueio — as outras serão colocadas no estado de espera. Quando uma thread chama unlock, o bloqueio do objeto é liberado e uma thread em espera tentando bloquear o objeto prossegue. • A classe ReentrantLock é uma implementação básica da interface Lock. • O construtor ReentrantLock aceita um boolean que especifica se o bloqueio tem uma diretiva de imparcialidade. Se true, a diretiva de imparcialidade ReentrantLock é “a thread em espera mais longa irá adquirir o bloqueio quando ele estiver disponível” — isso impede o adiamento indefinido. Se o argumento estiver configurado como false, não é garantido que a thread na espera irá adquirir o bloqueio quando ele estiver disponível. • Se uma thread que possui um Lock determina que não é possível continuar sua tarefa até que alguma condição seja satisfeita, a thread pode esperar em um objeto condição. Utilizar objetos Lock permite declarar explicitamente os objetos condição nos quais uma thread pode precisar esperar. • Os objetos condição estão associados a um Lock específico e são criados chamando o método Lock newCondition, que retorna um objeto Condition. Para esperar em uma Condition, a thread pode chamar o método de espera da Condition. Isso libera imediatamente o Lock associado e coloca a thread no estado de espera dessa Condition. Outras threads podem então tentar obter o Lock. • Quando uma thread executável completar uma tarefa e determinar que a thread na espera agora pode continuar, a thread executável pode chamar o método Condition signal para permitir que uma thread no estado de espera dessa Condition retorne ao estado executável. Nesse ponto, a thread que fez a transição do estado de espera para o estado executável pode tentar readquirir o Lock. • Se múltiplas threads estiverem no estado de espera de uma Condition quando signal for chamado, a implementação padrão de Condition sinaliza a thread de espera mais longa para fazer a transição para o estado executável. • Se uma thread chamar método Condition signalAll, então todas as threads que esperam essa condição mudam para o estado executável e tornam-se elegíveis para readquirir o Lock. • Quando uma thread concluir sua tarefa com um objeto compartilhado, ela deve chamar o método unlock para liberar o Lock. • Os Locks permitem interromper threads em espera ou especificar um tempo-limite a esperar a fim de adquirir um bloqueio — o que é não possível fazer com synchronized. Além disso, um objeto Lock não é obrigado a ser adquirido e liberado no mesmo bloco do código, que é o caso com a palavra-chave synchronized. • Objetos Condition permitem especificar múltiplas condições nas quais a thread podem esperar. Assim, é possível indicar as threads em espera que um objeto condição específico é agora verdadeiro, chamando os métodos signal ou signallAll desse objeto Condition. Com synchronized, não há nenhum modo de afirmar explicitamente a condição que as threads estão esperando.

Seção 26.11  Multithreading com GUI • A thread de despacho de eventos trata interações com os componentes GUI do aplicativo. Todas as tarefas que interagem com a GUI são colocadas em uma fila de eventos e executadas em sequência por essa thread. • Os componentes GUI Swing não são seguros para thread. A segurança de thread é alcançada assegurando que os componentes Swing são acessados apenas a partir da thread de despacho de eventos. • Realizar um cálculo longo em resposta a uma interação da interface com usuário amarra a thread de despacho de eventos, impedindo-a de atender a outras tarefas e tornando os componentes GUI não responsivos. Os cálculos demorados devem ser tratados em threads separadas. • Você pode estender a classe genérica SwingWorker (pacote javax.swing), que implementa Runnable, para realizar cálculos demorados em uma thread trabalhadora e atualizar componentes Swing da thread de despacho de eventos com base nos resultados dos cálculos. Você sobrescreve seus métodos doInBackground e done. O método doInBackground realiza o cálculo e retorna o resultado. O método done exibe os resultados na GUI.



Terminologia

855

• O primeiro parâmetro de tipo da classe SwingWorker indica o tipo retornado pelo método doInBackground; o segundo indica o tipo que é passado entre os métodos publish e process para tratar dos resultados intermediários. • O método doInBackground é chamado a partir de uma thread trabalhadora. Depois de doInBackground retornar, o método done é chamado a partir da thread de despacho de eventos para exibir os resultados. • Um ExecutionException é lançada se ocorrer uma exceção durante o cálculo. • O método SwingWorker publish repetidamente envia resultados intermediários ao método process, que exibe os resultados em um componente de GUI. O método setProgress atualiza a propriedade de progresso. • O método process executa na thread de despacho de eventos e recebe dados do método publish. A passagem de valores entre publish na thread trabalhadora e process na thread de despacho de eventos é assíncrona; process não é necessariamente invocado para cada chamada feita a publish. • PropertyChangeListener é uma interface do pacote java.beans que define um único método, PropertyChange. Toda vez que o método set­ Progress é invocado, uma PropertyChangeEvent é gerada para indicar que a propriedade de progresso mudou.

Seção 26.12  Interfaces Callable e Future • A interface Callable (do pacote java.util.concurrent) declara um método único chamado call. A interface é semelhante a Runnable — permitindo que uma ação seja realizada concorrentemente em uma thread separada — mas call permite que a thread retorne um valor ou lance uma exceção verificada. • O método ExecutorService submit executa um Callable passado como seu argumento. • O método submit retorna um objeto do tipo Future (do pacote java.util.concurrent) que representa Callable em execução. A interface Future declara o método get para retornar o resultado de Callable e fornece outros métodos para gerenciar a execução de Callable.

Terminologia adiamento indefinido, 808 adquirir o bloqueio, 812 agendamento de rodízio, 807 agendamento preemptivo, 808 agendando threads, 806 await, método da interface Condition, 836 awaitTermination, método da interface ExecutorService, 814 BlockingQueue, interface, 823 bloqueio de monitor, 812 bloqueio intrínseco, 812 buffer, 818 buffer circular, 830 buffer limitado, 830 call, método da interface Callable, 851 Callable, interface, 851 cancel, método da classe SwingWorker, 850 condição, objeto, 836 Condition, interface, 836 confinamento de thread, 840 consumidor, 818 Crivo de Eratóstenes, 847 dados mutáveis, 817 dependente de estado, 818 despachar uma thread, 806 diretiva de imparcialidade de um bloqueio, 835 espera sincronizada, estado, 806 estado bloqueado, 806 estado de espera, 806 estado de execução, 806 estado executável, 805 estado morto, 806 estado novo, 805 estado pronto, 806 estado terminado, 806 exclusão mútua, 812

execute, método do Executor interface, 810

quantum, 806

Executor, interface, 810

ReentrantLock, classe, 835

Executors, classe, 810

run, método da interface Runnable, 808

ExecutorService, interface, 810 fila de prioridade de múltiplos níveis, 808 fração de tempo, 806 Future, interface, 851 get, método da interface Future, 851 guardando código com um bloqueio, 812 IllegalMonitorStateException, classe, 825 impasse, 836 inanição, 808 interrupt, método da classe Thread, 809 InterruptedException, classe, 809 intervalo de sono, 806 isCancelled, método da classe SwingWorker, 847 Lock, interface, 835 lock, método da interface Lock, 835 monitor, 812 multithreading, 804 newCachedThreadPool, método da classe Executors, 810 newCondition, método da interface Lock, 836 notify, método da classe Object, 825 notifyAll, método da classe Object, 825 operação atômica, 816 operações concorrentes, 804 operações paralelas, 804 pool de threads, 810 prioridade de thread, 807 produtor, 818 produtor/consumidor, relacionamento, 818 programação concorrente, 805 PropertyChangeListener, interface, 850 put, método da interface BlockingQueue, 823

Runnable, interface, 808 scheduler de thread, 807 segurança de thread, 815 shutdown, método da classe ExecutorService, 811 signal, método da interface Condition, 836 signalAll, método da interface Condition, 836 sincronizar threads, 805 size, método da classe ArrayBlockingQueue, 824 sleep, método da classe Thread, 809 spooling de impressão, 818 submit, método da classe ExecutorService, 851 SwingWorker, classe, 841 synchronized, instrução, 812 synchronized, método, 812 synchronized, palavra-chave, 812 take, método da classe BlockingQueue, 823 thread, agendamento, 806 Thread, classe, 807 thread, sincronização, 812 thread adormecida, 806 thread consumidor, 818 thread de despacho de evento (event-dispatch thread — EDT), 840 thread principal, 810 thread produtor, 818 threads de execução, 804 unlock, método da interface Lock, 835 valor obsoleto, 815 wait, método da classe Object, 825

856

Capítulo 26  Multithreading

Exercícios de autorrevisão 26.1 Preencha as lacunas em cada uma das seguintes afirmações: a) C e C++ são linguagens de ________ enquanto o Java é uma linguagem de ________. b) Uma thread entra no estado terminado quando ________. c) Para pausar um número designado de milissegundos e retomar a execução, uma thread deve chamar o método ________ da classe ________. d) O método ________ da classe Condition move uma única thread no estado de espera de um objeto para o estado executável. e) O método ________ da classe Condition move toda thread no estado de espera de um objeto para o estado executável. f) Uma thread ________ entra no estado ________ quando ela completa sua tarefa ou, de outro modo, termina. g) Uma thread executável pode entrar no estado ________ por um intervalo especificado de tempo. h) No nível do sistema operacional, o estado executável realmente inclui dois estados separados: ________ e ________. i) Runnables são executadas utilizando uma classe que implementa a interface ________. j) O método ExecutorService ________ termina cada thread em um ExecutorService logo que termina de executar sua Runnable atual, se houver alguma. k) Uma thread pode chamar o método ________ em um objeto Condition para liberar o Lock associado e colocar essa thread no estado ________. l) Em um relacionamento________, o(a) ________ gera dados e os armazena em um objeto compartilhado, e o(a) ________ lê os dados do objeto compartilhado. m) A classe ________ implementa a interface BlockingQueue que utiliza um array. n) A palavra-chave ________ indica que somente uma thread por vez deve executar em um objeto.

26.2 Determine se cada uma das seguintes afirmações é verdadeira ou falsa. Se falsa, explique por quê. a) Uma thread não é executável se tiver terminado. b) Alguns sistemas operacionais utilizam fracionamento de tempo com threads. Portanto, eles podem permitir os threads fazer preempção de threads da mesma prioridade. c) Quando o quantum da thread expira, a thread retorna ao estado de execução enquanto o sistema operacional a atribui a um processador. d) Em um sistema de um único processador sem fracionamento de tempo, cada thread em um conjunto de threads de igual prioridade (sem outras threads presentes) executa até a conclusão antes de outras threads de igual prioridade obterem uma chance de executar.

Respostas dos exercícios de autorrevisão 26.1 26.2

a) uma única thread, múltiplas threads. b) seu método run é encerrado. c) sleep, Thread. d) signal. e) signalAll. f) executável, terminado. g) espera sincronizada. h) pronto, em execução. i) Executor. j) shutdown. k) await, em espera. l) produtor/consumidor, produtora, consumidora. m) ArrayBlockingQueue. n) synchronized. a) Verdadeira b) Falsa. Fracionamento de tempo permite uma thread executar até que sua fração de tempo (ou quantum) expire. Então outras threads de igual prioridade podem executar. c) Falsa. Quando o quantum de uma thread expira, a thread retorna ao estado pronto e o sistema operacional atribui ao processador outra thread. d) Verdadeira.

Exercícios 26.3 Determine se cada uma das seguintes alternativas é verdadeira ou falsa. Se falsa, explique por quê. a) O método sleep não consome tempo de processador enquanto uma thread dorme. b) Declarar um método synchronized garante que o impasse não ocorra. c) Uma vez que um ReentrantLock foi obtido por uma thread, o objeto ReentrantLock não permitirá que outra thread obtenha o bloqueio até que a primeira thread libere-o. d) Os componentes Swing são seguros para thread.

26.4 Defina cada um dos seguintes termos. a) thread b) multithreading c) estado executável d) estado de espera sincronizada e) agendamento preemptivo f) interface Runnable g) método notifyAll h) produtor/consumidor, relacionamento i) quantum



Exercícios

857

26.5 Discuta cada um dos seguintes termos no contexto de mecanismos de thread do Java: a) synchronized b) produtor c) consumidor d) wait e) notify f) Lock g) Condition

26.6 Liste as razões para entrar no estado bloqueado. Para cada uma delas, descreva como o programa normalmente deixará o estado bloqueado e entrará no estado executável.

26.7 Dois problemas que podem ocorrer em sistemas que permitem às threads esperar são os impasses, em que uma ou mais threads esperarão eternamente por um evento que pode não ocorrer, e o adiamento indefinido, em que uma ou mais threads serão retardadas por um tempo imprevisivelmente longo. Dê um exemplo de como cada um desses problemas podem ocorrer em programas Java de múltiplas threads.

26.8 (Rebatendo a bola) Escreva um programa que faz uma bola azul rebater dentro de um JPanel. A bola deve começar a se mover com um evento mousePressed. Quando a bola atingir a borda do JPanel, ela deve rebater fora da borda e continuar na direção oposta. A bola deve ser atualizada com uma interface Runnable.

26.9 (Rebatendo as bolas) Modifique o programa no Exercício 26.8 para adicionar uma nova bola toda vez que o usuário clicar no mouse. Ofereça um mínimo de 20 bolas. Escolha a cor para cada nova bola aleatoriamente.

26.10 (Bolas que rebatem com sombras) Modifique o programa do Exercício 26.9 para adicionar sombras. À medida que uma bola se mover, desenhe uma oval sólida preta na parte inferior do JPanel. Você pode considerar a adicionar um efeito 3-D aumentando ou diminuindo o tamanho de cada bola quando ela atingir a borda do JPanel.

26.11 (Buffer circular com Locks e Conditions) Reimplemente o exemplo da Seção 26.9 utilizando os conceitos Lock e Condition apresentados na Seção 26.10.

27

Se a presença da eletricidade pode ser tornada visível em qualquer parte de um circuito, não vejo razão por que a inteligência não possa ser transmitida instantaneamente pela eletricidade. — Samuel F. B. Morse

Protocolo é tudo. — François Giuliani

O que as redes de ferrovias, rodovias e canais foram em outra era, as redes de telecomunicações, informações e computação ... são hoje em dia. — Bruno Kreisky

The port is near, the bells I hear, the people all exulting. [A porta está próxima, ouço os sinos, o povo está exultante.] — Walt Whitman

Redes

Objetivos Neste capítulo, você aprenderá: 

A entender a tecnologia de redes do Java com URLs, sockets e datagramas.



A implementar aplicativos de rede de Java utilizando sockets e datagramas.



A entender como implementar clientes e servidores Java que se comunicam entre si.



A entender como implementar aplicativos colaborativos baseados em rede.



A construir um servidor multiencadeado.

27.1 Introdução

Sumário

27.1 Introdução 27.2 Manipulando URLs 27.3 Lendo um arquivo em um servidor da Web 27.4 Estabelecendo um servidor simples utilizando sockets de fluxo 27.5 Estabelecendo um cliente simples utilizando sockets de fluxo

859

27.6 Interação cliente/servidor com conexões de socket de fluxo 27.7 Interação cliente/servidor sem conexão com datagramas 27.8 Jogo da velha cliente/servidor que utiliza um servidor com multithread 27.9 [Bônus Web] Estudo de caso: Servidor e Cliente DeitelMessenger

27.10 Conclusão

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios

27.1 Introdução Há um grande entusiasmo com a Internet e a World Wide Web. A internet une o mundo da informação. A World Wide Web torna a Internet fácil de utilizar e se beneficia da “onda” multimídia. As organizações consideram a Internet e a Web como cruciais para suas estratégias na área de sistemas de informações. O Java fornece diversas capacidades de rede predefinidas que tornam fácil desenvolver aplicativos com base na Internet e na Web. O Java pode permitir que programas pesquisem informações no mundo e colaborem com programas que são executados em outros computadores internacionalmente, nacionalmente ou simplesmente em uma empresa. O Java pode permitir que applets e aplicativos se comuniquem entre si (sujeito a restrições de segurança). Os recursos fundamentais das redes em Java são declarados pelas classes e interfaces do pacote java.net, por meio do qual o Java oferece comunicações baseadas em fluxo que permitem aos aplicativos visualizar as redes como fluxos de dados. As classes e interfaces do pacote java.net também oferecem comunicações baseadas em pacotes para transmitir pacotes individuais de informações — comumente utilizados para transmitir dados, imagens, áudio e vídeo pela Internet. Neste capítulo, mostramos como criar e manipular sockets e como se comunicar com pacotes e fluxos de dados. Focalizamos os dois lados do relacionamento cliente/servidor. O cliente solicita que alguma ação seja realizada e o servidor realiza a ação e responde para o cliente. Uma implementação comum do modelo de solicitação e resposta é aquela encontrada nos navegadores e servidores Web. Quando um usuário seleciona um site da Web para navegar com seu navegador (o aplicativo cliente), uma solicitação é enviada para o servidor da Web apropriado (o aplicativo servidor). O servidor normalmente responde para o cliente enviando uma página Web apropriada. Introduzimos as comunicações baseadas em socket do Java, que permitem aos aplicativos visualizar a rede como se fosse uma E/S de arquivo — um programa pode ler um socket ou gravar em um socket tão simplesmente quanto lê de um arquivo ou grava em um arquivo. O socket é simplesmente uma construção de software que representa uma extremidade final de uma conexão. Mostramos como criar e manipular sockets de fluxo e sockets de datagrama. Com sockets de fluxo, um processo estabelece uma conexão com outro processo. Enquanto a conexão estiver no ar, os dados fluem entre os processos em fluxos contínuos. Dizemos que os sockets de fluxo fornecem um serviço orientado para conexão. O protocolo utilizado para transmissão é o popular TCP (Transmission Control Protocol). Com sockets de datagrama, são transmitidos pacotes individuais de informações. Isso não é apropriado para programadores rotineiros, porque o protocolo utilizado — UDP, o User Datagram Protocol — é um serviço sem conexão e, assim, não garante que pacotes cheguem em uma ordem particular. Com o UDP, os pacotes podem até mesmo ser perdidos ou duplicados. É necessária uma quantidade significativa de programação extra de sua parte para lidar com esses problemas (se optar por fazer isso). O UDP é mais apropriado para aplicativos de rede que não requerem verificação de erros e confiabilidade do TCP. Os sockets de fluxo e o protocolo TCP serão os mais desejáveis para ampla maioria dos programadores Java.

Dica de desempenho 27.1 Serviços sem conexão geralmente oferecem desempenho maior, mas menos confiabilidade que os serviços orientados para conexão.

Dica de portabilidade 27.1 O TCP, UDP e protocolos relacionados permitem que sistemas de computadores heterogêneos (isto é, sistemas de computadores com diferentes processadores e diferentes sistemas operacionais) se intercomuniquem.

860

Capítulo 27

Redes

Apresentamos um estudo de caso que implementa um aplicativo de chat (“bate-papo”) cliente/servidor semelhante aos serviços de sistema de troca de mensagens instantâneas populares na Web hoje em dia. Esse estudo de caso é fornecido em inglês como um bônus Web em www.deitel.com/books/jhtp8/. O aplicativo incorpora muitas técnicas para redes introduzidas neste capítulo. Ele também introduz o multicasting, com o qual um servidor pode publicar informações e muitos clientes podem assiná-las. Toda vez que o servidor publica informações adicionais, todos os assinantes recebem essas informações. Por todos os exemplos deste capítulo, veremos que vários detalhes das redes são tratados pelas Java APIs.

27.2 Manipulando URLs [Nota: O exemplo nesta seção requer um pouco de conhecimento de XHTML. Para aprender mais sobre a XHTML, visite nosso XHTML Resource Center em www.deitel.com/xhtml/. Talvez você também esteja interessado na capacidade de formatação de páginas Web conhecida como Cascading Style Sheets (CSS). Para informações adicionais, visite nosso CSS Resource Center em www.deitel.com/ CSS21/.] A Internet oferece muitos protocolos. O HyperText Transfer Protocol (HTTP), que forma a base da World Wide Web, utiliza URIs (Uniform Resource Identifiers) para identificar dados na Internet. URIs que especificam as localizações dos documentos são chamados URLs (Uniform Resource Locators). URLs comuns fazem referência a arquivos ou diretórios e podem também fazer referências a objetos que realizam tarefas complexas, como pesquisas no banco de dados e pesquisas na Internet. Se conhecer o URL de uma página Web publicamente disponível, você poderá acessá-la por meio do HTTP. O Java torna fácil manipular os URLs. Ao utilizar um URL que faz referência à localização exata de um recurso (por exemplo, uma página Web) como um argumento para o método showDocument da interface AppletContext, o navegador em que o applet está executando acessará e exibirá esse recurso. O applet nas figuras 27.1–27.2 demonstra capacidades simples de rede. Ele permite que o usuário selecione uma página Web a partir de uma JList e faz com que o navegador exiba a página correspondente. Neste exemplo, a conexão de rede é realizada pelo navegador. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Site Selector









Figura 27.1 | O documento XHTML para carregar o applet SiteSelector. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

// Figura 27.2: SiteSelector.java // Carregando um documento de um URL em um navegador. import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.ArrayList; import java.awt.BorderLayout; import java.applet.AppletContext; import javax.swing.JApplet; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JScrollPane; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; public class SiteSelector extends JApplet { private HashMap< String, URL > sites; // nomes e URLs de site

19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

27.2  Manipulando URLs private ArrayList< String > siteNames; // nomes de site private JList siteChooser; // lista de sites a escolher // lê os parâmetros e configura a GUI public void init() { sites = new HashMap< String, URL >(); // cria HashMap siteNames = new ArrayList< String >(); // cria a ArrayList // obtém os parâmetros do documento XHTML getSitesFromHTMLParameters(); // cria componentes GUI e a interface de layout add( new JLabel( "Choose a site to browse" ), BorderLayout.NORTH ); siteChooser = new JList( siteNames.toArray() ); // preenche a JList siteChooser.addListSelectionListener( new ListSelectionListener() // classe interna anônima { // vai ao site selecionado pelo usuário public void valueChanged( ListSelectionEvent event ) { // obtém o nome do site selecionado Object object = siteChooser.getSelectedValue(); // utiliza o nome do site para localizar o URL correspondente URL newDocument = sites.get( object ); // obtém o contêiner de applets AppletContext browser = getAppletContext(); // instrui o contêiner de applets a mudar as páginas browser.showDocument( newDocument ); } // fim do método valueChanged } // fim da classe interna anônima ); // fim da chamada para addListSelectionListener add( new JScrollPane( siteChooser ), BorderLayout.CENTER ); } // fim do método init // obtém os parâmetros do documento XHTML private void getSitesFromHTMLParameters() { String title; // título do site String location; // localização do site URL url; // URL da localização int counter = 0; // conta número de sites title = getParameter( "title" + counter ); // obtém o primeiro título do site // faz um loop até que não haja mais parâmetros no documento XHTML while ( title != null ) { // obtém a localização do site location = getParameter( "location" + counter ); try // coloca título/URL no HashMap e título na ArrayList { url = new URL( location ); // converte a localização em URL sites.put( title, url ); // coloca título/URL no HashMap siteNames.add( title ); // coloca o título na ArrayList } // fim do try catch ( MalformedURLException urlException ) { urlException.printStackTrace(); } // fim do catch ++counter; title = getParameter( "title" + counter ); // obtém o próximo título do site

861

862 88 89 90

Capítulo 27  Redes } // fim do while } // fim do método getSitesFromHTMLParameters } // fim da classe SiteSelector

Figura 27.2  |  Carregando um documento de um URL em um navegador.

Processando parâmetros de applet Esse applet tira proveito dos parâmetros de applet especificados no documento XHTML que invoca o applet. Ao navegar pela World Wide Web, você irá frequentemente se deparar com applets que são de domínio público — você pode utilizá-los gratuitamente em suas próprias páginas Web (normalmente em troca do reconhecimento da autoria do applet). Muitos applets podem ser personalizados via parâmetros fornecidos no arquivo XHTML que invoca o applet. Por exemplo, a Figura 27.1 contém o XHTML que invoca o applet SiteSelector na Figura 27.2. O documento XHTML contém oito parâmetros especificados com o param element — essas linhas devem aparecer entre os tags ap­ plet de abertura e fechamento. O applet pode ler e utilizar esses valores para se personalizar. Qualquer número de elementos param pode aparecer entre os tags applet inicial e final. Cada parâmetro tem um nome e um valor únicos. O método Applet getParameter retorna o value associado a um nome de parâmetro específico como uma String. O argumento passado para getParameter é uma String contendo o nome do parâmetro no elemento param. Nesse exemplo, os parâmetros representam o título e a localização de cada site da Web que o usuário pode selecionar. Os parâmetros especificados para esse applet são nomeados title#, onde o valor de # inicia em 0 e incrementa por 1 para cada novo título. Cada título deve ter um parâmetro de localização correspondente na forma location#, onde o valor de # inicia em 0 e incrementa por 1 para cada nova localização. A instrução String title = getParameter( "title0" );

obtém o valor associado com o parâmetro "title0" e o atribui à referência title. Se não houver um tag param que contém o parâmetro especificado, getParameter retornará null.

Armazenando nomes e URLs de sites Web O applet (Figura 27.2) obtém no documento XHTML (Figura 27.1) as opções que serão exibidas na JList do applet. A classe SiteSe­ lector utiliza um HashMap (pacote java.util) para armazenar os nomes e URLs de sites da Web. Neste exemplo, a chave é a String na JList que representa o nome do site da Web e o valor é um objeto URL que armazena a localização do site que será exibido no navegador. A classe SiteSelector também contém uma ArrayList (pacote java.util) em que os nomes de site são colocados de modo que possam ser usados para inicializar a JList (uma versão do construtor JList recebe um array de Objects que é retornado pelo método ArrayList toArray). Uma ArrayList é um array dinamicamente redimensionável de referências. A classe ArrayList fornece o método add para adicionar um novo elemento ao final da ArrayList. (ArrayList e HashMap foram discutidos no Capítulo 20.) As linhas 25–26 no método init do applet (linhas 23–57) criam um objeto HashMap e um objeto ArrayList. A linha 29 chama nosso método utilitário getSitesFromHTMLParameters (declarado nas linhas 60–89) para obter os parâmetros XHTML no documento XHTML que invocou o applet.

27.3 Lendo um arquivo em um servidor da Web

863

O método getSitesFromHTMLParameters utiliza o método Applet getParameter (linha 67) para obter o título de um site Web. Se title não for null, as linhas 73–87 são executadas. A linha 73 utiliza o método Applet getParameter para obter a localização do site da Web. A linha 77 utiliza location como o valor de um novo objeto URL. O construtor URL determina se seu argumento representa um URL válido. Se não representar, o construtor URL lança uma MalformedURLException. Observe que o construtor URL deve ser chamado em um bloco try. Se o construtor URL gerar uma MalformedURLException, a chamada a printStackTrace (linha 83) faz com que o programa mostre um rastreamento de pilha no console Java. Em máquinas Windows, o console Java pode ser visualizado dando um clique com botão direito do mouse no ícone Java na área de notificação da barra de tarefas. Em outras plataformas, isso é tipicamente acessível por meio de um ícone de área de trabalho. Então, o programa tenta obter o próximo título de site da Web. O programa não adiciona o site do URL inválido ao HashMap, portanto o título não será exibido na JList. Para um URL apropriado, a linha 78 insere title e URL no HashMap, e a linha 79 adiciona title a ArrayList. A linha 87 obtém o próximo título do documento XHTML. Quando a chamada a getParameter na linha 87 retorna null, o loop termina.

Criando a GUI do applet Quando o método getSitesFromHTMLParameters retorna a init, as linhas 32–56 constroem a GUI do applet. A linha 32 adiciona o JLabel Choose a site to browse ao NORTH do BorderLayout do JApplet. A linha 34 cria JList siteChooser para permitir que o usuário selecione uma página da Web para visualizar. As linhas 35–54 registram um ListSelectionListener para tratar os eventos JList. A linha 56 adiciona siteChooser ao CENTER do BorderLayout do JFrame. Processando uma seleção de usuário Quando o usuário seleciona um dos sites Web listados em siteChooser, o programa chama o método valueChanged (linhas 39– 52). A linha 42 obtém o nome do sites selecionados na JList. A linha 45 passa o nome do site selecionado (a chave) para o método HashMap get, que localiza e retorna uma referência ao objeto URL correspondente (o valor) atribuído à referência newDocument. A linha 48 utiliza o método Applet getAppletContext para obter uma referência a um objeto AppletContext que representa o contêiner de applets. A linha 51 utiliza a referência AppletContext browser para invocar o método showDocument, que recebe um objeto URL como um argumento e o passa para o AppletContext (isto é, o navegador). O navegador exibe na janela de navegador atual o recurso associado com esse URL. Neste exemplo, todos os recursos são documentos XHTML. Especificando o quadro-alvo para o método showDocument Uma segunda versão do método AppletContext showDocument permite a um applet especificar o chamado frame-alvo em que o recurso da Web será exibido. Essa segunda versão recebe dois argumentos — um objeto URL que especifica o recurso a ser exibido e uma String que representa o frame-alvo. Há alguns frames especiais de alvo que podem ser utilizados como o segundo argumento. O frame-alvo _blank resulta em uma nova janela de navegador Web para exibir o conteúdo do URL especificado. O frame-alvo _self determina que o conteúdo do URL especificado deve ser exibido no mesmo frame do applet (nesse caso, a página XHTML do applet é substituída). O frame-alvo _top especifica que o navegador deve remover os frames atuais da janela do navegador e, então, exibir o conteúdo do URL especificado na janela atual. Dica de prevenção de erro 27.1 O applet na Figura 27.2 deve ser executado a partir de um navegador Web, como o Mozilla ou o Microsoft Internet Explorer, para ver os resultados da exibição de outra página Web. O appletviewer é capaz de executar somente applets — ele ignora todos os outros tags de XHTML. Se os sites da Web no programa contivessem applets Java, somente esses applets apareceriam no appletviewer quando o usuário selecionasse um site. Cada applet executaria em uma janela appletviewer separada.

27.3 Lendo um arquivo em um servidor da Web Nosso próximo exemplo mais uma vez oculta os detalhes de conexão de rede. O aplicativo na Figura 27.3 utiliza o componente GUI Swing JEditorPane (do pacote javax.swing) para exibir o conteúdo de um arquivo em um servidor da Web. O usuário insere um URL no JTextField na parte superior da janela e o aplicativo exibe o documento correspondente (se existir) no JEditorPane. A classe JEditorPane é capaz de renderizar texto simples e texto formatado em XHTML, como ilustrado nas duas capturas de tela (Figura 27.4); assim, esse aplicativo atua como um simples navegador Web. O aplicativo também demonstra como processar HyperlinkEvents quando o usuário clica em um hiperlink do documento XHTML. As técnicas mostradas nesse exemplo também podem ser utilizadas em applets. Entretanto, um applet tem permissão para ler arquivos somente no servidor a partir do qual foi feito o download. [Nota: Esse programa talvez não funcione se o navegador Web precisar acessar a Web por meio de um servidor proxy. Se você criar um documento JNLP para esse programa e utilizar o Java Web Start para inicializá-lo, o Java Web Start utilizará as configurações do servidor proxy a partir do navegador Web padrão.]

864

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71

Capítulo 27  Redes

// Figura 27.3: ReadServerFile.java // Ler um arquivo abrindo uma conexão por meio de um URL. import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; public class ReadServerFile extends JFrame { private JTextField enterField; // JTextField para inserir o nome de site private JEditorPane contentsArea; // para exibir o site Web // configura a GUI public ReadServerFile() { super( "Simple Web Browser" ); // cria o enterField e registra seu ouvinte enterField = new JTextField( "Enter file URL here" ); enterField.addActionListener( new ActionListener() { // obtém o documento especificado pelo usuário public void actionPerformed( ActionEvent event ) { getThePage( event.getActionCommand() ); } // fim do método actionPerformed } // fim da classe inner ); // fim da chamada para addActionListener add( enterField, BorderLayout.NORTH ); contentsArea = new JEditorPane(); // cria a contentsArea contentsArea.setEditable( false ); contentsArea.addHyperlinkListener( new HyperlinkListener() { // se o usuário clicou no hyperlink, vai para a página especificada public void hyperlinkUpdate( HyperlinkEvent event ) { if ( event.getEventType() == HyperlinkEvent.EventType.ACTIVATED ) getThePage( event.getURL().toString() ); } // fim do método hyperlinkUpdate } // fim da classe inner ); // fim da chamada a addHyperlinkListener add( new JScrollPane( contentsArea ), BorderLayout.CENTER ); setSize( 400, 300 ); // configura o tamanho da janela setVisible( true ); // mostra a janela } // fim do construtor ReadServerFile // carrega o documento private void getThePage( String location ) { try // carrega o documento e exibe a localização { contentsArea.setPage( location ); // configura a página enterField.setText( location ); // configura o texto } // fim do try catch ( IOException ioException ) { JOptionPane.showMessageDialog( this, "Error retrieving specified URL", "Bad URL",



27.3  Lendo um arquivo em um servidor da Web

72 73 74 75

865

JOptionPane.ERROR_MESSAGE ); } // fim do catch } // fim do método getThePage } // fim da classe ReadServerFile

Figura 27.3  |  Lendo um arquivo ao abrir uma conexão por meio de um URL. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura 27.4: ReadServerFileTest.java // Cria e inicia um ReadServerFile. import javax.swing.JFrame; public class ReadServerFileTest { public static void main( String[] args ) { ReadServerFile application = new ReadServerFile(); application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); } // fim de main } // fim da classe ReadServerFileTest

Figura 27.4  |  Classe de teste para o ReadServerFile.

A classe de aplicativo ReadServerFile contém JTextField enterField, em que o usuário insere o URL do arquivo para ler e o JEditorPane contentsArea para exibir o conteúdo do arquivo. Quando o usuário pressiona a tecla Enter no enterField, o aplicativo chama o método actionPerformed (linhas 31–34). A linha 33 utiliza o método ActionEvent getActionCommand para obter a String que o usuário inseriu no JTextField e passa a String para o método utilitário getThePage (linhas 61–74). A linha 65 invoca o método JEditorPane setPage para fazer o download do documento especificado pela location e exibi-lo no JEditorPane. Se houver um erro ao fazer o download do documento, o método setPage lançará uma IOException. Além disso, se um URL inválido for especificado, uma MalformedURLException (uma subclasse de IOException) ocorrerá. Se o documento carregar com sucesso, a linha 66 exibe a localização atual no enterField. Tipicamente, um documento XHTML contém hiperlinks que, quando clicados, fornecem acesso rápido a outro documento na Web. Se um JEditorPane contiver um documento XHTML e o usuário clicar em um hiperlink, o JEditorPane irá gerar um HyperlinkEvent (pacote javax.swing.event) e notificará todos os HyperlinkListeners registrados (pacote javax.swing.event) desse evento. As linhas 42–53 registram um HyperlinkListener para tratar HyperlinkEvents. Quando um HyperlinkEvent ocorre, o programa chama o método hyperlinkUpdate (linhas 46–51). As linhas 48–49 utilizam o método HyperlinkEvent getEventType para determinar o tipo do HyperlinkEvent. A classe HyperlinkEvent contém uma classe aninhada public chamada EventType que declara três objetos static EventType, que representam os tipos de eventos de hiperlink. ACTIVATED indica que o usuário clicou em um hiperlink para mudar páginas Web, ENTERED indica que o usuário moveu o mouse sobre um hiperlink e EXITED indica que o usuário tirou o mouse de cima de um hiperlink. Se um hiperlink estiver ACTIVATED, a linha 50 utiliza o método HyperlinkEvent getURL para obter o URL representado pelo hiperlink. O método toString converte o URL retornado em uma String que pode ser passada ao método utilitário getThePage.

Observações sobre a aparência e comportamento 27.1 Um JEditorPane gera HyperlinkEvents somente se eles não forem editáveis.

866

Capítulo 27

Redes

27.4 Estabelecendo um servidor simples utilizando sockets de fluxo Os dois exemplos discutidos até agora utilizam recursos de alto nível de Java para redes a fim de se comunicarem entre aplicativos. Nos exemplos, não era sua responsabilidade estabelecer a conexão entre um cliente e um servidor. O primeiro programa contava com o navegador Web para se comunicar com um servidor Web. O segundo programa contava com um JEditorPane para realizar a conexão. Esta seção inicia nossa discussão sobre a criação dos seus próprios aplicativos que podem se comunicar entre si.

Passo 1: Cria um ServerSocket Estabelecer um servidor simples em Java requer cinco passos. O passo 1 é criar um objeto ServerSocket. Uma chamada ao construtor ServerSocket como ServerSocket server = new ServerSocket( númeroDePorta, comprimentoDaFila );

registra um número de porta TCP disponível e especifica um número máximo de clientes que podem esperar para se conectar ao servidor (isto é, o comprimento da fila). O número da porta é utilizado pelos clientes para localizar o aplicativo servidor no computador servidor. Isso costuma ser chamado ponto de handshake. Se a fila estiver cheia, o servidor recusará as conexões do cliente. O construtor estabelece a porta em que o servidor espera as conexões dos clientes — um processo conhecido como vincular o servidor à porta. Cada cliente solicitará para conectar-se ao servidor nessa porta. Somente um aplicativo por vez pode ser vinculado a uma porta específica no servidor.

Observação de engenharia de software 27.1 Os números de porta podem estar entre 0 e 65.535. A maioria dos sistemas operacionais reserva números de porta abaixo de 1.024 para serviços de sistema (por exemplo, correio eletrônico e servidores da World Wide Web). Geralmente, essas portas não devem ser especificadas como portas de conexão em programas de usuário. De fato, alguns sistemas operacionais requerem privilégios especiais de acesso para se vincularem a números de porta abaixo de 1.024.

Passo 2: Espera uma conexão Os programas gerenciam cada conexão de cliente com um objeto Socket. No Passo 2, o servidor passa a ouvir indefinidamente (ou bloquear) uma tentativa de conexão por um cliente. Para ouvir uma conexão de cliente, o programa chama o método ServerSocket accept, como em Socket connection = server.accept();

que retorna um Socket quando uma conexão com um cliente é estabelecida. O Socket permite ao servidor interagir com o cliente. Na verdade, as interações com o cliente ocorrem em uma porta diferente de servidor a partir do ponto de handshake. Isso permite que a porta especificada no Passo 1 seja utilizada novamente em um servidor de múltiplas threads para aceitar outra conexão de cliente. Demonstraremos esse conceito na Seção 27.8.

Passo 3: Obtém os fluxos de E/S do Socket O Passo 3 é obter os objetos OutputStream e InputStream que permitem ao servidor se comunicar com o cliente enviando e recebendo bytes. O servidor envia informações ao cliente via um OutputStream e recebe informações do cliente via um InputStream. O servidor invoca o método getOutputStream no Socket para obter uma referência ao OutputStream do Socket e invoca o método getInputStream no Socket para obter uma referência ao InputStream do Socket. Os objetos-fluxo podem ser utilizados para enviar ou receber bytes individuais ou sequências de bytes com o método write de OutputStream e com o método read de InputStream, respectivamente. Com frequência, é útil enviar ou receber valores de tipos primitivos (por exemplo, int e double) ou objetos Serializable (por exemplo, Strings ou outros tipos serializáveis) em vez de enviar bytes. Nesse caso, podemos utilizar as técnicas discutidas no Capítulo 17 para empacotar outros tipos de fluxo (por exemplo, ObjectOutputStream e ObjectInputStream) em torno do OutputStream e InputStream associados com o Socket. Por exemplo, ObjectInputStream input = new ObjectInputStream( connection.getInputStream() ); ObjectOutputStream output = new ObjectOutputStream( connection.getOutputStream() );

A beleza de estabelecer esses relacionamentos é que tudo o que o servidor grava no ObjectOutputStream é enviado via o OutputStream e está disponível no InputStream do cliente e que tudo o que o cliente grava em seu OutputStream (com um ObjectOutputStream correspondente) está disponível via o InputStream do servidor. A transmissão dos dados sobre a rede é transparência e é tratada completamente pelo Java.

27.5 Estabelecendo um cliente simples utilizando sockets de fluxo

867

Passo 4: Realiza o processamento O Passo 4 é a fase de processamento em que o servidor e o cliente comunicam-se via objetos OutputStream e InputStream. Passo 5: Fecha a conexão No Passo 5, quando a transmissão está completa, o servidor fecha a conexão invocando o método close nos fluxos e no Socket. Observação de engenharia de software 27.2 Com os sockets, a E/S de rede aparece para programas Java como similar à E/S de arquivo sequencial. Os sockets ocultam de você muito da complexidade da programação de rede.

Observação de engenharia de software 27.3 Um servidor com múltiplos threads pode receber o Socket retornado por cada chamada a accept e criar uma nova thread que gerencia a E/S de rede por meio desse Socket. Alternativamente, um servidor com múltiplas threads pode manter um pool de threads (um conjunto de threads existente) pronto para gerenciar a E/S de rede por meio dos novos Sockets à medida que são criados. Essas técnicas permitem que servidores multithread gerenciem várias conexões cliente simultâneas.

Dica de desempenho 27.2 Em sistemas de alto desempenho nos quais há memória abundante, um servidor multithread pode criar um grupo de threads que podem ser atribuídos rapidamente para tratar E/S de rede para novos Sockets à medida que eles são criados. Portanto, quando o servidor recebe uma conexão, ele não precisa incorrer no overhead da criação de thread. Quando a conexão é fechada, a thread é retornada ao pool para reutilização.

27.5 Estabelecendo um cliente simples utilizando sockets de fluxo Estabelecer um cliente simples em Java exige quatro passos.

Passo 1: Cria um Socket para conectar ao servidor No Passo 1, criamos um Socket para nos conectarmos ao servidor. O construtor Socket estabelece a conexão. Por exemplo, a instrução Socket connection = new Socket( endereçoDoServidor, porta );

utiliza o construtor de Socket com dois argumentos — o endereço do servidor (endereçoDoServidor) e o número da porta. Se a tentativa de conexão for bem-sucedida, essa instrução retorna um Socket. Uma tentativa de conexão que falha lança uma instância de uma subclasse de IOException, muitos programas simplesmente capturam IOException. Uma UnknownHostException ocorre especificamente quando o sistema não consegue converter o nome do servidor especificado na chamada ao construtor de Socket para um endereço IP correspondente.

Passo 2: Obtém os fluxos de E/S do Socket No Passo 2, o cliente utiliza os métodos getInputStream e getOutputStream de Socket para obter referências ao InputStream e OutputStream do Socket. Como mencionamos na seção anterior, podemos utilizar as técnicas do Capítulo 17 para empacotar outros tipos de fluxos no InputStream e OutputStream associado com o Socket. Se o servidor está enviando informações na forma de tipos reais, o cliente deve receber as informações no mesmo formato. Assim, se o servidor enviar valores com um ObjectOutputStream, o cliente deverá ler esses valores com um ObjectInputStream. Passo 3: Realiza o processamento O Passo 3 é a fase de processamento em que o cliente e o servidor comunicam-se via objetos InputStream e OutputStream. Passo 4: Fecha a conexão No Passo 4, o cliente fecha a conexão quando a transmissão está completa invocando o método close nos fluxos e no Socket. O cliente deve determinar quando o servidor termina o envio das informações de modo que possa chamar close para fechar a conexão no Socket. Por exemplo, o método InputStream read retorna o valor –1 quando detecta o fim de fluxo (também chamado EOF [end-of-file, fim do arquivo]). Se um ObjectInputStream ler as informações do servidor, uma EOFException ocorrerá quando o cliente tentar ler o valor de um fluxo em que o fim de fluxo for detectado.

868

Capítulo 27

Redes

27.6 Interação cliente/servidor com conexões de socket de fluxo As figuras 27.5 e 27.7 utilizam os sockets de fluxo, ObjectInputStream e ObjectOutputStream para demonstrar um aplicativo de chat client/server simples. O servidor espera uma tentativa de conexão do cliente. Quando um cliente se conecta ao servidor, o aplicativo servidor envia ao cliente um objeto String (lembre-se de que Strings são objetos Serializáveis) para indicar que a conexão foi bem-sucedida. Em seguida, o cliente exibe a mensagem. Os aplicativos servidores e clientes fornecem campos de texto que permitem ao usuário digitar uma mensagem e enviá-la a outro aplicativo. Quando o cliente ou o servidor envia a String "TERMINATE", a conexão é encerrada. Então o servidor espera o próximo cliente se conectar. A declaração da classe Server aparece na Figura 27.5. A declaração da classe Client aparece na Figura 27.7. As capturas de tela que mostram a execução entre o cliente e o servidor são mostradas na Figura 27.8.

Classe Server O construtor da classe Server (Figura 27.5, linhas 30–55) cria a GUI do servidor, que contém um JTextField e uma JTextArea. A classe Server exibe sua saída na JTextArea. Quando o método main (linhas 6–11 da Figura 27.6) executa, ele cria um objeto Server, especifica a operação padrão de fechamento de janela e chama o método runServer (Figura 27.5, linhas 57–86). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

// Figura 27.5: Server.java // A parte servidor de uma conexão cliente/servidor de socket de fluxo. import java.io.EOFException; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingUtilities; public class Server extends JFrame { private JTextField enterField; // insere a mensagem do usuário private JTextArea displayArea; // exibe informações para o usuário private ObjectOutputStream output; // gera fluxo de saída para o cliente private ObjectInputStream input; // gera fluxo de entrada a partir do cliente private ServerSocket server; // socket de servidor private Socket connection; // conexão com o cliente private int counter = 1; // contador do número de conexões // configura a GUI public Server() { super( "Server" ); enterField = new JTextField(); // cria enterField enterField.setEditable( false ); enterField.addActionListener( new ActionListener() { // envia a mensagem ao cliente public void actionPerformed( ActionEvent event ) { sendData( event.getActionCommand() ); enterField.setText( "" ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener add( enterField, BorderLayout.NORTH ); displayArea = new JTextArea(); // cria displayArea add( new JScrollPane( displayArea ), BorderLayout.CENTER );

51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119

27.6  Interação cliente/servidor com conexões de socket de fluxo

setSize( 300, 150 ); // configura o tamanho da janela setVisible( true ); // mostra a janela } // fim do construtor Server // configura e executa o servidor public void runServer() { try // configura o servidor para receber conexões; processa as conexões { server = new ServerSocket( 12345, 100 ); // cria ServerSocket while ( true ) { try { waitForConnection(); // espera uma conexão getStreams(); // obtém fluxos de entrada e saída processConnection(); // processa a conexão } // fim do try catch ( EOFException eofException ) { displayMessage( "\nServer terminated connection" ); } // fim do catch finally { closeConnection(); // fecha a conexão ++counter; } // fim de finally } // fim do while } // fim do try catch ( IOException ioException ) { ioException.printStackTrace(); } // fim do catch } // fim do método runServer // espera que a conexão chegue e então exibe informações sobre a conexão private void waitForConnection() throws IOException { displayMessage( "Waiting for connection\n" ); connection = server.accept(); // permite que servidor aceite a conexão displayMessage( "Connection " + counter + " received from: " + connection.getInetAddress().getHostName() ); } // fim do método waitForConnection // obtém fluxos para enviar e receber dados private void getStreams() throws IOException { // configura o fluxo de saída para objetos output = new ObjectOutputStream( connection.getOutputStream() ); output.flush(); // esvazia buffer de saída para enviar as informações de cabeçalho // configura o fluxo de entrada para objetos input = new ObjectInputStream( connection.getInputStream() ); displayMessage( "\nGot I/O streams\n" ); } // fim do método getStreams // processa a conexão com o cliente private void processConnection() throws IOException { String message = "Connection successful"; sendData( message ); // envia uma mensagem de conexão bem-sucedida // ativa enterField de modo que usuário do servidor possa enviar mensagens setTextFieldEditable( true ); do // processa as mensagens enviadas pelo cliente

869

870 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

Capítulo 27  Redes { try // lê e exibe a mensagem { message = ( String ) input.readObject(); // lê uma nova mensagem displayMessage( "\n" + message ); // exibe a mensagem } // fim do try catch ( ClassNotFoundException classNotFoundException ) { displayMessage( "\nUnknown object type received" ); } // fim do catch } while ( !message.equals( "CLIENT>>> TERMINATE" ) ); } // fim do método processConnection // fecha os fluxos e o socket private void closeConnection() { displayMessage( "\nTerminating connection\n" ); setTextFieldEditable( false ); // desativa enterField try { output.close(); // fecha o fluxo de saída input.close(); // fecha o fluxo de entrada connection.close(); // fecha o socket } // fim do try catch ( IOException ioException ) { ioException.printStackTrace(); } // fim do catch } // fim do método closeConnection // envia a mensagem ao cliente private void sendData( String message ) { try // envia o objeto ao cliente { output.writeObject( "SERVER>>> " + message ); output.flush(); // esvazia a saída para o cliente displayMessage( "\nSERVER>>> " + message ); } // fim do try catch ( IOException ioException ) { displayArea.append( "\nError writing object" ); } // fim do catch } // fim do método sendData // manipula a displayArea na thread de despacho de eventos private void displayMessage( final String messageToDisplay ) { SwingUtilities.invokeLater( new Runnable() { public void run() // atualiza a displayArea { displayArea.append( messageToDisplay ); // acrescenta a mensagem } // fim do método run } // fim da classe interna anônima ); // fim da chamada para SwingUtilities.invokeLater } // fim do método displayMessage // manipula o enterField na thread de despacho de eventos private void setTextFieldEditable( final boolean editable ) { SwingUtilities.invokeLater( new Runnable() { public void run() // configura a editabilidade do enterField {

189 190 191 192 193 194

27.6  Interação cliente/servidor com conexões de socket de fluxo

871

enterField.setEditable( editable ); } // fim do método run } // fim da classe inner ); // fim da chamada para SwingUtilities.invokeLater } // fim do método setTextFieldEditable } // fim da classe Server

Figura 27.5  |  A parte do servidor de uma conexão cliente/servidor de socket de fluxo. 1 2 3 4 5 6 7 8 9 10 11 12 13

// Figura 27.6: ServerTest.java // Testa o aplicativo servidor. import javax.swing.JFrame; public class ServerTest { public static void main( String[] args ) { Server application = new Server(); // cria o servidor application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); application.runServer(); // executa o aplicativo servidor } // fim de main } // fim da classe ServerTest

Figura 27.6  |  Classe de texte para Server.

Método runServer O método runServer (Figura 27.5, linhas 57–86) configura o servidor para receber uma conexão e processar uma conexão em um dado momento. A linha 61 cria um ServerSocket chamado server para esperar conexões. O ServerSocket ouve uma conexão de um cliente na porta 12345. O segundo argumento para o construtor é o número de conexões que podem esperar em uma fila para se conectar ao servidor (100 nesse exemplo). Se a fila estiver cheia quando um cliente tenta se conectar, o servidor recusará a conexão. Erro comum de programação 27.1 Especificar uma porta que já está em utilização ou especificar um número de porta inválido ao criar um ServerSocket resulta em uma BindException.

A linha 67 chama o método waitForConnection (declarado nas linhas 89–95) para esperar uma conexão do cliente. Depois que a conexão for estabelecida, a linha 68 chama o método getStreams (declarado nas linhas 98–108) para obter referências para os fluxos dessa conexão. A linha 69 chama o método processConnection (declarado nas linhas 111–132) para enviar a mensagem inicial de conexão ao cliente e processar todas as mensagens recebidas dele. O bloco finally (linhas 75–79) termina a conexão cliente chamando o método closeConnection (linhas 135–150), mesmo se uma exceção ocorrer. Esses métodos chamam displayMessage (linhas 168–179), que utiliza a thread de despacho de eventos para exibir mensagens em JTextArea do aplicativo. O método SwingUtilities invokeLater recebe um objeto Runnable como seu argumento e insere-o na thread de despacho de eventos para execução. Isso assegura que um componente GUI não seja modificado a partir de um thread além da thread de despacho de eventos, o que é importante uma vez que componentes da GUI do Swing não são seguros para threads. Utilizamos uma técnica semelhante no método setTextFieldEditable (linhas 182–193), para estabelecer a editabilidade de enterField. Para obter informações adicionais sobre a interface Runnable, consulte o Capítulo 26.

Método waitForConnection O método waitForConnection (linhas 89–95) utiliza o método ServerSocket accept (linha 92) para esperar uma conexão de um cliente. Quando ocorre uma conexão, o Socket resultante é atribuído a connection. O método accept bloqueia até que uma conexão seja recebida (isto é, a thread em que accept é chamado interrompe a execução até que um cliente se conecte). As linhas 93–94 geram a saída do nome do host do computador que fez a conexão. O método Socket getInetAddress retorna um InetAddress (pacote java. net) que contêm informações sobre o computador cliente. O método InetAddress getHostName retorna o nome do host do computador cliente. Por exemplo, há um endereço IP (127.0.0.1) e um nome de host (localhost) especial que é útil para testar aplicações de rede no seu computador local (isso também é conhecido como endereço de loopback). Se getHostName for chamado em um InetAddress contendo 127.0.0.1, o nome do host correspondente retornado pelo método será localhost. Método getStreams O método getStreams (linhas 98–108) obtém os fluxos de Socket e utiliza-os para inicializar um ObjectOutputStream (linha 101) e um ObjectInputStream (linha 105), respectivamente. Observe a chamada ao método ObjectOutputStream flush na linha

872

Capítulo 27  Redes

102. Essa instrução faz o ObjectOutputStream do servidor enviar um cabeçalho de fluxo para o correspondente ObjectInputStream do cliente. O cabeçalho de fluxo contém informações como a versão de serialização de objeto sendo utilizada para enviar os objetos. Essas informações são necessárias para ObjectInputStream poder se preparar para receber esses objetos corretamente.

Observação de engenharia de software 27.4 Ao utilizar ObjectOutputStream e ObjectInputStream para enviar e receber dados em uma conexão de rede, sempre crie primeiro o ObjectOutputStream e esvazie (flush) o fluxo de modo que o ObjectInputStream do cliente possa se preparar para receber os dados. Isso é necessário apenas para aplicativos de rede que se comunicam utilizando ObjectOutputStream e ObjectInputStream.

Dica de desempenho 27.3 Os componentes E/S do computador são geralmente muito mais lentos do que a memória do computador. Os buffers de saída são utilizados para aumentar a eficiência de um aplicativo enviando volumes maiores de dados menos vezes, reduzindo assim o número de vezes que um aplicativo acessa os componentes E/S do computador.

Método processConnection A linha 114 do método processConnection (linhas 111–132) chama o método sendData para enviar "SERVER>>> Connection successful" como uma String ao cliente. O loop nas linhas 119–131 executa até que o servidor receba a mensagem "CLIENT>>> TERMINATE". A linha 123 utiliza o método ObjectInputStream readObject para ler uma String a partir do cliente. A linha 124 invoca método displayMessage para acrescentar a mensagem ao JTextArea. Método closeConnection Quando a transmissão estiver completa, o método processConnection retorna, e o programa chama o método closeConnection (linhas 135–150) para fechar os fluxos associados com o Socket e fechar o Socket. Então, o servidor espera a próxima tentativa de conexão de um cliente continuando com a linha 67 no começo do loop while. Observe que Server recebe uma conexão, processa-a, fecha-a e espera a próxima conexão. Um cenário mais provável seria um Server que recebe uma conexão, configura essa conexão para ser processada como uma thread de execução separada e então imediatamente espera novas conexões. As threads separadas que processam conexões existentes podem continuar a executar enquanto o Server concentra-se em novas solicitações de conexão. Isso torna o servidor mais eficiente, porque múltiplas solicitações de clientes podem ser processadas concorrentemente. Demonstramos um servidor com múltiplas threads na Seção 27.8. Processando interações de usuário Quando o usuário do aplicativo servidor insere uma String no campo de texto e pressiona a tecla Enter, o programa chama o método actionPerformed (linhas 39–43), que lê a String do campo de texto e chama o método utilitário sendData (linhas 153–165) para enviar a String ao cliente. O método sendData grava o objeto, esvazia o buffer de saída e acrescenta a mesma String à área de texto na janela do servidor. Aqui não é necessário invocar displayMessage para modificar a área de texto, porque o método sendData é chamado a partir de um handler de evento — portanto, sendData executa como parte da thread de despacho de eventos. Classe Client Como ocorre com a classe Server, o construtor da classe Client (Figura 27.7, linhas 29–56) cria a GUI do aplicativo (um JTextField e uma JTextArea). Client exibe sua saída na área de texto. Quando método main (linhas 7–19 da Figura 27.8) executa, ele cria uma instância de classe Client, especifica a operação padrão de fechamento da janela e chama o método runClient (Figura 27.7, linhas 59–79). Neste exemplo, você pode executar o cliente a partir de qualquer computador na Internet e especificar o endereço IP ou nome de host do computador servidor como um argumento de linha de comando para o programa. Por exemplo, o comando java Client 192.168.1.15

tenta se conectar ao Server no computador com o endereço IP 192.168.1.15.

1 2 3 4 5 6 7 8

// Figura 27.7: Client.java // A parte do cliente de uma conexão com socket de fluxo entre cliente e servidor. import java.io.EOFException; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.InetAddress; import java.net.Socket;

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

27.6  Interação cliente/servidor com conexões de socket de fluxo import import import import import import import import

java.awt.BorderLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JFrame; javax.swing.JScrollPane; javax.swing.JTextArea; javax.swing.JTextField; javax.swing.SwingUtilities;

public class Client extends JFrame { private JTextField enterField; // insere informações fornecidas pelo usuário private JTextArea displayArea; // exibe informações para o usuário private ObjectOutputStream output; // gera o fluxo de saída para o servidor private ObjectInputStream input; // gera o fluxo de entrada a partir do servidor private String message = ""; // mensagem do servidor private String chatServer; // servidor de host para esse aplicativo private Socket client; // socket para comunicação com o servidor // inicializa chatServer e configura a GUI public Client( String host ) { super( "Client" ); chatServer = host; // configura o servidor ao qual esse cliente se conecta enterField = new JTextField(); // cria enterField enterField.setEditable( false ); enterField.addActionListener( new ActionListener() { // envia mensagem ao servidor public void actionPerformed( ActionEvent event ) { sendData( event.getActionCommand() ); enterField.setText( "" ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener add( enterField, BorderLayout.NORTH ); displayArea = new JTextArea(); // cria displayArea add( new JScrollPane( displayArea ), BorderLayout.CENTER ); setSize( 300, 150 ); // configura o tamanho da janela setVisible( true ); // mostra a janela } // fim do construtor Client // conecta-se ao servidor e processa as mensagens a partir do servidor public void runClient() { try // conecta-se ao servidor, obtém fluxos, processa a conexão { connectToServer(); // cria um Socket para fazer a conexão getStreams(); // obtém os fluxos de entrada e saída processConnection(); // processa a conexão } // fim do try catch ( EOFException eofException ) { displayMessage( "\nClient terminated connection" ); } // fim do catch catch ( IOException ioException ) { ioException.printStackTrace(); } // fim do catch finally { closeConnection(); // fecha a conexão

873

874 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146

Capítulo 27  Redes } // fim de finally } // fim do método runClient // conecta-se ao servidor private void connectToServer() throws IOException { displayMessage( "Attempting connection\n" ); // cria Socket fazer a conexão ao servidor client = new Socket( InetAddress.getByName( chatServer ), 12345 ); // exibe informações sobre a conexão displayMessage( "Connected to: " + client.getInetAddress().getHostName() ); } // fim do método connectToServer // obtém fluxos para enviar e receber dados private void getStreams() throws IOException { // configura o fluxo de saída para objetos output = new ObjectOutputStream( client.getOutputStream() ); output.flush(); // esvazia buffer de saída para enviar as informações de cabeçalho // configura o fluxo de entrada para objetos input = new ObjectInputStream( client.getInputStream() ); displayMessage( "\nGot I/O streams\n" ); } // fim do método getStreams // processa a conexão com o servidor private void processConnection() throws IOException { // ativa enterField de modo que o usuário cliente possa enviar mensagens setTextFieldEditable( true ); do // processa as mensagens enviadas do servidor { try // lê e exibe a mensagem { message = ( String ) input.readObject(); // lê uma nova mensagem displayMessage( "\n" + message ); // exibe a mensagem } // fim do try catch ( ClassNotFoundException classNotFoundException ) { displayMessage( "\nUnknown object type received" ); } // fim do catch } while ( !message.equals( "SERVER>>> TERMINATE" ) ); } // fim do método processConnection // fecha os fluxos e o socket private void closeConnection() { displayMessage( "\nClosing connection" ); setTextFieldEditable( false ); // desativa enterField try { output.close(); // fecha o fluxo de saída input.close(); // fecha o fluxo de entrada 1 client.close(); // fecha o socket } // fim do try catch ( IOException ioException ) { ioException.printStackTrace(); } // fim do catch } // fim do método closeConnection // envia mensagem ao servidor

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188

27.6  Interação cliente/servidor com conexões de socket de fluxo private void sendData( String message ) { try // envia o objeto ao servidor { output.writeObject( "CLIENT>>> " + message ); output.flush(); // esvazia os dados para saída displayMessage( "\nCLIENT>>> " + message ); } // fim do try catch ( IOException ioException ) { displayArea.append( "\nError writing object" ); } // fim do catch } // fim do método sendData // manipula a displayArea na thread de despacho de eventos private void displayMessage( final String messageToDisplay ) { SwingUtilities.invokeLater( new Runnable() { public void run() // atualiza a displayArea { displayArea.append( messageToDisplay ); } // fim do método run } // fim da classe interna anônima ); // fim da chamada para SwingUtilities.invokeLater } // fim do método displayMessage // manipula o enterField na thread de despacho de eventos private void setTextFieldEditable( final boolean editable ) { SwingUtilities.invokeLater( new Runnable() { public void run() // configura a editabilidade do enterField { enterField.setEditable( editable ); } // fim do método run } // fim da classe interna anônima ); // fim da chamada para SwingUtilities.invokeLater } // fim do método setTextFieldEditable } // fim da classe Client

Figura 27.7  |  A parte do cliente de uma conexão com socket de fluxo entre cliente e um servidor. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

// Figura 27.8: ClientTest.java // A classe que testa o Client. import javax.swing.JFrame; public class ClientTest { public static void main( String[] args ) { Client application; // declara o aplicativo cliente // se não houver if ( args.length application = else application =

nenhum argumento de linha de comando == 0 ) new Client( "127.0.0.1" ); // conecta-se ao host local new Client( args[ 0 ] ); // utiliza argumentos para se conectar

application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); application.runClient(); // executa o aplicativo cliente } // fim de main } // fim da classe ClientTest

875

876

Capítulo 27  Redes

Figura 27.8  |  A classe que testa o Client.

Método runClient O método Client runClient (Figura 27.7, linhas 59–79) configura a conexão com o servidor, processa as mensagens recebidas do servidor e fecha a conexão quando a comunicação estiver completa. A linha 63 chama o método connectToServer (declarado nas linhas 82–92) para realizar a conexão. Depois de conectar, a linha 64 chama o método getStreams (declarado nas linhas 95–105) para obter referências aos objetos fluxo do Socket. Em seguida, a linha 65 chama o método processConnection (declarado nas linhas 108–126) para receber e exibir as mensagens enviadas a partir do servidor. O bloco finally (linhas 75–78) chama closeConnection (linhas 129–144) para fechar os fluxos e o Socket mesmo se uma exceção ocorreu. O método displayMessage (linhas 162–173) é chamado a partir desses métodos para utilizar a thread de despacho de eventos para exibir as mensagens na área de texto do aplicativo. Método connectToServer O método connectToServer (linhas 82–92) cria um Socket chamado client (linha 87) para estabelecer uma conexão. O método passa dois argumentos para o construtor Socket — o endereço IP do computador servidor e o número da porta (12345) em que o aplicativo servidor espera conexões de clientes. No primeiro argumento, o método static getByName retorna um objeto InetAddress contendo o endereço IP especificado como um argumento de linha de comando para o aplicativo (ou 127.0.0.1 se nenhum argumento de linha de comando for especificado). O método getByName pode receber uma String contendo tanto o endereço IP real como o nome de host do servidor. O primeiro argumento também poderia ser escrito de outras maneiras. Para o endereço localhost 127.0.0.1, o primeiro argumento pode ser especificado com qualquer uma das expressões a seguir: InetAddress.getByName( "localhost" ) InetAddress.getLocalHost()

Além disso, há versões do construtor Socket que recebem o endereço IP ou o nome de host como uma String. O primeiro argumento poderia ter sido especificado como o endereço IP "127.0.0.1" ou o nome de host "localhost". Escolhemos demonstrar o relacionamento cliente/servidor fazendo conexões entre aplicativos no mesmo computador (localhost). Normalmente, esse primeiro argumento seria o endereço IP de outro computador. O objeto InetAddress para o outro computador pode ser obtido especificando o endereço IP do computador ou nome de host como o argumento para o método InetAddress getByName. O segundo argumento do construtor Socket é o número da porta do servidor. Esse número deve corresponder ao número de porta em que o servidor está esperando as conexões (chamado ponto de handshake). Depois que a conexão ocorre, as linhas 90–91 exibem uma mensagem na área de texto que indica o nome do computador servidor ao qual o cliente se conectou. O Client utiliza um ObjectOutputStream para enviar os dados para o servidor e um ObjectInputStream para receber os dados do servidor. O método getStreams (linhas 95–105) cria os objetos ObjectOutputStream e ObjectInputStream que utilizem os fluxos associados com o socket client.

Métodos processConnection e closeConnection O método processConnection (linhas 108–126) contém um loop que executa até que o cliente receba a mensagem "SERV­ ER>>> TERMINATE". A linha 117 lê um objeto String a partir do servidor. A linha 118 invoca displayMessage para acrescentar a mensagem à área de texto. Quando a transmissão está completa, o método closeConnection (linhas 129–144) fecha os fluxos e o Socket. Processando interações de usuário Quando o usuário do aplicativo cliente digita uma String no campo de texto e pressiona Enter, o programa chama o método action­ Performed (linhas 41–45) para ler a String e então invoca o método utilitário sendData (147–159) para enviar a String ao servidor. O método sendData grava o objeto, esvazia o buffer de saída e acrescenta a mesma String à JTextArea da janela cliente. Mais uma vez, aqui não é necessário invocar o método utilitário displayMessage para modificar a área de texto, porque o método sendData é chamado a partir de um handler de evento.

27.7 Interação cliente/servidor sem conexão com datagramas

877

27.7 Interação cliente/servidor sem conexão com datagramas Estávamos discutindo transmissão orientada para conexão e baseada em fluxo. Agora consideramos a transmissão sem conexão com datagramas. A transmissão orientada para conexão é como o sistema de telefonia em que você disca e recebe uma conexão ao telefone da pessoa com quem você deseja se comunicar. A conexão é mantida para sua chamada telefônica, mesmo quando você não está falando. A transmissão sem conexão com datagramas é mais parecida com a maneira como o correio é transportado via serviço postal. Se uma mensagem grande não couber em um envelope, você a dividirá em partes separadas e a colocará em envelopes numerados sequencialmente. Todas as cartas serão então remetidas de uma vez. As letras poderiam chegar na ordem, fora da ordem ou simplesmente não chegarem (este último caso é raro). A pessoa na extremidade receptora monta os pedaços em ordem sequencial antes de tentar dar sentido à mensagem. Se a sua mensagem for suficientemente pequena para caber em um envelope, você não precisa se preocupar com o problema “fora de sequência”, mas ainda é possível que sua mensagem talvez não chegue. Uma vantagem dos datagramas em relação ao correio postal é que duplicatas dos datagramas podem chegar ao computador receptor. As figuras 27.9–27.12 utilizam datagramas para enviar pacotes de informações via User Datagram Protocol (UDP) entre um aplicativo cliente e um aplicativo servidor. No aplicativo Client (Figura 27.11), o usuário digita uma mensagem em um campo de texto e pressiona Enter. O programa converte a mensagem em um array de bytes e a coloca em um pacote de datagrama que é enviado para o servidor. O Server (figuras 27.9–27.10) recebe o pacote e exibe as informações dele e, então, ecoa o pacote de volta para o cliente. Ao receber o pacote, o cliente exibe as informações que ele contém.

Classe Server A classe Server (Figura 27.9) declara dois DatagramPackets que o servidor utiliza para enviar e receber informações e um DatagramSocket que envia e recebe os pacotes. O construtor (linhas 19–37), que é chamado a partir de main (Figura 27.10, linhas 7–12), cria a GUI no qual os pacotes de informação serão exibidos. A linha 30 cria o DatagramSocket em um bloco try. A linha 30 na Figura 27.9 utiliza o construtor DatagramSocket que recebe um argumento de número inteiro para a porta (nesse exemplo, 5000) a fim de vincular o servidor a uma porta em que possa receber pacotes dos clientes. Clients que enviam pacotes para esse Server especificam o mesmo número da porta nos pacotes por eles enviados. Um SocketException é lançado se o construtor DatagramSocket não conseguir vincular o DatagramSocket à porta especificada. Erro comum de programação 27.2 Especificar uma porta que já está em utilização ou especificar um número de porta inválido ao criar um DatagramSocket resulta em uma SocketException.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

// Figura 27.9: Server.java // Lado do servidor da computação cliente/servidor sem conexão com datagramas. import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.SwingUtilities; public class Server extends JFrame { private JTextArea displayArea; // exibe os pacotes recebidos private DatagramSocket socket; // socket para conectar ao cliente // configura o DatagramSocket e a GUI public Server() { super( "Server" ); displayArea = new JTextArea(); // cria displayArea add( new JScrollPane( displayArea ), BorderLayout.CENTER ); setSize( 400, 300 ); // configura o tamanho da janela setVisible( true ); // mostra a janela try // cria DatagramSocket para envio e recebimento de pacotes {

878 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

Capítulo 27  Redes socket = new DatagramSocket( 5000 ); } // fim do try catch ( SocketException socketException ) { socketException.printStackTrace(); System.exit( 1 ); } // fim do catch } // fim do construtor Server // espera que os pacotes cheguem, exibe os dados e ecoa o pacote para o cliente public void waitForPackets() { while ( true ) { try // recebe o pacote, exibe o conteúdo, retorna uma cópia ao cliente { byte[] data = new byte[ 100 ]; // configura o pacote DatagramPacket receivePacket = new DatagramPacket( data, data.length ); socket.receive( receivePacket ); // espera receber o pacote // exibe informações a partir do pacote recebido displayMessage( "\nPacket received:" + "\nFrom host: " + receivePacket.getAddress() + "\nHost port: " + receivePacket.getPort() + "\nLength: " + receivePacket.getLength() + "\nContaining:\n\t" + new String(receivePacket.getData(), 0, receivePacket.getLength() ) ); sendPacketToClient( receivePacket ); // envia o pacote ao cliente } // fim do try catch ( IOException ioException ) { displayMessage( ioException + "\n" ); ioException.printStackTrace(); } // fim do catch } // fim do while } // fim do método waitForPackets // ecoa o pacote para o cliente private void sendPacketToClient( DatagramPacket receivePacket ) throws IOException { displayMessage( "\n\nEcho data to client..." ); // cria o pacote a enviar DatagramPacket sendPacket = new DatagramPacket( receivePacket.getData(), receivePacket.getLength(), receivePacket.getAddress(), receivePacket.getPort() ); socket.send( sendPacket ); // envia o pacote ao cliente displayMessage( "Packet sent\n" ); } // fim do método sendPacketToClient // manipula a displayArea na thread de despacho de eventos private void displayMessage( final String messageToDisplay ) { SwingUtilities.invokeLater( new Runnable() { public void run() // atualiza a displayArea { displayArea.append( messageToDisplay ); // exibe a mensagem } // fim do método run } // fim da classe interna anônima ); // fim da chamada para SwingUtilities.invokeLater } // fim do método displayMessage } // fim da classe Server

Figura 27.9  |  Lado do servidor da computação cliente/servidor sem conexão com datagramas.

1 2 3 4 5 6 7 8 9 10 11 12 13

27.7  Interação cliente/servidor sem conexão com datagramas

879

// Figura 27.10: ServerTest.java // A classe que testa o Server. import javax.swing.JFrame; public class ServerTest { public static void main( String[] args ) { Server application = new Server(); // cria o servidor application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); application.waitForPackets(); // executa o aplicativo servidor } // fim de main } // fim da classe ServerTest Janela Server depois que o pacote de dados foi recebido de Client

Figura 27.10  |  A classe que testa o Server.

Método waitForPackets O método Server waitForPackets (Figura 27.9, linhas 40–68) utiliza um loop infinito para esperar que os pacotes cheguem ao Server. As linhas 47–48 criam um DatagramPacket em que um pacote recebido de informações pode ser armazenado. O construtor Da­ tagramPacket para esse propósito recebe dois argumentos — um array de bytes em que os dados serão armazenados e o comprimento do array. A linha 50 utiliza o método DatagramSocket receive para esperar que um pacote chegue ao Server. O método receive bloqueia até que um pacote chegue e então armazena esse pacote no seu argumento DatagramPacket. O método lança uma IOException se um erro ocorrer ao receber um pacote. Método displayMessage Quando um pacote chega, as linhas 53–58 chamam o método displayMessage (declarado nas linhas 86–97) para acrescentar o conteúdo do pacote à área de texto. O método DatagramPacket getAddress (linha 54) retorna um objeto InetAddress contendo o endereço IP do computador do qual o pacote foi enviado. O método getPort (linha 55) retorna um inteiro que especifica o número da porta pela qual o computador cliente enviou o pacote. O método getLength (linha 56) retorna um inteiro que representa o número de bytes dos dados recebidos. O método getData (linha 57) retorna um array de bytes contendo os dados. As linhas 57–58 inicializam um objeto String utilizando um construtor de três argumentos que recebe um array de bytes, o deslocamento e o comprimento. Essa String é então acrescentada ao texto a ser exibido. Método sendPacketToClient Depois de exibir um pacote, a linha 60 chama o método sendPacketToClient (declarado nas linhas 71–83) para criar um novo pacote e enviá-lo ao cliente. As linhas 77–79 criam um DatagramPacket e passam quatro argumentos para seu construtor. O primeiro argumento especifica o array de bytes a ser enviado. O segundo argumento especifica o número de bytes a enviar. O terceiro argumento especifica o endereço IP do computador cliente para o qual o pacote será enviado. O quarto argumento especifica a porta na qual o cliente está esperando receber os pacotes. A linha 81 envia o pacote pela rede. O método send do DatagramSocket lança uma IOException se um erro ocorrer ao enviar um pacote. Classe Client A classe Client (figuras 27.11–27.12) funciona de maneira semelhante à classe Server, exceto que Client envia pacotes apenas quando o usuário digita uma mensagem em um campo de texto e pressiona a tecla Enter. Quando isso ocorre, o programa chama o método actionPerformed (Figura 27.11, linhas 32–57), que converte a String que o usuário inseriu em um array de bytes (linha 41). As linhas 44–45 criam um DatagramPacket e o inicializam com o array de bytes, o comprimento da String que foi inserido pelo usuário, o endereço IP ao qual o pacote deve ser enviado (InetAddress.getLocalHost(), nesse exemplo) e o número da porta em que o Server espera os pacotes (5000, nesse exemplo). A linha 47 envia o pacote. Observe que o cliente nesse exemplo deve saber que o servidor está recebendo pacotes na porta 5000 — caso contrário, o servidor não receberá os pacotes.

880

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

Capítulo 27  Redes

// Figura 27.11: Client.java // Lado cliente da computação cliente/servidor sem conexão com datagramas. import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingUtilities; public class Client extends JFrame { private JTextField enterField; // para inserir mensagens private JTextArea displayArea; // para exibir mensagens private DatagramSocket socket; // socket para conectar ao servidor // configura o DatagramSocket e a GUI public Client() { super( "Client" ); enterField = new JTextField( "Type message here" ); enterField.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent event ) { try // cria e envia o pacote { // obtém a mensagem no campo de texto String message = event.getActionCommand(); displayArea.append( "\nSending packet containing: " + message + "\n" ); byte[] data = message.getBytes(); // converte em bytes // cria sendPacket DatagramPacket sendPacket = new DatagramPacket( data, data.length, InetAddress.getLocalHost(), 5000 ); socket.send( sendPacket ); // envia o pacote displayArea.append( "Packet sent\n" ); displayArea.setCaretPosition( displayArea.getText().length() ); } // fim do try catch ( IOException ioException ) { displayMessage( ioException + "\n" ); ioException.printStackTrace(); } // fim do catch } // fim do método actionPerformed } // fim da classe inner ); // fim da chamada para addActionListener add( enterField, BorderLayout.NORTH ); displayArea = new JTextArea(); add( new JScrollPane( displayArea ), BorderLayout.CENTER ); setSize( 400, 300 ); // configura o tamanho da janela setVisible( true ); // mostra a janela try // cria DatagramSocket para envio e recebimento de pacotes {

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122

27.7  Interação cliente/servidor sem conexão com datagramas socket = new DatagramSocket(); } // fim do try catch ( SocketException socketException ) { socketException.printStackTrace(); System.exit( 1 ); } // fim do catch } // fim do construtor Client // espera que os pacotes cheguem do Server, exibe o conteúdo do pacote public void waitForPackets() { while ( true ) { try // recebe o pacote e exibe o conteúdo { byte[] data = new byte[ 100 ]; // configura o pacote DatagramPacket receivePacket = new DatagramPacket( data, data.length ); socket.receive( receivePacket ); // espera o pacote // exibe o conteúdo do pacote displayMessage( "\nPacket received:" + "\nFrom host: " + receivePacket.getAddress() + "\nHost port: " + receivePacket.getPort() + "\nLength: " + receivePacket.getLength() + "\nContaining:\n\t" + new String(receivePacket.getData(), 0, receivePacket.getLength() ) ); } // fim do try catch ( IOException exception ) { displayMessage( exception + "\n" ); exception.printStackTrace(); } // fim do catch } // fim do while } // fim do método waitForPackets

}

// manipula a displayArea na thread de despacho de eventos private void displayMessage( final String messageToDisplay ) { SwingUtilities.invokeLater( new Runnable() { public void run() // atualiza a displayArea { displayArea.append( messageToDisplay ); } // fim do método run } // fim da classe inner ); // fim da chamada para SwingUtilities.invokeLater } // fim do método displayMessage // fim da classe Client

Figura 27.11  |  Lado cliente da computação cliente/servidor sem conexão com datagramas. 1 2 3 4 5 6 7 8 9 10 11 12 13

// Figura 27.12: ClientTest.java // Testa a classe Client. import javax.swing.JFrame; public class ClientTest { public static void main( String[] args ) { Client application = new Client(); // cria o cliente application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); application.waitForPackets(); // executa o aplicativo cliente } // fim de main } // fim da classe ClientTest

881

882

Capítulo 27

Redes Janela Client depois de enviar pacotes para Server e receber pacotes de volta de Server

Figura 27.12 | A classe que testa o Client.

Observe que a chamada do construtor DatagramSocket (Figura 27.11, linha 71) nesse aplicativo não especifica quaisquer argumentos. Esse construtor sem argumento permite que o computador selecione o próximo número de porta disponível para o DatagramSocket. O cliente não precisa de um número específico de porta porque o servidor recebe o número de porta do cliente como parte de cada Data­ gramPacket enviado pelo cliente. Portanto, o servidor pode enviar pacotes de volta para o mesmo computador e número de porta do qual ele recebe um pacote de informações.

Método waitForPackets O método Client waitForPackets (linhas 81–107) utiliza um loop infinito para esperar os pacotes do servidor. A linha 91 bloqueia até que um pacote chegue. Isso não impede que o usuário envie um pacote, porque os eventos GUI são tratados na thread de despacho de eventos. Apenas impede que o loop while continue até um pacote chegar ao Client. Quando um pacote chega, a linha 91 armazena esse pacote em receivePacket e as linhas 94–99 chamam o método displayMessage (declarado nas linhas 110–121) para exibir o conteúdo do pacote na área de texto.

27.8 Jogo da velha cliente/servidor que utiliza um servidor com multithread Esta seção apresenta o popular jogo Tic-Tac-Toe, ou jogo da velha, implementado utilizando-se as técnicas cliente/servidor com sockets de fluxo. O programa consiste em um aplicativo TicTacToeServer (figuras 27.13–27.14) que permite que dois aplicativos TicTac­ ToeClient (figuras 27.15–27.16) se conectem ao servidor e joguem esse jogo. As saídas de exemplo são mostradas na Figura 27.17.

Classe TicTacToeServer À medida que a classe TicTacToeServer recebe cada conexão de cliente, ela cria uma instância da classe interna Player (Figura 27.13, linhas 182–304) para processar o cliente em uma thread separada. Essas threads permitem que os clientes joguem o jogo independentemente. O primeiro cliente a se conectar ao servidor é o jogador X e o segundo é o jogador O. O jogador X faz a primeira jogada. O servidor mantém as informações sobre o tabuleiro de modo que possa determinar se uma jogada é válida. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

// Figura 27.13: TicTacToeServer.java // Lado do servidor do programa Tic­Tac­Toe cliente/servidor. import java.awt.BorderLayout; import java.net.ServerSocket; import java.net.Socket; import java.io.IOException; import java.util.Formatter; import java.util.Scanner; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.Condition; import javax.swing.JFrame; import javax.swing.JTextArea; import javax.swing.SwingUtilities; public class TicTacToeServer extends JFrame { private String[] board = new String[ 9 ]; // tabuleiro do jogo da velha private JTextArea outputArea; // para gerar saída das jogadas

22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90

27.8  Jogo da velha cliente/servidor que utiliza um servidor com multithread private private private private private private private private private private

Player[] players; // array de Players ServerSocket server; // socket de servidor para conectar com clientes int currentPlayer; // monitora o jogador com a jogada atual final static int PLAYER_X = 0; // constante para o primeiro jogador final static int PLAYER_O = 1; // constante para o segundo jogador final static String[] MARKS = { "X", "O" }; // array de marcas ExecutorService runGame; // executará os jogadores Lock gameLock; // para bloquear a sincronização do jogo Condition otherPlayerConnected; // para esperar outro jogador Condition otherPlayerTurn; // para esperar a jogada do outro jogador

// configura o servidor de tic-tac-toe e a GUI que exibe as mensagens public TicTacToeServer() { super( "Tic-Tac-Toe Server" ); // configura o título da janela // cria ExecutorService com uma thread para cada jogador runGame = Executors.newFixedThreadPool( 2 ); gameLock = new ReentrantLock(); // cria um bloqueio para o jogo // variável de condição para os dois jogadores sendo conectados otherPlayerConnected = gameLock.newCondition(); // variável de condição para a jogada do outro jogador otherPlayerTurn = gameLock.newCondition(); for ( int i = board[ i ] players = new currentPlayer

0; i < 9; i++ ) = new String( "" ); // cria o tabuleiro de jogo da velha Player[ 2 ]; // cria array de jogadores = PLAYER_X; // configura o jogador atual como o primeiro jogador

try { server = new ServerSocket( 12345, 2 ); // configura ServerSocket } // fim do try catch ( IOException ioException ) { ioException.printStackTrace(); System.exit( 1 ); } // fim do catch outputArea = new JTextArea(); // cria JTextArea para saída add( outputArea, BorderLayout.CENTER ); outputArea.setText( "Server awaiting connections\n" ); setSize( 300, 300 ); // configura o tamanho da janela setVisible( true ); // mostra a janela } // fim do construtor TicTacToeServer // espera duas conexões para que o jogo possa ser jogado public void execute() { // espera que cada cliente se conecte for ( int i = 0; i < players.length; i++ ) { try // espera a conexão, cria Player, inicia o executável { players[ i ] = new Player( server.accept(), i ); runGame.execute( players[ i ] ); // executa o executável de jogador } // fim do try catch ( IOException ioException ) { ioException.printStackTrace(); System.exit( 1 ); } // fim do catch } // for final gameLock.lock(); // bloqueia o jogo para sinalizar a thread do jogador X

883

884 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

Capítulo 27  Redes try { players[ PLAYER_X ].setSuspended( false ); // retoma o jogador X otherPlayerConnected.signal(); // acorda a thread do jogador X } // fim do try finally { gameLock.unlock(); // desbloqueia o jogo depois de sinalizar para o jogador X } // fim de finally } // fim do método execute // exibe uma mensagem na outputArea private void displayMessage( final String messageToDisplay ) { // exibe uma mensagem a partir da thread de despacho de eventos da execução SwingUtilities.invokeLater( new Runnable() { public void run() // atualiza a outputArea { outputArea.append( messageToDisplay ); // adiciona mensagem } // fim do método run } // fim da classe inner ); // fim da chamada para SwingUtilities.invokeLater } // fim do método displayMessage // determina se a jogada é válida public boolean validateAndMove( int location, int player ) { // enquanto não for o jogador atual, deve esperar a jogada while ( player != currentPlayer ) { gameLock.lock(); // bloqueia o jogo para que o outro jogador prossiga try { otherPlayerTurn.await(); // espera a jogada do jogador } // fim do try catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch finally { gameLock.unlock(); // desbloqueia o jogo depois de esperar } // fim de finally } // fim do while // se a posição não estiver ocupada, faz a jogada if ( !isOccupied( location ) ) { board[ location ] = MARKS[ currentPlayer ]; // configura uma jogada no tabuleiro currentPlayer = ( currentPlayer + 1 ) % 2; // troca o jogador // deixa que novo jogador atual saiba que a jogada ocorreu players[ currentPlayer ].otherPlayerMoved( location ); gameLock.lock(); // bloqueia o jogo para sinalizar ao outro jogador a prosseguir try { otherPlayerTurn.signal(); // sinaliza que o outro jogador continue } // fim do try finally { gameLock.unlock(); // desbloqueia o jogo depois de sinalizar } // fim de finally return true; // notifica o jogador que a jogada foi válida

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228

27.8  Jogo da velha cliente/servidor que utiliza um servidor com multithread } // fim do if else // a jogada não foi válida return false; // notifica o jogador que a jogada foi inválida } // fim do método validateAndMove // determina se a posição está ocupada public boolean isOccupied( int location ) { if ( board[ location ].equals( MARKS[ PLAYER_X ] ) || board [ location ].equals( MARKS[ PLAYER_O ] ) ) return true; // posição está ocupada else return false; // posição não está ocupada } // fim do método isOccupied // coloque o código nesse método para determinar se o jogo terminou public boolean isGameOver() { return false; // isso é deixado como exercício } // fim do método isGameOver // classe interna privada Player gerencia cada Player como um executável private class Player implements Runnable { private Socket connection; // conexão com o cliente private Scanner input; // entrada do cliente private Formatter output; // saída para o cliente private int playerNumber; // monitora qual é o jogador private String mark; // marca para esse jogador private boolean suspended = true; // se a thread está suspensa // configura a thread Player public Player( Socket socket, int number ) { playerNumber = number; // armazena o número desse jogador mark = MARKS[ playerNumber ]; // especifica a marca do jogador connection = socket; // armazena o socket para o cliente try // obtém fluxos a partir de Socket { input = new Scanner( connection.getInputStream() ); output = new Formatter( connection.getOutputStream() ); } // fim do try catch ( IOException ioException ) { ioException.printStackTrace(); System.exit( 1 ); } // fim do catch } // fim do construtor Player // envia uma mensagem de que o outro jogador fez uma jogada public void otherPlayerMoved( int location ) { output.format( "Opponent moved\n" ); output.format( "%d\n", location ); // envia a posição da jogada output.flush(); // esvazia a saída } // fim do método otherPlayerMoved // execução da thread de controle public void run() { // envia ao cliente a marca (X try { displayMessage( "Player " + output.format( "%s\n", mark output.flush(); // esvazia a

ou O), processa as mensagens do cliente

mark + " connected\n" ); ); // envia a marca do jogador saída

// se for o jogador X, espera que o outro jogador chegue

885

886 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297

Capítulo 27  Redes if ( playerNumber == PLAYER_X ) { output.format( "%s\n%s", "Player X connected", "Waiting for another player\n" ); output.flush(); // esvazia a saída gameLock.lock(); // bloqueia o jogo para esperar o segundo jogador try { while( suspended ) { otherPlayerConnected.await(); // espera o jogador O } // fim do while } // fim do try catch ( InterruptedException exception ) { exception.printStackTrace(); } // fim do catch finally { gameLock.unlock(); // desbloqueia o jogo depois do segundo jogador } // fim de finally // envia uma mensagem de que o outro jogador se conectou output.format( "Other player connected. Your move.\n" ); output.flush(); // esvazia a saída } // fim do if else { output.format( "Player O connected, please wait\n" ); output.flush(); // esvazia a saída } // fim de else // enquanto jogo não terminou while ( !isGameOver() ) { int location = 0; // inicializa a posição da jogada if ( input.hasNext() ) location = input.nextInt(); // obtém a posição da jogada // verifica uma jogada válida if ( validateAndMove( location, playerNumber ) ) { displayMessage( "\nlocation: " + location ); output.format( "Valid move.\n" ); // notifica o cliente output.flush(); // esvazia a saída } // fim do if else // jogada foi inválida { output.format( "Invalid move, try again\n" ); output.flush(); // esvazia a saída } // fim de else } // fim do while } // fim do try finally { try { connection.close(); // fecha a conexão com o cliente } // fim do try catch ( IOException ioException ) { ioException.printStackTrace(); System.exit( 1 ); } // fim do catch } // fim de finally } // fim do método run

298 299 300 301 302 303 304 305

27.8  Jogo da velha cliente/servidor que utiliza um servidor com multithread

887

// configura se a thread está ou não suspensa public void setSuspended( boolean status ) { suspended = status; // configura o valor do suspenso } // fim do método setSuspended } // fim da classe Player } // fim da classe TicTacToeServer

Figura 27.13  |  O lado do servidor do programa Tic-Tac-Toe cliente/servidor. 1 2 3 4 5 6 7 8 9 10 11 12 13

// Figura 27.14: TicTacToeServerTest.java // Classe que testa o servidor Tic-Tac-Toe. import javax.swing.JFrame; public class TicTacToeServerTest { public static void main( String[] args ) { TicTacToeServer application = new TicTacToeServer(); application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); application.execute(); } // fim de main } // fim da classe TicTacToeServerTest

Figura 27.14  |  A classe que testa o servidor Tic-Tac-Toe.

Começamos com uma discussão do lado do servidor do jogo Tic-Tac-Toe. Quando o aplicativo TicTacToeServer executa, o método (linhas 7–12 da Figura 27.14) cria um objeto TicTacToeServer chamado application. O construtor (Figura 27.13, linhas 34–69) tenta configurar um ServerSocket. Se bem-sucedido, o programa exibe a janela de servidor e então main invoca o método TicTac­ ToeServer execute (linhas 72–100). O método execute faz um loop duas vezes, bloqueando na linha 79 toda vez enquanto espera a conexão de um cliente. Quando um cliente se conecta, a linha 79 cria um novo objeto Player para gerenciar a conexão como uma thread separada e a linha 80 executa o Player no pool de threads runGame. Quando o TicTacToeServer cria um Player, o construtor Player (linhas 192–208) recebe o objeto Socket que representa a conexão com o cliente e obtém os fluxos de entrada e de saída associados. A linha 201 cria um Formatter (ver o Capítulo 17) empacotando-o no fluxo de saída do socket. O método Player run (linhas 219–297) controla as informações enviadas e recebidas do cliente. Primeiro, ele passa para o cliente o caractere que o cliente colocará no tabuleiro quando ocorre uma jogada (linha 225). A linha 226 chama o método Formatter flush para forçar essa saída para o cliente. A linha 241 suspende a thread do jogador X à medida que ele inicia a execução, porque o jogador X só pode jogar depois que jogador O se conectar. Quando o jogador O se conectar, o jogo poderá ser jogado e o método run iniciará a execução da sua instrução while (linhas 264– 283). Cada iteração desse loop lê um inteiro (linha 269) que representa a posição em que o cliente quer colocar uma marca (bloqueando para esperar uma entrada, se necessário) e a linha 272 invoca o método TicTacToeServer validateAndMove (declarado nas linhas 118–163) para verificar a jogada. Se a jogada for válida, a linha 275 enviará uma mensagem ao cliente para esse efeito. Se não, a linha 280 envia uma mensagem que indica que a jogada foi inválida. O programa mantém as localizações do tabuleiro como números de 0 a 8 (0 a 2 para a primeira linha, 3 a 5 para a segunda linha e 6 a 8 para a terceira linha). main

888

Capítulo 27  Redes

O método validateAndMove (linhas 118–163 na classe TicTacToeServer) permite que apenas um jogador faça uma jogada por vez, impedindo assim que eles modifiquem as informações sobre o estado do jogo simultaneamente. Se o Player que tenta validar uma jogada não for o jogador atual (isto é, aquele com permissão para fazer uma jogada), ele é colocado em um estado de espera (wait) até chegar sua vez de jogar. Se a posição para o movimento sendo validado já estiver ocupada no tabuleiro, validMove retorna false. Caso contrário, o servidor coloca uma marca para o jogador na sua representação local do tabuleiro (linha 142), notifica o outro objeto Player (linha 146) de que uma jogada foi feita (de modo que seja possível enviar uma mensagem ao cliente), invoca o método signal (linha 152) para que o Player esperando (se houver um) possa validar uma jogada e retorna true (linha 159) para indicar que a jogada é válida.

Classe TicTacToeClient Cada aplicativo TicTacToeClient (figuras 27.15–27.16; saídas de exemplo na Figura 27.17) mantém sua própria versão da GUI do tabuleiro do Tic-Tac-Toe em que exibe o estado do jogo. Os clientes somente podem colocar uma marca em um quadrado vazio no tabuleiro. A classe interna Square (Figura 27.15, linhas 205–261) implementa cada um dos nove quadrados do tabuleiro. Quando um TicTac­ ToeClient inicia a execução, ele cria uma JTextArea em que as mensagens do servidor e uma representação do tabuleiro que utilizam nove objetos Square são exibidas. O método startClient (linhas 80–100) abre uma conexão com o servidor e obtém os fluxos de entrada e de saída associados ao objeto Socket. As linhas 85–86 fazem uma conexão com o servidor. A classe TicTacToeClient implementa a interface Runnable de modo que uma thread separada possa ler as mensagens a partir do servidor. Essa abordagem permite ao usuário interagir com o tabuleiro (na thread de despacho de eventos) enquanto espera as mensagens do servidor. Depois de estabelecer a conexão com o servidor, a linha 99 executa o cliente com o worker ExecutorService. O método run (linhas 103–126) controla a thread separada de execução. O método primeiro lê o caractere de marcação (X ou O) do servidor (linha 105) e então repete um loop continuamente (linhas 121–125) e lê as mensagens do servidor (linha 124). Cada mensagem é passada para o método processMessage (linhas 129–156) para processamento. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

// Figura 27.15: TicTacToeClient.java // Lado do cliente de programa cliente/servidor Tic-Tac-Toe. import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.net.Socket; import java.net.InetAddress; import java.io.IOException; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingUtilities; import java.util.Formatter; import java.util.Scanner; import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; public class TicTacToeClient extends JFrame implements Runnable { private JTextField idField; // campo de texto para exibir a marca do jogador private JTextArea displayArea; // JTextArea para exibir a saída private JPanel boardPanel; // painel para o tabuleiro do jogo da velha private JPanel panel2; // painel para conter o tabuleiro private Square[][] board; // tabuleiro do jogo da velha private Square currentSquare; // quadrado atual private Socket connection; // conexão com o servidor private Scanner input; // entrada a partir do servidor private Formatter output; // saída para o servidor private String ticTacToeHost; // nome do host para o servidor private String myMark; // marca desse cliente private boolean myTurn; // determina de qual cliente é a vez private final String X_MARK = "X"; // marca para o primeiro cliente private final String O_MARK = "O"; // marca para o segundo cliente // configura a interface com o usuário e o tabuleiro public TicTacToeClient( String host ) {

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111

27.8  Jogo da velha cliente/servidor que utiliza um servidor com multithread ticTacToeHost = host; // configura o nome do servidor displayArea = new JTextArea( 4, 30 ); // configura JTextArea displayArea.setEditable( false ); add( new JScrollPane( displayArea ), BorderLayout.SOUTH ); boardPanel = new JPanel(); // configura o painel para os quadrados no tabuleiro boardPanel.setLayout( new GridLayout( 3, 3, 0, 0 ) ); board = new Square[ 3 ][ 3 ]; // cria o tabuleiro // faz um loop pelas linhas no tabuleiro for ( int row = 0; row < board.length; row++ { // faz um loop pelas colunas no tabuleiro for ( int column = 0; column < board[ row { // cria um quadrado board[ row ][ column ] = new Square( ' boardPanel.add( board[ row ][ column ] } // fim do for interno } // fim do for externo

)

].length; column++ )

', row * 3 + column ); ); // adiciona um quadrado

idField = new JTextField(); // configura o campo de texto idField.setEditable( false ); add( idField, BorderLayout.NORTH ); panel2 = new JPanel(); // configure o painel que irá conter o boardPanel panel2.add( boardPanel, BorderLayout.CENTER ); // adiciona o painel do tabuleiro add( panel2, BorderLayout.CENTER ); // adiciona o painel contêiner setSize( 300, 225 ); // configura o tamanho da janela setVisible( true ); // mostra a janela startClient(); } // fim do construtor TicTacToeClient // inicia a thread do cliente public void startClient() { try // conecta-se ao servidor e obtém fluxos { // faz uma conexão com o servidor connection = new Socket( InetAddress.getByName( ticTacToeHost ), 12345 ); // obtém os fluxos de entrada e saída input = new Scanner( connection.getInputStream() ); output = new Formatter( connection.getOutputStream() ); } // fim do try catch ( IOException ioException ) { ioException.printStackTrace(); } // fim do catch // cria e inicia a thread de trabalhador para esse cliente ExecutorService worker = Executors.newFixedThreadPool( 1 ); worker.execute( this ); // executa o cliente } // fim do método startClient // thread de controle que permite atualização contínua da displayArea public void run() { myMark = input.nextLine(); // obtém a marca do jogador (X ou O) SwingUtilities.invokeLater( new Runnable() { public void run() {

889

890 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

Capítulo 27  Redes // exibe a marca do jogador idField.setText( "You are player \"" + myMark + "\"" ); } // fim do método run } // fim da classe interna anônima ); // fim da chamada para SwingUtilities.invokeLater myTurn = ( myMark.equals( X_MARK ) ); // determina se a vez do cliente // recebe as mensagens enviadas para o cliente e gera saída delas while ( true ) { if ( input.hasNextLine() ) processMessage(input.nextLine() ); } // fim do while } // fim do método run // processa as mensagens recebidas pelo cliente private void processMessage( String message ) { // ocorreu uma jogada válida if ( message.equals( "Valid move." ) ) { displayMessage( "Valid move, please wait.\n" ); setMark( currentSquare, myMark ); // configura a marca no quadrado } // fim do if else if ( message.equals( "Invalid move, try again" ) ) { displayMessage( message + "\n" ); // exibe jogada inválida myTurn = true; // ainda é a vez desse cliente } // fim de else if else if ( message.equals( "Opponent moved" ) ) { int location = input.nextInt(); // obtém a posição da jogada input.nextLine(); // pula uma nova linha depois da posição de int int row = location / 3; // calcula a linha int column = location % 3; // calcula a coluna setMark( board[ row ][ column ], ( myMark.equals( X_MARK ) ? O_MARK : X_MARK ) ); // marca a jogada displayMessage( "Opponent moved. Your turn.\n" ); myTurn = true; // agora é a vez desse cliente } // fim de else if else displayMessage( message + "\n" ); // exibe a mensagem } // fim do método processMessage // manipula displayArea na thread de despacho de eventos private void displayMessage( final String messageToDisplay ) { SwingUtilities.invokeLater( new Runnable() { public void run() { displayArea.append( messageToDisplay ); // atualiza a saída } // fim do método run } // fim da classe inner ); // fim da chamada para SwingUtilities.invokeLater } // fim do método displayMessage // método utilitário para configurar a marca sobre o tabuleiro na thread de despacho de eventos private void setMark( final Square squareToMark, final String mark ) { SwingUtilities.invokeLater( new Runnable() { public void run()

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

27.8  Jogo da velha cliente/servidor que utiliza um servidor com multithread { squareToMark.setMark( mark ); // configura a marca no quadrado } // fim do método run } // fim da classe interna anônima ); // fim da chamada para SwingUtilities.invokeLater } // fim do método setMark // envia mensagem para o servidor indicando public void sendClickedSquare( int location { // se for minha vez if ( myTurn ) { output.format( "%d\n", location ); // output.flush(); myTurn = false; // não é mais a minha } // fim do if } // fim do método sendClickedSquare

o quadrado clicado )

envia a posição ao servidor vez

// configura o Squareatual public void setCurrentSquare( Square square ) { currentSquare = square; // configura o quadrado atual para o argumento } // fim do método setCurrentSquare // classe interna privada para os quadrados no tabuleiro private class Square extends JPanel { private String mark; // marca a ser desenhada nesse quadrado private int location; // posição do quadrado public Square( String squareMark, int squareLocation ) { mark = squareMark; // configura a marca para esse quadrado location = squareLocation; // configura a posição desse quadrado addMouseListener( new MouseAdapter() { public void mouseReleased( MouseEvent e ) { setCurrentSquare( Square.this ); // configura o quadrado atual // envia a posição desse quadrado sendClickedSquare( getSquareLocation() ); } // fim do método mouseReleased } // fim da classe interna anônima ); // fim da chamada para addMouseListener } // fim do construtor Square // retorna o tamanho preferido de Square public Dimension getPreferredSize() { return new Dimension( 30, 30 ); // retorna o tamanho preferido } // fim do método getPreferredSize // retorna o tamanho mínimo de Square public Dimension getMinimumSize() { return getPreferredSize(); // retorna o tamanho preferido } // fim do método getMinimumSize // configura a marca para Square public void setMark( String newMark ) { mark = newMark; // configura a marca do quadrado repaint(); // repinta o quadrado

891

892 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263

Capítulo 27  Redes } // fim do método setMark // retorna a posição de Square public int getSquareLocation() { return location; // retorna a posição do quadrado } // fim do método getSquareLocation // desenha Square public void paintComponent( Graphics g ) { super.paintComponent( g ); g.drawRect( 0, 0, 29, 29 ); // desenha o quadrado g.drawString( mark, 11, 20 ); // desenha a marca } // fim do método paintComponent } // fim da classe interna Square } // fim da classe TicTacToeClient

Figura 27.15  |  O lado do cliente de programa cliente/servidor Tic-Tac-Toe. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// Figura 27.16: TicTacToeClientTest.java // Testa a classe para o cliente de Tic-Tac-Toe. import javax.swing.JFrame; public class TicTacToeClientTest { public static void main( String[] args ) { TicTacToeClient application; // declara o aplicativo cliente // se não houver if ( args.length application = else application =

nenhum argumento de linha de comando == 0 ) new TicTacToeClient( "127.0.0.1" ); // host local new TicTacToeClient( args[ 0 ] ); // utiliza argumentos

application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); } // fim de main } // fim da classe TicTacToeClientTest

Figura 27.16  |  Testa a classe para o cliente de Tic-Tac-Toe.

Se a mensagem recebida for "Valid move.", as linhas 134–135 exibem a mensagem "Valid move, please wait." e chamam o método setMark (linhas 173–184) para configurar a marca do cliente no quadrado atual (aquele em que o usuário clicou), utilizando o método SwingUtilities invokeLater para assegurar que as atualizações das GUIs ocorram na thread de despacho de eventos. Se a mensagem recebida for "Invalid move, try again.", a linha 139 exibe a mensagem de modo que o usuário possa clicar em um quadrado diferente. Se a mensagem recebida for "Opponent moved.", a linha 144 lê um inteiro a partir do servidor indicando onde o oponente jogou e as linhas 149–150 colocam uma marca nesse quadrado do tabuleiro (novamente utilizando o método SwingUtilities invoke­ Later para assegurar que as atualizações das GUIs ocorram na thread de despacho de eventos). Se qualquer outra mensagem for recebida, a linha 155 simplesmente exibirá a mensagem.

27.9 [Bônus Web] Estudo de caso: Servidor e cliente DeitelMessenger

893

Figura 27.17 | Saídas de exemplo do programa cliente/servidor Tic-Tac-Toe.

27.9 [Bônus Web] Estudo de caso: Servidor e cliente DeitelMessenger [Nota: Esse estudo de caso está disponível em inglês em www.deitel.com/books/jhtp8/.] Salas de chat são comuns na internet. Elas fornecem um local central em que os usuários podem conversar uns com os outros via mensagens curtas de texto. Cada participante pode ver todas as mensagens que os outros usuários postaram e cada usuário pode postar mensagens. Esse estudo de caso integra muitos dos recursos de rede e multithread do Java e da GUI do Swing que você aprendeu até agora para construir um sistema de chat on-line. Também introduzimos o multicasting, que permite a um aplicativo enviar DatagramPackets para grupos de clientes. O estudo de caso DeitelMessenger é um aplicativo significativo que utiliza muitos recursos Java intermediários, como redes com Sockets, DatagramPackets e MulticastSockets, multithreading e GUI Swing. O estudo de caso também demonstra boas práticas de engenharia de software separando a interface da implementação, o que permite aos desenvolvedores dar suporte a diferentes protocolos de rede e fornecer diferentes interfaces com usuário. Depois de ler esse estudo de caso, você será capaz de construir aplicativos de rede mais significativos.

27.10 Conclusão Neste capítulo, você aprendeu os princípios básicos da programação de rede em Java. Começamos com um applet e um aplicativo simples em que o Java cuidava das questões de rede para você. Aprendeu dois métodos diferentes de enviar dados por uma rede — rede baseada em fluxos utilizando TCP/IP e rede baseada em datagramas utilizando UDP. Mostramos como construir programas de chat cliente/servidor simples utilizando redes baseadas em fluxos e redes baseadas em datagramas. Vimos então um jogo cliente/servidor do tipo jogo da velha chamado Tic-Tac-Toe que permite a dois clientes jogar interagindo com um servidor baseado em múltiplas threads que mantém a lógica e o estado do jogo. No próximo capítulo, você aprenderá conceitos básicos sobre banco de dados, como interagir com os dados em um banco de dados utilizando o SQL e como utilizar o JDBC para permitir que aplicativos Java manipulem os dados no banco de dados.

894

Capítulo 27  Redes

Resumo Seção 27.1  Introdução • O Java fornece sockets de fluxo e sockets de datagrama. Com sockets de fluxo, um processo estabelece uma conexão com outro processo. Enquanto a conexão estiver no ar, os dados fluem entre os processos em fluxos contínuos. Dizemos que os sockets de fluxo fornecem um serviço orientado para conexão. O protocolo utilizado para transmissão é o popular TCP (Transmission Control Protocol). • Com sockets de datagrama, são transmitidos pacotes individuais de informações. O UDP (User Datagram Protocol) é um serviço sem conexão que não garante que os pacotes não serão perdidos, duplicados ou que cheguem fora de sequência.

Seção 27.2  Manipulando URLs • O protocolo HTTP (HyperText Transfer Protocol), que forma a base da Web, usa URIs (Uniform Resource Identifier) para localizar dados na Internet. Os URIs comuns representam arquivos ou diretórios e podem representar tarefas complexas como pesquisas de banco de dados e pesquisas de Internet. Um URI que representa um documento é chamado URL (Uniform Resource Locator). • O método applet getAppletContext retorna um AppletContext que representa o navegador em que o applet está executando. O método Ap­ pletContext showDocument recebe um URL e o passa para o AppletContext, que exibe o recurso Web correspondente. Uma segunda versão de showDocument permite que um applet especifique o frame-alvo em que será exibido um recurso Web.

Seção 27.3  Lendo um arquivo em um servidor da Web • O método JEditorPane setPage baixa o documento especificado pelo seu argumento e o exibe. • Em geral, um documento XHTML contém hiperlinks que, quando clicados, levam o usuário para outros documentos na Web. Se um documento XHTML for exibido em uma JEditorPane e o usuário clicar em um hiperlink, o JEditorPane gera um hiperlink e notifica seu HyperlinkListeners. • O método HyperlinkEvent getEventType determina o tipo de evento. HyperlinkEvent contém a classe aninhada EventType, que declara três tipos de evento de hiperlink: ACTIVATED (hiperlink clicado), ENTERED (mouse sobre um hiperlink) e EXITED (mouse saiu de cima de um hiperlink). O método HyperlinkEvent getURL obtém o URL representado pelo hiperlink.

Seção 27.4  Estabelecendo um servidor simples utilizando sockets de fluxo • As conexões baseadas em fluxo são gerenciadas com os objetos Socket. • Um objeto ServerSocket estabelece a porta onde um servidor espera conexões de clientes. O segundo argumento para o construtor ServerSocket é o número de conexões que pode esperar em uma fila para se conectar ao servidor. Se a fila de clientes estiver cheia, as conexões do cliente são recusadas. O método de ServerSocket accept espera indefinidamente (isto é, bloqueia) uma conexão de um cliente e retorna um objeto Socket quando uma conexão é estabelecida. • Os métodos getOutputStream e getInputStream de Socket obtêm referências a um OutputStream e InputStream de Socket, respectivamente. O método Socket close termina uma conexão.

Seção 27.5  Estabelecendo um cliente simples utilizando sockets de fluxo • Um nome de servidor e número de porta são especificados ao criar um objeto Socket para permitir que ele conecte um cliente ao servidor. Uma falha na tentativa de conexão lança uma IOException. • O método InetAddress getByName retorna um objeto InetAddress que contém o endereço IP do computador especificado. O método InetAddress getLocalHost retorna um objeto InetAddress que contém o endereço IP do computador local que executa o programa.

Seção 27.7  Interação cliente/servidor sem conexão com datagramas • A transmissão orientada para conexão é como o sistema de telefonia — você disca e recebe uma conexão para o telefone da pessoa com quem deseja se comunicar. A conexão é mantida até o fim da sua chamada telefônica, mesmo quando você não estiver conversando. • A transmissão sem conexão com datagramas é semelhante ao envio de correspondências via serviço postal. Uma mensagem grande que não se cabe em um envelope pode ser dividida em partes separadas de mensagem que são colocadas em envelopes separados, numerados sequencialmente. Todas as cartas são então remetidas de uma vez. Elas podem chegar na ordem, fora da ordem ou simplesmente não chegar. • Os objetos DatagramPacket armazenam pacotes de dados que devem ser enviados ou que são recebidos por um aplicativo. DatagramSockets envia e recebe DatagramPackets. • O construtor DatagramSocket que não recebe nenhum argumento vincula o DatagramSocket a uma porta escolhida pelo computador que está executando o programa. Aquele que recebe um argumento inteiro de número de porta vincula o DatagramSocket à porta especificada. Se um construtor Datagram-Socket não conseguir vincular o DatagramSocket a uma porta, uma Socket-Exception ocorrerá. O método DatagramSocket receive bloqueia (espera) até que um pacote chegue e, então, armazena o pacote no seu argumento. • O método DatagramPacket getAddress retorna um objeto InetAddress contendo informações sobre o computador do qual (ou para qual) o pacote foi enviado. O método getPort retorna um inteiro que especifica o número da porta por meio da qual o DatagramPacket host foi enviado ou recebido. O método getLength retorna um inteiro que representa o número de bytes dos dados em um DatagramPacket. O método getData retorna um array de bytes contendo os dados em um DatagramPacket.



Terminologia

895

• O construtor DatagramPacket para um pacote a ser enviado aceita quatro argumentos — o array de bytes a ser enviado, o número de bytes a ser enviado, o endereço de cliente para o qual o pacote será enviado e o número da porta onde o cliente está esperando receber os pacotes. • O método de DatagramSocket send envia um DatagramPacket para fora da rede. • Se ocorrer um erro ao receber ou enviar um DatagramPacket, uma IOException ocorrerá.

Terminologia 127.0.0.1, (localhost), endereço IP, 871 accept, método da classe ServerSocket, 866 ACTIVATED, constante da classe aninhada EventType, 865 AppletContext, interface, 860 BindException, classe, 871 _blank, frame-alvo, 863 bloquear, 866 chat cliente-servidor, 868 cliente, 859 close, método da classe Socket, 867 comprimento da fila, 866 comunicação baseada em pacotes, 859 comunicação baseada em socket, 859 conexão, 859 DatagramPacket, classe, 877 DatagramSocket, classe, 877 ecoar um pacote de volta para o cliente, 877 endereço de loopback, 871 ENTERED, constante da classe aninhada EventType, 865 EventType, classe aninhada de HyperlinkEvent, 865 EXITED, constante da classe aninhada EventType, 865 flush, método da classe Formatter, 887 flush, método da classe ObjectOutputStream, 871 fluxo, cabeçalho, 872 fluxo, comunicações baseadas em, 859 fluxos, 859 fluxo, socket, 859 frame-alvo, 863 getAddress, método da classe DatagramPacket, 879 getAppletContext, método da classe Applet, 863

getByName, método da classe InetAddress,

876

getData, método da classe DatagramPacket,

879

getEventType, método da classe HyperlinkEvent, 865 getHostName, método da classe InetAddress,

871

getInetAddress, método da classe Socket,

871

getInputStream, método da classe Socket,

866

getLength, método da classe DatagramPacket, 879 getLocalHost, método da classe InetAddress, 876 getOutputStream, método da classe Socket,

866

getParameter, método da classe Applet, 862 getPort, método da classe DatagramPacket,

879

getURL, método da classe HyperlinkEvent,

865 handshake, ponto, 866 hyperlink, 865 HyperlinkEvent, classe, 863 HyperlinkListener, interface, 865 hyperlinkUpdate, método da interface HyperlinkListener, 865 HyperText Transfer Protocol (HTTP), 860 InetAddress, classe, 871 invokeLater, método da classe SwingUtilities, 871 java.net, pacote, 859 JEditorPane, classe, 863 ligando o servidor a uma porta, 866 localhost, (127.0.0.1), endereço, 871

MalformedURLException, classe, 863 multicast, 860 número de porta, 866 pacote, 859 pacote de datagrama, 859 param, elemento, 862 parâmetro de applet, 862 porta, 866 receive, método da classe DatagramSocket, 879 registrar uma porta, 866 relacionamento cliente-servidor, 859 _self, frame-alvo, 863 send, método da classe DatagramSocket, 879 ServerSocket, classe, 866 serviço orientado para conexão, 859 serviço sem conexão, 859 servidor, 859 setPage, método da classe JEditorPane, 865 showDocument, método da interface AppletContext, 860 socket, 859 Socket, classe, 866 socket de datagrama, 859 SocketException, classe, 877 SwingUtilities, classe, 871 TCP (Transmission Control Protocol), 859 _top, frame-alvo, 863 transmissão sem conexão, 877 UDP (User Datagram Protocol), 859 Uniform Resource Identifier (URI), 860 UnknownHostException, classe, 867 URI (Uniform Resource Identifier), 860 URL (Uniform Resource Locator), 860 User Datagram Protocol (UDP), 859

Exercícios de autorrevisão 27.1 Preencha as lacunas em cada uma das seguintes afirmações: a) A exceção ________ ocorre quando um erro de entrada/saída ocorre ao fechar um socket. b) A exceção ________ ocorre quando um nome de host indicado por um cliente não pode ser convertido em um endereço. c) Se um construtor DatagramSocket não conseguir configurar um DatagramSocket adequadamente, uma exceção do tipo ________ ocorrerá. d) Muitas classes para redes do Java estão contidas no pacote ________. e) A classe ________ vincula o aplicativo a uma porta para transmissão de datagrama. f) Um objeto da classe ________ contém um endereço IP. g) Os dois tipos de sockets que discutimos neste capítulo são ________ e ________. h) O acrônimo URL significa ________. i) O acrônimo URI significa ________. j) O protocolo-chave que forma a base da World Wide Web é ________. k) O método AppletContext ________ recebe um objeto URL como um argumento e exibe em um navegador o recurso da World Wide Web associado com esse URL.

896

Capítulo 27  Redes l) O método getLocalHost retorna um objeto ________ contendo o endereço IP local do computador em que o programa está executando. m) O construtor URL determina se seu argumento de String é um URL válido. Se for, o objeto URL é inicializado com essa localização. Se não, uma exceção ________ ocorre.

27.2 Determine se cada uma das seguintes afirmações é verdadeira ou falsa. Se falso, explique por quê. a) O UDP é um protocolo orientado para conexão. b) Com sockets de fluxo um processo estabelece uma conexão com outro processo. c) Um servidor espera conexões de um cliente em uma porta. d) A transmissão de pacote de datagrama em uma rede é confiável — garante-se que os pacotes chegarão na sequência correta.

Respostas dos exercícios de autorrevisão 27.1 27.2

a) IOException. b) UnknownHostException. c) SocketException. d) java.net. e) DatagramSocket. f) InetAddress. g) sockets de fluxo, sockets de datagrama. h) Uniform Resource Locator. i) Uniform Resource Identifier. j) HTTP. k) showDocument. l) InetAddress. m) Mal­ formedURLException. a) Falso; o UDP é um protocolo sem conexão e o TCP é um protocolo orientado a conexões. b) Verdadeiro. c) Verdadeiro. d) Falso; os pacotes podem ser perdidos, chegar fora da ordem ou ser duplicados.

Exercícios 27.3 Faça uma distinção entre serviços de rede sem conexão e orientados para conexão. 27.4 Como um cliente determina o nome de host do computador cliente? 27.5 Sob que circunstâncias uma SocketException seria lançada? 27.6 Como um cliente pode obter uma linha de texto de um servidor? 27.7 Descreva como um cliente se conecta a um servidor. 27.8 Descreva como um servidor envia os dados para um cliente. 27.9 Descreva como preparar um servidor para receber uma solicitação de conexão baseada em fluxo a partir de um único cliente. 27.10 Como um servidor ouve conexões baseadas em fluxo de sockets em uma porta? 27.11 O que determina quantas solicitações de conexão de clientes podem esperar em uma fila para se conectar a um servidor? 27.12 Como descrito no texto, que razões podem fazer com que um servidor recuse uma solicitação de conexão de um cliente? 27.13 Utilize uma conexão de socket para permitir a um cliente especificar um nome de arquivo e fazer o servidor enviar o conteúdo do arquivo ou indicar que o arquivo não existe.

27.14 Modifique o Exercício 27.13 para permitir ao cliente modificar o conteúdo do arquivo e enviar o arquivo de volta ao servidor para armazenamento. O usuário pode editar o arquivo em uma JTextArea, então clique em um botão salvar alterações para enviar o arquivo de volta para o servidor.

27.15 Modifique o programa na Figura 27.2 para permitir aos usuários adicionar e remover seus próprios sites da lista. 27.16 Os servidores multiencadeados são bem populares hoje, especialmente por causa da utilização crescente de servidores multiprocessados. Modifique

a aplicação de servidor simples apresentado na Seção 27.6 para ser um servidor com múltiplas threads. Então utilize vários aplicativos clientes e faça cada um deles se conectar ao servidor simultaneamente. Utilize um ArrayList para armazenar as threads de cliente. O ArrayList fornece vários métodos de uso neste exercício. O método size determina o número de elementos em um ArrayList. O método get retorna o elemento na localização especificada pelo seu argumento. O método add coloca seu argumento no fim do ArrayList. O método remove exclui seu argumento do ArrayList.

27.17 (Jogo de damas) No texto, apresentamos um programa de jogo-da-velha controlado por um servidor com múltiplas threads. Desenvolva um

programa de damas modelado com base no programa Tic-Tac-Toe (jogo da velha). Os dois usuários devem fazer movimentos alternados. Seu programa deve mediar os movimentos dos jogadores, determinando de quem é a vez e permitindo apenas movimentos válidos. Os próprios jogadores determinarão quando o jogo acabou.

27.18 (Jogo de xadrez) Desenvolva um programa de jogo de xadrez modelado de acordo com o programa de damas no Exercício 27.17. 27.19 (Jogo de cartas “vinte e um”) Desenvolva um programa de jogo de cartas do tipo “vinte e um” em que o aplicativo servidor distribui as cartas para cada um dos clientes. O servidor deve distribuir as cartas adicionais (de acordo com as regras do jogo) para cada jogador quando solicitado.

27.20 (Jogo de pôquer) Desenvolva um jogo de cartas de pôquer em que o aplicativo servidor dá as cartas para cada um dos clientes. O servidor deve distribuir as cartas adicionais (de acordo com as regras do jogo) para cada jogador quando solicitado.

27.21 (Modificações no programa Tic-Tac-Toe com múltiplos threads) Os programas nas figuras 27.13 e 27.15 implementaram uma versão

cliente/servidor com múltiplas threads do jogo-da-velha. Nosso objetivo ao desenvolver esse jogo foi demonstrar um servidor multiencadeado que



Exercícios

897

pode processar múltiplas conexões de clientes ao mesmo tempo. O servidor no exemplo é na realidade um mediador entre os dois applets clientes — ele se certifica de que cada movimento é válido e que cada cliente move-se na ordem adequada. O servidor não determina quem ganhou ou quem perdeu nem se houve um empate. Além disso, não há capacidade para permitir que um novo jogo seja jogado ou para terminar um jogo existente. A seguir há uma lista das modificações sugeridas para as Figuras 27.13 e 27.15: a) Modifique a classe TicTacToeServer para testar uma vitória, derrota ou empate depois de cada movimento. Envie uma mensagem para cada cliente indicando o resultado do jogo quando o jogo acabar. b) Modifique a classe TicTacToeClient para exibir um botão que quando clicado permita ao cliente jogar outro jogo. O botão somente deve ser ativado quando um jogo terminar. Observe que tanto a classe TicTacToeClient como a classe TicTacToeServer devem ser modificadas para redefinir o tabuleiro e todas as informações de estado. Além disso, o outro TicTacToeClient deve ser notificado de que um novo jogo está para iniciar de modo que seu tabuleiro e seu estado possam ser reinicializados. c) Modifique a classe TicTacToeClient para fornecer um botão que permita a um cliente terminar o programa a qualquer hora. Quando o usuário clicar no botão, o servidor e o outro cliente devem ser notificados. O servidor então deve esperar uma conexão do outro cliente para que um novo jogo possa começar. d) Modifique a classe TicTacToeClient e a classe TicTacToeServer de modo que o vencedor de um jogo possa escolher o ser o X ou o O para o próximo jogo. Lembre-se: X sempre começa primeiro. e) Se você se sentir ambicioso, permita que um cliente jogue contra o servidor enquanto o servidor espera uma conexão de outro cliente.

27.22 (Tic-Tac-Toe 3-D Multiencadeado) Modifique o programa cliente/servidor Tic-Tac-Toe multiencadeado para implementar uma versão tridi-

mensional do jogo 4 por 4 por 4. Implemente o aplicativo servidor para mediar entre os dois clientes. Exiba o tabuleiro tridimensional como quatro tabuleiros contendo quatro linhas e quatro colunas cada um. Se você quiser ser ambicioso, experimente as seguintes modificações: a) Desenhe o tabuleiro de uma maneira tridimensional. b) Permita ao servidor testar se há um ganhador, um perdedor ou um empate. Cuidado! Há muitas possíveis maneiras de ganhar em um tabuleiro 4 por 4 por 4!

27.23 (Código Morse em rede) Talvez o mais famoso de todos os esquemas de codificação seja o código Morse, desenvolvido por Samuel Morse em

1832 para utilização com o sistema de telégrafo. O código Morse atribui uma série de pontos e traços para cada letra do alfabeto, para cada dígito e alguns caracteres especiais (como ponto, vírgula, dois-pontos e ponto-e-vírgula). Em sistemas orientados para áudio, o ponto representa um som curto e o traço representa um som longo. Outras representações de pontos e traços são utilizadas com sistemas baseados em sinais luminosos e sistemas baseados em sinais de bandeira. A separação entre palavras é indicada por um espaço, ou, simplesmente, a ausência de um ponto ou traço. Em um sistema orientado a som, um espaço é indicado por um tempo curto durante o qual nenhum som é transmitido. A versão internacional do código Morse aparece na Figura 27.18

Caractere

Código

Caractere

Código

Caractere

Código

A

.-

N

-.

Dígitos

B

-...

O

---

1

.----

C

-.-.

P

.--.

2

..---

D

-..

Q

--.-

3

...--

E

.

R

.-.

4

....-

F

..-.

S

...

5

.....

G

--.

T

-

6

-....

H

....

U

..-

7

--...

I

..

V

...-

8

---..

J

.---

W

.--

9

----.

K

-.-

X

-..-

0

-----

L

.-..

Y

-.--

M

--

Z

--..

Figura 27.18  |  Letras e dígitos no código Morse internacional. Escreva um aplicativo cliente/servidor em que dois clientes possam enviar mensagens em Código Morse entre si por meio de um aplicativo servidor com múltiplas threads. O aplicativo cliente deve permitir que o usuário digite frases em linguagem natural em uma JTextArea. Quando o usuário envia a mensagem, o aplicativo cliente codifica o texto em Código Morse e envia a mensagem codificada, por meio do servidor, para o outro cliente. Utilize um espaço em branco entre cada letra codificada em Morse e três espaços em branco entre cada palavra codificada em Morse. Quando as mensagens são recebidas, elas devem ser decodificadas e exibidas como caracteres normais e como código Morse. O cliente deve ter um JTextField para digitar e uma JText-Area para exibir mensagens do outro cliente.

28

É um equívoco capital teorizar antes de ter os dados. — Arthur Conan Doyle

Vai pois agora, escreve isso numa tábua perante eles, registra-o num [livro]; para que fique como testemunho para o tempo vindouro, para sempre. — A Bíblia Sagrada, Isaías 30:8

Obtenha seus fatos primeiro e então você pode distorcê-los o quanto quiser. — Mark Twain

Gosto de dois tipos de homens: americanos e estrangeiros. — Mae West

Acesso a bancos de dados com o JDBC Objetivos Neste capítulo, você aprenderá: 

Conceitos de banco de dados relacional.



Utilizar Structured Query Language (SQL) para recuperar dados de um banco de dados e manipular dados em um banco de dados.



Utilizar o JDBC™ API do pacote java.sql para acessar bancos de dados.



Utilizar a interface RowSet do pacote javax.sql para manipular bancos de dados.



Utilizar a descoberta automática de driver JDBC do JDBC 4.0.



Utilizar PreparedStatements para criar instruções de SQL precompiladas com parâmetros.



Como o processamento de transação torna aplicativos de banco de dados mais robustos.

28.1 Introdução

28.1 Introdução 28.2 Bancos de dados relacionais 28.3 Visão geral de banco de dados relacional: O banco de dados books 28.4 SQL

28.6 Instruções para configuração de uma conta de usuário MySQL 28.7 Criando banco de dados books no MySQL 28.8 Manipulando bancos de dados com o JDBC 28.8.1 Consultando e conectando-se a um banco de dados

28.4.1 Consulta SELECT básica

Sumário

28.4.2 Cláusula WHERE 28.4.3 Cláusula Order BY 28.4.4 Mesclando dados a partir de múltiplas tabelas: INNER JOIN

28.4.5 Instrução INSERT 28.4.6 Instrução UPDATE 28.4.7 Instrução DELETE

28.5 Instruções para instalar o MySQL e o MySQL Conector/J

899

28.8.2 Consultando o banco de dados books

28.9 28.10 28.11 28.12 28.13 28.14 28.15

Interface RowSet Java DB/Apache Derby PreparedStatements

Procedures armazenadas Processamento de transações Conclusão Recursos da Web

Resumo | Terminologia | Exercício de autorrevisão | Respostas do exercício de autorrevisão | Exercícios

28.1 Introdução Um banco de dados é uma coleção organizada de dados. Há muitas estratégias diferentes para organizar dados para facilitar acesso e manipulação. Um sistema de gerenciamento de bancos de dados (Database Management System — DBMS) fornece mecanismos para armazenar, organizar, recuperar e modificar dados para muitos usuários. Os sistemas de gerenciamento de bancos de dados permitem acesso e armazenamento de dados sem envolver a representação interna de dados. Os sistemas de banco de dados atuais mais populares são os bancos de dados relacionais, nos quais os dados são armazenados sem se levar em conta sua estrutura física (Seção 28.2). Uma linguagem chamada SQL é a linguagem padrão internacional utilizada quase universalmente com bancos de dados relacionais para realizar consultas (isto é, solicitar informações que satisfazem determinados critérios) e manipular dados. Alguns sistemas de gerenciamento de banco de dados relacional (Relational Database Management Systems — RDBMSs) populares são Microsoft SQL Server, Oracle, Sybase, IBM DB2, Informix, PostgreSQL e MySQL. O JDK vem agora com um RDBMS puro Java chamado Java DB — a versão da Sun para o Apache Derby. Neste capítulo, apresentamos exemplos utilizando MySQL e Java DB. Os programas Java comunicam-se com bancos de dados e manipulam seus dados utilizando a Java Database Connectivity (JDBC™) API. Um driver JDBC permite aos aplicativos Java conectar-se a um banco de dados em um DBMS particular e permite a você manipular esse banco de dados utilizando o JDBC API.

Observação de engenharia de software 28.1 Usar a JDBC API permite que desenvolvedores mudem o DBMS subjacente sem modificar o código Java que acessa o banco de dados.

Os sistemas de gerenciamento de bancos de dados mais populares agora fornecem drivers JDBC. Também há muitos drivers JDBC independentes disponíveis. Neste capítulo, introduzimos o JDBC e o utilizamos para manipular bancos de dados MySQL e Java DB. As técnicas demonstradas aqui também podem ser utilizadas para manipular outros bancos de dados que têm drivers JDBC. Verifique a documentação do DBMS para determinar se o seu DBMS vem com um driver JDBC. Se não, fornecedores independentes comercializam drivers JDBC para muitos DBMSs.

Observação de engenharia de software 28.2 A maioria dos fornecedores de banco de dados importantes fornece seus próprios drivers de banco de dados de JDBC e muitos fornecedores independentes também fornecem drivers JDBC. Para obter informações adicionais visite devapp.sun.com/product/jdbc/ drivers.

900

Capítulo 28

Acesso a bancos de dados com o JDBC

Para obter informações adicionais sobre o JDBC, visite java.sun.com/javase/technologies/database/index.jsp

que contém informações sobre o JDBC incluindo a especificação JDBC, FAQs, um centro de recursos de aprendizagem e downloads de software para pesquisa de drivers JDBC para DBMS, e developers.sun.com/product/jdbc/drivers/

que fornece um sistema de pesquisa para ajudá-lo a localizar drivers adequados para o seu DBMS.

28.2 Bancos de dados relacionais Um banco de dados relacional é uma representação lógica de dados que permite que os dados sejam acessados sem considerar sua estrutura física. Um banco de dados relacional armazena dados em tabelas. A Figura 28.1 ilustra uma tabela de exemplo que pode ser utilizada em um sistema pessoal. O nome da tabela é Employee e seu principal propósito é armazenar os atributos de um empregado. As tabelas são compostas de linhas e as linhas são compostas de colunas nas quais os valores são armazenados. Essa tabela consiste em seis linhas. A coluna Number de cada linha é chave primária da tabela — uma coluna (ou grupo de colunas) com um valor único que não pode ser duplicado em outras linhas. Isso garante que cada linha possa ser identificada por sua chave primária. Bons exemplos de colunas de chave primária são o número do CPF, o número de ID de um empregado e o número serial de um produto em um sistema de inventário, uma vez que é garantido que os valores em cada uma dessas colunas são únicos. As linhas na Figura 28.1 são exibidas em ordem por chave primária. Nesse caso, as linhas são listadas em ordem crescente, mas também poderíamos utilizar a ordem decrescente.

Linha

Number

Name

Department

Salary

Location

23603

Jones

413

1100

New Jersey

24568

Kerwin

413

2000

New Jersey

34589

Larson

642

1800

Los Angeles

35761

Myers

611

1400

Orlando

47132

Neumann

413

9000

New Jersey

78321

Stephens

611

8500

Orlando

Chave primária

Coluna

Figura 28.1 | Dados de exemplo da tabela Employee.

Não é garantido que as linhas nas tabelas serão armazenadas em alguma ordem particular. Como demonstraremos em um exemplo mais adiante, os programas podem especificar critérios de ordenação ao solicitar dados de um banco de dados. Cada coluna representa um atributo de dados diferente. Normalmente, as linhas são únicas (pela chave primária) dentro de uma tabela, mas os valores de coluna particulares podem ser duplicados entre as linhas. Por exemplo, três linhas diferentes na coluna Department da tabela Employee contêm o número 413. Frequentemente, diferentes usuários de um banco de dados estão interessados em diferentes dados e diferentes relacionamentos entre os dados. A maioria dos usuários exige apenas os subconjuntos das linhas e colunas. Para obter esses subconjuntos, utilize consultas para especificar quais dados selecionar de uma tabela. Você utiliza a SQL para definir consultas complexas. Por exemplo, poderia selecionar dados da tabela Employee para criar um resultado que mostra onde cada departamento está localizado, apresentando os dados classificados em ordem crescente por número de departamento. Esse resultado é mostrado na Figura 28.2. As consultas SQL são discutidas na Seção 28.4. Department

Location

413 611 642

New Jersey Orlando Los Angeles

Figura 28.2 | Resultado de selecionar dados distintos de Department e Location a partir da tabela Employee.

28.3 Visão geral de um banco de dados relacional: o banco de dados books Agora fornecemos uma visão geral de bancos de dados relacionais no contexto de um banco de dados books de exemplo que criamos para este capítulo. Antes de discutirmos SQL, apresentamos as tabelas do banco de dados books. Utilizamos esse banco de dados para introduzir vários conceitos de banco de dados, incluindo como utilizar SQL para obter informações do banco de dados e manipular os dados. Fornecemos um script para criar o banco de dados. Você pode localizar o script no diretório de exemplos deste capítulo. A Seção 28.7 explica como utilizar esse script.



28.3  Visão geral de um banco de dados relacional: o banco de dados books

901

O banco de dados consiste em três tabelas: Authors, AuthorISBN e Titles. A tabela Authors (descrita na Figura 28.3) consiste em três colunas que mantêm o número de ID único, nome e sobrenome de cada autor. A Figura 28.4 contém os dados da tabela Authors do banco de dados books. Coluna

Descrição

AuthorID

Número de ID do autor no banco de dados. No banco de dados books, essa coluna inteira é definida como autoincrementada — para cada linha inserida nessa tabela, o valor AuthorID é aumentado automaticamente por 1 para assegurar que cada linha tenha um único AuthorID. Essa coluna representa a chave primária da tabela.

FirstName

Nome do autor (uma string).

LastName

Sobrenome do autor (uma string).

Figura 28.3  |  Tabela Authors do banco de dados books. AuthorID

FirstName

LastName

1

Harvey

Deitel

2

Paul

Deitel

3

Andrew

Goldberg

4

David

Choffnes

Figura 28.4  |  Dados de exemplo da tabela authors.

A tabela AuthorISBN (descrita na Figura 28.5) consiste em duas colunas que mantêm cada ISBN e o número de ID do autor correspondente. Essa tabela associa autores com seus livros. Ambas as colunas são chaves estrangeiras que representam o relacionamento entre as tabelas Authors e Titles — uma linha na tabela Authors poder ser associada com muitas linhas na tabela Titles e vice-versa. As colunas combinadas da tabela AuthorISBN representam a chave primária da tabela — assim, cada linha nessa tabela deve ser uma combinação única de um AuthorID e um ISBN. A Figura 28.6 contém os dados da tabela Author-ISBN do banco de dados books. [Nota: Para economizar espaço, dividimos o conteúdo dessa tabela em duas colunas, cada uma contendo as colunas AuthorID e ISBN]. A coluna chamada AuthorID é uma chave estrangeira — uma coluna nessa tabela que coincide com a coluna de chave primária em outra tabela (isto é, AuthorID na tabela Authors). As chaves estrangeiras são especificadas ao criar uma tabela. A chave estrangeira ajuda a manter a Regra de Integridade Referencial — cada valor de chave estrangeira deve aparecer como outro valor de chave primária da tabela. Isso permite que o DBMS determine se o valor AuthorID de um livro particular é válido. As chaves estrangeiras também permitem que os dados relacionados em múltiplas tabelas sejam selecionados a partir dessas tabelas para fins analíticos — isso é conhecido como fazer uma join, ou junção, dos dados. Coluna

Descrição

AuthorID

O número de ID do autor, uma chave estrangeira para a tabela Authors.

ISBN

O ISBN de um livro, uma chave estrangeira para a tabela Titles.

Figura 28.5  |  Tabela AuthorISBN do banco de dados books. AuthorID

ISBN

AuthorID

ISBN

1

0131869000

2

0131450913

2

0131869000

1

0131828274

1

0132222205

2

0131828274

2

0132222205

3

0131450913

1

0131450913

4

0131828274

Figura 28.6  |  Dados de exemplo da tabela AuthorISBN de books.

902

Capítulo 28  Acesso a bancos de dados com o JDBC

A tabela Titles descrita na Figura 28.7 consiste em quatro colunas que representam o ISBN, o título, o número de edição e o ano dos direitos autorais. A tabela está na Figura 28.8. Coluna

Descrição

ISBN

ISBN do livro (uma string). A chave primária da tabela. O ISBN é a abreviação de “International Standard Book Number” — um sistema de numeração padronizado que os editores utilizam para dar a todos os livros um número de identificação único.

Title

Título do livro (uma string).

EditionNumber

Número de edição do livro (um inteiro).

Copyright

Ano de direitos autorais do livro (uma string).

Figura 28.7  |  Tabela Titles do banco de dados books. ISBN

Title

EditionNumber

Copyright

0131869000

Visual Basic 2005 How to Program

3

2006

0131525239

Visual C# 2005 How to Program

2

2006

0132222205

Java How to Program

7

2007

0131857576

C++ How to Program

5

2005

0132404168

C How to Program

5

2007

0131450913

Internet & World Wide Web How to Program

3

2004

0131828274

Sistemas operacionais

3

2004

Figura 28.8  |  Dados de exemplo da tabela Titles do banco de dados books.

Há um relacionamento de um para muitos entre uma chave primária e uma chave estrangeira correspondente (por exemplo, um editor pode publicar muitos livros). Uma chave estrangeira pode aparecer muitas vezes na própria tabela, mas pode aparecer apenas uma vez (como a chave primária) em outra tabela. A Figura 28.9 é um diagrama de relacionamento de entidade (Entity-Relationship — ER) do banco de dados books. Esse diagrama mostra as tabelas de banco de dados e as relações entre elas. O primeiro compartimento em cada quadro contém o nome da tabela e os demais compartimentos contêm as colunas da tabela. Os nomes em itálico são chaves primárias. Uma chave primária da tabela identifica unicamente cada linha na tabela. Cada linha deve ter um valor de chave primária, e esse valor deve ser único na tabela. Isso é conhecido como Regra de Integridade de Entidade. Novamente, para a tabela AuthorISBN, a chave primária é a combinação de ambas as colunas. Authors AuthorID

1

FirstName

AuthorISBN AuthorID ISBN

LastName

1

Titles ISBN Title EditionNumber Copyright

Figura 28.9  |  Relações de tabela no banco de dados books.

Erro comum de programação 28.1 Não fornecer um valor para cada coluna em uma chave primária quebra a Regra de Integridade de Entidade e faz com que o DBMS informe um erro.

Erro comum de programação 28.2 Fornecer o mesmo valor da chave primária em múltiplas linhas faz com que a DBMS informe um erro.

28.4 SQL

903

As linhas que conectam as tabelas (Figura 28.9) representam os relacionamentos entre as tabelas. Considere a linha entre as tabelas e Authors. Na extremidade Authors da linha há um 1, e na extremidade AuthorISBN há um símbolo de infinito (∞), indicando um relacionamento de um para muitos em que cada editor na tabela Authors pode ter um número arbitrário de livros na tabela AuthorISBN. A linha de relação vincula a coluna AuthorID em Authors (isto é, sua chave primária) à coluna AuthorID em AuthorISBN (isto é, a sua chave estrangeira). A coluna AuthorID na tabela AuthorISBN é uma chave estrangeira. AuthorISBN

Erro comum de programação 28.3 Fornecer um valor de chave estrangeira que não aparece como um valor de chave primária em outra tabela quebra a Regra de Integridade Referencial e faz com que o DBMS informe um erro.

A linha entre Titles e AuthorISBN ilustra outro relacionamento um para muitos; um título pode ser escrito por qualquer número de autores. De fato, o único propósito da tabela AuthorISBN é fornecer um relacionamento de muitos para muitos entre Authors e Titles — um autor pode escrever muitos livros e um livro pode ter muitos autores.

28.4 SQL Agora fornecemos uma visão geral de SQL no contexto de nosso banco de dados books. Você será capaz de utilizar a SQL discutida aqui nos exemplos posteriores do capítulo e nos exemplos dos capítulos 30–31. As várias subseções a seguir discutem as palavras-chave de SQL listadas na Figura 28.10 no contexto de consultas e instruções SQL. Outras palavras-chave de SQL estão além do escopo deste texto. Para aprender outras palavras-chave, consulte o guia de referência de SQL fornecido pelo fornecedor do RDBMS que você está utilizando. [Nota: Para obter mais informações sobre SQL, consulte os recursos Web na Seção 28.15.] SQL, palavras-chave

Descrição

SELECT

Recupera dados de uma ou mais tabelas.

FROM

Tabelas envolvidas na consulta. Requeridas em cada SELECT.

WHERE

Critérios de seleção que determinam as linhas a ser recuperadas, excluídas ou atualizadas. Opcional em uma consulta ou uma instrução de SQL.

GROUP BY

Critérios para agrupar linhas. Opcional em uma consulta SELECT.

ORDER BY

Critérios para ordenar linhas. Opcional em uma consulta SELECT.

INNER JOIN

Mescla linhas de múltiplas tabelas.

INSERT

Insere linhas em uma tabela especificada.

UPDATE

Atualiza linhas em uma tabela especificada.

DELETE

Exclui linhas de uma tabela especificada.

Figura 28.10 | As palavras-chave de consulta de SQL.

28.4.1 Consulta SELECT básica Vamos considerar várias consultas de SQL que extraem informações do banco de dados books. Uma consulta de SQL “seleciona” linhas e colunas de uma ou mais tabelas em um banco de dados. Essas seleções são realizadas por consultas com a palavra-chave SELECT. O formato básico de uma consulta SELECT é SELECT * FROM nomeDaTabela

em que o asterisco (*) indica que todas as colunas da tabela nomeDaTabela devem ser recuperadas. Por exemplo, para recuperar todos os dados na tabela Authors, utilize SELECT * FROM Authors

A maioria dos programas não exige todos os dados em uma tabela. Para recuperar somente colunas específicas de uma tabela, substitua o asterisco (*) por uma lista dos nomes de coluna separados por vírgulas. Por exemplo, para recuperar somente as colunas AuthorID e LastName de todas as linhas na tabela authors, utilize a consulta SELECT AuthorID, LastName FROM Authors

Essa consulta retorna os dados listados na Figura 28.11.

904

Capítulo 28  Acesso a bancos de dados com o JDBC

AuthorID

LastName

1

Deitel

2

Deitel

3

Goldberg

4

Choffnes

Figura 28.11  |  Dados AuthorID e LastName de exemplo da tabela Authors.

Observação de engenharia de software 28.3 Para a maioria das consultas, o asterisco (*) não deve ser utilizado para especificar nomes de coluna. Em geral, os programadores processam resultados sabendo antecipadamente a ordem das colunas no resultado — por exemplo, selecionar AuthorID e LastName da tabela Authors assegura que as colunas aparecerão no resultado com AuthorID como a primeira coluna e LastName como a segunda coluna. Em geral, os programas processam colunas de resultados especificando o número de coluna no resultado (iniciando do número 1 da primeira coluna). Selecionar colunas por nome também evita retornar colunas desnecessárias e ainda protege contra modificações na ordem real das colunas na(s) tabela(s) retornando colunas na ordem exata especificada.

Erro comum de programação 28.4 Se um programador assume que as colunas são sempre retornadas na mesma ordem de uma consulta que utiliza o asterisco (*), o programa pode processar o resultado incorretamente. Se a ordem de coluna na(s) tabela(s) mudar ou se colunas adicionais forem adicionadas posteriormente, a ordem das colunas no resultado mudaria de maneira correspondente.

28.4.2  Cláusula WHERE Na maioria dos casos, é necessário localizar linhas em um banco de dados que satisfaçam certos critérios de seleção. Somente as linhas que satisfazem os critérios de seleção (formalmente chamadas predicados) são selecionadas. O SQL utiliza a cláusula WHERE opcional em uma consulta para especificar os critérios de seleção para a consulta. A forma básica de uma consulta com critérios de seleção é SELECT nomeDaColuna1, nomeDaColuna2, … FROM nomeDaTabela

WHERE critérios

Por exemplo, para selecionar as colunas Title, EditionNumber e Copyright da tabela Titles para a qual os dados de Copyright são maiores que 2005, utilize a consulta SELECT Title, EditionNumber, Copyright FROM Titles WHERE Copyright > '2005'

Observe que as strings na SQL são delimitadas por aspas simples (') em vez de aspas duplas (""). A Figura 28.12 mostra o resultado da consulta precedente. Os critérios da cláusula WHERE podem conter os operadores , =, =, e LIKE. O operador LIKE é utilizado para correspondência de padrão com caracteres curingas porcentagem (%) e sublinhado (_). A correspondência de padrão permite à SQL procurar strings que correspondem a um dado padrão. Title

EditionNumber

Copyright

Visual C# 2005 How to Program

2

2006

Visual Basic 2005 How to Program

3

2006

Java How to Program

7

2007

C How to Program

5

2007

Figura 28.12  |  Amostragem de títulos com direitos autorais posteriores a 2005 da tabela Titles.

Um padrão que contém um caractere porcentagem (%) procura strings que tenham zero ou mais caracteres na posição do caractere porcentagem no padrão. Por exemplo, a seguinte consulta localiza as linhas de todos os autores cujo sobrenome começa com a letra D:



28.4  SQL

905

SELECT AuthorID, FirstName, LastName FROM Authors WHERE LastName LIKE 'D%'

A consulta anterior seleciona as duas linhas mostradas na Figura 28.13, porque dois dos quatro autores em nosso banco de dados têm um sobrenome que inicia com a letra D (seguida por zero ou mais caracteres). O % no padrão LIKE da cláusula WHERE indica que qualquer número de caracteres pode aparecer depois da letra D na coluna LastName. Observe que a string do padrão é envolvida entre caracteres de aspas simples. AuthorID

FirstName

LastName

1

Harvey

Deitel

2

Paul

Deitel

Figura 28.13  |  Autores cujo sobrenome começa com D da tabela authors.

Dica de portabilidade 28.1 Consulte a documentação do sistema de banco de dados para determinar se a SQL diferencia letras maiúsculas de minúsculas no sistema e a sintaxe de palavras-chave de SQL (isto é, todas devem estar em maiúsculas, todas em minúsculas ou algumas combinações das duas?).

Dica de portabilidade 28.2 Leia a documentação do sistema de banco de dados atentamente para determinar se ele suporta o operador LIKE conforme discutido aqui. O SQL que discutimos é suportado pela maioria dos RDBMSs, mas é sempre uma boa ideia verificar os recursos SQL que são suportados pelo seu RDBMS.

Um caractere sublinhado (_) na string do padrão indica um único curinga nessa posição no padrão. Por exemplo, a seguinte consulta localiza as linhas de todos os autores cujos sobrenomes começam com qualquer caractere (especificado por _) seguido pela letra o, seguida por qualquer número de caracteres adicionais (especificado por %): SELECT AuthorID, FirstName, LastName FROM Authors WHERE LastName LIKE '_o%'

A consulta precedente produz a linha mostrada na Figura 28.14 porque apenas um autor em nosso banco de dados tem um sobrenome que contém a letra o como sua segunda letra. AuthorID

FirstName

LastName

3

Andrew

Goldberg

Figura 28.14  |  O único autor da tabela authors cujo sobrenome contém o como a segunda letra.

28.4.3  Cláusula Order BY As linhas no resultado de uma consulta podem ser classificadas em ordem crescente ou decrescente utilizando a cláusula ORDER opcional O formato básico de uma consulta com uma cláusula ORDER BY é SELECT nomeDaColuna1, nomeDaColuna2, … FROM nomeDaTabela SELECT nomeDaColuna1, nomeDaColuna2, … FROM nomeDaTabela

BY.

ORDER BY coluna ASC ORDER BY coluna DESC

onde ASC especifica a ordem crescente (do mais baixo para o mais alto), DESC especifica ordem decrescente (do mais alto para o mais baixo) e coluna especifica a coluna em que a classificação é baseada. Por exemplo, para obter a lista de autores em ordem crescente por sobrenome (Figura 28.15), utilize a consulta SELECT AuthorID, FirstName, LastName FROM Authors ORDER BY LastName ASC

906

Capítulo 28  Acesso a bancos de dados com o JDBC

AuthorID

FirstName

LastName

4

David

Choffnes

1

Harvey

Deitel

2

Paul

Deitel

3

Andrew

Goldberg

Figura 28.15  |  Dados de exemplo da tabela Authors em ordem crescente por LastName.

Observe que a ordem de classificação padrão é crescente, então ASC é opcional. Para obter a mesma lista de autores em ordem decrescente por sobrenome (Figura 28.16), utilize a consulta SELECT AuthorID, FirstName, LastName FROM Authors ORDER BY LastName DESC

AuthorID

FirstName

LastName

3

Andrew

Goldberg

1

Harvey

Deitel

2

Paul

Deitel

4

David

Choffnes

Figura 28.16  |  Dados de exemplo da tabela Authors em ordem decrescente por LastName.

As múltiplas colunas podem ser utilizadas para classificação com uma cláusula ORDER BY da forma ORDER BY coluna1 ordemDeClassificação, coluna2 ordemDeClassificação, …

onde ordemDeClassificação é tanto ASC como DESC. Observe que a ordemDeClassificação não tem de ser idêntica para cada coluna. A consulta SELECT AuthorID, FirstName, LastName FROM Authors ORDER BY LastName, FirstName

classifica todas as linhas em ordem crescente por sobrenome, e depois por nome. Se quaisquer linhas tiverem o mesmo valor de sobrenome, elas serão retornadas classificadas por nome (Figura 28.17). AuthorID

FirstName

LastName

4

David

Choffnes

1

Harvey

Deitel

2

Paul

Deitel

3

Andrew

Goldberg

Figura 28.17  |  Dados de exemplo de Authors em ordem crescente por LastName e FirstName.

As cláusulas WHERE e ORDER BY podem ser combinadas em uma consulta. SELECT ISBN, Title, EditionNumber, Copyright FROM Titles WHERE Title LIKE '%How to Program' ORDER BY Title ASC

que retorna o ISBN, o Title, o EditionNumber e o Copyright de cada livro na tabela Titles que tem um Title terminando com "How to Program" e os classifica em ordem crescente por Title. Os resultados de consulta são mostrados na Figura 28.18.



28.4  SQL

ISBN

Title

Edition-Number

Copy-right

0132404168 0131857576 0131450913 0132222205 0131869000 013152539

C How to Program C++ How to Program Internet and World Wide Web How to Program Java How to Program Visual Basic 2005 How to Program Visual C# 2005 How to Program

5 5 3 7 3 2

2007 2005 2004 2007 2006 2006

Figura 28.18  |  Amostragem de livros da tabela Titles cujos títulos acabam com How

to Program

907

em ordem crescente por Title.

28.4.4  Mesclando dados a partir de múltiplas tabelas: INNER JOIN Os projetistas de banco de dados costumam dividir os dados relacionados em tabelas separadas para assegurar que um banco de dados não armazene dados de maneira redundante. Por exemplo, o banco de dados books tem tabelas Authors e Titles. Utilizamos uma tabela AuthorISBN para armazenar os dados de relacionamento entre autores e seus títulos correspondentes. Se não separássemos essas informações em tabelas individuais, precisaríamos incluir as informações de autor com cada entrada na tabela Titles. Isso resultaria no armazenamento de informações de autor duplicadas no caso dos autores que escreveram múltiplos livros. Frequentemente é necessário mesclar dados de múltiplas tabelas em um único resultado. Referido como join, ou junção, de tabelas, esse procedimento é especificado por um operador INNER JOIN na consulta. Uma INNER JOIN mescla linhas de duas tabelas correspondendo valores em colunas que são comuns às tabelas. A forma básica de uma INNER JOIN é: SELECT nomeDaColuna1, nomeDaColuna2, … FROM tabela1 INNER JOIN tabela2 ON tabela1.nomeDaColuna = tabela2.nomeDaColuna

A cláusula ON da INNER JOIN especifica as colunas de cada tabela que são comparadas para determinar que linhas são mescladas. Por exemplo, a consulta a seguir produz uma lista de autores acompanhados pelos ISBNs dos livros escritos por cada autor: SELECT FirstName, LastName, ISBN FROM Authors INNER JOIN AuthorISBN ON Authors.AuthorID = AuthorISBN.AuthorID ORDER BY LastName, FirstName

A consulta mescla dados das colunas FirstName e LastName de tabela Authors com a coluna ISBN da tabela AuthorISBN, classificando o resultado em ordem crescente por LastName e FirstName. Observe o uso da sintaxe nomeDaTabela.nomeDaColuna na cláusula ON. Essa sintaxe (chamada de nome qualificado) especifica as colunas de cada tabela que devem ser comparadas para unir as tabelas. A sintaxe “NomeDaTabela.” é requerida se as colunas tiverem o mesmo nome em ambas as tabelas. A mesma sintaxe pode ser utilizada em qualquer consulta para distinguir colunas em diferentes tabelas que tenham o mesmo nome. Em alguns sistemas, os nomes de tabela qualificados com o nome de banco de dados podem ser utilizados para realizar consultas entre vários bancos de dados. Como sempre, a consulta pode conter uma cláusula ORDER BY. A Figura 28.19 mostra os resultados da consulta anterior, ordenada por LastName e FirstName. [Nota: Para economizar espaço, dividimos o resultado da consulta em duas colunas, cada uma contendo as colunas FirstName, LastName e ISBN]. FirstName

LastName

ISBN

FirstName

LastName

ISBN

David Harvey Harvey Harvey Harvey Harvey Harvey Harvey

Choffnes Deitel Deitel Deitel Deitel Deitel Deitel Deitel

0131828274 0131869000 0131525239 0132222205 0131857576 0132404168 0131450913 0131828274

Paul Paul Paul Paul Paul Paul Paul Andrew

Deitel Deitel Deitel Deitel Deitel Deitel Deitel Goldberg

0131869000 0131525239 0132222205 0131857576 0132404168 0131450913 0131828274 0131450913

Figura 28.19  |  Amostragem de autores e ISBNs dos livros que eles escreveram em ordem crescente por LastName e FirstName.

908

Capítulo 28  Acesso a bancos de dados com o JDBC

Observação de engenharia de software Se uma instrução de SQL incluir colunas de múltiplas tabelas que tenham o mesmo nome, a instrução deve preceder esses nomes de coluna com seus nomes de tabela e um ponto ( por exemplo, Authors.AuthorID).

Erro comum de programação 28.5 Em uma consulta, a falha em qualificar nomes de colunas que tenham o mesmo nome em duas ou mais tabelas é um erro.

28.4.5  Instrução INSERT A instrução INSERT insere uma linha em uma tabela. A forma básica dessa instrução é INSERT INTO nomeDaTabela ( nomeDaColuna1, nomeDaColuna2, …, nomeDaColunaN ) VALUES ( valor1, valor2, …, valorN )

onde nomeDaTabela é a tabela na qual inserir a linha. O nomeDaTabela é seguido por uma lista de nomes de coluna separados por vírgulas entre parênteses (essa lista não é necessária se a operação INSERT especificar um valor para cada coluna da tabela na ordem correta). A lista de nomes de coluna é seguida pela palavra-chave de SQL VALUES e uma lista separada por vírgulas de valores entre parênteses. Os valores especificados aqui devem corresponder às colunas especificadas depois do nome de tabela tanto pela ordem como pelo tipo (por exemplo, se nomeDaColuna1 deve ser a coluna FirstName, então o valor1 deve ser uma string entre aspas simples que representa o nome). Sempre liste explicitamente as colunas ao inserir linhas. Se a ordem de coluna da tabela mudar ou uma nova coluna for adicionada, usar somente VALUES pode causar um erro. A instrução INSERT INSERT INTO Authors ( FirstName, LastName ) VALUES ( 'Sue', 'Smith' )

insere uma linha na tabela Authors. A instrução indica que os valores são fornecidos para as colunas FirstName e LastName. Os valores correspondentes são 'Sue' e 'Smith'. Não especificamos um AuthorID nesse exemplo porque AuthorID é uma coluna autoincementada na tabela Authors. Para cada linha adicionada a essa tabela, o DBMS atribui um único valor AuthorID que é o próximo valor na sequência autoincementada (isto é, 1, 2, 3 e assim por diante). Nesse caso, Sue Smith receberia o AuthorID número 5. A Figura 28.20 mostra a tabela Authors depois da operação INSERT. [Nota: Nem todo sistema de gerenciamento de bancos de dados suporta colunas autoincementadas. Verifique a documentação do seu DBMS para alternativas às colunas de autoincemento]. AuthorID

FirstName

LastName

1

Harvey

Deitel

2

Paul

Deitel

3

Andrew

Goldberg

4

David

Choffnes

5

Sue

Smith

Figura 28.20  |  Os dados de exemplo da tabela Authors depois de uma operação INSERT.

Erro comum de programação 28.6 É um erro especificar um valor para uma coluna autoincementada.

Erro comum de programação 28.7 O SQL delimita strings com aspas simples ('). Uma string que contém uma aspa simples (por exemplo, O’Malley) deve ter duas aspas simples na posição em que a aspa simples aparece ( por exemplo, 'O''Malley'). A primeira atua como um caractere de escape da segunda. Não “escapar” caracteres aspas simples em uma string que seja parte de uma instrução de SQL é um erro de sintaxe de SQL.

28.4.6  Instrução UPDATE Uma instrução UPDATE modifica os dados em uma tabela. Sua forma básica é



28.4  SQL

909

UPDATE nomeDaTabela SET nomeDaColuna1= valor1, nomeDaColuna2= valor2, …, nomeDaColunaN= valorN WHERE critérios

onde nomeDaTabela é a tabela a atualizar. O nomeDaTabela é seguido por uma palavra-chave SET e uma lista separada por vírgulas de pares nome-valor de coluna na forma nomeDaColuna = valor. A cláusula WHERE opcional fornece critérios que determinam quais linhas atualizar. Apesar de não ser necessária, a cláusula WHERE é geralmente utilizada, a menos que uma alteração seja feita em cada linha. A instrução UPDATE UPDATE Authors SET LastName = 'Jones' WHERE LastName = 'Smith' AND FirstName = 'Sue'

atualiza uma linha na tabela Authors. A instrução indica que LastName receberá o valor Jones para a linha em que LastName é igual a Smith e FirstName é igual a Sue. [Nota: Se houver múltiplas linhas com o nome “Sue” e o sobrenome “Smith,” essa instrução modificará todas essas linhas para ter o sobrenome “Jones”]. Se soubéssemos o AuthorID antes da operação UPDATE (possivelmente porque pesquisamos por ela anteriormente), a cláusula WHERE poderia ser simplificada da seguinte maneira: WHERE AuthorID = 5

AuthorID

FirstName

LastName

1

Harvey

Deitel

2

Paul

Deitel

3

Andrew

Goldberg

4

David

Choffnes

5

Sue

Jones

Figura 28.21  |  Os dados de exemplo da tabela Authors depois de uma operação UPDATE.

28.4.7  Instrução DELET Uma instrução DELETE de SQL remove as linhas de uma tabela. Sua forma básica é DELETE FROM nomeDaTabela

WHERE critérios

onde nomeDaTabela é a tabela a partir da qual excluir. A cláusula WHERE opcional especifica os critérios utilizados para determinar quais linhas excluir. Se essa cláusula for omitida, todas as linhas da tabela serão excluídas. A instrução DELETE DELETE FROM Authors WHERE LastName = 'Jones' AND FirstName = 'Sue'

exclui a linha para Sue Jones na tabela Authors. Se soubermos o AuthorID antes de a operação DELETE ocorrer, a cláusula WHERE pode ser simplificada assim: WHERE AuthorID = 5

A Figura 28.22 mostra a tabela Authors depois de a operação DELETE ter acontecido. AuthorID

FirstName

LastName

1

Harvey

Deitel

2

Paul

Deitel

3

Andrew

Goldberg

4

David

Choffnes

Figura 28.22  |  Os dados de exemplo da tabela Authors depois de uma operação INSERT.

910

Capítulo 28

Acesso a bancos de dados com o JDBC

28.5 Instruções para instalar o MySQL e o MySQL Conector/J MySQL 5.0 Community Edition é um sistema de gerenciamento de bancos de dados de código-fonte aberto que executa em muitas plataformas, incluindo Windows, Solaris, Linux e Macintosh. Informações completas sobre o MySQL estão disponíveis em www.mysql.com. Os exemplos das seções 28.8–28.9 manipulam bancos de dados MySQL.

Instale o MySQL Para instalar o MySQL Community Edition: 1. Para aprender os requisitos de instalação da sua plataforma, visite o site dev.mysql.com/doc/refman/5.0/en/installing­cs.html. 2. Visite dev.mysql.com/downloads/mysql/5.0.html e baixe o instalador da sua plataforma. Para os exemplos de MySQL neste capítulo, você só precisa do pacote Windows Essentials no Microsoft Windows, ou do pacote Standard na maioria das outras plataformas. [Nota: Para essas instruções, assumimos que você esteja executando o Microsoft Windows. As instruções de instalação completas de outras plataformas estão disponíveis em dev.mysql.com/doc/refman/5.0/en/installing.html.] 3. Dê um clique duplo em mysql­essential­5.0.67­win32.msi para iniciar o instalador. [Nota: Esse nome pode diferir de acordo com a versão atual do MySQL 5.0.] Clique em Next>. 4. Escolha Typical para o Setup Type e clique em Next >. Então clique em Install. Quando a instalação estiver completa, clique em Next> duas vezes e, depois em Finish para iniciar a configuração do servidor. Para configurar o servidor: 1. Clique em Next >, então selecione Standard Configuration e clique em Next > novamente. 2. Você tem a opção de instalar MySQL como um serviço Windows, o que permite que o servidor MySQL comece a executar automaticamente toda vez que o sistema iniciar. Para nossos exemplos, isso é desnecessário, portanto, desmarque Install as a Windows Service e marque Include Bin Directory in Windows PATH. Isso permitirá utilizar os comandos MySQL no Windows Command Prompt. 3. Clique em Next > e, então, clique em Execute para realizar a configuração de servidor. 4. Clique em Finish para fechar o assistente. Instale o MySQL Connector/J Para usar o MySQL com o JDBC, você também precisa instalar o MySQL Connector/J (o J significa Java) — um driver JDBC que permite que programas usem o JDBC para interagir com MySQL. O MySQL Connector/J pode ser baixado de dev.mysql.com/downloads/connector/j/5.1.html

A documentação do Conector/J encontra-se em dev.mysql.com/doc/connector/j/en/connector­j.html. Quando escrevíamos este livro, a versão estável atual do MySQL Connector/J era a versão 5.1.7. Para instalar o MySQL Connector/J: 1. O arquivo para download é mysql­connector­java­5.1.7.tar.gz. 2. Abra mysql­connector­java­5.1.7.tar.gz com um extrator de arquivos, como o WinZip (www.winzip.com). Extraia seu conteúdo para a unidade C:\. Isso criará um diretório chamado mysql­connector­java­5.1.7. O subdiretório docs dessa pasta contém a documentação do MySQL Connector/J (connector­j.pdf). Você também pode visualizá-lo on-line em dev.mysql.com/ doc/connector/j/en/connector­j.html.

28.6 Instruções para configuração de uma conta de usuário MySQL Para que os exemplos no livro executem corretamente, você precisa configurar uma conta de usuário que permita aos usuários criar, excluir e modificar um banco de dados. Depois que o MySQL estiver instalado, siga os passos abaixo para configurar uma conta de usuário (esses passos assumem que MySQL está instalado no diretório de instalação padrão): 1. Abra um prompt de comando e inicie o servidor do bancos de dados executando o comando mysqld­nt.exe. Observe que esse comando não tem saída — ele simplesmente inicia o servidor MySQL. Não feche essa janela — isso encerra o servidor. 2. Em seguida, você iniciará o monitor MySQL para poder configurar uma conta de usuário, abrir outro prompt de comando e executar o

comando mysql ­h localhost ­u root

A opção ­h indica o host (isto é, computador) no qual o servidor MySQL está executando — nesse caso o seu computador local (lo­ calhost). A opção ­u indica a conta de usuário que será utilizada para fazer login no servidor — root é a conta de usuário padrão

criada durante a instalação para permitir que você configure o servidor. Assim que você fizer o login, verá um prompt mysql> no qual pode digitar comandos para interagir com o servidor MySQL.

911

28.7 Criando banco de dados books no MySQL 3. No prompt mysql>, digite USE mysql;

para selecionar o banco de dados predefinido chamado mysql, que armazena informações sobre o servidor, como contas de usuário e seus privilégios para interagir com o servidor. Observe que cada comando deve terminar com um ponto-e-vírgula. Para confirmar o comando, MySQL emite a mensagem “Database changed.” 4. Em seguida, você adicionará a conta de usuário deitel ao banco de dados interno mysql. O banco de dados mysql contém uma

tabela chamada user com colunas que representam o nome do usuário, a senha e deitel com a senha deitel, execute os seguintes comandos do prompt mysql>:

vários privilégios. Para criar a conta de usuário

create user 'deitel'@'localhost' identified by 'deitel'; grant select, insert, update, delete, create, drop, references, execute on *.* to 'deitel'@'localhost';

Isso cria o usuário deitel com os privilégios necessários para criar os bancos de dados utilizados neste capítulo e manipulá-los. 5. Digite o comando exit;

para encerrar o monitor MySQL.

28.7 Criando banco de dados books no MySQL Para cada banco de dados MySQL que discutimos, fornecemos um script SQL em um arquivo .sql que configura o banco de dados e suas tabelas. Você pode executar esses scripts no monitor MySQL. No diretório de exemplos deste capítulo, você localizará o script books. sql para criar o banco de dados books. Para os próximos passos, supomos que o servidor MySQL (mysqld­nt.exe) ainda esteja rodando. Para executar o script books.sql: 1. Abra um prompt de comando e utilize o comando cd para mudar os diretórios para o local que contenha o script books.sql. 2. Inicie o monitor MySQL digitando mysql ­h localhost ­u deitel ­p

A opção ­p solicita a senha para a conta de usuário

deitel. Quando solicitado, insira a senha deitel.

3. Execute o script digitando source books.sql;

Isso cria um novo diretório chamado books no diretório data do servidor — localizado por padrão gram Files\MySQL\MySQL Server 5.0\data. Esse novo diretório contém o banco de dados books.

no Windows em

C:\Pro­

4. Digite o comando exit;

para terminar o monitor MySQL. Agora você está pronto para avançar para o primeiro exemplo de JDBC.

28.8 Manipulando bancos de dados com o JDBC Nesta seção, apresentamos dois exemplos. O primeiro exemplo introduz como se conectar a um banco de dados e consultá-lo. O segundo exemplo demonstra como exibir o resultado da consulta em uma JTable.

28.8.1 Consultando e conectando-se a um banco de dados O exemplo da Figura 28.23 realiza uma consulta simples no banco de dados books que recupera a tabela Authors inteira e exibe os dados. O programa ilustra a conexão com o banco de dados, consultando o banco de dados e processando o resultado. A seguinte discussão apresenta os aspectos-chave de JDBC do programa. [Nota: As seções 28.5–28.7 demonstram como iniciar o servidor MySQL, configurar uma conta de usuário e criar o banco de dados books. Esses passos devem ser realizados antes de executar o programa da Figura 28.23.] 1 2 3

// Figura 28.23: DisplayAuthors.java // Exibindo o conteúdo da tabela authors. import java.sql.Connection;

912 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70

Capítulo 28  Acesso a bancos de dados com o JDBC import import import import import

java.sql.Statement; java.sql.DriverManager; java.sql.ResultSet; java.sql.ResultSetMetaData; java.sql.SQLException;

public class DisplayAuthors { // URL do banco de dados static final String DATABASE_URL = "jdbc:mysql://localhost/books"; // carrega o aplicativo public static void main( { Connection connection Statement statement = ResultSet resultSet =

String args[] ) = null; // gerencia a conexão null; // instrução de consulta null; // gerencia resultados

// conecta-se ao banco de dados books e o consulta try { // estabelece uma conexão com o banco de dados connection = DriverManager.getConnection( DATABASE_URL, "deitel", "deitel" ); // cria Statement para consultar banco de dados statement = connection.createStatement(); // consulta o banco de dados resultSet = statement.executeQuery( "SELECT AuthorID, FirstName, LastName FROM Authors" ); // processa os resultados da consulta ResultSetMetaData metaData = resultSet.getMetaData(); int numberOfColumns = metaData.getColumnCount(); System.out.println( "Authors Table of Books Database:\n" ); for ( int i = 1; i ( tableModel ); resultTable.setRowSorter( sorter );

921

922 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196

Capítulo 28  Acesso a bancos de dados com o JDBC setSize( 500, 250 ); // configura o tamanho da janela setVisible( true ); // exibe a janela // cria o ouvinte para filterButton filterButton.addActionListener( new ActionListener() { // passa o texto do filtro para o ouvinte public void actionPerformed( ActionEvent e ) { String text = filterText.getText(); if ( text.length() == 0 ) sorter.setRowFilter( null ); else { try { sorter.setRowFilter( RowFilter.regexFilter( text ) ); } // fim do try catch ( PatternSyntaxException pse ) { JOptionPane.showMessageDialog( null, "Bad regex pattern", "Bad regex pattern", JOptionPane.ERROR_MESSAGE ); } // fim do catch } // fim de else } // fim do método actionPerfomed } // fim da classe interna anônima ); // fim da chamada para addActionListener } // fim do try catch ( SQLException sqlException ) { JOptionPane.showMessageDialog( null, sqlException.getMessage(), "Database error", JOptionPane.ERROR_MESSAGE ); // assegura que a conexão de banco de dados está fechada tableModel.disconnectFromDatabase(); System.exit( 1 ); // termina o aplicativo } // fim do catch // dispõe da janela quando o usuário fecha o aplicativo (isso sobrescreve // o padrão de HIDE_ON_CLOSE) setDefaultCloseOperation( DISPOSE_ON_CLOSE ); // assegura que a conexão de banco de dados é fechada quando usuário fecha o aplicativo addWindowListener( new WindowAdapter() { // desconecta-se do banco de dados e sai quando a janela for fechada public void windowClosed( WindowEvent event ) { tableModel.disconnectFromDatabase(); System.exit( 0 ); } // fim do método windowClosed } // fim da classe WindowAdapter interna ); // fim da chamada a addWindowListener } // fim do construtor DisplayQueryResults // executa o aplicativo public static void main( String args[] ) { new DisplayQueryResults(); } // fim de main } // fim da classe DisplayQueryResults



28.8  Manipulando bancos de dados com o JDBC

923

Figura 28.28  |  Exibindo o conteúdo do banco de dados books.

As linhas 27–29 e 32 declaram o URL, o nome de usuário, a senha e a consulta padrão que são passados ao construtor ResultSet­ para fazer a conexão inicial com o banco de dados e realizar a consulta padrão. O construtor DisplayQueryResults (linhas 38–189) cria um objeto ResultSetTableModel e a GUI do aplicativo. A linha 68 cria o objeto JTable e passa um objeto Re­ sultSetTable-Model para o construtor JTable, que então registra a JTable como um ouvinte para TableModelEvents gerados pelo ResultSetTable-Model. Observe que as variáveis locais filterText (linha 71) e sorter (linhas 126–127) são declaradas final. Essas duas são utilizadas a partir de um handler de evento que é implementado como uma classe interna anônima (linhas 134–158). Qualquer variável local que for utilizada em uma classe interna anônima deve ser declarada final; caso contrário, ocorre um erro de compilação. As linhas 85–124 registram um handler de evento para submitButton em que o usuário clica para submeter uma consulta para o banco de dados. Quando o usuário clica no botão, o método actionPerformed (linhas 90–122) invoca o método setQuery da classe ResultSetTableModel para executar a nova consulta (linha 95). Se a consulta do usuário falhar (por exemplo, por causa de um erro de sintaxe na entrada do usuário), as linhas 107–108 executam a consulta padrão. Se a consulta padrão também falhar, haverá um erro mais grave, então a linha 117 assegura que a conexão de banco de dados é fechada e a linha 119 fecha o programa. As capturas de tela na Figura 28.28 mostram os resultados de duas consultas. A primeira captura de tela mostra a consulta padrão que recupera todos os dados da tabela Authors do banco de dados books. A segunda captura de tela mostra uma consulta que seleciona o nome e o sobrenome de cada autor a partir da tabela Authors e combina essas informações com o título e número de edição da tabela Titles. Tente inserir suas próprias consultas na área de texto e clique no botão Submit Query para executar a consulta. No Java SE 6, JTables agora permitem a usuários classificarem linhas pelos dados em uma coluna específica. As linhas 126–127 utilizam a classe TableRowSorter (do pacote javax.swing.table) para criar um objeto que utiliza nosso ResultSetTableModel para classificar linhas na JTable que exibe resultados de consulta. Quando o usuário clicar no título de determinada coluna JTable, o TableRowSorter interage com o TableModel subjacente para reordenar as linhas com base nos dados dessa coluna. A linha 128 utiliza o método JTable setRowSorter para especificar o TableRowSorter para resultTable. TableModel

924

Capítulo 28

Acesso a bancos de dados com o JDBC

JTables podem mostrar agora subconjuntos dos dados do TableModel subjacente. Isso é conhecido como filtragem dos dados. As linhas 133–159 registram um handler de evento para o filterButton no qual o usuário clica para filtrar os dados. No método action­ Performed (linhas 137–157), a linha 139 obtém o texto de filtro. Se o usuário não tiver especificado o texto de filtro, a linha 142 utilizará o método JTable setRowFilter para remover algum filtro anterior configurando o filtro como null. Caso contrário, as linhas 147–148 utilizam setRowFilter para especificar um RowFilter (do pacote javax.swing) com base na entrada do usuário. A classe RowFilter fornece vários métodos para criar filtros. O método static regexFilter recebe uma String contendo um padrão de expressão regular como seu argumento e um conjunto opcional de índices que especifica quais colunas filtrar. Se nenhum índice for especificado, então todas as colunas são pesquisadas. Nesse exemplo, o padrão de expressão regular é o texto que o usuário digitou. Assim que o filtro for configurado, o dado exibido no JTable é atualizado com base no TableModel filtrado. As linhas 177–188 registram um WindowListener para o evento windowClosed, que ocorre quando o usuário fecha a janela. Como WindowListeners podem tratar vários eventos de janela, estendemos a classe WindowAdapter e sobrescrevemos apenas o handler de evento windowClosed.

28.9 Interface RowSet Nos exemplos anteriores, você aprendeu a consultar um banco de dados estabelecendo explicitamente uma Connection com o banco de dados, preparando uma Statement para consultar o banco de dados e executar a consulta. Nesta seção, demonstramos a interface RowSet, que configura a conexão de banco de dados e prepara automaticamente as instruções de consulta. A interface RowSet fornece vários métodos set que permitem especificar as propriedades necessárias para estabelecer uma conexão (como o URL do banco de dados, o nome de usuário e a senha do banco de dados) e criar uma Statement (como uma consulta). RowSet também fornece vários métodos get que retornam essas propriedades. Há dois tipos de objetos RowSet — conectado e desconectado. Um objeto RowSet conectado conecta-se ao banco de dados uma vez e permanece conectado enquanto o objeto estiver em uso. Um objeto RowSet desconectado conecta-se ao banco de dados, executa uma consulta para recuperar os dados do banco de dados e depois fecha a conexão. Um programa pode alterar os dados em um RowSet desconectado enquanto ele estiver desconectado. Dados modificados podem ser atualizados no banco de dados depois que um RowSet desconectado restabelecer a conexão com o banco de dados. O pacote javax.sql.rowset contém duas subinterfaces de RowSet — JdbcRowSet e CachedRowSet. JdbcRowSet, um RowSet conectado, atua como um empacotador em torno de um objeto ResultSet e permite aos programadores percorrer e atualizar as linhas no ResultSet. Lembre-se de que, por padrão, um objeto ResultSet é não rolável e apenas de leitura — você deve configurar explicitamente a constante do tipo result set como TYPE_SCROLL_INSENSITIVE e configurar a constante de concorrência do result set como CONCUR_UPDATABLE para fazer um objeto ResultSet rolável e atualizável. Um objeto JdbcRowSet é rolável e atualizável por padrão. CachedRowSet, um RowSet desconectado, armazena os dados em cache de um ResultSet na memória e desconecta-se do banco de dados. Como JdbcRowSet, um objeto CachedRowSet é rolável e atualizável por padrão. Um objeto CachedRowSet também é serializável, então ele pode ser passado entre aplicativos Java por uma rede, como a internet. Entretanto, CachedRowSet tem uma limitação — a quantidade de dados que pode ser armazenada na memória é limitada. O pacote javax.sql.rowset contém três outras subinterfaces de RowSet. Para obter detalhes dessas interfaces, visite java.sun.com/javase/6/docs/technotes/guides/jdbc/getstart/rowsetImpl.html.

Dica de portabilidade 28.5 Um RowSet pode fornecer a capacidade de rolagem para drivers que não suportam ResultSets roláveis.

A Figura 28.29 reimplementa o exemplo da Figura 28.23 utilizando um RowSet. Em vez de estabelecer a conexão e criar um State­ ment explicitamente, a Figura 28.29 utiliza um objeto JdbcRowSet para criar uma Connection e um Statement automaticamente.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

// Figura 28.29: JdbcRowSetTest.java // Exibindo o conteúdo da tabela authors com JdbcRowSet. import java.sql.ResultSetMetaData; import java.sql.SQLException; import javax.sql.rowset.JdbcRowSet; import com.sun.rowset.JdbcRowSetImpl; // Implementação JdbcRowSet da Sun public class JdbcRowSetTest { // nome do driver JDBC e URL do banco de dados static final String DATABASE_URL = "jdbc:mysql://localhost/books"; static final String USERNAME = "deitel"; static final String PASSWORD = "deitel"; // construtor conecta­se ao banco de dados, consulta banco de dados, processa // os resultados e os exibe na janela public JdbcRowSetTest()



28.9  Interface RowSet

18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63

925

{ // conecta-se ao banco de dados books e o consulta try { // especifica propriedades de JdbcRowSet JdbcRowSet rowSet = new JdbcRowSetImpl(); rowSet.setUrl( DATABASE_URL ); // configura o URL de banco de dados rowSet.setUsername( USERNAME ); // configura o nome de usuário rowSet.setPassword( PASSWORD ); // configura a senha rowSet.setCommand( "SELECT * FROM Authors" ); // configura a consulta rowSet.execute(); // executa a consulta // processa resultados da consulta ResultSetMetaData metaData = rowSet.getMetaData(); int numberOfColumns = metaData.getColumnCount(); System.out.println( "Authors Table of Books Database:\n" ); // exibe o cabeçalho rowset for ( int i = 1; i digite connect 'jdbc:derby:AddressBook;create=true;user=deitel; password=deitel';

a fim de criar o banco de dados AddressBook no diretório atual. Esse comando também cria o usuário deitel com a senha deitel para acessar o banco de dados. 7. Para criar a tabela de banco de dados e inserir dados de exemplo no banco de dados, digite run 'address.sql';

8. Para terminar a ferramenta de linha de comando Java DB, digite exit;

Você está pronto agora para executar o aplicativo AddressBook da Seção 28.11. Observe que o MySQL (ou qualquer outro banco de dados que suporte JDBC PreparedStatements) também pode ser utilizado na Seção 28.11.

28.11 PreparedStatements

927

28.11 PreparedStatements A interface

PreparedStatement

permite criar instruções SQL compiladas que executam mais eficientemente do que os objetos

Statement. PreparedStatements também podem especificar parâmetros, tornando-os mais flexíveis do que as Statements. Os progra-

mas podem executar a mesma consulta repetidamente com diferentes valores de parâmetro. Por exemplo, no banco de dados books, você poderia querer localizar todos os títulos de livro de um autor com um sobrenome e nome específico e poderia querer executar essa consulta para vários autores. Com uma PreparedStatement, essa consulta é definida como mostrado a seguir: PreparedStatement authorBooks = connection.prepareStatement( "SELECT LastName, FirstName, Title " + "FROM Authors INNER JOIN AuthorISBN " + "ON Authors.AuthorID=AuthorISBN.AuthorID " + "INNER JOIN Titles " + "ON AuthorISBN.ISBN=Titles.ISBN " + "WHERE LastName = ? AND FirstName = ?" );

Os dois pontos de interrogação (?) na linha última da instrução SQL anterior são um espaço reservado para valores que serão passados como parte da consulta ao banco de dados. Antes de executar uma PreparedStatement, o programa deve especificar os valores de parâmetro utilizando os métodos set da interface PreparedStatement. Para a consulta anterior, ambos os parâmetros são strings que podem ser configuradas com o método PreparedStatement setString como mostrado a seguir: authorBooks.setString( 1, "Deitel" ); authorBooks.setString( 2, "Paul" );

O primeiro argumento do método setString representa o número do parâmetro sendo configurado, e o segundo argumento é o valor desse parâmetro. Os números de parâmetro são contados a partir de 1, iniciando com o primeiro ponto de interrogação (?). Quando o programa executa a PreparedStatement anterior com os valores de parâmetro mostrados aqui, a SQL passada para o banco de dados é SELECT LastName, FirstName, Title FROM Authors INNER JOIN AuthorISBN ON Authors.AuthorID=AuthorISBN.AuthorID INNER JOIN Titles ON AuthorISBN.ISBN=Titles.ISBN WHERE LastName = 'Deitel' AND FirstName = 'Paul'

O método setString escapa automaticamente valores de parâmetro String conforme necessário. Por exemplo, se o sobrenome for O’Brien, a instrução authorBooks.setString( 1, "O'Brien" );

escapa o caractere ' em O’Brien substituindo-o por dois caracteres de aspa simples.

Dica de desempenho 28.2 PreparedStatements

são mais eficientes do que Statements ao executar instruções SQL múltiplas vezes e com diferentes valores

de parâmetro.

Dica de prevenção de erro 28.2 Utilize PreparedStatements com parâmetros de consultas que recebem valores String como argumentos para assegurar que as Strings sejam colocadas corretamente entre aspas na instrução SQL.

A interface PreparedStatement fornece métodos set para cada tipo SQL suportado. É importante utilizar o método set correto para o tipo SQL do parâmetro no banco de dados — SQLExceptions ocorrem quando um programa tenta converter um valor de parâmetro em um tipo incorreto. Para uma lista completa dos métodos set da interface PreparedStatement, consulte java.sun.com/javase/6/ docs/api/java/sql/PreparedStatement.html.

Aplicativo AddressBook que usa PreparedStatements Agora apresentamos um aplicativo de catálogo de endereços que permite navegar por entradas existentes, adicionar novas entradas e pesquisar por entradas com um sobrenome específico. O nosso banco de dados Java DB AddressBook contém uma tabela Addresses com as colunas addressID, FirstName, LastName, Email e PhoneNumber. A coluna addressID é a chamada coluna de identidade. Essa é a maneira padrão SQL para representar uma coluna autoincrementada. O script SQL que fornecemos para esse banco de dados utiliza a

928

Capítulo 28  Acesso a bancos de dados com o JDBC

palavra-chave IDENTITY de SQL para marcar a coluna addressID como uma coluna de identidade. Para obter mais informações sobre como utilizar a palavra-chave IDENTITY e criar bancos de dados, consulte o guia do desenvolvedor em Java DB em developers.sun. com/docs/javadb/10.4.2.0/devguide/derbydev.pdf. Nosso aplicativo de catálogo de endereços consiste em três classes — Person (Figura  28.30), PersonQueries (Figura  28.31) e Address­ BookDisplay (Figura 28.32). Person é uma classe simples que representa uma pessoa no catálogo de endereços. A classe contém campos para o ID de endereço, o nome, o sobrenome, o endereço de e-mail e o número de telefone, bem como os métodos set e get para manipular esses campos. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

// Figura 28.30: Person.java // Classe Person que representa uma entrada em um catálogo de endereços. public class Person { private int addressID; private String firstName; private String lastName; private String email; private String phoneNumber; // construtor sem argumento public Person() { } // fim do construtor sem argumentos da classe Stack // construtor public Person( int id, String first, String last, String emailAddress, String phone ) { setAddressID( id ); setFirstName( first ); setLastName( last ); setEmail( emailAddress ); setPhoneNumber( phone ); } // fim do construtor CommissionEmployee de cinco argumentos // configura o IDdeEndereço public void setAddressID( int id ) { addressID = id; } // fim do método setAddressID // retorna o IDdeEndereço public int getAddressID() { return addressID; } // fim do método getAddressID // estabelece o nome public void setFirstName( String first ) { firstName = first; } // fim do método setFirstName // retorna o nome public String getFirstName() { return firstName; } // fim do método getFirstName // configura o sobrenome public void setLastName( String last ) { lastName = last; } // fim do método setLastName // retorna o sobrenome public String getLastName() {

60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

28.11  PreparedStatements

929

return lastName; } // fim do método getLastName // configura o endereço de e-mail public void setEmail( String emailAddress ) { email = emailAddress; } // fim do método setEmail // retorna o endereço de e-mail public String getEmail() { return email; } // fim do método getEmail // configura o número de telefone public void setPhoneNumber( String phone ) { phoneNumber = phone; } // fim do método setPhoneNumber // retorna o número de telefone public String getPhoneNumber() { return phoneNumber; } // fim do método getPhoneNumber } // fim da classe Person

Figura 28.30  |  A classe Person que representa uma entrada em um AddressBook.

Classe PersonQueries A classe PersonQueries (Figura 28.31) gerencia a conexão de banco de dados do aplicativo de catálogo de endereços e cria as Pre­ paredStatements que o aplicativo utiliza para interagir com o banco de dados. As linhas 18–20 declaram três variáveis PreparedState­ ment. O construtor (linhas 23–49) conecta-se ao banco de dados nas linhas 27–28. As linhas 31–32 invocam o método Connection prepareStatement para criar a PreparedStatement chamada selectAll­ People que seleciona todas as linhas na tabela Addresses. As linhas 35–36 criam a PreparedStatement chamada selectPeopleBy­ LastName com um parâmetro. Essa instrução seleciona todas as linhas na tabela Addresses que correspondem a determinado sobrenome. Observe o caractere ? que é utilizado para especificar o parâmetro de sobrenome. As linhas 39–42 criam a PreparedStatement chamada insertNewPerson com quatro parâmetros que representam o nome, o sobrenome, o endereço de e-mail e o número de telefone de uma nova entrada. Novamente, observe os caracteres ? utilizados para representar esses parâmetros. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

// Figura 28.31: PersonQueries.java // PreparedStatements utilizadas pelo aplicativo AddressBook. import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.ArrayList; public class PersonQueries { private static final String URL = "jdbc:derby:AddressBook"; private static final String USERNAME = "deitel"; private static final String PASSWORD = "deitel"; private private private private

Connection connection = null; // gerencia a conexão PreparedStatement selectAllPeople = null; PreparedStatement selectPeopleByLastName = null; PreparedStatement insertNewPerson = null;

// construtor public PersonQueries()

930 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

Capítulo 28  Acesso a bancos de dados com o JDBC { try { connection = DriverManager.getConnection( URL, USERNAME, PASSWORD ); // cria a consulta que seleciona todas as entradas no AddressBook selectAllPeople = connection.prepareStatement( "SELECT * FROM Addresses" ); // cria a consulta que seleciona entradas com um sobrenome específico selectPeopleByLastName = connection.prepareStatement( "SELECT * FROM Addresses WHERE LastName = ?" ); // cria a inserção que adiciona uma nova entrada no banco de dados insertNewPerson = connection.prepareStatement( "INSERT INTO Addresses " + "( FirstName, LastName, Email, PhoneNumber ) " + "VALUES ( ?, ?, ?, ? )" ); } // fim do try catch ( SQLException sqlException ) { sqlException.printStackTrace(); System.exit( 1 ); } // fim do catch } // fim do construtor // seleciona todos os endereços no banco de dados public List< Person > getAllPeople() { List< Person > results = null; ResultSet resultSet = null; try { // executeQuery retorna ResultSet que contém entradas correspondentes resultSet = selectAllPeople.executeQuery(); results = new ArrayList< Person >(); while ( resultSet.next() ) { results.add( new Person( resultSet.getInt( "addressID" ), resultSet.getString( "FirstName" ), resultSet.getString( "LastName" ), resultSet.getString( "Email" ), resultSet.getString( "PhoneNumber" ) ) ); } // fim do while } // fim do try catch ( SQLException sqlException ) { sqlException.printStackTrace(); } // fim do catch finally { try { resultSet.close(); } // fim do try catch ( SQLException sqlException ) { sqlException.printStackTrace(); close(); } // fim do catch } // fim de finally return results; } // fim do método getAllPeople

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

28.11  PreparedStatements // seleciona a pessoa pelo sobrenome public List< Person > getPeopleByLastName( String name ) { List< Person > results = null; ResultSet resultSet = null; try { selectPeopleByLastName.setString( 1, name ); // especifica o sobrenome // executeQuery retorna ResultSet que contém entradas correspondentes resultSet = selectPeopleByLastName.executeQuery(); results = new ArrayList< Person >(); while ( resultSet.next() ) { results.add( new Person( resultSet.getInt( "addressID" ), resultSet.getString( "FirstName" ), resultSet.getString( "LastName" ), resultSet.getString( "Email" ), resultSet.getString( "PhoneNumber" ) ) ); } // fim do while } // fim do try catch ( SQLException sqlException ) { sqlException.printStackTrace(); } // fim do catch finally { try { resultSet.close(); } // fim do try catch ( SQLException sqlException ) { sqlException.printStackTrace(); close(); } // fim do catch } // fim de finally return results; } // fim do método getPeopleByName // adiciona uma entrada public int addPerson( String fname, String lname, String email, String num ) { int result = 0; // configura parâmetros e então executa insertNewPerson try { insertNewPerson.setString( 1, fname ); insertNewPerson.setString( 2, lname ); insertNewPerson.setString( 3, email ); insertNewPerson.setString( 4, num ); // insere a nova entrada; retorna o número de linhas atualizadas result = insertNewPerson.executeUpdate(); } // fim do try catch ( SQLException sqlException ) { sqlException.printStackTrace(); close(); } // fim do catch return result; } // fim do método addPerson

931

932 162 163 164 165 166 167 168 169 170 171 172 173 174 175

Capítulo 28  Acesso a bancos de dados com o JDBC

// fecha a conexão de banco de dados public void close() { try { connection.close(); } // fim do try catch ( SQLException sqlException ) { sqlException.printStackTrace(); } // fim do catch } // fim do método close } // fim da classe PersonQueries

Figura 28.31  |  PreparedStatements utilizadas pelo aplicativo de catálogo de endereços.

O método getAllPeople (linhas 52–91) executa o método selectAllPeople de PreparedStatement (linha 60) chamando o método executeQuery, que retorna um ResultSet contendo as linhas que correspondem à consulta (nesse caso, todas as linhas na tabela Addresses). As linhas 61–71 colocam os resultados da consulta em um ArrayList de objetos Person, que é retornado ao chamado na linha 90. O método getPeopleByLastName (linhas 94–135) utiliza o método PreparedStatement de setString para configurar o parâmetro para selectPeopleByLastName (linha 101). Então, a linha 104 executa a consulta e as linhas 106–115 colocam os resultados de consulta A linha 134 retorna a ArrayList ao chamador. O método addPerson (linhas 138–161) utiliza o método PreparedStatement setString (linhas 146–149) para configurar os parâmetros para o insertNewPerson PreparedStatement. A linha 152 utiliza o método PreparedStatement executeUpdate para inserir o novo registro. Esse método retorna um número inteiro que indica o número de linhas que foram atualizadas (ou inseridas) no banco de dados. O método close (linhas 164–174) simplesmente fecha a conexão de banco de dados.

Classe AddressBookDisplay O aplicativo AddressBookDisplay (Figura 28.32) utiliza um objeto PersonQueries para interagir com o banco de dados. A linha 59 cria o objeto PersonQueries. Quando o usuário pressionar o JButton Browse All Entries, o handler browseButtonAction­Performed (linhas 309–335) será chamado. A linha 313 chama o método getAllPeople no objeto PersonQueries para obter todas as entradas no banco de dados. O usuário então pode rolar pelas entradas utilizando os JButtons Previous e Next. Quando o usuário pressionar o JButton Find, o handler queryButtonActionPerformed (linhas 265–287) será chamado. As linhas 267–268 chamam o método getPeopleBy­ LastName no objeto PersonQueries para obter as entradas no banco de dados que correspondem ao sobrenome especificado. Se houver várias entradas, o usuário pode então rolar por elas utilizando os JButtons Previous e Next. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

// Figura 28.32: AddressBookDisplay.java // Um catálogo de endereços simples import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.FlowLayout; import java.awt.GridLayout; import java.util.List; import javax.swing.JButton; import javax.swing.Box; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.WindowConstants; import javax.swing.BoxLayout; import javax.swing.BorderFactory; import javax.swing.JOptionPane; public class AddressBookDisplay extends JFrame { private Person currentEntry; private PersonQueries personQueries; private List< Person > results; private int numberOfEntries = 0; private int currentEntryIndex;

28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98

28.11  PreparedStatements

private private private private private private private private private private private private private private private private private private private private private private private

JButton browseButton; JLabel emailLabel; JTextField emailTextField; JLabel firstNameLabel; JTextField firstNameTextField; JLabel idLabel; JTextField idTextField; JTextField indexTextField; JLabel lastNameLabel; JTextField lastNameTextField; JTextField maxTextField; JButton nextButton; JLabel ofLabel; JLabel phoneLabel; JTextField phoneTextField; JButton previousButton; JButton queryButton; JLabel queryLabel; JPanel queryPanel; JPanel navigatePanel; JPanel displayPanel; JTextField queryTextField; JButton insertButton;

// construtor sem argumento public AddressBookDisplay() { super( "Address Book" ); // estabelece a conexão de banco de dados e configura PreparedStatements personQueries = new PersonQueries(); // cria a GUI navigatePanel = new JPanel(); previousButton = new JButton(); indexTextField = new JTextField( 2 ); ofLabel = new JLabel(); maxTextField = new JTextField( 2 ); nextButton = new JButton(); displayPanel = new JPanel(); idLabel = new JLabel(); idTextField = new JTextField( 10 ); firstNameLabel = new JLabel(); firstNameTextField = new JTextField( 10 ); lastNameLabel = new JLabel(); lastNameTextField = new JTextField( 10 ); emailLabel = new JLabel(); emailTextField = new JTextField( 10 ); phoneLabel = new JLabel(); phoneTextField = new JTextField( 10 ); queryPanel = new JPanel(); queryLabel = new JLabel(); queryTextField = new JTextField( 10 ); queryButton = new JButton(); browseButton = new JButton(); insertButton = new JButton(); setLayout( new FlowLayout( FlowLayout.CENTER, 10, 10 ) ); setSize( 400, 300 ); setResizable( false ); navigatePanel.setLayout( new BoxLayout( navigatePanel, BoxLayout.X_AXIS ) ); previousButton.setText( "Previous" ); previousButton.setEnabled( false ); previousButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent evt )

933

934 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167

Capítulo 28  Acesso a bancos de dados com o JDBC { previousButtonActionPerformed( evt ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener navigatePanel.add( previousButton ); navigatePanel.add( Box.createHorizontalStrut( 10 ) ); indexTextField.setHorizontalAlignment( JTextField.CENTER ); indexTextField.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent evt ) { indexTextFieldActionPerformed( evt ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener navigatePanel.add( indexTextField ); navigatePanel.add( Box.createHorizontalStrut( 10 ) ); ofLabel.setText( "of" ); navigatePanel.add( ofLabel ); navigatePanel.add( Box.createHorizontalStrut( 10 ) ); maxTextField.setHorizontalAlignment( JTextField.CENTER ); maxTextField.setEditable( false ); navigatePanel.add( maxTextField ); navigatePanel.add( Box.createHorizontalStrut( 10 ) ); nextButton.setText( "Next" ); nextButton.setEnabled( false ); nextButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent evt ) { nextButtonActionPerformed( evt ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener navigatePanel.add( nextButton ); add( navigatePanel ); displayPanel.setLayout( new GridLayout( 5, 2, 4, 4 ) ); idLabel.setText( "Address ID:" ); displayPanel.add( idLabel ); idTextField.setEditable( false ); displayPanel.add( idTextField ); firstNameLabel.setText( "First Name:" ); displayPanel.add( firstNameLabel ); displayPanel.add( firstNameTextField ); lastNameLabel.setText( "Last Name:" ); displayPanel.add( lastNameLabel ); displayPanel.add( lastNameTextField ); emailLabel.setText( "Email:" ); displayPanel.add( emailLabel ); displayPanel.add( emailTextField );

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236

28.11  PreparedStatements phoneLabel.setText( "Phone Number:" ); displayPanel.add( phoneLabel ); displayPanel.add( phoneTextField ); add( displayPanel ); queryPanel.setLayout( new BoxLayout( queryPanel, BoxLayout.X_AXIS) ); queryPanel.setBorder( BorderFactory.createTitledBorder( "Find an entry by last name" ) ); queryLabel.setText( "Last Name:" ); queryPanel.add( Box.createHorizontalStrut( 5 ) ); queryPanel.add( queryLabel ); queryPanel.add( Box.createHorizontalStrut( 10 ) ); queryPanel.add( queryTextField ); queryPanel.add( Box.createHorizontalStrut( 10 ) ); queryButton.setText( "Find" ); queryButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent evt ) { queryButtonActionPerformed( evt ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener queryPanel.add( queryButton ); queryPanel.add( Box.createHorizontalStrut( 5 ) ); add( queryPanel ); browseButton.setText( "Browse All Entries" ); browseButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent evt ) { browseButtonActionPerformed( evt ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener add( browseButton ); insertButton.setText( "Insert New Entry" ); insertButton.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent evt ) { insertButtonActionPerformed( evt ); } // fim do método actionPerformed } // fim da classe interna anônima ); // fim da chamada para addActionListener add( insertButton ); addWindowListener( new WindowAdapter() { public void windowClosing( WindowEvent evt ) { personQueries.close(); // fecha a conexão System.exit( 0 ); } // fim do método windowClosing } // fim da classe interna anônima ); // fim da chamada para addWindowListener

935

936 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305

Capítulo 28  Acesso a bancos de dados com o JDBC setVisible( true ); } // fim do construtor sem argumentos // trata a chamada quando previousButton é clicado private void previousButtonActionPerformed( ActionEvent evt ) { currentEntryIndex--; if ( currentEntryIndex < 0 ) currentEntryIndex = numberOfEntries - 1; indexTextField.setText( "" + ( currentEntryIndex + 1 ) ); indexTextFieldActionPerformed( evt ); } // fim do método previousButtonActionPerformed // trata a chamada quando nextButton é clicado private void nextButtonActionPerformed( ActionEvent evt ) { currentEntryIndex++; if ( currentEntryIndex >= numberOfEntries ) currentEntryIndex = 0; indexTextField.setText( "" + ( currentEntryIndex + 1 ) ); indexTextFieldActionPerformed( evt ); } // fim do método nextButtonActionPerformed // trata a chamada quando queryButton é clicado private void queryButtonActionPerformed( ActionEvent evt ) { results = personQueries.getPeopleByLastName( queryTextField.getText() ); numberOfEntries = results.size(); if ( numberOfEntries != 0 ) { currentEntryIndex = 0; currentEntry = results.get( currentEntryIndex ); idTextField.setText( "" + currentEntry.getAddressID() ); firstNameTextField.setText( currentEntry.getFirstName() ); lastNameTextField.setText( currentEntry.getLastName() ); emailTextField.setText( currentEntry.getEmail() ); phoneTextField.setText( currentEntry.getPhoneNumber() ); maxTextField.setText( "" + numberOfEntries ); indexTextField.setText( "" + ( currentEntryIndex + 1 ) ); nextButton.setEnabled( true ); previousButton.setEnabled( true ); } // fim do if else browseButtonActionPerformed( evt ); } // fim do método queryButtonActionPerformed // trata a chamada quando um novo valor é inserido em indexTextField private void indexTextFieldActionPerformed( ActionEvent evt ) { currentEntryIndex = ( Integer.parseInt( indexTextField.getText() ) - 1 ); if ( numberOfEntries != 0 && currentEntryIndex < numberOfEntries ) { currentEntry = results.get( currentEntryIndex ); idTextField.setText("" + currentEntry.getAddressID() ); firstNameTextField.setText( currentEntry.getFirstName() ); lastNameTextField.setText( currentEntry.getLastName() ); emailTextField.setText( currentEntry.getEmail() ); phoneTextField.setText( currentEntry.getPhoneNumber() ); maxTextField.setText( "" + numberOfEntries ); indexTextField.setText( "" + ( currentEntryIndex + 1 ) ); } // fim do if

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359

28.11  PreparedStatements } // fim do método indexTextFieldActionPerformed // trata a chamada quando browseButton é clicado private void browseButtonActionPerformed( ActionEvent evt ) { try { results = personQueries.getAllPeople(); numberOfEntries = results.size(); if ( numberOfEntries != 0 ) { currentEntryIndex = 0; currentEntry = results.get( currentEntryIndex ); idTextField.setText( "" + currentEntry.getAddressID() ); firstNameTextField.setText( currentEntry.getFirstName() ); lastNameTextField.setText( currentEntry.getLastName() ); emailTextField.setText( currentEntry.getEmail() ); phoneTextField.setText( currentEntry.getPhoneNumber() ); maxTextField.setText( "" + numberOfEntries ); indexTextField.setText( "" + ( currentEntryIndex + 1 ) ); nextButton.setEnabled( true ); previousButton.setEnabled( true ); } // fim do if } // fim do try catch ( Exception e ) { e.printStackTrace(); } // fim do catch } // fim do método browseButtonActionPerformed // trata a chamada quando insertButton é clicado private void insertButtonActionPerformed( ActionEvent evt ) { int result = personQueries.addPerson( firstNameTextField.getText(), lastNameTextField.getText(), emailTextField.getText(), phoneTextField.getText() ); if ( result == 1 ) JOptionPane.showMessageDialog( this, "Person added!", "Person added", JOptionPane.PLAIN_MESSAGE ); else JOptionPane.showMessageDialog( this, "Person not added!", "Error", JOptionPane.PLAIN_MESSAGE ); browseButtonActionPerformed( evt ); } // fim do método insertButtonActionPerformed // método main public static void main( String args[] ) { new AddressBookDisplay(); } // fim do método main } // fim da classe AddressBookDisplay

937

938

Capítulo 28

Acesso a bancos de dados com o JDBC

Figura 28.32 | Um catálogo de endereços simples.

Para adicionar uma nova entrada no banco de dados AddressBook, o usuário pode inserir o nome, o sobrenome, o e-mail e o número de telefone (o AddressID se autoincrementará) nos JTextFields e pressionar o JButton Insert New Entry. Quando o usuário pressiona Insert New Entry, o handler insertButtonActionPerformed (linhas 338–352) é chamado. As linhas 340–342 chamam o método addPerson no objeto PersonQueries para adicionar uma nova entrada ao banco de dados. A linha 351 chama browseButtonAction­ Performed para obter o conjunto atualizado de pessoas no catálogo de endereços e atualizar a GUI apropriadamente. O usuário então pode examinar as diferentes entradas pressionando o JButton Previous ou o JButton Next, que resulta em chamadas aos métodos previousButtonActionPerformed (linhas 241–250) ou nextButtonActionPerformed (linhas 253–262), respectivamente. Alternativamente, o usuário pode inserir um número no indexTextField e pressionar Enter para examinar determinada entrada. Isso resulta em uma chamada ao método indexTextFieldActionPerformed (linhas 290–306) para exibir o registro especificado.

28.12 Procedures armazenadas Muitos sistemas de gerenciamento de bancos de dados podem armazenar instruções de SQL individuais ou conjuntos de instruções de SQL em um banco de dados, de modo que os programas que acessam esse banco de dados possam invocá-las. Essas coleções de SQL nomeadas são chamadas de procedures armazenadas. O JDBC permite que os programas invoquem procedures armazenadas utilizando objetos que implementam a interface Callable­Statement. Callable­Statements podem receber argumentos especificados com os métodos herdados da interface PreparedStatement. Além disso, CallableStatements podem especificar parâmetros de saída nos quais uma procedure armazenada pode colocar valores de retorno. A interface CallableStatement inclui métodos para especificar quais parâmetros em uma procedure armazenada são parâmetros de saída. A interface também inclui métodos para obter os valores de parâmetros de saída retornados de uma procedure armazenada. Para aprender mais sobre CallableStatements, visite java.sun.com/javase/6/docs/technotes/guides/jdbc/getstart/ callablestatement.html#999652

Dica de portabilidade 28.6 Embora a sintaxe para criar procedures armazenadas seja diferente entre sistemas de gerenciamento de bancos de dados, a interface CallableStatement fornece uma interface uniforme para especificar os parâmetros de entrada e saída para procedures armazenadas e para invocar procedures armazenadas.

28.13 Processamento de transações

939

Dica de portabilidade 28.7 De acordo com a documentação da Java API para interface CallableStatement, para a máxima portabilidade entre sistemas de banco de dados, os programas devem processar as contagens de atualização ou os ResultSets retornados de um CallableStatement antes de obter os valores de qualquer parâmetro de saída.

28.13 Processamento de transações Muitos aplicativos do banco de dados impõem garantias de que uma série de inserções, atualizações e exclusões no banco de dados executem corretamente antes de os aplicativos continuarem o processamento da próxima operação de banco de dados. Por exemplo, quando você transfere dinheiro eletronicamente entre contas bancárias, vários fatores determinam se a transação é bem-sucedida. Você começa especificando a conta de origem e a quantia que deseja transferir dessa conta para uma conta de destino. Depois, especifica a conta de destino. O banco verifica a conta de origem para determinar se os fundos são suficientes para completar a transferência. Nesse caso, o banco retira a quantia especificada e, se tudo correr bem o deposita na conta de destino para concluir a transferência. O que acontece se a transferência falha depois de o banco retirar o dinheiro da conta de origem? Em um sistema bancário real, o banco deposita novamente o dinheiro na conta de origem. Como você se sentiria se o dinheiro fosse retirado da sua conta de origem e o banco não o depositasse na conta de destino? O processamento de transação permite que um programa que interage com um banco de dados trate uma operação de banco de dados (ou conjunto de operações) como uma operação única. Essa operação também é conhecida como operação atômica ou transação. No fim de uma transação, pode-se tomar a decisão de confirmar a transação ou reverter a transação. Confirmar a transação finaliza a(s) operação(ões) do banco de dados; todas as inserções, atualizações e exclusões realizadas como parte da transação não podem ser revertidas sem realizar uma nova operação de banco de dados. A reversão da transação deixa o banco de dados no seu estado anterior à operação de banco de dados. Isso é útil quando parte de uma transação não consegue ser completada corretamente. Na nossa discussão sobre transferência da conta bancária, a transação seria revertida se o depósito não pudesse ser feito na conta de destino. O Java fornece processamento de transações por meio de métodos da interface Connection. O método setAutoCommit especifica se cada instrução SQL é confirmada depois de ser completada (um argumento true) ou se várias instruções SQL devem ser agrupadas como uma transação (um argumento false). Se o argumento para setAutoCommit for false, o programa deve seguir a última instrução SQL na transação com uma chamada ao método Connection commit (para confirmar as modificações no banco de dados) ou método Connection rollback (para retornar o banco de dados ao estado anterior da transação). A interface Connection também fornece o método getAutoCommit para determinar o estado autocommit para Connection.

28.14 Conclusão Neste capítulo, você aprendeu os conceitos básicos de banco de dados, a interagir com dados em um banco de dados utilizando SQL e a utilizar JDBC para permitir que os aplicativos Java manipulem dados de banco de dados. Aprendeu sobre os comandos SQL SELECT, INSERT, UPDATE e DELETE, bem como cláusulas, como WHERE, ORDER BY e INNER JOIN. Conheceu os passos explícitos para obter uma Connection com o banco de dados, para criar uma Statement para interagir com os dados do banco de dados, executar a instrução e processar os resultados. Então utilizou um RowSet para simplificar o processo de conexão com um banco de dados e criar instruções. Utilizou Pre­ paredStatements para criar instruções SQL precompiladas. Também aprendeu a criar e configurar bancos de dados tanto no Java DB como no MySQL utilizando scripts SQL predefinidos. Também fornecemos visões gerais sobre as CallableStatements e o processamento de transação. No próximo capítulo, você aprenderá sobre o desenvolvimento de aplicativos Web com o JavaServer Faces.

28.15 Recursos da Web java.sun.com/javase/technologies/database/index.jsp

Sun Microsystems, Java SE database technologies home page. java.sun.com/docs/books/tutorial/jdbc/index.html

Trilha JDBC do The Java Tutorial. developers.sun.com/product/jdbc/drivers

O sistema de pesquisa da Sun Microsystems para localizar drivers JDBC. www.sql.org

Esse portal de SQL fornece links para muitos recursos, incluindo sintaxe de SQL, dicas, tutoriais, livros, revistas, grupos de discussão, empresas com serviços de SQL, consultores de SQL e softwares gratuitos. www.datadirect.com/developer/jdbc/topics/perfoptjdbc/index.ssp

Artigo que discute como projetar um bom aplicativo JDBC.

940

Capítulo 28  Acesso a bancos de dados com o JDBC

java.sun.com/javase/6/docs/technotes/guides/jdbc/index.html

A documentação do JDBC API da Sun Microsystems. www.mysql.com

Esse site é a home page do banco de dados MySQL. Você pode fazer download das últimas versões de MySQL e MySQL Connector/J e acessar sua documentação on-line. dev.mysql.com/doc/refman/5.0/en/index.html

Manual de referência de MySQL. java.sun.com/javase/6/docs/technotes/guides/jdbc/getstart/rowsetImpl.html

Visão geral da interface RowSet e suas subinterfaces. Esse site também discute as implementações de referência dessas interfaces da Sun e sua utilização. java.sun.com/developer/Books/JDBCTutorial/chapter5.html

O Capítulo 5 (Tutorial RowSet) do livro The JDBC 2.0 API Tutorial and Reference, Second Edition.

Resumo Seção 28.1  Introdução • Um banco de dados é uma coleção integrada de dados. Um sistema de gerenciamento de bancos de dados (Database Management System — DBMS) fornece mecanismos para armazenar, organizar, recuperar e modificar dados para muitos usuários. • Os sistemas de gerenciamento de bancos de dados mais populares de hoje são sistemas de bancos de dados relacionais. • SQL é a linguagem padrão internacional utilizada para consultar e manipular dados relacionais. • Os programas conectam-se aos bancos de dados relacionais e interagem com estes por meio de uma interface — o software que facilita comunicações entre um sistema de gerenciamento de bancos de dados e um programa. • Um driver JDBC permite que os aplicativos Java se conectem a um banco de dados em um DBMS particular e permite aos programadores recuperar e manipular dados de banco de dados.

Seção 28.2  Bancos de dados relacionais • Um banco de dados relacional armazena dados em tabelas. As tabelas são compostas de linhas, e as linhas são compostas de colunas nas quais os valores são armazenados. • Uma chave primária fornece um valor único que não pode ser duplicado em outras linhas da mesma tabela. • Cada coluna de uma tabela representa um atributo diferente. • A chave primária pode ser composta de mais de uma coluna. • Cada coluna em uma chave primária deve ter um valor, e o valor da chave primária deve ser único. Isso é conhecido como Regra de Integridade de Entidade. • Um relacionamento de um para muitos entre tabelas indica que uma linha em uma tabela pode ter muitas linhas relacionadas em uma tabela separada. • Uma chave estrangeira é uma coluna em uma tabela que corresponde à coluna de chave primária em outra tabela. Isso é conhecido como Regra de Integridade de Entidade. • As chaves estrangeiras permitem que as informações de múltiplas tabelas sejam unidas. Há um relacionamento um para muitos entre uma chave primária e sua chave estrangeira correspondente.

Seção 28.4.1  Consulta SELECT básica  • A forma básica de uma consulta é SELECT * FROM

nomeDaTabela

onde o asterisco (*) indica que todas as colunas de nomeDaTabela devem ser selecionadas e nomeDaTabela especifica a tabela no banco de dados a partir da qual as linhas serão recuperadas. • Para recuperar colunas específicas, substitua o

* por uma lista separada por vírgulas de nomes de coluna.

Seção 28.4.2  Cláusula WHERE • A cláusula WHERE opcional em uma consulta especifica os critérios de seleção da consulta. A forma básica de uma consulta com critérios de seleção é SELECT

nomeDaColuna1, nomeDaColuna2, …

FROM

nomeDaTabela

WHERE

critérios

• A cláusula WHERE pode conter os operadores , =, =, e LIKE. O operador LIKE é utilizado para correspondência de string de padrão com os curingas porcentagem (%) e sublinhado (_).



Resumo

941

• Um caractere de porcentagem (%) em um padrão indica que uma string que corresponde ao padrão tem zero ou mais caracteres na posição do caractere de porcentagem no padrão. • Um sublinhado (_) na string de padrão indica um único caractere nessa posição do padrão.

Seção 28.4.3 Cláusula ORDER BY • O resultado de uma consulta pode ser classificado com a cláusula ORDER BY. A forma mais simples de uma cláusula ORDER BY é SELECT SELECT

nomeDaColuna1, nomeDaColuna2, … nomeDaColuna1, nomeDaColuna2, …

FROM FROM

nomeDaTabela nomeDaTabela

ORDER BY ORDER BY

coluna coluna

ASC DESC

onde ASC especifica a ordem crescente, DESC especifica a ordem decrescente e coluna especifica a coluna em que a classificação é baseada. A ordem de classificação padrão é crescente, então ASC é opcional. • Múltiplas colunas podem ser utilizadas para fins de ordenação com uma cláusula ORDER BY da forma ORDER BY

coluna1 ordemDeClassificação, coluna2 ordemDeClassificação, …

• As cláusulas WHERE e ORDER BY podem ser combinadas em uma consulta. Se utilizada, ORDER

BY deve ser a última cláusula na consulta.

25.4.4 Mesclando dados a partir de múltiplas tabelas: INNER JOIN • Uma INNER JOIN mescla linhas de duas tabelas correspondendo valores em colunas que são comuns às tabelas. A forma básica do operador INNER JOIN é: SELECT nomeDaColuna1, nomeDaColuna2, … FROM tabela1 INNER JOIN tabela2 ON tabela1.nomeDaColuna = tabela2.nomeDaColuna

A cláusula ON especifica as colunas de cada tabela que são comparadas para determinar as linhas que são unidas. Se uma instrução de SQL utiliza colunas com o mesmo nome de múltiplas tabelas, os nomes de coluna devem ser completamente qualificados prefixando-os com seus nomes de tabela e um ponto (.).

Seção 28.4.5  Instrução INSERT • Uma instrução INSERT insere uma nova linha em uma tabela. A forma básica dessa instrução é INSERT INTO VALUES (

nomeDaTabela ( nomeDaColuna1, nomeDaColuna2, …, nomeDaColunaN valor1, valor2, …, valorN )

)

onde nomeDaTabela é a tabela na qual inserir a linha. O nomeDaTabela é seguido por uma lista separada por vírgulas de nomes de coluna entre parênteses. A lista de nomes de coluna é seguida pela palavra-chave de SQL VALUES e uma lista separada por vírgulas de valores entre parênteses. • SQL utiliza aspa simples (') para delimitar strings. Para especificar uma string que contém uma aspa simples em SQL, escape a aspa simples com outra aspa simples (isto é, '').

Seção 28.4.6  Instrução UPDATE • Uma instrução UPDATE modifica os dados em uma tabela. A forma básica de uma instrução UPDATE é UPDATE nomeDaTabela SET nomeDaColuna1 = valor1, WHERE critérios

nomeDaColuna2 = valor2, …, nomeDaColunaN = valorN

onde nomeDaTabela é a tabela em que atualizar os dados. O nomeDaTabela é seguido pela palavra-chave SET e uma lista separada por vírgulas de pares nomeDaColuna = valor. Os critérios da cláusula WHERE opcional determinam que linhas atualizar.

Seção 28.4.7 Instrução DELETE • Uma instrução DELETE remove as linhas de uma tabela. A forma mais simples de uma instrução DELETE é DELETE FROM

nomeDaTabela

WHERE

critérios

onde nomeDaTabela é a tabela a partir da qual excluir uma linha (ou linhas). Os critérios WHERE opcionais determinam que linhas excluir. Se essa cláusula for omitida, todas as linhas da tabela serão excluídas.

25.8.1 Consultando e conectando-se a um banco de dados • O pacote java.sql contém as classes e interfaces para acessar bancos de dados relacionais em Java. • Um objeto que implementa a interface Connection gerencia a conexão entre um programa Java e um banco de dados. Os objetos Connection permitem aos programas criar instruções de SQL que acessam dados. • O método DriverManager getConnection tenta conectar-se a um banco de dados em um URL que especifica o protocolo para a comunicação, o subprotocolo da comunicação e o nome de banco de dados. • O método Connection createStatement cria um objeto Statement, que pode ser utilizado para enviar instruções SQL para o banco de dados. • O método Statement executeQuery executa uma consulta e retorna um objeto que implementa a interface ResultSet que contém o resultado de consulta. Os métodos ResultSet permitem a um programa manipular resultados de consulta.

942

Capítulo 28  Acesso a bancos de dados com o JDBC

• Um objeto ResultSetMetaData descreve o conteúdo de um ResultSet. Os programas podem utilizar metadados programaticamente para obter informações sobre os nomes de coluna e tipos de ResultSet. • O método ResultSetMetaData getColumnCount recupera o número de colunas ResultSet. • O método ResultSet next posiciona o cursor ResultSet na próxima linha e retorna true se a linha existir; caso contrário, retorna false. Esse método deve ser chamado para começar o processamento de um ResultSet porque o cursor está inicialmente posicionado antes da primeira linha. • É possível extrair cada coluna ResultSet como um tipo Java específico. O método ResultSetMetaData getColumnType retorna uma constante Types (pacote java.sql) para indicar o tipo da coluna. • Os métodos ResultSet get em geral recebem como um argumento um número de coluna (como um int) ou um nome de coluna (como uma String) indicando que valor da coluna obter. • Os números de linha e coluna de ResultSet iniciam em 1. • Todo objeto Statement pode abrir apenas um objeto ResultSet por vez. Quando um Statement retorna um novo ResultSet, Statement fecha o ResultSet anterior. • O método Connection createStatement tem uma versão sobrecarregada que recebe o tipo de resultado e a concorrência de resultado. O tipo de resultado especifica se o cursor de ResultSet é capaz de rolar em ambas as direções ou apenas avançar e se o ResultSet é sensível ou não às alterações. A concorrência de resultado especifica se o ResultSet pode ser atualizado com os métodos de atualização do ResultSet. • Alguns drivers JDBC não suportam ResultSets roláveis ou atualizáveis.

Seção 28.8.2 Consultando o banco de dados books • O método TableModel getColumnClass retorna um objeto Class que representa a superclasse de todos os objetos em determinada coluna. A JTable utiliza essas informações para configurar o renderizador de célula e editor de célula padrão para essa coluna na JTable. • O método ResultSetMetaData getColumnClassName obtém o nome de classe totalmente qualificado de uma coluna. • O método TableModel getColumnCount retorna o número de colunas no ResultSet subjacente. • O método TableModel getNomeDaColuna retorna o nome de coluna no ResultSet subjacente. • O método ResultSetMetaData getNomeDaColuna obtém o nome de coluna do ResultSet. • O método TableModel getRowCount retorna o número de linhas no modelo ResultSet. • O método TableModel getValueAt retorna o Object em uma linha e coluna particulares do ResultSet subjacente do modelo. • O método ResultSet absolute posiciona o cursor ResultSet em uma linha específica. • O método AbstractTableModel fireTableStructureChanged notifica qualquer JTable utilizando um objeto TableModel particular como seu modelo que os dados no modelo alterou.

Seção 28.9  Interface RowSet • A interface RowSet configura a conexão de banco de dados e executa a consulta automaticamente. • Um RowSet conectado permanece conectado ao banco de dados enquanto o objeto estiver em uso. Um RowSet desconectado conecta, executa uma consulta e, então, fecha a conexão. • JdbcRowSet, um RowSet conectado, empacota um objeto ResultSet e permite que você role e atualize as linhas. Ao contrário de um objeto Result­ Set, um objeto JdbcRowSet é rolável e atualizável por padrão. • CachedRowSet, um RowSet desconectado, armazena em cache dados de um ResultSet na memória. Um CachedRowSet é rolável e atualizável. Um CachedRowSet também é serializável.

Seção 28.10  Java DB/Apache Derby • No JDK 6, a Sun Microsystems distribui o código-fonte aberto, o banco de dados “puro Java” chamado Java DB (a versão com a marca Sun do Apache Derby) juntamente com o JDK. • O Java DB tem uma versão incorporada e uma versão de rede.

Seção 28.11  PreparedStatements • PreparedStatements são compiladas, portanto, executam mais eficientemente do que as Statements. • PreparedStatements podem ter parâmetros, portanto, a mesma consulta pode executar com diferentes argumentos. • Um parâmetro é especificado com um sinal de interrogação (?) na instrução SQL. Antes de executar uma PreparedStatement, você deve utilizar os métodos set da PreparedStatement para especificar os argumentos. • O primeiro argumento do método PreparedStatement setString representa o número do parâmetro sendo configurado e o segundo argumento é o valor desse parâmetro. • Os números de parâmetro são contados a partir de 1, iniciando com o primeiro ponto de interrogação (?). • O método setString escapa automaticamente valores de parâmetro String conforme necessário. • A interface PreparedStatement fornece métodos set para cada tipo SQL suportado.



Terminologia

943

• Uma coluna de identidade é a maneira padrão do SQL de representar uma coluna autoincrementada. A palavra-chave IDENTITY SQL marca uma coluna como uma coluna de identidade.

Seção 28.12  Procedures armazenadas • O JDBC permite que programas invoquem procedimentos armazenados (ou stored procedures) para utilizar objetos CallableStatement. • CallableStatement pode especificar parâmetros de entrada. CallableStatement pode especificar parâmetros de saída nos quais um procedimento armazenado pode colocar valores de retorno.

Seção 28.13  Processamento de transações • O processamento de transação permite que um programa que interage com um banco de dados trate uma operação de banco de dados (ou conjunto de operações) como uma operação única — conhecida como operação atômica ou transação. • No fim de uma transação, pode-se tomar uma decisão para confirmar ou reverter a transação. • Confirmar uma transação finaliza a(s) operação(ões) de banco de dados — inserções, atualizações e exclusões não podem ser revertidas sem realizar uma nova operação de banco de dados. • Reverter uma transação deixa o banco de dados no estado anterior à operação de banco de dados. • O Java fornece processamento de transação via métodos de interface Connection. • O método setAutoCommit especifica se cada instrução SQL confirma depois que ela for completada (um argumento true) ou se várias instruções SQL devem ser agrupadas como uma transação. • Quando a confirmação automática (autocommit) estiver desativada, o programa deve seguir a última instrução SQL na transação com uma chamada para o método Connection commit (para confirmar as alterações no banco de dados) ou o método Connection rollback (para retornar o banco de dados ao seu estado anterior à transação). • O método getAutoCommit determina o estado de confirmação automática para a Connection.

Terminologia *, caractere curinga de SQL, 903 %, caractere curinga de SQL, 904 _, caractere curinga de SQL, 904 absolute, método de ResultSet, 919 AbstractTableModel, classe, 915 addTableModelListener, método de TableModel, 915

banco de dados, 899 banco de dados relacional, 900 CachedRowSet, interface, 924 CallableStatement, interface, 938 chave estrangeira, 901 chave primária, 900 close, método da interface JdbcRowSet, 926 coluna autoincrementada, 901 coluna em uma tabela de banco de dados, 900 com.sun.rowset, pacote, 925 commit, método da interface Connection, 939 concorrência de conjunto de resultados, 918 CONCUR_READ_ONLY, constante, 919 CONCUR_UPDATABLE, constante, 919 conectando tabelas de banco de dados, 901 confirmar uma transação, 939 Connection, interface, 913 consulta, 899 correspondência de padrão, 904 createStatement, método de Connection, 914 critérios de seleção, 904 DELETE, instrução SQL, 909 descoberta automática de driver ( JDBC 4), 913 diagrama de relacionamento de entidades, 902 driver JDBC, 899 DriverManager, classe, 913 execute, método de JdbcRowSet, 925

executeQuery, método da interface PreparedStatement, 932 executeQuery, método da interface Statement, 914 executeUpdate, método da interface PreparedStatement, 932 fireTableStructureChanged, método de AbstractTableModel, 919 getAutoCommit, método da interface Connection, 939 getColumnClass, método de TableModel, 915 getColumnClassName, método de ResultSetMetaData, 919 getColumnCount, método de ResultSetMetaData, 914 getColumnCount, método de TableModel, 915 getColumnName, método de ResultSetMetaData, 919 getColumnName, método de TableModel, 915 getColumnType, método de ResultSetMetaData, 914 getConnection, método de DriverManager,

913

getInt, método de ResultSet, 914 getObject, método da interface ResultSet,

914

getRow, método da interface ResultSet, 919 getRowCount, método da interface TableModel, 915 getValueAt, método da interface TableModel,

915

IDENTITY, palavra-chave (SQL), 928 INNER JOIN, cláusula de SQL, 907 INSERT, instrução SQL, 908

interface, 914 Java Database Connectivity ( JDBC), 899

Java DB, 926 javax.sql.rowset, pacote, 924 javax.swing.table, pacote, 923 JDBC API, 899 JdbcRowSet, interface, 924 JdbcRowSetImpl, classe, 925 JTable, classe, 915 last, método de ResultSet, 919 LIKE, operador (SQL), 904 linha em uma tabela de banco de dados, 900 metadados, 914 MySQL 5.0 Community Edition, 910 MySQL Connector/J, 910 next, método da interface ResultSet, 914 nome qualificado, 907 ON, cláusula, 907 operação atômica, 939 ORDER BY, cláusula de SQL, 905 parâmetro de saída para CallableStatement, 938 predicado, 904 PreparedStatement, interface, 927 prepareStatement, método da interface Connection, 929 procedure armazenada, 938 processamento de transação, 939 regexFilter, método da classe RowFilter, 924 Regra de Integridade de Entidade, 902 Regra de Integridade Referencial, 901 removeTableModelListener, método da interface TableModel, 915 ResultSet, interface, 914 ResultSetMetaData, interface, 914 reverter uma transação, 939

944

Capítulo 28  Acesso a bancos de dados com o JDBC

rollback, método da interface Connection,

setString, método da interface PreparedStatement, 927

RowFilter, classe, 924

setUrl, método da interface JdbcRowSet, 925

RowSet conectado, 924

setUsername, método da interface JdbcRowSet, 925

939

RowSet desconectado, 924 RowSet, interface, 924 SELECT, SQL, palavras-chave, 903 SET, cláusula de SQL, 909 setAutoCommit, método da interface Connection, 939 setCommand, método da interface JdbcRowSet,

925

setPassword, método da interface JdbcRowSet, 925 setRowFilter, método da classe JTable, 924 setRowSorter, método da classe JTable, 923

sistema de gerenciamento de bancos de dados relacional (RDBMS), 899 sistema de gerenciamento de bancos de dados (Database Management System — DBMS), 899 SQLException, classe, 913 Statement, interface, 914 Structured Query Language (SQL), 899 subprotocolo para comunicação, 913 tabela de um banco de dados, 900 TableModel, interface, 915

TableRowSorter, classe, 923

tipo de conjunto de resultados, 918 transação, 939 TYPE_FORWARD_ONLY, constante, 918 TYPE_SCROLL_INSENSITIVE, constante, 918 TYPE_SCROLL_SENSITIVE, constante, 918 Types, classe, 914 um para muitos, relacionamento, 903 UPDATE, instrução SQL, 908 VALUES, cláusula de SQL, 908 WHERE, cláusula de SQL, 904 WindowAdapter, classe, 924 windowClosed, método da interface WindowListener, 924 WindowListener, interface, 924

Exercício de autorrevisão 28.1 Preencha as lacunas em cada uma das seguintes afirmações: a) A linguagem padrão internacional de banco de dados é ________. b) Uma tabela em um banco de dados consiste em ________ e ________ . c) Os objetos de instrução retornam resultados de consulta de SQL como objetos ________. d) O ________ identifica unicamente cada linha em uma tabela. e) A palavra-chave de SQL ________ é seguida pelos critérios de seleção que especificam as linhas a selecionar em uma consulta. f) As palavras-chave de SQL ________ especificam a ordem em que linhas são classificadas em uma consulta. g) Mesclar linhas de múltiplas tabelas de banco de dados é chamado de fazer ________ das tabelas. h) Um(a) ________ é uma coleção organizada de dados. i) Um(a) ________ é um conjunto de colunas cujos valores correspondem aos valores de chave primária de outra tabela. j) O método ________ ________ é utilizado para obter uma Connection com um banco de dados. k) A interface ________ ajuda a gerenciar a conexão entre um programa Java e um banco de dados. l) Um objeto ________ é utilizado para submeter uma consulta a um banco de dados. m) Ao contrário de um objeto ResultSet, os objetos ________ e ________ são roláveis e atualizáveis por padrão. n) ________, um RowSet desconectado, armazena os dados em cache de um ResultSet na memória.

Respostas do exercício de autorrevisão 28.1 a) SQL. b) linhas, colunas. c) ResultSet. d) chave primária. e) WHERE. f) ORDER BY. g) junção. h) banco de dados. i) chave estrangeira. j) DriverManager, getConnection k) Connection. l) Statement. m) JdbcRowSet, CachedRowSet n) CachedRowSet.

Exercícios 28.2 (Aplicativo de consulta para o banco de dados books) Utilizando as técnicas mostradas neste capítulo, defina um aplicativo de consulta

completo para o banco de dados books. Forneça as seguintes consultas predefinidas: a) Selecione todos os autores da tabela Authors. b) Selecione um autor específico e liste todos os livros para esse autor. Inclua o título, ano e ISBN de cada livro. Ordene as informações alfabeticamente pelo sobrenome do autor e depois pelo nome. c) Selecione um editor específico e liste todos os livros publicados por esse editor. Inclua o título, ano e ISBN. Ordene as informações alfabeticamente por título. d) Forneça quaisquer outras consultas que você considerar apropriadas.



Exiba um JComboBox com nomes apropriados para cada consulta predefinida. Também permita que os usuários forneçam suas próprias consultas.

28.3 (Aplicativo de manipulação de dados para o banco de dados books) Defina um aplicativo de manipulação de dados para o banco de

dados books. O usuário deve ser capaz de editar os dados existentes e adicionar novos dados ao banco de dados (obedecendo às restrições de integridade referenciais e de entidade). Permita ao usuário editar o banco de dados das seguintes maneiras: a) Adicionar um novo autor. b) Editar as informações existentes para um autor. c) Adicione um novo título para um autor. (Lembre-se de que o livro deve ter uma entrada na tabela AuthorISBN). d) Adicione uma nova entrada na tabela AuthorISBN para vincular autores com títulos.



Exercícios

945

28.4 (Banco de dados de empregados) Na Seção 10.7, introduzimos uma hierarquia de folhas de pagamento de empregados para calcular a folha de pagamento de cada empregado. Nesse exercício, fornecemos um banco de dados de empregados que corresponde à hierarquia de folhas de pagamento dos empregados. (Um script SQL para criar o banco de dados employees é fornecido com os exemplos deste capítulo.) Escreva um aplicativo que permita ao usuário:

a) Adicionar empregados à tabela employee. b) Adicionar informações de folha de pagamento à tabela apropriada para cada novo empregado. Por exemplo, para um empregado assalariado adicione informações de folha de pagamento à tabela salariedEmployees. A Figura 28.33 é o diagrama de relacionamento de entidade do banco de dados employees. 28.5 (Aplicativo de consulta do banco de dados de empregados) Modifique o Exercício 28.4 para fornecer uma JComboBox e uma JTextArea para permitir ao usuário realizar uma consulta que seja selecionada a partir da JComboBox ou definida na JTextArea. As consultas predefinidas de exemplo são: a) Selecione todos os empregados que trabalham no Departamento de SALES. b) Selecione os empregados por hora que trabalham mais de 30 horas. c) Selecione todos os empregados comissionados em ordem decrescente da taxa de comissão.

28.6 (Aplicativo de manipulação de dados do banco de dados employee) Modifique o Exercício 28.5 para realizar as seguintes tarefas: a) Aumente 10% do salário-base de todos os empregados comissionados com salário-base. b) Se o aniversário do empregado cair no mês atual, adicione um bônus de US$ 100. c) Para todos os empregados comissionados com vendas brutas acima de US$ 10.000 adicione um bônus de US$ 100.

28.7 (Modificação do AddressBook: Atualize uma entrada existente) Modifique o programa das figuras  28.30–28.32 para fornecer um J ­ Button que permite ao usuário chamar um método denominado updatePerson na classe PersonQueries para atualizar a entrada atual no banco de dados AddressBook.

28.8 (Modificação do AddressBook: Exclua uma entrada existente) Modifique o programa do Exercício 28.7 para fornecer um JButton que permite ao usuário chamar um método denominado deletePerson na classe PersonQueries para excluir a entrada atual no banco de dados Address­ Book.

28.9 (Estudo de caso de ATM com um banco de dados) Modifique o Estudo de Caso de ATM (capítulos 12–13) para utilizar um banco de dados de verdade para armazenar informações sobre contas. Fornecemos um script SQL para criar o BankDatabase, que tem uma única tabela consistindo em quatro colunas — AccountNumber (um int), PIN (um int), AvailableBalance (um double) e TotalBalance (um double). salariedEmployees socialSecurityNumber

1

1

commissionEmployees socialSecurityNumber

weeklySalary

grossSales

bonus

commissionRate bonus 1 1

employees

1

socialSecurityNumber firstName

1

lastName birthday employeeType departmentName

basePluscommissionEmployees

hourlyEmployees socialSecurityNumber

1

1

socialSecurityNumber

hours

grossSales

wage

commissionRate

bonus

baseSalary bonus

Figura 28.33  |  Relações de tabela no banco de dados employees.

29

Se um homem qualquer descrever seu caso e colocar seu nome na primeira página, ele receberá uma resposta imediata. Se fizer com que eu precise virar a página, ele terá de esperar. — Lord Sandwich

Regra um: nosso cliente sempre tem razão. Regra dois: se você acha que nosso cliente está errado, veja a Regra 1. — Anônimo

Uma questão justa deve ser seguida por uma ação em silêncio. — Dante Alighieri

Você virá aqui e obterá livros que abrirão seus olhos, seus ouvidos e sua curiosidade, e farão uma revolução em sua mente. — Ralph Waldo Emerson

Aplicativos Web JavaServer™ Faces Objetivos Neste capítulo, você aprenderá: 

O desenvolvimento de aplicativos Web utilizando as tecnologias Java e NetBeans.



A criar JavaServer Pages com componentes JavaServer Faces.



A criar aplicativos Web que consistem em múltiplas páginas.



A validar a entrada de usuário em uma página Web.



A manter informações de estado específicas ao usuário por todo um aplicativo Web com rastreamento de sessão e cookies.

Sumário

29.1 Introdução

29.1 29.2 29.3 29.4

Introdução Transações HTTP simples Arquitetura de aplicativo multithread Tecnologias Web Java

947

29.5.2 Examinando um arquivo Page Bean 29.5.3 Ciclo de vida de processamento de eventos 29.5.4 Criando um aplicativo Web no NetBeans

29.6 Componentes JSF

29.4.1 Servlets

29.6.1 Componentes gráficos e de texto

29.4.2 JavaServer Pages

29.6.2 Validação utilizando componentes validadores e validadores personalizados

29.4.3 JavaServer Faces 29.4.4 Tecnologias Web no NetBeans

29.5 Criando e executando um aplicativo simples no NetBeans 29.5.1 Examinando um documento JSP

29.7 Monitoramento de sessão 29.7.1 Cookies 29.7.2 Rastreamento de sessão com beans de sessão

29.8 Conclusão

Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios

29.1 Introdução Neste capítulo, introduzimos o desenvolvimento de aplicativos Web com Java. Aplicativos baseados na Web criam conteúdo para clientes que utilizam navegadores Web. Esse conteúdo inclui a Extensible HyperText Markup Language (XHTML), criação de scripts do lado do cliente, imagens e dados binários. Se não estiver familiarizado com a XHTML, visite nosso XHTML Resource Center em www.deitel.com/XHTML/ para introduções, tutoriais e outros recursos que irão ajudá-lo a aprender a XHTML. Para uma lista completa dos nossos Resource Centers, visite www.deitel.com/ResourceCenters.html. Este capítulo começa com uma visão geral da arquitetura de aplicativos multicamadas e tecnologias Web baseadas em Java para implementar aplicativos multicamadas. Apresentamos então vários exemplos de aplicativos Web. O primeiro exemplo introduz o desenvolvimento Web com Java. No segundo exemplo, construímos um aplicativo Web que simplesmente mostra a aparência e funcionamento de vários componentes GUI de aplicativos Web. Em seguida, mostramos como utilizar componentes de validação e métodos de validação personalizados para assegurar que a entrada do usuário seja válida antes de ela ser submetida a processamento no servidor. O capítulo termina com dois exemplos de como manter informações específicas ao usuário em um aplicativo Web. O Capítulo 30 continua nossa discussão do desenvolvimento de aplicativos Web com Java com conceitos mais avançados. Por todo este capítulo e o Capítulo 30, utilizamos o IDE do NetBeans 6.5 e o servidor de aplicativo de código-fonte aberto GlassFish v2 UR2. Para implementar os exemplos apresentados neste capítulo, você deve instalar esses softwares. NetBeans está disponível em www.netbeans.org/downloads/index.html

Baixe e execute a versão Java ou All do instalador — o instalador que você escolhe deve incluir o Java Web and EE e o Glassfish V2 UR2. As duas versões instalam o IDE do NetBeans e o servidor GlassFish. Supomos que você utilize as opções de instalação padrão. Depois de instalar o NetBeans, execute-o. Utilize então a opção Check for Updates do menu Help para certificar-se de que você tem os componentes mais recentes. A maior parte do código que apresentamos neste capítulo é gerada pelo IDE do NetBeans. Reformatamos esse código e excluímos os comentários Javadoc que o IDE gera para corresponder com nossas convenções de codificação utilizadas por todo este livro e também para economizar espaço. Às vezes só mostramos uma parte do código. Nesses casos, fornecemos comentários indicando onde o código foi removido, e todos os números de linha correspondem ao código-fonte de exemplo completo.

29.2 Transações HTTP simples Considere o que ocorre nos bastidores quando um usuário solicita uma página Web em um navegador. Na sua forma mais simples, uma página Web nada mais é do que um documento XHTML que descreve para um navegador como exibir e formatar as informações do documento. Documentos XHTML normalmente contêm hiperlinks que levam a diferentes páginas ou a outras partes da mesma página. Quando o usuário clica em um hiperlink, a página Web solicitada é carregada no navegador Web do usuário. Além disso, o usuário pode digitar o endereço de uma página no campo de endereço do navegador.

URIs O protocolo HTTP permite a clientes e servidores interagir e trocar informações de uma maneira uniforme e confiável. O HTTP utiliza o URIs (Uniform Resource Identifier) para identificar dados na internet. URIs que especificam as localizações de documentos são chamados URLs (Uniform Resource Locators). URLs comuns fazem referência a arquivos, diretórios ou objetos podem realizar tarefas complexas, como pesquisas no banco de dados e pesquisas na internet. Se conhecer o URL de um recurso ou arquivo publicamente disponível em qualquer lugar na Web, você poderá acessá-lo por meio do HTTP.

948

Capítulo 29  Aplicativos Web JavaServer™ Faces

Partes de um URL Um URL contém informações que direcionam um navegador ao recurso que o usuário quer acessar. Computadores que executam softwares de servidor Web disponibilizam esses recursos. Vamos examinar os componentes do URL http://www.deitel.com/books/downloads.html

O http:// indica que o recurso deve ser obtido utilizando o protocolo HTTP. A parte www.deitel.com, é o hostname totalmente qualificado do servidor em que o recurso reside. O computador que hospeda e mantém recursos é conhecido como host. O www.deitel.com é convertido em um endereço IP — um valor numérico único que identifica o servidor, como um número de telefone define unicamente uma determinada linha telefônica. Essa conversão é realizada por um servidor Domain-Name System (DNS) — um computador que mantém um banco de dados dos nomes de host e seus endereços IP correspondentes — e o processo é chamado pesquisa de DNS. Para testar aplicativos Web, muitas vezes seu computador será usado como o host. Esse host é referenciado usando o nome de domínio reservado localhost, que costuma ser o endereço IP 127.0.0.1. (Consulte informações adicionais sobre endereços IP em en.wikipedia.org/wiki/IP_address.) O hostname totalmente qualificado pode ser seguido por dois-pontos (:) e um número de porta. Servidores Web esperam por padrão as solicitações na porta 80, mas muitos servidores Web de desenvolvimento utilizam um número de porta diferente, como 8080 — como veremos na Seção 29.5.4. O restante do URL (isto é, /books/downloads.html) especifica o nome do recurso solicitado (o documento XHTML downloads.html) e seu caminho ou localização (/books), no servidor Web. O caminho pode especificar a localização de um diretório real no sistema de arquivos do servidor Web. Mas, por razões de segurança, o caminho normalmente especifica a localização de um diretório virtual. O servidor converte o diretório virtual em uma localização real no servidor (ou em outro computador na rede do servidor), ocultando assim a localização real do recurso. Alguns recursos são criados dinamicamente usando outras informações armazenadas no computador do servidor, como um banco de dados. O hostname no URL para esse recurso especifica o servidor correto; as informações sobre o caminho e recurso que identificam o recurso com o qual interagir para responder à solicitação do cliente.

Fazendo uma solicitação e recebendo uma resposta Quando dado um URL, um navegador Web realiza uma transação HTTP para recuperar e exibir a página Web nesse endereço. A Figura 29.1 ilustra a transação, mostrando a interação entre o navegador Web (o cliente) e o aplicativo de servidor Web (o servidor). Na Figura 29.1, o navegador Web envia uma solicitação de HTTP ao servidor. A solicitação (na sua forma mais simples) é GET /books/downloads.html HTTP/1.1

A palavra get é um método HTTP indicando que o cliente deseja obter um recurso do servidor. O restante da solicitação fornece o nome do caminho do recurso (por exemplo, um documento XHTML), o nome do protocolo e o número de versão (HTTP/1.1). A solicitação do cliente também contém alguns cabeçalhos obrigatórios e outros opcionais, como Host (que identifica o computador-alvo) ou UserAgent (que identifica o tipo e versão do navegador). (a) O pedido GET é enviado do cliente ao servidor Web

Servidor Web (b) Depois de receber o pedido, o servidor Web pesquisa em seu sistema pelo recurso.

Cliente Internet

Figura 29.1  |  Cliente interagindo com servidor Web. Passo 1: A solicitação GET.

Qualquer servidor que entende o HTTP (versão 1.1) pode converter essa solicitação e responder apropriadamente. A Figura 29.2 representa o servidor que responde a uma solicitação. Servidor Web

Cliente Internet

Figura 29.2  |  Cliente interagindo com servidor Web. Passo 2: A resposta HTTP.

O servidor responde a uma solicitação com uma mensagem adequada e o conteúdo dos recursos.



29.2  Transações HTTP simples

949

O servidor primeiro responde enviando uma linha do texto indicando a versão HTTP, seguida por um código numérico e uma frase descrevendo o status da transação. Por exemplo, HTTP/1.1 200 OK

indica sucesso, enquanto HTTP/1.1 404 Not found

informa o cliente que o servidor Web não pode localizar o recurso solicitado. Em uma solicitação bem-sucedida, o servidor acrescenta o documento solicitado à resposta HTTP. Uma lista completa dos códigos numéricos que indicam o status de uma transação HTTP pode ser encontrada em www.w3.org/Protocols/rfc2616/rfc2616-sec10.html.

Cabeçalhos HTTP O servidor envia então um ou mais cabeçalhos HTTP que fornecem informações adicionais sobre os dados que serão enviados. Nesse caso, o servidor está enviando um documento de texto XHTML, portanto, um cabeçalho HTTP desse exemplo seria lido: Content-type: text/html

As informações fornecidas nesse cabeçalho especificam o tipo Multipurpose Internet Mail Extensions (MIME) do conteúdo que o servidor está transmitindo ao navegador. O MIME é um padrão internet que especifica formatos de dados para que os programas possam interpretar os dados corretamente. Por exemplo, o tipo MIME text/plain indica que as informações enviadas são texto que pode ser exibido diretamente, sem qualquer interpretação do conteúdo como marcação XHTML. Da mesma forma, o tipo MIME image/jpeg indica que o conteúdo é uma imagem JPEG. Quando o navegador recebe esse tipo MIME, ele tenta exibir a imagem. Para uma lista de tipos MIME disponíveis, visite www.w3schools.com/media/media_mimeref.asp. O cabeçalho ou conjunto de cabeçalhos é seguido por uma linha em branco, que indica ao navegador cliente que o servidor terminou de enviar os cabeçalhos HTTP. O servidor então envia o conteúdo do documento XHTML solicitado (downloads.html). O navegador do lado do cliente analisa a marcação XHTML que ele recebe e renderiza (ou exibe) os resultados. O servidor normalmente mantém a conexão aberta para processar outras solicitações do cliente.

Solicitações HTTP GET e POST Os dois mais comuns tipos de solicitação de HTTP (também conhecidos como métodos de solicitação) são GET e POST. Uma solicitação GET em geral pede um recurso específico em um servidor. Os usos comuns das solicitações GET são recuperar uma imagem ou um documento XHTML ou, ainda, buscar resultados de pesquisa com base em um termo de pesquisa submetido pelo usuário. Uma solicitação POST geralmente posta (ou envia) dados para o servidor. Os usos comuns das solicitações POST são enviar dados de formulário ou documentos a um servidor. Uma solicitação HTTP muitas vezes posta dados em um handler de formulário do lado do servidor que processa os dados. Por exemplo, quando um usuário realiza uma pesquisa ou participa de uma enquete baseada na Web, o servidor Web recebe as informações especificadas no formulário XHTML como parte da solicitação. Solicitações GET e POST podem ser utilizadas para enviar dados de formulário a um servidor Web, mas cada tipo de solicitação envia as informações de uma maneira diferente. Uma solicitação GET envia informações ao servidor no URL, por exemplo, www.google.com/search?q=deitel. Nesse caso, search é o nome do handler de formulário no servidor do Google, q é o name de uma variável no formulário de pesquisa do Google e deitel é o termo da pesquisa. O ? separa a string de consulta do restante do URL em uma solicitação. Um par de nome/valor é passado para o servidor com o nome e o valor separados por um sinal de igual (=). Se mais de um par de nome/valor for submetido, cada um é separado do próximo por caractere & (“e” comercial). O servidor utiliza os dados passados em uma string de consulta para recuperar um recurso apropriado do servidor. O servidor envia então uma resposta ao cliente. Uma solicitação GET pode ser iniciada submetendo um formulário XHTML cujo atributo method é configurado como "get", digitando o URL (possivelmente contendo uma string de consulta) diretamente no barra de endereços do navegador ou por meio de um hiperlink. Uma solicitação POST envia dados de formulário como parte da mensagem HTTP, não como parte do URL. Uma solicitação GET em geral limita a string de consulta (isto é, tudo à direita do ?) a um número específico de caracteres (2.083 no Internet Explorer; mais em outros navegadores), assim frequentemente é necessário enviar grandes volumes de informações utilizando o método POST. O método POST também é às vezes preferido porque ele oculta os dados submetidos do usuário incorporando-os a uma mensagem HTTP. Observação de engenharia de software 29.1 Os dados enviados em uma solicitação POST não são parte do URL, e o usuário não pode ver os dados por padrão. Mas há ferramentas disponíveis que exibem esses dados, portanto você não deve supor que os dados sejam seguros somente porque uma solicitação POST é usada.

Cache do lado do cliente Navegadores muitas vezes armazenam em cache (salvam em disco) páginas Web para recarregamento rápido. Se não houver alterações entre a última versão armazenada no cache e a versão atual na Web, isso acelerará sua navegação. Uma resposta HTTP pode indicar por quanto tempo o conteúdo permanece atualizado. Se esse período de tempo não for atingido, o navegador poderá impedir outra solicitação ao servidor. Do contrário, o navegador solicita o documento do servidor. Portanto, o navegador minimiza o download da quantidade de dados

950

Capítulo 29

Aplicativos Web JavaServer™ Faces

que deve ser feito para que você visualize uma página Web. Os navegadores em geral não armazenam em cache da resposta do servidor para uma solicitação POST porque a próxima solicitação POST pode não retornar o mesmo resultado. Por exemplo, em uma pesquisa, muitos usuários podem visitar a mesma página Web e responder uma pergunta. Os resultados de pesquisa então podem ser exibidos para o usuário. Cada nova resposta altera os resultados totais da pesquisa. Ao utilizar um sistema de pesquisa baseado na Web, normalmente o navegador fornece as informações que você especifica em um formulário XHTML para o sistema de pesquisa com uma solicitação GET. O sistema de pesquisa realiza a pesquisa e, então, retorna os resultados para você como uma página Web. Essas páginas às vezes são armazenadas em cache pelo navegador caso você realize a mesma pesquisa novamente.

29.3 Arquitetura de aplicativo multithread Aplicativos na Web são aplicativos multicamadas (às vezes conhecidos como aplicativos de n camadas). Aplicativos multicamadas dividem as funcionalidades em camadas separadas (isto é, agrupamentos lógicos das funcionalidades). Embora as camadas possam estar localizadas no mesmo computador, as camadas dos aplicativos baseados na Web costumam residir em computadores separados. A Figura 29.3 apresenta a estrutura básica de um aplicativo baseado na Web de três camadas. Camada superior Interface do usuário com o cliente

Navegador

Camada intermediária Lógica do negócio

XHTML

Servidor Web

Camada inferior Dados Informações

JDBC

Banco de dados

Figura 29.3 | Arquitetura de três camadas.

A camada de informações (também chamada camada de dados ou camada inferior) mantém os dados que pertencem ao aplicativo. Essa camada, em geral, armazena dados em um sistema de gerenciamento de banco de dados relacional (RDBMS). Discutimos RDBMSs no Capítulo 28. Por exemplo, uma loja de varejo poderia ter um banco de dados para armazenar informações sobre produtos como descrições, preços e quantidade em estoque. O mesmo banco de dados também poderia conter informações sobre clientes como nomes de usuário, endereços para cobrança e números de cartão de crédito. Essa camada pode conter múltiplos bancos de dados que, em conjunto, abrangem os dados necessários para nosso aplicativo. A camada intermediária implementa a lógica do negócio, a lógica do controlador e a lógica da apresentação a fim de controlar interações entre os clientes do aplicativo e os dados do aplicativo. A camada intermediária funciona como um mediador entre os dados na camada de informações e os clientes do aplicativo. A lógica do controlador da camada intermediária processa as solicitações do cliente (como solicitações para visualizar um catálogo de produtos) e recupera dados do banco de dados. A lógica de apresentação da camada intermediária processa então os dados da camada de informações e apresenta o conteúdo ao cliente. Aplicativos Web costumam apresentar dados a clientes como documentos XHTML. A lógica do negócio na camada intermediária impõe regras do negócio e assegura que os dados sejam confiáveis antes do aplicativo servidor atualizar o banco de dados ou apresentar os dados aos usuários. As regras do negócio determinam como os clientes podem e como não podem acessar dados de aplicativo, e como os aplicativos processam dados. Por exemplo, uma regra de negócio na camada intermediária do aplicativo na Web de uma loja de varejo poderia assegurar que a quantidade de todos os produtos permaneça positiva. Uma solicitação de cliente para configurar uma quantidade negativa no banco de dados de informações sobre produtos da camada inferior seria rejeitada pela lógica do negócio da camada intermediária. A camada cliente, ou camada superior, é a interface com o usuário do aplicativo, que coleta a entrada e exibe a saída. Os usuários interagem diretamente com o aplicativo por meio da interface (em geral vista em um navegador Web), teclado e mouse. Em resposta às ações do usuário (por exemplo, clicar em um hiperlink), a camada de cliente interage com a camada intermediária para fazer solicitações e recuperar dados da camada de informações. A camada cliente exibe então os dados recuperados da camada intermediária ao usuário. A camada cliente nunca interage diretamente com a camada de informações.

29.4 Tecnologias Web Java As tecnologias Web do Java evoluem continuamente a fim de fornecer aos desenvolvedores níveis mais altos de abstração e melhor separação das camadas do aplicativo, o que torna aplicativos Web mais fáceis de manter e mais extensíveis. Elas também permitem uma divisão efetiva do trabalho. Um designer gráfico pode construir a interface do aplicativo sem se preocupar com a lógica subjacente da página, que será tratada por um programador. Enquanto isso, você fica livre para se concentrar na lógica de negócio do aplicativo, deixando os detalhes da construção de um aplicativo atraente e fácil de usar ao designer. O NetBeans permite desenvolver a GUI de um aplicativo Web em uma ferramenta de design de arrastar e soltar enquanto permite tratar a lógica do negócio em classes Java separadas. Aplicativos Java multicamadas costumam ser implementados utilizando os recursos do Java Enterprise Edition ( Java EE). As tecnologias que utilizamos para desenvolver aplicativos Web nos capítulos 29–30 são parte do Java EE 5 (java.sun.com/javaee).



29.4  Tecnologias Web Java

951

29.4.1  Servlets Servlets são a visualização de nível mais baixo das tecnologias de desenvolvimento Web com Java que discutiremos neste capítulo. Eles utilizam o modelo de solicitação/resposta HTTP da comunicação entre o cliente e servidor. Servlets estendem a funcionalidade de um servidor permitindo que ele gere conteúdo dinâmico. Por exemplo, servlets podem gerar dinamicamente documentos XHTML personalizados, ajudar a fornecer acesso seguro para um site Web, interagir com bancos de dados em nome de um cliente e manter informações de sessão únicas para cada cliente. Um componente de servidor Web chamado contêiner de ­servlets executa e interage com servlets. Os pacotes javax.servlet e javax.servlet.http fornecem as classes e interfaces para definir os servlets. O contêiner de servlets recebe solicitações HTTP de um cliente e direciona cada solicitação ao servlet apropriado. O servlet processa a solicitação e retorna uma resposta apropriada ao cliente — normalmente na forma de um documento XHTML ou XML (Extensible Markup Language) para exibir no navegador. A XML é uma linguagem utilizada para trocar dados estruturados na Web. Arquitetonicamente, todos os servlets devem implementar a interface Servlet do pacote javax.servlet, que assegura que cada servlet pode executar no framework fornecido pelo contêiner de servlets. A interface Servlet declara os métodos utilizados pelo contêiner de servlets para gerenciar o ciclo de vida do servlet. Um ciclo de vida do servlet inicia quando o contêiner de servlets carrega-o na memória — geralmente em resposta à primeira solicitação ao servlet. Antes que o servlet possa tratar essa solicitação, o contêiner invoca o método init do servlet, que só é chamado uma vez durante o ciclo de vida de um servlet para inicializar o servlet. Depois que init completar a execução, o servlet estará pronto para responder à sua primeira solicitação. Todas as solicitações são tratadas pelo método service de um servlet, que é o método-chave para definir a funcionalidade de um servlet. O método service recebe a solicitação, processa-a e envia uma resposta ao cliente. Durante o ciclo de vida de um servlet, service é chamado uma vez por solicitação. Cada nova solicitação costuma ser tratada em um thread separado da execução (gerenciado pelo contêiner de servlets), assim cada servlet deve ser seguro a threads. Quando o contêiner de servlets termina o servlet (por exemplo, quando o contêiner de servlets precisa de mais memória ou quando ele é desativado), o método destroy do servlet é chamado para liberar quaisquer recursos mantidos pelo servlet.

29.4.2  JavaServer Pages A tecnologia JavaServer Pages ( JSPs) é uma extensão da tecnologia de servlet. Cada JSP é um documento que é convertido pelo contêiner JSP em um servlet. Diferentemente dos servlets, JSPs ajudam a separar a apresentação do conteúdo. As JavaServer Pages permitem aos programadores de aplicativo Web criar conteúdo dinâmico reutilizando componentes predefinidos e interagindo com componentes que utilizam script do lado do servidor. Os programadores de JSP podem utilizar componentes de softwares especiais chamados JavaBeans e bibliotecas de tags personalizados que encapsulam funcionalidades dinâmicas complexas. Um JavaBean é um componente reutilizável que segue certas convenções para o design de classe e que pode ser manipulado visualmente em uma ferramenta de construtor, como o NetBeans ou Eclipse. As classes JavaBean que permitem leitura e gravação das variáveis de instância devem fornecer métodos get e set apropriados, que, juntos, definem as propriedades das classes JavaBean. O conjunto completo das convenções do design de classe é discutido na especificação do JavaBean (java-.sun.com/javase/technologies/desktop/javabeans/glasgow/index.html).

Bibliotecas de tags personalizados Lembre-se do Capítulo 23 de que documentos XHTML e XML, em geral, consistem em elementos que são delimitados por tags. Bibliotecas de tags personalizados são um recurso poderoso do JSP que permite aos desenvolvedores Java ocultar o código para acesso de banco de dados e outras operações complexas em tags personalizados. Para utilizar essas capacidades, você simplesmente adiciona os tags personalizados à página. Essa simplicidade permite a designers de página Web que não estão familiarizados com o Java aprimorar páginas Web com capacidades poderosas de processamento dinâmico e conteúdo dinâmico. As classes e interfaces JSP estão localizadas nos pacotes javax-.servlet.jsp e javax.servlet.jsp.tagext. Componentes JSP Há quatro componentes-chave para JSPs — diretivas, ações, elementos de script e bibliotecas de tags. Diretivas são mensagens ao contêiner JSP — o componente de servidor Web que executa JSPs. As diretivas permitem especificar configurações de página, incluir o conteúdo a partir de outros recursos e especificar bibliotecas de tags personalizados para uso em JSPs. As ações encapsulam funcionalidades em tags predefinidos que programadores podem incorporar em JSPs. As ações frequentemente são realizadas com base nas informações enviadas para o servidor como parte da solicitação de um cliente em particular. Elas também podem criar objetos Java para utilização em JSPs. Os elementos de script permitem inserir código Java que interage com componentes em uma JSP (e possivelmente outros componentes de aplicativo Web) para realizar o processamento de solicitação. As bibliotecas de tags fazem parte do mecanismo de extensão de tag que permite aos programadores criar tags personalizados. Tais tags permitem que os projetistas de página Web manipulem conteúdo da JSP sem conhecimento anterior do Java. A JavaServer Pages Standard Tag Library ( JSTL) fornece funcionalidades para muitas tarefas comuns relacionadas a aplicativos Web, como iterar por uma coleção de objetos e executar instruções SQL. Conteúdo estático JSPs podem conter outro conteúdo estático. Por exemplo, as JSPs normalmente incluem marcação de XHTML ou XML. Tal marcação é conhecida como dados de template fixa ou texto de template fixa. Qualquer texto literal ou marcação XHTML em um JSP é convertido em uma String na representação servlet do JSP.

952

Capítulo 29

Aplicativos Web JavaServer™ Faces

Processando uma solicitação JSP Quando um servidor com JSP ativado recebe a primeira solicitação para uma JSP, o contêiner de JSPs traduz a JSP em um servlet Java que trata a solicitação atual e as futuras solicitações para a JSP. Portanto, JSPs contam com o mesmo mecanismo de solicitação/resposta que o dos servlets para processar solicitações e enviar respostas aos clientes. Dica de desempenho 29.1 Alguns contêineres JSP convertem JSPs em servlets no momento da implantação do JSP (isto é, quando o aplicativo é instalado em um servidor Web). Isso elimina o overhead da conversão para o primeiro cliente que solicita cada JSP, uma vez que o JSP será convertido antes de ele ser solicitado por um cliente.

29.4.3 JavaServer Faces JavaServer Faces (JSF) — suportado por servidores compatíveis com o Java Enterprise Edition 5 ( Java EE 5), como o GlassFish v2 UR2 — é um framework de aplicativo Web que simplifica o design da interface com o usuário de um aplicativo e separa ainda mais a apresentação de um aplicativo Web da sua lógica de negócio. Um framework fornece bibliotecas e às vezes ferramentas de software para ajudá-lo a organizar e construir seus aplicativos. Embora o framework JSF possa utilizar muitas tecnologias para definir as páginas em aplicativos Web, este capítulo focaliza os aplicativos JSF que utilizam JavaServer Pages. O JSF fornece um conjunto de componentes de interface com o usuário, ou componentes JSF, que simplificam o design de páginas Web. Esses componentes são semelhantes aos componentes Swing utilizados para construir aplicativos GUI. O JSF fornece duas bibliotecas de tags personalizados JSP para adicionar esses componentes a uma página JSP. O JSF também inclui APIs para tratar eventos de componentes (como o processamento de modificações de estado dos componentes e validação da entrada de usuário), navegação entre páginas de aplicativo Web etc. Você cria a aparência e o funcionamento de uma página com o JSF adicionando elementos a um documento JSP e manipulando seus atributos. Você define o comportamento da página separadamente em arquivos de código-fonte Java relacionados. Embora os componentes JSF padrão sejam suficientes para a maioria dos aplicativos Web básicos, você também pode escrever bibliotecas de componentes personalizados. Bibliotecas de componentes adicionais são disponibilizadas por vários projetos de código-fonte aberto e de fornecedores independentes. Por exemplo, o Oracle fornece quase 100 componentes na sua biblioteca ADF Faces. [Nota: Há muitos outros frameworks de aplicativo Web populares, incluindo Spring, Struts e Hibernate — todos os quais também são suportados no NetBeans. Optamos por demonstrar apenas o JavaServer Faces.]

29.4.4 Tecnologias Web no NetBeans Aplicativos Web NetBeans que utilizam o framework JavaServer Faces consistem em uma ou mais páginas Web JSP. Esses arquivos JSP contêm a extensão de nome de arquivo .jsp e também elementos GUI das páginas Web. Os JSPs também podem conter JavaScript para adicionar funcionalidades à página. Os JSPs podem ser personalizados no NetBeans adicionando componentes JSF, incluindo rótulos, campos de texto, imagens, botões e outros componentes GUI. O IDE permite criar páginas visualmente arrastando e soltando esses componentes em uma página; você também pode personalizar uma página Web editando o arquivo .jsp manualmente. Cada arquivo JSP criado no NetBeans representa uma página Web e tem uma classe JavaBean correspondente chamada bean de página. Uma classe JavaBean deve ter um construtor padrão (ou sem argumento), e métodos get e set para todas as propriedades do bean (isto é, variáveis de instância). O bean de página define as propriedades para todos os elementos da página com os quais você quer interagir programaticamente. O bean de página também contém handlers de evento e métodos de ciclo de vida de página para gerenciar tarefas, como inicialização e renderização de página, e outros códigos de suporte ao aplicativo Web. Cada aplicativo Web NetBeans JSF define três mais JavaBeans. O objeto RequestBean é mantido no escopo de solicitação — ele só existe pelo tempo de duração da solicitação HTTP. Um objeto SessionBean tem escopo de sessão — ele existe por toda a sessão de navegação de um usuário ou até que a sessão expire. Há um único objeto SessionBean para cada usuário. Por fim, o objeto ApplicationBean tem escopo de aplicativo — ele é compartilhado por todas as instâncias de um aplicativo e existe pelo tempo que o aplicativo permanece implantado em um servidor Web. Esse objeto é utilizado para armazenagem ou processamentos de dados por todo o aplicativo; só há uma instância para o aplicativo, independentemente do número de sessões abertas.

29.5 Criando e executando um aplicativo simples no NetBeans Nosso primeiro exemplo exibe a hora do dia do servidor Web em uma janela do navegador. Ao executar, esse programa exibe o texto

"Current time on the web server:", seguido pela hora do servidor Web. O aplicativo contém uma única página Web e, como menciona-

do anteriormente, consiste em dois arquivos relacionados — um documento JSP (Figura 29.4) e um arquivo de bean de página de suporte (Figura 29.6). O aplicativo também tem três beans de dados com escopo para os escopos de solicitação, sessão e aplicativo que não são utilizados nesse exemplo. Primeiro discutimos a marcação no documento JSP, a saída do aplicativo e o código no arquivo de bean de página, depois fornecemos instruções passo a passo para criar esse aplicativo Web. [Nota: A marcação na Figura 29.4 e outras listagens de arquivos JSP neste capítulo são as mesmas da marcação que aparece no NetBeans, mas reformatamos essas listagens para fins de apresentação.] 1 2 3 4 5



6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

29.5  Criando e executando um aplicativo simples no NetBeans

953













Figura 29.4  |  O documento JSP gerado pelo NetBeans que exibe a hora atual no servidor Web.

NetBeans gera a maior parte da marcação mostrada na Figura 29.4 quando você configura o título da página Web, arrasta dois componentes Static Text até a página e especifica as propriedades dos componentes Static Text. Componentes Static Text exibem um texto que não pode ser editado pelo usuário. Mostramos esses passos mais adiante.

29.5.1  Examinando um documento JSP Os documentos JSP neste e nos próximos exemplos são gerados quase inteiramente pelo NetBeans, que fornece um editor visual que permite construir a GUI de uma página arrastando e soltando componentes em uma área de design. O IDE gera um arquivo JSP em resposta às suas interações. A linha 1 da Figura 29.4 é a declaração XML, indicando o fato de que o JSP é expresso em sintaxe XML e a versão da XML que é utilizada. As linhas 3–5 são comentários que adicionamos ao JSP para indicar o número da figura, o nome do arquivo e o objetivo. A linha 6 inicia o elemento raiz do JSP. Todos os JSPs precisam ter esse elemento jsp:root, que tem um atributo version para indicar a versão JSP que é utilizada (linha 6) e um ou mais atributos xmlns (linhas 6–9). Cada atributo xmlns especifica um prefixo e um URL para uma biblioteca de tags, permitindo que a página utilize elementos dessa biblioteca. Por exemplo, a linha 8 permite que a página utilize os elementos JSP padrão, mas o tag de cada elemento deve ser precedido pelo prefixo jsp. Todos os JSPs gerados pelo NetBeans incluem as bibliotecas de tags especificadas nas linhas 6–9 (a biblioteca de componentes básicos JSF, a biblioteca de componentes JSF HTML, a biblioteca de componentes padrão JSP e a biblioteca de componentes de interface com o usuário JSF). As linhas 10–11 são o elemento jsp:directive.page. Seu atributo contentType especifica o tipo MIME (text/html) e o conjunto de caracteres (UTF-8) que a página utiliza. O atributo page-Encoding especifica a codificação de caracteres utilizada pelo código-fonte da página. Esses atributos ajudam o cliente (em geral um navegador Web) a determinar como exibir o conteúdo. Todas as páginas que contêm componentes JSF são representadas em uma árvore de componentes (semelhante àquela mostrada na Figura 29.5) com a raiz do elemento JSF f:view, que é do tipo UIViewRoot. Essa estrutura de árvore de componentes é representada em um JSP aninhando todos os tags dos componentes JSF dentro do elemento f:view (linhas 12–33 nesse exemplo). As linhas 13–18 iniciam a definição do JSP com os elementos webuijsf:page, webuijsf:html e webuijsf::head, todos provenientes da biblioteca de tags webuijsf (componentes da interface com o usuário JSF). O elemento webuijsf:head (linhas 15–18) tem um atributo title que especifica o título da página. Esse elemento também contém um elemento webuijsf:link (linha 16) que especifica a folha de estilo CSS utilizada pela página, e um elemento webuijsf:meta (linha 17) que atualiza a página a cada 60 segundos. O elemento webuijsf:body (linhas 19–30) contém um elemento webuijsf:form (linhas 20–29), que contém dois componentes

954

Capítulo 29  Aplicativos Web JavaServer™ Faces

webuijsf:staticText (linhas 21–24 e 25–28) que exibem o texto da página. O componente timeHeader (linhas 21–24) tem um atributo text (linha 24) que especifica o texto a exibir (isto é, "Current time on the web server"). O componente clockText (linhas 25–28) não especifica um atributo text porque o texto desse componente será configurado programaticamente. O elemento webuijsf:staticText nas linhas 25–28 tem o atributo binding = "# {Time.clockText}" (linha 25). Esse atributo utiliza a notação JSF Expression Language (isto é, # {Time.clockText}) para referenciar a propriedade clockText na classe Time

que representa o bean de página (você verá essa classe na Figura 29.6). Você pode vincular um atributo de um elemento JSP ao valor de uma propriedade em quaisquer JavaBeans do aplicativo Web. Por exemplo, o atributo text de um componente webuijsf:staticText pode ser vinculado a uma propriedade int no SessionBean do aplicativo (como veremos na Seção 29.7.2).

jsp:root

jsp:directive

f:view

webuijsf:page

webuijsf:html

webuijsf:head

webuijsf:link

(fornece a folha de estilos)

webuijsf:body

webuijsf:meta

webuijsf:form

(crianças são components visíveis)

webuijsf:StaticText

webuijsf:StaticText

Figura 29.5  |  Árvore de componentes JSF de exemplo dentro de um documento JSP.

Todos os elementos dos JSPs são mapeados pelo servidor de aplicativo para uma combinação de elementos XHTML e código JavaScript que permite ao navegador exibir a página. O JavaScript é uma linguagem de criação de scripts que é interpretada em todos os atuais navegadores Web populares. Ele pode ser utilizado para executar tarefas que manipulam elementos de página Web em um navegador Web e fornecem interatividade com o usuário. Você pode aprender mais sobre JavaScript no nosso JavaScript Resource Center em www.deitel. com/JavaScript/. O mesmo componente Web pode mapear para diferentes elementos XHTML e código JavaScript, dependendo do navegador cliente e das configurações de propriedade do componente. Os componentes webuijsf:staticText (linhas 21–24, 25–28) costumam mapear para elementos XHTML span. Um elemento span contém o texto que é exibido em uma página Web e é, em geral, utilizado para controlar a formatação do texto. Os atributos style do elemento webuijsf:staticText de um JSP são mapeados para o correspondente atributo style do elemento span, que o navegador então utiliza para controlar a aparência do elemento.

29.5.2  Examinando um arquivo Page Bean A Figura 29.6 apresenta o arquivo de bean de página autogerado. A linha 11 inicia a declaração da classe Time e indica que ela estende a classe AbstractPageBean (do pacote com.sun.rave.web.ui.appbase). Todas as classes de bean de página que suportam documentos JSP com componentes JSF devem estender a classe abstrata Abstract-Page-Bean, que fornece métodos de ciclo de vida de página. Observe que o IDE faz o nome de classe corresponder ao nome da página. O pacote com.sun.webui.jsf.component fornece classes para muitos dos componentes básicos da interface com usuário JSF, incluindo o componente StaticText.

1 2 3 4 5

// Figura 29.6: Time.java // O arquivo de bean de página que configura clockText como a hora no servidor Web package webtime; import com.sun.rave.web.ui.appbase.AbstractPageBean;

6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

29.5  Criando e executando um aplicativo simples no NetBeans import import import import

com.sun.webui.jsf.component.StaticText; javax.faces.FacesException; java.text.DateFormat; java.util.Date;

public class Time extends AbstractPageBean { private void _init() throws Exception { } // fim do método _init private StaticText clockText = new StaticText(); public StaticText getClockText() { return clockText; } // fim do método getClockText public void setClockText( StaticText st ) { this.clockText = st; } // fim do método setClockText public Time() { } // fim do construtor @Override public void init() { super.init(); try { _init(); } // fim do try catch ( Exception e ) { log( "Page1 Initialization Failure" , e ); throw e instanceof FacesException ? ( FacesException ) e : new FacesException( e ); } // fim do catch } // fim do método init // o método chamado quando ocorre um postback @Override public void preprocess() { } // fim do método preprocess // o método chamado antes de a página ser exibida @Override public void prerender() { clockText.setValue( DateFormat.getTimeInstance( DateFormat.LONG ).format( new Date() ) ); } // fim do método prerender // o método chamado depois da exibição completa, se init foi chamado @Override public void destroy() { } // fim do método // retorna uma referência ao bean de sessão protected SessionBean1 getSessionBean1() { return ( SessionBean1 ) getBean( "SessionBean1" ); } // fim do método

955

956 75 76 77 78 79 80 81 82 83 84 85 86 87

Capítulo 29  Aplicativos Web JavaServer™ Faces

// retorna uma referência ao bean de solicitação protected RequestBean1 getRequestBean1() { return ( RequestBean1 ) getBean( "RequestBean1" ); } // fim do método // retorna uma referência ao bean de aplicativo protected ApplicationBean1 getApplicationBean1() { return ( ApplicationBean1 ) getBean( "ApplicationBean1" ); } // fim do método } // fim da classe Time

Figura 29.6  |  O arquivo do bean de página que configura clockText como a hora no servidor Web.

Esse arquivo de bean de página define uma variável (linha 17) para interagir programaticamente com o elemento clockText do documento JSP da Figura 29.4. O IDE também autogera os métodos get e set (linhas 19–27) para essa variável, como veremos na Seção 29.5.4, Passo. A variável clockText é do tipo StaticText. A única lógica requerida nessa página é configurar o texto do componente clockText como a hora atual no servidor. Fazemos isso no método prerender (linhas 58–62). O significado desse e de outros métodos de bean de página é discutido na Seção 29.5.3. As linhas 60–61 buscam e formatam a hora no servidor e configuram o valor de clockText como essa hora.

29.5.3  Ciclo de vida de processamento de eventos Vários métodos no bean de página associam ao ciclo de vida do processamento de eventos JSF quatro etapas principais — inicialização, pré-processamento, pré-renderização e destruição. Cada um equivale a um método na classe de bean de página — init, prepro­ cess, prerender e destroy, respectivamente. O NetBeans cria esses métodos sobrescritos para que você possa personalizá-los a fim de tratar tarefas de processamento de ciclo de vida, como exibição de um elemento em uma página somente se um usuário clicar em um botão. O método init (Figura 29.6, linhas 34–48) é chamado pelo contêiner JSP na primeira vez que a página é solicitada e em postbacks. Um postback ocorre quando os dados de formulário são submetidos, e a página e seu conteúdo são enviados ao servidor para ser processados. O método init invoca sua versão da superclasse (linha 36) e então tenta chamar o método _init (declarado nas linhas 13–15). O método _init também é automaticamente gerado e trata tarefas de inicialização de componentes (se houver um), tais como configurar as opções de um grupo de botões de opção. O método preprocess (linhas 52–54) é chamado depois de init, mas somente se a página estiver processando um postback. O método prerender (linhas 58–62) é chamado imediatamente antes de uma página ser renderizada (isto é, exibida) pelo navegador. Esse método deve ser utilizado para configurar as propriedades do componente; as propriedades que são configuradas mais cedo (como no método init) podem ser sobrescritas antes de a página ser de fato renderizada pelo navegador. Por essa razão, configuramos o valor de clockText no método prerender. Por fim, o método destroy (linhas 66–68) é chamado depois que a página foi renderizada, mas somente se o método init for chamado. Esse método trata tarefas, como liberar os recursos utilizados para renderizar a página.

29.5.4  Criando um aplicativo Web no NetBeans Agora que apresentamos o arquivo JSP, o arquivo de bean de página e a página Web XHTML resultante enviada ao navegador Web, discutiremos os passos para criar esse aplicativo. Para construir o aplicativo WebTime, siga estes passos no NetBeans:

Passo 1: criando o projeto do aplicativo Web Selecione File> New Project... para exibir o diálogo New Project. Selecione Java Web no painel Categories, Web Application no painel Projects e clique em Next>. Mude o nome do projeto para WebTime. No campo Project Location, especifique onde você quer armazenar o projeto. Essas configurações criarão um diretório WebTime para armazenar os arquivos do projeto no diretório-pai que você especificou. Mantenha as outras configurações padrão e clique em Next>. Mantenha as opções padrão para Server and Settings e clique em Next>. Selecione Visual Web Java­ Server Faces como o framework a utilizar nesse aplicativo Web, clique então em Finish para criar o projeto do aplicativo Web. Passo 2: examinando a janela do editor visual do novo projeto As várias figuras a seguir descrevem recursos importantes do NetBeans, começando com a janela do editor visual (Figura 29.7). O NetBeans cria uma página Web única nomeada Page1 para cada novo projeto. Essa página é aberta por padrão no editor visual no modo Design quando o projeto é carregado pela primeira vez. À medida que você arrasta e solta novos componentes na página, o modo Design permite ver como o navegador irá renderizar a página. O arquivo JSP para essa página, nomeado Page1.jsp, pode ser visualizado clicando



29.5  Criando e executando um aplicativo simples no NetBeans

957

no botão JSP na parte superior do editor visual ou clicando como botão direito do mouse em qualquer lugar no editor visual e selecionando Edit JSP Source. Como mencionado anteriormente, cada página da Web é suportada por um arquivo de bean de página. O NetBeans cria um arquivo nomeado Page1.java quando um novo projeto é criado. Para abri-lo, clique no botão Java na parte superior do editor visual ou clique com o botão direito do mouse em qualquer lugar no editor visual e escolha Edit Java Source. O botão Preview in Browser na parte superior da janela do editor visual permite visualizar suas páginas em um navegador sem a necessidade de construir e executar o aplicativo inteiro. O botão Refresh redesenha a página no editor visual. O botão Show Virtual Forms permite ver quais elementos de formulário participam de formulários virtuais (discutidos no Capítulo 30). A lista drop-down Target Browser Size permite especificar a melhor resolução de navegador para visualizar a página e permite ver a aparência da página em diferentes resoluções da tela. Preview in Browser…

Refresh

Show Virtual Forms

Target Browser Size

Modo Design Visualiza arquivo JSP Visualiza arquivo bean de página Java

Figura 29.7  |  Janela do editor visual no modo Design.

Passo 3: examinando a Palette no NetBeans A Figura 29.8 mostra a Pallete exibida no IDE quando o projeto é carregado. A parte (a) exibe a lista Woodstock Basic dos componentes Web, e a parte (b) exibe os componentes Woodstock Layout. Woodstock é um conjunto dos componentes da interface com o usuário para aplicativos JavaServer Faces. Você pode aprender mais sobre esses componentes em woodstock.dev.java.net. Discutimos componentes específicos na Figura 29.8 uma vez que eles são utilizados por todo o capítulo. (a) Categoria Woodstock Basic de Palette

(b) Categoria Woodstock Layout de Palette e outras categorias ocultas

Figura 29.8  |  Palette no NetBeans.

Passo 4: examinando a janela Projects A Figura 29.9 exibe a janela Projects que aparece no canto superior esquerdo do IDE. Essa janela exibe a hierarquia de todos os arquivos incluídos no projeto. Os arquivos JSP para cada página são listados abaixo do nó Web Pages. Esse nó também inclui a pasta resources que contém folhas de estilo CSS para o projeto e todos os outros arquivos que as páginas talvez precisem para exibir apropriadamente, como arquivos de imagem. O código-fonte Java, incluindo o arquivo de bean de página para cada página Web e os beans de aplicativo, sessão e escopo de solicitação, pode ser encontrado sob o nó Source Packages.

958

Capítulo 29  Aplicativos Web JavaServer™ Faces Folhas de estilos CSS, imagens e outros recursos de página Arquivo JSP

Arquivo bean de página Java

Figura 29.9  |  Janela Projects para o projeto WebTime.

Passo 5: examinando arquivos JSP e Java no IDE A Figura 29.10 exibe Page1.jsp — o documento JSP gerado pelo NetBeans para Page1. [Nota: Reformatamos o código para corresponder às nossas convenções de codificação.] Clique no botão JSP na parte superior do editor visual para visualizar esse arquivo. Quando esse arquivo é criado pela primeira vez, ele contém elementos para configurar a página, incluindo links para a folha de estilo da página e declaração das bibliotecas JSF que serão utilizadas. Por padrão, o NetBeans não mostra números de linha no editor de código-fonte. Para visualizar os números de linha, selecione View> Show Line Numbers.

Figura 29.10  |  O documento JSP gerado para Page1 pelo NetBeans.

A Figura 29.11 exibe parte do Page1.java — o arquivo de bean de página gerado pelo NetBeans para Page1. Clique no botão Java no editor para abrir o arquivo de bean de página. Esse arquivo contém uma classe Java com o mesmo nome que a página (isto é, Page1), que

Clique no sinal de mais (+) para exibir o código oculto gerado

Figura 29.11  |  Arquivo de bean de página para Page1.jsp gerado pelo NetBeans.



29.5  Criando e executando um aplicativo simples no NetBeans

959

estende a classe AbstractPageBean. Como já mencionado, AbstractPageBean tem vários métodos que gerenciam o ciclo de vida da página. Quatro destes métodos — init, preprocess, prerender e destroy — são sobrescritos por Page1.java. Além do método init, esses métodos inicialmente estão vazios. Eles servem como reserva de espaço para personalizar o comportamento do seu aplicativo Web.

Passo 6: renomeando os arquivos JSP e Java Em geral, é recomendável renomear os arquivos JSP e Java para que os nomes sejam relevantes ao seu aplicativo. Clique com o botão direito do mouse em Page1.jsp na janela Projects e selecione Refactor> Rename para exibir a caixa de diálogo Rename Page1. Digite o novo nome, Time, e marque a caixa de seleção Apply Rename on Comments. Para fazer as modificações imediatamente, clique no botão Refactor. Se você quiser visualizar as modificações antes que elas sejam aplicadas, clique no botão Preview para exibir a janela Refactoring na parte inferior do IDE. A refatoração é o processo de modificar o código-fonte para aprimorar sua legibilidade e resusabilidade sem alterar seu comportamento — por exemplo, renomeando métodos ou variáveis, ou dividindo métodos longos em métodos mais curtos. O NetBeans tem ferramentas de refatoração internas que automatizam algumas tarefas de refatoração. Usar essas ferramentas para renomear os arquivos de projeto atualiza o nome do arquivo JSP para Time.jsp e seu arquivo de bean de página correspondente para Time. java. A ferramenta de refatoração também muda o nome de classe no arquivo de bean de página e todas as vinculações de atributo no documento JSP para refletir o novo nome de classe. Se optar por visualizar as modificações de refatoração, as modificações só serão feitas depois de você clicar no botão Do Refactoring na janela Refactoring. Passo 7: alterando o título da página Antes de criar o conteúdo da página Web, atribuímos o título "Web Time: A Simple Example" a ela. Por padrão, a página não tem um título quando é gerada pelo IDE. Para adicionar um título, abra o arquivo JSP no modo Design. Na janela Properties, insira o novo título ao lado da propriedade Title e pressione Enter. Examine o JSP para ver se o atributo title= "Web Time: A Simple Example" foi automaticamente adicionado ao tag webuijsf:head. O título da página aparecerá na barra de título do navegador quando ela for exibida. Passo 8: criando a página Criar uma página Web é simples no NetBeans. Para adicionar componentes à página no modo Design, arraste e solte-os da Palette até a página. Cada componente é um objeto que tem propriedades, métodos e eventos. Você pode configurar essas propriedades e eventos visualmente utilizando a janela Properties ou programaticamente no arquivo de bean de página. O IDE gera os tags JSP para os componentes que você arrasta e solta utilizando um layout de grade, como especificado no tag webuijsf:body. Os componentes são exibidos utilizando o posicionamento absoluto — eles aparecem exatamente onde são soltos na página. À medida que você adiciona componentes, o atributo style no elemento JSP de cada componente incluirá o número de pixels das margens superiores e esquerdas da página nas quais o componente é posicionado. Esse exemplo utiliza dois componentes Static Text. Para adicionar o primeiro, arraste e solte-o da lista de componentes Woodstock Basic da Palette até a página. Edite o texto do componente digitando "Current time on the web server:" diretamente no componente. O texto também pode ser editado alterando a propriedade text do componente na janela Properties. O NetBeans é um editor WYSIWYG (What You See Is What You Get) — quando você faz uma modificação em uma página no modo Design, o IDE cria a marcação (visível no modo JSP) necessária para alcançar os efeitos visuais desejados vistos no modo Design e também poderia adicionar código Java ao arquivo .java do bean de página. Depois de adicionar o texto à página Web, alterne para o modo JSP. Você deve ver que o IDE aninhou um elemento webuijsf:staticText (com o atributo text que contém o texto que você acabou de inserir) no elemento webuijsf:form dentro do elemento webuijsf:body. O componente Static Text é vinculado ao objeto staticText1 no arquivo de bean de página. De volta ao modo Design, clique no componente Static Text para selecioná-lo. Na janela Properties, clique no botão de reticências (…) ao lado da propriedade style a fim de abrir uma caixa de diálogo para editar o estilo do texto. Escolha 18 px para o tamanho da fonte e clique em OK. Novamente na janela Properties, altere a propriedade id para timeHeader. Configurar a propriedade id também muda o nome da variável correspondente do componente no bean de página e atualiza seu atributo binding no JSP correspondentemente. O IDE agora deve se parecer como a Figura 29.12. Você pode examinar o arquivo JSP para ver se font-size: 18 px foi adicionado ao atributo style e se atributo id foi alterado para timeHeader no tag do componente.

Figura 29.12  |  Time.jsp depois de inserir o primeiro componente Static Text.

960

Capítulo 29  Aplicativos Web JavaServer™ Faces

Solte um segundo componente Static Text na página e configure o id como clockText. Edite sua propriedade style para que o tamanho da fonte seja 18 px, a cor do texto yellow e a cor do fundo black. Não edite o texto do componente, uma vez que isso será configurado programaticamente no arquivo de bean de página. O componente será exibido com o texto Static Text no IDE, mas ele só exibirá texto em tempo de execução se o texto foi configurado programaticamente. A Figura 29.13 mostra o IDE depois que o segundo componente é adicionado.

Passo 9: adicionando lógica de página Depois de criar a interface com o usuário, você pode modificar o arquivo de bean de página para configurar o texto do elemento clock­ Text. Para interagir com um componente JSF programaticamente, você primeiro precisa clique com o botão direito do mouse no controle no modo Design e escolha Add Binding Attribute. No arquivo de bean de página, isso cria uma variável que você pode utilizar para interagir com o componente bem como métodos set e get para acessar o componente.

Figura 29.13  |  Time.jsp depois de adicionar o segundo componente StaticText.

Nesse exemplo, adicionamos uma instrução ao método prerender (linhas 58–62 da Figura 29.6) no modo Java. Lembre-se de que utilizamos o método prerender para assegurar que clockText seja atualizado toda vez que a página for atualizada. As linhas 60–61 da Figura 29.6 configuram programaticamente o texto de clockText como a hora atual no servidor. Para que essa instrução funcione, você também precisará das duas imports mostradas nas linhas 8–9 da Figura 29.6. O IDE pode inserir essas instruções import para você. Simplesmente clique com o botão direito do mouse na janela do editor de código e escolha Fix Imports no menu pop-up que aparece. Queremos que essa página seja atualizada uma vez por minuto para exibir uma hora atualizada. Para realizar isso, adicione ao documento JSP, como o último elemento aninhado no elemento webuijsf:head. Esse elemento instrui o navegador a recarregar a página automaticamente a cada 60 segundos. Você também pode adicionar esse tag arrastando um componente Meta da seção Advanced da Palette para sua página e então configurando o atributo content do componente como 60 e seu atributo httpEquiv como refresh. Se fizer isso, o componente Meta será exibido na janela Navigator.

Passo 10: examinando a janela Navigator A Figura 29.14 exibe a janela Navigator no NetBeans. Expandimos o nó Time, que representa o arquivo de bean de página e mostra o conteúdo da árvore de componentes. Os beans de solicitação, sessão e escopo de aplicativo permanecem ocultos, uma vez que não adicionamos nenhuma propriedade a esses beans nesse exemplo. Clicar em um item na árvore de componentes da página seleciona o item no editor visual.

Figura 29.14  |  Janela Navigator no NetBeans.

Passo 11: executando o aplicativo Depois de criar a página Web, você pode visualizá-la de várias maneiras. Primeiro, selecione Run> Build Project e, depois da compilação terminar, selecione Run> Run Project para executar o aplicativo em uma janela de navegador. Você também pode executar um projeto que já foi compilado pressionando o ícone Run Project ( ) na barra de ferramentas na parte superior do IDE ou pressionando F6. Observe que o projeto

29.6 Componentes JSF

961

deve ser recompilado para refletir as alterações feitas quando o aplicativo for visualizado em um navegador — isso ocorrerá automaticamente se você executar o projeto sem o recompilar explicitamente. Como esse aplicativo foi compilado no sistema de arquivos local, o URL exibido na barra de endereços do navegador quando o aplicativo é executado será http­://localhost:8080/WebTime/ (o Figura 29.4), onde 8080 é o número da porta no qual o servidor de teste — GlassFish v2 UR2 — executa por padrão. Alternativamente, pressione Ctrl + F5 para compilar o aplicativo e então executá-lo no modo de depuração — o depurador interno NetBeans pode ajudá-lo a solucionar problemas em aplicativos. (Visite www.deitel.com/books/jhtp8/ para um vídeo que introduz o depurador NetBeans.) Se pressionar F6, o programa executará sem a depuração ativada.

Dica de prevenção de erro 29.1 Se tiver problemas para compilar seu projeto por causa de erros nos arquivos XML gerados pelo NetBeans utilizados para a compilação, tente limpar o projeto e o compilar novamente. Você pode fazer isso selecionando Run > Clean and Build Project ou pressionando Shift + F11.

Dica de prevenção de erro 29.2 Se tentar limpar e recompilar seu projeto e receber uma mensagem de erro indicando que um ou mais arquivos não podem ser excluídos, pare o servidor GlassFish e então tente novamente o processo de limpeza e recompilação. Para parar o servidor, clique na guia Services no NetBeans, expanda o nó Servers, clique com o botão direito do mouse em GlassFish v2 e escolha Stop. Depois de o servidor parar de executar, escolha Run> Clean and Build Project ou pressione Shift + F11 para limpar e recompilar o projeto.

Por fim, você pode executar seu aplicativo compilado abrindo uma janela de navegador e digitando o URL da página Web no campo Address. Como seu aplicativo reside no sistema de arquivos local, você deve primeiro iniciar o servidor de aplicativo GlassFish. Se executou

anteriormente o aplicativo utilizando um dos métodos citados, o servidor já estará inicializado. Do contrário, você pode iniciar o servidor a partir do IDE abrindo a guia Services (localizada no mesmo painel que o Projects), expandindo o nó Servers, dando um clique com o botão direito do mouse em GlassFish v2 e selecionando Start. Você pode então digitar o URL (incluindo o número de porta para o servidor de aplicativo, 8080) no navegador para executar o aplicativo. Para esse exemplo não é necessário digitar o URL inteiro, http://localhost:8080/ WebTime/faces/Time.jsp. O caminho para o arquivo Time.jsp (isto é, faces/Time.jsp) pode ser omitido, porque esse arquivo foi configurado por padrão como a página inicial do projeto. Para projetos com múltiplas páginas, você pode alterar a página inicial dando um clique com o botão direito do mouse na página desejada na janela Projects e selecionando Set As Start Page. A página inicial é indicada por um ícone com um símbolo de botão play verde ( ) ao lado do nome da página na janela Projects.

29.6 Componentes JSF Esta seção introduz alguns componentes JSF apresentados na Palette (Figura 29.8). A Figura 29.15 resume alguns componentes JSF utilizados nos exemplos do capítulo. Componente JSF

Descrição

Label

Exibe texto que pode ser associado a um elemento de entrada.

Static Text

Exibe texto que o usuário não pode editar.

Text Field

Coleta entrada de usuário e exibe texto.

Button

Desencadeia um evento quando clicado.

Hyperlink

Exibe um hiperlink.

Drop Down List

Exibe uma lista drop-down de opções.

Radio Button Group

Botões de opção de grupos.

Image

Exibe imagens (por exemplo, GIF e JPG).

Figura 29.15 | Componentes JSF comumente utilizados.

29.6.1 Componentes gráficos e de texto A Figura 29.16 exibe uma forma simples de coletar s entrada de usuário. Esse exemplo utiliza a maioria dos componentes na Figura 29.15. Todo o código na Figura 29.16 foi gerado pelo NetBeans em resposta a ações executadas no modo Design. Para criar esse aplicativo do zero, revise os passos na Seção 29.5.4. Esse exemplo não executa uma tarefa quando o usuário clica em Register. Mais adiante demonstramos como adicionar funcionalidades a muitos desses componentes.

962

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

Capítulo 29  Aplicativos Web JavaServer™ Faces























70 71 72 73 74 75

29.6  Componentes JSF

963





Imagem

Campo de texto

Lista suspensa

Grupo de botões de opções

Botão

Figura 29.16  |  Formulário de registro que demonstra componentes JSF.

Lembre-se de que NetBeans utiliza o posicionamento absoluto por padrão, portanto, os componentes são exibidos onde quer que eles sejam soltos no editor visual. Nesse exemplo, além do posicionamento absoluto, utilizamos um componente Grid Panel (linhas 30–45) do grupo de componentes Woodstock da Layout Palettte. O prefixo h: indica que ele pode ser encontrado na biblioteca de tags JSF HTML. Esse componente — um objeto da classe HtmlPanelGrid no pacote javax.faces.component.html — controla o posicionamento dos componentes que ele contém. O componente Grid Panel permite ao designer especificar o número de colunas que a grade deve conter. Os componentes podem então ser soltos dentro do painel, e eles serão automaticamente reposicionados em colunas uniformemente espaçadas na ordem na qual eles são soltos. Quando o número de componentes excede o número de colunas, o painel move os componentes adicionais para uma nova linha. O Grid Panel comporta-se como uma tabela XHTML e, na verdade, é exibido como uma tabela XHTML no navegador. Nesse exemplo, usamos o Grid Panel para controlar as posições dos componentes Image e Text Field na seção de informações do usuário da página.

Adicionando um componente de formatação a uma página Web Para criar o layout para a seção User Information do formulário mostrado na Figura 29.16, arraste um componente Grid Panel até a página. Na janela Properties, altere o componente id para gridPanel e configure a propriedade columns do componente como 4. O componente também tem propriedades para controlar o preenchimento de célula, espaçamento de célula e outros elementos da aparência do componente. Nesse caso, aceite os padrões para essas propriedades. Agora você pode simplesmente arrastar Imagens e Text Fields da seção informações de usuário até Grid Panel. O Grid Panel gerenciará o espaçamento e a organização em linhas e colunas. Examinando componentes Web em um formulário de registro de exemplo Aqui focalizaremos os novos elementos de interface com usuário no exemplo. As linhas 27–29 da Figura 29.16 definem uma Image, um objeto da classe ImageComponent que insere uma imagem em uma página Web. As imagens nesse exemplo estão localizadas no diretório ch29\images. As imagens a ser exibidas em uma página Web devem ser inseridas na pasta resources do projeto. Para adicionar imagens ao projeto, solte um componente Image na página e clique no botão de reticências ( ) ao lado da propriedade url na janela ­Properties. Isso abre uma caixa de diálogo na qual você pode selecionar a imagem a exibir. Como ainda nenhuma imagem foi adicionada à pasta r ­ esources, clique no botão Add File, localize o diretório de imagens ch29\images e clique no botão Add File a fim de copiar o arquivo que você selecionou para a pasta resources do projeto na pasta Web Pages. Agora você pode selecionar a imagem e clicar em OK para inseri-la na página.

964

Capítulo 29  Aplicativos Web JavaServer™ Faces

As linhas 30–45 contêm um elemento h:panelGrid que representa o componente Grid Panel. Dentro desse elemento, há oito componentes Image e Text Field. Text Fields permitem obter a entrada de texto do usuário. Por exemplo, a linha 35 define um controle Text Field utilizado para obter o primeiro nome. Você pode rotular um Text Field configurando a propriedade label, que posiciona o texto à esquerda do Text Field. Alternativamente, você pode rotular um Text Field arrastando e soltando um componente Label até a página, o que permite personalizar a posição e estilo do Label. Nesse exemplo, utilizamos imagens para indicar o objetivo de cada Text Field. A ordem na qual Text Fields são arrastados até a página é a ordem em que seus elementos JSP são adicionados ao documento JSP. Por padrão, quando o usuário pressiona a tecla Tab para navegar entre campos de entrada, o foco alterna entre um componente e outro na ordem em que os elementos JSP ocorrem no documento JSP. Para especificar a ordem de navegação, arraste componentes até a página na ordem apropriada, ou configure que cada propriedade tab-Index do campo de entrada na janela Properties para configurar explicitamente a ordem das guias. Um componente com um índice de guia de 1 será o primeiro na sequência de guias. As linhas 52–55 definem uma Drop Down List. Quando um usuário clica na lista drop-down, ela se expande e exibe uma lista em que o usuário pode fazer uma seleção. Esse componente é um objeto da classe DropDown e é vinculado ao objeto booksDropDownDefaultOptions, um objeto SingleSelectOptionsList que gerencia a lista de opções. Esse objeto pode ser configurado automaticamente clicando com o botão direito do mouse na lista drop-down no modo Design e selecionando Configure Default Options…, que abre a caixa de diálogo Options Customizer para que você possa adicionar opções à lista. Cada opção consiste em uma String de exibição que o usuário visualizará no navegador e um valor String que é retornado quando você recupera a seleção do usuário de uma lista drop-down. O NetBeans constrói o objeto SingleSelectOptionsList no arquivo de bean de página com base nos pares de valor de exibição inseridos na caixa de diálogo Options Customizer. Para visualizar o código que constrói o objeto, feche a caixa de diálogo clicando em OK, abra o arquivo de bean de página e expanda o nó Managed Component Definition que vem depois da chave de abertura da definição de classe. O objeto é construído no método _init, que é chamado pelo método init na primeira vez que a página carrega. O componente Hyperlink (linhas 56–59) da classe Hyperlink adiciona um hiperlink a uma página Web. A propriedade url desse componente especifica o recurso (http://www.deitel.com nesse caso) que é solicitado quando um usuário clica no hiperlink. Por padrão, componentes Hyperlink fazem páginas abrir na mesma janela de navegador, mas você pode configurar a propriedade target do componente para alterar esse comportamento. As linhas 63–66 definem um componente Radio Button Group da classe RadioButtonGroup, que fornece uma série de botões de opção a partir da qual o usuário pode selecionar apenas um. Como um Drop Down List, um Radio Button Group é vinculado a um objeto SingleSelectOptionList. As opções podem ser editadas clicando com o botão direito do mouse no componente e selecionando Configure Default Options…. Assim como a lista drop-down, o SingleSelectOptionsList é automaticamente gerado pelo IDE e inserido no método _init da classe de bean de página. As linhas 67–69 definem um componente Button da classe Button que desencadeia uma ação quando clicado. Em geral, um componente Button mapeia para um elemento XHTML input com o atributo type configurado como submit. Como afirmado antes, clicar no botão Register nesse exemplo não faz nada.

29.6.2  Validação utilizando componentes validadores e validadores personalizados Validar a entrada de usuário é um passo importante ao coletar informações de usuários. A validação de formulário ajuda a evitar erros de processamento por causa de entrada de usuário incompleta ou impropriamente formatada. Por exemplo, você pode executar a validação para assegurar que todos os campos requeridos contenham dados ou que um campo de código do código postal tenha o número correto de dígitos. O NetBeans fornece três componentes validadores. Um Length Validator determina se um campo contém um número aceitável de caracteres. Double Range Validators e Long Range Validators determinam se a entrada numérica está dentro de intervalos aceitáveis. O pacote javax.faces.validators contém as classes para esses validadores. O NetBeans também permite validação personalizada com métodos validadores no arquivo de bean de página. O exemplo a seguir demonstra a validação utilizando tanto um validador de componente como uma validação personalizada.

Validando dados de formulário em um aplicativo Web O exemplo nesta seção solicita que o usuário digite um nome, endereço de e-mail e número de telefone. Depois que o usuário insere quaisquer dados, mas antes que os dados sejam enviados ao servidor Web, a validação assegura que o usuário inseriu um valor em cada campo, que o nome digitado não excede 30 caracteres, e que o endereço de e-mail e os valores de número de telefone estão em um formato aceitável. Se o JavaScript não estiver ativado no cliente, a validação será então executada no servidor. Nesse exemplo, (555) 123–4567, 555–123–4567 e 123–4567 são todos considerados números de telefone válidos. Depois que os dados são submetidos, o servidor Web responde exibindo uma mensagem apropriada e um componente Grid Panel repetindo as informações submetidas. Observe que, em geral, um aplicativo do negócio real armazenaria os dados submetidos em um banco de dados ou em um arquivo no servidor. Simplesmente enviamos os dados de volta à página para demonstrar que o servidor recebeu os dados. Criando a página Web [Nota: Para criar esse aplicativo do zero, revise os passos na Seção 29.5.4.] Esse aplicativo Web introduz os componentes JSF Label e Mes­ sage da seção Woodstock Basic da Palette. Cada um dos três campos de texto da página deve ter seu próprio rótulo e mensagem. Componentes Label descrevem outros componentes e podem ser associados a campos de entrada de usuário configurando a propriedade for. Componentes Message exibem mensagens de erro quando a validação falha. Essa página requer três Text Fields, três Labels e três Messages, bem como um Button de submissão. Para associar os componentes Label e componentes Message com seus componentes Text Field correspondentes,



29.6  Componentes JSF

965

mantenha pressionadas as teclas Ctrl e Shift, então arraste o rótulo ou a mensagem até o Text Field apropriado. Na janela Properties, observe que a propriedade for de cada componente Label e Message é configurada com o Text Field apropriado. Você também deve adicionar um componente Static Text para exibir uma mensagem de sucesso de validação na parte inferior da página. Configure o texto como "Thank you for your submission.
We received the following information:" e altere id do componente para resultText. Na janela Properties, redefina as propriedades rendered e escape do componente. A propriedade rendered controla se o componente será exibido na primeira vez que a página carrega. Configurar escape como false (isto é, desmarcado) permite que o navegador reconheça o tag
de XHTML para que ele possa iniciar uma nova linha do texto em vez de exibir os caracteres "
" na página Web.

Observações sobre a aparência e comportamento 29.1 Ao configurar propriedade rendered de um componente como false (desmarcada), o componente não mais aparecerá no editor visual. Para selecionar esse controle a fim de manipular as suas propriedades, utilize a janela Navigator no modo Design (como mostrado na Figura 29.12).

Adicione um componente Grid Panel abaixo do componente resultText. O painel deve ter duas colunas, uma para exibir componentes Static Text que rotulam os dados validados do usuário (nomeados nameText, emailText e phoneText, respectivamente), e uma para exibir componentes Static Text que exibem esses dados (nomeados nameValueText, emailValueText e phoneValueText, respectivamente). A propriedade rendered do painel deve ser configurada como false para que não seja exibida inicialmente.

Adicionando atributos de vinculação para interagir programaticamente com componentes Lembre-se de que para cada controle com o qual você planeja interagir programaticamente, será necessário clicar com o botão direito do mouse no controle no modo Design e selecionar Add Binding Attribute para adicionar uma propriedade do controle ao arquivo de bean de página. Nesse exemplo, você deve fazer isso para cada um dos componentes Text Field (para poder obter seus valores), para o componente resultText Static Text (para poder exibi-lo), para o componente Grid Panel (para poder exibi-lo) e para os componentes Static Text name­ ValueText, emailValueText e phoneValueText no Grid Panel (para poder configurar o texto). Revisando o documento JSP O arquivo JSP para essa página é exibido na Figura 29.17. As linhas 31–35, 42–46 e 54–58 definem webuijsf:textFields para recuperar o nome, endereço de correio eletrônico e número de telefone do usuário, respectivamente. As linhas 28–30, 39–41 e 51–53 definem webuijsf:labels para cada um desses campos de texto. As linhas 36–38, 47–50 e 59–62 definem os elementos webuijsf:message dos campos de texto. As linhas 63–66 definem um Submit webuijsf:button. As linhas 67–71 criam um webuijsf:staticText nomeado resultText que exibe o texto quando o usuário submete com sucesso o formulário, e as linhas 72–91 definem um h:panelGrid que contém componentes para exibir a entrada de usuário validada no navegador. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27











966 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97

Capítulo 29  Aplicativos Web JavaServer™ Faces



validate. Isso cria um método de validação para o componente com um corpo vazio no arquivo de bean de página. Adicionaremos o código a esse método mais adiante. Observe que os atributos validatorExpression de emailTextField e phoneTextField são vinculados aos seus respectivos métodos de validação personalizados no arquivo de bean de página (linhas 45–46 e 57–58). Examinando no arquivo de bean de página um formulário que recebe entrada de usuário A Figura 29.18 contém o arquivo de bean de página do arquivo JSP na Figura 29.17. A linha 21 configura o tamanho máximo para o nameLengthValidator, que é uma propriedade desse bean de página. Lembre-se de que o campo de texto de nome foi vinculado a essa propriedade no documento JSP. Os métodos emailTextField_validate (linhas 189–200) e phoneTextField_validate (linhas 204–216) são métodos validadores personalizados que verificam o endereço de correio eletrônico e número de telefone inserido pelo usuário, respectivamente. O método submitButton_action (linhas 219–230) ecoa os dados de volta ao usuário se a validação for bem-sucedida. Os métodos validadores são chamados antes do handler de evento, assim se a validação falhar, submitButton_action não será chamado e a entrada de usuário não será ecoada. 1 2 3 4 5 6 7 8 9 10

// Figura 29.18: Validation.java // Validando a entrada de usuário. package validation; import import import import import import

com.sun.rave.web.ui.appbase.AbstractPageBean; com.sun.webui.jsf.component.StaticText; com.sun.webui.jsf.component.TextField; javax.faces.FacesException; javax.faces.application.FacesMessage; javax.faces.component.UIComponent;

11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231

29.6  Componentes JSF import import import import

969

javax.faces.component.html.HtmlPanelGrid; javax.faces.context.FacesContext; javax.faces.validator.LengthValidator; javax.faces.validator.ValidatorException;

public class Validation extends AbstractPageBean { // Definição de componente gerenciado private void _init() throws Exception { nameLengthValidator.setMaximum( 30 ); } // fim do método _init // Para economizar espaço, omitimos o código nas linhas 24–186. O código-fonte // completo é fornecido com os exemplos deste capítulo. // valida o endereço de e-mail inserido contra a expressão regular // isso representa a forma de um endereço de e-mail válido public void emailTextField_validate( FacesContext context, UIComponent component, Object value ) { String email = String.valueOf( value ); // se o endereço de e-mail inserido não estiver em um formato válido if ( !email.matches( "\\w+([-+.’]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*" ) ) { throw new ValidatorException( new FacesMessage( "Enter a valid e-mail address, e.g. [email protected]" ) ); } // fim do if } // fim do método emailTextField_validate // valida o número de telefone inserido contra a expressão regular // isso representa a forma de um número de telefone válido public void phoneTextField_validate( FacesContext context, UIComponent component, Object value ) { String phone = String.valueOf( value ); // se o número de telefone inserido não estiver em um formato válido if ( !phone.matches( "((\\(\\d{3}\\) ?)|(\\d{3}-))?\\d{3}-\\d{4}" ) ) { throw new ValidatorException( new FacesMessage( "Enter a valid phone number, e.g. (555) 555-1234" ) ); } // fim do if } // fim do método phoneTextField_validate // exibe os valores que o usuário inseriu e submeteu public String submitButton_action() { String name = String.valueOf(nameTextField.getValue() ); String email = String.valueOf(emailTextField.getValue() ); String phone = String.valueOf(phoneTextField.getValue() ); nameValueText.setValue( name ); emailValueText.setValue( email ); phoneValueText.setValue( phone ); gridPanel.setRendered( true ); resultText.setRendered( true ); return null; } // fim do método submitButton_action } // fim da classe Validation

Figura 29.18  |  O bean de página para validar a entrada de usuário e reexibir essa entrada se válida.

Os dois métodos validadores personalizados nesse arquivo de bean de página validam o conteúdo de um campo de texto contra uma expressão regular utilizando o método String matches, que recebe uma expressão regular como um argumento e retorna true se a String estiver em conformidade com o formato especificado.

970

Capítulo 29

Aplicativos Web JavaServer™ Faces

Para o método emailTextField_validate, utilizamos a seguinte expressão de validação \w+([­+.']\w+)*@\w+([­.]\w+)*\.\w+([­.]\w+)*

Observe que cada barra invertida na expressão regular String (linha 196) deve ser escapada com outra barra invertida (como em \\), porque o caractere de barra invertida normalmente representa o início de uma sequência de escape no Java. Essa expressão regular indica que um endereço de e-mail é válido se a parte antes do símbolo @ contiver um ou mais caracteres de texto (isto é, caracteres alfanuméricos ou sublinhados), seguidos por zero ou mais Strings compreendidas por um hífen, sinal de adição, ponto ou apóstrofo e caracteres de texto adicionais. Depois do símbolo @, um endereço de e-mail válido deve conter um ou vários grupos de caracteres de texto potencialmente separados por hífens ou pontos, seguidos por um ponto requerido e outro grupo de um ou mais caracteres de texto potencialmente separados por hífens ou pontos. Por exemplo, os endereços de e-mail [email protected], bob­white@my­email.com e bob. [email protected] de bob são todos válidos. Se o usuário inserir texto em um emailTextField que não tem o formato correto e tentar submeter o formulário, as linhas 197–198 lançarão uma ValidatorException. O componente Message captura essa exceção e exibe a mensagem em vermelho. A expressão regular em phoneTextField_validate assegura que phoneTextField contém um número de telefone válido antes de o formulário ser submetido. A entrada de usuário é correspondida contra a expressão regular ((\(\d{3}\) ?)|(\d{3}­))?\d{3}­\d{4}

(Novamente, cada barra invertida é escapada na expressão regular String na linha 211.) Essa expressão indica que um número de telefone pode conter um código de área de três dígitos entre parênteses seguido por um espaço opcional ou sem parênteses e seguido por um hífen obrigatório. Depois de um código de área opcional, um número de telefone deve conter três dígitos, um hífen e outros quatro dígitos. Por exemplo, (555) 123­4567, 555­123­4567 e 123­4567 são todos números de telefone válidos. Se um usuário inserir um número de telefone inválido, as linhas 213–214 lançarão uma ValidatorException. O componente Message captura essa exceção e exibe a mensagem de erro em vermelho. Se os seis validadores forem bem-sucedidos (isto é, cada TextField contém dados, o nome terá menos de 30 caracteres e o endereço de e-mail e número de telefone são válidos), clicar no botão Submit envia os dados do formulário ao servidor. Como mostrado na Figura 29.17 (d), o método submitButton_action exibe os dados submetidos no componente gridPanel de Grid Panel (linhas 221–227) e exibe o componente resultsText Static Text (linha 228).

29.7 Monitoramento de sessão Nos primórdios da internet, e-businesses não podiam fornecer o tipo de serviço personalizado que lojas físicas (brick-and-mortar) geralmente forneciam. Para resolver esse problema, lojas virtuais começaram a estabelecer mecanismos por meio dos quais eles poderiam personalizar experiências de navegação dos usuários, personalizando o conteúdo para usuários específicos e, ao mesmo tempo, permitindo que eles pulassem informações irrelevantes. Os negócios alcançam esse nível de serviço monitorando o movimento de cada cliente nos seus sites Web e combinando os dados coletados com as informações fornecidas pelos consumidores, incluindo informações de cobrança, preferências pessoais, interesses e hobbies.

Personalização A personalização permite que sites de comércio eletrônico se comuniquem efetivamente com seus clientes e também aprimora a capacidade do usuário de localizar produtos e serviços desejados.. Empresas que fornecem conteúdo de interesse particular para usuários podem estabelecer relacionamentos com clientes e aprimorar esses relacionamentos ao longo do tempo. Além disso, visando aos consumidores com ofertas pessoais, recomendações, anúncios, promoções e serviços, as lojas de comércio eletrônico estimulam a fidelidades dos clientes. Sites Web podem utilizar tecnologias sofisticadas para permitir que visitantes personalizem home pages para que estas se ajustem às suas necessidades e preferências individuais. De maneira semelhante, sites de compra on-line muitas vezes armazenam informações pessoais para os clientes, personalizando notificações e ofertas especiais de acordo com seus interesses. Esses serviços estimulam os clientes a visitar os sites e fazer compras mais frequentemente. Privacidade Há, porém, um dilema entre serviços de comércio eletrônico personalizados e proteção da privacidade. Alguns consumidores aceitam a ideia do conteúdo personalizado, mas outros temem as possíveis consequências adversas se as informações que eles fornecem aos sites de comércio eletrônico forem publicadas ou coletadas por tecnologias de rastreamento. Consumidores e defensores da privacidade perguntam: E se os e-businesses aos quais fornecemos dados pessoais venderem ou disponibilizarem essas informações a outra organização sem nosso conhecimento? E se não quisermos que nossas ações na Internet — um meio supostamente anônimo — sejam rastreadas e registradas por partes desconhecidas? E se as partes não autorizadas ganharem acesso aos dados privados confidenciais, como números de cartão de crédito ou histórico médico? Todas essas são questões que devem ser discutidas e abordadas por programadores, consumidores, sites de comércio eletrônico e legisladores.



29.7  Monitoramento de sessão

971

Reconhecendo clientes Para fornecer serviços personalizados aos consumidores, os sites de comércio eletrônico devem ser capazes de reconhecer os clientes quando eles solicitam informações de um site. Como discutido, o sistema de solicitação/resposta no qual a Web opera é facilitado pelo HTTP. Infelizmente, o HTTP é um protocolo sem estado — ele não suporta conexões persistentes que permitiriam aos servidores Web manter informações de estado em relação a determinados clientes. Desse modo, os servidores Web não podem determinar se uma solicitação vem de um determinado cliente ou se uma série de solicitações vem de um ou mais clientes. Para superar esse problema, os sites podem fornecer mecanismos para identificar clientes específicos. Uma sessão representa um único cliente em um site Web. Se o cliente sair de um site e então retornar mais tarde, o cliente ainda será reconhecido como o mesmo usuário. Para ajudar o servidor a distinguir entre clientes, cada cliente deve identificar-se para o servidor. O rastreamento de clientes específicos, conhecido como rastreamento de sessão, pode ser alcançado de vários modos nos JSPs. Uma técnica popular utiliza cookies (Seção 29.7.1); outra utiliza o objeto SessionBean (Seção 29.7.2). Técnicas de rastreamento de sessão adicionais incluem o uso de elementos input form do tipo "hidden" e rescrita de URL. Com elementos de formulário "hidden", um Web Form pode gravar dados de rastreamento de sessão em um form na página Web que ele retorna ao cliente em resposta a uma solicitação prévia. Quando o usuário submete o formulário na nova página Web, todos os dados de formulário, incluindo os campos "hidden", são enviados ao handler de formulário no servidor Web. Com a rescrita de URL, o servidor Web incorpora as informações de rastreamento de sessão diretamente nos URLs dos hiperlinks em que o usuário clica para enviar solicitações subsequentes ao servidor Web.

29.7.1  Cookies Cookies fornecem aos desenvolvedores Web uma ferramenta para personalizar páginas Web. Um cookie é um dado, em geral, armazenado em um arquivo de texto no computador do usuário. Um cookie mantém informações sobre o cliente durante e entre sessões de navegador. Na primeira vez que um usuário visita o site Web, o computador do usuário poderia receber um cookie; esse cookie é então reativado toda vez que o usuário revisita esse site. O objetivo é criar um registro anônimo que contém os dados utilizados para personalizar as futuras visitas do usuário ao site. Por exemplo, cookies em um aplicativo de compra poderiam armazenar identificadores únicos para usuários. Quando um usuário adiciona itens a um carrinho de compras on-line ou realiza outra tarefa resultante em uma solicitação para o servidor Web, o servidor recebe um cookie do cliente contendo o identificador único do usuário. O servidor utiliza então o identificador único para localizar o carrinho de compras e executar qualquer processamento necessário. Além de identificar usuários, cookies também podem indicar preferências de compra dos clientes. Quando um servidor Web recebe uma solicitação de um cliente, o servidor pode examinar o(s) cookie(s) que ele enviou ao cliente durante a comunicação anterior, identificar as preferências do cliente e exibir imediatamente produtos que interessam ao cliente. Cada interação baseada em HTTP entre um cliente e um servidor inclui um cabeçalho contendo as informações sobre a solicitação (quando a comunicação é do cliente para o servidor) ou sobre a resposta (quando a comunicação é do servidor para o cliente). Quando uma página recebe uma solicitação, o cabeçalho inclui informações como o tipo de solicitação (por exemplo, GET ou POST) e quaisquer cookies que foram enviados anteriormente a partir do servidor a ser armazenado na máquina do cliente. Quando o servidor formula sua resposta, as informações de cabeçalho contêm todos os cookies que o servidor quer armazenar no computador do cliente e outras informações, como o tipo MIME da resposta. A data de expiração de um cookie determina quanto tempo o cookie permanece no computador do cliente. Se você não configurar uma data de expiração para um cookie, o navegador Web manterá o cookie pela duração da sessão de navegação. Do contrário, o navegador Web manterá o cookie até a data de expiração ocorrer. Quando o navegador solicita um recurso de um servidor Web, os cookies anteriormente enviados ao cliente por esse servidor Web são retornados ao servidor Web como parte da solicitação formulada pelo navegador. Cookies são excluídos quando expiram.

Dica de portabilidade 29.1 Os clientes podem desativar cookies nos navegadores Web para mais privacidade. Quando esses clientes utilizam aplicativos Web que dependem de cookies para manter informações de estado, os aplicativos não executarão corretamente.

Utilizando cookies para fornecer recomendações sobre livros O próximo aplicativo Web mostra como utilizar cookies. O exemplo contém duas páginas. Na página Options.jsp (figuras 29.19 e 29.21), os usuários selecionam uma linguagem de programação favorita a partir de um grupo de botões de opção e submetem o formulário ao servidor Web para processamento. O servidor Web responde criando um cookie que armazena a linguagem selecionada e o número ISBN de um livro recomendado sobre esse tema. O servidor exibe então diferentes componentes no navegador que permitem ao usuário visualizar as opções e selecionar outra linguagem de programação favorita ou visualizar a página Recommendations.jsp no nosso aplicativo (Figuras 29.22–29.23), que lista livros recomendados que pertencem à(s) linguagem(ns) de programação que o usuário selecionou. Como iremos ocultar e mostrar programaticamente os componentes no arquivo Options.jsp, cada componente na página requer um atributo de vinculação. Quando o usuário clica no hiperlink para visualizar os livros recomendados, os cookies anteriormente armazenados no cliente são enviados ao servidor, lidos pelo aplicativo e utilizados para formar a lista de livros recomendados.

972

Capítulo 29  Aplicativos Web JavaServer™ Faces

O arquivo Options.jsp na Figura 29.19 contém um Radio Button Group (linhas 24–28) com as opções Java, C++, Visual Basic 2008, Visual C# 2008 e Internet & Web. Lembre-se de que você pode configurar a exibição e o valor de Strings dos botões de opção clicando com o botão direito do mouse no Radio Button Group e selecionando Configure Default Options.... O código para essas opções é mostrado nas linhas 22–34 da Figura 29.21. O usuário seleciona uma linguagem de programação clicando em um botão de opção e então pressionando Submit para enviar a seleção ao servidor. Isso cria uma solicitação POST HTTP para o aplicativo Web no servidor, que obtém a seleção do usuário, cria um cookie que contém a seleção e o adiciona ao cabeçalho de resposta HTTP que é enviado ao cliente como parte da resposta. O navegador armazena então o cookie no computador cliente. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55



















29.7  Monitoramento de sessão

973

(a) O usuário seleciona uma linguagem de programação e clica em Submit para solicitar novamente Options.jsp.

(b) Options.jsp exibe uma mensagem de boas-vindas e fornece links que permitem ao usuário selecionar outras linguagens ou visualizar recomendações sobre o livro. O usuário escolhe selecionar outra linguagem, que solicita novamente Options.jsp.

(c) O usuário seleciona uma linguagem de programação e clica em Submit para solicitar novamente Options.jsp.

(d) Options.jsp exibe uma mensagem de boas-vindas e fornece links que permitem ao usuário selecionar outra linguagem ou visualizar recomendações sobre o livro. O usuário escolhe visualizar recomendações sobre o livro.

Figura 29.19  |  O arquivo JSP que permite ao usuário selecionar uma linguagem de programação.

Quando o usuário clica em Submit, os elementos webuijsf:staticText, webuijsf:radioButtonGroup e webuijsf:button utilizados para selecionar uma linguagem permanecem ocultos, e um elemento webuijsf:staticText e dois elementos webuijsf:hyperlink são exibidos. As propriedades rendered de um webuijsf:staticText e de dois webuijsf:hyperlinks são inicialmente configuradas como false (linhas 35, 41 e 46). Isso indica que esses componentes não são visíveis quando a página carrega, uma vez que queremos que primeiro o usuário visualize a página para incluir somente os componentes para selecionar uma linguagem de programação e submeter a seleção.

974

Capítulo 29  Aplicativos Web JavaServer™ Faces

O primeiro hiperlink (linhas 38–43) solicita essa página e o segundo (linhas 44–49) solicita Recommendations.jsp. A propriedade url não é configurada para o primeiro link; discutimos isso mais adiante. A propriedade url do segundo link é configurada como /faces/ Recommendations.jsp. Lembre-se de que no início do capítulo configuramos uma propriedade url para um site Web remoto (http:// www.deitel.com). Para configurar essa propriedade para uma página dentro do aplicativo em uso, você pode clicar no botão de reticências

( ) ao lado da propriedade url na janela Properties a fim de abrir uma caixa de diálogo que contém uma lista das páginas do aplicativo, e então selecionar uma página existente como o destino do link.

Adicionando e vinculando a uma nova página Web Para configurar a propriedade url como uma página de destino (isto é, Recommendations.jsp) no aplicativo em uso, a página de destino já deve existir. Para criar Recommendations.jsp, clique com o botão direito do mouse no nó Web Pages na janela Projects e selecione New> Visual Web JSF Page… no menu que aparece. Na caixa de diálogo New Visual Web JSF Page, mude o nome da página para Recommendations e clique em Finish para criar os arquivos Recommendations.jsp e Recommendations.java. (Discutiremos o conteúdo desses arquivos em breve.) Você agora pode selecionar Recommendations.jsp como o valor url para recommendationsLink. É possível ver o valor url na linha 49. Ocultando e exibindo elementos de uma página e solicitando a página novamente Quando o usuário clica em languagesLink, queremos solicitar Options.jsp novamente e exibir a lista de opções para que usuário possa fazer outra escolha. Em vez de configurar a propriedade url de languagesLink, adicionaremos um handler de ação desse componente ao bean de página. Você pode fazer isso clicando com botão direito do mouse em languagesLink na janela Navigator (enquanto no modo Design) e selecionando Edit action Event Handler. O handler de ação permitira exibir e ocultar componentes da página sem redirecionar o usuário a outra página. Especificar um url de destino sobrescreveria o handler de ação do componente e redirecionaria o usuário à página especificada. Nesse caso é importante não configurar a propriedade url porque queremos ocultar alguns elementos da página e exibir outros. Como utilizamos o languagesLink para recarregar a página atual, simplesmente retornamos null a partir do handler de ação, o que faz Options.jsp recarregar. Adicionando um handler de ação a um Hyperlink e redirecionando a outra página Se precisar adicionar um handler de ação a um hiperlink que também deve direcionar o usuário a outra página, você precisará adicionar uma regra ao arquivo Page Navigation (Figura 29.20). [Nota: Isso não é necessário para o exemplo atual.] Para editar esse arquivo, clique com o botão direito do mouse em qualquer lugar no designer visual e escolha Page Navigation. Clique no ícone de sinal de adição ( ) para Options.jsp no designer de navegação para exibir seus componentes que poderiam fazer a página solicitar outra página. Localize o link cuja regra de navegação você gostaria de configurar (por exemplo, recommendationsLink) e arraste-o para a página de destino (por exemplo, Recommendations.jsp). Agora o link pode direcionar o usuário a uma nova página (Recommendations.jsp) e você também pode inserir o código no handler de ação do componente Hyperlink que será executado quando o usuário clicar no link. Editar o arquivo Page Navigation também é útil se você quiser que os elementos de ação que não podem especificar uma propriedade url, como botões, direcionem os usuários para outra página.

Figura 29.20  |  Editando o arquivo Page Navigation.

Arquivo de bean de página para Options.jsp A Figura 29.21 contém o código que grava um cookie na máquina cliente quando o usuário seleciona uma linguagem de programação. O arquivo também determina quais componentes aparecem na página, exibindo os componentes para escolher uma linguagem ou os hiperlinks para navegar pelo aplicativo, dependendo das ações do usuário.

1 2 3 4 5 6

// Figura 29.21: Options.java // O bean de página que armazena a seleção de idioma do usuário // como um cookie no cliente. package cookies; import com.sun.rave.web.ui.appbase.AbstractPageBean;

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201

29.7  Monitoramento de sessão import import import import import import import import import

com.sun.webui.jsf.component.Button; com.sun.webui.jsf.component.Hyperlink; com.sun.webui.jsf.component.RadioButtonGroup; com.sun.webui.jsf.component.StaticText; com.sun.webui.jsf.model.SingleSelectOptionsList; java.util.HashMap; javax.faces.FacesException; javax.servlet.http.Cookie; javax.servlet.http.HttpServletResponse;

public class Options extends AbstractPageBean { // Definição de componente gerenciado private void _init() throws Exception { languageRadioButtonGroupDefaultOptions.setOptions( new com.sun.webui.jsf.model.Option[] { new com.sun.webui.jsf.model.Option( "Java", "Java" ), new com.sun.webui.jsf.model.Option( "C++", "C++" ), new com.sun.webui.jsf.model.Option( "Visual-Basic-2008", "Visual Basic 2008" ), new com.sun.webui.jsf.model.Option( "Visual-C#-2008", "Visual C# 2008" ), new com.sun.webui.jsf.model.Option( "Internet-&-Web", "Internet & Web" ) } // fim do inicializador de array ); // fim da chamada para setOptions } // fim do método _init private SingleSelectOptionsList languageRadioButtonGroupDefaultOptions = new SingleSelectOptionsList(); // Para economizar espaço, omitimos o código nas linhas 40–121. O código-fonte // completo é fornecido com os exemplos deste capítulo. private HashMap books = new HashMap(); // Constrói uma nova instância do bean de página e inicializa as propriedades // que mapeiam os idiomas para números ISBN dos livros recomendados. public Options() { // inicializa o objeto HashMap dos valores a ser armazenados como cookies. books.put( "Java", "0132222205" ); books.put( "C++", "0136152503" ); books.put( "Visual-Basic-2008", "013605305X" ); books.put( "Visual-C#-2008", "013605322X" ); books.put( "Internet-&-Web", "0131752421" ); } // fim do construtor Options // Para economizar espaço, omitimos o código nas linhas 136–185. O código-fonte // completo é fornecido com os exemplos deste capítulo. // Handler de ação do botão Submit. Verifica se um idioma // foi selecionado e, nesse caso, cria um cookie para esse idioma e // configura responseText para indicar o idioma selecionado. public String submitButton_action() { String message = "Welcome to Cookies! You "; // se o usuário fez uma seleção if ( languageRadioButtonGroup.getSelected() != null ) { String language = languageRadioButtonGroup.getSelected().toString(); message += "selected " + language.replace( '-', ' ' ) + "."; // obtém o número de ISBN do livro para o idioma dado String ISBN = books.get( language );

975

976 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237

Capítulo 29  Aplicativos Web JavaServer™ Faces

// cria o cookie utilizando um par de nome/valor do idioma ISBN Cookie cookie = new Cookie( language, ISBN ); // adiciona o cookie ao cabeçalho de resposta para inseri-lo na máquina do usuário HttpServletResponse response = ( HttpServletResponse ) getExternalContext().getResponse(); response.addCookie( cookie ); } // fim do if else { message += "did not select a language."; } // fim de else responseText.setValue( message ); languageRadioButtonGroup.setRendered( false ); // oculta instructionText.setRendered( false ); // oculta submitButton.setRendered( false ); // oculta responseText.setRendered( true ); // exibe languagesLink.setRendered( true ); // exibe recommendationsLink.setRendered( true ); // exibe return null; // reloads the page } // fim do método submitButton_action // reexibe os componentes para selecionar um idioma public String languagesLink_action() { responseText.setRendered( false ); // oculta languagesLink.setRendered( false ); // oculta recommendationsLink.setRendered( false ); // oculta languageRadioButtonGroup.setRendered( true ); // exibe instructionText.setRendered( true ); // exibe submitButton.setRendered( true ); // exibe return null; // reloads the page } // fim do método languagesLink_action } // fim da classe Options

Figura 29.21  |  O bean de página que armazena a seleção de idioma do usuário em um cookie cliente.

Como mencionado anteriormente, o método _init trata a inicialização de componentes. Como essa página contém um objeto Ra­ que requer inicialização, o método _init (linhas 20–35) constrói um array de objetos Option a ser exibidos pelos botões. As linhas 129–133 no construtor inicializam um objeto HashMap (definido na linha 122) — uma estrutura de dados que armazena pares chave/valor. Nesse caso, as chaves e os valores são Strings. O aplicativo utiliza a chave para armazenar e recuperar o valor associado no objeto HashMap. Nesse exemplo, as chaves contêm os nomes da linguagem de programação, e os valores contêm os números de ISBN dos livros recomendados. A classe HashMap fornece o método put, que recebe como argumentos uma chave e um valor. Um valor que é adicionado via método put é inserido no HashMap em uma localização determinada pela chave. O valor de uma entrada HashMap específica pode ser determinado invocando o método get no objeto HashMap com a chave desse valor como um argumento. Observe que o NetBeans pode importar automaticamente quaisquer pacotes ausentes que seu arquivo Java precise. Por exemplo, depois de adicionar o objeto HashMap a Options.java, você pode clicar com o botão direito do mouse na janela do editor Java e selecionar Fix Imports para importar java.util.HashMap. Essa opção também pode remover declarações import não utilizadas. Clicar em Submit invoca o handler de evento submitButton_action (linhas 189–224), que exibe uma mensagem indicando a linguagem selecionada no elemento responseText e adiciona um novo cookie à resposta. Se uma linguagem foi selecionada (linha 194), o item selecionado é recuperado (linhas 196–197). A linha 198 adiciona a linguagem selecionada à string message. A linha 201 recupera o ISBN do idioma selecionado a partir do objeto HashMap books. A linha 204 cria então um novo objeto Cookie (no pacote javax.servlet.http), utilizando o idioma selecionado como o nome do cookie e um ISBN correspondente como o valor do cookie. Esse cookie é adicionado ao cabeçalho de resposta HTTP nas linhas 207–209. Um objeto da classe HttpServletResponse (do pacote javax.servlet.http) representa a resposta. Esse objeto pode ser acessado invocando o método herdado getExternalContext no bean de página, então invocando getResponse no objeto resultante. Se uma linguagem não foi selecionada, a linha 213 configura a mensagem de resultados para indicar que nenhuma seleção foi feita. [Nota: Nomes de cookies não podem conter espaços em branco. Por essa razão, hifenizamos os nomes de múltiplas palavras que representam os nomes de cookies (isto é, os nomes da linguagem de programação nesse exemplo) nas linhas 27, 29, 31 e 131–133. Utilizamos o método replace String para substituir os hífens por espaços quando exibimos o nome da linguagem.] dioButtonGroup



29.7  Monitoramento de sessão

977

As linhas 216–222 controlam a aparência da página depois que o usuário clica em Submit. A linha 216 configura o responseText para exibir a String message. Como o usuário acabou de submeter uma seleção de linguagem, os componentes utilizados para coletar a seleção permanecem ocultos (linhas 217–219), e responseText e os links utilizados para navegar pelo aplicativo são exibidos (linhas 220–222). O handler de ação retorna null na linha 223, que recarrega Options.jsp. As linhas 227–236 contêm o handler de evento do languagesLink. Quando o usuário clica nesse link, responseText e os dois links permanecem ocultos (linhas 229–231), e os componentes que permitem ao usuário selecionar uma linguagem são exibidos novamente (linhas 232–234). O método retorna null na linha 235, fazendo Options.jsp recarregar.

Exibindo recomendações sobre livros com base nos valores de cookie Depois de clicar em Submit, o usuário pode solicitar uma recomendação sobre livros. O hiperlink para recomendações sobre livros direciona o usuário a Recommendations.jsp (Figura 29.22) para exibir as recomendações com base nas seleções de idioma do usuário. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36















Figura 29.22  |  O arquivo JSP que exibe recomendações sobre livros com base em cookies.

978

Capítulo 29  Aplicativos Web JavaServer™ Faces

Recommendations.jsp contém um Label (linhas 19–22), uma Text Area (linhas 23–26) e um Hyperlink (linhas 27–30). O Label exibe o texto Recommendations na parte superior da página. Um componente Text Area pode exibir múltiplas linhas de texto. A Text Area nesse exemplo exibe as recomendações criadas pelo bean de página Recommendations.java (Figura 29.23), ou o texto "No Recom­ mendations. Please select a language." O Hyperlink permite ao usuário retornar a Options.jsp (especificado pelo link url) para selecionar linguagens adicionais.

Bean de página que cria recomendações de livros a partir de cookies Em Recommendations.java (Figura 29.23), o método prerender (linhas 65–96) recupera os cookies do cliente, utilizando o método getCookies do objeto de solicitação (linhas 68–70). Um objeto da classe HttpServletRequest (do pacote javax.servlet.http) representa a solicitação. Esse objeto pode ser obtido invocando o método getExternalContext no bean de página, e depois invocando getRequest no objeto resultante. A chamada a getCookies retorna um array dos cookies anteriormente gravados no cliente. Cookies só podem ser lidos por um aplicativo se eles foram criados por um servidor no domínio no qual o aplicativo está em execução — por razões de segurança, um servidor Web não pode acessar cookies criados por servidores em outros domínios. Por exemplo, um cookie criado por um servidor Web no domínio deitel.com não pode ser lido por um servidor Web em nenhum outro domínio. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

// Figura 29.23: Recommendations.java // Exibe recomendações sobre livros com base em cookies que // contêm as linguagens de programação selecionadas pelo usuário. package cookies; import import import import import

com.sun.rave.web.ui.appbase.AbstractPageBean; com.sun.webui.jsf.component.TextArea; javax.faces.FacesException; javax.servlet.http.Cookie; javax.servlet.http.HttpServletRequest;

public class Recommendations extends AbstractPageBean { // Para economizar espaço, omitimos o código nas linhas 14–62. O código-fonte // completo é fornecido com os exemplos deste capítulo. // processa e exibe as seleções do usuário @Override public void prerender() { // recupera os cookies HttpServletRequest request = ( HttpServletRequest ) getExternalContext().getRequest(); Cookie[] cookies = request.getCookies(); // se houver cookies, armazena os // livros e números de ISBN correspondentes em uma String String recommendations = ""; if ( cookies.length > 1 ) { for ( int i = 0; i < cookies.length - 1; i++ ) { String language = cookies[ i ].getName().replace( '-', ' ' ); // ignora o cookie criado quando o usuário retorna a // Options.jsp a partir de Recommendations.jsp if ( !language.equals( "/Recommendations.jsp" ) ) recommendations += language + " How to Program. ISBN#: " + cookies[ i ].getValue() + "\n"; } // for final } // fim do if else { recommendations =



29.7  Monitoramento de sessão

92 93 94 95 96 97 98 99 117

979

"No recommendations. Please select a language."; } // fim de else recommendationsTextArea.setText( recommendations ); } // fim do método prerender // Para economizar espaço, omitimos o código nas linhas 98–116. O código-fonte // completo é fornecido com os exemplos deste capítulo. } // fim da classe Recommendations

Figura 29.23  |  O bean de página que exibe recomendações sobre livros com base nos cookies que armazenam as linguagens selecionadas pelo usuário.

A linha 76 determina se existem pelo menos dois cookies. As linhas 78–87 adicionam as informações contidas no(s) cookie(s) à string recommendations, desde que o nome do cookie não seja "/Recommendations.jsp" — um cookie com esse nome é adicionado quando o usuário vai de Recommendations.jsp a Options.jsp. O loop recupera o nome e o valor de cada cookie, utilizando a variável de controle para determinar o valor atual no array de cookies. Se nenhuma linguagem foi selecionada, as linhas 91–92 atribuem a recommenda­ tions uma mensagem que instrui o usuário a selecionar uma linguagem. A linha 95 configura recommendationsTextArea para exibir a string recommendations resultante. Resumimos os métodos Cookie comumente utilizados na Figura 29.24. Método

Descrição

getDomain

Retorna uma String contendo o domínio do cookie (isto é, o domínio a partir do qual o cookie foi gravado). Isso determina quais servidores Web podem receber o cookie. Por padrão, os cookies são enviados para o servidor Web que originalmente enviou o cookie para o cliente. Alterar a propriedade Domain faz o cookie retornar a um servidor Web diferente daquele que originalmente o gravou.

getMaxAge

Retorna um int indicando por quantos segundos o cookie persistirá no navegador. Isso é –1 por padrão, significando que o cookie persistirá até que o navegador seja fechado.

getName

Retorna uma String contendo o nome do cookie.

getPath

Retorna uma String contendo o caminho para um diretório no servidor ao qual o cookie é aplicado. Cookies podem ser direcionados a diretórios específicos no servidor Web. Por padrão, um cookie é retornado apenas para aplicativos que operam no mesmo diretório que o aplicativo que enviou o cookie ou um subdiretório desse diretório. Alterar a propriedade Path faz o cookie retornar a um diretório além daquele em que foi originalmente gravado.

getSecure

Retorna um valor boolean indicando se o cookie deve ser transmitido por meio de um protocolo seguro. O valor true faz com que um protocolo seguro seja utilizado.

getValue

Retorna uma String contendo o valor do cookie.

Figura 29.24  |  Métodos javax.servlet.http.Cookie.

29.7.2  Rastreamento de sessão com beans de sessão Você também pode realizar o rastreamento de sessão com a subclasse de AbstractSessionBean que é fornecida em cada aplicativo Web. Por padrão, a subclasse é nomeada SessionBean1. Quando um usuário solicita uma página no aplicativo Web, um objeto Ses­ sionBean1 é criado no servidor. As propriedades desse objeto podem ser acessadas por toda a sessão de navegador invocando o método getSessionBean1 de bean de página. Para demonstrar as técnicas de rastreamento de sessão utilizando o SessionBean1, modificamos os arquivos de bean de página nas figuras 29.21 e 29.23 para que elas utilizem o objeto SessionBean1 a fim de armazenar as seleções de linguagem do usuário. Começamos com o arquivo Options.jsp atualizado (Figura 29.25). A Figura 29.27 apresenta o arquivo Session­ Bean1.java, e a Figura 29.28 apresenta o arquivo de bean de página modificado para Options.jsp. O arquivo Options.jsp na Figura  29.25 é semelhante àquele da Figura  29.19. As linhas 38–47 definem dois elementos webuijsf:staticText adicionais. O primeiro exibe o texto "Number of selections so far:”. O segundo atributo text é vinculado à propriedade numberOfSelections no objeto SessionBean1 (linha 47). Discutimos como vincular o atributo text a uma propriedade de bean de sessão mais adiante.

980

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

Capítulo 29  Aplicativos Web JavaServer™ Faces





















29.7  Monitoramento de sessão

981

(a) O usuário seleciona a linguagem de programação e clica em Submit para solicitar novamente Options.jsp.

(b) Options.jsp exibe uma mensagem de boas-vindas e fornece links que permitem ao usuário selecionar outra linguagem ou visualizar recomendações sobre o livro. O usuário escolhe selecionar outra linguagem, que solicita novamente Options.jsp.

(c) O usuário seleciona uma linguagem de programação e clica em Submit para solicitar novamente Options.jsp.

(d) Options.jsp exibe uma mensagem de boas-vindas e fornece links que permitem ao usuário selecionar outra linguagem ou visualizar recomendações sobre o livro. O usuário escolhe visualizar recomendações de livro.

Figura 29.25  |  O arquivo JSP que permite ao usuário selecionar uma linguagem de programação.

Adicionando propriedades ao SessionBean Esse exemplo utiliza o rastreamento de sessão para armazenar as linguagens selecionadas pelo usuário e o número de seleções que o usuário faz. Para armazenar informações de sessão, adicionamos propriedades à classe SessionBean1. Comece clicando duas vezes no nó SessionBean1 na janela Navigator para abrir SessionBean1.java no editor. Declare uma variável de instância numberOfSelections do tipo int para armazenar o número de seleções que o usuário faz. Em seguida, clique com o botão direito do mouse na variável no editor e selecione Refactor> Encapsulate Fields…. Na caixa de diálogo que aparece, mantenha as opções padrão e clique no botão Refactor. Isso cria métodos get e set para a variável de instância numberOfSelections na parte inferior do arquivo de código-fonte. Em conjunto, a variável de instância e seus métodos get e set representam a propriedade numberOfSelections do bean. Como você verá, manipulamos a propriedade numberOfSelections no arquivo de bean de página para rastrear o número de linguagens que o usuário seleciona. Você pode ver na Figura 29.25 (b) e (d) que exibimos esse valor na página toda vez que o usuário faz outra seleção. Para exibir o valor no elemento selectionsValueText, mude para o modo Design, clique com o botão direito do mouse no ele-

982

Capítulo 29  Aplicativos Web JavaServer™ Faces

mento na janela Navigator ou no editor visual e selecione Bind to Data…. Na caixa de diálogo Bind to Data (Figura 29.26), clique na guia Bind to an Object, selecione a propriedade numberOfSelections no nó SessionBean1 e clique em OK. O elemento selectionsValueText agora está vinculado ao valor da propriedade numberOfSelections de SessionBean1. Quando o valor da propriedade muda, o texto na página muda correspondentemente — não é necessário configurar programaticamente o texto no arquivo do bean de página. Agora que adicionamos uma propriedade à classe SessionBean1 para armazenar o número de seleções, vamos adicionar outra propriedade para armazenar as seleções em si. Queremos armazenar seleções como pares de chave/valor da linguagem selecionada e o número de ISBN de um livro relacionado, assim como as seleções foram armazenadas utilizando cookies. Para fazer isso, adicione uma variável de instância HashMap nomeada selections à classe SessionBean1, refatore então o código para criar métodos get e set como você fez para numberOfSelections. As duas propriedades que adicionamos são mostradas no arquivo SessionBean1.java (Figura 29.27).

Figura 29.26  |  Caixa de diálogo Bind to Data. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 70 71 72 73 74 75 76 77 78 79 80 81 82

// Figura 29.27: SessionBean1.java // Arquivo SessionBean para armazenar seleções de idioma. package sessions; import com.sun.rave.web.ui.appbase.AbstractSessionBean; import java.util.HashMap; import javax.faces.FacesException; public class SessionBean1 extends AbstractSessionBean { private int numberOfSelections; // armazena o número de linguagens selecionadas // armazena HashMap que contém as seleções do usuário private HashMap selections = new HashMap(); // Para economizar espaço, omitimos o código nas linhas 17–69. O código-fonte // completo é fornecido com os exemplos deste capítulo. public int getNumberOfSelections() { return numberOfSelections; } // fim do método getNumberOfSelections public void setNumberOfSelections( int numberOfSelections ) { this.numberOfSelections = numberOfSelections; } // fim do método setNumberOfSelections public HashMap getSelectedLanguages() { return selections;

83 84 85 86 87 88 89

29.7  Monitoramento de sessão

983

} // fim do método getSelectedLanguages public void setSelectedLanguages( HashMap selections ) { this.selections = selections; } // fim do método setSelectedLanguages } // fim da classe SessionBean1

Figura 29.27  |  Arquivo SessionBean para armazenar seleções de idioma.

A linha 11 declara a variável de instância numberOfSelections, e as linhas 70–73 e 75–78 definem os métodos get e set, respectivamente, para completar a propriedade numberOfSelections. As linhas 14–15 definem o objeto HashMap selections que armazenará as seleções de usuário. As linhas 80–83 e 85–88 são métodos get e set para essa propriedade. Lembre-se de que o IDE gerou os métodos get e set quando clicamos com o botão direito do mouse em cada variável de instância, selecionamos Refactor > Encapsulate Fields… e clicamos no botão Refactor.

Manipulando propriedades de bean de sessão em um arquivo de bean de página O arquivo de bean de página para a página Options.jsp é exibido na Figura 29.28. Como a maior parte desse exemplo é idêntica ao anterior, só discutiremos os novos recursos. Como não utilizaremos cookies nesse exemplo, não será necessário hifenizar os nomes da linguagem de programação que foram anteriormente utilizados como nomes de cookies (linhas 24, 26, 28 e 151–153). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 142 143 144 145 146

// Figura 29.28: Options.java // O bean de página que armazena a seleção de idioma do usuário em um SessionBean. package sessions; import import import import import import import import

com.sun.rave.web.ui.appbase.AbstractPageBean; com.sun.webui.jsf.component.Button; com.sun.webui.jsf.component.Hyperlink; com.sun.webui.jsf.component.RadioButtonGroup; com.sun.webui.jsf.component.StaticText; com.sun.webui.jsf.model.SingleSelectOptionsList; java.util.HashMap; javax.faces.FacesException;

public class Options extends AbstractPageBean { // Managed Component Definition" private void _init() throws Exception { languageRadioButtonGroupDefaultOptions.setOptions( new com.sun.webui.jsf.model.Option[] { new com.sun.webui.jsf.model.Option( "Java", "Java" ), new com.sun.webui.jsf.model.Option( "C++", "C++" ), new com.sun.webui.jsf.model.Option( "Visual Basic 2008", "Visual Basic 2008" ), new com.sun.webui.jsf.model.Option( "Visual C# 2008", "Visual C# 2008" ), new com.sun.webui.jsf.model.Option( "Internet & Web", "Internet & Web" ) } // fim do inicializador de array ); // fim da chamada para setOptions } // fim do método _init private SingleSelectOptionsList languageRadioButtonGroupDefaultOptions = new SingleSelectOptionsList(); // Para economizar espaço, omitimos o código nas linhas 36–141. O código-fonte // completo é fornecido com os exemplos deste capítulo. private HashMap books = new HashMap(); // Constrói uma nova instância do bean de página e inicializa as propriedades // que mapeiam os idiomas para números ISBN dos livros recomendados. public Options()

984 147 148 149 150 151 152 153 154 155 156 157 158 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262

Capítulo 29  Aplicativos Web JavaServer™ Faces { // inicializa o objeto HashMap dos valores a serem armazenados na sessão. books.put( "Java", "0132222205" ); books.put( "C++", "0136152503" ); books.put( "Visual Basic 2008", "013605305X" ); books.put( "Visual C# 2008", "013605322X" ); books.put( "Internet & Web", "0131752421" ); } // fim do construtor Options // Para economizar espaço, omitimos o código nas linhas 156–205. O código-fonte // completo é fornecido com os exemplos deste capítulo. // Handler de ação do botão Submit. Verifica se um idioma // foi selecionado e, nesse caso, cria uma entrada para essa linguagem e // configura responseText para indicar o idioma selecionado. public String submitButton_action() { String message = "Welcome to Sessions! You "; // se o usuário fez uma seleção if ( languageRadioButtonGroup.getSelected() != null ) { String language = languageRadioButtonGroup.getSelected().toString(); message += "selected " + language + "."; // obtém o número de ISBN do livro para o idioma dado String ISBN = books.get( language ); // adiciona a seleção ao objeto Properties de SessionBean HashMap selections = getSessionBean1().getSelectedLanguages(); String result = selections.put( language, ISBN ); // incrementa numberOfSelections em SessionBean // se o usuário não fez essa seleção antes if ( result == null ) { int selected = getSessionBean1().getNumberOfSelections(); getSessionBean1().setNumberOfSelections( ++selected ); } // fim do if } // fim do if else { message += "did not select a language."; } // fim de else responseText.setValue( message ); languageRadioButtonGroup.setRendered( false ); // oculta instructionText.setRendered( false ); // oculta submitButton.setRendered( false ); // oculta responseText.setRendered( true ); // exibe selectionsText.setRendered( true ); // exibe selectionsValueText.setRendered( true ); // exibe languagesLink.setRendered( true ); // exibe recommendationsLink.setRendered( true ); // exibe return null; // reloads the page } // fim do método submitButton_action // reexibe os componentes para selecionar um idioma public String languagesLink_action() { responseText.setRendered( false ); // oculta selectionsText.setRendered( false ); // oculta selectionsValueText.setRendered( false ); // oculta languagesLink.setRendered( false ); // oculta recommendationsLink.setRendered( false ); // oculta languageRadioButtonGroup.setRendered( true ); // exibe instructionText.setRendered( true ); // exibe

263 264 265 266

29.7  Monitoramento de sessão

985

submitButton.setRendered( true ); // exibe return null; // reloads the page } // fim do método languagesLink_action } // fim da classe Options

Figura 29.28  |  O bean de página que armazena seleções de idioma em uma propriedade SessionBean.

O handler de ação submitButton (linhas 209–251) armazena as seleções do usuário em SessionBean1 e incrementa o número de seleções feitas, se necessário. As linhas 224–225 recuperam a partir de SessionBean1 o objeto HashMap que contém as seleções do usuário. A linha 226 adiciona a seleção atual ao HashMap. O método put retorna o valor anteriormente associado com a nova chave, ou null se essa chave ainda não foi armazenada no objeto HashMap. Se a adição da nova propriedade retornar null, o usuário fez então uma nova seleção. Nesse caso, as linhas 232–233 incrementam a propriedade numberOfSelections de SessionBean1. As linhas 242–249 e o handler de ação languaguesLink (linhas 254–265) controlam os componentes que são exibidos, assim como nos exemplos de cookies.

Observação de engenharia de software 29.2 Uma vantagem do uso de propriedades de bean de sessão (em vez de cookies) é que elas podem armazenar qualquer tipo de objeto (não apenas Strings) como valores de atributo. Isso fornece maior flexibilidade e poder ao manter informações de estado do cliente.

Exibindo recomendações com base em valores de sessão Como no exemplo de cookies, esse aplicativo fornece um link para Recommendations.jsp, que exibe uma lista de recomendações sobre livros com base nas seleções de idioma do usuário. Como esse JSP é idêntico à versão na Figura 29.22, mostramos apenas a saída de exemplo dessa página na Figura 29.29. Bean de página que cria recomendações sobre livro a partir de uma propriedade SessionBean A Figura 29.30 apresenta o bean de página para Recommendations.jsp. Mais uma vez, boa parte dele é semelhante ao bean de página utilizado no exemplo de cookies. Discutimos apenas os novos recursos. As linhas 67–68 recuperam o objeto HashMap que contém as seleções do usuário a partir do bean de sessão, e a linha 69 recupera o número de seleções feitas. Se alguma seleção foi feita, as linhas 78–82 acrescentam recomendações sobre livros à string recommendations. A linha 78 utiliza o método keySet de HashMap para obter um Set das chaves no HashMap, e a linha 81 utiliza cada chave para obter o ISBN do livro correspondente.

Figura 29.29  |  O arquivo JSP que exibe recomendações sobre livros com base nas seleções de idioma armazenadas no escopo de sessão. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura 29.30: Recommendations.java // Exibe recomendações sobre livros com base em uma propriedade HashMap no // bean de sessão que contém as linguagens de programação selecionadas pelo usuário. package sessions; import import import import

com.sun.rave.web.ui.appbase.AbstractPageBean; com.sun.webui.jsf.component.TextArea; java.util.HashMap; javax.faces.FacesException;

public class Recommendations extends AbstractPageBean {

986 13 14 15 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 112

Capítulo 29

Aplicativos Web JavaServer™ Faces

// Para economizar espaço, omitimos o código nas linhas 13–61. O código­fonte // completo é fornecido com os exemplos deste capítulo. // processa e exibe as seleções do usuário @Override public void prerender() { // recupera as seleções do usuário e o número de seleções feitas HashMap languages = getSessionBean1().getSelectedLanguages(); int numberSelected = getSessionBean1().getNumberOfSelections(); // se houver seleções, armazena os correspondentes // livros e números de ISBN correspondentes em uma String String recommendations = ""; // se pelo menos uma seleção foi feita if ( numberSelected > 0 ) { for ( String language : languages.keySet() ) { recommendations += language + " How to Program. ISBN#: " + languages.get( language ) + "\n"; } // for final } // fim do if else { recommendations = "No recommendations. Please select a language."; } // fim de else recommendationsTextArea.setText( recommendations ); } // fim do método prerender // Para economizar espaço, omitimos o código nas linhas 92–111. O código­fonte // completo é fornecido com os exemplos deste capítulo. } // fim da classe Recommendations

Figura 29.30 | Exibe recomendações sobre livros com base em uma propriedade SessionBean.

29.8 Conclusão Neste capítulo, introduzimos o desenvolvimento de aplicativos Web utilizando JavaServer Pages e JavaServer Faces no NetBeans. Começamos elencando as transações HTTP simples que acontecem quando você solicita e recebe uma página Web por meio de um navegador Web. Discutimos então as três camadas (isto é, a camada cliente ou superior, a camada da lógica do negócio ou intermediária e a camada de informações ou inferior) que cobrem a maioria dos aplicativos Web. Você aprendeu o papel dos arquivos JSP e dos arquivos de bean de página, e a relação entre eles. Aprendeu a utilizar o NetBeans para compilar visualmente aplicativos Web utilizando as capacidades de arrastar e soltar do NetBeans, você então compilou e executou-os. Demonstramos vários componentes JSF comuns utilizados para exibir texto e imagens em páginas Web. Também discutimos componentes de validação e métodos validadores personalizados, que permitem assegurar que a entrada do usuário satisfaz os requisitos do seu aplicativo. Enumeramos os benefícios de manter informações de usuário em múltiplas páginas de um site Web. Demonstramos então como você pode incluir essas funcionalidades em um aplicativo Web utilizando cookies ou propriedades da classe de bean de sessão que é incluída em cada aplicativo Web. No Capítulo 30, continuamos nossa discussão do desenvolvimento de aplicativos Web em Java com conceitos mais avançados. Você aprenderá a acessar um banco de dados a partir de um aplicativo Web JSF, utilizar componentes JSF compatíveis como AJAX e utilizar formulários virtuais. O AJAX ajuda aplicativos baseados na Web a fornecer interatividade e responsividade que os usuários em geral esperam de aplicativos desktop.



Resumo

987

Resumo Seção 29.1  Introdução • Aplicativos baseados na Web criam conteúdo, como a Extensible HyperText Markup Language (XHTML), criação de script no lado do cliente, imagens e dados binários, para clientes baseados em navegadores Web.

Seção 29.2  Transações HTTP simples • Na sua forma mais simples, uma página Web nada mais é do que um documento XHTML que descreve para um navegador Web como exibir e formatar as informações do documento. • Documentos XHTML normalmente contêm hiperlinks que levam a diferentes páginas ou a outras partes da mesma página. Quando o usuário clica em um hiperlink, a página da Web solicitada é carregada no navegador. • O protocolo HTTP permite a clientes e servidores interagir e trocar informações. • O HTTP utiliza o URIs (Uniform Resource Identifiers) para identificar dados na internet. URIs que especificam as localizações de documentos são chamados URLs (Uniform Resource Locators). • Um URL contém informações que direcionam um navegador ao recurso que o usuário quer acessar. Computadores que executam softwares de servidor Web disponibilizam esses recursos. • O computador que hospeda e mantêm recursos normalmente é tratado como host. • Nomes de host são convertidos em endereços IP pelos servidores de Domain-Name System (DNS). • Quando dado um URL, um navegador Web realiza uma transação HTTP para recuperar e exibir a página Web nesse endereço. • Cabeçalhos HTTP fornecem informações adicionais sobre os dados que serão enviados. • O Multipurpose Internet Mail Extensions (MIME) é um padrão internet que especifica formatos de dados para que os programas possam interpretar os dados corretamente. • Os dois tipos mais comuns de solicitação de HTTP são GET e POST. Uma solicitação GET em geral pede um recurso específico em um servidor. Uma solicitação POST geralmente posta (ou envia) dados para o servidor. • Tanto solicitações GET como solicitações POST podem ser utilizadas para enviar dados de formulário a um servidor Web, mas cada tipo de solicitação envia as informações de uma maneira diferente. Uma solicitação GET envia informações ao servidor no URL. Uma solicitação POST envia dados de formulário como parte da mensagem HTTP. • Os navegadores costumam armazenar em cache páginas Web para recarregamento rápido. Se não houver alterações entre a última versão armazenada no cache e a versão atual na Web, isso acelerará sua navegação. • Uma resposta HTTP pode indicar por quanto tempo o conteúdo permanece atualizado. Se esse período de tempo não for atingido, o navegador poderá impedir outra solicitação ao servidor. • Os navegadores, em geral, não armazenam em cache da resposta do servidor para uma solicitação POST porque a próxima solicitação POST pode não retornar o mesmo resultado.

Seção 29.3  Arquitetura de aplicativo multithread • Aplicativos baseados na Web são aplicativos multicamadas (ou aplicativos de n camadas) que dividem as funcionalidades em camadas separadas (isto é, agrupamentos lógicos das funcionalidades). Embora as camadas possam estar localizadas no mesmo computador, as camadas dos aplicativos baseados na Web costumam residir em computadores separados. • A camada de informações mantém os dados que pertencem ao aplicativo. • A camada intermediária implementa a lógica do negócio, a lógica do controlador e a lógica da apresentação a fim de controlar interações entre os clientes do aplicativo e os dados do aplicativo. A lógica do negócio na camada intermediária impõe regras do negócio e assegura que os dados são confiáveis antes de o aplicativo servidor atualizar o banco de dados ou apresentar os dados aos usuários. As regras do negócio determinam como os clientes podem e como não podem acessar dados de aplicativo, e como os aplicativos processam dados. • A camada cliente é a interface com o usuário do aplicativo, que coleta a entrada e exibe a saída. Os usuários interagem diretamente com o aplicativo por meio da interface com o usuário. Em resposta às ações do usuário (por exemplo, clicar em um hiperlink), a camada de cliente interage com a camada intermediária para fazer solicitações e recuperar dados da camada de informações.

Seção 29.4  Tecnologias Web Java • Aplicativos Java multicamadas são implementados utilizando os recursos do Java Enterprise Edition.

Section 29.4.1  Servlets • Servlets utilizam o modelo de solicitação/resposta HTTP da comunicação entre cliente e servidor. • Servlets estendem a funcionalidade de um servidor permitindo que ele gere conteúdo dinâmico. • Um componente de servidor Web chamado contêiner de servlets executa e interage com servlets. O contêiner de servlets recebe solicitações HTTP de um cliente e direciona cada solicitação ao servlet apropriado. O servlet processa a solicitação e retorna uma resposta apropriada ao cliente.

988

Capítulo 29  Aplicativos Web JavaServer™ Faces

• Todos os servlets devem implementar a interface Servlet do pacote javax.servlet, que assegura que cada servlet pode executar no framework fornecido pelo contêiner de servlets. • A interface Servlet declara métodos de ciclo de vida dos servlets. O contêiner invoca o método init do servlet somente uma vez durante o ciclo de vida de um servlet para inicializar o servlet. Depois que init completar a execução, o servlet estará pronto para responder à sua primeira solicitação. Cada solicitação é tratada por um método service do servlet, que recebe a solicitação, processa e envia uma resposta ao cliente. • Quando o contêiner de servlets termina o servlet, o método destroy do servlet é chamado para liberar quaisquer recursos mantidos pelo servlet.

Section 29.4.2  JavaServer Pages • A tecnologia JavaServer Pages ( JSPs) é uma extensão da tecnologia de servlet. Cada JSP é convertido pelo contêiner JSP em um servlet. • JSPs ajudam a separar a apresentação do conteúdo. As JSPs permitem criar conteúdo dinâmico reutilizando componentes predefinidos e interagindo com componentes que utilizam script do lado do servidor. • Um JavaBean é um componente reutilizável que segue certas convenções para o design de classe e que pode ser manipulado visualmente em uma ferramenta de construtor, como o NetBeans ou Eclipse. As classes JavaBean que permitem leitura e gravação das variáveis de instância devem fornecer métodos get e set apropriados — juntas, estas definem as propriedades das classes JavaBean. • Bibliotecas de tags personalizados permitem ocultar o código para acesso de banco de dados e outras operações complexas nos tags personalizados. Para utilizar essas capacidades, você simplesmente adiciona os tags personalizados à página. Essa simplicidade permite a designers de página Web que não estão familiarizados com o Java aprimorar páginas Web com capacidades poderosas de processamento dinâmico e conteúdo dinâmico. • Há quatro componentes-chave para JSPs — diretivas, ações, elementos de script e bibliotecas de tags. Diretivas são mensagens ao contêiner JSP que permitem especificar configurações de página, incluir conteúdo de outros recursos e especificar bibliotecas de tags personalizados para uso em JSPs. As ações encapsulam funcionalidades em tags predefinidos que programadores podem incorporar em JSPs. Elementos de script permitem inserir código Java que interage com componentes em um JSP para executar o processamento de solicitações. As bibliotecas de tags fazem parte do mecanismo de extensão de tag que permite aos programadores criar tags personalizados. A JavaServer Pages Standard Tag Library ( JSTL) fornece funcionalidade para muitas tarefas comuns de aplicativo Web. • JSPs podem conter conteúdo estático, como marcação XML ou XHTML. Tal marcação é conhecida como dados de template fixa ou texto de template fixa. • Quando um servidor com JSP ativado recebe a primeira solicitação para uma JSP, o contêiner de JSPs traduz a JSP em um servlet Java que trata a solicitação atual e as futuras solicitações para a JSP.

Section 29.4.3  JavaServer Faces • O JavaServer Faces ( JSF) é um framework de aplicativo Web que simplifica o design da interface com o usuário de um aplicativo e amplia a separação entre a apresentação de um aplicativo Web e a lógica do negócio. • Componentes JSF simplificam o design de páginas Web. O JSF fornece duas bibliotecas de tags personalizados JSP para adicionar esses componentes a uma página JSP. O JSF também inclui APIs para tratar eventos de componentes, navegação entre páginas de aplicativo Web e mais. • Você cria a aparência e o funcionamento de uma página com o JSF adicionando elementos a um documento JSP e manipulando seus atributos. Você define o comportamento da página separadamente nos arquivos Java relacionados.

Section 29.4.4  Tecnologias Web no NetBeans • Aplicativos Web NetBeans que utilizam o framework JavaServer Faces consistem em uma ou mais páginas Web JSP. Esses JSPs têm a extensão de nome de arquivo .jsp e contêm os elementos GUI das páginas Web. JSPs podem ser personalizados no NetBeans adicionando componentes JSF. • Cada arquivo JSP criado no NetBeans representa uma página Web e tem uma classe JavaBean correspondente chamada bean de página. Uma classe JavaBean deve ter um construtor padrão (ou sem argumento), e métodos get e set para todas as propriedades do bean (isto é, variáveis de instância). O bean de página define as propriedades para todos os elementos da página com os quais você quer interagir programaticamente. O bean de página também contém handlers de evento e métodos de ciclo de vida de página para gerenciar tarefas, como inicialização e renderização de página, e outros códigos de suporte ao aplicativo Web. • Cada aplicativo Web NetBeans JSF define três mais JavaBeans. O objeto RequestBean é mantido no escopo de solicitação — ele só existe pelo tempo de duração de uma solicitação HTTP. Um objeto SessionBean tem escopo de sessão — ele existe por toda a sessão de navegação de um usuário ou até que a sessão expire. Há um único objeto SessionBean para cada usuário. Por fim, o objeto ApplicationBean tem escopo de aplicativo — ele é compartilhado por todas as instâncias de um aplicativo e existe desde que o aplicativo permaneça implantado em um servidor Web. Esse objeto é utilizado para armazenagem ou processamento de dados por todo o aplicativo; somente uma instância existe para o aplicativo, independentemente do número de sessões abertas.

Section 29.5.1  Examinando um documento JSP • Todos os JSPs devem ter um elemento jsp:root, que tem um atributo version para indicar a versão JSP que é utilizada e um ou mais atributos xmlns. Cada atributo xmlns especifica um prefixo e um URL para uma biblioteca de tags, permitindo que a página utilize elementos dessa biblioteca. • O atributo contentType do elemento jsp:directive.page especifica o tipo MIME (text/html) e o conjunto de caracteres (UTF-8) que a página utiliza. Seu atributo pageEncoding especifica a codificação de caractere utilizada pelo código-fonte da página. Esses atributos ajudam o navegador a renderizar o conteúdo. • Todas as páginas que contêm componentes JSF são representadas em uma árvore de componentes com o elemento JSF raiz f:view em que outros tags de componentes JSF são aninhados.



Resumo

989

• O elemento webuijsf:head tem um atributo title que especifica o título da página. • O elemento webuijsf:body contém um elemento webuijsf:form no qual os componentes JSF da interface com o usuário são definidos. • Um webuijsf:staticText exibe o texto na página. • A JSF Expression Language permite vincular um atributo de um elemento JSP ao valor de uma propriedade em qualquer JavaBean do aplicativo Web. • Elementos JSP são mapeados pelo servidor de aplicativo para uma combinação de elementos XHTML e código JavaScript que permite ao navegador exibir a página. • O JavaScript é uma linguagem de criação de scripts que é interpretada em todos os atuais navegadores Web populares. Eles podem ser utilizados para manipular elementos de página Web em um navegador e fornecer interatividade com o usuário. • O mesmo componente Web pode mapear para diferentes elementos XHTML e código JavaScript, dependendo do navegador cliente e das configurações de propriedade do componente.

Section 29.5.2  Examinando um arquivo Page Bean • Classes de bean de página estendem a classe AbstractPageBean (do pacote com.sun.rave.web.ui.appbase), que fornece métodos de ciclo de vida de página. • O pacote com.sun.webui.jsf.component fornece boa parte dos componentes GUI JSF básicos.

Section 29.5.3  Ciclo de vida de processamento de eventos • Vários métodos no bean de página associam ao ciclo de vida do processamento de eventos JSF quatro etapas principais — inicialização, pré-processamento, pré-renderização e destruição. Cada um equivale a um método na classe de bean de página — init, preprocess, prerender e destroy, respectivamente. • O contêiner JSP chama init na primeira vez que a página é solicitada e nos postbacks. Um postback ocorre quando os dados de formulário são submetidos para processamento no servidor. • O método preprocess é chamado depois de init, mas somente se a página estiver processando um postback. O método prerender configura propriedades de componente imediatamente antes de uma página ser exibida pelo navegador. • O método destroy é chamado depois que a página foi exibida, mas somente se o método init for chamado. Esse método trata tarefas, como liberar os recursos utilizados para renderizar a página.

Section 29.5.4  Criando um aplicativo Web no NetBeans • Para criar um projeto de aplicativo Web, selecione File> New Project… para exibir a caixa de diálogo New Project. Selecione Java Web no painel Categories, Web Application no painel Projects e clique em Next >. Nomeie o projeto e, no campo Project Location, especifique onde você quer armazenar o projeto. Mantenha as outras configurações padrão e clique em Next>. Mantenha as opções padrão para Server e Settings e clique em Next >. Selecione Visual Web JavaServer Faces como o framework a utilizar nesse aplicativo Web, clique então em Finish para criar o projeto do aplicativo Web. • A Palette exibida no IDE mostra os componentes Woodstock. Woodstock é um conjunto dos componentes da interface com o usuário para aplicativos JavaServer Faces. • A janela Projects exibe a hierarquia de todos os arquivos incluídos no projeto. Os arquivos JSP para cada página são listados abaixo do nó Web Pages. O código Java está no nó Source Packages. • A refatoração é o processo de modificar o código-fonte para aprimorar sua legibilidade e capacidade de reutilização sem alterar seu comportamento. O NetBeans tem ferramentas de refatoração internas. • Para adicionar componentes à página no modo Design, arraste e solte-os da Palette até a página. Cada componente é um objeto que tem propriedades, métodos e eventos. Você pode manipular estes visualmente utilizando a janela Properties ou programaticamente no arquivo de bean de página. • O NetBeans é um editor WYSIWYG (What You See Is What You Get) — quando você faz uma modificação em uma página no modo Design, o IDE cria a marcação (visível no modo JSP) necessária para alcançar os efeitos visuais desejados vistos no modo Design e também poderia adicionar código Java ao arquivo .java do bean de página. • Na janela Properties, clique no botão de reticências ( ) ao lado da propriedade style de um componente para abrir uma caixa de diálogo a fim de editar o estilo do componente. • Para interagir com um componente JSF programaticamente, você primeiro deve clicar com o botão direito do mouse no controle no modo Design e escolher Add Binding Attribute. Isso cria uma variável no bean de página que você pode utilizar para interagir com o componente e métodos set e get para acessar o componente. • Execute seu projeto selecionando Run> Build Project, então Run> Run Project. Você também pode executar um projeto que já foi compilado pressionando o ícone Run Project ( ) na barra de ferramentas na parte superior do IDE ou pressionando F6. • Pressione Ctrl + F5 para compilar o aplicativo, então o execute no modo de depuração. Se pressionar F6, o programa executará sem a depuração ativada. • Para projetos com múltiplas páginas, você pode alterar a página inicial dando um clique com o botão direito do mouse na página desejada na janela Projects e selecionando Set As Start Page. A página inicial é indicada por um ícone com um símbolo de botão play verde ( ) ao lado do nome da página na janela Projects.

990

Capítulo 29  Aplicativos Web JavaServer™ Faces

Section 29.6.1  Componentes gráficos e de texto • O NetBeans utiliza o posicionamento absoluto por padrão, portanto, os componentes são exibidos onde quer que eles sejam soltos no editor visual. • Um Grid Panel permite ao designer especificar o número de colunas que a grade deve conter. Os componentes são posicionados em colunas uniformemente espaçadas na ordem na qual eles são adicionados. • Para adicionar uma imagem, solte um componente Image na página e clique no botão de reticências ( ) ao lado da propriedade url na janela Properties, selecione então a imagem a exibir. • Você pode rotular um Text Field configurando a propriedade label, que posiciona o texto à esquerda do Text Field. Alternativamente, você pode rotular um Text Field arrastando e soltando um componente Label até a página, o que permite personalizar a posição e estilo do Label. • A ordem na qual os componentes são arrastados até a página é a ordem na qual seus elementos JSP são adicionados ao documento JSP. • Por padrão, quando um usuário pressiona a tecla Tab para navegar entre campos de entrada, o foco alterna entre um componente e outro na ordem em que os elementos JSP ocorrem no documento JSP. Para especificar a ordem de navegação, arraste os componentes para a página na ordem apropriada, ou configure cada propriedade tabIndex do campo de entrada na janela Properties. • Uma Drop Down List exibe uma lista de opções em que o usuário pode fazer uma seleção. Um objeto SingleSelectOptionsList gerencia a lista de opções. Esse objeto pode ser configurado clicando com o botão direito do mouse na lista drop-down no modo Design e selecionando Configure Default Options…, que abre a caixa de diálogo Options Customizer para você poder adicionar opções à lista. • O componente Hyperlink adiciona um hiperlink a uma página Web. Sua propriedade url especifica o recurso a solicitar quando um usuário clica no hiperlink. • Um Radio Button Group fornece botões de opção a partir dos quais o usuário pode selecionar somente um. Um Radio Button Group é vinculado a um objeto SingleSelectOptionList. As opções podem ser editadas clicando com o botão direito do mouse no componente e selecionando Configure Default Options…. • Um componente Button desencadeia uma ação quando clicado e, em geral, mapeia para um elemento XHTML input com o atributo type configurado como submit.

Section 29.6.2  Validação utilizando componentes validadores e validadores personalizados • A validação de formulário ajuda a evitar erros de processamento por causa de entrada de usuário incompleta ou impropriamente formatada. Um Length Validator determina se um campo contém um número aceitável de caracteres. Double Range Validators e Long Range Validators determinam se a entrada numérica está incluída em intervalos aceitáveis. Você também pode criar métodos validadores personalizados no arquivo de bean de página. • Componentes message exibem mensagens de erro quando a validação falha. • Para associar componentes Label e Message com uma Text Field correspondente, mantenha pressionado as teclas Ctrl e Shift e arraste o Label ou Message até o Text Field. Na janela Properties, observe que a propriedade for de cada componente Label e Message é configurada com o Text Field apropriado. • Para assegurar que o usuário fez uma seleção ou inseriu algum texto em um elemento de entrada requerido marque a caixa required na janela Properties do elemento. A propriedade required de um componente deve ser configurada como true (marcada) para a validação ocorrer. • Para adicionar um Length Validator a um componente, arraste o validador da Palette e solte-o no campo a validar. Configure as propriedades maximum e minimum do Length Validator como o número desejado de caracteres na janela Properties. • A validação no lado do cliente pode ser burlada. Realize validação importante no servidor. • É possível limitar o tamanho da entrada de usuário configurando a propriedade maxLength de um Text Field. • Corresponder a entrada de usuário contra uma expressão regular é um modo efetivo de assegurar que a entrada está formatada apropriadamente. Isso pode ser feito com validadores personalizados.

Seção 29.7  Monitoramento de sessão • A personalização permite que sites de comércio eletrônico se comuniquem efetivamente com seus clientes e também aprimora a capacidade do usuário de localizar produtos e serviços desejados. • Há, porém, um dilema entre serviços de comércio eletrônico personalizados e proteção da privacidade. Alguns consumidores aceitam a ideia do conteúdo personalizado, mas outros temem as possíveis consequências adversas se as informações que eles fornecem aos sites de comércio eletrônico forem publicadas ou coletadas por tecnologias de rastreamento. • O HTTP é um protocolo sem estado — ele não suporta conexões persistentes que permitiriam aos servidores Web manter informações de estado em relação a determinados clientes. Desse modo, os servidores Web não podem determinar se uma solicitação vem de um determinado cliente ou se uma série de solicitações vem de um ou mais clientes. • Para ajudar o servidor a distinguir entre clientes, cada cliente deve identificar-se para o servidor. O rastreamento de clientes específicos, conhecido como rastreamento de sessão, pode ser alcançado de várias maneiras. Uma técnica popular utiliza cookies; outra utiliza o objeto SessionBean. • Com elementos de formulário "hidden", um Web Form pode gravar dados de rastreamento de sessão em um form na página Web que ele retorna ao cliente em resposta a uma solicitação anterior. Quando o usuário submete o formulário na nova página Web, todos os dados de formulário, incluindo os campos "hidden", são enviados ao handler de formulário no servidor Web. Com a rescrita de URL, o servidor Web incorpora as informações de rastreamento de sessão diretamente nos URLs dos hiperlinks em que o usuário clica para enviar solicitações subsequentes ao servidor Web.



Terminologia

991

Section 29.7.1  Cookies • Um cookie é um dado, em geral, armazenado em um arquivo de texto no computador do usuário. Um cookie mantém informações sobre o cliente durante e entre sessões de navegador. • Na primeira vez que um usuário visita o site Web, o computador do usuário poderia receber um cookie; esse cookie é então reativado toda vez que o usuário revisita esse site. As informações coletadas visam ser um registro anônimo que contém dados utilizados para personalizar as futuras visitas do usuário ao site. • Cada interação baseada em HTTP entre um cliente e um servidor inclui um cabeçalho contendo as informações sobre a solicitação (quando a comunicação é do cliente para o servidor) ou sobre a resposta (quando a comunicação é do servidor para o cliente). • Quando uma página recebe uma solicitação, o cabeçalho inclui informações, como o tipo de solicitação e quaisquer cookies que foram enviados anteriormente do servidor para serem armazenados na máquina do cliente. Quando o servidor formula sua resposta, as informações de cabeçalho contêm todos os cookies que o servidor quer armazenar no computador do cliente e outras informações, como o tipo MIME da resposta. • A data de expiração de um cookie determina quanto tempo o cookie permanece no computador do cliente. Se você não configurar a data de expiração de um cookie, o navegador Web manterá o cookie pelo tempo de duração da sessão de navegação. Do contrário, ele manterá o cookie até a data de expiração. • Configurar o handler de ação para um Hyperlink permite responder a um clique sem redirecionar o usuário a outra página. • Para adicionar um handler de ação a um Hyperlink que também deve direcionar o usuário a outra página, você deve adicionar uma regra ao arquivo Page Navigation. Para editar esse arquivo, clique com o botão direito do mouse no Designer Visual e escolha Page Navigation…, então arraste o Hyperlink apropriado até a página de destino. • Um objeto cookie é uma instância da classe Cookie no pacote javax.servlet.http. • Um objeto da classe HttpServletResponse (do pacote javax.servlet.http) representa a resposta. Esse objeto pode ser acessado invocando o método getExternalContext no bean de página e então invocando getResponse no objeto resultante. • Um objeto da classe HttpServletRequest (do pacote javax.servlet.http) representa a solicitação. Esse objeto pode ser obtido invocando o método getExternalContext no bean de página, e depois invocando getRequest no objeto resultante. • O método addCookie HttpServletRespone adiciona um cookie à resposta HTTP. • O método HttpServletRequest getCookies retorna os cookies anteriormente gravados no cliente. • Um servidor Web não pode acessar cookies criados por servidores em outros domínios.

Section 29.7.2  Rastreamento de sessão com beans de sessão • Você pode executar o rastreamento de sessão com a subclasse AbstractSessionBean fornecida em todo aplicativo Web do NetBeans. Por padrão, a subclasse é nomeada SessionBean1. As propriedades desse objeto podem ser acessadas por toda uma sessão de navegador invocando o método getSessionBean1 no bean de página e então invocando os métodos apropriados para acessar as propriedades do bean de sessão. • Para armazenar informações de sessão, adicione propriedades ao bean de sessão. Para fazer isso, declare uma variável do tipo apropriado no bean de sessão, então clique com o botão direito do mouse no nome da variável e escolha Refactor> Encapsulate Fields… para criar os métodos get e set apropriados.

Terminologia AbstractPageBean, classe, 954

com.sun.webui.jsf.component, pacote, 954

AbstractSessionBean, classe, 979

contêiner de servlets, 951 cookie, 971 dados de template fixa, 951 data de expiração de um cookie, 971 Design, modo no NetBeans, 956 diretiva, 951 diretório virtual, 948 DNS (Domain Name System), servidor, 948 Double Range Validator, componente JSF, 964 Drop Down List, componente JSF, 964 editor visual (NetBeans), 956 elemento de script, 951 endereço de IP, 948 escape, propriedade de um componente Static Text, 965 escopo de aplicativo, 952 escopo de sessão, 952 escopo de solicitação, 952 f:view, elemento, 953 framework, 952 GET, solicitação HTTP, 948

ação, 951 aplicativo de n camadas, 950 aplicativo de múltiplas camadas, 950 ApplicationBean, 952 árvore componente, 953 bean de página, 952 biblioteca de tags, 951 bibliotecas de tags personalizados, 951 Button, componente JSF, 964 cache, 949 camada de cliente, 950 camada de dados, 950 camada de informações, 950 camada em um aplicativo de múltiplas camada, 950 camada inferior, 950 camada intermediária, 950 camada superior, 950 ciclo de vida do processamento de evento, 956 com.sun.rave.web.ui.appbase, pacote, 954

GlassFish, servidor de aplicativo, 947 Grid Panel, componente JSF, 963 handler do formulário do lado do servidor, 949 host, 948 hostname, 948 Hyperlink, componente JSF, 964 Image, componente JSF, 963 init, método, 956 JavaBean, 951 JavaServer Faces ( JSF), 952 JavaServer Faces ( JSF) Expression Language, 954 JavaServer Pages Standard Tag Library ( JSTL), 951 javax.faces.validators, pacote, 964 javax.servlet.http, pacote, 951 javax.servlet, pacote, 951 JSF ( JavaServer Faces), 952 JSP, contêiner, 951 .jsp, extensão de nome do arquivo, 952 JSP ( JavaServer Pages), 951

992

Capítulo 29  Aplicativos Web JavaServer™ Faces

JSTL ( JavaServer Pages Standard Tag Library), 951 Label, componente JSF, 964 Length Validator, componente JSF, 964 lógica da apresentação, 950 lógica do controlador, 950 lógica do negócio, 950 Long Range Validator, componente JSF, 964 mecanismo de extensão de tags, 951 Message, componente JSF, 964 MIME (Multipurpose Internet Mail Extensions), 949 Multipurpose Internet Mail Extensions (MIME), 949 Navigator, janela no NetBeans, 960 NetBeans IDE, 947 Palette no NetBeans, 957 personalização, 970

pesquisa de DNS, 948 posicionamento absoluto, 959 postback, 956 preprocess, método ( JSF), 956 prerender, método ( JSF), 956 protocolo de estado (HTTP), 971 Radio Button Group, componente JSF, 964 refatorando, 959 regra de negócio, 950 rendered, propriedade de um componente JSF, 965 renderizando XHTML em um navegador da Web, 949 RequestBean, 952 required, propriedade de um componente JSF, 968 resposta de servidor, 949 service, método da interface Servlet, 951

servidor Web, 948 servlet, 951 Servlet, interface, 951 sessão, monitoramento, 971 SessionBean, 952 sistema de nome de domínio (Domain Name System — DNS), servidor, 948 solicitação, método de, 949 span, elemento, 954 Static Text, componente JSF, 953 string de consulta, 949 tags personalizados, 951 Text Area, componente JSF, 978 texto de template fixa, 951 Text Field, componente JSF, 964 UIViewRoot, classe, 953 validação, 964 xmlns, atributos, 953

Exercícios de autorrevisão 29.1

Determine se cada um dos seguintes itens é verdadeiro ou falso. Se falso, explique por quê. a) Cada página Web JSP criada no NetBeans tem seus próprios arquivos ApplicationBean, SessionBean e RequestBean. b) O método de ciclo de vida do processamento de eventos init é invocado toda vez que uma página carrega. c) Cada componente em uma página Web JSP é vinculado a uma propriedade no arquivo de bean de página Java. d) Um único componente JSF pode conter múltiplos componentes de validação. e) Se nenhuma data de expiração for configurada para um cookie, esse cookie será destruído no fim da sessão do navegador. f) Cada componente JSF mapeia para exatamente um único elemento XHTML correspondente. g) Expressões na sintaxe da JSF Expression Language são delimitadas por











29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55

31.10  Consumindo um serviço Web baseado em SOAP orientado a banco de dados







a) Selecionando um lugar

b) Lugar reservado com sucesso

c) Tentando reservar outro lugar na janela na classe econômica quando não há esse lugar disponível

d) Nenhum lugar corresponde com o tipo de lugar e de classe solicitados

Figura 31.20  |  O JSP que permite a um usuário selecionar uma poltrona.

1049

1050

Capítulo 31  Serviços Web

Reserve.java

A Figura 31.21 contém o código de bean de página que fornece a lógica para Reserve.jsp. Como discutido na Seção 29.5.2, a classe que representa o bean da página estende AbstractPageBean. Quando o usuário seleciona um valor em um dos DropDownLists, o handler de evento correspondente — seatTypeDropDown_process-ValueChange (linhas 199–204) ou classTypeDropDown_ processValueChange (linhas 207–212) — é chamado para configurar as propriedades de sessão seatType e classType, que adicionamos ao bean de sessão do aplicativo Web. Os valores dessas propriedades são utilizados como argumentos na chamada ao método reserve do serviço Web. Quando o usuário clica em Reserve na página JSP, o handler de evento reserveButton_action (linhas 215–248) executa. As linhas 219–221 utilizam o objeto SEI (criado nas linhas 40–41) para invocar o método reserve do serviço Web, passando o tipo de poltrona selecionado e o tipo de classe como argumentos. Se reserve retornar true, as linhas 225–230 ocultarão os componentes GUI no JSP e exibirão o successLabel (linha 229) para agradecer ao usuário por fazer uma reserva; do contrário, as linhas 234–239 asseguram que os componentes GUI permaneçam visíveis, exibem o errorLabel (linha 239) para notificar o usuário de que o tipo de poltrona solicitado não está disponível e instruir o usuário a tentar novamente. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 198 199 200 201 202 203 204

// Figura 31.21: Reserve.java // bean de página para reserva de poltrona pelo cliente. package reservationclient; import import import import import import import import import

com.deitel.java.reservation.Reservation; com.deitel.java.reservation.ReservationService; com.sun.rave.web.ui.appbase.AbstractPageBean; com.sun.webui.jsf.component.Button; com.sun.webui.jsf.component.DropDown; com.sun.webui.jsf.component.Label; com.sun.webui.jsf.model.SingleSelectOptionsList; javax.faces.FacesException; javax.faces.event.ValueChangeEvent;

public class Reserve extends AbstractPageBean { // referencia o objeto SEI (isto é, o proxy) private Reservation reservationServiceProxy; // referência a proxy // Managed Component Definition" private void _init() throws Exception { seatTypeDropDownDefaultOptions.setOptions( new com.sun.webui.jsf.model.Option[] { new com.sun.webui.jsf.model.Option( "Aisle", "Aisle" ), new com.sun.webui.jsf.model.Option( "Middle", "Middle" ), new com.sun.webui.jsf.model.Option( "Window", "Window" ) } ); classTypeDropDownDefaultOptions.setOptions( new com.sun.webui.jsf.model.Option[] { new com.sun.webui.jsf.model.Option( "Economy", "Economy" ), new com.sun.webui.jsf.model.Option( "First", "First" ) } ); // obtém a SEI ReservationService reservationService = new ReservationService(); reservationServiceProxy = reservationService.getReservationPort(); } // fim do método _init // As linhas 44–197 do código autogerado foram removidas para economizar // espaço. O código completo está disponível na pasta desse exemplo. // armazena o tipo de poltrona selecionado no bean de sessão public void seatTypeDropDown_processValueChange( ValueChangeEvent event ) { getSessionBean1().setSeatType( ( String ) seatTypeDropDown.getSelected() ); } // fim do método seatTypeDropDown_processValueChange

31.11 Gerador de equação: retornando tipos definidos pelo usuário 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249

1051

// armazena a classe selecionada no bean de sessão public void classTypeDropDown_processValueChange( ValueChangeEvent event ) { getSessionBean1().setClassType( ( String ) classTypeDropDown.getSelected() ); } // fim do método classTypeDropDown_processValueChange // invoca o serviço Web quando o usuário clica no botão Reserve public String reserveButton_action() { try { boolean reserved = reservationServiceProxy.reserve( getSessionBean1().getSeatType(), getSessionBean1().getClassType() ); if ( reserved ) // exibe successLabel; oculta todos os outros { instructionLabel.setRendered( false ); seatTypeDropDown.setRendered( false ); classTypeDropDown.setRendered( false ); reserveButton.setRendered( false ); successLabel.setRendered( true ); errorLabel.setRendered( false ); } // fim do if else // exibe todos, exceto successLabel { instructionLabel.setRendered( true ); seatTypeDropDown.setRendered( true ); classTypeDropDown.setRendered( true ); reserveButton.setRendered( true ); successLabel.setRendered( false ); errorLabel.setRendered( true ); } // fim de else } // fim do try catch ( Exception e ) { e.printStackTrace(); } // fim do catch return null; } // fim do método reserveButton_action } // fim da classe Reserve

Figura 31.21 | Bean de página para reserva de poltrona pelo cliente.

31.11 Gerador de equação: retornando tipos definidos pelo usuário A maioria dos serviços Web que demonstramos recebia e retornava instâncias de tipo primitivo. Também é possível processar instâncias dos tipos de classe em um serviço Web. Esses tipos podem ser passados a ou retornados de métodos de serviço Web. Esta seção apresenta um serviço Web EquationGenerator RESTful que gera equações aritméticas aleatórias do tipo Equation. O cliente é um aplicativo de ensino de matemática que aceita informações sobre a pergunta que o usuário deseja tentar (adição, subtração ou multiplicação) e o nível de habilidade do usuário (1 especifica equações que utilizam números de 1 a 9, 2 especifica equações que envolvem números de 10 a 99, e 3 especifica equações que contêm números de 100 a 999). O serviço Web gera então uma equação que consiste em números aleatórios no intervalo apropriado. O aplicativo cliente recebe a Equation e exibe a pergunta de exemplo ao usuário.

Definindo a classe Equation Definimos a classe Equation na Figura 31.22. Todos os programas nesta seção têm uma cópia dessa classe no pacote correspondente. Exceto pelo nome do pacote, a classe é idêntica em cada projeto, portanto a mostramos somente uma vez. Observe que, como a classe Text­ Message utilizada antes, as cópias do lado do servidor e do lado do cliente da classe Equation não estão relacionados entre si. O único requisito para que a serialização e desserialização funcionem com as classes JAXB e Gson é que a classe Equation deve ter as mesmas propriedades public tanto no servidor como no cliente. Essas propriedades podem ser variáveis de instância públicas ou variáveis de instância privadas que têm os métodos set e get correspondentes.

1052

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

Capítulo 31  Serviços Web

// Figura 31.22: Equation.java // Classe Equation que contém informações sobre uma equação. package com.deitel.equationgeneratorxml; public class Equation { private int leftOperand; private int rightOperand; private int result; private String operationType; // necessário construtor sem argumento public Equation() { this( 0, 0, "add" ); } // fim do construtor sem argumentos // o construtor que recebe o tipo de operação e os operandos public Equation( int leftValue, int rightValue, String type ) { leftOperand = leftValue; rightOperand = rightValue; // determina o resultado if ( type.equals( "add" ) ) // adição { result = leftOperand + rightOperand; operationType = "+"; } // fim do if else if ( type.equals( "subtract" ) ) // subtração { result = leftOperand - rightOperand; operationType = "-"; } // fim do if else // multiplicação { result = leftOperand * rightOperand; operationType = "*"; } // fim de else } // fim do construtor com três argumentos // obtém o leftOperand public int getLeftOperand() { return leftOperand; } // fim do método getLeftOperand // setter necessário public void setLeftOperand( int value ) { leftOperand = value; } // fim do método setLeftOperand // obtém o rightOperand public int getRightOperand() { return rightOperand; } // fim do método getRightOperand // setter necessário public void setRightOperand( int value ) { rightOperand = value; } // fim do método setRightOperand // obtém o resultValue public int getResult() {



31.11  Gerador de equação: retornando tipos definidos pelo usuário

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

1053

return result; } // fim do método getResult // setter necessário public void setResult( int value ) { result = value; } // fim do método setResult // obtém o operationType public String getOperationType() { return operationType; } // fim do método getOperationType // setter necessário public void setOperationType( String value ) { operationType = value; } // fim do método setOperationType // retorna o lado esquerdo da equação como uma String public String getLeftHandSide() { return leftOperand + " " + operationType + " " + rightOperand; } // fim do método getLeftHandSide // retorna o lado direito da equação como uma String public String getRightHandSide() { return "" + result; } // fim do método getRightHandSide // retorna uma representação String de uma Equation public String toString() { return getLeftHandSide() + " = " + getRightHandSide(); } // fim do método toString } // fim da classe Equation

Figura 31.22  |  A classe Equation que contém informações sobre uma equação.

As linhas 19–40 definem um construtor que recebe dois ints representando os operandos esquerdos e direitos, e uma String representando a operação aritmética. O construtor armazena essas informações e então calcula o resultado. O construtor sem parâmetros (linhas 13–16) chama o construtor de três argumentos (linhas 19–40) e passa valores padrão. A classe Equation define os métodos get e set para variáveis de instância leftOperand (linhas 43–52), rightOperand (linhas 55–64), result (linhas 67–76) e operationType (linhas 79–88). Ela também fornece métodos get para os lados esquerdo e direito da equação e um método toString que retorna a equação inteira como uma String. Uma variável de instância só pode ser serializada se ela tiver um método get e um set. Como os diferentes lados da equação e o resultado de toString podem ser gerados a partir de outras variáveis de instância, não há necessidade de enviá-los pela rede. O cliente nesse estudo de caso não utiliza o método getRightHandSide, mas nós o incluímos aqui no caso de futuros clientes optarem por utilizá-lo.

31.11.1  Criando o serviço Web XML baseado em REST EquationGenerator A Figura 31.23 apresenta a classe EquationGeneratorXML do serviço Web para criar Equações geradas aleatoriamente. O método getXml (linhas 19–38) recebe dois parâmetros — uma String representando a operação matemática ("add", "subtract" ou "multiply") e um int representando o nível de dificuldade. O JAX-RS converte automaticamente os argumentos no tipo correto e retornará um erro “not found” ao cliente se o argumento não puder ser convertido de String para o tipo de destino. Os tipos suportados para conversão incluem tipos inteiros, tipos de ponto flutuante, boolean e as classes empacotadoras de tipo correspondentes.

1 2 3 4 5

// Figura 31.23: EquationGeneratorXMLResource.java // O gerador de equação RESTful que retorna a XML. package com.deitel.equationgeneratorxml; import java.io.StringWriter;

1054 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39

Capítulo 31  Serviços Web import import import import import import

java.util.Random; javax.ws.rs.GET; javax.ws.rs.Path; javax.ws.rs.PathParam; javax.ws.rs.Produces; javax.xml.bind.JAXB; // classe utilitária para operações JAXB comuns

@Path( "equation" ) public class EquationGeneratorXMLResource { static Random randomObject = new Random(); // gerador de número aleatório // recupera uma equação formatada como XML @GET @Path( "{operation}/{level}" ) @Produces( "application/xml" ) public String getXml( @PathParam( "operation" ) String operation, @PathParam( "level" ) int level ) { // calcula os valores mínimos e máximos dos números int minimum = ( int ) Math.pow( 10, level - 1 ); int maximum = ( int ) Math.pow( 10, level ); // cria os números no lado esquerdo da equação int first = randomObject.nextInt( maximum - minimum ) + minimum; int second = randomObject.nextInt( maximum - minimum ) + minimum; // cria o objeto Equation e o empacota na XML Equation equation = new Equation( first, second, operation ); StringWriter writer = new StringWriter(); // saída da XML aqui JAXB.marshal( equation, writer ); // grava Equation em StringWriter return writer.toString(); // retorna a string XML } // fim do método getXml } // fim da classe EquationGeneratorXMLResource

Figura 31.23  |  Um gerador de equação REST que retorna XML.

O método getXml primeiro determina os valores mínimos (inclusivamente) e máximos (exclusivamente) para os números na equação que ele retornará (linhas 26–27). Ele então utiliza um membro estático da classe Random (linha 16) para gerar dois números aleatórios nesse intervalo (linhas 30–31). A linha 34 cria um objeto Equation, passando esses dois números e a operação solicitada ao construtor. O método getXml utiliza então JAXB para converter o objeto Equation em XML (linha 36), que é gerado para o StringWriter criado na linha 35. Por fim, ele recupera os dados que foram gravados em StringWriter e retorna-os ao cliente. [Nota: Reimplementaremos esse serviço Web com o JSON na Seção 31.11.3.]

31.11.2  Consumindo o serviço Web XML baseado em REST EquationGenerator O aplicativo EquationGeneratorXMLClient (Figura 31.24) recupera um objeto Equation formatado como XML a partir do serviço Web EquationGeneratorXML. O aplicativo cliente exibe então o lado esquerdo de Equation e espera que o usuário avalie a expressão e insira o resultado. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

// Figura 31.24: EquationGeneratorXMLClientJFrame.java // Programa de ensino de matemática que utiliza REST e XML para gerar equações. package com.deitel.equationgeneratorxmlclient; import javax.swing.JOptionPane; import javax.xml.bind.JAXB; // classe utilitária para operações JAXB comuns public class EquationGeneratorXMLClientJFrame extends javax.swing.JFrame { private String operation = "add"; // operação feita pelo usuário é testada em private int difficulty = 1; // 1, 2 ou 3 dígitos em cada número private int answer; // resposta correta à pergunta // construtor sem argumento public EquationGeneratorXMLClientJFrame() {

17 18 19 20 21 22 23 24 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203

31.11  Gerador de equação: retornando tipos definidos pelo usuário initComponents(); } // fim do construtor sem argumentos // // // //

O método initComponents é autogerado pelo NetBeans e é chamado a partir do construtor para inicializar a GUI. Esse método não é mostrado aqui para economizar espaço. Abra EquationGeneratorXMLClientJFrame.java na pasta desse exemplo para examinar o código gerado completo.

// determina se o usuário respondeu corretamente private void checkAnswerJButtonActionPerformed( java.awt.event.ActionEvent evt) { if ( answerJTextField.getText().equals( "" ) ) { JOptionPane.showMessageDialog( this, "Please enter your answer." ); } // fim do if int userAnswer = Integer.parseInt( answerJTextField.getText() ); if ( userAnswer == answer ) { equationJLabel.setText( "" ); // limpa o rótulo answerJTextField.setText( "" ); // limpa o campo de texto checkAnswerJButton.setEnabled( false ); JOptionPane.showMessageDialog( this, "Correct! Good Job!", "Correct", JOptionPane.PLAIN_MESSAGE ); } // fim do if else { JOptionPane.showMessageDialog( this, "Incorrect. Try again.", "Incorrect", JOptionPane.PLAIN_MESSAGE ); } // fim de else } // fim do método checkAnswerJButtonActionPerformed // recupera a equação do serviço Web e exibe o lado esquerdo ao usuário private void generateJButtonActionPerformed( java.awt.event.ActionEvent evt) { try { String url = String.format( "http://localhost:8080/" + "EquationGeneratorXML/resources/equation/%s/%d", operation, difficulty ); // converte a XML de volta em um objeto Equation Equation equation = JAXB.unmarshal( url, Equation.class ); answer = equation.getResult(); equationJLabel.setText( equation.getLeftHandSide() + " =" ); checkAnswerJButton.setEnabled( true ); } // fim do try catch ( Exception exception ) { exception.printStackTrace(); } // fim do catch } // fim do método generateJButtonActionPerformed // obtém a operação matemática selecionada pelo usuário private void operationJComboBoxItemStateChanged( java.awt.event.ItemEvent evt) { String item = ( String ) operationJComboBox.getSelectedItem(); if ( item.equals( "Addition" ) ) operation = "add"; // o usuário selecionou adição else if ( item.equals( "Subtraction" ) ) operation = "subtract"; // o usuário selecionou subtração else

1055

1056 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241

Capítulo 31  Serviços Web operation = "multiply"; // o usuário selecionou multiplicação } // fim do método operationJComboBoxItemStateChanged // obtém o nível de dificuldade selecionado pelo usuário private void levelJComboBoxItemStateChanged( java.awt.event.ItemEvent evt) { // índice inicia em 0, assim adiciona 1 para obter o nível de dificuldade difficulty = levelJComboBox.getSelectedIndex() + 1; } // fim do método levelJComboBoxItemStateChanged // método main inicia a execução public static void main(String args[]) { java.awt.EventQueue.invokeLater( new Runnable() { public void run() { new EquationGeneratorXMLClientJFrame().setVisible( true ); } // fim do método run } // fim da classe interna anônima ); // fim da chamada a java.awt. EventQueue.invokeLater } // fim de main // Declaração de variáveis - não modifique private javax.swing.JLabel answerJLabel; private javax.swing.JTextField answerJTextField; private javax.swing.JButton checkAnswerJButton; private javax.swing.JLabel equationJLabel; private javax.swing.JButton generateJButton; private javax.swing.JComboBox levelJComboBox; private javax.swing.JLabel levelJLabel; private javax.swing.JComboBox operationJComboBox; private javax.swing.JLabel operationJLabel; private javax.swing.JLabel questionJLabel; // Fim da declaração de variáveis } // fim da classe EquationGeneratorXMLClientJFrame

a) Gerando uma equação simples.

b) Respondendo uma equação de subtração mais complicada.

Figura 31.24  |  Programa de ensino de matemática utilizando REST e XML para gerar equações.

A configuração padrão do nível de dificuldade é 1, mas o usuário pode alterar isso escolhendo um nível no JComboBox Choose level. Alterar o valor selecionado invoca o handler de evento levelJComboBoxItemStateChanged (linhas 208–213), que configura a variável difficulty de instância com o nível selecionado pelo usuário. Embora a configuração padrão para o tipo de pergunta seja Addition, o usuário também pode alterar isso escolhendo no JComboBox Choose operation. Isso invoca o handler de evento operationJComboBoxItemStateChanged nas linhas 194–205, que atribui à variável de instância operation a String correspondente à seleção do usuário. O handler de evento para generateJButton (linhas 171–191) constrói o URL para invocar o serviço Web, em seguida passa esse URL para o método unmarshal, com uma instância de Class, para que o JAXB possa converter a XML em um objeto Equation (linha 181). Depois que a XML foi convertida de volta em uma Equation, as linhas 183–184 recuperam a resposta correta e exibem o lado esquerdo da equação. O botão Check Answer é então ativado (linha 185), e o usuário deve resolver o problema e inserir a resposta. Quando o usuário insere um valor e clica em Check Answer, o handler de evento checkAnswer JButtonActionPerformed (linhas 144–168) recupera a resposta do usuário que foi armazenada anteriormente (linha 155). Se elas corresponderem, as linhas 157–161 reinicializam os elementos GUI para que o usuário possa gerar outra equação e dizer ao usuário que a resposta estava correta. Se não corresponderem, uma caixa de mensagem solicita ao usuário a tentar novamente é exibida (linhas 165–166).



31.11  Gerador de equação: retornando tipos definidos pelo usuário

1057

31.11.3  Criando o serviço Web JSON baseado em REST EquationGenerator Como vimos na Seção 31.8, serviços Web RESTful também podem retornar dados formatados como JSON. A Figura 31.25 é uma reimplementação do serviço EquationGeneratorXML que retorna uma Equation no formato JSON. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

// Figura 31.25: EquationGeneratorJSONResource.java // O gerador de equação RESTful que retorna JSON. package com.deitel.equationgeneratorjson; import import import import import import

com.google.gson.Gson; // converte POJO em JSON e volta novamente java.util.Random; javax.ws.rs.GET; javax.ws.rs.Path; javax.ws.rs.PathParam; javax.ws.rs.Produces;

@Path( "equation" ) public class EquationGeneratorJSONResource { static Random randomObject = new Random(); // gerador de número aleatório // recupera uma equação formatada como JSON @GET @Path( "{operation}/{level}" ) @Produces( "application/json" ) public String getJson( @PathParam( "operation" ) String operation, @PathParam( "level" ) int level ) { // calcula os valores mínimos e máximos dos números int minimum = ( int ) Math.pow( 10, level - 1 ); int maximum = ( int ) Math.pow( 10, level ); // cria os números no lado esquerdo da equação int first = randomObject.nextInt( maximum - minimum ) + minimum; int second = randomObject.nextInt( maximum - minimum ) + minimum; // cria o objeto Equation e o resultado de retorno Equation equation = new Equation( first, second, operation ); return new Gson().toJson( equation ); // converte em JSON e retorna } // fim do método getJson } // fim da classe EquationGeneratorJSONResource

Figura 31.25  |  O gerador de equação RESTful que retorna JSON.

A lógica implementada aqui é a mesma que a da versão XML, exceto a última linha (linha 34), que utiliza o Gson para converter o objeto Equation no JSON em vez de utilizar o JAXB para convertê-lo em XML. Observe que a anotação @Produces (linha 20) também mudou para refletir o formato de dados JSON.

31.11.4  Consumindo o serviço Web JSON baseado em REST EquationGenerator O programa na Figura 31.26 consome o serviço EquationGeneratorJSON e executa a mesma função que EquationGeneratorXML­ Client — a única diferença é a maneira como o objeto Equation é recuperado do serviço Web. As linhas 181–183 constroem o URL que é

utilizado para invocar o serviço EquationGeneratorJSON. Como no exemplo WelcomeRESTJSONClient, utilizamos a classe URL e um InputStreamReader para invocar o serviço Web e ler a resposta (linhas 186–187). O JSON recuperado é desserializado utilizando o Gson (linha 191) e convertido de volta em um objeto Equation. Como anteriormente, utilizamos o método getResult (linha 194) do objeto desserializado para obter a resposta e o método getLeftHandSide (linha 195) para exibir o lado esquerdo da equação.

1 2 3 4 5 6 7 8

// Figura 31.26: EquationGeneratorJSONClientJFrame.java // Programa de ensino de matemática que utiliza REST e JSON para gerar equações. package com.deitel.equationgeneratorjsonclient; import import import import

com.google.gson.Gson; // converte POJO em JSON e volta novamente java.io.InputStreamReader; java.net.URL; javax.swing.JOptionPane;

1058 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

Capítulo 31  Serviços Web

public class EquationGeneratorJSONClientJFrame extends javax.swing.JFrame { private String operation = "add"; // operação feita pelo usuário é testada em private int difficulty = 1; // 1, 2, ou 3 dígitos em cada número private int answer; // resposta correta à pergunta // construtor sem argumento public EquationGeneratorJSONClientJFrame() { initComponents(); } // fim do construtor sem argumentos // // // //

O método initComponents é autogerado pelo NetBeans e é chamado a partir do construtor para inicializar a GUI. Esse método não é mostrado aqui para economizar espaço. Abre EquationGeneratorJSONClientJFrame.java no pasta desse exemplo para examinar o código gerado completo.

// determina se o usuário respondeu corretamente private void checkAnswerJButtonActionPerformed( java.awt.event.ActionEvent evt) { if ( answerJTextField.getText().equals( "" ) ) { JOptionPane.showMessageDialog( this, "Please enter your answer." ); } // fim do if int userAnswer = Integer.parseInt( answerJTextField.getText() ); if ( userAnswer == answer ) { equationJLabel.setText( "" ); // limpa o rótulo answerJTextField.setText( "" ); // limpa o campo de texto checkAnswerJButton.setEnabled( false ); JOptionPane.showMessageDialog( this, "Correct! Good Job!", "Correct", JOptionPane.PLAIN_MESSAGE ); } // fim do if else { JOptionPane.showMessageDialog( this, "Incorrect. Try again.", "Incorrect", JOptionPane.PLAIN_MESSAGE ); } // fim de else } // fim do método checkAnswerJButtonActionPerformed // recupera a equação do serviço Web e exibe o lado esquerdo ao usuário private void generateJButtonActionPerformed( java.awt.event.ActionEvent evt) { try { // URL do serviço EquationGeneratorJSON, com parâmetros String url = String.format( "http://localhost:8080/" + "EquationGeneratorJSON/resources/equation/%s/%d", operation, difficulty ); // abre URL e cria um Reader para ler os dados InputStreamReader reader = new InputStreamReader( new URL( url ).openStream() ); // converte o JSON de volta em um objeto Equation Equation equation = new Gson().fromJson( reader, Equation.class ); // atualiza o estado interno e a GUI para refletir a equação answer = equation.getResult(); equationJLabel.setText( equation.getLeftHandSide() + " =" ); checkAnswerJButton.setEnabled( true ); } // fim do try

31.12 Conclusão 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252

1059

catch ( Exception exception ) { exception.printStackTrace(); } // fim do catch } // fim do método generateJButtonActionPerformed // obtém a operação matemática selecionada pelo usuário private void operationJComboBoxItemStateChanged( java.awt.event.ItemEvent evt) { String item = ( String ) operationJComboBox.getSelectedItem(); if ( item.equals( "Addition" ) ) operation = "add"; // o usuário selecionou adição else if ( item.equals( "Subtraction" ) ) operation = "subtract"; // o usuário selecionou subtração else operation = "multiply"; // o usuário selecionou multiplicação } // fim do método operationJComboBoxItemStateChanged // obtém o nível de dificuldade selecionado pelo usuário private void levelJComboBoxItemStateChanged( java.awt.event.ItemEvent evt) { // índice inicia em 0, assim adiciona 1 para obter o nível de dificuldade difficulty = levelJComboBox.getSelectedIndex() + 1; } // fim do método levelJComboBoxItemStateChanged // método main inicia a execução public static void main( String args[] ) { java.awt.EventQueue.invokeLater( new Runnable() { public void run() { new EquationGeneratorJSONClientJFrame().setVisible( true ); } // fim do método run } // fim da classe interna anônima ); // fim da chamada a java.awt. EventQueue.invokeLater } // fim de main // Declaração de variáveis ­ não modifique private javax.swing.JLabel answerJLabel; private javax.swing.JTextField answerJTextField; private javax.swing.JButton checkAnswerJButton; private javax.swing.JLabel equationJLabel; private javax.swing.JButton generateJButton; private javax.swing.JComboBox levelJComboBox; private javax.swing.JLabel levelJLabel; private javax.swing.JComboBox operationJComboBox; private javax.swing.JLabel operationJLabel; private javax.swing.JLabel questionJLabel; // Fim da declaração de variáveis } // fim da classe EquationGeneratorJSONClientJFrame

Figura 31.26 | Programa de ensino de matemática que utiliza REST e JSON para gerar equações.

31.12 Conclusão Este capítulo introduziu serviços da Web — um conjunto de tecnologias para construir sistemas distribuídos nos quais os componentes de sistema se comunicam entre si por redes. Em particular, apresentamos serviços Web baseados em JAX-WS SOAP e serviços Web baseados em JAX-RS REST. Você aprendeu que um serviço Web é uma classe que permite ao software cliente chamar os métodos do serviço Web remotamente via protocolos e formatos de dados comuns como XML, JSON, HTTP, SOAP e REST. Também discutimos vários benefícios da computação distribuída com serviços Web.

1060

Capítulo 31  Serviços Web

Explicamos como o NetBeans e o JAX-WS e JAX-RS APIs facilitam publicar e consumir serviços Web. Você aprendeu a definir métodos e serviços Web utilizando o protocolo SOAP e a arquitetura REST e a retornar dados nos formatos XML e JSON. Você consumiu serviços Web baseados em SOAP utilizando classes proxy para chamar os métodos do serviço Web. Você também consumiu serviços Web baseados em REST utilizando a classe URL para invocar os serviços e abrir InputStreams a partir dos quais os clientes podem ler as respostas dos serviços. Você aprendeu a definir serviços Web e métodos Web e também a consumir ambos a partir de aplicativos desktop Java e aplicativos Web. Depois de explicar os mecânicos dos serviços Web por meio dos nossos exemplos Welcome, demonstramos serviços Web mais sofisticados que utilizam rastreamento de sessão, acesso a banco de dados e tipos definidos pelo usuário. Também explicamos a serialização XML e JSON e mostramos como recuperar objetos dos tipos definidos pelo usuário a partir de serviços Web.

Resumo Seção 31.1  Introdução • Um serviço Web é um componente de software armazenado em um computador que pode ser acessado por um aplicativo (ou outro componente de software) em outro computador por uma rede. • Serviços Web se comunicam utilizando tecnologias como XML, JSON e HTTP. • O JAX-WS é baseado no Simple Object Access Protocol (SOAP) — um protocolo baseado em XML que permite a serviços Web e clientes se comunicarem. • O JAX-RS utiliza o Representational State Transfer (REST) — uma arquitetura de rede que utiliza os mecanismos de solicitação/resposta tradicionais da Web como solicitações GET e POST. • Serviços da Web permitem aos negócios conduzir transações via serviços Web padronizados amplamente disponíveis em vez de contar com aplicativos proprietários. • Serviços Web são independentes de plataforma e linguagem, portanto, as empresas podem colaborar via serviços Web sem problemas de compatibilidade de hardware, software e comunicação. • O NetBeans é uma entre várias ferramentas que permitem “publicar” e/ou “consumir” serviços Web.

Seção 31.2  Fundamentos do serviço Web • A máquina na qual um serviço Web reside é chamada host de serviço Web. • Um aplicativo cliente que acessa o serviço Web envia uma chamada de método por uma rede ao host de serviço Web, que processa a chamada e retorna uma resposta pela rede ao aplicativo. • No Java, um serviço Web é implementado como uma classe. A classe que representa o serviço Web reside em um servidor — ela não é parte do aplicativo cliente. • Disponibilizar um serviço Web para receber solicitações de cliente é conhecido como publicar um serviço Web; utilizar um serviço Web a partir de um aplicativo cliente é conhecida como consumir um serviço Web.

Seção 31.3  Simple Object Access Protocol (SOAP) • O SOAP é um protocolo independente de plataforma que utiliza XML para fazer chamadas de procedimento remoto, geralmente via HTTP. Cada solicitação e resposta são empacotadas em uma mensagem SOAP — uma mensagem XML que contém as informações que um serviço Web exige para processar a mensagem. • Mensagens SOAP são escritas em XML para que sejam legíveis por computadores e humanos e para que sejam independentes de plataforma. • O SOAP suporta um extenso conjunto de tipos — tipos primitivos, bem como DateTime, XmlNode e outros. O SOAP também pode transmitir arrays desses tipos. • Quando um programa invoca um método de um serviço Web SOAP, a solicitação e todas as informações relevantes são empacotadas em uma mensagem SOAP, colocadas em um envelope SOAP e enviadas ao servidor no qual o serviço Web reside. • Quando um serviço Web recebe uma mensagem SOAP, ele analisa a XML que representa a mensagem e então processa o conteúdo da mensagem. A mensagem especifica o método que o cliente deseja executar e os argumentos que o cliente passou para esse método. • Depois que um serviço Web analisa uma mensagem SOAP, ele chama o método apropriado com os argumentos especificados (se houver algum) e reenvia a resposta ao cliente em outra mensagem SOAP. O cliente analisa a resposta para recuperar o resultado do método.

Seção 31.4  REpresentational State Transfer (REST) • O REpresentational State Transfer (REST) refere-se a um estilo arquitetônico de implementar serviços Web. Esses serviços Web costumam ser chamados de serviços Web RESTful. Embora o próprio REST não seja um padrão, serviços Web RESTful são implementados utilizando padrões Web. • Cada operação em um serviço Web RESTful é identificada por um URL exclusivo. • O REST pode retornar dados em muitos formatos, incluindo XML e JSON.



Resumo

1061

Seção 31.5  JavaScript Object Notation ( JSON) • A JavaScript Object Notation ( JSON) é uma alternativa à XML para representar dados. • O JSON é um formato baseado em texto de troca de dados utilizado para representar objetos em JavaScript como coleções de pares de nome/valor representados como Strings. • O JSON é um formato simples que torna objetos fáceis de ler, criar e analisar e permite a programas transmitir dados eficientemente pela Internet porque ele é bem menos prolixo do que a XML. • Cada valor em um array JSON pode ser uma string, um número, um objeto JSON, true, false ou null.

Seção 31.6.1 Criando de um projeto de aplicativo Web e adicionando uma classe de serviço Web no NetBeans • Ao criar um serviço Web no NetBeans, você focaliza a lógica do serviço Web e deixa o IDE tratar a infraestrutura do serviço Web. • Para criar um serviço Web no NetBeans, você primeiro cria um projeto Web Application.

Seção 31.6.2 Definindo o serviço Web WelcomeSOAP no NetBeans • Por padrão, cada nova classe de serviço Web criada com as APIs do JAX-WS é um POJO (Plain Old Java Object) — você não precisa estender uma classe ou implementar uma interface para criar um serviço Web. • Ao implantar um aplicativo Web que contém um serviço Web JAX-WS, o servidor cria os artefatos no lado do servidor que suportam o serviço Web. • A anotação @WebService indica que uma classe representa um serviço Web. O elemento name opcional especifica o nome da classe de interface de ponto de extremidade de serviço (Service Endpoint Interface — SEI). O elemento serviceName opcional especifica o nome da classe que o cliente utiliza para obter um objeto SEI. • O NetBeans insere a anotação @WebService no início de cada nova classe de serviço Web que você cria. Você pode adicionar os elementos name serviceName opcionais nos parênteses da anotação. • Os métodos que são marcados com a anotação @WebMethod podem ser chamados remotamente. • Os métodos que não são marcados com @WebMethod não são acessíveis a clientes que consomem o serviço Web. Esses métodos costumam ser métodos utilitários dentro da classe de serviço Web. • O elemento operationName opcional da anotação @WebMethod especifica o nome do método que é exposto aos clientes do serviço Web. • Parâmetros de métodos Web são anotados com a anotação @WebParam. O elemento opcional name indica o nome de parâmetro que é exposto aos clientes do serviço Web.

Seção 31.6.3 Publicando o serviço Web WelcomeSOAP a partir do NetBeans • O NetBeans trata todos os detalhes da criação e implantação de um serviço Web para você. Isso inclui criar o framework necessário para suportar o serviço Web. • Para determinar se há algum erro de compilação no seu projeto, clique com o botão direito do mouse no nome do projeto na guia NetBeans Projects e selecione a opção Build. • Quando o projeto compila com sucesso, você pode selecionar Deploy para implantá-lo no servidor que você selecionou durante a configuração do aplicativo. • Se o código no projeto mudou desde a última construção, selecionar Deploy também constrói o projeto. • Selecionar Run executa o aplicativo Web. • As opções Deploy e Run também constroem o projeto se ele mudou e iniciam o servidor de aplicativos se ele ainda não estiver em execução. • Para assegurar que todos os arquivos de código-fonte em um projeto são recompilados durante a próxima operação de construção, utilize as opções Clean ou Clean and Build.

Seção 31.6.4 Testando o serviço Web WelcomeSOAP com a página Web Tester do servidor de aplicativos GlassFish • GlassFish pode criar dinamicamente uma página Web para testar métodos de um serviço Web em um navegador Web. Para abrir a página de teste, expanda o nó Web Services do projeto na guia NetBeans Projects, então clique com o botão direito do mouse no nome da classe do serviço Web e escolha Test Web Service. • Um cliente só pode acessar um serviço Web quando o servidor de aplicativos está em execução. Se o NetBeans inicializar o servidor de aplicativos para você, o servidor será desativado quando você fecha o NetBeans. Para manter o servidor de aplicativos pronto e funcionando, você pode inicializá-lo independentemente do NetBeans.

Seção 31.6.5 Descrevendo um serviço Web com a Web Service Description Language (WSDL) • Para consumir um serviço Web, um cliente deve saber onde localizá-lo e deve ter a descrição do serviço Web. • JAX-WS utiliza a Web Service Description Language Web (WSDL) — um vocabulário XML padrão para descrever serviços a Web de uma maneira independente de plataforma.

1062

Capítulo 31  Serviços Web

• O servidor gera a WSDL de um serviço Web dinamicamente para você, e as ferramentas cliente podem analisar a WSDL para ajudar a criar a classe proxy no lado do cliente que um cliente utiliza para acessar o serviço Web. • Para visualizar a WSDL para um serviço Web, digite o URL no campo de endereço do navegador seguido por ?WSDL ou clique no link WSDL File no página Web Tester do serviço.

Seção 31.6.6 Criando um cliente para consumir o serviço Web WelcomeSOAP • Um cliente de serviço Web pode ser qualquer tipo do aplicativo ou até mesmo outro serviço Web. • Você ativa um aplicativo cliente baseado em Java para consumir um serviço Web adicionando uma referência de serviço Web, que define a classe SEI para o cliente poder acessar o serviço Web. • Um aplicativo que consome um serviço Web baseado em SOAP invoca métodos em um objeto SEI que interagem com o serviço Web no nome do cliente. • O objeto SEI trata os detalhes da passagem dos argumentos de método e recebimento dos valores de retorno do serviço Web. Essa comunicação pode ocorrer por uma rede local, pela Internet ou até mesmo com um serviço Web no mesmo computador. • O NetBeans cria essas classes SEI para você. • Ao adicionar a referência de serviço Web, o IDE cria e compila os artefatos no lado do cliente — o framework do código Java que suporta a classe SEI do lado do cliente. A classe SEI utiliza o resto dos artefatos para interagir com o serviço Web. • Uma referência de serviço Web é adicionada fornecendo ao NetBeans o URL do arquivo WSDL do serviço Web.

Seção 31.6.7 Consumindo o serviço Web WelcomeSOAP • Ao criar uma GUI no NetBeans, ele utiliza nomes de classe totalmente qualificados; portanto, declarações import são desnecessárias. • Para consumir um serviço Web JAX-WS, você deve obter um objeto SEI chamando o método getWelcomeSOAPPort na classe que foi especificada com o elemento serviceName da anotação @WebService. Esse método retorna um objeto SEI da classe que foi especificada com o elemento name da anotação @WebService. Você então invoca os métodos de serviços por meio do objeto SEI.

Seção 31.7.1 Criando um serviço Web XML baseado em REST • O plug-in RESTful Web Services para o NetBeans fornece várias templates para criar serviços Web RESTful, incluindo aquelas que podem interagir com bancos de dados no nome do cliente. • A anotação @Path em uma classe de serviço Web JAX-RS indica o URI para acessar o serviço Web. Ela é anexada ao URL do projeto de aplicativo Web para invocar o serviço. Os métodos da classe também podem utilizar a anotação @Path. • Partes do caminho especificado entre colchetes indicam parâmetros — eles são marcadores de lugar para argumentos que são passados para o serviço Web como parte do caminho. O caminho de base do serviço é o diretório resources do projeto. • Os argumentos em um URL podem ser utilizados como argumentos para um método de serviço Web. Para fazer isso, vincule os parâmetros descritos na especificação @Path para os parâmetros de um método de serviço Web com a anotação @PathParam. Quando a solicitação é recebida, o servidor passa o(s) argumento(s) no URL para o(s) parâmetro(s) apropriado(s) no método de serviço Web. • A anotação @GET denota que um método é acessado via solicitação GET HTTP. Anotações semelhantes existem para as solicitações PUT, POST, DELETE e HEAD HTTP. • A anotação @Produces denota o tipo de conteúdo retornado ao cliente. É possível ter múltiplos métodos com o mesmo caminho e método HTTP, mas diferentes anotações @Produces, e o JAX-RS chamará o método correspondente ao tipo de conteúdo solicitado pelo cliente. • A anotação @Consumes restringe o tipo de conteúdo que um serviço Web aceita de uma solicitação PUT. • JAXB ( Java Architecture for XML Binding) é um conjunto de classes para converter POJOs em e da XML. A classe JAXB (pacote javax.xml.bind) contém métodos static para operações comuns. • O método static marshal da classe JAXB converte um objeto Java no formato XML. • O GlassFish não fornece páginas de teste para serviços RESTful, mas o NetBeans gera uma página de teste que pode ser acessada clicando com o botão direito do mouse no nó do projeto na guia Projects e selecionando Test RESTful Web Services. • Na página de teste, selecione um elemento de método na coluna esquerda. O lado direito da página exibe um formulário que permite escolher o tipo MIME dos dados e deixa inserir os argumentos do método. Clique no botão Test para invocar o serviço Web e exibir os dados retornados. • WADL (Web Application Description Language) tem objetivos de design semelhantes à WSDL, mas descreve serviços RESTful em vez de serviços SOAP.

Seção 31.7.2 Consumindo um serviço Web XML baseado em REST • Clientes de serviços Web RESTful não requerem referências de serviço Web. • A classe

JAXB

tem um método

static unmarshal

que recebe como argumentos um nome de arquivo ou URL como uma

Class que indica a classe Java na qual a XML será convertida.

String,

e um objeto



Resumo

1063

Seção 31.8  Publicando e consumindo serviços Web JSON baseados em REST • Componentes JSON — objetos, arrays, strings, números — podem ser facilmente mapeados para construções no Java e em outras linguagens de programação. • Há muitas bibliotecas JSON código-fonte aberto para o Java e outras linguagens. A biblioteca Gson em code.google.com/p/google-gson/ fornece um modo simples de converter POJOs em e do JSON.

Seção 31.8.1 Criando um serviço Web JSON baseado em REST • Para adicionar um arquivo JAR como uma biblioteca no NetBeans, clique com o botão direito do mouse na pasta Libraries do seu projeto, escolha Add JAR/Folder…, localize o arquivo JAR e clique em Open. • Para um método de serviço Web que retorna texto JSON, o argumento para o atributo @Produces deve ser "application/json". • No JSON, todos os dados devem ser encapsulados em um tipo de dados composto. • Crie um objeto Gson (do pacote com.google.gson) e chame o método toJson para converter um objeto na sua representação String JSON.

Seção 31.8.2 Consumindo um serviço Web JSON baseado em REST • Para ler dados JSON de um URL, crie um objeto URL e chame o método openStream. Isso invoca o serviço Web e retorna um InputStream do qual o cliente pode ler a resposta. Empacote o InputStream em um InputStreamReader para que ele possa ser passado como o primeiro argumento para o método fromJson da classe Gson.

Seção 31.9  Rastreamento de sessão em um serviço Web baseado em SOAP • Pode ser vantajoso que um serviço Web mantenha informações de estado de cliente, eliminando assim a necessidade de passar informações cliente entre o cliente e o serviço Web múltiplas vezes. O armazenamento de informações de sessão também permite que um serviço Web diferencie seus clientes.

Seção 31.9.1 Criando um serviço Web Blackjack • Para utilizar o rastreamento de sessão em um serviço Web, você deve incluir o código dos recursos que mantêm as informações de estado de sessão. JAX-WS trata isso para você via anotação @Resource. Essa anotação “injeta” o código de suporte complexo na sua classe, permitindo assim que você se concentre na lógica do negócio em vez do código de suporte. • Utilizar anotações para adicionar código que suporta suas classes é conhecido como injeção de dependência. Anotações como @WebMethod e @WebParam também executam a injeção de dependência.

@WebService,

• Um objeto WebServiceContext ativa um serviço Web para acessar e manter informações para uma solicitação específica. O código que cria um objeto WebServiceContext é injetado na classe por uma anotação @Resource. • O objeto WebServiceContext é utilizado para obter um objeto MessageContext. Um serviço Web utiliza um MessageContext para obter um objeto HttpSession para o cliente atual. • O método get do objeto MessageContext obtém o objeto HttpSession para o cliente atual. O método get recebe uma constante indicando o que obter do MessageContext. A constante MessageContext.SERVLET_REQUEST indica que queremos obter o objeto HttpServletRequest para o cliente atual. Chamamos então o método getSession para obter o objeto HttpSession a partir do objeto HttpServletRequest. • O método HttpSession getAttribute recebe uma String que identifica o Object a obter do estado de sessão.

Seção 31.9.2 Consumindo o serviço Web Blackjack • No framework JAX-WS, o cliente deve indicar se ele quer permitir que o serviço Web mantenha informações de sessão. Para fazer isso, primeiro faça uma coerção do objeto proxy no tipo de interface BindingProvider. Um BindingProvider permite que o cliente manipule as informações de solicitação que serão enviadas ao servidor. Essas informações são armazenadas em um objeto que implementa a interface RequestContext. BindingProvider e RequestContext são parte do framework que é criado pelo IDE quando você adiciona um cliente de serviço Web ao aplicativo. • Em seguida, invoque o método getRequestContext de BindingProvider para obter o objeto RequestContext. Chame então o método put de RequestContext para configurar a propriedade BindingProvider.SESSION_MAINTAIN_PROPERTY como true, o que ativa o rastreamento de sessão no lado de cliente para que o serviço Web saiba que o cliente está invocando os métodos Web do serviço.

Seção 31.11  Gerador de equação: retornando tipos definidos pelo usuário • Também é possível processar instâncias dos tipos de classe em um serviço Web. Esses tipos podem ser passados a ou retornados de métodos de serviço Web. • Uma variável de instância só pode ser serializada se ela for public ou tiver um método get e um set. • As propriedades que podem ser geradas dos valores de outras propriedades não devem ser serializadas para evitar redundância. • O JAX-RS converte automaticamente argumentos de uma anotação @Path no tipo correto, e retornará um erro “not found” ao cliente se o argumento não puder ser convertido da String passada como parte do URL no tipo de destino. Os tipos suportados para conversão incluem tipos inteiros, tipos de ponto flutuante, boolean e as classes empacotadoras de tipo correspondentes.

1064

Capítulo 31  Serviços Web

Terminologia adicionando uma referência de serviço Web a um aplicativo, 1024 artefatos do lado do cliente, 1025 artefatos do lado do servidor, 1021 BindingProvider, interface, 1045 business-to-business (B2B), transações, 1019 classe proxy de um serviço Web, 1022 @Consumes, anotação, 1029 consumir um serviço Web, 1019 descrição de um serviço Web, 1024 firewall, 1019 fromJson, método da classe Gson, 1034 @GET, anotação, 1029 getAttribute, método da interface HttpSession, 1037 getRequestContext, método da interface BindingProvider, 1045 getSession, método da interface HttpSession, 1037 host de serviço Web, 1019 injeção de dependência, 1037 interface de ponto de extremidade de serviço (SEI), 1022

JAXB, classe, 1029

JAXB ( Java Architecture for XML Binding), 1029 JAX-RS, 1018 JAX-WS, 1018 Json, método da classe Gson, 1034 JSON ( JavaScript Object Notation), 1020 marshal, método da classe JAXB, 1030 MessageContext, interface, 1037 name, atributo da anotação @WebParam, 1022 name, atributo da anotação @WebService, 1022 openStream, método da classe URL, 1034 operationName, atributo da anotação @ WebMethod, 1022 @Path, anotação, 1029 @PathParam, anotação, 1029 POJO (Plain Old Java Object), 1021 @Produces, anotação, 1029 publicar um serviço Web, 1019, 1060 put, método da interface RequestContext, 1045 RequestContext, interface, 1045 @Resource, anotação, 1037 RESTful, serviços Web, 1020

REST (Representational State Transfer), 1018 SEI (Service Endpoint Interface), 1022 serviceName, atributo da anotação @ WebService, 1022 serviço Web, 1018 Serviço Web de Neutralidade Sexual, 1065 setAttribute, método da interface HttpSession, 1037 SOAP, formato wire, 1019 SOAP, mensagem, 1019 SOAP (Simple Object Access Protocol), 1018 StringWriter, classe, 1030 toJson, método da classe Gson, 1033 unmarshal, método da classe JAXB, 1031 WADL (Web Application Description Language), 1030 @WebMethod, anotação, 1022 Web Application, projeto, 1020 @WebParam, anotação, 1022 @WebService, anotação, 1021 WebServiceContext, interface, 1037 WSDL (Web Service Description Language), 1024

Exercícios de autorrevisão 31.1 Determine se cada um dos seguintes itens é verdadeiro ou falso. Se falso, explique por quê. a) Todos os métodos de uma classe de serviço Web podem ser invocados pelos clientes desse serviço Web. b) Ao consumir um serviço Web em um aplicativo cliente criado no NetBeans, você deve criar a classe proxy que permite ao cliente se comunicar com o serviço Web. c) Uma classe proxy que se comunica com um serviço Web normalmente utiliza o SOAP para enviar e receber mensagens. d) O rastreamento de sessão é automaticamente ativado em um cliente de um serviço Web. e) Métodos Web não podem ser declarados static. f) Um tipo definido pelo usuário utilizado em um serviço Web deve definir os métodos get e set para qualquer propriedade que será serializada. g) Operações em um serviço Web REST são definidas pelos seus próprios URL únicos. h) Um serviço Web baseado em SOAP pode retornar dados no formato JSON.

31.2 Preencha as lacunas de cada uma das seguintes afirmações: a) Uma diferença chave entre o SOAP e o REST é que os dados das mensagens SOAP são empacotados em um(a) ________. b) Um serviço Web no Java é um(a) ________ — ele não precisa implementar uma interface ou estender classes. c) Solicitações de serviço Web são geralmente transportadas pela Internet via protocolo ________. d) Para definir o nome exposto de um método Web, utilize o elemento ________ da anotação @WebMethod. e) ________ transforma um objeto em um formato que pode ser enviado entre um serviço Web e um cliente. f) Para retornar dados no formato JSON a partir de um método de um serviço Web baseado em REST, a anotação @Produces é configurada como ________. g) Para retornar dados no formato XML a partir de um método de um serviço Web baseado em REST a anotação @Produces é configurada como ________.

Respostas dos exercícios de autorrevisão 31.1

a) Falso. Somente os métodos declarados com a anotação @WebMethod podem ser invocados pelos clientes de um serviço Web. b) Falso. A classe proxy é criada pelo NetBeans quando você adiciona um cliente de serviço Web ao aplicativo. c) Verdadeiro. d) Falso. No framework JAX-WS, o cliente deve indicar se ele quer permitir que o serviço Web mantenha informações de sessão. Primeiro, você deve fazer coerção do objeto proxy no tipo de interface BindingProvider e então utilizar o método getRequestContext de BindingProvider para obter o objeto Request­ Context. Por fim, você deve utilizar o método put de RequestContext para configurar a propriedade BindingProvider.SESSION_MAIN­ TAIN_PROPERTY como true. e) Verdadeiro. f) Verdadeiro. g) Verdadeiro. h) Falso. Um serviço Web SOAP retorna implicitamente os dados no formato XML.



Exercícios

1065

31.2 a) Mensagem SOAP ou envelope SOAP. b) POJO (Plain Old Java Object) c) HTTP. d)  operationName. serialização e). f)  "application/json". g) "application/xml".

Exercícios 31.3 (Serviço Web de lista telefônica) Crie um serviço Web RESTful que armazena entradas de lista telefônica no banco de dados PhoneBookDB e um aplicativo cliente Web que consome esse serviço. O serviço Web deve gerar XML. Utilize os passos na Seção 30.2.1 para criar o banco de dados

PhoneBook utilizando o script PhoneBookDB.sql fornecido na pasta de exemplos. O banco de dados contém uma tabela — PhoneBook — com três colunas — LastName, FirstName e PhoneNumber. As colunas LastName armazenam até 30 caracteres. A coluna PhoneNumber suporta números de telefone da forma (800) 555-1212 que contêm 14 caracteres. Dê ao usuário cliente a capacidade de inserir um novo contato (método Web addEntry) e localizar contatos pelo sobrenome (método Web getEntries). Passe somente Strings como argumentos para o serviço Web. O método Web getEntries deve retornar um array Strings que contém as entradas de lista telefônica correspondentes. Cada String no array deve consistir no sobrenome, nome e número de telefone para uma

entrada de lista telefônica. Esses valores devem ser separados por vírgulas. A consulta SELECT que localizará uma entrada PhoneBook pelo sobrenome deve ser: SELECT LastName, FirstName, PhoneNumber FROM PhoneBook WHERE (LastName = Sobrenome)

A instrução INSERT que insere uma nova entrada no banco de dados PhoneBook deve ser: INSERT INTO PhoneBook (LastName, FirstName, PhoneNumber) VALUES (Sobrenome, Nome, NúmeroDoTelefone)

31.4 (Modificação do serviço Web de lista telefônica) Modifique o Exercício 31.3 para que ele utilize uma classe nomeada PhoneBookEntry a fim de representar uma linha no banco de dados. O serviço Web deve retornar os objetos do tipo PhoneBookEntry no formato XML para o método getEntries, e o aplicativo cliente deve utilizar o método JAXB unmarshal para recuperar objetos PhoneBookEntry.

31.5 (Serviço Web de lista telefônica com JSON) Modifique o Exercício 31.4 para que a classe PhoneBookEntry seja passada para e do serviço Web como um objeto JSON. Utilize a serialização para converter o objeto JSON em um objeto do tipo PhoneBookEntry.

31.6 (Modificação do serviço Web Blackjack) Modifique o exemplo do serviço Web Blackjack na Seção 31.9 para incluir a classe Card. Modifique o método Web dealCard para que ele retorne um objeto do tipo Card e modifique o método Web getHandValue para que ele receba um array de objetos Card do cliente. Também modifique o aplicativo cliente para monitorar as cartas distribuídas utilizando ArrayLists dos objetos Card. A classe proxy criada pelo NetBeans tratará o parâmetro de array de um método Web como uma List, assim você pode passar essas ArrayLists dos objetos Card diretamente para o método getHandValue. Sua classe Card deve incluir os métodos get e set para a face e o naipe da carta.

31.7 (Modificação do serviço Web de reservas de passagens aéreas) Modifique o serviço Web de reservas de passagens aéreas na Seção 31.10

para que ele contenha dois métodos separados — um que permite aos usuários visualizar todas as poltronas disponíveis, e o outro que permite aos usuários fazer a reserva de uma determinada poltrona atualmente disponível. Utilize um objeto do tipo Ticket para passar informações para e a partir do serviço Web. O serviço Web deve ser capaz de tratar casos em que dois usuários visualizam as poltronas disponíveis, um faz a reserva de uma poltrona e o segundo usuário tenta fazer a reserva da mesma poltrona, sem saber que ela já foi reservada. Os nomes dos métodos que executam devem ser reserve e getAllAvailableSeats.

31.8 (Serviço Web de código Morse) No Exercício 16.22, você aprendeu o código Morse e escreveu aplicativos que podem traduzir frases em inglês

para o código Morse e vice-versa. Crie um serviço Web baseado em SOAP que forneça dois métodos — um que traduz uma frase inglesa ao Código Morse e outro que traduz o código Morse para o inglês. Em seguida, construa um aplicativo GUI tradutor do código Morse que invoca o serviço Web para executar essas traduções.

Fazendo a diferença 31.9 (Serviço Web de scanner de spam) No Exercício 16.27, você criou um aplicativo scanner de spam que varria um e-mail e atribuía uma pontuação com base na ocorrência de certas palavras e frases que comumente aparecem em e-mails de spam e quantas vezes as palavras e frases ocorreram no e-mail. Crie um serviço Web de scanner de spam baseado em SOAP. Em seguida, modifique o aplicativo GUI que você criou no Exercício 16.27 para utilizar o serviço Web a fim de analisar um e-mail. Exiba então a avaliação em pontos retornada pelo serviço Web.

31.10 (Serviço Web de SMS) No Exercício 16.28, você criou um aplicativo tradutor de mensagens SMS. Crie um serviço Web baseado em SOAP com três métodos: a) um que recebe uma abreviação SMS e retorna a palavra inglesa ou frase correspondente, b) um que recebe uma mensagem SMS inteira e retorna o texto inglês correspondente, e c) um que traduz o texto inglês em uma mensagem SMS. Utilize o serviço Web a partir de um aplicativo GUI que exibe as respostas do serviço Web.

31.11 (Serviço Web de neutralidade sexual) No Exercício 1.14, você pesquisou como eliminar o machismo em todas as formas de comunicação. Você então descreveu o algoritmo que utilizaria para ler todo um parágrafo de texto e substituir palavras específicas por equivalentes neutras ao sexo. Crie um serviço Web baseado em SOAP que recebe um parágrafo de texto e então substitui palavras específicas ao sexo da pessoa por palavras neutras. Utilize o serviço Web a partir de um aplicativo GUI que exibe o texto neutro resultante.

A

Tabela de precedência de operadores

Os operadores são mostrados em ordem de precedência decrescente de cima para baixo (Figura A.1).

Operador

Descrição

Associatividade

++

incremento pós-fixo unário decremento pós-fixo unário

da direita para a esquerda

pré-incremento unário pré-decremento unário mais unário menos unário negação unária lógica complemento unário sobre bits coerção unária

da direita para a esquerda

multiplicação divisão módulo

da esquerda para a direita

adição ou concatenação de strings subtração

da esquerda para a direita

deslocamento de bits para a esquerda deslocamento para a direita com sinal deslocamento para a direita sem sinal

da esquerda para a direita

menor que menor que ou igual a o maior que maior que ou igual a comparação de tipo

da esquerda para a direita

é igual a não é igual a

da esquerda para a direita

&

E sobre bits E lógico booleano

da esquerda para a direita

^

OU exclusivo sobre bits OU lógico booleano exclusivo

da esquerda para a direita

­­ ++ ­­ + ­ ! ~ (

tipo

)

* / % + ­ > >>>
= instanceof == !=



Apêndice A  Tabela de precedência de operadores

Operador

Descrição

Associatividade

|

OU inclusivo sobre bits OU inclusivo lógico booleano

da esquerda para a direita

&&

E condicional

da esquerda para a direita

||

OU condicional

da esquerda para a direita

?:

condicional

da direita para a esquerda

=

da direita para a esquerda atribuição atribuição de adição atribuição de subtração atribuição de multiplicação atribuição de divisão atribuição de resto atribuição E sobre bits atribuição de OU exclusivo sobre bits atribuição de OU inclusivo sobre bits atribuição de deslocamento para a esquerda sobre bits atribuição de bits com deslocamento para a direita com sinal

+= -= *= /= %= &= ^= |= = >>>=

Figura A.1  |  Tabela de precedência de operadores.

1067

B 0

1

Conjunto de caracteres ASCII 2

3

4

5

6

7

8

9

0

nul

soh

stx

etx

eot

enq

ack

bel

bs

ht

1

nl

vt

ff

cr

so

si

dle

dc1

dc2

dc3

2

dc4

nak

syn

etb

can

em

sub

esc

fs

gs

3

rs

us

sp

!

"

#

$

%

&

'

4

(

)

*

+

,

­

.

/

0

1

5

2

3

4

5

6

7

8

9

:

;

6




?

@

A

B

C

D

E

7

F

G

H

I

J

K

L

M

N

O

8

P

Q

R

S

T

U

V

W

X

Y

9

Z

[

\

]

^

_



a

b

c

10

d

e

f

g

h

i

j

k

l

m

11

n

o

p

q

r

s

t

u

v

w

12

x

y

z

{

|

}

~

del

Figura B.1 | Conjunto de caracteres ASCII.

Os dígitos à esquerda da tabela são os dígitos à esquerda dos equivalentes decimais (0–127) dos códigos de caractere e os dígitos na parte superior da tabela são os dígitos à direita dos códigos de caractere. Por exemplo, o código de caractere para “F” é 70 e o código de caractere para “&” é 38. Portanto, cruzando linhas com colunas, você obtém os códigos de caractere para cada caractere na tabela. A maioria dos usuários deste livro está interessada no conjunto de caracteres ASCII utilizado para representar caracteres da língua inglesa em muitos computadores. O ASCII é um subconjunto do conjunto de caracteres Unicode utilizado pelo Java para representar caracteres da maioria dos idiomas do mundo. Para mais informações sobre o conjunto de caracteres Unicode, consulte o Apêndice L.

Palavras-chave e palavras reservadas

C

Palavras-chave do Java abstract

assert

boolean

break

byte

case

catch

char

class

continue

default

do

double

else

enum

extends

final

finally

float

for

if

implements

import

instanceof

int

interface

long

native

new

package

private

protected

public

return

short

static

strictfp

super

switch

synchronized

this

throw

throws

transient

try

void

volatile

while

Palavras-chave que não são atualmente utilizadas: const

goto

Figura C.1 | Palavras-chave do Java.

O Java também contém as palavras reservadas true e false, que são literais boolean e null, que é o literal que representa uma referência a nada. Assim como as palavras-chave, essas palavras reservadas não podem ser utilizadas como identificadores.

D Tipo

Tamanho em bits

Tipos primitivos

Valores

Padrão

true ou false

boolean

[Nota: a representação de um boolean é específica à Java Virtual Machine em cada plataforma.] char

16

'\u0000' a '\uFFFF' (0 a 65535)

byte

8

–128 a +127 (–27 a 27 – 1)

short

16

–32.768 a +32.767 (–215 a 215 – 1)

int

32

–2.147.483.648 a +2.147.483.647 (–231 a 231 – 1)

long

64

–9.223.372.036.854.775.808 a +9.223.372.036.854.775.807 (–263 a 263 – 1)

float

32

Intervalo negativo: –3,4028234663852886E+38 a –1,40129846432481707e–45

(conjunto de caracteres Unicode ISO)

(ponto flutuante IEEE 754)

Intervalo positivo: 1,40129846432481707e–45 a 3,4028234663852886E+38 double

64

Intervalo negativo: –1,7976931348623157E+308 a –4,94065645841246544e–324

(ponto flutuante IEEE 754)

Intervalo positivo: 4,94065645841246544e–324 a 1,7976931348623157E+308 Figura D.1 | Tipos primitivos do Java.

Para obter mais informações sobre o IEEE 754, visite grouper.ieee.org/groups/754/. Para informações adicionais sobre o Unicode, consulte o Apêndice L, “Unicode®”.

Usando a documentação da Java API

E

E.1 Introdução A biblioteca de classes Java contém milhares de classes e interfaces predefinidas que os programadores podem utilizar para escrever seus próprios aplicativos. Essas classes são agrupadas em pacotes com base nas suas funcionalidades. Por exemplo, as classes e interfaces utilizadas para processamento de arquivos estão agrupadas no pacote java.io e as classes e interfaces para aplicativos de rede estão agrupadas no pacote java.net. A documentação da Java API lista os membros public e protected de cada classe e os membros public de cada interface na biblioteca de classes Java. A documentação apresenta uma visão geral de todas as classes e interfaces, resume seus membros (isto é, os campos, construtores e métodos das classes e os campos e métodos das interfaces) e fornece descrições detalhadas sobre cada membro. A maioria dos programadores Java conta com essa documentação ao escrever programas. Normalmente, os programadores pesquisariam a API para encontrar o seguinte: 1. o pacote que contém uma classe ou interface particular; 2. os relacionamentos entre uma classe ou interface particular e outras classes e interfaces; 3. classe ou constantes de interface — normalmente declaradas como campos public static final; 4. construtores para determinar como um objeto da classe pode ser inicializado; 5. os métodos de uma classe para determinar se eles são static ou não static, os números e tipos dos argumentos que você precisa passar, os tipos de retorno e quaisquer exceções que poderiam ser lançadas a partir do método. Além disso, os programadores costumam recorrer à documentação para descobrir classes e interfaces que eles ainda não usaram. Por isso, demonstramos a documentação com classes que você conhece e com classes que talvez ainda não tenha estudado. Mostramos como usar a documentação para localizar as informações necessárias para usar uma classe ou interface eficientemente.

E.2 Navegando pela Java API Você pode fazer download da documentação da Java API para seu disco rígido local ou visualizá-la on-line. Para fazer o download da documentação da Java API, vá para java.sun.com/javase/downloads/ role até a seção Additional Resources e clique no botão Download à direita de Java SE 6 Documentation. Você será solicitado a aceitar um contrato de licença. Para fazer isso, clique em Accept e então clique em Continue. Marque a caixa de seleção ao lado de Java SE Development Kit Documentation 6 e então clique no link jdk­6u10­docs.zip logo abaixo da caixa de seleção. Depois de baixar o arquivo compactado, você pode usar um programa como o WinZip (www.winzip.com) para extrair os arquivos. Se estiver utilizando Windows, extraia o conteúdo do diretório de instalação do seu JDK. Para visualizar a documentação da API no seu disco rígido local no Microsoft Windows, abra a página C:\Program Files\Java\SuaVersãoDoJDK\docs\api\index. html no seu navegador. Para examinar a documentação de API on-line, vá para java.sun.com/javase/6/docs/api/index.html (Figura E.1).

Frames na página index.html da documentação da API A documentação da API está dividida em três frames (veja a Figura E.1). O frame superior esquerdo lista todos os pacotes da Java API em ordem alfabética. O frame inferior esquerdo lista as classes e interfaces da Java API em ordem alfabética. Nomes de interfaces são exibidos

1072

Apêndice E  Usando a documentação da Java API

em itálico. Ao clicar em um pacote específico no frame superior esquerdo, o frame esquerdo inferior lista as classes e interfaces do pacote selecionado. O frame direito fornece inicialmente uma breve descrição de cada pacote da especificação da Java API — leia essa visão geral para conhecer as capacidades gerais das Java APIs. Se selecionar uma classe ou interface no frame esquerdo inferior, o frame direito exibe informações sobre essa classe ou interface. O frame do canto superior esquerdo lista todos os pacotes em ordem alfabética.

O link Tree exibe O link Deprecated a hierarquia de todos lista partes da API os pacotes e que não devem classes. mais ser utilizadas.

O link Index lista campos, métodos, classes e interfaces.

O link Help descreve como a API é organizada.

O frame do canto inferior esquerdo lista O frame direito oferece uma visão geral da especificação da API e contém descrições todas as classes e interfaces em ordem alfabética. de cada pacote. Quando você seleciona uma classe particular ou interface no As interfaces são exibidas em itálico. no frame esquerdo inferior, as informações são exibidas aqui.

Figura E.1  |  Visão geral da Java API. (Cortesia da Sun Microsystems, Inc.)

Links importantes na página index.html Na parte superior do frame direito (Figura E.1), há quatro links — Tree, Deprecated, Index e Help. O link Tree exibe a hierarquia de todos os pacotes, classes e interfaces em uma estrutura de árvore. O link Deprecated exibe interfaces, classes, exceções, campos, construtores e métodos que não devem ser mais utilizados. O link Index exibe classes, interfaces, campos, construtores e métodos em ordem alfabética. O link Help descreve como a documentação da API é organizada. Você provavelmente deve iniciar lendo a página Help. Visualizando a página Index Se não souber o nome da classe que você está procurando, mas souber o nome de um método ou campo, poderá utilizar o índice da documentação para localizar a classe. O link Index está localizado próximo ao canto superior direito do frame direito. A página de índice (Figura E.2) exibe campos, construtores, métodos, interfaces e classes em ordem alfabética. Por exemplo, se estiver procurando o método As classes, interfaces e seus membros são listados em ordem alfabética. Clique em uma letra para visualizar todos os campos, construtores, métodos, interfaces e classes que começam com essa letra.

Figura E.2  |  Visualizando a página Index. (Cortesia da Sun Microsystems, Inc.)

Clique no link Index para exibir o índice da documentação.



E.2  Navegando pela Java API

1073

hasNextInt de Scanner e não conhecer o nome da classe, poderá clicar no link H a fim de ir para a listagem alfabética de todos os itens na Java API que começam com "h". Role até o método hasNextInt (Figura E.3). Depois que estiver aí, cada método chamado hasNextInt é listado com o nome do pacote e classe ao qual o método pertence. A partir daí, você pode clicar no nome da classe para visualizar os detalhes completos da classe ou pode clicar no nome do método para visualizar os detalhes desse método. Clique no nome do método para visualizar os detalhes do método.

Clique no nome de classe para visualizar os detalhes completos da classe.

Figura E.3  |  Role até o método hasNextInt. (Cortesia da Sun Microsystems, Inc.)

Visualizando um pacote específico Quando você clicar no nome de pacote no frame superior esquerdo, todas as classes e interfaces desse pacote são exibidas no frame inferior esquerdo e são divididas em cinco subseções — Interfaces, Classes, Enums, Exceptions e Errors — cada um listado alfabeticamente. Por exemplo, os conteúdos do pacote javax.swing são exibidos no frame inferior esquerdo (Figura E.4) quando você clica em javax.swing no frame superior esquerdo. Você pode clicar no nome de pacote no frame esquerdo inferior para obter uma visão geral do pacote. Se achar que um pacote contém várias classes que poderiam ser úteis no seu aplicativo, a visão geral do pacote poderá ser especialmente útil. Clique em um nome de pacote no frame Clique no nome do pacote no frame superior esquerdo para visualizar todas inferior esquerdo para exibir um resumo O conteúdo do pacote javax.swing as classes e interfaces definidas no pacote. desse pacote no frame direito. é exibido no frame inferior esquerdo.

Figura E.4  |  Clicar em um nome de pacote no frame superior esquerdo para visualizar todas as classes e interfaces declaradas nesse pacote. (Cortesia da Sun Microsystems, Inc.)

Visualizando os detalhes de uma classe Ao clicar em um nome de classe ou em um nome de interface no frame esquerdo inferior, o frame direito exibe os detalhes dessa classe ou interface. Primeiro, você verá o nome do pacote da classe seguido por uma hierarquia que mostra o relacionamento da classe com outras classes. Você também verá uma lista das interfaces implementadas pela classe e as subclasses conhecidas dessa classe. A Figura E.5 mostra o começo da página de documentação para a classe JButton do pacote javax.swing. A página primeiro mostra o nome do pacote em que a classe aparece. Isso é seguido pela hierarquia de classes que leva à classe JButton, as interfaces que a classe JButton implementa e as subclasses da classe JButton. A parte inferior do frame direito mostra o começo da descrição da classe JButton. Observe que quando você vê a documentação de uma interface, o frame direito não exibe uma hierarquia dessa interface. Em vez disso, o frame direito lista as superinterfaces da interface, subinterfaces conhecidas e classes de implementação conhecidas.

1074

Apêndice E  Usando a documentação da Java API Clique no nome da classe para visualizar informações detalhadas sobre a classe.

Informações detalhadas sobre a classe são exibidas no frame direito.

Hierarquia da classe JButton

Interfaces implementadas pela classe JButton Subclasses JButton Descrição da classe JButton

Clique no link para carregar a página que contém um tutorial sobre como utilizar botões. [Nota: A maioria das classes não tem links para tutoriais.]

Figura E.5  |  Clicando no nome de uma classe para visualizar informações detalhadas sobre a classe. (Cortesia da Sun Microsystems, Inc.)

Seções de resumo na página de documentação de uma classe Outras partes de cada página da API estão listadas a seguir. Cada parte só é apresentada se a classe contiver ou herdar os itens especificados. Os membros de classe mostrados nas seções de resumo são public a menos que eles sejam explicitamente marcados como protected. Os membros private de uma classe não são mostrados na documentação, uma vez que eles não podem ser utilizados diretamente nos seus programas. 1. A seção Nested Class Summary resume as classes aninhadas public e protected da classe — isto é, classes definidas dentro da classe. A menos que explicitamente especificado, essas classes são public e não static. 2. A seção Field Summary resume os campos public e protected da classe. A menos que explicitamente especificado, esses campos são public e não static. A Figura E.6 mostra a seção Field Summary da classe Color.

Seção Field Summary da classe Color Clique no nome do campo para ir para a seção Field Detail, que fornece informações adicionais sobre o campo.

Clique no tipo de campo para ir para sua página. Se o campo tiver o mesmo tipo que sua classe, clicar nele fará você retornar à parte superior da página atual.

Figura E.6  |  Seção Field Summary da classe Color. (Cortesia da Sun Microsystems, Inc.) 3. A seção Constructor Summary resume os construtores da classe. Estes não são herdados, portanto essa seção só aparece na documentação

para uma classe se a classe declarar um ou mais construtores. A Figura E.7 mostra a seção Constructor Summary da classe JButton.



E.2  Navegando pela Java API

1075

Seção Constructor Summary

Clique no tipo de parâmetro para carregar sua classe. Clique no nome do construtor para ir para a seção Constructor Detail, que fornece informações adicionais sobre o construtor.

Figura E.7  |  Seção Constructor Summary da classe JButton. (Cortesia da Sun Microsystems, Inc.) 4. A seção Method Summary resume os métodos public e protected da classe. A menos que explicitamente especificado, esses méto-

dos são public e não static. A Figura E.8 mostra a seção Method Summary da classe BufferedInputStream. Observe que, em geral, as seções de resumo fornecem apenas uma descrição de uma frase do membro de uma classe. Detalhes adicionais são apresentados nas seções de detalhes discutidas a seguir.

Seção Method Summary

Clique no nome do método para ir para a seção Method Detail, que fornece informações adicionais sobre esse método.

Figura E.8  |  Seção Method Summary da classe BufferedInputStream. (Cortesia da Sun Microsystems, Inc.)

Seções de detalhes na página de Documentação de uma classe Depois das seções de resumo, há seções de detalhes que normalmente fornecem mais discussão sobre membros particulares de classe. Não há uma seção de detalhes para classes aninhadas. Quando você clica no link no Nested Class Summary para uma classe aninhada em particular, é exibida uma página de documentação descrevendo essa classe aninhada. As seções de detalhes são descritas a seguir. 1 . A seção Field Detail fornece a declaração de cada campo. Ela também discute cada campo, incluindo os modificadores e significado do campo. A Figura E.9 mostra a seção Field Detail da classe Color.

A seção Field Detail descreve o propósito de cada campo.

Figura E.9  |  Seção Field Detail da classe Color. (Cortesia da Sun Microsystems, Inc.)

1076

Apêndice E  Usando a documentação da Java API

2. A seção Constructor Detail fornece a primeira linha da declaração de cada construtor e discute os construtores. A discussão inclui os modificadores de cada construtor, uma descrição de cada construtor, os parâmetros de cada construtor e quaisquer exceções lançadas por cada construtor. A Figura E.10 mostra a seção Constructor Detail da classe JButton.

A seção Constructor Detail descreve cada construtor.

Figura E.10  |  Seção Constructor Detail da classe JButton. (Cortesia da Sun Microsystems, Inc.)

3. A seção Method Detail fornece a primeira linha de cada método. A discussão de cada método inclui seus modificadores, uma descrição mais completa do método, os parâmetros do método, o tipo de retorno do método e quaisquer exceções lançadas pelo método. A Figura E.11 mostra a seção Method Detail da classe BufferedInputStream. O método read lança IOException. Clique em IOException para carregar a página de informações da classe IOException e aprender mais sobre o tipo de exceção (por exemplo, por que tal exceção poderia ser lançada).

Seção Method Detail

O método read sobrescreve o método read em FilterInputStream. Clique no nome do método sobrescrito para visualizar informações detalhadas sobre a versão da superclasse desse método.

Figura E.11  |  Seção Method Detail da classe BufferedInputStream. (Cortesia da Sun Microsystems, Inc.)

Os detalhes de método mostram outros métodos que talvez sejam de interesse (rotulados como See Also). Se o método sobrescrever um método da superclasse, o nome do método da superclasse e o nome da superclasse serão fornecidos para que você possa vincular-se ao método ou superclasse para informações adicionais. À medida que examina a documentação, você notará que há links para outros campos, métodos, classes aninhadas e classes de primeiro nível. Esses links permitem pular da classe que você está examinando para outra parte relevante da documentação.

Fomos criados para cometer equívocos, programados para erro. — Lewis Thomas

O que antecipamos raramente ocorre; o que menos esperamos geralmente acontece. — Benjamin Disraeli

Ele pode correr, mas não pode se esconder. — Joe Louis

Uma coisa é mostrar a um homem que ele está errado e, uma outra, é colocá-lo de posse da verdade. — John Locke

F

Utilizando o depurador

Objetivos Neste apêndice, você aprenderá: 

A configurar pontos de interrupção para depurar aplicativos.



A utilizar o comando run para executar um aplicativo por meio do depurador.



A utilizar o comando stop para configurar um ponto de interrupção.



A utilizar o comando cont para continuar a execução.



A utilizar o comando print para avaliar expressões.



A utilizar o comando set para alterar valores de variáveis durante a execução do programa.



A utilizar os comandos step, step up e next para controlar a execução.



A utilizar o comando watch para ver como um campo é modificado durante a execução do programa.



A utilizar o comando clear para listar pontos de interrupção ou remover um ponto de interrupção.

1078

Apêndice F

Utilizando o depurador

F.1 Introdução

Sumário

F.2 Pontos de interrupção e os comandos run, stop, cont e print F.3 Os comandos print e set F.4 Controlando a execução utilizando os comandos step, step up e next F.5 O comando watch F.6 O comando clear F.7 Resumo Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão

F.1 Introdução No Capítulo 2, você aprendeu que há dois tipos de erros — erros de sintaxe e erros de lógica — e aprendeu a eliminar erros de sintaxe do seu código. Erros de lógica não impedem que o aplicativo compile com sucesso, mas fazem com que ele produza resultados errôneos quando executado. O JDK inclui um software chamado depurador que permite monitorar a execução dos seus aplicativos para que você possa localizar e remover erros de lógica. O depurador será uma das suas ferramentas mais importantes de desenvolvimento de aplicativos. Muitos IDEs fornecem depuradores próprios semelhantes àquele incluído no JDK ou fornecem uma interface gráfica com o usuário para o depurador do JDK. Este apêndice demonstra os recursos-chave do depurador do JDK que utilizam aplicativos de linha de comando e não recebem nenhuma entrada do usuário. Os mesmos recursos de depurador discutidos aqui podem ser utilizados para depurar aplicativos que recebem a entrada do usuário, mas depurar esses aplicativos requer uma configuração um pouco mais complexa. Para focalizar os recursos do depurador, optamos por demonstrá-lo com aplicativos de linha de comando simples que não envolvem nenhuma entrada do usuário. Você também pode localizar mais informações sobre o depurador de Java em java.sun.com/javase/6/docs/technotes/tools/windows/jdb.html.

F.2 Pontos de interrupção e os comandos run, stop, cont e print Iniciamos nosso estudo do depurador investigando os pontos de interrupção, que são marcadores que podem ser configurados em qualquer linha executável do código. Quando a execução do aplicativo alcança um ponto de interrupção, a execução pausa, permitindo que você examine os valores das variáveis para ajudar a determinar se há erros de lógica. Por exemplo, você pode examinar o valor de uma variável que armazena o resultado de um cálculo a fim de determinar se o cálculo foi realizado corretamente. Observe que configurar um ponto de interrupção em uma linha de código que não é executável (como um comentário) faz com que o depurador exiba uma mensagem de erro. Para ilustrar os recursos do depurador, utilizaremos o aplicativo AccountTest (Figura F.1), que cria e manipula um objeto da classe Account (Figura 3.13). A execução de AccountTest inicia em main (linhas 7–24). A linha 9 cria um objeto Account com um saldo inicial de US$ 50.00. Lembre-se de que o construtor de Account aceita um argumento, que especifica o balance (saldo) inicial de Account. As linhas 12–13 geram a saída do saldo inicial na conta utilizando o método getBalance de Account. A linha 15 declara e inicializa uma variável local depositAmount. As linhas 17–19 então imprimem depositAmount e o adicionam ao balance de Account utilizando seu método credit. Por fim, as linhas 22–23 exibem o novo balance. [Nota: O diretório de exemplos do Apêndice F contém uma cópia de Account.java idêntica àquela na Figura 3.13.] 1 2 3 4 5 6 7 8 9 10 11 12 13

// Figura F.1: AccountTest.Java // Cria e manipula um objeto Account. public class AccountTest { // método main inicia a execução public static void main( String[] args ) { Account account = new Account( 50.00 ); // cria o objeto Account // exibe o saldo inicial do objeto Account System.out.printf( "initial account balance: $%.2f\n", account.getBalance() );



F.2  Pontos de interrupção e os comandos run, stop, cont e print

14 15 16 17 18 19 20 21 22 23 24 25 26

1079

double depositAmount = 25.0; // deposita uma quantia System.out.printf( "\nadding %.2f to account balance\n\n", depositAmount ); account.credit( depositAmount ); // adiciona ao saldo da conta // exibe um novo saldo System.out.printf( "new account balance: $%.2f\n", account.getBalance() ); } // fim de main } // fim da classe AccountTest

initial account balance: $50.00 adding 25.00 to account balance new account balance: $75.00

Figura F.1  |  A classe AccountTest cria e manipula um objeto Account.

Nos passos a seguir, utilize pontos de interrupção e vários comandos de depurador para examinar o valor da variável depositAmount declarada em AccountTest (Figura F.1) 1 . Abrindo a janela Prompt do MS-DOS e mudando de diretório. Abra a janela Command Prompt selecionando Start> Programs> Accessories> Command Prompt. Mude para o diretório contendo os exemplos do Apêndice F digitando cd  C:\examples\debugger [Nota: Se seus exemplos estiverem em um diretório diferente, utilize esse diretório aqui]. 2 . Compilando o aplicativo para depuração. O depurador Java só funciona com os arquivos .class compilados com a opção de compilador -g, que gera informações utilizadas pelo depurador para ajudar a depurar seus aplicativos. Compile o aplicativo com a opção de linha de comando -g digitando javac -g AccountTest.java Account.java. A partir do Capítulo 3, lembre-se de que esse comando compila tanto AccountTest.java como Account.java. O comando java -g *.java compila todos os arquivos .java do diretório de trabalho para depuração. 3 . Iniciando o depurador. No Prompt de Comando, digite jdb (Figura F.2). Esse comando iniciará o depurador Java e permitirá que você utilize os seus recursos. [Nota: modificamos as cores da nossa janela Command Prompt por questão de legibilidade.]

Figura F.2  |  Iniciando o depurador Java.

4. Executando um aplicativo no depurador. Execute o aplicativo AccountTest por meio do depurador digitando run Account­ Test (Figura F.3). Se você não configurar pontos de interrupção antes de executar seu aplicativo no depurador, o aplicativo executará como faria utilizando o comando java.

Figura F.3  |  Executando o aplicativo AccountTest por meio do depurador.

1080

Apêndice F  Utilizando o depurador

5. Reiniciando o depurador. Para utilização adequada do depurador, você deve configurar pelo menos um ponto de interrupção antes de executar o aplicativo. Reinicie o depurador digitando jdb. 6. Inserindo pontos de interrupção em Java. Você configura um ponto de interrupção em uma linha específica do código no seu aplicativo. Os números das linhas utilizados nestes passos são provenientes do código-fonte na Figura F.1. Configure um ponto de interrupção na linha 12 do código-fonte digitando stop at AccountTest:12 (Figura F.4). O comando stop insere um ponto de interrupção no número da linha especificada depois do comando. Você pode configurar quantos pontos de interrupção forem necessários. Configure outro ponto de interrupção na linha 19 digitando stop at AccountTest:19 (Figura F.4). Quando o aplicativo executa, ele interrompe a execução em qualquer linha que contém um ponto de interrupção. Diz-se que o aplicativo está no modo de interrupção (break mode) quando o depurador pausa a execução do aplicativo. Pontos de interrupção podem ser até mesmo configurados depois de o processo de depuração ter iniciado. Observe que o comando de depurador stop in, seguido por um nome de classe, um ponto e um nome de método (por exemplo, stop in Account.credit) instrui o depurador a configurar um ponto de interrupção na primeira instrução executável do método especificado. O depurador pausa a execução quando o controle do programa entra no método.

Figura F.4  |  Configurando pontos de interrupção nas linhas 12 e 19.

7. Executando o aplicativo e começando o processo de depuração. Digite run AccountTest para executar o aplicativo e começar o processo de depuração (Figura F.5) O depurador imprime texto indicando que os pontos de interrupção foram configurados nas linhas 12 e 19. Ele identifica cada ponto de interrupção como um “deferred breakpoint” (“ponto de interrupção adiado”), pois cada um foi configurado antes de o aplicativo iniciar a execução no depurador. O aplicativo pausa quando a execução alcança o ponto de interrupção na linha 12. Nesse ponto, o depurador o notifica de que um ponto de interrupção foi alcançado e exibe o código-fonte nessa linha (12). Essa linha do código é a próxima instrução que será executada.

Próxima linha de código a executar

O ponto de interrupção é alcançado

Figura F.5  |  Reiniciando o aplicativo AccountTest.

8. Utilizando o comando cont para resumir a execução. Digite cont. O comando cont faz com que o aplicativo continue a execução até o próximo ponto de interrupção ser alcançado (linha 19), quando o depurador o notifica (Figura F.6). Observe que a saída normal de AccountTest aparece entre as mensagens no depurador. Outro ponto de interrupção é alcançado

Figura F.6  |  A execução alcança o segundo ponto de interrupção.

F.3 Os comandos print e set

1081

9. Examinando o valor de uma variável. Digite

print depositAmount para exibir o valor atual armazenado na variável (Figura F.7). O comando print permite examinar dentro do computador o valor de uma das suas variáveis. Esse comando lhe ajudará a encontrar e eliminar erros de lógica no seu código. Observe que o valor exibido é 25.0 — o valor atribuído a deposit­Amount na linha 15 da Figura F.1. depositAmount

Figura F.7 | Examinando o valor da variável depositAmount. 10. Continuando a execução do aplicativo. Digite cont para continuar a execução do aplicativo. Não há nenhum ponto de inter-

rupção, portanto o aplicativo não está mais no modo de interrupção. O aplicativo continua a execução e consequentemente termina (Figura F.8). O depurador irá parar quando o aplicativo terminar.

Figura F.8 | Continuando a execução do aplicativo e encerrando o depurador.

Nesta seção, você aprendeu a ativar o depurador e a configurar pontos de interrupção para examinar variáveis com o comando print enquanto um aplicativo está em execução. Você também aprendeu a utilizar o comando cont para continuar a execução depois que um ponto de interrupção é alcançado.

F.3 Os comandos print e set Na seção anterior, você aprendeu a utilizar o comando print do depurador para examinar o valor de uma variável durante a execução do programa. Nesta seção, você aprenderá a utilizar o comando print para examinar o valor de expressões mais complexas. Você também aprenderá como utilizar o comando set, que permite ao programador atribuir novos valores a variáveis. Para esta seção, supomos que você seguiu os Passos 1 e 2, da Seção F.2, para abrir a janela Command Prompt, mudar para o diretório que contém os exemplos deste apêndice (por exemplo, C:\examples\debugger) e compilar o aplicativo AccountTest (e a classe Account) para depuração. 1. Iniciando a depuração. No Command Prompt, digite jdb para iniciar o depurador de Java. 2. Inserindo um ponto de interrupção. Configure um ponto de interrupção na linha 19 no código-fonte digitando stop at AccountTest:19. 3. Executando o aplicativo e alcançando um ponto de interrupção. Digite run AccountTest para começar o processo de depuração (Figura F.9). Isso fará com que o método main de AccountTest seja executado até o ponto de interrupção na linha 19 ser alcançado. Isso suspende a execução do aplicativo e alterna o aplicativo para o modo de interrupção. Nesse ponto, as instruções nas linhas 9–13 criaram um objeto Account e imprimiram o saldo inicial da Account obtido chamando o método getBalance. A instrução na linha 15 (Figura F.1) declarou e inicializou a variável local depositAmount como 25.0. A instrução na linha 19 é a próxima instrução que será executada.

Figura F.9 | A execução do aplicativo é suspensa quando o depurador alcança o ponto de interrupção na linha 19.

1082

Apêndice F

Utilizando o depurador

4. Avaliando expressões aritméticas e booleanas. A partir da Seção F.2, lembre-se de que depois que o aplicativo entra no modo de

interrupção, você pode explorar os valores das variáveis do aplicativo usando o comando print do depurador. Você também pode usar o comando print para avaliar expressões aritméticas e booleanas. Na janela Command Prompt, digite print depositAmount ­ 2.0. Observe que o comando print retorna o valor 23.0 (Figura F.10). Mas esse comando na verdade não muda o valor de depositAmount. Na janela Command Prompt, digite print depositAmount == 23.0. As expressões que contêm o símbolo == são tratadas como expressões boolean. O valor retornado é false (Figura. O f 10) porque depositAmount não contém atualmente o valor 23.0 — depositAmount ainda é 25.0.

Figura F.10 | Examinando os valores de uma expressão aritmética e booleana. 5. Modificando valores. O depurador permite alterar os valores das variáveis durante a execução do aplicativo. Isso pode ser valioso

para experimentar diferentes valores e localizar erros de lógica nos aplicativos. Você pode utilizar o comando set do depurador para alterar o valor de uma variável. Digite set depositAmount = 75.0. O depurador altera o valor de depositAmount e exibe seu novo valor (Figura F.11)

Figura F.11 | Modificando valores. 6. Visualizando o resultado do aplicativo. Digite cont para continuar a execução do aplicativo. A linha 19 de AccountTest (Figu-

ra F.1) executa, passando depositAmount para o método credit de Account. O método main então exibe o novo saldo. Observe que o resultado é $125.00 (Figura F.12) Isso mostra que o passo anterior alterou o valor de depositAmount a partir do seu valor inicial (25.0) para 75.0.

Novo saldo da conta com base no valor alterado da variável depositAmount

Figura F.12 | Saída exibida depois do processo de depuração.

Nesta seção, você aprendeu a utilizar o comando print do depurador para avaliar expressões aritméticas e boolean. Você também aprendeu a utilizar o comando set para modificar o valor de uma variável durante a execução do seu aplicativo.

F.4 Controlando a execução utilizando os comandos step, step up e next Às vezes, será necessário executar um aplicativo linha por linha para encontrar e corrigir erros. Investigar uma parte do seu aplicativo desta maneira pode ajudá-lo a verificar se o código de um método executa corretamente. Nesta seção, você aprenderá a utilizar o depurador para essa tarefa. Os comandos que você aprenderá nesta seção permitem executar um método linha por linha, executar todas as instruções de um método de uma vez ou executar apenas as instruções remanescentes de um método (se você já tiver executado algumas instruções dentro do método). Mais uma vez, supomos que você esteja trabalhando no diretório que contém os exemplos deste apêndice e que tenha compilado para depuração com a opção de compilador ­g.



F.4  Controlando a execução utilizando os comandos step, step up e next

1083

1. Iniciando o depurador. Inicie o depurador digitando jdb. 2. Configurando um ponto de interrupção. Digite stop at AccountTest:19 para configurar um ponto de interrupção na linha 19. 3. Executando o aplicativo. Execute o aplicativo digitando run AccountTest. Depois de o aplicativo exibir as duas mensagens de saída, o depurador indica que o ponto de interrupção foi alcançado e exibe o código na linha 19 (Figura F.13). O depurador e o aplicativo então pausam e esperam o próximo comando a ser inserido.

Figura F.13  |  Alcançando o ponto de interrupção no aplicativo AccountTest.

4. Usando o comando step. O comando step executa a próxima instrução no aplicativo. Se a próxima instrução a executar for uma chamada de método, o controle será transferido para o método chamado. O comando step permite que você entre em um método e estude as instruções individuais desse método. Por exemplo, você pode utilizar os comandos print e set para visualizar e modificar as variáveis dentro do método. Você utilizará agora o comando step para entrar no método credit da classe Account (Figura 3.13) digitando step (Figura F.14). O depurador indica que o passo foi completado e exibe a próxima instrução executável — nesse caso, a linha 21 da classe Account (Figura 3.13).

Figura F.14  |  Entrando na inspeção (stepping into) do método credit.

5. Utilizando o comando step up. Depois de entrar no método credit com Step Into, digite step up. Esse comando executa as instruções restantes no método e retorna o controle ao local em que o método foi chamado. O método credit contém apenas uma instrução para adicionar o parâmetro amount do método à variável de instância balance. O comando step up executa essa instrução e então pausa antes da linha 22 em AccountTest. Portanto, a próxima ação a ocorrer será imprimir o novo saldo da conta (Figura F.15) Em métodos longos, é recomendável examinar algumas linhas-chave do código e, então, continuar a depurar o código do chamador. O comando step up é útil para situações em que você não quer continuar a investigar todo o método linha por linha. 6. Utilizando o comando cont para continuar a execução. Insira o comando cont (Figura F.16) para continuar a execução. A instrução nas linhas 22–23 é executada, exibindo o novo saldo e então o aplicativo e o depurador terminam.

Figura F.15  |  Saindo da inspeção (stepping out) de um método.

1084

Apêndice F  Utilizando o depurador

Figura F.16  |  Continuando a execução do aplicativo AccountTest. 7 . Reiniciando o depurador. Reinicie o depurador digitando jdb. 8. Configurando um ponto de interrupção. Os pontos de interrupção só persistem até o fim da sessão de depuração em que eles são configurados — depois de o depurador encerrar, todos os pontos de interrupção são removidos. (Na Seção F.6, você aprenderá a remover um ponto de interrupção manualmente antes do fim da sessão de depuração). Portanto, o ponto de interrupção configurado para a linha 19 no Passo 2 não mais existirá depois da reinicialização do depurador no Passo 7. Para redefinir o ponto de interrupção na linha 19, digite mais uma vez stop at AccountTest:19. 9 . Executando o aplicativo. Digite run AccountTest para executar o aplicativo. Como no Passo 3, AccountTest é executado até o ponto de interrupção na linha 19 ser alcançado, o depurador então pausa e espera o próximo comando (Figura F.17)

Figura F.17  |  Alcançando o ponto de interrupção no aplicativo AccountTest.

10. Utilizando o comando next. Digite next. Esse comando se comporta como o comando step, exceto quando a próxima instrução a executar contém uma chamada de método. Nesse caso, o método chamado é executado na sua totalidade e o aplicativo avança para a próxima linha executável depois da chamada de método (Figura F.18) A partir do que foi discutido no Passo 4, lembre-se de que o comando step entraria no método chamado. Neste exemplo, o comando next faz com que método Account credit execute e então o depurador pausa na linha 22 em AccountTest.

Figura F.18  |  Inspeção (stepping over) de uma chamada de método.

11. Utilizando o comando exit. Utilize o comando exit para encerrar a sessão de depuração (Figura F.19). Esse comando faz com que o aplicativo AccountTest termine imediatamente em vez de executar as instruções restantes em main. Observe que ao depurar alguns tipos de aplicativos (por exemplo, aplicativos GUI), o aplicativo continuará a executar mesmo depois de a sessão de depuração terminar.

Figura F.19  |  Encerrando o depurador.

F.5 O comando watch

1085

Nesta seção, você aprendeu a utilizar os comandos step e step up do depurador para depurar métodos chamados durante a execução do seu aplicativo. Viu como o comando next pode ser utilizado para inspecionar uma chamada de método. Também aprendeu que o comando exit encerra uma sessão de depuração.

F.5 O comando watch Nesta seção, apresentamos o comando watch, que instrui o depurador a monitorar um campo. Quando esse campo está em vias de ser alterado, o depurador o notificará. Aprenderá também a utilizar o comando watch para ver como o campo balance do objeto Account é modificado durante a execução do aplicativo AccountTest. Como ocorreu nas duas seções anteriores, supomos que você seguiu o Passo 1 e o Passo 2 na Seção F.2 para abrir a Command Prompt, mudar para o diretório de exemplos correto e compilar as classes AccountTest e Account para depuração (isto é, com a opção de compilador ­g). 1. Iniciando o depurador. Inicie o depurador digitando jdb. 2. Monitorando o campo de uma classe. Configure um ponto de observação no campo balance digitando watch Account. balance (Figura F.20). Você pode configurar um watch em qualquer campo durante a execução do depurador. Sempre que o valor em um campo está em vias de mudar, o depurador entra no modo de interrupção e o notifica de que o valor irá mudar. Os pontos de monitoração só podem ser colocados em campos, não em variáveis locais.

Figura F.20 | Configurando um watch no campo balance de Account. 3. Executando o aplicativo. Execute o aplicativo com o comando run AccountTest. O depurador agora o notificará que o valor do

campo balance irá mudar (Figura F.21) Quando o aplicativo é inicializado, uma instância de Account é criada com um saldo inicial de US$ 50.00 e uma referência ao objeto Account é atribuído à variável local account (linha 9, Figura F.1). Com base na Figura 3.13, lembre-se de que quando o construtor desse objeto é executado, se o parâmetro initialBalance for maior que 0.0, a variável de instância balance será atribuída ao valor do parâmetro initialBalance. O depurador o notifica de que o valor de balance será configurado como 50.0.

Figura F.21 | O aplicativo AccountTest para quando account é criado e seu campo balance será modificado. 4. Adicionando dinheiro à conta. Digite cont para continuar a executar o aplicativo. O aplicativo executa normalmente até alcançar o

código na linha 19 da Figura F.1, que chama o método credit de Account para aumentar o balance do objeto Account por um amount especificado. O depurador o notifica de que a variável de instância balance irá mudar (Figura F.22) Observe que, embora a linha 19 da classe AccountTest chame o método credit, é a linha 21 do método credit de Account que na verdade altera o valor de balance.

Figura F.22 | Altere o valor de balance chamando o método Account credit.

1086

Apêndice F

Utilizando o depurador

5. Continuando a execução. Digite cont — o aplicativo terminará a execução porque o aplicativo não tenta nenhuma alteração

adicional em balance (Figura F.23).

Figura F.23 | Continuando a execução de AccountTest. 6. Reiniciando o depurador e redefinindo o ponto de monitoração sobre a variável. Digite jdb para reiniciar o depurador.

Uma vez mais, configure um comando watch na variável de instância Account.balance digitando watch Account.balance e, então, digite run AccountTest para executar o aplicativo (Figura F.24).

Figura F.24 | Reiniciando o depurador e redefinindo o ponto de monitoração na variável balance. 7. Removendo o ponto de monitoração no campo. Suponha que em um campo você queira monitorar somente uma parte da exe-

cução de um programa. É possível remover o ponto de monitoração do depurador na variável balance digitando unwatch Account. balance (Figura F.25). Digite cont — o aplicativo terminará a execução sem reentrar no modo de interrupção.

Figura F.25 | Removendo o ponto de monitoração na variável balance. 8. Fechando a janela Prompt do MS-DOS. Feche a janela Command Prompt clicando no botão de fechar.

Nesta seção, você aprendeu a utilizar o comando watch para ativar o depurador a fim de notificá-lo sobre alterações no valor de um campo por todo o ciclo de vida de um aplicativo. Também aprendeu a utilizar o comando unwatch para remover um watch em um campo antes do fim do aplicativo.

F.6 O comando clear Na seção anterior, você aprendeu a utilizar o comando unwatch para remover um watch em um campo. O depurador também fornece o comando clear para remover um ponto de interrupção de um aplicativo. Frequentemente, precisará depurar aplicativos que contêm ações repetitivas, como um loop. Talvez você queira examinar os valores das variáveis durante várias, mas possivelmente não todas, iterações do loop. Se configurar um ponto de interrupção no corpo de um loop, o depurador pausará antes de cada execução da linha que contém um ponto de interrupção. Depois de determinar que o loop está funcionando adequadamente, talvez queira remover o ponto de interrupção e permitir que as iterações restantes prossigam normalmente. Nesta seção, utilizaremos o aplicativo de juros compostos na Figura 5.6 para demonstrar como o depurador comporta-se quando você configura um ponto de interrupção no corpo de uma instrução for e como remover um ponto de interrupção no meio de uma sessão de depuração.



F.6  O comando clear

1087

1. Abrindo a janela Prompt do MS-DOS, mudando de diretório e compilando o aplicativo para depuração. Abra a janela Command Prompt e, então, mude para o diretório que contém os exemplos deste apêndice. Para sua conveniência, fornecemos uma cópia do arquivo Interest.java neste diretório. Compile o aplicativo para depuração digitando javac -g Interest.java. 2. Iniciando o depurador e configurando pontos de interrupção. Inicie o depurador digitando jdb. Configure os pontos de interrupção nas linhas 13 e 22 da classe Interest digitando stop at Interest:13 e stop at Interest:22 (Figura F.26).

Figura F.26  |  Configurando pontos de interrupção no aplicativo Interest.

3. Executando o aplicativo. Execute o aplicativo digitando run Interest. O aplicativo executa até alcançar o ponto de interrupção na linha 13 (Figura F.27). 4 . Continuando a execução. Digite cont para continuar — o aplicativo executa a linha 13, imprimindo os títulos da coluna "Year"

e "Amount on deposit". Observe que a linha 13 aparece antes da instrução for nas linhas 16–23 em Interest (Figura 5.6) e assim é executada somente uma vez. A execução continua da linha 13 até o ponto de interrupção na linha 22 ser alcançado durante a primeira iteração da instrução for (Figura F.28).

Figura F.27  |  Alcançando o ponto de interrupção na linha 13 no aplicativo Interest.

Figura F.28  |  Alcançando o ponto de interrupção na linha 22 no aplicativo Interest.

5. Examinando valores de variáveis. Digite print year para examinar o valor atual da variável year (isto é, a variável de controle do for). Imprima também o valor da variável amount (Figura F.29).

Figura F.29  |  Imprimindo year e amount durante a primeira iteração do for de Interest.

6. Continuando a execução. Digite cont para continuar a execução. A linha 22 executa e imprime os valores atuais de year e amount. Depois de o for entrar na sua segunda iteração, o depurador o notifica de que o ponto de interrupção na linha 22 foi alcançado uma segunda vez. Observe que o depurador pausa toda vez que uma linha em que um ponto de interrupção foi configurado está em

1088

Apêndice F

Utilizando o depurador

vias de executar — quando o ponto de interrupção aparece em um loop, o depurador pausa durante cada iteração. Imprima os valores das variáveis year e amount mais uma vez para ver como os valores mudaram desde a primeira iteração do for (Figura F.30).

Figura F.30 | Imprimindo year e amount durante a segunda iteração do for de Interest. 7. Removendo um ponto de interrupção. Você pode exibir uma lista de todos os pontos de interrupção no aplicativo digitando clear

(Figura F.31). Suponha que você esteja satisfeito com o funcionamento da instrução for do aplicativo Interest, assim quer remover o ponto de interrupção na linha 22 e permitir que as demais iterações do loop prossigam normalmente. Você pode remover o ponto de interrupção na linha 22 digitando clear Interest:22. Agora digite clear para listar os pontos de interrupção restantes no aplicativo. O depurador deve indicar que só o ponto de interrupção na linha 13 permanece (Figura F.31). Observe que esse ponto de interrupção já foi alcançado e assim não mais afetará a execução.

Figura F.31 | Removendo o ponto de interrupção na linha 22. 8. Continuando a execução depois de remover um ponto de interrupção. Digite cont para continuar a execução. Lembre-se

de que a execução foi pausada pela última vez antes da instrução printf na linha 22. Se o ponto de interrupção na linha 22 foi removido com sucesso, continuar o aplicativo gerará a saída correta para as iterações atuais e remanescentes da instrução for sem que o aplicativo pare (Figura F.32).

Figura F.32 | O aplicativo executa sem um ponto de interrupção configurado na linha 22.

Nesta seção, você aprendeu a utilizar o comando clear para listar todos os pontos de interrupção configurados para um aplicativo e a remover um ponto de interrupção.

F.7 Resumo Neste apêndice, você aprendeu a inserir e remover pontos de interrupção no depurador. Os pontos de interrupção permitem pausar a execução do aplicativo para que possa examinar os valores das variáveis com o comando print do depurador. Essa capacidade o ajudará a localizar e corrigir erros de lógica nos seus aplicativos. Você viu como utilizar o comando print para examinar o valor de uma expressão e como utilizar o comando set para alterar o valor de uma variável. Também aprendeu os comandos de depurador (incluindo os comandos step, step up e next) que podem ser utilizados para determinar se um método está executando corretamente. Aprendeu ainda a utilizar o comando watch para monitorar um campo por toda a vida de um aplicativo. Por fim, você aprendeu a utilizar o comando clear para listar todos os pontos de interrupção configurados para um aplicativo ou a remover pontos de interrupção individuais para continuar a execução sem pontos de interrupção.



Exercícios de autorrevisão

1089

Exercícios de autorrevisão F.1

Preencha as lacunas em cada uma das seguintes afirmações: a) Um ponto de interrupção não pode ser configurado em um(a) ________. b) Você pode examinar o valor de uma expressão utilizando o comando ________ do depurador. c) Você pode modificar o valor de uma variável utilizando o comando ________ do depurador. d) Durante a depuração, o comando ________ executa as instruções restantes do método atual e retorna o controle do programa ao local em que o método foi chamado. e) O comando ________ do depurador comporta-se como o comando step quando a próxima instrução a executar não contém uma chamada de método. f) O comando watch do depurador permite visualizar todas as alterações em um(a) ________.

F.2

Determine se cada um dos seguintes itens é verdadeiro ou falso. Se falso, explique por quê. a) Quando a execução do aplicativo é suspensa em um ponto de interrupção, a próxima instrução a ser executada é a instrução depois do ponto de interrupção. b) Os comandos watch podem ser removidos com o comando clear do depurador. c) A opção de compilador-g deve ser utilizada ao se compilar classes para depuração. d) Quando um ponto de interrupção aparece em um loop, o depurador só faz uma pausa na primeira vez em que o ponto de interrupção é encontrado.

Respostas dos exercícios de autorrevisão F.1 F.2

a) comentário. b) print. c) set. d) step up. e) next. f) campo. a) Falso. Quando a execução do aplicativo é suspensa em um ponto de interrupção, a próxima instrução a ser executada é a instrução no ponto de interrupção. b) Falso. Os comandos watch podem ser removidos com o comando unwatch do depurador. c) Verdadeiro. d) Falso. Quando um ponto de interrupção aparece em um loop, o depurador faz uma pausa durante cada iteração.

G

Todas as notícias que merecem ser publicadas. — Adolph S. Ochs

Que busca louca? Que luta para escapar? — John Keats

Não remova o marco divisório dos campos. — Amenehope

Saída formatada

Objetivos Neste apêndice, você aprenderá: 

A entender os fluxos de entrada e saída.



A utilizar a formatação



A imprimir com larguras e precisão de campo.



A utilizar flags de formatação na string de formato printf.



A imprimir com um índice de argumento.



A gerar saída de literais e sequências de escape.



A formatar a saída com a classe Formatter.

printf.

G.1 Introdução

Sumário

G.1 Introdução G.2 Fluxos G.3 Formatando a saída com printf G.4 Imprimindo inteiros G.5 Imprimindo números de ponto flutuante G.6 Imprimindo strings e caracteres G.7 Imprimindo datas e horas

1091

G.9 Imprimindo com larguras e precisões de campos G.10 Utilizando flags na string de formato printf G.11 Imprimindo com índices de argumento G.12 Imprimindo literais e sequências de escape G.13 Formatando saída com a classe Formatter G.14 Conclusão

G.8 Outros caracteres de conversão Resumo | Terminologia | Exercícios de autorrevisão | Respostas dos exercícios de autorrevisão | Exercícios

G.1 Introdução Neste apêndice, discutimos os recursos de formatação do método printf e da classe Formatter (pacote java.util). A classe formata e gera a saída de dados para um destino especificado, como um fluxo de saída de string ou de arquivo. Muitos recursos do printf foram discutidos anteriormente neste livro. Este apêndice resume esses recursos e apresenta outros, como exibição de dados de data e hora em vários formatos, reordenamento de saída com base no índice do argumento e exibição de números e strings com vários flags. Formatter

G.2 Fluxos Normalmente, a entrada e a saída são realizadas com fluxos, que são sequências de bytes. Nas operações de entrada, os bytes fluem de um dispositivo (por exemplo, teclado, unidade de disco, conexão de rede) para a memória principal. Nas operações de saída, os bytes fluem da memória principal para um dispositivo (por exemplo, monitor, impressora, unidade de disco, conexão de rede). Quando a execução do programa inicia, três fluxos são criados. O fluxo de entrada padrão tipicamente lê bytes do teclado, e o stream de saída padrão tipicamente gera a saída de caracteres em uma janela de comando. Um terceiro fluxo, fluxo de erro padrão (System.err), costuma gerar a saída de caracteres em uma janela de comando e é utilizado para gerar saída a mensagens de erro para que elas possam ser visualizadas imediatamente. Em geral, os sistemas operacionais permitem que esses fluxos sejam redirecionados para outros dispositivos. Os fluxos são discutidos em detalhes no Capítulo 17, “Arquivos, fluxos e serialização de objetos”, e no Capítulo 27, “Redes”.

G.3 Formatando a saída com printf Formatação de saída precisa é alcançada com printf. O Java SE 5 adotou (e aprimorou) esse recurso da linguagem de programação C. O método printf pode realizar as capacidades de formatação a seguir, cada uma das quais é discutida neste apêndice: 1. Arredondar valores de ponto flutuante para um número indicado de casas decimais. 2. Alinhar uma coluna de números com pontos de fração decimal aparecendo um em cima do outro. 3. Alinhamento à direita e alinhamento à esquerda das saídas. 4. Inserir caracteres literais em locais precisos em uma linha de saída. 5. Representar números de ponto flutuante em formato exponencial. 6. Representar inteiros em formato octal e hexadecimal. 7. Exibir todos os tipos de dados com campos de largura de tamanho fixo e precisão. 8. Exibir datas e horas em vários formatos. Cada chamada a printf fornece como o primeiro argumento uma string de formato que descreve o formato de saída. A string de formato pode consistir em texto fixo e especificadores de formato. A saída de texto fixo é gerada por printf assim como seria gerada pelos métodos System.out print ou println. Cada especificador de formato é um marcador de lugar para um valor e especifica o tipo da saída de dados. Especificadores de formato também podem incluir informações opcionais de formatação. Na forma mais simples, cada especificador de formato inicia com um sinal de porcentagem (%) e é seguido por um caractere de conversão que representa o tipo de dados do valor a ser impresso. Por exemplo, o especificador de formato %s é um marcador de lugar para uma string e o especificador de formato %d é um marcador de lugar para um valor int. As informações de formatação opcionais, como um índice de argumento, flags, precisão e largura de campo, são especificadas entre o sinal de porcentagem e o caractere de conversão. Demonstramos cada uma dessas capacidades.

1092

Apêndice G

Saída formatada

G.4 Imprimindo inteiros A Figura G.1 descreve os caracteres de conversão integrais. (Consulte no Apêndice H um resumo dos sistemas numéricos binários, octais, decimais e hexadecimais.) A Figura G.2 utiliza cada um para imprimir um número inteiro. Nas linhas 9–10, observe que o sinal de adição não é exibido por padrão, mas o sinal de subtração é. Mais adiante neste apêndice (Figura G.14), veremos como forçar a impressão de sinais de adição. Caractere de conversão

Descrição

d

Exibe um inteiro decimal (base 10).

o

Exibe um inteiro octal (base 8).

x ou X

Exibe um número inteiro hexadecimal (base 16). X utiliza letras maiúsculas.

Figura G.1 | Caracteres de conversão de inteiros. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

// Figura G.2: IntegerConversionTest.java // Utilizando os caracteres de conversão de inteiros. public class IntegerConversionTest { public static void main( String[] args ) { System.out.printf("%d\n", 26 ); System.out.printf("%d\n", +26 ); System.out.printf("%d\n", ­26 ); System.out.printf("%o\n", 26 ); System.out.printf("%x\n", 26 ); System.out.printf("%X\n", 26 ); } // fim de main } // fim da classe IntegerConversionTest

26 26 ­26 32 1a 1A

Figura G.2 | Utilizando caracteres de conversão de inteiros.

O método printf tem a forma printf( string-de-formato, lista-de-argumentos );

onde string-de-formato descreve o formato de saída e a lista-de-argumentos opcional contém os valores que correspondem a cada especificador de formato em string-de-formato. Há muitos especificadores de formato em uma string de formato. Cada string de formato nas linhas 8–10 especifica que printf deve gerar saída de um inteiro decimal (%d) seguido por um caractere de nova linha. Na posição do especificador de formato, printf substitui o valor do primeiro argumento depois da string de formato. Se a string de formato contiver múltiplos especificadores de formato, em cada posição subsequente do especificador de formato, printf substituirá o valor do próximo argumento na lista de argumentos. O especificador de formato %o na linha 11 gera saída de inteiros no formato octal. O especificador de formato %x na linha 12 gera saída de inteiros no formato hexadecimal. O especificador de formato %X na linha 13 gera saída de inteiros no formato hexadecimal com letras maiúsculas.

G.5 Imprimindo números de ponto flutuante A Figura G.3 descreve as conversões de pontos flutuantes. Os caracteres de conversão e e E exibem os valores de ponto flutuante em notação científica computadorizada (também chamada notação exponencial). A notação exponencial é o equivalente de computador à notação científica utilizada na matemática. Por exemplo, o valor 150,4582 é representado na notação científica matemática como 1,504582 ×102



G.5  Imprimindo números de ponto flutuante

1093

e é representado na notação exponencial como 1,504582e+02

no Java. Essa notação indica que 1,504582 é multiplicado por 10 elevado à segunda potência (e+02). O e significa “expoente”. A saída dos valores impressos com os caracteres de conversão e, E e f é gerada, por padrão, com seis dígitos de precisão à direita do ponto de fração decimal (por exemplo, 1,045921) — outras precisões devem ser especificadas explicitamente. Para valores impressos com o caractere de conversão g, a precisão representa o número total de dígitos exibido sem o expoente. O padrão é seis dígitos (por exemplo, 12345678,9 é exibido como 1,23457e+07). O caractere de conversão f sempre imprime pelo menos um dígito à esquerda do ponto de fração decimal. Os caracteres de conversão e e E imprimem o e em minúscula e o E em maiúscula antes do expoente e sempre imprime exatamente um dígito à esquerda do ponto de fração decimal. O arredondamento ocorre se o valor sendo formatado tiver mais dígitos significativos do que de precisão. Caractere de conversão e ou E f

Descrição

Exibe um valor de ponto flutuante em notação exponencial. O caractere de conversão E exibe a saída em letras maiúsculas. Exibe um valor de ponto flutuante no formato decimal.

g ou G

Exibe um valor de ponto flutuante no formato de ponto flutuante f ou no formato exponencial e com base na magnitude do valor. Se a magnitude for menor que 10-3, maior ou igual a 107, o valor de ponto flutuante será impresso com e (ou E). Caso contrário, o valor é impresso no formato f. Quando o caractere de conversão G é utilizado, a saída é exibida em letras maiúsculas.

a ou A

Exibe um número de ponto flutuante no formato hexadecimal. O caractere de conversão A exibe a saída em letras maiúsculas.

Figura G.3  |  Caracteres de conversão de ponto flutuante.

O caractere de conversão g (ou G) imprime o e (E) ou f em qualquer formato, dependendo do valor de ponto flutuante. Por exemplo, os valores 0,0000875, 87500000,0, 8,75, 87,50 e 875,0 são impressos como 8,750000e-05, 8,750000e+07, 8,750000, 87,500000 e 875,000000 com o caractere de conversão g. O valor 0,0000875 utiliza a notação e porque a magnitude é menor que 10-3. O valor 87500000.0 usa a notação e porque a magnitude é maior do que 107. A Figura G.4 demonstra os caracteres de conversão de ponto flutuante. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

// Figura G.4: FloatingNumberTest.java // Utilizando caracteres de conversão de ponto flutuante. public class FloatingNumberTest { public static void main( String[] args ) { System.out.printf("%e\n", 12345678.9 ); System.out.printf("%e\n", +12345678.9 ); System.out.printf("%e\n", -12345678.9 ); System.out.printf("%E\n", 12345678.9 ); System.out.printf("%f\n", 12345678.9 ); System.out.printf("%g\n", 12345678.9 ); System.out.printf("%G\n", 12345678.9 ); } // fim de main } // fim da classe FloatingNumberTest

1.234568e+07 1.234568e+07 -1.234568e+07 1.234568E+07 12345678.900000 1.23457e+07 1.23457E+07

Figura G.4  |  Utilizando caracteres de conversão de ponto flutuante.

1094

Apêndice G

Saída formatada

G.6 Imprimindo strings e caracteres Os caracteres de conversão c e s imprimem caracteres e strings específicos, respectivamente. Os caracteres de conversão c e C requerem um argumento char. Os caracteres de conversão s e S podem receber uma String ou algum Object como um argumento. Quando os caracteres de conversão C e S são utilizados, a saída é exibida em letras maiúsculas. A Figura G.5 exibe caracteres, strings e objetos com os caracteres de conversão c e s. Observe que o autoboxing (“empacotamento”) ocorre na linha 9 quando uma constante int é atribuída a um objeto Integer. A linha 15 gera saída de um argumento Integer com o caractere de conversão s, que invoca implicitamente o método toString para obter o valor de inteiro. Observe que você também pode gerar a saída de um objeto Integer utilizando o especificador de formato %d. Nesse caso, o valor int no objeto Integer será “desempacotado” (unboxing) e impresso.

Erro de programação comum G.1 Utilizar %c para imprimir uma String resulta em uma IllegalFormatConversionException — uma String não pode ser convertida em um caractere.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

// Figura G.5: CharStringConversion.java // Utilizando caractere e caracteres de conversão de string. public class CharStringConversion { public static void main( String[] args ) { char character = 'A'; // inicializa char String string = "This is also a string"; // Objeto String Integer integer = 1234; // inicializa inteiro (autoboxing) System.out.printf("%c\n", character ); System.out.printf("%s\n", "This is a string" ); System.out.printf("%s\n", string ); System.out.printf("%S\n", string ); System.out.printf("%s\n", integer ); // chamada implícita para toString } // fim de main } // fim da classe CharStringConversion

A This is a string This is also a string THIS IS ALSO A STRING 1234

Figura G.5 | Utilizando caracteres de conversão de caractere e string.

G.7 Imprimindo datas e horas O caractere de conversão t (ou T) é utilizado para imprimir datas e horas em vários formatos. Ele sempre é seguido por um caractere de sufixo de conversão que especifica que o formato de data e/ou hora. Quando o caractere de conversão T é utilizado, a saída é exibida em letras maiúsculas. A Figura G.6 lista os caracteres de sufixo de conversão comuns para formatar composições de data e hora que exibem tanto a data como a hora. A Figura G.7 lista os caracteres de sufixo de conversão comuns para formatar datas. A Figura G.8 lista os caracteres de sufixo de conversão comuns para formatar horas. Para a lista completa de caracteres de sufixo de conversão, visite java. sun.com/javase/6/docs/api/java/util/Formatter.html. Caractere de sufixo de conversão

c

Descrição

Exibe a data e hora formatadas como day month date hour:minute:second time­zone year

com três caracteres para day e month, dois dígitos para date, hour, minute e second e quatro dígitos para year — por exemplo, Wed Mar 03 16:30:25 GMT­05:00 2004. O relógio de 24 horas é utilizado. GMT­05:00 é o fuso horário.



G.7  Imprimindo datas e horas

Caractere de sufixo de conversão

F D r R T

1095

Descrição

Exibe a data formatada como year-month-date com quatro dígitos para o year e dois dígitos cada para month e a date (por exemplo, 2004-05-04). Exibe a data formatada como month/day/year com dois dígitos cada para o month, day e year (por exemplo, 03/03/04). Exibe a hora no formato de 12 horas como hour:minute:second AM|PM com dois dígitos cada para a hour, minute e second (por exemplo, 04:30:25 PM). Exibe a hora formatada como hour:minute com dois dígitos cada para a hour e minute (por exemplo, 16:30). O relógio de 24 horas é utilizado. Exibe a hora como hour:minute:second com dois dígitos para hour, minute e second (por exemplo, 16:30:25). O relógio de 24 horas é utilizado.

Figura G.6  |  Caracteres de sufixo de conversão de composição de data e hora. Caractere de sufixo de conversão A a B b d m e Y y j

Descrição

Exibe o nome completo do dia da semana (por exemplo, Wednesday). Exibe o nome com três caracteres do dia da semana (por exemplo, Wed). Exibe o nome completo do mês (por exemplo, March). Exibe o nome abreviado com três caracteres do mês (por exemplo, Mar). Exibe o dia do mês com dois dígitos, preenchendo com zeros à esquerda conforme necessário (por exemplo, 03). Exibe o mês com dois dígitos, preenchendo com zeros à esquerda conforme necessário (por exemplo, 07). Exibe o dia de mês sem zeros à esquerda (por exemplo, 3). Exibe o ano com quatro dígitos (por exemplo, 2004). Exibe os dois últimos dígitos do ano com zeros à esquerda (por exemplo, 04). Exibe o dia do ano com três dígitos, preenchendo com zeros à esquerda conforme necessário (por exemplo, 016).

Figura G.7  |  Caracteres de sufixo de conversão de formatação de data. Caractere de sufixo de conversão H I k l M S Z p P

Descrição

Exibe horas no relógio de 24 horas com um zero à esquerda conforme necessário (por exemplo, 16). Exibe horas no relógio de 12 horas com um zero à esquerda conforme necessário (por exemplo, 04). Exibe horas em relógio de 24 horas sem zeros à esquerda (por exemplo, 16). Exibe horas em relógio de 12 horas sem zeros à esquerda (por exemplo, 4). Exibe minutos com um zero à esquerda conforme necessário (por exemplo, 06). Exibe segundos com um zero à esquerda conforme necessário (por exemplo, 05). Exibe a abreviação para o fuso horário (por exemplo, EST, significa Eastern Standard Time, que está 5 horas atrás do Greenwich Mean Time). Exibe marcador de manhã ou de tarde em letras minúsculas (por exemplo, pm). Exibe marcador de manhã ou de tarde em letras maiúsculas (por exemplo, PM).

Figura G.8  |  Caracteres de sufixo de conversão de formatação de hora.

1096

Apêndice G

Saída formatada

A Figura G.9 utiliza os caracteres de conversão t e T com os caracteres de sufixo de conversão para exibir datas e horas em vários formatos. O caractere de conversão t requer que o argumento correspondente seja uma data ou hora do tipo long, Long, Calendar (pacote java.util) ou Date (pacote java.util) — os objetos de cada uma dessas classes podem representar datas e horas. A classe Calendar é a preferida para esse propósito porque alguns construtores e métodos da classe Date são substituídos por aqueles da classe Calendar. A linha 10 invoca o método static getInstance da classe Calendar para obter um calendário com a data e hora atual. As linhas 13–17, 20–22 e 25–26 utilizam esse objeto de Calendar nas instruções printf como o valor a ser formatado com o caractere de conversão t. Observe que linhas 20–22 e 25–26 utilizam o índice de argumentos ("1$") opcional para indicar que todos os especificadores de formato na string de formato utilizam o primeiro argumento depois da string de formato na lista de argumentos. Você aprenderá mais sobre índices de argumentos na Seção G.11. Utilizando o índice de argumentos elimina a necessidade de listar repetidamente o mesmo argumento. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

// Figura G.9: DateTimeTest.java // Formatando datas e horas com os caracteres de conversão t e T. import java.util.Calendar; public class DateTimeTest { public static void main( String[] args ) { // obtém a data e hora atual Calendar dateTime = Calendar.getInstance(); // imprimindo com caracteres de conversão para composições de data/hora System.out.printf("%tc\n", dateTime ); System.out.printf("%tF\n", dateTime ); System.out.printf("%tD\n", dateTime ); System.out.printf("%tr\n", dateTime ); System.out.printf("%tT\n", dateTime ); // imprimindo com caracteres de System.out.printf("%1$tA, %1$tB System.out.printf("%1$TA, %1$TB System.out.printf("%1$ta, %1$tb

conversão para data %1$td, %1$tY\n", dateTime ); %1$Td, %1$TY\n", dateTime ); %1$te, %1$ty\n", dateTime );

// imprimindo com caracteres de conversão para hora System.out.printf("%1$tH:%1$tM:%1$tS\n", dateTime ); System.out.printf("%1$tZ %1$tI:%1$tM:%1$tS %tP", dateTime ); } // fim de main } // fim da classe DateTimeTest

Wed Feb 25 15:00:22 EST 2009 2009­02­25 02/25/09 03:00:22 PM 15:00:22 Wednesday, February 25, 2009 WEDNESDAY, FEBRUARY 25, 2009 Wed, Feb 25, 09 15:00:22 EST 03:00:22 PM

Figura G.9 | Formatando datas e horas com caracteres de conversão t e T.

G.8 Outros caracteres de conversão Os caracteres de conversão restantes são b, B, h, H, % e n. Estes são discutidos na Figura G.10. As linhas 9–10 da Figura G.11 utilizam %b para imprimir o valor de valores boolean (ou Boolean) false e true. A linha 11 associa uma String a %b, que retorna true porque não é null. A linha 12 associa um objeto null a %B, que exibe FALSE porque test é null. As linhas 13–14 utilizam %h para imprimir as representações de string dos valores de código de hash para as strings "hello" e "Hello". Esses valores poderiam ser utilizados para armazenar ou localizar as strings em uma Hashtable ou HashMap (ambas discutidas no Capítulo 20, “Coleções genéricas”). Observe que os valores de código de hash para essas duas strings diferem, pois uma string inicia com uma letra minúscula e a outra com uma letra



G.8  Outros caracteres de conversão

1097

maiúscula. A linha 15 utiliza %H para imprimir null em letras maiúsculas. As duas últimas instruções printf (linhas 16–17) utilizam %% para imprimir o caractere % em uma string e %n para imprimir um separador de linha específico à plataforma. Caractere de conversão

Descrição

b ou B

Imprime "true" ou "false" para o valor de um boolean ou Boolean. Esses caracteres de conversão também podem formatar o valor de qualquer referência. Se a referência for não null, "true" será impresso; do contrário, "false". Quando o caractere de conversão B é utilizado, a saída é exibida em letras maiúsculas.

h ou H

Imprime a representação de string de um valor de código de hash do objeto em formato hexadecimal. Se o argumento correspondente for null, "null" será impresso. Quando o caractere de conversão H é utilizado, a saída é exibida em letras maiúsculas.

%

Imprime o caractere de porcentagem.

n

Imprime o separador de linha específico à plataforma (por exemplo, \r\n no Windows ou \n no UNIX/LINUX).

Figura G.10  |  Outros caracteres de conversão.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// Figura G.11: OtherConversion.java // Utilizando os caracteres de conversão b, B, h, % e n. public class OtherConversion { public static void main( String[] args ) { Object test = null; System.out.printf( "%b \n", false ); System.out.printf( "%b \n", true ); System.out.printf( "%b \n", "Test" ); System.out.printf( "%B \n", test ); System.out.printf( "Hashcode of \"hello\" is %h\n", "hello" ); System.out.printf( "Hashcode of \"Hello\" is %h\n", "Hello" ); System.out.printf( "Hashcode of null is %H \n", test ); System.out.printf( "Printing a %% in a format string\n" ); System.out.printf( "Printing a new line %n next line starts here" ); } // fim de main } // fim da classe OtherConversion

false true true FALSE Hashcode of "hello" is 5e918d2 Hashcode of "Hello" is 42628b2 Hashcode of null is NULL Printing a % in a format string Printing a new line next line starts here

Figura G.11  |  Utilizando os caracteres de conversão b, B, h, H, % e n.

Erro de programação comum G.2 Tentar imprimir um caractere de percentagem literal com % em vez de %% na string de formato resultaria em um erro de lógica difícil de detectar. Quando % aparece em uma string de formato, ele deve ser seguido por caractere de conversão na string. O caractere de percentagem individual poderia ser acidentalmente seguido por um caractere de conversão legítimo resultando em um erro de lógica.

1098

Apêndice G

Saída formatada

G.9 Imprimindo com larguras e precisões de campos O tamanho exato de um campo em que dados são impressos é especificado por uma largura de campo. Se a largura de campo for maior que os dados a serem impressos, os dados serão alinhados por padrão à direita no campo. Discutimos a justificação à esquerda na Seção G.10. Insira um inteiro que representa a largura de campo entre o % e o caractere de conversão (por exemplo, %4d) no especificador de formato. A Figura G.12 imprime dois grupos de cinco números cada, alinhando à direita os números que contém menos dígitos do que a largura de campo. Observe que a largura de campo é aumentada para imprimir valores com largura maior do que o campo e que o sinal de subtração para um valor negativo utiliza uma posição de caractere no campo. Além disso, se nenhuma largura de campo for especificada, os dados serão impressos exatamente de acordo com o número de posições necessárias. As larguras de campo podem ser usadas com todos os especificadores de formato, exceto o separador de linha (%n). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

// Figura G.12: FieldWidthTest.java // Alinhando inteiros à direita nos campos. public class FieldWidthTest { public static void main( String[] args ) { System.out.printf("%4d\n", 1 ); System.out.printf("%4d\n", 12 ); System.out.printf("%4d\n", 123 ); System.out.printf("%4d\n", 1234 ); System.out.printf("%4d\n\n", 12345 ); // dados muito grandes System.out.printf("%4d\n", ­1 ); System.out.printf("%4d\n", ­12 ); System.out.printf("%4d\n", ­123 ); System.out.printf("%4d\n", ­1234 ); // dados muito grandes System.out.printf("%4d\n", ­12345 ); // dados muito grandes } // fim de main } // fim da classe RightJustifyTest

1 12 123 1234 12345 ­1 ­12 ­123 ­1234 ­12345

Figura G.12 | Alinhando inteiros à direita nos campos.

Erro de programação comum G.3 Não fornecer uma largura de campo suficientemente grande para tratar um valor a ser impresso pode deslocar outros dados que estão sendo impressos e produz saídas confusas. Conheça seus dados!

O método printf também fornece a capacidade de especificar a precisão com que os dados são impressos. A precisão tem diferentes significados para diferentes tipos. Quando usada com caracteres de conversão de ponto flutuante e e f, a precisão é o número de dígitos que aparece depois do ponto de fração decimal. Quando usada com os caracteres de conversão g, a ou A, a precisão é o número máximo de dígitos significativos a ser impresso. Quando usada com o caractere de conversão s, a precisão é o número máximo de caracteres a ser gravado da string. Para utilizar a precisão, coloque entre o sinal de porcentagem e o especificador de conversão um ponto de fração decimal (.) seguido por um inteiro que representa a precisão. A Figura G.13 demonstra o uso da precisão em strings de formato. Observe que quando um valor de ponto flutuante é impresso com uma precisão menor do que o número original de casas decimais no valor, o valor é arredondado. Também observe que o especificador de formato %.3g indica que o número total de dígitos usado para exibir o valor de ponto flutuante é 3. Como o valor tem três dígitos à esquerda do ponto de fração decimal, ele é arredondado para a casa das unidades.

G.10 Utilizando flags na string de formato printf

1099

A largura e a precisão de campo podem ser combinadas colocando a largura de campo, seguida por um ponto de fração decimal, seguido por uma precisão entre o sinal de porcentagem e o caractere de conversão, como na instrução printf( "%9.3f", 123.456789 );

que exibe 123.457 com três dígitos à direita do ponto de fração decimal alinhado à direita em um campo de nove dígitos — esse número será precedido no campo por dois espaços em branco. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

// Figura G.13: PrecisionTest.java // Utilizando a precisão para números de ponto flutuante e strings. public class PrecisionTest { public static void main( String[] args ) { double f = 123.94536; String s = "Happy Birthday"; System.out.printf( "Using precision for floating­point numbers\n" ); System.out.printf( "\t%.3f\n\t%.3e\n\t%.3g\n\n", f, f, f ); System.out.printf( "Using precision for strings\n" ); System.out.printf( "\t%.11s\n", s ); } // fim de main } // fim da classe PrecisionTest

Using precision for floating­point numbers 123.945 1.239e+02 124 Using precision for strings Happy Birth

Figura G.13 | Utilizando a precisão para números de ponto flutuante e strings.

G.10 Utilizando flags na string de formato printf Vários flags podem ser utilizados com o método printf para suplementar suas capacidades de formatação de saída. Sete flags estão disponíveis para utilização nas strings de formato (Figura G.14). Flag

Descrição

- (sinal de subtração)

Alinha a saída à esquerda dentro do campo especificado.

+ (sinal de adição)

Exibe um sinal de adição precedendo valores positivos e um sinal de subtração precedendo valores negativos.

espaço

Imprime um espaço antes de um valor positivo não impresso com o flag +.

#

O prefixo 0 para o valor de saída quando utilizado com o caractere de conversão octal o. Adiciona o prefixo 0x ao valor de saída quando utilizado com o caractere de conversão hexadecimal x.

0 (zero)

Preenche um campo com zeros à esquerda.

, (vírgula)

Utiliza o separador específico de localidade de milhares (isto é, ',' para localidade nos EUA) para exibir números decimais e de ponto flutuante.

(

Inclui números negativos dentro de parênteses.

Figura G.14 | Flags de string de formato.

Para utilizar um flag em uma string de formato, coloque-o imediatamente à direita do sinal de porcentagem. Vários flags podem ser utilizados no mesmo especificador de formato. A Figura G.15 demonstra o alinhamento à direita e o alinhamento à esquerda de uma string, um inteiro, um caractere e um número de ponto flutuante. Observe que a linha 9 serve como um mecanismo de contagem para a saída na tela.

1100

1 2 3 4 5 6 7 8 9 10 11 12 13 14

Apêndice G  Saída formatada

// Figura G.15: MinusFlagTest java // Valores para o alinhamento à direita e à esquerda. public class MinusFlagTest { public static void main( String[] args ) { System.out.println( "Columns:" ); System.out.println( "0123456789012345678901234567890123456789\n" ); System.out.printf( "%10s%10d%10c%10f\n\n", "hello", 7, 'a', 1.23 ); System.out.printf( "%-10s%-10d%-10c%-10f\n", "hello", 7, 'a’, 1.23 ); } // fim de main } // fim da classe MinusFlagTest

Columns: 0123456789012345678901234567890123456789 hello hello

7 7

a a

1.230000 1.230000

Figura G.15  |  Valores para o alinhamento à direita e à esquerda.

A Figura G.16 imprime um número positivo e um número negativo, cada um com e sem o flag +. Observe que o sinal de subtração é exibido nos dois casos, mas o sinal de adição somente quando o flag + é utilizado. 1 2 3 4 5 6 7 8 9 10 11 786 +786

// Figura G.16: PlusFlagTest.java // Imprimindo números com e sem o flag +. public class PlusFlagTest { public static void main( String[] args ) { System.out.printf( "%d\t%d\n", 786, -786 ); System.out.printf("%+d\t%+d\n", 786, -786 ); } // fim de main } // fim da classe PlusFlagTest -786 -786

Figura G.16  |  Imprimindo números com e sem o flag +.

A Figura G.17 prefixa um espaço para o número positivo com o flag espaço. Isso é útil para alinhar números positivos e negativos com o mesmo número de dígitos. Observe que o valor -547 não é precedido por um espaço na saída por causa do seu sinal de subtração. A Figura G.18 utiliza o flag # para prefixar 0 para o valor octal e 0x para o valor hexadecimal. 1 2 3 4 5 6 7 8 9 10

// Figura G.17: SpaceFlagTest.java // Imprimindo um espaço antes de valores não negativos. public class SpaceFlagTest { public static void main( String[] args ) { System.out.printf( "% d\n% d\n", 547, -547 ); } // fim de main } // fim da classe SpaceFlagTest

547 -547

Figura G.17  |  Imprimindo um espaço antes de valores não negativos.



G.10  Utilizando flags na string de formato printf

1 2 3 4 5 6 7 8 9 10 11 12 13

1101

// Figura G.18: PoundFlagTest.java // Utilizando o flag # com os caracteres de conversão o e x. public class PoundFlagTest { public static void main( String[] args ) { int c = 31; // inicializa c System.out.printf( "%#o\n", c ); System.out.printf( "%#x\n", c ); } // fim de main } // fim da classe PoundFlagTest

037 0x1f

Figura G.18  |  Utilizando o flag # com caracteres de conversão o e x.

A Figura G.19 combina o flag +, o flag 0 e o flag espaço para imprimir 452 em um campo de largura 9 com um sinal + e zeros à esquerda, em seguida imprime 452 em um campo de largura 9 utilizando somente o flag 0 e, então, imprime 452 em um campo de largura 9 utilizando somente o flag espaço. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura G.19: ZeroFlagTest.java // Imprimindo com flags 0 (zero) preenche com zeros à esquerda. public class ZeroFlagTest { public static void main( String[] args ) { System.out.printf("%+09d\n", 452 ); System.out.printf("%09d\n", 452 ); System.out.printf("% 9d\n", 452 ); } // fim de main } // fim da classe ZeroFlagTest

+00000452 000000452 452

Figura G.19  |  Imprimindo com flags 0 (zero) preenche com zeros à esquerda.

A Figura G.20 utiliza o flag vírgula (,) para exibir um número decimal e um número de ponto flutuante com o separador de milhares. A Figura G.21 inclui números negativos entre parênteses utilizando o flag (. Observe que o valor 50 não é incluído entre parênteses na saída porque é um número positivo. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura G.20: CommaFlagTest.java // Utilizando o flag vírgula (,) para exibir números com separador de milhares. public class CommaFlagTest { public static void main( String[] args ) { System.out.printf("%,d\n", 58625 ); System.out.printf("%,.2f", 58625.21 ); System.out.printf("%,.2f", 12345678.9 ); } // fim de main } // fim da classe CommaFlagTest

1102

Apêndice G

Saída formatada

58,625 58,625.21 12,345,678.90

Figura G.20 | Utilizando o flag vírgula (,) para exibir números com o separador de milhares. 1 2 3 4 5 6 7 8 9 10 11 12

// Figura G.21: ParenthesesFlagTest.java // Utilizando o flag ( para colocar parênteses em torno de números negativos. public class ParenthesesFlagTest { public static void main( String[] args ) { System.out.printf("%(d\n", 50 ); System.out.printf("%(d\n", ­50 ); System.out.printf("%(.1e\n", ­50.0 ); } // fim de main } // fim da classe ParenthesesFlagTest

50 (50) (5.0e+01)

Figura G.21 | Utilizando o flag ( para colocar parênteses em torno de números negativos.

G.11 Imprimindo com índices de argumento Um índice de argumentos é um inteiro opcional seguido por um sinal $ que indica a posição do argumento na lista de argumentos. Por exemplo, as linhas 20–22 e 25–26 na Figura G.9 utilizam o índice de argumentos "1$" para indicar que todos os especificadores de formato utilizam o primeiro argumento da lista de argumentos. Os índices de argumentos permitem que os programadores reordenem a saída de modo que os argumentos na lista de argumentos não necessariamente estejam na ordem dos seus especificadores de formato correspondentes. Os índices de argumentos também ajudam a evitar a duplicação de argumentos. A Figura G.22 imprime os argumentos da lista de argumentos na ordem inversa utilizando o índice de argumentos. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

// Figura G.22: ArgumentIndexTest // Reordenando a saída com índices de argumentos. public class ArgumentIndexTest { public static void main( String[] args ) { System.out.printf( "Parameter list without reordering: %s %s %s %s\n", "first", "second", "third", "fourth" ); System.out.printf( "Parameter list after reordering: %4$s %3$s %2$s %1$s\n", "first", "second", "third", "fourth" ); } // fim de main } // fim da classe ArgumentIndexTest

Parameter list without reordering: first second third fourth Parameter list after reordering: fourth third second first

Figura G.22 | Reordenando a saída com índices de argumentos.

G.12 Imprimindo literais e sequências de escape

1103

G.12 Imprimindo literais e sequências de escape A maioria dos caracteres literais a ser impressa em uma instrução printf pode ser simplesmente incluída na string de formato. Entretanto, há vários caracteres “problemáticos”, como aspas (") que delimitam a própria string de formato. Vários caracteres de controle, com a nova linha e tabulação, devem ser representados por sequências de escape. Uma sequência de escape é representada por uma barra invertida (\), seguida por um caractere de escape. A Figura G.23 lista as sequências de escape e as ações resultantes.

Erro de programação comum G.4 Tentar imprimir como dados literais em uma instrução printf um caractere de aspa dupla ou de barra invertida sem preceder esse caractere com uma barra invertida para formar uma sequência de escape adequada resultaria em um erro de sintaxe.

Sequência de escape

Descrição

\' (aspa simples)

Gera saída do caractere de aspa simples (').

\" (aspas duplas)

Gera a saída do caractere de aspas duplas (").

\\ (barras invertidas)

Gera a saída do Caractere de barra invertida (\).

\b (backspace)

Move o cursor de volta uma posição na linha atual.

\f (nova página ou avanço de formulário)

Move o cursor para o início da próxima página lógica.

\n (nova linha)

Move o cursor para o começo da próxima linha.

\r (retorno de carro)

Move o cursor para o começo da linha atual.

\t (tabulação horizontal)

Move o cursor para a próxima posição da tabulação horizontal.

Figura G.23 | Sequências de escape.

G.13 Formatando saída com a classe Formatter Até agora, discutimos a exibição de saída formatada para o fluxo de saída padrão. O que devemos fazer se quisermos enviar saídas formatadas a outros fluxos de saída ou dispositivos, como um JTextArea ou um arquivo? A solução conta com a classe Formatter (do pacote Formatter), que fornece as mesmas capacidades de formatação de printf. Formatter é uma classe utilitária que permite aos programadores gerar saída formatada de dados para um destino especificado, como um arquivo em disco. Por padrão, uma Formatter cria uma string na memória. A Figura G.24 mostra como utilizar uma classe Formatter para construir uma string formatada, que é então exibida em uma caixa de diálogo de mensagem. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

// Figura G.24: FormatterTest.java // Formatando a saída com a classe Formatter. import java.util.Formatter; import javax.swing.JOptionPane; public class FormatterTest { public static void main( String[] args ) { // cria Formatter e formata a saída Formatter formatter = new Formatter(); formatter.format( "%d = %#o = %#X", 10, 10, 10 ); // exibe a saída em JOptionPane JOptionPane.showMessageDialog( null, formatter.toString() ); } // fim de main } // fim da classe FormatterTest

1104

Apêndice G

Saída formatada

Figura G.24 | Formatando saída com a classe Formatter.

A linha 11 cria um objeto Formatter utilizando o construtor padrão, assim esse objeto construirá uma string na memória. Outros construtores são fornecidos para que você possa especificar o destino em que os dados formatados devem ser impressos. Para detalhes, consulte java.sun.com/javase/6/docs/api/java/util/Formatter.html. A linha 12 invoca o método format para formatar a saída. Como ocorre com printf, o método format recebe uma string de formato e uma lista de argumentos. A diferença é que printf envia a saída formatada diretamente para o fluxo de saída padrão, enquanto o método format envia a saída formatada para o destino especificado pelo seu construtor (nesse programa, uma string na memória). A linha 15 invoca o método toString de Formatter para obter os dados formatados como uma string, que então é exibida em uma caixa de diálogo de mensagem. Observe que a classe String também fornece um método static de conveniência chamado format que permite criar uma string na memória sem precisar antes criar um objeto Formatter. As linhas 11–12 e a linha 15 na Figura G.24 poderiam ter sido substituídas por String s = String.format( "%d = %#o = %#x", 10, 10, 10 ); JOptionPane.showMessageDialog( null, s );

G.14 Conclusão Este apêndice resumiu como exibir saída formatada com vários caracteres e flags de formato. Exibimos números decimais utilizando os caracteres de formato d, o, x e X; números de ponto flutuante utilizando os caracteres de formato e, E, f, g e G; e as datas e horas em vários formatos utilizando os caracteres de formato t e T e seus caracteres de sufixo de conversão. Você aprendeu a exibir saída com larguras e precisão de campo. Introduzimos os flags +, ­, espaço, #, 0, vírgula e ( utilizados juntamente com os caracteres de formato para produzir a saída. Também demonstramos como formatar a saída com a classe Formatter.

Resumo Seção G.2 Fluxos • Normalmente, a entrada e saída são realizadas com fluxos, que são sequências de bytes. • Normalmente, o fluxo de entrada padrão está conectado ao teclado e o fluxo de saída padrão está conectado à tela do computador.

Seção G.3 Formatando a saída com printf • A string de formato de printf descreve os formatos em que os valores de saída aparecem. O especificador de formato consiste em índice de argumentos, flags, larguras de campo, precisões e caracteres de conversão.

Seção G.4 Imprimindo números inteiros • Os inteiros são impressos com os caracteres de conversão d para inteiros decimais, o para inteiros na forma octal e x (ou X) para inteiros na forma hexadecimal. X exibe letras maiúsculas.

Seção G.5 Imprimindo números de ponto flutuante • Valores de ponto flutuante são impressos com os caracteres de conversão e (ou E) para notação exponencial, f para notação de ponto flutuante regular e g (ou G) para notação e (ou E) ou notação f. Para o especificador de conversão g, o caractere de conversão e será utilizado se o valor for menor que 10-3, maior ou igual a 107; caso contrário, o caractere de conversão f é utilizado.

Seção G.6 Imprimindo strings e caracteres • O caractere de conversão c imprime um caractere. • O caractere de conversão s (ou S) imprime uma string de caracteres. O caractere de conversão S exibe a saída em letras maiúsculas.



Terminologia

1105

Seção G.7 Imprimindo datas e horas • O caractere de conversão t (ou T) seguido por um caractere de sufixo de conversão imprime a data e hora em várias formas. O caractere de conversão T exibe a saída em letras maiúsculas. • O caractere de conversão t (ou T) requer que o argumento seja do tipo long, Long, Calendar ou Date.

Seção G.8 Outros caracteres de conversão • O caractere de conversão b (ou B) gera saída da representação de string de um boolean ou Boolean. Esses caracteres de conversão também imprimem "true" para referências não null e "false" para referências null. O caractere de conversão B imprime em letras maiúsculas. • O caractere de conversão h (ou H) retorna null a uma referência null e uma representação de String do valor de código de hash (na base 16) de um objeto. Códigos de hash são utilizados para armazenar e recuperar objetos em Hashtables e HashMaps. O caractere de conversão H imprime em letras maiúsculas. • O caractere de conversão n imprime o separador de linha específico à plataforma. • O caractere de conversão % é utilizado para exibir um literal %.

Seção G.9 Imprimindo utilizando precisão e larguras de campo • Se a largura de campo for maior que o objeto que está sendo impresso, o objeto será alinhado à direita no campo. • As larguras de campo podem ser utilizadas com todos os caracteres de conversão, exceto a conversão de separador de linha. • A precisão utilizada com os caracteres de conversão de ponto flutuante e e f indica o número de dígitos que aparece depois do ponto de fração decimal. A precisão utilizada com o caractere de conversão de ponto flutuante g indica o número de dígitos significativos que aparece. • A precisão utilizada com o caractere de conversão s indica o número de caracteres a ser impresso. • A largura e precisão de campo podem ser combinadas colocando-se a largura de campo, seguida por um ponto de fração decimal, seguida pela precisão entre o sinal de porcentagem e o caractere de conversão.

Seção G.10 Utilizando flags na string de formato printf • O flag - é alinhado à esquerda do seu argumento em um campo. • O flag + imprime um sinal de adição para valores positivos e um sinal de subtração para valores negativos. • O flag espaço imprime um espaço que precede um valor positivo. O flag espaço e o flag + não podem ser utilizados juntos em um caractere de conversão de inteiros. • O flag # prefixa 0 para os valores octais e 0x para valores hexadecimais. • O flag 0 imprime zeros à esquerda para um valor que não ocupa todo o seu campo. • O flag de vírgula (,) utiliza o separador de milhares específicos à localidade para exibir números inteiros e números de ponto flutuante. • O flag ( coloca um número negativo dentro de parênteses.

Seção G.11 Imprimindo com índices de argumento • Um índice de argumentos é um inteiro decimal opcional seguido por um sinal $ que indica a posição do argumento na lista de argumentos. • Os índices de argumentos permitem que os programadores reordenem a saída de modo que os argumentos na lista de argumentos não necessariamente estejam na ordem dos seus especificadores de formato correspondentes. Os índices de argumentos também ajudam a evitar a duplicação de argumentos.

Seção G.11 Formatando a saída com a classe Formatter • A classe Formatter (no pacote java.util) fornece as mesmas capacidades de formatação do printf. Formatter é uma classe utilitária que permite aos programadores imprimir saída formatada para vários destinos, incluindo componentes GUI, arquivos e outros fluxos de saída. • O método Formatter format gera saída formatada de dados para o destino especificado pelo construtor de Formatter. • O método static format String formata dados e retorna os dados formatados como uma String.

Terminologia -, flag, 1099

+, flag, 1099

B, caractere de sufixo de conversão, 1095

‚ (vírgula), flag, 1099

0 (zero), flag, 1099

c, caractere de conversão, 1094

(, flag, 1099 #, flag, 1099 %, caractere de conversão, 1096 %f, especificador de formato, 1093 %s, especificador de formato, 1094

a, caractere de sufixo de conversão, 1095

C, caractere de conversão, 1094

A, caractere de sufixo de conversão, 1095

c, caractere de sufixo de conversão, 1094

b, caractere de conversão, 1096

Calendar, classe, 1096 caractere de conversão, 1091 caractere de sufixo de conversão, 1094

B, caractere de conversão, 1096 b, caractere de sufixo de conversão, 1095

1106

Capítulo G  Saída formatada

caracteres de conversão de inteiros, 1092 composições de data e hora, 1094 d, caractere de conversão, 1092 d, caractere de sufixo de conversão, 1095 D, caractere de sufixo de conversão, 1095 Date, classe, 1096 e, caractere de sufixo de conversão, 1095 espaço, flag, 1099 especificadores de formato, 1091 f, caractere de conversão, 1093 F, caractere de sufixo de conversão, 1095 fluxo de erro padrão, 1091 format, método da classe Formatter, 1104 formato, string, 1091 Formatter, classe, 1091 g, caractere de conversão, 1093

G, caractere de conversão, 1093

p, caractere de sufixo de conversão, 1095

getInstance, método de Calendar, 1096

P, caractere de sufixo de conversão, 1095

h, caractere de conversão, 1096

r, caractere de sufixo de conversão, 1095

H, caractere de conversão, 1096

R, caractere de sufixo de conversão, 1095

H, caractere de sufixo de conversão, 1095

s, caractere de conversão, 1094

I, caractere de sufixo de conversão, 1095

S, caractere de conversão, 1094

índice de argumentos, 1096 j, caractere de sufixo de conversão, 1095 k, caractere de sufixo de conversão, 1095 l, caractere de sufixo de conversão, 1095 m, caractere de sufixo de conversão, 1095 M, caractere de sufixo de conversão, 1095 n, caractere de conversão, 1096 notação científica computadorizada, 1092 notação exponencial, 1092 o, caractere de conversão, 1092

S, caractere de sufixo de conversão, 1095 t, caractere de conversão, 1094 T, caractere de conversão, 1094 T, caractere de sufixo de conversão, 1095 texto fixo, 1091 x, caractere de conversão, 1092 X, caractere de conversão, 1092 y, caractere de sufixo de conversão, 1095 Y, caractere de sufixo de conversão, 1095 Z, caractere de sufixo de conversão, 1095

Exercícios de autorrevisão G.1

Preencha as lacunas em cada uma das alternativas ou em cada um dos itens: a) Lidamos com todas as entradas e saídas na forma de ________. b) O fluxo ________ normalmente está conectado ao teclado. c) O fluxo ________ normalmente está conectado à tela do computador. d) O método ________ de System.out pode ser utilizado para formatar texto que é exibido na saída padrão. e) O caractere de conversão ________ pode ser utilizado para gerar saída de um inteiro decimal. f) Os caracteres de conversão ________ e ________ são utilizados para exibir inteiros na forma octal e hexadecimal, respectivamente. g) O caractere de conversão ________ é utilizado para exibir um valor de ponto flutuante na notação exponencial. h) Os caracteres de conversão e e f são exibidos com os dígitos ________ de precisão à direita do ponto de fração decimal se nenhuma precisão for especificada. i) Os caracteres de conversão ________ e ________ são utilizados para imprimir strings e caracteres, respectivamente. j) O caractere de conversão ________ e o caractere de sufixo de conversão ________ são utilizados para imprimir tempo no relógio de 24 horas como hour:minute:second. k) O flag ________ faz com que a saída seja alinhada à esquerda em um campo. l) O flag ________ faz com que os valores sejam exibidos com um sinal de adição ou um sinal de subtração. m) O índice de argumentos ________ corresponde ao segundo argumento na lista de argumentos. n) A classe ________ tem a mesma capacidade de printf, mas permite que programadores imprimam a saída formatada para vários destinos além do fluxo de saída padrão.

G.2

Localize o erro em cada um dos seguintes itens e explique como eles podem ser corrigidos. a) A instrução a seguir deve imprimir o caractere 'c'. System.out.printf( "%c\n", "c" ); b) A instrução a seguir deve imprimir 9,375%. System.out.printf( "%.3f%", 9.375 ); c) A instrução a seguir deve imprimir o terceiro argumento na lista de argumentos. System.out.printf( "%2$s\n", "Mon", "Tue", "Wed", "Thu", "Fri" ); d) System.out.printf( ""A string in quotes"" ); e) System.out.printf( %d %d, 12, 20 ); f) System.out.printf( "%s\n", 'Richard' );

G.3

Escreva uma instrução para cada um dos seguintes tópicos: a) Imprima 1234 alinhado à direita em um campo de 10 dígitos. b) Imprima 123,456789 na notação exponencial com um sinal (+ ou -) e 3 dígitos de precisão. c) Imprima 100 na forma octal precedida por 0. d) Dado um objeto calendar de Calendar, imprima uma data formatada como mês/dia/ano (cada com dois dígitos). e) Dado um objeto calendar de Calendar, imprima uma hora no relógio de 24 horas como hour:minute:second (cada com dois dígitos) utilizando o índice de argumentos e caracteres de sufixo de conversão para formatar a hora. f) Imprima 3,333333 com um sinal (+ ou -) em um campo de 20 caracteres com uma precisão de 3.



Respostas dos exercícios de autorrevisão

1107

Respostas dos exercícios de autorrevisão G.1

a) Fluxos. b) Entrada padrão. c) Saída padrão. d) printf. e) d. f) o, x ou X. g) e ou E. h) 6. i) s ou S, c ou C. j) t, T. k) - (sinal de subtração). l) + (sinal de adição). m) US$ 2. n) Formatter. G.2 a) Erro: o caractere de conversão c espera um argumento do tipo primitivo char. Correção: para imprimir o caractere 'c', altere "c" para 'c'. b) Erro: tentar imprimir o caractere literal % sem utilizar o especificador de formato %%. Correção: utilize %% para imprimir um caractere % literal. c) Erro: o índice de argumentos não inicia em 0; por exemplo, o primeiro argumento é 1$. Correção: para imprimir o terceiro utilize o argumento 3$. d) Erro: tentar imprimir o caractere literal " sem utilizar a sequência de escape \". Correção: substitua cada citação no conjunto interno de aspas por \". e) Erro: a string de formato não é incluída entre aspas duplas. Correção: inclua %d %d entre aspas duplas. f) Erro: a string a ser impressa é incluída entre aspas simples. Correção: utilize aspas duplas em vez de aspas simples para representar uma string. G.3 a) System.out.printf( "%10d\n", 1234 ); b) System.out.printf( "%+.3e\n", 123.456789 ); c) System.out.printf( "%#o\n", 100 ); d) System.out.printf( "%tD\n", calendar ); e) System.out.printf( "%1$tH:%1$tM:%1$tS\n", calendar ); f) System.out.printf( "%+20.3f\n", 3.333333 );

Exercícios G.4

Escreva a(s) instrução(ões) para cada um dos seguintes itens: a) Imprima o inteiro 40000 alinhado à direita em um campo de 15 dígitos. b) Imprima 200 Com e sem sinal. c) Imprima 100 na forma hexadecimal precedido por 0x. d) Imprima 1,234 com três dígitos de precisão em um campo de 9 dígitos com zeros no início.

G.5

Mostre o que é impresso por cada uma das instruções a seguir. Se uma instrução estiver incorreta, indique por quê. a) System.out.printf( "%-10d\n", 10000 ); b) System.out.printf( "%c\n", "This is a string" ); c) System.out.printf( "%8.3f\n", 1024.987654 ); d) System.out.printf( "%#o\n%#X\n", 17, 17 ); e) System.out.printf( "% d\n%+d\n", 1000000, 1000000 ); f) System.out.printf( "%10.2e\n", 444.93738 ); g) System.out.printf( "%d\n", 10.987 );

G.6

Encontre o(s) erro(s) em cada um dos segmentos de programa a seguir. Mostre a instrução corrigida. a) System.out.printf( "%s\n", 'Happy Birthday' ); b) System.out.printf( "%c\n", 'Hello' ); c) System.out.printf( "%c\n", "This is a string" ); d) A seguinte instrução deve imprimir "Bon Voyage" com aspas duplas:

System.out.printf( ""%s"", "Bon Voyage" );

e) A seguinte instrução imprime "Today is Friday":

System.out.printf( "Today is %s\n", "Monday", "Friday" );

f) System.out.printf( 'Enter your name: ' ); g) System.out.printf( %f, 123.456 ); h) A instrução a seguir deve imprimir a hora atual no formato "hh:mm:ss":

G.7



Calendar dateTime = Calendar.getInstance();



System.out.printf( "%1$tk:1$%tl:%1$tS\n", dateTime );

(Imprimindo datas e horas) Escreva um programa que imprime datas e horas nos formatos a seguir: GMT-05:00 04/30/04 09:55:09 AM GMT-05:00 April 30 2004 09:55:09

1108

Capítulo G  Saída formatada 2004-04-30 day-of-the-month:30 2004-04-30 day-of-the-year:121 Fri Apr 30 09:55:09 GMT-05:00 2004

[Nota: dependendo da sua localização, talvez haja outros fusos horários além do GMT-05:00.] G.8

Escreva um programa para testar os resultados da impressão do valor inteiro 12345 e do valor de ponto flutuante 1,2345 em vários campos de vários tamanhos.

G.9

(Arredondando números) Escreva um programa que imprime o valor 100,453627 arredondado para o dígito mais próximo, décimo, centésimo, milésimo e décimo de milésimo.

G.10 Escreva um programa que gera a saída de uma palavra digitada partir do teclado e determina seu comprimento. Imprima a palavra utilizando um tamanho duas vezes maior que o comprimento da largura de campo.

G.11 (Convertendo temperaturas Fahrenheit em Celsius) Escreva um programa que converte valores inteiros de temperatura em Fahrenheit entre 0 e 212 graus para temperaturas em graus Celsius de pontos flutuantes com três dígitos de precisão. Utilize a fórmula celsius = 5.0 / 9.0 * ( fahrenheit - 32 );



para realizar o cálculo. A saída deve ser impressa em duas colunas alinhadas à direita, cada coluna deve conter 10 caracteres cada e as temperaturas em graus Celsius devem ser precedidas por um sinal para valores positivos e negativos.

G.12 Escreva um programa para testar todas as sequências de escape na Figura G.23. Para as sequências de escape que movem o cursor, imprima um caractere antes e depois da sequência de escape de modo que fique claro o local para onde o cursor se moveu.

G.13 Escreva um programa que utiliza o caractere de conversão g para gerar a saída do valor 9876,12345. Imprima o valor com precisões no intervalo entre 1 e 9.

Apêndices na Web

Os apêndices a seguir estão disponíveis em www.prenhall.com/deitel_br como documentos PDF: • Apêndice H, “Sistemas de numeração” • Apêndice I, “GroupLayout” • Apêndice J, “Java Desktop Integration Components ( JDIC)” • Apêndice K, “Mash ups” • Apêndice L, “Unicode®” • Apêndice M, “Criando documentação com javadoc” • Apêndice N, “Manipulação de bits” • Apêndice O, “Instruções break e continue rotuladas” • Apêndice P, “UML 2: Tipos de diagramas adicionais” • Apêndice Q, “Padrões de design” Esses arquivos podem ser visualizados no programa Adobe® Reader® (get.adobe.com/reader). Se você ainda não estiver registrado em seu site Web, dirija-se a www.deitel.com e clique no link Register- abaixo de nosso logotipo no canto superior esquerdo da página. Preencha suas informações. Não há nenhum custo para se registrar e não compartilhamos nossas informações com ninguém. Enviaremos somente correios eletrônicos de gerenciamento de conta a não ser que você se registre separadamente em nosso boletim por e-mail gratuito Deitel® Buzz Online em www.deitel.com/newsletter-/subscribe.html. Depois de se registrar, você receberá um correio eletrônico de confirmação com o seu código de verificação. Você precisará desse código para fazer a assinatura em www.deitel.com pela primeira vez. Configure seu cliente de correio eletrônico para permitir e-mails da deitel.com, de modo a assegurar que o e-mail de confirmação não seja filtrado com mensagem maliciosa. Em seguida, dirija-se a www.deitel.com e faça a assinatura utilizando o link Login debaixo de nosso logotipo no canto superior esquerdo da página. Dirija-se a www.deitel.com/books/jhtp8/. Você encontrará os links para esses apêndices sob o título Download Code Examples and Other Premium Content for Registered Users na coluna esquerda da página.

Índice remissivo Símbolos ^=, operador de atribuição OU exclusivo sobre

bits, LI ^, OU exclusivo sobre bits, XLIII ^, OU lógico booleano exclusivo, operador, 136, 138 tabela-verdade, 138 _, caractere curinga de SQL, 904, 905 -, (sinal de subtração), flag de formatação, 126 -, flag, 1099 -=, operador de atribuição de subtração, 102 ,, (vírgula), flag de formatação, 127 !, NÃO lógico, operador, 136, 138 tabela-verdade, 138 !=, não igual a, 43 ? (argumento de tipo curinga), 688 ?:, operador condicional ternário, 86, 104 ., ponto separador, 60 ‚ (vírgula), flag, 1099 ", aspa dupla, sequência de escape, 35 (, flag, 1099 {, chave esquerda, 31 }, chave direita, 31 @, símbolo, XXXIV *, caractere curinga de SQL, 903 *, curinga em um nome de arquivo, 60 *, multiplicação, 41, 42 *=, operador de atribuição de multiplicação, 102 /, barras em tags de fechamento, 735 /** */, comentário de documentação Java, XXXIV //** */, comentário de documentação Java, 30 /* */, comentário tradicional, 29 /=, operador de atribuição de divisão, 102 //, comentário de fim de linha, 29 \", 1103 \’, sequência de escape de caractere de aspas simples, 1103 \b, sequência de escape, 1103 \f, avanço de formulário, sequência de escape, 1103 \n, nova linha, sequência de escape, 35, 1103 \r, retorno de carro, sequência de escape, 35, 1103 \t, tabulação horizontal, sequência de escape, 35 \\, caractere de barra invertida, sequência de escape, 1103 &&, E condicional, operador, 136 tabela-verdade, 136

&, E lógico booleano, operador, 136, 137 &, E sobre bits, XLIII &=, operador de atribuição E sobre bits, LI #, caractere, XXXIX #, flag, 1099, 1100 %, módulo, 41, 42 %, caractere de conversão, 1096 %a, especificador de formato, 1093 %A, especificador de formato, 1093 %b, especificador de formato, 138, 1096, 1097 %B, especificador de formato, 1096 %c, especificador de formato, 54, 1094 %C, especificador de formato, 1094 %, caractere curinga de SQL, 904 %d, especificador de formato, 39, 1091, 1092 %e, especificador de formato, 1092 %E, especificador de formato, 1092 %%, especificador de formato, 1097 %f, especificador de formato, 53, 71, 1093 %g, especificador de formato, 1093 %G, especificador de formato, 1093 %h, especificador de formato, 1097 %H, especificador de formato, 1096 %n, especificador de formato, 1097 %n, formatar o especificador (separador de

linha), 562

%o, especificador de formato, 1092 %=, operador de atribuição de resto, 102 %s, especificador de formato, 36, 1091, 1094 %S, especificador de formato, 1094 %t, especificador de formato, 1094 %T, especificador de formato, 1094 %x, especificador de formato, 1092 %X, especificador de formato, 1092 +, adição, 41, 42 +=, operador de atribuição de adição, 131 +, flag, 1099, 1100 +=, operador de atribuição de adição, 102 +=, operador de atribuição de concatenação de

strings, 527

+=, operador de atribuição de subtração, 102 ++, pré-incremento/pós-incremento, 102 =, operador de atribuição de deslocamento para a direita com sinal, LI >>>=, operador de atribuição de deslocamento para a direita sem sinal, LI ||, OU condicional, operador, 136, 137 tabela-verdade, 137 |, OU inclusivo lógico booleano, operador, 136, 137 |=, operador de atribuição OU inclusivo sobre bits, LI |, OU inclusivo sobre bits, XLIII ~, (complemento de bits), XLIII >>, deslocamento para a direita com sinal,

Números 0, flag, 196 0, formato, flag, 244 0x, (prefixo hexadecimal), 1100 0 (zero), flag, 1099, 1101

15, jogo do, exercício, 766 127.0.0.1, (endereço de IP localhost), 871, 948

A abordagem de blocos de construção para criar programas, 7, 16 abordagem de código ativo, 2 abordagem de dividir para conquistar, 591 abordagem iterativa (não recursiva), 591

abreviações em inglês,  5 abreviando expressões de atribuição,  102 abrir um arquivo,  554 abs, método de Math,  157 absolute, método de ResultSet,  919 abstract, palavra-chave,  309 AbstractButton, classe,  435, 438, 777 addActionListener, método,  438 addItemListener, método,  440 isSelected, método,  779 setMnemonic, método,  777 setRolloverIcon, método,  438 setSelected, método,  778 AbstractCollection, classe,  664 Abstract Factory, padrão de design,  LIX, LX, LXVII AbstractList, classe,  664 AbstractMap, classe,  664 AbstractPageBean, classe,  954, 958, 1050 AbstractQueue, classe,  664 AbstractSequentialList, classe,  664 AbstractSessionBean, classe,  979 AbstractSet, classe,  664 AbstractTableModel, classe,  915, 919 fireTableStructureChanged, método,  919 Abstract Window Toolkit (AWT),  423 pacote,  163 Abstract Window Toolkit Event Package,  163 ação,  85, 88, 951 ação a executar,  82 ação de um objeto,  375 accept, método da classe ServerSocket,  866, 871 Account, classe (estudo de caso ATM),  365, 367, 370, 372, 377, 382, 383, 384, 385, 386, 406 acessibilidade,  425 acesso a pacotes,  267 acesso a pacotes, métodos,  267 acesso simultâneo à Collection por múltiplas threads,  663 acoplamento,  LXV ActionEvent, classe,  432, 435, 470, 751 getActionCommand, método,  433, 438 ActionListener, interface,  432, 435 actionPerformed, método,  432, 467, 470 actionPerformed, método da interface ActionListener,  432, 467, 470 ACTIVATED, constante da classe aninhada EventType,  865 Ada, linguagem de programação,  8, 804 Ada Lovelace,  8 Adapter, padrão de design,  LIX, LXIII addActionListener, método da classe AbstractButton,  438 da classe JTextField,  432 addAll, método Collections,  645, 652 List,  643 add, método ArrayList,  221, 862 BigInteger,  595 ButtonGroup,  442 JFrame,  300, 427 JFrame, classe,  107 JMenu,  777 JMenuBar,  778 LinkedList,  644 List,  641, 643 addFirst, método da classe LinkedList,  644 addGap, método da classe GroupLayout.Group,  XIII addGap, método da classe GroupLayout. ParallelGroup,  XIII addGap, método da classe GroupLayout. SequentialGroup,  XIII

Índice remissivo addItemListener, método da classe AbstractButton,  440 addKeyListener, método da classe Component,  459 addLast, método da classe LinkedList,  644 addListSelectionListener, método da classe JList,  447 addMouseListener, método da classe Component,  452 addMouseMotionListener, método da classe Component,  452

Add Binding Attribute,  959, 996, 1006

addPoint, método da classe Polygon,  501, 503 AddressBook, aplicativo, exercício de modificação

do,  1016

AddressBook, aplicativo, modificação do,  1016 addSeparator, método da classe JMenu,  778 addTab, método da classe JTabbedPane,  789 addTableModelListener, método de TableModel,  915 addTrayIcon, método da classe SystemTray,  XXIV addWindowListener, método da classe Window,  773

ADF Faces,  952 adiamento indefinido,  808, 839, 857 adição, operador de atribuição composta, +=,  102 Adicionando serialização de objeto ao aplicativo de desenho MyShape (exercício),  587 adicionando uma referência de serviço Web a um aplicativo,  1024 adicionar uma referência de serviço Web a um aplicativo NetBeans,  1025 adicionar um handler de evento em Netbeans,  XVI “Adivinhe o número”, jogo,  187, 479 adquirir o bloqueio,  812 Agendamento de evento,  XXVII agendamento de prioridade,  807 agendamento de rodízio,  807 agendamento preemptivo,  808 agendando threads,  806 Agile Alliance (www.agilealliance.org),  18 Agile Manifesto (www.agilemanifesto.org),  18 Agile Software Development,  18 Agregação de ouvintes de evento,  XXVIII agregação na UML,  369 agregação, símbolo (na UML),  89 .aif, extensão de arquivo,  756, 758 .aiff, extensão de arquivo,  756, 758 AIFF, formato de arquivo do Macintosh (extensões .aif ou .aiff),  756 Ajax, aplicativo Web,  1006 Ajax (Asynchronous JavaScript and XML),  18, 1005, 1006 Ajax, bibliotecas,  1005 Ajax, compatível com aplicativos Web,  xxii bibliotecas de componentes,  995 componentes JSF,  xxii alfabetando,  519 algoritmo,  82, 85, 89, 95, 596, LI classificação de bucket,  634 classificação por flutuação,  634 classificação por inserção,  625 classificação por intercalação,  628 classificação por seleção,  622 em Java Collections Framework,  645 pesquisa binária,  619 pesquisa binária recursiva,  635 pesquisa linear,  616 quicksort,  635 algoritmo de avaliação de expressão pós-fixa,  717 algoritmo de classificação por intercalação,  628, 632 algoritmo de classificação por seleção,  622, 625 algoritmo de conversão de infixo para pós-fixo,  717

1111

algoritmo de pesquisa binária,  619, 622, 651 algoritmo de pesquisa linear,  616, 622 algoritmos de classificação classificação de bucket,  634 classificação por flutuação,  634 classificação por inserção,  625 classificação por intercalação,  628 classificação por seleção,  622 quicksort,  635 algoritmos de pesquisa pesquisa binária,  619 pesquisa binária recursiva,  635 pesquisa linear,  616 pesquisa linear recursiva,  635 alinhado à direita,  461 alinhado à esquerda,  126, 428, 461 alinhamento à direita,  1091, 1099 alinhamento à esquerda,  1091 alinhando componentes em GroupLayout,  XIII alinhando pontos de fração decimal na saída,  1091 allClasses-frame.html, gerado por javadoc,  XLI alocação dinâmica de memória,  696 alterando a aparência e funcionamento de uma GUI baseada no Swing,  784 alto-falante,  756 altura de uma fonte,  493 altura de um retângulo em pixels,  487 ALU (arithmetic and logic unit),  3 Amazon,  XXVI Amazon Web Services,  XXVI ambiente de desenvolvimento integrado,  9 American Standard Code for Information Interchange (ASCII), conjunto de caracteres,  239 American Standard Code for Information Interchange, conjunto de caracteres (ASCII),  134 American Standard Code for Information Interchange, conjunto de caracteres ASCII,  553, XXIX amostras de cor,  491 Ampliação de Imagem, exercício,  765 ampliando,  765 analisar os requisitos de um projeto,  16 análise de texto,  546 análise e design orientado a objetos (objectoriented analysis and design — OOAD),  16 Analog Clock, exercício,  766 anchor, campo da classe GridBagConstraints,  793 âncora (a), elemento,  736 AND, (em SQL),  909 ângulos de arco negativo,  498 ângulos de arco positivo e negativo,  498 animação,  730, 743, 752, 765 www.animationfactory.com,  762 ângulos inicial de um arco,  498 animação, exercício,  765 animando uma série de imagens,  748 Animator, applet,  724 aninhamento,  85 aninhamento de instruções de controle,  84 Anotações,  xxiv @Consumes,  1029 @GET,  1029 injeção de dependência,  1037 @Override,  284 Path,  1028 @PathParam,  1029 @Produces,  1029 @Resource,  1037 @WebMethod,  1022

1112

Índice remissivo

@WebParam,  1022 @WebService,  1021

anular um método de superclasse,  281, 284 Apache Derby,  xxii, 926 Apache Software Foundation,  18 Apache Tomcat (tomcat.apache.org),  1021 Apagando aleatoriamente uma imagem, exercício,  765 aparência de blocos de construção,  142 aparência e comportamento,  423, 425, 460, 781 Nimbus,  420 aparência e comportamento de uma GUI baseada no Swing,  781 aparência e comportamento de um aplicativo,  423 aparência e funcionamento plugável (pluggable look-and-feel — PLAF),  425, 769 API (application programming interface),  37, 155 API de preferências,  661 APIs comumente utilizadas em mashups,  XXVII aplicativo,  29, 31, 59 argumentos da linha de comando,  158 aplicativo cliente/servidor distribuído,  4 aplicativo de consulta para o banco de dados books,  944 Aplicativo de desenho Interativo, exercício,  480 aplicativo de n camadas,  950 aplicativo de múltiplas camadas,  950 aplicativo de terminal (Mac OS X),  10 aplicativo robusto,  336 aplicativos comerciais,  552 aplicativo Web Ajax,  1006 tradicional,  1005 append, método da classe StringBuilder,  529 appendRow, método da classe CachedRowSetDataProvider,  1004 Apple Computer, Inc.,  4, XXIX applet,  723, 726, 732, 862 arquivo .class,  729 arrastável,  748 Applet, classe getAppletContext, método,  863 getAudioClip, método,  756 getCodeBase, método,  756 getParameter, método,  862 play, método,  756 showStatus, método,  753 applet-desc, elemento de um documento JNLP,  736 applet, elemento XHTML,  729 applet arrastável,  733, 748 AppletContext, interface,  860 showDocument, método,  860, 863 applet que desenha uma string,  728 applets, diretório applets de exemplo no JDK,  723 applets em domínio público,  862 appletviewer, contêiner de applets,  723, 724 Applet, menu,  725 Quit, item de menu,  725 Reload, item de menu,  725 application-desc, elemento de um documento JNLP,  736 height, atributo,  736 main-class, atributo,  736 name, atributo,  736 width, atributo,  736 ApplicationBean,  952 Aprimorando a classe Date (exercício),  275 Aprimorando a classe Time2 (exercício),  275 aprimorar desempenho da classificação por flutuação,  634 Arc2D, classe,  484

CHORD, constante,  506 OPEN, constante,  506 PIE, constante,  506 Arc2D.Double, classe,  503, 513

arco,  498, 724 arco, ângulo,  498 arco em forma de torta,  506 ArcTest, applet,  724 área ativa,  753 área de desenho personalizada,  455 área dedicada de desenho,  455 área de exibição,  729 área de um círculo,  187 área rígida de classe Box,  791 args, parâmetro,  218 argumento de tipo,  679 argumento para um método,  32, 61 argumentos reais de tipo,  674 ArithmeticException, classe,  337, 342 arquivo,  552, 553 arquivo binário,  554 arquivo de acesso sequencial,  552, 553, 558, 867 Arquivo de Calendário/Anotações, exercício de,  766 arquivo de classe,  33 arquivo de contas a receber,  587 arquivo de folha de pagamento,  553 arquivo de leitura,  574 arquivo de texto,  554 arquivo de transação,  586 arquivo mestre,  586 arquivos de intercalação de áudio/vídeo da Microsoft,  758 arrastando o mouse para destacar,  470 arrastar a caixa de rolagem,  444 array,  190, 552, 862, LI length, variável de instância,  191 passando arrays para métodos,  203 passando elementos de array para métodos,  203 array bidimensional,  209, 210 array bidimensional com três linhas e quatro colunas,  210 ArrayBlockingQueue, classe,  823, 830 size, método,  824 array classificado,  697 arraycopy, método da classe System,  219 array de apoio,  644 array de arrays de uma dimensão,  209, 210 array dinamicamente redimensionável,  862 ArrayIndexOutOfBoundsException, classe,  199, 336, 503 ArrayList, classe,  639, 651 ArrayList, classe genérica,  221, 687, 862, 1045 add, método,  221, 862 clear, método,  221 contains, método,  221, 222 get, método,  222 indexOf, método,  221 isEmpty, método,  253 remove, método,  221, 222 size, método,  222 toString, método,  688 trimToSize, método,  221 array multidimensional,  209, 210 array redimensionável,  862 implementação da List,  639 Arrays, classe,  219 asList, método,  644 binarySearch, método,  219 equals, método,  219 fill, método,  219 sort, método,  219, 619 toString, método,  541, 616

arredondando,  1091 arredondando um número,  41, 93, 157, 185 arredondar um número,  127 arredondar um número de ponto flutuante para fins de exibição,  98 artefatos do lado do cliente,  1025 artefatos do lado do servidor,  1021 Artist, exercício,  766 árvore,  656, 709, 726 árvore binária,  695, 713 classificação,  713 exclusão,  719 pesquisa,  719 árvore componente,  953 árvore de pesquisa binária,  709, 713 árvore fortemente empacotada,  714 árvore fortemente equilibrada,  714 ascendente de uma fonte,  493 ASCII (American Standard Code for Information Interchange), conjunto de caracteres,  553 asList, método da classe Arrays,  644 asList, método de Arrays,  644 aspas duplas, ",  32, 35 aspas francesas (« e ») na UML,  69 aspas simples, caractere,  516, 904 assembler,  5 assert, instrução,  354, 1069 AssertionError, classe,  354 assertiva,  354 assinatura,  175 assinatura de um método,  175 associação,  16 associação (na UML),  368, 369, 393 nome,  368 associação na UML,  16 associatividade da direita para a esquerda,  104 da esquerda para a direita,  104 associatividade de operadores,  41, 46 da direita para a esquerda,  41 da esquerda para a direita,  46 associatividade dos operadores,  104 asterisco (*),  41 asterisco (*), caractere curinga de SQL,  903 ativação em um diagrama de sequência na UML,  385 atividade (na UML),  83, 365, 374, 375 atividades paralelas,  8 ativo de software,  16 ATM, classe (estudo de caso ATM),  368, 371, 372, 374, 377, 382, 383, 384, 391 ATM (automated teller machine), estudo de caso,  361, 364 ATMCaseStudy, classe (estudo de caso ATM),  415 ATM, sistema,  365, 366, 370, 374, 377, 391 ator no caso de uso na UML,  364 atribuir um valor a uma variável,  39 atributo,  391, 393 compartimento em um diagrama de classes,  372 declaração na UML,  372, 374 de tipo na UML,  373 de uma classe,  6 de um elemento XHTML,  729 de um objeto,  15 na UML,  15, 60, 368, 370, 372, 373, 374, 375, 397 nome na UML,  373 atualização automática,  733 atualização de página parcial,  1006 .au, extensão de arquivo,  756, 758 AudioClip, interface,  756 loop, método,  756 play, método,  756

stop, método,  756 Áudio dinâmico e caleidoscópio gráfico, exercício,  766 Australian Botanical Gardens (www.anbg.gov.au/ anbg/index.html),  761 authorISBN, tabela do banco de dados books,  901 Author:, nota,  XXXVII -author, opção,  XL authors, tabela do banco de dados books,  901 @author, tag javadoc,  XXXVII autoboxing,  534, 638, 676 autoboxing um int,  676 auto commit, estado,  939 autoComplete, atributo de um Text Field,  1010 AutoComplete, interface,  1012 getOptions, método,  1010, 1012 autoComplete, propriedade de um componente JSF Text Field,  1006 autocomplete, campo de texto,  1006 autoCompleteExpression, atributo de um Text Field,  1010 autodocumentando,  38 Automatic Jigsaw Puzzle Generator, exercício,  766 autoridade certificadora,  733 auto-unboxing,  638 avaliação da esquerda para a direita,  42 avaliação e certificação em Java,  xxvii avaliando expressões,  717 .avi, extensão de arquivo,  758 await, método da interface Condition,  836, 838 awaitTermination, método da interface ExecutorService,  814 AWT (Abstract Window Toolkit),  423 componentes,  423 AWTEvent, classe,  433

B B2B (business-to-business), transações,  1019 Babbage, Charles,  8 b, caractere de conversão,  1096 B, caractere de conversão,  1096 Background Audio, exercício,  765 Backpack,  XXVII Backpack API,  XXVII BalanceInquiry, classe (estudo de caso ATM),  367, 370, 371, 372, 373, 375, 377, 383, 384, 385, 393, 395, 396 Balking, padrão de design,  LIX, LXVII banco de dados,  553, 899, 903 tabela,  900 banco de dados MySQL,  911 banco de dados relacional,  899, 900 BankDatabase, classe (estudo de caso ATM),  367, 370, 372, 377, 378, 382, 383, 384, 385, 386, 391, 393 BarChart, applet,  724 barra de asteriscos,  196 barra de menus,  419, 773, 777 de um JComboBox,  444 barra de rolagem horizontal, diretiva,  470 barra de status,  728 barra de título,  419, 425 barra de título de janela interna,  786 barra de título de uma janela,  422, 772 barra invertida, sequência de escape (\\),  35 barras invertidas (\),  1103 base,  XLIII baseado em evento,  428 BASELINE, constante de alinhamento em GroupLayout,  XIII

Índice remissivo BasePlusCommissionEmployee, classe que estende CommissionEmployee,  316

Basic (Beginner's All-Purpose Symbolic Instruction Code),  8 Basic (Beginner’s All-Purpose Symbolic Instruction Code),  695 Basic Latin, bloco,  XXXIII BasicStroke, classe,  484, 505, 506 CAP_ROUND, constante,  506 JOIN_ROUND, constante,  506 BCPL, linguagem de programação,  6 bean de página,  952 Beginner's All-Purpose Symbolic Instruction Code (Basic),  8 Bell Laboratories,  6 biblioteca de classes,  279, 297 biblioteca de classes Java,  155 biblioteca de tags,  951, 953 bibliotecas de tags personalizados,  951 BigDecimal, classe,  70, 127, 593 documentação (java.sun.com/javase/6/docs/api/ java/math/BigDecimal.html),  127 BigInteger, classe,  593, 841 add, método,  595 compareTo, método,  594 multiply, método,  594 ONE, constante,  594, 595 subtract, método,  595 ZERO, constante,  595 binário,  188 sistema numérico de base 2,  II binarySearch, método de Arrays,  219, 221 de Collections,  645, 651, 652 BindException, classe,  871 BindingProvider, interface,  1045 getRequestContext, método,  1045 bit (dígito binário),  552 BitSet, classe,  XLIII, LI and, método,  LI clear, método,  LI equals, método,  LI get, método,  LI or, método,  LI set, método,  LI size, método,  LI toString, método,  LI xor, método,  LI bits vagos,  XLIX bitwise, operadores,  136, XLIV ^, (OU exclusivo sobre bits),  XLIII &, (AND sobre bits),  XLIII , (deslocamento para a direita com sinal),  XLIII >>>, (deslocamento para a direita sem sinal),  XLIII |, (OU inclusivo sobre bits),  XLIII ~, (complemento),  XLIII Blackjack,  1035 jogo,  896 Web Service Modification,  1065 _blank, frame-alvo,  863 B, linguagem de programação,  6 Blink, applet,  724 BlockingQueue, interface,  823 put, método,  823, 824 take, método,  823, 824 bloco de construção aninhado,  144 Bloco de Notas,  8 bloco rotulado,  LIV blocos de construção,  82

1113

blocos de construção empilhados,  144 blog,  18 Blogger ATOM, feed,  XXVII Blogging,  XXVII blogosfera,  18 bloquear,  866 bloquear o incremento de um JSlider,  769 bloqueia até conexão ser recebida,  871 bloqueia um objeto,  827 bloqueio de monitor,  812 bloqueio intrínseco,  812 BlueJ (www.blueJ.org),  9 Boa prática de programação,  7 Boas práticas de programação, visão geral,  xxv body, elemento XHTML,  729 Bohm, C.,  83, 144 BOLD, constante da classe Font,  491, 492 Booch, Grady,  16 books, banco de dados,  900 relacionamentos de tabela,  902, 945 boolean

expressão,  86, 1082 promoções,  162 Boolean

atributo na UML,  371 classe,  638 boolean, tipo primitivo,  86, 1069, 1070, 1082 BorderLayout, classe,  300, 452, 459, 463, 470 CENTER, constante,  300, 452, 463, 465 EAST, constante,  300, 452, 463 NORTH, constante,  300, 452, 463 SOUTH, constante,  300, 452, 463 WEST, constante,  300, 452, 463 botão,  419, 435 botão de alternação,  435 botão de comando,  435 botão de estado,  438 botão de opção,  435, 440 botão de opção, grupo,  440 botão do meio do mouse,  454 botão do mouse,  725 botão, rótulo,  436 BOTH, constante da classe GridBagConstraints,  793 Box, classe,  470, 789, 791 createGlue, método,  792 createHorizontalBox, método,  470, 791 createHorizontalGlue, método,  791 createHorizontalStrut, método,  791 createRigidArea, método,  791 createVerticalBox, método,  791 createVerticalGlue, método,  791 createVerticalStrut, método,  791 X_AXIS, constante,  792 Y_AXIS, constante,  792 boxing, conversão,  638, 676 BoxLayout, classe,  470, 789 BoxLayout, gerenciador de layout,  789 braile, leitor de tela,  425 break,  1069 break, instrução,  132, 134, 153 break, instrução rotulada,  LIV saindo de uma aninhada for instrução,  LIV Bridge, padrão de design,  LIX, LXIII brilho,  491 browse, método da classe Desktop,  XXII buffer,  577, 818 buffer circular,  830 buffer compartilhado,  818 BufferedImage, classe,  506 createGraphics, método,  506 TYPE_INT_RGB, constante,  506 BufferedInputStream, classe,  577

1114

Índice remissivo

BufferedOutputStream, classe,  577, LXVIII flush, método,  577 BufferedReader, classe,  578 BufferedWriter, classe,  578

buffer limitado,  830 Builder, padrão de design,  LX Build, opção, no NetBeans,  1022 busca (fetch),  238 business-to-business (B2B), transações,  1019 ButtonGroup, classe,  440, 773, 778 add, método,  442 Button componente JSF primary, propriedade,  996 reset, propriedade,  996 Button, componente JSF,  961, 964 byte,  553 Byte, classe,  638 byte, palavra-chave,  1070 ByteArrayInputStream, classe,  577 ByteArrayOutputStream, classe,  577 byte, tipo primitivo,  129, 553, 1069, XLIII promoções,  162 Bytecode,  10, 33, 695

C cabeça de uma fila,  695, 707 cabeçalho da instrução de repetição for,  122 cabeçalho de método,  59 Caça-níqueis, exercício,  766 cache,  949 CachedRowSet, interface,  924, 998, 1001 close, método,  926 CachedRowSetDataProvider, classe,  998, 1004 appendRow, método,  1004 commitChanges, método,  1004 refresh, método,  1004 caixa eletrônico (automated teller machine — ATM),  361, 364 estudo de caso,  xxi interface com o usuário,  361 caixa de combinação,  419, 443 caixa de diálogo,  74, 421, 422, 777 caixa de diálogo modal,  422, 778 caixa de rolagem,  444 caixa de seleção,  435, 440 caixa de seleção, rótulo,  440 Calculadora de frequência cardíaca alvo,  80 calculadora de pegada de carbono,  26 cálculo aritmético,  41 cálculos,  4, 47, 83 cálculos fatoriais com um método recursivo,  592, 593 cálculos matemáticos,  7 cálculos monetários,  127 Calendar, classe,  1096 getInstance, método,  1096 Callable, interface,  851 call, método,  851 CallableStatement, interface,  938 call, método da interface Callable,  851 camada de cliente,  950, LXX camada de dados,  950 camada de informações,  950, LXIX camada em um aplicativo de múltiplas camada,  950 camada inferior,  950 camada intermediária,  950, LXIX camada superior,  950 caminho absoluto,  555, 556, 558 caminho geral,  506

caminho para um recurso,  948 caminho relativo,  555 campo,  16, 63, 553 valor inicial padrão,  65 campo de texto,  75 campo de uma classe,  172, 553 campos ocultos,  172 cancel, método da classe SwingWorker,  850 CANCEL_OPTION, constante de JFileChooser,  581 Cancel, botão,  75 CannotRealizePlayerException, exceção,  759 canRead, método de File,  556 canto superior esquerdo de um componente GUI,  105, 484 canWrite, método de File,  556 c, opção do comando jar,  734 capacidade da StringBuilder,  526 capacidade de reutilização,  678, 695 capacidade de reutilização de software,  156 capacity, método da classe StringBuilder,  527 CAP_ROUND, constante da classe BasicStroke,  506 Capítulo sobre arrays na Java Language Specification (java.sun.com/docs/books/ jls/third_edition/html/arrays.html),  299 captura uma exceção,  339 uma exceção de superclasse,  344 Capturando exceções com escopos externos, exercício,  359 Capturando exceções com superclasses, exercício,  359 Capturando exceções utilizando a classe Exception, exercício,  359 capture ou declare, requisito,  343 caractere,  163, 553 conjunto,  54 constante,  134 literal,  516 set,  553 caractere de barra (/) em tags de fechamento,  735 caractere de conversão,  1091 caractere de escape,  35, 908 caractere de espaço em branco,  30, 525, 535, 536 caractere de sufixo de conversão,  1094 a,  1095 A,  1095 b,  1095 B,  1095 c,  1094 d,  1095 D,  1095 e,  1095 F,  1095 H,  1095 I,  1095 j,  1095 k,  1095 l,  1095 m,  1095 M,  1095 p,  1095 P,  1095 r,  1095 R,  1095 S,  1095 T,  1095 y,  1095 Y,  1095 Z,  1095 caractere especial,  38, 516 caractere palavra (expressões regulares),  536

caracteres string de,  32 Caracteres aleatórios, exercício,  512 caracteres de conversão %,  1097 a,  1093 A,  1093 b,  1096, 1097 B,  1096 c,  1094 C,  1094 d,  1092 e,  1092 E,  1092 f,  1093 g,  1093 G,  1093 h,  1097 H,  1096 n,  1097 o,  1092 s,  1094 S,  1094 t,  1094 T,  1094 x,  1092 X,  1092 caracteres de conversão de inteiros,  1092 caractere separador,  558 caracteres finais de espaço em branco,  525 CardTest, applet,  724 carregador de classe,  10, 267, 427 carregando,  10 Carregando e executando um AudioClip,  756 carregando e exibindo uma imagem em um applet,  743 carregar outra página Web em um navegador,  753, 755 case, palavra-chave,  132 case, palavra-chave,  1069 CashDispenser, classe (estudo de caso ATM),  367, 369, 372, 377, 386, 404 caso básico,  591, 595, 599 caso de uso na UML,  364 casos de uso, diagrama na UML,  364, 365 casos de uso, modelagem,  364 cassino,  164, 168 catálogo de endereços, aplicativo Web multicamada baseado em banco de dados,  xxii catch

bloco,  340, 341, 342, 345, 348, 351 cláusula,  340, 1069 palavra-chave,  340 catch, bloco, correspondendo,  341 ceil, método de Math,  157 Celsius,  479, 1108 equivalente de uma temperatura em Fahrenheit,  187 CENTER, constante BorderLayout,  452, 463, 465 FlowLayout,  463 GridBagConstraints,  793 GroupLayout,  XIII centralizado,  461 certificado de segurança,  733 certificado digital,  733 Chain-of-Responsibility, padrão de design,  LIX, LXII, LXV chamada assíncrona,  384 chamada de método,  57, 156, 159 chamada por referência,  205 chamada por valor,  205



Índice remissivo

chamada recursiva indireta,  591 Chamadas de método feitas dentro da chamada fibonacci(3),  596 chamadas de método na pilha, método de execução do programa,  597 chamada síncrona,  384 Chamando atenção para uma imagem, exercício,  765 ChangeEvent, classe,  772 ChangeListener, interface,  772 stateChanged, método,  772 char

tipo primitivo,  129 char

array,  517 palavra-chave,  1069, 1070 promoções,  162 Character, classe,  516, 531, 638 charValue, método,  534 digit, método,  533 forDigit, método,  533 isDefined, método,  533 isDigit, método,  533 isJavaIdentifierPart, método,  533 isJavaIdentifierStart, método,  533 isLetter, método,  533 isLetterOrDigit, método,  533 isLowerCase, método,  533 isUpperCase, método,  533 static, conversão, métodos,  533 toLowerCase, método,  533 toUpperCase, método,  533 CharArrayReader, classe,  578 CharArrayWriter, classe,  578 charAt, método da classe String,  518 da classe StringBuilder,  528 char, tipo primitivo,  38 CharSequence, interface,  541 charValue, método da classe Character,  534 chat cliente-servidor,  868 chave de classificação,  615 chave de pesquisa,  615 chave direita, },  31, 37, 91, 97 chave esquerda, {,  31, 37 chave estrangeira,  901, 903 chave primária,  900, 902 chaves ({ e }),  88, 97, 122, 129, 193 não requerido,  131 chave/valor, par,  659, 976, 982 chegada de mensagem de rede,  342 CHORD, constante da classe Arc2D,  506 ciclo de execução de instrução,  238 ciclo de vida de software,  364 ciclo de vida de uma thread,  805 ciclo de vida do processamento de evento,  956, 958 destroy, método,  956, 958, 1004 init, método,  956, 958 preprocess, método,  956, 958 prerender, método,  956, 958 Círculos concêntricos que utilizam a classe Ellipse2D.Double exercício,  512 Círculos concêntricos que utilizam o método drawArc, exercício,  512 círculo sólido cercado por um círculo vazio (na UML),  84 círculo sólido incluído em um círculo aberto (para representar o final de um diagrama de atividades da UML),  375 círculo sólido (na UML),  84

círculo sólido (para representar um estado inicial em um diagrama da UML) na UML,  374, 375 Círculos que utilizam classe Ellipse2D.Double, exercício,  512 circunferência,  53, 741 CJK Unified Ideographs, bloco,  XXX clareza,  2, 11 .class, arquivo separado um para cada classe,  246 class,  553 Class, classe,  298, 320, 427, 919 getName, método,  298, 320 getResource, método,  427 .class, extensão de arquivo,  756 class, palavra-chave,  30, 59, 1069 .class, arquivo,  10, 33 ClassCastException, classe,  336, 677 classe,  7, 15, 155, 372, 377, 378, 391 arquivo,  33 campo,  63 class, palavra-chave,  59 construtor,  60, 68, 393 construtor padrão,  68 declaração,  30, 728 declarando com um método,  58 get, método,  248 instanciar um objeto,  58 nome,  30, 265, 393 ocultamento de dados,  64 set, método,  248 variável de instância,  58, 64, 158 classe abstrata,  306, 309, 311, 322, LXII classe adaptadora,  452 classe aninhada,  431, 784 relacionamento entre uma classe interna e sua classe de primeiro nível,  440 classe auto-referencial,  695, 696 classe básica,  279 classe concreta,  309 classe de applet compilado,  729 classe de caractere predefinida (expressões regulares),  536 classe definida pelo programador,  30 classe de primeiro nível,  431 classe derivada,  279 classe genérica,  221, 678 classe interior anônima,  432, 444, 456 classe interna,  431, 440, 456, 778 anônimo,  444 objeto de,  440 relacionamento entre uma classe interna e sua classe de primeiro nível,  440 classe não pode estender uma classe final,  321 classe parametrizada,  678 classe proprietária,  297 classe proxy de um serviço Web,  1022, 1025 Classe que utiliza grade Line2D.Double, exercício,  512 Classe que utiliza grade Rectangle2D.Double, exercício,  512 Classes AbstractButton,  435, 438, 777 AbstractCollection,  664 AbstractList,  664 AbstractMap,  664 AbstractPageBean,  954, 1050 AbstractQueue,  664 AbstractSequentialList,  664 AbstractSessionBean,  979 AbstractSet,  664 AbstractTableModel,  915, 919

ActionEvent,  432, 435, 470, 751 Arc2D,  484 Arc2D.Double,  503 ArithmeticException,  337 ArrayBlockingQueue,  823, 830

1115

ArrayIndexOutOfBoundsException,  336 ArrayList,  639, 651 ArrayList,  221, 222, 687, 862 Arrays,  219 AssertionError,  354 AWTEvent,  433 BasicStroke,  484, 505, 506 BigDecimal,  70, 127, 593 BigInteger,  593, 841 BindException,  871 BitSet,  XLIII Boolean,  638 BorderLayout,  452, 459, 463, 470 Box,  470, 789, 791 BoxLayout,  470, 789 BufferedImage,  506 BufferedInputStream,  577 BufferedOutputStream,  577 BufferedReader,  578 BufferedWriter,  578 ButtonGroup,  440, 773, 778 Byte,  638 ByteArrayInputStream,  577 ByteArrayOutputStream,  577 CachedRowSetDataProvider,  998, 1004 Calendar,  1096 ChangeEvent,  772 Character,  516, 531, 533, 638 CharArrayReader,  578 CharArrayWriter,  578 Class,  298, 320, 427, 919 ClassCastException,  336, 677 Collections,  639, 675 Color,  176, 484 Component,  424, 449, 486, 746, 752, 789, 796 ComponentAdapter,  453 ComponentListener,  460 Container,  424, 447, 459, 465, 467 ContainerAdapter,  453 Cookie,  976 DatagramPacket,  877, 893 DatagramSocket,  877 DataInputStream,  577 DataOutputStream,  577 Date,  1096 Desktop,  XXII Dimension,  752 Double,  638, 687 DriverManager,  913 Ellipse2D,  484 Ellipse2D.Double,  503 Ellipse2D.Float,  503 EmptyStackException,  655 EnumSet,  257 Error,  343 EventListenerList,  434 Exception,  343 ExecutionException,  842 Executors,  810 File,  555 FileInputStream,  555 FileOutputStream,  555 FileReader,  555, 578 FileWriter,  555 FilterInputStream,  577 FilterOutputStream,  577 Float,  638

1116

Índice remissivo

FlowLayout,  427, 461 FocusAdapter,  453 Font,  440, 484, 491 FontMetrics,  484, 494 Formatter,  555, 1091 Frame,  772 GeneralPath,  484, 506 GradientPaint,  484, 505 Graphics2D,  484, 503, 505 Graphics,  456, 484, 502, 747 GridBagConstraints,  792, 796 GridBagLayout,  789, 792, 796 GridLayout,  461, 466 GroupLayout,  460, XII GroupLayout.Group,  XIII GroupLayout.ParallelGroup,  XIII GroupLayout.SequentialGroup,  XIII Gson,  1033 HashMap,  658, 862, 976, 982, 983 HashSet,  656 Hashtable,  658 HttpServletRequest,  978 HttpServletResponse,  976 HyperlinkEvent,  863, 865 IllegalMonitorStateException,  825, 836 Image,  744 ImageIcon,  427, 744, 751 InetAddress,  871, 876, 879, LXVIII InputEvent,  450, 454, 458 InputMismatchException,  338 InputStream,  576, 866, 867 InputStreamReader,  578 Integer,  422, 638, 687 InterruptedException,  809 ItemEvent,  440, 442 JApplet,  728, 773 JAXB,  1029 JButton,  423, 436, 437, 465 JCheckBox,  423, 438 JCheckBoxMenuItem,  773, 778 JColorChooser,  489 JComboBox,  423, 443, 793 JComponent,  425, 427, 434, 443, 445, 455, 467,

484, 486, 752

JdbcRowSetImpl,  925 JDesktopPane,  784, 802 JDialog,  778 JEditorPane,  863 JFileChooser,  578 JFrame,  772 JInternalFrame,  784 JLabel,  423, 425 JList,  423, 445 JMenu,  773, 776, 786 JMenuBar,  773, 778, 786 JMenuItem,  773, 786 JOptionPane,  74, 421, 799 JPanel,  423, 455, 461, 467, 748, 770 JPasswordField,  429, 433 JPopupMenu,  779 JProgressBar,  848 JRadioButton,  438, 440, 442 JRadioButtonMenuItem,  773, 778 JScrollPane,  447, 470 JSlider,  769, 771, XIII JTabbedPane,  787, 792 JTable,  915 JTextArea,  459, 468, 469, 793, 795 JTextComponent,  429, 431, 468 JTextField,  423, 429, 431, 433, 468 JToggleButton,  438 KeyAdapter,  453

KeyEvent,  435, 458 Line2D,  484, 506 Line2D.Double,  503

LinearGradientPaint,  505 LineNumberReader,  578 LinkedList,  639 ListSelectionEvent,  445 ListSelectionModel,  447 Long,  638 MalformedURLException,  863 Manager,  758 Matcher,  516, 541 Math,  156, 157 MemoryImageSource,  765 MouseAdapter,  452 MouseEvent,  435, 449, 781 MouseMotionAdapter,  453, 456 MouseWheelEvent,  450 NullPointerException,  336 Number,  687 Object,  258 ObjectInputStream,  555, 866, 867, 871 ObjectOutputStream,  555 OutputStream,  576, 866, 867 OutputStreamWriter,  578 Pattern,  516, 541 PipedInputStream,  576 PipedOutputStream,  576 PipedReader,  578 PipedWriter,  578 PixelGrabber,  765 Point,  456 Polygon,  484, 500 PrintStream,  577 PrintWriter,  578 PriorityQueue,  655 Properties,  661, 981 RadialGradientPaint,  505 Random,  163, 164, 231 Reader,  578 Rectangle2D,  484 Rectangle2D.Double,  503 ReentrantLock,  835, 836 RoundRectangle2D,  484 RoundRectangle2D.Double,  503, 506 RowFilter,  924 RuntimeException,  343, 349 Scanner,  38, 62 ServerSocket,  866, 871, 887 ServiceManager,  746 Short,  638 Socket,  866, 876, 887, 888, LXVIII SocketException,  877 SplashScreen,  XXI SQLException,  913 SQLFeatureNotSupportedException,  919 Stack,  654 StackTraceElement,  351 String,  74, 516 StringBuffer,  526 StringBuilder,  516, 526 StringIndexOutOfBoundsException,  523, 528 StringReader,  578 StringWriter,  578, 1030 SwingUtilities,  784, 871 SwingWorker,  841 SystemColor,  505 SystemTray,  XXIV TableModelEvent,  923 TableRowSorter,  923 TexturePaint,  484, 505, 506 Thread,  807, 809

Throwable,  343, 349 Timer,  751, 752 TrayIcon,  XXIV TreeMap,  658 TreeSet,  656 Types,  914 UIManager,  784 UIViewRoot,  953 UnknownHostException,  867 UnsupportedOperationException,  644 URL,  755 ValidatorException,  970 Vector,  639 Window,  772 WindowAdapter,  453, 924 Writer,  578

classes adaptadoras utilizadas para implementar rotinas de tratamento de evento,  452 classes de evento,  433 classes empacotadoras de tipo,  532, 638, 675 implementa Comparable,  675 classes genéricas,  671 classes numéricas,  638 classe utilitária que exibe representação de bits de um inteiro,  XLV classificação de bucket,  634 classificação de dados,  615 classificação descendente (DESC),  905, 906 classificação por flutuação,  634 aprimorando o desempenho,  634 classificação por inserção,  625 algoritmo,  625, 627 classificação por intercalação,  628 classificação, técnicas,  724 classificando,  695 com a Comparator,  647 ordem decrescente,  646 classificando dados,  622 classpath,  267, 913 -classpath, opção de linha de comando para java,  267 -classpath, opção de linha de comando para javac,  267 CLASSPATH

informações (java.sun.com/javase/6/docs/

technotes/tools/index.html#general),  267 variável de ambiente,  34, 267 -classpath, linha de comando, argumento,  560 Clean and Build, opção, no NetBeans,  1022 Clean, opção, no NetBeans,  1022 clear, comando de depurador,  1088 clear, método de ArrayList,  221 de BitSet,  LI de List,  643 de PriorityQueue,  656 clearRect, método da classe Graphics,  496 clicando a caixa de fechamento,  802 clicar com o mouse,  438, 724, 725 clicar nas setas de rolagem,  444 clicar uma guia,  726 clicar um botão,  428 cliente,  859 de uma classe,  16, 377, 384 objeto,  LXVIII cliente, conexão,  866 cliente de um objeto,  66 C, linguagem de programação,  6 C#, linguagem de programação,  8 C++, linguagem de programação,  6 clip-art (www.clipart.com),  762 clipe de áudio,  756, 758, 761

clique com o botão central do mouse,  455 clique com o botão esquerdo do mouse,  454 clique de botão do mouse,  454 clique de mouse,  453 cliques com os botões esquerdo, central e direito do mouse,  453 clock,  724, 765 Clock, applet,  724 clonando objetos cópia em profundidade,  298 cópia superficial,  298 Cloneable, interface documentação (java.sun.com/j2se/5.0/docs/api/ java/lang/Cloneable.html),  LXXI clone, método de Object,  298 clone, objeto,  570 close, método da interface Connection,  915 da interface ResultSet,  915 de CachedRowSet,  926 de Connection,  915 de Formatter,  562 de interface Statement,  915 de JdbcRowSet,  926 de ObjectOutputStream,  574 de ResultSet,  915 de Socket,  867 de Statement,  915 closePath, método da classe GeneralPath,  508 Cobol (COmmon Business Oriented Language),  7 code, atributo de tag,  729 codebase, atributo do elemento jnlp,  735 codificação de caracteres,  XXIX código autodocumentado,  38 código de cliente,  307 código de operação,  236 código dependente de implementação,  245 código de tecla virtual,  460 código de versão do recurso,  345 código-fonte,  8, 297 código-fonte aberto, software,  xxii, 18 Código Morse,  548 Código Morse em Rede,  897 código, reutilização,  279, 637 coerção downcast,  307 operador,  54, 97, 163 coerção de tipo,  98 colaboração na UML,  382, 384 cola horizontal,  791 colchete angular () para elementos XML,  735 colchetes, [],  190, 198 colchetes angulares (< e >),  674 colchetes mais internos,  198 coleção,  221 coleções,  637 estrutura,  637 lineares,  696 não-modificáveis,  637, 639 sincronizadas,  639 coleções genéricas,  xxi coleta de lixo,  258, 805 automática,  345 coletando requisitos,  364 coletor de lixo,  258, 342, 345, 756 colisão de nomes,  265 colisão em uma tabela de hash,  659 Collection, interface,  637, 639, 641, 645 contains, método,  641 iterator, método,  641 Collections, classe,  639, 675 addAll, método,  645, 652

Índice remissivo binarySearch, método,  645, 651, 652 copy, método,  645, 650 disjoint, método,  645, 653

empacotador, métodos,  639 fill, método,  645, 650 frequency, método,  645, 653 max, método,  645, 650 min, método,  645, 650 reverse, método,  645, 650 reverseOrder, método,  646 shuffle, método,  645, 648, 649 sort, método,  645 Collections, métodos reverse, fill, copy, max e min,  650 colocando chamadas de método em linha,  251 colocar em uma pilha,  161 Color.BLACK,  176 Color.BLUE,  176 Color, classe,  176, 484 getBlue, método,  487, 489 getColor, método,  487 getGreen, método,  487, 489 getRed, método,  487, 489 setColor, método,  487 Color, constante,  486, 488 Color.CYAN,  176 Color.DARK_GRAY,  176 Color.GRAY,  176 Color.GREEN,  176 Color.LIGHT_GRAY,  176 Color.MAGENTA,  176 Color.ORANGE,  176 Color.PINK,  176 Color.RED,  176 Color.WHITE,  176 Color.YELLOW,  176 Colorindo fotografias e imagens em preto e branco, exercício,  766 coluna,  209, 900 coluna autoincrementada,  901, 908 coluna de identidade,  927 coluna em uma tabela de banco de dados,  900 colunas de um array bidimensional,  209 com.google.gson.Gson, pacote,  1033 com.sun.rave.web.ui.appbase, pacote,  954 com.sun.rowset, pacote,  925 com.sun.webui.jsf.component, pacote,  954 com.sun.webui.jsf.model, pacote,  1012 comentário de uma única linha,  31 de única linha (fim do arquivo),  31 fim de linha (de uma única linha), //,  29 Javadoc,  30, 947 tradicional (/* */ ),  29 comércio eletrônico,  XXVII comissão,  115, 230 comissões sobre vendas,  230 Command Prompt,  10 Command, padrão de design,  LIX, LXII, LXV CommissionEmployee, classe derivada de Employee,  315 commit, método da interface Connection,  939 commitChanges, método da classe CachedRowSetDataProvider,  1004 Comparable, interface,  645, 712 compareTo, método,  645 Comparable, interface,  329, 521, 675, 712 compareTo, método,  675 comparação lexicográfica,  520, 521 comparando objetos String,  519 Comparator, interface,  645 compare, método,  647

1117

Comparator, objeto,  645, 650, 657, 658 em sort,  645 compare, método de interface Comparator,  647 compareTo, método da classe String,  519, 521 de Comparable,  645 compareTo, método da classe BigInteger,  594 compareTo, método de Comparable,  675

compartilhamento de foto,  17, XXVII compartilhamento de tempo,  4 compartilhando vídeo,  17, XXVII compartimento,  60 em um diagrama de classe UML,  60 compartimento de operação em um diagrama de classes,  377 compensação entre espaço na memória e tempo de execução,  659 compilador,  5 compilando um aplicativo com múltiplas classes,  60 compilar,  33 compilar um programa,  10 compile, método da classe Pattern,  541 complemento de bits (~), operador,  XLIII, XLIV complemento de dois,  VIII complemento de um,  VIII, XLIX complemento lógico, !, operador,  138 Complex,  276 complexidade, teoria da,  596 Component, classe,  424, 449, 486, 490, 746, 752, 789, 796, LXIII, LXIV addKeyListener, método,  459 addMouseListener, método,  452 addMouseMotionListener, método,  452 documentação (java.sun.com/javase/6/docs/api/ java/awt/Component.html),  424 getHeight, método,  747 getMaximumSize, método,  XIII getMinimumSize, método,  752, 772, XIII getPreferredSize, método,  752, 772, XIII getWidth, método,  747 repaint, método,  456 setBackground, método,  490 setBounds, método,  460 setFont, método,  440 setLocation, método,  460, 772 setSize, método,  460, 772 setVisible, método,  465, 772 ComponentAdapter, classe,  453 componente,  6, 16, 163, 447, 724 componente de origem,  781 componente de um array,  190 componente GUI de peso leve,  424, 778 componente na UML,  16 componente opaco,  455 componente reutilizável padrão,  279 componentes de peso pesado,  424 componentes GUI Swing,  423 componentes Java puros,  423 Componentes JSF comumente utilizados,  961 componentes reutilizáveis de software,  6, 163, 279 ComponentListener, interface,  453, 460 comportamento,  15, 377 de uma classe,  6 de um objeto,  15 de um sistema,  374, 375, 376, 384 comportamento de sistema,  365 composição,  253, 279, 281, 369, 387 na UML,  369 composições de data e hora,  1094 Composite, padrão de design,  LIX, LXIII, LXIV comprimento da fila,  866

1118

Índice remissivo

computação,  2 computação cliente/servidor,  4 computação corporativa crítica,  336 computação de missão crítica,  336 computação distribuída,  4 computação pessoal,  4 computador,  2 computador cliente,  4 computadores na educação,  188 Computadorização dos registros de saúde,  80 comunicação baseada em pacotes,  859 comunicação baseada em socket,  859 concat, método da classe String,  524 concatenação,  160 concatenação de string,  160 concatenar strings,  260 conclusão de E/S de disco,  342 concorrência,  804 concorrência de conjunto de resultados,  918 CONCUR_READ_ONLY, constante,  919 CONCUR_UPDATABLE, constante,  919 condição,  43, 128 condição de guarda (na UML),  85, 375 condição dependente,  137 condição, objeto,  836 condição simples,  136 Condition, interface,  836 await, método,  836, 838 signalAll, método,  836 signal, método,  836 conectando tabelas de banco de dados,  901, 911 conectar ao servidor,  866, 867 conectar-se a um banco de dados,  911 conexão,  859, 867, 876, 877, 887, 888 conexão entre cliente e servidor termina,  867 conexão entre programa Java e banco de dados,  913 configurar (set) um valor,  66 configurar tratamento de eventos,  431 Configure Virtual Forms…,  1007 confinamento de thread,  840 confirmar uma transação,  939 conflito de nomes,  265 confundir o operador de igualdade == com o operador de atribuição =,  45 conjunto de caracteres,  553, XXIX conjunto de caracteres de dois bytes (double-byte character set — DBCS),  XXXI conjunto de caracteres multibyte (multibyte character set — MBCS),  XXXI Conjunto de chamadas recursivas para fibonacci(3),  595 conjunto de constantes como uma interface,  322 Conjunto de inteiros, exercício,  276 conjunto vazio,  276 Connection, interface,  913, 914, 918, 939 close, método,  915 commit, método,  939 createStatement, método,  914, 918 getAutoCommit, método,  939 prepareStatement, método,  929 rollBack, método,  939 setAutoCommit, método,  939 constante,  262 em uma interface,  329 Math.PI,  53 constante de enumeração,  171 constante de ponto flutuante,  125 constante identificado,  195 construindo seu próprio compilador,  695 construindo seu próprio computador,  236 construtor,  60, 68, 393

chamar outro construtor da mesma classe utilizando this,  250 lista de parâmetros,  68 múltiplos parâmetros,  70 sem argumento,  250 sobrecarregado,  248 construtores não podem especificar um tipo de retorno,  69 construtores sobrecarregados,  248 construtor padrão,  68, 252, 284 de uma classe interna anônima,  445 construtor sem argumento,  250, 251 consulta,  899, 900 consultar um banco de dados,  911 @Consumes, anotação,  1029 consumidor,  818 consumir um evento,  432 consumir um serviço Web,  1019, 1020, 1060 cont, comando de depurador,  1080 conta de poupança,  125 contador,  90, 94, 99 Contador de hits de página com ApplicationBean,  993 Contador de hits de página com ApplicationBean exercício,  993 Contador de hits de página com cookies,  993 Contador de hits de página com cookies, exercício,  993 contagem de cliques,  453 Container, classe,  424, 447, 459, 465, 467, LXIV documentação (java.sun.com/javase/6/docs/api/ java/awt/Container.html),  424 setLayout, método,  427, 460, 465, 792 validate, método,  467 ContainerAdapter, classe,  453 ContainerListener, interface,  453 contains, método de Collection,  641 contains, método da classe ArrayList,  221, 222 containsKey, método de interface Map,  661 contêiner de applets,  723, 730 contêiner de servlets,  950 contêiner para menus,  773 conteúdo dinâmico,  6 contexto de imagens gráficas,  484 continue, instrução,  134, 135, 153, 1069, LIV continue, instrução rotulada,  LIV terminando uma iteração única de uma rotulada - for instrução,  LV controlador (na arquitetura MVC),  LXIX controle de programa,  82 controles,  419 convergir para um caso de base,  591 conversão explícita,  98 conversão implícita,  98 conversão unboxing,  638 converter entre sistemas numéricos,  534 um número binário em decimal,  VI um número hexadecimal em decimal,  VI um número octal em decimal,  VI um valor integral em um valor de ponto flutuante,  162 cookie,  971, 977, 979 cabeçalho,  971 data de expiração,  971 domínio,  979 exclusão,  971 expiração,  971 Cookie, classe,  976 getDomain, método,  979 getMaxAge, método,  979

getName, método,  979 getPath, método,  979 getSecure, método,  979 getValue, método,  979 Cookies, array,  978

coordenada horizontal,  105, 484 coordenadas,  728 coordenadas (0, 0),  105, 484 coordenada vertical,  105, 484 coordenada x superior esquerda,  487 coordenada y superior esquerda,  487 coordenas em pixels,  729 cópia em profundidade,  298 copiando objetos cópia em profundidade,  298 cópia superficial,  298 cópia superficial,  298, 299 cópia temporária,  97 copy, método da classe Collections,  645, 650 cor,  484 cor de fundo,  489, 490 cores,  176 Cores aleatórias, exercício,  513 cor, manipulação,  484 corpo de uma declaração de classe,  31 de uma instrução if,  43 de um loop,  88 de um método,  31 corpo da definição de método,  32 correio eletrônico,  866 correspondência de arquivo com de serialização de objeto, exercício,  587 com múltiplas transações, exercício,  587 exercício,  586 programa,  586 correspondência de padrão,  904 Corrida de cavalos, exercício,  766 corrigir em um sentido matemático,  140 cos, método de Math,  157 co-seno,  157 co-seno trigonométrico,  157 Cozinhando com ingredientes mais sadios,  549 -cp, opção de linha de comando para java,  267 CPU,  4 CraigsList (www.craigslist.org),  18, XXVI craps (jogo de dados de cassino),  168, 188, 231 createGlue, método da classe Box,  792 createGraphics, método da classe BufferedImage,  506 createHorizontalBox, método da classe Box,  470, 791 createHorizontalGlue, método da classe Box,  791 createHorizontalStrut, método da classe Box,  791 createRealizedPlayer, método da classe Manager,  758 createRigidArea, método da classe Box,  791 createStatement, método de Connection,  914, 918 createVerticalBox, método da classe Box,  791 createVerticalGlue, método da classe Box,  791 createVerticalStrut, método da classe Box,  791 cresça e brilhe, algoritmo,  82 Crescimento demográfico mundial,  118 criando e inicializando um array,  193 criando um banco de dados Java DB em NetBeans,  996 criando um objeto de uma classe,  59 criar uma classe reutilizável,  264 criar um aplicativo desktop em NetBeans,  1025 criar um aplicativo Web em NetBeans,  1020 criar um Socket,  867 criptografar,  118

critérios de seleção,  904 Crivo de Eratóstenes,  234, 847 Crivo de Eratóstenes, utilizando um BitSet,  LI cross-site scripting,  1030 CSS (Cascading Style Sheets) folha de estilo,  953, 957 tutoriais (www.deitel.com/CSS21/),  860 ctrl, tecla,  131 -d,  131 Ctrl, tecla,  447, 460 -z,  131 curinga,  688 argumento de tipo,  688 em um parâmetro de tipo genérico,  687 limite superior,  688 currentThread, método da classe Thread,  813 currentTimeMillis, método da classe System,  613 cursor,  31, 32 cursor de saída,  32, 34 cursor de tela,  35 cursos avançados de ciência da computação,  xxvii curto-circuito, avaliação,  137 curva,  506, 724 curva complexa,  506

D -d, opção de compilador,  265 dados,  3 dados compartilhados de acesso,  823 dados consistentes,  243 dados de template fixa,  951 dados mutáveis,  817 dados persistentes,  552 Damas, jogo,  896 data,  163 data de expiração de um cookie,  971 DatagramPacket, classe,  877, 893 getAddress, método,  879 getData, método,  879 getLength, método,  879 getPort, método,  879 DatagramSocket, classe,  877 receive, método,  879 send, método,  879 DataInput, interface,  577 DataInputStream, classe,  577 DataOutput, interface,  577 writeBoolean, método,  577 writeByte, método,  577 writeBytes, método,  577 writeChar, método,  577 writeChars, método,  577 writeDouble, método,  577 writeFloat, método,  577 writeInt, método,  577 writeLong, método,  577 writeShort, método,  577 writeUTF, método,  577 DataOutputStream, classe,  577 Date, classe,  1096 exercício,  276 DateAndTime, classe, (exercício),  276 DBCS (double byte character set),  XXXI decimal (sistema numérico de base 2,  II decisão,  43, 85 símbolo (na UML),  85 símbolo na UML,  375 decisão lógica,  2 decisões, tomando,  48 declaração

Índice remissivo classe,  30 import,  37, 38 método,  30 declaração de interface,  322 declaração de método,  159 declaração import do tipo por demanda,  266 declaração import do tipo simples,  266 declarar um método de uma classe,  58 Decorator, padrão de design,  LIX, LXI, LXVIII default, caso em um switch,  132, 133, 167 default, palavra-chave,  1069 definir uma área de desenho personalizada,  455 [email protected],  3 Deitel Buzz Online, boletim,  19 Deitel Buzz Online Newsletter (www.deitel.com/ newsletter/subscribe.html),  3, 18 Deitel Resource Centers (www.deitel.com/ ResourceCenters.html),  3, 18 Deitel, site Web (www.deitel.com),  2 delegar uma chamada de método,  706 DELETE, instrução SQL,  909 delete, método da classe StringBuilder,  530 DELETE, SQL, instrução,  903 deleteCharAt, método da classe StringBuilder,  530 del.icio.us,  17, XXVI delimitador para tokens,  535 demo, diretório,  726 demo de imagens gráficas,  726 demo de imagens gráficas bidimensionais,  726 demo de pacote de navegador,  XXV demo Wallpaper API,  XXV Department of Defense (DOD),  8 dependência de plataforma,  808 dependências entre capítulos, gráfico de,  xxi, xxiv dependente de estado,  818 dependente de máquina,  5 Deploy, opção, no NetBeans,  1022 Deposit, classe (estudo de caso ATM),  367, 369, 371, 372, 377, 383, 384, 388, 393, 395 DepositSlot, classe (estudo de caso ATM),  369, 372, 377, 384, 393 deprecated-list.html, gerado por javadoc,  XLI Deprecated, nota,  XL Deprecated, link na API,  1072 @deprecated, tag javadoc,  XL depurador,  1078 clear, comando,  1088 cont, comando,  1080 definição,  1078 erro de lógica,  1078 exit, comando,  1084 -g, opção de compilador,  1079 inserindo pontos de interrupção,  1080 jdb, comando,  1079 modo de interrupção,  1080 next, comando,  1084 ponto de interrupção,  1078 print, comando,  1081, 1082 run, comando,  1079, 1081 set, comando,  1081, 1082 step, comando,  1083 step up, comando,  1083 stop, comando,  1080, 1081 suspendendo a execução do programa,  1081 unwatch, comando,  1086 watch, comando,  1085 depurar,  7 descendente de uma fonte,  493 descoberta automática de driver ( JDBC 4),  xxii, 913 descobrindo um componente,  486 descrição de um serviço Web,  1024

1119

descriptografar,  118 desempenho da classificação e pesquisa na árvore binária,  721 desempilhando,  348 desempilhar a pilha de chamadas de método,  348 desencadear um evento,  423 desenhando na tela,  484 desenhar imagens gráficas,  728 desenhar uma curva complexa,  724 desenhar um arco,  724 desenhar um retângulo,  732 desenho, cores,  487 desenho, formas,  484 desenho, linhas e pontos,  724 desenvolvimento de aplicativo Web,  947 desenvolvimento de GUIs para diversas plataformas,  423 desenvolvimento rápido de aplicativos Web,  xxii Designer de fogos de artifício, exercício,  766 Design, modo no NetBeans,  956, 959, 996 design orientado a objetos (object-oriented design — OOD),  15 Design, visualização no Netbeans,  XIII design padrão,  LVIII, LIX, LXII, LXIII, LXIV, LXV, LXVII, LXXI Desktop, classe,  XXII browse, método,  XXII getDesktop, método,  XXII isDesktopSupported, método,  XXII mail, método,  XXII open, método,  XXII desktop, elemento de um documento JNLP,  735 deslocamento de bits para a esquerda (),  XLIII, XLIV, XLIX deslocamento para a direita sem sinal (>>>),  XLIII deslocar (números aleatórios),  165 despachar uma thread,  806 despacha um evento,  435 destroy, método de JApplet,  728, 731 JavaServer Faces,  956, 958, 1004 Determinando pontos C e D para nível 1 de “Fractal de Lo”,  602 diacrítico,  XXX diagrama da UML elidido,  368 diagrama de atividades,  83, 85, 124, 140 do...while, instrução,  128 for, instrução,  124 if...else, instrução,  85 if, instrução,  85 instrução sequencial,  83 na UML,  89, 365, 375, 389 switch, instrução,  134 while, instrução,  89 diagrama de atividades mais simples,  142 diagrama de atividades na estrutura de sequência,  83 diagrama de classes na UML,  365, 368, 369, 371, 372, 377, 391, 393, 396, 397 para o modelo do sistema ATM,  371, 387 diagrama de colaboração na UML,  366, 384 diagrama de comunicação na UML,  366, 384 diagrama de estado na UML,  374 diagrama de estado para o objeto ATM,  374 diagrama de gráfico de estado do ciclo vida de threads,  805, 806 diagrama de interação na UML,  384 diagrama de máquina de estado na UML,  365, 374 diagrama de relacionamento de entidades,  902

1120

Índice remissivo

diagrama de sequência na UML,  366, 384 Dialog, font,  492 DialogInput, font,  492 diálogo,  74, 421 diálogo de entrada,  74, 421 diálogo de mensagem,  74, 421, 422 tipos,  422 diálogo de seleção de cor,  490 diálogo modal,  490 diâmetro,  53, 512, 741 Dica de desempenho,  7 Dica de portabilidade,  7 Dica de prevenção de erro,  7 Dicas de desempenho, visão geral,  xxv dicas de ferramenta,  425, 427 Dicas de portabilidade, visão geral,  xxv Dicas de prevenção de erro, visão geral,  xxv digitalização de imagens,  3 digit, método da classe Character,  533 digitando um campo de texto,  428 dígito,  38, 533, 536, II dígito binário (bit),  552 dígito decimal,  553 dígitos invertidos,  187 Dimension, classe,  752 dir, comando no Windows,  724 DIRECTORIES_ONLY, constante de JFileChooser,  580 diretiva,  951 diretiva de imparcialidade de um bloqueio,  835 diretivas de barra de rolagem,  470 diretório,  555, 556 árvore,  726 nome,  555 separador,  267 diretório-pai,  556 diretório-raiz,  555 diretório virtual,  948 diretrizes de projeto GUI recomendadas utilizadas por GroupLayout,  XIII disco,  3, 10, 552 disco óptico,  552 disjoint, método da classe Collections,  645, 653 dispose, método da classe Window,  772 DISPOSE_ON_CLOSE, constante de interface WindowConstants,  772 dispositivo de armazenamento secundário,  552 dispositivo eletrônico inteligente de consumo popular,  6 dispositivos de entrada,  3 dispositivos de saída,  3 dispositivos eletrônicos de consumo popular,  6 distância entre valores (números aleatórios),  167 distinção de letras maiúsculas e minúsculas,  30 comandos do Java,  12 distribuindo cartas,  199 DitherTest, applet,  724 dividir para conquistar, abordagem,  155, 156 dividir por zero,  10, 337 dividir um array na classificação por intercalação,  628 divisão de inteiro,  41 divisão de ponto flutuante,  98 divisão, operador de atribuição composta, /=,  102 divisão por zero,  94 DNS (domain name system), servidor,  948 DO_NOTHING_ON_CLOSE, constante de interface WindowConstants,  772 do...while, instrução de repetição,  84, 127, 128, 144, 1069 documentação, comentários,  XXXIV

documentação da estrutura de coleções (java.sun. com/javase/6/docs/technotes/guides/ collections/index.html),  637

Documentação da Java API download (java.sun.com/javase/downloads/),  11, 35, 40 java.sun.com/javase/6/docs/api/,  11, 35, 39, 1071 documentação de API (java.sun.com/javase/6/docs/ api/),  164 documentar um programa,  29 documento,  769, 784 documento de requisitos,  15, 361, 364 dois maiores valores,  116 Dojo Toolkit,  995, 1012 -d, opção,  XL (double), coerção,  98 Double, classe,  638, 687 parseDouble, método,  732 double, tipo primitivo,  38, 70, 95, 1069, 1070 promoções,  162 Double Range Validator, componente JSF,  964 doubleValue, método de Number,  688 downcast,  307, 319 draw3DRect, método da classe Graphics,  496, 498 draw, método da classe Graphics2D,  505 drawArc, método da classe Graphics,  224, 498, 512 drawImage, método da classe Graphics,  747 drawLine, método da classe Graphics,  107, 495 drawOval, método da classe Graphics,  144, 498, 741 drawPolygon, método da classe Graphics,  501, 502 drawPolyline, método da classe Graphics,  501, 503 drawRect, método da classe Graphics,  144, 495, 506, 512, 741 drawRoundRect, método da classe Graphics,  496 drawString, método da classe Graphics,  488, 728, 732 DrawTest, applet,  724, 726 driver, classe,  59 driver JDBC,  899 DriverManager, classe,  913 getConnection, método,  913 Dropcash,  XXVII Drop Down List, componente JSF,  961, 964 dump de computador,  238 duplicata de datagrama,  877 duplo igual, ==,  45

E EAST, constante da classe BorderLayout,  452, 463 da classe GridBagConstraints,  793

eBay,  XXVII echo, caractere da classe JPasswordField,  429 Eclipse vídeo de demonstração (www.deitel.com/books/ jhtp8),  29 Eclipse Foundation,  18 Eclipse (www.eclipse.org),  9 ecoar um pacote de volta para o cliente,  877 eco, caractere,  429 Ecofont,  481 E condicional, &&, operador,  136, 137 tabela-verdade,  136 editar um programa,  9 editor,  8 editor de texto,  32, 516 editor visual,  953 editor visual (NetBeans),  956 efeito colateral,  137

eficiência de classificação por flutuação,  634 classificação por inserção,  628 classificação por intercalação,  631 classificação por seleção,  625 pesquisa binária,  622 pesquisa linear,  618 eficiente (princípio do projeto Unicode),  XXIX é um, relacionamento,  279, 307 eixo x,  484 eixo y,  484 elemento chance,  164 elemento de formulário oculto,  971 elemento de script,  951 elemento de um array,  190 elemento-raiz,  953 elemento-raiz (XML),  735 elemento (XML),  735 elemento XML vazio,  735 eliminação de duplicatas,  713 Eliminação de duplicatas, exercício,  230 eliminar vazamentos de recurso,  346 Ellipse2D, classe,  484 Ellipse2D.Double, classe,  503, 512 Ellipse2D.Float, classe,  503 E lógico booleano, &, operador,  136, 137 else oscilante, problema,  87, 117 else, palavra-chave,  86, 1069 emacs,  8 embaralhamento e distribuição de cartas,  235 por Collections método shuffle,  648 empacotador de sincronização,  663 empacotador não modificável,  664 empacotando objetos fluxo,  570, 574 empacotar tipos de fluxo,  866, 867 empilhamento de instruções de controle,  85 empilhamento, regra,  142 empilhando instruções de controle,  144 Employee, classe que implementa Payable,  325 Employee, programa de teste de hierarquia de classe,  317 Employee, superclasse abstrata,  312 EmptyStackException, classe,  655 en.wikipedia.org/wiki/IP_address,  948 encapsulamento,  15 Encontrar um valor mínimo em um array, exercício,  612 endereço de IP,  879, 948 do servidor,  876 endereço de IP (en.wikipedia.org/wiki/IP_ address),  948 endereço de loopback,  871 endereço Internet do servidor,  867 End, tecla,  458 endsWith, método da classe String,  521 engenharia de software,  252 Enquete com alunos, exercício,  587 enqueue, operação de fila,  707 ensureCapacity, método da classe StringBuilder,  527 ENTERED, constante da classe aninhada EventType,  865 Enter (ou Return), tecla,  32, 432, 726 entidade de segurança em um cálculo de juros,  125 entrada de dados,  74 entrada de dados a partir do teclado,  47 entrada/saída,  555 entrada/saída, operação,  236 entrada/saída, operações,  84 entrada/saída, pacote,  163 entrada única/saída única, instruções de controle de,  141



Índice remissivo

entrelinha de uma fonte,  493 enum

constante,  256 EnumSet, classe,  257 palavra-chave,  1069 values, método,  257 enum, palavra-chave,  171 enumeração,  171 EnumSet, classe,  257 java.sun.com/javase/6/docs/api/java/util/ EnumSet.html,  257 range, método,  257

enviar dados a um servidor,  876 enviar mensagem,  67 enviar uma mensagem para um objeto,  57 EOF (end-of-file),  867 EOFException, classe,  576 equals, método da classe Arrays,  219 da classe BitSet,  LI da classe Object,  298 da classe String,  519, 520 equalsIgnoreCase, método da classe String,  519, 521 erasure,  675, 677 erro de compilação,  30 erro de compilador,  30 erro de estouro,  239 erro de lógica,  9, 39, 88, 122, 1078 erro de lógica em tempo de execução,  39 erro de lógica fatal,  88 erro de lógica não-fatal,  88 Erro de programação comum,  7 erro de sintaxe,  30, 31, 32 erro de tempo de execução,  11 erro em tempo de compilação,  30 erro fatal,  11, 88, 239 erro não-fatal de tempo de execução,  11 erro por um (off-by-one),  122 Error, classe,  343 Erros de programação comuns, visão geral,  xxv erro síncrono,  342 E/S, aprimoramento do desempenho,  577 E/S armazenada em buffer,  577 escalar,  203 escalonando (números aleatórios),  165 escalonando uma imagem,  747 escape, propriedade de um componente Static Text,  965 escopo,  123 escopo de aplicativo,  952 escopo de sessão,  952 escopo de solicitação,  952 escopo de uma declaração,  172 escopo de um parâmetro de tipo,  679 escopo de variável,  123 Escrevendo o valor de um cheque por extenso,  547 esfera,  182 E sobre bits (&),  XLIII E sobre bits, OU inclusivo sobre bits, OU exclusivo sobre bits e operadores de complemento de bits,  XLIII espaçamento horizontal,  465 espaçamento vertical,  465 espaçar entre componentes em GroupLayout,  XIII espaço em branco,  30, 46 espaço em disco,  696 espaço, flag,  1099, 1100 especialização,  279 especialização na UML,  395 especificação do projeto,  365

especificadores de conversão de ponto flutuante,  1098 especificadores de formato,  1091 %%,  1097 %.2f, para números de ponto flutuante com precisão,  98 %b,  1096 %B,  1096 %b, para valores boolean,  138 %c,  54, 1094 %d,  39, 1091, 1092 %e,  1093 %E,  1093 %f,  53, 71, 1093 %g,  1093 %G,  1093 %h,  1096 %H,  1096 %n,  1096 %n, (separador de linha),  562 %o,  1092 %s,  1091, 1094 %S,  1094 %x,  1092 %X,  1092 especificidades,  307 espera, fila,  639, 655 espera sincronizada, estado,  806 espera para uma nova conexão,  871 espiral,  513, 594 estação de trabalho,  4 estado,  365 estado bloqueado,  806, 812 estado consistente,  248 estado da ação (na UML),  83, 141, 375 estado de espera,  806 estado de execução,  806 estado de um objeto,  371, 374 estado executável,  805, 806 estado final (na UML),  84, 140, 375 estado inicial,  140 estado inicial (na UML),  84, 374, 375 estado morto,  806 estado na UML,  365, 374 estado novo,  805 estado pronto,  806 estender uma classe,  279 estilo de fonte,  438, 492 estouro,  342 estouro aritmético,  342 estouro de pilha,  162 estrela de cinco pontas,  506 estrutura de aplicativo Web,  951 estrutura de coleções,  637 estrutura de dados,  190, LXVII estrutura de dados bidimensional,  709 estrutura de dados dinâmica,  695 estrutura de dados linear,  709 estrutura de dados, primeiro a entrar, primeiro a sair (first-in, first-out — FIFO),  707 estrutura de dados subjacente,  655 estrutura de dados último a entrar, primeiro a sair (last-in-first-out — LIFO),  704 estrutura de um sistema,  374 estruturas de dados não-lineares,  696 estruturas de dados predefinidas,  637 estrutura vertical,  791 Estudo de caso de engenharia de software,  xxiv estudos de caso,  xxii etapa de análise do ciclo de vida de software,  364 Euclides, algoritmo de,  187 Euler,  232

1121

EventListenerList, classe,  434 evento,  329, 374, 428, 509 evento assíncrono,  342 EventObject, classe getSource, método,  433 evento de gatilho pop-up,  779, 781 evento de janela,  773 evento de mouse,  435, 449 evento externo,  449 eventos de mouse,  781 EventType, classe aninhada ACTIVATED, constante,  865 ENTERED, constante,  865 EXITED, constante,  865 EventType, classe aninhada de HyperlinkEvent,  865 exceção,  199, 336 exceção encadeada,  351 exceção não-interceptada,  341 exceção não-verificada,  343 exceção verificada,  343 Exception, classe,  343 excluindo um item de uma árvore binária,  714 excluir uma pilha,  162 exclusão mútua,  812 execução sequencial,  83 executando um aplicativo,  12 executando um AudioClip,  756 executar,  33 executar um applet em um navegador Web,  727, 730 execute, método de Executor,  811 de JdbcRowSet,  925 execute, método do Executor interface,  810 executeQuery, método de PreparedStatement,  932 de Statement,  914 executeUpdate, método da interface PreparedStatement,  932 ExecutionException, classe,  842 Executor, interface,  810 execute, método,  810, 811 Executors, classe,  810 newCachedThreadPool, método,  810 ExecutorService, interface,  810, 851 awaitTermination, método,  814 shutdown, método,  811 submit, método,  851 exibindo texto em uma caixa de diálogo,  74 exibir saída,  46 exibir uma linha de texto,  32 exists, método de File,  556 exit, comando de depurador,  1084 exit, método da classe System,  345, 561 EXIT_ON_CLOSE, constante da classe JFrame,  107 EXITED, constante da classe aninhada EventType,  865 exp, método de Math,  157 exponenciação,  239 expressão,  39 expressão condicional,  86, 243 expressão de ação (na UML),  84, 85, 375 expressão de acesso ao array,  190 expressão de controle da instrução switch,  132 expressão de criação de array,  191 expressão de criação de instância de classe,  60, 69 expressão integral,  134 expressão integral constante,  129, 134 expressão regular,  536, 541, 968 ^,  538 ?,  539 {, n },  539

1122

Índice remissivo

{, n,},  539 {, n, m },  539 *,  539 \d,  536 \D,  536 \s,  536 \S,  536 \w,  536 \W,  536 +,  539

extends, palavra-chave,  107, 282, 289, 1069

extensibilidade,  307 Extensible HyperText Markup Language (XHTML),  947, 950 Extensible Markup Language (XML),  735, 950, 1024 extensões de arquivo .aif,  756, 758 .aiff,  756, 758 .au,  756, 758 .avi,  758 .class,  756 .gif,  744 .jpeg,  744 .jpg,  744 .mid,  756, 758 .mov,  758 .mp3,  758 .mpeg,  758 .mpg,  758 .png,  744 .rmi,  756, 758 .spl,  758 .swf,  758 .wav,  756

F fábrica,  LXVIII fábrica, método,  LXIII Facade, padrão de design,  LIX, LXI, LXVIII FaceBook,  18 fachada, objeto,  LXVIII factorial, método,  591, 592 Factory Method, padrão de design,  LIX, LX, LXIII Fahrenheit,  479, 1108 equivalente de uma temperatura em Celsius,  187 falando com um computador,  3 Falha de construtor, exercício,  359 false, palavra-chave,  43, 86, 1069 f, opção do comando jar,  734 fase,  94 fase de implementação,  398 fase de inicialização,  94 fase de processamento,  94 F, sufixo para literais float,  655 fator de carga,  659 fator de escalonamento (números aleatórios),  165, 167 fatorial,  118, 151, 591 f:view, elemento,  953 fazer sua pontuação (jogo de dados craps),  168 fechar (ocultar) um diálogo,  422 fechar uma janela,  425, 428 feedback visual,  438 ferramenta de desenvolvimento,  724 ferramenta de desenvolvimento de programas,  85, 95 fibonacci, método,  595 Fibonacci, série,  235 Fibonacci, série de,  594, 595

definido recursivamente,  594 gerada com um método recursivo,  595 fila,  639, 655, 695, 707 fila de prioridade de múltiplos níveis,  808 fila para o servidor,  871 File, classe,  555 canRead, método,  556 canWrite, método,  556 exists, método,  556 File, métodos,  556 getAbsolutePath, método,  556 getName, método,  556 getParent, método,  556 getPath, método,  556 isAbsolute, método,  556 isDirectory, método,  556 isFile, método,  556 lastModified, método,  556 length, método,  556 list, método,  556 toURI, método,  761 utilizado para obter informações sobre arquivos e diretórios,  556 File, documentação de classe (java.sun.com/ javase/6/docs/api/java/io/File. html),  556

File, métodos,  556 File.pathSeparator,  558 FileContents, interface,  751 getLength, método,  746

FileExplorer, demo,  XXV FileInputStream, classe,  555, 570, 572, 574, 576, 663 FileNotFoundException, classe,  561 FileOpenService, interface,  743, 746 openFileDialog, método,  746 openMultiFileDialog, método,  751 FileOutputStream, classe,  555, 570, 572, 663, LXVIII FileReader, classe,  555, 578 FILES_AND_DIRECTORIES, constante de JFileChooser,  580 FILES_ONLY, constante de JFileChooser,  580 FileWriter, classe,  555, 578 filho direito,  709 filho esquerdo,  709 fill3DRect, método da classe Graphics,  496, 498 fill, método da classe Arrays,  219, 220 da classe Collections,  645, 650 da classe Graphics2D,  505, 506, 508, 513 fillArc, método da classe Graphics,  223, 224, 498 fillOval, método da classe Graphics,  176, 457, 496, 498 fillPolygon, método da classe Graphics,  501, 503 fillRect, método da classe Graphics,  176, 487, 495, 506 fillRoundRect, método da classe Graphics,  496 FilterInputStream, classe,  577 FilterOutputStream, classe,  577 filtrar um fluxo,  577 fim da entrada de dados,  93 fim de fluxo,  867 fim de linha (de uma única linha), comentário, //,  29, 31 fim do arquivo (end-of-file¨hi — EOF),  867 fim do arquivo (end-of-file — EOF) indicador,  131 fim do arquivo (end-of-file — EOF) combinações de teclas,  562 marcador,  554 final

classe,  321

método,  321 palavra-chave,  134, 158, 195, 262, 321, 817, 1069 variável,  195 variável deve ser inicializada,  263 variável local,  445 final, Classes e métodos java.sun.com/docs/books/tutorial/java/IandI/ final.html,  321 www.ibm.com/developerworks/java/library/jjtp1029.html,  321 final, palavra-chave,  158

final de uma fila,  695, 707 finalize, método,  258, 298 finally

bloco,  341, 345, 349, 838 cláusula,  345, 1069 palavra-chave,  341 find, método da classe Matcher,  541 Firefox, navegador Web,  74 fireTableStructureChanged, método de AbstractTableModel,  919 firewall,  1019 first, método de interface SortedSet,  658 fita magnética,  552 flags,  1091, 1099 Flasher de Imagem, exercício,  765 Flasher de texto, exercício,  765 Flickr,  17, XXVI, XXVII APIs,  XXVI float

promoções de tipos primitivos,  162 sufixo de literal F,  655 tipo primitivo,  38, 1069, 1070 Float, classe,  638 float, tipo primitivo,  70 Floating Dock, demo,  XXV floor, método de Math,  157 FlowLayout, classe,  427, 461 CENTER, constante,  463 LEFT, constante,  463 RIGHT, constante,  463 setAlignment, método,  463 flush, método da classe BufferedOutputStream,  577 da classe Formatter,  887 da classe ObjectOutputStream,  871 fluxo,  347, 1091 fluxo baseado em bytes,  554 fluxo baseado em caracteres,  554 fluxo, cabeçalho,  872 fluxo, comunicações baseadas em,  859 fluxo de bytes,  554 fluxo de controle,  89, 97 fluxo de controle na instrução if...else,  86 fluxo de entrada padrão (System.in),  554 fluxo de erro padrão,  341, 347, 1091 fluxo de erro padrão (System.err),  554, 577 fluxo de saída padrão,  347 fluxo de saída padrão (System.out),  554, 577 fluxo de trabalho,  83 fluxo de trabalho de um objeto na UML,  375 fluxo, processamento,  552 fluxos,  859 fluxo, socket,  859, 867, 882 fluxos, transmissão baseada em,  877 Flyweight, padrão de design,  LXI foco,  429 foco para um aplicativo GUI,  769, 781 FocusAdapter, classe,  453 FocusListener, interface,  453 folha,  LXIV

folha de estilo,  953, 958 font estilo,  491 nome,  491 tamanho,  491 Font, classe,  440, 484, 491 BOLD, constante,  491, 492 getFamily, método,  491, 493 getName, método,  491, 492 getSize, método,  491, 492 getStyle, método,  491, 493 isBold, método,  492, 493 isItalic, método,  492, 493 isPlain, método,  492, 493 ITALIC, constante,  491, 492 PLAIN, constante,  491, 492 fonte manipulação,  484 fonte de evento,  432, 434 fontes do Java Dialog,  492 DialogInput,  492 Monospaced,  492 SansSerif,  492 Serif,  492 FontMetrics, classe,  484, 494 getAscent, método,  494 getDescent, método,  494 getFontMetrics, método,  494 getHeight, método,  494 getLeading, método,  494 for aninhado, instrução,  196, 211, 212, 215, LIV fora dos limites de um array,  199 for, instrução de repetição,  84, 121, 122, 123, 124, 125, 126, 144, 1069 aninhado,  196 diagrama de atividades,  124 exemplo,  124 for, instrução de repetição aprimorada,  202 for, propriedade de um campo de entrada JSF,  965 força bruta,  233, 234 Passeio do cavalo,  233 forDigit, método da classe Character,  533 forma,  503 forma animada,  726 forma de linha reta (expressões aritméticas em Java),  41 forma preenchida,  176, 506 formas,  724 formas bidimensionais,  484 formas dimensionadas aleatoriamente,  513 formatação exibição de dados formatados,  35 formatação de data,  1091 formatação de data/hora,  1091 formatação de inteiro decimal,  39 format, método da classe Formatter,  562, 1104 da classe String,  75, 1104 format, método da classe String,  244 Formatando data e hora com caractere de conversão t,  1096 Formatar saída com a classe Formatter,  1104 formato de data/hora universal,  242, 243, 244 formato de relógio de 24 horas,  242 formato exponencial,  1091 formato padrão de data/hora,  244 formato, string,  1091, 1098 formato tabular,  193 forma tridimensional,  725 Formatter, classe,  555, 560, 1091, 1103 close, método,  562

Índice remissivo documentação (java.sun.com/javase/6/docs/api/ java/util/Formatter.html),  1094, 1104 flush, método,  887 format, método,  562, 1104 toString, método,  1104 FormatterClosedException, classe,  562 formulando algoritmos,  93 formulários virtuais,  998, 1006 participante,  1007 submissor,  1006 fornecedor de software independente (ISV),  297 Fortran (FORmula TRANslator),  7 fração de tempo,  806 fracionamento do tempo,  807 fractal,  600 curva de Koch,  601 exercícios,  612 floco de neve de Koch,  601 “fractal de Lo” no nível,  602, 603 “fractal de Lo” no nível 1, com C e D pontos determinados para nível 2,  602 “fractal de Lo” no nível 2, linhas tracejadas no nível 1 fornecido,  602 fractal estritamente auto-similar,  600 nível,  601 ordem,  601 profundidade,  601 propriedade auto-similar,  600 Fractal, applet,  724 fractal da Curva de Koch,  601 fractal de floco de Neve de Koch,  601 “fractal de Lo” no nível 0,  601 “fractal de Lo” no nível 1, com C e D pontos determinados para nível 2,  602 “fractal de Lo” no nível 2,  603 “fractal de Lo” no nível 2, linhas tracejadas no nível 1 fornecido,  603 fractal estritamente auto-similar,  600 Fractal, interface com usuário,  603 Frame, classe,  772 frame-alvo,  863 _blank,  863 _self,  863 _top,  863 frame interno que pode ser fechado,  786 que pode ser maximizado,  786 que pode ser minimizado,  786 que pode ser redimensionado,  786 frame interno maximizado,  786 frame (na UML),  385 framework,  951 frase com verbo no documento de requisitos,  377 FreeTTS (freetts.sourceforge.net/docs/index. php),  762 frequency, método da classe Collections,  653 frequency, método de Collections,  645 FROM, cláusula de SQL,  903 fromJson, método de classe Gson,  1034 função,  16, 156 função de retorno de chamada,  1006 funcionalidade de negócios (mashups),  XXVII Future, interface,  851 Future Splash (.spl), formato de arquivo,  758

G gabinete de arquivos,  726 Gamma, Erich,  LVIII “Gang of Four”,  LVIII, LIX, LX, LXI

1123

GCD (máximo divisor comum),  611 generalidades,  307 generalização na UML,  395 GeneralPath, classe,  484, 506, 512 closePath, método,  508 lineTo, método,  508 moveTo, método,  508 genéricos,  xxi, 638, 671 ? (argumento de tipo curinga),  688 argumento de tipo curinga,  688 argumentos reais de tipo,  674 classe parametrizada,  678 colchetes angulares (< e >),  674 curingas,  687, 688 curinga sem um limite superior,  689 erasure,  675 escopo de um parâmetro de tipo,  679 limite superior de um curinga,  688 limite superior de um parâmetro de tipo,  676, 677 limite superior padrão (Object) de um parâmetro de tipo,  679 método,  673 parâmetro formal de tipo,  674 seção de parâmetro de tipo,  674 tipo parametrizado,  678 tipo, parâmetro,  674 tipo, variável,  674 Gerador de palavras cruzadas,  549 gerador de palavras para números de telefone, exercício,  587 Gerando e Percorrendo um Labirinto, exercício,  766 Gerando labirintos aleatoriamente, exercício,  613 gerenciador de layout,  427, 452, 460, 465, XII BorderLayout,  452 FlowLayout,  427 GridLayout,  466 Gerencia programas de publicidade do Google AdWords,  XXVII gestão de relacionamento com o cliente (customer relationship management — CRM),  XXVII getAbsolutePath, método da classe File,  556 getActionCommand, método da classe ActionEvent,  433, 438 getAddress, método da classe DatagramPacket,  879 get, método da classe ArrayList,  222 da classe BitSet,  LI de interface Future,  851 de interface List,  641 de interface Map,  661 de interface MessageContext,  1037 @GET, anotação,  1029 getAppletContext, método da classe Applet,  863 getAscent, método da classe FontMetrics,  494 get, solicitação,  949 GET, solicitação HTTP,  948 getAttribute, método da interface HttpSession,  1037 getAudioClip, método da classe Applet,  756 getAutoCommit, método da interface Connection,  939 getBlue, método da classe Color,  487, 489 getByName, método da classe InetAddress,  876 getChars, método da classe String,  518 da classe StringBuilder,  528 getClass, método da classe Object,  320, 427 getClass, método de Object,  298 getClassName, método da classe StackTraceElement,  351

1124

Índice remissivo

getClassName, método da classe UIManager. LookAndFeelInfo,  784 getClickCount, método da classe MouseEvent,  455 getCodeBase, método da classe Applet,  756 getColor, método da classe Color,  487 getColor, método da classe Graphics,  487 getColumnClass, método de TableModel,  915, 919 getColumnClassName, método de ResultSetMetaData,  919 getColumnCount, método de ResultSetMetaData,  914,

getOptions, método da interface AutoComplete,  1010, 1012 getOutputStream, método da classe Socket,  866 getParameter, método da classe Applet,  862 getParent, método da classe File,  556 getPassword, método da classe JPasswordField,  433 getPath, método da classe File,  556 getPoint, método da classe MouseEvent,  456 getPort, método da classe DatagramPacket,  879 getPreferredSize, método da classe Component,  752,

getColumnCount, método de TableModel,  915, 919 getColumnName, método de ResultSetMetaData,  919 getColumnName, método de TableModel,  915, 919 getColumnType, método de ResultSetMetaData,  914 getConnection, método de DriverManager,  913 getContentPane, método da classe JFrame,  447 getControlPanelComponent, método da interface Player,  760 getData, método da classe DatagramPacket,  879 getDefaultSystemTray, método da classe SystemTray,  XXIV getDelay, método da classe Timer,  765 getDescent, método da classe FontMetrics,  494 getDesktop, método da classe Desktop,  XXII getEventType, método da classe HyperlinkEvent,  865 getFamily, método da classe Font,  491, 493 getFileName, método da classe StackTraceElement,  351 getFont, método da classe Graphics,  492 getFontMetrics, método da classe FontMetrics,  494 getFontMetrics, método da classe Graphics,  494 getGreen, método da classe Color,  487, 489 getHeight, método da classe Component,  747 getHeight, método da classe FontMetrics,  494 getHeight, método da classe JPanel,  107 getHostName, método da classe InetAddress,  871 getIcon, método da classe JLabel,  428 getIconHeight, método da classe ImageIcon,  747 getIconWidth, método da classe ImageIcon,  747 getImage, método da classe ImageIcon,  747

getProperty, método da classe Properties,  661 getRed, método da classe Color,  487, 489 getRequestContext, método da interface BindingProvider,  1045 getResource, método da classe Class,  427 getRow, método da interface ResultSet,  919 getRowCount, método da interface TableModel,  915,

919

get, método,  66, 248, 252 getInetAddress, método da classe Socket,  871 getInputStream, método da classe Socket,  866, 867 getInstalledLookAndFeels, método da classe UIManager,  784 getInstance, método de Calendar,  1096 getInt, método de ResultSet,  914 getKeyChar, método da classe KeyEvent,  460 getKeyCode, método da classe KeyEvent,  460 getKeyModifiersText, método da classe KeyEvent,  460 getKeyText, método da classe KeyEvent,  460 getLeading, método da classe FontMetrics,  494 getLength, método da classe DatagramPacket,  879 getLength, método da interface FileContents,  746 getLineNumber, método da classe StackTraceElement,  351 getLocalHost, método da classe InetAddress,  876, 879 getMaximumSize, método da classe Component,  XIII getMessage, método da classe Throwable,  349 getMethodName, método da classe StackTraceElement,  351 getMinimumSize, método da classe Component,  752, 772, XIII getModifiers, método da classe InputEvent,  460 getName, método da classe Class,  298, 320 getName, método da classe File,  556 getName, método da classe Font,  491, 492 getObject, método da interface ResultSet,  914, 926

772, XIII

919

getSelectedFile, método da classe JFileChooser,  581 getSelectedIndex, método da classe JComboBox,  445 getSelectedIndex, método da classe JList,  447 getSelectedText, método da classe JTextComponent,  470 getSelectedValues, método da classe JList,  449 getSession, método da interface HttpSession,  1037 getSize, método da classe Font,  491, 492 getSource, método da classe EventObject,  433 getStackTrace, método da classe Throwable,  349 getStateChange, método da classe ItemEvent,  445 getStyle, método da classe Font,  491, 493 getText, método da classe JLabel,  428 getText, método da classe JTextComponent,  779 gettingreal.37signals.com/toc.php,  19 getURL, método da classe HyperlinkEvent,  865 getValue, método da classe JSlider,  772 getValueAt, método da interface TableModel,  915,

919

getVisualComponent, método da interface Player,  760 getWidth, método da classe Component,  747 getWidth, método da classe JPanel,  107 getX, método da classe MouseEvent,  452 getY, método da classe MouseEvent,  452 .gif, extensão de arquivo,  744 .gif, extensão de arquivo,  427

GIF (Graphics Interchange Format),  427, 744 G.I.M.P.,  743 Givezilla,  XXVIII GlassFish, servidor de aplicativo,  947, 951, 960 glassfish.dev.java.net,  1021 Tester, página Web,  1023 glifo,  XXX Google,  17 AdWords,  XXVII Blog Search,  18 Mapas,  18, XXVI Maps,  XXVI -g, opção de linha de comando para javac,  1079 Gosling, James,  6 goto, eliminação,  83 goto, instrução,  83 grade,  466 grade para gerenciador de layout GridBagLayout,  792 gradiente,  505 gradiente acíclico,  505 gradiente cíclico,  505 GradientPaint, classe,  484, 505, 512 gráfico,  152 gráfico de barras,  152, 196, 724

gráfico de dependências entre capítulos,  xxi, xxiv gráfico de pizza, exercício,  513 gráfico de torta,  513 gráfico, informações,  196 Gráficos de Tartaruga,  231, 512 Gráficos de tartaruga, exercício,  512 Grand, Mark,  LXVII Graphics2D, classe,  484, 503, 505, 508, 512 draw, método,  505 fill, método,  505, 506, 508, 513 rotate, método,  508 setPaint, método,  505 setStroke, método,  505 translate, método,  508 Graphics, classe,  105, 176, 223, 330, 456, 484, 502, 728, 730, 732, 747 clearRect, método,  496 draw3DRect, método,  496, 498 drawArc, método,  498, 512 drawImage, método,  747 drawLine, método,  107, 495 drawOval, método,  496, 498, 741 drawPolygon, método,  501, 502 drawPolyline, método,  501, 503 drawRect, método,  495, 506, 512, 741 drawRoundRect, método,  496 drawString, método,  488, 728, 732 fill3DRect, método,  496, 498 fillArc, método,  498 fillOval, método,  176, 457, 496, 498 fillPolygon, método,  501, 503 fillRect, método,  176, 487, 495, 506 fillRoundRect, método,  496 getColor, método,  487 getFont, método,  492 getFontMetrics, método,  494 setColor, método,  176, 506 setFont, método,  492 Graphics Interchange Format (GIF),  427, 744 graphicssoft.about.com/od/pixelbasedfreewin,  762 GraphicsTest, applet,  724 GraphLayout, applet,  724 grau,  498 graus negativos,  498 graus positivos,  498 gravável,  556 GridBagConstraints, classe,  792, 796 anchor, campo,  793 BOTH, constante,  793 CENTER, constante,  793 EAST, constante,  793 gridheight, campo,  793 gridwidth, campo,  793 gridx, campo,  793 gridy, campo,  793 HORIZONTAL, constante,  793 NONE, constante,  793 NORTH, constante,  793 NORTHEAST, constante,  793 NORTHWEST, constante,  793 RELATIVE, constante,  796 REMAINDER, constante,  796 SOUTH, constante,  793 SOUTHEAST, constante,  793 SOUTHWEST, constante,  793 variáveis de instância,  792 VERTICAL, constante,  793 weightx, campo,  793 weighty, campo,  793 WEST, constante,  793 GridBagConstraints, constantes RELATIVE e REMAINDER,  796

GridBagLayout, classe,  789, 792, 796 setConstraints, método,  796 GridBagLayout, gerenciador de layout,  794 gridheight, campo da classe GridBagConstraints,  793 GridLayout, classe,  461, 466 GridLayout, contendo seis botões,  466

Grid Panel, componente JSF,  963

gridwidth, campo da classe GridBagConstraints,  793 gridx, campo da classe GridBagConstraints,  793 gridy, campo da classe GridBagConstraints,  793 GROUP BY,  903 group, método da classe Matcher,  542 GroupLayout, classe,  460, XII BASELINE, constante de alinhamento,  XIII CENTER, constante de alinhamento,  XIII

diretrizes de design GUI recomendadas,  XIII espaçamento entre componentes,  XIII gerenciador de layout padrão no Netbeans,  XII grupos,  XIII layout paralelo de componentes GUI,  XII layout sequencial de componentes GUI,  XII LEADING, alinhando componentes,  XIII LEADING, constante de alinhamento,  XIII orientação horizontal sequencial,  XII TRAILING, constante de alinhamento,  XIII GroupLayout.Group, classe,  XIII addGap, método,  XIII GroupLayout.ParallelGroup, classe,  XIII addGap, método,  XIII GroupLayout.SequentialGroup, classe,  XIII addGap, método,  XIII grupos em GroupLayout,  XIII Gson, classe,  1033 code.google.com/p/google-gson/,  1032 Json, método,  1034 toJson, método,  1033 guardando código com um bloqueio,  812 Guarded Suspension, padrão de design,  LIX, LXVII Guestbook, aplicativo,  1016 Guestbook, aplicativo, exercício,  1016 GUI portável,  164 GUIs (graphical user interfaces),  329 componente,  419 ferramenta de design,  460

H h, caractere de conversão,  1096 H, caractere de conversão,  1096

handler de evento,  329, 428 handler de exceção,  340 handler de exceção padrão,  350 handler do formulário do lado do servidor,  949 handshake, ponto,  866, 876 hardcopy, impressora,  10 hardware,  3, 5 hash bucket,  659 hashCode, método de Object,  299 hashing,  659 HashMap, classe,  658, 862, 976, 982, 983 get, método,  976 keySet, método,  661, 985 put, método,  976 HashSet, classe,  656 hash, tabela,  656, 659 Hashtable, classe,  658, 659, LXXI hasNext, método da classe Scanner,  131, 562 de interface Iterator,  641, 643

Índice remissivo hasPrevious, método de interface ListIterator,  643 headSet, método da classe TreeSet,  658 height, atributo do elemento applet-desc,  736 height de um applet em pixels,  729

Helm, Richard,  LVIII helpdoc.html, gerado por javadoc,  XLII Help, link na API,  1072 herança,  15, 107, 279, 395, 397, 398 exemplos,  280 extends, palavra-chave,  282, 289 hierarquia,  280, 311 hierarquia MembrosDaComunidade da universidade,  280 múltipla,  279 único,  279 herdar,  107 heurística,  233 heurística de acessibilidade,  233 hidden

campo,  971 elemento,  971 HIDE_ON_CLOSE, constante de interface WindowConstants,  772 hierarquia de coleções,  639 hierarquia de dados,  553 Hierarquia de formas, exercício,  334 hierarquia de herança,  280 hipotenusa de um triângulo reto,  186 Hiragana, bloco,  XXXIII Home, chave,  458 HORIZONTAL, constante da classe GridBagConstraints,  793 HORIZONTAL_SCROLLBAR_ALWAYS, constante da classe JScrollPane,  470 HORIZONTAL_SCROLLBAR_AS_NEEDED, constante da classe JScrollPane,  471 HORIZONTAL_SCROLLBAR_NEVER, constante da classe JScrollPane,  471 horizontal JSlider, componente,  769 host,  948 host de serviço Web,  1019 hostname,  948 HourlyEmployee, classe derivada de Employee,  314 HousingMaps.com (www.housingmaps.com),  18 href, atributo do elemento jnlp,  735 .htm, extensão de nome de arquivo,  729 .html, extensão de nome de arquivo,  729 HTML, tutoriais (www.deitel.com/xhtml/),  723, 860 HTTP, códigos de status (www.w3.org/Protocols/ rfc2616/rfc2616-sec10.html),  948 HTTP (Hypertext Transfer Protocol),  860, 971 cabeçalho,  949 em uso com firewalls,  1019 método,  948 protocolo sem estado,  971 solicitação, tipo,  949 transação,  948 HttpServletRequest, classe,  978 HttpServletResponse, classe,  976 HttpSession, interface,  1037 getAttribute, método,  1037 getSession, método,  1037 setAttribute, método,  1037 HugeInteger, Classe,  276 exercício,  276 hyperlink,  863, 865 HyperlinkEvent, classe,  863, 865 EventType, classe aninhada,  865 getEventType, método,  865 getURL, método,  865 HyperlinkListener, interface,  865 hyperlinkUpdate, método,  865

1125

Hyperlink, componente JSF,  961, 964

hyperlinkUpdate, método da interface HyperlinkListener,  865

Hypertext Transfer Protocol (HTTP),  860, 948, 971

I IBM, computador pessoal,  4 IBM Corporation,  4, 7, XXIX IBM, Enterprise Mashup Tool Enterprise da,  XXVIII Icon, interface,  427 ícone,  422 ícones de bandeja,  XXIV ID de evento,  435 IDE,  9 identificador,  30, 36 identificador válido,  38 IDENTITY, palavra-chave (SQL),  928 IDEs NetBeans,  1019 IEEE 754 (grouper.ieee.org/groups/754/),  1070 IEEE 754, ponto flutuante,  1070 if...else aninhado, instrução de seleção,  86, 87 if...else, instrução de seleção dupla,  84, 85, 97, 129, 144 diagrama de atividades,  85 if, instrução de seleção única,  43, 84, 85, 129, 144, 1069 diagrama de atividades,  85 ignorando elemento zero,  199 IllegalMonitorStateException, classe,  825, 836 IllegalStateException, classe,  565 Image, classe,  744 ImageIcon, classe,  300, 427, 744, 751 getIconHeight, método,  747 getIconWidth, método,  747 getImage, método,  747 paintIcon, método,  751 imagem,  730, 743, 761 ImageMap, applet,  724 imagens gráficas,  455, 724, 726, 743 imagens gráficas bidimensionais,  503 imagens gráficas coloridas, de alta resolução e tridimensionais,  743 imagens gráficas em uma maneira independente de plataforma,  484 ImageObserver, interface,  747 Image, componente JSF,  961, 963 impasse,  836, 839, 856, LXVII implantando um serviço Web,  1022 implementação abstrata,  664 implementação de coleção,  664 implementação de uma função,  313 implementar múltiplas interfaces,  449 implementa uma interface,  306, 322, 327 implements,  1069 implements, palavra-chave,  322, 325 Impondo privacidade com criptografia,  118 import, declaração,  37, 38, 63, 264, 1069 imprimindo árvores,  720 imprimindo uma árvore binária em um formato de árvore de duas dimensões,  714 imprimir em múltiplas linhas,  35 imprimir uma linha de texto,  32 imprimir um array,  612 Imprimir um array de para trás para frente, exercício,  612 Imprimir um array, exercício,  612 imprimir um array recursivamente,  612 inanição,  808 incluir na pilha, operação,  701

1126

Índice remissivo

incrementar uma variável de controle,  121 incremento,  125 de uma instrução for,  123 de uma variável de controle,  120 expressão,  135 operador, ++,  102 incremento e decremento, operadores,  102 index-all.html, gerado por javadoc,  XLII index.html, gerado por javadoc,  XLI indexOf, método da classe ArrayList,  221 indexOf, método da classe String,  522 IndexOutOfBoundsException, classe,  650 indicador de classe JSlider,  769 índice de argumentos,  1091, 1096, 1102 índice de array fora dos limites,  199, 342 índice de massa corpórea (IMC),  26 calculadora,  26 índice de um JComboBox,  444 índice (subscript),  190 índice zero,  190 InetAddress, classe,  871, 876, 879, LXVIII getByName, método,  876 getHostName, método,  871 getLocalHost, método,  876, 879 informações de caminho,  555 informações de nível de classe,  258 informações persistentes,  971 informações sobre fontes,  484 informações voláteis,  3 information, elemento de um documento JNLP,  735 inicialização no começo de cada repetição,  100 inicializador de array,  193 aninhado,  210 para array multidimensional,  210 inicializadores de array aninhados,  210 inicializando arrays bidimensionais nas declarações,  211 inicializar um applet,  730 inicializar uma variável em uma declaração,  38 inicializar variáveis de instância do applet,  732 iniciar uma ação,  773 init, método,  956, 958 de JApplet,  728, 730, 732 initComponents, método autogerado em Netbeans,  XVI _init, método,  956, 976 injeção de dependência,  1037 INNER JOIN, cláusula de SQL,  903, 907 input,  971 InputEvent, classe,  450, 454, 458 getModifiers, método,  460 isAltDown, método,  454, 460 isControlDown, método,  460 isMetaDown, método,  454, 460 isShiftDown, método,  460 InputMismatchException, classe,  338, 340 InputStream, classe,  570, 576, 663, 866, 867 read, método,  746 InputStreamReader, classe,  578 inserir caracteres literais na saída,  1091 INSERT, instrução SQL,  908 insert, método da classe StringBuilder,  530 INSERT, SQL, instrução,  903 instanceof, operador,  319, 1069 instância de uma classe,  64 instância (não- static) método,  259 instanciar,  16 instanciar um objeto de uma classe,  58 instrução,  32, 59 instrução auxiliada por computador (computerassisted instruction — CAI),  188

Monitorando o desempenho do aluno,  188 Níveis de dificuldade,  188 Reduzindo a fadiga do aluno,  188 Variando os tipos de problema,  188 instrução de atribuição,  39 instrução de controle,  82, 84, 85, 597 aninhamento,  84 empilhamento,  84 instrução de declaração de variável,  38 instrução rotulada,  LIV Instruções,  94 aninhadas,  99 break,  132, 134, 135, 153 break rotulada,  LIV continue,  134, 153, LIV continue rotulada,  LV de controle aninhadas,  99, 142, 144 de controle de entrada única/saída única,  84 de controle empilhadas,  141 do...while,  84, 127, 128, 144 empilhamento de instruções de controle,  84 for,  84, 121, 122, 123, 124, 126, 144 for aprimorada,  202 if,  43, 84, 85, 129, 144 if...else,  84, 85, 86, 97, 129, 144 if...else aninhadas,  86, 87 instrução de controle,  82, 84, 85 instrução vazia,  88 loop,  84 problema dos resultados do exame com instruções de controle aninhadas,  100 profundamente aninhadas,  143 repetição,  83, 84, 88 return,  156, 161 seleção,  83, 84 seleção dupla,  84, 99 seleção múltipla,  84 seleção única,  84 switch,  84, 129, 133, 144 switch, instrução de múltipla seleção,  167 vazias (um ponto-e-vírgula, ;),  46, 88, 129 while,  84, 89, 92, 97, 120, 144 instruções de controle aninhadas,  167, LIV int, tipo primitivo,  38, 95, 102, 129, 1069, 1070 promoções,  162 Integer, classe,  219, 422, 638, 687 parseInt, método,  219, 422 toBinaryString, método,  XLV integerPower, método,  186 integração com o desktop,  733 integridade de dados,  253 inteiro,  36 array,  193 divisão,  93 quociente,  41 valor,  38 inteiro binário,  117 inteiro decimal,  1092 inteiro hexadecimal,  1092 inteiro octal,  1092 inteiros sufixo L,  655 inteiros alinhados à direita,  1098 Intel,  758 interação entre um cliente de serviço Web e um serviço Web,  1025 interações entre objetos,  382, 384 intercalação na UML,  375 intercalar dois arrays,  628 interface,  15, 306, 322, 327, 914 declaração,  322 implementando mais de uma por vez,  452

interface de tags,  570

interface, palavra-chave,  322, 1069

interface com o usuário,  950, LXI interface de múltiplos documentos (multiple document interface — MDI),  769, 784 interface de ponto de extremidade de serviço (SEI),  1022 interface de ponto de extremidade de serviço (service endpoint interface — SEI),  1022, 1024 interface de programas aplicativos (applications programming interface — API),  7, 155 interface de tags,  322, 570 interface genérica,  675 interface gráfica com usuário (graphical user interface — GUI),  74, 163, 419 componente,  74 ferramenta de design,  460 Interfaces,  321 ActionListener,  432, 435 AppletContext,  860 AudioClip,  756 BlockingQueue,  823 CachedRowSet,  924, 998 Callable,  851 CallableStatement,  938 ChangeListener,  772 CharSequence,  541 Collection,  637, 639, 645 Comparable,  329, 521, 645, 675, 712 Comparator,  645 ComponentListener,  453 Condition,  836 Connection,  913, 914, 918 ContainerListener,  453 DataInput,  577 DataOutput,  577 Executor,  810 ExecutorService,  810, 851 FileOpenService,  743, 746 FocusListener,  453 Future,  851 HyperlinkListener,  865 Icon,  427 ImageObserver,  747 ItemListener,  440, 778 Iterator,  639 JdbcRowSet,  924 KeyListener,  435, 453, 458, 459 LayoutManager2,  463 LayoutManager,  460, 463 List,  637, 643 ListIterator,  639 ListSelectionListener,  447, 863 Lock,  835 Map,  637, 658 MessageContext,  1037 MouseInputListener,  449, 452 MouseListener,  435, 449, 452, 781 MouseMotionListener,  435, 449, 452 MouseWheelListener,  450 ObjectInput,  570 ObjectOutput,  570 Player,  758 PreparedStatement,  938 PropertyChangeListener,  850 Queue,  637, 639, 655, 823 RequestContext,  1045 ResultSet,  914 ResultSetMetaData,  914 RowSet,  924 Runnable,  329, 808, 888



Índice remissivo Serializable,  329, 570 Servlet,  950 Set,  637, 639, 656 SortedMap,  658 SortedSet,  657 Statement,  914 SwingConstants,  329, 428, 772 TableModel,  915 WebServiceContext,  1037 WindowConstants,  772 WindowListener,  452, 773, 924

interfaces com usuário baseadas em multithreading,  xxiv internacionalização,  163 internalVirtualForms, propriedade de um Table,  998 Internet,  5, 860 Internet Explorer,  74 Internet, telefonia,  18 interpretador,  6 Interpreter, padrão de design,  LXII interrupt, método da classe Thread,  809 InterruptedException, classe,  809 interseção de dois conjuntos,  276 interseção teórica,  276 intervalo de sono,  806 introdução java.sun.com/new2java/,  8 Introdução ao Java (java.sun.com/javase/6/ docs/technotes/guides/jdbc/getstart/ GettingStartedTOC.fm.html),  914

invocar um método,  67, 156

invokeLater, método da classe SwingUtilities,  871 IOException, classe,  574 isAbsolute, método de File,  556 isActionKey, método da classe KeyEvent,  460 isAltDown, método da classe InputEvent,  454, 460 isBold, método da classe Font,  492, 493 isCancelled, método da classe SwingWorker,  847 isControlDown, método da classe InputEvent,  460 isDefined, método da classe Character,  533 isDesktopSupported, método da classe Desktop,  XXII isDigit, método da classe Character,  533 isDirectory, método de File,  556 isEmpty, método ArrayList,  253 Map,  661 Stack,  655 isFile, método de File,  556 isItalic, método da classe Font,  492, 493 isJavaIdentifierPart, método da classe Character,  533 isJavaIdentifierStart, método da classe Character,  533 isLetter, método da classe Character,  533 isLetterOrDigit, método da classe Character,  533 isLowerCase, método da classe Character,  533 isMetaDown, método da classe InputEvent,  454, 460 isPlain, método da classe Font,  492, 493 isPopupTrigger, método da classe MouseEvent,  781 isRunning, método da classe Timer,  751 isSelected, método AbstractButton,  777 JCheckBox,  440 isShiftDown, método da classe InputEvent,  460 isUpperCase, método da classe Character,  533 ITALIC, constante da classe Font,  491, 492

item de menu,  773, 777 ItemEvent, classe,  440, 442 getStateChange, método,  445 ItemListener, interface,  440, 778 itemStateChanged, método,  440, 779

itemStateChanged, método da interface ItemListener,  440, 779

iteração,  92, 597 de um loop,  120, 135 iterador,  637 iterador bidirecional,  643 Iterator, interface,  639 hasNext, método,  641 next, método,  641 remove, método,  641 iterator, método de interface Collection,  641 Iterator, padrão de design,  LIX, LXII, LXXI

J Jacobson, Ivar,  16 Jacopini, G.,  83, 144 janela,  74, 105, 107, 772 janela de comando,  32, 724, 725, 729 janela de terminal,  32 janela, eventos,  773 janela filha,  769, 786, 787 janela filha em uma interface de múltiplos documentos,  784 janela pai,  74, 422, 769 janela pai em uma interface de múltiplos documentos,  784 janela pai especificada como nulo,  777 janela pai para uma caixa de diálogo,  777 janelas, sistema,  424 JApplet, classe,  728, 730, 773 destroy, método,  728 init, método,  728, 732 paint, método,  728, 732 start, método,  728, 732 stop, método,  728 jar, comando,  734 c, opção,  734 f, opção,  734 v, opção,  734 jar, elemento de um documento JNLP,  736 JAR, arquivo,  747, 753 Java2D, applet,  726 Java2D, diretório,  726 Java2D API,  484, 503, 726, 743 Java 2D, documentação (java.sun.com/javase/6/ docs/technotes/guides/2d),  503 Java 2D, formas,  503 Java 3D,  767 Java 3D API,  743, 762 Java 3D, documentação (java.sun.com/javase/ technologies/desktop/java3d/),  743 java.applet, pacote,  163 java.awt, classe,  772 java.awt.color, pacote,  503 java.awt.event, pacote,  163, 433, 452, 460 java.awt.font, pacote,  503 java.awt.geom, pacote,  163, 503 java.awt.image, pacote,  503 java.awt.image.renderable, pacote,  503 java.awt, pacote,  163, 423, 486, 500, 503, 744, 752, 781 java.awt.print, pacote,  503 java.beans, pacote,  850 Java Abstract Window Toolkit (AWT), pacote,  163 Java Abstract Window Toolkit Event, pacote,  163 java.com,  723 java, comando,  10, 12, 29 -splash, opção,  XXI Java Advanced Imaging API,  743 java, elemento de um documento JNLP,  736

1127

java, interpretador,  33 java.io, documentação de pacote (java.sun.com/ javase/6/docs/api/java/io/package-tree. html),  555 java.io, hierarquia de pacote (java.sun.com/ javase/6/docs/api/java/io/package-tree. html),  555 java.io, pacote,  163, 555 java.lang, pacote,  38, 157, 163, 282, 298, 516,

808, XL importado em cada programa Java,  39 java.math, pacote,  70, 593 Java, ambiente de desenvolvimento,  9, 724 java.net, pacote,  163, 859 Java API,  155, 329 documentação (java.sun.com/javase/6/docs/api/ index.html),  11 visão geral (java.sun.com/javase/6/docs/api/ overview-summary.html),  164 Java API Interfaces,  329 Java, applet,  728 Java Applet Package,  163 Java Application Programming Interface ( Java API),  7, 37, 155, 163 Java Architecture for XML Binding ( JAXB),  1029 Java archive ( JAR), arquivo,  734 java.sql, pacote,  163, 913 java.swing, pacote,  728 java.text, pacote,  163 java.util.concurrent.locks, pacote,  835, 836 java.util.concurrent, pacote,  164, 810, 823, 851 java.util, pacote,  37, 163, 221, 638, 654, 687, 696, 1096 Calendar, classe,  1096 Date, classe,  1096 java.util.prefs, pacote,  661 java.util.regex, pacote,  516 JavaBean,  951, 952, 953 Java, biblioteca de classe,  7, 37 javac, compilador,  33 Java, compilador,  10 Java Concurrency Package,  164 Java Database Connectivity ( JDBC),  899 Java DB,  xxii, 899, 926, 995 Developer’s Guide (developers.sun.com/docs/ javadb/10.4.2.0/devguide/derbydev. pdf),  928 Java, depurador,  1078 javadoc, opções -author,  XL -d,  XL -link,  XL javadoc, programa utilitário,  30 javadoc, tag,  XXXIV javadoc, tags {@link},  XL @author,  XXXVII @deprecated,  XL @param,  XXXVIII @return,  XXXIX @see,  XXXVII @since,  XL @throws,  XXXVIII @version,  XL Javadoc, comentário,  30, 947 Java EE 5,  xxii, xxvii, 951 java.sun.com/javaee,  950 Java Enterprise Edition ( Java EE),  3, 950 .java, extensão,  10 .java, extensão de nome de arquivo,  58 Java Foundation Classes ( JFC),  423 Java HotSpot, compilador,  10

1128

Índice remissivo

Java Como Programar, 8/E recursos para o aluno,  xxvi recursos para o professor,  xxvii Java How to Program, 8/E site Web (www.deitel.com/books/jhtp8/),  2 Java Image I/O API,  743 Java Input/Output Package,  163 Java Language Package,  163 Java Language Specification, capítulo sobre arrays (java.sun.com/docs/books/jls/third_ edition/html/arrays.html),  299 Java Media Framework ( JMF) API,  758 download (java.sun.com/javase/technologies/ desktop/media/jmf/2.1.1/download. html),  758 java.sun.com/javase/technologies/desktop/ media/jmf/,  758

Java Media Framework ( JMF), API,  743, 756 Java Media Framework Package,  164 Java Micro Edition ( Java ME),  3 Java, navegador da Web compatível com,  723 Java Networking Package,  163 Java Network Launch Protocol ( JNLP),  723, 733, 734 Java Plug-in,  723 Java, repositório de aparência e funcionamento (java.sun.com/developer/techDocs/hi/ repository),  762 Java, repositório de imagens gráficas de aparência e comportamento,  762 Java Resource Centers (www.deitel.com/ ResourceCenters.html),  33 JavaScript,  952 JavaScript assíncrono e XML (Ajax),  1005 JavaScript Object Notation ( JSON),  1020 Java SE 6,  xxii, xxvii documentação de API (java.sun.com/javase/6/ docs/api/),  164 Visão geral do pacote (java.sun.com/javase/6/ docs/api/overview-summary.html),  164 Java SE Development Kit ( JDK),  8, 30, 33 JavaServer Faces ( JSF),  xxii, 951 Button, componente,  961, 964 componentes,  952, 954 Drop Down List, componente,  961, 964 for, propriedade de um campo de entrada,  965 Grid Panel, componente,  963 Hyperlink componente,  961, 964 Image, componente,  961, 963 Label, componente,  961, 964 Message, componente,  964 Message Group, componente,  999 Meta, componente,  960 Radio Button Group, componente,  961, 964 Static Text, componente,  953, 961 Table componente,  995, 997 Text Area, componente,  978 Text Field, componente,  961, 964 JavaServer Faces ( JSF), elementos webuijsf:staticText,  1001 webuijsf:table,  1001 webuijsf:tableColumn,  1001 webuijsf:tableRowGroup,  1001 JavaServer Faces ( JSF) Expression Language,  953 JavaServer Pages ( JSP),  951 ação,  951 declaração XML,  953 diretiva,  951 elemento de script,  951 elemento-raiz,  953 xmlns, atributos,  953

JavaServer Pages Standard Tag Library ( JSTL),  951 Java, site Web (java.sun.com),  164 Java Sound API,  743, 762 Java Speech API,  743, 762 Java Speech API (java.sun.com/products/javamedia/speech),  762 Java Standard Edition 6 (Mustang),  xxvi Java Standard Edition ( Java SE),  3 Java Swing Event Package,  164 Java Swing GUI Components Package,  164 Java, tecnologias de desktop (java.sun.com/javase/ technologies/desktop/),  423 Java Text Package,  163 Java™ Language Specification (java.sun.com/docs/ books/jls/),  41 Java Utilities Package,  163 Java Virtual Machine ( JVM),  10, 11, 29 Java Web Start,  723, 733 atualização automática,  733 integração com o desktop,  733 javaws, comando,  736 visão geral (java.sun.com/javase/6/docs/ technotes/guides/javaws/),  737 javax.faces.validators, pacote,  964 javax.jnlp, pacote,  734, 743, 746 javax.media, pacote,  164, 758 javax.servlet.http, pacote,  950, 976, 978 javax.servlet.jsp, pacote,  951 javax.servlet.jsp.tagext, pacote,  951 javax.servlet, pacote,  950 javax.sql.rowset, pacote,  924 javax.swing.event, pacote,  164, 433, 447, 452, 772 javax.swing, pacote,  74, 164, 421, 423, 427, 433, 435, 470, 486, 728, 744, 772, 784, 786 javax.swing.table, pacote,  915, 923 JAXB, classe,  1029 marshal, método,  1030 unmarshal, método,  1031 JAXB ( Java Architecture for XML Binding),  1029 JAX-RS,  1018 JAX-WS,  1018, 1025 JAX-WS, pacote,  164 JBoss Application Server (www.jboss.com/products/ platforms/application),  1021 JBuilder (www.codegear.com),  9 JButton, classe,  423, 436, 437, 465 JCheckBox, botões e eventos de item,  438 JCheckBox, classe,  423, 438 isSelected, método,  440 JCheckBoxMenuItem, classe,  773, 778 JColorChooser, classe,  489, 491 showDialog, método,  490 JColorChooser, Exercício de diálogo,  513 JComboBox, classe,  423, 443, 793 getSelectedIndex, método,  445 setMaximumRowCount, método,  444 JComboBox, que exibe uma lista de nomes de imagens,  443 JComponent, classe,  425, 427, 434, 443, 445, 455, 467, 484, 486, 752, LXIV documentação (java.sun.com/javase/6/docs/api/ javax/swing/JComponent.html),  425 paintComponent, método,  107, 455, 484, 751, 770, 772 repaint, método,  486 setForeground, método,  778 setOpaque, método,  455, 457 setToolTipText, método,  427 JCreator (www.jcreator.com),  9 jdb, comando,  1079 JDBC API,  899, 911, 938

driver,  899 JDBC 4,  xxii jdbc:mysql://localhost/books

,  913 JDBC, documentação (java.sun.com/javase/ technologies/database/index.jsp),  900 JDBC, drivers (devapp.sun.com/product/jdbc/ drivers),  900 JDBC, drivers (developers.sun.com/product/jdbc/ drivers/),  900 JDBC, informações (java.sun.com/javase/ technologies/database/index.jsp),  900 JDBC, pacote,  163 JdbcRowSet, interface,  924 close, método,  926 execute, método,  925 setCommand, método,  925 setPassword, método,  925 setUrl, método,  925 setUsername, método,  925 JdbcRowSetImpl, classe,  925 JDesktopPane, classe,  784, 802 JDesktopPane, documentação (java.sun.com/ javase/6/docs/api/javax/swing/ JDesktopPane.html),  787

JDialog, classe,  778 JDIC Incubator Projects,  XXV JDIC ( Java Desktop Integration Components) addTrayIcon, método da classe SystemTray,  XXIV browse, método da classe Desktop,  XXII demo de pacote de navegador,  XXV Demos,  XXV demo Wallpaper API,  XXV Desktop, classe,  XXII FileExplorer, demo,  XXV Floating Dock, demo,  XXV getDefaultSystemTray, método da classe SystemTray,  XXIV getDesktop, método da classe Desktop,  XXII ícones de bandeja,  XXI isDesktopSupported, método da classe Desktop,  XXII JDIC Incubator Projects,  XXV mail, método da classe Desktop,  XXII open, método da classe Desktop,  XXII pacote de demonstração TrayIcon,  XXIV removeTrayIcon, método da classe SystemTray,  XXIV -splash, opção de linha de comando para o comando java,  XXI SplashScreen, classe,  XXI SystemTray, classe,  XXIV tela de splash,  XXI TrayIcon, classe,  XXIV JDK,  8, 33 demo, diretório,  723, 726 JEditorPane, classe,  863 setPage, método,  865 Jesse James Garrett,  1005 JFC ( Java Foundation Classes),  423 JFileChooser, classe,  578 CANCEL_OPTION, constante,  581 FILES_AND_DIRECTORIES, constante,  580 FILES_ONLY, constante,  580 getSelectedFile, método,  581 setFileSelectionMode, método,  580 showOpenDialog, método,  581 JFileChooser, diálogo,  578 JFrame, classe,  107, 177, 772 add, método,  107, 427 EXIT_ON_CLOSE,  428 getContentPane, método,  447



Índice remissivo setDefaultCloseOperation, método,  107, 428,

772

setJMenuBar, método,  778 setSize, método,  107, 428 setVisible, método,  107, 428 JFrame, classe EXIT_ON_CLOSE, constante,  107 JFrame.EXIT_ON_CLOSE,  428 jGRASP (www.jgrasp.org),  9 JInternalFrame, classe,  784 documentação (java.sun.com/javase/6/docs/api/ javax/swing/JInternalFrame.html),  787

JIT, compilador,  10

JLabel, classe,  299, 300, 423, 425 documentação (java.sun.com/javase/6/docs/api/ javax/swing/JLabel.html),  425 getIcon, método,  428 getText, método,  428 setHorizontalAlignment, método,  428 setHorizontalTextPosition, método,  428 setIcon, método,  427 setText, método,  428 setVerticalAlignment, método,  428 setVerticalTextPosition, método,  428 JList, classe,  423, 445 addListSelectionListener, método,  447 getSelectedIndex, método,  447 getSelectedValues, método,  449 setFixedCellHeight, método,  449 setFixedCellWidth, método,  449 setListData, método,  449 setSelectionMode, método,  447 setVisibleRowCount, método,  446 JMenu, classe,  773, 776, 786 add, método,  777 addSeparator, método,  778 JMenu, e mnemônicos,  773 JMenuBar, classe,  773, 778, 786 add, método,  778 JMenuItem, classe,  773, 786

JMF ( Java Media FrameWork) API,  758 JNLP,  746, 747, 752 FileOpenService,  743, 746 main-class,  734 ServiceManager, classe,  746 jnlp, elemento de um documento JNLP,  735 codebase, atributo,  735 href, atributo,  735 jnlp.jar,  747, 752 JNLP, documentação (java.sun.com/javase/6/docs/ jre/api/javaws/jnlp),  746 JNLP, documento,  734 applet-desc, elemento,  736 application-desc, elemento,  736 desktop, elemento,  735 information, elemento,  735 jar, elemento,  736 java, elemento,  736 jnlp, elemento,  735 offline-allowed, elemento,  735 resources, elemento,  736 shortcut, elemento,  735 title, elemento,  735 vendor, elemento,  735 JNLP ( Java Network Launch Protocol),  734 jogo,  164 Jogo de bilhar, exercício,  766 jogo de dados,  168 Jogo de dados (craps),  231 jogos de cartas,  199 Johnson, Ralph,  LVIII JOIN_ROUND, constante da classe BasicStroke,  506

Joint Photographic Experts Group ( JPEG),  427, 744 JOptionPane, classe,  74, 421, 422, 799 constantes para diálogos de mensagens,  423 documentação (java.sun.com/j2se/1.5.0/docs/ api/javax/swing/JOptionPane.html),  422 documentação (java.sun.com/javase/6/docs/api/ javax/swing/JOptionPane.html),  422 PLAIN_MESSAGE, constante,  422 showInputDialog, método,  74, 422 showMessageDialog, método,  74, 422 JOptionPane, constantes para diálogos de mensagens JOptionPane.ERROR_MESSAGE,  423 JOptionPane.INFORMATION_MESSAGE,  423 JOptionPane.PLAIN_MESSAGE,  423 JOptionPane.QUESTION_MESSAGE,  423 JOptionPane.WARNING_MESSAGE,  423 JPanel, classe,  105, 107, 423, 455, 461, 467, 748, 770, LXIV getHeight, método,  107 getWidth, método,  107 JPasswordField, classe,  429, 433 getPassword, método,  433 .jpeg, extensão de nome de arquivo,  427 .jpeg, extensão de arquivo,  744 JPEG ( Joint Photographic Experts Group),  427, 744 .jpg, extensão de nome de arquivo,  427 .jpg, extensão de arquivo,  744 JPopupMenu, classe,  779 show, método,  781 JProgressBar, classe,  848 JRadioButton, classe,  438, 440, 442 JRadioButtonMenuItem, classe,  773, 778 JScrollPane, classe,  447, 470 HORIZONTAL_SCROLLBAR_ALWAYS, constante,  470 HORIZONTAL_SCROLLBAR_AS_NEEDED, constante,  471 HORIZONTAL_SCROLLBAR_NEVER, constante,  471 setHorizontalScrollBarPolicy, método,  470 setVerticalScrollBarPolicy, método,  470 VERTICAL_SCROLLBAR_ALWAYS, constante,  470 VERTICAL_SCROLLBAR_AS_NEEDED, constante,  471 VERTICAL_SCROLLBAR_NEVER, constante,  471 JScrollPane, diretivas de barra de rolagem,  470 JSF ( JavaServer Faces),  951 JSlider, classe,  769, 771, XIII documentação (java.sun.com/javase/6/docs/api/ javax/swing/JSlider.html),  772 getValue, método,  772 incremento de bloco,  769 marcador,  769 marcas de medida menores,  769 marcas de medida significativas,  769 marcas de verificação,  769 setInverted, método,  770 setMajorTickSpacing, método,  772 setPaintTicks, método,  772 tiques de aderência,  769 Json, método da classe Gson,  1034 JSON ( JavaScript Object Notation),  1020 JSON (www.json.org),  1020 jsp:root, elemento,  953 JSP, contêiner,  951 .jsp, extensão de nome do arquivo,  952 JSP ( JavaServer Pages),  951 JSTL ( JavaServer Pages Standard Tag Library),  951 JTabbedPane, classe,  787, 792 addTab, método,  789 SCROLL_TAB_LAYOUT, constante,  792 TOP, constante,  792 JTable, classe,  915 classificando e filtrando,  xxiv

1129

RowFilter,  924 setRowFilter, método,  924 setRowSorter, método,  923 TableRowSorter,  923 JTextArea, classe,  459, 468, 469, 793, 795 setLineWrap, método,  470 JTextArea não editável,  468 JTextComponent, classe,  429, 431, 468 getSelectedText, método,  470 getText, método,  779 setDisabledTextColor, método,  460 setEditable, método,  431 setText, método,  470 JTextField, classe,  423, 429, 431, 433, 468 addActionListener, método,  432 JTextField, e JPasswordField s,  429 JToggleButton, classe,  438 JumpingBox, applet,  724

junções de linha,  505 juros compostos,  125, 152 just-in-time, compilação,  10 just-in-time ( JIT), compilador,  10

K Kelvin, temperatura, escala,  479 KeyAdapter, classe,  453 KeyEvent, classe,  435, 458 getKeyChar, método,  460 getKeyCode, método,  460 getKeyModifiersText, método,  460 getKeyText, método,  460 isActionKey, método,  460 KeyListener, interface,  435, 453, 458 keyPressed, método,  458, 460 keyReleased, método,  458 keyTyped, método,  458 Keypad, classe (estudo de caso ATM),  365, 367, 369, 377, 382, 383, 384, 385, 393, 394, 416 keyPressed, método da interface KeyListener,  458, 460 keyReleased, método da interface KeyListener,  458 keySet, método da classe HashMap,  661, 985 da classe Properties,  663 keyTyped, método da interface KeyListener,  458 KIS (“keep it simple”),  11 Koenig, Andrew,  336

L Label, componente JSF,  961, 964

Labirintos de qualquer de tamanho, exercício,  613 Lady Ada Lovelace,  8 LAMP,  19 Lançamento de dados,  231 lançar uma exceção,  337, 340 largura,  495 largura de arco e altura de arco para retângulos arredondados,  498 largura de campo,  126, 1091, 1098 largura de um retângulo em pixels,  487 last, método da classe SortedSet,  658 last, método de ResultSet,  919 lastIndexOf, método da classe String,  522 lastModified, método da classe File,  556 L, sufixo para long literais,  655 Latim de porco,  546 layout,  300 layoutContainer, método da interface LayoutManager,  463

1130

Índice remissivo

layout de página, software,  516 LayoutManager2, interface,  463 LayoutManager, interface,  460, 463 layoutContainer, método,  463 layout padrão do painel de conteúdo,  470 layout paralelo de componentes GUI,  XII layout sequencial de componentes GUI,  XII LEADING, constante de alinhamento em GroupLayout,  XIII Lea, Doug,  LXVII Lebre e a tartaruga, A,  234, 513 Lebre e a tartaruga, A, exercício,  513 LEFT, constante da classe FlowLayout,  463 legibilidade,  29, 100 Leilões,  XXVII Leis de Morgan,  152 lendo um arquivo em um servidor Web,  863 length, campo de um array,  191 length, método da classe String,  518 length, método da classe StringBuilder,  527 length, método de File,  556 length, variável de instância de um array,  191 Length Validator,  999 Length Validator, componente JSF,  964, 968 letra,  553 letra maiúscula,  30, 38 letra minúscula,  30, 553 liberar um bloqueio,  827 liberar um recurso,  345 LIFO (last-in, first-out – último a entrar, primeiro a sair),  162, 681 ligando o servidor a uma porta,  866, 877 LIGHTWEIGHT_RENDERER, constante da classe Manager,  759 LIKE, cláusula de SQL,  905, 906 LIKE, operador (SQL),  904 Limericks,  546 limericks aleatórios,  546 Limericks, exercício,  765 limite de crédito numa conta-corrente,  115 limite superior,  676 de um curinga,  688 limite superior de um parâmetro de tipo,  676, 677 limite superior padrão (Object) de um parâmetro de tipo,  679 Line2D, classe,  484, 506 Line2D.Double, classe,  503, 512 LinearGradientPaint, classe,  505 LineNumberReader, classe,  578 lineTo, método da classe GeneralPath,  508 linguagem assembly,  5 linguagem de alto nível,  5 linguagem de logotipo,  231 linguagem de máquina,  5 linguagem de máquina, programação,  236 linguagem de modelagem gráfica (UML),  xxiv linguagem de programação procedural,  16 linguagem extensível,  17, 60 linguagem híbrida,  6 linguagem natural de um computador,  5 linguagem orientada a objetos,  16 linguagens fortemente tipadas,  105 linha,  484, 495, 500, 902, 904, 905, 908 linha de base da fonte,  492 linha de comando,  32 linha de comando, argumento,  158, 218, 219 linha de vida de um objeto em um diagrama de sequência da UML,  385 linha em branco,  30, 95 linha em uma tabela de banco de dados,  900 linha pontilhada (na UML),  84

Linhas aleatórias utilizando a classe Line2D.Double, exercício,  512 linhas a serem recuperadas,  903 linhas conectadas,  501 linhas de guia (Netbeans),  XIV, XV linhas de um array bidimensional,  209 linha separadora em um menu,  778 linhas espessas,  503 linhas tracejadas,  503 link,  696, 709 {@link}, tag javadoc,  XL link de árvore na API,  1072 link de índice na API,  1072 -link, opção,  XL LinkedList, classe,  639, 651, 669 add, método,  644 addFirst, método,  644 addLast, método,  644 links da API Deprecated,  1072 Help,  1072 Índice,  1072 Tree,  1072 Linux,  5, 8, 19, 32, 562, 723 lista,  443 lista de argumentos,  1092 lista de argumentos de comprimento variável,  217 lista de compras,  88 lista de parâmetros,  61, 68 lista de parâmetros de métodos,  217 lista de seleção múltipla,  445, 447 lista de seleção única,  445 lista drop-down,  423, 443 listagens classificadas,  17 lista inicializadora,  193 List, interface,  637, 639, 643, 645, 650 addAll, método,  643 add, método,  641, 643 clear, método,  643 get, método,  641 listIterator, método,  643 size, método,  641, 643 subList, método,  643 toArray, método,  644 list, método da classe Properties,  663 list, método de File,  556, 558 dos argumentos,  36, 38 dos parâmetros,  159 listas indexadas,  721 lista vinculada,  695, 696, LXXI lista vinculada individualmente,  697 ListIterator, interface,  639 hasPrevious, método,  643 previous, método,  643 set, método,  643 listIterator, método de interface List,  643 ListSelectionEvent, classe,  445 ListSelectionListener, interface,  447, 863 valueChanged, método,  447 ListSelectionModel, classe,  447 MULTIPLE_INTERVAL_SELECTION, constante,  447 SINGLE_INTERVAL_SELECTION, constante,  447 SINGLE_SELECTION, constante,  447 literais ponto flutuante,  70 literal de caractere,  516 literal de ponto flutuante,  70 double, por padrão,  70 literal de string,  516 Live-Code, abordagem,  xxv load, método da classe Properties,  663 local,  163

localhost,  948 localhost, (127.0.0.1), endereço,  871

localização,  425, XXIX localização de uma variável na memória do computador,  40 Lock, interface,  835 lock, método,  835, 838 newCondition, método,  836, 837 unlock, método,  835, 838 lock, método da interface Lock,  835, 838 log, método de Math,  157 logaritmo,  157 logaritmo natural,  157 lógica da apresentação,  950 lógica do controlador,  950 lógica do negócio,  950 loja física,  970 long

sufixo de literal L,  655 Long, classe,  638 long, palavra-chave,  1069, 1070 long, promoções,  162

Long Range Validator, componente JSF,  964

LookAndFeelInfo, classe aninhada da classe UIManager,  784 lookingAt, método da classe Matcher,  541 lookup, método da classe ServiceManager,  746

loop,  89, 90, 92, 94, 765 aninhado dentro de um loop,  99 condição de continuação,  84 contador,  120 corpo,  127 infinito,  89, 97 instrução,  84, 88 loop, método da interface AudioClip,  756 loop, condição de continuação,  120, 121, 122, 123, 124, 127, 128, 135, 199 loop infinito,  89, 97, 123, 879, 882 Lord Byron,  8 losango (na UML),  83 losango na UML,  153 losangos sólidos (representando composição) na UML,  369 losangos vazios (representando agregação) na UML,  369 lote, arquivo,  562 Lovelace, Ada,  8 ls, comando no UNIX,  724

M Macintosh,  484 Macintosh AIFF, formato de arquivo (extensões .aif ou aiff),  758 Macintosh, aparência e comportamento,  781 Mac OS X,  5, 8, 32, 562 Macromedia Flash 2, filmes (.swf),  758 Macromedia Flash, filmes (.swf),  758 mail, método da classe Desktop,  XXII main-class, atributo do elemento applet-desc,  736 main-class especificada em um documento JNLP,  734 main, método,  31, 33, 60 maiúsculas de fim (de uma linha),  505 MalformedURLException, classe,  863 Malha, jogo, exercício,  766 Manager, classe,  758 createRealizedPlayer, método,  758 LIGHTWEIGHT_RENDERER, constante,  759 setHint, método,  759 Mandelbrot, Benoit,  600

manipulação de bits,  XLIII manipulação de fonte,  484 manutenciabilidade,  695 mapa de imagem,  724, 743, 753 Map, interface,  637, 658 containsKey, método,  661 get, método,  661 isEmpty, método,  661 put, método,  661 size, método,  661 Mapas,  XXVII mapeamentos de tipos SQL para tipos Java,  914 mapeando APIs,  XXVI Máquina Analítica,  8 máquina virtual (VM),  10 marcado para coleta de lixo,  261 marcador de visibilidade na UML,  391 marcas de medida em um JSlider,  769 marcas de medidas secundárias da classe JSlider,  769 marcas de tique importantes da classe JSlider,  769 margem de uma janela,  772 marshal, método da classe JAXB,  1030 mashup, aplicativo,  XXVI mashups,  xxii, 18 Mashups Agendamento de evento,  XXVII Agregação de ouvintes de evento,  XXVIII Amazon,  XXVII Amazon Web Services,  XXVI APIs comumente utilizadas,  XXVII Backpack,  XXVII Backpack API,  XXVII Blogger ATOM, feed,  XXVII Blogging,  XXVII comércio eletrônico,  XXVII compartilhamento de foto,  XXVII compartilhamento de vídeo,  XXVII Craigslist,  XXVI de aplicativos,  XXVI del.icio.us,  XXVI Dropcash,  XXVII eBay,  XXVII Flickr,  XXVI, XXVII Flickr APIs,  XXVI funcionalidade de negócios,  XXVII Gerencia programas de publicidade do Google AdWords,  XXVII gestão de relacionamento com o cliente (customer relationship management — CRM),  XXVII Givezilla,  XXVIII Google AdWords,  XXVII Google Maps,  XXVI, XXVII IBM, Enterprise Mashup Tool Enterprise da,  XXVIII Leilões,  XXVII mapeando APIs,  XXVI mashups populares,  XXVI Mashups Resource Center,  XXVII Microsoft,  XXVI Microsoft Virtual Earth,  XXVI Organizador de angariação de fundos,  XXVII Pagamentos,  XXVII PayPal,  XXVII Pesquisa local,  XXVII Podbop,  XXVIII ProgrammableWeb,  XXVII questões de desempenho e confiabilidade,  XXVIII reutilização de software,  XXVII RSS feed,  XXVI RSS Resource Center,  XXVIII

Índice remissivo SalesForce,  XXVII Salesforce.com,  XXVII serviços Web,  XXVI Smashforce,  XXVIII social bookmarking,  XXVII Strmz,  XXVIII Technorati APIs,  XXVIII TypePad ATOM,  XXVII Upcoming.org,  XXVII Web 2.0,  XXVI Yahoo! Maps,  XXVIII Yahoo! Search,  XXVIII YouTube,  XXVII mashups populares,  XXVI Mashups Resource Center,  XXVII Matcher, classe,  516, 541 find, método,  541 group, método,  542 lookingAt, método,  541 matches, método,  541 replaceAll, método,  541 replaceFirst, método,  541 matcher, método da classe Pattern,  541 matches, método da classe Matcher,  541 matches, método da classe Pattern,  541 matches, método da classe String,  536 Math, classe,  126, 156, 157 abs, método,  157 ceil, método,  157 cos, método,  157 E, constante,  157 exp, método,  157 floor, método,  157 log, método,  157 max, método,  157 min, método,  157 PI, constante,  157, 182 pow, método,  126, 156, 157, 182 random, método,  164 sqrt, método,  157, 162 tan, método,  157 Math.E,  157 Math.PI,  157 Math.PI, constante,  53, 512, 741 Matisse GUI, designer (Netbeans),  XII max, algoritmo,  650 max, método da classe Collections,  645, 650 max, método de Math,  157 maximizar uma janela,  425, 787 máximo divisor comum (MDC),  187, 611 exercício,  611 MBCS (multi-byte character set),  XXXI MDI (Multiple Document Interface),  769, 784 mecanismo de extensão,  267 estendendo Java com bibliotecas de classes adicionais,  267 java.sun.com/javase/6/docs/technotes/guides/ extensions/,  267

mecanismo de extensão de tags,  951 Mecanismo de extensão do Java (java.sun.

com/javase/6/docs/technotes/guides/ extensions/),  267

média,  42, 89, 91 média aritmética,  42 média áurea,  594 Mediator, padrão de design,  LXII meia palavra,  239 membro não-static,  259 membros de acesso a pacotes de uma classe,  268 memento, objeto,  LXII Memento, padrão de design,  LIX, LXII memória,  3

1131

memória, buffer,  577 memória, consumo,  598, 637 memória, posição,  40 memória principal,  3 memória reivindicada,  261 memória, utilização,  659 memória, vazamento,  258, 345 MemoryImageSource, classe,  765 menor de vários inteiros,  151 menor inteiro em um grupo,  741 mensagem,  15, 67, 724, 728 mensagem aninhada na UML,  385 mensagem de retorno na UML,  385 mensagem na UML,  382, 383, 384, 385 menu,  419, 468, 773 menu pop-up sensível ao contexto,  779 MessageContext, interface,  1037 get, método,  1037 Message, componente JSF,  964 Message Group, componente JSF,  999 metadados,  914 Meta, chave,  455 metal, aparência,  769, 781 Meta, componente JSF,  960 método,  7, 16, 31, 391 assinatura,  175 declaração,  31 lista de parâmetros,  61 parâmetro,  61, 62 static,  126 tipo de retorno,  65 variável local,  63 método abstrato,  309, 311, 313, 398, 434, 1071 método auxiliar,  133, 713 método chamador,  59, 65, 156 método de acesso,  252 método de classe,  156 método de comparação natural,  645 método de consulta,  252 método exponencial,  157 método genérico,  671, 673, 677 método modificador,  252 método predicado,  253 Método que utiliza grade drawLine, exercício,  512 Método que utiliza grade drawRect, exercício,  512 métodos chamados automaticamente durante execução do applet,  730 métodos de ciclo de vida,  954, 956, 958 destroy,  956, 958 init,  956, 958 preprocess,  956, 958 prerender,  956, 958 métodos de tratamento de evento de janela,  452 métodos de visualização de intervalo,  643, 657 métodos empacotadores da classe Collections,  639 métodos implicitamente final,  321 método sobrecarregado,  672 método utilitário,  133 métrica de fonte,  493 altura,  495 ascendente,  495 descendente,  495 entrelinha,  495 Microsoft,  XXVI, XXIX Microsoft Audio/Video Interleave (.avi), formato de arquivo,  758 Microsoft Internet Explorer,  74 Microsoft SQL Server,  899 Microsoft Virtual Earth,  XXVI Microsoft Windows,  131, 484, 772, 781 Microsoft Windows, aparência e funcionamento,  781

1132

Índice remissivo

.mid, extensão de arquivo,  756, 758 MIDI (Musical Instrument Digital Interface), formato de arquivo (extensões .mid ou .rmi),  756, 758 MIME (Multipurpose Internet Mail Extensions),  949, 971 min, algoritmo,  650 min, método da classe Collections,  645, 650 min, método de Math,  157 minimizar frame interno,  786 minimizar uma janela,  425, 773, 787 m por n, array,  209 mnemônico,  425, 773, 776 modelo de evento de delegação,  434 modelo de retomada de tratamento de exceções,  341 modelo de retomada do tratamento de exceções,  341 modelo de segurança de caixa de areia,  733 modelo de um sistema de software,  368, 374, 396 modelo em cascata,  364 modelo iterativo,  364 modelo (na arquitetura MVC),  LXIX Model-View-Controller (MVC),  LXIX, LXX Modificação da forma de registro, exercício,  993 Modificação de serviço Web de reserva de companhia aérea,  1065 Modificação do sistema de contas a pagar, exercício,  334 Modificação do sistema de folha de pagamento, exercício,  334 modificador de acesso,  59, 64, 391 private,  64, 245, 281 protected,  245, 281 public,  59, 245, 281 modificador de acesso na UML + (publicã),  60 - (private),  67 modificando a representação interna dos dados de uma classe, exercício,  275 modo de interrupção,  1080 modos de seleção,  447 modularizando um programa com métodos,  156 módulo,  41, 155 módulos em Java,  155 moeda, lançamento,  165, 187 MoleculeViewer, applet,  724 monitor,  812 monitorar eventos do mouse,  450 monitor de exibição,  484 Monospaced, fonte do Java,  492 Morse Code Web Service,  1065 Morse, código,  897 Motif-style (UNIX), aparência e funcionamento,  769, 781 mouse,  3, 419, 724 MouseAdapter, classe,  452 mousePressed, método,  857 mouseClicked, método da interface MouseListener,  449, 452 mouse de múltiplos botões,  454 mouse de três botões,  454 mouse de um, dois ou três botões,  454 mouseDragged, método da interface MouseMotionListener,  449, 455 mouseEntered, método da interface MouseListener,  449 MouseEvent, classe,  435, 449, 781 getClickCount, método,  455 getPoint, método,  456 getX, método,  452 getY, método,  452

isAltDown, método,  455 isMetaDown, método,  455 isPopupTrigger, método,  781 mouseExited, método da interface MouseListener,  449 MouseInputListener, interface,  449, 452 MouseListener, interface,  435, 449, 452, 781 mouseClicked, método,  449, 452 mouseEntered, método,  449 mouseExited, método,  449 mousePressed, método,  449, 781 mouseReleased, método,  449, 781 MouseMotionAdapter, classe,  453, 456 MouseMotionListener, interface,  435, 449, 452 mouseDragged, método,  449, 455 mouseMoved, método,  449, 455 mouseMoved, método da interface MouseMotionListener,  449, 455 mousePressed, método da classe MouseAdapter,  857 mousePressed, método da interface MouseListener,  449, 781 mouseReleased, método da interface MouseListener,  449, 781 MouseWheelEvent, classe,  450 MouseWheelListener, interface,  450 mouseWheelMoved, método,  450 mouseWheelMoved, método da interface MouseWheelListener,  450 .mov, extensão de arquivo,  758 moveTo, método da classe GeneralPath,  508

Mozilla Foundation,  18 .mp3, extensão de arquivo,  758 MPEG-1, vídeos (extensões .mpeg ou .mpg),  758 .mpeg, extensão de arquivo,  758 MPEG Layer 3 Audio (.mp3), formato de arquivo,  758 .mpg, extensão de arquivo,  758 mudando de diretório,  33, 724 muitos para um, mapeamento,  658 multicast,  860, 893 multimídia,  743 múltiplas declarações de classe em um arquivo de código-fonte,  248 MULTIPLE_INTERVAL_SELECTION, constante de interface ListSelectionModel,  447 multiplicação, *, operador,  41 multiplicação, operador de atribuição composta, *=,  102 multiplicidade,  368 multiply, método da classe BigInteger,  594 multiprocessador,  4 multiprogramação,  4 Multipurpose Internet Mail Extensions (MIME),  949, 971 multitarefa,  8 multithreading,  8, 639, 804, LXVII mundo virtual,  18 Musical Instrument Digital Interface (MIDI), formato de arquivo (extensões .mid ou .rmi),  756 Musical Instrument Digital Interface (MIDI), formato de arquivo (.mid ou .rmi extensões),  758 MVC (Model-View-Controller),  LXIX MyShape, hierarquia,  330 MyShape, hierarquia com MyBoundedShape,  331 MySpace,  18 MySQL,  19, 899 MySQL 5.0 Community Edition,  910 MySQL Connector/J,  910 mysqld-nt.exe,  910 MySQL (www.mysql.com),  910

MySQL (www.mysql.com/products/mysql/),  910

N n, caractere de conversão,  1096 name, atributo do applet-desc elemento,  736

não-ambíguo, princípio do projeto Unicode,  XXIX NASA Multimedia Gallery,  761 NASA, multimídia (www.nasa.gov/multimedia/ highlights/index.html),  761 native, palavra-chave,  1069 navegabilidade bidirecional na UML,  391 navegador,  74 navegador Web,  74, 723, 863 executar um applet,  727, 730 navegando,  862 Navigator, janela no NetBeans,  960 negação lógica, !,  138 NervousText, applet,  724 NetBeans IDE,  947, 1019, XII adicionar uma referência de serviço Web a um aplicativo,  1025 adicionar um handler de evento,  XVI criar um aplicativo desktop,  1025 criar um aplicativo Web,  1020 criar um banco de dados Java DB,  996 criar um novo projeto,  XIII Design, visualização,  XIII editor visual,  956 grade de alinhamento,  XII GroupLayout,  XII initComponents, método autogerado,  XVI linhas de guia,  XIV, XV New JFrame Form, diálogo,  1026 New Web Service Client diálogo,  1025 New Web Service diálogo,  1021 Palette, janela,  XIII, XIV Properties, janela,  XIII, XV Services, guia,  997 Show Line Numbers,  958 Source, visualização,  XIII vídeo de demonstração (www.deitel.com/books/ jhtp8),  29 vinculando um JSF Table a uma tabela de banco de dados,  997 Web Application, projeto,  1020 Netbeans IDE, ferramenta de design,  xxiv Netbeans Matisse GUI, designer,  XII NetBeans (www.netbeans.org),  9 new, palavra-chave,  38, 60, 191, 1069 newCachedThreadPool, método da classe Executors,  810 newCondition, método da interface Lock,  836, 837 New JFrame Form diálogo,  1026 New Web Service Client diálogo,  1025 New Web Service diálogo,  1021 new Scanner(System.in), expressão,  38 New to Java Center (java.sun.com/new2java/),  8 next, método de ResultSet,  914 next, método da classe Scanner,  62 next, método de interface Iterator,  641 nextDouble, método da classe Scanner,  72 nextInt, método da classe Random,  164, 167 nextLine, método da classe Scanner,  62 Nimbus, aparência e comportamento,  420, 781 swing.properties,  xxxii, 420 níveis de aninhamento,  LIV nível de recuo,  86 nó,  LXIV nó em uma lista,  696

nó filho,  709 nó folha,  709 em uma árvore de pesquisa binária,  713 NomeDaClasse .this,  778 nome de classe,  30 completamente qualificado,  63 nome de classe completamente qualificado,  63, 265 nome de domínio Internet em ordem inversa,  265 nome de fonte,  492 nome de host,  876 nome de papel na UML,  369 nome de um array,  191 nome de uma variável,  40 nome de um parâmetro,  862 nome qualificado,  907 nomes de diretório na declaração package,  265 nome simples de uma classe,  265 NONE, constante da classe GridBagConstraints,  793 nó pai,  709, 719 NoPlayerException, exceção,  759 nó raiz,  709 NORTH, constante da classe BorderLayout,  452, 463 NORTH, constante da classe GridBagConstraints,  793 NORTHEAST, constante da classe GridBagConstraints,  793 NORTHWEST, constante da classe GridBagConstraints,  793 nós irmãos,  709 NoSuchElementException, classe,  562, 565 notação algébrica,  41 Notação Big O,  618, 622, 625, 628, 632 notação científica,  1092 notação científica computadorizada,  1092 notação de infixo,  717 notação exponencial,  1092 notação pós-fixa,  717 nota (na UML),  84 notifyAll, método da classe Object,  299, 825, 827 notify, método da classe Object,  299 notify, método da classe Object,  825 nova linha, caractere (\l),  34 nova linha, sequência de escape, ,  239 nova linha, sequência de escape, \n,  35, 38, 516 null,  1069 null, palavra-chave,  65, 67, 74, 192, 422, 696 null, palavra reservada,  105 NullPointerException, classe,  336 Number, classe,  687 doubleValue, método,  688 número complexo,  276 número de coluna em um conjunto de resultados,  904 número de identificação de empregado,  553 número de ponto flutuante de precisão dupla,  70 número de ponto flutuante de precisão simples,  70 número de porta,  866, 867, 876, 877, 882 número de porta do servidor,  876 número não especificado de argumentos,  217 número perfeito (exercício),  187 número primo,  234 número pseudoaleatório,  164, 167 número real,  38 números aleatórios,  167 deslocar um intervalo,  165 diferença entre valores,  167 elemento de chance,  164 escalonamento,  165 fator de escalonamento,  165, 167 geração,  199 geração para criar orações,  546 número pseudoaleatório,  164

Índice remissivo processando,  163 semear,  164 valor de deslocamento,  165, 167 valor de semente,  167 Números complexos, exercício,  276 Números racionais, exercício,  276

O O(1), tempo,  618 O(log n), tempo,  622 O(n), tempo,  618 O(n2ÿ), tempo,  618 O(n log n), tempo,  632 Object, classe,  258, 279, 282, 576 clone, método,  298 equals, método,  298 finalize, método,  298 getClass, método,  298, 320, 427 hashCode, método,  299 notifyAll, método,  299, 825, 827 notify, método,  299, 825 toString, método,  284, 299 wait, método,  299, 825 ObjectInput, interface,  570 readObject, método,  570 ObjectInputStream, classe,  555, 570, 574, 866, 867, 871 Object Management Group (OMG),  17 www.omg.org,  17 Object Management Group (www.omg.org),  17 ObjectOutput, interface,  570 writeObject, método,  570 ObjectOutputStream, classe,  555, 570, 663, LXVIII close, método,  574 flush, método,  871 objeto,  2 objeto-assunto,  LXV objeto de entrada padrão (System.in),  38 objeto de evento,  434 objeto de saída padrão (System.out),  32 objeto desserializado,  570 objeto de uma classe derivada,  307 objeto de uma classe derivada é instanciado,  296 objeto empacotador (coleções),  663 objeto imutável,  260 objeto, orientação,  15 objeto originador,  LXII objeto (ou instância),  15, 384, 728 objeto proxy,  LXI objeto serializado,  570 objeto zelador,  LXII Observable, classe,  LXV Observações de engenharia de software,  7 Observações de engenharia de software, visão geral,  xxv Observações sobre a aparência e comportamento,  7 Observer, interface,  LXV observer, objeto,  LXV Observer, padrão de design,  LIX, LXII, LXV obter (get) um valor,  66 ocultamento de dados,  64 ocultamento de informações,  15, 64 ocultar detalhes da implementação,  156, 245 offer, método da classe PriorityQueue,  655 offline-allowed, elemento de um documento JNLP,  735 Oito rainhas, exercício,  234, 612 abordagens de força bruta,  234 OK, botão,  75 OMG (Object Management Group),  17

1133

ON, cláusula,  907 ONE, constante da classe BigInteger,  594, 595

OOAD (object-oriented analysis and design),  16 OOD (object-oriented design),  xxiv, 15, 361, 365, 366, 371, 374 OOP (object-oriented programming),  16, 242, 279 opções de compilador -d,  265 opções mutuamente exclusivas,  440 OPEN, constante da classe Arc2D,  506 open, método da classe Desktop,  XXII openFileDialog, método da interface FileOpenService,  746 openMultiFileDialog, método da interface FileOpenService,  751 openStream, método da classe URL,  1034 operação,  60 na UML,  60 operação atômica,  816, 939 Operação de um objeto,  15 operação física de entrada,  577 operação física de saída,  577 operação na UML,  368, 377, 380, 393, 394, 397 operação para desenfileiramento de fila,  707 operação para enfileiramento de fila,  707 operações concorrentes,  804 operações de carregar/armazenar,  236 operações de deslocamento de bits,  L operações lógicas de entrada,  577 operações lógicas de saída,  577 operações paralelas,  804 operador,  39 operador condicional, ?:,  86, 104 operador de comparação,  329 operador de decremento, --,  102 operador de igualdade == para comparar objetos String,  519 operador de resto, %,  41 Operadores ^, OU lógico booleano exclusivo,  136, 138 --, pré-decremento/pós-decremento,  103 -=, operador de atribuição de subtração,  102 !, NÃO lógico,  136, 138 ?:, operador condicional ternário,  86, 104 *=, operador de atribuição de multiplicação,  102 /=, operador de atribuição de divisão,  102 &&, E condicional,  136 &, E lógico booleano,  136, 137 %=, operador de atribuição de resto,  102 +=, operador de atribuição de adição,  102, 131 ++, pré-incremento/pós-incremento,  103 ++, pré-incremento/pré-incremento,  103 =, atribuição,  39, 45 ||, OU condicional,  136, 137 |, OU inclusivo lógico booleano,  136, 137 atribuição, =,  45 binário,  39, 41, 138 coerção,  97 complemento lógico, !,  138 de atribuição composta,  102, 104 E condicional, &&,  136, 137 E lógico booleano, &,  136, 137 exponenciação,  126 incremento, ++,  102 incremento e decremento,  102 lógicos,  138 módulo, %,  41 multiplicação, *,  41 multiplicativos, *, / e %,  98 negação lógica, !,  138 operador condicional, ?:,  86, 104 operador de decremento, --,  102

1134

Índice remissivo

operadores lógicos,  136, 138, 140 OU condicional, ||,  136, 137 OU inclusivo lógico booleano, |,  137 OU lógico booleano exclusivo, ^,  136, 138 pós-decremento,  102 pré-decremento,  102 pré-incremento,  102 resto, %,  41 ternário,  86 unário,  138 operadores de atribuição de bits,  L ^=, (OU exclusivo sobre bits),  LI &=, (AND sobre bits),  LI =, (deslocamento para a direita com sinal),  LI >>>=, (deslocamento para a direita sem sinal),  LI |=, (OU inclusivo sobre bits),  LI operador unário,  97 coerção,  97 operando,  39, 98, 237 or, método da classe BitSet,  LI Oracle Corporation,  899, XXIX ordem,  82 ordem crescente,  219 ASC, em SQL,  905, 906 Ordem de bloclos catch, exercício,  359 ordem de classificação,  657, 658 ordem decrescente,  219 ordem de handlers de exceção,  359 ordem em que as ações devem ser executadas,  82 ordenação de registros,  903 ORDER BY, cláusula de SQL,  903, 905, 906 Organizador de angariação de fundos,  XXVII orientação horizontal sequencial em GroupLayout,  XII orientado para a ação,  16 otimização do compilador,  127 OU condicional, ||, operador,  136, 137 tabela-verdade,  137 OU exclusivo sobre bits (^) operador,  XLIII, XLIV OU inclusivo sobre bits (|) operador,  XLIII OU lógico booleano exclusivo, ^, operador,  136, 138 tabela-verdade,  138 OU lógico booleano inclusivo, |, operador,  137 OutOfMemoryError,  696 OutputStream, classe,  570, 576, 866, 867 OutputStreamWriter, classe,  578 ouvinte de evento,  329, 432, 452 classe adaptadora,  452 interface,  431, 432, 433, 435, 449, 452 ouvinte registrado,  434 ouvir eventos,  431, 432 oval,  495, 498, 728 oval preenchida com cores que mudam gradualmente,  505 oval unida por um retângulo,  498 @Override, anotação,  284

P package, declaração,  264 package-list, gerado por javadoc,  XLII package, palavra-chave,  1069 pack, método da classe Window,  787

pacote,  37, 163, 264, 859, 877, XXXIX pacote básico,  33 pacote de datagrama,  859, 877 pacote de rede,  163 pacote, estrutura de diretórios,  264 pacote, nome,  63 pacote opcional,  267

pacote padrão,  63, 264 Pacotes,  155

com.google.gson.Gson,  1033 com.sun.rave.web.ui.appbase,  954 com.sun.webui.jsf.component,  954 com.sun.webui.jsf.model,  1012 java.applet,  163 java.awt,  163, 423, 486, 503, 744, 752, 772, 781 java.awt.color,  503 java.awt.event,  163, 433, 452, 460 java.awt.font,  503 java.awt.geom,  163, 503 java.awt.image,  503 java.awt.image.renderable,  503 java.awt.print,  503 java.beans,  850 java.io,  163, 555 java.lang,  38, 157, 163, 282, 298, 516, 808 java.math,  70, 593 java.net,  163, 859 java.sql,  163, 913 java.swing,  728 java.text,  163 java.util,  37, 163, 221, 687 java.util.concurrent,  164, 810, 823, 851 java.util.concurrent.locks,  835, 836 java.util.prefs,  661 java.util.regex,  516 javax.faces.validators,  964 javax.jnlp,  734, 743, 746 javax.media,  164, 758 javax.servlet,  950 javax.servlet.http,  950, 976, 978 javax.servlet.jsp,  951 javax.servlet.jsp.tagext,  951 javax.sql.rowset,  924 javax.swing,  164, 421, 423, 427, 435, 470, 486,

744, 772, 784, 786

javax.swing.event,  164, 433, 447, 452, 772 javax.swing.table,  915, 923

pacote padrão,  63 pacotes da Java API,  163 pacote, visão geral (java.sun.com/javase/6/docs/ api/overview-summary.html),  164 padrão,  506 padrão arquitetônico Layers,  LXX padrão de 1 e 0 s,  553 padrão de design Visitor,  LXII padrão de preenchimento,  506 padrão, princípio do projeto Unicode,  XXIX padrões arquitetônicos,  LIX, LXIX, LXX padrões de design,  xxii, 18 padrões de design comportamentais,  LIX, LXI, LXIV padrões de design criacionais,  LIX, LXII, LXVII, LXX padrões de design de concorrência,  LIX, LXVII padrões de design estruturais,  LIX, LXI, LXIII, LXVIII padrões de design Java,  xxvii padrões de design, elementos de software orientado a design reutilizáveis,  LVIII Pagamentos,  XXVII Page Down, tecla,  458 Page Up, tecla,  458 página inicial,  961 paginationControls, propriedade de um Table,  998 página Web,  74 painel,  467 Painel de caracteres rolantes, exercício,  766 painel de conteúdo,  447, 778 setBackground, método,  447 Painel de imagens rolantes, exercício,  766 painel de vidro,  447

paint, método da classe JApplet,  728 paint, método de JApplet,  730, 732 Paint, objeto,  505 paintComponent, método da classe JComponent,  107,

455, 484, 751, 770, 772

paintIcon, método da classe ImageIcon,  751

palavra-chave,  30, 84 palavra reservada,  30, 83, 1069 false,  86 null,  65, 67, 105 true,  85 Palavras-chave abstract,  309 boolean,  86, 1082 break,  132 case,  132 catch,  340 char,  38 class,  30, 59 continue,  134 default,  132 do,  84, 127 double,  38, 70 else,  84 enum,  171 extends,  107, 282, 289 false,  86, 1069 final,  134, 158, 195, 817 finally,  341 float,  38, 70 for,  84 if,  84 implements,  322 import,  37 instanceof,  319 int,  38 interface,  322 new,  38, 60, 191 null,  67, 192, 1069 private,  64, 245, 252 public,  31, 58, 59, 64, 245 reservadas, mas não utilizadas pelo Java,  1069 return,  65, 156, 161 static,  74, 126, 156 super,  281, 297 switch,  84 synchronized,  812 tabela de palavras-chave e palavras reservadas,  1069 this,  246, 259 throw,  348 true,  86, 1069 try,  340 void,  31, 59 while,  84, 127 Palavras-chave do Java,  1069 palavras cruzadas, exercício,  766 palavras e frases descritivas,  371, 372 Palette,  959, 961 Palette no NetBeans,  957 palíndromo,  117, 611 Palíndromos, exercício,  611 papel na UML,  369 param, elemento,  862 Parameters:, nota nota,  XXXVIII parâmetro,  61, 62 formal,  159 parâmetro de applet,  862 parâmetro de exceção,  341 parâmetro de operação na UML,  63, 377, 380, 381 parâmetro de saída para CallableStatement,  938

parâmetro formal,  159 parâmetro formal de tipo,  674 parâmetro na UML,  62, 377, 380, 381 @param, tag javadoc,  XXXVIII parênteses,  31, 41 aninhados,  41 redundantes,  43 parênteses aninhados,  41 parênteses desnecessários,  43 parseDouble, método de Double,  732 parseInt, método da classe Integer,  75, 145, 219, 422 parte imaginária,  276 parte real,  276 partes intercambiáveis padronizadas,  16 parte superior,  655 parte superior de uma pilha,  695 participante de um formulário virtual,  1007 Pascal, Blaise,  8 passagem de mensagem na UML,  385 passando arrays para métodos,  203 passando elementos de array para métodos,  203 passando opções para um programa,  218 passar por referência,  205 passar por valor,  203, 205 passeio completo,  513 Passeio do cavalo,  232, 513 abordagem de força bruta,  233 exercício,  513 teste do passeio fechado,  234 passeio fechado,  234, 513 passo de partição em quicksort,  635 pasta de arquivo,  726 @Path, anotação,  1029 PATH, variável de ambiente,  xxxi, 33 @PathParam, anotação,  1029 pathSeparator, campo static de File,  558 Pattern, classe,  516, 541 compile, método,  541 matcher, método,  541 matches, método,  541 Payable, declaração de interface,  323 Payable, diagrama de classe UML de hierarquia de interface,  323 Payable, processamento de programa de teste de interface Invoice e Employee polimorficamente,  328 PayPal,  XXVII pedagogia baseada na introdução antecipada de classes e objetos,  xxi peek, método da classe PriorityQueue,  655 peek, método da classe Stack,  655 pequenos círculos (na UML),  83 pequeno símbolo de losango (para representar uma decisão em um diagrama de atividades da UML),  375 percorrer uma árvore,  713 percorrer um array,  211 percurso em uma árvore binária em ordem de nível,  714, 720 percurso na ordem,  709 percurso na pós-ordem,  710, 712 percurso na pré-ordem,  710 Percurso para sair de um labirinto utilizando reversão recursiva, exercício,  612 persistente,  4 persistente Hashtable,  661 personalização,  970 pesquisa,  199 pesquisa binária de um array,  199 pesquisa de dados,  615 pesquisa de DNS,  948

Índice remissivo Pesquisa local,  XXVII pesquisando,  695 Pesquisa sequencialmente um item em um array,  617 PHP,  19 PI,  512 PIE, constante da classe Arc2D,  506 pilha,  161, 678, 695, 703 estouro de pilha,  162 pilha de chamadas de método,  162 programa, pilha de execução,  162 pilha de chamadas de método,  162 pilha de execução do programa,  162 pilha, operação colocar na pilha (push),  704 remover da pilha (pop),  704 pior cenário de tempo de execução para um algoritmo,  618 pipe,  576 PipedInputStream, classe,  576 PipedOutputStream, classe,  576 PipedReader, classe,  578 PipedWriter, classe,  578 PixelGrabber, classe,  765 pixel (picture element),  105, 484 placa de som,  756 PLAF (pluggable look-and-feel),  769 PLAIN, constante da classe Font,  491, 492 PLAIN_MESSAGE,  422 plataforma .NET,  8 play, método da classe Applet,  756 play, método da interface AudioClip,  756 Player, interface,  758 getControlPanelComponent, método,  760 getVisualComponent, método,  760 start, método,  760 .png, extensão de nome de arquivo,  427 .png, extensão de arquivo,  744 PNG (Portable Network Graphics),  427, 744 Podbop,  XXVIII Point, classe,  456 POJO (Plain Old Java Object),  1021 polígono,  500, 503 polígonos fechados,  501 polilinha,  500 polilinhas,  500 polimorfismo,  134, 301, 305, 395, 396, 402 polinômio,  42, 43 polinômio de segundo grau,  42, 43 poll, método da classe PriorityQueue,  655 Polygon, classe,  484, 500 addPoint, método,  501, 503 ponta de seta em um diagrama de sequência na UML,  385 pontilhamento,  724 ponto,  492, 726 ponto ativo (mapa de imagens),  724 ponto de entrada,  140 ponto de inserção,  221, 651, 697 ponto de interrupção,  1078 inserindo,  1080, 1081 listando,  1088 removendo,  1088 ponto de lançamento,  338 ponto de saída,  140 de uma instrução de controle,  84 ponto-e-vírgula (;),  32, 38, 46 ponto flutuante, número,  70, 93, 95, 97, 655, 732, 1093 divisão,  98 double, tipo primitivo,  70 float, tipo primitivo,  70

1135

precisão dupla,  70 precisão simples,  70 pontos ativos no bytecode,  10 ponto separador (.),  60, 74, 126, 157, 258, 503 pool de threads,  810, 867 pop, método da classe Stack,  655 pôquer,  235 Pôquer, jogo,  896 porcentagem (%), caractere curinga de SQL,  904 porta,  866 portabilidade,  11, 492, XXXI Portable Network Graphics (PNG),  427, 744 porta de conexão,  866 portável,  10 pós-condição,  354 pós-decrementar,  103 pós-decremento, operador,  102 posição do indicador de classe JSlider,  772 posição, número,  190 posicionamento absoluto,  959 pós-incrementar,  103, 104 pós-incremento, operador,  102 postback,  956 PostgreSQL,  899 potência de 3 maiores que 100,  89 potência (expoente),  157, 186 pow, método da classe Math,  127, 156, 157, 182 precedência,  41, 46, 104, 595 gráfico,  41 operadores aritméticos,  41 tabela,  98 precedência de operadores,  41, 595 regras,  41 tabela de precedência de operadores,  98 precedência de operadores, tabela,  1066 precisão,  71, 1091, 1093 formato de um número de ponto flutuante,  98 precisão de um número de ponto flutuante formatado,  71 precisão de um valor de ponto flutuante,  70 pré-condição,  354 pré-decrementar,  103 pré-decremento, operador,  102 predicado,  904 predicado, método,  701 preencher com cor,  484 pré-incrementar,  103, 104 pré-incrementar e pós-incrementar,  103 PreparedStatement, interface,  927, 929, 938 documentação (java.sun.com/javase/6/docs/api/ java/sql/PreparedStatement.html),  927 executeQuery, método,  932 executeUpdate, método,  932 setString, método,  927, 932 prepareStatement, método da interface Connection,  929 preprocess, método ( JSF),  956, 958 prerender, método ( JSF),  956, 958 previous, método de interface ListIterator,  643 primary, propriedade de um JSF Button,  996 primeiro refinamento,  99 primeiro refinamento no refinamento passo a passo de cima para baixo,  93 primos,  187, 669 princípio do menor privilégio,  262 print, comando de depurador,  1081 printArray, método genérico,  674 printf, método de System.out,  1091 printStackTrace, método da classe Throwable,  349 PrintStream, classe,  577, 663 PrintWriter, classe,  562, 578 prioridade de thread,  807

1136

Índice remissivo

PriorityQueue, classe,  655 clear, método,  656 offer, método,  655 peek, método,  655 poll, método,  655 size, método,  656 private

campo,  252 dados,  252 modificador de acesso,  64, 245, 281 palavra-chave,  252, 391, 1069 private, palavra-chave,  64 private static

membro de classe,  258 probabilidade,  164 probabilidade igual,  166 problema da média de classe,  89, 90, 95 problema da média de classe geral,  93 problema de concorrência,  LXVII problema dos resultados do exame,  100 procedimento,  156 procedimento para resolver um problema,  82 procedure armazenada,  938 processador de dois núcleos,  4 processador de múltiplos núcleos,  4 processador de quatro núcleos,  4 processador de texto,  516, 522 processamento de arquivo,  555 processamento de dados comercial,  586 processamento de transação,  939 processamento em lote,  4 processamento polimórfico de coleções,  639 processamento polimórfico de exceções relacionadas,  344 processando polimorficamente Invoice e Employee s,  328 processo baseado em evento,  509 processo de design,  16 processo de implementação,  378, 391 processo de refinamento,  93 processo do projeto,  361, 364, 378, 381 @Produces, anotação,  1029 produto de inteiro ímpar,  151 produtor,  818 produtor/consumidor, relacionamento,  818, 830 Professor de digitação Aprimorando uma habilidade crucial na era dos computadores,  481 programa,  3 programação concorrente,  805 programação de jogos,  xxii, xxvii Programação de jogos,  18 programação estruturada,  2, 3, 8, 83, 120, 136, 140 resumo,  140 programação orientada a objetos (object-oriented programming — OOP),  2, 3, 6, 16, 242, 279 programa de computador,  3 Programa de conversão métrica,  548 programa de gerenciamento de vídeo,  306 programa de jogo de xadrez,  896 Programa de Planta Baixa, exercício,  766 programador,  3 programador de computador,  3 programa, pilha de execução,  704 programa, princípios de construção,  147 programar no específico,  305 programar no geral,  305, 334 programas gráficos gratuitos (www.freebyte.com/ graphicprograms),  762 programa tolerante a falha,  336

programa tradutor,  5 ProgrammableWeb,  XXVII Projects, janela,  957 Projeto de acessibilidade Reconhecimento da fala,  767 Speech Synthesis,  767 projeto orientado a objetos (object-oriented design — OOD),  361, 365, 366, 371, 374, 391 projeto orientado a objetos utilizando a UML,  xxiv projetos de programação,  xxii, xxvii promoção,  98 regras,  98, 162 promoção de argumentos,  162 promoções para tipos primitivos,  162 prompt,  38 Prompt de Comando,  32 Properties, classe,  661, 983 getProperty, método,  661 keySet, método,  663 list, método,  663 load, método,  663 setProperty, método,  661 store, método,  663 Properties, janela,  959 propertyChange, método da interface PropertyChangeListener,  850 PropertyChangeListener, interface,  850 propertyChange, método,  850 propriedade auto-similar de um fractal,  600 propriedade de uma classe JavaBean,  951 proteção de cheque, exercício,  547 proteção de privacidade,  970 protected, modificador de acesso,  245, 281, 1069 Protetor de tela com formas, exercício,  512 Protetor de tela, exercício,  512 Protetor de tela para um número aleatório de linhas, exercício,  512 Protetor de tela utilizando a API Java2D, exercício,  512 Protetor de tela utilizando Timer, exercício,  512 protocolo de comunicação (jdbc),  913 protocolo de estado (HTTP),  971 protocolo seguro,  979 prototipagem rápida,  xxii Prototype, padrão de design,  LIX, LX, LXX provedor de dados,  998 Proxy, padrão de design,  LIX, LXI pseudocódigo,  16, 82, 85, 90, 99 algoritmo,  94 primeiro refinamento,  93, 99 segundo refinamento,  94, 100 public

interface,  242 método,  243, 245 método encapsulado em um objeto,  245 modificador de acesso,  245 serviços de uma classe,  242 static, membros de classe,  258 static, método,  258 public abstract, método,  322 final, static dados,  322

membro de uma subclasse,  281 método,  107 modificador de acesso,  58, 59, 64, 281 palavra-chave,  391, 393, 394, 1069 public, classe,  31 Publicando um serviço Web com um aplicativo Java SE 6 padrão (today.java.net/pub/a/ today /2007/07/03/jax-ws-web-serviceswithout-ee-containers.html),  1022 public, palavra-chave,  31, 64

publicar um serviço Web,  1019, 1020, 1022, 1060

push, método da classe Stack,  655 put, método de interface BlockingQueue,  823, 824 de interface Map,  661 de interface RequestContext,  1045

Q quadrados eliminados posicionando uma rainha no canto superior esquerdo de um tabuleiro de xadrez,  612 quadro delimitador para um oval,  741 quadro de pilha,  162 quantificadores utilizados em expressões regulares,  539 quantificador ganancioso (expressões regulares,  539 quantificador preguiçoso (expressôes regulares),  539 quantificador relutante (expressões regulares),  539 quantum,  806 quebra de linha,  470 QUESTION_MESSAGE,  422 Questionário sobre o aquecimento global,  153 Queue, interface,  637, 639, 655, 823 quicksort, algoritmo,  635 QuickTime (.mov), formato de arquivo,  758 Quilometragem de combustível,  115

R RadialGradientPaint, classe,  505 radianos,  157 radical (base) de um número,  533, 534 Radio Button Group, componente JSF,  961, 964 raio,  512, 741 raio de um círculo,  187 raiz quadrada,  157 ramificação,  LXIV Random, classe,  163, 164, 231 java.sun.com/javase/6/docs/api/java/util/ Random.html,  164 nextInt, método,  164, 167 setSeed, método,  168 random, método da classe Math,  164 range, método da classe EnumSet,  257

rastreamento de pilha,  337 rastreando clientes,  970 Rational, classe,  276 Rational Software Corporation,  17, 365 Rational Software Corporation™,  365 RDBMS (relational database management system),  950 read, método da classe InputStream,  746 Reader, classe,  578 readObject, método de ObjectInput,  570 readObject, método de ObjectInputStream,  576 Read/Write Lock, padrão de design,  LIX, LXVII realização na UML,  323 realizando operações concorrentemente,  804 realizando uma tarefa,  59 realizar uma ação,  32 realizar um cálculo,  47 recarregar uma página Web inteira,  1005 receber dados de um servidor,  876 receber uma conexão,  871 receive, método da classe DatagramSocket,  879 recomendações curriculares da ACM/IEEE e do Computer Science Advanced Placement Examination,  xxii

reconhecendo clientes,  971 reconhecimento de fala,  767 recorrendo texto em uma JTextArea,  470 Rectangle2D, classe,  484 Rectangle2D.Double, classe,  503 Rectangle, Classe (exercício),  275 recuo,  86, 87 recuperar-se de um erro,  199 recursão algoritmo recursivo de pesquisa binária,  635 algoritmo recursivo de pesquisa linear,  635 avaliação recursiva,  592 avaliação recursiva de 5!,  592 chamada recursiva,  591, 595 gerando recursivamente números de Fibonacci,  595 imprimir recursivamente uma lista de trás para frente,  719 método factorial recursivo,  592 método power recursivo, exercício,  611 método recursivo,  590 overhead,  598 passo de recursão,  591, 595 pesquisar recursivamente uma lista,  719 retorno recursivo,  607 Recursão, exercícios Encontrar o valor mínimo em um array,  612 Fractais,  612 Gerador de labirintos aleatório,  613 Imprimir um array,  612 Imprimir um array de trás para frente,  612 Labirintos de qualquer tamanho,  613 Máximo divisor comum,  611 Método power recursivo, exercício,  611 Oito rainhas,  612 Palíndromos,  611 Percorrer um labirinto utilizando retorno recursivo,  612 pesquisa binária,  635 pesquisa linear,  635 Tempo para calcular números de Fibonacci,  613 Visualizando uma recursão,  611 recursão indireta,  591 recursão infinita,  296, 593, 597, 598 recursos deste livro para o professor,  xxvii Recursos para o aluno incluídos neste livro,  xxvi recurso, vazamento,  258 rede,  552 rede de computadores,  4 rede local (local area network — LAN),  4 rede peão,  4 rede social,  18 redimensionamento dinâmico,  190 redirecionar um fluxo padrão,  555 redireciona um fluxo,  555 ReentrantLock, classe,  835, 836 refatoração,  xxii refatorando,  18, 958 referência,  67 referência de serviço Web,  1025 referenciar um objeto,  67 refinamento passo a passo de cima para baixo,  93, 95, 99 refresh, método da classe CachedRowSetDataProvider,  1004 regexFilter, método da classe RowFilter,  924 regionMatches, método da classe String,  519 registrador do acumulador,  236, 238 registrar um ActionListener,  778 registrar uma porta,  866 registrar um handler de evento,  431 Registration Form Modification,  993

Índice remissivo registro,  553, 558 registro, chave,  553, 586 registro de ativação,  162, 598 registro de evento,  432 registro de transação,  586 registros de intercalação a partir de tabelas,  907 regra de aninhamento,  142 Regra de Integridade de Entidade,  902 Regra de Integridade Referencial,  901 regra de negócio,  950 regra geral (heurística),  136 regras de precedência de operador,  41, 595 regras de promoção para tipos primitivos,  162 regras para formar programas estruturados,  141 Regular Expressions Resource Center (www.deitel. com/regularexpressions/),  542 reinventando a roda,  7, 37, 219 relação áurea,  594 relação de números de Fibonacci sucessivos,  594 relacionamento cliente-servidor,  859 relacionamento de muitos para muitos,  903 relacionamento de muitos para um na UML,  370 relacionamento de um para muitos na UML,  370 relacionamento de um para um na UML,  370 relacionamento entre uma classe interna e sua classe de primeiro nível,  440 relacionamento hierárquico de método trabalhador/método chefe,  156 relacionamento inteiro/parte,  369 relacionamento tem um,  253, 279, 369 relançando exceções, exercício,  359 relançar uma exceção,  348, 359 RELATIVE, constante da classe GridBagConstraints,  796 Reload, item do menu Applet de appletviewer,  725, 726 Relógio digital, exercício,  765 REMAINDER, constante da classe GridBagConstraints,  796 remove, método da classe ArrayList,  221, 222 remove, método de interface Iterator,  641 remover da pilha, operação,  701 remover duplicata String,  657 removeTableModelListener, método da interface TableModel,  915 removeTrayIcon, método da classe SystemTray,  XXIV rendered, propriedade de um componente JSF,  965 renderizando XHTML em um navegador da Web,  949 reordenar a saída com um índice de argumento,  1102 repaint, método da classe Component,  456 repaint, método da classe JComponent,  486 repetição,  84, 144 controlada por contador,  90, 97, 99 controlada por sentinela,  93, 94, 95, 96, 97 definida,  90 repetição controlada por contador,  90, 97, 99, 120, 121, 237, 597 repetição controlada por sentinela,  93, 94, 95, 96, 97, 152, 237 repetição definida,  90 repetição indefinida,  93 repetição, instrução de,  83, 84, 88, 94, 597 do...while,  84, 127, 128, 144 for,  84, 124, 144 while,  84, 89, 91, 97, 120, 144 repetição termina,  89 repintado,  730 replaceAll, método da classe Matcher,  541 da classe String,  540

replaceFirst, método da classe Matcher,  541 da classe String,  540

1137

repositórios de arquivos,  267 representação de array bidimensional de um labirinto,  613 representando inteiros no formato hexadecimal,  1092 representar inteiros no formato octal,  1091 Representational State Transfer (REST),  1018, 1020 reproduzindo áudio,  756 RequestBean,  952 RequestContext, interface,  1045 put, método,  1045 required, propriedade de um componente JSF,  968 requisitos,  16, 364 reservas, sistema,  231 reset, propriedade de um JSF Button,  996 resolução,  105, 484 resolver o problema das Torres de Hanói com um método recursivo,  600 @Resource, anotação,  1037 resources, elemento de um documento JNLP,  736 resposta de servidor,  949 respostas para uma pesquisa,  198 restart, método da classe Timer,  751, 763 RESTful, serviços Web,  1020 resto, operador , %,  117 resto, operador de atribuição composta, %=,  102 REST (Representational State Transfer),  1018 resultado,  904 ResultSet, interface,  914, 918, 919 absolute, método,  919 close, método,  915 CONCUR_READ_ONLY, constante,  919 CONCUR_UPDATABLE, constante,  919 constante de concorrência,  919 getInt, método,  914 getObject, método,  914, 926 getRow, método,  919 last, método,  919 next, método,  914 nome de coluna,  914 número de coluna,  914 TYPE_FORWARD_ONLY, constante,  918 TYPE_SCROLL_INSENSITIVE, constante,  918 TYPE_SCROLL_SENSITIVE, constante,  918 ResultSetMetaData, interface,  914, 919 getColumnClassName, método,  919 getColumnCount, método,  914, 919 getColumnName, método,  919 getColumnType, método,  914 ResultSetTableModel, ativa JTable para exibir os conteúdos de ResultSet,  915 retângulo,  275, 484, 487, 495, 724, 728 retângulo arredondado,  496, 506 retângulo arredondado (para representar um estado em um diagrama de estado da UML),  374 retângulo delimitador,  144, 496, 498, 770 retângulo em alto relevo,  498 retângulo em baixo relevo,  498 retângulo preenchido,  487 retângulo tridimensional,  496 retângulo tridimensional preenchido,  496 reticências (...) em uma lista de parâmetros de método,  217 Retornando indicadores de erros a partir de métodos, exercício,  275 retorno de carro,  35 return, instrução,  591 return, palavra-chave,  65, 156, 161, 1069

1138

Índice remissivo

Return, tecla,  726 Returns:, nota,  XXXIX @return, tag javadoc,  XXXIX reutilização de software,  7, 264, 279, 671, XXVII reutilizar,  16, 37 reversão recursiva,  608 reverse,  650 reverse, método da classe Collections,  645, 650 reverse, método da classe StringBuilder,  528 reverseOrder, método da classe Collections,  646 reverter uma transação,  939 RGB, valor,  491 RGB, valores,  176, 486 Richards, Martin,  6 RIGHT, constante da classe FlowLayout,  463 Ritchie, Dennis,  6 .rmi, extensão de arquivo,  756, 758 robusto,  39 roda de mouse,  450 rolagem,  444, 447 rolagem automática,  447 rolagem vertical,  470 rolando dois dados,  170 rollback, método da interface Connection,  939 rollover Icon,  437 Rotação de imagens, exercício,  766 rotate, método da classe Graphics2D,  508 rótulo,  299, 425, LIV rótulo em uma switch,  132 rótulos para marcas de verificação,  769 RoundRectangle2D, classe,  484 RoundRectangle2D.Double, classe,  503, 506 RowFilter, classe,  924 RowSet conectado,  924 RowSet desconectado,  924 RowSet, documentação (java.sun.com/javase/6/ docs/technotes/guides/jdbc/getstart/ rowsetImpl.html),  924

RowSet, guia de introdução java.sun.com/javase/6/docs/technotes/guides/ jdbc/getstart/rowsetImpl.html,  924 RowSet, interface,  924

RSS feed,  XXVI RSS feeds,  18 RSS Resource Center,  XXVIII Ruby on Rails,  19 Rumbaugh, James,  16 run, comando de depurador,  1079 run, método da interface Runnable,  808, 887 Runnable, interface,  329, 808, 888 run, método,  808, 887 Run, opção no NetBeans,  1022 RuntimeException, classe,  343, 349

S SaaS (Software as a Service),  xxii, 19 saída,  32, 35 saída alinhada à direita,  126 saída formatada,  1097 –, (sinal de subtração), flag de formatação,  126 , , (vírgula), flag de formatação,  127 %f, especificador de formato,  71 0, flag,  196, 244 alinhamento à direita,  126, 1091 alinhamento à esquerda,  126, 1091 alinhando pontos de fração decimal na saída,  1091 arredondando,  1091 boolean, valores,  138 caractere de conversão,  1091

caracteres de sufixo de conversão de data e hora,  1094 composições de data e hora,  1094 datas,  1091 formato exponencial,  1091 inserindo caracteres literais,  1091 inteiros no formato hexadecimal,  1092 inteiros no formato octal,  1091 largura de campo,  126, 1091 números de ponto flutuante,  71 precisão,  71, 1091 separador de agrupamento,  127 sinal de subtração (–), flag de formatação,  126 vírgula (,), flag de formatação,  127 saindo da instrução for,  135 SalariedEmployee, classe concreta que estende classe abstract Employee,  313 SalariedEmployee, classe que implementa o método getPaymentAmount da interface Payable,  328 salário bruto,  115 Salesforce,  18 SalesForce,  XXVII Salesforce.com,  XXVII SansSerif, fonte do Java,  492 saturação,  491 SavingsAccount, Classe (exercício),  275 Scanner, classe documentação (java.sun.com/javase/6/docs/api/ java/util/Scanner.html),  258 hasNext, método,  131 java.sun.com/javase/6/docs/api/java/util/ Scanner.html,  258 next, método,  62 nextDouble, método,  72 nextLine, método,  62 Scanner, classe,  37, 38

Scanner de phishing,  588 Scanner de spam,  550 Scanner de spam, serviço Web,  1065 scheduler de thread,  807 Screen, classe (estudo de caso ATM),  367, 369, 377, 382, 383, 384, 385, 386, 393 script de shell,  562 script (Unicode),  XXXIII SCROLL_TAB_LAYOUT, constante da classe JTabbedPane,  792 seção administrativa do computador,  4 seção de armazenamento do computador,  3 seção de detalhe de método na API,  1076 seção de detalhes de construtor na API,  1076 seção de detalhes de um campo na API,  1075 seção de entrega do computador,  3 seção de recebimento do computador,  3 seção de resumo de classe aninhada na API,  1074 seção de resumo de construtor na API,  1074 seção de resumo de método na API,  1075 seção de resumo de um campo na API,  1074 Seção especial construindo seu próprio compilador,  695 construindo seu próprio computador,  236 exercícios de manipulação avançada de string,  546 projetos desafiadores de manipulação de string,  548 Second Life,  18 SecurityException, classe,  561 See Also:, nota,  XXXVII @see, tag javadoc,  XXXVII segundo refinamento,  100 segundo refinamento no refinamento passo a passo de cima para baixo,  94

segurança,  10 segurança de thread,  815, 840 segurança de tipo em tempo de compilação,  671 SEI (service endpoint interface),  1022, 1024 seleção,  84, 144 seleção dupla,  144 seleção dupla, instrução,  84 seleção, instrução,  83, 84 if,  84, 85, 129, 144 if...else,  84, 85, 97, 129, 144 switch,  84, 129, 133, 144 seleção múltipla, instrução,  84 if,  85 seleção única, instrução de,  144 selecionando dados a partir de uma tabela,  900 Selecionando formas, exercício,  513 selecionar cada dígito,  54 selecionar um item de um menu,  428 SELECT, SQL, palavras-chave,  903, 904, 905, 906 _self, frame-alvo,  863 semear números aleatórios,  164 send, método da classe DatagramSocket,  879 senha,  429 seno,  157 seno trigonométrico,  157 separador de diretório,  267 separador de grupos (saída formatada),  127 SequenceInputStream, classe,  578 sequência,  84, 144, 639, 709 sequência de escape,  35, 38, 558, 1103, 1108 barra invertida,  35 ", aspas duplas,  35 \t, tabulação horizontal,  35 nova linha, ,  38 nova linha, \l,  35 sequência de mensagens na UML,  384 sequência, estrutura,  83 Serializable, interface,  329, 570 serialização de objeto,  570, 872 serialized-form.html, gerado por javadoc,  XLII série infinita,  152 Serif, fonte do Java,  492 ServerSocket, classe,  866, 871, 887 accept, método,  866, 871 service, método da interface Servlet,  951 ServiceManager, classe,  746 lookup, método,  746 Services, guia no NetBeans,  997 serviço de sistema,  866 serviço de uma classe,  245 serviço orientado para conexão,  859 serviço sem conexão,  859, 877 serviços Web,  18 adicionando uma referência de serviço Web a um aplicativo,  1024 artefatos do lado do cliente,  1025 artefatos do lado do servidor,  1021 Build, opção no NetBeans,  1022 classe proxy,  1022, 1025 Clean and Build, opção no NetBeans,  1022 Clean, opção no NetBeans,  1022 consumir um serviço Web,  1019, 1060 Deploy, opção no NetBeans,  1022 do servidor de aplicativo GlassFish Tester página Web,  1023 @GET, anotação,  1029 host de serviço Web,  1019 implantando um serviço Web,  1022 JAX-RS,  1018 JAX-WS,  1018, 1025 MessageContext, interface,  1037 name, atributo da anotação @WebParam,  1022



Índice remissivo name, atributo da anotação @WebService,  1022 @Path, anotação,  1029 @PathParam, anotação,  1029

POJO (Plain Old Java Object),  1021 processando tipos definidos pelo usuário,  1051 @Produces, anotação,  1029 publicando um serviço Web,  1060 Publicando um serviço Web com um aplicativo Java SE 6 padrão (today.java.net/pub/a/ today /2007/07/03/jax-ws-web-serviceswithout-ee-containers.html),  1022 publicar um serviço Web,  1019 referência de serviço Web,  1025 RequestContext, interface,  1045 @Resource, anotação,  1037 REST,  1018 Run, opção no NetBeans,  1022 serviceName, atributo da anotação @ WebService,  1022 SOAP,  1025 Testando um serviço Web de outro computador,  1024 testar um serviço Web,  1023 @WebMethod, anotação,  1022 @WebParam, anotação,  1022 @WebService, anotação,  1021 WebServiceContext, interface,  1037 serviço Web,  1018, XXVI implementado como uma classe,  1019, 1060 publicando um serviço Web,  1022 Serviço Web de Lista telefônica com JSON,  1065 Serviço Web de Neutralidade Sexual,  1065 servidor,  4, 859 servidor de aplicativo,  947 servidores de aplicativo Apache Tomcat (tomcat.apache.org),  1021 GlassFish (glassfish.dev.java.net),  1021 JBoss Application Server (www.jboss.com/ products/platforms/application),  1021 servidor espera conexões de clientes,  866 servidor multiencadeado,  896, 897 servidor Web,  866, 948 servlet,  950 Servlet, interface,  950 destroy, método,  951 init, método,  950 service, método,  951 sessão,  971 sessão, monitoramento,  971 em serviços Web,  1035 SessionBean,  952 seta,  83 SET, cláusula de SQL,  909 set, comando de depurador,  1081 seta de navegabilidade na UML,  391 seta de rolagem,  444 seta de transição,  86, 89 na UML,  83 seta de transição (na UML),  89 Set, interface,  637, 639, 656, 658 seta, teclas,  458 setAlignment, método da classe FlowLayout,  463 set, método da classe BitSet,  LI de interface ListIterator,  643 setAttribute, método da interface HttpSession,  1037 setAutoCommit, método da interface Connection,  939 setBackground, método da classe Component,  223, 447, 490 setBounds, método da classe Component,  460 setCharAt, método da classe StringBuilder,  528

setColor, método da classe Color,  487 setColor, método da classe Graphics,  177, 487, 506 setCommand, método da interface JdbcRowSet,  925 setConstraints, método da classe GridBagLayout,  796 setDefaultCloseOperation, método da classe JFrame,  107, 428, 772 setDelay, método da classe Timer,  765 setDisabledTextColor, método da classe JTextComponent,  460 setEditable, método da classe JTextComponent,  431 setErr, método da classe System,  555 setFileSelectionMode, método da classe JFileChooser,  580 setFixedCellHeight, método da classe JList,  449 setFixedCellWidth, método da classe JList,  449 setFont, método da classe Component,  440 setFont, método da classe Graphics,  492 setForeground, método da classe JComponent,  778 setHint, método da classe Manager,  759 setHorizontalAlignment, método da classe JLabel,  428 setHorizontalScrollBarPolicy, método da classe JScrollPane,  470 setHorizontalTextPosition, método da classe JLabel,  428 setIcon, método da classe JLabel,  427

set, método,  66, 248 setIn, método da classe System,  555 setInverted, método da classe JSlider,  770 setJMenuBar, método da classe JFrame,  778 setLayout, método da classe Container,  427, 460, 465, 792 setLineWrap, método da classe JTextArea,  470 setListData, método da classe JList,  449 setLocation, método da classe Component,  460, 772 setLookAndFeel, método da classe UIManager,  784 setMajorTickSpacing, método da classe JSlider,  772 setMaximumRowCount, método da classe JComboBox,  444 setMnemonic, método da classe AbstractButton,  777 setOpaque, método da classe JComponent,  455, 457 setor,  499 setOut, método de System,  555 setPage, método da classe JEditorPane,  865 setPaint, método da classe Graphics2D,  505 setPaintTicks, método da classe JSlider,  772 setPassword, método da interface JdbcRowSet,  925 setProperty, método da classe Properties,  661 setRolloverIcon, método da classe AbstractButton,  438 setRowFilter, método da classe JTable,  924 setRowSorter, método da classe JTable,  923 setSeed, método da classe Random,  168 setSelected, método da classe AbstractButton,  778 setSelectionMode, método da classe JList,  447 setSize, método da classe Component,  460, 772 setSize, método da classe JFrame,  107, 428 setString, método da interface PreparedStatement,  927, 932 setStroke, método da classe Graphics2D,  505 setText, método da classe JLabel,  300, 428 setText, método da classe JTextComponent,  470 setToolTipText, método da classe JComponent,  427 setUrl, método da interface JdbcRowSet,  925 setUsername, método da interface JdbcRowSet,  925 setVerticalAlignment, método da classe JLabel,  428 setVerticalScrollBarPolicy, método da classe JScrollPane,  470

1139

setVerticalTextPosition, método da classe JLabel,  428 setVisible, método da classe Component,  428, 465,

772

setVisible, método da classe JFrame,  107 setVisibleRowCount, método da classe JList,  446 Shape, hierarquia de classes,  281, 303 Shape, objeto,  505

shell,  32 shell, prompt no Linux,  10 Shift,  460 Short, classe,  638 short, tipo primitivo,  129, 1069, 1070 promoções,  162 shortcut, elemento de um documento JNLP,  735 show, método da classe JPopupMenu,  781 showDialog, método da classe JColorChooser,  490 showDocument, método da interface AppletContext,  860, 863 showInputDialog, método da classe JOptionPane,  75, 422 showMessageDialog, método da classe JOptionPane,  74, 422 Show Line Numbers,  958 showOpenDialog, método da classe JFileChooser,  581 showStatus, método da classe Applet,  753 shuffle,  201 algoritmo,  648 shuffle, método da classe Collections,  645, 648, 649 shutdown, método da classe ExecutorService,  811 signalAll, método da interface Condition,  836 signal, método da interface Condition,  836, 838 Silicon Graphics,  758 Simbad Robotics Simulator Project,  767 símbolo de estado de ação,  83 símbolo de infinidade,  903 símbolos especiais,  553 SimpleGraph, applet,  724 Simple Object Access Protocol (SOAP),  1018, 1020 Simpletron Machine Language (SML),  236, 695 Simpletron, simulador,  237, 239, 695, 766 simulação,  164 moeda, lançamento,  187 Simulação Lebre e a tartaruga, A,  513 Simulação: A lebre e a tartaruga,  234 simulação de software,  236 simulação de supermercado,  718 simulador,  236 simulador de computador,  237 simulador de robótica,  767 Simulador de Simpletron baseado em multimídia, exercício,  766 simular um clique de botão direito do mouse em um mouse de um botão,  454 simular um clique no botão do meio do mouse com um mouse de um ou dois botões,  454 sinais de cifrão ($),  30 sinal de adição (+) indicando visibilidade pública na UML,  391 sinal de subtração (–), flag de formatação,  126 sinal de subtração (-) indicando visibilidade privada na UML,  391 sin, método da classe Math,  157 Since:, nota,  XL @since, tag javadoc,  XL sincronização,  826 sincronizar acesso a uma coleção,  639 sincronizar threads,  805

1140

Índice remissivo

SINGLE_INTERVAL_SELECTION, constante de interface ListSelectionModel,  447 SINGLE_SELECTION, constante de interface ListSelectionModel,  447 SingleSelectOptionsList, classe,  964

Single-Threaded Execution, padrão de design,  LIX, LXVII Singleton, padrão de design,  LIX, LX sintaxe,  30 sintaxe de chamada de construtor de superclasse,  291 síntese de fala,  767 sistema,  365 sistema de composição,  516 sistema de coordenadas,  105, 484, 485 sistema de gerenciamento de banco de dados relacional (RDBMS),  899, 950 sistema de gerenciamento de bancos de dados (DataBase Management System — DBMS),  553, 899 sistema de nome de domínio (domain name system — DNS), servidor,  948 sistema de números octal (base 8),  188, II sistema de pesquisa,  949 Sistema de reservas de passagens aéreas,  231 sistema de software de comando e controle,  804 sistema de telefonia,  877 sistema, estrutura,  365 sistema numérico hexadecimal (base 16),  188, 239, II sistema operacional,  4 sistema, requisitos,  364 sistemas de número,  533 sistemas de pesquisa de código e sites de código,  xxii, xxvii size, método da classe ArrayBlockingQueue,  824 da classe ArrayList,  222 da classe BitSet,  LI da classe PriorityQueue,  656 de interface List,  641, 643 de interface Map,  661 Skype,  18 sleep, método da classe Thread,  809, 819, 820 Smashforce,  XXVIII SML,  695 SMS Language,  550 SMS, serviço Web,  1065 SOAP, formato wire,  1019 SOAP (Simple Object Access Protocol),  xxii, 1018, 1019, 1020, 1025 envelope,  1020 mensagem,  1019 wire, formato,  1019 sobrecarga de método,  174 sobrecarregar métodos genéricos,  677 sobrecarregar um método,  174 social bookmarking,  17, XXVII socket,  859 Socket, classe,  866, 876, 887, 888, LXVIII close, método,  867 getInetAddress, método,  871 getInputStream, método,  866, 867 getOutputStream, método,  866 socket de datagrama,  859, 877 SocketException, classe,  877 SocketImpl, classe,  LXVIII software,  2, 3 Software as a Service (SAAS),  19 software frágil,  293 software, modelo,  237 software profissional (business software),  7

solicitação assíncrona,  1006 solicitação, método de,  949 solicitação síncrona,  1005 solução fatorial iterativa,  598 som,  730, 743 somar elementos de um array,  195 sombrear um campo,  172 som, sistema,  756 sons (www.soundcentral.com),  762 sort,  219 sort, método da classe Arrays,  219, 619 da classe Collections,  645 SortDemo, applet,  724 SortedMap, interface,  658 SortedSet, interface,  657, 658 first, método,  658 last, método,  658 Source, visualização no Netbeans,  XIII SOUTH, constante da classe BorderLayout,  452, 463 SOUTH, constante da classe GridBagConstraints,  793 SOUTHEAST, constante da classe GridBagConstraints,  793 SOUTHWEST, constante da classe GridBagConstraints,  793 span, elemento,  954 .spl, extensão de arquivo,  758 -splash, opção,  XXI -splash, opção de linha de comando para o comando java,  XXI SplashScreen, classe,  XXI split, método da classe String,  535, 540 spooler,  707 spooling,  707 spooling de impressão,  707, 818 SpreadSheet, applet,  725 .sql, extensão de nome de arquivo,  911 SQL,  899, 900, 903, 908 DELETE, instrução,  903, 909 FROM, cláusula,  903 GROUP BY,  903 IDENTITY, palavra-chave,  928 INNER JOIN, cláusula,  903, 907 INSERT, instrução,  903, 908 LIKE, cláusula,  905 ON, cláusula,  907 ORDER BY, cláusula,  903, 905, 906 SELECT, consulta,  903, 904, 905, 906 SET, cláusula,  909 UPDATE, instrução,  903 VALUES, cláusula,  908 WHERE, cláusula,  904 SQLException, classe,  913, 914, 927 SQLFeatureNotSupported-Exception, classe,  919 SQL, instrução,  938, 939 SQL, palavras-chave,  903 SQL, script,  911 SQL (Structured Query Language),  927 sqrt, método da classe Math,  157, 162 Stack, classe,  654, 655, 704 isEmpty, método,  655 peek, método,  655 pop, método,  655 push, método,  655 Stack, classe genérica,  678 Stack< Double >,  683 Stack< Integer >,  683 Stack, declaração de classe genérica,  678 Stack, documentação de classe (java.sun.com/ javase/6/docs/api/java/util/Stack. html),  654 StackTraceElement, classe,  351

getClassName, método,  351 getFileName, método,  351 getLineNumber, método,  351 getMethodName, método,  351 start, método da classe JApplet,  728, 730, 732 start, método da classe Timer,  751 start, método da interface Player,  760 startsWith, método da classe String,  521 stateChanged, método da interface ChangeListener,  772 Statement, interface,  914, 927 close, método,  915 executeQuery, método,  914

state, objeto,  LXVI State, padrão de design,  LIX, LXII, LXVI classe state,  LXII objeto de contexto,  LXII state, objeto,  LXII subclasse state,  LXII static

campo (variável de classe),  258 importação,  261 importação simples,  261 importação sob demanda,  261 membro de classe,  258 método,  60, 74, 126 palavra-chave,  156, 1069 variável de classe,  259 Static Text, componente JSF,  953, 961 step, comando de depurador,  1083 step up comando de depurador,  1083 stop, comando de depurador,  1080 stop, método da classe JApplet,  728 stop, método da classe Timer,  752 stop, método da interface AudioClip,  756 store, método da classe Properties,  663 strategy, objeto,  LXVI Strategy, padrão de design,  LIX, LXII, LXVI strictfp, palavra-chave,  1069 string,  32, 163 literal,  32 String, classe,  516 charAt, método,  518, 528 compareTo, método,  519, 521 concat, método,  524 endsWith, método,  521 equals, método,  519, 520 equalsIgnoreCase, método,  519, 521 format, método,  74, 244, 1104 getChars, método,  518 imutável,  260 indexOf, método,  522 lastIndexOf, método,  522 length, método,  518 matches, método,  536 regionMatches, método,  519 replaceAll, método,  540 replaceFirst, método,  540 split, método,  535, 540 startsWith, método,  521 substring, método,  523 toCharArray, método,  525, 612 toLowerCase,  643 toLowerCase, método,  525 toUpperCase,  643 toUpperCase, método,  525 trim, método,  525 valueOf, método,  525 String, classe, métodos de pesquisa de classe,  523 String, objeto, imutável,  517 StringBuffer, classe,  526 StringBuilder, classe,  516, 526



Índice remissivo append, método,  529 capacity, método,  527 charAt, método,  528

construtores,  527 delete, método,  530 deleteCharAt, método,  530 ensureCapacity, método,  527 getChars, método,  528 insert, método,  530 length, método,  527 reverse, método,  528 setCharAt, método,  528 string, concatenação,  260 string de caractere,  32 string de consulta,  949 string de formato,  36 string delimitadora,  536

StringIndexOutOfBoundsException, classe,  523, 528 StringReader, classe,  578

string vazia,  433, 517

StringWriter, classe,  578, 1030

Strmz,  XXVIII

Stroke, objeto,  505, 506

Stroustrup, Bjarne,  6, 336 Structured Query Language (SQL),  899, 900, 903 suavização (anti-aliasing),  726 subárvore direita,  709, 713, 719 subárvore esquerda,  709, 713, 719 subclasse,  107, 279, 395, LX subclasse concreta,  313 subclasse personalizada de classe JPanel,  455 subdiretório,  725 sublinhado (_) caractere curinga SQL,  904, 905 sublista,  643 subList, método de interface List,  643 submenu,  773 submenu expandido,  778 submissor em um formulário virtual,  1006 submit, método da classe ExecutorService,  851 subprotocolo para comunicação,  913 subscript (índice),  190 subsistema,  LXVIII substantivos compostas no documento de requisitos,  366 substantivos compostos no documento de requisitos,  371 substitutos,  XXX substring, método da classe String,  523 operador, -,  41 subtração, operador de atribuição composta, -=,  102 subtract, método da classe BigInteger,  595 Sun Audio, formato de arquivo (extensão .au),  756, 758 Sun Microsystems,  2, XXIX, LVIII super.paintComponent(g),  107 super, palavra-chave,  281, 297, 1069 chamar construtor de superclasse,  291 superclasse,  107, 279, 395, LXIV construtor,  284 construtor padrão,  284 direta,  279, 280 indireta,  279, 280 método sobrescrito em uma subclasse,  297 sintaxe da chamada de construtor,  291 superclasse abstrata,  309, 398 supercomputador,  3 suspender execução de um applet,  730 .swf, extensão de arquivo,  758 swing.properties, arquivo,  xxxii, 420 SwingConstants, interface,  329, 428, 772 Swing Event Package,  164

Swing GUI, APIs,  419 Swing GUI, pacote de componentes,  164 SwingSet3, demo (download.java.net/javadesktop/ swingset3/SwingSet3.jnlp),  419 SwingUtilities, classe,  784, 871 invokeLater, método,  871 updateComponentTreeUI, método,  784 SwingWorker, classe,  841 cancel, método,  850 doInBackground, método,  841, 842 done, método,  841, 842 execute, método,  841 get, método,  841 isCancelled, método,  847 process, método,  841, 848 publish, método,  841, 847 setProgress, método,  841, 847 switch, instrução de múltipla seleção,  129, 133, 144, 167, 1069 case, rótulo,  132 default, caso,  132, 133, 167 diagrama de atividades com instruções break,  134 expressão controladora,  132 switch, instrução de seleção múltipla,  84 switch, lógica,  134 Sybase,  899 Sybase, Inc.,  XXIX synchronized

instrução,  812 método,  812 palavra-chave,  664, 812, 1069 System, classe arraycopy,  219, 221 currentTimeMillis, método,  613 exit, método,  345, 561 setErr, método,  555 setIn, método,  555 setOut,  555 System.err, (fluxo de erro padrão),  341, 554, 577, 1091 System.in, (fluxo de entrada padrão),  554 System.out print, método,  32, 34 printf, método,  35 println, método,  32, 34 System.out, (fluxo de saída padrão),  554, 577 System.out, (objeto de saída padrão),  32 System.out.print, método,  32, 34 System.out.printf, método,  35 System.out.println, método,  32, 34 SystemColor, classe,  505 SystemTray, classe,  XXIV addTrayIcon, método,  XXIV getDefaultSystemTray, método,  XXIV removeTrayIcon, método,  XXIV

T tabela,  209 tabela de banco de dados relacional,  900 tabela de hash, colisões,  659 tabela de precedência de operadores,  1066 tabela de um banco de dados,  900 tabela de valores,  209 tabela, elemento,  209 tabelas-verdade para operador ^,  138 para operador !,  138 para operador &&,  136 para operador ||,  136

1141

tabela-verdade,  136 tabela-verdade, negação lógica ou operador NÃO lógico (!),  138 Tab, tecla,  31 TableModel, interface,  915 addTableModelListener,  915 getColumnClass, método,  915, 919 getColumnCount, método,  915, 919 getColumnName, método,  915, 919 getRowCount, método,  915 getValueAt, método,  915 removeTableModelListener,  915 TableModelEvent, classe,  923 Table componente ( JSF),  997 internalVirtualForms, propriedade,  998 paginationControls, propriedade,  998 Table componente JSF,  995 TableRowSorter, classe,  923 tabulação,  1103 tabulação, caractere, \t,  35 tabulação horizontal,  35 tabulação, paradas,  31, 35 tabulação (para recuo em código),  30 tabuleiro de damas, padrão,  53 exercício,  741 tag de abertura,  735 tag de fechamento,  735 tag (em um documento de XHTML),  729 tags personalizados,  951 tailSet, método da classe TreeSet,  658 take, método da classe BlockingQueue,  823, 824 tamanho da área de exibição do applet,  729 tamanho de fonte,  492 tamanho de uma variável,  40 tan, método da classe Math,  157 tangente,  157 tangente trigonométrica,  157 tarefa,  4 taxa de juros,  125 Tax Plan Alternatives,  153 TCP (Transmission Control Protocol),  859 Technorati,  18 Technorati APIs,  XXVIII tecla constante,  460 tecla de ação,  458 tecla de atalho,  773 tecla de função,  458 teclado,  3, 4, 419 teclado, evento,  435, 458 tecla modificadora,  460 tela,  3, 4 tela de splash,  XXI Template Method, padrão de design,  LIX, LXII, LXVI tempo de execução constante,  618 tempo de execução linear,  618 tempo de execução logarítmico,  622 tempo de execução quadrático,  618 Tempo para calcular números de Fibonacci, exercício,  613 terminação, preparação,  258, 298 terminação, teste de,  597 terminado, estado,  806 terminal,  4 terminar com sucesso,  561 terminar um aplicativo,  777 terminar um loop,  94 término anormal de um programa,  199 término, fase,  94 Testador de tempo de reação/precisão de reação, exercício,  766 Testando a classe de classificação por inserção,  627

1142

Índice remissivo

Testando a classe de classificação por intercalação,  631 Testando a classe de classificação por seleção,  625 Testando um serviço Web de outro computador,  1024 testar um serviço Web,  1023 Text Area, componente JSF,  978 texto de leitura,  425 texto de template fixa,  951 Text Field componente autocompletar,  1006 autoComplete, atributo,  1010 autoCompleteExpression, atributo,  1010 Text Field, componente JSF,  961, 964 texto fixo,  39, 1091 em uma string de formato,  1091 texto ou ícones não editáveis,  423 texto para voz (text-to-speech — TTS),  767 texto que pula,  724 texto selecionado em um JTextArea,  468 Text Package,  163 textura de preenchimento,  506 TexturePaint, classe,  484, 505, 506 The FairTax,  153 The Free Site (www.thefreesite.com),  762 The Java™ Language Specification (java.sun.com/ docs/books/jls/),  41 this

palavra-chave,  246, 259, 1069 para chamar um outro construtor da mesma classe,  250 referência,  246 Thompson, Ken,  6 thread,  341, 486, 730, LIX agendamento,  806, 820 agendamento de prioridade,  807 ciclo de vida,  805 de execução,  804 estado,  805 sincronização,  663, 812 Thread, classe,  807, 809 currentThread, método,  813 interrupt, método,  809 sleep, método,  809 thread adormecida,  806 Thread.MAX_PRIORITY,  807 Thread.MIN_PRIORITY,  807 Thread.NORM_PRIORITY,  807 thread consumidor,  818 thread de despacho de evento (event-dispatch thread — EDT),  840 thread de despacho do evento (event-dispatch thread — EDT),  486, 871 thread de espera,  827 thread, estados bloqueado,  806, 812 espera,  806 espera sincronizada,  806 executando,  806 executável,  805, 806 morto,  806 novo,  805 pronto,  806 terminado,  806 thread principal,  810 thread produtor,  818 threads concorrentes,  823 threads de execução,  804 ThreeDimensionalShape, classe,  303 throughput,  4 Throwable, classe,  343, 349

documentação (java.sun.com/javase/6/docs/api/ java/lang/Throwable.html),  343 getMessage, método,  349 getStackTrace, método,  349 hierarquia,  343 printStackTrace, método,  349 throw, instrução,  347 throw, palavra-chave,  348, 1069 throws, cláusula,  342 throws, palavra-chave,  342, 1069 @throws, tag javadoc,  XXXVIII Tic-Tac-Toe 3-D multithreaded,  897 TicTacToe,  277 applet,  725 exercício,  277 Tic-Tac-Toe (jogo da velha),  882 Tic-Tac-Toe multiencadeado, programa,  897 Timer, classe,  512, 751, 752 getDelay, método,  765 isRunning, método,  751 restart, método,  751 setDelay, método,  765 start, método,  751 stop, método,  752 tipo,  38 tipo bruto,  684 tipo de conjunto de resultados,  918 tipo de dados abstrato (ADT),  242 tipo de retorno,  65 de um método,  59, 65 na UML,  377, 381 tipo de uma variável,  40 tipo parametrizado,  678 tipo, parâmetro,  674, 678, 683 escopo,  679 seção,  674, 678 tipo por referência,  67, 268 tipo primitivo,  38, 67, 105, 162 boolean,  1082 byte,  129 char,  38, 129 double,  38, 70, 95 float,  38, 70 int,  38, 95, 102, 129 nomes são palavras-chave,  38 passado por valor,  205 promoções,  162 short,  129 tipo, variável,  674 title, elemento de um documento JNLP,  735 titles, tabela do banco de dados books,  901, 902 toArray, método de interface List,  644 toArray, método de List,  645 toBinaryString, método da classe Integer,  XLV toCharArray, método da classe String,  525 toCharArray, método de String,  612 toJson, método da classe Gson,  1033 token de uma String,  535 tokenização,  535 tolerante a falhas,  39, 336 toLowerCase, método da classe Character,  533 toLowerCase, método da classe String,  525, 643 tom,  491 TOP, constante da classe JTabbedPane,  792 _top, frame-alvo,  863 topo,  93 Torres de Hanói,  598 Torres de Hanói para o caso com quatro discos,  599 toString, método da classe ArrayList,  645, 688 da classe Arrays,  541, 616 da classe BitSet,  LI

da classe Formatter,  1104 da classe Object,  284, 299 total,  90, 94 toUpperCase, método da classe Character,  533 toUpperCase, método da classe String,  525, 643 toURI, método da classe File,  761 toURL, método da classe URI,  761 trabalho,  4 tradução,  5 TRAILING, constante de alinhamento em GroupLayout,  XIII transação,  939 Transaction, classe (estudo de caso ATM),  395, 396, 397, 398, 416 transferência de controle,  83, 238, 239 Transição aleatória de imagens, exercício,  765 transição entre estados na UML,  374, 375 transição (na UML),  83 transient, palavra-chave,  572, 1069 translate, método da classe Graphics2D,  508 transmissão baseada em fluxo orientada para conexão,  877 transmissão sem conexão,  877 transparência de JComponent,  455 tratamento de evento,  428, 431, 432 fonte de evento,  434 tratamento de evento de mouse,  449 tratamento de eventos de teclado,  458 tratamento de exceções,  336 tratar uma exceção,  339 TrayIcon, classe,  XXIV TrayIcon, pacote de demonstração,  XXIV TreeMap, classe,  658 TreeSet, classe,  656, 657, 658 headSet, método,  658 tailSet, método,  658 Triângulos aleatórios, exercício,  512 triângulos gerados aleatoriamente,  512 trim, método da classe String,  525 trimToSize, método da classe ArrayList,  221 Triplos de Pitágoras,  152 trocando valores,  622, 624 true,  43, 1069 true, palavra reservada,  85, 86 truncado,  560 truncar,  41 truncar parte fracionária de um cálculo,  93 try, bloco,  340, 351 termina,  341 try, bloco, envolvente,  349 try, instrução,  342 try, palavra-chave,  340, 1069 TwoDimensionalShape, classe,  303 Two-Phase Termination, padrão de design,  LIX, LXVII TYPE_FORWARD_ONLY, constante,  918 TYPE_INT_RGB, constante da classe BufferedImage,  506 TYPE_SCROLL_INSENSITIVE, constante,  918 TYPE_SCROLL_SENSITIVE, constante,  918 TypePad ATOM,  XXVII Types, classe,  914

U U+aaaa (convenção notacional Unicode),  XXX UDP (User Datagram Protocol),  859, 877 UIManager, classe,  784 getInstalledLookAndFeels, método,  784 LookAndFeelInfo, classe aninhada,  784 setLookAndFeel, método,  784

UIManager.LookAndFeelInfo, classe getClassName, método,  784 UIViewRoot, classe,  953

último a entrar, primeiro a sair (last in, first out — LIFO),  162 último a entrar, primeiro a sair (last in, first out — LIFO),,  681 uma instrução por linha,  45 UML, diagrama de atividades círculo sólido incluído em um círculo aberto (para representar o fim de uma atividade) na UML,  375 círculo sólido (para representar um estado inicial) na UML,  375 pequeno símbolo de losango (para representar uma decisão) na UML,  375 UML, diagrama de casos de uso na ator,  364 caso de uso,  364 UML, diagrama de classes,  368 compartimento de atributos,  372 compartimento de operações,  377 UML, diagrama de estado círculo sólido (para representar um estado inicial) na UML,  374 retângulo arredondado (para representar um estado) na UML,  374 UML, diagrama de sequência ativação,  385 ponta de seta,  385 UML, parceiros,  17 UML (Unified Modeling Language),  15, 361, 365, 368, 372, 374, 395 agregação,  369 aspas francesas (« e »),  69 associação,  368 atributo,  15 Centro de Recursos (www.deitel.com/UML/),  366 círculo sólido,  84 círculo sólido cercado por um círculo vazio,  84 compartimento em um diagrama de classes,  60 condição de guarda,  85 de um para um, relacionamento,  370 diagrama,  365 diagrama de atividades,  83, 86, 89, 124, 128 diagrama de classes,  60 diagrama elidido,  368 estado final,  84 linha pontilhada,  84 losango,  85 losango sólido representando composição,  369 losango vazio representando agregação,  369 multiplicidade,  368 nome de papel,  369 nota,  84 quadro,  385 relacionamento de muitos para um,  370 seta,  83 símbolo de agregação,  89 Specification (www.omg.org/technology/ documents/formal/uml.htm),  369 um para muitos, relacionamento,  370 UML (www.uml.org),  84 um para muitos, relacionamento,  903 um para um, mapeamento,  658 união de dois conjuntos,  276 união teórica,  276 Unicode, conjunto de caracteres,  54, 105, 134, 516, 520, 533, 553, 1070 Unicode Consortium,  XXIX Unicode, padrão,  XXIX único ponto de entrada,  140

Índice remissivo único ponto de saída,  140 unidade central de processamento (central processing unit — CPU),  4 unidade de aritmética e lógica (ALU),  3 unidade de armazenamento secundária,  4 unidade de disco,  724 unidade de entrada,  3 unidade de memória,  3 unidade de processamento,  4 unidade de resolução de tela,  105 unidade de saída,  3 unidade lógica,  4 unidades independentes,  4 Unified Modeling Language (UML),  xxii, 15, 16, 361, 365, 368, 372, 374, 395 uniforme, princípio do projeto Unicode,  XXIX Uniform Resource Identifier (URI),  555, 860, 948 Uniform Resource Locator (URL),  555, 860, 948 universal, princípio do projeto Unicode,  XXIX UNIX,  32, 131, 562, 723, 781 UnknownHostException, classe,  867 unlock, método da interface Lock,  835, 838 unmarshal, método da classe JAXB,  1031 UnsupportedOperationException, classe,  644 unwatch, comando de depurador,  1086 Upcoming.org,  XXVII UPDATE, instrução SQL,  903, 908 updateComponentTreeUI, método da classe SwingUtilities,  784 URI, classe toURL, método,  761 URL,  LXVIII URL, classe,  755 openStream, método,  1034 URLStreamHandler, classe,  LXVIII reescrevendo,  971 User Datagram Protocol (UDP),  859, 877 uso de letras maiúsculas e minúsculas no estilo de frases,  422 uso de letras maiúsculas e minúsculas no estilo título de livro,  422, 435 UTF-8,  XXX UTF-16,  XXX UTF-32,  XXX Utilities Package,  163 Utiliza pesquisa binária para localizar um item em um array.,  622

V validação,  964 validade, verificação,  253 validate, método da classe Container,  467 ValidatorException, classe,  970 Validators Double Range,  964 Length,  964, 968 Long Range,  964 valor absoluto,  157 valor-chave,  714 valor consistente,  243 valor correto,  243 valor de código,  XXX valor de deslocamento,  165 valor de deslocamento (números aleatórios),  167 valor de flag,  93 valor de semente (números aleatórios),  167 valor de sentinela,  93, 94, 97 valor de sinal,  93 valor de uma variável,  40 valor de um parâmetro,  862

1143

valores duplicados,  714 valor fictício,  93 valor final,  121 valor inicial da variável de controle,  120 valor inicial de um atributo,  372 valor inicial padrão,  65 valor obsoleto,  815 valor padrão,  65, 105 valor para o inteiro mais próximo,  185 Valor unicode do caractere digitado,  460 valueChanged, método da interface ListSelectionListener,  447 valueOf, método da classe String,  525 VALUES, cláusula de SQL,  908 values, método de um enum,  257 v, opção do comando jar,  734 variação na UML,  375 variável,  36, 38 nome,  38, 40 tamanho,  40 tipo,  40 tipo por referência,  67 valor,  40 variável constante,  134, 195, 262 deve ser inicializada,  195 variável de ambiente CLASSPATH,  34 PATH,  33 variável de classe,  158, 258 variável de controle,  90, 120, 121, 122 variável de instância,  58, 64, 70, 158 variável, escopo,  123 variável local,  63, 91, 172, 173, 247 variável não é modificável,  262 varredura,  224 varredura de um arco,  498 varrer,  498 varrer no sentido anti-horário,  498 vazamento de recurso,  345 Vector, classe,  639 velocidade de piscamento,  765 Vendas totais,  231 vendor, elemento de um documento JNLP,  735 verificador de bytecode,  10 Verificador de Ortografia, projeto,  548 Verificando com assert que um valor está dentro do intervalo,  354 Version nota,  XL @version, tag javadoc,  XL VERTICAL, constante da classe GridBagConstraints,  793 VERTICAL_SCROLLBAR_ALWAYS, constante da classe JScrollPane,  470 VERTICAL_SCROLLBAR_AS_NEEDED, constante da classe JScrollPane,  471 VERTICAL_SCROLLBAR_NEVER, constante da classe JScrollPane,  471 vi,  8 vídeo,  743, 761 videogame,  165 View,  419 vinculação dinâmica,  319 vinculação estática,  321 vinculação tardia,  319 vinculando de atributo,  960, 971 vinculando um JSF Table a uma tabela de banco de dados,  997 vincular a outro nó (link),  696 vírgula (,),  125 vírgula (,) flag de formatação,  127 visibilidade na UML,  391 Visual Basic, linguagem de programação,  8

1144

Índice remissivo

Visual C#, linguagem de programação,  8 Visual C++, linguagem de programação,  8 visualização em uma coleção,  643 visualização (no MVC),  LXIX visualização tridimensional,  724 visualizando recursão, exercício,  611 visualizar uma forma de ângulos diferentes,  724 Vlissides, John,  LVIII void, palavra-chave,  31, 59, 1069 volatile, palavra-chave,  1069 volume de uma esfera,  182, 184 volume, operação de,  639

W WADL (Web Application Description Language),  1030 wait, método da classe Object,  299, 825 watch, comando de depurador,  1085 .wav, extensão de arquivo,  756 Web 2.0,  17, XXVI Web 3.0,  18 Web Application Description Language (WADL),  1030 @WebMethod, anotação,  1022 operationName, atributo,  1022 Web Application, projeto,  1020 @WebParam, anotação,  1022 name, atributo,  1022 Web Semântica,  18 @WebService, anotação,  1021 name, atributo,  1022 serviceName, atributo,  1022 WebServiceContext, interface,  1037 Web Service Description Language (WSDL),  1024 WebTime, modificação,  992 exercício,  992 webuijsf:body, elemento JSF,  953 webuijsf:form, elemento JSF,  953 webuijsf:head, elemento JSF,  953 webuijsf:html, elemento JSF,  953 webuijsf:label, elemento JSF,  965 webuijsf:link, elemento JSF,  953 webuijsf:message, elemento JSF,  965 webuijsf:meta, elemento JSF,  953 webuijsf:page, elemento JSF,  953 webuijsf:staticText, elemento JSF,  953, 1001 webuijsf:table, elemento JSF,  1001 webuijsf:tableColumn, elemento JSF,  1001 webuijsf:tableRowGroup, elemento JSF,  1001 webuijsf:textField, elemento JSF,  965 weightx, campo da classe GridBagConstraints,  793 weighty, campo da classe GridBagConstraints,  793 WEST, constante da classe BorderLayout,  452, 463 WEST, constante da classe GridBagConstraints,  793 WHERE, cláusula de SQL,  903, 904, 905, 906, 909

while, instrução de repetição,  84, 88, 89, 92, 97,

120, 144, 1069 diagrama de atividades (na UML),  89 widgets,  419 width, atributo do elemento applet-desc,  736 width de um applet em pixels,  729 Wikipedia,  18 Window, classe,  772 addWindowListener, método,  773 dispose, método,  772 pack, método,  787 windowActivated, método da interface WindowListener,  773 WindowAdapter, classe,  453, 924 windowClosed, método da interface WindowListener,  773, 924 windowClosing, método da interface WindowListener,  773 WindowConstants, interface,  772 DISPOSE_ON_CLOSE, constante,  772 DO_NOTHING_ON_CLOSE, constante,  772 HIDE_ON_CLOSE, constante,  772 windowDeactivated, método da interface WindowListener,  773 windowDeiconified, método da interface WindowListener,  773 window gadgets,  419 windowIconified, método da interface WindowListener,  773 WindowListener, interface,  452, 773, 924 windowActivated, método,  773 windowClosed, método,  773, 924 windowClosing, método,  773 windowDeactivated, método,  773 windowDeiconified, método,  773 windowIconified, método,  773 windowOpened, método,  773 windowOpened, método da interface WindowListener,  773 Windows,  5, 8, 131, 562, 723 Windows, aparência e funcionamento,  769 Windows Performance Package,  758 Windows Vista,  1024 Windows Wave, formato de arquivo (extensão .wav),  756 Windows XP acesso HTTP a partir de outros computadores da rede,  1024 permitir solicitações Web,  1024 WireFrame, applet,  725 Withdrawal, classe (estudo de caso ATM),  367, 368, 370, 371, 375, 377, 383, 385, 386, 393, 394, 395, 396, 398 “World Wide Wait”,  1005 World Wide Web (WWW),  5, 862 navegador,  74 writeBoolean, método da interface DataOutput,  577

writeByte, método da interface DataOutput,  577 writeBytes, método da interface DataOutput,  577 writeChar, método da interface DataOutput,  577 writeDouble, método de interface DataOutput,  577 writeFloat, método de interface DataOutput,  577 writeInt, método da interface DataOutput,  577 writeLong, método da interface DataOutput,  577 Writer, classe,  578 writeShort, método da interface DataOutput,  577 writeUTF, método da interface DataOutput,  577

WSDL (Web Service Description Language),  1024 WYSIWYG (What You See Is What You Get), editor,  959

X X_AXIS, constante da classe Box,  792

Xadrez, jogo,  896 X, eixo,  105 XHTML (Extensible HyperText Markup Language),  947 applet, elemento,  729 body, elemento,  729 span, elemento,  954 tag,  729 tutoriais (www.deitel.com/xhtml/),  723, 860 XHTML (Extensible HyperText Markup Language), documento,  723, 729, 862 x, coordenada,  105, 484, 502, 728 XML (Extensible Markup Language),  735, 950, 1024 declaração,  953 elemento,  735 elemento-raiz,  735 tag de abertura,  735 tag de fechamento,  735 vocabulário,  735 XMLHttpRequest, objeto,  1005 xmlns, atributos,  953 xor, método da classe BitSet,  LI

Y Y_AXIS, constante da classe Box,  792

Yahoo! Maps,  XXVIII Yahoo! Search,  XXVIII Y, eixo,  105 y, coordenada,  105, 484, 502 YouTube,  17, XXVII

Z zero, contagem baseada em,  122 zero-ésimo elemento,  190 ZERO, constante da classe BigInteger,  595

LEIA COM ATENÇÃO Ao abrir o pacote do SOFTWARE, você concorda em vincular-se aos termos do contrato abaixo expresso. Portanto, não tome nenhuma ação antes da completa leitura e compreensão dos termos abaixo propostos.

Termos de licença de uso do Software Pelo presente instrumento particular, de um lado a PEARSON EDUCATION DO BRASIL LTDA., doravante denominada CONTRATADA, e de outro lado o comprador/consumidor, doravante denominado CONTRATANTE, independentemente da assinatura escrita, mas condicionada à instalação do SOFTWARE, que valerá por aquela, anuem ao que segue: 1.

Do objeto

1.1. Este termo tem por objeto o presente SOFTWARE, que inclui o programa de computador e poderá incluir meios físicos, materiais impressos e documentação on-line ou eletrônica, sem, no entanto, incluir seu conteúdo literário, que é de propriedade da CONTRATADA. 2.

Dos direitos de uso

2.1. Ao instalar o SOFTWARE o CONTRATANTE está concordado em vincular-se aos termos e condições aqui expressos. Caso não esteja de acordo, o contratante deve interromper imediatamente a sequência de instalação, sendo-lhe facultada a devolução do produto à sede da CONTRATADA para obter reembolso do valor pago. 2.2. O CONTRATANTE possui somente a mídia magnética ou física (a mídia incluída) em que o SOFTWARE foi registrado ou corrigido, mas a CONTRATADA e os desenvolvedores de software retêm todos os direitos, títulos e posse do SOFTWARE registrado na(s) cópia(s) original(is) da mídia(s) e todas as cópias subsequentes do SOFTWARE, independentemente da forma ou mídia em que o original ou outras cópias possam existir. Esta licença não é uma venda do SOFTWARE original ou de quaisquer cópias do conteúdo do presente software, de propriedade da CONTRATADA. O conteúdo do cd está protegido pela Lei de Direitos Autorais, sendo proibida a reprodução total ou parcial, em qualquer forma ou por qualquer meio, estando o CONTRATANTE sujeito, no caso de infração, às penalidades previstas em lei. Assim, o contratante deverá tratar o SOFTWARE como qualquer outro material protegido por direito autoral.

2.3.2. Fazer uma única cópia do SOFTWARE somente para propósitos de backup ou de arquivamento. 2.3.3. Transferir o SOFTWARE fisicamente de um computador para outro desde que o SOFTWARE seja utilizado somente em um computador por vez. 2.4. Não é permitido ao CONTRATANTE: 2.4.1. Copiar a Documentação ou o SOFTWARE. O CONTRATANTE pode ser legalmente responsável por cópia ou infração de direitos autorais causadas ou encorajadas por sua falha em obedecer aos termos dessa restrição. 2.4.2. Compartilhar o SOFTWARE em uma rede ou por outros meios ou utilizá-lo em mais de um computador ou terminal de computador ao mesmo tempo. Não pode distribuir cópias do SOFTWARE ou da Documentação a outros. 2.4.3. Fazer engenharia reversa, desassemblar, descompilar, modificar, adaptar, traduzir ou criar trabalhos derivados baseados no SOFTWARE ou na Documentação sem a permissão prévia por escrito da EMPRESA. 2.4.4. Transferir o SOFTWARE para outra pessoa sem a permissão prévia por escrito da EMPRESA. 2.4.5. Alugar ou arrendar o SOFTWARE, nem mesmo revendê-lo por qualquer valor. 2.4.6. O SOFTWARE é licenciado como um produto único. Seus componentes não poderão ser separados para utilização em mais de um computador. 3.

Disposições gerais

3.1. A CONTRATADA poderá rescindir o presente termo caso o contratante não cumpra com seus termos e com suas condições, sem prejuízo das penalidades da lei. 3.2. O contratante, não aceitando as condições ora ofertadas e objetivando o pacto contratual, poderá não dar sequência à instalação do produto, interrompendo-a imediatamente, sendo-lhe facultado o pleito de devolução do valor pago mediante sua solicitação expressa, condicionada à devolução do produto na sede da contratada.

Requisitos mínimos de sistema • Processador Pentium de 800 MHz (mínimo) III ou mais rápido. • Windows Vista ou Windows XP (com Service Pack 2). • 512 MB de RAM no mínimo; recomenda-se 1 GB.

2.3. É permitido ao CONTRATANTE:

• 1,5 GB de espaço de disco rígido no mínimo.

2.3.1. Utilizar e exibir não exclusivamente a cópia do programa de software incluído em um único programa de computador (isto é, em uma única CPU) e em um único local, desde que o CONTRATANTE se submeta aos termos deste Acordo.

• Unidade de CD-ROM. • Internet connection. • Navegador da Web, Adobe® Reader® e um utilitário de descompactação de arquivos zip.

VIRA

VIRA

VIRA

VIRA

Você tem em mãos a mais consagrada obra de introdução à programação orientada a objetos que se baseia no DEITEL® live-code para ensinar tudo o que você precisa saber sobre Java™ Standard Edition 6, JDBC™ 4, desenvolvimento de aplicativos Web e Web Services! O Java™ é atualmente a linguagem de programação mais popular do mundo. Esta nova edição, completamente atualizada, utiliza uma abordagem agradável e cuidadosamente compassada para que o estudante desenvolva aplicativos baseados em Web e desktop. Os novos exercícios “Fazendo a diferença” envolvem questões relacionadas à consciência social que são importantes tanto para estudantes como para profissionais. “Um ótimo livro-texto com uma enorme variedade de exemplos de vários domínios de aplicações — excelente para um curso de ciência da computação.” — William E. Duncan, Louisiana State University “Esta nova edição atualizada reflete o estado da arte em tecnologias Java. Como sempre, suas explicações profundas e claras tornam o livro indispensável.” — José Antonio González Seco, Parlamento da Andaluzia “Java — como programar introduz noções de boas práticas de projetos e metodologias desde o começo. É um excelente ponto de partida para o desenvolvimento de aplicações Java robustas e de alta qualidade.” — Simon Ritter, Sun Microsystems “Tem um estilo fácil de ler, como se fosse um bate-papo. Traz exemplos de códigos claros que permitem ao leitor tornar-se proficiente em Java.” — Patty Kraft, San Diego State University “O melhor livro-texto introdutório que já vi. Gostaria que este livro tivesse sido publicado quando eu estava aprendendo a programar!” — Lance Andersen, Sun Microsystems “As técnicas de projetos orientados a objetos estão presentes em todo o livro. Os exemplos são de fácil compreensão e trazem ótimas oportunidades de aprendizado! A explicação sobre herança é muito boa — o conceito é construído com base no exemplo, podendo ser facilmente compreendido. Fiquei muito contente de ver o desenho de elementos gráficos opcionais no início do livro. Os exercícios são desafiadores e divertirão os estudantes.” — Sue McFarland Metzger, Villanova University “Traz aos novos programadores o conhecimento proveniente de muitos anos de experiência no desenvolvimento de softwares.” — Edward F. Gehringer, North Carolina State University “Parabenizo os autores pela dedicação a pesquisas detalhadas e pelo desenvolvimento de exemplos tão ilustrativos. A visão geral passo a passo de projetos orientados a objetos é excelente.” — Ric Heishman, George Mason University “Acho os novos exercícios ‘Fazendo a diferença’ muito interessantes. Abordando temas contemporâneos, eles encorajam o estudante a pesquisar dados na Internet e aplicá-los no exercício em questão.” — Vince O’Brien, Pearson Education “Este livro oferece uma base sólida para programação em Java. A maioria dos principais conceitos é ilustrada por programas completos. A abordagem é compreensível e detalhada. Há uma grande variedade de exercícios para testar sua compreensão do assunto.” — Shyamal Mitra, University of Texas at Austin Esta oitava edição de Java — como programar apresenta uma abordagem abrangente sobre a programação orientada a objetos e traz vários estudos de caso que tratam de: classes (GradeBook, Time, Employee), sistema OOD/UML™ ATM opcional, GUI e desenho de elementos gráficos opcionais, aplicativo Web baseado em banco de dados operando em múltiplas camadas (livro de endereços) e serviços Web (Blackjack, sistema de reservas de passagens aéreas, gerador de equações etc.) Paul J. Deitel e o dr. Harvey M. Deitel são os fundadores da Deitel & Associates, Inc., uma organização internacionalmente reconhecida de treinamento corporativo, criação e desenvolvimento de negócios na Internet, especializada em Java™, C, C++, C#, Visual Basic®, Visual C++®, Python®, XML, Internet, Web e tecnologias de objeto. Os Deitel são autores de muitos livros-texto, livros profissionais e cursos em vídeo líderes de venda internacionalmente. Visite os sites do livro (www.pearson.com.br/deitel e www.deitel.com/books/jhtp8). Contate os autores pelo e-mail [email protected]. Para receber o newsletter Deitel® Buzz Online e para informações sobre a série de treinamentos corporativos Deitel® Dive Into® Series, oferecida em todo o mundo, acesse o site www.deitel.com/newsletter/subscribe.html.

www.pearson.com.br/deitel No site, professores têm acesso ao manual de soluções (em inglês) e a apresentações em PowerPoint, enquanto estudantes contam com exercícios de múltipla escolha e com os códigos (em inglês) dos exemplos apresentados no livro. ISBN 978-85-7605-563-1

w w w. p e a r s o n . c o m . b r

9788576055631_Deitel_Dez2012.indd 1

27/12/2012 11:18:03