Você está na página 1de 495

Cobrir

Y
FL
MA
TE

Team-Fly ®
Página i

Desenvolvendo aplicativos Java ™ de mainframe


Página ii

Esta página foi intencionalmente deixada em branco.


Página iii

Desenvolvendo aplicativos Java ™ de mainframe

LouMarco
Página iv

Editor: Robert Ipsen


Editor: Margaret Eldridge
Editor assistente: Adaobi Obi Editor
administrativo: John Atkins
Design e composição de texto: MacAllister Publishing Services, LLC

As designações usadas pelas empresas para distinguir seus produtos são freqüentemente consideradas marcas registradas. Em todos os casos em
que John Wiley & Sons, Inc. está ciente de uma reclamação, os nomes dos produtos aparecem em maiúsculas inicial ou TODAS AS LETRAS
MAIÚSCULAS. Os leitores, entretanto, devem entrar em contato com as empresas apropriadas para obter informações mais completas sobre marcas
comerciais e registros.

Copyright © 2001 por Lou Marco. Todos os direitos reservados. Publicado

por John Wiley & Sons, Inc.

Nenhuma parte desta publicação pode ser reproduzida, armazenada em um sistema de recuperação ou transmitida em qualquer forma
ou por qualquer meio, eletrônico, mecânico, fotocópia, gravação, digitalização ou outro, exceto conforme permitido nas Seções 107 ou
108 do Copyright de 1976 dos Estados Unidos Agir, sem a permissão prévia por escrito do Editor, ou autorização por meio do
pagamento da taxa por cópia apropriada para o Copyright Clearance Center, 222 Rosewood Drive, Danvers, MA 01923, (978) 750-

8400, fax (978) 750-4744. Os pedidos de permissão ao Editor devem ser dirigidos ao Departamento de Permissões, John
Wiley & Sons, Inc., 605 Third Avenue, New York, NY 10158-0012, (212) 850-6011, fax (212) 850-6008, E -Enviar: PERMREQ@WILEY.CO
.

Esta publicação foi projetada para fornecer informações precisas e confiáveis em relação ao assunto abordado. É vendido com o
entendimento de que o editor não está envolvido em serviços profissionais. Se for necessário aconselhamento profissional ou outra
assistência especializada, os serviços de um profissional competente devem ser procurados.

Este título também está disponível na versão impressa como ISBN 0-471-41528-6

Para obter mais informações sobre os produtos Wiley, visite nosso site em www.Wiley.com
Página v

Conteúdo

Prefácio xiii

Agradecimentos xvii

Parte 1 Fundamentos de Java 1

Capítulo 1 Introdução 3

Java Trek 3

Um Novo Mundo de Reutilização de 3

Objetos 4

Herança 4

Encapsulamento 4

Stack Class 5

Polimorfismo 6

Java e C ++ 6

C ++ 7

Java 8

Uma versão PL / I da classificação por bolha no 17

resumo 20

Capítulo 2 O que é Java? 23

Uma breve história de Java O 23

mundo de Java hoje 25

Java: a linguagem de programação 25

Java: a linguagem de programação orientada a objetos Java: a 25

linguagem de programação portátil 26


Página vi

Java: a linguagem de programação "Pointer-Less" Java: a 27

linguagem MultiThreaded 28

Esforços de Java da IBM 29

Java versus COBOL e PL / I: Uma breve análise do 29

resumo 34

Capítulo 3 Criando seu primeiro programa Java 35

Instalando o JDK 35

O JDK está instalado corretamente? 36

Compilando e executando o programa 37

Compilando e executando programas Java: uma segunda análise Cuidado 38

com os nomes de classe 42

Qual é a aparência de um erro de compilação? Vamos dar 43

uma olhada no programa HelloWorld de volta ao nosso 43

programa 46

Em suma 47

Capítulo 4 Ferramentas JDK básicas do Sun Java 2 49

Ferramentas JDK básicas 49

Appletviewer 50

jarra 51

extcheck 53

Java 54

Javac 58

Javadoc 62

javah 66

Javap 66

jdb
68

Em suma 76

Capítulo 5 Declarando e Definindo Dados 77

Tipos primitivos Java 77

Algumas palavras sobre fundição de tipo 78

variável de digitação 80

Java Variable Typing 81

O tipo primitivo booleano O tipo 82

primitivo char Tipo primitivo 83

inteiro 84

Tipos primitivos de ponto flutuante Strings 85

de caracteres em Java Java Reference 86

Data Types 86
Página vii

Convenções de nomenclatura Java 88

Em suma 89

Capítulo 6 Sintaxe da linguagem Java 91

Itens diversos de sintaxe Java O código-fonte Java 92

diferencia maiúsculas de minúsculas 92

Instruções Java terminadas por um ponto-e-vírgula Java 92

oferece suporte a vários estilos de comentário Java não tem 92

COPY ou instrução include Java não tem ponteiros 93

93

Java não tem palavras reservadas de 93

pré-processador existentes em Java 93

Java é uma linguagem de programação de formato livre. 95

Declarações de atribuição de Java 96

Operadores Java 97

Operadores Java não encontrados nas linguagens de programação de mainframe Java 100

Arithmetic Anomalies 106

Mistura de tipos primitivos em expressões aritméticas Perda de precisão 107

ao dividir números inteiros Transbordamento de resultados aritméticos de 108

ponto flutuante inadequado e contínuo 109

110

Declarações de controle de programa Java 111

Construções de loop 111

Interrompendo o processamento normal de Loops Java 117

Decision Constructs 119

Em suma 123
Capítulo 7 Representação de Classe e Objeto 125

Anatomia de modificadores de 125

visibilidade de método Java 125

Outros Modificadores 127

Tipos retornados codificados em cabeçalhos de método Nomes 128

de métodos e listas de argumentos 128

O lança a Opção de Nome de Exceção em Cabeçalhos de Método AWord ou 128

Dois sobre Pacotes Java 129

A declaração do pacote 131

Anatomia de uma classe Java 132

Métodos de construtor e variáveis de instância no 132

resumo 147
Página viii

Capítulo 8 Encapsulando e ocultando dados e métodos 149

Encapsulamento 149

Por que encapsular e ocultar seus dados e métodos? Como você 150

encapsula suas classes em Java? 153

Um exemplo de PL / I: Esta programação é orientada a objetos? Usando 158

métodos de acesso (Get e Set) 161

Em suma 164

Capítulo 9 Herança 165

Herança definida 165

Mesmas classes, diferentes árvores de herança 166

Herança única versus múltipla 168

Exemplo de uma árvore de herança das bibliotecas Java Um exemplo: 169


Y
FL

implementação de contas bancárias 170


M

Código para classes de conta corrente e poupança 171


A
TE

aproveitando a herança 174

Efeitos do lançamento de e para superclasses e subclasses no resumo 181

183

Capítulo 10 Interfaces 185

O que são interfaces? 185

Por que as interfaces são úteis? 186

E as superclasses abstratas? Criação de 187

interfaces 188

Exemplo: Implementando os Tipos de Veículos em 188

Resumo 193

Capítulo 11 Noções básicas de manipulação de eventos Java 195

Processamento de Eventos

Team-Fly ®
196

Componentes de interface gráfica Java Eventos 197

Java 199

O modelo de processamento de eventos Java 200

Variações em um Tema 1: Usando Classes de Adaptador Variações em 205

um Tema 2: Usando Classes de Nível Superior Variações em um Tema 3: 206

Usando Classes Internas no Resumo 207

208
Página ix

Capítulo 12 Tratamento de exceções e princípios básicos de thread 209

O que são exceções? 209

A hierarquia de exceção Java A 210

classe de exceção Java 211

Mecanismos de tratamento de exceções da linguagem de programação de 212


mainframe

O mecanismo de tratamento de exceções Java 214

lançando exceções 214

Lançamento de exceções - Continuação Declarando 215

exceções potencialmente lançadas Capturando exceções 216

com try / catch / finally Resumo de exceções Java 218

220

Tópicos básicos do tópico 220

Por que codificar programas multithread? O que 220

são threads Java? 221

Executando seus threads 222

Amostra de Programa MultiThreaded 222

Em suma 227

Capítulo 13 O Sistema de Agendador de Aulas do Departamento de Treinamento 229

O aplicativo definido 229

Opções do aplicativo para os alunos Opções do 230

aplicativo para os instrutores A interface do usuário 230

230

A interface do usuário OS / 390 Mainframe A interface 231

do usuário Java 234

The Data Stores 237


O arquivo de informações do curso O arquivo 238

de informações da turma O arquivo de 238

informações do instrutor As saídas do arquivo 238

de informações do funcionário 238

238

Saídas OS / 390 IBMMainframe 239

Saídas Java 242

Juntando o aplicativo 243

Uma solução de linguagem procedural Uma 244

solução de linguagem Java em resumo 249

254
Página x

Parte 2 Java no ambiente OS / 300 Mainframe 255

Capítulo 14 Visão geral da infraestrutura / 257


arquitetura do OS / 390 Java

Requisitos de software 257

Arquiteturas de aplicativos Java 258

Componentes do software Java versus programas independentes acessando o 259

software do sistema OS / 390 259

Ferramentas de desenvolvimento IBM Java 262

em resumo 262

Capítulo 15 Visão Geral dos Serviços do Sistema OS / 390 UNIX 265

The Command Shell 265

O sistema de arquivos HFS trabalhando 267

com arquivos HFS 268

Comparando os conceitos de MVS, UNIX e OS / 390 em resumo 270

278

Capítulo 16 Java e MVS Batch 279

Executando Java em lote 279

Executando programas Java com BPXBATCH Executando 280

programas Java compilados e vinculados Executando programas 284

Java como tarefas iniciadas no resumo 285

286

Capítulo 17 Java Record I / O usando o pacote JRIO 287

O que é JRIO? 287

Conteúdo do pacote JRIO 288

Interfaces JRIO 288

Constantes JRIO 290


Exceções JRIO 292

Usando JRIO 294

Directory and Dataset Naming Conventions import 294

Statements Needed for JRIO Representing Record 295

Structures 295

Exemplos de codificação JRIO 298

Buscando dados codificados e configurando dados para campos no resumo 311

313

Capítulo 18 Java, CICS e IMS 315

Java e CICS 315

O CICS Transaction Gateway 315

Escrevendo um programa JCICS usando COMMAREA 320


Página xi

Java e IMS 321

O que é IMS Connect for Java? Em 322

suma 326

Capítulo 19 Java e DB2 327

Visão geral de Java e DB2 para OS / 390 Java 327

Database Connectivity (JDBC) usando JDBC 329

329

Java e SQLJ 334

Usando SQLJ 334

Comparando JDBC com SQLJ no 337

resumo 337

Capítulo 20 O Sistema Revisitado do Programador de Aulas do Departamento de 339


Treinamento

O recurso do aplicativo definido O 339

SQL usado no exemplo 339

Uma solução de linguagem procedural para a opção "Exibir lista de turmas depois da data 340
inserida"

Comentários sobre a solução COBOL 340

Uma solução de linguagem Java para a opção "Exibir lista de turmas depois da data 344
inserida"

O código para uma única classe recuperado do banco de dados no resumo 344

347

Parte 3 Java: acima e além de outras linguagens de programação 349

Capítulo 21 Applets 351

Um pouco de história 351

Aplicativos versus miniaplicativos 351

Codificando a página da Web que usa o miniaplicativo 352


Como um miniaplicativo é executado 352

em resumo 357

Capítulo 22 Fundamentos da interface do usuário Java 359

Bibliotecas de componentes Java GUI 359

Componentes padrão da GUI 360

Java Containers 360

Outro exemplo 366

Em suma 371
Página xii

Capítulo 23 Arquivo Java E / S 373

O arquivo 374

Arquivos (conjuntos de dados) em COBOL, 374

arquivos PL / I em Java 375

O conceito de um fluxo em COBOL 378

e fluxos PL / I em Java 378

380

Em suma 383

Capítulo 24 Bibliotecas Java 2 Enterprise Edition 387

O que é J2EE? 387

APIs J2EE de Java no 388

servidor 389

Em suma 394

Capítulo 25 Invocação de Método Remoto 395

O que é Java RMI? Mecânica 395

Java RMI 396

É hora de um exemplo 398

Etapa 1: Crie a interface RMI Etapa 2: Codifique 398

a classe de cliente Etapa 3: Codifique a classe 400

de servidor 401

Etapa 4: compilar a interface, o servidor e as classes de cliente 403

Etapa 5: Gerar o Stub com o programa rmic 403

Etapa 6: coloque o arquivo de classe Stub onde o cliente e as classes de servidor possam 403
encontrá-los

Etapa 7: iniciar o registro rmi Etapa 8: criar 404

um arquivo de política 404


Etapa 9: execute a classe de servidor 405

Etapa 10: Executar o Resumo da Classe Cliente (Invocar o Método Remoto) das 405

Etapas de RMI 405

Em suma 407

Glossário 409

Bibliografia 417

Índice 419
Página xiii

Prefácio

Desenvolvimento de aplicativos Java para mainframe fornece aos processadores de dados big iron uma ferramenta de referência e
aprendizagem que eles podem usar para escrever programas Java executados no OS / 390. O objetivo do livro é descrever Java na
linguagem do profissional de mainframe e mostrar como esses profissionais desenvolveriam aplicativos Java para o IBMmainframe.

Este é um livro "como fazer", cujo objetivo é transmitir regras e técnicas gerais, fazendo analogias entre o familiar e o novo. Pouca
menção é feita às complexidades técnicas da Java Virtual Machine, algoritmos de coleta de lixo, "a domesticação dos threads" ou
outros tópicos que lidam com componentes internos de Java.

Quem deveria ler esse livro?

O público principal deste livro é o programador de mainframe. Esses programadores têm anos de experiência no mainframe e, embora
seja alta a probabilidade de terem um desktop wintel (para e-mail, produtividade de escritório e emulação de terminal de mainframe), eles
podem não ser adeptos da programação em qualquer coisa que não seja um mainframe.
Y

O livro ajuda os programadores a aprender programação Java, mas o livro tem um público mais amplo do que os programadores de
FL

mainframe. Os analistas de sistemas precisam entender do que se trata o Java, assim como os programadores. A administração,
especialmente os gerentes de primeira e segunda linha, precisa entender Java e uma maneira de relacionar Java a sua formação técnica.
A M
TE

Como um aparte, o livro assume que o leitor não tem experiência em programação C ou C ++, o que significa que a sintaxe
Java, até o uso de chaves, pode ser desconhecida para o leitor.

Team-Fly ®
Página xiv

A Organização do Livro

A Parte 1, "Fundamentos de Java", descreve Java comparando seus recursos de linguagem com aqueles de linguagens procedurais de
terceira geração, como COBOL e PL / I. O livro descreve Java como uma linguagem de programação orientada a objetos. A Parte 1
conclui mostrando algum código Java para um aplicativo e comparando esse código Java ao código COBOL e PL / I que executa funções
semelhantes.

A Parte 1 contém capítulos que discutem loops, construções de decisão, declaração de dados e invocação de sub-rotina / função (métodos,
na verdade) - a linguagem das linguagens de programação procedural. Outros capítulos discutem representação de classe / objeto, herança
e encapsulamento - a linguagem das linguagens de programação orientadas a objetos. Depois que o leitor concluir a Parte 1, ele terá uma
boa compreensão de como usar Java e como Java se compara a linguagens de programação de mainframe familiares.

Os objetivos da Parte 1 são os seguintes:

Para descrever Java comparando e contrastando Java com linguagens de programação familiares

Para apresentar o Sun Java JDK para que o leitor possa criar e executar programas Java simples em seu PC

Explicar como Java implementa a metáfora da linguagem de programação orientada a objetos, mostrando assim como
Java é diferente de PL / I, COBOL e outras linguagens usadas pelo programador de mainframe

A Parte 2, "Java no Ambiente de Mainframe", descreve a estratégia "Java em todo lugar" da IBM examinando Java no ambiente
OS / 390. Cada capítulo cobre como Java funciona com uma marca específica de tecnologia IBM, como CICS, DB2 ou VSAM. O
código Java que explora tecnologias específicas da IBM está incluído. A seção termina com o código Java que acessa as tabelas
do DB2.

A Parte 2 explica mostra como a IBM forneceu ao programador Java acesso a tecnologias testadas e comprovadas. Depois
que o leitor concluir esta seção, ele terá uma boa noção de como explorar o Java no ambiente OS / 390. O leitor se sentirá
bastante confortável com Java; ele conhece a sintaxe, como Java implementa a visão de mundo orientada a objetos (da
Parte 1) e como usar Java com tecnologias IBM familiares (Parte 1).

Os objetivos da Parte 2 são os seguintes:

Explicar como a IBM implementou Java em seu ambiente de mainframe Explicar como

usar Java com as seguintes tecnologias IBM: CICS

IMS

Lote

VSAM
DB2

Para comparar e contrastar o código Java com código COBOL e PL / I ao usar a lista anterior de tecnologias
Página xv

Para descrever as classes JRIO da IBM e como o programador Java no OS / 390 usa essas classes para realizar E / S de registro

Para mostrar o código do aplicativo usando JDBC para acessar dados do DB2

Nas primeiras duas partes, o livro mostra o código COBOL e PL / I que funciona como o código Java nos snippets e aplicativos. No
entanto, essa abordagem só pode levar o leitor até certo ponto no mundo do Java. Simplificando, Java é muito mais capaz do que
COBOL e PL / I juntos. Portanto, a última seção do livro descreve vários recursos-chave de Java que são encontrados apenas em
Java.

A Parte 3, "Java: Acima e Além de Outras Linguagens de Programação", aborda levemente vários recursos e capacidades Java, como a
criação de miniaplicativos, as classes de interface do usuário Swing, Java 2, Enterprise Edition (J2EE) e Remote Method Invocation
(RMI) .

Os objetivos da Parte 3 são descrever alguns dos recursos exclusivos do Java, incluindo

Codificação de miniaplicativo

Classes Java Native File I /

O Java GUI

As bibliotecas que constituem o Java 2, Enterprise Edition

As classes Remote Method Invocation (RMI), que permitem a um programador Java executar programas Java em
outras máquinas em uma rede

Convenções utilizadas neste livro.

As listagens de código e as saídas aparecem em uma fonte monoespaçada, como

public static void main (String [] args)

A primeira vez que um termo é usado em um capítulo, o termo é impresso em itálico.

Ao longo do livro, você encontrará informações da barra lateral que contêm informações relevantes que não se encaixam no contexto
atual. Esta é a aparência de uma barra lateral:

Você também encontrará notas que aumentam o material anterior à nota. Aqui está

PODE OLHAR ESTE É TÍTULO DA BARRA LATERAL HOWA E aqui está algum

texto explicando o título da barra lateral. . .

como pode ser uma nota:

Este texto pode explicar o material imediatamente anterior à nota.

Você também encontrará dicas que oferecem um atalho ou uma solução para um problema comum relacionado ao material que acabou de ler.
Este texto pode fornecer um atalho ou solução para um problema comum.
Página xvi

Esta página foi intencionalmente deixada em branco.


Página xvii

Agradecimentos
Este trabalho não teria sido possível sem a paciência quase infinita do povo trabalhador da John Wiley & Sons. Claro, meus parabéns vão
para minha esposa, que teve que testemunhar e suportar alguns comportamentos estranhos de minha parte durante a conclusão deste
trabalho.
Página xviii

Esta página foi intencionalmente deixada em branco.


Página 1

Parte um
Fundamentos de Java
Página 2

Esta página foi intencionalmente deixada em branco.


Página 3

CAPÍTULO 1
Introdução

Java Trek

Você é um programador de mainframe envolvido com a programação COBOL. Você não pode escapar do canto da sereia de programação
orientada a objetos ( OOP). Cada publicação comercial que você lê tem artigos de especialistas da indústria entoando o mantra do
objeto. De maneira evangélica, esses novos discípulos do objeto fé citam capítulo e versículo dos mísseis de São Booch e São
Jacobsen. A mensagem é clara - você deve adotar o Object Tao e expulsar com justiça os velhos demônios da programação
procedural e do design estruturado.

Ok, você recebeu a ligação. Como um mamífero altamente evoluído, seu instinto de sobrevivência entra em ação. Você sente que é melhor descobrir
do que se trata essa coisa de objeto. Se esses arautos do caminho do objeto estiverem corretos, você pode se tornar obsoleto antes de se tornar
investido em seu plano de pensão. Devidamente motivado, você começa sua busca pela verdade sobre a tecnologia de objetos.

Um Novo Mundo de Objetos


Y
FL

Os oceanos da literatura e a galáxia de páginas da Web contêm muitos novos termos e conceitos. Por conta própria, o admirável mundo
novo da tecnologia de objetos pode ser bastante assustador. Nesta exploração, você terá um guia experiente para ajudá-lo a classificar as
M

diferenças entre os conceitos de programação de mainframe e a tecnologia de objetos - especificamente a programação Java.
A
TE

Team-Fly ®
Página 4

Os aplicativos de objeto não são conjuntos de procedimentos relacionados que atuam em fontes de dados externas, mas conjuntos de objetos em
comunicação. Esses objetos contêm todos os seus dados e procedimentos necessários. A visão de objeto de sistemas de software de modelagem é
baseada nas propriedades e comportamentos de entidades de aplicativos reais. Comparativamente, o antigo modelo de design de software de criar
modelos de dados e gráficos de estrutura separados parece arcaico.

Reutilização.

Os profetas do objeto fazem fortes afirmações sobre a reutilização de software. Sobre a questão da reutilização, os profetas falam a uma só
voz. Eles dizem que apenas usando a tecnologia de objeto você, o programador, pode criar um código verdadeiramente reutilizável.

Todos nós sabemos que a reutilização de software não é um problema novo. Na verdade, a reutilização de software sempre foi o ideal platônico
de programação. Você provavelmente esteve muito perto desse ideal às vezes. É possível escrever código reutilizável com uma linguagem
procedural, mas a capacidade de reutilização é alcançada apesar, e não por causa de, suas ferramentas de programação e ambiente.

Existe realmente um ambiente de programação em algum lugar que permite que você crie código reutilizável como uma regra, não como uma
exceção? Qualquer cultura avançada o suficiente para produzir ovos sem colesterol e dispositivos de amamentação para machos deve ser
avançada o suficiente para produzir esse ambiente de programação. Mas os ambientes de objeto realmente ajudam o programador a escrever
código reutilizável?

Herança

Você pode ter lido sobre essa propriedade maravilhosa de objetos chamada herança. Com a herança, você escreve o código que implementa algum
comportamento para um grupo de objetos semelhantes ou uma classe. Você cria subclasses com base em algum relacionamento - subclasse A
superclasse ISA de B, por exemplo. Uma vez feito isso, o código que implementa comportamentos para a superclasse é automaticamente conhecido
pela subclasse. Sim, eu disse automaticamente conhecido pela subclasse.

Bem, isso certamente soa como um recurso que me ajudaria a escrever código reutilizável. Pense em uma superclasse de conta bancária
com subclasses de conta corrente e poupança. Você escreve o código para implementar o comportamento de retirada para a classe de
conta bancária. Este código torna-se automaticamente conhecido pela superclasse de conta corrente e poupança. Uma rotina, três aulas.
Isso supera a capacidade de reutilização do código mundial COBOL, copiando em um membro separado do seu PDS e alterando algumas
linhas. Você vê o valor real neste material de herança?

Encapsulamento

Você provavelmente encontrará o termo encapsulamento em qualquer exploração da tecnologia de objetos. Com o encapsulamento, os dados e o
código que implementam comportamentos em objetos de aplicativo são ocultados de outros objetos. A grande ideia é que, como outros objetos
não sabem sobre os dados internos e representações de comportamento de um objeto, esses outros objetos não podem alterar essas
representações. Resumindo, o encapsulamento fornece um mecanismo de segurança que evita alterações indesejadas nos dados de um objeto.
Página 5

Como esse encapsulamento realmente ajuda a prevenir alterações indesejadas? Pense no último conjunto de módulos COBOL que você
escreveu. Digamos que você codificou uma lista de parâmetros na instrução CALL / USING do módulo de chamada. Lembra do
comportamento inesperado do programa de chamada? Lembra de tentar depurar o programa de chamada? Lembra como o programa
chamado mudou um dos parâmetros fornecidos na seção LINKAGE? Lembra como esse problema foi difícil de localizar? De repente,
como a lâmpada piscando sobre a cabeça do coiote naqueles desenhos animados bobos do roadrunner, você vê a beleza, majestade e
praticidade do encapsulamento.

O problema com os módulos COBOL é que tanto os módulos de chamada quanto os chamados precisavam saber a representação dos dados
dos parâmetros. Como o módulo chamado conhecia a representação dos dados, o módulo continha o código que alterou o parâmetro. Às
vezes, você conta com a capacidade do módulo COBOL chamado de alterar o valor do parâmetro e escrever o código para fazer as alterações
desejadas. Infelizmente, você pode deslizar e escrever código que altera inadvertidamente os parâmetros passados. O módulo de chamada não
tem conhecimento dessas alterações e não executa corretamente.

Devido ao encapsulamento, uma aplicação de objeto nunca pode sofrer esse destino. O que um objeto não sabe, um objeto não pode
mudar. Os dados contidos em objetos estão protegidos contra adulteração indesejada de outros objetos.

Stack Class

Digamos que Joe Programmer escreveu um programa PL / I há algum tempo. Por ser um cara esperto, ele implementou uma pilha como uma
estrutura de dados controlada (uma estrutura de dados controlada em PL / I - declarada com a classe de armazenamento CONTROLLED - significa
que o programa PL / I pode alocar memória dinamicamente para a estrutura com a instrução ALLOCATE). Todas as rotinas que usaram a pilha
tiveram esta declaração de estrutura controlada. O código de Joe funcionou; ele estava imensamente satisfeito consigo mesmo. Quando o líder da
equipe agendou a revisão do código, ele mal podia esperar para demonstrar seu conhecimento superior da linguagem PL / I.

Bem, acontece que Joe deveria saber que o líder do projeto não é tão versado em PL / I quanto ele. Quando ela viu a pilha e sua alocação
controlada, ela gritou: "O que é isso?" Com orgulho, Joe descreveu as complexidades da classe de armazenamento controlado da PL / I. Uma
carranca lentamente se espalhou pelo rosto do líder do projeto. Ela claramente não ficou impressionada com o código de Joe e pediu a ele para
reescrever o código da pilha, usando uma estrutura de dados mais convencional. Quando Joe humildemente pediu uma sugestão, ela sacou
uma representação de matriz em um quadro branco. Joe voltou para seu cubo para fazer as mudanças necessárias.

Joe procurou cada módulo que acessou a pilha porque ele teve que mudar cada um. Ele teve que mudar as rotinas POP,
PUSH e ISEMPTY também. Que dor. Talvez Joe devesse ter conferido com Hagatha, o líder da equipe, antes de embarcar
em sua jornada de codificação.

Se esta aplicação fosse feita em uma linguagem de objeto, Joe precisaria alterar apenas a classe da pilha. Todos os objetos que se
comunicam com a pilha, ignorando a representação dos dados da pilha, não precisariam ser alterados.

Outro bônus - por causa da herança, todas as subclasses de uma classe alterada sabem automaticamente da mudança. Joe não precisa fazer nada
para implementar uma nova representação de dados ou um comportamento nas subclasses.
Página 6

A esta altura, você deve estar firmemente convencido de que esse objeto definitivamente vale o preço de admissão. Esta representação de classe /
objeto, combinada com herança e encapsulamento, cria um código poderoso. Muitas outras maravilhas da tecnologia de objetos estão aí para
desenterrar - vamos continuar escavando.

Polimorfismo

E se você pudesse enviar a mesma mensagem para objetos diferentes e cada objeto invocasse o comportamento apropriado em resposta à
mensagem? Lembre-se de que os aplicativos de objeto são objetos de comunicação, não chamadas de função / módulo separadas agindo
em fontes de dados externas. O termo
mensagem faz sentido na linguagem do objeto. Os objetos comunicam-se emitindo mensagens uns para os outros. A mensagem invoca
algum comportamento que você implementou no código. O impulso do polimorfismo é que cada objeto responde a uma mensagem de
acordo com sua compreensão dessa mensagem.

Há uma verdade prática e cotidiana nisso. Como as pessoas respondem às mensagens? Não respondemos à nossa maneira? Pessoas
diferentes (e cachorros, nesse caso) não respondem de maneira diferente à mesma mensagem? Quando você se acostuma e sintoniza
com esse conceito, a visão de mundo do polimorfismo parece tão natural e apropriada quanto usar roupas íntimas.

Você pode pensar que a tecnologia de objetos é recente, digamos, dos anos 1990. No entanto, a tecnologia de objetos existe desde os anos
1970. Pode ser estranho perceber que a tecnologia de objetos é mais antiga que o seu cachorro. Pode ser mais velho que seu outro
significativo. É certamente mais antigo do que algumas das grandes tecnologias de ferro como DB2 e REXX que você tem usado nos últimos
15 anos.

Java e C ++

Agora, você já absorveu o essencial de um sistema de objetos: representação de classe / objeto, encapsulamento, herança e
polimorfismo. Isso é bom. Mas, como programador, você pode ter curiosidade natural sobre as linguagens de programação que
implementam esses fundamentos. Parece que toda linguagem de programação tem um tipo de objeto (até mesmo COBOL). No
entanto, as mais comuns são as linguagens de programação C ++ e Java. Vamos investigar essas duas linguagens.

Seu primeiro impulso pode ser correr para uma livraria e comprar alguns dos incontáveis livros sobre essas línguas. Como a
maioria dos livros de programação custa mais de 40 dólares, alguns livros equivalem a muito dinheiro. Afinal, você é um
programador, não um traficante de drogas ou dentista. Três livros equivalem a comida por duas semanas. A essa altura, o pessoal
de casa deve estar cansado de comer gorros. Talvez você possa se virar sem os livros por enquanto.

As pesquisas na Internet revelam uma grande variedade de links em C ++ e Java. Existem muitos códigos-fonte em C ++ e Java, muitas
terminologias e acrônimos e muitas conversas sobre UNIX na rede. Há pouca informação sobre como a tecnologia de objetos é usada por
empresas em certos setores - principalmente o seu. Para descobrir como este objeto - C ++ - a tecnologia Java é usada por seu setor e sua
empresa fará mais pesquisas.
Página 7

Talvez seja necessária uma abordagem direta. Por que não conversar com o pessoal da sua organização para saber se alguém no mesmo prédio está
usando tecnologia de objetos? Você pode encontrar alguns convertidos de objetos sob o teto de sua empresa. Talvez alguns desses profissionais de
objetos possam lançar alguma luz interessante sobre o uso de C ++ e Java dentro da empresa.

C ++

Joe, o primeiro praticante de objetos que você conhece, usa a linguagem de programação C ++. Este programador jura com o fervor de um
ex-fumante recente que qualquer programador de objetos que se preze usa C ++. Ele alegremente leva tempo para explicar seu ardor por C
++.

Joe diz de maneira pomposa e irritante que C ++ oferece suporte a todos os recursos da programação de objetos: representação de classe /
objeto, encapsulamento, herança e polimorfismo. Ele explica que C ++ é uma linguagem de objeto híbrida - uma linguagem construída
sobre uma linguagem de programação procedural existente (C, neste caso). Ele se orgulha de que sua experiência em C e conhecimento
de conceitos de objetos o habilitaram a começar a codificar quase imediatamente.

Ele continua explicando que C ++ oferece suporte a herança múltipla. A herança múltipla permite que uma subclasse
herde atributos de dados ou comportamentos de mais de uma superclasse. Claro, ele sorri, a herança múltipla é mais
flexível e superior à herança única.

Por causa de seu trabalho árduo e pesquisa, você entende do que Joe está falando. Você se enche de alegria ao perceber que pode falar
com o objeto. Você não pode esperar para andar o objeto andar. Impacientemente, você pede a ele para lhe mostrar algum código C ++.

Enquanto seus olhos examinam o código C ++, seu rosto brilhante e alegre rapidamente se transforma em um bichano maçante e triste. Você não
consegue entender nada disso. Este código pode ser uma escrita cuneiforme babilônica, no que diz respeito a você. Você não pode acreditar que
sob essa bagunça está essencialmente um código em uma linguagem procedural.

Você se lembra da primeira vez que viu PL / I. Você não entendia os detalhes básicos, mas tinha um bom entendimento do que o código
fazia. Sua experiência em COBOL foi uma boa preparação para a compreensão de PL / I. Infelizmente, essas coisas do C ++ parecem muito
enigmáticas.

O programador C ++ percebe seu estado de confusão geral e pergunta quanta experiência em C você tem em seu currículo. Pode haver
muito sob seu cinto atualmente, mas nada disso é experiência de programação C. Você é um programador COBOL e PL / I. Lembra quando
você teve que aprender a usar o ponteiro em PL / I? Você finalmente percebeu que um ponteiro é meramente um endereço de quatro bytes.
Joe mostra ponteiros para cadeias de caracteres, ponteiros para inteiros e (suspiro!) Ponteiros para ponteiros, explicando que, embora esses
ponteiros sejam endereços, são todos tipos de dados diferentes. Você agradece ao Joe por seu tempo e passeie pelo seu cubículo. Sozinho
com seus pensamentos, o horror de aprender C ++ se infiltra. Você pensou que a programação de objetos seria mais fácil do que a
programação procedural convencional. Essa coisa de C ++ parece ser programação procedural no seu pior.

No almoço, você menciona a um amigo que está procurando por programação C ++. Esse amigo sabe uma ou duas coisas sobre C ++. Ele
possui algumas informações C ++ obtidas de um fornecedor de ferramentas de desenvolvimento de software Macintosh. Ele acredita que
esta informação é específica do Mac. Mas as informações C ++ do fornecedor do Mac tornaram o
Página 8

coisas que você reuniu do programador feliz com o ponteiro hoje mais cedo parecem uma canção de ninar. Aqui está o que você lê:

Quando você chama a função "MYSTREAM.read ()", ela normalmente retorna uma referência ao próprio objeto de fluxo, ou seja,.
MYSTREAM. Isso é para que você possa encadear essas funções, assim:

MyStream.read (MyVar1) .read (MyVar2) .read (MyVar3)

Mas quando você chama "MYSTREAM.read ()" de uma função que espera um número inteiro, o compilador executa alguns "truques de mão" e
realmente retorna MYSTREAM.good () "como o resultado da função.

Então é para isso que o mundo da programação evoluiu?

Desesperadamente, você vasculha suas anotações. Você se lembra de ter ouvido através da fábrica que seu departamento de treinamento
corporativo pode estar desenvolvendo cursos sobre tecnologias de Internet. Você se lembra do instrutor da última aula que você assistiu, tantas luas
atrás. Por que não ligar para ela?

Java

Jane, uma instrutora corporativa, está desenvolvendo um curso sobre a linguagem de programação Java. Java, ela explica com
entusiasmo, é parte de um conjunto de tecnologias que permitem a um programador escrever um programa uma vez e executá-lo em
qualquer plataforma - até mesmo na Internet. Ótimo - você precisa lidar com um conjunto de tecnologias para aprender e usar Java. Isso é
exatamente o que você precisa, certo? Antes que você se empolgue com suítes de tecnologia e similares, seu guia Java diz que Java tem
uso e valor como linguagem de programação à parte e à parte da Internet; Java é muito mais do que a "linguagem Applet da Internet". Ao
ouvir isso, você recupera a compostura.

Jane começa a falar sobre o mecanismo de herança do Java. Quando você comenta sobre C ++ e seu suporte para herança múltipla,
ela dá uma risadinha audível. Jane menciona que, com herança múltipla, você não tem uma maneira direta de saber a origem de algum
comportamento herdado. Seu ponto é que um aplicativo desenvolvido com herança múltipla pode facilitar a criação inicial da classe e o
desenvolvimento do aplicativo, mas pode tornar a manutenção do aplicativo um pesadelo. Agora, você supõe que o Java suporta
apenas herança única.

No entanto, quando você aponta as vantagens de livro didático da herança múltipla, ela ri novamente. Seu guia Java aponta que ela
nunca explicou o mecanismo de herança do Java para você. Ela diz a você que Java oferece suporte a herança única, assim como todo
objeto é criado a partir de uma classe que possui uma e apenas uma superclasse. No entanto, os designers de Java estavam bem
cientes das limitações da herança única, bem como das armadilhas da herança múltipla. Ela explica que o mecanismo de herança do
Java é único; uma classe Java tem a capacidade de herdar comportamentos de várias classes, mas apenas uma dessas classes é a
superclasse. Os comportamentos herdados que não pertencem à superclasse são definidos para a classe por um interface.

Você está confuso porque isso soa como herança múltipla. Jane explica que Java oferece suporte a duas hierarquias separadas:
a Classe hierarquia, que define o
Página 9

relacionamento classe / superclasse, e um Interface hierarquia, que permite a uma classe implementar comportamentos de classes
não contidas na hierarquia de classes. Em JavaSpeak, uma classe estende sua superclasse e (opcionalmente) implementos uma ou
mais interfaces.

Bem, isso ainda soa como herança múltipla, mas você entendeu: uma classe Java tem uma superclasse genuína, mas pode implementar
comportamentos de outras classes. Quando você comenta que uma classe Java pode herdar implementações de método apenas da
superclasse, e a classe deve fornecer implementações para todos os métodos usados por uma interface, seu guia concorda com
aprovação. Ela sugere que você ainda não se preocupe com as diferenças entre uma superclasse e uma interface.

Jane explica que o programador C ++ preguiçoso pode escrever código que não faz as coisas legais do objeto, como encapsulamento ou
herança. O programador C ++ preguiçoso poderia escrever código C ++ para ser completamente procedural. A princípio, você pode ficar tentado
a acreditar que essa capacidade do C ++ de usar dois chapéus, por assim dizer, é uma coisa boa. Sim, mas você viu o código C ++, lembra? C
++ não é a sua ideia de programação procedural.

Na verdade, ela diz, uma visão comum de C ++ é que C ++ é uma versão melhor de C; C ++ foi desenvolvido para superar as deficiências da
linguagem de programação C (da qual existem muitas). Agora, você não conhece C e não se importa em aprender, mas vê a sabedoria de suas
palavras. Você começa a entender que C ++ foi desenvolvido a partir de C, em vez de ter sido desenvolvido a partir do zero. C ++ é um C melhor,
não uma linguagem necessariamente projetada para oferecer suporte à orientação a objetos.

O guru Java continua. Ela afirma que você poderia fazer programação procedural em Java, mas qualquer coisa substancial, como E / S de
arquivo ou banco de dados, requer classes e objetos e herança - a matéria dos objetos. Embora você pudesse forçar um programa Java a
ser procedural e não usar recursos orientados a objetos, seu programa provavelmente não poderia fazer nada útil.

Java é uma linguagem moderna, diz ela. Os designers de Java da Sun Microsystems tinham ideias definidas em mente quando o Java foi para
a prancheta. Considerando que o Java foi lançado no final de 1995, você pode dizer que essa tecnologia é quente.

Algum código Java

Talvez você deva olhar algum código Java antes que ela entre em um frenesi Java. Jane apresenta uma implementação do Tipo de bolha em
Java, conforme mostrado na Listagem 1.1.

Parece aquele material C ++ que você viu antes. Você vê aqueles ++, - e as chaves {}. Pelo menos há um uso para essas chaves
agora. No momento, você não está impressionado com a sintaxe dessa linguagem "moderna".

Meu amigável instrutor de Java explica que sintaxe é sintaxe; afinal, não é tudo uma questão de a que você está acostumado? É claro que,
como programador, você dedica tempo para explicar que a sintaxe da linguagem de programação é paralela à linguagem escrita, visto que
ambas precisam ser lidas, escritas e compreensíveis. Você dedica tempo para explicar que uma linguagem repleta de pontuação é mais
difícil de digerir do que uma linguagem sem pontuação excessiva. No entanto, você é uma pessoa cortês e elegante e prontamente cede ao
ponto do seu Guia Java sobre sintaxe.

A vida e a programação são intensas demais para suar as pequenas coisas. Jane diz que seu tempo é curto e ela prefere não revisar muitos
detalhes de sintaxe agora; ela prefere se limitar a explicar os conceitos Java que podem ser assustadores para um aspirante a Java como
você.
Página 10

class BubSort { // 1
public static void main (String args []) { // 2
int anarray [] = {3,10,6,1,2,9}; // 3
sort (anarray); // 4
para (int idx = 0; idx <anarray.length; idx ++)
System.out.print1n (anarray [idx]);

} // 5

static void sort (int a []) { // 6


para (int idx1 = a.length; --idx1> = 0;) {
boolean swapped = false;
para (int idx2 = 0; idx2 <idx1; idx2 ++) {

if (a [idx2]> a [idx2 + 1]) {


int temp = a [idx2];
a [idx2] = a [idx2 + 1];
a [idx2 + 1] = temp;
trocado = verdadeiro;

}
if (! trocado)
Retorna ;
}

} // 7

} // 8

Listagem 1.1 O tipo de bolha infame.

Ela primeiro explica que as barras duplas denotam um comentário de uma única linha; a combinação barra-asterisco denota um comentário de
várias linhas. Ela menciona que o Java diferencia maiúsculas de minúsculas. Ou seja, os nomes de variáveis myPay e MYPAY são variáveis
diferentes. Como uma cantora que começa a cantar, ela começa a explicar as partes do programa pelos números. Ela diz que mesmo este
pequeno exemplo contém muitos recursos Java; ela tem tempo para cobrir apenas alguns pontos. Você expressa sua gratidão e pede a ela que
prossiga.

Ela explica o quadro geral afirmando que o programa tem um método principal que declara e inicializa uma matriz de números, chama um
método de classificação e imprime a matriz classificada. O método sort () implementa nosso amigo, o velho Mr. Bubble. Ambos os principais ()
Página 11

e os métodos sort () fazem parte de uma classe chamada BubSort. Você pode entender o raciocínio dela; é a maneira como você pode codificar
um exemplo para ilustrar um tipo ou recursos de uma linguagem de programação.

O programa Java - pelos números

Ela começa a discutir as linhas numeradas. A linha // 1 é

a declaração da classe:

class BubSort {

Todo arquivo Java contendo código-fonte Java possui uma declaração de classe. A declaração da classe deve corresponder ao nome do
conjunto de dados. Nesse caso, o nome do conjunto de dados seria BubSort.java.

A linha // 2 é a declaração do método main ():

public static void main (String args []) {

Todo aplicativo Java possui um método main (). Agora, ela continua, isso não significa que todo conjunto de dados com código-fonte Java tenha um
método main (). Alguns arquivos de origem Java têm conjuntos de dados de suporte. E, ela acrescenta, os miniaplicativos Java não têm um método
main ().

Sim, miniaplicativos. Você se lembra de ter lido sobre applets Java - pequenos programas executados em uma página da web. Mas você
retém suas perguntas porque está no meio da dissecação desse tipo de bolha.

Falando nisso, Jane retorna à explicação da linha // (2). Bem, essas palavras vazio estático público
tem significado. Ela diz que tem a ver com a visibilidade do método (que é o público palavra-chave) - o método não pertence a
nenhum objeto específico (que é o estático palavra-chave) e o que o método faz ou não retorna (esse é o vazio palavra-chave).

Sim, ela diz— Java tem palavras-chave, ou palavras reservadas, como a maioria das linguagens de programação. A linha // 3 é uma

declaração de array Java:

int anarray [] = {3,10,6,1,2,9};

A primeira palavra-chave, int, é o tipo de dados dos elementos da matriz. Você percebeu que a palavra-chave int
é curto para inteiro. Ela diz que você pode estar certo, pode estar errado - depende do que você acha que um inteiro é.

Você diz que a definição de um inteiro depende do sistema operacional. Em algumas plataformas, um número inteiro tem quatro bytes, digite
binário, sem sinal. Em outras plataformas, um inteiro tem quatro bytes, tipo compactado, assinado. Você diz o int declaração depende da plataforma
em que você está escrevendo Java.

Java e independência de plataforma

Bem, seu sofredor e humilde instrutor de Java certamente tem uma ou duas coisas a dizer agora. Ela pergunta se você leu qualquer
coisa sobre Java. Se sim, ela afirma, você saberia
Página 12

que o Java não se importa com as particularidades do sistema operacional, como tamanhos e formatos de bytes de tipo de dados primitivos. o beleza
de Java é que o Java foi projetado para ser compatível com o código-fonte em várias plataformas. O mesmo Java que executa em uma caixa
Wintel será executado sem modificação em um Macintosh, um IBM RS6000 executando AIX ou em um IBMmainframe executando OS / 390.

Você ri baixinho. Parece que você já ouviu isso antes. Você se lembra como C era a ferramenta definitiva de desenvolvimento de software de
plataforma cruzada. C era o "ser tudo, acabar com tudo" das linguagens de programação. Agora, você nunca se tornou proficiente em C, mas
trabalha com processamento de dados, certo? Se C cumprisse um décimo de sua promessa, você teria ouvido falar sobre isso, certo? Bem, você
ainda gasta seu tempo codificando declarações EVALUATE e olhando para os despejos de Abend-Aid, certo?

Aparentemente sentindo sua descrença sobre as palavras dela, ela explica que o Java alcança essa vantagem de execução em plataforma
cruzada compilando em um formato neutro de plataforma chamado bytecodes. Um software específico de plataforma chamado Máquina Virtual
JAVA traduz os bytecodes em código de máquina específico da plataforma. Conseqüentemente, o compilador Java não precisa se preocupar com
os detalhes do sistema operacional; a Java Virtual Machine cuida disso. E a Java Virtual Machine é específica da plataforma.

Você vê a beleza dessa abordagem. O programador Java realmente não precisa saber o quão grandes são os inteiros ou como os booleanos
são representados em uma plataforma específica. Tudo o que o programador Java precisa saber é quão grandes são os inteiros Java ou como
Java representa booleanos. A Java Virtual Machine cuida dos detalhes mundanos específicos da plataforma.

Oh, de volta à questão, quão grandes são int tipos de dados? Ela diz que Java usa inteiros binários assinados de quatro bytes.

Objetos Java e tipos primitivos.

Uma declaração Java típica é o tipo de dados ou nome da classe seguido pela variável ou objeto nome. Na linha // 3, o identificador uma
matriz é o nome da matriz declarada. Meu guia diz que variáveis ou objetos Java podem ter outros atributos anexados a eles, mas por
enquanto vamos manter as coisas simples.

Você está um pouco confuso com essa coisa de "variável ou objeto". Java não é orientado a objetos? Tudo o que você usa em Java não é um objeto
instanciado de uma classe? Não é assim que funciona essa coisa orientada a objetos?

Ela lembra que Java é uma nova linguagem. Ao longo dos anos, os cientistas da computação aprenderam uma ou duas coisas sobre
linguagens de programação. Por exemplo, uma linguagem de "objeto puro" como Smalltalk nunca, nunca, jamais, permitirá que você use
qualquer coisa, exceto objetos. Em Smalltalk, até o número 2 é considerado um objeto. Alguns cientistas da computação concluíram que essa
abordagem de "tudo é um objeto" não funciona para alguns aplicativos. Um runtime de linguagem de programação deve controlar a memória
alocada para objetos (entre outras coisas). A maioria das linguagens de objeto precisa de rotinas personalizadas para comparar objetos
quanto à igualdade e para ler e gravar objetos. Resumindo, usar objetos é muito trabalhoso para um computador.

Um exemplo de um tipo de aplicativo que não é adequado para a tecnologia de objetos é o tipo de aplicativo de "processamento de números" usado em
laboratórios de ciências. Lembra-se daqueles antigos programas Fortran com cinco loops aninhados? Para usar um sistema orientado a objetos para
realizar
Página 13

cálculos numéricos parecem um desperdício de recursos de computação, considerando o que o sistema pode exigir para manter o controle de
todos os objetos usados. Claro, isso não quer dizer que você não poderia aplicação de código deste tipo em linguagens de objeto.

Java permite que um programador use tipos de dados primitivos em vez de objetos. Por exemplo, este exemplo usa poucos objetos. Os elementos
da matriz, os índices da matriz e o sinalizador booleano não são objetos; esses elementos do programa são variáveis. O array declarado na linha //
3 é o que ela chama de tipo de dados de referência.

À parte, os arrays Java são muito parecidos com objetos. Os arrays podem ser atribuídos a variáveis do tipo Object (Object é a
classe raiz em Java, a classe no topo da hierarquia). Mas, ela continua, os praticantes de Java não consideram um array um objeto.

Você medita sobre isso. Você meio que entende que a declaração do array não declara um objeto. Objetos usam métodos e
não se fala de métodos anexados a este array. Este array é muito parecido com um array usado em COBOL ou PL / I.

Você acredita que algumas dessas coisas de objetos parecem realmente boas "no papel", mas frequentemente falham no teste do mundo real. Você
leu que essa coisa de objeto tem décadas. Vamos enfrentá-lo - se essa tecnologia fosse tão boa, seria mais difundida. Uma mistura de programação
procedural antiquada e esse material de objeto pode ser exatamente o que precisamos no processamento de dados, talvez.

Possivelmente, diz seu guia Java. Ela continua a discutir o exemplo de classificação.
Y

Voltar para o Java Bubble Sort


FL

Voltar para a linha // 3. Observe que esta instrução inicializa a matriz. Em Java, os arrays começam com índice 0, não 1 como algumas linguagens de
M

programação. Apenas algo para se manter em mente, ela brinca.


A

Jane comenta que precisa encerrar logo; ela tem reuniões para assistir e memorandos para escrever. Ela começa a discutir a linha // 4:
TE

sort (anarray);

É aqui que você invoca o método sort () e passa o argumento do array. Isso não é difícil, novo ou estranho. Isso é
programação!

Onde estão os objetos? Onde estão as aulas? Java é orientado a objetos, certo? Claro, diz Jane, mas não precisamos de objetos para fazer uma
classificação por bolha. Java permite que você faça algumas coisas de maneira procedimental.

Dê uma olhada na linha // 6:

classificação de vazio estático (int a [])

O método de classificação tem uma palavra-chave especial, estático, o que significa que você não precisa anexar esse método a um objeto. Às
vezes, você só precisa de um pedaço de código que não depende dos dados de nenhum objeto em particular. O tipo de bolha é assim; a
classificação é inteiramente controlada por parâmetros. O que precisamos é uma maneira de dizer ao Java que queremos esse método, mas não
queremos criar objetos e anexar o método ao objeto. A palavra-chave estática faz exatamente isso.

Ela explica que se ela (ou qualquer pessoa) precisasse de uma classificação por bolha, ela poderia usar o método de classificação na classe
BubSort. Primeiro, o programador removeria o método main () de BubSort.java; Listagem

Team-Fly ®
1.2 mostra o novo BubSort.java.
Página 14

class BubSort {

static void sort (int a []) {

para (int idx1 = a.length; --idx1> = 0;) {


boolean swapped = false;
para (int idx2 = 0; idx2 <idx1; idx2 ++) {

if (a [idx2]> a [idx2 + 1]) {


int T = a [idx2];
a [idx2] = a [idx2 + 1];
a [idx2 + 1] = T;
trocado = verdadeiro;
}

}
if (! swapped) return;

Listagem 1.2 Classificação por bolha em sua própria classe.

Como você pode ver, o método sort () não mudou, mas a classe não tem um método main (). Presumivelmente, a classe
que usará o método sort () tem um método main () e apenas um método main () por aplicativo Java é permitido.

Supondo que a classe BubSort esteja no mesmo diretório que a classe UseBub ou no que os especialistas em Java chamam de classpath,
quando você executa o UseBub, o Java runtime extrairá o que o UseBub precisa do BubSort. Em particular, UseBub precisa da rotina
sort (). Verifique as linhas marcadas com // *** na Listagem 1.3.

BubSort.sort (anarray); BubSort.sort (anarray2);

O método sort () é qualificado com a classe que contém o método.

Você pergunta sobre as outras instruções no programa de classificação por bolha original - aquela da Listagem 1.1. Em particular, você
pergunta sobre essas chaves. O resto das instruções numeradas são colchetes que fecham os blocos. Ela explica que Java usa as chaves
como construções de bloco. A construção do bloco ajuda a definir a visibilidade variável. Além disso, a construção de bloco é necessária
quando mais de uma instrução segue um E se declaração ou uma construção de loop. Mesmo um pequeno programa Java pode conter vários
colchetes, diz ela.

A visibilidade dessas variáveis pode ser definida por chaves; variáveis declaradas dentro de um par de chaves são conhecidas dentro
dessas chaves. Existem alguns casos em que uma variável é declarada em uma instrução. Nesse caso, essa variável é visível apenas
nessa instrução. Ela direciona você para a seguinte declaração:
Página 15

class UseBub {

static void printResults (int classificado []) {

para (int idx = 0; idx <ordenado.length; idx ++)


System.out.println (classificado [idx]);

public static void main (String args []) {

int anarray [] = {3,10,6,1,2,9}; BubSort.sort (anarray);


// ***

int anarray2 [] = {12, 4, 35, 1, 55, 76, 3}; BubSort.sort (anarray2);


// ***

printResults (anarray); printResults (anarray2);

Listagem 1.3 Invocar a classificação por bolha de uma classe.

para (int idx = 0; idx <anarray.length; idx ++) System.out.println (anarray [idx]);

Isto é um declaração única. A variável idx é visível apenas nesta declaração.

Ela pega você olhando para esta declaração. Ela o pega no meio do pensamento e explica que esta é uma construção em loop que
imprime um valor de array no fluxo de saída padrão. Ela também diz que está ficando sem tempo rapidamente e que precisa aumentar
o ritmo.

Depois de uma pausa significativa, você pergunta mansamente a ela onde estão as referências do ponteiro. Você percebe que as linguagens
"modernas" como C ++ e Java estão repletas de indicadores. Você cita sua discussão com o programador C ++ e o que ele lhe disse sobre
ponteiros em C ++. E quanto ao uso de ponteiro em Java, você pergunta?

Com um sorriso travesso, ela me diz que Java não usa ponteiros. Sem dicas? Nenhum, ela responde. Java é capaz de fazer o que toda
linguagem de programação faz, e muito mais, sem o uso de ponteiros explicitamente declarados e manipulados. Um programador Java não
pode declarar um ponteiro ou acessar o endereço inicial de uma estrutura de dados de nenhuma forma.

Bem, bem - sem dicas em Java! Essa única afirmação é música para seus ouvidos de codewear. Você começa a acreditar que esses
engenheiros Java da Sun realmente sabem o que fazem.
Página 16

Conheça o Sun JDK

Você pergunta a ela como ela compila e vincula este programa. Ela lembra que o Java é compilado em um formato de dados independente de
plataforma chamado bytecodes. Você conscientemente acena em concordância. Ela está usando uma caixa WinTel para compilar seu
código-fonte Java. Ela diz que o mercado está repleto de ferramentas Java e que o pessoal sério de Java tem um arsenal de ferramentas para
ajudar no desenvolvimento Java. Por enquanto, ela explica, ela usará o conjunto de ferramentas padrão, ou Java Development Kit, disponível na
Sun Microsystems (os desenvolvedores de Java). Ela abre uma janela do DOS e compila o programa. Em seguida, ela executa o programa. A
Figura 1.1 mostra a janela DOS após compilar e executar o bubble sort.

o Javac comando invoca o compilador Java. De maneira realmente esparsa, uma compilação bem-sucedida não fornece diagnósticos.
Uma compilação Java bem-sucedida produz um arquivo de classe - neste caso,
BubSort.class. A seguir, o Java comando executa o programa. O comando java passa o arquivo de classe criado
anteriormente, no formato Bytecode, para a Java Virtual Machine para execução. O resultado da execução é visto na janela
do DOS anteriormente.

Bem, bem, você pensa. Isso certamente supera a necessidade de enviar compilações em lote e links. Você insere o código-fonte em um
conjunto de dados nomeado apropriadamente, muda para uma janela do DOS onde você faz suas compilações e repete até obter uma
compilação limpa. Em seguida, você executa seu programa na mesma janela.

Você nota que precisa de um programa para executar seu programa Java. Você pergunta a ela onde o arquivo executável é produzido
pelo vinculador. Na verdade, você pergunta a ela onde e como ela fez o link. Seu guia Java informa que ela não vinculou o código Java;
ela não produziu um executável. O compilador java, javac, produziu um classe arquivo composto por bytecodes. Os bytecodes são
interpretados pelo Java
comando.

Você pergunta a ela quanto custam essas ferramentas Java - o compilador (javac), o programa que executa os bytecodes (java) e a Java Virtual
Machine. Você gostaria de obter uma cópia em sua máquina. Quando ela responde que essas ferramentas são livre, você escorrega da cadeira.
Livre? Como isso pode ser? Nossa loja costuma pagar uma fortuna por qualquer ferramenta de software.

Figura 1.1 Janela do DOS após compilar e executar a classificação.


Página 17

Meu guia explica que a Sun fornece um conjunto básico de ferramentas, que inclui o compilador. Outros fornecedores comprometidos com Java
fornecem Java Virtual Machines, ou JVMs, intérpretes de bytecode e visualizadores de miniaplicativos. Ela diz que o padrão Kit de desenvolvimento
do Java, ou JDK, inclui um gerador de documentação (javadoc), um depurador java (jdb), outras ferramentas que lidam com a segurança do
sistema (javakey) e uma maneira de desmontar classes / bytecode Java em código-fonte Java (javap). Ela acrescenta que os desenvolvedores
Java sérios têm ferramentas visuais como o resto do mundo e essas ferramentas visuais custam dinheiro, mas qualquer pessoa pode compilar e
executar programas Java com os brindes incluídos no JDK.

Você está um pouco surpreso com isso, e com razão. Agora você tem um vislumbre do exagero, a empolgação que Java causou na
comunidade de computação. Esse material Java é o mais próximo de um padrão que você já ouviu falar. Para completar, as ferramentas de
desenvolvimento são gratuitas. Claro, você ainda está se recuperando da frase: "Sem ponteiros em Java".

Seu paciente guia Java, e professor, pausa mais uma vez para ouvir seu feedback. Um sorriso lentamente lava seu rosto quando
você percebe que entendeu quase tudo o que ela disse. O programa que você examinou faz sentido em uma espécie de macro. No
entanto, você está bastante experiente para perceber que toda linguagem de programação tem suas peculiaridades e estranhezas e
que a sintaxe da linguagem apenas precisa ser aprendida. No meio de suas reflexões, algo estranho chama sua atenção.

Você não pode deixar de notar seu humilde guia Java folheando alguns manuais de referência de linguagem IBM. Quando você
pergunta o que ela precisa saber, ela responde que gostaria de saber um pouco mais sobre construções de linguagem de
programação em COBOL, PL / I e REXX. Ela deseja comparar e contrastar algumas construções Java com construções de linguagens
mais familiares.

Que grande idéia. Ela gosta que você encontre mérito na ideia dela. Ela olha para o relógio na parede, rapidamente se levanta, pega um
fichário e diz que precisa desligar. Você quer mostrar seu apreço por dedicar seu tempo e energia para lhe mostrar as coisas. Quando ela
ignora suas ofertas de comprar café ou almoço algum dia, você tem uma boa ideia: diga a ela que poderia escrever o programa de
classificação anteriormente em uma linguagem IBM comumente usada. Ela concorda que sua ideia tem mérito. Você pede uma referência
para a sintaxe Java, que ela fornece com prazer. Você desfila no ambiente de trabalho designado para cumprir sua promessa.

Uma versão PL / I do Bubble Sort

Você decide almoçar no cubículo (nada incomum, certo?) E digita alguns códigos. Você sabiamente gasta algum tempo
lendo o material de referência do Java. Você vê que o Java JDK vem com
pacotes. Esses pacotes são coleções de classes relacionadas que executam tarefas variadas de banco de dados e E / S de arquivo a cálculos
matemáticos para movimentação de dados de rede e desenvolvimento de interface gráfica com o usuário. Você pode imaginar que uma grande
parte do desenvolvimento de software Java é saber quais classes já estão disponíveis para seu uso. As mais de 1.000 classes, constantes,
definições de interface e exceções disponíveis parecem assustadoras. Você coloca essa parte da referência de lado por enquanto e decide
continuar examinando a sintaxe para criar uma versão compatível em uma linguagem procedural.

Você deseja codificar uma versão do tipo bolha que faça o que a implementação Java fazia anteriormente, mas no estilo e sabor de uma
linguagem procedural. Demora um pouco para descobrir algumas dessas construções Java; a construção de loop exige um pouco de
leitura. Felizmente, você está familiarizado com o tipo de bolha - uma grande ajuda quando você
Página 18

BubSort: Opções Proc (arg) (principal);


Dcl arg Car (40) Variando;
Dcl anarray (6) Bin fixo (15) Init
(3, 10, 6, 1, 2, 9);
Dcl idx Bin fixo (15) Init (0b);
Dcl arrayLength Bin fixo (15) Init (Comprimento
( uma matriz ) )
;
Comprimento Dcl Construídas em ;

Classificação de chamada (anarray);

Faça idx = 1 para arrayLength;


Colocar lista de pulos (anarray (idx)); Fim ;

Classificar: Proc (a); Dcl a (*)


Escaninho fixo (15);
Dcl arraylength Bin fixo (15) Init (Comprimento (a)); Dcl idx1
Bin fixo (15) Init (0b);
Dcl idx2 Bin fixo (15) Init (0b);
Dcl temp Bin fixo (15) Init (0b);
Dcl trocado Bit (1) Init ('1'B); /* VERDADE */

Faça idx1 = arraylength para 1 por -1;


trocado = '0'B; / * FALSE * /
Faça idx2 = 1 a idx1;
Se a (idx2)> a (idx2 + 1) Então faça;

temp = a (idx2);
a (idx2) = a (idx2 + 1);
a (idx2 + 1) = temp; trocado
= '1'B;
Fim ; Fim ;
if (ˆtrocado) Então retorna; Fim ;

En d Classificar;

End BubSort;

Listagem 1.4 Implementação PL / I de um tipo de bolha.

deseja codificar uma implementação. A Listagem 1.4 é uma implementação PL / I que você escreveu durante um almoço gorduroso de taco de
terça-feira.

Não há diferença significativa entre as implementações Java e PL / I. Ambas as partes do código classificam o array declarado e
inicializado e listam os elementos do array ordenado
Página 19

no terminal. O código PL / I poderia ter delimitadores de bloco Begin / End para imitar a funcionalidade das chaves Java. Por
exemplo, você poderia ter codificado:

Se a (idx2)> a (idx2 + 1) Então


Início;

Dcl temp Bin fixo (15) Init (0B);

temp = a (idx2);
a (idx2) = a (idx2 + 1);
a (idx2 + 1) = temp; trocado
= '1'B;
Fim ;

Mas você sabe que os programadores PL / I quase nunca codificam neste estilo.

Ao olhar para este código PL / I, você não pode deixar de ver as notáveis semelhanças com o código Java. Você percebe que, todo o
hype sobre Java à parte, no cerne dessa coisa Java é Java, a linguagem de programação. Esta linguagem de programação é muito
semelhante às linguagens de programação que você já conhece. Você pensa sobre o quanto aprendeu sobre Java em poucas horas;
isso lhe dá uma sensação calorosa de satisfação.

Enquanto isso, de volta à mesa. . .

Infelizmente, sua realidade como um programador de mainframe se arrasta e você verifica sua fila cada vez maior de solicitações de mudança.
Aqui está uma solicitação de mudança para modificar um relatório criado por um aplicativo COBOL tão antigo que você pode datar com
carbono. Conforme você folheia a documentação praticamente inútil para este aplicativo, você não consegue tirar Java e a cosmovisão do
objeto da sua mente. À medida que você pesquisa o código-fonte e as listagens do compilador em busca de referências de variáveis, você
lamenta que este aplicativo não explore a tecnologia de objetos. Nenhum desses dados é encapsulado ou oculto de alguma forma; toda rotina
que acessa esses dados tem a possibilidade de alterá-los. Você precisa encontrar e analisar todas as referências a esses dados em vários
módulos compilados separadamente.

Bem, algum dia você poderá usar Java em vez de codificar instruções PERFORM e observar os fluxos de trabalho JCL. No
meio de seu devaneio, seu chefe passa e o convoca para uma reunião improvisada de equipe na sala de conferências. Você
pega seu cronômetro e marcha rapidamente para a sala de conferências; você não quer ficar de pé.

Seu chefe anuncia uma nova iniciativa estratégica empurrada do raro ar de fila executiva. Os meninos grandes aprovaram o novo sistema de
informações do cliente. A diferença é que os meninos grandes querem que esse sistema seja desenvolvido com tecnologia de objetos. Seu chefe
pergunta à assembléia se algum dos presentes sabe alguma coisa sobre essas coisas.

Assim que o eco da boca de seu chefe desaparece, o tagarela do departamento explode de forma desagradável. O falastrão
sabe tudo sobre essas coisas, diz ele. Ele diz que leu muito sobre C ++ e essa é a direção que este projeto, ou melhor, toda a
empresa, deve seguir. Os participantes da reunião parecem impressionados. Você, é claro, sabe melhor.
Página 20

A oportunidade bate à porta

“Se você quer fazer coisas reais orientadas a objetos, Java é o caminho a seguir”, você diz. Você explica que Java é compatível com o código-fonte
em todas as plataformas. Você continua citando que as ferramentas de desenvolvimento Java são gratuitas, incluindo geradores de documentação
(que parecem chamar a atenção de todos). Além disso, você não precisa mexer com todos esses ponteiros porque o Java não tem ponteiros.

O tagarela late que Java é suposto ser legal e tudo mais, mas é interpretativo. "Podemos suportar o impacto no desempenho?" O
administrador do banco de dados acrescenta à discussão que o aplicativo provavelmente será vinculado a E / S e que a velocidade de
execução do aplicativo pode não ser um gargalo.

A atenção agora volta para você. Você não pode acreditar que todo mundo, incluindo o falastrão, está prestando atenção em cada palavra sua. Você se
inclina para frente com confiança. Com as sobrancelhas arqueadas triunfantemente, você continua.

Java vem com pacotes, que são coleções de rotinas relacionadas, que permitem ao programador criar telas de interface, ler bancos de dados e
conversar pela rede. Além disso, você cita, a indústria está por trás do Java com os principais participantes oferecendo conjuntos de
ferramentas de desenvolvimento. Java é muito, muito mais do que a "linguagem da Internet". Além disso, a Training and Development está
desenvolvendo um curso interno sobre programação Java.

Você sente que seu chefe precisa de alguns itens de ação para a reunião. Você sugere que seu chefe chame o gerente de
Treinamento e Desenvolvimento para saber como é esse negócio de Java versus C ++. Seu chefe adia a reunião.

Após o bate-papo obrigatório pós-reunião com outros escravos assalariados, você se agacha em seu bunker, buscando referências de
variáveis a este aplicativo COBOL para implementar essa solicitação de mudança. Sua mente divaga. Você não pode deixar de se
perguntar se esta é a última solicitação de mudança para um aplicativo COBOL antigo que você fará. Você entende o entusiasmo e o fervor
dos discípulos em geral, e dos evangelistas de Java em particular. Os discípulos e evangelistas podem estar certos? A programação
procedural é coisa do passado?

A única constante neste mundo é a mudança. Você não pode escapar da Internet e do impacto que as tecnologias da Internet têm nos negócios
e na sociedade. As pontocom anunciam no Super Bowl e colocam anúncios em outdoors nas estradas. Os especialistas em negócios falam
sobre downsizing, terceirização, reengenharia e a nova forma dos Sistemas de Informação. Você ouve as últimas palavras da moda e clichês.
Você ouve sua gestão fervilhando com esses clichês. É muito assustador.

No entanto, você começou bem. Você tem alguma compreensão do hype, da comoção e até mesmo de alguns dos chavões e clichês!
Agora, chegou a hora de você aprender como um processador de dados habilitado para escrever programas COBOL e PL / I pode
dominar Java.

Em suma

O mundo do Java cobre todas as facetas do processamento de dados, desde programas de cliente único até grandes sistemas distribuídos em
toda a empresa. À medida que você aprende mais e mais sobre o admirável mundo novo, você encontrará inúmeras referências a sons estranhos
Página 21

tecnologias, incluindo diretório e serviços de nomenclatura, invocações de métodos remotos e Enterprise JavaBeans. Qualquer
compreensão dessas e de outras tecnologias Java começa com a compreensão da linguagem de programação Java.

Aqui está você, com o livro nas mãos. Você deu um grande passo - mas você já sabia disso - para um reino diferente. Boa sorte em sua
jornada Java. Se você perseverar, estará codificando Java em um piscar de olhos e este livro o ajudará.
Página 22

Esta página foi intencionalmente deixada em branco.

Y
FL
A M
TE

Team-Fly ®
Página 23

CAPÍTULO 2
O que é Java?

Aqui, você lerá um pouco sobre a história do desenvolvimento do Java, alguns dos princípios orientadores dos criadores do Java (Sun
Microsystems) e o que pode estar reservado para o Java em um futuro próximo. Este capítulo aborda as semelhanças e diferenças de conceito
entre Java e linguagens de programação de mainframe familiares. Este capítulo também discute por que conhecer Java é uma habilidade chave
para um processador de dados, seja esse processador de dados um programador, analista, gerente de projeto ou gerenciamento de SI.

Uma breve história de Java

A Sun Microsystems, uma empresa da qual você ouvirá muito neste livro, criou o progenitor do Java nos velhos tempos do início dos anos
1990. Acredite ou não, a Sun inicialmente teve a ideia de usar o que seria chamado de Java para controlar eletrodomésticos. Traga esses
fornos de microondas interativos. Você pode ver as campanhas publicitárias: "O futuro é brilhante com as torradeiras programáveis da Sun
Microsystems?"

Talvez não seja o momento certo para máquinas de lavar inteligentes controladas por computador e coisas do gênero. Aparentemente, a Sun
Microsystems está à frente de seu tempo. Em qualquer caso, esta linguagem de programação, agora apelidada de Java, projetada para controlar
dispositivos tinha algumas propriedades interessantes:

Os programas escritos em Java tinham que ser pequenos ou não requerem muitos recursos para serem executados. Afinal, você não quer
equipar sua TV com muitos megabytes de memória, certo?
Página 24

Os programas escritos em Java tiveram que ser colocados em dispositivos diferentes ou executados em hardware diferente.

Essas duas propriedades tornam os programas Java ideais para execução na web. Os criadores da Sun perceberam isso e, em 1994,
demonstraram o uso do Java na Web escrevendo programas Java, chamados applets que funcionava com um navegador, também escrito em Java.
O miniaplicativo é um pedaço de código Java que é baixado para uma página da Web (como uma imagem) e executado. Quando você sai da página
clicando em um link, o miniaplicativo é encerrado.

Java tocou o pessoal da Netscape, tanto que a Netscape licenciou o Java em 1995. O mundo começou a ver miniaplicativos Java aparecendo em
páginas da web. Lembre-se de que a Web ainda era relativamente nova em 1995; Naquela época, o mundo corporativo não permitia o acesso à
Internet no trabalho e apenas alguns poucos iluminados entre nós tinham uma conexão à Internet em casa. Mesmo assim, a Sun percebeu que Java
foi um sucesso e aumentou seus esforços de desenvolvimento Java. Java havia se tornado digno de cobertura em vários jornais especializados. O
uso de Java começou a se espalhar pela Internet com miniaplicativos aparecendo em páginas da Web após páginas da Web. Java versão 1.0 nasceu
oficialmente. Java estava a caminho.

Para encorajar o desenvolvimento Java, a Sun faz seu Kit de desenvolvimento do Java ( JDK), disponível em
http://java.sun.com , sem nenhum custo para quem deseja escrever Java. Como a Sun é a proprietária do Java (Java não é "código
aberto"), você pode contar com o Sun JDK para trabalhar com a versão mais recente e melhor do Java. Você lerá mais sobre o JDK
posteriormente e usará o JDK, se desejar, para compilar e executar alguns dos programas Java neste livro.

Sim, está tudo muito bem. No entanto, se tudo o que o Java faz é executado em uma janela do navegador, você não ouviria um décimo do hype
e, com toda a probabilidade, não estaria lendo este livro. O fato é que Java é muito mais do que a linguagem de miniaplicativos da Internet. E, no
início de 1997, com o lançamento da versão Java
1.1, a comunidade Java percebeu esse fato. A própria linguagem Java mudou muito pouco desde a versão
1,0 a 1,1; Java versão 1.1 é uma versão mais rápida, robusta e com recursos do Java do que a versão 1.0. A versão mais recente incluída interfaces
de programação de aplicativos ( APIs) para acesso remoto, acesso a banco de dados, impressão, criptografia e muito mais. O JDK para este
lançamento incluiu ferramentas que geraram documentação, um depurador de linha de comando, desmontador e outros. Resumindo, o Java
versão 1.1 levou o Java da linguagem de miniaplicativo da Internet para uma linguagem completa que pode ser usada para desenvolver
aplicativos, dentro ou fora da Internet.

A maior parte do hype em torno do Java trata dos novos recursos e capacidades decorrentes do Java versão 1.1. O Java versão 1.0, usado
por um ou dois anos, demonstrou sua eficácia e a Sun demonstrou um compromisso com o Java. A indústria se levantou e percebeu. Os
grandes participantes da indústria começaram a embarcar no Java. Vários consórcios foram formados na tentativa de criar algum tipo de
padrão para a tecnologia Java, embora alguns digam que esses consórcios foram formados como uma defesa contra, bem, você sabe quem.
Fornecedores de ferramentas de software, como Borland, Symantec, IBM e Sun começaram a oferecer ferramentas comerciais de
desenvolvimento Java. As empresas estavam fazendo as coisas com a versão Java

1.1.

A divisão Java da Sun avançou com a próxima versão do Java, que a Sun originalmente chamou de Java versão 1.2, oficialmente lançada no final
de 1998 e apelidada de Java 2. O salto de Java 1.1 para Java 2 não é tão longe quanto o salto de Java 1.0 para Java 1.1. No entanto, Java 2
contém aprimoramentos significativos em relação ao Java 1.1 nas áreas de manipulação de eventos e construção de interface com o usuário, para
citar alguns. O JDK que oferece suporte ao Java 2 foi chamado de JDK 1.2.
Página 25

O mundo de Java hoje.

Hoje em dia, o povo Java não fala de Java língua versões demais. O povo de Java fala de versões da plataforma. Por exemplo, a Sun tem um Java Padrão
plataforma (J2SE para Java 2 Standard Edition), que programadores experientes em Java podem usar para desenvolver miniaplicativos e pequenos
aplicativos. A Sun também possui uma plataforma Java Enterprise (J2EE para Java 2 Enterprise Edition), que programadores experientes em Java
podem usar para desenvolver grandes aplicativos em um ambiente de computação distribuído (empresarial). Além disso, a Sun tem um JDK para
cada plataforma e conjuntos de APIs que são peculiares a cada plataforma (ou apenas peculiares, talvez).

Hoje, o mundo Java está repleto de siglas, ou TLAs (que é Acrônimos de três letras) como JSP, EJB e RMI, para citar alguns. Se
você verificar http://java.sun.com/products , você verá dezenas de acrônimos - alguns FLAs, como JNDI e JDBC, também.

Java como linguagem de programação, além da programação de máquinas de lavar louça, tem cerca de cinco anos. Considerando que
existem mais de 1.200 livros sobre Java e os TLAs e FLAs mencionados anteriormente, é algo notável. Uma pesquisa superficial na Internet
por "Código-fonte Java" produz milhares de ocorrências. A pessoa interessada em computadores poderia assinar várias revistas e e-zines que
relatam sobre os desenvolvimentos do Java. A Sun Microsystems disponibiliza muitos códigos-fonte Java. Você pode participar de bate-papos
online com especialistas e autores em Java. Resumindo, parece que este é um ótimo momento para aprender Java.

Java: a linguagem de programação

No centro de toda essa conversa sobre plataformas e o fluxo aparentemente infinito de TLAs e FLAs está Java, a linguagem de
programação. Java possui vários recursos que o tornam a escolha de linguagens de programação nesses ambientes de computação
altamente distribuídos que consideramos a norma atualmente. Quais são esses recursos, você pergunta? Vejamos alguns.

Java: a linguagem de programação orientada a objetos

O Capítulo 7, "Representação de classes e objetos", descreve os recursos de software orientado a objetos em geral e a implementação
Java de recursos orientados a objetos em particular. Aqui, vamos fazer uma rápida comparação e contraste de chamar módulos COBOL e
chamar código escrito em Java.

Ao codificar em COBOL ou alguma outra linguagem de programação de terceira geração, você conceitua seu aplicativo como um conjunto de
unidades de programa geralmente chamadas de módulos. Esses módulos interagem entre si por meio de um conjunto de interfaces. Lembra
dos gráficos de estrutura com as setas vazadas e preenchidas? Se isso soa como programação estruturada, você está no dinheiro.

A programação estruturada enfatiza a separação dos dados do processo. Em teoria, um pedaço de código PL / I ou COBOL precisa apenas
conhecer a estrutura dos dados (caractere de 20 bytes,
Página 26

por exemplo) para agir sobre ele. A semântica dos dados não entra em jogo no nível da linguagem. Por exemplo, um compilador e
vinculador COBOL não engasgaria com o seguinte snippet:

Programa de Chamadas Programa Chamado 'COBEX'


01 CÓDIGO POSTAL PIC (9) 9. SEÇÃO DE LIGAÇÃO.
* Código Adicional
LIGUE PARA 'COBEX' USANDO O CÓDIGO POSTAL. 01 SSN PIC (9) 9.

Como os programas chamadores e chamados estão passando um número de nove dígitos, tudo é kosher. Claro, este programa não será
executado corretamente; módulo COBEX está esperando um número da Segurança Social ( SSN), mas está recebendo um código postal de nove
dígitos.

Em contraste, ao codificar em Java, uma linguagem de programação orientada a objetos, você conceitua seu aplicativo como um conjunto de
unidades de aplicativo chamadas objetos. Ao contrário dos módulos, um objeto não é separado dos dados - muito pelo contrário. Um objeto é um
amálgama feliz de dados e código de programa que atua sobre esses dados. O código que atua sobre os dados define as operações permitidas
nesses dados. Na verdade, o código do programa é parte do objeto, tanto parte do objeto quanto os dados.

Em um aplicativo desenvolvido com uma linguagem de programação orientada a objetos, o cenário anterior com o CEP e o SSN não
poderia acontecer facilmente. Você presumiria que os CEPs e SSNs têm semânticas diferentes; uma operação feita em um código
postal provavelmente não seria feita em um SSN.

Em Java, (como em todas as linguagens de programação orientadas a objetos), os objetos vêm de um classe. Quando você chama o código escrito em
Java e passa objetos como argumentos, a classe do argumento no código de chamada e o código chamado devem corresponder. Por exemplo, o
código mostrado a seguir simplesmente não funciona:

Programa de Chamadas Programa Chamado 'JavaEX'


ZipCodeClass myZip; void JavaEX (SSNClass aSSN) {
// Código Adicional
JavaEX (myZip);

Aqui, a rotina de chamada passa um objeto de argumento da classe ZipCodeClass para uma rotina que espera um objeto de argumento da classe
SSNCLass. O código anterior não será compilado, muito menos executado. Ter incompatibilidades de argumentos detectadas no tempo de
compilação relativamente cedo no processo de desenvolvimento de software é um bom presságio para aqueles de nós que ganham a vida
escrevendo software.

Como mencionado anteriormente, o Capítulo 7 tem muito mais a dizer sobre os recursos orientados a objetos do Java.

Java: a linguagem de programação portátil

Portabilidade, a capacidade do software de executar em diferentes plataformas sem modificação do código-fonte, há muito tempo é um recurso
procurado por muitos designers de linguagem de programação. Tradicionalmente, você interrompe o código e, em seguida, passa seu
código-fonte para um compilador que cria o código de máquina para uma plataforma específica. Se você quiser colocar seu programa em
Página 27

uma plataforma diferente, você teria que recompilar seu código-fonte com um compilador na plataforma diferente, que produz
código de máquina para essa plataforma. Depois de trabalhar as diferenças sutis e não tão sutis entre as plataformas, você pode
obter um aplicativo funcional.

O programador Java não sofre esse destino. Java, você vê, não compila em código de máquina. Java compila em um formato chamado bytecode.
O formato do bytecode é o mesmo para todos os sistemas operacionais. Você compila um programa Java em um Macintosh, obtém
algum bytecode; você compila a mesma fonte Java em uma máquina Windows ou OS / 390, obtém o mesmo bytecode. A frase de dez
dólares é que o bytecode é arquitetonicamente neutro.

Como os diferentes sistemas operacionais entendem essas coisas de bytecode? A resposta é que o sistema operacional precisa
ter um Máquina Virtual JAVA ( JVM) instalado para interpretar o bytecode. Às vezes, a JVM é chamada de Java runtime ou o Intérprete
Java. Seja como for, a JVM é o software específico do sistema operacional que interpreta o bytecode neutro da arquitetura em
código executável.

Como você lerá mais tarde, a IBM oferece um compilador Java denominado compilador Java de alto desempenho que produz código OS /
390 nativo em vez de bytecode. O código produzido pelo compilador Java de alto desempenho não requer um JVM para interpretação. A
desvantagem é que o código produzido por este compilador só roda em OS / 390. Obviamente, a IBM também possui um compilador Java
que produz bytecode e requer um JVM específico para a plataforma OS / 390.

Outro recurso Java que ajuda a garantir que os programas Java sejam portáteis entre plataformas é que o Java não tem nenhum tipo de dados
específico da plataforma. Algumas linguagens de programação alteram o tamanho dos tipos de dados dependendo do sistema operacional. Por
exemplo, a linguagem de programação C usa números inteiros de 16, 32 ou 64 bits, dependendo do sistema operacional. Java tem um tamanho
para seus tipos de dados - um inteiro tem 32 bits. Se precisar de números maiores, você declara um tipo ou classe diferente.

Lembre-se do slogan da Sun, "Escreva uma vez, execute em qualquer lugar."

Java: a linguagem de programação "Pointer-Less"

Java começou a vida como um competidor para controlar dispositivos e rapidamente evoluiu como uma linguagem para a criação de
miniaplicativos. Hoje, Java é usado para criar aplicativos dentro e fora da web. A linguagem Java suporta as construções de linguagem
familiares e não familiares. O familiar inclui loops, decisão, caso e construções de função. O desconhecido inclui tratamento de erros e
construções de linguagem para multitarefa.

Um recurso de linguagem de programação notavelmente ausente do Java são os ponteiros. Como você sabe, um ponteiro é um endereço de memória,
normalmente 4 bytes, que indica a localização inicial de uma estrutura de dados na memória.

O programador COBOL normalmente não usa muito ponteiros; o programador PL / I usa ponteiros de vez em quando. Portanto, você pode
achar que a dissertação anterior sobre a proibição do uso de ponteiros pode se enquadrar na categoria de "Não há Grande Negócio". Nesse
caso, continue lendo para ver por que não ter ponteiros é tão importante.
Página 28

Você conhece os programadores que costumam usar ponteiros; eles podem ser identificados por uma névoa opaca em seus rostos, uma contração
nervosa em um dos olhos e uma erupção estranha. Esses programadores conseguem isso realizando aritmética em ponteiros para gerar endereços
de memória, passando arrays de ponteiros como argumentos para procedimentos ou (gasp!) Usando ponteiros para ponteiros para dados de
referência.

Você nunca verá um programador Java sofrer as doenças anteriores pela simples razão de que o programador Java não pode usar ponteiros. Em
outras palavras, a linguagem Java não oferece suporte ao uso de ponteiros. Você não pode declarar um ponteiro em Java e não tem como
acessar a localização inicial da memória de qualquer estrutura de dados.

Ao eliminar o uso de ponteiros, os programas Java não são alimento para uma série de bugs relacionados à memória. Tarefas como alocar
memória dinamicamente para várias estruturas, como listas e árvores vinculadas, são tediosas, sujeitas a erros e extremamente dependentes do
ambiente de execução atual. Como você lerá em capítulos posteriores, Java trata do gerenciamento de memória para você. Java detecta quando
os objetos não estão mais em uso e libera memória automaticamente para uso posterior em seu programa. Como você adivinhou, há uma frase
de dez dólares para este processo - coleta de lixo.

Um efeito colateral da eliminação do uso de ponteiros é que um programa Java é mais seguro do que programas que usam ponteiros. Como um
programa Java não pode acessar diretamente a memória, um programa Java (ou miniaplicativo) não pode usar ponteiros para sair de uma string ou
da memória de um array. O recurso sem ponteiros do Java é uma defesa simples, mas eficaz, contra hacks maliciosos que querem causar danos a
você e ao seu precioso computador.

Ainda no assunto de segurança, o Java possui recursos de segurança além de ser sem ponteiros. Programas Java desconhecidos, incluindo
miniaplicativos, são colocados em uma "caixa de areia" onde o programa pode fazer o que quiser dentro dessa caixa. Por exemplo, applets Java
não confiáveis não podem acessar o sistema de arquivos local. Java possui recursos que reforçam a segurança no nível do programa. Você
dará uma olhada no pacote de segurança Java, java.security, no Capítulo 10, "Interfaces".

Java: a linguagem MultiThreaded

A maioria dos programas de mainframe é executada de forma linear, com um único fluxo de controle. É claro que os programas têm
ramificações com base em entradas e várias condições ambientais. No entanto, o próprio programa é normalmente fazendo uma coisa a
qualquer momento. Vamos chamar programas deste tipo
single-threaded programas.

A maioria dos programas COBOL e PL / I são de thread único. PL / I possui suporte a idiomas para múltiplas tarefas (threading) no OS / 390. No
entanto, o programa de mainframe típico normalmente não requer nenhum tipo de mecanismo de segmentação múltipla. Os trabalhos em lote não
exigem nenhum tipo de multithreading. Mesmo os programas de conversação que requerem a interação do usuário escrita para serem executados em
IMS ou CICS podem ser executados com um único encadeamento.

Os programas baseados na Web podem ser exatamente o oposto. Imagine ficar online, baixar um arquivo e clicar em um link. Você
percebe que o navegador continua o download do arquivo enquanto exibe a página solicitada recentemente ao mesmo tempo. Nós
usamos o termo multithread para descrever este estado de dois ou mais processos (download do arquivo e exibição da página, neste
caso) simultaneamente.
Página 29

O bom senso dirá que uma linguagem de programação usada para desenvolver aplicativos da Web deve tornar a criação de programas
multithreaded um tanto fácil. Java contém uma classe desenvolvida especificamente para facilitar o uso de thread. Além disso, Java tem
o sincronizar palavra-chave, que ajuda o programador Java a controlar quando certas partes do código devem ser executadas.

Você verá alguns exemplos de código multithread neste livro.

Esforços de Java da IBM

Desde 1995, a IBM tem trabalhado, e continua a trabalhar, em estreita colaboração com a Sun e outros pesos pesados da indústria na definição e
refinamento de várias tecnologias Java. A IBM percebeu logo no início do jogo que Java é simplesmente bom demais para ser ignorado. A IBM
forneceu informações vitais para o desenvolvimento de tecnologias Java nos "velhos tempos" que ajudaram a tornar essa tecnologia adequada para o
desenvolvimento de aplicativos corporativos. O resultado dos trabalhos da IBM é um conjunto completo de programas de desenvolvimento Java,
canais de suporte e ferramentas adequadas para o desenvolvimento de aplicativos de classe empresarial para o sistema OS / 390 e sistemas AS /
400.

Os clientes da Big Blue precisam acessar anos de dados armazenados em seus sistemas de mainframe com tecnologias da web. A IBM
desenvolveu tecnologias relacionadas ao Java com esse objetivo em mente. A IBM fez vários aprimoramentos para adequar o Java a
seu ambiente de mainframe OS / 390. Por exemplo, a IBM tem pacotes Java - você lerá mais sobre pacotes Java mais tarde - que
permitem que um programador execute E / S baseada em registro, leia e grave conjuntos de dados VSAM e acesse bancos de dados
IMS. A seção II, dedicada inteiramente ao acesso de dados usando tecnologias Java e IBM, contém material e código de amostra que
faz tudo isso.

A IBM tem seu próprio conjunto de ferramentas de desenvolvimento Java chamado Visual Age para Java, que tem um cliente e um sabor do lado do
servidor. Outras ferramentas IBM Java incluem um compilador Java chamado High Performance Java Compiler que ignora a execução interpretativa
/ JVM compilando o código-fonte Java em código de máquina.

Para obter informações detalhadas sobre os esforços de Java da IBM, você pode verificar http://ibm.java.com .

Java versus COBOL e PL / I: um breve olhar

Uma parte respeitável da Parte 1 do livro compara Java a linguagens de programação procedurais mais antigas ainda em uso hoje. Verifique
o gráfico a seguir para ver uma visão rápida dos recursos de linguagem e onde no livro esses recursos são abordados em detalhes.

Aqui estão algumas palavras sobre algumas dessas entradas de tabela:

Estrutura do aplicativo. Em um alto nível, um aplicativo Java é um conjunto de arquivos chamados classe arquivos. Cada arquivo de classe
contém código que define os dados necessários e o código para definir os objetos dessa classe. Um desses arquivos de classe tem uma
rotina, ou método, chamado a Principal. O aplicativo Java funciona chamando o código desses e de outros arquivos de classe durante a
execução. Capítulo 3, "Criando seu primeiro programa Java", onde
Página 30

Tabela 2.1 Recursos de linguagem em Java versus linguagens procedurais

LÍNGUA JAVA COBOL PL / I


CARACTERÍSTICA

Estrutura do aplicativo Um ou mais arquivos, um dos quais Um ou mais arquivos Um ou mais arquivos, um dos quais tem um
tem um método principal contendo fonte procedimento principal

Tipos de variáveis e Tipos de dados primitivos Representante Representante, algum especial


mecanismo de digitação (variáveis) Dados personalizados tipos (arquivo, complexo, por
tipos (objetos) Tipagem forte exemplo)

Dados agregados Arrays Matrizes, registros Matrizes, registros


estruturas suportadas

Argumentos passaram por Referência interna (objetos) Valor de referência Valor de referência
(tipos primitivos)

Escopo variável Local, classe Global para módulo Local, global para módulo, global para
programa

Variável declarada e Modificadores (visibilidade, outros) Alinhamento de armazenamento Classe de armazenamento, armazenamento
atributos de objeto alinhamento, outros

Suporte a objetos? sim Não Não

Ponteiros suportados? Não sim sim

Programador Não. Gerenciamento de memória Sem recursos para Sim dinâmico. Memória feita para o
manipulação de programador estruturas de dados Alocar / desalocar e assim por diante.
dados dinâmicos
estruturas?
Página 31

Tabela 2.1 Recursos de linguagem em Java versus linguagens procedurais ( contínuo)

LÍNGUA JAVA COBOL PL / I


CARACTERÍSTICA

Tipos de subprograma Interno e externo Sub-rotina externa Interno e externo


métodos (pode retornar objeto, tipo sub-rotinas e funções
primitivo ou vazio)

Manipulação de exceção? Sim (joga, pega) Não Sim (sinal, ligado)

Multithreading sim Não sim


suporte de linguas?

Pré-processador, Não Alguns sim


condicional
compilação?

Funções integradas? Não Não Cerca de 100


Página 32

você dará uma olhada em alguns programas Java simples, é onde você lerá mais sobre a estrutura do aplicativo Java.

Tipos de variáveis e mecanismo de digitação. Java é uma linguagem de programação orientada a objetos. Além disso, Java também
oferece suporte a variáveis declaradas como um tipo de dados primitivo. As variáveis Java correspondem estreitamente às variáveis
declaradas em COBOL, PL / I e outras linguagens de programação procedural. Como você lerá mais tarde, Java geralmente trata as
variáveis de maneira diferente dos objetos.

A tipagem de variável vem em quatro opções: forte, fraco, nenhum, e representante. A tipificação forte significa que as variáveis devem
ser do mesmo tipo de dados para uso em expressões. Uma digitação fraca significa que você pode misturar e combinar variáveis de
diferentes tipos em expressões. Nenhum (sem digitação) significa que você não declara variáveis de um tipo. Representativo significa
que as variáveis são declaradas como um tipo que reflete sua representação de máquina, como um número compactado. Leia o Capítulo
5, “Declarando e Definindo Dados,” para mais informações sobre como Java lida com a declaração de variáveis.

Estruturas de dados agregadas com suporte. Java oferece suporte a matrizes de qualquer tipo ou objeto. Java não oferece suporte a
estruturas de registro como as que você usa com frequência em COBOL e PL / I. No entanto, isso não é muito difícil porque Java, com seu
suporte para objetos, pode facilmente emular estruturas de registro.

Discussões passaram. Os programas podem passar argumentos para subprogramas por referência ou valor. Um programa que
passa argumentos por referência passa o endereço inicial da variável. Um programa que passa argumentos por valor passa
Y

uma cópia da variável. Existem várias diferenças entre os dois mecanismos de passagem. O mais visível e importante é que um
FL

subprograma pode alterar o valor de um argumento passado por referência (porque o subprograma e o programa acessam o
argumento por sua localização na memória) e não pode alterar o valor de um argumento passado por valor (porque o
M

subprograma funciona com uma cópia do original).


A
TE

Java usa dois mecanismos separados de passagem de argumento. Java passa tipos primitivos por valor e passa objetos usando
uma referência interna, não um endereço. O Capítulo 7, dedicado inteiramente à classe Java e representação de objeto, cobre a
passagem de referências de objeto para métodos em detalhes.

Escopo variável. O escopo de uma variável declarada define onde no programa essa variável pode ser acessada. Java usa um conceito
relacionado chamado variável e objeto visibilidade ( consulte o seguinte) para abordar o escopo da variável. Aqui, mencionamos que o Java
tem suporte para variáveis locais e variáveis de classe. Vamos adiar qualquer discussão sobre variáveis de classe para o Capítulo 7,
certo?

As variáveis locais são declaradas dentro de um bloco de código e só são conhecidas dentro dele. Este bloco de código pode ser um
método ou um bloco "do" ou "if". O Capítulo 5 tem mais informações sobre o uso de variáveis locais.

Atributos de variáveis declarados. Algumas linguagens de programação permitem definir melhor as qualidades de uma variável,
oferecendo suporte a vários atributos. Por exemplo,

Team-Fly ®
Página 33

PL / I suporta um atributo de classe de armazenamento e COBOL e PL / I permitem que você declare variáveis que se alinham em limites de
byte, palavra ou palavra dupla.

Java vai muito além de seus primos de linguagem procedural distantes, suportando uma variedade de atributos chamados modificadores na
linguagem Java. Java suporta uma classe de modificadores chamada visibilidade
modificadores. O modificador de visibilidade determina onde uma variável ou objeto (ou método ou classe) pode ser visto. Usar
modificadores de visibilidade é um tópico em si, sobre o qual você lerá no Capítulo 7.

Java permite outros modificadores, que (você adivinhou) você verá no Capítulo 7.

Suporte a objetos. A linha no gráfico diz tudo.

Ponteiros suportados. Idem.

Manipulação do programador de estruturas de dados dinâmicas. Você pode usar estruturas de dados dinâmicas, como árvores
binárias e listas circulares vinculadas, em Java? Por que, certamente. O Java força você, o programador, a lidar com os detalhes
de alocação de memória para nós, fixando a memória em endereços, verificando se a alocação de memória foi bem-sucedida,
liberando nós não utilizados de sua estrutura? Claro que não.

Java fornece classes que permitem usar certas estruturas de dados dinâmicas sem a confusão e confusão de gerenciamento
de memória. Se você já teve o prazer de cuidar daqueles detalhes mundanos de programação listados no parágrafo anterior,
pode desenvolver aquela névoa opaca no rosto, uma contração nervosa em um dos olhos e aquela estranha erupção
cutânea.

Tipos de subprograma. Os termos de procedimento para subprograma são sub-rotina e função; o termo do objeto para o subprograma é
método. Uma sub-rotina é um bloco de código que contém seus próprios dados e lógica; uma função é uma sub-rotina que retorna um
valor para o programa de chamada. Você pode substituir uma chamada de função por uma variável do tipo de dados retornado da
função e o código "faz sentido".

Java é capaz de usar métodos declarados internamente, no mesmo arquivo de classe, ou externamente, em arquivos de classe diferentes.
Um método Java pode retornar praticamente qualquer coisa (se comportar como uma função) ou nada (se comportar como uma
sub-rotina).

Manipulação de exceção. O Java oferece a capacidade de interceptar várias condições e (dependendo da condição) afetar o reparo ou sair
normalmente. Exceções em Java são objetos que contêm (como outros objetos) dados e métodos. Como tal, as exceções podem ser
usadas como objetos; eles podem ser passados como argumentos para métodos, por exemplo. O Capítulo 12, "Tratamento de exceções
e conceitos básicos de thread", é dedicado a este tópico interessante de tratamento de exceções em Java.

Suporte a idiomas multithread. Já cobrimos esse assunto anteriormente neste capítulo. Não precisa ser repetitivo.

Pré-processador, compilação condicional. Java não tem pré-processador ou compilação condicional porque não precisa desses
recursos. Pré-processadores e compilação condicional são usados principalmente para gerar código específico de plataforma. Como
você
Página 34

sabe, Java não precisa desse absurdo. Java não precisa incluir fisicamente o código-fonte, como COBOL COPY ou PL / I%
Include, porque Java identifica arquivos por sua localização no sistema (esta localização é definida por uma variável de
ambiente) e o nome da classe.

Funções integradas. Uma função embutida é uma função que faz parte da definição da linguagem. Por exemplo, PL / I tem uma função
substr que retorna parte de uma string ou uma função de comprimento que retorna o tamanho de uma string. Java não oferece suporte
a funções integradas per se, como parte da definição da linguagem. No entanto, Java possui métodos para classes comuns. Java tem
substring e comprimento métodos que fazem a mesma coisa que as funções PL / I citadas anteriormente.

Em suma.

Então, o que é Java? Java é a bala de prata que nós, processadores de dados, esperávamos? O Java tornará o mundo existente de fluxos
de trabalho JCL obsoleto? Java é bem aceito pelos principais participantes da indústria. Novas informações sobre Java surgem assim que a
tinta seca sobre as informações existentes. Uma coisa é certa: o Java não irá desaparecer tão cedo. Portanto, talvez uma boa resposta à
pergunta anterior seja que Java é um conjunto de tecnologias que, simplesmente, não pode ser ignorado e deve ser considerado. Feliz
ajuste de contas!
Página 35

CAPÍTULO 3
Criando seu primeiro programa Java

Neste capítulo, você usará alguns dos programas disponíveis para download no site do livro,
www.wiley.com/compbooks/marco , para criar um programa Java simples em uma máquina wintel. (O software necessário para criar um
programa no mainframe é um pouco complicado para um exercício de "levantar e começar".) Você fará pequenas alterações no programa e
usará o software baixado para executar versões modificadas. Este capítulo serve como uma boa transição para o próximo capítulo.

Aqui, você usará o da Sun Kit de desenvolvimento do Java ( JDK), versão 1.3. O Capítulo 4 descreve alguns dos
programas incluídos nesta versão do JDK. Por enquanto, o objetivo é colocar um programa Java simples em
funcionamento em sua máquina, aprender um pouco sobre Java dissecando este programa e compilar e executar algumas
variações. Depois de aprender a usar o JDK, você estará pronto para o restante dos capítulos desta seção.

Instalando o JDK

Instalar o Java Development Kit é bastante simples. Aqui está o que você faz:

Veja se você tem 50 MB ou mais de sobra para a instalação do JDK. Verdade seja dita, você pode se safar com cerca de 30 MB.
No entanto, você terá a opção de instalar um
Página 36

muito Código fonte Java - o código-fonte da própria linguagem Java. Você não precisa dessa fonte para usar o compilador Java ou o
tempo de execução, mas você pode realmente aprender uma ou duas coisas tendo o código-fonte à mão!

Se você tem dificuldade para raspar 50 MB em seu disco rígido, talvez seja a hora de se livrar de todas aquelas
animações de e-mail, cartões eletrônicos e fotos desbotadas que você não vê há algum tempo.

Baixe o JDK mais recente do site da Sun Java http://java.sun.com/j2se .

Ao fazer download da versão mais recente do JDK do site da Sun, você deve verificar se obteve o arquivo
completo. O site tem o tamanho do arquivo. Depois de baixar o JDK, verifique o tamanho do arquivo
exibindo seu propriedades.

Supondo que o tamanho do arquivo encontrado no site da Sun e o tamanho do arquivo encontrado ao investigar as propriedades do
arquivo concordem, basta clicar duas vezes no arquivo baixado e seguir as instruções. Os padrões funcionarão bem.

Se os tamanhos dos arquivos discordarem, você deve tentar outro download. Você precisa de um JDK funcional para usar os exemplos neste livro;
se o tamanho dos seus arquivos discordar, você encontrará problemas no futuro.

O JDK está instalado corretamente?

Antes de entrar neste capítulo, você deve garantir que instalou corretamente o JDK. Felizmente, essa verificação é bastante fácil.
Tudo que você precisa fazer é abrir uma janela de Comando (uma janela do DOS, na verdade) (Menu Iniciar -> Executar - Enter
"Comando") e digitar:

java -version

Este comando chama o interpretador Java com uma opção para relatar a versão Java instalada. Se você instalou o JDK com êxito,
poderá ver uma mensagem semelhante a:

versão java "1.3.0"


Java (TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C) Java HotSpot (TM) Client VM (build 1.3.0-C,
modo misto)

Ou você pode ver uma mensagem indicando uma liberação secundária, como:

versão java "1.2.2"


VM clássica (compilação JDK-1.2.2-001, threads nativos, symcjit)

Se você omitir o travessão na frente de "versão", verá:

Exceção no thread "main" java.lang.NoClassDefFoundError: version


Página 37

Sem o traço, o interpretador Java pensa que você deseja executar um programa Java compilado anteriormente chamado version.java que
compilou em version.class.

Se você digitar incorretamente "versão", verá:

Opção não reconhecida: -voision


Não foi possível criar a máquina virtual Java.

Basta estar mais atento ao que você digita e tentar novamente. Vocês não quero

ver isso:

Comando ou nome de arquivo incorreto

Se fizer isso, você terá que reinstalar o JDK.

Compilando e executando o programa

Supondo que você tenha vencido com sucesso o desafio de instalação do JDK, é hora de inserir um pequeno programa Java em um editor de texto.
Os programas Java, como os de outras linguagens de programação, são fluxos de texto. Qualquer programa de texto ou processador de texto capaz
de salvar seu código-fonte Java como texto simples e antigo será suficiente.

Você entrará no modo antigo e clássico de exemplos de linguagem de programação: HelloWorld. Abra seu processador de palavras ou
texto e insira o código-fonte Java na Listagem 3.1.

Claro, você precisa inserir o código Java exatamente como mostrado. Preste atenção especial a
capitalização. Os programadores Java seguem convenções de nomenclatura que regem a capitalização; você lerá o skinny completo sobre
essas convenções nos capítulos 5, 6 e 7. Mais adiante neste capítulo, você verá um exemplo do que acontece quando você não presta
atenção à capitalização adequada.

Salve o arquivo como HelloWorld.java. Você verá que o compilador Java se preocupa pouco com o nome do arquivo de código-fonte; o
compilador, no entanto, está realmente interessado no nome do classe que você usou.

Abra uma janela do DOS e verifique o diretório referenciado na janela para garantir que o diretório seja o mesmo em que
HelloWorld.java reside. Depois de fazer isso, digite:

javac HelloWorld.java

class HelloWorld {

public static void main (String [] commandLineArgs) {


System.out.println ("Olá, mundo!"); }

Listagem 3.1 O exemplo clássico do HelloWorld.


Página 38

Depois que seu disco rígido cuspir por um ou dois segundos, você verá o prompt do DOS novamente. O compilador Java, Javac, não relatará
nenhum diagnóstico se você tiver uma compilação bem-sucedida. Se você vir algum diagnóstico, significa que cometeu um erro. Eventualmente,
você terá uma compilação bem-sucedida. Para executar o programa, digite o seguinte na mesma janela do DOS:

java HelloWorld

Sem surpresa, você verá:

Olá Mundo!

Nenhuma surpresa aqui, certo? Vamos dedicar um pouco de tempo ao que está acontecendo com a compilação e a execução, seguido por
um exame desse programa tremendamente emocionante e complicado que você acabou de inserir, compilou e executou.

A Figura 3.1 é uma imagem de uma janela do DOS mostrando a execução do compilador Java e o tempo de execução Java.

Observe a escassez de diagnósticos. Lembre-se de que a invocação do compilador Java não tinha opções e o programa apenas emitia uma
string de texto. Vamos enfrentá-lo - não está acontecendo muita coisa aqui, certo?

No entanto, as aparências enganam. O compilador e o interpretador Java ficam extremamente ocupados, mesmo quando compilando e
executando um programa extremamente simples. Você verá isso na próxima seção.

Compilando e executando programas Java: um segundo olhar

O compilador Java, javac, localizou seu arquivo de origem Java HelloWorld.java e verificou sua origem em busca de referências a outras
classes Java. Mesmo um programa contendo nada além de uma assinatura de método exigiria classes Java adicionais para uma compilação
bem-sucedida.

Figura 3.1 Executando o programa HelloWorld Java.


Página 39

De qualquer forma, o compilador Java coleta referências de classe e disponibiliza as classes referenciadas para seu programa.

A Listagem 3.2 é uma listagem do compilador que mostra o javac carregando as classes necessárias para uma compilação bem-sucedida. A
invocação do compilador Java está em negrito. Observe o uso do - prolixo opção de gerar a lista de classes carregadas durante a compilação (sim,
o traço inicial é necessário).

Uma vez que o javac encontra e carrega as classes que você referenciou em seu código-fonte, o javac compila o código-fonte em bytecode. O
compilador Java não se importa com o nome do arquivo de origem; você poderia ter salvo o arquivo como doodah.java e o código seria compilado
com sucesso. No entanto, é aconselhável prestar muita atenção (muito perto!) À capitalização porque a maioria das ferramentas Java presta
muita atenção (muito perto!) E, como um programador, você deve estar realmente preocupado com o comportamento da linguagem e das
ferramentas. Vindo do mundo de mainframe que não diferencia maiúsculas de minúsculas da programação COBOL, você está avisado!

A execução do compilador Java sem opções não produzirá nenhum diagnóstico quando a compilação for bem-sucedida. Esta é uma grande
mudança em relação ao mundo dos compiladores de mainframe! No entanto, quem no grande mundo do ferro roda compiladores sem opções?
Aqui, usamos o - prolixo opção para ver o javac carregando as classes. No Capítulo 4, você verá o resto do

Javac -verbose helloworld.java

[analisou HelloWorld.java em 660 ms] [carregado C: \


JDK1.2.2 \ JRE \ lib \ rt.jar
(java / lang / Object.class) em 50 ms] [verificação de classe
HelloWorld]
[carregado C: \ JDK1.2.2 \ JRE \ lib \ rt.jar
(java / lang / String.class) em 60 ms] [carregado C: \ JDK1.2.2
\ JRE \ lib \ rt.jar
(java / io / Serializable.class) em 0
em]
[carregado C: \ JDK1.2.2 \ JRE \ lib \ rt.jar
(java / lang / Comparable.class) em 0
em]
[carregado C: \ JDK1.2.2 \ JRE \ lib \ rt.jar
(java / lang / System.class) em 0 ms] [carregado C: \ JDK1.2.2
\ JRE \ lib \ rt.jar
(java / io / PrintStream.class) em 60
em]
[carregado C: \ JDK1.2.2 \ JRE \ lib \ rt.jar
(java / io / FilterOutputStream.class)
em 0 ms]
[carregado C: \ JDK1.2.2 \ JRE \ lib \ rt.jar
(java / io / OutputStream.class) em 50
em]
[carregado C: \ JDK1.2.2 \ JRE \ lib \ rt.jar
(java / io / IOException.class) em 0 ms] [carregado C: \ JDK1.2.2 \
JRE \ lib \ rt.jar
(java / lang / Exception.class) em 0 ms] [carregado C: \ JDK1.2.2 \
JRE \ lib \ rt.jar
(java / lang / Throwable.class) em 0 ms]
[escreveu C: \ Lou's Folder \ Writings \ Java for MF DPers Book Folder \ Book Chapters \ Capítulo 3 Seu primeiro
programa Java \ HelloWorld.class] [feito em 1480 ms]

Listagem 3.2 Compilando HelloWorld.java com a opção -verbose.


Página 40

as opções que você pode usar com o compilador Java no Sun JDK. Na prática, você usará algumas opções ao compilar
seus programas Java.

Sua fonte e classes incluídas vêm no compilador Java; uma arquivo de classe sai do compilador Java. O arquivo de classe é composto pelo
bytecode de plataforma neutra sobre o qual você tanto ouviu falar. Este arquivo de classe pode ser executado em qualquer máquina que tenha um
Java runtime instalado. O arquivo de classe residirá no mesmo diretório do código-fonte Java, a menos que você diga ao javac o contrário usando
uma opção do compilador.

Ao passar esse arquivo de classe para o interpretador Java (ou Java runtime, se preferir), você está executando seu programa Java. O Java
runtime procura por todas as classes solicitadas por seu programa e as carrega dinamicamente na memória do sistema. A Listagem 3.3 é uma
lista abreviada das classes de carregamento de tempo de execução Java da biblioteca de tempo de execução Java durante a execução de
HelloWorld.class.

Parece que o Java runtime está extremamente ocupado, mesmo para um programa simples! Observe o negrito Olá Mundo! incluído
na Listagem 3.3. Essa é a saída real do programa misturada com os diagnósticos.

Lembre-se de que Java é interpretativo. Você não vincula programas Java para produzir código executável nativo. (No entanto, algumas empresas,
a IBM por exemplo, oferecem um compilador Java que produz código OS / 390 nativo.) Após uma compilação bem-sucedida, você está pronto
para carregar e começar.

A Figura 3.2 mostra o processo em fotos.

Figura 3.2 Compilando e executando um programa Java.


Página 41

java -verbose HelloWorld

[C: \ JDK1.2.2 \ JRE \ lib \ rt.jar aberto em 50 ms] [C: \ JDK1.2.2 \ JRE \ lib \
i18n.jar aberto em 0 ms] [Carregado java.lang.NoClassDefFoundError de

C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]


[Carregado java.lang.Class de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar] [Carregado java.lang.Object de C: \ JDK1.2.2 \
JRE \ lib \ rt.jar] [Carregado java. lang.Throwable de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar] [Carregado
java.io.Serializable de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar] [Carregado java.lang.String de C: \ JDK1.2.2 \ JRE \ lib \
rt.jar] [Carregado java.lang.Comparable de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar] [Carregado
java.io.ObjectStreamClass de C: \ JDK1 .2.2 \ JRE \ lib \ rt.jar]

[Carregado java.io.ObjectStreamClass $ ObjectStreamClassEntry de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado java.lang.ref.SoftReference de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado java.lang.ref.Reference de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado java.io.ObjectStreamField de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado java.io.ObjectStreamClass $ CompareClassByName de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

(Outras 100 ou mais classes são carregadas)

[Carregado sun.net.www.URLConnection de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado java.net.URLConnection de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Loaded java.net.UnknownConten Olá Mundo!


tHandler de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]
[Carregado java.net.ContentHandler de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado sun.net.www.MessageHeader de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado java.io.FilePermission de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado java.io.Filepermission $ 1 de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado java.lang.RuntimePermission de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado java.lang.SecurityException de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

[Carregado java.security.cert.Certificate de
C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]
[Carregado [Ljava.security.cert.Certificate;]
[HelloWorld carregado]
[Carregado java.lang.ref.Finalizer $ 3 de C: \ JDK1.2.2 \ JRE \ lib \ rt.jar]

Listagem 3.3 Executando HelloWorld.class com a opção -verbose.

Seu código-fonte Java, localizado em HelloWorld.java, é passado para o compilador Java (o programa javac). Javac inclui classes
necessárias durante a compilação e produz bytecode conhecido em seu sistema como
helloworld.class. Isso conclui o processo de compilação. Para executar seu programa, passe o arquivo de classe para o tempo de execução Java (o
programa Java). o

Y
FL
A M
TE

Team-Fly ®
Página 42

O Java runtime exigirá classes adicionais para executar com sucesso o seu programa. Essas classes necessárias são carregadas
dinamicamente durante a execução do programa. Depois que todas as classes necessárias forem aplicadas, seu programa será
executado com êxito. Aqui, o programa produz a frase gasta, Olá Mundo!

Observe que você deve observar a distinção entre maiúsculas e minúsculas ao invocar seu programa Java. O nome da classe codificado em sua fonte
deve corresponder ao argumento passado para o programa Java Runtime. Continue lendo para mais detalhes.

Cuidado com esses nomes de classe.

É aqui que a sensibilidade ao caso entra em jogo, em grande. Você deseja que o nome e o caso do arquivo de classe produzido pelo
compilador sejam iguais ao nome da classe em seu código-fonte Java. A Figura 3.3 mostra uma captura de tela de compilação e
execução em que esse conselho sensato não é atendido.

Que bagunça! O texto preto é o que nosso "ignorante de conselhos" digitou. Observe que depois que essa pessoa entrou

javac helloworld.java

O DOS volta com um prompt e nenhum diagnóstico; a compilação foi bem-sucedida. No entanto, quando isso é inserido na janela do
DOS:

java helloworld

Figura 3.3 Não ouvir bons conselhos.


Página 43

o interpretador Java tem uma ou duas coisas a dizer. Por enquanto, podemos nos concentrar na primeira linha do diagnóstico:

Exceção no thread "main" java.lang.NoClassDefFoundError: helloworld (nome errado: HelloWorld)

Não é preciso ser um cientista para deduzir que o Java runtime (ou interpretador Java, se preferir) relatou uma exceção
chamada NoClassDefFoundError e a definição de classe que não foi encontrada é helloworld. O Java runtime até relatou
a classe que deveria ser usada!

O resto dos diagnósticos despejados na janela do DOS mostram a localização (número da linha) de várias classes Java que você nem
percebeu que estavam sendo usadas em seu programa de três linhas. Acredite ou não, depois de ler a Seção I, você entenderá o que
significa essa sequência de diagnósticos.

Qual é a aparência de um erro de compilação?

Boa pergunta. Encare isso - você deseja cometer erros de compilação; quanto mais erros de compilação você comete, mais Java você está
fazendo, certo? De qualquer forma, vamos mudar sua obra de arte acima omitindo a citação de fechamento. Aqui está a linha ofensiva da fonte
Java:

System.out.println ("Olá, mundo!);

E a saída do compilador resultante:

helloworld.java:5: String não terminada no final da linha.


System.out.println ("Olá, mundo!);
^

helloworld.java:6: ')' esperado.


}
^

2 erros

Nada mal, certo? Parece que o javac é capaz de resolver o problema com boa precisão. É claro que os compiladores têm o péssimo hábito de
espalhar erros de compilação em cascata. É por isso que essa compilação defeituosa mostra dois erros de compilação onde, na verdade, existe
apenas um erro verdadeiro.

Observe que o compilador relata as linhas de origem do problema por número. Retroceder uma ou duas páginas revela que o compilador Java conta
as linhas em branco. Portanto, a linha 5 não é a quinta linha do código-fonte Java; a linha 5 é a quinta linha no arquivo de origem.

Vamos dar uma olhada no programa HelloWorld

Agora que você viu o que esse programa gigantesco faz, vamos examiná-lo linha por linha. O programa é repetido na Listagem 3.4 com
comentários como números de linha para sua conveniência.
Página 44

/ * 1 * / class HelloWorld {/ * 2 * /

/*3*/ public static void main (String [] commandLineArgs) {


/*4*/ System.out.println ("Olá, mundo!");
/*5*/ }
/*6*/
/ * 7 * /}

Listagem 3.4 O clássico HelloWorld, mais uma vez.

Observe que o Java pode usar comentários no estilo PL / I. Java oferece suporte a outros estilos de comentário, sobre os quais você lerá no
Capítulo 6.

Cada programa Java consiste em um grupo de classes cooperantes. Você pode criar (ou codificar, se preferir) várias classes por conjunto de
dados, mas vamos nos limitar a criar uma classe por conjunto de dados por enquanto. A linha 1 mostra o nome da classe. Além disso, a linha 1
exibe uma construção de bloco de abertura, mais conhecida por um
chaveta; a construção do bloco de fechamento está na linha 7. Um bloco define um escopo onde as entidades declaradas são conhecidas. Em outras
palavras, as entidades declaradas entre a construção de bloco do Java podem ser referenciadas e alteradas em qualquer linha entre as chaves.
Nesse sentido, a construção de bloco de Java é um tanto semelhante em função às construções de bloco de PL / I Begin / End e Proc / End.

A linha 2 é um comentário. Os comentários podem ocupar mais de uma linha. Como mencionado anteriormente, o Capítulo 6 apresenta todos os
detalhes sobre os comentários Java.

Um desvio rápido para cabeçalhos de método

A linha 3 é uma declaração do a Principal método. Na verdade, esse método é especial porque é o método principal da classe. Para
aqueles que estão familiarizados com PL / I, pense em "Proc Options (Main)."

Todo programa Java possui um método principal conforme declarado anteriormente. Ou seja, você precisa das palavras vazio estático
público main (String [] aLegalJavaVariableName) para denotar este método principal uma vez em um aplicativo. A parte em negrito,
contendo o nome do método e a descrição do argumento, é chamada de assinatura. Para o método principal, as palavras em itálico são
necessárias. A palavra "aLegalJavaVariableName" pode ser qualquer nome de variável legal; em nosso exemplo, usamos
commandLineArgs.

A Figura 3.4 mostra uma visão mais detalhada do cabeçalho do método usado aqui.

Indo da esquerda para a direita, aqui estão algumas palavras que descrevem os componentes do cabeçalho acima.

Público é chamado de modificador de visibilidade. Em Java, você pode controlar quais classes / métodos acessam outras classes,
objetos, métodos e variáveis. A palavra-chave public torna o método acessível a qualquer classe em seu aplicativo. O método principal
deve ser declarado público.

o estático palavra-chave denota a existência de um método de classe. A visão de mundo orientada a objetos é que você cria objetos a partir de
classes (a palavra de dez dólares é instanciação, ou você
Página 45

Figura 3.4 A anatomia de um cabeçalho de método.

instanciar um objeto de uma classe, ou um objeto é um instância de uma classe). As classes servem como modelo para objetos semelhantes.
No entanto, às vezes você precisa de um método, mas não quer que o método venha de um objeto de instância. O método principal é esse
método. Você pode ter apenas um método principal e este método não é chamado de nenhuma instância de uma classe. Em outras palavras,
existe apenas uma versão de um método de classe por classe.

Os métodos podem retornar uma variável, um objeto ou nada. Pense em uma função PL / I com uma instrução RETURN e uma
instrução PROC listando o tipo de dados retornado. o vazio palavra-chave significa que o método não retorna uma variável ou
objeto. Se um método retornar algo, é aqui que você codificaria o tipo de dados ou o nome da classe correspondente ao tipo ou
classe da entidade retornada. O método também conteria um ou mais Retorna afirmações.

Os métodos devem ter um nome, portanto, certifique-se de dar um a eles.

Métodos, como funções e sub-rotinas, podem aceitar argumentos. Pense na LINKAGE SECTION em COBOL, onde você definiria a ordem
e o tipo de argumentos passados para as sub-rotinas. Java requer que você codifique o tipo de dados ou nome de classe seguido por
um nome de argumento para cada argumento passado para o método. O método principal requer uma lista de parâmetros de um matriz de
strings. Em breve, você mudará o programa HelloWorld para aceitar e usar parâmetros. Java impõe consistência de tipo; o tipo de dados
ou nome da classe usado na instrução que evoca o método (chama o método, por assim dizer) deve concordar com os tipos de dados e
classes codificados na assinatura do método.

Você também deve fornecer nomes de variáveis para seus argumentos. Esses nomes não são significativos além das convenções de
nomenclatura Java aceitas. Eles são usados apenas no corpo do método.

Esta não é a história completa dos cabeçalhos de método. Para manter as coisas simples, algumas palavras-chave opcionais são omitidas.
Lembre-se de que o objetivo deste capítulo é colocar um programa Java simples em execução e descrever o que está acontecendo com o
compilador Java e o tempo de execução. Você terá mais para ler sobre assinaturas e convenções de nomenclatura nos próximos capítulos.
Página 46

Voltar para nosso programa

Observe a presença de uma chave de abertura no final da linha 3. Todo método requer um par de chaves, abertas e fechadas, para
servir como delimitadores de código de método. A linha 5 contém a chave de fechamento correspondente à chave de abertura na linha 3.
Tudo o que está imprensado entre a chave de abertura e de fechamento é chamado de corpo do método. Como você verá mais tarde,
você codificará pares de chaves em métodos para várias construções de linguagem de programação.

A linha 4 invoca o método System.out.println para gravar a saída no console ou no fluxo de saída padrão. A classe System
possui vários objetos e métodos; o método println bombeia a saída para o fluxo de saída padrão. O método
System.out.println faz o que o verbo DISPLAY em COBOL e a instrução PUT SKIP LIST em PL / I fazem. Essa linha é todo o
corpo do método principal.

Uma rápida recapitulação: todo programa Java requer pelo menos uma classe e um e apenas um método principal em uma e somente uma classe.
Chaves encaracoladas delimitam blocos de código.

Vamos mudar o programa

Vamos mudar o infame programa HelloWorld permitindo que o programa aceite um parâmetro e grave esse parâmetro no fluxo
de saída padrão. A Listagem 3.5 mostra essa mudança.

Depois de uma compilação bem-sucedida, veja como você executaria o programa:

java HelloWorld Lou

Aqui está sua saída:

Ola lou

O que está acontecendo aqui, você pergunta? Passamos um valor de parâmetro, Lou, para o método principal na linha de comando. A
linha alterada cria uma string de saída ao concatenar a palavra "Hello" com o primeiro argumento passado para o método. Java usa + como

class HelloWorld {

public static void main (String [] commandLineArgs) {


/ * Alterado * /
System.out.println ("Hello" + commandLineArgs [0]);
}
}

Listagem 3.5 O clássico HelloWorld com uma torção.


Página 47

o operador de concatenação de string. Observe que o Java faz referência a matrizes da posição 0. Em outras palavras, dizemos que o Java usa matrizes
baseadas em zero. Isso contrasta com as linguagens de programação de mainframe que usam como padrão arrays baseados em um.

Em suma.

Você instalou um JDK em sua máquina. Você compilou e executou alguns programas Java simples. Você leu um pouco sobre como o
Java carrega classes dinamicamente durante a compilação e execução. Você viu alguns erros de compilação e execução. Você também
viu uma assinatura de método de perto, mas em breve. Você desmontou e alterou um programa Java. No geral, você tem estado muito
ocupado! No próximo capítulo, você lerá sobre o JDK que acabou de usar.
Página 48

Esta página foi intencionalmente deixada em branco.


Página 49

CAPÍTULO 4
Ferramentas JDK básicas do Sun Java 2

Este capítulo descreve os programas ou ferramentas incluídos no Sun Java Development Kit (JDK). Alguns programas, como o
compilador Java (javac.exe) e o interpretador Java (java.exe), você já viu. Alguns programas, como os programas de segurança
(keytool.exe, por exemplo) e de invocação remota (rmic.exe), você verá em capítulos posteriores do livro. Alguns programas, como
idltojava.exe, não estão incluídos na distribuição Sun JDK.

A Sun classifica as ferramentas como Ferramentas básicas, ferramentas de invocação de método remoto, ferramentas de internacionalização,
ferramentas de segurança, e Ferramentas Java IDL. Você lerá sobre as ferramentas básicas neste capítulo. Você lerá mais sobre ferramentas
específicas em capítulos posteriores.

Ferramentas JDK básicas

Verdade seja dita, você provavelmente acabará usando ferramentas que têm mais força do que as ferramentas Sun JDK. A multidão profissional de
Java exige ferramentas com interfaces gráficas, criadores de tela e recursos de modelagem; quando você se juntar à multidão, você não será exceção.
No entanto, como programador de mainframe, você pode estar acostumado a enviar compilações em lote e não ter nenhuma ferramenta para gerar
documentação. As ferramentas JDK permitem compilações interativas, depuração e geração de documentação, entre outras coisas: um passo à frente
da execução de trabalhos em lote, com certeza! Além disso, essas ferramentas são grátis, grátis, e por conta da casa. Além disso, o Sun JDK sempre
funciona com a versão mais recente do JDK. Portanto, não descarte essas ferramentas imediatamente.
Página 50

Você usará essas ferramentas na Tabela 4.1 para codificar, executar e depurar aplicativos e miniaplicativos. Aqui estão mais

informações sobre as ferramentas básicas do JDK.

Appletviewer

A ferramenta appletviewer permite que você visualize miniaplicativos sem executar um navegador. Você especifica uma ou mais opções
seguidas por um ou mais URLs. Os URLs devem fazer referência a uma página da Web que contém uma referência ao miniaplicativo; se não,
o appletviewer emite um diagnóstico para esse efeito. Observe que o appletviewer não exibe a página da web. A ferramenta appletviewer usa
as tags APPLET, EMBED ou OBJECT na página para obter informações de parâmetro e tamanho da janela para exibição.

A sintaxe é:

appletviewer <options> URL 1 URL 2 ... URL n

Se você esquecer a sintaxe, basta inserir appletviewer no prompt de comando. Appletviewer responderá da seguinte forma:

Nenhum arquivo de entrada especificado.


uso: appletviewer [-debug] [-J <javaflag>] [-encoding <tipo de codificação de caracteres>] url | arquivo ...

A Tabela 4.2 lista as opções que você pode passar para a ferramenta appletviewer. Todas essas opções são maiúsculas e Minúsculas. Se
você quiser ver o appletviewer em ação, abra esta pasta em sua área de trabalho.

Jdk1.3 \ demo \ applets \ moleculeviewer

Tabela 4.1 As descrições de uma linha

aAppletviewer Ver miniaplicativos em uma janela semelhante a um navegador (mas não em um navegador)

jarra Combina vários arquivos em um Arquivo Java ( Arquivo JAR) Verifique se um arquivo Java (JAR)

extcheck está em conflito com outros arquivos JAR. Execute aplicativos Java (mas não miniaplicativos)

Java

Javac Compilar aplicativos e miniaplicativos Java Gerar

Javadoc documentação Java

javah Gerar arquivos de cabeçalho C para uso na escrita de métodos de código

Javap nativo.

jdb Depurar programas e miniaplicativos Java


Página 51

Tabela 4.2 Opções do Appletviewer

- depurar Comece o Depurador Java ( jdb). Posteriormente, você lerá


mais sobre jdb.

- J <somestring> Passe <somestring> para o Java runtime. Use esta opção apenas se
você realmente souber o que está fazendo, pois essas sequências
afetam o Java Runtime Environment.

- codificação <codificação O esquema de codificação dos URLs passados como argumentos


Nome para o appletviewer.

Esta pasta contém alguns miniaplicativos de amostra e foi copiada em seu disco quando você instalou o JDK. Assim que esta pasta estiver
aberta, abra uma janela do DOS. Você deve estar no diretório correspondente à pasta anterior. Caso contrário, altere o diretório para
corresponder a este caminho. Digite este comando no prompt:

appletviewer example1.html

Você verá uma janela do DOS parecida com a mostrada na Figura 4.1.

Example1.html tem uma referência ao miniaplicativo na forma de uma tag <applet>. A Listagem 4.1 mostra a tag <applet> codificada em
Example1.html.

Observe que o appletviewer tem um menu. O menu do miniaplicativo inclui opções para recarregar o miniaplicativo, para chamar os métodos Start () e
Stop () (que são métodos especiais em miniaplicativos) e para mostrar informações sobre a marca HTML que faz referência ao miniaplicativo.
Y
FL

jarra
A M

Você usa o utilitário jar para combinar um ou mais arquivos de classe Java com outros arquivos que seus arquivos de classe fazem referência
TE

(como imagens e sons) em um arquivo chamado de Arquivo Java Archive. A ferramenta jar também compacta os arquivos.

Um uso comum para arquivos jar é empacotar arquivos de classe representando applets com outros recursos em um arquivo. Um miniaplicativo
empacotado dessa maneira pode ser baixado para um navegador em uma única solicitação, em vez de fazer o navegador ir e voltar para pegar
cada arquivo. Além disso, como os arquivos jar são compactados, eles baixam mais rápido do que seus irmãos não compactados.

Arquivos Jar contêm um manifesto arquivo, que contém informações sobre os arquivos no arquivo. A ferramenta jar cria automaticamente um arquivo
de manifesto durante o empacotamento. Opcionalmente, você pode usar o javakey ferramenta para fornecer uma assinatura digital, garantindo assim
que os arquivos contidos no arquivo não sejam adulterados desde a sua criação.

Esta é uma visão geral da sintaxe para jar:

jar <options> <manifest-file> jar-file-name <input-files>

A ferramenta jar contém dez opções que podem ser combinadas de várias maneiras. Aqui estão alguns exemplos de como o jar é comumente
usado.

Team-Fly ®
Página 52

Figura 4.1 Executando um applet com appletviewer.

<applet code = XYZApp.class width = 300 height = 300>


<param name = model value = models / HyaluronicAcid.xyz>
alt = "Seu navegador entende a tag & lt; APPLET & gt; mas não é
executando o miniaplicativo, por algum motivo. "
Seu navegador está ignorando completamente o & lt; APPLET & gt; tag! </applet>

Listagem 4.1 Tag de miniaplicativo de exemplo.

Esta é talvez a maneira mais comum de usar o jar:

jar cf aJarFile * .class


Página 53

Este comando cria um arquivo chamado aJarFile.jar a partir de todos os arquivos no diretório atual com a extensão "class". o c opção
direciona jar para criar um novo arquivo; a f opção diz ao jar o nome do novo arquivo.

jar cria um arquivo de manifesto denominado META-INF / MANIFEST.MF e inclui esse arquivo como o primeiro no archive.

Se você quiser ver quais arquivos estão contidos em seu arquivo, insira esta variação do comando jar:

jar tf aJarFile.jar

o t opção diz ao jar para listar os nomes dos arquivos no arquivo; a f opção diz ao jar o nome do arquivo no qual operar.

Se você deseja adicionar um arquivo a um arquivo existente, codifique:

jar uf aJarFile.jar another.class

o você opção diz ao jar para atualizar um arquivo jar; a f a opção nomeia o arquivo jar a ser atualizado. Se você deseja extrair os arquivos que
constituem um jar, codifique:

jar xf aJarFile.jar

Como o jar usa o formato de compactação ZIP, você pode usar qualquer ferramenta que opere em arquivos ZIP para extrair arquivos de um
arquivo jar.

Entrando jarra sem parâmetros produz as informações úteis na Listagem 4.2.

Um ponto final: as opções que você usa com o jar são maiúsculas e Minúsculas. Observe que –m e –M são opções diferentes.

extcheck

O utilitário extcheck verifica os arquivos jar de extensão em busca de conflitos com os arquivos jar atualmente instalados. Arquivos jar de extensão
são armazenados em seu jre \ lib \ ext diretório. O utilitário extcheck é uma boa maneira de garantir que você não substitua uma versão de algum
arquivo jar necessário por uma versão mais antiga (sim, sabe-se que isso aconteceu!).

Extcheck usa as informações no arquivo de manifesto do jar para a comparação. Extcheck compara as informações de título e versão encontradas no
manifesto com os arquivos de manifesto de todos os jars encontrados no diretório de extensão. Se extcheck encontrar qualquer versão anterior (mais
recente) do jar, ele emitirá um código de retorno diferente de zero; caso contrário, extcheck retorna zero.

Esta é a sintaxe (é o que você veria se inserisse extcheck sem parâmetros):

extcheck <-verbose> aJarFile.jar

A única opção usada com extcheck é a opção detalhada. Verbose lista os arquivos jar encontrados no diretório de extensão verificado em relação ao
argumento do arquivo jar para extcheck. Verbose também relatará sobre os jars de extensão encontrados em conflito com o argumento do arquivo
jar para extcheck.
Página 54

Uso: jar {ctxu} [vfm0M] [jar-file] [manifest-file] [-C dir] files


...
Opções:
-c criar novo arquivo
-t listar índice para extrato de arquivo nomeado (ou todos) os arquivos do
-x arquivo atualizar o arquivo existente
- você
-v gerar saída detalhada na saída padrão especificar o nome do arquivo
-f
-m incluir informações de manifesto apenas do armazenamento de arquivo de manifesto especificado; não
-o use compressão ZIP
-M Não crie um arquivo de manifesto para as entradas
-C mude para o diretório especificado e inclua o seguinte
Arquivo
arquivo é um diretório, então é processado recursivamente.
Caso existam
O nome do arquivo de manifesto e o nome do arquivo compactado precisam ser especificados na mesma ordem em que os
sinalizadores 'm' e 'f' são especificados.

Exemplo 1: para arquivar dois arquivos de classe em um arquivo chamado classes.jar:

jar cvf classes.jar Foo.class Bar.class


Exemplo 2: use um arquivo de manifesto existente 'mymanifest' e arquive todos os arquivos no diretório foo / em 'classes.jar':

jar cvfm classes.jar mymanifest -C foo /.

Listagem 4.2 ajuda do comando jar.

Java

Você já usou java, o Java runtime ou o interpretador Java. Você usa o java para executar ou lançar aplicativos a partir de uma linha
de comando. A Listagem 4.3 é o que você veria inserindo Java na linha de comando.

Você pode executar um aplicativo especificando o nome de um arquivo de classe que contém um método main ()
(JAVA.EXE [-options] class [args...]) ou especificando um arquivo jar contendo uma classe com um método main
() ( JAVA.EXE -jar [-options] jarfile [args. . . ]).
Até agora, você usou a primeira forma de sintaxe para invocar o interpretador Java.

Você codifica sinalizadores de opção entre o comando java e o nome da classe ou após o sinalizador -jar. Os args codificados após o nome da
classe ou o nome do arquivo jar são strings passadas para o método main (). (Lembre-se de main (String [] commandLineArgs) do capítulo
anterior?) Estas opções são maiúsculas e Minúsculas. Vamos dar uma olhada nessas opções.

O - cp ou - classpath opções permitem que você especifique um caminho de busca para escrita pelo usuário classes (em oposição às
classes do sistema) ou outros arquivos exigidos por seu aplicativo. Recordar de
Página 55

Uso: JAVA.EXE [-options] class [args. . . ]


(para executar uma aula)
ou JAVA.EXE -jar [-options] jarfile [args. . . ]
(para executar um arquivo jar)

Onde as opções incluem:


- c p -classpath <diretórios e arquivos zip / jar separados por;>
definir o caminho de pesquisa para classes e recursos de aplicativos
- D <nome> = <valor>
definir uma propriedade do sistema para
- detalhado [: class | gc | jni]
permitir a saída detalhada
- versão versão do produto para impressão
-? -ajuda imprimir esta mensagem de ajuda
-X imprimir ajuda em opções fora do padrão

Listagem 4.3 ajuda do comando java.

No capítulo anterior, que o interpretador Java carrega classes dinamicamente, conforme necessário para a execução adequada de seu aplicativo.
Java.exe sabe onde buscar essas classes de sistema. Se o seu aplicativo requer classes que você ou outra pessoa escreveu, pode ser necessário
informar ao java.exe onde encontrar as classes. Se você colocar essas classes em um diretório que não está no caminho de pesquisa, será
necessário informar java.exe, o diretório que contém essas classes com a opção -cp. Por exemplo, para dizer ao java.exe que você deseja que o
tempo de execução procure em myclassdir para encontrar classes escritas pelo usuário, invoque o interpretador da seguinte maneira:

java -cp c: \ somedir \ myclassdir MyApp

O - D opção permite definir uma propriedade do sistema. No ambiente wintel, você define as propriedades do sistema atribuindo
uma variável de ambiente a algum valor. O exemplo fornecido na documentação da Sun é como definir a opção
JAVA_COMPILER para desabilitar um compilador (usar o interpretador) ou usar um compilador diferente. Confira:

java -Djava.compiler = adifferentcompiler MyApp

Observe a ausência de um espaço entre -D e a propriedade do sistema; isso não é acidente ou erro de digitação.

O - prolixo opção exibe informações sobre classes carregadas e outros eventos de interesse para um ambiente Java. Você pode
dizer quais informações deseja codificando uma subopção. Aqui estão alguns exemplos.

java -verbose: class MyApp ou


java -verbose MyApp
Página 56

O Java runtime listará informações sobre as classes que estão sendo carregadas dinamicamente. Você deve se lembrar do capítulo
anterior que viu uma lista parcial da opção -verbose repleta de informações de classe carregadas.

java -verbose: gc MyApp

O Java runtime relatará eventos de coleta de lixo. Você deve se lembrar de ter lido um pouco sobre a capacidade do Java de coletar blocos de
memória não utilizados usando um processo encantadoramente chamado coleta de lixo. Se você quiser saber quando seu aplicativo está
levando a lixeira, use esta opção.

java -verbose: jni MyApp

jni significa Interface nativa Java. O jni permite que você crie métodos em linguagens de programação diferentes de Java (C é a
linguagem jni mais comumente usada). A ideia é que, para alguns aplicativos, o desempenho pode ser fundamental. Um aplicativo
Java, que é interpretativo, não pode esperar corresponder à velocidade de uma linguagem compilada (embora a lacuna diminua a
cada nova versão do JDK). Use esta opção se quiser saber quando seu aplicativo está fazendo uma chamada para um programa de
idioma nativo.

O - versão opção informa qual versão do JDK você está usando. Você deve se lembrar de usar esta opção para verificar se a instalação do
JDK estava correta.

O -? ou - Socorro opções exibem as informações de ajuda mostradas na Listagem 4.3.

O - X opção exibe informações de ajuda sobre o fora do padrão Opções de tempo de execução Java. Você pode interpretar
fora do padrão como "não tão usado como padrão" por enquanto. A Listagem 4.4 mostra o que você veria se inserisse este
comando.

java -X
-
Xbootclasspath: <diretórios e arquivos zip / jar separados por;>
definir o caminho de pesquisa para classes e recursos de bootstrap

- Xnoclassgc desativar a coleta de lixo da classe definir o tamanho de


- Xms <tamanho> heap Java inicial definir o tamanho de heap Java máximo
- Xmx <tamanho> reduzir o uso de sinais do sistema operacional
- Xrs
-
Xcheck: jni realizar verificações adicionais para funções JNI
- Xrunhprof [: ajuda] | [: <opção> = <valor>,. . . ]
realizar heap, cpu ou monitor de perfil
- Xdebug habilitar depuração remota
-
Xfuture permitir verificações mais rígidas, antecipando o futuro
padrão
As opções -x não são
padrão e sujeito a alterações sem aviso prévio.

Listagem 4.4 Tela de ajuda para java -X (opções não padronizadas).


Página 57

Aqui estão algumas notas sobre essas opções raramente usadas.

O - Xbootclasspath opção é semelhante à opção -cp, exceto que -Xbootclasspath permite que você defina o caminho de pesquisa para classes
do sistema.

Se uma classe não estiver mais sendo usada, o Máquina Virtual JAVA ( JVM) coletará sua memória. Se seu programa criar uma
instância dessa classe descartada, Java ressuscitará a classe. Essa constante purga / ressurreição pode prejudicar o desempenho. Se
você deseja desabilitar a coleta de lixo para as classes, talvez para tentar melhorar o desempenho do aplicativo, você pode desligar a
coleta de lixo da classe com o - Xnoclassgc opção.

- Xrs reduz os sinais enviados ao seu aplicativo do sistema operacional host. Você pode querer diminuir os sinais do sistema operacional
durante o desenvolvimento do aplicativo para simplificar as coisas.

- Xdebug permite a depuração remota - um recurso útil em que você inicia seu aplicativo enquanto está no depurador e emite comandos
de depuração - mesmo se em uma máquina diferente! Você encontrará mais detalhes na seção que descreve o depurador Java (jdb).

O - Xrunhprof opção permite que você veja as informações do perfil do aplicativo. Como a opção -X, o
- A opção Xrunhprof fornece ainda outra lista de subopções. Você verá a tabela de opções ilustrada na Listagem 4.5 ao
inserir:

java -Xrunhprof: help

Por exemplo, se você quiser saber onde em seu programa a CPU está soprando ciclos, você pode receber uma descrição do uso da
CPU chamando o tempo de execução Java com opções como o exemplo mostrado na parte inferior da Listagem 4.5.

Uso de Hprof: -Xrunhprof [: ajuda] | [<opção> = <valor>,. . . ] Nome e valor da opção

Descrição Padrão

----------------------- ----------- -------

heap = dump | sites | todos perfil de heap tudo


cpu = amostras | tempos | antigos utilização do CPU fora
monitor = y | n monitorar contenção n
format = a | b saída ascii ou binária uma
arquivo = <arquivo> gravar dados no arquivo java.hprof
(.txt para
net = <host>: <port> enviar dados por meio de um soquete ascii)
profundidade = <tamanho> profundidade de rastreamento de pilha escrever no arquivo
corte = <valor> ponto de corte de saída 4
lineno = y | n número da linha em rastros? 0,0001
thread = y | n fio em rastros? y
doe = y | n despejar na saída? n
y

Exemplo: java -
Xrunhprof: cpu = samples, file = log.txt, depth = 3 FooClass

Listagem 4.5 valores java hprof.


Página 58

Como você pode ver, o Java runtime certamente tem muitas opções! Este capítulo não é o lugar para explorar essa miríade de opções em
detalhes. Na verdade, você deve ter um pouco de experiência em programação Java endurecida pela batalha antes de se aprofundar em
algumas dessas opções. A boa notícia é que a maioria dos ambientes visuais oferece maneiras fáceis de ativar e visualizar os resultados
dessas opções.

Um último ponto: o Sun JDK contém o programa javaw, que é idêntico ao java, exceto que não abre uma janela de comando.
Se você deseja executar seu aplicativo Java sem ver uma janela de comando, use javaw. Todas as opções para o comando
java funcionam com javaw. Por exemplo, este comando não produz nenhuma saída visível.

javaw -Xrunhprof: help

Claro, se você usasse java em vez de javaw, veria a Listagem 4.5.

Javac

Javac é o compilador Java fornecido pela Sun Microsystems. Como você pode imaginar, o compilador tem muitas opções. A
Listagem 4.6 mostra o que você veria se inserisse javac sozinho na linha de comando.

Uso: javac <options> <source files>

onde <options> inclui:


-g Gerar todas as informações de depuração

- g: nenhum Não gerar nenhuma informação de depuração

- g: {lines, vars, source} Gera apenas algumas informações de depuração


-O Otimize; pode dificultar a depuração ou aumentar os arquivos de classe

- nowarn Gerar sem avisos


- verboso Mensagens de saída sobre o que o compilador está fazendo

- depreciação Locais de origem de saída onde APIs obsoletas são usadas

- classpath <path> Especifique onde encontrar arquivos de classe de usuário


- sourcepath <path> Especifique onde encontrar os arquivos de origem de entrada
- bootclasspath <path> Substituir a localização dos arquivos de classe de bootstrap
- extdirs <dirs> Substituir a localização das extensões instaladas
- d <diretório> Especifique onde colocar os arquivos de classe gerados
- codificação <encoding> Especifique a codificação de caracteres usada pelos arquivos de origem

- alvo <release> Gerar arquivos de classe para uma versão específica da VM

Listagem 4.6 lista de ajuda do javac.


Página 59

O programa javac permite que você compile mais de um arquivo de origem por chamada. Por exemplo, você pode listar seus arquivos de
origem Java um após o outro na linha de comando da seguinte maneira:

javac JavaSrc1.java JavaSrc2.java JavaSrc3.java

Este comando compilaria três arquivos de origem Java e criaria três arquivos de classe no mesmo diretório que os arquivos de
origem.

Se você deseja compilar um número maior de arquivos, pode colocar os nomes dos arquivos do código-fonte Java em um arquivo de texto e direcionar
o compilador Java para compilar todos os arquivos listados neste arquivo de texto. Por exemplo, suponha que o arquivo srcs.txt continha esta
informação:

JavaSrc1.java
JavaSrc2.java
JavaSrc3.java
JavaSrc4.java
JavaSrc5.java
JavaSrc6.java
DiffSrc1.java
DiffSrc2.java
DiffSrc3.java

Você poderia dizer ao javac para compilar todos esses arquivos da seguinte maneira:

javac @ srcs.txt

É o sinal @ que faz a diferença.

O - g opções dizem ao compilador java para colocar informações de depuração em seu bytecode. Para aqueles que estão familiarizados com os
PL / I, esta opção é semelhante aos PL / I's TESTE (TODOS, SYM) opção do compilador. Se você executar o depurador (jdb) sem primeiro buscar
informações de depuração com a opção -g, tudo o que você terá acesso em seu programa é o número da linha e o código-fonte. A opção -g
permite acessar informações de variáveis locais. Você pode coletar informações de depuração específicas codificando o linhas, vars, ou fonte palavras-chave
para adicionar número de linha, variável local ou informações de depuração de código-fonte, respectivamente. Para criar um conjunto de dados de
bytecode enxuto, você pode desativar as informações de depuração com a opção -g: none.

O - O opção executa certas otimizações de código que melhoram o desempenho do seu código às custas do tamanho do código. Por exemplo,
a opção -O pode expandir pequenos métodos dentro de seu arquivo de classe (métodos embutidos), o que resulta em uma execução mais
rápida, mas em um tamanho de arquivo maior. Um efeito colateral de realizar otimizações de código é que o código não é adequado para
depuração porque o código otimizado pode conter métodos embutidos que você não codificou. Em essência, usar a opção -O pode resultar em
um programa diferente (mas com funcionamento idêntico - não se assuste!).

O - nowarn opção suprime avisos do compilador. Se você está extremamente confiante em suas habilidades de programação Java ou está
cansado de obter determinados diagnósticos, esta opção de compilador é para você. Pense em configurar um compilador COBOL para
suprimir mensagens informativas (código de retorno = zero ou quatro) e você entenderá a ideia da opção -nowarn.
Página 60

Use o - prolixo opção de receber informações sobre cada arquivo de classe carregado em seu aplicativo. Você viu a opção
-verbose em ação no capítulo anterior, lembra?

O - depreciação permite que você saiba onde e como seu programa está usando métodos ou classes obsoletas. Em resumo, um
método ou classe obsoleto é aquele que foi usado em versões anteriores do JDK, mas sua função foi assumida por um ou mais
métodos recentes. Freqüentemente, seu aplicativo ainda funcionará com alguns métodos ou classes obsoletas. Dito isso, usar
métodos ou classes obsoletas é a marca do amador. Considerando que o compilador Java pode dizer onde e como esses
métodos e classes obsoletos são usados, você seria negligente em não tentar corrigir a situação.

Então, você deve codificar - depreciação para cada compilação? Bem, na verdade não. Veja, o comportamento padrão do javac é
informar os arquivos de origem que usam métodos ou classes obsoletas. Depois de aprender que seu aplicativo usa tais métodos e
classes, você pode usar a opção -deprecated para aprimorar o uso exato e tomar uma ação corretiva.

O - classpath opção para javac tem a mesma função que a opção -cp ou -classpath para java, o tempo de execução. Caso você tenha esquecido, a
opção -classpath permite que você especifique um caminho de pesquisa para escrita pelo usuário arquivos de classe. Se você não definir a variável
de ambiente CLASSPATH ou não usar a opção - classpath, javac esperará que qualquer uma de suas classes necessárias para esta compilação
residam no diretório atual.

Adivinha o que - sourcepath opção faz? Você não precisa ter três Ph.D. para descobrir que esta opção permite especificar um caminho de pesquisa
para os arquivos de origem. Se você não codificar a opção -sourcepath, javac espera encontrar sua fonte no caminho de pesquisa do arquivo de
classe de usuário. Em outras palavras, usar a opção - classpath sem usar a opção -sourcepath informa ao javac para procurar neste caminho
arquivos de classe de usuário e arquivos de origem Java.

Quando você pensou que já havia lido toda a história sobre caminhos de pesquisa, surge outra opção de definição de caminho: o - bootclasspath opção.
Esta opção permite definir um caminho de pesquisa para classes de sistema Java.

Um ponto importante em relação a todas essas opções de configuração de caminho é que essas opções requerem uma lista delimitada por
ponto-e-vírgula de diretórios, arquivos jar ou arquivos ZIP.

O - extdirs opção é mais uma opção de configuração do caminho de pesquisa. Esta opção permite direcionar o compilador Java para um
diretório (diretórios) contendo arquivos de classe que representam extensões do sistema. No Capítulo 1, você leu um pouco sobre o extcheck
Utilitário. Essas extensões são o que você usaria com extcheck.

O - alvo A opção permite criar bytecode compatível ou incompatível com diferentes versões da JVM. O comportamento
padrão do javac, versão 1.2, é gerar bytecode compatível com a versão 1.1. Por codificação alvo 1.2, você pode direcionar
o javac para gerar bytecode incompatível com a versão 1.1.

As últimas três opções discutidas permitem que você direcione o bytecode Java para uma JVM diferente. A opção -target não é suficiente para
fazer isso. Se você codificar a opção -target sem codificar as opções -bootclassbath e - extdirs, poderá obter uma compilação limpa, mas falhar
durante a execução quando seu bytecode carregar um arquivo de classe de sistema ou usar uma extensão de sistema de outra JVM (padrão).

O - d opção permite colocar a saída do compilador, ou arquivos de classe, em um diretório especificado. Por padrão, o javac coloca a saída
no mesmo diretório do arquivo de origem. A sabedoria aceita no mundo Java é que você deve ter seus arquivos de classe em diretórios
diferentes do seu código-fonte. Então, acostume-se a
codificando esta opção do compilador!

Y
FL
MA
TE

Team-Fly ®
Página 61

Outro bom motivo para codificar -d para suas compilações java é que a opção -d faz com que a estrutura de diretório de seus arquivos de classe
reflita sua estrutura de pacote. Você ainda não leu muito sobre pacotes. Por enquanto, pense em um pacote como um conjunto de classes
relacionadas e outras construções de software Java. Suponha que você tenha criado um pacote com.myPackage.MyClass e você codifica o
comando javac da seguinte maneira:

javac -dc: \ myclassfiles MyClass.java

javac nomeia o arquivo de classe resultante em c: \ myclassfiles \ com \ mypackage \ myclass.class. O recurso interessante e valioso é que
o javac cria todos os subdiretórios necessários. Dito de outra forma, usar a opção -d é uma maneira fácil e inteligente de manter a estrutura
de suas classes.

O - codificação opção permite que você especifique um esquema de codificação. O padrão é qualquer esquema de codificação usado pelo seu
sistema. A menos que seu aplicativo tenha preocupações internacionais, você provavelmente conseguirá ficar sem codificar essa opção por um
longo, longo tempo.

Você se lembra da seção sobre o tempo de execução java com o opções fora do padrão? Bem, o compilador Java também tem
alguns deles. A Listagem 4.7 ilustra o que você vê quando insere este código na linha de comando:

javac -X

Algumas palavras sobre essas opções são necessárias.

Quando você codifica - Xdepend, você está dizendo ao Javac para pesquisar e recompilar o código-fonte Java usado em seu aplicativo. Se
você precisar recompilar a fonte java porque alguma fonte mantida em um arquivo diferente foi alterada, a opção -Xdepend encontrará e
recompilará a fonte.

Normalmente, o javac envia mensagens de erro para qualquer arquivo ou recurso mapeado para System.err. Usando -
Xstdout direciona mensagens de erro javac para System.out, o fluxo de saída padrão. Muitas vezes, System.out e System.err
referem-se ao mesmo fluxo de qualquer maneira.

- Xverbosepath fornece informações sobre onde o javac procura os arquivos de classe e outros arquivos de origem. Quando você usa esta opção, javac
responde listando o caminho de origem e o caminho de classe.

-
Xdepend Pesquise recursivamente por arquivos de origem mais recentes para
recompilar
- Xstdout Envie mensagens para System.out
- Xverbosepath Descreva como caminhos e extensões padrão foram pesquisados

- J <runtime flag> Passar argumento para o interpretador java

As opções -X e -J não são


padrão e sujeito a alterações sem aviso prévio.

Listagem 4.7 Opções do compilador javac fora do padrão.


Página 62

Finalmente, o - J opção permite que você passe uma opção para o tempo de execução java que é chamado quando você executa o
compilador Java. A opção que você passa seria uma das opções descritas na seção "Java" acima. A ferramenta appletviewer também usa
esta opção.

Você pode ver que o compilador java tem várias opções. Felizmente, você pode fazer muitas coisas usando apenas algumas dessas
opções, se houver.

Javadoc

o Javadoc ferramenta gera documentação (sim, documentação!) em formato HTML para um ou mais arquivos de origem Java. Javadoc pode
aceitar nomes de pacotes, arquivos de código-fonte Java ou ambos. Na verdade, o Javadoc invoca parte do compilador Java para analisar
suas declarações de método Java. Como o javadoc usa cabeçalhos de método para gerar documentação, ele nem mesmo requer que os
métodos sejam implementados!

Javadoc irá procurar por todas as classes referenciadas e carregadas, incluindo classes de sistema. Portanto, o javadoc precisa localizar
todas essas classes. Como você pode imaginar, você pode codificar opções para direcionar o javadoc aos diretórios que contêm as
classes e pacotes necessários.

A Listagem 4.8 é um exemplo de parte do código Java passado para a ferramenta javadoc e parte do que o javadoc gerou.

Aqui está o comando.

javadoc queryBean

A Figura 4.2 mostra algumas das saídas HTML.

Agora, isso é muito bom! javadoc extrai todos esses objetos, fornece uma referência cruzada por meio de hiperlinks para descrições
adicionais dos objetos (campos), coloca a parte descritiva em um quadro e coloca um índice no topo da página para métodos depreciados,
visualização em árvore e um índice alfabético . Esta página HTML é uma das nove (!) Páginas, incluindo um Planilha em estilo cascata ( CSS)
usado para formatar as páginas HTML.

Javadoc é um aplicativo robusto com inúmeras opções. O espaço nos impede de dar um tratamento completo. Para se ter uma ideia
das várias opções de javadoc disponíveis, a Listagem 4.9 mostra o que você veria se inserisse este código na linha de comando:

javadoc -help

Embora você possa gerar documentação com javadoc sem usar nenhuma opção, provavelmente usará várias. Você pode usar o - sourcepath opção
para dizer ao javadoc onde residem as classes carregadas. Você provavelmente também incluirá uma tag de autor com - autor opção. Talvez
você queira que o título de sua própria janela apareça em seus documentos HTML usando o - título da janela opção. Talvez você queira um
documento HTML separado para cada entrada alfabética (uma página HTML para UMA, um para D, e assim por diante) usando o - splitindex opção.

Você pode usar comentários especiais com javadoc chamado comentários de documentos para incluir documentação para objetos de software. Um
comentário de documento começa com / ** e termina com * /. Agora, o compilador Java trata os comentários de documentos como qualquer outro
comentário. No entanto, o javadoc seleciona os comentários do documento para processamento adicional.
Página 63

import java.sql. *;
import java.io. *;

public class queryBean extends sqlBean


{
String myCustQuery = "selecione * dos clientes onde CustomerID =";

ResultSet myResultSet = null; public queryBean ()


{super ();}

public boolean getCustomerInfo {String custID} lança exceção


{
String myQuery = myCustQuery + "'" + custID + "'";

Instrução stmt = myConn.createStatement (); myResultSet =


stmt.executeQuery (myQuery);
return (myResultSet! = null); }

public boolean getNextCustInfo () lança exceção


{
return myResultSet.next ();
}

public String getColumn (String inCol) lança exceção


{
return myResultSet.getString (inCol);
}
}
}
Listagem 4.8 Alguma fonte Java.

Você pode usar Tag para dizer ao javadoc para incluir certas informações, como o autor e a versão, e para estabelecer hiperlinks (pense em
"Consulte também <algumURL>"). Por exemplo, este comentário de documento contém algumas tags.

/ ** Esta classe cria filtros passa-baixa que


* imagens suaves.
*
* @author <a href = "http: //loushomepage.html"
* Lou Marco </a>
*
* @ver <a href = "http: //imageenhancementinfo.html"
* Site de informações de aprimoramento de imagem </a>
Página 64

*
* @ver "The Big Image Book"
*/

Observe que o comentário começa com dois asteriscos, mas termina com um - um comentário de documento. A tag @author adiciona uma entrada
de autor à página HTML. Observe o uso de codificação HTML no comentário do documento.

A tag @see exibe o texto "Consulte também" em grande negrito. Quando você usa a tag @see, provavelmente deseja um hiperlink,
então codifique um. A segunda tag @see também exibe "Consulte também" em negrito grande. Esta tag não tem um hiperlink
acompanhante; o texto entre aspas é exibido em "Consulte também".

Hoje, o javadoc oferece suporte a 13 tags. Amanhã, o javadoc provavelmente oferecerá mais suporte. javadoc usa um formato padrão para
suas páginas HTML geradas como o da Figura 4.2, chamado de padrão doclet. Olhando a Listagem 4.9, você vê que um bom pedaço de
opções javadoc estão disponíveis quando você usa o doclet padrão. Você pode criar o seu próprio

Figura 4.2 A página de índice da documentação HTML javadoc.


Página 65

uso: javadoc [options] [packagenames] [sourcefiles] [classnames] [@files]

- visão geral <file> Leia a documentação geral do arquivo HTML


- público Mostrar apenas classes e membros públicos
- protegido Mostrar classes e membros protegidos / públicos (padrão)

- pacote Mostrar classes e membros de pacotes / protegidos / públicos

- privado Mostrar todas as classes e membros


- Socorro Exibir opções de linha de comando
- doclet <class> Gerar saída via doclet alternativo
- docletpath <path> Especifique onde encontrar arquivos de classe doclet
- 1,1 Gerar saída usando doclet de emulação JDK 1.1

- sourcepath <pathlist> Especifique onde encontrar os arquivos fonte


- classpath <pathlist> Especifique onde encontrar arquivos de classe de usuário
- Substituir a localização dos arquivos de classe carregados
bootclasspath <pathlist> pelo carregador de classes bootstrap
- extdirs <dirlist> Substituir a localização das extensões instaladas
- verboso Mensagens de saída sobre o que Javadoc está fazendo
- locale <nome> Local a ser usado, por exemplo, en_US ou en_US_WIN
- codificação <nome> Nome da codificação do arquivo de origem
- J <flag> Passe <flag> diretamente para o sistema de tempo de execução

Fornecido pelo doclet padrão:


- d <diretório> Diretório de destino para arquivos de saída
- usar Criar páginas de uso de classe e pacote
- versão Incluir parágrafos @version
- autor Incluir parágrafos @author
- splitindex Divida o índice em um arquivo por letra
- windowtitle <text> Título da janela do navegador para a documentação
- doctitle <html-code> Incluir título para a página de índice do pacote (primeira)

- cabeçalho <html-code> Incluir texto de cabeçalho para cada página


- rodapé <html-code> Incluir texto de rodapé para cada página
- inferior <html-code> Incluir texto inferior para cada página
- link <url> Crie links para a saída javadoc em <url>
- Link para documentos em <url> usando lista de pacotes em
linkoffline <url> <url2> <url2>
- Agrupe pacotes especificados em
grupo <nome> <p1>: <p2> ..
Visão geral
página
- nodeprecated Não incluir informações @deprecated Não gerar lista obsoleta Não
- nodeprecatedlist gerar hierarquia de classes Não gerar índice
- notree
- noindex
- nenhuma ajuda Não gerar link de ajuda
Página 66

- nonavbar Não gerar barra de navegação Gerar aviso sobre a tag @serial
- serialwarn Charset para visualização em plataforma cruzada da documentação
- charset <charset> gerada.

Listagem 4.9 As opções abundantes para a ferramenta javadoc.

doclets usando o doclet Java Interface do programa de aplicação ( API). Seus doclets não precisam gerar HTML; XML, RTF ou algum outro
formato de documento é um jogo justo! Além disso, seus doclets personalizados podem usar tags personalizadas.

Aqui, nós apenas arranhamos a superfície. Javadoc está constantemente melhorando a cada nova versão do JDK. Você pode descobrir o que
está acontecendo com o javadoc verificando a página do javadoc da Sun em
http://java.sun.com/j2se/1.3/docs/javadoc .

javah.

A ferramenta javah gera cabeçalho C e arquivos de origem que você usa para criar métodos nativos programados em C. Esses métodos nativos
escritos em C podem se comunicar com suas classes Java. Mesmo que você nunca possa codificar nenhum método nativo ou escrever código C,
algumas palavras sobre javah são necessárias.

Javah gera arquivos de cabeçalho (pense em arquivos de cabeçalho como arquivos COBOL COPY ou PL / I% Include) contendo estruturas C que
correspondem às variáveis de instância da classe ou classes fornecidas como parâmetros (pense em estruturas como variáveis de registro
COBOL com vários níveis ) Esses arquivos de cabeçalho são colocados no mesmo diretório em que você executou javah.

A Listagem 4.10 mostra a lista de opções que você pode usar com javah. Como sempre, esses parâmetros diferenciam maiúsculas de minúsculas.

Observe as opções de configuração de caminho comumente usadas pelas ferramentas JDK.

Javap

Use o javap para obter o código-fonte Java de um arquivo de classe. Agora, você não obterá o código exatamente como inserido. Você não obterá
comentários, mas como poucos programadores inserem comentários, você perdeu pouco, certo?

Aqui está um exemplo do que o javap oferece fora da caixa. Consulte a Listagem 4.7 em nossa discussão sobre javadoc da classe Java Vidtype.
Digite este comando enquanto estiver no mesmo diretório do arquivo de classe.

Javap Vidtype

A Figura 4.3 mostra uma captura de tela do que o javap retorna.


Página 67

Uso: javah [opções] <classes> onde [opções] incluem:

- Socorro Imprima esta mensagem de ajuda


- classpath <path> Caminho a partir do qual carregar as classes Caminho a
- partir do qual carregar o bootstrap
bootclasspath <path>
Aulas
- d <dir> Diretório de saída
- o <arquivo> Arquivo de saída (apenas um de -d ou - o pode ser

usava
- jni Gerar JNI-
arquivo de cabeçalho de estilo (padrão)
- velho Gerar arquivo de cabeçalho antigo no estilo JDK1.0 Gerar arquivo
- stubs stubs
- versão Imprimir informações da versão
- verboso Ativar saída detalhada
- força Sempre escreva arquivos de saída

<classes> são especificados com seus nomes totalmente qualificados (por exemplo, java.lang.Object).

Listagem 4.10 opções javah.

Figura 4.3 saída padrão do javap.

Você pode querer comparar o código-fonte Java com o que o javap retorna. Observe a ausência de comentários e valores usados para
inicialização.

Você usa javap como uma maneira rápida de ver quais parâmetros um método requer (chamado de assinatura do método) ou que tipo / classe
de dados um método retorna (se houver). As vezes,
Página 68

inserir javap <classname> é mais rápido do que procurar a assinatura do método no papel ou por outros meios. Dito isso, a maioria dos
ambientes de desenvolvimento integrados ao Java tem informações de assinatura de método a um clique do mouse.

Agora, dê uma olhada no depurador Java (jdb).

jdb

Jdb é um depurador orientado a linha. Se você usou depuradores gráficos como SmartTest para COBOL ou PLITEST para PL / I,
ficará desapontado com o jdb. Dito isso, jdb é uma ferramenta útil para localizar erros em seus programas Java.

O modo básico de operação é invocar jdb para um aplicativo ou applet substituindo jdb por java. Em outras palavras, em vez de invocar o
Java runtime com java.exe, invoque o depurador com jdb.exe. Para depurar miniaplicativos, você precisa de - depurar opção da ferramenta
appletviewer (lembra?). Além disso, você deve usar o - g opção quando você compila para incluir todas as informações de depuração.

Você sabe o que fazer, certo? A Listagem 4.11 mostra o que você verá ao inserir:

Importante! Observe a última linha da lista acima. Quando você está no depurador, pode obter uma lista rápida de todos os comandos jdb. Mais
tarde, faremos exatamente isso.

jdb -help
Uso: jdb <options> <class> <arguments>

onde as opções incluem:


- Socorro imprima esta mensagem e saia imprima a versão de compilação e
- versão saia
- host <hostname> máquina hospedeira do intérprete para anexar à senha do intérprete para anexar
- senha <psswd> (from - debug)

- dbgtrace imprimir informações para depurar jdb

opções encaminhadas para o processo de depuração:


- D <nome> = <valor> define uma propriedade do sistema
- classpath <diretórios separados por ";">
liste os diretórios nos quais procurar classes
- X <opção> opção não padrão de depuração de VM

<class> é o nome da classe para começar a depuração <arguments> são os argumentos


passados para o método main () de <class>

Para obter ajuda do comando digite 'help' no prompt do jdb

Listagem 4.11 opções jdb.


Página 69

Você provavelmente não fará muita depuração remota, mas uma ou duas palavras sobre - hospedeiro e -
senha opções é apropriado. Use essas opções quando estiver depurando uma sessão interpretativa java já iniciada à
distância. Quem iniciou a sessão interpretativa java deve usar o -
Xdebug opção para gerar uma senha. Você usaria essa senha para obter acesso a esta sessão.

Observe a presença do muito familiar - classpath opção. O - X opção permite inserir as mesmas opções não padrão
permitidas para o comando java. Isso faz sentido porque o comando jdb substitui o comando java; portanto, o
comando jdb tem algumas das mesmas opções do comando java.

Sem mais delongas, a Listagem 4.12 lista os comandos de depuração jdb. Para visualizar esta lista, você

deve executar jdb e, em seguida, inserir Socorro no prompt jdb (>).

Usando jdb

Vamos usar jdb para depurar um pequeno programa. Usaremos uma variação da implementação de classificação por bolha mostrada no
Capítulo 1 com uma modificação para causar um erro. A Listagem 4.13 mostra a fonte.

E aqui está o resultado da execução.

3
1
2
6
9
10

Parece que temos um problema!

Vamos executar o programa em jdb. Primeiro, compile o programa com a opção -g para incluir todas as informações de
depuração.

javac -g BubSortMod.java

O que se segue é um exemplo de sessão que mostra alguns dos comandos de depuração com comentários em itálico sobre o que os
comandos de depuração fazem. As entradas humanas estão em negrito; à esquerda do negrito está o prompt jdb. Você não veria
esses comentários durante a depuração. Veja como você ativa o depurador.

jdb BubSortMod

Inicializando jdb ...


0xaa: classe (BubSortMod)

Pare o programa no método de classificação.

> parar em BubSortMod.sort


Ponto de interrupção definido em BubSortMod.sort
Página 70

> ajuda
* * lista de comandos **
tópicos [threadgroup] - - listar tópicos
tópico <id do tópico> - - definir tópico padrão
suspender [id (s) do tópico] - - suspender tópicos (padrão: todos)
retomar [id (s) do tópico] - - retomar tópicos (padrão: todos)
onde [id do tópico] | all wherei [thread id] | - - despeja a pilha de um thread
todos os grupos de discussão -- despejar a pilha de um thread, com informações do pc
- - listar grupos de discussão
threadgroup <nome> -- definir o grupo de discussão atual

imprimir <id> [id (s)] -- imprimir objeto ou campo


despejar <id> [id (s)] -- imprimir todas as informações do objeto

habitantes locais -- imprimir todas as variáveis locais no quadro atual


pilha

Aulas -- lista as classes atualmente conhecidas lista os


métodos <id da classe> -- métodos de uma classe

stop in <class id>. <method> [(argument_type,...)] - definir um ponto de interrupção em


Y

um método
FL

stop at <class id>: <line> - define um breakpoint em uma linha [n frames]


M

- - sobe na pilha de um tópico


A

para baixo [n frames] - - mover para baixo na pilha de um tópico


TE

limpar <id de classe>. <método> [(tipo_de_contigo,...)] - - limpar um


ponto de interrupção

em um método
limpar <class id>: <line> - - limpar um ponto de interrupção em uma linha
degrau - - executa a linha atual
Passo acima --
executar até que o método atual retorne
para seu chamador
stepi - - executa a instrução atual
Próximo - - etapa uma linha (etapa OVER chamadas)
cont - - continuar a execução do ponto de interrupção

pegar <id de classe> - - quebra para a exceção especificada


ignorar <id de classe> - - ignora quando a exceção especificada

lista [número da linha | método] - imprimir o código-fonte, usar [caminho do


arquivo-fonte] - - exibe ou altera o caminho de origem

memória - - relatar o uso de memória

Team-Fly ®
Página 71

gc - - libera objetos não utilizados

carregar nome da classe - - carrega a classe Java a ser depurada


execute <class> [args] -
iniciar a execução de uma classe Java carregada
!! - - repete o último comando
ajuda (ou?) - - lista de comandos
sair (ou sair) - - sair do depurador
>

Listagem 4.12 Comandos de depuração jdb.

class BubSortMod {

public static void main (String args []) {


int anarray [] = {3,10,6,1,2,9};

sort (anarray);
para (int idx = 0; idx <anarray.length; idx ++)
System.out.println (anarray [idx]);

static void sort (int a []) {

para (int idx1 = a.length; --idx1> = 0;) {


boolean swapped = false;
para (int idx2 = 1; idx2 <idx1; idx2 ++) {
if (a [idx2]> a [idx2 + 1]) {
int T = a [idx2];
a [idx2] = a [idx2 + 1];
a [idx + 1] = T;
trocado = verdadeiro;
}

}
if (! swapped) return;

}
}

Listagem 4.13 Uma implementação de classificação de bolha com defeito.


Página 72

Continue a execução até que o programa atinja o primeiro ponto de interrupção.

> correr

execute BubSortMod
corrida ...
principal [1]
Ponto de interrupção atingido: BubSortMod.sort (BubSortMod: 13)

Liste as variáveis locais e os parâmetros do método.

principal [1] habitantes locais


Argumentos do método:
Variáveis locais:
this = BubSortMod @ 49bcdc7d
a = {3, 10, 6, ...}

Observe que jdb não lista todo o array. Observe também que o prompt indica a localização do programa. Determine o
comprimento da matriz.

principal [1] imprimir a.length


a.length = 6

Liste algum código-fonte ao redor da linha onde o jdb interrompeu o programa.

principal [1] Lista


9 }
10
11 static void sort (int a []) {
12
13 => para (int idx1 = a.length; --idx1> = 0;) {
14 boolean swapped = false;
15 para (int idx2 = 1; idx2 <idx1; idx2 ++) {
16 if (a [idx2]> a [idx2 + 1]) {
17 int T = a [idx2];

Execute o programa para o próximo ponto de interrupção.

principal [1] corre


execute BubSortMod
corrida ...
principal [1]
Ponto de interrupção atingido: BubSortMod.sort (BubSortMod: 13)

Execute o programa uma instrução de cada vez.

principal [1] degrau


principal [1]
Ponto de interrupção atingido: BubSortMod.sort (BubSortMod: 14)
Página 73

Dê uma olhada para ver onde estamos no programa.

principal [1] Lista


10
11 static void sort (int a []) {
12
13 para (int idx1 = a.length; --idx1> = 0;) {
14 => boolean swapped = false;
15 para (int idx2 = 1; idx2 <idx1; idx2 ++) {
16 if (a [idx2]> a [idx2 + 1]) {
17 int T = a [idx2];
18 a [idx2] = a [idx2 + 1];
principal [1] etapa
principal [1]
Ponto de interrupção atingido: BubSortMod.sort (BubSortMod: 15) principal [1] etapa

principal [1]
Ponto de interrupção atingido: BubSortMod.sort (BubSortMod: 16)

Liste o valor de uma variável usada aqui.

principal [1] imprimir um [1]


a [1] = 10
principal [1] Lista
12
13 para (int idx1 = a.length; --idx1> = 0;) {
14 boolean swapped = false;
15 para (int idx2 = 1; idx2 <idx1; idx2 ++) {
16 => if (a [idx2]> a [idx2 + 1]) {
17 int T = a [idx2];
18 a [idx2] = a [idx2 + 1];
19 a [idx2 + 1] = T;
20 trocado = verdadeiro;
principal [1] degrau
principal [1]
Ponto de interrupção atingido: BubSortMod.sort (BubSortMod: 17) main [1] corre

execute BubSortMod
corrida ...
principal [1]
Ponto de interrupção atingido: BubSortMod.sort (BubSortMod: 13)

Verifique a cadeia de chamadas ou o rastreamento de pilha.

principal [1] onde 1


[1] BubSortMod.sort (BubSortMod: 13)
[2] BubSortMod.main (BubSortMod: 6) main [1] imprimir idx1

idx1 = 5
Página 74

Execute até que o programa chegue à instrução imediatamente após a invocação do método.

principal [1] Passo acima

principal [1]
Ponto de interrupção atingido: BubSortMod.main (BubSortMod: 7) lista principal [1]

3 public static void main (String args []) {


4 int anarray [] = {3,10,6,1,2,9);
5
6 sort (anarray);
7 => para (int idx = 0; idx <anarray.length; idx ++)
8 System.out.println (anarray [idx]);
9 }
10
11 static void sort (int a []) {

Liste a matriz a ser classificada - estamos perto?

principal [1] imprimir uma matriz


anarray = {3, 1, 2, ...} Liste o último elemento do
array main [1] print anarray [5]

anarray [5] = 10

Observe que jdb é inteligente o suficiente para relatar referências fora dos limites.

main [1] imprimir anarray [6]


6 está fora dos limites para {3, 1, 2, ...}

Continue a execução e pare na instrução imediatamente após a invocação do método.

principal [1] corre


execute BubSortMod
corrida ...
principal [1]
Ponto de interrupção atingido: BubSortMod.sort (BubSortMod: 13) main [1] Passo
acima
principal [1]
Ponto de interrupção atingido: BubSortMod.main (BubSortMod: 7) main [1] imprimir
uma matriz
uma matriz = {3, 1, 2, ...}

Liste os elementos da matriz não mostrados pelo comando de impressão acima.

main [1] imprimir anarray [3] anarray [4] anarray [5] anarray [3] = 6

anarray [4] = 9
anarray [5] = 10
Página 75

Parece que o primeiro elemento da matriz não está "se movendo". O método de classificação está fazendo referência ao primeiro elemento da matriz?

principal [1] corre


execute BubSortMod
corrida ...
principal [1]
Ponto de interrupção atingido: BubSortMod.sort (BubSortMod: 13) main [1] Lista

9 }
10
11 static void sort (int a []) {
12
13 => para (int idx1 = a.length; --idx1> = 0;) {
14 boolean swapped = false;
15 para (int idx2 = 1; idx2 <idx1; idx2 ++) {
16 if (a [idx2]> a [idx2 + 1]) {
17 int T = a [idx2];

Verifique a linha 15. A variável de índice idx2 não deveria começar do zero? Limpe os pontos de interrupção e conclua a execução.

main [1] clear BubSortMod.sort


Ponto de interrupção apagado em BubSortMod.sort main [1]
executado
execute BubSortMod
corrida ...
principal [1] 3
1
2
6
9
10

O tópico atual "principal" morreu. A execução continua. . .


>
BubSortMod saiu

Alterando este programa na linha 15 para:

para (int idx2 = 0; idx2 <idx1; idx2 ++)

corrige o problema.
Página 76

Em suma

Seu rápido tour pelas ferramentas JDK básicas da Sun acabou. Espero que tenham gostado do espetaculo. Parte do motivo pelo qual o Java tem, é
e terá ampla aceitação e sucesso na comunidade de computação é a disponibilidade dessas e de outras ferramentas. Embora alguns possam
reclamar da natureza não visual dessas ferramentas, poucos podem argumentar que elas fazem um bom trabalho para ferramentas gratuitas. Hoje
em dia, se você consegue algo que funciona de graça, do que reclamar?

Sim, quando você entrar no mundo real do desenvolvimento Java, estará usando um conjunto de ferramentas visuais (se não, vá trabalhar para outra
empresa!). Outro ponto que vale a pena mencionar é que as ferramentas Sun JDK funcionam com a versão mais recente do Java. Se quiser verificar
um recurso Java disponível com a versão mais recente, você terá que usar o Sun JDK ou esperar até que a nova versão do Java seja suportada pelo
fornecedor da ferramenta.
Página 77

CAPÍTULO 5
Declaração e definição de dados

Antes de fazer qualquer coisa com um dado em Java, você deve tornar esse dado conhecido para seu programa. Nenhuma surpresa aqui,
declarar dados para uso em um programa é um requisito compartilhado pela maioria das linguagens de programação. Aqui, você lerá como
definir dados em Java e verá como as declarações de dados Java se comparam a declarações de dados semelhantes em COBOL e outras
linguagens de programação IBMmainframe.

Este capítulo descreve como um programador define itens de dados para programas Java. Aqui, as comparações entre Java, COBOL e PL / I
aumentam. Este capítulo passa algum tempo discutindo tipagem de dados de linguagem forte (Java) versus tipagem de dados fraca (REXX)
versus tipagem de dados representativa (COBOL). A distinção do Java entre tipos de dados primitivos e tipos de dados de referência é
abordada, bem como a forma como o conceito de tipos de dados primitivos e de referência são expressos na linguagem do programador de
mainframe. Vários fragmentos de código Java são mostrados aqui para ilustrar esses conceitos.

Tipos primitivos Java

Java oferece suporte ao estilo de programação orientado a objetos. Portanto, você usará objetos em seus programas Java. No entanto, Java também
permite que você declare variáveis que não se comportam como objetos. Essas variáveis se comportam como, bem, variáveis. Nós, povos de Java,
dizemos que essas variáveis Java são declaradas com um
Tipo primitivo. Vamos adiar a discussão sobre objetos para o Capítulo 7, "Representação de classes e objetos", certo?

O que são esses tipos primitivos, afinal? A Tabela 5.1 fornece essas informações.
Página 78

Tabela 5.1 Tipos primitivos Java

PRIMITIVO TIPO PADRÃO TAMANHO FAIXA DE VALORES


TIPO REPRESENTA VALOR TAMANHO
UMA

boleano Verdadeiro falso falso 1 bit verdadeiro falso

byte Inteiro assinado 0 8 bits - 128 10 para 127 10

Caracteres Unicode 0 16 0000 a16FFFF 16


personagem bits
em dobro Minúsculo ou 0,0 64 4.94065645841246544 *
numero gigantesco bits 10- 324 para
1.79769313486231570 *
10 308

flutuador Extremamente 0,0 32 1,40239846 * 10- 45 para


pequeno ou grande bits
3.40282347 * 10 38
número

int Inteiro assinado 0 32 - 2147483648 10 para


bits 2147483647 10

longo Inteiro assinado 0 64 -


bits 9223372036854775808 10
para
9223372036854775807 10

curto Inteiro assinado 0 16 - 32768 10 para 32767 10


bits

Os tipos de dados primitivos Java têm um tamanho fixo, independentemente da plataforma. Por que isso, você pergunta? Lembre-se de que
Java é uma plataforma neutra; o código escrito em uma plataforma deve ser executado com sucesso em outra plataforma, desde que ambas as
plataformas tenham a mesma versão do Java runtime.

Como esses tipos primitivos se comparam aos tipos declarados em COBOL e PL / I? A Tabela 5.2 fornece essas informações.

Agora, COBOL e PL / I declara para tipos primitivos Java não são exatos. Você aprenderá sobre as diferenças ao ler sobre cada
tipo de dado primitivo a seguir. No entanto, você deve primeiro ler um pouco sobre esse negócio de digitação de variáveis em
geral.

Algumas palavras sobre digitação variável

Você deve se lembrar de algumas discussões do Capítulo 2, "O que é Java?", Sobre digitação de variáveis. Em particular, as linguagens de
programação geralmente seguem uma das quatro classificações de tipos de variáveis: Forte, Fraco, Nenhum, e Representante. A classificação de
tipagem governa como variáveis de diferentes tipos podem ser usadas juntas em expressões.

A tipificação forte significa que diferentes variáveis usadas na mesma expressão devem ser do mesmo tipo de dados. Além disso, deve haver uma
correspondência entre os tipos de dados usados como argumentos para um subprograma e os parâmetros desse subprograma. Perceba que o lan-
Página 79

Tabela 5.2 Java, COBOL e declarações de variáveis PL / I de tipos primitivos

PRIMITIVO COBOL
TIPO JAVA DECLARAR DECLARAR PL / I DECLARO
boleano boolean aBool; Não pode ser feito! Não pode ser feito! *

byte byte aByte; ABYTE PIC 'X'. Dcl aByte Bin Fixo (8); Não

Caracteres char aChar; Não pode ser feito! pode ser feito! **

em dobro double aDoub; ADOUB COMP-2 Dcl aDuplo Flutuante (64);

flutuador flutuar flutuando; AFLOAT COMP-1 Dcl aFloat Float (32);

int int anInt; ANINT COMP. Dcl anInt Fixed Bin (31); Não pode

longo muito tempo; Não pode ser feito! ser feito!

curto short aShort; ASHORT COMP. Dcl aShort Fixed Bin (15);

* PL / I usa o tipo BIT para imitar booleanos (Dcl aBit BIT (1)), mas BITs não são booleanos.
* * PL / I suporta um conjunto de caracteres de byte duplo com o atributo GRAPHIC, mas DBCS não é Unicode.

compilador de calibração impõe essa correspondência forçada de tipos de dados. O fato é que é menos provável que erros ocorram em
seu programa quando você deve usar variáveis do mesmo tipo em expressões e chamadas de subprogramas; pelo menos você não
combinará maçãs e laranjas em uma linguagem fortemente tipada. Se você declarar dois tipos de dados como inteiros, não poderá
adicionar variáveis desses tipos sem codificação adicional. A família de linguagens Pascal (Pascal, Modula-2 e Ada) é fortemente tipada.

Uma digitação fraca significa que você pode combinar variáveis de tipos principalmente diferentes em expressões. Entenda que a semântica da
expressão não precisa fazer sentido. Por exemplo, algumas linguagens de programação mal digitadas permitem que você adicione caracteres a
números. Algumas linguagens com tipagem fraca permitem que você coloque rapidamente um pequeno aplicativo em funcionamento sem o
esforço extra envolvido em impor a consistência do tipo de variável; outras são tão difíceis, se não mais, do que algumas linguagens fortemente
tipadas. Vários sabores de BASIC e C são exemplos de linguagens fracamente tipadas.

Nenhum, ou nenhum tipo de variável, ou sem tipo, significa que você não declara variáveis de um tipo. Você apenas "codifica e pronto". Linguagens
de programação não tipadas costumam ser interpretativas, trocando a facilidade de codificação por desempenho de tempo de execução. Essas
linguagens são frequentemente usadas de forma muito eficaz como linguagens de script, porque o sucesso de um script raramente depende do
desempenho do tempo de execução. REXX é um exemplo de uma linguagem de programação sem tipo.

A tipagem representativa significa que as variáveis são declaradas como um tipo que reflete a representação dos dados da máquina. Dois exemplos
frequentemente usados de um tipo de dados representativo são embalado e binário. No mundo mainframe do OS / 390, os dados são armazenados
em formato compactado; algumas linguagens de programação usadas no mainframe permitem variáveis declaradas no formato compactado, idem
para dados binários. COBOL e PL / I são as linguagens de programação usadas com mais frequência que seguem um esquema de digitação de
dados representativo.
Página 80

Fundição de Tipo Variável

Uma pergunta óbvia é evidente: o que você faz se quiser usar duas variáveis numéricas de tipos diferentes na mesma expressão
quando estiver programando em uma linguagem fortemente tipada? Por exemplo, se você tiver uma linguagem hipotética fortemente
tipada que permite declarações como

Inteiro anInteger = 25. - - Tipo de dados inteiro


Decimal aDecimal = 50,75. - - Tipo de dados decimais

Como você executaria uma operação aritmética simples como a seguinte sem gerar erros de incompatibilidade de tipo?

Decimal theSum = anInteger + aDecimal.

A linguagem de programação pode converter anInteger em uma variável equivalente do tipo Decimal e realizar a adição. No
entanto, isso parece violar o motivo de ter uma linguagem fortemente tipada em primeiro lugar, certo? Você deseja que o
compilador detecte o que pode ser um descuido de sua parte.

Talvez você devesse ter declarado anInteger como um tipo de dados decimal e evitado o problema. Infelizmente, você pode precisar de
umInteger como um tipo Integer posteriormente no programa. Parece bobagem ter duas variáveis que representam a mesma coisa em um
programa, mas sendo de tipos de dados diferentes. Isso não é o que a maioria das pessoas sãs chamaria de uma boa prática de
programação!
Y

O que você precisa é uma maneira de coagir explicitamente uma variável a um tipo de dados diferente para uma única instrução. Ao codificar
FL

explicitamente alguma construção, você não irá escorregar e inadvertidamente misturar tipos de dados. Além disso, você pode usar a mesma variável
onde precisar, sem ter que declarar cópias de diferentes tipos de dados.
A M

A resposta é codificar uma construção chamada de fundida. As linguagens de programação mais fortemente tipadas suportam isso, e até mesmo
TE

algumas linguagens fracas também. A forma comum do operador de conversão é colocar o tipo de que você precisa entre parênteses antes que a
variável seja declarada com o tipo "ofensivo". Observe como o elenco funciona em nossa linguagem hipotética.

Decimal theSum = ( Decimal) anInteger + aDecimal.

O operador de conversão não altera a variável que está sendo lançada! O que está acontecendo anteriormente é, apenas para esta instrução,
o operador de conversão cria uma variável temporária que tem o mesmo valor que anInteger apenas do tipo Decimal. Com a concordância
dos tipos de dados, a aritmética pode ser realizada.

Claro, a razão para esta diatribe é que Java suporta o operador de elenco para tipos primitivos e
para objetos. Agora, perceba que você não pode converter qualquer variável de um certo tipo para qualquer outro tipo; existem limites. Você
lerá sobre regras de lançamento para objetos no Capítulo 9, "Herança". Neste capítulo, você lerá sobre regras de fundição para tipos
primitivos.

Team-Fly ®
Página 81

Java Variable Typing

Agora, você está se perguntando, onde Java se encaixa nesse esquema de tipagem de variável? Java possui um mecanismo híbrido de tipagem
de dados. Para tipos primitivos, Java é fracamente tipado para algumas expressões e fortemente tipado para outras. Para objetos, você terá que
esperar até o Capítulo 7 para a explicação completa.

Java permite que você misture primitivos, não booleano tipos, desde que o destino da expressão seja um tipo de dados que pode
conter o maior número de quaisquer componentes da expressão. Sim, isso é bastante complicado. Talvez o exemplo na
Listagem 5.1 divida a boca em pedaços pequenos.

No entanto, a expressão demonstrada na Listagem 5.2 causa um erro de compilação Java. Aqui está o erro do

compilador:

Tipo incompatível para declaração. Elenco explícito necessário para converter duplo em flutuante.

float aFloat2 = anInt + aDoub + aByte;


^

Curiosamente, javac aponta para o destino da expressão enquanto diz que você precisa explicitamente converter o em dobro para um flutuador.
Portanto, esta mudança obtém uma compilação bem-sucedida:

float aFloat2 = anInt + ( flutuador) aDoub + aByte;

OU

float aFloat2 = ( flutuador)( anInt + aDoub + aByte);

int anInt = 325; // Faixa de -2 31 para 2 31- 1


flutuador à tona = 21,25f; // Alcance muito maior
byte aByte = -56; // Varia de -128 a 127
/ **
A variável float aFloat2 pode conter o maior número de qualquer um dos adendos abaixo.

À parte, observe como a variável de ponto flutuante foi inicializada.

**/
flutuador aFloat2 = anInt + aFloat + aByte;

Listagem 5.1 Java pode lidar com a mistura desses tipos primitivos.
Página 82

int anInt = 325; // Faixa de -2 31 para 2 31- 1 // Varia de -128 a


byte aByte = -56; 127
em dobro aDoub = 31E10d; // Intervalo muito maior que
//flutuador
/ **
A variável float aFloat2 não pode conter o maior número do adendo aDoub abaixo. Observe que
aDoub foi inicializado bem dentro do intervalo permitido para uma variável flutuante Java.

À parte, observe como a variável dupla foi inicializada.

**/
flutuador aFloat2 = anInt + aDoub + aByte;

Listagem 5.2 Java clama por um elenco ao misturar esses tipos primitivos.

O operando anterior deve ser convertido ou o destino do resultado da expressão para um número declarado com o tipo primitivo duplo
deve ser convertido? Você não pode responder a essa pergunta de forma inteligente sem saber o uso completo dessas variáveis em um
programa.

A seguir, são listadas mais informações sobre cada um dos tipos primitivos de Java.

O tipo primitivo booleano.

Java suporta um tipo de dados booleano, que possui apenas dois valores: verdadeiro e falso. Booleanos Java não pode ser lançado em nada. Os
booleanos Java não são inteiros como em C, ou comutadores de bits como em PL / I. Conseqüentemente, os booleanos não podem ser usados em
nenhuma declaração aritmética de maneira significativa; javac vai parar essas tentativas tolas.

A dissertação anterior sobre fundição de tipos primitivos não se aplica a booleanos. Use os booleanos Java para valores verdadeiros e
falsos e esqueça os "truques booleanos" usados em outras linguagens de programação, como configurar várias opções de bits para
representar valores booleanos.

Os valores booleanos literais só podem ser verdadeiros ou falsos, nenhuma outra opção existe. A seguir, as declarações são listadas:

boleano aTrueBool = true; aFalseBool =


boleano false;
Página 83

O tipo primitivo char

Você pode pensar que sabe quais dados são uma variável do tipo Caracteres segura, mas provavelmente você está errado. Você esperaria que um
char contivesse um caractere, que é uma string de bits que representa um membro de um conjunto de caracteres, como EBCDIC ou ASCII. Bem, isso é
correto. No entanto, em Java, o conjunto de caracteres é chamado Unicode,
e cada caractere no conjunto de caracteres Unicode tem 16 bits ou dois bytes de tamanho. A opção mais próxima disponível no mundo mainframe é o Conjunto
de Caracteres de Byte Duplo ( DBCS). Unicode não é o mesmo que DBCS; tudo o que eles compartilham é o fato de que ambos os conjuntos de
caracteres consistem em caracteres de 16 bits.

A boa notícia é que você não precisa se preocupar com o Unicode se tudo o que você está trabalhando é ASCII ou EBCDIC comum. Java
esconde de você os detalhes complicados de trabalhar com caracteres Unicode. Na verdade, os primeiros 8 bits do conjunto de caracteres
Unicode são iguais aos do conjunto de caracteres ASCII Latin-I ou 8-it.

Mainframers experientes em trabalhar com o conjunto de caracteres EBCDIC terão que traduzir para ASCII, como sempre fizeram
quando os mainframers precisaram acessar qualquer plataforma não IBM.

Se você tem experiência em programação C, a seguinte instrução Java não será surpreendente. Do contrário, você estará coçando a
cabeça.

int anInt = 32 + 'a'; // anInt = 129

Parece estranho poder adicionar a letra 'a' ao inteiro 32, não é? Aqui está mais um:

char aChar = 32 + 'a'; // aChar = '?'

Em geral, Java trata as variáveis char como inteiros. No entanto, se você converter variáveis char em bytes ou variáveis curtas, poderá
obter resultados inesperados. Como você pode imaginar, o conceito de um número com sinal, comum para inteiros, não tem sentido para
variáveis char. A abordagem inteligente é usar variáveis char como caracteres e inteiros como números.

Esse tratamento de variáveis char como inteiros é uma ressaca do mundo da programação C. Você não tem que usar variáveis
char dessa forma, mas você deve saber sobre esse uso; você provavelmente verá isso feito por outros programadores Java.

Literais de caracteres são caracteres únicos entre aspas simples, sequências de escape de caractere, sequências de escape octal e
hexadecimal e sequências de escape Unicode. Confira as declarações de declaração do Java abaixo:

char aCharInQuotes = 'a'; // Apenas um caractere entre aspas // Sequência


char aCharEscSeq = '\ n'; de escape (nova linha)
char aUnicodeEscSeq = '\ u10D1'; // Sequência Unicode (????)

As sequências de escape frequentemente utilizadas são mostradas na Tabela

5.3. Logo, a linha de código,

System.out.println ("Hello" + '\ n' + "How Are You?");


Página 84

Tabela 5.3 Sequências de escape comuns

SEQUÊNCIA DE FUGA O PERSONAGEM


\n Nova linha

\b Backspace

\t Aba

\r Retorna

\\ Barra invertida

\' Citação única

\" Citação dupla

produz a seguinte saída:

Olá
Como você está?

Tipos primitivos inteiros

Em Java, você pode agrupar o byte, curto, int, e longo tipos primitivos como inteiros. Como você leu anteriormente, o Caracteres o tipo
primitivo é considerado um inteiro por muitos; entretanto, não perpetuaremos tal consideração. Todos esses tipos podem ser lançados
uns para os outros. Infelizmente, o que você vê nem sempre é o que você obtém. Aqui está um exemplo:

int anInt = 70000;


System.out.println ((abreviatura) anInt); / ** Java imprime 4464 !!! ** /

Aqui, estamos convertendo um inteiro (32 bits) em um inteiro curto (16 bits). Coisas importantes estão acontecendo aqui e são dignas
de nota. Primeiro, o compilador, javac, não reclama de forma alguma, embora o valor inicializado de anInt exceda o valor máximo para
um inteiro curto (32767). Em segundo lugar, o tempo de execução não bloqueia; na verdade, ele cospe um número.

A moral da história é que você deve ter cuidado ao lançar tipos inteiros. Java é muito complacente no uso de tipos de dados primitivos
inteiros. Lembre-se, ou tenha um gráfico à mão, dos valores mínimo e máximo ou do tamanho dos bits dos tipos inteiros primitivos.

Você viu alguns exemplos de codificação de literais inteiros. Caso você tenha uma retenção muito baixa, exemplos
adicionais estão listados a seguir:

int anInt = 4000; // Inteiro, 32 bits


curto aShort = 1000; // Curto, 16 bits
Página 85

muito tempo = 40001; // Longo, 64 bits. Observe o '1' // Byte, 8 bits


byte aByte = 40 ;

Linguagens de mainframe não têm suporte para inteiros além de 32 bits; portanto, o de Java longo tipo primitivo não tem análogo direto em COBOL
e PL / I. Se precisar de números maiores do que 15 dígitos em um programa COBOL (15 dígitos significativos), você terá que usar um tipo de dados
de ponto flutuante (COMP-1 ou COMP-2).

Tipos primitivos de ponto flutuante

Java oferece suporte a dois tipos de ponto flutuante: flutuador e em dobro. Embora as linguagens de programação de mainframe tenham tipos de
ponto flutuante, esses tipos raramente são usados; os mainframers têm acesso a não inteiros usando números compactados, declarados com um
número de dígitos decimais. Essa capacidade vai ao coração da tipagem de dados representacional do COBOL, onde o mainframe contém
instruções de máquina para manipular números compactados.

O programador COBOL pode declarar o número 3.556 sem usar um tipo de ponto flutuante, enquanto o programador Java é
obrigado a usar um float ou um duplo para representar tal número.

Literais de ponto flutuante são declarados da seguinte maneira.

flutuador à tona = 1.0012f;


em dobro o dobro = 1,0012d;
em dobro o dobro = 1,0012; // não é necessário para duplo

Os tipos de ponto flutuante Java têm algum comportamento incomum. Por exemplo, verifique a seguinte linha de código.

// Divisão por zero!!!!!!!! System.out.println (aFloat / 0 + "


"+ -aFloat / 0);

A saída está listada a seguir.

Infinidade - infinito

Sim, você está lendo isso corretamente. Quando você divide um float por zero, você obtém Infinidade; quando você divide um número de ponto flutuante
negativo por zero, você obtém. . . Infinidade.

A propósito, você não pode dividir um número declarado como um tipo inteiro por zero. Você obterá a resposta esperada e familiar.

Ok, aqui está outro exemplo de comportamento estranho. O código é:

float aFloat2 = 0,0f;


System.out.println (- (aFloat - aFloat) + " "+ aFloat2 / aFloat2);

A saída é

- 0,0 NaN
Página 86

Sim, Java irá gerar algo como zero negativo. Se flutuante fosse uma variável de um tipo inteiro, Java produziria 0. O "NaN"
significa "Não é um número", que é uma quantidade não ordenada que compara falso a todos os números e a si mesmo.

A moral da história é ficar de olho nesses números de ponto flutuante; eles podem agir de maneiras incomuns!

Strings de caracteres em Java

Strings de caracteres em Java são uma instância de classe Corda. Strings não são um tipo de dados primitivo. Dito isso, os
criadores do Java entenderam que as strings são uma estrutura de dados comum e frequentemente usada, e que o Java deve tornar
mais fácil para o programador criar e usar strings. Strings em Java são diretas, como mostra o exemplo a seguir.

Corda aString = "Oi Ho, Cherrio!" ;

Em outras palavras, as strings "parecem" um tipo de dados primitivo. Java converte todas as ocorrências de strings em um objeto da classe
String. No entanto, essa conversão tem impacto mínimo no programador Java.

A classe String tem uma variedade de métodos utilitários que alcançam uma string para puxar uma substring, procuram uma string dentro
da outra e determinam o comprimento da string.

Tipos de dados de referência Java

Java faz uma distinção entre tipos de dados primitivos e tipos de dados de referência. Enquanto os tipos primitivos são passados para
métodos por valor, os tipos de referência são passados por referência.

Qual é a diferença? Bem, quando um argumento é passado para um método por valor, uma cópia da variável do argumento é enviada para
o método. Quando um argumento é passado por referência, o endereço inicial do argumento é passado para o método. No Capítulo 2, você
lê porções sobre esse problema candente; você revisitará brevemente este tópico terrivelmente excitante de passagem por valor e por
referência no Capítulo
7

Os tipos de dados de referência em Java são matrizes e objetos. No Capítulo 7, você lerá sobre objetos; aqui, você lerá sobre
matrizes.

Arrays Java

Um array é uma estrutura de dados agregados em que cada membro do agregado é do mesmo tipo de dados e cada membro do
agregado pode ser referenciado por um índice. Se você tem experiência em programação, já viu arrays. Dito isso, os arrays Java têm
algumas características interessantes não encontradas em COBOL ou PL / I. Vamos dar uma olhada.

Algumas declarações de matriz são listadas a seguir.

int anIntArray [] = {12, 24, 36, 4 * 24, anotherInt}; uma linha []


Corda = {"Oi", "Ho", "Cherrio!"};
int uma mesa[] [] ={
Página 87

{12, 24, 36},


{23, -45},
(12345}
};

Observe que a primeira declaração inclui um cálculo e uma referência a outra variável (vamos assumir que essa outra variável
existe e tem um valor, ok?). Os elementos da matriz são separados por vírgulas, incluídos entre chaves.

A terceira declaração é um array de arrays. Observe os pares de chaves aninhadas. Observe também a dimensão da matriz
aTable. Quantos de vocês leitores perceberam que a primeira "linha" da tabela tinha três elementos, a segunda "linha" tinha dois
e a terceira "linha" tinha um? Como o Java implementa arrays multidimensionais como arrays de arrays, você não precisa ter
seus arrays retangulares típicos. Tente isso em COBOL ou PL / I.

Observe que a terceira declaração é descrita como uma "matriz de matrizes", em oposição a uma matriz multidimensional. Qual é a diferença,
você pergunta? Veja a declaração anterior de aTable. ATable não é multidimensional; não é 2 × 3,2 × 2,2 × 1, nem nada × nada. ATable é
uma matriz de três elementos; o primeiro elemento é uma matriz de três elementos, o segundo é uma matriz de dois elementos, o terceiro é
uma matriz de um elemento. O termo para esse tipo de estrutura é uma matriz de matrizes. Claro, você pode implementar matrizes
multidimensionais codificando uma matriz 3 X 3 ou 100 X 100 X 100, ou qualquer matriz em que os tamanhos de dimensão sejam os
mesmos. Sem querer fazer um acordo sobre isso, mas os arrays Java são mais flexíveis do que os arrays declarados em outras linguagens
procedurais que são limitadas a arrays multidimensionais.

Outro recurso compartilhado por esses três declara é que todos esses arrays são inicializados. Você não pode codificar

int anIntArray [];

porque o Java não saberia quanta memória alocar para o array. Se você não tem valores iniciais, pode declarar seus arrays
da seguinte maneira:

int anIntArray [] = novo int [200];

Aqui, você usa o Novo operador, comumente usado para criar objetos, para criar matrizes sem valores iniciais. Você pode declarar
matrizes multidimensionais com Novo, conforme mostrado a seguir:

Corda aStringTable [] [] [] = nova String [10] [20] [200];

Na verdade, você pode até mesmo deixar de fora alguns dos tamanhos de dimensão ao declarar uma matriz. Observe o seguinte:

Corda aStringTable [] [] [] = nova String [10] [] [];

Como isso pode ser? Como o Java pode saber quanta memória alocar para este array? Bem, Java vê essa estrutura como um
array unidimensional de 10 elementos; cada elemento é uma matriz bidimensional.
Página 88

Java também aceita esta sintaxe:

int [] anIntArray = {12, 24, 36, 4 * 24, anotherInt}; = {"Oi", "Ho", "Cherrio!"}; = {
Corda[] uma linha
int [] [] uma mesa
{12, 24, 36},
{23, -45},
(12345}
};

Agora você tem a imagem de um tipo de dados de uma matriz de inteiros ou um tipo de dados de uma matriz de strings. Ao usar o conceito de uma
matriz como um tipo de dados, Java permite uma construção como a seguinte:

int [] anIntArray ; // Tipo de dados é uma matriz de inteiros


anIntArray = new int [200]; // Faz com que umIntArray contenha 200 inteiros anIntArray = new int [30]; // Faz com
que umIntArray contenha 30 inteiros.

Você sabe como referenciar matrizes, certo? Provavelmente não, porque em COBOL e PL / I, os arrays normalmente começam com 1,
enquanto em Java, os arrays começam com 0. Ergo, referindo-se aos arrays na página anterior:

anIntArray [1] aString [0] = 24;


= "Oi";
aTabela [1] [0] = 23;
aTable [2] [1] está fora dos limites

Se você quiser se referir ao último elemento em aTable, a referência correta é

aTabela [2] [0] = 12345;

Os arrays são uma grande parte de qualquer linguagem de programação e Java não é exceção. Java permite que você crie arrays semelhantes e
diferentes daqueles em COBOL e PL / I. Em Java, os arrays são baseados em zero (um fato que vale a pena repetir).

Convenções de nomenclatura Java

Antes de deixarmos o assunto de definição de dados, algumas palavras sobre a nomenclatura de variáveis Java são apropriadas. Java permite
variáveis de qualquer tamanho prático. Uma variável Java não começa com um número; começa com uma letra minúscula, sublinhado ou um
cifrão. O início de cada nova palavra é maiúsculo, todas as outras letras são minúsculas. Todos, exceto a primeira posição do nome, podem ser
letras, números e alguns símbolos especiais. Além disso, os nomes das variáveis devem ser substantivos.

Os objetos Java seguem a mesma convenção de nomenclatura que as variáveis de tipos primitivos.

1 As classes Java começam com uma letra maiúscula, com o início de cada nova palavra em maiúscula.
Página 89

2 Use substantivos com o adjetivo ocasional para nomear classes, objetos e variáveis primitivas Java. Você se lembra de
"pessoa, lugar ou coisa", certo?

3 - Os métodos Java seguem a mesma convenção de nomenclatura que as variáveis: comece com minúsculas e coloque cada palavra
em maiúscula. No entanto, os métodos Java devem denotar algum tipo de ação ou, dito de outra forma, os métodos Java devem
conter um verbo.

Muito simples, certo?

Em suma

A linguagem de programação Java fornece suporte para tipos de dados e estruturas de dados familiares. O tratamento Java de tipos primitivos
e uso de dados pode parecer incomum para o programador de mainframe, especialmente o borramento dos tipos de dados int e char. No
entanto, as semelhanças entre o tratamento Java dos tipos primitivos e o tratamento da linguagem de programação de mainframe da maioria
dos tipos de dados são mais reveladoras.
Página 90

Esta página foi intencionalmente deixada em branco.

Y
FL
A M
TE

Team-Fly ®
Página 91

CAPÍTULO 6
Sintaxe da linguagem Java

Este capítulo cobre a maioria dos recursos e construções da linguagem Java, incluindo o seguinte:

Declarações de atribuição

Operadores

Aritmética

boleano

Corda

Mordeu

Objeto

Java Arithmetic Anomalies

Construções de loop

Declarações de decisão

Cada recurso de linguagem é comparado a recursos de linguagem de funcionamento idêntico (quando possível) de COBOL e PL / I.
Além disso, são discutidos os recursos de linguagem presentes em linguagens de programação de mainframe que não têm analogia
Java, como ponteiros, arquivos de cabeçalho / COPY e pré-processador. Completando o capítulo, há discussões sobre o curto-circuito
de expressões booleanas e diversos itens de sintaxe Java.

Este capítulo contém muitos códigos e tabelas destacando as semelhanças e diferenças entre Java e as linguagens de
programação de mainframe. Você pode querer
Página 92

insira algumas instruções semelhantes aos exemplos em um arquivo de origem Java; então você deve compilar, executar e observar os
resultados.

Itens diversos de sintaxe Java.

Alguns itens de sintaxe em Java são diferentes das linguagens de programação de mainframe mais familiares. Vamos discutir
esses itens primeiro.

O código-fonte Java diferencia maiúsculas de minúsculas

Ao contrário de COBOL, PL / I e uma série de outras linguagens de programação usadas no mainframe, Java, diferenciam maiúsculas de
minúsculas. A distinção entre maiúsculas e minúsculas pode ser uma fonte de frustração para o programador COBOL e PL / I que está
acostumado a codificar tudo em maiúsculas. Conforme discutido no Capítulo 5, "Declarando e definindo dados", os programadores Java devem
seguir a convenção de nomenclatura discutida anteriormente para suas entidades de programa, como variáveis, constantes, classes, objetos e
métodos. Você não terá escolha se deseja usar uma entidade existente (o que ocorrerá 99,999999999999999 por cento do tempo); você deve
observar o caso do nome da entidade em seus programas. Portanto, acostume-se a estar ciente da disposição da tecla shift ao inserir o
código-fonte Java.

Instruções Java terminadas por um ponto e vírgula

Não há muito a acrescentar a isso, certo? Pense em COBOL com o ponto ou PL / I com o ponto e vírgula e você entenderá a ideia.

Java suporta vários estilos de comentário

Na maioria dos idiomas, um comentário é um comentário. Não é assim em Java, que oferece suporte a três estilos de comentário.

O primeiro é um comentário de uma única linha que começa com uma barra dupla (//) e continua até o final da linha. Este estilo de comentário não
possui delimitador de comentário de fechamento.

O segundo exemplo é um estilo de comentário de várias linhas que começa com / * e termina com * /. Este é o estilo de comentário usado
pelos programadores PL / I, REXX e C. Ao contrário de PL / I e REXX, Java não permitirá que você ninho, ou coloque um dentro do outro,
comentários.

O exemplo final é um comentário especial de estilo apenas Java que começa com / ** e termina com * /. Este estilo de comentário é chamado de comentário
doc. Os comentários do documento podem ser processados por um utilitário Java chamado javadoc, um programa projetado para gerar
documentação, em parte, a partir de comentários escritos no código-fonte. Consulte o Capítulo 4, "Ferramentas JDK básicas do Sun Java 2," para
obter informações adicionais sobre javadoc.

Agora, os programadores PL / I, REXX e C reconheceriam esse estilo como sendo o mesmo estilo de comentário de várias linhas. Na verdade,
você pode codificar este comentário de estilo em seus programas Java. Colocando de outra forma, o compilador Java lê / ** da mesma forma que /
*. A propósito, um comentário que começa com / ** e termina com ** / é não um comentário doc.
Página 93

Como os comentários Java multilinha e de documento não se aninham, você deve se acostumar a codificar a variedade de comentários de linha única
(//). Dessa forma, se você quiser comentar os blocos de código, pode fazer o que os programadores PL / I fazem - codificar um / * antes do bloco e * /
depois do bloco.

Java não tem declaração de COPY ou include

Java não tem nenhum recurso para trazer fonte externa para compilar, como COBOL's COPY ou PL / I's% Include instrução. Java conta com uma
convenção de nomenclatura de arquivo estrita para classes Java. Se o seu programa precisar de métodos ou outras entidades de um arquivo de
classe, o Java será capaz de localizá-lo. Java não precisa trazer o código-fonte de outros arquivos de classe para o seu programa.

Java não tem ponteiros

Você já leu isso antes: Java não tem ponteiros. Java não tem suporte para nada relacionado a ponteiros; ele lida automaticamente com
referência e desreferenciamento de objetos.

Você não pode manipular ponteiros ou endereços de memória executando aritmética de ponteiros, convertendo objetos em tipos de dados
primitivos ou computando ou alterando o tamanho de bytes de qualquer objeto primitivo.

Felizmente, os engenheiros da Sun que desenvolveram o Java perceberam que os ponteiros, seu uso e especialmente seu abuso, são
uma fonte hedionda de bugs de software. Ao eliminar ponteiros da linguagem, os criadores do Java, de uma só vez, removeram
permanentemente uma fonte persistente de bugs.

Outro fator a ser considerado é que os ponteiros permitem que um programador entre nas entranhas de uma máquina. Os desenvolvedores de Java
estavam preocupados com a segurança. Ponteiros e aritmética de ponteiros podem permitir que um programador nefasto contorne qualquer
mecanismo de segurança em vigor. Remover ponteiros é um golpe para a segurança do sistema, mantendo os componentes internos do sistema
removidos dos programadores.

Java não tem pré-processador

Os programadores PL / I e C estão familiarizados com um pré-processador. O pré-processador gera o código-fonte que, por sua vez, é
passado para o compilador específico. Embora a instrução COPY do COBOL inclua código-fonte, o COBOL não oferece suporte a um
pré-processador.

Uma razão comum para o uso do pré-processador é para compilação condicional. Java, sendo plataforma neutra, certamente não precisa
de especificações de plataforma para compilar com sucesso.

Palavras reservadas existem em Java

Como a maioria das linguagens de programação, Java usa Palavras reservadas. Você deve usar essas palavras em seu contexto sintático e
semântico adequado. A Tabela 6.1 mostra a lista de palavras reservadas do Java.
Página 94

Tabela 6.1 Palavras Reservadas de Java


resumo outro int estático

boleano estende interface super

quebrar falso longo interruptor

byte final nativo sincronizado


por valor finalmente Novo isto
caso flutuador nulo lançar

fundida para operador lança


pegar futuro exterior transitório

Caracteres genérico pacote verdade

classe Vamos para privado tentar

const E se protegido var

continuar implementos público vazio

padrão importar descansar volátil


Faz interior Retorna enquanto

em dobro instancia de curto

Observe que todas as palavras reservadas do Java estão em minúsculas. Claro, o compilador Java (e você) reconhece a diferença
entre a palavra reservada falso, e alguma entidade de programa chamada Falso.

Algumas das palavras reservadas não têm significado sintático ou semântico. Estas palavras são mostradas na Tabela
6,2

Java tem a audácia de reservar a tabela de palavras anterior, mas não tem uso para elas. Você, entretanto, está proibido de usar essas
palavras em seus programas Java. Observe que goto é uma palavra reservada em Java, mas Java não oferece suporte a uma instrução
goto.

Os nomes dos métodos da classe raiz do Java Objeto não são reservados, mas esses nomes de métodos justificam um tratamento especial.
Como você lerá no Capítulo 7, "Representação de classes e objetos", toda classe herda métodos da classe Object. A menos que você pretenda
substituir esse método em sua classe, você não deve usar esses nomes de método para seus próprios métodos. A Tabela 6.3 é a lista de nomes
de métodos da classe Object.

Os leitores mais astutos perceberão o uso da convenção de nomenclatura usada na nomenclatura desses e de outros
métodos Java. Sim, é o mesmo discutido no Capítulo 5, "Declarando e definindo dados".

Se você fizer qualquer REXX, saberá que REXX tem algumas palavras chamadas reservadas que não são realmente reservadas. Por exemplo, a
palavra RC, que representa uma variável REXX que contém o valor do código de retorno de uma chamada do sistema operacional, não deve ser
usada em um contexto diferente. No entanto, o programador REXX coxo poderia codificar
Página 95

Tabela 6.2 Palavras reservadas sem sentido (por enquanto) do Java


por valor genérico exterior

fundida vamos para descansar

const interior var

futuro operador

Tabela 6.3 Palavras pseudo-reservadas de Java

clone getClass notificar tudo

é igual a hashCode

finalizar avisar esperar

RC = "Tenho certeza que sou um Programador REXX Lame!"

O interpretador ou compilador REXX não engasgaria, embora seus colegas programadores REXX sim.

Java também perdoa o uso desses nomes de métodos pseudo-reservados. Por exemplo, se você codificou este método:

void toString (int a) { // toString é uma daquelas palavras


int x = 5;
if (x> a)
System.out.println ("Hah!");
}

ou codificou esta declaração de variável:

int toString = 10;

javac, o compilador, seria muito complacente; Java, o interpretador de bytecode, também seria muito tolerante. Você suportaria o peso de
todas as piadas de seus colegas Javaianos se eles o pegassem usando esses nomes de métodos da classe Object de uma maneira
inconsistente com sua existência. Além disso, você não obteria simpatia deste canto.

Java é uma linguagem de programação de formato livre

Na época em que a Terra estava esfriando, os engenheiros do compilador pensaram que seria inteligente tornar as coisas mais fáceis para o
compilador, forçando os programadores a codificar vários
Página 96

coisas em colunas específicas. Aqueles de nós que se lembram de Woodstock, lembrem-se das primeiras versões do Fortran
onde você tinha que codificar entre as colunas 8 e 72. Claro, o ofensor mais sério de "forçar os programadores a codificar em
tal e tal coluna" é o COBOL. Existe um número quase incontável de instruções COBOL codificadas nas margens A e B.

Como o Java foi desenvolvido após a Era Cambriana, ele não impõe restrições de coluna ao código. Você pode iniciar e terminar seu
código Java em qualquer coluna que desejar.

Declarações de atribuição Java

As instruções de atribuição em Java apresentam algumas surpresas e podem se comportar de maneira diferente das instruções de atribuição em
outras linguagens de programação. Vejamos o que as instruções de atribuição Java têm em comum com outras linguagens de programação.

Java oferece suporte à instrução de atribuição esperada, usual, comum e comum:

aJavaVariable = aJavaExpression;

onde um JavaVariable é uma variável declarada anteriormente e, bem, você entende.

Você viu no capítulo anterior que pode atribuir um valor a uma variável Java (ou objeto) na declaração. A saber:

Caracteres aCharVar = 'L'; anObj // Tipo Primitivo


Minha classe = nova MinhaClasse (); //Objeto

COBOL permite que você inicialize uma variável com o Valor cláusula; PL / I permite que você inicialize uma variável com o Iniciar opção.

Isso é praticamente toda a gama de semelhanças entre instruções de atribuição Java e instruções de atribuição COBOL ou PL /
I. Teremos mais a dizer sobre as expressões Java posteriormente neste capítulo.

A diferença mais dramática entre as instruções de atribuição Java e aquelas usadas em linguagens de programação de mainframe
é a atribuição com operador afirmações. Este capítulo contém mais informações sobre esses e os demais operadores Java. Por
enquanto, saiba que o Java permite que você execute uma operação e atribua o resultado a uma variável em uma operação. Aqui
está um exemplo, com a promessa de vários outros exemplos incluídos na seção intitulada Operadores Java:

int anInt = 6; // Apenas a inicialização normal de sua variável anInt


+ = 6; // Adicione 6 a anInt, reatribua - anInt = 12
// - +, * = e outros também disponíveis

anInt = anInt + 6; // Funcionalmente equivalente a anInt + = 6;

Os programadores de C por aí devem estar bastante confortáveis e familiarizados com a família de operadores "operar e atribuir".
Página 97

Que tal outra diferença entre instruções de atribuição Java e instruções de atribuição COBOL e PL / I? Como você acha
que Java interpretaria esta declaração?

int a, b;
a = b = 7;

Esta declaração de atribuição, no COBOLSpeak, é semelhante a uma mudança de grupo. Resumindo, as variáveis aeb têm o valor 7 após a
execução da instrução de atribuição. Em PL / I e REXX, a declaração seria avaliada da seguinte forma:

a = (b = 7);

A variável b não mudaria. A variável b não recebe um valor; b é testado para o valor 7. Apenas a variável a recebe um valor
de verdade ( se b for igual a 7) ou falso ( se b for igual a algum outro valor).

Ao ler sobre a classe Java e a representação do objeto no Capítulo 7, você aprenderá que as atribuições de objetos
são um pouco diferentes das atribuições de variáveis.

Operadores Java

O que é uma linguagem de programação sem operadores? Você verá que Java possui vários operadores não encontrados em COBOL
e PL / I; aqui, você dará uma olhada nos operadores Java. Você aprenderá quais operadores Java têm em comum com COBOL e PL / I;
você lerá um pouco mais sobre e verá exemplos de operadores Java que não têm contrapartes no mundo da programação de
mainframe.

Você está com a janela do código-fonte Java e a janela do DOS abertas? Provavelmente, é um bom investimento de seu tempo
experimentar alguns dos exemplos à medida que lê.

A Tabela 6.4 mostra os operadores Java com sua classificação de precedência, seus tipos de operando, sua associatividade
e, é claro, uma breve descrição da operação.

Da operadora precedência rege quando a operação é executada em relação a outras operações na mesma linha. Uma operação de
precedência 1, na parte superior da tabela, é executada antes de um operador de precedência numerado mais alto quando ambos estão na
mesma linha de código. Por exemplo:

myMeagerPay = myTooSmallGross - shockinglyHighTaxRate *


mytooSmallGross;

A operação de multiplicação é executada primeiro, seguida pela operação de subtração e, em seguida, pela operação
de atribuição.

Isso é muito normal, certo? Tal como acontece com outras linguagens de programação, você pode usar parênteses para alterar a ordem natural de
execução, conforme definido pela precedência do operador.

myMeagerPay = myTooSmallGross * (1.0 - shockinglyHighTaxRate);


Página 98

Tabela 6.4 Operadores Java por precedência

PRECEDÊNCIA OPERADOR OPERANDO ASSOCIATIVIDADE OPERAÇÃO


TIPO (S)
1 ++ Aritmética RL Incremento Unário

- Aritmética RL Decremento unário

+, - Aritmética RL Elogio unário mais, unário menos


- Integrante RL unário bit a bit
! boleano RL Elogio Lógico Unário
(vartype) Qualquer RL Tipo Elenco

2 *, /,% Aritmética LR Multiplicar, dividir, restante


3 +, - Aritmética LR Adição subtração
+ Corda LR Concatenação

4 << Integrante LR Shift Left


>> Integrante LR Shift Right, Extend Sign Shift
>>> Integrante LR Right, Zero -Fill
5 <, <= Aritmética LR Menor, Menor ou Igual a

>,> = Aritmética LR Maior que, Maior que ou Igual a

instancia de Objeto, LR Comparação de tipo / classe

Tipo
6 == Primitivo LR Igualdade por ter valores idênticos

!= Primitivo LR Diferente por ter valores


diferentes
Página 99

Tabela 6.4 Operadores Java por precedência ( contínuo)

PRECEDÊNCIA OPERADOR OPERANDO ASSOCIATIVIDADE OPERAÇÃO


TIPO (S)
== Objeto LR Igual por referência ao
mesmo objeto

!= Objeto LR Diferente por referência a


objetos diferentes

7 E Integrante LR Bit AND


E boleano LR E
8 ^ Integrante LR Bit Exclusivo OU (XOR)
^ boleano LR Exclusivo OU (XOR)
9 | Integrante LR Bit OU
| boleano LR OU

10 && boleano LR AND (usado em


Declarações condicionais)

11 || boleano LR OU (usado em declarações


condicionais)

12 ?: Booleano, Qualquer, RL Ternário (usado em


Qualquer Declarações condicionais)

13 = Variável, Qualquer RL Tarefa


+ =, / =,% = Variável, Qualquer RL Atribuição com
Operação

+ =, - =

<< =, >> =

>>> =

& =, ^ =,! =
Página 100

A subtração dentro do par de parênteses é realizada primeiro, seguida pela multiplicação e, em seguida, pela
atribuição.

Em geral, a precedência do operador Java, do primeiro ao último, é a seguinte:

1 Operadores de incremento e decremento

2 Operadores aritméticos

3 - Operadores de comparação

4 - Operadores booleanos

5 Operadores de atribuição

Da operadora associatividade rege a ordem de execução quando dois ou mais operadores de igual precedência estão na mesma instrução. Se
uma operadora tem Da esquerda para direita ( LR) associatividade, então, dois ou mais dos mesmos operadores de precedência na mesma
linha são avaliados da esquerda para a direita. Por exemplo, a Tabela 6.4 afirma que o Adição operador, +, tem associatividade LR. Portanto, a
declaração

soma = adição1 + adição2 + adição3;

avalia adicionando primeiro addend1 a addend2 e, em seguida, adicionando essa soma a addend3. A Tabela 6.4 afirma que o tarefa
Y
FL

operador, =, tem associatividade RL. Portanto, a declaração

var1 = var2 = var3 = 20;


A M

avalia atribuindo 20 a var3, depois atribui var3 a var2 e, a seguir, atribui var2 a var1.
TE

Para a maior parte, COBOL e PL / I, na ausência de parênteses, usam associatividade LR para os mesmos operadores de precedência.

Você notou que Java tem muito mais operadores do que COBOL e outras linguagens de programação de mainframe. A Tabela 6.5
mostra quais operações Java, COBOL e PL / I têm em comum.

Vamos passar algum tempo examinando os operadores Java não encontrados em COBOL e outras linguagens de programação de
mainframe.

Operadores Java não encontrados nas linguagens de programação de mainframe.

Os operadores encontrados em Java, mas não em COBOL ou PL / I, são dignos de explicação.

Incremento e decremento unário

Incremento unário (++) e Decremento unário ( ––) operadores are often used in Java. These operators add or subtract 1 from a
variable and assign the result back to the variable. You'll see these operators in loops where your Java code increments a loop
counter.

Team-Fly ®
Page 101

Table 6.5 Java Operators Used in COBOL and PL/I


OPERAND COBOL PL/I
OPERATOR TYPE(S) OPERATION OPERATION OPERATION
++ Arithmetic Unary Increment None None

–– Arithmetic Unary Decrement None None

+,- Arithmetic Unary Plus, Unary Minus Unary +,- +,-

- Integral Bitwise Compliment None None

! Boolean Unary Logical Compliment NOT ^


(vartype) Any Type Cast None None

* , /,% Arithmetic Multiply, Divide, Remainder * , /,None * , /,None

+,- Arithmetic Addition, Subtraction +,- +,-

+ String Concatenation None ||

<< Integral Shift Left


>> Integral Shift Right, Extend Sign Shift
>>> Integral Right, Zero -Fill
<, <= Arithmetic Less than, Less than or Equal to < <= <, <=

>,>= Arithmetic Greater than, Greater than or Equal >,>= >,>=

to

instanceof Object, Type Type/Class Compare None None

== Primitive Equal by Having Identical Values = =

!= Primitive Not Equal by Having NOT EQUALS ^=̂


Different Values
Page 102

Table 6.5 Java Operators Used in COBOL and PL/I ( continued)


OPERAND COBOL PL/I
OPERATOR TYPE(S) OPERATION OPERATION OPERATION
== Object Equal by Referring to Same None None
Object

!= Object Not Equal by Referring to None None


Different Object
& Integral Bit AND None None

& Boolean AND AND &


^ Integral Bit Exclusive OR (XOR) None None

^ Boolean Exclusive OR (XOR) None None

| Integral Bit OR None None

| Boolean OR OR |

&& Boolean AND (Used in AND &


Conditional Statements)

|| Boolean OR (Used in Conditional OR |


Statements)

?:Boolean, Any, Ternary (Used in None None


Any Conditional Statements)

= Variable, Any Assignment = =


+ =, /=, %= Variable, Any Assignment With None None
Operation

+ =, -=

<<=, >>=
>>>=

&=, ^=, !=
Page 103

Here are some straightforward examples:

int aInt = 10 ;
aInt ++ ; // aInt equals 11 (postfix) // aInt equals 10
aInt -- ; (postfix) // aInt equals 9 (prefix) // aInt equals 10
- - aInt ; (prefix)
+ + aInt ;

The increment and decrement operators behave differently based on the placement of the operator. We draw a distinction
between prefix and postfix increment and decrement operators. The prefix operators are placed before the variable and perform
the operation before any assignment or calculation takes place; the postfix operators are placed after the variable and perform
the operation after any assignment or calculation tales place. Yes, this certainly sounds like a mouthful; looking at the four
examples above, you can't see any difference in the behavior of the postfix and prefix operators.

To see the difference between the behavior of the postfix and prefix operators, check this out:

int aInt = 10 ;
int bInt ;
bInt = aInt ++ ; // bInt is assigned aInt's current
// value of 10 before aInt is incremented // to 11. So aInt = 11, bInt = 10

bInt = -- aInt; // aInt is decremented from 11 to 10,


// then aInt's value is assigned to bInt // So aInt = 10, bInt = 10

The increment and decrement operators, when used in assignment statements like the previous examples, have this odd
property of changing the values of variables on the right hand side of the assignment operator. To a C programmer, these
operators are standard fare; to a mainframe programmer, these operators may seem unusual.

On the one hand, there is nothing forcing you to use these operators. You could add or subtract 1 from a variable using a more
familiar construct. However, you can bet the ranch that you'll see these operators in someone else's Java code. You must
understand the behavior of these operators.

Assignment with Operators

The assignment with operators is a shorthand way of performing an operation on a variable and assigning the new result
to the same variable. Some examples are in order here:

int aInt = 10 ; int bInt ; aInt *= 2 ;


// Multiply by 2 and reassign; aInt = 12
aInt += aInt ; // Add aInt to itself and reassign.
bInt = aInt /= 2 ; // aInt is halved to 4 then its value // assigned to bInt. Recall that
these // operators have R-L associativity.
Page 104

These assignments with operators can take some getting used to, especially if you chain them one after another on single
statements. There is no force on Earth that can make you code with these operators. As with the increment and decrement
operators, you should have a familiarity of how these operations work so you can cope with code that contains them.

Bit Operators

The bit operators perform the indicated logical operator on a binary representation of the arguments in pairs. PL/I supports bit
operators whereas COBOL does not. Three examples should drive the point home:

int aInt = 10; // Bit pattern: 1010 // Bit pattern:


int bInt = 8; 1000
int cInt = aInt & bInt ;// AND in pairs: 1000 = 8 cInt = aInt | bInt ; // OR in pairs: 1010 = 10
cInt = aInt ^ bInt ; // XOR in pairs: 0010 = 2

The Exclusive OR ( XOR), returns true when only one of the arguments is true, not both.

Shift Operators

The shift operators also work on bit representations of integers. Also, a couple of examples should shed some insight on the
workings of these operations:

int aInt = 10; // Bit pattern: 1010


int bInt = aInt << 2;// Shift left two digits to 101000, or 40 int cInt = aInt >> 2 ;// Shift left two digits to 0010, or 2

The Cast Operator

The cast operator is used to change the data type of a variable or object. In Chapter 7, you'll read about the rules governing the
casting of objects. Some primitive types may be cast to different types. Java does not permit the casting of an object to a
primitive type variable or the casting of a primitive type variable to an object.

If you enter this declaration,

float myFloat = .012 ;

javac will tell you that you need an explicit cast from double to float.

Some primitive types do not require an explicit cast. Chapter 5 mentions that the char data type can be used as an integer
without an explicit cast. Hence, the declaration passes javac without comment:

int anInt = 'a' ;


Page 105

Chapter 5 contains more information on Java primitive data types, including the integer types.

Boolean and Conditional Operators

You may be a bit befuddled and partially perplexed by the different classifications of logical operators. The boolean
operators (&, | ) and the conditional operators (&&, | | ) both perform the logical AND and OR we've come to know and
love. However, there is a sneaky difference that is illustrated by the following code:

int aInt = 10, bInt = 20, cInt = 0 ;


boolean aBool = ( aInt > 10 ) & ( bInt / cInt > 0 ) ; boolean bBool = ( aInt = 10 ) | ( bInt / cInt > 0 ) ;

What is going to happen here? Java, in its attempt to initialize aBool, reduces aInt > 10 to false; it then figures the quotient
bInt / cInt, which causes an exception— division by zero. Therefore, this code comes to a screeching, tire-smoking halt.

Well, no surprises here, right? This is the way COBOL and its mainframe kin behave. Programming languages take a dim
view toward division by zero. However, there is an unspoken tale here.

If you look at the previous statement, because aInt was not greater than 10, or aInt > 10 is false, there is no chance the
expression would evaluate to true. You see that, right? Regardless of the boolean value of the right hand side of the expression,
there is no chance the expression would evaluate to true.

This begs the question: Why would Java even bother to evaluate the remainder of the expression when the outcome is not in
doubt? The answer is simple: that is the purpose of the boolean operators. The boolean operators always evaluate each and
every operation before assigning a boolean value to the target.

Now, look at the example code of assigning a value to bBool. The operation that assigns a value to bBool during initialization
is the boolean OR. The first part of the expression, aInt = 10, is true; therefore, the remainder of the expression must be true. However,
Java will evaluate the second part of the expression and, well, you know the inevitable outcome.

Now, check this out:

int aInt = 10, bInt = 20, cInt = 0 ;


boolean aBool = ( aInt > 10 ) && ( bInt / cInt > 0 ) ; boolean bBool = ( aInt = 10 ) || ( bInt / cInt > 0 ) ;

You see the difference between this and the previous example, right? This example uses the
conditional operators. As you've undoubtedly guessed by now, the conditional operators will suppress execution of
expressions when those expressions play no part in the final outcome. Concerning the initialization of aBool, because the left
part of the logical expression is false, the right part cannot contribute to the final result and is not evaluated. The right part of
the initialization of bBool, too, is not evaluated, owing to the truth of the left part.
Page 106

A 10-dollar phrase is used to describe the trait of the conditional operators. We in the know say that these conditional operators short
circuit logical expression evaluation. Some programming languages (C and Ada) short circuit logical expressions. The
mainframe languages (COBOL, PL/I, and REXX) do not; these languages have logical operators that behave like Java's
boolean operators. Java gives you operations that enable you to short circuit or avoid short circuiting.

The String Concatenation Operator.

We've already seen the string concatenation operator (+). This one is a breeze. The concatenation operator combines two
strings into one string. Now PL/I has a concatenation operator, but COBOL does not. The examples that show Java writing
output to the screen with the println() method show the use of this operator. The concatenation operator creates a long string
by (pardon the pun) stringing together two strings:

String sVar1 = "Left", sVar2 = "Right", SVar3 = sVar1 + sVar2 ;


// sVar3 = "LeftRight"

The Ternary Operator

The last new operator you'll read about in this section is the conditional or ternary operator . This operator is shorthand for
an if-else construct. A basic template of this operator is:

logicalExpression ? valueWhenTrue : valueWhenFalse ;

These examples should prove to be useful:

int mySalary = 999999 ;


boolean rUWealthy = (mySalary > 1000000) ? true : false ; uGetGovtCheese = (mySalary < 10) ? 'Y'
char : 'N' ;

The ternary operator comes in handy when assigning variables one of two values.

You see that Java has many interesting operators with interesting subtleties. There is no substitute for entering various
expressions and seeing firsthand how Java behaves with your code. You'll learn all sorts of odds and ends about Java operators
not mentioned here or in other books.

Speaking of interesting subtleties, let's take a look at arithmetic in Java next.

Java Arithmetic Anomalies

Many programming languages treat some aspects of arithmetic in a counterintuitive manner. Java is no exception. Here, you'll
see some differences between Java and the IBMmainframe languages in the treatment of arithmetic.
Page 107

Mixing Primitive Types in Arithmetic Expressions

Now, Java is considered to be a strongly typed language. However, the strong typing comes into play with reference types
(objects and arrays), not with primitive data types. Look at the following statement. Any thoughts on how a strongly typed
language should behave?

char aChar = 30 + 70 ;

Here, we are assigning the result of an arithmetic operation to a variable of type char. Believe it or not, javac, the Java compiler,
will not choke on the assignment. Actually, aChar will have the value of d. Java performs the calculation, yielding (think fast!)
100, before interpreting 100 as an ISO-Latin I character and converting 100 to the letter d.

The previous assignment statement does not surprise the readership with a background in C programming. The COBOL
programmer cannot get away with this. A COBOL compiler would gush up a diagnostic if a said compiler encountered the
following:

aChar Pic X(1) Value( 30 + 70 ) .

The PL/I programmer coding this statement would get a surprise:

Dcl aChar Char(1) Init ( 30 + 70 ) ;

The actual result depends on the compiler settings. aChar could have the value'', or could generate a diagnostic saying that
"the stringsize condition would be raised" if that statement executed.

C programmers are not surprised at Java's behavior toward arithmetic being assigned to char variables. The conversion from
a number to a coded representation is as old as C itself. If you want to add integers, doesn't it make sense to use int
variables?

It is important to know that Java will change the type (if possible) of the result of calculations to match the type of the target
variable. Put another way, Java will force the result of a computation to fit the type of the target variable. A good programmer
takes steps to use variables of the same type.

It's also important to note that the previous spiel applies to computations with variables declared with
primitive types. If you were to declare and use objects in your calculations, Java would expect more care. Here is an example:

//Primitive Data Types Object Declaration

int cPrim ; Integer cObj ;


cPrim = 'a' + 'b' ; cObj = 'a' + 'b';

Java handles the assignment statement on the left as follows: Java converts the character a to 97 and
b to 98 before adding the two to get (think quickly!) 195. Therefore, cPrim equals 195.
Page 108

Java handles the assignment statement on the right a bit differently. The Java compiler burps, saying

Incompatible type for assignment. Can't convert int to java.lang.Integer.

Try it. Enter similar statements and see the Java compiler tell you off.

Loss of Precision when Dividing Integers

A consequence of Java converting the result of an arithmetic operation on primitive types is that you may lose precision when
you divide. This precision loss problem is as old as computers. You may think that as we jet-ski into the twenty-first century,
we computer folk would not be plagued with such ancient problems. You may think that Java, a modern, recently created
computer programming language, would not suffer from this age-old problem. You would be both right and wrong.

You would be right if you declared and used objects. Java would not let you get away with the mixing of object types or
classes in expressions, hoping that Java would convert the result.

You would be wrong if you declared and used primitive types. Java would truncate fractional results of computations when
assigning a quotient to an int variable. To wit:

//Primitive Data Types Object Declaration

int int1 = 7 ; Integer i01 = new Integer(7) ; Integer i02 = new Integer(3) ;
int int2 = 3 ; Float afloat= new Float(i01/i02) ;
float fPrim = int1/int2;

The code on the left, using primitive data types, compiles and executes. Here, fPrim equals 2.0 ( note the .0). The code on the
right, using objects, does not compile.

COBOL and PL/I fare a bit better than Java in this instance. For example,

* COBOL /* PL/I */

Int1 Pic S(9) Comp Value '7'> Int2 Pic S(9) Comp Dcl Int1 Fixed Bin(15)
Init(7) ;
Value '3'. Dcl Int2 Fixed Bin(15)
Init(3) ;
Fprim Pic S(9.999) Comp. Fprim = Int1 / Dcl Fprim Fixed Dec(5,4) ; Fprim = Int1 / Int2 ; /*
Int2. Fprim = 2.3333 */
* Fprim = 2.3333

Using the previous programming languages, you need to declare the quotient with your desired precision, which cuts down
on certain surprises when performing calculations. However, if you divided integers and assigned the result to an integer in
COBOL or PL/I, the result would contain no fractional part.
Page 109

Shoddy Floating Point Arithmetic Results

The funny thing is, just about everyone who hasn't lived in a cave for the past few decades knows that computers can surely
add up numbers. Computers can add many numbers quickly, easily, and exactly. After all, computers can't think, but they
can sure crunch numbers. Well, like most generalizations, there are exceptions.

Let's take a look at some code and see these exceptions:

f = .125f ; System.out.println( " f = " + f ) ; // f = 0.125


f + = .125f ; System.out.println( " f = " + f ) ; // f = 0.25
f + = .125f ; System.out.println( " f = " + f ) ; // f = 0.375
f + = .125f ; System.out.println( " f = " + f ) ; // f = 0.50
f + = .125f ; System.out.println( " f = " + f ) ; // f = 0.625
f + = .125f ; System.out.println( " f = " + f ) ; // f = 0.750
f + = .125f ; System.out.println( " f = " + f ) ; // f = 0.875
f + = .125f ; System.out.println( " f = " + f ) ; // f = 1.0
f + = .125f ; System.out.println( " f = " + f ) ; // f = 1.125

No surprise here, right? Let's make a minor change:

f = .1f ; System.out.println( " f = " + f ) ; // f = 0.1


f + = .1f ; System.out.println( " f = " + f ) ; // f = 0.2
f + = .1f ; System.out.println( " f = " + f ) ; // f = 0.3
f + = .1f ; System.out.println( " f = " + f ) ; // f = 0.4
f + = .1f ; System.out.println( " f = " + f ) ; // f = 0.5
f + = .1f ; System.out.println( " f = " + f ) ; // f = 0.6
f + = .1f ; System.out.println( " f = " + f ) ; // f =
// 0.70000005
f + = .1f ; System.out.println( " f = "+ f ) ; // f = 0.8000001
f + = .1f ; System.out.println( " f = "+ f ) ; // f = 0.9000001

Something seems a bit awry here. COBOL and PL/I behave a bit better because you typically declare the precision of your
results in thee languages; also, the IBMmachine instruction set includes fixed- point decimal arithmetic. For example, these
statements in PL/I produce the indicated result:

Dcl f Fixed Dec (5,4) Init( 0.1 ) ;


f= f +.1; /* f = 0.2000 */
f= f +.1; /* f = 0.3000 */
f= f +.1; /* f = 0.4000 */
f= f +.1; /* f = 0.5000 */
f= f +.1; /* f = 0.6000 */
f= f +.1; /* f = 0.7000 */

Believe it or not, this precision problem with floating point numbers is experienced by nearly every programmer outside of
the IBMmainframe community who uses floating point arithmetic. Floating point arithmetic is a nightmare of sorts; to hope
for an exact result when using floating point is akin to hope for six good numbers in a
Page 110

small, state sanctioned ticket. Floating point arithmetic requires numbers being converted into a binary representation with
exponents (and mantissas— remember that word from high school math?), and back again. The massive load of converting is
bound to introduce inaccuracies.

As an aside, you could argue that we're comparing apples and oranges. The mainframe languages have floating point types.
Why not show an example with, say, a COBOL program declaring floating point numbers and show the accuracy of COBOL's
floating point arithmetic? Well, every mainframe programmer I know (and all you know, too, I'm sure) uses a fixed decimal
representation for nonintegers. They have no need to use floating point because the IBMmainframe instruction set provides the
tools to get accurate decimal results. Simply put, the IBMmainframer has no cause to use floating point, as opposed to fixed
decimal, numbers.

If possible, try to use integer arithmetic; then, divide the result of the integer arithmetic at the end to help guard against
floating point accuracy errors. To wit:

int iVar = 1 ;
float fVar = iVar / 10 ; // fVar = 0.1
iVar++; fVar = iVar / 10 ; iVar++; fVar = iVar / 10 // fVar = 0.2
; iVar++; fVar = iVar / 10 ; // fVar = 0.3
// fVar = 0.4

Overflow and Keep Going


Y

Java has an interesting habit of offering extreme forgiveness toward its primitive types. For example, when you add two integer
FL

types that exceed the range for that type, Java does not overflow, or underflow, or generate any runtime errors, as this example
points out:
M

int aInt = 999999999 ;


A

System.out.println( aInt += 999999999 ) ;//aInt = 1999999998 System.out.println( aInt += 999999999 ) ;//aInt =


TE

-1294967299!! System.out.println( aInt += 999999999 ) ;//aInt = -294967300!!!

Recall that the allowable range for int types in Java is -2 31 to 2 31 - 1. The first add and assign operation yields a number within
this range. However, the second one yields 2999999997, which is outside this range. Java will compile and execute these
statements (try them and see). In contrast, COBOL and PL/I will generate an error when your program exceeds the numeric
bounds for a variable.

The good news is that the Java engineers provided a solution to this and other problems by providing the classes BigInteger and
BigDecimal in package java.math. These classes enable for arbitrary precision, do not overflow like variables of some of Java's
primitive types do, and come with a spate of methods that handle conversions. Chapter 10, "Interfaces," has more information on
the package java.math.

The moral of this tragic tale of obnoxious overflow is to be sure that your Java variables declared as an integer primitive type
have plenty of room to grow.

We've yet to look at Java statements that affect the program's control flow. Java supports loops, decision statements,
and case constructs. Let's now take a look at program control statements.

Team-Fly ®
Page 111

Java Program Control Statements.

You know that a loop is a group of statements that either execute a fixed number of times or execute based on the value of a
boolean. You also know that a decision statement is a two (or more) way branch that directs a program to execute statements
based on the value of a boolean. I'm wagering that you know that a case is a construct that selects a group of statements to
execute based on the value of a variable. I'll also wager that you aren't exactly sure how to code the previous three statement
types in Java. For more on this, read on.

Loop Constructs

Here, you'll take a look at Java's loop constructs. Like most programming languages, Java supports an iterative loop and two
forms of a conditional loop. Let's see.

The Java for Loop

The for loop is Java's implementation of an iterative loop. Here is the basic template:

for (index = startval; executioncondition; changeindex ) { //


one or more statements that constitute the loop body }

Notice the use of semicolons inside the loop construct.

Here is an example with a COBOL and PL/I iterative loop thrown in for comparison:

for (idx = 10 ;idx <= 15; idx++) {


System.out.println("Loop index is " + idx ) ; }

A COBOL example follows:

Perform Varying Idx From 10 to 15.


Display 'Loop Index is ', Idx. End-Perform.

PL/I example:

Do Idx = 10 to 15;
Put Skip List( 'Loop Index is ', Idx ) ; End ;

As you can see, the Java for loop has up to four parts.

1. The Initialization Part

The initialization part is represented by index = startval in the basic template. Your for loop can initialize more than one
variable by separating each variable assignment with a
Page 112

comma (index1 = 10, and index2 = 15). Also, you can declare variables in the initialization part. These declared variables
are known only in the for loop (are local to the loop).

Perhaps an example will drive this point home:

for (int idx = 0; idx < 10; idx++ )


System.out.println( "Inside loop " + idx ) ;

System.out.println( "Outside loop " + idx ) ;

Javac responds to this code with the following diagnostic:

Undefined variable: idx


System.out.println( "Outside Loop " + idx ) ;
^

As an aside, note that this for loop does not use curly braces. Later on, you'll read more about the use of these braces in loops
and decision statements. The short story is that when your program executes only one statement in a loop, your program does
not need the braces surrounding that single statement.

2. The Execution Test

The execution test, represented by executioncondition, is a boolean value that governs the execution of the loop. Simply put,
as long as executioncondition is true, the loop executes. Your job is to look twice to insure that executioncondition will
eventually change to false, or else your loop will attain a somewhat permanent execution status.

3. The Changing of the Loop Index

The Changing of the loop index is represented by changeindex. In a for loop, the index is connected to the executioncondition.
The usual method of changing the executioncondition is by operating on the index in the changeindex section. The examples
we've seen show the use of Java's unary increment operator, which is arguably the most common use of this operator.

You can code any expression that changes the index.

4. The Loop Body

The loop body is represented by the braces and the statements between and braces. What's a loop without a loop body? The
loop body is the real work of the loop and will execute as long as executecondition is true. You've figured out that you need to
sandwich loop body statements between curly braces if you want these statements to execute as a unit or a block. Curly braces
are Java's
block construct. We'll meet up with the block construct many a time in this chapter.

Now, you could code an empty loop, a loop without a body. For example, if you wanted to find the first element of an array
that has a certain property, you could code

for (int idx = 0;


idx < myArray.length && hasTheProperty( myArray[ idx ] ); idx++ ) ;
Page 113

For the purposes of this somewhat contrived example, assume that the method hasTheProperty()
returns true if the indicated array element has the property, false otherwise. You recall from Chapter 5 that Java arrays start
with a zero index, right? Also, you recall from Chapter 5 the length property, right? Finally, you recall from this chapter why
this example uses the conditional AND, not the
boolean AND, right?

Note the semicolon after the previous for statement. The Java files are replete with the sad saga of programmers who
mistakenly code a construct similar to the following one:

for (int idx = 0; idx < aNumber; idx++) ;


{ Statement 1

Statement n
}

The semicolon— yes, the big one— after the for statement, causes Java not to associate the statements in the following braces
with the for statement. In short, the previous for statement coded is an empty loop. The statements between the braces execute
once. The Java compiler will not indicate the presence of an empty loop. However, if a variable declared in the for statement,
like idx above, is referenced in the following block, the compiler will report that idx is an undefined variable.

It is virtually impossible to mistakenly code an empty loop in COBOL or PL/I because of the loop delimiters used in these
languages. Could you see yourself coding anything like the following?

* COBOL /* PL/I */

Perform Do Idx = 1 to 10 ; End ;


idx Varying from 1 to 10. End-Perform.

In brief, the Java for loop executes like its mainframe language brethren's loop. The initialization section is executed first. Next,
the executioncondition evaluates. If true, the loop executes. After the statements in the loop body execute, the changeindex part
executes. The cycle of executioncondition, followed by statement body execution, followed by changeindex part, continues until
executioncondition is false (or a program error causes a choke).

Of course, for statements may be nested. Nested loops in Java behave like their mainframe language brethren. Nested loops
are commonly used to process elements in multidimensional arrays. Most programming languages have a limit on the number
of nested loops; PL/I and COBOL have a limit of 16 nested loops. Java, being interpretive, doesn't seem to care; you can code
40 plus nested loops and the compiler won't even peep. Of course, come execution time, the Java Virtual Machine and Java
Runtime will be playing a different tune.

Java supports conditional loops, or loops that execute a body of statements depending on the truth value of a boolean
condition. Now is the time to explore these useful constructs.
Page 114

Did you catch the comment about a Java for loop having up to four parts? Well, you can omit parts of a for loop provided you
take care of the necessary details elsewhere. Actually, Java doesn't care if you omit parts of the for loop. You, on the other
hand, will probably care a great deal when your loops don't terminate or variables assume (incorrect) default values. In any
event, this is a valid Java for loop:

int idx = 0 ;
for (; idx < 10; ) {
// Body of the loop idx++ ;

Note that the index variable initialization is done when the variable is declared and the index variable increment is done inside
the loop. This coding construct falls into the category of "you should know what this looks like in case you come across it."

The following will be of no surprise. Like iterative loops in most programming languages, the index variable is incremented
before the test is applied. Ergo, in this code snippet:

int idx ;
for (idx = 0; idx < 10; idx++ ) {
//Do something here
}
System.out.println("outside idx = " + idx ) ;

Java prints out 10.

The savvy programmer insures that the ignominious fate of having index variables one off by not relying on index
variables for processing outside the loop. Fortunately in Java, you can easily enforce this by declaring your index variable
on the for statement as described a few pages ago.

The Java while Loop

As the name implies, the Java while loop executes while a condition is true. In brief, the condition is first evaluated; if true, the
loop body executes. Control is transferred to the while statement where the condition is reevaluated; if true, the loop body
executes. This cycle of condition test/loop body execution stops when the condition becomes false.

The basic template is

One or more statements setting the value of aConditionIsTrue while ( aConditionIsTrue ) {

one or more statements that constitute the loop body

one or more statements that eventually will change the


value of aConditionIsTrue
}
Page 115

Syntactically, the bare-bones while loop is

while ( aConditionIsTrue ) { code block }

However, you'll rarely see while loops like the bare-bones variety. A well formed while loop can have four parts.

1. Statements that set the initial value of the condition. The condition in the while loop, represented by aConditionIsTrue
in the basic template, must be true for the loop to execute. The condition is evaluated first. If the evaluation resolves to
true, the loop body executes. Your program should have one or more statements that set the initial value of the
condition. Now, these statements need not be coded immediately before the while statement, but they should be
reasonably close to the while loop itself.

2. The condition that governs the execution of the loop. There is not much to add here. Remember that you can use
either boolean or conditional operators for compound conditions.

3. The Loop Body. Like the for loop previously discussed, what is a loop without a loop body? If your loop body consists
of more than one statement, you'll need the curly braces to form a block. Java accepts empty while loops, however,
such loops will cause your program to enter a permanent state of execution. Can you figure out why?

4. Code that eventually changes the loop condition. Recall that a for loop has a built-in self- terminating mechanism if
you code it properly. A bare-bones while loop has none; if the loop condition is true and there is no code to change
the condition, the loop will execute forever.
Because nothing you do is important enough to continue forever, you should take great pains to insure that your
loops terminate.

That is why the well-formed loop has code that will, upon successive executions, change the value of the condition.

Here is an interesting and somewhat counterintuitive observation made over the coding of many while loops: The code
that sets the initial value of the condition is often identical to the code that eventually changes the condition. Here is an
example:

custName = getNextCustomerName() ; while ( custName !=


null ) {
//Loop Body executes here

//Get another customer


custName = getNextCustomerName() ;
}

For the purposes of this example, assume that getNextCustomerName is a method that returns an object representing a
customer name or null if no name is available. Note that the method invocation is used to set the initial condition and to
(eventually) change the condition.
Page 116

Occasionally, you can encapsulate various processing details in methods and really streamline your code. One technique is to
have a method return a boolean and to use that return value as a conditional in a loop. Your opinion, please:

while ( weHaveaCustomerName( ) ) {
//Loop Body executes here }

boolean WeHaveaCustomerName( ) {

custName = getNextCustomerName() ;
return( custName != null ) ;
}

Notice that the method WeHaveaCustomerName tends to the checking for null after the invocation of method
GetNextCustomerName( ). This technique works with any programming language that enables you to code functions (code
that returns a single value) or methods that can return boolean values. This technique will be used quite a bit in the longer
examples of the book. Alas, poor COBOL programmer, this technique is not available to you.

One point worth noting— If the code that sets the initial condition sets that condition to be false, the loop body does not
execute. In our small, previous example, this makes sense; if no name is fetched, there is no need to enter the loop.

Here are the analogues in mainframe programming languages:

* COBOL /* PL/I */

* Code that sets condition Perform With Test /*Code that sets condition */ Do While
Before Until NOT aConditionIsTrue.
(aConditionIsTrue);

* Loop Body /* loop Body */


* Code that changes /*Code that changes
* Condition condition*/
End-perform End ;

COBOL conditional testing is based on executing statements until a condition becomes true. In COBOL, if the condition is
true, the statements in the perform group do not execute, hence, the use of the NOT operator.

Let's look at a different flavor of a Java conditional loop called a do while loop.

The Java do while Loop

This conditional loop is very similar to the while loop previously described. The syntactic difference is, of course, the use of the
word do; the operational difference is the location of the testing of the condition. Java's do while loop tests the condition at the
bottom of the loop.
Page 117

Here is the basic layout of Java's do while loop:

do {
One or more statements that constitute the loop body

One or more statements setting and eventually changing


the value of aConditionIsTrue
}
while ( aConditionIsTrue ) ;

Note that you still need some code to set and change the value of the condition used to govern the loop's execution. Also,
there is no hard and fast rule about the placement of the condition setting code relative to the loop body.

You see, perhaps, the most important difference between the while and do while loops, right? Once you read that the do while
loop tests the condition at the bottom, it was pretty obvious. This difference is that a do while loop must execute at least once, whereas
a while loop may never execute at all. This difference governs when you should use a while loop versus a do while loop.

The PL/I and COBOL analogues to Java's do while loop are shown in the following:

* COBOL /* PL/I */

Perform With Test After Do Until


Until NOT aConditionIsTrue. (^aConditionIsTrue);
* Loop Body /* Loop Body */
* Code that changes /* Code that changes
* Condition Condition */
End-Perform. End ;

Note: The use of the NOT operators in each language to create an equivalent construct to Java's do while looping construct.

Interrupting the Normal Processing of Loops

In general, processing enters code blocks in loops from the to and exits from the bottom. However, Java gives you the capability
to code exits from the middle, or anywhere, inside a loop. Java contains two statements that support premature loop ejection: break
and continue statements.

The break Statement

You have already seen the break statement used in switch statements. You may not have guessed that break statements may
also be used in loops. In a general sense, the break statement transfers the program's flow of control out of the currently
executing block as long as that block is in a loop or switch statement. As with the switch statement, the
Page 118

break statement causes Java to leave the currently executing loop and pick up execution at the first statement after the loop.
A simple example should suffice:

int idx ;
for (idx = 5; idx < someString.length; idx++ ) {
if (idx == 7)
break ;

System.out.println( "Inside the Loop " + idx ) ; System.out.println( "Outside the loop = " +
}
idx ) ;

Well, assuming that someString has more than six characters, Java will encounter the break statement inside this loop.
Once encountered, program control flow exits the loop and continues at the first statement outside the loop. Therefore, this
little code snippet will produce the following output:

Inside the Loop 5 Inside the


Loop 6 Inside the Loop 7
Outside the Loop 7

When you place a break statement inside nested loops, the break statement will cause program control to exit from the
inner loop to the statement after, in the outer loop. Assuming the outer loop will execute, your program will enter your inner
loop again. The following example shows this:

int aIdx, bIdx ;


for (aIdx = 5; aIdx < 7; aIdx++ ) {
System.out.println( "Outer loop = " + aIdx ) ; for (bIdx = 10; bIdx < 15; bIdx++ ) {

if (bIdx == 12)
break ;
System.out.println("Inner loop = " + bIdx ) ; }

The output follows:

Outer loop 5
Inner loop 10
Inner loop 11
Outer loop 6
Inner loop 10
Inner loop 11
Page 119

The continue Statement.

Another Java statement enabless a program to sidestep normal loop processing. The continue
statement, when executed, causes the program's flow of control to go to the bottom of the loop. REXX programmers who are
familiar with the REXX Iterate statement will immediately understand how to use Java's continue statement. The following
shows how the continue statement acts with Java's three different loop types:

When used in for loops, the continue statement transfers the flow of control to the loop bottom. From the bottom, the program
flow heads to the for statement, where the increment expression is evaluated, the test is made, and, if successful, the body of
the loop executes.

When used in while loops, the continue statement transfers the flow of control to the loop bottom. From there, program flow
heads to the top of the loop where the condition in the while statement is still true; remember— the continue statement
caused your program to skip past the code in the loop that could have changed the value of the loop conditional. Hence, the
continue statement in a while loop will cause the loop body to execute.

When used in do while loops, the continue statement causes the same result as being used in while
loops.

Here is another way of looking at the continue statement:

//Using the continue Statement for ( int a=5; a<10; Not Using Continue
a++){ for (int a=5;a<10; a++) { if (a != 8) {
if (a == 8) continue ; //Remainder of the
loop //body is here //Remainder of the
//loop body is here }
}
}

It looks a bit cleaner to use the continue statement. What do you think?

Enough about loops, okay? Next, you'll read about the terribly exciting topic of decision constructs.

Java Decision Constructs

Java decision constructs fall into two categories: if statements and switch statements. Let's examine these statements in turn.

Java if Statements

Java if statements are identical to C if statements. However, you would rather know how similar Java if statements are to
COBOL or PL/I if statements. You should find this out for yourself. Here is a basic template for a Java if statement:
Page 120

if ( aConditionIsTrue ) {
// Execute this block of statements when condition is true }

else {
// Execute this block of statements when condition is false }

Notice the absence of the word then in the construct. Notice, and remember— no then in Java if statements.

Of course, you realize that the else clause is optional. You also realize that if you want to execute only one statement after
the if or else, the block brackets are not necessary.

You also realize that you can nest if statements as follows:

if ( aConditionIsTrue ) {
// Execute this block of statements when aCondition is true }

else
if (bConditionIsTrue ) {
// Execute this block of statements when bCondition is true }

if (cConditionIsTrue ) {
// Execute this block of statements when cCondition is true }
Y
FL

Remember the oddball ternary operator? Can you see how this operator can take the place of constructs like the
following?
A M

//Simple if Construct Assigning a //Variable One of Two Ternary Operator


Values That Does The Same Thing
TE

if ( aConditionIsTrue ) aVar = aConditionIsTrue


aVar = valueA ; ? valueA: valueB ;
else
aVar = valueB ;

You think there is a place for the ternary operator in your Java programming bag of tricks?

Java if statements behave much like COBOL and PL/I if statements. There isn't much left to add about if/else statements. You've
seen them before and you'll see them again. Remember to choose the correct logical operator— the conditional versus the boolean
operators to form your conditions.

Java switch Statements

Java supports a construct that enables your code to test a variable's value against a list of values. You can use a series of if/else
statements as follows:

Team-Fly ®
Page 121

if (sysCode == 'A') //You remember that == is the //compare operator,


doAMeth() ; right?
else
if (sysCode == 'B')
doBMeth() ;
else
if (sysCode == 'C')
doCMeth() ;
else
if (sysCode == 'D')
doDMeth() ;
else
doEMeth() ;

You've seen this sort of stuff before, right? You, of course, would never code anything so crass. You'd use a case construct. In
COBOL, the case construct is called an Evaluate statement; in PL/I, the case construct is called the Select statement. Java's
implementation of the case construct is called the switch statement. Listing 6.1 shows how it looks.

Well, it looks like all the ingredients for a case construct are present. The default option— the last option coded in the Java
switch statement— is a catch all category. However, the Java switch statement has break statements after every option.

The Java switch statement has a couple of unexpected surprises. First of all, you need a break
statement after each choice or Java will fall into the other choices. For example, without the previous break statements, all of the
methods, doA through doE, would be invoked regardless of the value of sysCode.

When Java hits a break statement, control transfers to the first statement after the switch closing curly brace. That said, the
case will sometimes occur when you may want the code in your switch statement to fall through. In other words, you may not
want to code a break statement for one or more options. Let's say you want the same action(s) to take place when a variable is
any one of three values. You could code your switch statement as follows:

switch (aVar) {
case val1:
case val2:
case val3:
TakeSomeAction( aVar ) ; break ;

default:
;
}

Also, notice the null statement in the default option. If you can't think of anything to code for a default, the null Java
statement often fits the bill.

Another unexpected surprise, especially for a modern programming language, is the limited functionality of the
switch statement. Java's switch statement can only use
Page 122

JAVA COBOL PL/I


switch( sysCode ) Evaluate Select (Syscode) ;
{ When SysCode = 'A' When ('A')
case 'A': Perform doAMeth
doAMeth() ; Thru doAMeth- Call doAMeth();
break ; Exit. When ('B')
case 'B': When ('B') Call doBMeth();
doBMeth() ; Perform doBMeth When ('C')
break ; Call doCMeth();
case 'C' : Thru doBMeth- When ('D')
do DMeth() ; Exit. Call doDMeth();
break; When SysCode = 'C' Otherwise
case 'D': Call doEMeth();
doDMeth() ; Perform doCMeth End ;
break; Thru doCMeth-
default: Exit.
doEMeth() ; When SysCode = 'D'

Perform doDMeth
Thru doDMeth-
Exit.
When Other
Perform doEMeth
Thru doEMeth-
Exit.
End-Evaluate.
Listing 6.1 Case constructs in several programming languages.
Page 123

values that are castable to integers. For example, if I wanted to compare a variable's value to a floating point number, I
would have to use a series of nested if/elses. Java's switch statement can't cope with nonintegral values. Before you
comment on the previous example shown in Listing 6.1 using characters in a Java switch statement, recall that Java treats
characters as integers.

Note that you do not need to surround the statements after the case keyword with braces. Yes, it certainly looks strange
considering that braces are used everywhere else when more than one statement is executed as a block. You could use the
curly braces if you wish; javac will accept the code, but you don't have to use the braces.

Unfortunately and regrettably, the only operator allowable in the Java switch statement when comparing the variable's value is
equality. For example, this PL/I Select statement has no direct Java switch statement analogue:

Select ;
When ( RetirementMoney < 150000 & Age > 40 )
Put Skip List( 'You're running out of time') ; When ( RetirementMoney > 1500000 &
Age > 40 )
Put Skip List( 'Whatcher waiting for?' ) ; When ( RetirementMoney > 2000000
& Age > 70 )
Put Skip List( 'Old Folk Sure Have All the Fun!' ) ; Otherwise

Put Skip List( 'Get back to Work' ) ;


End ;

You would code if/elses to mimic this functionality in Java.

The Java switch statement is a construct of limited functionality with quirks that betray Java's C heritage. However, even
with limited functionality, you'll be coding many switch statements. Remember to put in that default option; the braces to
group code after each option are not required.

In Summary

You've read much in this chapter. You've read about Java's bizarre operators, the likes of which have never been seen in the
mainframe world. You've read about the essentials of busting Java code— the big three statement types: assignment
statements, loop statements, and decision statements. You've even learned a quirk or two about Java.

This chapter is only one of a book containing 26. You've read so much about Java. At this point, you see that despite the hype
hysteria and hoopla, Java is a programming language. Subsequent chapters will deal with the object-oriented nature of Java.
However, you cannot exploit Java's OO properties without an understanding of the material in this chapter.

After all, how can you write any methods unless you know how to code the big three?
Page 124

This page intentionally left blank.


Page 125

CHAPTER 7
Class and Object Representation

Java's Object/Class representation, arguably the heart of any object-oriented language, is discussed in this chapter. You'll read
about the makeup of a Java method, the contents of a Java class, and how to create objects from classes. Also, you'll see how
to code methods, which is the meat and potatoes of implementing object behavior. Along the way, you'll read a thing or two
about Java packages.

Anatomy of a Java Method

In Chapter 3, "Creating Your First Java Program," you've seen the main() method, chock full of Java keywords. In general,
Figure 7.1 shows what a method header looks like.

Not shown in Figure 7.1 is the method body, which is responsible for implementing the behavior the method is to model.

You'll read a brief description of some keywords here and a not-so-brief description later in the chapter.

Visibility Modifiers

Java enables you to dictate what other members (methods, classes, and packages) can access or use your methods by coding
an optional visibility modifier. The following descriptions in Table 7.1 apply to any member type (method, class, and package)
declared with the modifier. At most, You can code one visibility modifier per member. Table 7.1 lists your choices.
Page 126

Figure 7.1 A Java method header.

Table 7.1 Visibility Modifier Overview


VISIBILITY
MODIFIER CAN BE SEEN (USED) BY:
public Every class of your application (the world)
protected The package (if one exists) that holds the class containing the member, the class
containing the member, and all subclasses of the class containing the member

Default Package The package (if one exists) that holds the class containing the member and
Visibility (not the class containing the member.
coded)

private Every method or class within the class containing the member.
Page 127

Here's the short story on when to use which visibility modifier.

Use public when you want free and unfettered access to the member. Period.

Use protected when you want to keep classes from changing data in the member and you want subclasses (Chapter 9,
"Inheritance" explores subclasses further) to have full access to the member.

Use the default package visibility ( do not code a visibility modifier) if you want only the members in the package or
class containing the member to have free and unfettered access.

Use private when you want the only methods in the class containing the member to access the member.

You'll return to visibility modifiers in Chapter 8, "Encapsulating and Hiding Data and Methods," when you read about
data hiding and encapsulation (big word!).

Other Modifiers

Java enables you to declare an additional modifier. These modifiers cannot be lumped into a single category like the visibility
modifiers. As with the visibility modifiers, these other modifiers apply to classes and, sometimes, variables and methods. You
may code more than one of these modifiers, at times, for the same member. Here's the list:

An abstract method is a method that contains no body; an abstract class contains one or more abstract methods. You'll
explore abstract classes in Chapter 9.

A final method is one that cannot be overridden, a final class cannot be subclassed, and a final
variable cannot be changed (constant declaration). The final modifier is the opposite of the abstract modifier.
Again, you'll read more about the final modifier in Chapter 9.

A native method is implemented in some other programming language (usually the C programming
language).

A static method is a method that models some behavior applicable to the class as a whole; a
static variable is a variable that represents a property applicable to the class as a whole. You'll read more about static
methods and variables in this chapter.

A synchronized method, class or code block (code between curly braces) causes the runtime to lock referenced objects to
prevent other processes, or threads from changing them. Threads are discussed in Chapter 12, "Exception Handling and
Thread Basics."
Page 128

Returned Types Coded in Method Headers.

Java methods may or may not return a value. The classical distinction in procedural programming is of a function that returns a
value and a subroutine that does not. If your Java method does not explicitly return data using a return statement, you must
code void in the function header. If your Java method uses a return statement, you must code the primitive data type or the
class of the data item being returned.

Method Names and Argument Lists

A Java method name can be a string of practically any length not containing spaces and not beginning with a number.
Typically, method names should be verbs that denote some action, indicative of what they do; whereas variable names
should be nouns, indicative of what they are.

An Argument list, if present, is a comma delimited list of Primitive Type/Class-variable name pairs. If the method does not
require an argument, you must code a pair of empty parenthesis. The variable names used in the Type/Class-name pairs have
meaning only within the method. Think of these argument variables as local to the method.

As an aside, Java passes arguments of primitive types by value and arguments of objects by internal reference. The long and the
short of it is that for primitive type arguments, Java passes a copy of the argument to the method. Therefore, the method is free to
make changes to primitive type arguments and these changes will not be made to the corresponding parameters in the calling
method. In contrast, for object arguments, Java passes a reference of the argument to the method. Therefore, if the method
changes the argument object, the changes will be made to the corresponding parameter in the calling method. That's why objects
in Java are also known as reference types.

The throws Exception-Name Option in Method Headers

Chapter 12 discusses exceptions. For now, know that a method may include an optional throws exception-name option,
which specifies that if a coded class of exceptional condition(s) arise, the condition trickles up to an error handler, where
(hopefully) you've written code to tend to the condition.

Passing by Reference and Passing by Value

Let's face it; a program written in a procedural language is a coupled collection of procedures acting independently on data in
various formats. Each procedure does one of three things to a piece of data: gets it into the system, transforms the data into
some other data, and gets the data out of the system. The issue is how do you, the long suffering and humble programmer,
ensure that these procedures act only on the data you want to be acted on, and no other?

The nature of programming languages used in the mainframe world, with their representative typing scheme, is an accident
waiting to happen. Procedures in these
Page 129

languages will accept any data as long as the passed data bears a resemblance to the correct parameters. Many programmers
get into trouble because they may not fully understand how parameters are passed to subprograms. In other words, you can pass
parameters to a subprogram with the intent of having that subprogram change the parameter value, thereby making the changed
value known in the calling routine. Conversely, you can pass data to a subprogram with the intent of not changing the parameter
value. The terms used to describe these mechanisms are passing parameters by reference and passing parameters by value.

You've read about these mechanisms in Chapter 2, "What is Java?"; no need to rehash the information. What needs to be said
here is that Java passes arguments of primitive types by value and arguments of reference types by reference. Hence, the code
snippet:

int aNum = 10 ;
doit( vStack, aNum ) ;
System.out.println( "aNum = " + aNum ) ;

static void doit( VectorStack aStack, int aNumber ) {


aNumber++ ;
VectorStack.pushTheStack( aStack, "A Stack Element" ); }

would list aNum = 10. The value of the parameter aNum is not changed in the method; the method operates on a copy of the
parameter. However, because the argument aStack is not a primitive type, the invocation of the method called
pushTheStack() inside method doit() changes the object.

A Word or Two about Java Packages

A Java package is a construct that contains related classes and other Java entities. Packages have two important traits. One trait
is that every method contained within has a unique name. Figure 7.2 shows how you name methods with packages to get this
absolute uniqueness.

The first method name comes from the Java class library; the second is a method name thought up by DigVidIncLtd. Every
word to the left of the class name is deemed to be the package name. In the case of DigVidIncLtd, their Web site is http://DigVidIncLtd.org
, which is guaranteed to be unique. Hence, the method name is unique.

How do you make these methods known to your program? You need to direct your system to look on a directory (local or
remote) where the desired package resides. Then you code the method name, package and all, or use the Java import statement.
Look at the following:

public MyClass {
//Java statements
void myMethod() {
//Java statements
org.DigVidIncLtd.RandD.frequencyTransform.fourier(aFrame);
Page 130

Figure 7.2 Guaranteeing unique method names.

This is what you expected, right? All you do is refer to the method name. The Java compiler and runtime are responsible
for locating the package, with a little help from you.

The import Statement


Y

As straightforward as the previous is, no true, red-blooded Java programmer would enter such a long method name. If all you
FL

need is one method from the package, well, maybe. However, if you need several, you could wear out your fingers with all that
typing. Fortunately, Java provides a mechanism for dealing with this problem: the import statement.
A M
TE

The import statement merely provides a shorthand for referencing entities from packages. Take a look at Listing 7.1.

The import statement does not "bring in" any code to your program. The import statement does not behave like a COBOL
COPY or PL/I %Include statement. You can have zero to many import statements. The presence or absence of import
statements has no effect on your program's size or execution speed.

The difference between the two forms shown in Listing 7.1 is that the first one enables the program to use shorthand to access
any class in the package, whereas the second form enables the program to use shorthand only for the named class.
Understand that you can always code the fully qualified method name.

Some say that you should use the second form to clearly show where your classes are coming from. Sounds like good
advice. Now, if you're using 15 or so classes from one package, I'm sure the Java style police aren't going to bust you for
using the first

Team-Fly ®
Page 131

import org.DigVidIncLtd.RandD.* ;
//or import org.DigVidIncLtd.RandD.frequencyTransform ;

public MyClass {
//Java statements
void myMethod() {
//Java statements
frequencyTransform.fourier( aFrame );

Listing 7.1 Using the import statement to refer to a method in a package.

form. As a programmer who has picked up someone else's code from time to time (we all have, right?) and wished the code
were better documented, you should appreciate every little mechanism provided by the environment and programming
language to document things. End of editorial comment.

What is the other important trait? Package names mirror the directory where they are stored. This mirroring is partly how
the Java compiler and runtime can find the package. Using our previous examples, there would be a subdirectory
structure java\lang ( UNIX), java/lang ( Windows), or
java:lang ( Mac) within a group of directories called the Classpath.

The package Statement

You can (and should) group related classes into packages. Look, if it's good enough for the developers of Java, it's good
enough for you. It's pretty easy to create a package. All you do is put your (hopefully) related classes in one source code file
and code a package statement as the first line in the file. To wit:

package myPackageName ;

class MyClass1 {
/** methods, etc. for MyClass1 **/ }

public class MyClass2 { /** Ditto **/

}
class MyClass3 {
/** Get the idea? **/ }

Note the absence of a curly brace; just what you see previously, okay? You can use whatever package name that will
guarantee uniqueness.
Page 132

One caveat— recall from the previous that the package name must reflect the actual directory structure where the file is
stored. Therefore, pay heed to your package name and where you put the compiled package. Java will look along the
directory structure mandated by your package name.

Here's where the visibility modifiers come in handy. If you want classes and methods coded in a package to be visible
outside the package, you must remember to use the public visibility modifier on those classes. Now, when you use an
import statement like the following:

import myPackageName.* ;

You'll import only those classes with the public modifier.

When you compile a source file with a package statement, Java creates a directory structure that mimics the package
name. Compiling the previous example would create a directory called
myPackageName. Inside the directory would be the class files MyClass1.class, MyClass2.class, and MyClass3.class.

Anatomy of a Java class

Recall that a class is a template used to create objects that have the same behavior but different properties. Every Java source
code file compiles into a class file. Put differently, class files represent the platform-neutral bytecode you've heard so much
about.

Every Java class is a subclass of class Object. In other words, class Object is the root class. The upshot of every class being a
subclass of Object is that methods defined in class Object are available to each and every Java class. You'll read about the Object
class, and how Java implements subclasses and implements Inheritance in Chapter 9.

Because a single Java class could be an entire application, you could find many Java software elements, or members, inside
a class. Table 7.2 lists what you could find in a typical Java class.

Let's describe each of these members in more detail.

Constructor Methods and Instance Variables

When you create an object (instantiate the object) from a class, just what happens? In short, the created object acquires
behaviors from the class (the methods) and some piece of user code imbues the object with properties (the data). Generally, the
data from one object is what distinguishes it from another of the same class (of course, two identical objects could have the
same data but different names, but that's why the preceding sentence began with "generally").

In Java, you can accomplish the previous by coding and invoking a constructor method. Let's take a look at Listing 7.2.

Before you think you are straying from a description of the items in Table 7.1, fear not. In Listing
7.2, the variables autoName, modelYearYYYY, and retailValue are Instance
Page 133

Table 7.2 Class Member One-Liner Descriptions

A. Members That Are Used with Objects Instantiated from the Class
Constructor Used to implement some custom object creation process.
Methods

Instance Variables Used to hold data that describes some property that may be unique to the object

Instance Methods Used to implement some behavior that is usually common to all objects
instantiated from the class

Finalizer Methods Used to detach resources held by some objects that cannot be released by the
system

Inner Classes Used to create objects that are wholly contained within other objects, code
blocks, or methods

B. Members That Are Used to Describe or Implement Some Behavior Peculiar to the Class

Class Variables Used to hold data that describes some property of the class

Class Methods Used to implement some behavior peculiar to the class as a whole Used to

Static Blocks implement some initialization for the class

Nested Top-Level Used to group related classes within the class together
Class

Variables. These variables are present with every instance of class Automobile. Put differently, every object from class
Automobile has the properties autoName, model YearYYYY, and retailValue.

When you create an object from class Automobile, you can provide values for these properties by invoking a constructor
method. Note the following properties of the constructor method, shared by constructor methods, shown in Listing 7.2:

The constructor method name is always the same as the class name. The constructor

method cannot have a return statement.

More often than not, constructor methods have public visibility. You'll read more about visibility modifiers later in this
chapter. For now, a member declared with public visibility means that the member can be accessed by any class in the
application.

How do you create an object? You call its constructor. And, pray tell, how is that done? Well, you invoke the constructor
with the new operator. Behold and learn in Listing 7.3.

Once the bolded statement in Listing 7.3 executes, the object myCar has its instance variables set to the values passed to the
constructor. Also, any methods defined in class Automobile are now a part of the object myCar.
Page 134

//Here is our class . . . . public class Automobile {

String autoName ;
int modelYearYYYY ;
/** Other variables relevant to a particular Automobile **/ int
retailValue;
/** Other variables, methods, any or all of the stuff
listed in Table 7.1 **/
/** Here is a constructor method **/ public Automobile( String
aName,
int aYear,
/**Other args for other vars **/ int
aValue) {
autoName = aName ;
modelYearYYYY = aYear ;
/** Other variables relevant to an Automobile retailValue =
object set here **/
aValue ;
}
}

Listing 7.2 A class with a constructor method.

public class someClass {


//Create an object of class Automobile
Automobile myCar = new Automobile ("Rolls Royce
Silver Ghost",
1942,
/** Other Parms **/
87500 ) ;
}

Listing 7.3 Invoking a constructor method.

To refer to the instance variables for a particular object, you prefix the instance variable name with that of the object. For
example, to reference the value of the object created previously, you would code:

myCar.retailValue

There's more to say about constructor methods. You could have more than one constructor method for objects of the same
class. Before you ask why you would want multiple constructors, first take a look at Listing 7.4 to see how you code multiple
constructors.
Page 135

/** Here is another constructor method **/ public Automobile( String aName,

int aYear,
/**Other args for other vars **/
){
autoName = aName ;
modelYearYYYY = aYear ;
/** Other variables relevant to an Automobile
object set here **/
retailValue = 0;

Listing 7.4 Another constructor method.

This constructor has the same name as the first. Also, you would code the second constructor in the same source file as the
first. How can Java tell them apart?

You see that the signatures of the two constructors differ, don't you? The second constructor does not take retailValue as a
parameter; the car is probably too old and not worth selling. The second constructor assigns a blue book value of 0 to all
Automobiles created with this constructor.

There's still more to say about constructor methods. You don't have to code one, you know. The skinny is that if you don't,
Java will use a default constructor with no arguments. However, most objects will require some sort of initial data, which
means that you'll need a "real" constructor of some sort. The default constructor insures that every class has a
constructor, although the default constructor doesn't do much.

Another point about constructors is that you cannot invoke a constructor method apart from creating an object. Too bad,
because such a capability could prove useful if you needed to reinitialize an object. Perhaps in a future release.

Believe it or not, there's still more to say about constructors, but you'll have to wait until Chapter 9, where you read about
superclasses and subclasses.

By the way, Java does not require you to code anything to destroy an object. Some programming languages (C++ comes to
mind) require you to code destructor methods to remove unused objects, thereby reclaiming the memory for subsequent use.
Java's garbage collection does this reclaiming for you. In short, Java's memory management keeps track of object use and
destroys those objects not in use. If you had to write code to free memory by destroying objects yourself, you know what a relief
it is to have the runtime do this unpleasant, error-prone task for you?

Back to the subject of creating objects, you may think that you can create an object the same way as you create a variable of a
primitive data type. Why not just declare the object with the class name as the object's type, as follows?

Automobile someonesCar ;
Page 136

Truth be told, you could code the previous and Java would successfully compile. However, you have not created an object from
class Automobile! What you've done is create a reference for an object of class Automobile.

Let's step back. No, we're not splitting hairs here. A huge difference between the object and the reference to the object exists.
Think of the difference between a variable and a pointer to the variable (although, as you know, Java doesn't enable the
programmable use of pointers). Take a look at the code in Listing 7.5 and anticipate the output.

Class Demo1 has a main() method, so we can run this and get output. Note the use of the constructor method to create the
object named yourCar. Seems you're riding fancy these days. Looking at line //1. You see that this statement does not use the
constructor; this statement declares an object reference to aCar. Line // 2 looks like it assigns the contents of yourCar to aCar. In
other words, both objects would seem to have the same data. Line // 3 changes the value of the autoName property of the aCar
object, right? Thus, the output statements should list the autoName property values of both cars: "Dodge Stealth" and "Ford
Fiesta," right?

Well, here's what's going on. While line // 2 is assigning the contents of yourCar to aCar, it is also assigning the reference of
yourCar to the reference of aCar. Now, yourCar and aCar reference the same object. Along comes line // 3 to change the
value of the autoName property of aCar and yourCar, because both names reference the same object. Now, when the output
statements execute, you'll see:

You Drive a Ford Fiesta


The Other Car is a Ford Fiesta

So much for trading up!

Pop quiz: what if statement // 1 in Listing 7.5 were coded:

Automobile aCar = new Automobile("Buick", 1993, 3000) ;

public class Demo {


public static void main(String[] args) {
Automobile yourCar = new Automobile("Dodge Stealth",
1999, 56500) ;
Automobile aCar ; // 1
aCar = yourCar; // 2
aCar.autoName = "Ford Fiesta" ; // 3
System.out.println( "You Drive a " +
yourCar.autoName ) ;
System.out.println( "The Other Car is a " +
aCar.autoName ) ;
}
}

Listing 7.5 A nasty shock!


Page 137

The object aCar is now created with a "real" constructor. Would the output produced by the code in Listing 7.5 with the new
line // 1 be now? If you thought that you'd be holding onto that fancy, fast Dodge Stealth, you'd be wrong. The behavior of the
assignment of the object references on line // 2 does not change. The labels aCar and yourCar still point to the same object.

Do you recall that in Java, a string is an instance of class String? One consequence is that you cannot do what you think is
obvious with strings. Listing 7.6 shows some innocent looking statements that behave in a counterintuitive manner.

Here's the output:

1 and 2 Equal Strings Hello There


1 and 3 Unequal Strings Hello There

What gives? The equality test does not compare the contents of the string objects; the equality test compares the references of
the string objects. Now, Java is "smart" enough to reuse the storage allocated for string1 and string2. In essence, string1 and
string2 are the same object. However, such is not the case for string1 and string3. Java sees the initialization of these string
objects results in different strings and considers them different objects. However, when you change string3 to have the same content
as string1, Java still considers them different objects. When you do the second compare, Java tells you that these string objects
are, indeed, different.

Remember that Java variables declared of a primitive type behave as expected, or behave as variables from a
procedural language. Java Objects, however, behave a bit differently.

A sober question is how would you assign the contents of one object to another of the same class but maintain the first object's
reference?

String string1 = "Hello There" ; string2 = "Hello


String There" ;
if ( string1 == string2)
System.out.println("1 and 2 Equal Strings " + string2) ; else

System.out.println
("1 and 2 UnEqual Strings " + string2 ) ;

String string3 = "Hello " ;


String3 = string3 + "There" if ( string1 == string3 )

System.out.println("1 and 3 Equal Strings " + string3) ; else

System.out.println("1 and 3 Unequal Strings " + string3) ;

Listing 7.6 You know what will happen, right?


Page 138

Instance Methods

In truth, Java provides a mechanism for assigning objects to one another by using a clone method in the java.lang.Object
class. However, by showing different approaches to rolling your own assignment method, you will read about the different
method types available to you. So, without any further ado . . .

One solution to the previous question is to code a method like the one in Listing 7.7.

You see the logic here, right? We sidestep the assignment operator that, for objects, assigns references by assigning the object
properties. The void keyword tells Java that this method does not return a value.

Where do you put the method? You could put this method in class Automobile, under the constructor method. Listing 7.8
shows the code for class Automobile.

Coding the assignTo() method in class Automobile makes this new method part of the "template" for the class. Objects
instantiated from class Automobile will have this new method as part of their behavior. Put differently, this new method is an Instance
method.

Now, how do you use this assignTo() method? Let's look at the Demo class with a change in Listing
7.9 that answers the aforementioned question.

Well, this certainly looks good except for one small detail: The code in Listing 7.9 will not compile.
The reason occurs because the assignTo() method, an instance method, is not attached to an object.
Java expects an object to go along with this method. Therefore, either of the following statements will satisfy the compiler:

yourCar.assignTo( yourCar, aCar ); aCar.assignTo( yourCar,

aCar );

Now, Java is able to properly reference the method and execute the statements within.

Soon, you'll see other, better ways of implementing this assignTo() method. Before you check out other ways, here's another
example of an instance method. The behavior you need to implement is to determine if a particular automobile is overpriced
given its retail value, name, and model year. Assume the existence of a class called BlueBook that has routines that return a
car's blue book value based on model year and

void assignTo( Automobile src, Automobile tgt) {

tgt.autoName = src.autoName ;
tgt.modYear = src.modYear ;
tgt.retailValue = src.retailValue ;
}

Listing 7.7 A method that assigns one car object to another.


Page 139

public class Automobile {


String autoName ;
int modelYearYYYY ;
/** Other variables relevant to a particular Automobile **/ int
retailValue;

public Automobile( String aName,


int aYear,
/**Other args for other vars **/ int
aValue){
autoName = aName ;
modelYearYYYY = aYear ;
/** Other variables relevant to an Automobile object set here **/

retailValue = aValue ;
}
/** Assign one Automobile to another **/
void assignTo( Automobile src, Automobile tgt) {

tgt.autoName = src.autoName ;
tgt.modYear = src.modYear ;
tgt.retailValue = src.retailValue ;
}
}

Listing 7.8 The assignTo() method in class Automobile.

public class Demo {


public static void main(String[] args) {
Automobile yourCar = new Automobile("Dodge Stealth",
1999, 56500) ;
Automobile aCar new Automobile("Buick", 1993, 3000);

assignTo( yourCar, aCar );


aCar.autoName = "Ford Fiesta" ; System.out.println( "You Drive a
"+
yourCar.autoName ) ;
System.out.println( "The Other Car is a " +
aCar.autoName ) ;
}
}

Listing 7.9 Problem solved?


Page 140

make. Your method compares the blue book value with the retailPrice property to determine the car's "overpriced" status. Call
the method isCarOverpriced().

You can see that the previous method would be peculiar to each object from class Automobile, right? The data required to
make this determination for each car comes from that car (and from method(s) in class BlueBook). A possible implementation
is demonstrated in Listing 7.10.

Place this code in the source file for the Automobile class. Notice that the method returns a boolean, indicating whether or not
the car is overpriced. Also notice the invocation of the method getBlueBookValue from class BlueBook; the method name is
prefixed with the class name. You'll read more about such methods soon.

To invoke this method to determine if, say, the yourCar object is overpriced, you code:

yourCar.isCarOverpriced ( yourCar ) ;

Yes, this does work. However, a hard look at the syntax reveals the clumsiness of the solution. This invocation requires your
coding a reference to the yourCar object twice: once as a prefix for the method (because it is an instance method) and once
as an argument to the method. Java, a modern language, should be able to do better, right?

this–A Special Keyword


Y

Because the previous invocation specifically calls the method belonging to object yourCar, you should not have to tell Java again
FL

that you want to use the yourCar object as an argument. Java gives you a way of referring to the object whose method is being
invoked within the body of the method. You use the reserved Java keyword this to refer to the object. Take a look at Listing
M

7.11.
A

Notice the changes. You no longer need the argument because Java knows that the reserved keyword
TE

this refers to the object belonging to the method being invoked, or the current object. Now, the invocation of
isCarOverpriced() becomes:

yourCar.isCarOverpriced( ) ;

Looks better, right? Of course, the reserved word this must be used in the body of an instance method.

boolean isCarOverpriced( Automobile aCar) { int blueBookValue =

BlueBook.getBlueBookValue( aCar.autoName,
aCar.modYear ) ;
return blueBookValue < aCar.retailValue ;
}

Listing 7.10 Instance method is CarOverpriced().

Team-Fly ®
Page 141

boolean isCarOverpriced( ) {

int blueBookValue =
BlueBook.getBlueBookValue( this. autoName,
this. modYear ) ;
return blueBookValue < this. retailValue ;
}

Listing 7.11 Instance method isCarOverpriced() revisited.

public Automobile( String aName,


int aYear,
/**Other args for other vars **/
int aValue){
// Call the two-arg constructor in Listing 7.4 this( aName, aYear ) ;

Listing 7.12 Another permitted use of this .

Another point about using this: You may code this on the first statement of a constructor to invoke another constructor. See
Listing 7.12, for example.

Yes, such a construct is not too useful, but Java permits it.

Do you recall the comment about implementing the assignTo() method in a "better" way than as an instance method? Let's
explore that now. Using this, you can recode the assignTo() method as in Listing 7.13.

Notice the use of this to refer to the current object. Also notice that this method has a return value: an object of class
Automobile.

Now, to invoke this method, you code:

aCar = yourCar.assignTo() ;

Other ways to code assignTo exist. Let's look at them now.

Class Methods

As you know, the term method is a piece of code that implements some behavior. The original object-oriented languages
required that only classes had methods that they passed to their instantiated objects. However, experience has shown
that forcing methods to be associated with objects is limiting in some regards. Could the class as a whole
Page 142

Automobile assignTo( ) {

Automobile target = new Automobile( this.autoName,


this.modYear,
this.retailValue ) ;
return target ;
}

Listing 7.13 assignTo() revisited.

have some behavior that demands implementation? How about some behavior that requires data from more than one object? Shouldn't
there be a straightforward way of implementing behaviors of an entire class or more than one object?

What a coincidence! The issue of assigning object contents deals with implementing a behavior that requires data from two objects.
Perhaps this is one of those situations that is not well served by a method that should, in theory, require data from one object.

If you think about it, why should the assignTo() method be attached to a single object? As you've seen, we could have
coded the method invocation with the prefix of any Automobile object.

In addition, the assignTo() method implements what car-like behavior? I never heard of a car "assigning" itself to another
car. This is not what object-oriented programming is all about. Even though the coding of the assignTo() method works,
we have higher standards, don't we?

What makes more sense is to think of the assignTo() method as a utility needed for applications that use objects of class
Automobile. Can we have this method available to our application by not referencing this method from an object?

Yes, we can. We can code the assignTo() method to belong to the class Automobile, as opposed to
objects of class Automobile. In JavaSpeak, methods that belong to a class are called Class Methods.

The change needed to change assignTo() from an instance method to a class method is pretty subtle. You still code the
method within the class file. Listing 7.14 shows the class method assignTo().

Notice the presence of the word static in the Method header. A poor choice of a word, really, to describe its purpose. Static is a
hangover from the land of C and PL/I programming. When a C or PL/I programmer declares a software entity as static, she
tells the compiler to allocate memory for that object, as opposed to having the runtime allocate memory during program
execution. This is not
what the keyword static means in Java.

You can think of a class member declared as static as having only one instance or occurrence. Members belonging to
objects have as many copies as there are objects.
Page 143

static void assignTo( Automobile src, Automobile tgt) {

tgt.autoName = src.autoName ;
tgt.modYear = src.modYear ;
tgt.retailValue = src.retailValue ;
}

Listing 7.14 The class method assignTo().

The assignTo() method coded in Listing 7.10 has a single occurrence. So, how is this class method invoked? Like this:

Automobile.assignTo( yourCar, aCar );

You saw the previous syntax coming, right? You invoke instance methods by prefixing the method name with a particular object; you
invoke class methods by prefixing the method name with the class name. Sounds simple enough.

The piece of code in Listing 7.9 that fetches the Blue Book value is a class method of BlueBook. Here it is again, with the
class name bolded:

int blueBookValue =
BlueBook. getBlueBookValue(aCar.autoName, aCar.modYear) ;

Oddly, Java treats class methods as belonging to every object instantiated from the class. This is unfortunate; such usage
clouds the purpose of the class method as being a property of the class as opposed to a property of the objects from the
class. Here's an example of such usage:

yourCar.assignTo( yourCar, aCar ); aCar.assignTo( yourCar,

aCar );

This usage is the same as that for instance methods. Java accepts this syntax for class methods, bowing to the treatment of
class methods belonging to every object. Looking at the two previous invocations, you'd be hard pressed to know that
assignTo() is a class method. You could (likely) believe that the application has a need to include the behavior implemented
by the assignTo() method to objects. Looking at the invocation prefixed by the class name, you couldn't miss the fact that
assignTo() is a class method.

Why confuse the issue? If you want to use a class method, make it easier on those long-suffering programmers that will
follow in your codesteps by prefixing the method name with the class name. Regardless of how you invoke a class method,
you'll have only one copy in your application.
Page 144

Use a class method when the desired behavior does not apply to a particular object but to the class as a whole or to multiple
objects. Use a class method when the implementation of said behavior requires data from multiple objects from the same class.

Use an instance method if the behavior applies to a particular object. Use an instance method when the implementation of
said behavior requires data from a single object.

Finalizer Methods.

Java, as you know, reclaims memory from unused, or dead, objects by using a garbage collection scheme. If you have a need
to perform some activity between the time the garbage collector recognizes an object as garbage and the time the memory for
that object is reclaimed, you can code a
finalizer method. Some resources, such as file locks, may be held even after an object representing a file is garbage collected.

All finalizer methods must have this signature:

protected void finalize() throws Throwable

When you read about threads in Chapter 11, "Java Event Handling Basics," you'll revisit finalizer methods, but not that
much— you won't use them all that much.

Class Variables

Objects have behaviors that are relevant to their being implemented as instance methods. Classes have behaviors that are
relevant to the class as a whole implemented as class methods. Objects have properties that describe particulars about the
object stored as instance variables. Classes have properties that describe particulars about the class as a whole stored as class
variables.

For example, the number of cars in class Automobile is a property of the class, not any particular object. Java provides a
mechanism for declaring variables that belong to the class. All you need do is use the static modifier. Look at Listing 7.15.

Notice that you can use the class name to reference the variable.

Yes, this works. Do any of you astute readers see a problem with this technique? Here's a hint: The problem is not with the
declaring and use of the class variable in class Automobile; the problem is in the access of this variable in class Demo.

You'll revisit this situation of improper access of class and instance variables in the next chapter.

Static Blocks

Java enables you to code blocks of code that are not part of any method. Recall that a block is one or more statements
enclosed in curly braces. To code blocks that do not belong to a method, merely affix the modifier static to the start of the block,
as follows:

static {
//Some code
}
Page 145

Class Automobile {
//Other Statements
//Here is the Class Variable
static int numberOfCars = 0 ;
/** Here is a constructor method **/ public Automobile( String
aName,
int aYear,
int aValue) {
autoName = aName ;
modYear = aYear ;
retailValue = aValue ;
// Tally up the new Automobiles
numberOfCars++ ;
}
//Remainder of class Automobile
}
Class Demo {
//Other Statements
System.out.println( "Number of Cars = " +
Automobile.numberOfCars ) ;
//Rest of class Demo
}

Listing 7.15 An example of using and referencing a class variable.

The code must be inside a class (not between a package statement and a class or between two classes) and outside all
methods.

Static blocks execute only once when the class loads. You cannot invoke static code like you would a static method. Indeed, if
you need to invoke the code yourself, you cannot use static code.

Static code is good for initializations when you must guarantee that some activity relevant to the class takes place only once
and at the start of the class load. Also, static code can access only static, or class, variables. Although instance variables are
coded outside of any methods, these variables are still off-limits to static code blocks.

Nested Top-Level Classes and Inner Classes

The last topics on Table 7.2 are the Nested Top-Level and Inner Classes. Sometimes, you may find it convenient to include one
class wholly within another. The nested top-level class mechanism enables you to declare a static class within another class.
You may find using a nested top-level class a handy way of organizing significant data that is tightly coupled to the enclosing
class. The only requirements for a nested top-level
Page 146

class are that you must use the enclosing class name when referring to objects of the nested top-level class outside of the
enclosing class. For example:

class MainClass {
//Statements
static class NestedClass {
//Variables, methods, etc - same old stuff }

//Reference object of NestedClass within enclosing class void aMethod() {

NestedClass objNestedClass = new NestedClass() ;


}

} // of MainClass

Recall that everything you code in Java has to be contained within some class. Some feel that this requirement, not specific to
Java, is a flaw with object-oriented programming. If you want to code an itty-bitty utility method, you need to create a class
before coding the method inside this class. Of course, to use this utility, you need to ensure that this class can be located (on
the CLASSPATH) when you compile.

Inner classes help you out here. You can code a class inside an existing class. The syntax depends on the type of inner class
you want. In JavaSpeak, three types of inner classes exist: Member classes, Local classes, and Anonymous classes.

You code member classes like top-level nested classes except that you omit the static qualifier. Thus, member classes are
associated with every instance of the enclosing class. Think of objects from the member class like other members associated
with objects of the enclosing class. The inner class can reference members in its enclosing class. The enclosing class needs to
declare objects from the member class before using the inner class methods.

Member classes are rarely required. These classes are often a convenience. The member classes are not visible outside the
enclosing class.

Here's a sample declaration and invocation of a member class:

class MainClass {
//Statements
class MemberClass {
//Variables, methods, etc - same old stuff }

//Reference object of NestedClass within enclosing class void aMethod() {

MemberClass objMemberClass = new MemberClass() ;


}

} // of MainClass

Local classes are another type of inner class. The difference between a member class and a local class is that a local
class is defined within a method. The common use for a local class is to implement some behavior for a graphical interface
object. An anony-
Page 147

mous class is a refinement of the local class. This form of inner class combines the class definition with an instantiation of
the class.

You'll read about local and anonymous class usage in Chapter 11 on events.

In Summary

You've read much about how Java represents objects and classes. However, the best is yet to come. In the next chapter,
you'll read about that infamous object property of encapsulation. In Chapter 9, you'll read about the nefarious object property
of inheritance. That will pretty much describe how Java implements the object-oriented view of programming.
Page 148

This page intentionally left blank.


Page 149

CHAPTER 8
Encapsulating and Hiding Data and Methods

This chapter explores Java's technique for data hiding by way of visibility modifiers. Java code samples showing the effects of
declaring objects and variables with different visibility modifiers is included. Encapsulation goes hand in hand with data hiding,
and this chapter has examples of objects with protected attributes accessible by interface code only. This too is a concept that
may be unfamiliar to the reader.

This chapter also includes PL/I code that, at first glance, seems to implement encapsulation. This PL/I code is compared and
contrasted to Java code that successfully encapsulates object properties and behaviors. The chapter ends with a discussion
on get and set methods, which enable users of your classes to have controlled access to instance variables.

Encapsulation

Encapsulation. Quite a twenty-dollar word! For our uses, encapsulation is the mechanism that prevents a variable from one
class to be inadvertently modified by a method in another class. Encapsulation is a major property of object-oriented
programming in general, and Java programming in particular.

Think of a class as a capsule surrounding the classes' data and methods. This capsule shields the contents of your class
from outside, prying eyes. You allow access to the data and methods in the class by coding a well-defined interface. In
other words, programmers do not know how you've implemented your classes and, as a consequence, cannot rely on
intimate, implementation details when accessing your classes'
Page 150

data. In addition, you are free to change the underlying class implementation without affecting how others access your class
(this is where the well-defined interface comes into play).

This capsule offers protection against data being improperly changed and methods being illegally invoked. The strength of
this capsule is directly proportional to the features of the programming language that support encapsulation. Put in the
language of the systems analyst, the capsule binds related data and operations that imbue data with behavior into a highly
cohesive object.

As you'll read soon, Java has all the essentials to firm up that capsule wall and gives you, the humble programmer, complete
control over what code can touch or access your class.

Hand in hand with encapsulation is the concept of data hiding. Data hiding is the strategy for implementing an encapsulation
mechanism. Essentially, data hiding enables you to protect data from changes by hiding this data from unauthorized classes.
Sometimes, the terms encapsulation and data hiding are used interchangeably.

Why Encapsulate and Hide Your Data and Methods?

It's all about control.

The reason you want to encapsulate your classes is to give you control over who, what, and where your class is accessed.
Given this control, you are free to change your classes' implementation without fear of breaking existing code. In addition,
you can relax, knowing that someone cannot
Y

mistakenly change your object's data because you have enforced access to this data by way of a well- defined interface.
FL
M

Before going into the details of how to encapsulate your classes and hide your data in Java, a few words on how data can be
A

unwittingly and erroneously changed in a Java program are in order, and what Java features help prevent such unwanted
TE

changes.

Preventing Unwarranted Changes to Your Data

Preventing unwarranted changes to your data touches on a few issues: variable scoping, the parameter passing mechanism
supported by the programming language, and the features available in the programming language to hide your data and
encapsulate your classes. The first two items are issues to reckon with in procedural languages as well as Java.

Variable Scoping

Variable scoping is where a variable is known in a program. If a variable is known in a program, the value of the variable can
be changed. At times, the variable is unknowingly or mistakenly changed by a piece of code that has no business making such
changes.

Team-Fly ®
Page 151

In COBOL, variables global to the compile unit are truly the rule; in PL/I, local variables are actually the exception. A telling clue
is that compilers in these languages have options to produce variable by statement number cross-reference listings because
variables can be used (and changed) on practically any statement in the compile unit. For compile units consisting of thousands
of lines of source, no practical way exists for a mere mortal to keep track of what gets accessed and changed where.

Naturally, a program unit changing variables throughout its 5,000 line code is quite the mess. Commonly, COBOL and PL/I
applications used in today's data intensive industries contain anywhere from 100 to 1,000 such compile units; each compile unit
contains hundreds of declared variables, swimming in a sea of code.

Maybe this explains the sky-high maintenance budgets of the legacy DP shop.

Limiting variable use, or limiting the scope of a variable, to a subprogram makes sense because now, a mere mortal can keep
up with variable changes. Now, a particular variable, more properly scoped, can be changed on, say, 100 lines as opposed to
5,000 lines. If a problem in the application can be tracked down to a compile unit, these more properly scoped variables can, in
all likelihood, be removed as suspects.

Java enforces variable and object scooping with the block construct. You've already read that by declaring a variable or
object within a pair of curly braces, the block construct, you've limited the access to that variable or object to that block. The
COBOL programmer has no blocking construct; the PL/I programmer has two.

Some scoping examples follow. Any thoughts on what Java would do with the code snippet in Listing 8.1?

Notice that the loop index variable is declared within the loop. As an aside, you recall that the brackets used in the loop are
optional, right? Well, here's what the Java compiler, javac, has to say about this piece of code in Listing 8.2.

Notice that the variable idx is not known outside the loop. The lingo is that the reference to the variable idx falls outside
the variable's declared scope. Javac will not report the diagnostic by referring to scope; javac just tells you it cannot
locate a definition for the referenced variable.

public class ScopingExample {


public static void main(String[] args) {

for (int idx = 0; idx < 5; idx++ ) {


System.out.println
("idx Known in loop " + idx ) ;
}
System.out.println
("idx Known in method " + idx ) ;

Listing 8.1 What will Javac and Java do?


Page 152

ScopingExample.java:8: Undefined variable: idx


System.out.println
("idx Known in method " + idx ) ;
^
1 error

Listing 8.2 Javac's response.

Main: Proc (aStringArgument) Options( Main ) ;


Dcl aStringArgument Char( 40 ) Varying ;

Begin ;
Dcl idx Fixed Bin(31) Init( 0 ) ;
Do idx = 0 to 5 ;
Put Skip List ("idx known in loop " || idx ) ;
End ;
End ;
Put Skip List ("idx known in method " || idx ) ;
End Main ;

Listing 8.3 A PL/I version of the above Java code.

As a comparison, Listing 8.3 shows how you'd code the previous (well, close enough!) in PL/I.

As an aside, PL/I will compile this example and provide a default value and data type for the variable idx referenced outside
the Begin block.

One more example is demonstrated in Listing 8.4.

This main() method has two separate variables named idx. Each variable is defined in a separate block; therefore, each
variable has its own scope. Put differently, each variable is known (and can be changed in) different parts of the program.

Now, you don't have to be especially brilliant to realize that using the same variable name in differently scoped
areas of the same method is, quite frankly, the mark of the amateur.

Pop quiz: How many blocks are present in Listing 8.4? This listing has four blocks: the block defined for the class, the block
defined for the main() method, the block defined in the for loop, and the block defined by the interior curly braces.

The previous examples showed scoping of Java primitive type variables; the same scoping rules apply to objects, or
reference data types as well.
Page 153

public class ScopingExample {

public static void main(String[] args) {

for (int idx = 0; idx < 5; idx++ )


System.out.println
("idx Known in loop " + idx ) ;

{//Block defines part of the method with unique scope


int idx = 10 ;
if ( idx < 20)
System.out.println("idx Known in if stmt " +
idx);
}//End of the block

Listing 8.4 Another example to show Java blocks and variable scoping.

Understanding the Parameter Passing Mechanism

You've read in Chapter 7, "Class and Object Representation," that Java passes primitive types to methods by value and passes
reference types by reference. Here, we mention that by not understanding the difference, you can get into a lot of trouble.
COBOL and PL/I each provide mechanisms for passing parameters by value or by reference; Java does not. Thus, make it a
point to get the passing parameters by value and by reference thing straight and understand Java's parameter passing
mechanism.

How Do You Encapsulate Your Classes in Java?

As you might well imagine, the benefits of encapsulation and data hiding do not miraculously happen. You have to use
the features of the programming language to make it happen. Java, of course, has features to enable you to encapsulate
your data and methods. Your job is to use the correct Java constructs in the correct ways.

That's what this chapter is all about.


Page 154

Table 8.1 Java's Visibility Modifiers

VISIBILITY
MODIFIER CAN BE SEEN (USED) BY:
public Every class of your application (the world)
protected The package (if one exists) that holds the class containing the member, the class
containing the member, and all subclasses of the class containing the member

Default Package The package (if one exists) that holds the class containing the member and
Visibility (not the class containing the member
coded)

private Every method or class within the class containing the member

The primary Java feature you'll use in your programs to encapsulate and hide your data is the
visibility modifier. You've read a bit about these modifiers in Chapter 7. Here, you'll delve more deeply into this vitally
important Java feature.

Table 8.1 shows Java's visibility modifiers once again.

You can qualify (or modify, if you prefer) a primitive type variable, an object, a method, or a class with a visibility modifier.
Here's an example from Chapter 7. Here, notice that the visibility modifiers are bolded. The purpose of this dissertation is to
illustrate how to encapsulate items in Java. The goal is to allow access to a stack by the approved methods: popTheStack
and pushTheStack. We don't care how these methods work or what other methods are needed by the stack to get the job
done. All we care about is popping and pushing.

Using Visibility Modifiers.

Let's see if the class with the chosen visibility modifiers in Listing 8.5 does the trick. What follows is a

short explanation on choosing the bolded visibility modifiers.

If we assume that you want other classes to access objects of class VectorStack, then you need to declare the class VectorStack
public. Usually, you'd make the class describing a useful data structure public so others can use it.

If you do not want any users of your class to determine if a stack is empty, you declare your isStackEmpty() method as a private
method. Now, isStackEmpty() is known only to the methods in your class. Notice that your popTheStack() method first invokes
the isStackEmpty() method before popping. Because method isStackEmpty() has use inside your class and nowhere else, the
method should be declared private.

Methods popTheStack() and pushTheStack() have to be accessible by any class needing a stack. Thus, the proper
visibility modifier for these methods is public.

The constructor method enables users of your class to create objects from your class. Seems logical that the constructor
must be declared public. Oddly, using any visibility modifier other than private works here.

Now, if you've been paying attention, you might get this question correct: What is the proper visibility modifier for
the data structure that implements objects of class
Page 155

import java.util.* ;
/** Use the methods in class Vector to implement a stack. **/
public class VectorStack{
//Possible Stack implementation.
//Now, the methods . . . . . . . static private boolean isStackEmpty( VectorStack myStack ) {

return myStack.aStack.isEmpty() ;

}
static public Object popTheStack (VectorStack myStack ) {
Object stackElement = null ;
if (!isStackEmpty( myStack ) ) {
stackElement = myStack.aStack.lastElement();
myStack.aStack.removeElement( stackElement ) ;
}
return stackElement ;
}
static public void pushTheStack( VectorStack myStack,
Object myStackElement ) {
myStack.aStack.addElement( myStackElement );
}
//The constructor . . . ..
public VectorStack() {
aStack = new Vector() ;
}
//Finally, the data structure
??? Vector aStack ;
}

Listing 8.5 A possible stack implementation.

VectorStack? Your first instinct may be to declare aStack, the data structure corresponding to an object of VectorStack as a
public structure so other classes can access the stack. If so, your instincts are dead wrong.

Recall that the goal is to have objects of class VectorStack accessed by the classes' popTheStack() and pushTheStack()
methods, none other. The idea is that a stack can only be accessed from the "top." You should not be permitted to go in the
"middle" of a stack and yank out a stack element.

Let's assume you've declared aStack previously as a public member of class VectorStack. Take a look at the code in Listing
8.6 that uses this class.

After a successful compile, here's the output of this routine:

Element 4
Element 3
Element 2
Page 156

public class StackWork {

public static void main( String[] args) {

VectorStack vStack = new VectorStack


(); //(1)

VectorStack.pushTheStack
(vStack, "Element 1") ; //(2)
VectorStack.pushTheStack
(vStack, "Element 2") ; //(3)
VectorStack.pushTheStack
(vStack, "Element 3") ; //(4)
VectorStack.pushTheStack
(vStack, "Element 4") ; //(5)

System.out.println( VectorStack.popTheStack
(vStack)) ; //(6)
System.out.println( VectorStack.popTheStack
(vStack)) ; //(7)

System.out.println( vStack.aStack.elementAt
(1) ) ; //(8)

Listing 8.6 Using the stack with a publicly declared data structure.

Line //(1) creates an object of class VectorStack with the new operator. As you know, the Java runtime will invoke the
constructor method for class VectorStack in class VectorStack to create object vStack.

Lines //(2) through //(5) push an element onto the stack using the public method pushTheStack() in class VectorStack.

Lines //(6) and //(7) print out the popped stack element. Hence, you'd see the phrase "Element 4" followed by "Element 3"
written to the default output stream. Again, the class uses the public method popTheStack() to access the "top" element.

Let's take a look at line //(8). This line uses a method called elementAt(). Now, this method is not one of the approved public
methods in class VectorStack. Well, just what is this method elementAt() anyway?

Take a look at this declare in class VectorStack:

public Vector aStack ;

This is the public declaration of the stack implementation in class VectorStack. Notice that class VectorStack implements the
stack as an object of class Vector. Now, class Vector is a very handy class that you'll read more on in Chapter 10, "Interfaces."
Without giving too much away, know that a vector in Java is a data structure that holds an array of objects that can grow or
shrink in size during runtime. Class Vector is so handy that an object of class Vector has 24 public methods to add and remove
elements, of which one is the elementAt() method. As the name suggests, elementAt()
Page 157

returns a vector element at a given position. The pushTheStack() method added four elements to the stack; the popTheStack()
element removed the "top" two elements. Thus, the stack has two elements remaining. The elementAt( 1 ) method returns the
second element in the vector (vectors are indexed from 0, like arrays). Hence, the last line of output is as shown previously,
"Element 2".

So, what's going on here, anyway? Seems that we are able to access our stack with methods other than the approved public
methods, pushTheStack() and popTheStack(). In JavaSpeak, we have not encapsulated the stack. Actually, as the code sits,
objects instantiated from class VectorStack are not stacks because the operation elementAt() is not a stack operation;
elementAt() is a vector operation.

Is this a big deal? Well, if you wanted to code an implementation of a stack and you care if you've done a good job, it sure is a
big deal. After all, why go through the coding exercise of creating a stack when all you've done is hooked a few extra methods
on a vector? If you wanted to create a class that has the features of class Vector with a few additional methods, you should
have subclassed
the Vector class. Chapter 9, "Inheritance," discusses how to create subclasses in particular and the object property of Inheritance
in general.

Meanwhile, back at the ranch, you're still checking out this stack implementation. Take a look at the three following lines:

VectorStack vStack = new VectorStack( ) ; VectorStack.pushTheStack(vStack, "Element 1") //(1)


; System.out.println( vStack.aStack.elementAt(1) ) ; //(2)
//(8)

Notice that line //(2) using the approved public method refers to the instance of the stack, vStack,
whereas line //(8) using the method elementAt() refers to the instance of the vector aStack.

How did this programmer know that objects of class VectorStack were defined in terms of objects of class Vector and that the
definition relies on a vector called aStack? In the absence of any other compelling evidence, the best guess is that she looked
at the code for class VectorStack. She could have run the javap program with the -c option to disassemble the class (read
Chapter 4, "The Sun Java 2 Basic SDK Tools," to get the dope on javap).

Bottom line: The issue is not what the programmer who accesses the VectorStack class knows; the issue is the shoddiness of
the code that enables a programmer to bypass the interface and directly access the underlying data structure.

You know this problem is easy to correct, right? All you need to do is declare the object of class VectorStack in Listing
8.5 as private, like so:

//Finally, the data structure


private Vector aStack ;

After compiling the revised VectorStack class, you compile the code shown in Listing 8.6 and, lo and behold, Listing 8.7
is what you see.

The private visibility modifier has done its job. You cannot access the Vector object aStack used to define objects of class
VectorStack. The encapsulation mechanism of Java, implemented by using visibility modifiers, will not let you. So there.
Page 158

StackWork.java:17: Variable aStack in class VectorStack not accessible from class StackWork.

System.out.println( vStack.aStack.elementAt(1) ) ;
^
1 error

Listing 8.7 Javac tells you off.

StackWork.java:17: Method elementAt(int) not found in class VectorStack.

System.out.println( vStack.elementAt(1) ) ;

1 error

Listing 8.8 Do you get it now?

You may be thinking that you can fool Java by coding the invocation of elementAt() without referencing the Vector
object aStack. Well, if you coded:

System.out.println( vStack.elementAt(1) ) ; //(8)

Listing 8.8 is what you'd see.

Now, you cannot use the elementAt() method, or any method from class Vector, in a class that accesses VectorStack
because the stack representation of VectorClass objects is invisible to all classes but the class VectorStack.

Looks like the objective is met. The previous implementation allows access to a data structure from one location: the "top." You
can only use methods to put something on and take something off. The code models the behavior of a stack, the removing of an
item from the top, and the placing of an item on the top, like cafeteria trays. The code successfully encapsulates the stack and
hides the internal representation from user classes.

A PL/I Example: Is This Object-Oriented Programming?

To an experienced object-oriented programmer, encapsulation is par for the course. For the mainframe data processor using
procedural languages, encapsulation might sound akin to voodoo. That said, some mainframe languages have features that
would seem to allow for the encapsulation of data.
Page 159

Take a look at the PL/I code in Listing 8.9.

This compile unit implements a stack as controlled data structure. The code allocates memory at runtime for each stack
element when required (when pushTheStack is called). Each stack element is a 32K byte varying string, which is generic as it
gets in PL/I.

Listing 8.10 shows how you'd call these routines to use the stack.

So, what does this PL/I code do? The compile unit Stack defines a data structure and two routines to gain access to the stack:
popTheStack() and pushTheStack(). The PL/I stack also uses a function, isStackEmpty(), to determine if a pop operation will
be

Stack: Proc ;
Dcl popTheStack Entry
Returns( Char( 32767 ) Varying ) ; Entry( Char (32767 ) ;
Dcl pushTheStack
Dcl aStack Char( 32767 ) Varying Controlled ; Builtin ;
Dcl Allocate

popTheStack: Entry( ) Returns( Bit( 1 ) ) ;


Dcl stackElement Char( 32767 ) Varying Init( '' ) ;

If ^stackIsEmpty() Then
Do ;
stackElement = aStack ; Free( aStack ) ;

End ;
Return( StackElement ) ;

End popTheStack ;

pushTheStack: Entry( stackElement ) ;


Dcl stackElement Char( 32767 ) Varying Init( '' ) ;

Allocate( aStack ) ; aStack = stackElement ) ;

end pushTheStack ;

stackIsEmpty: Proc Returns( Bit( 1 ) ) ;


Return( Allocate( aStack ) = 0 ; End stackIsEmpty ;

End Stack ;

Listing 8.9 PL/I stack implementation.


Page 160

useTheStack: Proc Options( Main ) ;

Dcl popTheStack Entry( Char (32767) Varying) Returns( Char( 32767 ) Varying
);
Dcl pushTheStack Entry( Char (32767 ) ;
/** Put something on the Stack **/ Call pushTheStack("
Element 1" ) ; Call pushTheStack(" Element 2" ) ; Call
pushTheStack(" Element 3" ) ; Call pushTheStack(" Element
4" ) ; /** Take something off **/

Put Skip List( popTheStack() ) ; Put Skip List(


popTheStack() ) ;

end UseTheStack ;

Listing 8.10 Using the PL/I stack.

permitted. The function isStackEmpty() is hidden from users of the stack. The code in Listing 8.10 declares the stack
interface routines and shows how to use these routines.

Well, well. Looks like the astute PL/I programmer can implement encapsulation by declaring a data structure, coding a
well-defined interface, and hiding implementation details from users of the structure. However, one caveat exists. Do you see
it?

This code does, indeed, implement a stack. Unfortunately, the code can use only one stack. Notice that the code in Listing
Y

8.10 never refers to a stack by name. That's because the code in Listing 8.9 implements a single stack. Why bother with a
FL

name when there's only one stack to work with? In other words, the PL/I code is missing some sort of new operator to create
a stack.
A M

Before you think that all you need to do is declare a stack in the program shown in Listing 8.10 and modify the routines in
TE

compile unit Stack to use this declared stack, remember that once you declare the stack in Listing 8.10, you have access to the
data without using the approved interface routines. Hence, you could change the routines to use more than one stack, but you'd
sacrifice the benefits of encapsulation and expose the stack to whatever you could do in PL/I to the underlying data type, a
string of characters. This is akin to using the elementAt() method on an object of the underlying class Vector.

If you wanted to work with two stacks in the previous Java code, all you need to do is code another object instantiation:

VectorStack anotherStack = new VectorStack( ) ;

To use two stacks in the previous PL/I code, you'd need a copy of the Stack compile unit, say, Stack2, or you'd have to
write code to empty the old stack before using the new one. The act of cleaning out a stack is not a known behavior of a
stack; this is an

Team-Fly ®
Page 161

implementation detail forced on the programmer by the limitations of the programming language.

Although PL/I has some features that allow for good procedural programming, PL/I does not implement common
object-oriented features. PL/I programmers cannot implement the concept of instantiating objects from classes. That's why
the previous code must reference a single stack; no language support for class creation exists.

Using Accessor (Get and Set) Methods

Data hiding means to eliminate the possibility of an unwanted change to data by removing that data from sight; you can't
change what you can't see (or don't know about). Sometimes you want users of your class to have access to data in your
objects or classes and, at times, change this data, but you don't want to give away the store. Put differently, you want users of
your class to have controlled
access to class and object data.

Let's say that your stack class keeps a running total of the number of objects of class String on the stack. Listing 8.11
shows a way of doing this.

To summarize the changes: Declare an instance variable numberStrings to hold the number of string elements. Use the instanceof
operator to check for String elements every time pushTheStack() is invoked. Because you need users of your class to access
this quantity, you declare the variable
public.

Here's a line of code used in class StackWork (Listing 8.6) to access this quantity:

System.out.println( vStack.numberStrings ) ;

Do you see a problem? Here's a hint: Any thoughts on what will happen with the following code:

System.out.println( vStack.numberStrings ) ; vStack.numberStrings = 25252 ;

System.out.println( vStack.numberStrings ) ;

public int numberStrings = 0 ; static public void

pushTheStack( VectorStack myStack,


Object myStackElement ) {
if (myStackElement instanceof String)
myStack.numberStrings++ ;

myStack.aStack.addElement( myStackElement );
}

Listing 8.11 Changing vectorstack to track number of string elements.


Page 162

The first println produces the correct number of string elements on the stack; the second produces the changed value.

Clearly, this property of the stack object should not be changed in this fashion. However, you face a dilemma: How do you
allow read access to this property and prevent write access?

A related problem is how do you allow controlled write access to an object property. For example, let's say you want to give
users of your VectorClass stack the ability to set the size, or set the maximum number of elements, of a stack. The caveat is
that the user of the stack cannot set a stack size more than 100.

Do you see the problem? If you were to create another instance variable, say, numberStackElements, and enable the user to
change it (which she must), how can you enforce the less-than-101 requirement?

The object-oriented technique for solving these dilemmas is by coding accessor, or get and set methods. The idea is to allow
access to these instance variables (or properties of the object, if you prefer) by invoking methods as opposed to by coding
assignment statements. The code in the get or set method enforces any restrictions on the access of the object property.

To enforce read-only access to the number of strings in the stack, you may code as in Listing 8.12.

Now, the instance variable numberStrings is declared as private. As you know, the private visibility modifier blocks any outside
class from accessing the variable. The only way to read this variable is by invoking the get method, getNumberStrings. Here's
a line of code that accesses the variable via the get method:

System.out.println( vStack.getNumberStrings( ) ) ;

Were you to try to read the value of numberStrings directly, Listing 8.13 shows what javac would tell you.

A few points about the get method are in order. Notice the name of the method, get<variableName>. This is conventional. Also
notice the use of the reserved Java word, this , to refer to the object that invoked the method. Because this is an instance

private int numberStrings = 0 ;

public int getNumberStrings( ) {

return numberStrings ;
//or return this.numberStrings ;
}

Listing 8.12 Using a get method to read an object property.


Page 163

method, you need not pass the stack as a parameter to the method. Contrast the parameterless mode of the instance method
with the class methods pushTheStack() and popTheStack. These are class methods. Hence, you must supply the name of the
object as a parameter.

Set methods follow the same principle of allowing access to an instance variable only through a method. Listing 8.14
shows how you could code a set method for the instance variable numberStackElements.

Once again, the hidden variable is declared private and the set method is declared public. We've shown a constant declaration
that applies to every stack created from VectorClass. Notice that the get method has the name get<variableName>, again, by
convention. Set methods do not return a value, hence, the void keyword is present on the method header. The argument is the
value to use to set the instance variable. Here, this set method uses the value of the passed parameter if less than the
maximum, otherwise, the maximum. Again, notice the use of the keyword this to refer to the object invoking the method. Here's a
sample invocation:

vStack.setNumberStackElements( 40 ) ;

For instance variables declared as boolean, many use a get method named is<variableName> to retrieve the value.

StackWork.java:19: Variable numberStrings in class VectorStack not accessible from class


StackWork. System.out.println( vStack.numberStrings ) ;

1 error

Listing 8.13 Again, Javac tells you off.

private int numberStackElements ;


private final static int MAXNUMSTACKELEMENTS = 100 ;

public void setNumberStackElements( int numberElements ) {

numberStackElements =
(numberElements <= MAXNUMSTACKELEMENTS)
? numberElements
: MAXNUMSTACKELEMENTS ;
}

Listing 8.14 Using a set method to write an object property.


Page 164

Throughout this discussion of get and set methods, the emphasis is on instance variables. Of course, you may code get and set
methods for class variables as well. However, normally an application needs access to the objects instantiated from classes as
opposed to the classes themselves.

In Summary

This chapter described encapsulation, a critical feature of object-oriented programming, and how you encapsulate objects in
Java. By using visibility modifiers and get/set methods, you can allow users of your classes to access needed object properties
without accidentally corrupting, or changing, other properties. The result is better quality software than you could ever hope to
achieve with procedural languages that lack support for encapsulation and data hiding.
Page 165

CHAPTER 9
Inheritance.

In this chapter, you'll read about the critically important property of both object environments (in general) and Java (in
particular), which is inheritance. This chapter begins with various comments on inheritance and a cursory examination of
various inheritance hierarchies, followed by a short discussion on the merits of single versus multiple inheritance. Next, you'll
see inheritance in action by a Java implementation of bank account classes. After defining the requirements for your bank
accounts, you'll see the Java code that does not take advantage of inheritance, followed by Java code implementation that
does.

Inheritance Defined

The simple definition of inheritance that follows belies the power of inheritance. Inheritance is a mechanism whereby one
class can use the behaviors (methods) and properties (data) from other classes. Inheritance enables you to define new
classes as a combination of existing classes. Additionally, you would add functionality not found in the existing classes to
the new classes.

In objectspeak, a subclass inherits from one or more superclasses. In Javaspeak, a subclass extends
its superclass. The astute reader notes the use of the plural in the objectspeak phrase, and the singular in the Javaspeak phrase.
No, this is not accidental. Java was designed to not enable a subclass to have more than one superclass. Shortly, in this chapter,
you'll read some reasons for this design decision.
Page 166

The strength of inheritance is that you, the Java programmer, need do little to reuse behaviors and properties from existing
classes. By using a few Java keywords here and there, you're using inheritance. You don't copy and paste code from one class
to another; you reference the methods and properties in the superclass you want to use in your subclass as if they were defined
(coded) in the subclass. Many times, if you look at the invocation (in the subclass) of inherited methods (from the superclass),
you would not know that the methods are inherited.

Another way of looking at a group of subclasses and superclasses is that these classes form a hierarchy, with the
superclasses on the top, and the subclasses underneath. The terms inheritance tree or hierarchy tree are aptly used to
describe the inheritance relationship.

Same Classes, Different Inheritance Trees

Before you read about the Java treatment of inheritance, take a look at the two examples of hierarchy trees in Table 9.1, which
are composed of real world entities. The trees show different hierarchies of the same entities. The hierarchies reflect different
ways of illustrating the relationships among the same entities.

The following table shows two possible arrangements of vehicles. The first classifies vehicles by what they travel on, or in; the
second classifies vehicles by what the vehicle transports. Under each hierarchy is a short list of behaviors. Think of the vehicle
classifications as classes, and the behaviors as methods.

With both hierarchies, some behaviors for a vehicle are start, stop, and steer. Every vehicle has this behavior, regardless of
where the vehicle travels or what the vehicle transports. If you were to model the behaviors of different vehicles, you would
provide an implementation for starting, stopping, and steering a vehicle. If your implementation environment supports
inheritance, you could provide implementations at the topmost entities of the hierarchy and allow the bottom entities to use all,
or part of these implementations. Put differently, objects of class Cars or Space Shuttles or any other vehicle would have
access to these methods.

Every operator of a land vehicle has a need to repair whatever touches the road from time-to-time, be it a tire or a tread. This
repair behavior is peculiar to land vehicles because, simply put, land vehicles have tires or tread. Put another way, land vehicle
objects have a property of 'have tires' or 'have treads.' By implementing a 'repair tire' behavior in an environment that supports
inheritance, all land vehicles, or classes that are children of the land vehicle class, that have tires can inherit and therefore use
the implementation.

When vehicles are classified according to what they transport, the behavior of repairing a tire does not fit neatly into the
classification. If you need to implement a 'repair tire or tread' behavior, you
cannot take advantage of inheritance with the second hierarchy because this hierarchy does not enable you to specify a parent
class in the hierarchy as one having tires or treads. In other words, you cannot model a Passenger, Cargo, or Military Vehicle as
one having tires or tread with the existing second hierarchy.

The vehicle classes used in both hierarchies are the same; they have the same attributes and behaviors. After all,
that's the point of object technology— objects have a set of
Page 167

Table 9.1 Two Vehicle Hierarchies

VEHICLE HIERARCHY 1 VEHICLE HIERARCHY 2


• Vehicle • Vehicle
• Land Vehicle • Passenger Vehicles
• Cars • Cars
• Buses • Buses
• Trucks • Trucks
• Tanks • Trains
• Troop Carriers • Boats, Yachts
• Trains • Sailboats
• Water Vehicles • Cruise Ships
• Boats, Yachts • Planes
• Sailboats • Jets
• Cruise Ships • Space Shuttles
• Cargo Tankers • Gliders
• Warships • Airships
• Submarines • Cargo Vehicles
• Air Vehicles • Trucks
• Planes • Trains
• Jets • Cargo Tankers
• Gliders • Planes
• Airships • Jets
• Space Vehicles • Rockets
• Space Shuttles • Military Vehicles
• Rockets • Tanks
• ICBMs • Troop Carriers
• Warships
• Submarines
• Planes
• Jets
• Rockets
• ICBMs
Some Vehicle Behaviors Some Vehicle Behaviors
• Start, Stop, Steer • Stop, Start, Steer
Some Land Vehicle Behaviors Some Passenger Vehicle Behaviors
• Repair Tire or Tread • Collect Fares, Confirm Destination
Some Water Vehicle Behaviors Some Cargo Vehicle Behaviors
• Bail Water, Drop Anchor • Deliver Payload, Confirm Shipment
Some Air Vehicle Behaviors Some Military Vehicle Behaviors
• Take off, Land • Confirm Orders, Deliver Payload
Some Space Vehicle Behaviors
• Ignite Boosters, Achieve Orbit

properties and behaviors. These properties and behaviors doesn't change when you create different superclass/subclass
hierarchies. Whether you model a truck as a child of a land vehicle or passenger vehicle, a truck has tires. Now, the problem at
hand may
Page 168

not require you to implement a "Repair Tire" method when you classify vehicles by what they transport. If you must, you
can still implement such a method. The point is that you cannot easily leverage inheritance to reuse this method when you
are using the second hierarchy.

Classes often have several hierarchical relationships. Here, you've seen two possible arrangements of vehicles. You could have
arranged the vehicle classes by the size of the passengers and crew, or the type of fuel used to propel the vehicle. The hierarchy
you choose to create should be heavily dependent on your problem domain.

Classes at the top of a hierarchy tree are more abstract than those at the bottom. Another way of putting this is that the
classes at treetop do not correspond to tangible, real world entities, whereas those classes at the bottom do. In our previous
example hierarchies, the topmost class is called Vehicle. Now, vehicles have real behaviors and properties. However, real
world vehicles do not come from this topmost class; they come from a bottommost class. In objectspeak, you doesn't
instantiate objects from classes Vehicle or Military Vehicles; you instantiate objects from classes Tank or ICBM. In
Javaspeak, class Vehicle or class Military Vehicles are abstract classes. The top classes exist to provide an inheritance
mechanism whereby properties and behaviors may be inherited by the real world, bottommost classes. After all, cars and
buses are real world things, whereas Passenger Vehicles is an abstraction.

Although the top- and mid-level classes are abstract, you'll still implement methods that correspond to the classes' behaviors.
However, the logic behind coding methods for top- and mid-level classes is to make life easier for the bottom level classes.
Just like life, the parents always work hard and make sacrifices for their children. The same is true with object environments
and inheritance relationships among parent and child classes.

The keen reader will notice that some classes in the second hierarchy are bolded and italicized. You'll revisit these
classes in the section Single Versus Multiple Inheritance, coming up next.

Single Versus Multiple Inheritance

The definition of inheritance, used in this chapter, allows for one or more subclasses inheriting behaviors and properties from
one or more superclasses. The industry jargon for the object property of allowing a class to inherit from more than one
superclass is multiple inheritance. You don't have to be exceedingly sharp to deduce the industry jargon of prohibiting a class
from inheriting from more than one superclass.

Recall that Java does not permit a class to inherit from more than one subclass. In other words, Java permits single
inheritance only. You may reasonably question the wisdom of disallowing multiple inheritance. After all, if inheriting properties
and behaviors from one superclass is effective, shouldn't inheriting from more than one class be even more effective?

To shed some light on potential problems with multiple inheritance, take a look at the second hierarchy in Table 9.1. The
reader will notice that some classes are bolded and italicized. These classes are repeated in the hierarchy; they have
multiple superclasses. In English, a plane can transport passengers and cargo, or soldiers and arma-
Page 169

ments. Given this hierarchy, a plane class could be a child of all three superclasses.

Now, the second hierarchy shown in Table 9.1 does not reflect a multiple inheritance scenario. You could construct the
hierarchy and classes such that the Plane class with a superclass of Passenger Vehicle is different than the Plane class with a
superclass of Military Vehicle. Here, to draw distinctions between single and multiple inheritance, we're assuming that the plane
class (and others) share properties and behaviors from multiple superclasses.

Notice that the behavior Deliver Payload exists for subclasses of Cargo Vehicles and Military Vehicles. You can safely
assume that the implementation for the Deliver Payload behavior for a subclass of Cargo Vehicle will differ from that of a
subclass of Military Vehicle. Given that objects from class Plane inherit from these two superclasses, the question arises:

Which superclass does the behavior Deliver Payload come from?

It turns out that no straightforward way exists for avoiding ambiguities arising from name clashes. You could change the names
of the methods in the superclasses, but that seems to defeat the purpose of using an inheritance mechanism in the first place.

You can be reasonably assured that implementation for deliver payload for a cargo vehicle is somewhat different than
that for a military craft. How can an object of one class intelligently use both implementations?

To avoid such ambiguities, the engineers at Sun, who developed Java, decided to forgo using multiple inheritance. Still,
you can't deny that the ability to inherit from several superclasses has benefits. The Sun folk use a construct called an Interface.
Rather than spending more time on interfaces in Java, let's defer the discussion to Chapter 10, "Interfaces."

Example of an Inheritance Tree From the Java Libraries

You will see many hierarchies of superclasses and subclasses in an object system. Nowhere is this more evident than in the
Java libraries. For example, Figure 9.1 shows the hierarchy, or tree, for the Java package java.sql from the Sun
Documentation.

You probably have a good idea of how to interpret the following hierarchy tree. The classes to the left are the parents, or the
superclasses, of the classes under and to the right. For example, the class java.util.Date (second from the top) is a subclass
of java.lang.Object; the class
java.lang.SQLException is a subclass of java.lang.Exception.

You may be thinking that the class hierarchy shown in Figure 9.1 belies the comment regarding Java classes inheriting from (at
most) one superclass. You should notice that Figure 9.1 shows class java.sql.DataTruncation as a subclass (or child) of class
java.sql.SQLWarning; it also shows java.sql.SQLWarning as a child of java.sql.SQLException. You may think that class
java.sql.DataTruncation has more than one superclass: the immediate parent (java.sql.SQLWarning) and the 'grandparent'
(java.sql.SQLException). Well, the preceding comment on the parent/grandparent classes is, of course, true. However, when
the literature speaks of a superclass/subclass relationship, the relationship is with the parent and child, not with any
grandparents or grandchildren.
Página 170

Figura 9.1 Árvore de hierarquia de classes para java.sql.

O leitor atento nota que uma classe neste diagrama não tem pai. A classe java.lang.Object é o pai final de cada classe Java.
Em outras palavras, se você fosse examinar cada hierarquia para cada pacote em Java (pacotes que fazem parte do tempo
de execução Java e pacotes criados por desenvolvedores de aplicativos), você encontraria a classe java.lang.Object no
topo de a cadeia de hierarquia.
Y
FL

Como um programador Java, você gastará muito tempo examinando as árvores de hierarquia de classes. Afinal, você não quer codificar
M

quando puder herdar, certo?


A
TE

Um exemplo: implementação de contas bancárias

As tabelas 9.2 e 9.3 com a Listagem 9.1 nas páginas a seguir mostram uma implementação direta dos comportamentos da conta
corrente e poupança que ilustram o uso de herança em Java. As contas possuem as propriedades e comportamentos mostrados nas
Tabelas 9.2 e 9.3.

Team-Fly ®
Página 171

Tabela 9.2 Verificando propriedades e comportamentos da conta

NOME DA PROPRIEDADE FORMATO

Nome da conta Cadeia de caracteres


Suporte

Saldo Atual Moeda


Número de retiradas Inteiro
Número de depósitos Inteiro

COMPORTAMENTO DESCRIÇÃO
Abrir conta corrente Crie uma conta à ordem com o nome do titular da conta e um saldo
inicial. Se o nome da conta não for fornecido, abra a conta para "Pete
Moss".
Faça um depósito Some o valor a ser depositado ao Saldo Atual, gerando um novo valor
para o Saldo Atual. Relatório sobre o status da transação. Se for
bem-sucedido, relate o valor do Saldo Atual e aumente o número de
depósitos.
Faça uma retirada Subtraia o valor a ser retirado do Saldo Atual, gerando um novo valor para o
Saldo Atual. Relatório sobre o status da transação. Se a transação for
bem-sucedida, informe o novo valor do Saldo Atual e aumente o número de
retiradas. Além disso, se o número de retiradas exceder cinco, informe
sobre o número de retiradas.

Denunciar conta Exibir as propriedades acima de uma conta à ordem.


Em formação

Código para classes de conta corrente e poupança

A implementação desses métodos é relativamente direta. A Listagem 9.1 mostra uma implementação não muito ótima.
Seguindo a listagem e uma explicação da versão um tanto sem brilho, você verá uma implementação melhor que
aproveita a herança e alguns outros recursos Java.

Alguns pontos sobre o código a seguir valem um ou dois comentários. Consulte os números colocados
convenientemente à direita do código ao ler esta dissertação.

A linha 1 inicia o construtor para a classe de conta corrente. Você cria objetos da classe de conta corrente usando o Novo
operador, passando um nome e um saldo inicial da conta.
Página 172

Tabela 9.3 Propriedades e comportamentos da conta poupança

NOME DA PROPRIEDADE FORMATO

Nome da conta Cadeia de caracteres


Suporte

Saldo Atual Moeda


Número de retiradas Inteiro
Número de depósitos Inteiro

COMPORTAMENTO DESCRIÇÃO
Abrir conta poupança Crie uma conta poupança com o nome do titular da conta e um saldo inicial.
Se o nome da conta não for fornecido, abra a conta para "Pete Moss".

Faça um depósito Some o valor a ser depositado ao Saldo Atual, gerando um novo valor para
o Saldo Atual. Relatório sobre o status da transação. Se for bem-sucedido,
relate o valor do Saldo Atual e aumente o número de depósitos. Além
disso, se o Saldo Atual exceder 1.000 dólares, adicione 5 por cento do
valor que excede 1.000 dólares ao Saldo Atual.

Faça uma retirada Subtraia o valor a ser retirado do Saldo Atual, gerando um novo valor para o
Saldo Atual. Relatório sobre o status da transação. Se a transação for
bem-sucedida, informe o novo valor do Saldo Atual e aumente o número de
retiradas.

Denunciar conta Exibir as propriedades acima de uma conta poupança.


Em formação

/ **

Uma implementação longe de ser ótima das classes de contas bancárias com as propriedades e
comportamentos descritos

Esta é a classe Checking Account: class CheckingAccount


**/

{
String nameHolder; //Dono da conta
Equilíbrio duplo;
int numWithdrawals = 0, numDeposits = 1;
Página 173

// Construtor para quando passarmos um titular de conta. CheckingAccount (String strAccHolder, int
intDeposit) // 1 {

// Basta inicializar os campos de propriedade para os valores passados nameHolder


= strAccHolder;
Saldo = intDeposit;
}
// Construtor para Pete Moss: {
CheckingAccount (int intDeposit) // 2

// Use Pete M. para titular da conta e o valor passado // para o depósito inicial.

NameHolder = "Pete Moss";


Saldo = intDeposit;
}
/ **
Nenhuma surpresa aqui. . . Subtrair a quantidade (passado arg) de
Saldo da conta existente (saldo)
**/
public void CheckingWithdraw (valor duplo) { // 3

Resposta da string;
if (quantidade> saldo) {

resposta = "Fundos insuficientes. Saldo =" +


saldo + "Da conta corrente";
}
outro
{
saldo - = montante;
numWithdrawals ++;
resposta = "Retirada com sucesso. Saldo =" +
saldo + "Da conta corrente";
}
System.out.println (resposta);
// Lembre-se de que se o número de retiradas //
// excede 5, para relatar o número de retiradas

if (numWithdrawals> 5)
System.out.println ("Número de retiradas =" +
NumWithdrawals);

}
// Coloque algum $$$ em
public void CheckingDeposit (double amount) { // 4

saldo = saldo + montante;


Página 174

numDeposits ++;
System.out.println ("Depósito bem sucedido. Saldo =" +
Saldo + "para conta corrente");
}
//
// Exibir informações sobre esta conta corrente //

public void CheckingAccountInfo () // 5


{
System.out.println ("Verificando Informações da Conta \ n");
System.out.println
("Titular da conta : "+ nomeHolder);
System.out.println ("Dólares na conta:" + saldo); System.out.println ("Num Retiradas:" +

numWithdrawals);
System.out.println ("Num Depósito : "+ numDeposits);
}
}

Listagem 9.1 Implementando a classe de conta corrente.

A linha 2 inicia outro construtor. Este construtor faz com que o nome do titular da conta seja padronizado como Pete Moss. Observe
que o construtor leva um argumento, o saldo inicial da conta.

A linha 3 é o cabeçalho do método para o método de retirada de conta corrente. O código incluído no método implementa o
comportamento da ação de retirada conforme descrito na Tabela 9.2.

As linhas 4 e 5 são os cabeçalhos dos métodos para depósito em conta corrente e métodos de exibição de informações, respectivamente.

A Listagem 9.2 abaixo mostra uma possível implementação Java de uma classe de conta poupança com as propriedades e
comportamentos descritos na Tabela 9.3.

Quaisquer semelhanças entre o código que implementa a conta poupança e o código que implementa a conta corrente não são
acidentais. Você deve ver que a maior parte do código está duplicada em ambas as classes. Este triste estado de coisas clama por
remédio. Felizmente, esse código é Java, o que significa que podemos aproveitar a herança para eliminar a duplicação. A próxima seção
explica essa tarefa.

Aproveitando a herança.

Lembre-se das árvores de hierarquia de veículos que as classes superiores da árvore contêm comportamentos comuns a todos os veículos.
Algumas superclasses no meio da hierarquia tinham propriedades (como pneus e bandas de rodagem) comuns a um grupo de subclasses
relacionadas.
Página 175

/ **
Aqui está a classe Conta Poupança. classe SavingsAccount
**/

{
String nameHolder; //Dono da conta
equilíbrio duplo;
int numWithdrawals = 0, numDeposits = 1;
//
SavingsAccount (String strAccHolder, int intDeposit) {
nameHolder = strAccHolder;
Saldo = intDeposit;
}
//
SavingsAccount (int intDeposit) {
nameHolder = "Pete Moss";
Saldo = intDeposit;
}
//
public void SavingsWithdraw (valor duplo) {
Resposta da string;
if (quantidade> saldo)
resposta = "Fundos insuficientes. Saldo =" +
saldo + "Poupança";
outro {
saldo = saldo - montante; numWithdrawals ++;

resposta = "Retirada com sucesso. Saldo =" +


saldo + "Poupança";
}
System.out.println (resposta);
}
//
public void SavingsDeposit (valor duplo) {
saldo = saldo + montante; numDeposits ++;

// Lembre-se do requisito de que, se o saldo exceder 1.000 //, esse banco acumula 5% a mais no saldo.

if (saldo> 1000)
saldo + = (saldo - 1000) * 0,05;

System.out.println ("Depósito bem sucedido. Saldo =" +


saldo + "para conta poupança");
}
//
public void SavingsAccountInfo () {
System.out.println ("Informações da conta poupança \ n");
Página 176

System.out.println
("Titular da conta : "+ nomeHolder);
System.out.println ("Dólares na conta:" + saldo); System.out.println

("Num de retiradas:" + numWithdrawals);


System.out.println
("Número de depósitos : "+ numDeposits);
}

Listagem 9.2 Implementando a classe de poupança.

Uma observação rápida, mas precisa, seria que as superclasses contêm propriedades e comportamentos comuns a várias
subclasses.

Isso também é verdade com nossas classes de conta corrente e poupança. Um exame superficial das propriedades e comportamentos
dessas classes de contas, com o objetivo de encontrar um terreno comum, produz os dados da Tabela 9.4.

Parece que há muitos pontos em comum aqui. Podemos alavancar a herança executando as seguintes etapas:

1 Crie uma superclasse contendo as propriedades e comportamentos comuns a ambas as subclasses.

2 Escreva o código para as subclasses para refletir as diferenças entre as implementações da subclasse e da superclasse.

Portanto, sem mais delongas, a Listagem 9.3 mostra o código para as classes de conta. Agora temos o código para uma nova classe:
class Conta , a superclasse para as classes CheckingAccount e SavingsAccount, com o código para as classes conta corrente e
poupança. Este código também contém algumas construções Java não peculiares para demonstrar o uso de herança, mas
demonstrando algumas boas práticas de codificação. Dito isso, alguns códigos não são ideais; o sacrifício é feito para ilustrar alguns
pontos sobre herança em Java.

Mais uma vez, consulte as linhas numeradas durante a breve e informativa dissertação sobre o código.

A linha 1 é a declaração de classe para a classe Account (a superclasse). A classe Account termina pouco antes da linha 5; as
propriedades comuns a todas as subclasses de Account são declaradas aqui. Por herança, qualquer subclasse tem acesso imediato e
irrestrito a essas propriedades.

Observe o qualificador de classe - resumo. Ao declarar a classe como abstrata, o Java não permitirá que a classe seja instanciada.
Lembre-se de que estamos interessados nas contas correntes e de poupança; a razão para usar a classe Account é tirar vantagem
do mecanismo de herança do Java. Nós não deseja objetos da classe Conta. Podemos estabelecer a lei e Java irá aplicá-la. Por
exemplo, este código,

Conta anAcct = nova conta ("Lou", 250000);

irá produzir o seguinte diagnóstico do compilador Java:

classe Conta é uma classe abstrata. Não pode ser instanciado.


Conta anAcct = nova conta ("Lou", 250000);
Página 177

Tabela 9.4 Propriedades e comportamentos comuns a contas correntes e de poupança

NOME DA PROPRIEDADE FORMATO

Nome da conta Cadeia de caracteres


Suporte

Saldo Atual Moeda


Número de retiradas Inteiro
Número de depósitos Inteiro

COMPORTAMENTO DESCRIÇÃO
Abra uma conta Crie uma conta com o nome do titular da conta e um saldo inicial. Se o
nome da conta não for fornecido, abra a conta para "Pete Moss".

Faça um depósito Some o valor a ser depositado ao Saldo Atual, gerando um novo valor
para o Saldo Atual. Relatório sobre o status da transação. Se for
bem-sucedido, relate o valor do Saldo Atual e aumente o número de
depósitos.
Faça uma retirada Subtraia o valor a ser retirado do Saldo Atual, gerando um novo valor para o
Saldo Atual. Relatório sobre o status da transação. Se a transação for
bem-sucedida, informe o novo valor do Saldo Atual e aumente o número de
retiradas.

Denunciar conta Exibir as propriedades acima de uma conta.


Em formação

Classe abstrata Conta // 1


{
privado String nameHolder; // 2
privado equilíbrio duplo;
privado int retiradas = 0, depósitos = 1;
//
Conta (String strAccHolder, int intDeposit) { // 3
// Basta inicializar os campos de propriedade para os valores passados nameHolder
= strAccHolder;
Saldo = intDeposit;
}
Conta (int intDeposit) {
nameHolder = "Pete Moss";
Página 178

Saldo = intDeposit;
}
public String getNameHolder () { // 4
return nameHolder;
}
public double getBalance () {
saldo de retorno;
}
public int getWithdrawals () {
devolver retiradas;
}
public int getDeposits () {
depósitos de retorno;
}
//
public void saque (valor duplo) {
Resposta da string;
if (quantidade> saldo)
resposta = "Fundos insuficientes."; outro
{
saldo = saldo - montante; retiradas ++;

resposta = "Retirada com sucesso.";


}
System.out.println (resposta);
}
//
depósito anulado público (valor duplo) {
saldo = saldo + montante; depósitos ++;

}
//
public String accountInfo () {
return ("Titular da conta : "+ nomeHolder +" \ n "+:" + saldo + "\ n" +
"Dólares na conta
"Número de retiradas:" + retiradas + "\ n" + "Número de depósitos
: "+ depósitos);
}
} // 5
//
classe CheckingAccount extends Account { // 6
CheckingAccount (String strAccHolder, int intDeposit) {
super (strAccHolder, intDeposit);
}
CheckingAccount (int intDeposit) {
super (intDeposit); }
Página 179

// 7
public void saque (valor duplo) {
String strMsg;
// 8
super.retirar (valor); // 9
strMsg = "Balance =" + getBalance () +
"para conta corrente";
// 10
retiradas int = getWithdrawals (); if (retiradas> 5)

strMsg = strMsg + "\ n # de retiradas é" +


retiradas;
System.out.println (strMsg);
}
depósito anulado público (valor duplo) {
String strMsg;
super.deposit (quantidade);
System.out.println ("Balance =" + getBalance () +
"para conta corrente");
}
//
public String accountInfo () {
String strMsg;
strMsg = super.accountInfo ();
strMsg = "Verificando informações da conta \ n" +
"---------------------------- \ n" +
strMsg;
return strMsg;
} // 11
}
classe SavingsAccount estende conta
{
//
SavingsAccount
(String strAccHolder, int intDeposit) {
super (strAccHolder, intDeposit); }

SavingsAccount (int intDeposit) {


super (intDeposit); }

//
public void savWithdraw (double amount) {

String strMsg;

retirar montante);
System.out.println ("Balance =" + getBalance () +
"para conta poupança");
Página 180

}
public void savDeposit (double amount) {
String strMsg;
equilíbrio duplo = getBalance (); if (saldo + montante>
1000)
depósito (valor + (valor - 1000) * 0,05);
outro // 12
valor do depósito ) ;
System.out.println ("Balance =" + getBalance () +
"para conta poupança");
}
public String savAccountInfo () {
String strMsg;
strMsg = accountInfo ();
strMsg = "Informações da conta poupança \ n" +
"--------------------------- \ n" +
strMsg;
return strMsg;
}
}

Listagem 9.3 Classes de contas bancárias usando herança.

Você tem que adorar o diagnóstico simples em inglês do compilador Java, não é?

A linha 2 inicia o primeiro construtor; o segundo construtor segue imediatamente o primeiro. Agora, como você viu, uma chamada
Y

para o construtor não vou crie um objeto da classe Account. No entanto, você verá como o código Java usa esses construtores nas
FL

classes CheckingAccount e SavingsAccount.


M

A linha 3 inicia um grupo de métodos chamados obter métodos. Você se lembra de ter lido sobre os métodos get e set no Capítulo 8,
A

"Encapsulando e ocultando dados e métodos". Os programadores Java podem discordar em muitas coisas, mas são quase unânimes em
concordar sobre a declaração de variáveis de instância privado, e usando métodos get e set para acessar as variáveis. A motivação é parar o
TE

código que irá alterar o estado de uma conta acessando indevidamente uma variável de instância. Por exemplo, se uma classe usando as
classes de conta bancária tivesse o seguinte código,

CheckingAccount anAcct = new CheckingAccount ("Lou", 250000); anAcct.deposits


+ = 12;

o estado do objeto anAcct seria inconsistente. Esta referência é ilegal quando os depósitos são declarados privados. A linha 9 mostra como
uma subclasse faria referência a uma propriedade usando o método get.

Os métodos entre as linhas 4 e 5 implementam um comportamento comum às classes de conta corrente e poupança.
Posteriormente no código, você verá referências a esses métodos nas subclasses.

Team-Fly ®
Página 181

A linha 5 é a instrução de classe para a primeira subclasse, CheckingAccount. Observe o uso do estende
palavra-chave. A palavra-chave extends informa ao Java que as duas classes nomeadas têm um relacionamento de subclasse / superclasse. A
linha 12 também usa a palavra-chave extends.

A linha 6 inicia os construtores da classe CheckingAccount. Observe a referência aos construtores na superclasse usando o super palavra-chave.
Você verá uma referência semelhante para a outra subclasse após a linha 11.

A linha 7 inicia a implementação do método de retirada para a subclasse CheckingAccount. Como o nome do método encontrado na
superclasse também é retirado, Java precisa de um mecanismo para se referir ao método com nome semelhante na superclasse. Java
usa o super palavra-chave para esse fim, conforme visto na linha 8.

Como as variáveis de instância são declaradas privadas na classe Account, todas as outras classes precisam invocar os métodos get
para acessar os valores da variável. As linhas 9 e 10 ilustram o uso dos métodos get para o saldo atual e o número de propriedades de
retiradas. o depósito método na classe CheckingAccount usa construções semelhantes para fazer o trabalho.

Quando uma subclasse usa um método com o mesmo nome de um em sua superclasse, dizemos que a subclasse é
predominante o método da superclasse. Os dois métodos devem retornar o mesmo tipo de dados, um objeto da mesma classe ou void.

Os métodos da classe SavingsAccount são não nomeado de forma idêntica àqueles em sua superclasse. Portanto, os métodos de
SavingsAccount podem fazer referência a métodos de superclasse diretamente. A linha 12 mostra essa referência ao método retirar. O
Java runtime procura o método declarado na classe contida (SavingsAccount). Não encontrando o método, o Java runtime pesquisa
a superclasse para o método, o encontra e o executa. O Java runtime continuaria pesquisando na árvore de hierarquia procurando o
método e executando o primeiro que encontrar.

À primeira vista, você pode pensar que o código que não depende de herança é mais curto e simples. Bem, talvez neste caso isso seja
verdade. No entanto, este código mostra como a herança em Java é um recurso poderoso que permite a verdadeira reutilização de código.
Imagine implementar um terceiro tipo de conta bancária, digamos uma conta do mercado monetário, e usar os comportamentos da classe
Conta pai. Como os veículos que todos precisam ser ligados, parados e dirigidos, as contas bancárias precisam ser abertas, ter dinheiro
depositado e ter dinheiro retirado. A herança certamente aliviará sua carga.

Efeitos do lançamento de e para superclasses e subclasses

Antes de terminar este capítulo, você precisa saber que pode lançar de e para classes e superclasses e alguns dos efeitos
interessantes dessa conversão. Como você lerá, às vezes você verá alguns resultados que parecem contra-intuitivos.

o apenas permitido o cast entre objetos de classes diferentes é quando essas classes têm um relacionamento superclasse /
subclasse. Por exemplo, como a classe Object é a classe raiz ou uma superclasse de todas as classes Java, o seguinte é
válido:

Object myObj = (Object) myChecking; // 1


Página 182

Em outras palavras, cada objeto de qualquer classe em Java pode ser convertido em um objeto da classe Object. Como a classe

Account é a superclasse da classe CheckingAccount, isso também funciona:

Conta myAcct = (Account) myChecking; // 2

No entanto, este não vou trabalhos

MyChecking = (SavingsAccount) mySavings; // 3

porque as classes CheckingAccount e SavingsAccount não têm um relacionamento superclasse / subclasse. Não se deixe enganar porque
as classes de conta corrente e poupança compartilham uma superclasse.

Como uma classe filha 'pertence' a seu pai no sentido de que o específico (subclasse) é uma instância da geral (superclasse), você
não precisa codificar um elenco ao atribuir um filho a um pai. O operador de atribuição Java funciona imediatamente. Portanto,
essas declarações são legais,

Object myObj = myChecking;


Conta myAcct = myChecking; // 4

e equivalente às atribuições de elenco rotuladas // 1 e // 2. No entanto, você não pode atribuir o geral ao específico; um elenco é
necessário. Então, você pode codificar o seguinte,

myChecking = (conta) myAcct; // 5

mas não o seguinte:

myChecking = myAcct; // 6

Olhando para trás na linha // 5, você deve saber que embora esta linha mostre uma construção de codificação válida, o código ainda pode resultar em
um erro de tempo de execução. Por exemplo, você pode tentar ser astuto e dissimulado e codificar o seguinte:

Conta myAcct = myChecking; // 7


mySavings = (Conta Poupança) Minha Conta;

Seu raciocínio pode ser que essa construção permite que você lance uma conta corrente em uma conta poupança ou, para obter a linha // 3
acima para compilar e executar. Afinal, todo mundo tenta enganar o compilador de vez em quando. Bem, as duas linhas anteriores são
compiladas. No entanto, durante a execução, o Java runtime emite um
ClassCastException, que é basicamente o que parece.

Lembre-se de que a atribuição de objetos em Java não cria cópias dos objetos. Os nomes de objetos Java são uma referência ao
objeto, não ao próprio objeto. A atribuição da verificação
Página 183

objeto de conta para um da classe Conta não cria um novo objeto, apenas uma referência a um existente. Portanto, o seguinte
código

Conta myAcct = (Account) myChecking;


System.out.println ("Informações da conta =" + minhaAcct.accountInfo ());

irá listar as informações da conta para myChecking, não myAcct.

Além disso, como a atribuição de objeto opera em referências de objeto, você pode atribuir e lançar objetos de classes relacionadas para
frente e para trás. Olhando anteriormente para a linha 1, você pode pensar que quando o Java lançou o objeto da subclasse
CheckingAccount para um objeto da (mais) geral das classes Java, o ambiente Java 'perdeu' as informações da subclasse. Você pode achar
que isso é verdade, especialmente se esqueceu que Java opera em referências, não em objetos. Mesmo após a atribuição na linha 1, o
objeto original ainda existe. Ergo, as declarações

Object myObj = myChecking;


myChecking = (CheckingAccount) myObj; // 8
System.out.println (myChecking.accountInfo ());

listará as informações da conta corrente. Claro, você percebe que o elenco codificado na linha // 8 é requeridos.

Para envolver essa atribuição / conversão de e para subclasses e superclasses, saiba que você pode atribuir uma instância de subclasse a uma
instância de superclasse com ou sem elenco, você pode atribuir uma instância de superclasse a uma instância de subclasse com um elenco, e você
não pode atribuir instâncias de classes não relacionadas, com ou sem um elenco.

Em suma

Este capítulo explorou a propriedade vitalmente importante da herança. Você viu como a herança permite modelar
características comuns de várias classes em uma superclasse. O código que implementa as características comuns pode
ser usado por classes filhas.

É claro que a reutilização de código economiza tempo porque você não precisa reinventar as rodas, mas a melhoria da qualidade do sistema é
outro de seus benefícios. O código reutilizado, presumivelmente livre de erros, não deve apresentar problemas quando incorporado aos sistemas.

Herança é um recurso não disponível para o programador COBOL ou PL / I na loja de mainframe. Você, o programador de
mainframe, pode ter que pensar em linhas diferentes ao escrever Java porque a herança é parte integrante dos programas
Java.
Página 184

Esta página foi intencionalmente deixada em branco.


Página 185

CAPÍTULO 10
Interfaces

O Capítulo 9, "Herança", discutiu a importante propriedade de objeto da herança. Você leu que Java oferece suporte a um único
esquema de herança. Você também leu uma vaga referência ou duas sobre um esquema Java que fornece alguns benefícios de
herança múltipla, a saber interfaces. Agora é a hora de explorar as interfaces Java em detalhes.

Você lerá uma breve descrição das interfaces, seguida por uma explicação de como as interfaces Java superam algumas das
limitações do modelo de herança única. Este breve capítulo cobre o relacionamento entre interfaces, classes abstratas e classes
concretas. Você aprenderá como criar e implementar interfaces e verá alguns exemplos de codificação de interfaces em uso.

O que são interfaces?

Uma interface Java é uma coleção de comportamentos abstratos; um objeto declara que implementa isso. Em outras palavras, uma interface
é uma promessa de que os comportamentos, ou resumo métodos contidos na interface serão implementados pela declaração de objetos.

O conceito de uma interface Java é semelhante ao de uma API. Afinal, o I na API significa interface, certo? Uma API é um conjunto de
comportamentos usados por objetos. Quando você usa o Conectividade de banco de dados Java ( JDBC) API para emitir uma instrução SQL,
você conta com a API JDBC para fazer seu trabalho. Você não sabe como a chamada é implementada. Tudo o que importa é a promessa do
comportamento esperado.

Claro, apenas declarar que um objeto usa este ou aquele comportamento não é suficiente. Os comportamentos requerem uma
implementação. No caso de usar um núcleo, opcional ou
Página 186

API do fornecedor, a implementação já está codificada e disponibilizada para seu uso. No caso de uma interface Java,
alguém deve codificar a implementação. Afinal, não é mágica.

O jargão relevante é que uma classe implementos interfaces; isso não deve ser confundido com uma subclasse que
estende uma superclasse. Uma classe pode estender uma superclasse e implementar uma ou mais interfaces. A declaração tem a seguinte
aparência:

classe MySubClass estende MySuperClass implementa MyInterface1, MyInterface2


{. . .

A palavra-chave implements pode ser usada separadamente da palavra-chave extends e vice-versa.

Os programadores de mainframe também usam APIs para emitir SQL ou criar telas de entrada de dados. APIs de mainframe são como bibliotecas de
funções; o programa emite uma chamada para uma função API e obtém um resultado. As interfaces em Java não são usadas como bibliotecas de
funções. Interfaces especificam o que deve ser feito, não como fazer isso.

Por que as interfaces são úteis?

Às vezes, as interfaces são incorretamente anunciadas como permitindo ao programador Java tirar vantagem da herança múltipla.
Uma inclinação mais precisa é que as interfaces fornecem ao programador Java um mecanismo para superar algumas das
deficiências da herança única.

Uma dessas deficiências foi ilustrada no Capítulo 9 com as duas árvores de hierarquia para veículos. Usando um único modelo de
herança, você não tem um método fácil de implementar as árvores e aproveitar a herança. Você escolheria uma árvore,
implementando os métodos na outra sem os benefícios da herança.

Um conjunto de interfaces define outra hierarquia separada e distinta da hierarquia de herança. No entanto, as interfaces são muito mais;
as interfaces permitem ao programador Java imbuir objetos com comportamentos de várias classes. Quando você afirma que sua classe
implementa uma interface, você está dizendo que os objetos de sua classe podem ser usados em qualquer lugar em que a interface
seja usada. Por exemplo, a seguinte declaração

public class Jets extends AirVehicles implementa PassengerVehicles, CargoVehicles, MilitaryVehicles {. . .

estabelece duas hierarquias distintas: a hierarquia de passageiros / carga / veículo militar e a hierarquia de terra / mar / ar /
espaço. Esta declaração afirma que você pode usar objetos da classe Jets em qualquer lugar em que usaria um
PassengerVehicle, um CargoVehicle ou um MilitaryVehicle.

Lembre-se de que, com a hierarquia de herança, as subclasses têm acesso imediato às implementações fornecidas na superclasse. A
hierarquia da interface é um animal diferente. Cada classe que implementa uma interface deve fornecer ou ter acesso a uma
implementação dos comportamentos declarados na interface. O acesso seria por meio de uma superclasse. Em outras palavras, se
uma superclasse implementa uma ou mais interfaces, todas as subclasses, portanto, têm acesso às implementações.
Página 187

Referindo-se à declaração anterior e à Tabela 9.1, a classe Jets precisaria implementar os métodos CollectFares,
DeliverPayload, ConfirmDestination e assim por diante. Agora, se a seguinte declaração existisse,

public class Jets extends AirVehicles implementa PassengerVehicles, CargoVehicles, MilitaryVehicles {. . .

então a classe Jets teria acesso às implementações das três interfaces da classe AirVehicles.

Outra razão pela qual as interfaces são úteis é que as interfaces fornecem um mecanismo para separar as implementações das
classes que usam as implementações. Ao separar os dois, você pode alterar as implementações sem impactar as classes que as
usam. Como diz o velho ditado, a mudança é transparente para o usuário.

E as superclasses abstratas ?.

Comparar interfaces com uma superclasse abstrata é uma comparação útil. Lembre-se de que uma classe abstrata contém métodos
abstratos ou assinaturas de método, tipos de retorno e uma cláusula opcional throws, mas sem implementação. As interfaces são
semelhantes, visto que uma interface também contém uma lista de métodos abstratos. No entanto, existem diferenças conceituais entre
os dois.

Uma superclasse abstrata carrega esse tom de "incompletude"; a superclasse requer uma ou mais subclasses para completá-la. As interfaces
não são incompletas neste sentido. Uma interface é uma declaração de que uma lista de comportamentos deve ser implementada de acordo com
um conjunto de especificações (assinaturas).

O código presente na superclasse impõe algumas restrições sobre como as subclasses concluem a implementação. Em outras palavras, a
superclasse pode fornecer uma implementação básica, a subclasse pode fornecer os detalhes. A implementação básica pode limitar as
implementações de subclasses subsequentes.

Por exemplo, vamos supor que nossa hierarquia de contas bancárias habilitada para um identificador de conta propriedade. Os comportamentos que
lidam com essa propriedade podem ser os seguintes:

Atribua um identificador de conta.

Recupere informações da conta por identificador. Adicionar,

alterar ou excluir conta por identificador.

A implementação desses comportamentos pode depender do identificador de conta ou partes do identificador de conta, sendo um certo tipo
primitivo, como int, ou objeto de uma determinada classe criada pelo usuário. Nesse caso, as implementações de método nas subclasses
de conta corrente e poupança devem aderir a quaisquer restrições colocadas pela seleção do tipo de dados subjacente usado na
superclasse.

Então onde está o problema? Um problema pode surgir se houver um desejo de implementar essa propriedade como diferentes tipos de dados,
dependendo das várias circunstâncias. Talvez uma implementação dessa propriedade seja mais adequada para acesso à memória, outra
poderia ser mais adequada para acesso ao disco. O modelo de herança única não permite facilmente esse tipo de flexibilidade.
Página 188

Criação de interfaces

A sintaxe para criar uma interface é direta. A seguir está um exemplo:

public interface PassengerVehicles {

Public double collectFares ();


public boolean confirmDestination (Destination where); // Mais assinaturas de método aqui, talvez

Você codifica a declaração de interface anterior em um arquivo chamado PassengerVehicle.java como você faria com qualquer fonte
java. O compilador cria um arquivo de classe, que é PassengerVehicle.class. Você pode passar instâncias de PassengerVehicles, que são
argumentos para métodos. Até agora, declarar e usar a interface se assemelha muito a declarar e usar uma classe, certo? No entanto,
existem diferenças, que são mostradas a seguir:

As interfaces são públicas; todos os métodos contidos são públicos. As interfaces não podem conter variáveis

de "classe" - apenas variáveis declaradas final .

As interfaces não contêm corpo, apenas assinaturas de método.

Vamos ver alguns exemplos de código que unem interfaces e classes.

Exemplo: Implementando os Tipos de Veículos

Aqui, você verá algum código que implementa a herança e as hierarquias de interface para nossas classes de veículos. Para sua
conveniência, a Tabela 10.1 é uma seção da Tabela 9.1 mostrando partes das hierarquias do veículo.

Tabela 10.1 Tabela 9.1 revisitado

VEÍCULO HIERARQUIA 1 VEÍCULO HIERARQUIA 2


• Veículo • Veículo
• Veículo Terrestre • Veículos de passageiros

• Caminhões • Caminhões
• Veículos aquáticos • Jatos
• Barcos, iates • Veículos de carga
• Veículos Aéreos • Caminhões
• Jatos • Veículos militares
• Veículos Espaciais • Tanques
• Foguetes • Jatos
Página 189

• Alguns comportamentos do veículo • Alguns comportamentos do veículo

• Iniciar, parar, dirigir • Pare, comece, dirija


• Alguns comportamentos de veículos terrestres • Alguns comportamentos de veículos de passageiros

• rodagem
Reparar pneu ou banda de • Recolher as tarifas, confirmar o destino
• Alguns comportamentos de veículos aéreos • Alguns comportamentos de veículos de carga

• Descolar • Entregar carga útil, confirmar embarque


• Terra • Alguns comportamentos de veículos militares

• Confirmar pedidos, entregar carga útil

O código a seguir mostra o herança hierarquia:

// A classe pública de classe superior


Vehicle {

String VehicleName;
//Construtor
Veículo (String aName) {
VehicleName = aName;
}

public void start () {


System.out.println (VehicleName + "Foi Iniciado");

}
public void steer () {
System.out.println (VehicleName + "é direcionado");

}
public void stop () {
System.out.println (this.vehicleName + "Parou");

}
}

public class LandVehicles extends Vehicle {


//Construtor
LandVehicles (String aName) {
// Observe a referência ao construtor da superclasse super ("Land Vehicle" +
aName);
}

public void repairTireorTread () {


Página 190

System.out.println ("Reparando pneus ou rodando" +


this.vehicleName);

}
public class AirVehicles extends Vehicle {

AirVehicles (String aName) {


super ("Veículo Aéreo" + aNome); }

public void takeOff (AirVehicles aVeh) {


System.out.println (aVeh.vehicleName + "Decolando !!!");

}
public void land () {
System.out.println ("O" + this.vehicleName +
"pousou");

}
}

Observe que os métodos têm implementações (ou, por assim dizer). A implementação do método takeOff () leva um argumento da
classe AirVehicles para fazer referência à propriedade; o método land () usa isto,
as implementações de start () e steer () (e outros) fazem referência à variável de instância diretamente. A última referência é preferível
às duas primeiras.
Y

interface PassengerVehicles {
FL

Public double collectFares ();


public boolean confirmDestination (Destination where);
A M

}
TE

interface CargoVehicles {
public boolean confirmShipment (Shipment whatShipped); public void deliveryPayload ();

interface MilitaryVehicles {
public boolean confirmOrders (Ordena as Ordens); public void deliveryPayload ();

Essas interfaces não têm o público modificador de visibilidade codificado. Você pode supor que esse código aparece em um arquivo de
classe com o modificador público. Não espere ver nenhum corpo de método aqui como você fez com as superclasses anteriores.
Lembre-se de que os métodos declarados nas interfaces são implementados nas classes que usam as interfaces.

Team-Fly ®
Página 191

Os códigos para as classes Jet e Truck estão listados a seguir:

classe Jets estende AirVehicles


implementa PassengerVehicles, CargoVehicles, MilitaryVehicles
{
//Construtor
Jets (String aName) {
super ("O Jet" + aNome); }

public double collectFares () {


System.out.println ("Jet Fares Collected For" +
this.vehicleName);
return 1010,11;
}
public boolean confirmDestination (Destination jetDest) {
System.out.println ("Jet Destination Confirmed For"
+ this.vehicleName);
retorna falso ) ;
}
public void confirmShipment () {
System.out.println ("Envio a jato confirmado para"
+ this.vehicleName);
}
public void confirmOrders () {
System.out.println ("Pedidos de Jet confirmados para"
+ this.vehicleName);
}
public void DeliverPayload () {
System.out.println ("Payload DeliveredFor"
+ this.vehicleName);
}
// Outros métodos, talvez

classe Trucks estende LandVehicles


implementa PassengerVehicles, CargoVehicles, MilitaryVehicles
{
// refere-se ao construtor da superclasse Trucks (String aName) {

super ("O Caminhão" + aNome); }

public double collectFares () {


System.out.println ("Tarifas de caminhão coletadas para"
+ this.vehicleName);
return 2323,11;
}
public boolean confirmDestination (Destination truckDest) {
System.out.println ("Destino do caminhão confirmado para"
Página 192

+ this.vehicleName);
return (true);
}
public void confirmShipment () {
System.out.println ("Envio de caminhão confirmado para"
+ this.vehicleName);
}
public void confirmOrders () {
System.out.println ("Pedidos de caminhão confirmados para"
+ this.vehicleName);
}
public void DeliverPayload () {
System.out.println ("Truck Payload Delivered For"
+ this.vehicleName);
}
// Outros métodos, talvez
}

Observe que ambas as classes devo fornecer implementações para tudo métodos declarados nas interfaces. Obviamente, você está livre para
declarar e codificar outros métodos não definidos na interface. Aqui, você deve usar a palavra reservada Java isto para se referir ao objeto em
questão quando você precisa acessar as variáveis de instância.

A seguir, o código a seguir se refere a objetos das classes Jet e Truck:

public class VehicleExample {

public static void main (String [] nomes) {

Jatos myJet = novos Jets (nomes [0]);


Caminhões myTruck = novos caminhões (nomes [1]);

jetFare duplo = myJet.collectFares (); // 1


tarifa de caminhão dupla = meuTruck.collectFares (); // 2

whichPVehicle (myJet); // 3
whichPVehicle (myTruck); // 4

}
void static whichPVehicle (PassengerVehicles ph) {
System.out.println ("em que Rotina de Veículo"); System.out.println (ph.collectFares
());
}
}

O método main () aceita a matriz de argumento string, que o método usa para nomear os objetos. Observe que os objetos Jets e
Trucks têm sua própria implementação do método collectFares (); a linha 1 invoca o método para a classe Jets, a linha 2 invoca
o método para a classe Trucks.
Página 193

As linhas 3 e 4 mostram a força das interfaces. Observe que o método whichPVehicle () aceita um argumento de Veículo passageiro. No entanto, a
linha 3 passa um argumento da classe Jets. Java permitirá essa construção; quando uma classe implementa uma interface, a classe pode ser
usada em qualquer lugar em que a interface possa ser usada. É por isso que a linha 4, que passa um argumento da classe Trucks, também é
permitida.

Em suma

A importância das interfaces é separar os comportamentos das classes que usam os comportamentos. O código anterior mostra como você
pode abstrair esses comportamentos referenciando um comportamento de classe por meio de uma interface. Se você fosse alterar as
implementações dos métodos declarados nas interfaces, não precisava alterar o código que acessa esses métodos.

No próximo capítulo, você verá como usar interfaces com elementos GUI para lidar com eventos.
Página 194

Esta página foi intencionalmente deixada em branco.


Página 195

CAPÍTULO 11
Java Event-Handling Basics

Antigamente, as telas verdes eram a interface entre o cliente e o computador e dominavam a paisagem de processamento de dados.
(Agora, os emuladores de terminal de mainframe que rodam em PCs substituíram as clássicas telas verdes.) Os programas tinham um
fluxo sequencial. Sistemas em lote executando montanhas de produção autônoma. Para os sistemas que exigiam entradas do usuário, os
requisitos de entrada - o quê, onde e quando - eram bem definidos. As entradas do usuário, em sua maioria, preenchiam telas verdes e
transações acionadas.

Grande parte do mundo do mainframe ainda é assim, mas com uma grande exceção: a tela verde cedeu ao computador pessoal
como a interface entre o cliente e o computador. O cliente inserindo dados em uma tela verde usa uma interface baseada em
caracteres; sua amiga no final do corredor, inserindo dados em um PC, usa um Interface gráfica do usuário ( GUI).

As entradas de tela verde normalmente permitem que um cliente preencha os campos de entrada de dados e pressione o Entrar ou uma tecla de
função para aceitar as entradas e prosseguir para a próxima etapa do aplicativo. Em contraste, as entradas da GUI permitem que um cliente insira
dados, clique em botões, invoque ações de menus, exiba janelas adicionais e assim por diante. Como você pode imaginar, programar um
aplicativo para aceitar entradas do usuário a partir de uma tela verde é muito diferente de uma GUI.

Este breve capítulo discute os fundamentos da escrita de programas Java para processar eventos. Após um breve discurso sobre eventos, você
lerá sobre estruturas de processamento de eventos de alto nível chamadas modelos de eventos
e, claro, o modelo de evento Java. Para ter um controle sobre o tratamento de eventos Java, você precisará de um pouco de conhecimento sobre
contêineres GUI e componentes de interface, que você também obterá neste capítulo. Você verá algum código Java que captura eventos da GUI. Este
capítulo termina com um resumo do tratamento de eventos Java.
Página 196

Processamento de Eventos

Em um alto nível, você pode definir um evento como algo que acontece durante a execução de um programa que exige atenção. Por
exemplo, quando seu programa chega ao final de um arquivo, ele deve realizar alguma ação, como fechar o arquivo. Muitos programas
executados em um ambiente de mainframe lidam com uma série de previsível eventos, especialmente programas que são executados em
lote. O tratamento de eventos é parte integrante do trabalho do programador.

Observe que esta descrição não trata especificamente de aplicativos que aceitam entradas do usuário. O Capítulo 12, "Tratamento de
exceções e princípios básicos de thread", discute o mecanismo Java para tratar certos eventos chamados exceções. Neste capítulo, você lerá
sobre como o Java lida com eventos que são criados como resultado de entradas do usuário.

Os aplicativos de tela verde processam as entradas do usuário de maneira síncrona; ou seja, as entradas chegam ao programa como um conjunto de
fluxos bem ordenados. O programa "espera" as entradas do usuário em determinados momentos durante sua execução. Na maioria das vezes, o
programa ficará parado e aguardará que os usuários concluam suas entradas, preencham as telas de entrada de dados e pressione E NTER ou uma tecla
de função para permitir que o programa continue a execução.

O processo que os programas de mainframe de tela verde usam para coletar entradas do usuário é relativamente simples: o programa é
executado até exibir uma tela verde onde o programa espera pacientemente que o usuário complete as entradas da tela e acerte um continuar ou
processo chave. Este processo é repetido até que o programa tenha todas as entradas necessárias do usuário, após o que o programa é
executado, sem ser percebido pelo usuário.

Em contraste, um aplicativo GUI processa as entradas do usuário de maneira assíncrona. As entradas chegam ao programa de maneiras
imprevisíveis: algumas clicando em um botão, algumas inserindo texto em uma área de texto e outras selecionando um item de menu. Um
programa GUI não tem uma maneira razoável de saber
antes do tempo que tipo de elementos da GUI um usuário usará para fornecer entradas a qualquer momento.

A imprevisibilidade das entradas recebidas por um aplicativo GUI exige um processo diferente daquele usado para aplicativos de tela verde. Um
aplicativo GUI precisa lidar com tudo o que for lançado em seu caminho; o aplicativo deve responder aos eventos conforme eles ocorrem. O
termo de cinco dólares usado para descrever a programação para responder a eventos é (não surpreendentemente) programação orientada a
eventos.

Um esquema frequentemente usado para implementar programação orientada a eventos é codificar uma construção de caso bastante grande dentro de
um loop condicional. O loop é executado enquanto a entrada do usuário é processada. A construção de caso que entra em ação dependendo do tipo de
evento gerado está dentro do loop. Quando não há mais eventos que precisam ser processados, o programa processa o que deve na ausência de
eventos do usuário, caso contrário, ele termina. A Figura 11.1 ilustra esse conceito.

A coluna da esquerda mostra algumas das inúmeras fontes de entrada disponíveis para aceitar entradas ou aquelas disponíveis para gerar
eventos de entrada. Agora, o programa não tem como saber quais dispositivos de entrada serão usados ou em que ordem o usuário usará esses
dispositivos. Ergo, o ambiente de computação, estabelece um fila de eventos para hospedar eventos gerados. A linguagem de programação possui
uma biblioteca de funções de interface de programa de aplicativo (API) que pode buscar esses eventos e informações relacionadas (como a
origem do evento) da fila de eventos. O loop condicional contém uma construção de caso que invoca algum usuário
Página 197

Figura 11.1 Loop de programação orientado a eventos.

rotina escrita com base na origem do evento. O loop termina quando nenhum outro evento precisa ser processado.

Com a fonte de entrada determinada dentro da construção de caso, cada rotina chamada na construção de caso determina a natureza do
evento. Por exemplo, o doKeyPressed a rotina escrita pelo usuário emite chamadas API adicionais para determinar qual caractere do
teclado, ou combinação de caracteres, foi pressionado; a doMouseClick a rotina emite chamadas para determinar qual botão do mouse foi
clicado, onde o mouse foi clicado na tela e assim por diante. Se a determinação da natureza do evento exigir informações adicionais, o
programa emitirá chamadas API adicionais para obter essas informações. Se o evento foi gerado por um clique do mouse, o programa
pode precisar determinar apenas o que foi clicado (botão, barra de rolagem e assim por diante) ou onde o clique ocorreu. É tudo muito
processual, não é?

Componentes de interface gráfica Java

Antes de ler detalhes sobre o tratamento de eventos Java, algumas palavras sobre os componentes da interface gráfica Java são
necessárias. Este tópico é grande o suficiente para vários capítulos; a
Página 198

As bibliotecas de componentes da interface Java contêm centenas de classes. O objetivo principal desta seção é familiarizá-lo com alguns
fundamentos da GUI Java que são necessários para compreender o mecanismo de manipulação de eventos do Java; o objetivo secundário é
fornecer uma base suficiente para tornar mais inteligíveis as leituras sobre os componentes da GUI Java.

Por que não usar vários capítulos para explicar os componentes da GUI Java, você pode perguntar? Bem, com toda a probabilidade, você, o
programador de mainframe, não desenvolverá interfaces de usuário complexas em Java para o cliente. Mais provavelmente, você escreverá Java
para usar uma interface de usuário existente, como um navegador da web. Seu tempo é valioso, certo?

A base dos componentes Java GUI é o Abstract Windowing Toolkit ( AWT). O AWT faz mais do que fornecer classes para
componentes GUI. No entanto, nesta seção, limitaremos a discussão do AWT aos componentes gráficos do AWT.

A ideia por trás do AWT é fornecer um conjunto de serviços que permitiria a um programa Java usar a GUI e outros serviços na plataforma. Em
outras palavras, um componente de botão AWT criado por um programa Java em execução em uma caixa Wintel é na verdade um botão do
Windows; em um Macintosh, é um botão do Mac e assim por diante. O AWT depende muito da plataforma para renderizar os componentes da
GUI.

Essa dependência de serviços do sistema operacional para renderizar componentes GUI é um pouco preocupante. Muitas pessoas pensaram que a
renderização de componentes Java GUI deveria ser feita com Java. Em resposta a esta linha de pensamento, a Sun criou o Balanço conjunto de
componentes. Swing, uma biblioteca central desde o JDK 1.2, destina-se a substituir os componentes da GUI no AWT.

Embora você possa misturar componentes GUI do AWT e Swing em um aplicativo, é recomendável seguir esta
diretriz simples: Ao usar JDK 1.1 e anterior, use AWT; ao usar JDK
1.2 e posterior, use Swing.

Usar componentes GUI do AWT ou do Swing é conceitualmente semelhante; você instancia uma classe Java da biblioteca
apropriada (AWT ou Swing) para criar um objeto que representa o componente gráfico desejado. Portanto, para AWT, este
código cria um botão:

import java.awt. *;
// Um dos dois construtores para a classe AWT Button Button myButton = new Button
("Texto do botão");

E este código cria um botão Swing:

import javax.swing. *;
// Um dos quatro construtores para a classe Swing JButton JButton mySwingButton = new
JButton ("Texto do botão");

O componente Swing GUI correspondente à sua contraparte AWT é nomeado J seguido pelo nome do componente AWT.

Tanto AWT quanto Swing contam com o conceito de recipientes. Um contêiner é uma abstração que contém componentes da GUI, entre
outros contêineres. Você cria uma interface adicionando componentes GUI aos contêineres. Aqui está um snippet de código que ilustra o
conceito:
Página 199

// Para AWT Frame Container e Button Component import java.awt. *;

Quadro, Armação myFrame = novo quadro ("Meu quadro");


Botão myButton = novo botão ("Botão A");
myFrame.add (myButton);

A ideia é que o objeto myFrame sirva como um local para colocar os componentes da GUI. Para contêineres e componentes Swing, a
mecânica é um pouco diferente, mas a ideia é a mesma.

// Para Swing JFrame Container e JButton Component import javax.swing. *;

// JFrame é uma subclasse de Frame, a propósito JFrame


myJFrame = novo JFrame ("Meu Swing Frame");
JButton myJButton = novo JButton ("Um botão Swing");
// Os painéis são uma construção Swing que permite camadas, transparência MyJFrame.getContentPane (). Add
(myJButton);

Você entendeu a ideia? Crie um contêiner e adicione elementos de GUI. Cada componente GUI (AWT ou Swing) é um objeto de uma
classe que contém propriedades e métodos peculiares ao componente GUI. Isso é tudo que você precisa saber sobre a criação de
elementos GUI em Java para entrar no processamento de eventos.

Eventos Java

Como você pode imaginar, os objetos representam eventos em Java. Da mesma forma, um objeto de evento tem (drumroll, por favor) uma lista
de propriedades e um conjunto de comportamentos. O Java runtime define classes de eventos que descrevem eventos que são gerados por
vários dispositivos de entrada e vários componentes gráficos.

A classe pai de todos os eventos em Java é java.util.EventObject. Para eventos gerados por componentes AWT GUI, a classe pai
é java.awt.AWTEvent. Alguns componentes Swing geram eventos que são representados por subclasses de EventObject.

Quando você clica em uma janela criada por um aplicativo Java, o Java runtime gera um evento de janela ou um objeto da classe java.awt.event.WindowEvent.
Quando você pressiona uma ou mais teclas, o tempo de execução gera um evento de teclado ou um objeto de classe java.awt.event.KeyEvent.
Às vezes, esses eventos são chamados de eventos de baixo nível.

O Java runtime define certas classes de eventos para capturar semântico, ou eventos de alto nível. Por exemplo, quando você
seleciona uma janela, um item de lista ou algum outro componente de interface selecionável, o Java Runtime gera um evento de
item além do evento correspondente ao tipo de componente específico selecionado. Se necessário, seu programa Java pode
responder ao evento significativo de alto nível de selecionar um item em vez de lidar com o dispositivo que realizou a seleção real (o
mouse).
Página 200

Tabela 11.1 Alguns componentes da GUI com eventos gerados

AULAS COMO ISTO


REPRESENTANTE EVENTO É
GUI EVENTOS GERADOS GERADO
COMPONENTE POR ESTE COMPONENTE

Botão ActionEvent Clicar em um botão


Caixa de Seleção ItemEvent Selecionando / desmarcando um item

Caixa combo ItemEvent Selecionando / desmarcando um item

Lista ActionEvent ItemEvent Clicar duas vezes em um item da lista


Selecionando / desmarcando um item

Item do menu ActionEvent Selecionando um item de menu

Botao de radio ItemEvent Selecionando / desmarcando um item

Barra de rolagem AdjustmentEvent Movendo uma barra de rolagem

Caixa de entrada de texto TextEvent ActionEvent Alterando texto Concluindo edição de texto

Janela WindowEvent Abrir / fechar / minimizar / restaurar


uma janela

A Tabela 11.1 mostra alguns dos eventos Java gerados pelos componentes da interface gráfica Java. Em outras palavras, a tabela
mostra as classes de objetos correspondentes aos eventos gerados por esses componentes.

A propósito, objetos de contêiner geram eventos de classe ContainerEvent.


Y
FL

O modelo de processamento de eventos Java


A M

A linguagem usada para descrever como os eventos são passados para o seu código que lida com os eventos é chamada de
modelo de evento. Embora o modelo de um loop de evento processando eventos com base na origem do evento seja conceitualmente simples,
TE

o modelo é implementado principalmente em linguagens de programação procedurais. Nenhuma sugestão de alavancar a tecnologia de objetos
aparece aqui. Afinal, você esperaria que Java processasse eventos usando uma metáfora de objeto de algum tipo, o que Java faz, é claro.

O modelo de evento usado por Java é chamado de baseado em delegação modelo. Este modelo permite que um programador Java
conecte componentes gráficos que geram eventos aos objetos que manipulam esses eventos. Esta é a receita para implementar o modelo
baseado em delegação em Java:

Team-Fly ®
Página 201

1 Crie um objeto para representar o componente gráfico que gera os eventos, que chamaremos de fonte do evento. Adicione
o componente a um contêiner, se necessário.

2 Crie uma classe para lidar com os eventos gerados a partir da fonte do evento. A classe de manipulação de eventos implementa
uma interface chamada de ouvinte no java.awt.event pacote, que é peculiar ao componente gráfico.

3 - Implementações de código da interface do ouvinte que implementam a ação apropriada em resposta ao evento
gerado. Como o ouvinte é uma interface, você devo fornecer implementações para todos os comportamentos
declarados na interface.

4 - Delegue o código que trata os eventos ao componente que os gera da seguinte maneira:

uma. Instancie um objeto da classe criada na Etapa 2 para o objeto de componente criado na Etapa 1.

b. Chame o componente adicionar ouvinte método. Em Javaspeak, a invocação do ouvinte add


método é chamado registrar o ouvinte. O método add listener, que é peculiar a cada componente gráfico, usa o
objeto criado na Etapa 2 como um argumento.

O modelo de evento não determina a natureza exata do objeto de origem. Você é livre para registrar o ouvinte em qualquer objeto que
faça sentido. Normalmente, você registrará o ouvinte de um componente no contêiner que contém o componente. Se o componente
for um container, você registrará o ouvinte com o objeto container.

Para capturar eventos de uma janela, você codifica uma classe que implementa a interface do ouvinte para janelas, que
é o WindowListener interface. Registre o ouvinte invocando a janela
addWindowListener método, passando uma instância da classe que implementa a interface WindowListener.

Para capturar eventos de um botão, você codifica uma classe que implementa a interface do ouvinte para botões, que é o ActionListener interface.
Depois de adicionar o objeto de botão a um recipiente adequado com o
adicionar método, você registra o ouvinte invocando o método do botão addActionListener , passando uma instância da classe
que implementa a interface ActionListener.

Vamos ver um código que dará vida a essas palavras.

O ANTIGO MODELO DE EVENTO JAVA

Java 1.0 usa um modelo de evento baseado em um Evento superclasse e um Componente


superclasse. Você, o programador Java 1.0, acessaria variáveis de instância da classe Event que
descrevem o evento. Seu aplicativo codificaria métodos que substituem os métodos da classe Component
que manipulam eventos específicos como mouseMove () e keyDown (). Este capítulo não discutirá o modelo
de evento Java 1.0, mas você deve estar ciente de sua existência no caso de executar qualquer código Java
antigo.
Página 202

Exemplo: Capturando eventos de janela

O código a seguir cria uma janela e relata vários eventos de janela. Este exemplo usa componentes Swing. O código
que usa componentes AWT é incluído como comentários.

import javax.swing. *;
import java.awt.event. *;
import java.awt. *;

public class WindowDemo {

public static void main (String [] args) {


// Cria um objeto container - Neste caso, uma janela // Esta é a fonte do evento

JFrame myJFrame = novo JFrame ("Minha janela"); // 1


// Essas duas próximas instruções são necessárias para ver a janela myJFrame.setSize (600, 600);

myJFrame.setVisible (true);
// Crie uma instância do código que realmente responde aos // eventos - o manipulador de eventos

myWindowEventHandlerCode mWEHC =
new myWindowEventHandlerCode (); // 4a
// Registra o manipulador de eventos com o objeto que recebe os // eventos

myJFrame.addWindowListener (mWEHC); // 4b
//
/ ** Esta é a versão que usa componentes AWT

Quadro, Armação myFrame = novo quadro ("Meu quadro"); // 1

myFrame.setSize (600, 600); myFrame.setVisible


(true);

myWindowEventHandlerCode mWEHC = // 4a
new myWindowEventHandlerCode ();
myFrame.addWindowListener (mWEHC); // 4b

**/
//
class myWindowEventHandlerCode implementa WindowListener { // 2

public void windowClosing (WindowEvent we) {


System.out.println ("Tchau!");
System.exit (0);
}
public void windowActivated (WindowEvent we) { // 3
Página 203

System.out.println ("Janela ativada");


}
public void windowDeactivated (WindowEvent we) { // 3
System.out.println ("Janela desativada");
}
public void windowIconified (WindowEvent we) { // 3
System.out.println ("Janela minimizada / iconificada");
}
public void windowDeiconified (WindowEvent we) { // 3
System.out.println ("Janela maximizada / desconsiderada");
}
public void windowOpened (WindowEvent we) { } // 3

Os números em negrito à direita das afirmações correspondem aos números mostrados na receita anterior. As linhas
numeradas 1 criam um objeto JFrame (Swing) e um objeto Frame (AWT). Esses objetos gerarão os eventos aos quais o
código responderá.

A linha 2 é a classe que contém as implementações da resposta aos eventos que podem ser gerados pelos objetos criados nas linhas
1. Observe que essa classe implementa a interface de ouvinte apropriada para os objetos geradores de eventos. Os objetos da classe
criada na linha 2 são os manipuladores de eventos. Se esse programa criou vários JFrames e esses JFrames separados exigiram
respostas diferentes para WindowEvents, você codificaria diferentes instruções de classe e forneceria diferentes implementações da
interface WindowListener.

As linhas 3 são os comportamentos que requerem implementação. A única coisa acontecendo aqui é que em resposta ao evento, Java grava
algum texto em System.out. Quando a janela está fechando, o Java emite uma chamada para o método System.exit (), que interrompe o
programa.

As linhas numeradas 4a criam um objeto que responderá aos eventos gerados. As linhas 4b invocam o método add listener para o
componente JFrame (ou Frame) usando o objeto manipulador de eventos como argumento.

A Figura 11.2 mostra um exemplo de execução do programa.

Claro, quando você executa este programa, seus resultados variam dependendo do que e onde você clica.

Uma ação do usuário em um componente pode acionar vários eventos. Quando você minimiza, ou iconifica, uma janela, o objeto
JFrame gera dois eventos de janela: um evento WindowIconified e um evento WindowDeselected. Isso faz sentido porque,
quando a janela é minimizada, ela também é desmarcada.

Você pode estar se perguntando como o Java runtime sabe quando disparar os métodos que implementam a interface do listener. Olhando para o
programa WindowDemo, você pode ver que as invocações de método, digamos, windowActivated () ou windowIconified () não existem em nenhum
lugar do código. Este é o ponto crucial do modelo de delegação de eventos: o tempo de execução delega eventos ao código conforme ocorrem. O
tempo de execução sabe quais eventos vão para qual código por um ligue de volta mecanismo. Em outras palavras, o evento faz com que o tempo de
execução chame de volta o código que está conectado ao ouvinte.
Página 204

Figura 11.2 Executando o programa WindowDemo.

Exemplo: Capturando eventos de botão.

Vejamos outro exemplo. O código a seguir mostra um botão adicionado ao JFrame (ou Frame) e um ouvinte de evento que é
conectado para relatar quando o botão é clicado. Apenas o código que mostra a criação do botão, implementação do ouvinte e
registro do ouvinte é apresentado. Os números entre parênteses referem-se à receita mostrada anteriormente.

Crie o botão e adicione a um recipiente (1).

//Balanço
JButton myJButton = novo JButton ("Meu botão");
myJFrame.getContentPane () .add (myJButton); // AWT

Botão myButton = novo botão ("Meu botão");


Página 205

myFrame.add (myButton);

Crie uma classe para tratar os eventos gerados pelo componente (2). Se você olhar a Tabela 11.1, notará que os botões geram
ActionEvents. Ergo, o código de manipulação de eventos, implementa a interface ActionListener, conforme mostrado aqui:

// Swing e AWT
class myButtonEventHandlerCode implementa ActionListener {

Implementações de código de todos os comportamentos exigidos pela interface do ouvinte (3). A interface ActionListener contém apenas
um comportamento: ação executada.

// Swing e AWT
public void actionPerformed (ActionEvent ae) {
System.out.println ("Clique com o mouse neste botão"); }

Delegue o manipulador de eventos ao componente que gera o evento instanciando um objeto manipulador de eventos (4a)

// Swing e AWT
myButtonEventHandlerCode mBEHC = new myButtonEventHandlerCode ();

e invocar o método add listener do componente (4b).

//Balanço
myJButton.addActionListener (mBEHC); // AWT

myButton.addActionListener (mBEHC);

Você pode criar skin para eventos Java de mais de uma maneira. As próximas duas seções mostram técnicas de codificação alternativas
para lidar com eventos.

Variações sobre um tema 1: usando


classes de adaptadores

Um ponto que vale a pena mencionar é que você, o programador Java, deve fornecer implementações para tudo
os comportamentos indicados na interface. Se você esqueceu esse fato importante, releia o Capítulo 10 "Interfaces". Se você não
precisa fornecer uma implementação para um comportamento de interface, pode "simular". Por exemplo, veja o windowOpened () método
codificado na implementação da interface WindowListener - não possui código. Se você estivesse interessado em atuar em apenas
um dos eventos de janela gerados, você teria que fornecer implementações fictícias dos seis comportamentos restantes.
Página 206

Java tem vários classes de adaptadores que ajudam a mitigar essa situação ímpar. Uma classe de adaptador é uma classe de métodos fictícios que
implementam uma interface. Você estende a classe do adaptador e substitui os métodos fictícios pelos seus. Usando uma classe de adaptador, a
linha 2 se torna:

classe myWindowEventHandlerCode extends WindowAdapter {

Observe que a definição da classe não implementa uma interface. Como a classe Window Adapter implementa a interface
WindowListener, a classe criada pelo usuário estende uma classe. Obviamente, se a classe manipuladora de eventos precisar estender
uma classe diferente, você terá que implementar a interface porque, como sabe, uma classe Java pode ter apenas uma superclasse.

Supondo que estejamos interessados em fechar apenas a janela, a classe do manipulador de eventos que estende a classe do adaptador
seria assim:

class myWindowEventHandlerCode extends WindowAdapter {

public void windowClosing (WindowEvent we) {


System.out.println ("Tchau!");
System.exit (0);
}
}

Curto e grosso.

Variações sobre um tema 2: usando


classes de nível superior

Os exemplos de código anteriores mostram o uso de uma classe separada que implementa a interface do ouvinte ou estende a classe do
adaptador. Você pode usar praticamente qualquer classe para fazer isso. Veja o seguinte exemplo.

import javax.swing. *;
import java.awt.event. *;
import java.awt. *;

public class WindowDemo2 implementa WindowListener {

public static void main (String [] a) {

JFrame myJFrame = novo JFrame ("Minha janela");

myJFrame.setSize (400, 200); myJFrame.setVisible


(true);

myJFrame.addWindowListener (new WindowDemo2 ()); // 1

}
Página 207

public void windowClosing (WindowEvent we) {


System.out.println ("Tchau!");
System.exit (0);
}
public void windowActivated (WindowEvent we) {
System.out.println ("Janela ativada");
}
public void windowDeactivated (WindowEvent we) {
System.out.println ("Janela desativada");
}
public void windowIconified (WindowEvent we) {
System.out.println ("Janela minimizada / iconificada");
}
public void windowDeiconified (WindowEvent we) {
System.out.println ("Janela maximizada / desconsiderada");
}
public void windowOpened (WindowEvent we) {}

public void windowClosed (WindowEvent we) {}


}

Este código mostra que você pode dispensar a criação de uma classe separada para o código de tratamento de eventos e usar uma
classe existente. Dois pontos são dignos de nota aqui:

1 Como você não tem uma classe separada que lida com os eventos, você criaria uma instância da classe de nível superior e usaria a
instância como o argumento para o método add listener ou usaria uma instância existente da classe de nível superior como o
argumento, conforme mostrado na linha 1.

2 Sua classe de nível superior pode estender uma classe de adaptador em vez de implementar a interface do listener.

Se você quisesse ouvir eventos de janela e eventos de botão em sua classe de nível superior, sua classe implementaria ambos os ouvintes.

Variações sobre um tema 3: usando


classes internas

No Capítulo 7, "Representação de classes e objetos", você leu um pouco sobre classes internas. Acontece que o principal uso das classes internas é
codificar manipuladores de eventos. Por que não codificar seu código de manipulação de eventos próximo ao código que cria o componente que gera
o evento? Esta é a aparência do nosso método add listener quando uma classe interna representa o manipulador de eventos.

myJButton.addActionListener (new ActionListener () {


public void actionPerformed (ActionEvent ae) {
System.out.println ("Clique com o mouse neste botão"); }

}
);
Página 208

Você também pode usar uma classe de adaptador com a classe interna da seguinte maneira:

myJFrame.addWindowListener (new WindowAdapter () {


public void windowIconified (WindowEvent we) {
System.out.println ("Diminuindo agora...."); }

}
);

Usar classes internas funciona muito bem quando o código de tratamento de eventos é curto. No entanto, quando o manipulador de eventos crescer,
você pode achar que percorrer o código é um pouco cansativo.

Em suma

Em resumo, Java trata de eventos conectando os componentes que geram os eventos à implementação de ouvintes de eventos por um
mecanismo de retorno de chamada. As implementações do ouvinte de evento são os métodos que reagem ao recebimento do evento.

Depois de criar seus componentes e colocá-los em contêineres, você codifica as implementações para os ouvintes dos componentes e,
frequentemente, os contêineres. Os ouvintes são ajustados para refletir os comportamentos dos componentes e contêineres. Depois que os
ouvintes forem implementados, registre-os nos objetos apropriados com o método add listener do ouvinte. Depois de registrado, seu trabalho
está feito. O Java runtime irá despachar os eventos para os métodos de tratamento de eventos apropriados para você automaticamente.

Você leu sobre os fundamentos do processamento de eventos Java. O resto está nos detalhes de eventos específicos e interfaces de ouvinte
que são necessários para implementar uma GUI com recursos completos.
Página 209

CAPÍTULO 12
Tratamento de exceções e conceitos básicos de thread.

No mundo real, os programadores devem tomar medidas para se proteger contra ocorrências imprevisíveis, infelizes e evitáveis. Ocorrências
imprevisíveis geralmente ocorrem quando os clientes inserem dados falsos ou não seguem os procedimentos operacionais. Ocorrências
infelizes incluem arquivos de entrada contendo dados estragados, corrompidos ou desatualizados. Exemplos de ocorrências evitáveis
incluem a presença de erros de tempo de execução, como divisão por zero e estouros aritméticos.

A má notícia é que essas ocorrências sempre farão parte do mundo do programador; a boa notícia é que o Java tem um mecanismo
que ajuda o programador a superar alguns dos efeitos nocivos que surgem dessas ocorrências. Este mecanismo, chamado manipulação
de exceção, é um dos assuntos deste capítulo.

Suporte de linguagem Java para vários tópicos é o outro assunto abordado. Um thread é um fluxo de controle de programa; vários threads
significam que um único programa Java pode criar e gerenciar vários fluxos de programa de controle. Esses múltiplos fluxos de programa podem
ser usados para trabalhar em diferentes partes de um grande problema ou para manter os recursos de computação ocupados. Enquanto um
thread espera por, digamos, entradas do usuário, outro thread está ocupado executando alguma tarefa em segundo plano. Como você verá, criar
e gerenciar tarefas em Java é bastante simples, uma vez que você entende o básico.

O que são exceções?

Em linguagem simples, uma exceção é algum evento que não é usual, normal ou antecipado. Na linguagem dos
programadores, uma exceção significa praticamente a mesma coisa.
Página 210

No entanto, o termo exceção tem um significado específico em Java. Uma exceção é uma instância de uma classe derivada da classe
Throwable.

Este significado nos diz que as exceções são, é claro, objetos. Isso não deve surpreendê-lo agora. Afinal, o coração e a alma de toda
essa visão orientada a objetos é expressar entidades como objetos. Em Java, as entidades usadas em seus aplicativos e entidades do
sistema, como arquivos e exceções, são expressas como objetos.

Como as exceções são objetos, cada objeto de exceção possui propriedades e comportamentos. Você pode fazer coisas de objeto com
exceções, como criar novas exceções, passar exceções como argumentos para métodos e invocar métodos que implementam
comportamento de exceção. Além disso, Java contém suporte de linguagem customizada para lidar com esses objetos de exceção. Esse
suporte de idioma é, obviamente, um tópico deste capítulo. Antes de trabalhar no suporte à exceção Java, dê uma olhada na hierarquia de
exceção Java.

A hierarquia de exceção Java

A hierarquia de exceção Java é mostrada na Figura 12.1.

No topo da cadeia está a classe Throwable. A classe Throwable contém a maioria dos métodos que você achará úteis, como imprimir uma
lista dos métodos chamados (chamados de pilha
Y
FL
A M
TE

Figura 12.1 A hierarquia de classes Java Exception.

Team-Fly ®
Página 211

vestígio), buscando ou definindo uma string que descreve a exceção. A classe Throwable tem duas subclasses diretas: Erro e
Exceção.

Classe Erro descreve exceções que, quase sempre, significam morte certa e desgraça para seu programa. As exceções derivadas da
classe Error relatam problemas com a Java Virtual Machine, como falta de memória ou esgotamento de algum outro recurso do sistema.
Há pouco ou nada que você possa fazer a respeito dessas exceções. A especificação da linguagem Java se refere a exceções
derivadas da classe Error como
exceções não verificadas. Felizmente, as exceções derivadas da classe Error são raras.

Classe Exceção é onde você gastará seu tempo recuperando e (com sorte) se recuperando de condições excepcionais. Todos os nomes
de cada subclasse da classe Exception terminam com a palavra exceção.
Como a maior parte do seu código Java capturará exceções da classe Exception ou uma de suas subclasses, a classe Exception merece
tratamento especial.

A classe de exceção Java

Não se confunda - este capítulo, em parte, fala sobre o tratamento de exceções Java, e a classe Exception é uma classe que
descreve exceções. Você saberá o que a literatura Java quer dizer com condição excepcional e um objeto da classe Exception.

A exceção de classe tem cerca de 35 subclasses, variando de geral (por exemplo, SQLException e IOException) a específica
(por exemplo, ClassNotFoundException e ServerNotActiveException). Você adivinhou - as classes de exceção gerais têm
subclasses diretas, enquanto as classes de exceção específicas não.

Você pode agrupar as 35 ou mais subclasses de Exceção em 3 categorias:

1 Exceções de E / S

2 Exceções de que seu código deve parar de ocorrer

3 - Outras exceções que não se enquadram nas categorias anteriores

Exceções de I / O, a primeira categoria, são objetos de classe IOException ou uma de suas subclasses. Como você deduziu, as exceções
nesta classe descrevem exceções decorrentes de E / S, como
FileNotFoundException ou MalformedURLException. Como regra, você deve tentar se recuperar das exceções dessas classes. Se o
seu programa encontrar uma FileNotFoundException, obtenha outro arquivo (possivelmente existente). Se o seu programa tentar
usar um URL em um formato incorreto, você deve corrigir isso. No mínimo, seu programa deve exibir diagnósticos informativos; tente
reverter o programa para um estado estável e saia normalmente. Exceções de IOException e as de suas subclasses são chamadas exceções
verificadas.

O povo Java usa uma abreviatura para expressar exceções na segunda categoria. Tempo de execução exceções ou exceções da classe
RuntimeException, ou uma de suas subclasses diretas, são exceções que surgem principalmente de código Java de baixa qualidade. Agora, o termo
exceção de tempo de execução é um pouco impróprio porque todas as exceções ocorrem em tempo de execução. Exceções da classe
RuntimeException ou uma de suas subclasses são chamadas de exceções não verificadas. Uma exceção de tempo de execução deve nunca ocorre porque
você, o programador Java, pode escrever um código para impedir que essa classe de exceções apareça.

Vamos ser honestos, ok? Se, durante o plantão, você acordasse do cochilo em alguma hora da manhã para remendar um programa que falhou por
causa de uma divisão por zero, você seria mais do que
Página 212

ligeiramente irado, estou certo? Você sabe que uma única linha de código antes da divisão interromperia a ocorrência da divisão por zero.
Você pode pensar que existe um lugar especial abaixo (não na Austrália) onde os programadores que colocam esse tipo de código em
produção devem ir. Você estremece ao pensar que outras surpresas estão reservadas - como acessar uma matriz com um índice fora dos
limites ou acessar um elemento de dados não inicializado.

O zerodivide e outras condições excepcionais mencionadas anteriormente podem ter, e deveria ter,
nunca ocorreu. Seu código Java deve verificar os tipos e objetos primitivos de seu programa para se proteger contra essas condições. Em
outras palavras, você não deve confiar no mecanismo de gerenciamento e relatório de exceções do Java para lidar com as exceções da
classe RuntimeException e suas subclasses diretas.

Verifique se há ArithmeticException verificando o divisor antes de realizar a divisão. (Você se lembra que a aritmética de ponto
flutuante nunca causa exceções, certo?) Verifique se há
ArrayIndexOutOfBoundsException comparando o índice da matriz com o comprimento da matriz. Verifique se há uma ClassCastException
usando o instancia de operador da seguinte forma:

MyClass anotherObject;
If (instância myObject de MyClass)
outroObjeto = (MinhaClasse) meuObjeto;

Portanto, você vê que um pouco de cautela e bom senso podem impedir que essas exceções sejam lançadas.

As exceções que se enquadram na terceira categoria são uma miscelânea de exceções; você usará o mecanismo de tratamento de
exceções do Java para processar. Essas exceções são exceções verificadas.

Em suma, você escreverá código para lidar com exceções verificadas e permitir exceções não verificadas para passar para o tempo de execução
Java. Em breve, você aprenderá o código a escrever que tratará as exceções verificadas.

Mecanismos de tratamento de exceções da linguagem de programação de mainframe

O programador COBOL lida com exceções da maneira antiga. Supondo que o programa tenha acesso à (s) exceção
(ões), o programa tenta a operação e, se malsucedido, gera um código que descreve a exceção.

Aqui, o programa COBOL possui código para lidar com a exceção mencionada antes de prosseguir. Por exemplo, um programa
COBOL que emite SQL em tabelas do DB2 deve estar à procura de uma variedade de condições excepcionais. Como a emissão de
SQL gera um número fixo de códigos de retorno correspondentes a uma variedade de condições, o programa COBOL pode detectar
e lidar com essas condições. A saber:

EXEC SQL.
BUSCAR MEU CURSOR SELETOR
INTO: HOST-VAR1,: HOST-VAR2,: HOST-VAR3 END-EXEC.

SE SQLCODE = 100 ENTÃO


Página 213

DIAGNOSTIC = "Não há mais dados para processar".


OUTRO
SE SQLCODE <0 ENTÃO
DIAGNOSTIC = "Erro não especificado". OUTRO

REALIZE A SELEÇÃO DE PROCESSO ATRAVÉS DE SELEÇÃO DE PROCESSO. FIM SE.

Essa estratégia funciona um pouco quando o programa sabe quais exceções podem ocorrer; entretanto, presumindo que o programa
desconhece a (s) exceção (ões), o programa pode suspirar e chiar quando a exceção ocorre.

Infelizmente, a respiração ofegante e a respiração ofegante dos programas COBOL são comuns. Sim, você pode culpar o programador por esse
lamentável estado de coisas. O programador deve arcar com o peso da responsabilidade; no entanto, o programador precisa de um pouco de
ajuda. COBOL oferece praticamente nenhum suporte para interceptar e lidar com erros de maneira sistêmica. Os programas COBOL estão
repletos de verificação de código de retorno; é o único mecanismo disponível.

O programador PL / I tem construções de linguagem muito melhores para lidar com o inesperado. PL / I usa a construção de bloco ON
para interceptar e lidar com exceções. Por exemplo,

On Key (myFile) Begin;

If NumberOfRetries <4 Then Do;

Colocar lista de pulos ('Valor-chave' || myKey


|| 'Não encontrado no arquivo');
Coloque a lista de pulos ('Você tem' || 4 - NumberOfRetries ||
'Restam novas tentativas. Faça outra entrada ');
MyKey = ReEnterKeyValue (); Retorna ;

Fim ;
Outro
Faz ;
Coloque a lista de pulos ('Você esgotou suas chances.' ||
'Dá o fora' ) ;
Saída ;
Fim ;

Fim ;
/ * Alguns códigos PL / I seguem * /
Read File (myFile) Into (myStruct) KeyFrom (myKey);

Este trecho de código lê um arquivo em uma estrutura de registro devidamente declarada com base em um valor de chave mantido na variável myKey.
Se myKey não estiver presente no índice do arquivo, a condição Key é levantada e o bloco de código anterior é executado. PL / I também permite que o
programador intercepte erros não especificados com a instrução ON ERROR. Além disso, PL / I permite a declaração de condições definidas pelo
usuário, suportando um tipo de dados CONDITION.
Página 214

Na verdade, como você verá, o trapping e tratamento de condição do PL / I tem uma semelhança passageira com o tratamento de exceção do Java.
Agora, não há conversa sobre classes de exceção e coisas de objeto no mundo do programador PL / I. Ainda assim, a capacidade do PL / I de
detectar e lidar com erros específicos em diferentes partes da unidade de compilação é um passo na direção certa. Dito isso, o programador PL / I
deve codificar várias verificações de código de retorno como seus irmãos COBOL.

Todos os programadores devem estar vigilantes no tratamento de erros. O programador COBOL não possui recursos de linguagem específicos
para tratamento de erros, enquanto o programador PL / I possui vários. No entanto, o mecanismo de tratamento de exceções do Java é superior,
como você verá.

O mecanismo de tratamento de exceções Java

O tratamento de exceções Java é bastante direto. Em suma, a linguagem de programação Java permite que as exceções
sejam jogado e apanhado. O "lançamento" de uma exceção é a maneira Java de levar a exceção a um ponto em que ela
possa ser tratada, enquanto "captura" é o tratamento da exceção.

Lançamento de exceções

As exceções podem ser lançadas de uma das duas maneiras: seu código pode lançar a exceção explicitamente ou seu código lança a
exceção em resposta a um evento no tempo de execução Java. Em outras palavras, você ou a Java Virtual Machine lançam a exceção.

Você lança uma exceção ao codificar um lançar declaração a seguir,

lance myException;

Onde myException é um objeto de uma classe que estende a classe Throwable.

Por que você deseja lançar uma exceção? Um dos motivos é que você pode controlar como e quando essa exceção é
tratada. Outra razão é que quando você crie suas próprias exceções, lançar a exceção explicitamente pode ser o único meio
à sua disposição para gerenciar a exceção.

Criando suas próprias classes de exceção

Espere um minuto. Você está surpreso por poder criar suas próprias exceções? Pense nisso. As exceções são objetos instanciados
de uma subclasse de Throwable. Você pode usar os recursos do Java para criar suas próprias classes de exceção e, é claro,
objetos dessas classes. Um exemplo é listado a seguir:

// Converte coordenadas de cores RGB em cores H (ue) S (aturação) V (alor) // Coordenadas.

// Alguns valores de cor RGB produzem valores falsos de HSV classe


RGBToHSVConversionException
extends Exception {// pode estender Throwable ou outro
// classe de exceção
Página 215

// Construtores de código ..
RGBToHSVConversionException ()
{// Construtor padrão sem Arg}
RGBToHSVConversionException (String desc) {// Aproveitar a herança para
fazer o trabalho sujo super (desc);

Como você vê, essa classe de exceção é igual a muitas outras da classe Java. As classes de exceção normalmente têm dois
construtores: o padrão, sem construtor de argumento e aquele que recebe uma string como argumento. O argumento string é uma linha
que descreve a natureza da exceção, que você pode recuperar chamando o getMessage () método.

Agora, de volta ao lançamento de exceções.

Lançando exceções - Continuação

O seguinte mostra como você pode lançar exceções desta (e de qualquer outra) classe de exceção:

class HSVVideoFrame extends VideoFrame {


// Algum código aqui, algum código ali
anHSVCoordinate = convertRGBToHSV (anRGBCoordinate); if (anHSVCoordinate.hue> 360)

jogar novo
RGBToHSVConversionException ("Valor de matiz fora do intervalo");
// Mais um pouco de código
}

O código anterior está incompleto porque você precisará de construções de codificação adicionais que digam ao Java a natureza da exceção
que pode ser lançada. Você verá essas construções em breve.

E quanto aos casos em que o Java runtime lança a exceção? A JVM lançará exceções com mais freqüência do que o seu
código. A JVM lança uma exceção quando a exceção ocorre. A saber:

HSVCoordinate firstHSVCoordinate;
// Algum código, talvez
if (firstHSVCoordinate.value <0.1) // OOPS !!!
coordinateDescription = "Escuro";

Supondo que a primeira linha seja a única declaração do objeto firstHSVCoordinate, o Java runtime vai deixar seu programa
pendurado no final de um laço, arrotando um Null Pointer Exception. Você pode ver que o objeto firstHSVCoordinate não tem
valor, certo? Quando Java atinge o E se , a situação infeliz de uma referência de objeto nulo carrega sua cara feia, fazendo com
que o Java lance o NullPointerException. À parte, NullPointerException é uma subclasse da classe de exceção Runnable.
Portanto, o código deve ter verificado a validade da referência do objeto antes de fazer a referência.
Página 216

Você pode lançar exceções de sua criação, ou de uma classe, em uma das bibliotecas Java, codificando uma instrução throw. Todas as
exceções que você não lançar explicitamente serão lançadas para você pelo Java Runtime.

Parece que se seu código vai lançar exceções, o mínimo que ele pode fazer é permitir que os usuários de suas classes saibam quais
exceções podem ser lançadas. Continue lendo.

Declarando exceções potencialmente lançadas.

Se o seu código lançar exceções explicitamente usando um ou mais lança instruções ou contém código que pode fazer com que o
Java runtime lance exceções, você deve declarar ou pegar essas exceções. Nesta seção, você aprenderá como declarar as
exceções.

Por que declarar as exceções? Ao declarar em seus métodos que seu código pode, ou lança, uma exceção específica, você
anuncia aos métodos que invocam seu código que os métodos devem lidar com essas exceções. Este anúncio não pode ser
ignorado. O compilador Java impõe o requisito. Por exemplo, verifique o seguinte snippet:

class IOExample {
private static char readIt () {

int anInputChar = System.in.read ();


return (char) anInputChar;

}
}

No entanto, javac tem uma ou duas coisas a dizer sobre esse código. O seguinte mostra a reação de javac:

C: \ WINDOWS \ DESKTOP> javac IOExample.java


IOExample.java:4: Exceção java.io.IOException deve ser capturada ou deve ser declarada na cláusula throws
deste método.
int anInputChar = System.in.read ();
^
1 erro

Java é inteligente o suficiente para saber que o método System.in.read () poderia lançar uma exceção da classe IOException. Java também sabe que
este método não possui código para lidar com essa exceção. Conseqüentemente, o Java não permitirá que nenhum código use esse método, a menos
que o código possa manipular exceções da classe IOException. A seguir está outro exemplo da robustez do Java no trabalho. Java não permitirá que
exceções declaradas em métodos fiquem sem tratamento. Java está realmente tentando garantir que as exceções sejam levadas a sério.

Você pode se perguntar como Java sabia que System.in.read () poderia lançar uma IOException. Uma rápida olhada no cabeçalho do
método fornece uma pista:

public int read () lança IOException


Página 217

Parece que este método read () tem uma construção especificamente para anunciar uma exceção potencialmente lançada. Observe a escolha de
palavras no diagnóstico do compilador acima declarado no cláusula de lançamentos
deste método. Você não precisa ser um sábio para escolher a cláusula throws no cabeçalho do método read (), certo?

Ok, ouvimos o que o compilador Java está dizendo. Em resposta, altere o código do método readIt () para incluir uma cláusula throws da
seguinte forma:

private static char readIt () lança java.io.IOException

Agora o código compila sem reclamar. Claro que você notou isso, porque o método não tinha uma instrução de importação; a
classe IOException precisa de uma qualificação completa.

Por enquanto, tudo bem. Dissemos ao compilador Java que o método readIt () lança a exceção conforme ditado por readIt () chamando
System.in.read (). Agora vamos usar readIt () para buscar alguns caracteres do teclado:

public static String readaString () {

Corda aString = "";


Caracteres anInputChar = Leia-o() ;

while (anInputChar! = '\ n') {


aString = aString + anInputChar; anInputChar = Leia-o() ;

return aString.substring (0, aString.length () - 1);


)

Você consegue adivinhar com o que o compilador Java responderá?

C: \ WINDOWS \ DESKTOP> javac IOExample.java


IOExample.java:14: Exceção java.io.IOException deve ser capturada ou deve ser declarada na cláusula throws deste método.

Caracteres anInputChar = readIt ();


^
1 erro

Você adivinhou. Java o impedirá de não lidar com exceções declaradas.

Algumas dicas sobre como declarar exceções em uma cláusula throws são necessárias. Se um método pode lançar mais de uma exceção,
então declare as classes de exceção potencialmente lançadas no cabeçalho do método, separadas por vírgulas. Por exemplo,

public void myThrowingMethod () lança IOException, InterruptedException

Se um método declara uma classe de exceção em sua cláusula throws, esse método também pode lançar uma exceção derivada de
uma subclasse da classe de exceção declarada. O método System.in.read () pode lançar uma InterruptedIOException, uma
RemoteException ou uma IOException.
Página 218

Sua regra para o dia é a seguinte: Seus métodos devem declarar na cláusula throws todos os exceções verificadas esses métodos jogam. Em
outras palavras, você não precisa declarar exceções derivadas das classes Error ou RuntimeException, que, como você deve se lembrar,
também são conhecidas como exceções não verificadas.

Você agora leu sobre a declaração de exceções e o lançamento de exceções. Continue e leia como capturar exceções.

Captura de exceções com try / catch / finally

Capturar uma exceção é escrever um bloco de código que executa alguma ação em resposta ao lançamento da exceção. Lembre-se de que
você escreverá código para lidar com exceções verificadas - exceções que não pertencem às classes Error ou RuntimeException, ou
qualquer uma de suas subclasses. Você precisa de outra construção de linguagem Java para lidar com exceções: o blocos try / catch / finally.

UMA tentar bloco informa ao compilador Java e ao tempo de execução que uma exceção pode ser lançada pelo código dentro do bloco. UMA pegar O
bloco informa ao compilador Java e ao tempo de execução que, quando uma exceção de uma determinada classe é lançada, é aqui que essa exceção
será tratada. UMA finalmente block é um bloco executado em todas as circunstâncias, mesmo que nenhuma exceção seja lançada.

Se um método contém um bloco try, o método pode conter zero a muitos blocos catch, zero ou um bloco finally. Você não pode codificar
um bloco try sem finalmente uma captura ou um bloco finalmente. Na maioria das vezes, você codificará uma tentativa seguida por um ou
mais blocos catch.

A seguir está um exemplo simples:

private static char readIt () {

int anInputChar = 0;

tentar {
anInputChar = System.in.read ();
}
catch (IOException ioe) {
System.out.println ("Erro:" + ioe); }

return (char) anInputChar;


}

Observe que o código que pode causar o lançamento de IOException está incluído no bloco try. Embora o bloco try contenha apenas
uma única linha de código, as chaves do bloco são requeridos. Após a instrução try, o bloco catch capturará exceções do tipo
especificado. Você pode codificar apenas uma classe de exceção em um bloco try. Como com o bloco try, as chaves são necessárias
independentemente do número de instruções no bloco catch.

Seu bloco catch, se presente, deve seguir imediatamente seu bloco try. Você não pode ter nenhuma declaração entre os dois blocos.
Você não pode codificar dois blocos try após o outro. Você poderia codificar um par de blocos try / catch seguido por outro par try / block.
No entanto, você deve codificar um bloco de tentativa por método. Como você já deve ter adivinhado, se
Página 219

seu método pode lançar, digamos, exceções de três classes diferentes (e não relacionadas), você geralmente precisará de três blocos
catch. A estrutura básica seria

tentar {
// Algum código Java que pode lançar (ou causar um lançamento de) // classes de exceção 1, 2 e 3

}
catch (ExceptionClass1 e1) {
System.out.println ("Erro:" + e1.getMessage ()); // Mais código, talvez

catch (ExceptionClass2 e2) {


System.out.println ("Erro:" + e2.getMessage ());
// Mais código, talvez

catch (ExceptionClass3 e3) {


System.out.println ("Erro:" + e3.getMessage ()); // Mais código, talvez

}
// Código fora dos blocos try / catch

Você pode codificar um bloco finalmente dentro de um método. Nesse caso, o bloco finally deve seguir o bloco catch, se codificado, ou o bloco try, se
nenhum bloco catch estiver codificado. Como mencionado anteriormente, o código dentro do bloco finally é sempre executado, mesmo se o bloco try ou
catch tiver uma instrução de retorno. Se você quiser garantir que o código seja executado dentro de um método, coloque esse código dentro de um
bloco finally.

No exemplo anterior, se uma exceção da classe ExceptionClass2 for lançada por uma instrução no bloco try (por uma instrução throws ou
pelo tempo de execução Java), o controle do programa será transferido para o bloco catch para ExceptionClass2. O código neste bloco é
executado de cima para baixo. Se este bloco não contiver nenhuma transferência de instruções de controle, como a instrução return ou
System.exit (), e um bloco final não está presente, o programa continua a execução na instrução após todas as instruções catch rotuladas

Código fora dos blocos try / catch. Se um bloco finally estiver presente, o programa executa o código neste bloco depois de
executar o código no bloco catch ExceptionClass2.

As variáveis declaradas nos blocos try e catch são locais para esses blocos. Não declare variáveis dentro de um bloco try e espere que essas
variáveis sejam conhecidas em seus blocos catch e finally. Obviamente, as variáveis declaradas fora dos blocos try / catch / finally, mas dentro
do método que contém esses blocos, são conhecidas por esses blocos. Lembre-se da regra de visibilidade do escopo da variável: dentro de um
método, seu programa pode olhar para fora de um bloco e conhecer as variáveis "externas", mas não pode olhar dentro de um bloco e conhecer
as variáveis "internas".

Você também pode passar exceções na cadeia de chamada (chamada de rastreamento de pilha) para o método que invocou o método que
lançou a exceção. Em outras palavras, um método não precisa lidar com exceções com try / catch / finally. O método pode "chutá-lo
Página 220

upstairs "ativando o método de chamada para lidar com a exceção. No entanto, você deve tentar lidar com as exceções perto de onde
elas ocorrem. Claro, estamos assumindo que você sabe que o método de chamada pode lidar adequadamente com a exceção.

Quando uma exceção é lançada, o Java procura primeiro no método que lançou a exceção para um manipulador. Se nenhum for encontrado, o Java
procura no método que invocou o método de lançamento de exceção para um manipulador de exceção e assim por diante. Se nenhum bloco try / catch
/ finally for encontrado em todos os métodos de rastreamento de pilha, o Java runtime gera um diagnóstico, geralmente seguido por um rastreamento
de pilha.

Resumo de exceções Java

As exceções Java são objetos de classes que estendem Throwable.

As exceções não verificadas são objetos derivados das classes Error e RuntimeException. Deixe o Java runtime lidar com
essas exceções.

Você pode lançar exceções explicitamente com a instrução throw; o Java runtime pode lançar exceções,
geralmente em resposta a uma circunstância infeliz e invisível.

Você deve declarar ou capturar todas as exceções lançadas por seu método. Você captura

exceções com uma série de blocos try / catch / finally.


Y
FL

Java percorre o rastreamento da pilha para procurar um bloco try / catch que trata de exceções.
M

Tópicos básicos do tópico


A
TE

Nesta seção, você lerá uma visão geral do suporte a thread Java. No entanto, você não lerá sobre o skinny completo em threads Java.
Além disso, você lerá o suficiente sobre o suporte a thread Java para escrever alguns programas multithread simples. Obviamente, após
esta seção, você entenderá o suficiente sobre threads Java para aprender os usos mais avançados de threads em Java.

A maioria dos programas de mainframe tem um único fluxo de controle, que é governado por estados de entrada e instruções de controle do programa
(if / then, do / while e assim por diante). Vamos chamar um programa que tem um único fluxo de controle de programa de programa de encadeamento
único. Todos os programas Java que você viu até este ponto se enquadrarão nesta categoria. Claro, vamos chamar um programa com múltiplos fluxos
de controle de programa multithread.

Por que codificar programas multithread?

Programas multithread podem maximizar o uso de recursos. Imagine, se quiser, um aplicativo que apresenta uma tela
para o cliente. Se esse aplicativo fosse de thread único e enquanto o cliente hesitava, deliberando sobre sua escolha
de insumos, o aplicativo estava morto. O único tópico para na casa do cliente

Team-Fly ®
Página 221

tela de entrada, e ficará lá até que o cliente decida continuar. Agora, se este aplicativo fosse multithread, o aplicativo poderia estar
realizando algum trabalho útil enquanto o cliente pondera sobre suas entradas. O aplicativo pode estar acessando dados de uma
entrada anterior, executando um backup do banco de dados ou imprimindo. O importante é que o aplicativo não precisa parar. Outros
threads podem continuar, enquanto o thread correspondente à coleta da entrada do cliente tende para esse trabalho.

Muitos programas são, e devem ser, escritos como single threaded. Muitos programas devem ser escritos como programas multithread. Por exemplo,
classificar conjuntos muito grandes de dados é um bom candidato para multithreading. Um encadeamento pode particionar o trabalho de
classificação em vários trabalhos de classificação menores e atribuir um encadeamento a cada classificação menor. Quando todos os pequenos
trabalhos de classificação são concluídos, um thread mescla os resultados individuais no conjunto classificado final. Os cálculos que envolvem um
grande número de números também se enquadram nessa categoria. Mais tarde, você verá um programa multithread que soma 10.000.000.000 de
números.

Depois de aprender algumas classes Java adicionais, você será capaz de escrever muitos programas multithread. Antes de fazer isso, um pouco de
trabalho de base é necessário.

O que são threads Java?

A resposta curta é que um thread Java é um instância da classe Thread ou uma subclasse de Thread. Para nenhuma surpresa, um
encadeamento Java é um objeto. Você tem dois meios à sua disposição quando deseja criar tópicos:

1 Você pode estender a classe Thread e substituir o desta classe corre() método. Por exemplo,

class ThreadedAdd extends Thread {public void run () {

}
}

2 Você pode implementar o Executável interface antes de fornecer uma instância de sua classe que implementa
Runnable para o construtor da classe Thread. Por exemplo,

class ThreadedAdd implementa Runnable {


public void run () { // O único método no Runnable
interface
}
ThreadedAdd myAddThread = new ThreadedAdd (); Tópico myThread
= novo tópico (myAddThread); //Mão única
Tópico myOtherThread = novo Tópico (novo ThreadedAdd ())
; // Outra maneira
}

Pense rápido: se sua classe de thread (ThreadedAdd, neste caso) já estende uma classe existente, de que forma você deveria usar
para criar threads? Claro, você se lembra de que deve implementar Runnable porque Java não permitirá que você tenha uma
classe que se estenda a mais de uma classe.
Página 222

Independentemente de como você cria o thread, você codificará o trabalho do thread no método run (). Vocês
devo codifique o método run () com a assinatura mostrada acima. Você não pode passar nenhum argumento para run () nem seu método run ()
retornar nenhum valor.

A criação de threads não os executa. Você precisa continuar lendo para ver como executar seus threads recém-criados.

Executando seus threads

Se você acha que precisa invocar o método run () para iniciar seus threads, você está meio certo. Seu método run () devo executar, mas você
não invoca isto. Você precisa de um pouco da magia Java para invocar seu método run (), iniciando assim seus threads. Você precisa
invocar um método chamado começar(). Depois de invocar start (), o Java runtime invoca seu método run () para você.

Vamos recuar um pouco. Codifique um método run () que faz o trabalho real de seu (s) thread (s). Não escreva nenhum código que invoque
run () diretamente. Você invoca run invocando start (). Você não codifica um método start (). Esta é a mágica do Java - você invoca um método
que não codificou para executar um método que codificou. Se isso parece um pouco estranho, bem, você tem minha simpatia.

Aqui estão duas etapas que você pode seguir para escrever programas multithread:

1 Crie uma classe que estenda a classe Thread e contenha uma implementação de run () ou

Crie uma classe que implemente a interface Runnable e contenha uma implementação de run ().

2 Crie uma classe que irá criar instâncias de seus threads e controlar a execução dos threads. Se estiver estendendo a
classe Thread, você usará o novo operador em sua subclasse de thread e codificará uma chamada para
subclassThreadObject.start (). Se estiver implementando a interface Runnable, você criará uma instância da classe
Thread e passará uma instância de sua classe contendo a implementação de Runnable como um argumento para o
construtor de thread.

Vamos ver como é examinando um programa multithread.

Amostra de Programa MultiThreaded

Aqui está um programa que adicionará os números armazenados em uma matriz usando vários threads. A ideia básica é dividir o array e dar
a cada thread um pedaço do array. Depois que cada thread calcula uma soma parcial, as somas parciais são somadas para produzir um total
de todos os elementos da matriz.

Por exemplo, suponha que seu array tenha 1.000 elementos e você gostaria de usar 11 threads para somar todos os números. Você poderia
ter um thread como um driver de classificação (por exemplo, thread 11) criando e executando os 10 threads restantes. Quanto aos objetos
thread, poderíamos ter o thread 0 (o primeiro thread) somando os elementos do array 0 a 100, o thread 1, somando os elementos de 101 a
200, e assim por diante.
Página 223

Vamos usar a receita de duas etapas da seção anterior, certo? Primeiro, o seguinte mostra o código para uma subclasse que estende
Thread, ou anteriormente, o número 1:

Uma Subclasse de Class Thread

class AddThreads extends Thread { // 1


//
// Não use variáveis de classe aqui !!! Lembre-se ---- queremos acessar // as somas parciais da classe ThreadedAdd,
que chama esta // classe de thread

//
int parcialSum = 0; int // 2
startIDX, endIDX;
//
//Construtor. Tudo o que precisamos é configurar os limites para o // array que iremos acessar.

//
AddThreads (int idx) { // 3
int threadArrayLength = ThreadedAdd.adders.length;
int arrayLength = ThreadedAdd.addends.length;
startIDX = arrayLength / threadArrayLength * idx; endIDX
= arrayLength / threadArrayLength * (idx + 1) -1;
}
//
// Lembre-se de que os threads devem substituir o método run () da classe Thread // quando estivermos usando este
mecanismo de threading (ao contrário de // implementar a interface Runnable como você DEVE fazer com os
miniaplicativos. //

public void run () { // 4


// Uma linha deve bastar !!!
// Observe a referência à variável CLASS addends declarada // e inicializada na classe ThreadedAdd.

//
System.out.println ("In Thread:" + getName ()); // Faça a adição real

para (int arrIDX = startIDX; arrIDX <= endIDX; arrIDX ++)


parcialSum + = ThreadedAdd.addends [arrIDX]; System.out.println ("Concluído com Thread:"
+ getName ());

} // do método run ()} // da classe


AddThreads

Tudo bem - pelos números.

A linha 1 é o cabeçalho do método. Você sabe que há tarefas acontecendo quando você percebe que esta aula estende a aula Fio.
Esta classe contém a implementação do método run () que faz o trabalho do thread.

A linha 2 mostra algumas das propriedades que os objetos desta classe irão conter. A variável de instância
parcialSum contém a soma da peça da matriz gerada por um determinado
Página 224

fio. As variáveis de instância startIDX e endIDX contém os índices inicial e final da matriz que este segmento acessará. A expressão threadArrayLength
é o número de tópicos criados que irão somar os números. No método (ainda não mostrado) que cria e inicia esses threads, os threads são
armazenados como uma matriz de objetos de subclasse. Tomamos cuidado para garantir que o número de threads divida uniformemente o
número de elementos da matriz a serem adicionados, escolhendo os números apropriados para ambos.

A linha 3 inicia o construtor para os objetos da subclasse. Geramos os índices inicial e final de um índice passado pelo método
(ainda não mostrado) que cria os objetos da subclasse.

A linha 4 inicia o método run (). Este método usa o getName () método para buscar o nome da thread atualmente em execução,
imprime o nome e soma os números. Um loop for adiciona os elementos do array. Quando o loop é concluído, o método
imprime o nome do thread.

Até agora, tudo bem - temos uma subclasse de Thread e um método run (). Agora, precisamos da etapa 2, listada anteriormente - uma
classe para criar e gerenciar threads que irão somar os números. O código é mostrado na próxima seção.

Uma classe que cria e gerencia threads

public class ThreadedAdd {


//
// Observe que estamos usando variáveis CLASS aqui. Não há necessidade de // variáveis de instância porque
não vamos criar nenhum objeto // para a classe ThreadedAdd.

//
flutuação estática theSum = 0;
flutuação estática [] adendos = novo flutuador [1000000]; // 1
//
// Este é um array de objetos de uma classe que estende a classe Thread. // Os objetos desta classe corresponderão a um
thread que // realizará a adição real.

//
adders estáticos AddThreads [] = novo AddThreads [5]; // 2

public static void main (String [] args) {


// Como diz ........
loadAddendArray ();
//
// Crie os objetos de thread e inicie-os. //

para (int tidx = 0; tidx <adders.length; tidx ++) {


// Sim, eu sei que isso pode ser feito em uma instrução da seguinte maneira: // addres [tidx] = new
AddThreads (tidx) .start ();
adders [tidx] = new AddThreads (tidx); // 3
adders [tidx] .start ();
}
//
Página 225

// Espere que todos os threads chutados terminem antes de adicionar // as somas parciais.

// Se não esperarmos, não temos garantia de que todos os cinco threads // terminarão suas tarefas antes de
somarmos seus resultados // (possivelmente) inexistentes ou incompletos.

//
tentar {
para (int tidx = 0; tidx <adders.length; tidx ++)
adders [tidx] .join (); } // 4

catch (InterruptedException ie) {


System.out.println ("Tarefa interrompida");
}
//
// Acesse as variáveis de instância que contêm as somas parciais // para cada objeto de thread.

//
para (int tidx = 0; tidx <adders.length; tidx ++)
theSum + = somadores [tidx] .partialSum; // Imprima o resultado.

System.out.println ("A soma é igual a ......" + theSum);

} // do método main () //

// Inicialize o array com números de 1 ao tamanho do array // Também pode usar um método estático -
nenhum objeto real de jazz // necessário para esta rotina.

//
static void loadAddendArray () { // 5
para (int arrIDX = 0; arrIDX <addends.length; arrIDX ++)
adendos [arrIDX] = arrIDX + 1; } // de loadAddendArray

} // da classe ThreadedAdd

A ideia básica é criar cinco threads, iniciá-los e esperar que todos os cinco threads sejam concluídos. some as somas parciais
depois de concluídas. Portanto, quando o método main () executa e inicia os cinco threads, haverá seis tópicos executando de uma
vez. Java usa um thread para executar o método main (); os outros cinco threads são criados para fazer a aritmética. Os
comentários no código são autoexplicativos. No entanto, algumas linhas numeradas contêm comentários adicionais.

A linha 1 é a matriz de números que o programa adicionará. Se você colocar, digamos, 100 elementos na matriz, não verá nenhuma
evidência de multithreading. Mais tarde, quando vir a saída, verá essa evidência.

A linha 2 cria um matriz de objetos. Agora você sabe que esta declaração não cria os objetos.
Tudo o que essa declaração de array faz é criar uma referência para cinco objetos. Esses objetos são instâncias da classe anterior, a classe que
estende a classe Thread. Em outras palavras, esses objetos são nossos threads que irão adicionar os elementos do array.
Página 226

A linha 3 mostra a criação dos objetos de subclasse da classe Thread, e a invocação de threadObj.start (), para começar a executar as
threads. Lembre-se de que você codifica um método run () que faz o trabalho do thread; entretanto, você não codifique um método start ()
para iniciar a execução do thread. Vocês não
invoque o método run () enquanto você devo invoque o método start ().

A linha 4 é um método da classe Thread chamado Junte-se(). O método join () instrui o tempo de execução Java a fazer com que o encadeamento que
invocou o método join () (o encadeamento que controla a execução de main ()) espere até que o encadeamento referenciado no método join () (os
encadeamentos que fazem a aritmética) morre. Como o método join () está em um loop, o tempo de execução Java instruirá o encadeamento que rege
a execução do método main () a aguardar até que todos os cinco encadeamentos sejam concluídos. Como diz o comentário, você não tem garantia de
que esses tópicos serão concluídos ao mesmo tempo ou na ordem em que foram iniciados.

A classe Thread inclui métodos para obter ou definir a prioridade de um thread, colocá-lo em repouso por um período de tempo
especificado, entregar o processador a outro thread esperando para ser executado e outros. Além de run () e start (), o único outro
método de thread usado aqui é join ().

A linha 4 também mostra um bloco try / catch. O método de junção lança uma exceção de classe
InterruptedException, que, se você estiver prestando atenção, deve ser captado neste método.

A linha 5 mostra a matriz sendo inicializada para números inteiros 1 até o tamanho da matriz. Isso foi feito para verificar os resultados
usando a seguinte fórmula indutiva:

Soma dos números de 1 a N = N * (N + 1) / 2

Agora, para grandes tamanhos de array, erros de precisão de ponto flutuante aparecem; portanto, o resultado não será exato. A página a seguir

mostra um exemplo de saída de amostra.

A Figura 12.2 mostra três execuções do programa de adição multithread que adiciona números de um a 1.000.000. Observe como
os resultados mostram evidências de threads simultâneos. Se este fosse um programa de thread único, sua saída seria
semelhante à seguinte:

No Tópico: Tópico-0
No Tópico: Tópico 1
No Tópico: Tópico 2
No Tópico: Tópico 3
No Tópico: Tópico 4
Concluído com Tópico: Tópico-0 Concluído
com Tópico: Tópico-1 Concluído com Tópico:
Thread-2 Concluído com Tópico: Tópico-3 Feito
com Tópico: Tópico-4

No entanto, você pode ver os threads concluindo em uma ordem diferente para cada execução.
Página 227

Figura 12.2 Três exemplos de execuções de ThreadedAdd.

Em suma

Você leu um pouco sobre multithreading e viu a implementação de Threads em Java. Você leu que cria threads em Java
estendendo a classe Thread ou implementando a interface Runnable. Em ambos os casos, você deve codificar um método run ()
com esta assinatura:

public void run ()


Página 228

Além disso, você leu que seu código nunca invoca run () diretamente; não há nenhuma instrução invocando run (). Em vez disso, você invoca o
método start (), que inicia o método run (). Quando seu método run () começa a ser executado, você está executando multithreading.

Novamente, esta não é a história completa. Para escrever programas multithreading, você precisará de mais do que uma simples
familiaridade com os outros métodos da classe Thread e como usá-los. No entanto, com a explicação e o programa encadeado de amostra,
você será capaz de lidar com qualquer coisa pertencente a encadeamentos Java que você não poderia lidar com o tempo e um bom livro de
recursos.
Página 229

CAPÍTULO 13
O Sistema de Agendador de Aulas do Departamento de Treinamento.

Neste capítulo, você verá o código-fonte Java para um sistema de agendamento de aulas do departamento de treinamento. Ao longo do
caminho, você verá alguns códigos em COBOL e PL / I que fornecem funções semelhantes. Você também lerá sobre a análise do aplicativo,
comparando e contrastando a análise de uma solução Java com a análise de uma solução de linguagem procedural.

O capítulo começa descrevendo a funcionalidade de alto nível do aplicativo, seguido por algumas idéias sobre a interface do usuário. O
capítulo descreve o formato dos armazenamentos de dados subjacentes e as saídas do aplicativo. A seguir, o capítulo cobre o
processamento necessário para produzir as saídas.

Até agora, a descrição do aplicativo não é específica para nenhuma linguagem de programação. A seguir, você lerá sobre os detalhes
específicos do Java necessários para implementar a interface, saídas e processos anteriores. Você contrastará os detalhes do Java com os
detalhes do COBOL e PL / I que se seguem.

O restante do capítulo é o código Java que implementa o aplicativo completo com comentários e informações sobre os recursos
Java ainda não cobertos no livro. Às vezes, você verá módulos COBOL e PL / I que implementam recursos de aplicativo
semelhantes.

O aplicativo definido

O objetivo do programador de aulas do departamento de treinamento é permitir que alunos e instrutores consultem um conjunto de armazenamentos
de dados para encontrar informações sobre aulas e cursos
Página 230

e atualizar a loja com novas informações sobre aulas e cursos. Em outras palavras, alunos e instrutores podem encontrar informações
sobre aulas e cursos; os alunos podem se inscrever em cursos e os instrutores podem se programar para ministrar aulas. O aplicativo
permite um número fixo de alunos em uma turma e nenhum aluno pode frequentar duas turmas ao mesmo tempo. Além disso, nenhum
instrutor pode dar duas aulas ao mesmo tempo.

Aqui um curso é um corpo de material de instrução sobre um tópico específico. UMA classe é um curso programado para
ser ministrado em sala por um instrutor em uma determinada data.

O aplicativo não é particularmente robusto; um sistema comercial usado para rastrear as atividades de um departamento de
treinamento que conteria mais recursos e funções do que nosso aplicativo. Ao ler este capítulo, você dirá de vez em quando: "Por que
o aplicativo não executa a função X" ou "Eu teria feito a função Y de maneira diferente".

Opções de aplicação para os alunos

O aluno poderá realizar as seguintes atividades com o aplicativo:

Listar aulas oferecidas a partir de uma data específica Listar aulas

por número de sala


Y

Liste os cursos ou aulas por categoria Liste os


FL

cursos realizados pelo aluno


M

Liste as classes com vagas (vagas disponíveis) começando em uma data específica
A
TE

Opções de aplicação para os instrutores

O aplicativo oferece ao instrutor os mesmos recursos do aluno, além dos recursos adicionais listados a seguir:

Criar uma nova classe (o instrutor deve estar ensinando a nova classe) Excluir uma classe

existente (o instrutor deve estar ensinando a classe) Alterar uma classe existente (o

instrutor deve estar ensinando a classe)

Adicionar ou remover um ou mais alunos de uma classe (o professor deve estar ensinando a classe)

A interface do usuário

A interface do usuário consiste em telas de entrada e alguns diálogos. O usuário primeiro encontra uma tela onde ele insere
informações pessoais. Uma vez feito isso, o aplicativo determina se o usuário é um instrutor ou aluno e exibe a tela
apropriada mostrando

Team-Fly ®
Página 231

opções de aplicação permitidas. O usuário seleciona sua opção. Dependendo da opção selecionada, o aplicativo pode exibir telas
de entrada adicionais, talvez para capturar datas ou outras informações necessárias para concluir a solicitação.

A Interface de Usuário do Mainframe OS / 390

Aqui estão as telas de entrada do usuário que um usuário do mainframe OS / 390 pode encontrar se este aplicativo for codificado em uma linguagem
procedural como COBOL. As telas de entrada são 3270 telas de entrada do ISPF baseadas em texto.

A tela de identificação do usuário

A Figura 13.1 mostra a tela de identificação do usuário. Aqui, o usuário insere um nome e um ID de funcionário. O aplicativo verifica o ID do
funcionário em relação ao arquivo do funcionário. Se a ID do funcionário inserida estiver em arquivo e a ID do funcionário inserida corresponder
ao nome do funcionário no arquivo, o aplicativo permite que o usuário continue.

A Tela de Opções de Entrada do Aluno

A Figura 13.2 mostra uma tela de entrada do mainframe mostrando as opções disponíveis para um aluno. As opções são relativamente
autoexplicativas. A saída resultante da entrada das opções 1 ou 5 é uma tabela ISPF que mostra as informações da classe. Presumimos que o
aluno tenha acesso aos números dos quartos e categorias de cursos.

Figura 13.1 A tela de entrada de identificação do usuário do mainframe.


Página 232

Figura 13.2 A tela de entrada do mainframe mostrando as opções do aluno.

O instrutor entra na tela de opções

O instrutor tem a maioria das mesmas opções disponíveis para os alunos, além da capacidade de criar, remover e alterar as
informações da aula para aquelas aulas que o instrutor está programado para ministrar ou já ministrou. O resultado da seleção do mudar
lista de alunos opção é a mesma lista mostrada na Figura
13,5.

A Figura 13.3 mostra as opções disponíveis para um instrutor.

O instrutor cria uma nova tela de classe

Quando o instrutor seleciona a opção 6 na tela mostrada na Figura 13.3, o instrutor vê a tela mostrada na Figura 13.4. A tela
de entrada mostra o nome do instrutor por padrão; o instrutor pode alterar este campo. O ID do curso inserido deve existir
em arquivo.

A Figura 13.4 mostra a tela de entrada para a criação de uma nova classe. Aqui, o instrutor insere os detalhes do curso.

O Instrutor altera a tela de entrada da classe existente

A tela de mudança de classe existente parece a mesma que a tela mostrada na Figura 13.4, exceto que os campos de entrada são
pré-preenchidos com as informações de classe inseridas e o
Página 233

Figura 13.3 A tela de entrada do mainframe mostrando as opções do instrutor.

Figura 13.4 O mainframe cria uma nova tela de entrada de classe.

o texto de instrução diz "Insira as informações necessárias para a classe <classID> abaixo" e o título da tela é "Alterar as
informações para a classe <classID>", onde <classID> é a ID da classe inserida na tela anterior.
Página 234

A tela Alterar lista de alunos.

Aqui, o instrutor tem a capacidade de alterar a lista de alunos de uma classe que ela está programada para dar. O processamento é um
processamento típico de tabela ISPF - insira um código à esquerda de uma linha da tabela que direcione o processamento. Para alterar um aluno
existente, o instrutor sobrescreve o nome de um aluno existente. Para adicionar um aluno, o instrutor insere A no campo de comando da linha,
pressiona ENTER e obtém uma nova linha da tabela onde insere as informações do aluno.

A interface do usuário Java

As telas são o que um usuário pode encontrar se este aplicativo for codificado em Java em uma máquina pequena. Você verá o código-fonte
Java que criou essas telas posteriormente neste capítulo.

Este livro ainda não abordou a codificação dos componentes da interface do usuário Java. No entanto, não é possível fornecer um exemplo
de um aplicativo Java sem abordar a codificação dos componentes da interface do usuário até certo ponto. Para ver como um aplicativo
Java pode ser montado, você precisa saber algo sobre a construção da interface de usuário Java. Por outro lado, você provavelmente não
gastará muito do seu tempo, no que diz respeito ao Java, codificando interfaces de usuário Java.

A abordagem adotada neste livro é fornecer alguma exposição à codificação de componentes da interface do usuário Java sem entrar
em todos os detalhes. Você verá alguns exemplos de tela de entrada e saída e o código que produz os exemplos. Você lerá uma
explicação do código que cria os exemplos. Você não lerá sobre a maioria das nuances envolvidas na criação de interfaces de usuário
Java.

Figura 13.5 O mainframe muda a tela de entrada da lista de alunos.


Página 235

O objetivo deste capítulo é ver como você pode codificar um aplicativo Java que imita a funcionalidade de um aplicativo desenvolvido
em uma linguagem procedural no IBMmainframe. As telas de entrada Java foram desenvolvidas para serem semelhantes às telas do
ISPF mostradas anteriormente.

A tela de identificação do usuário

A tela a seguir executa a mesma função que a contraparte de mainframe mostrada na Figura
13.1. A tela de entrada do Java requer que o usuário clique no enviar botão enquanto a tela de entrada do mainframe requer que o
usuário pressione o ENTRAR após a entrada do campo.

A Figura 13.6 mostra a tela de identificação do usuário. Aqui, o usuário insere um nome e um ID de funcionário.

O aluno entra na tela de opções

Com Java, podemos usar botões de rádio para impor automaticamente uma escolha mutuamente exclusiva. Os campos de texto à
direita das opções do Aluno tornam-se ativos quando a opção correspondente é selecionada. Por exemplo, observe que a última opção
está selecionada e a caixa de texto à direita está habilitada ou pode aceitar entradas. Se o aluno clicar em outro botão de opção, a
caixa de texto correspondente será habilitada e a caixa de texto habilitada anteriormente será desabilitada.

O aluno deve clicar no botão enviar para prosseguir.

A Figura 13.7 mostra uma tela de entrada do Java mostrando as opções disponíveis para um aluno.

A tela de entrada de opções do instrutor Java

A estrutura da tela de entrada do instrutor é a mesma da tela de entrada do aluno. A semelhança é espelhada no
código usado para criar as duas telas de entrada.

A Figura 13.8 mostra as opções disponíveis para um instrutor.

A Tela Criar uma Nova Entrada de Classe

A Figura 13.9 mostra a tela de entrada para a criação de um novo curso. Aqui, o instrutor insere os detalhes do curso. Todos os campos
exigem uma entrada.

Figura 13.6 A tela de entrada de identificação da pequena máquina Java.


Página 236

Figura 13.7 A pequena tela da máquina Java mostrando as opções dos alunos.

Figura 13.8 A tela de entrada da pequena máquina Java mostrando as opções do instrutor.

Figura 13.9 A pequena máquina Java cria uma nova tela de entrada de classe.
Página 237

A tela de entrada da lista de mudança de alunos

Esta tela parece um pouco diferente da sua contraparte de mainframe. Em vez de ter um campo de opção à esquerda de cada
linha, tendo um Claro botão que exclui um aluno faz o trabalho. Quaisquer alterações que o instrutor fizer na lista não terão efeito
até que o instrutor clique no Lista de alunos do processo botão na parte inferior da tela.

A Figura 13.10 mostra a tela de entrada para alterar a lista de alunos de uma classe.

The Data Stores

Nesta seção, você lerá uma descrição dos arquivos usados para conter os dados usados pelo aplicativo. Os dados são mantidos em arquivos
sequenciais; no Capítulo 19, você verá esse aplicativo com os dados armazenados em arquivos VSAM e tabelas do DB2.

Você não leu muito sobre o tratamento Java da E / S de arquivos. O caso de não cobrir a E / S do arquivo Java em detalhes é semelhante ao de não
cobrir o desenvolvimento do componente da interface com o usuário Java em detalhes. Provavelmente, você gastará seu tempo escrevendo programas
Java para acessar bancos de dados ou estruturas de dados específicas da IBM, como arquivos sequenciais ou conjuntos de dados VSAM. Mesmo
assim, você merece alguma exposição à E / S do arquivo Java e aqui é onde você o obterá.

O conteúdo e a estrutura dos arquivos são os mesmos da versão procedural do programa e da versão Java. A seguir está uma
lista de arquivos e uma breve descrição de cada um.

Figura 13.10 A tela de entrada da lista de alunos da pequena máquina Java muda.
Página 238

O arquivo de informações do curso.

Este arquivo contém informações sobre os cursos oferecidos pela área de treinamento. Um curso é uma coleção de material relacionado
a um único tópico. A Tabela 13.1 mostra o layout do arquivo e uma descrição dos campos no arquivo.

O Arquivo de Informações da Classe

Uma aula é um curso programado. Este arquivo contém informações sobre ofertas de cursos ou classes específicas. A Tabela 13.2 mostra as
particularidades dos campos no arquivo de informações da classe.

O arquivo de informações do instrutor

O arquivo de informações do instrutor contém informações pessoais e as aulas que o instrutor deve ministrar. A Tabela 13.3 descreve
os campos no arquivo de informações do instrutor.

O arquivo de informações do funcionário

Este arquivo contém informações sobre os funcionários. Para ter uma visão simplista, um funcionário é um instrutor ou um aluno. Para manter as
coisas simples, este arquivo contém informações relevantes para o aplicativo de treinamento. A Tabela 13.4 contém uma descrição do conteúdo
deste arquivo.

Resultados do aplicativo

O aplicativo produz saídas como uma série de telas contendo informações correspondentes às seleções anteriores do usuário.
Veremos as saídas de nossa versão de mainframe e saídas semelhantes de nosso aplicativo Java. As telas a seguir não
permitem entradas do usuário.

Tabela 13.1 Campos no arquivo de informações do curso

NOME DO CAMPO POSIÇÃO DESCRIÇÃO


Identidade do curso 1-5 String alfa que identifica o curso. O campo CourseID é
exclusivo para cada registro.

Tema 5-20 String alfa servindo como uma breve descrição do


curso.

Descrição 21–50 Uma descrição mais longa do que o campo Tópico.

Pré-requisitos 51-70 Até quatro CourseIDs que são pré-requisitos para este
curso.

Duração 71-72 Número de horas programadas para entregar uma oferta de


curso.
Página 239

Tabela 13.2 Campos no arquivo de informação da turma

NOME DO CAMPO POSIÇÃO DESCRIÇÃO


ClassID 1-8 String alfa que identifica a classe. Os primeiros cinco
caracteres são o CourseID; os próximos três são numéricos.
O campo ClassID é exclusivo para cada registro.

DateOffered 9-14 Um campo de data no formato AAMMDD


identificando a data de início da aula.

Número do quarto 15-18 Sequência alfanumérica que identifica a sala em que a aula é
ministrada.

InstructorID 19-25 Sequência alfanumérica que identifica o instrutor


programado para ministrar a aula.

ID do Empregado 26-110 Até 12 strings alfanuméricas, cada string identificando um


aluno específico matriculado na classe. Cada EmployeeID
começa com dois caracteres alfabéticos seguidos por cinco
números.

Tabela 13.3 Campos no arquivo de informações do instrutor

NOME DO CAMPO POSIÇÃO DESCRIÇÃO


ID do Empregado 1-7 Uma sequência de sete caracteres que identifica exclusivamente
um funcionário. Os primeiros dois caracteres são alfa; os cinco
restantes são numéricos.

Primeiro nome 8-17 Um campo de 10 caracteres com conteúdo


autoexplicativo.

Último nome 18-27 Um campo de 10 caracteres com conteúdo


autoexplicativo.

CourseIDs 28-57 Até seis CourseIDs que o instrutor está qualificado para
ensinar.

Saídas OS / 390 IBMMainframe

Tal como acontece com as telas de entrada, as telas de saída são telas do ISPF Dialog Manager.

A primeira tela de saída, mostrada na Figura 13.11, é produzida quando um aluno solicita uma lista de aulas iniciando em ou depois
de uma data inserida. Um aluno visualizaria essa tela ao selecionar a primeira opção da tela de entrada mostrada na Figura 13.2.
Página 240

Tabela 13.4 Campos no arquivo de informações do funcionário

NOME DO CAMPO POSIÇÃO DESCRIÇÃO


ID do Empregado 1-7 String alfa que identifica exclusivamente o funcionário.
Os primeiros dois caracteres são alfa; os cinco
restantes são numéricos.

Primeiro nome 8-17 Um campo de 10 caracteres com conteúdo


autoexplicativo.

Último nome 15-18 Um campo de 10 caracteres com conteúdo


autoexplicativo.

ClassesTaken 19-65 Até seis ClassIDs de aulas cursadas pelo funcionário.


Cada ClassID tem oito caracteres.

Y
FL
A M
TE

Figura 13.11 As classes de mainframe iniciando em ou após uma data inserida.

A Figura 13.12 mostra uma lista de aulas oferecidas por número de sala. Um aluno ou instrutor veria a tela a seguir ao
selecionar a opção 2 da tela de entrada mostrada na Figura 13.2.

A Figura 13.13 mostra uma tabela dos cursos por categoria. O usuário pode visualizar a tela a seguir após selecionar a opção 3 da
tela de entrada mostrada na Figura 13.2.

A Figura 13.14 mostra uma lista de classes com vagas disponíveis. O aluno veria esta tela após selecionar a opção 5 da tela
mostrada na Figura 13.2.

A Figura 13.15 mostra uma lista de aulas cursadas por um aluno. O aluno veria esta tela após selecionar a opção 4
da tela mostrada na Figura 13.2.

Team-Fly ®
Página 241

Figura 13.12 As classes de mainframe classificadas por número de sala.

Figura 13.13 A lista de cursos do mainframe por categoria.

A lista de aulas ministradas por uma tela de instrutor é quase idêntica à tela mostrada na Figura
13.15, exceto que o título da tela é "Aulas ministradas", não "Aulas ministradas". Um instrutor veria a lista de aulas
ministradas ao selecionar a opção 4 da tela mostrada na Figura 13.3.
Página 242

Figura 13.14 A lista de classes do mainframe com vagas disponíveis.

Figura 13.15 A lista de mainframe das aulas realizadas por um aluno.

Saídas Java

Aqui, você verá telas semelhantes às mostradas nas Figuras 13.11 a 13.15. As telas desta seção foram produzidas com
código Java. A principal diferença na função
Página 243

A diferença entre as telas do mainframe e as telas do Java é que o usuário precisa pressionar uma tecla de função (geralmente PF3) nas
telas do mainframe para continuar, enquanto o usuário precisa clicar em um botão na parte inferior da tela do Java para continuar.

As descrições fornecidas para as telas de saída do mainframe servem para descrever as telas Java. Portanto, nas Figuras
13.16 a 13.20, apresentamos as telas Java sem comentários adicionais.

A lista de aulas ministradas por uma tela de instrutor é quase idêntica à tela mostrada na Figura
13.15, exceto que o título da tela é "Aulas ministradas", não "Aulas ministradas". Um instrutor veria a lista de aulas
ministradas ao selecionar a opção 4 da tela mostrada na Figura 13.3.

Juntando o aplicativo

Esta seção discute algumas idéias sobre como esse aplicativo pode ser construído em uma linguagem de programação procedural
de terceira geração e em Java. A análise que se segue é de alto nível, com alguma atenção aos detalhes de vez em quando.

Nós deliberadamente simplificamos o aplicativo. O objetivo é apresentar um problema e comparar e contrastar uma solução de linguagem
procedural com uma solução Java. Por exemplo, este aplicativo usa um conjunto de arquivos sequenciais, não um banco de dados. Além
disso, as telas e saídas da interface do usuário dificilmente são de qualidade comercial. Dito isso, você terá uma ideia das diferenças entre
reunir o aplicativo em uma linguagem procedural e Java.

Figura 13.16 As classes Java iniciando em ou após uma data inserida.


Página 244

Figura 13.17 As classes Java classificadas por número de sala.

Sem mais delongas, seguem algumas idéias sobre uma solução de linguagem procedural.

Uma solução de linguagem procedural

Se você pedisse a um processador de dados para resumir a natureza deste aplicativo, ele poderia responder que o aplicativo era um aplicativo
básico de redação de relatórios. Ela pode mencionar que o aplicativo segue o modelo clássico de entrada - processo - saída da maioria dos
aplicativos de processamento de dados.

Um processador de dados pode criar uma série de fluxogramas, escrever alguns pseudocódigos e criar um conjunto de gráficos de estrutura para
descrever os processos. O processador de dados mapearia processos que terminam como módulos em um gráfico de estrutura para processar caixas
e diamantes de decisão nos fluxogramas. Embora tenhamos evoluído muito desde os dias do design estruturado, as ferramentas mencionadas ainda
são usadas nas organizações de processamento de dados de hoje.

Vamos dar uma olhada em alguns fluxogramas que descrevem bastante os processos necessários. Não examinaremos todos os fluxogramas
necessários para descrever todo o comportamento do aplicativo. Posteriormente, mapearemos alguns módulos para os processos descritos nos
fluxogramas a seguir e incluiremos alguns pseudocódigos para alguns módulos.
Página 245

Figura 13.18 A lista de cursos Java por categoria.

Fluxogramas

A Figura 13.21 mostra o fluxograma que faz o aplicativo funcionar.

Depois que o aplicativo exibe a tela de identificação do usuário e aceita o nome e a ID do funcionário, o aplicativo confirma se o
nome corresponde ao ID. Se as duas entradas corresponderem, o aplicativo determina se o usuário é um instrutor ou não (um
aluno). A determinação é feita examinando o arquivo de informações do instrutor; se a identificação do funcionário for encontrada no
arquivo de informações do instrutor, o usuário é considerado um instrutor, caso contrário, o usuário é considerado um aluno.
Dependendo da qualificação do instrutor / aluno, o aplicativo exibe a tela de opções apropriadas.

A Figura 13.22 mostra um fluxograma que descreve o processo de um aluno solicitando que o aplicativo exiba uma lista de aulas
posteriores a uma data inserida.
Página 246

Figura 13.19 A lista Java de classes com vagas disponíveis.

Figura 13.20 A lista de mainframe das aulas realizadas por um aluno.

O arquivo de informações da classe contém um campo de data que identifica quando a classe começa. O processo consiste em ler cada registro,
comparando o campo de data no registro com o campo de data inserido. Se o campo de data inserido antecede a data no registro, os dados de
registro relevantes
Página 247

Figura 13.21 Fluxograma que descreve o início do aplicativo.

é adicionado à estrutura. Quando o programa atinge o EOF, o programa exibe a estrutura com as informações de classe
relevantes. O display mostrado na Figura 13.11 seria a saída resultante.

Os demais fluxogramas que descrevem os processos de geração das telas de saída seriam semelhantes ao fluxograma mostrado na
Figura 13.22. Eles envolveriam a leitura de um ou mais arquivos, a comparação de campos dos registros com os critérios inseridos, o
salvamento de dados nos registros que atendiam aos critérios e a exibição dos resultados em um meio de exibição adequado (neste
caso, painéis ISPF).
Página 248

Figura 13.22 Fluxograma para a opção "exibir lista de classes iniciando após uma determinada data".

Pseudocódigo para a opção "Exibir lista de turmas depois da data inserida"

A Listagem 13.1 mostra um exemplo do que pode ser passado como pseudocódigo em alguns trimestres para o fluxograma mostrado na Figura
13.22.

Alguns detalhes são omitidos do pseudocódigo, como o layout do registro classinfo.data e a maneira pela qual a data inserida
é obtida. No entanto, o pseudocódigo transmite o sentido dos elementos necessários de uma solução procedimental.
Página 249

Exibir aulas após a data inserida:


Buscar Data Inserida
Crie uma tabela ISPF para manter as informações da classe
TBCreate CLASINFO contendo ClassID, Tópico do Curso,
Nome do instrutor, data de início e número da sala
Abra o arquivo "classinfo.data"
Leia o arquivo "classinfo.data" no registro classinfo.data
em EOF, feche o arquivo "classinfo.data"
Se data no registro classinfo.data> Data inserida
Em seguida, emita TBADD CLASINFO Else

Leia o arquivo "classinfo.data" no registro classinfo.data

Emita TBDISPL com a tabela CLASINFO End:

Listagem 13.1 Amostra de pseudocódigo para fluxograma na Figura 13.22

O pseudocódigo que descreve como gerar as saídas restantes é muito semelhante à Listagem
13.1. As principais diferenças são os campos salvos dos registros, os arquivos lidos e os critérios usados.

Recursos de uma solução de linguagem procedural.

A abordagem adotada ao desenvolver uma solução de linguagem procedural é a ênfase do processo sobre os dados (daí o termo linguagem
procedural). Os fluxogramas e o pseudocódigo revelam uma abordagem quase padronizada - leia um registro, compare campos e salve
dados. O desenvolvimento do código reflete esses processos.

Além disso, o fluxo de controle do programa é sequencial, de cima para baixo. Nada acontece no programa a menos que o código invoque
explicitamente uma rotina ou "caia" em um bloco de código.

Uma solução de linguagem Java contrasta com a abordagem procedural. A seguir, discutiremos os elementos de uma abordagem da linguagem
Java.

Uma solução de linguagem Java

Para implementar o aplicativo do Departamento de Treinamento em Java, devemos pensar um pouco diferente de nossos irmãos de
programação de mainframe. Em Java, não prestaremos tanta atenção ao processo como faríamos ao usar uma linguagem procedural. Além
disso, teremos que renunciar ao fluxo sequencial estrito de controle. Como você verá, esse não é o jeito do Java, especialmente ao lidar com
interfaces de usuário.
Página 250

Vamos começar com os blocos de construção básicos. Em uma linguagem procedural, você pode começar com um fluxograma ou pseudocódigo
para fazer a bola rolar. Como Java é orientado a objetos, teremos que pensar em termos de objetos. Por exemplo, as telas de entrada e as
exibições de saída são objetos. Você se lembra de que os objetos têm um conjunto associado de propriedades e comportamentos. Ao desenvolver
nossa solução Java, fixaremos em quais objetos serão usados em nossa solução e nas propriedades e comportamentos desses objetos.

Em termos de procedimento, um programador pensa em programas que implementam processos que leem entradas e saídas e criam saídas. Em Java,
um programador pensa em objetos que possuem comportamentos que implementam os processos desejados agindo sobre os dados que são as
propriedades dos objetos. Portanto, um bom lugar para um programador Java começar a desenvolver uma solução é determinar quais objetos serão
necessários para resolver o problema.

Na verdade, você já viu um bom número de objetos Java necessários - as telas de entrada e saída. Essas telas representam
objetos com propriedades e comportamentos. Vamos colocar alguns sob o microscópio e ver como uma solução Java pode
ser montada.

A tela de identificação do usuário —Um objeto Java

Você se lembra da tela de identificação do usuário da Figura 13.6, certo? Aqui está a tela novamente, caso você tenha esquecido.

Esta tela é um objeto Java que possui as seguintes propriedades:

O nome do funcionário
Y
FL

O ID do funcionário
M

Agora, a tela pode ter outras propriedades de que necessita para fazer seu trabalho. No entanto, estamos interessados apenas na tela público
propriedades. O nome do funcionário e a ID do funcionário são duas quantidades que serão usadas em outro lugar no aplicativo. Qualquer
A

outra propriedade da tela é relevante apenas para a tela e somente quando a tela está funcionando.
TE

E quanto aos comportamentos da tela? Poderíamos dizer que o único comportamento que nos interessa é a capacidade da tela de capturar
as duas propriedades anteriores e disponibilizar as propriedades para outros objetos que possam precisar de seus valores.

Como vamos implementar este objeto? Como você faria com qualquer outro objeto Java; primeiro, você cria uma classe para servir de modelo para
objetos com tipos semelhantes. Você codifica variáveis de instância para representar propriedades e métodos para representar comportamentos. As
propriedades e comportamentos que você deseja conhecer no restante de seu aplicativo devem ser declarados com o público modificador de
visibilidade. Outras propriedades e métodos podem ser declarados privados ou com outro modificador.

A Listagem 13.2 mostra o código Java que representa a tela de identificação do usuário.

O código mostrado na Listagem 13.2 cria o painel mostrado na Figura 13.6 usando componentes de interface de usuário Java chamados balanço componentes.
Consulte o Capítulo 24 "As bibliotecas do Java 2 Enterprise Edition" para uma breve discussão sobre o uso de componentes swing para construir
interfaces de usuário. O ponto saliente com este código é a criação de um objeto que encapsula o comportamento e as propriedades necessárias
para o aplicativo.

A última rotina mostrada na Listagem 13.2 é a rotina que processa o clique do botão. Lembre-se do Capítulo 11, "Noções básicas de
manipulação de eventos Java", onde você leu sobre eventos

Team-Fly ®
Página 251

import java.awt. *;
import java.awt.event. *;
import javax.swing. *;

public class EntryScreen extends JFrame implementa ActionListener {

String userName;
String userID;

public EntryScreen () {

super ("Digite o nome e a ID do funcionário");


this.getContentPane (). setLayout (new GridLayout (3, 1));

JPanel userNamePanel = novo JPanel (novo GridLayout (1, 2));

JPanel userIDPanel = novo JPanel (novo GridLayout


(1, 2));

JLabel userNameLabel = new JLabel ("Nome userIDLabel ");


JLabel = novo JLabel ("ID do funcionário");

JTextField userNameTF = novo JTextField (20); JTextField userIDTF


= novo JTextField (6);

JButton submitBtn = novo JButton ("Enviar");


submitBtn.addActionListener (this);

userNamePanel.add (userNameLabel); userNamePanel.add


(userNameTF);

userIDPanel.add (userIDLabel); userIDPanel.add


(userIDTF);

this.getContentPane (). add (userNamePanel);


this.getContentPane (). add (userIDPanel); this.getContentPane (). add
(submitBtn);

addWindowListener (new WindowAdapter () {public void windowClosing


(WindowEvent we) {
System.exit (0); }

});

}
public void actionPerformed (ActionEvent aev) {
userName = userNameTF.getText (); ID do usuário
= userIDTF.getText ();
if ((userName.length ()! = 0) && (userID.length ()!
= 0))
AppUtilities.processEntries
(myES, userName, userID);

}
Listagem 13.2 Código Java para a tela de identificação do usuário.
Página 252

manipulação. Aqui, a classe de evento para cliques de botão (e outros componentes da GUI também) é ActionEvent. A interface do ouvinte é
chamada ActionListener e o retorno de chamada é chamado Ação executada. Aqui, o objeto da classe EntryScreen trata do usuário clicando no
botão Enviar. A ação realizada é passar os parâmetros para uma rotina de utilitário desenvolvida para processar o clique do botão. A Listagem
13.3 mostra o código para o método processEntries.

O objetivo dessa rotina é garantir que o nome do usuário e a ID do usuário estejam de acordo e aja dependendo do
usuário ser aluno ou instrutor. Se o usuário for instrutor, o aplicativo cria e exibe uma tela de opções do instrutor, da
mesma forma para os alunos.

Observe que uma referência ao objeto da tela de entrada é passada para processEntries. A razão é que, quando temos um funcionário genuíno
(nome e ID coincidem), não precisamos mais do objeto EntryScreen. No entanto, se o nome e o ID não corresponderem, queremos que o objeto
EntryScreen permaneça por perto. Portanto, passamos uma referência e quando obtemos uma correspondência no nome do usuário e ID,
removemos o objeto EntryScreen.

Os métodos nameAndIDMatch () e isAnInstructor () são métodos utilitários localizados na mesma classe que processEntries. Os métodos
são estáticos; você não precisa instanciar um objeto para usar esses métodos utilitários.

public static void processEntries (EntryScreen myES, String userName, String userID) {

booleano processado = falso;

// Temos um ao vivo ... este é um aluno, um instrutor ou faça o número e o nome

//não combina?
if (nameAndIDMatch (userName, userID)) {
myES.dispose ();
// Se for um instrutor, mostre a tela de escolha do instrutor if (isAnInstructor (userID)) {

InstructorOptionsScreen anInstrOptionScreen =
new InstructorOptionsScreen ();
anInstrOptionScreen.show ();
}
outro {
// Mostra a tela de escolha do aluno StudentOptionsScreen
aStudInstrScreen =
new StudentOptionsScreen ();
aStudInstrScreen.show ();
}

Listagem 13.3 Código Java para o método processEntries.


Página 253

public static boolean nameAndIDMatch (String empName, String


empID) {

correspondência booleana = falso; String


aLine;
tentar {
FileReader EmployeeFile =
novo FileReader
("employeeinfo.dat");
BufferedReader pão =
n ew BufferedReader (employeeFile);

while ((aLine = brea d.readLine ())! = null) {omFile = aLine.substring


String empIDFr
( 0, 7);
String fName = aLine.substring
( 7, 16);
String lNome = aLine.substring
( 17, 26);
Nome da string = fNome.trim () + ""
+ lNome.trim ();

if (empIDFrom File.equals (empID) &&


name.equals (empName)) {match = true;

quebrar ;
}
}

bread.close ();
}
catch (IOException ioe) {
System.out.println ("Exceção:"
+ ioe.getMessage ()); };

jogo de retorno;
}

Listagem 13.4 Código Java para o método nameAndIDMatch. A Listagem 13.4 contém o

código para o nameAndIDMatch () método.

O código na Listagem 13.4 aceita um nome e argumentos de ID como strings, lê um arquivo contendo nomes e IDs e compara os nomes
e IDs lidos com os valores dos argumentos. Se uma correspondência for encontrada, a rotina retorna verdadeira.

O método tem vários métodos de manipulação de string da classe String, como o aparar() e substring ()
funções. O método cria um nome a partir do nome e do sobrenome armazenados no arquivo para comparar com o nome inserido.

A rotina anterior também emprega Java arquivo de E / S. Consulte o Capítulo 24 para uma breve dissertação sobre os recursos de E / S de arquivos do
Java.
Página 254

Em suma

O objetivo deste capítulo é destacar a diferença em uma solução de linguagem procedural e uma solução Java (objeto) para um problema
comum de processamento de dados.

A solução procedural concentra-se em definir o aplicativo como um grupo separado de módulos para implementar funções que operam
em dados. Os dados são analisados e modelados separadamente dos módulos. O fluxo do sistema é concebido como uma sequência
bem definida de ações implementadas como módulos de programa.

A solução Java concentra-se em definir o aplicativo como um grupo de objetos criados a partir de classes. As classes imbuem os objetos com
propriedades e comportamentos que modelam a essência das entidades do aplicativo, como telas de entrada e relatórios de saída. O fluxo do
sistema é concebido como um grupo de comunicação de objetos por meio de um modelo de delegação de eventos em tempo de execução.
Página 255

Parte dois
Java no ambiente de mainframe OS / 300
Página 256

Esta página foi intencionalmente deixada em branco.


Página 257

CAPÍTULO 14
Visão geral da infraestrutura / arquitetura do OS / 390 Java

Você viu os detalhes básicos da programação Java em sua estação de trabalho baseada no Windows. No entanto, você pode
ter comprado este livro para aprender como Java se encaixa nas tecnologias IBMmainframe que você conhece e adora. Este
capítulo fornece a base necessária para prepará-lo para usar essas tecnologias com Java.

O capítulo começa com os requisitos de software necessários para escrever e executar Java em seu sistema OS / 390. A seguir,
você lerá uma visão geral de como Java funciona com um potpourri de tecnologias IBM, incluindo DB2, CICS, IMS e VSAM. Você
encontrará algumas novas terminologias IBM e Java neste capítulo também. Este capítulo termina com algumas referências para
leitura adicional.

Requisitos de software

Para executar o Java no OS / 390, você precisa habilitar os Serviços do Sistema Unix, rodando em
Ambiente de linguagem ( LE) Versão 1.5 ou superior e, claro, tem um Java runtime instalado. A versão do Java runtime que você
precisa instalar depende do release do OS / 390 que você está usando.

The short story is that you'll need access to OS/390 Version 1 Release 1 or higher. However, if you can get access to OS/390
Version 1 Release 6 or higher, you'll have access to a more current JDK and some useful proprietary IBM features. Table 14.1
shows some JDK features available to various releases of OS/390 Version 1.
Page 258

Table 14.1 Some Java Features Available to OS/390 by Release

OS/390 OS/390
R1, R2, OS/390 OS/390 R8 AND
FEATURE R3 R4, R5 R6, R7 ABOVE
Java 1.1 Yes Yes Yes Yes

SAF/RACF Security No Yes Yes Yes

Security Migration Aid No No Yes Yes

RMI-IIOP No No Yes Yes

Swing Classes No No Yes Yes

Support for Record I/O Java No No Yes Yes

1.2 No No No No
Java 1.3 No No No No

JAVA COMPILER OR JAVA INTERPRETER?


As you know, Java "compilers" generate bytecode, not native code. However, IBM has a product
called the High Performance Java Compiler (HPJ), which generates native OS/390 code. In the
discussions that follow, we assume that you are not using the HPJ. Any situations that require the
HPJ or require the use of the Java bytecode compiler will be cited where appropriate.

As you see, OS/390 Releases 1, 2, and 3 support Java version 1.1, Releases 6 and 7 support several features found in Java 2,
such as the Swing API, and Releases 8 and above support JDK 1.3. Actually, the official Java release supported by OS/390
Version 1 Release 6 and 7 is JDK 1.1.8 and for Releases 8 and above is JDK 1.3.0. The entry in Table 14.1 labeled Support for
Record I/O, is a set of proprietary IBM classes called JRIO, for Java Record I/O.

Java Application Architectures

This section discusses different architectures you may implement when coding Java on OS/390. Your choices fall into
one of the following categories:

Standalone program. Your Java program directly communicates to some OS/390 software. You execute your Java
program by invoking its main() method. The method invocation can be from a command window or a batch job.
Page 259

Fat client. Your Java component directly communicates to some OS/390 software, like a standalone program.
However, you do not invoke a main() method; you access OS/390 software with a Java applet or servlet. This
architecture is an example of a two-tiered architecture.

Thin client . Your Java component communicates with some OS/390 software through some intermediate software
layer. The Java components could be part of the intermediate software layer, like a servlet. This architecture is an
example of a three-tiered (or N-tiered) architecture.

Java Software Components Versus Standalone Programs

Most of the examples shown and discussed in Part II of this book concentrate on both writing standalone Java programs and
writing fat clients to access mainframe software and data. A good case could be made for spending more time discussing the
thin client application architecture. Part III of the book spends some time discussing three- and N-tiered application architecture
and the Java 2 Platform, Enterprise Edition (J2EE— refer to Chapter 2, "What is Java?" to refresh your memory).

The two- and three-tier models cited previously use Java software components as opposed to Java programs. For now, you
can think of a Java software component as a piece of Java software that cannot be run as a program but performs some
useful work. Put differently, the Java software component represents a tier of a multi-tiered application.

One example of such a Java software component is an applet, a Java software component that runs within the context of a
Y

Web browser. For the purposes of our discussion, an applet could be the implementation of the presentation tier of a
FL

multi-tiered application. The customer uses the applet to communicate with OS/390 system software to access mainframe data.
A M

Another example is a Java servlet, a Java software component that runs within the context of a Web server. A servlet is an
implementation of all or part of the business logic tier of a multi-tiered application. The customer does not access a servlet
TE

directly; the customer accesses the servlet typically through a Web page. The servlet performs the necessary functions of
mainframe data access and passes any results back to the software components implementing the presentation layer.

Whether the mainframe data or system software access is done by a standalone Java application or a Java software
component, the Java code uses a gateway or connector to access mainframe data. Think of a gateway as a software layer that
provides functions to connect Java software components with existing software on an OS/390 system. Next, you'll read about
gateways that enable you to access IBM system software products, like CICS and DB2.

Accessing OS/390 System Software

Here, we provide a summary of connecting to various IBM system software products. Some products, like DB2, do
not require proprietary connector software. Others, like

Team-Fly ®
Page 260

CICS, may require proprietary connector software. Let's examine how you, the mainframe programmer, access
IBM system software with Java.

Accessing DB2 with Java.

Your Java programs software components can access DB2 data by using one of three methods. You can use the Java
Database Connectivity ( JDBC) API, SQLJ, or Java to invoke a DB2 stored procedures.

JDBC is industry-standard technology implemented by nearly all database vendors that enables a programmer to access data
stored in relational databases. You, the mainframe Java programmer using JDBC, issue SQL calls as character-string
parameters of methods in java.sql.*. You would use JDBC mostly for dynamic SQL.

SQLJ is an alternative technology that enables you to imbed SQL calls in Java programs. However, in contract to using
JDBC, you would use SQLJ mostly for static SQL. Put another way, SQLJ requires the use of a preprocessor that translates
the SQL calls into source code. If you've done any DB2 programming on the host in COBOL or PL/I, the aforementioned
approach should sound familiar.

In Chapter 19 "Java and DB2," you'll read more about using Java to access data stored in DB2. In addition, the coding
example shown in Chapter 20, "The Training Department Class Scheduler System Revisited," has additional Java code
examples of DB2 data access.

Accessing CICS with Java

Your Java programs or software components can access CICS by using a custom connector called the CICS Transaction
Gateway ( CTG) or by using JCICS.

The CTG is a set of Java classes that enable your Java code to talk to CICS through the External CICS Interface ( EXCI).
CTG classes can execute over a network where the CICS servers and the clients accessing these servers can be located on
different OS/390 systems. Also, the CTG classes can execute locally where the CICS servers are located on the same
OS/390 system as the clients.

If you are using CICS Transaction Server Version 1.3 or later, you can access CICS transactions directly (without using a
custom connector like CTG) by using JCICS. JCICS is a set of Java classes that come with CICS. You merely make these
classes known to your Java development environment and code your Java/CICS programs.

In Chapter 15, "Overview of OS/390 UNIX System Services," you'll read how to access CICS transactions with Java.

Accessing IMS with Java

You have several options at your disposal to access IMS from a Java program or software component. You can use the
Callable Interface, which uses the Open Transaction Monitor Access
(OMTA) to access IMS. You can use Advanced Peer-to-Peer Communications ( APPC). You can use a custom IMS gateway
called the IMS Connector for Java (formerly called the IMS TOC Connector for Java).
You can establish both conversational and non-conversational IMS transactions from a Java client application or from a
Java software component (applet, servlet).
Page 261

THE JAVA NATIVE INTERFACE


Sometimes, you cannot access OS/390 resources from Java without calling a program, written in
another programming language, currently on the OS/390 system. Sun provides an API, called JNI
that enables you to call other, non- Java programs from Java programs. You'll see mention of JNI
pop up from time-to-time in our discussion of OS/390 data access. We'll provide particulars on JNI
where appropriate.

Some Java to IMS access technologies, like APPC, require the use of the Java Native Interface
(JNI).

Chapter 15 provides additional information on IMS access with Java using the aforementioned products.

Running Java under MVS Batch

You have three options for running your Java programs under MVS batch. You can use the BPXBATCH utility. You could
run your Java program compiled with the High Performance Compiler as an executable in an all-too-familiar JCL job. You
also could run your Java program as an MVS started task.

The BPXBATCH utility enables you to run a UNIX program or script. In principle, running BPXBATCH is not
that different than running a REXX EXEC under IKJEFT01 (TSO).

If you use the HPJ and linked your object code into an MVS load module, you run the Java program as you would run any
COBOL program you linked.

Running your Java application as a started task is no different than running any program or script as a started task.

Chapter 16, "Java and MVS Batch," has more information on running your Java programs in MVS batch.

Accessing OS/390 Record Structures with Java

You can access stream files by using the Java base I/O classes (the java.io package). However, you, the mainframe
programmer, rarely use stream I/O, right? How often do you code COBOL DISPLAY or PL/I PUT SKIP DATA statements in
your production programs? What you really need to know is how to perform record I/O in Java; you need the equivalent of
COBOL, PL/I READ FILE, and WRITE file statements in the Java language.

To access proprietary file structures with Java, IBM provides a custom package called Java Record I/O, or JRIO. You use
JRIO to access sequential files or members of a partitioned dataset. Chapter
17, "Java Record I/O Using the JRIO Package," has details on Java record access with JRIO, including copious
coding examples.
Page 262

You access VSAM files with Java by using JRIO, too. JRIO supports read, append, and update record operations on random,
keyed, and sequential VSAM files. You'll read the full story on Java VSAM file access with JRIO in Chapter 18, "Java COCS,
and IMS." The coding example in Chapter 20 uses JRIO to access VSAM files, too.

IBM Java Development Tools

The cross platform nature of Java implies that you can develop your Java programs or software components on any
hardware/software combination and upload the programs to OS/390. The reality is that you'll want your Java programs to
access proprietary IBM system software. Hence, you'll need custom IBM packages representing connectors. In all likelihood,
you'll not find the custom connection classes with non-IBM Java development tools.

As a mainframe COBOL or PL/I programmer, you may be accustomed to entering source code in the ISPF editor and running
batch compiles. As a Java programmer, you will get accustomed to entering source code in an integrated development
environment on a small machine and uploading a compiled piece of code or a software component to the host. Some of you
COBOL folk with exposure to MicroFocus COBOL get the idea.

IBM has a suite of small machine development tools called the Visual Age series. IBM has Visual Age for COBOL, PL/I, C,
C++, and Java. The Visual Age product has the same interface for all programming languages. Also, The Visual Age product
comes in Entry, Professional, and Enterprise Editions.

IBM VisualAge for Java is a powerful rapid application development tool for building Java- compatible applications, applets,
and Java software components. With the VisualAge for Java programming environment, you can build 100 percent Pure
Java applications that run on any Java- compatible Virtual Machine Java Development Kit or inside any Java-enabled
browser. With VisualAge for Java, you can add or change code and compile without exiting the test environment. The
product includes the Visual Composition Editor and a fully integrated, repository-based environment that provides complete
source and version control.

If your shop does not have, nor plans to use, IBM's Visual Age tools, do not fret. You can adapt any of the leading Java tools
to use the custom IBM classes, like JRIO.

Appendix B has more information about IBM's Visual Age for Java environment, including directions on downloading a
copy, and helpful tips on configuring the environment on Windows platforms, as well as information on other Java
development tools.

In Summary

You see that IBM has covered the bases in providing Java access to OS/390 system software. You, the Java mainframe
programmer, have the means to use Java to get to your mainframe-stored data, be it stored in a VSAM file or stored in a
relational database. The next six chapters show you how.
Page 263

You can reference the IBM site, www.s390.ibm.com/java/ , for the most current information on OS/390 and Java. The site www-4.ibm.com/sof
has information on using Java with DB2. The site www.ibm.com/developer/java/ has IBM Java information of interest to
programmers. Of course, general Web searches reveal a wealth of information you might find relevant.
Page 264

This page intentionally left blank.


Page 265

CHAPTER 15
Overview of OS/390 UNIX System Services

With OS/390 UNIX System Services, OS/390 and UNIX, two widely used operating systems, come together inside a single
box. As previously mentioned, to use Java for OS/390, the shop needs UNIX System Services installed. It would behoove
you, the IBMmainframe programmer, to know a bit about UNIX System Services, or OS/390 UNIX.

This chapter provides some information on OS/390 UNIX. We start by discussing the OS/390 command shell and methods of
accessing the OS/390 shell. You'll read portions about the file system used in OS/390 UNIX, namely the Hierarchical File
System ( HFS). Lastly, you'll compare and contrast MVS concepts with those in OS/390 UNIX.

It is not the intent of this chapter to provide a comprehensive description of OS/390 UNIX System Services. Rather, the
intent is to provide you, the MVS programmer, with the knowledge to use OS/390 UN IX System Services to write and
execute your Java programs.

The Command Shell

The shell is a command that reads lines from either a file or the terminal, interprets them, and generally executes other
commands. It is the program that is running when a user logs into the system. The shell implements a language that has flow
control constructs, as well as a macro facility that provides a variety of features in addition to data storage with built-in history
and line editing capabilities. It incorporates many features
Page 266

to aid interactive use; it's advantage is that the interpretative language is common to both interactive and non-interactive use
(shell scripts). That is, commands can either be typed directly to the running shell, or they can be put into a file that can be
executed directly by the shell.

Some of you stalwart MVSers may recall interacting with TSO by issuing commands at the READY prompt; using the
command shell is similar.

You may wonder how you get to the OS/390 command shell in the first place. The next section discusses getting to the
shell.

Accessing the Command Shell

OS/390 provides several terminal emulators that you can use to access the shells: The TSO/E OMVS

command, a 3270 terminal interface

The rlogin command, an asynchronous terminal interface The telnet command,

an asynchronous terminal interface

Your system administrator must set you up to use one of these terminal emulators. To see which terminal emulator you
are set up for, you should enter the TSO command LISTUSER as follows:

LISTUSER USERNAME OMVS

Typical output from the LISTUSER command would be

UID=0000000101
HOME=/tr23790/home/loum
PROGRAM=OMVS
CPUTIMEMAX=NONE
ASSIZEMAX=NONE
FILEPROCMAX=NONE
PROCUSERMAX=NONE
THREADSMAX=NONE
MMAPAREAMAX=NONE
READY

The italicized line shows the program being used to access the shell. The default shell is accessed through the TSO
OMVS command. Change the shell by entering the ALTUSER command. The following command entered at the READY
prompt changes the OS/390 shell from OMVS to the OS/390 shell:

ALTUSER USERNAME OMVS(PROGRAM('/bin/sh'))

If you come from a UNIX background, you'll likely access the command shell through rlogin. If you come from an MVS
background, which is a key assumption of this book, you'll likely access the shell by using the OMVS command. Let's spend
some time discussing the OMVS command.
Page 267

The OMVS Command

If you are not set up to use the OMVS command, you can change it by entering the OMVS command at the READY prompt.
Figure 15.1 shows the OMVS terminal emulator with default settings.

You enter commands at the command line prefaced with = = > at the bottom of the screen. Later in this chapter, you'll read
about some OS/390 UNIX commands you may enter in this screen.

The HFS File System.

OS/390 UNIX files are organized in a hierarchy, as in a UNIX system. All files are members of a directory, and each
directory is in turn a member of another directory at a higher level in the hierarchy. The highest level of the hierarchy is the
root directory. A file contained within the HFS hierarchy is called an HFS file.

Figure 15.2 shows a comparison between MVS files and HFS files.

Figure 15.2 shows that the root/ directory is analogous to the MVS catalog; the root/ directory assists the file manager in
locating HFS files. Each user in an HFS system is

Figure 15.1 The OMVS screen with default PFKey setting.


Página 268

Figura 15.2 Conjuntos de dados MVS e arquivos HFS comparados.

atribuído a diretório home, especificado por / você/. Os demais diretórios ax123 e adsn mostrar uma organização de arquivo possível. Os
arquivos / u / ax123 / adsn / mbra e / u / ax123 / adsn / mbrb mostra dois arquivos que residem no subdiretório mostrado anteriormente.
Y
FL

Resumindo, a estrutura de diretórios usada pelo HFS é semelhante à estrutura usada em PCs com Windows.
A M
TE

Trabalhando com arquivos HFS

Esta seção discute a nomenclatura de arquivos HFS e mostra alguns comandos que atuam em arquivos HFS.

Nomeando arquivos HFS

Um nome de arquivo pode ter até 255 caracteres. Para ser portátil, o nome do arquivo deve usar apenas os seguintes caracteres:

Team-Fly ®
Página 269

Letras maiúsculas ou minúsculas de A a Z

Números de 0 a 9

Período

Sublinhado

Hífen

Não inclua nenhum caractere nulo ou barra em um nome de arquivo. Os caracteres de byte duplo não são suportados em um nome de arquivo e são
tratados como dados de byte único. O uso de caracteres de byte duplo em um nome de arquivo pode causar problemas. Por exemplo, se você usar
um caractere de byte duplo em que um dos bytes é um. (ponto) ou / (barra), o sistema de arquivos trata isso como um delimitador especial no nome
do caminho.

O OS / 390 UNIX faz distinção entre maiúsculas e minúsculas e distingue os caracteres como maiúsculas ou minúsculas. Portanto, MyFile
não é o mesmo que myfile.

Um nome de arquivo pode incluir um sufixo ou extensão, que indica seu tipo de arquivo. Uma extensão consiste em um ponto () e vários
caracteres. Por exemplo, os arquivos que são código Java podem ter a extensão .java, como no nome de arquivo myprog.java. Ter grupos
de arquivos com sufixos idênticos facilita a execução de comandos em vários arquivos de uma vez. Por exemplo, para compilar todos os
programas Java no diretório atual, você pode inserir

javac * .java

Comandos de arquivo HFS

A Tabela 15.1 descreve alguns comandos que atuam em arquivos HFS. Alguns comandos aceitam caracteres curinga que permitem
que o comando atue em grupos de arquivos, como o exemplo anterior de chamada de comando javac.

Copiando arquivos entre arquivos UNIX e conjuntos de dados MVS

Você pode copiar arquivos HFS para conjuntos de dados MVS do shell OS / 390 usando o comando cp ou mv, ou usando os seguintes
comandos TSO. Alguns exemplos de uso de cp para copiar arquivos de e para HFS para MVS são os seguintes:

cp anhfsfile "//'myhlq.anmvsdsn '"

O comando anterior copia o arquivo anhfsfile para um conjunto de dados MVS myhlq.anmvsdsn. Observe o uso de aspas em torno do nome do
conjunto de dados MVS.

cp -p "DSORG = PS, RECFM = FB, ESPAÇO =


(20, 10) "anhfsfile" //'myhlq.anewmvsdsn '"

O comando anterior copia anhhsfile para um novo conjunto de dados MVS myhlq.anewmvsdsn com os DCBs especificados. Observe o parâmetro -P devo
ser codificado em maiúsculas.

A Tabela 15.2 lista os comandos TSO que você pode usar para copiar arquivos de e para HFS para MVS.
Página 270

Tabela 15.1 Comandos de arquivo do OS / 390 UNIX

COMANDO EXEMPLO DESCRIÇÃO


Dentro No caminho antigo Crie um Difícil ligação. Oldpath é o nome do caminho
novo caminho existente; novo caminho é a nova referência. Cada
referência a newpath é uma referência a oldpath. Links
físicos não podem acessar arquivos em sistemas de
arquivos (em uma estrutura raiz diferente).

No caminho antigo Crie um simbólico ligação. Um link simbólico atua


novo caminho como um link físico sem a restrição de acesso
aos sistemas de arquivos.

No caminho antigo Criar um externo ligação. Um link externo pode se referir


novo caminho a um conjunto de dados MVS.

rm rm afile bfile Exclua um ou mais arquivos ou links. Ao


excluir um link, você não afeta os arquivos,
apenas o (s) link (s) entre eles.

cp cp filea fileb cópia de filea para dentro fileb. Se o fileb não


existir, o cp o criará.

mv mv filea fileb Mova um ou mais arquivos para outro


dirA diretório.

mv -R dirA dirB Mova todos os arquivos do diretório A para o


diretório B. A opção -R devo ser codificado em
maiúsculas.

diferença diff filea fileb Compare filea e fileb.


banheiro wc filea Conte as palavras e linhas em um arquivo de texto.

grep grep aword afile Pesquise um arquivo por ocorrências de uma palavra.
O comando grep permite pesquisas de padrões. Os
padrões usados pelo grep não são os mesmos que

curingas usados em comandos. No entanto,


o conceito é semelhante.

Comparando os conceitos de MVS, UNIX e OS / 390

A lista a seguir descreve conceitos de computação independente de plataforma na linguagem do MVS, a linguagem do UNIX (AIX,
uma implementação IBM do UNIX) e OS / 390 UNIX. Você achará essas comparações úteis quando vir como os conceitos e
propriedades familiares do MVS são implementados no OS / 390 com UNIX System Services.
Página 271

Tabela 15.2 Comandos TSO para copiar arquivos de e para HFS para MVS

COMANDO TSO DESCRIÇÃO


OPUT Coloca (copia) um conjunto de dados sequenciais MVS ou membro do conjunto de dados
particionado no sistema de arquivos. Você pode especificar dados de texto ou binários.

OPUTX Coloca (copia) um conjunto de dados sequencial, um membro do conjunto de dados,


um conjunto de dados particionado MVS ou um PDSE em um diretório HFS. Você
pode especificar texto ou dados binários, selecionar conversão de página de código
para dados de byte único, especificar uma cópia para nomes de arquivos em
minúsculas e anexar um sufixo aos nomes dos membros quando eles se tornarem
nomes de arquivos. OPUTX é um REXX EXEC que invoca OPUT.

OGET Obtém um arquivo HFS e o copia em um conjunto de dados sequencial MVS ou


membro do conjunto de dados particionado. Você pode especificar dados de texto ou

binários e selecionar a conversão de página de código para dados de byte único.

OGETX Obtém um arquivo ou diretório HFS e o copia em um conjunto de dados particionado


MVS, PDSE ou conjunto de dados sequencial. Você pode especificar texto ou dados
binários, selecionar a conversão de página de código para dados de byte único, permitir
uma cópia de nomes de arquivos em minúsculas e excluir um ou todos os sufixos dos
nomes de arquivos quando eles se tornarem nomes de membros PDS. OGETX é um
REXX EXEC que invoca OGET.

OCOPY Copia dados em qualquer direção entre um conjunto de dados MVS e um arquivo
HFS, usando ddnames. OCOPY também pode copiar dentro do MVS (um conjunto de
dados para outro conjunto de dados) ou dentro do shell (um arquivo para outro
arquivo). OCOPY possui um operando CONVERT para converter dados de byte único
de uma página de código para outra.

Armazenamento virtual.

Armazenamento virtual (ou lógico) é um conceito que, quando implementado por um computador e seu sistema operacional, permite que os
programadores usem uma grande variedade de endereços de memória ou armazenamento para os dados armazenados. O sistema de
computação mapeia os endereços virtuais do programador para endereços reais de armazenamento de hardware. Normalmente, o programador
não precisa se preocupar com a disponibilidade de armazenamento de dados.

Em MVS, cada usuário recebe um espaço de endereço de 2 gigabytes de armazenamento virtual. Parte desse armazenamento contém código
comum para todos os usuários.

Em AIX UNIX, cada usuário obtém tudo o que precisa dentro das restrições do sistema operacional e também a quantidade de
memória real e armazenamento.

Em OS / 390 UNIX, cada usuário obtém um espaço de endereço MVS.


Página 272

Armazenamento de dados

O armazenamento de dados é o que chamamos de implementação física de conjuntos de dados relacionados. Em MVS, armazenamento

de dados é nomeado por conjuntos de dados, às vezes, escrito como uma única palavra conjuntos de dados.

Em AIX UNIX e OS / 390 UNIX, armazenamento de dados é nomeado por arquivos.

Dados de configuração

Os dados de configuração são um conjunto de atributos que descrevem o comportamento geral do sistema, como versões do software do
sistema e detalhes sobre vários serviços do sistema.

Em MVS, os dados de configuração são armazenados como membros do conjunto de dados particionado SYS1.PARMLIB, também chamado de
parmlib. Os dados no parmlib controlam o carregamento inicial do sistema e também como os espaços de endereço MVS se comportam.

Em AIX UNIX, os dados de configuração são armazenados em arquivos no diretório / etc. Além disso, um utilitário chamado Object Data Manager
armazena algumas informações de configuração.

Em OS / 390 UNIX, os dados de configuração também são armazenados no sistema de arquivos / etc.

Bit Bucket

O balde de bits, um termo um tanto caprichoso, é o coletor de dados universal (originalmente, o receptáculo mítico pegava os bits quando eles
caíam do final de um registro durante uma instrução de deslocamento). Diz-se que os dados descartados, perdidos ou destruídos "foram para o
balde de bits".

Em MVS, o balde de bits é o cartão DD DUMMY. Em AIX UNIX e OS / 390 UNIX, o depósito de bits é uma referência de

arquivo chamada / dev / null.

Localizando Dados

Como um usuário ou aplicativo localiza um conjunto de dados ou arquivo? Os arquivos de um sistema operacional são organizados em um sistema de
arquivos. Muitos ambientes, como o UNIX System Services, usam um sistema de arquivos que consiste em uma hierarquia de diretórios. MVS
convencional, no entanto, usa um sistema de arquivos não hierárquico em que grupos de conjuntos de dados são referidos por seus qualificador de
alto nível ( Especificação HLQ).

Em MVS, você localiza conjuntos de dados usando um catálogo ou um diretório PDS. Um catálogo é um conjunto de dados de acesso direto que
contém informações específicas do dispositivo usadas para localizar conjuntos de dados. Um diretório PDS é uma lista de nomes de membros PDS
com deslocamentos dentro do PDS usado para localizar membros.

Os catálogos MVS estão vinculados aos níveis do sistema. Por exemplo, um catálogo de sistema pode conter informações que você usaria para
localizar conjuntos de dados com um qualificador de alto nível em todo o sistema, como SYS1. Um catálogo de usuário pode conter informações
usadas para localizar conjuntos de dados com um qualificador de alto nível do usuário.

Além disso, no MVS, você pode usar o Índice de Volume ( VTOC) para listar os nomes dos conjuntos de dados que residem em um determinado
dispositivo de acesso direto. O VTOC não contém nenhuma informação sobre os membros do PDS.
Em AIX UNIX e OS / 390 UNIX, você navegaria na estrutura de diretório dos arquivos para localizar um arquivo. Se você está
familiarizado com Windows ou DOS, não tem dúvidas
Página 273

O PEGAJOSO
Este é o bit no modo de um arquivo UNIX que, se definido para um executável, diz ao kernel para manter o código
carregado no espaço de troca mesmo após a conclusão da execução, supondo que provavelmente será usado novamente
em breve. Essa otimização de desempenho foi incluída em algumas versões anteriores do UNIX para economizar o
recarregamento de programas usados com frequência, como o shell do disco.

familiarizado com um sistema de arquivos de diretório. Nesse caso, você sabe como navegar pelas estruturas de diretório. Você direciona o shell para
um diretório com um mudar de diretório e emitir comandos para referenciar arquivos armazenados nesse diretório.

No OS / 390 UNIX, o sistema de arquivos do diretório é chamado de sistema de arquivos hierárquico ( HFS). Você lerá expressões como arquivos
HFS (arquivos armazenados em um sistema de arquivos HFS) e programas HFS (programas que acessam arquivos HFS) neste e em qualquer
trabalho que lide com OS / 390 UNIX System Services.

Usando bibliotecas compartilhadas

Bibliotecas compartilhadas permitem que vários usuários acessem recursos úteis do sistema. Cada sistema operacional multiusuário permite que
certas bibliotecas sejam usadas por vários usuários.

Em MVS, o sistema tem uma área comum chamada Área do pacote de links ( LPA) que contém bibliotecas compartilhadas. O LPA está disponível para
cada espaço de endereço em execução no sistema.

Em AIX UNIX e OS / 390 UNIX, bibliotecas compartilhadas são carregadas no sistema sob demanda. Quando o primeiro usuário solicita o uso de uma
biblioteca, o sistema carrega essa biblioteca na memória. Os usuários subsequentes que precisam de acesso à biblioteca podem acessar a cópia
carregada anteriormente. O sistema operacional irá limpar a biblioteca quando nenhum usuário precisar de acesso adicional. Os programas OS / 390
UNIX, com seu sticky bit ativado, também podem acessar bibliotecas compartilhadas no LPA.

Codificação de Dados

Uma codificação de dados é uma representação de dados de baixo nível. Às vezes, o termo seqüência de agrupamento é usado para significar a
representação de codificação de dados. Algumas codificações de dados frequentemente usadas são EBCDIC, ASCII e Unicode.

Traçamos uma distinção entre a codificação dos dados e o formato dos dados. Por exemplo, você pode ter formatos de texto e binários com
o esquema de codificação EBCDIC, bem como com o esquema de codificação ASCII. Embora ambos os esquemas de codificação permitam
a representação de texto, seus padrões de bits subjacentes, ou representações de máquina, são diferentes.

Em MVS, você está livre para usar qualquer codificação de dados que desejar; os aplicativos são responsáveis pelo tratamento dos dados. No
entanto, a maioria dos programas MVS espera que os dados sejam codificados no esquema EBCDIC. Na maior parte, você está seguro ao
supor que os programas MVS usam dados codificados em EBCDIC.
Página 274

Em AIX UNIX, a expectativa é que os programas esperem que a codificação dos dados seja ASCII. Claro, você pode escrever código para usar dados
em EBCDIC ou qualquer codificação. No entanto, é seguro apostar que os programas UNIX esperam uma codificação de dados ASCII.

Em OS / 390 UNIX, você está livre para usar qualquer codificação que desejar. No entanto, os produtos de software de sistema IBM esperam
que os dados sigam a codificação EBCDIC. No entanto, se um programa proveniente de outro ambiente UNIX for portado para OS / 390
UNIX, você pode assumir que o programa a ser portado usa e produz dados em ASCII. Nesse caso, o programa portado em execução no OS
/ 390 UNIX precisa converter a codificação de ASCII para EBCDIC. Você pode usar os utilitários OS / 390 UNIX pax e iconv para converter
dados de ASCII em EBCDIC.

Conjunto de dados e formatos de arquivo.

Alguns sistemas operacionais fornecem conjuntos de dados e formatos de arquivo, que são abstrações que permitem que um aplicativo
acesse dados de maneira consistente e organizada.

Em MVS, conjuntos de dados podem ser formatados em registros lógicos e registros físicos (chamados de blocos). Quando um programa de
aplicativo lê dados de um arquivo, o programa pode ler um registro de dados com uma declaração de E / S de registro. Esta instrução de E / S opera
em um registro lógico. Registros físicos, ou blocos, são o que o MVS usa para armazenar os dados do armazenamento na memória para acesso
subsequente ao programa.

Em geral, UNIX e OS / 390 UNIX não tem nenhum formato de arquivo real ou método de acesso. Os dados contidos em arquivos são
um fluxo de bytes; qualquer organização exigida pelo aplicativo é imposta pelo aplicativo.

Organização do conjunto de dados e métodos de acesso

Em MVS, intimamente ligado ao conceito de formato do conjunto de dados está o conceito de organização do conjunto de dados. Enquanto o formato
de registro / bloco discutido anteriormente descreve como um aplicativo MVS acessa os dados em um conjunto de dados, a organização do conjunto
de dados fornece uma abstração de nível superior para acesso a dados personalizados. Freqüentemente, você ouvirá os programadores MVS usarem
o termo método de acesso intercambiavelmente com a organização do conjunto de dados.

Por exemplo, quando você, o programador MVS, executa I / O em um sequencial conjunto de dados, você está usando o Método Básico de
Acesso Sequencial ( BSAM). Ao realizar I / O em um conjunto de dados VSAM, você está usando o Método de acesso ao armazenamento
virtual. Agora, seu programa COBOL ou PL / I emite as instruções READ FILE INTO e WRITE FILE FROM; essas instruções podem realizar I /
O em conjuntos de dados sequenciais ou VSAM (é claro, conjuntos de dados VSAM têm mais opções de I / O do que conjuntos de dados
BSAM). O código de sistema de baixo nível que acessa os dados é completamente oculto do programa.

UNIX e OS / 390 UNIX não tem nenhum suporte nativo para métodos de acesso per se. A organização e o acesso aos arquivos são de
responsabilidade do aplicativo. No entanto, a IBM tem utilitários que você pode usar para copiar conjuntos de dados MVS para conjuntos de dados
HFS para permitir o acesso por programas HFS.
Página 275

Sensibilidade ao Caso

Vindo do mundo MVS, onde tudo geralmente é codificado em maiúsculas, você pode ter alguns problemas ao codificar no mundo do OS /
390 UNIX. Embora você, o programador MVS, possa codificar seus programas e emitir comandos TSO em maiúsculas, o MVS se preocupa
pouco com a caixa do seu código. Algumas exceções surgem quando você codifica JCL e parâmetros de tempo de execução codificados
em uma placa EXEC PGM = JCL.

Dentro UNIX e OS / 390 UNIX, a diferenciação de maiúsculas e minúsculas governa o poleiro; quase todos os comandos, nomes de arquivos e
construções de linguagem de programação diferenciam maiúsculas de minúsculas.

Linguagens de programação suportadas

Na maior parte, quase todas as linguagens de programação usadas são suportadas no MVS com uma exceção gritante, que é Java. Dito
isso, você, o programador MVS, poderia codificar Java no OS / 390 UNIX para acessar dados residentes em um sistema MVS. O
programador UNIX tem um compilador para quase todas as linguagens de programação imagináveis.

Os sistemas operacionais geralmente têm uma ou mais linguagens de script. Você usa uma linguagem de script para controlar os recursos do sistema,
como arquivos ou conjuntos de dados, e para invocar executáveis. Um script é uma curiosa combinação de instruções do sistema operacional que
podem manipular arquivos ou conjuntos de dados combinados com construções de linguagem de programação. Um exemplo de script típico pode ser
um programa que verifica a existência de um ou mais arquivos; se os arquivos existirem, o script executará o programa X, ou então o script executará o
programa Y. As linguagens de script são geralmente interpretadas e não são aplicativos que exigem muito do processador.

Em MVS, você pode codificar TSO CLISTS, que poucas pessoas usam atualmente, e REXX EXECs. Em AIX UNIX, você

tem acesso a todas as linguagens de script de MVS e AIX UNIX.

Ajuda online

Em MVS, você pode emitir o comando TSO HELP ou, se estiver no ambiente ISPF, pode pressionar PF1 para obter ajuda online. Você pode
configurar o ISPF para invocar o Book Manager para fazer referência à linguagem de programação IBM e aos manuais de software do sistema.

Em AIX UNIX e OS / 390 UNIX, a homem comando fornece ajuda para comandos de shell. Além disso, você pode obter ajuda online por meio
da GUI instalada. O OS / 390 UNIX possui uma interface com o Book Manager por meio do comando OHELP.

Código que executa o trabalho

Dentro MVS, a entidade elementar que executa o trabalho é chamada de tarefa. MVS representa tarefas usando um Bloco de Controle de Tarefas. O
sistema operacional MVS oferece suporte a várias tarefas em cada espaço de endereço. Os aplicativos escritos em COBOL e PL / I geralmente
correspondem a uma única tarefa.
Página 276

Dentro AIX UNIX e OS / 390 UNIX, a entidade elementar que executa o trabalho é chamada de thread. Um aplicativo UNIX (AIX ou OS / 390)
inicia uma unidade de trabalho chamada processo, que pode conter ou criar vários encadeamentos. Além disso, um processo AIX UNIX ou OS
/ 390 UNIX pode criar processos adicionais que, por sua vez, podem criar um ou mais encadeamentos.

Dentro MVS, uma tarefa de longa execução é conhecida como tarefa iniciada. Uma tarefa iniciada pode ser executada enquanto o sistema estiver
ativo. Dentro AIX UNIX e OS / 390 UNIX, uma tarefa de longa execução é chamada de daemon; uma tarefa de longa execução no OS / 390
também pode ser uma tarefa iniciada.

Ordem de pesquisa do programa.

A ordem de pesquisa do programa é a lista de bibliotecas e caminhos que o sistema pesquisa para localizar programas executáveis.

Dentro MVS, o sistema pesquisa os seguintes conjuntos de dados particionados (bibliotecas) alocados para os seguintes DDNAMES
na ordem listada: TASKLIB, STEPLIB, JOBLIB, LPALST e LNKLST.

Dentro AIX UNIX, o sistema pesquisa os diretórios especificados na variável de ambiente PATH do usuário. O valor de PATH é uma lista
de diretórios; o arquivo de programa encontrado no primeiro diretório listado no PATH é o arquivo carregado e executado. Para bibliotecas
de link dinâmico, o sistema pesquisa uma lista de diretórios especificados na variável de ambiente LIBPATH.

Dentro OS390 UNIX, o sistema pesquisa os diretórios especificados na variável PATH do usuário. No entanto, se o sticky bit do programa
estiver ativado, a ordem de pesquisa seguirá as regras anteriores para MVS.

Atribuição de armazenamento em disco

Dentro MVS, um usuário atribui armazenamento alocando conjuntos de dados. A alocação do conjunto de dados pode ser feita em primeiro plano
usando o comando TSO ALLOCATE ou usando os utilitários do conjunto de dados no ISPF (que é um front end para o comando ALLOCATE). Os
usuários do MVS podem alocar espaço em disco em lote usando instruções DD em um fluxo de trabalho JCL.

Dentro AIX UNIX, um administrador do sistema atribui arquivos a volumes de disco lógico que são mapeados para volumes de disco físico.

Dentro OS / 390 UNIX, um administrador do sistema aloca arquivos HFS. Além disso, um usuário pode executar um script de shell, REXX Exec ou um
programa que cria arquivos HFS a partir de dados do programa.

ProblemDetermination

Dentro MVS, você pode usar o TSO TEST, um depurador orientado a linha ou a ferramenta de depuração IBM. MVS suporta códigos ABEND e
pode produzir um dump do sistema mediante solicitação. Vários terceiros oferecem ferramentas de depuração de nível de origem.

Dentro AIX UNIX, você tem acesso a uma variedade de ferramentas de depuração de programação, bem como rotinas de sistema, como
errpt, para relatar erros do sistema; você também pode solicitar um despejo de memória.
Página 277

Dentro OS / 390 UNIX, você tem os mesmos recursos do AIX UNIX à sua disposição. Você também pode usar o produto Visual Age, que
fornece um depurador de nível de origem para várias linguagens de programação, incluindo Java.

Execução Online

Dentro MVS, você pode efetuar logon no TSO fornecendo um ID de usuário e uma senha. Ao fazer logon, o sistema envia uma tarefa iniciada
que representa o usuário conectado ao sistema. Cada usuário TSO pode ter apenas uma sessão TSO ativa em um determinado momento.

O usuário MVS executa um programa em primeiro plano digitando o nome do programa no prompt "pronto" do TSO ou por meio da opção 6
do ISPF. O sistema pesquisa as bibliotecas de programa alocadas aos DDNAMEs especificados na seção "Ordem de pesquisa de programa",
a menos que o usuário tenha especificado o nome do conjunto de dados totalmente qualificado.

Dentro AIX UNIX, os usuários fazem logon no sistema e executam scripts de shell. Um usuário do AIX pode emitir um comando rlogin ou telnet
para se conectar a outro sistema operacional. Além disso, o usuário AIX pode inserir o nome de um script ou executável. Os usuários do AIX
UNIX podem ter várias sessões de login simultaneamente.

Dentro OS / 390 UNIX, um usuário pode fazer logon com os comandos rlogin e telnet e, em seguida, fazer logon no TSO (MVS) e executar o
comando OMVS para ter uma sessão MVS. Assim como no AIX UNIXI, os usuários do OS / 390 UNIX podem inserir o nome de um script de shell ou
programa executável.
Y

Execução em segundo plano


FL

Dentro MVS, a execução do programa em segundo plano é feita enviando um arquivo em lote, contendo JCL, para o ambiente em lote.
A M
TE

Dentro AIX UNIX e OS / 390 UNIX, comandos shell prefixados com um e comercial (&) e comando cron serão executados em lote.
No OS / 390 UNIX, o comando BPXBATCH permite ao usuário enviar tarefas em lote e executar programas HFS em fluxos de
tarefas JCL.

Programas de agendamento para execução

Dentro MVS, um planejador de job pode usar vários utilitários do sistema, como JES ou o System Automation for OS / 390.

Dentro AIX UNIX e OS / 390 UNIX, o utilitário cron agenda programas que são executados em segundo plano. Também existem soluções de terceiros
para daemons de segundo plano do UNIX.

Criação de programas

o MVS o programador pode criar um programa usando um compilador com um vinculador ou fichário. A saída do fichário é um módulo de
carregamento - um membro de um PDS que contém o código executável.

Dentro AIX UNIX ou OS / 390 UNIX, o programador usa um compilador e um vinculador. Normalmente, um script especial chamado makefile é usado
para garantir a existência dos arquivos necessários, chamar o compilador e, se o código de retorno for satisfatório, emitir uma chamada para um
vinculador.

Team-Fly ®
Página 278

Editando Dados

Dentro MVS, a principal ferramenta usada por você, o programador MVS, para editar dados e código de programa é o editor ISPF.

Dentro AIX UNIX, você usaria um dos vários editores, como vi, ed ou emacs.

Dentro OS / 390 UNIX, você pode usar os editores AIX UNIX ou a ferramenta de edição. Você pode usar um editor baseado em GUI chamado
nedit, que também converte de ASCII para EBCDIC.

Ver e cancelar trabalhos

Dentro MVS, você pode usar SDSF ou um produto de terceiros, como IOF, para visualizar, eliminar e cancelar trabalhos. Essas ferramentas permitem
que você interrompa um trabalho em execução ou remova um trabalho com execução pendente da fila de trabalhos.

Dentro AIX UNIX, você pode usar o shell do ps para visualizar e eliminar threads. Para interromper um processo de execução online, você
usa a combinação de teclas Control-C.

Dentro OS / 390 UNIX, você tem os recursos disponíveis para MVS e AIX UNIX para visualizar e interromper threads.

Em suma

OS / 390 UNIX System Services reúne dois sistemas operacionais poderosos. Esses serviços de sistema permitem que você, o programador de
mainframe, acesse conjuntos de dados MVS de um ambiente UNIX. Visto que você deve compilar e executar seus programas Java de dentro
deste ambiente, você precisa de ferramentas para acessar conjuntos de dados de mainframe (MVS); O UNIX System Services oferece exatamente
isso.
Página 279

CAPÍTULO 16
Java e MVS Batch.
Mesmo nos dias e na era dos aplicativos GUI, sempre haverá a necessidade de executar programas em lote. Felizmente, executar
programas Java em lote não é difícil. Tudo que você precisa fazer é proteger a sua loja
Biblioteca de classes Java ( JCL) e modifique o fluxo de tarefas fornecendo seu programa Java, referências de conjunto de dados e outros
parâmetros de ambiente e tempo de execução.

Este capítulo fornece o que você, o programador de mainframe, precisa saber para executar um programa Java em lote.
Você lerá sobre as três opções disponíveis para a execução de programas Java em lote. Para cada opção, você lerá sobre os
parâmetros que precisará fornecer ao fluxo de trabalho. Ao longo do caminho, você verá quais variáveis de ambiente definir
e como defini-las no ambiente de lote do OS / 390.

Executando Java em lote

Três opções estão disponíveis para executar programas Java em lote:

1 Você pode executar o Armazenamento Virtual Múltiplo ( MVS) programa utilitário BPXBATCH. BXBATCH permite que você execute
um comando OS / 390 UNIX ou executável no MVS.

2 Se você compilar e vincular seu programa Java ao compilador de alto desempenho, poderá executar o programa
vinculado como faria com um programa COBOL ou PL / I.
Página 280

3 - Você pode executar programas Java como uma tarefa iniciada usando uma das duas opções descritas anteriormente. As próximas seções

examinam essas opções em detalhes, começando com o uso de BPXBATCH.

Executando programas Java com BPXBATCH

Como o interpretador Java é executado nos serviços do sistema OS / 390 UNIX, você pode usar o utilitário BPXBATCH para carregar o
interpretador Java e passar o nome de um programa Java. Você também pode fornecer parâmetros de tempo de execução para o método main ()
do programa Java ao mesmo tempo.

A Listagem 16.1 mostra o JCL que carrega BPXBATCH e executa um programa Java.

Esta lista é um mínimo absoluto. BPXBATCH grava em Armazenamento hierárquico de arquivos ( HFS) conjuntos de dados apenas. (BPXBATCH pode
ler a partir de conjuntos de dados MVS.) Portanto, você não pode usar SYSOUT = * para os fluxos de saída padrão. Seguem algumas informações
adicionais sobre este trabalho.

O parâmetro passado para BPXBATCH

Esta declaração na Listagem 16.1 é o parâmetro passado para BPXBATCH:

// PARM = 'SH javaprog parm1 parm2'

// WHOIAMA JOB (MYACCTINFO), CLASS = X, MSGCLASS = X, MSGLEVEL = (1,1), //


REGION = 16M, NOTIFY = & SYSUID
// *
// * Execute o programa Java com BPXBATCH // *

// RUNJAVA EXEC PGM = BPXBATCH,


// PARM = 'SH javaprog parm1 parm2' // STDOUT DD PATH = '/ u
/ homedir / stdout', //
PATHOPTS = (OCREAT, OTRUNC, OWRONLY),
// PATHMODE = SIRWXU
// STDERR DD PATH = '/ u / homedir / stderr', //
PATHOPTS = (OCREAT, OTRUNC, OWRONLY),
// PATHMODE = SIRWXU
// STDENV DD PATH = '/ u / homedir / myenvfil' / *

Listagem 16.1 Executando Java com BPXBATCH.


Página 281

ATENÇÃO
Se você errar ou omitir os DDNames, STDOUT ou STDERR em seu trabalho BPXBATCH,
BPXBATCH encaminhará a saída para a saída nula e invisível, / dev / null.

O parâmetro é o shell de comando padrão do UNIX, SH, com o tempo de execução Java, o nome de um programa Java e quaisquer
parâmetros que o programa Java requer. O programa Java conteria as seguintes construções:

public class javaprog {


public static void main (String [] args) {
// args [0] é igual a parm1, args [1] é igual a parm2}

Você não está limitado a passar o interpretador de tempo de execução Java para o shell de comando. Você pode passar um script que chama o
interpretador de tempo de execução Java. No script, você pode definir variáveis de ambiente ou usar a lógica do programa para executar diferentes
programas Java.

Arquivos padrão usados por BPXBATCH

Os arquivos usados por BPXBATCH são STDOUT, STDERR e STDENV.

STDOUT é o arquivo de saída padrão que corresponde a System.out.print em seu programa Java.

STDERR é a saída de erro em que o Java runtime grava rastreamentos de pilha semelhantes a diagnósticos. STDERR, se não
especificado em seu fluxo de tarefas, assume como padrão o conjunto de dados referenciado por STDOUT.

STDENV é um arquivo que contém opções para o tempo de execução Java, como uma configuração de caminho de classe.
STDENV não é necessário e geralmente é "simulado" (DD DUMMY).

A Listagem 16.1 mostra esses arquivos armazenados em um diretório inicial, mas os arquivos podem ser armazenados em qualquer lugar que o
usuário tenha permissão para salvá-los.

Um fluxo de trabalho mais robusto para executar BPXBATCH

No mundo real, um trabalho em lote incluiria uma etapa IEFBR14 no início e, talvez, uma etapa no final para limpar ou mover a
saída para outro local. A Listagem 16.2 mostra o trabalho BPXBATCH incluindo essas etapas.

A etapa DELETE faz o que você pensa que faz; ele executa o IEFBR14 para excluir quaisquer conjuntos de dados existentes que não deveriam
existir para este trabalho. Os conjuntos de dados em questão são a saída padrão e os conjuntos de dados de erro padrão.
Página 282

// WHOIAMA JOB (MYACCTINFO), CLASS = X, MSGCLASS = X, MSGLEVEL = (1,1), //


REGION = 16M, NOTIFY = & SYSUID
// *
// * Execute o programa Java com BPXBATCH // *

//EXCLUIR EXEC PGM = IEFBR14


// STDOUT DD PATH = '/ u / homedir / stdjava.out',
// PATHOPTS = (OCREAT, OWRONLY),
// PATHMODE = SIRWXU,
// PATHDISP = (DELETE)
// STDERR DD PATH = '/ u / homedir / stdjava.err',
// PATHOPTS = (OCREAT, OWRONLY),
// PATHMODE = SIRWXU,
// PATHDISP = (DELETE)
// *
// RUNJAVA EXEC PGM = BPXBATCH,
// PARM = 'SH javaprog parm1 parm2'
// SYSPRINT DD SYSOUT = *
// SYSOUT DD SYSOUT = *
// STDOUT DD PATH = '/ u / homedir / stdjava.out',
// PATHOPTS = (OWRONLY, OCREAT, OTRUNC),
// PATHMODE = SIRWXU
// STDERR DD PATH = '/ u / homedir / stdjava.err',
// PATHOPTS = (OWRONLY, OCREAT, OTRUNC),
// PATHMODE = SIRWXU
// STDENV DD MANEQUIM

// *
//COPIAR EXEC PGM = IKJEFTO1, DYNAMNBR = 300, COND = EVEN
// SYSTSPRT DD SYSOUT = *
// HFSOUT DD PATH = '/ u / homedir / stdjava.out'
// HFSERR DD PATH = '/ u / homedir / stdjava.err'
// * Shop - s DCBs específicos para arquivos de saída seguem
// STDOUTL DD SYSOUT = *, DCB = (RECFM = VB, LRECL = 133, BLKSIZE = 137)
// STDERRL DD SYSOUT = *, DCB = (RECFM = VB, LRECL = 133, BLKSIZE = 137)
// SYSPRINT DD SYSOUT = *
// SYSTSIN DD DATA
ocopy indd (HFSOUT) outdd (STDOUTL)
ocopy indd (HFSERR) outdd (STDERRL)
/*

Listagem 16.2 Executando Java com redirecionamento de saída.

A etapa RUNJAVA é igual à etapa RUNJAVA na Listagem 16.1.

A etapa COPYOUT chama o TSO em segundo plano e executa o comando OCOPY para copiar os dados dos conjuntos de dados HFS para um
conjunto de dados SYSOUT. Você pode facilmente codificar um
Página 283

MVS sequencial ou membro de um conjunto de dados particionado (PDS) para o destino do comando OCOPY.

Se você está se perguntando por que a etapa COPYOUT está lá, infelizmente, é porque nenhuma representação de conjunto de dados
temporário simbólico está disponível para conjuntos de dados HFS. Você não pode codificar && TEMP para conjuntos de dados STDOUT ou
STDERR. Além disso, conforme mencionado anteriormente, BPXBATCH grava apenas em conjuntos de dados HFS.

Compilando um programa Java em lote

Como BPXBATCH executa programas OS / 390 UNIX em lote e o compilador de bytecode Java é um programa OS / 390 UNIX,
você pode compilar programas Java em lote. A JCL é quase idêntica à da execução do programa. A única diferença é que em
vez de passar o Java para o shell de comando do UNIX, você passa o Javac comando. Você pode usar o trabalho mostrado na
Listagem 16.2 e alterar o cartão EXEC da etapa RUNJAVA para passar javac para compilar programas Java em lote.

Conforme mencionado anteriormente, você pode passar um script de shell para o processador de comandos para definir variáveis de ambiente e
opções do compilador.

Definindo Variáveis de Ambiente em Lote

Quer você compile ou execute programas Java em uma estação de trabalho ou em lote, você precisará estabelecer o ambiente adequado. Um método
conveniente para estabelecer seu ambiente Java para compilações e execuções em lote é definir suas variáveis de ambiente Java em um conjunto de
dados. O conjunto de dados é conhecido pelos trabalhos BPXBATCH como DDNAME STDENV. O conjunto de dados referenciado por DDNAME
STDENV pode ser um conjunto de dados HFS ou MVS (lembre-se de que BPXBATCH pode ler de conjuntos de dados MVS).

BPXBATCH também aceita configurações de variáveis de ambiente por meio de uma instrução DD instream. Por exemplo,

// SYSENV DD *
PATH == / usr / lpp / java12: / u / myhome / myprog
/*

Normalmente, você precisa definir as variáveis de ambiente PATH e CLASSPATH. É uma boa prática definir a variável JAVA_HOME
também. Codifique os valores para essas variáveis de ambiente em um
não numerado arquivo de texto (desligue a numeração de linha durante a edição) como pares nome-valor:
environmentvarname = varvalue. Veja como fazer atribuições de variáveis para algumas variáveis de ambiente Java
no OS / 390.

Atribuindo Variáveis de Ambiente no OS / 390

Aqui estão algumas regras básicas a serem seguidas ao atribuir variáveis de ambiente Java no OS / 390.

Use dois pontos para separar os nomes dos caminhos.

Exemplo: PATH = / usr / lpp / java12: / u / myhome / myprogs


Página 284

Isso define a variável de ambiente PATH para os dois diretórios (caminhos) separados por dois pontos. Claro, você pode, e
geralmente irá, usar mais de dois caminhos em uma atribuição de variável de ambiente.

Os nomes das variáveis de ambiente são referenciados por um $ no lado direito de uma instrução de atribuição.

Exemplo: CLASSPATH = $ CLASSPATH: / u / myhome / myprogs

Isso define o CLASSPATH para a atribuição CLASSPATH existente e o caminho após os dois pontos. Atribuir uma variável de
ambiente com seu valor existente mais caminhos adicionais é uma técnica comum para definir variáveis de ambiente.

A maioria das variáveis de ambiente lidam com caminhos. No entanto, vários permitem o uso de arquivos JAR e ZIP em conjunto com
caminhos.

Exemplo: CLASSPATH = $ CLASSPATH: / u / myhome / myprogs: /libs/tools.jar

Isso configura a atribuição CLASSPATH para o CLASSPATH atual, o caminho / u / myhome / myprogs e o arquivo JAR / libs /
tools / jar.

Você pode usar os valores de algumas variáveis de ambiente existentes como caminhos parciais ao atribuir outras variáveis de
ambiente.

Exemplo: PATH = / usr / lpp / java12: / u / myhome / myprogs: $ JAVA_HOME / lib

Isso usa o valor de JAVA_HOME para definir um caminho que define parcialmente a variável de ambiente PATH. Usar uma variável
de ambiente existente como parte de um caminho funciona quando a variável de ambiente existente pode se referir a apenas um
caminho, como JAVA_HOME.

Produtos diferentes usam conjuntos diferentes de variáveis de ambiente. Por exemplo, o Sistema de controle de informações do
cliente ( CICS) possui um conjunto de variáveis de ambiente que é exclusivo para usar Java e o gateway CICS. Usar Java com o
DB2 requer ainda outro conjunto de variáveis de ambiente.

Execução de programas Java compilados e vinculados

Executar programas Java compilados e vinculados ao Compilador Java de alto desempenho ( HPJC) pode ser feito de duas
maneiras. Se você vinculou seu módulo a um arquivo HFS, usará BPXBATCH. Se você vinculou seu módulo a um conjunto de dados
MVS, chamará o módulo diretamente com uma instrução EXEC PGM = JCL.

A Listagem 16.3 mostra um fluxo de trabalho de amostra que executa um executável Java compilado e vinculado a um conjunto de dados HFS.

Você terá que codificar uma instrução STEPLIB DD em seu JCL para trazer o HPJC e o IBM
Ambiente de linguagem ( LE) bibliotecas de tempo de execução. Os nomes dos conjuntos de dados mostrados aqui provavelmente não são iguais
aos usados na instalação. Observe a ausência da instrução STDENV DD; você não precisa de um ao executar executáveis Java.

A Listagem 16.4 mostra um fluxo de trabalho de amostra que executa um executável Java compilado e vinculado a um conjunto de dados MVS.
Sem surpresas aqui. Todos os arquivos referenciados no programa precisam de instruções DD, é claro.
Página 285

// WHOIAMA JOB (MYACCTINFO), CLASS = X, MSGCLASS = X, MSGLEVEL = (1,1), //


REGION = 16M, NOTIFY = & SYSUID
// *
// * Execute o programa Java executável com BPXBATCH // *

// RUNJAVA EXEC PGM = BPXBATCH,


// PARM = 'SH / u / homedir / javaprgs / ajavaprg'
// STEPLIB DD DSN = HPJ.SHPJMOD, DISP = SHR
// DD DSN = HPJ.SHPOMOD, DISP = SHR
// DD DSN = CEE.SCEERUN, DISP = SHR
// STDOUT DD PATH = '/ u / homedir / stdout',
// PATHOPTS = (OCREAT, OTRUNC, OWRONLY),
// PATHMODE = SIRWXU
// STDERR DD PATH = '/ u / homedir / stderr',
// PATHOPTS = (OCREAT, OTRUNC, OWRONLY),
// PATHMODE = SIRWXU

Listagem 16.3 Executando um executável Java com BPXBATCH.

// WHOIAMA JOB (MYACCTINFO), CLASS = X, MSGCLASS = X, MSGLEVEL = (1,1), //


REGION = 16M, NOTIFY = & SYSUID
// *
// * Executar um programa Java executável do MVS Load Module DSN // *

// RUNJAVA EXEC PGM = AJAVAPRG,


// STEPLIB DD DSN = MYHLQ.JAVA.LOAD, DISP = SHR
// DD DSN = HPJ.SHPJMOD, DISP = SHR
// DD DSN = HPJ.SHPOMOD, DISP = SHR
// DD DSN = CEE.SCEERUN, DISP = SHR
// STDOUT DD SYSOUT = *
// STDERR DD SYSOUT = *

Listagem 16.4 Executando um executável Java como um módulo de carregamento MVS.

Executar programas Java como tarefas iniciadas

Você pode executar um aplicativo Java como uma tarefa iniciada do MVS. Observe que o aplicativo Java é executado sob a autorização do ID do
usuário da tarefa iniciada, o que permite designar autoridades específicas a um aplicativo do servidor Java. Além disso, seu servidor Java é
executado em um ambiente familiar e "amigável ao operador" e pode ser facilmente iniciado, monitorado e cancelado a partir de um console MVS.

O JCL para a tarefa iniciada é o mesmo que para a lista de tarefas em lote mostrada na Listagem 16.1.
Página 286

Em suma

Felizmente, você pode ver que executar um programa Java (ou compilar um) em lote não é difícil. Se você não estiver familiarizado com a
organização do conjunto de dados HFS, as listagens JCL incluídas neste capítulo podem parecer um pouco estranhas. No entanto, se os
conjuntos de dados usados por seus programas Java são MVS ou HFS, tudo se resume aos mesmos conceitos: O ambiente de lote OS / 390
requer que certos DDNames sejam conhecidos por programas em execução. Apenas torne esses DDNames conhecidos e o ambiente fará o
resto.

Você também leu sobre como definir variáveis de ambiente. Os programadores PL / I se sentem confortáveis com a configuração de variáveis de
ambiente de tempo de execução; Os programadores COBOL provavelmente se sentem menos confortáveis. Na maioria das vezes, essas variáveis de
ambiente serão definidas em arquivos de script ou em SYSIN DD * JCL, portanto, você provavelmente pode escapar sem ser um especialista em
variáveis de ambiente Java por enquanto.

Y
FL
A M
TE

Team-Fly ®
Página 287

CAPÍTULO 17
E / S de registro Java usando o pacote JRIO.

Como programador de mainframe, você gasta muito do seu tempo escrevendo e mantendo programas que executam E / S de registro.
Há um pequeno problema - o Java não tem suporte para E / S de registro. Dizemos "leve" porque a natureza extensível do Java torna
possível escrever programas Java que realizam E / S de registro com uma pequena ajuda de nossos amigos da IBM. A ajuda vem na
forma de um pacote personalizado chamado JRIO, para Java Record Input e Output, que é o assunto deste capítulo.

Aqui, cobriremos os detalhes do uso do pacote JRIO da IBM. Você aprenderá onde obter uma cópia dos arquivos de classe
necessários e verá alguns códigos usando as classes JRIO para realizar tarefas comuns e úteis.

O que é JRIO?

JRIO é uma biblioteca de extensão que permite que aplicativos Java acessem sistemas de arquivos OS / 390 tradicionais, além do Sistema
de arquivos hierárquico ( HFS). O JRIO possibilita que aplicativos Java acessem registros dentro de arquivos e sistemas de arquivos por
meio de métodos nativos. Você não precisa escrever nenhum código usando o Interface Nativa Java ( JNI) para usar o JRIO.

Lembre-se de que java.io, o pacote de E / S básico da Sun, fornece orientado por byte ou orientado para o campo acesso aos arquivos. JRIO é uma
biblioteca de classes que fornece orientado para o registro Acesso. JRIO usa interfaces separadas para representar arquivos e diretórios. O JRIO
também fornece interfaces e classes para operações orientadas a registros binários e suporta campos apenas de texto em arquivos de registros
binários.
Página 288

O JRIO fornece métodos de acesso para ler, escrever ou atualizar BSAM sequencial, acesso aleatório e arquivos de acesso por chave que são
suportados. Além disso, os programas Java que usam JRIO podem acessar conjuntos de dados VSAM, um diretório PDS e o catálogo do sistema.

O JRIO permite que você acesse conjuntos de dados VSAM em ordem de sequência de entrada, acessando uma chave primária exclusiva ou
acessando um índice alternativo. As operações de diretório e catálogo suportadas pelo JRIO estão listando os qualificadores de alto nível no catálogo
do sistema, listando os conjuntos de dados começando com um qualificador e listando os membros de um PDS.

JRIO é parte do Java para OS / 390. Você pode baixar o JDK para OS / 390 em
www.s390.ibm.com/java . O JRIO é instalado quando você instala o JDK baixado.

Conteúdo do pacote JRIO

Esta seção descreve a lista de interfaces, classes relacionadas, constantes e exceções disponíveis no pacote JRIO com.ibm.recordio. Você
pode dividir os elementos no JRIO entre os elementos que lidam com arquivos HFS, arquivos OS / 390 não VSAM e arquivos OS / 390
VSAM.

Vamos dar uma olhada nas interfaces fornecidas pelo JRIO.

Interfaces JRIO

O JRIO fornece interfaces Java que permitem acessar informações de diretório, bem como realizar IO de registro em estruturas
de dados IBM comuns. As seções a seguir fornecem detalhes sobre as interfaces JRIO.

IDiretório

o IDiretório interface define as operações em um diretório, como obter atributos, listar conteúdos, criar novos diretórios e excluir ou
renomear diretórios existentes. A classe relacionada, Directory, atua como uma classe manipuladora quando as instâncias são criadas
usando o operador new (). A classe Directory também fornece vários métodos de fábrica estáticos getInstanceOf () que retornam
referências a classes concretas.

O QUE É UMA FÁBRICA?

UMA fábrica é um objeto que cria ou localiza outros objetos. Os objetos são geralmente criados com o
operador new, que é análogo a ter um método estático (fábrica) no objeto de classe do objeto.
Freqüentemente, esses métodos estáticos são nomeados
getInstanceOf () ou newInstance (). Isso permite que os usuários de uma interface obtenham instâncias de
objetos que implementam a interface desejada, sem saber ou especificar a classe concreta do objeto. Isso
permite um tempo de execução escolha de qual implementação usar, com base nas particularidades do
ambiente, entradas do usuário ou outros fatores.
Página 289

IRecordFile

o IRecordFile interface define as operações em um arquivo, como obter e definir atributos, criar novos arquivos e excluir ou renomear
arquivos existentes. A classe relacionada, RecordFile, atua como uma classe de manipulação quando as instâncias são criadas usando o
operador new (). A classe RecordFile também fornece vários métodos de fábrica estáticos getInstanceOf () que retornam referências a
classes concretas.

Eu gravo

A interface IRecord define as operações na classe Record, que é um wrapper para uma matriz de bytes. Um objeto do tipo byte[] é a forma
mais simples de um registro. A classe Record fornece navegação de campo por nome ou índice de campo e também fornece conversão
de tipo em nível de campo. Os fluxos de registro e arquivos de acesso aleatório e com chave suportam leitura e gravação de IRecords.
IRecord e Record fazem parte do pacote IBM VisualAge for Java Record Framework com.ibm.record. Este pacote está incluído no Java
para OS / 390.

Embora os fluxos de registro e os arquivos de acesso aleatório e de acesso por chave suportem a leitura e gravação de
matrizes de bytes, o aplicativo deve lidar com qualquer navegação de campo, conversão de tipo e exceções decorrentes de
conversões inválidas.

IFileInputRecordStream

o IFileInputRecordStream interface estende a interface InputRecordStream. Ambas as interfaces definem as operações de entrada de arquivo
sequencial para arquivos de registro, basicamente ler e fechar. A classe relacionada, FileInputRecordStream, atua como uma classe manipuladora
quando as instâncias são criadas usando o operador new ().

FileInputRecordStream também fornece vários métodos de fábrica estáticos getInstanceOf () que retornam referências a
classes concretas. FileInputRecordStream estende a classe abstrata Input Record Stream. FileInputRecordStream é
semelhante a FileInputStream no pacote java.io. InputRecordStream é semelhante a InputStream no pacote java.io.

IFileOutputRecordStream

A interface IFileOutputRecordStream estende a interface IOutputRecordStream. Ambas as interfaces definem as operações de saída de arquivo
sequencial para arquivos de registro, incluindo gravação, liberação e fechamento. A classe relacionada, FileOutputRecordStream, atua como uma
classe de manipulação quando as instâncias são criadas usando o operador new ().

FileOutputRecordStream também fornece vários métodos de fábrica estáticos getInstanceOf () que retornam referências a
classes concretas. FileOutputRecordStream estende a classe abstrata OutputRecordStream. FileOutputRecordStream é
semelhante a FileOutputStream no pacote java.io. OutputRecordStream é semelhante a OutputStream no pacote java.io.
Página 290

IRandomAccessRecordFile.

o IRandomAccessRecordFile interface define as operações de acesso aleatório para arquivos de registro, como posicionamento relativo,
busca, leitura, gravação e fechamento. A classe relacionada,
RandomAccessRecordFile, atua como uma classe de manipulação quando as instâncias são criadas usando o operador new ().
RandomAccessRecordFile também fornece vários métodos de fábrica estáticos getInstanceOf (...) Que retornam referências a classes concretas.

IKeyedAccessRecordFile

o IKeyedAccessRecordFile interface define as operações de acesso com chave para arquivos de registro, como posicionamento por chave, leitura,
gravação, atualização, exclusão de registros, obtenção de arquivos de índice relacionados e fechamento. A classe relacionada,
KeyedAccessRecordFile, atua como uma classe manipuladora quando as instâncias são criadas usando o operador new ().

KeyedAccessRecordFile também fornece vários métodos de fábrica estáticos getInstanceOf (...) Que retornam referências a classes
concretas. KeyedAccessRecordFile é um tanto semelhante a RandomAccessFile no pacote java.io, mas introduz novas funções de
posicionamento relacionadas a teclas e a capacidade de excluir registros logicamente. Sua função é um tanto semelhante à classe
java.util.Properties. Ele permite armazenar e recuperar registros usando uma chave.

IKeyDescriptor e IKey

o IKey interface define as operações para uma chave, como obter os bytes da chave e comparar o valor desta chave com o valor de outra
chave. A classe relacionada, Key, é um invólucro simples para uma matriz de bytes (ou seja, não é uma classe manipuladora). Ele fornece um
método utilitário, denominado getKey (), para criar um objeto key wrapper a partir de bytes extraídos de outra chave ou de um registro quando
você fornece o deslocamento e o comprimento dos bytes da chave no registro.

Um objeto da classe Key não contém seus deslocamentos ou seu comprimento. No entanto, um objeto da classe KeyDescriptor contém os atributos
de deslocamento e comprimento necessários para extrair a chave de um registro, mas não contém os dados reais da chave.

A próxima seção contém informações sobre as constantes usadas pelas classes no JRIO.

Constantes JRIO

Constantes JRIO são variáveis estáticas na interface com.ibm.recordio.IConstants. A Tabela 17.1 mostra as constantes JRIO usadas
pelas classes que implementam o Interface IRecord, seus valores, as classes JRIO nas quais você usará essas constantes e uma breve
descrição.

A Tabela 17.2 mostra as constantes JRIO que representam os atributos de arquivo usados pelo listDetailed () método da aula Diretório. ListDetailed
() retorna uma matriz de nomes de arquivo e atributos de arquivo. A classe Directory implementa o IDiretório interface.

A Listagem 17.1 mostra como usar o método listDetailed ().

A próxima seção tem algumas informações sobre exceções lançadas por métodos de classes JRIO.
Página 291

Tabela 17.1 Constantes JRIO usadas em classes que implementam IRecord

NOME DA CONSTANTE VALOR USADO NAS AULAS DESCRIÇÃO

JRIO_DEFAULT_RECORD_FORMAT String = RecordFile Formato de registro de arquivo padrão


"FB" RandomAccessRecordFile

JRIO_DEFAULT_RECORD_LENGTH int = 80 RecordFile LRECL padrão


RandomAccessRecordFile
KeyedAccessRecordFile

JRIO_FIXED_MODE String = RecordFile Representa Fixo Bloqueado


"FB" RECFM
RandomAccessRecordFile

JRIO_MAX_RECORD_LENGTH int = RecordFile O maior LRECL


32760 possível
RandomAccessRecordFile

JRIO_MIN_RECORD_LENGTH int = 1 RecordFile O menor LRECL


possível
RandomAccessRecordFile

JRIO_READ_EOF int = -1 FileInputRecordStream Fim do marcador de arquivo


RandomAccessRecordFile
KeyedAccessRecordFile

JRIO_READ_MODE String = RandomAccessRecordFile Modo somente leitura de acesso

"r" aleatório
KeyedAccessRecordFile

JRIO_READ_WRITE_MODE String = RandomAccessRecordFile Acesso aleatório de leitura e gravação


"rw" modo
KeyedAccessRecordFile

JRIO_VARIABLE_MODE String = RecordFile Representa variável


"vb" RECFM bloqueado
RandomAccessRecordFile
Página 292

Tabela 17.2 Constantes JRIO usadas por listDetailed () do Class Directory

NOME DA CONSTANTE VALOR DESCRIÇÃO

JRIO_DIR_ENTRY_POSIX byte = 0 A entrada do arquivo é um arquivo POSIX

JRIO_DIR_ENTRY_CATALOG byte = 1 A entrada do arquivo veio de um


catálogo

JRIO_DIR_ENTRY_HLQ byte = 2 A entrada do arquivo veio de um qualificador

de alto nível

JRIO_DIR_ENTRY_PDS byte = 3 A entrada do arquivo veio de um diretório


PDS

JRIO_DIR_ENTRY_TYPE_BYTE_SIZE byte = 1 Tamanho de um identificador de


entrada de diretório

final estático String myHLQ = "// TR23790";


// Obtenha uma referência para o Diretório (um HLQ neste caso) Diretório myDSNsDir = Diretório novo
(myHLQ); // Buscar nomes de conjuntos de dados do objeto de diretório HLQ byte [] [] myDSNs =
myDSNsDir.listDetailed (); // Isso conterá um nome de conjunto de dados individual

String aDSN;
//Isso vai sempre seja JRIO_DIR_ENTRY_HLQ (2) neste exemplo byte dsnEntryType;

para (int i = 0; i <myDSNs.length; i ++) {


aDSN = "";
// Acesse o primeiro byte da matriz de bytes retornada dsnSource = myDSNs [i] [0];

// Cria um nome de conjunto de dados como uma string a partir dos bytes retornados de // listDetails ()

para (int j = 1; j <myDSNs [i] .length; j ++)


aDSN = aDSN + (char) myDSNs [i] [j]; // Lista o nome do conjunto de
dados para a saída padrão System.out.println ("DSN =" + aDSN);

Listagem 17.1 exemplo listDetailed ()

Exceções JRIO

Os métodos das classes derivadas do JRIO lançam algumas exceções Java, bem como algumas exceções exclusivas do JRIO. Das
exceções exclusivas do JRIO, você pode agrupá-las em exceções comuns e
exceções de acesso com chave. As exceções comuns são lançadas por um
Página 293

variedade de métodos que operam em conjuntos de dados e diretórios com e sem chave, enquanto as exceções de acesso com chave são
lançadas por métodos que operam apenas em conjuntos de dados com chave.

As seguintes exceções Java gerais no pacote java.io são lançadas por muitos métodos de JRIO:
FileNotFoundException, IOException, IllegalArgumentException, IllegalStateException e SecurityException.

Seguem breves descrições das exceções específicas do JRIO, começando com as exceções comuns aos métodos que
operam em arquivos e diretórios VSAM e não-VSAM.

Exceções JRIO comuns

Os métodos JRIO lançam duas exceções comuns: RecordIOException e RecordIORuntimeException.

RecordIOException

RecordIOException é uma subclasse da classe de exceção Java java.io.IOException que distingue as exceções de E / S
detectadas ao usar métodos JRIO. RecordIOException é um verificado exceção, o que significa que o código do aplicativo deve
tratá-la explicitamente. O método que lança a exceção pode usar um bloco try-catch para manipular a exceção ou passar a
exceção para o método de chamada, declarando a exceção no cabeçalho do método.

RecordIORuntimeException

RecordIORuntimeException é uma subclasse da classe de exceção Java java.lang.RuntimeException que distingue as exceções de
tempo de execução detectadas no JRIO. RecordIORuntimeException é uma exceção não verificada, que os métodos do aplicativo não
precisam capturar nem declarar em uma cláusula throws.

Lembre-se de que seu aplicativo normalmente não pode fazer nada sobre exceções não verificadas, exceto, talvez,
relatar sobre elas e sair normalmente. Se o seu aplicativo detectar exceções não verificadas, você usará System.exit (0) para
interromper seu aplicativo. Se você permitir que a JVM manipule a expressão não verificada, a JVM interromperá seu aplicativo e
gerará um rastreamento de pilha.

Exceções JRIO exclusivas para processamento de arquivos codificados

As exceções descritas nesta seção são específicas para acesso a arquivos com chave. Eles são todos subclasses de
com.ibm.recordio.RecordIOException. Os métodos da classe KeyedAccessRecordFile podem lançar essas exceções. Como
RecordIOException, eles também são exceções verificadas. Algumas dessas exceções são resultado de erros de programação ou de
entrada; alguns podem resultar do processamento normal.
Página 294

DuplicateKeyException

A exceção DuplicateKeyException é lançada quando seu aplicativo chama um método de gravação ou atualização que usa um novo registro
contendo uma chave que viola a exclusividade em um índice de chave exclusiva de um cluster. DuplicateKeyException geralmente indica um
erro de lógica do aplicativo ou dados de entrada incorretos. É difícil imaginar uma situação em que a geração de uma chave duplicada resulte do
comportamento normal ou esperado do aplicativo.

IllegalKeyChangeException

A exceção IllegalKeyChangeException é lançada quando seu aplicativo chama um método de atualização que usa uma chave para localizar um
registro a ser atualizado e, em seguida, tenta alterar o campo que contém a chave. Seu aplicativo não pode alterar o campo que contém a chave
usada para localizar um registro. Você pode ser capaz de alterar as chaves em outros campos. No entanto, isso pode causar uma
DuplicateKeyException.

KeyNotFoundException

KeyNotFoundException é lançada quando seu aplicativo chama o positionForward (keyFile, thisKey) método e o método não
podem localizar um registro em keyFile contendo a chave com o valor thisKey. O método positionForward () não executa uma
operação de leitura; o método move o cursor para o registro com o valor-chave especificado em seu argumento. O método
similar
positionForwardGE (chave) não lança essa exceção, mas posiciona para a próxima chave superior se a chave especificada não for encontrada. A
próxima leitura pode obter uma indicação de Fim de Arquivo se não houver mais chaves neste índice.

MissingPriorReadException

MissingPriorReadException é lançada quando seu aplicativo chama deleteRecord (aRecordFile) ou update (aRecordFile, aRecord) e a
operação anterior não é uma leitura bem-sucedida do registro para excluir ou atualizar. Simplificando, sua chamada para deleteRecord
ou update não tem registro para excluir ou atualizar.

Agora que você leu um pouco sobre as interfaces, classes, constantes e exceções JRIO, pode verificar alguns Java que usa
JRIO. A próxima seção fornece alguns exemplos de codificação do JRIO em ação.

Usando JRIO

Aqui, apresentamos exemplos de código que ilustram os conceitos-chave do uso da API JRIO. Os exemplos de código não são programas
completos; seu propósito é dar uma ideia do uso do JRIO. Antes de pularmos para os exemplos de código, algumas palavras sobre convenções de
nomenclatura de conjuntos de dados e diretórios, instruções de importação obrigatórias e representação de registro JRIO são necessárias.

Convenções de nomenclatura de diretório e conjunto de dados

As rotinas JRIO esperam que os nomes de diretório e conjunto de dados sejam strings que começam com //. Portanto, todos os conjuntos de
dados com o qualificador de alto nível TR23790 seriam acessados
Página 295

através de um objeto de diretório "// TR23790". Um diretório PDS é o nome do conjunto de dados PDS, como
"//TR23790.MYPDS.LIB". A barra "//" representa a raiz de todos os qualificadores de alto nível.

Além de iniciar as cadeias com barras duplas, os nomes dos conjuntos de dados conhecidos pelos métodos JRIO são como os nomes dos
conjuntos de dados usados em um fluxo de trabalho JCL. Um nome de conjunto de dados não VSAM compreendido pelo JRIO poderia ser
"//TR23790.MYFLATFILE.DATA". Um membro de um PDS é nomeado como esperado, "//TR23790.MYPDS.LIB(MYMEM)". Um nome de conjunto
de dados VSAM sequencial pode ser "//MYHLQ.VSAMSEQ.ESDS" e um nome de conjunto de dados VSAM com chave pode ser

"//YOURHLQ.VSAMKEY.KSDS".

Você pode usar métodos JRIO para criar um conjunto de dados não-VSAM. No entanto, você não pode usar métodos JRIO
para criar um conjunto de dados VSAM. Vocês devo use IDCAMS, geralmente em um fluxo de trabalho JCL, para criar um conjunto de
dados VSAM antes de acessar o conjunto de dados com JRIO. Posteriormente, você verá um fragmento de código usando métodos
JRIO que criam um conjunto de dados não-VSAM.

Para conjuntos de dados HFS, você codificará uma string de caminho como "/homedir/hfsDsn.data". Onde "//" representa a raiz de todos os
qualificadores de alto nível para arquivos não HFS, uma única barra, "/", representa a raiz dos arquivos HFS.

Import Statements Needed for JRIO.

Conforme mencionado anteriormente, JRIO está na extensão com.ibm.recordio. Portanto, a declaração de importação:

import com.ibm.recordio. *;

É necessário. No entanto, você também precisará importar a biblioteca Java IO básica. Se você deseja usar classes de registro customizadas
em oposição a matrizes de bytes para representar suas estruturas de registro, você precisará do pacote com.ibm.record também. Você pode, é
claro, importar classes e interfaces públicas específicas, se desejar. Se você quiser "codificar e pronto", precisará das três instruções de
importação mostradas a seguir para começar a usar o JRIO:

import com.ibm.recordio. *;
import com.ibm.record. *;
import java.io. *;

Representando Estruturas de Registro

Você tem duas opções ao implementar estruturas de registro em Java para uso com JRIO: como matrizes de bytes
e como um registrar objetos do framework. Esta seção fornece detalhes sobre essas alternativas.

Representando registros como matrizes de bytes

Uma maneira simples de implementar registros é usar uma matriz de bytes. Você pode pensar em uma matriz de bytes como uma matriz de
caracteres. Agora você sabe que um array de caracteres não é um Java
Página 296

string, embora em COBOL e PL / I, uma matriz de caracteres seja praticamente a mesma coisa que uma string. Você pode ficar tentado a
implementar suas estruturas de registro como matrizes de bytes devido à familiaridade que você tem com o uso de matrizes de caracteres como
strings.

Quando você usa uma matriz de bytes para representar um registro, seu aplicativo deve conhecer cada deslocamento, comprimento e
tipo de campo para acessar um campo. Além disso, seu aplicativo é responsável por converter um campo para qualquer tipo de dados
necessário no momento e de volta para uma matriz de bytes, se necessário. Isso não é COBOL ou PL / I, onde você pode estabelecer
uma estrutura de registro como uma mistura de tipos de dados representativos, codificar seu aplicativo para ler um registro na estrutura
de registro sabendo que o tempo de execução armazenará caracteres como caracteres, números compactados como compactados e
assim por diante (desde que a estrutura do registro esteja de acordo com o conteúdo do registro, é claro). Todo esse acesso por
deslocamento e comprimento e conversão de um tipo de dados para outro e vice-versa soa como muito trabalho. Vocês'

Você cria uma matriz de bytes que representa um registro usando o Novo() operador da seguinte forma:

// LRECLMYDSN é o comprimento do registro lógico de um conjunto de dados. byte [] myRec =


novo byte [LRECLMYDSN];
// Você pode usar JRIO_MAX_RECORD_LENGTH da interface IConstants // se quiser que sua matriz de bytes mantenha
um registro de qualquer arquivo. byte [] anyRec = novo byte [JRIO_MAX_RECORD_LENGTH];

Lembre-se de que, com a representação da matriz de bytes, seu aplicativo é responsável por controlar todos os tipos de dados,
deslocamentos e comprimentos de campo. Não parece muito com orientação a objetos, não é?
Y
FL

Se a dissertação anterior sobre o uso de matrizes de bytes para representar registros não parece atraente, não tema. Você tem outra
M

escolha. Você pode representar seus registros como objetos de estrutura de registro. Não é por coincidência que o assunto da
representação da estrutura de registro é o tópico da próxima seção.
A
TE

Representando registros como objetos Record Framework

Outra maneira de implementar registros é como objetos de estrutura de registro. A estrutura de registro fornece a capacidade de criar um
JavaBean, que encapsula um registro fornecendo acessores (métodos get e set) para os campos de um registro. O tempo de execução da
estrutura de registro lida com qualquer conversão de dados nos campos, gerenciando um conjunto de metadados que descreve cada
deslocamento, comprimento e tipo de campo. A estrutura de registro fornece classes com operações que usam esses metadados que auxiliam
na localização e conversão de campos de e para bytes ao acessar um campo. Resumindo, o framework de registro faz todo o trabalho sujo que
você leu na seção anterior, trabalho que você teria que fazer ao usar uma matriz de bytes para representar um registro para o JRIO.

Digno de menção é que a estrutura de registro não executa nenhuma E / S. Não querendo insistir no óbvio, mas a E / S do arquivo é
realizada pelo JRIO, não pelo framework de registro. A estrutura de registro fornece uma representação de um registro que permite que
um aplicativo Java acesse os campos de registro sem os aborrecimentos associados à representação da matriz de bytes.

Team-Fly ®
Página 297

Você não precisa usar as classes do framework de registro para fazer o JRIO. No entanto, você seria negligente em sua responsabilidade
profissional se não investigasse usando a estrutura de registro em vez de usar matrizes de bytes sujeitas a erros e carregadas de detalhes.

Você cria um objeto de registro com o Novo() operador. No entanto, você precisa criar uma instância do tipo de registro e um objeto de
registro como um objeto do tipo de registro. Na prática, você codificaria uma classe que contenha os construtores e os métodos get e
set para todos os campos.

// Crie uma instância do tipo de registro.


// MyRecordType é uma classe que implementa IRecordType IRecordType myRecType = new
MyRecordType ();
// Cria uma instância de um registro de um tipo específico // myRecord É um método que cria uma
instância de myRec.
// myRec deve ser de uma classe que implementa a interface IRecord. IRecord myRec = myRecType.myRecord ();

As classes do framework de registro estão no pacote com.ibm.record. Você obtém este pacote com JRIO. Você pode codificar todos os métodos
de acesso, completos com as conversões de dados necessárias, manualmente. Uma estratégia melhor seria adquirir uma ferramenta que o
ajudasse a aproveitar as vantagens dos recursos de economia de trabalho da estrutura de registro. Essa ferramenta existe, o Visual Age for Java,
Enterprise Edition. Talvez uma palavra ou duas sobre este sabor do produto Visual Age sejam necessárias aqui.

Visual Age para Java, Enterprise Edition e o Record Framework

O VisualAge for Java Enterprise Edition Record Framework é uma coleção de classes predefinidas de objetos que trabalham juntos para lidar
com dados orientados a registros em Java. A estrutura de registro permite que você crie um JavaBean. Pense em um JavaBean, ou bean, como
uma classe que não contém variáveis de instância públicas - apenas métodos de acesso público (métodos get e set, lembra?). Em um bean
que corresponde a uma estrutura de registro, o bean possui métodos de acesso para os campos de registro. Portanto, quando seu programa
Java precisa acessar um campo no registro, seu programa invoca um método acessador. Quaisquer conversões de dados necessárias são
tratadas por métodos, geralmente os métodos de acesso.

O Record Framework é fornecido com o JRIO para facilitar o acesso dos aplicativos Java aos campos dos registros. O Visual Age
para Java Enterprise Edition para a estação de trabalho (NT, OS / 2, Windows
95, 98 e 2000, AIX) tem ferramentas que permitem ao programador criar beans de registro sem requerer nenhum conhecimento do
Record Framework ou mesmo escrever uma única linha de código. As ferramentas estão contidas no Enterprise Access Builder ( EAB)
característica do produto. Há um importador (que importa o código-fonte COBOL para criar um bean de registro) e um Editor de Registro
(que permite ao programador criar, manipular ou examinar os registros graficamente. Atualmente, o suporte só existe para

Registros C, COBOL e 3270, mas o programador avançado pode estender o Record Framework para suportar outros tipos de registros. Os
registros gerados por ferramentas fornecem acesso aos campos de um registro como propriedades do bean de registro gerado.

Infelizmente, o Visual Age para Java Enterprise Edition não é um download gratuito (embora o Visual Age para
Java Developers Edition seja).
Página 298

Exemplos de codificação JRIO

Aqui, você verá o código JRIO que executa ações comuns de E / S de arquivo. A lista a seguir mostra as ações abordadas nesta
seção.

Adicionando Registros

Adicionar um registro a um fluxo de registro de saída de arquivo

Adicionar um registro a um arquivo de registro de acesso com

chave Adicionar um registro a um arquivo de registro de acesso

aleatório Determinar a existência de um arquivo ou diretório

Verifique a existência de um diretório

Verifique a existência de um arquivo

Crie um diretório ou arquivo não-VSAM Crie um

diretório

Crie um arquivo de registro

Criar um diretório temporário Criar um

arquivo de registro temporário Criar (definir)

uma chave

Localize um registro

Localizar um registro por chave em um arquivo de registro de acesso com

chave Posição em um registro em um arquivo de registro de acesso aleatório

Ler um registro

Ler um registro de um fluxo de registro de entrada de arquivo Ler um

registro de um arquivo de registro de acesso com chave Ler um registro

de um arquivo de registro de acesso aleatório Excluir itens

Excluir um diretório

Excluir um arquivo de registro


Excluir um registro de um arquivo de registro de acesso com chave

Buscando dados codificados e definindo dados codificados para campos Atualizar um registro em

um arquivo de registro de acesso com chave

Compare duas chaves

Adicionando registros com JRIO

Os exemplos a seguir ilustram o uso do JRIO para adicionar registros. Os exemplos não mostre como os registros obtêm dados (esta seção tem
exemplos de como carregar campos com diferentes tipos de dados). Além disso, os exemplos não mostre tratamento de exceções ou código de
recuperação.
Página 299

Anexar um registro a um fluxo de registro de saída de arquivo

Os métodos JRIO usados no bloco a seguir são os construtores das classes que implementam as interfaces de registro JRIO.

// RecordFile é uma classe que implementa IRecordFile. // Observe as barras duplas para o
nome do arquivo.
// Pense no objeto de IRecordFile como sendo instanciado // do que chamaríamos de nome do arquivo.

//
IRecordFile myRecFile = new RecordFile ("// MYHLQ.FLATFILE.DATA"); // Dataname declara para um membro PDS
e um conjunto de dados HFS segue. //
myRecFile = new RecordFile ("// MYHLQ.PDSFILE.LIB (MEMBER)"); //Note o barra simples para o nome
do conjunto de dados HFS. //
myRecFile = novo RecordFile ("/ u / lou / myRecordFile"); // O primeiro bloco a seguir mostra o registro
representado // como uma matriz de bytes. Neste caso, precisamos de um gravador de fluxo //

IFileOutputRecordStream myOutStream = null;


// O verdadeiro parâmetro no segundo argumento diz ao Java para anexar // a saída subsequente ao final do fluxo.

//
myOutStream = FileOutputRecordStream.getInstanceOf (myRecFile, true); //

byte [] byteRecRepresentation = novo byte [JRIO_MAX_RECORD_LENGTH]; // Você teria um código que define os
elementos da matriz de bytes para valores // correspondentes aos campos no registro

//
// Observe que não estamos gravando diretamente no objeto IRecordFile //. Estamos gravando no fluxo que tem
uma associação // com o objeto IRecordFile.

//
myOutStream.write (byteRecRepresentation);

Aqui está um código que mostra a representação da estrutura de registro do registro.

IRecordFile myRecFile = new RecordFile ("// MYHLQ.FLATFILE.DATA"); IFileOutputRecordStream myOutStream =


null;
myOutStream = FileOutputRecordStream.getInstanceOf (myRecFile, true); // MyRecordType é uma implementação de
IRecordType correspondente // aos detalhes da estrutura do registro.

//
IRecordType myRecType = new MyRecordType (); IRecord myRecord =
myRecType.newRecord ();
// Você teria um código que define os campos em myRecord para valores // correspondentes aos campos no registro

//
myOutStream.write (myRecord);

Anexar um registro a um arquivo de registro de acesso com chave

Os métodos JRIO usados a seguir são os construtores das classes que implementam as interfaces de registro e arquivo
com chave.
Página 300

// Você precisa de uma instância de um IRecordFile que corresponda ao // nome do arquivo, ou o que alguns
pensam como o objeto de arquivo 'real' IRecordFile myRecFile =

RecordFile.getInstanceOf ("// MYHLQ.VSAM.KSDS.CLUSTER");


// Aqui você define o tipo de arquivo de registro criado na declaração // anterior. Lembre-se de que as constantes vêm
da interface IConstants //.

//
IKeyedAccessRecordFile myKeyedFile =
novo KeyedAccessRecordFile (myRecFile,
JRIO_READ_WRITE_MODE);
// Aqui está a representação da matriz de bytes //

byte [] byteRecRepresentation = novo byte [JRIO_MAX_RECORD_LENGTH]; // Defina o conteúdo do registro e grave


a matriz de bytes. //

myKeyedFile.write (byteRecRepresentation);

Aqui estão algumas linhas que mostram a operação de gravação com uma representação de estrutura de registro.

// Isso parece mais simples do que a representação da matriz de bytes // certo?

IRecordType myKeyedRecType = new MyRecordType (); IRecord myRecord =


myKeyedRecType.newKeyedRecord (); // Defina o conteúdo do registro

myKeyedRecType.write (myRecord);

Anexar um registro a um arquivo de registro de acesso aleatório

Os métodos JRIO usados a seguir são os construtores das classes que implementam as interfaces de registro e arquivo
aleatório.

IRecordFile myRecFile = null;


// Aqui estamos usando um arquivo HFS. Observe a estratégia de // criação do objeto - um pouco diferente das
mostradas nos exemplos anteriores.

// Você pode usar qualquer uma das instruções de objeto de arquivo de registro de criação // para qualquer objeto
de arquivo de registro.
//
myRecFile = novo RecordFile ("/ u / lou / myRecordFile"); // Novamente - uma instrução cria
um objeto IRecordFile
// correspondente ao arquivo 'real' e outro objeto correspondente // ao 'tipo' de arquivo que vamos usar.

//
IRandomAccessRecordFile myRandomFile = null; myRandomFile = new
RandomAccessRecordFile (myRecFile,
JRIO_READ_WRITE_MODE);
// Mova o cursor para o final do arquivo ou para o início // do último registro.
Página 301

//
myRandomFile.positionLast ();
// Você já viu isso antes ... //

byte [] byteRecRepresentation = novo byte [JRIO_MAX_RECORD_LENGTH]; // Defina o conteúdo do registro e grave


a matriz de bytes. //

myRandomFile.write (bytes);

Como nos exemplos anteriores, aqui está o código para a representação do registro.

IRecordType myRandomRecType = new MyRecordType (); IRecord myRecord =


myRandomRecType.newRandomRecord (); // Defina o conteúdo do registro

myRandomRecType.write (myRecord);

Determinar a existência de um arquivo ou diretório

Os exemplos a seguir ilustram o uso do JRIO para verificar se existem vários itens.

Verifique a existência de um diretório

Para verificar a existência de algum objeto JRIO, use o existe() método conforme mostrado a seguir.

IDiretório myDir = novo diretório ("// MYHLQ.PDSFILE.LIB"); // Aqui está o código para criar um
diretório HFS
// IDiretório myDir = new Directory ("/ u / lou / myHFSDir"); // O método getAbsolutePath () retorna uma String
que representa // (você adivinhou) o caminho do objeto

// Se você estiver realmente com ele, você descobrirá que o método exists () // retorna um booleano - verdadeiro Se o
objeto existe, falso se não. if (myDir.exists ())

System.out.println ("dir =" + myDir.getAbsolutePath () +


" existe");

Verifique a existência de um arquivo


IRecordFile myRecFile = new RecordFile ("// MYHLQ.FLATFILE.DATA");
// Aqui estão as declarações de arquivo para um membro PDS e um conjunto de dados HFS. //

// IRecordFile myRecFile = new


RecordFile ("// MYHLQ.PDSFILE.LIB (MEMBER)");
// IRecordFile myRecFile = new RecordFile ("/ u / lou / myHFSFil"); //

// Você já viu isso antes, certo? //

if (myRecFile.exists ())
System.out.println ("myRecFile =" + myRecFile.getAbsolutePath ()
Página 302

+ "existe." );

Criar um diretório ou arquivo não VSAM

Lembre-se de que você deve usar IDCAMS para criar arquivos VSAM. Em outras palavras, você não verá métodos JRIO que criam
arquivos VSAM.

Crie um diretório

Use o mkDir () método para criar um objeto de diretório, conforme mostrado a seguir. O método mkDir () usa os atributos padrão ao
criar um novo diretório. Por exemplo, sua loja pode usar um padrão de 96 blocos de diretório para uma alocação de espaço PDS. O
código a seguir também mostra o uso do
mkdirLike () método que cria um novo diretório com base nos atributos de um existente.

IDiretório myDir, yourDir;


// Criar um diretório HFS
myDir = novo diretório ("/ u / lou / myHFSDir"); if (myDir.mkDir ())

System.out.println ("Diretório HFS" +


myDir. getAbsolutePath () +
" Criado" ) ;
// Criar um diretório PDS
myDir = novo diretório ("// MYHLQ.PDSFILE.LIB"); if (myDir.mkDir ())

System.out.println ("PDS Directory" +


myDir. getAbsolutePath () +
" Criado" ) ;
// Crie um diretório PDS com os mesmos atributos de espaço // como um diretório PDS existente

myDir = novo diretório ("// MYHLQ.PDSFILE2.LIB"); yourDir = new Directory ("//


YOURHLQ.PDSFILE.LIB"); if (myDir.mkdirLike (yourDir))

System.out.println ("dir =" + dir.getAbsolutePath () +


"like =" + yourDir.getAbsolutePath () + "created");

Criar um arquivo não-VSAMRecord

O código a seguir mostra o uso do createFile () método para criar um arquivo HFS e um arquivo sequencial. O método createfile ()
usa o espaço padrão e os atributos DSN. Também é mostrado o uso do
createFileLike () método que faz o que você espera - cria um arquivo com base nos atributos de um arquivo existente.

IRecordFile myRecFile, yourRecFile;


// Defina o nome do arquivo - um arquivo HFS aqui myRecFile = new RecordFile ("/
u / lou / myHFSFil"); // Criar usando createFile ()

if (myRecFile.createFile ())
Página 303

System.err.println ("Arquivo de registro HFS localizado em" +


myRecFile.getAbsolutePath () + "criado");
// A criação de um arquivo sequencial (simples) também é simples. // Os métodos no pacote JRIO ocultam os
detalhes da criação // de um arquivo HFS versus a criação de um arquivo sequencial MVS.

// O construtor abaixo aloca o conjunto de dados nomeado com um // LRECL de 160 com um formato de
registro fixo.
myRecFile = new RecordFile ("// MYHLQ.FLATFILE.DATA",
160,
JRIO_FIXED_MODE);
if (myRecFile.createFile ())
System.err.println ("Arquivo de registro sequencial localizado em" +
myRecFile.getAbsolutePath () + "criado");
// Por último, mas não menos importante, crie um arquivo sequencial como um // arquivo existente

yourRecFile = new RecordFile ("// YOURHLQ.FLATFILE.DATA"); myRecFile


= novo RecordFile ("// MYHLQ.ANOTHER.FLATFILE.DATA");
if (myRecFile.createFileLike (yourRecFile))
System.err.println ("Arquivo de registro sequencial localizado em" +
myRecFile.getAbsolutePath () +
"com os mesmos atributos de" +
yourRecFile.getAbsolutePath () + "criado");

Crie um diretório temporário

Você usa os mesmos métodos JRIO, mkDir () ou mkdirLike (), para criar um diretório temporário como faria para criar um
permanente. O código a seguir mostra a remoção do diretório temporário em um
finalmente quadra. Você se lembra do Capítulo 11, "Princípios básicos do tratamento de eventos Java", que um bloco finally é sempre executado,
mesmo quando nenhuma exceção é lançada. Usar um bloco finally é uma maneira prática de garantir que o código com a tarefa de remover um
diretório temporário (ou arquivo, nesse caso) seja executado.

Como os métodos mkDir () e mkdirLike () não lançam nenhuma exceção, você não codificará uma cláusula catch; não há nada para
pegar.

Uma advertência adicional é que você verificará se o diretório não é nulo antes da exclusão. A chamada para delete () falhará se o
diretório não estiver vazio.

IDiretório myDir = null;


// Encase as chamadas de método JRIO em um bloco try. tentar {

myDir = novo diretório ("// MYHLQ.TEMPPDS.LIB"); if (myDir.mkdir ())

System.out.println ("Diretório localizado em" +


myDir.getAbsolutePath () + "criado");
// Faça o que for necessário com este diretório (ou seja, // crie arquivos)

// Se qualquer código que você colocar aqui lançar exceções, // capture-as com uma cláusula
catch
}
finalmente {
Página 304

if (myDir! = null)
// Se você criou algum arquivo no diretório temporário, remova-o // Hora de excluir o diretório

if (myDir.delete ())
System.out.println ("Diretório localizado em" + myDir.getAbsolutePath () +
"excluído"); }

Criar um arquivo de registro temporário

Tal como acontece com a criação de um diretório temporário, você não escreverá chamadas JRIO diferentes para criar um arquivo temporário como
para criar um arquivo permanente. Você precisará de um código que tente remover o conjunto de dados temporário, novamente, como trabalhar com
um diretório temporário.

Os métodos JRIO que escrevem dados e posicionam cursores lançam IOExceptions. Ao codificar esses métodos, você precisará de uma cláusula
catch para interceptar e lidar com essas exceções. Além disso, seu arquivo deve ser fechado antes de você tentar excluí-lo. Em Java, você não
abre explicitamente arquivos e streams, mas devo
feche-os.

Objetos instanciados da classe RecordFile são, essencialmente, conjuntos de dados. Após uma criação bem-sucedida de objetos RecordFile,
você cria objetos File, que são, essencialmente, DDNames. Você codifica as operações de leitura e gravação do JRIO como métodos
invocados de objetos File, da mesma forma que codificaria uma instrução READ FILE em COBOL ou WRITE FILE FROM em PL / I.

// Lembre-se de que, ao trabalhar com arquivos, você cria um conjunto de dados com um // nome e atributos opcionais.
Esta chamada para new () usa um // LRECL = 100.

// Pense em ALLOCATE DSN aqui. IRecordFile


myTempFile = novo
RecordFile ("// MYHLQ.TEMPRANDOM.DATA", 100);
// Em seguida, crie o arquivo no qual você lerá e gravará. // Pense DDNAME aqui.

IRandomAccessRecordFile myRandomAccessFile =
novo RandomAccessRecordFile (myTempFile);
// Faça coisas com seu arquivo recém-criado (ou seja, grave dados nele), tente {

myRandomAccessFile.write (...);
myRandomAccessFile.positionFirst (...);
myRandomAccessFile.read (...)
}
// Métodos JRIO que leem, posicionam o cursor e escrevem no registro // caso contrário, são tratados.
// arquivos lançam exceções verificadas que devem ser capturadas ou capturadas (IOException ioe)

{
ioe.printStackTrace (); //Tanto faz...
}
// Este código sempre é executado. {
finalmente

if (myTempFile! = null)
tentar {
Página 305

myRandomAccessFile.close ();
}
catch (IOException ioe) {
// Faça algo, rastreie a pilha, qualquer coisa ...}

if (myTempFile! = null)
tente {myTempFile.delete (); } catch (IOException
ioe) {
// Faça algo, rastreie a pilha, qualquer coisa ...}

Criar (definir) uma chave

O código a seguir mostra como você usaria o construtor da classe Key para criar objetos da classe Key. O construtor usa uma matriz
de bytes como argumento. O método JRIO getBytes () é um método útil que gera uma matriz de bytes a partir de uma string ou objeto
Key.

Chave k1 = nova chave ("10101" .getBytes ()); Chave k2 = nova chave


("ABC" .getBytes ());
// Se você tiver caracteres não imprimíveis, você pode criar sua // matriz de bytes conforme mostrado
abaixo
byte [] myByteArray = {
(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE,};
Y

Chave k3 = nova chave (myByteArray);


FL

Localizando e lendo registros com JRIO.


A M

O próximo grupo de trechos de código mostra como usar o JRIO para localizar e ler registros.
TE

Localizar um registro por chave em um arquivo de registro de acesso com chave

JRIO possui métodos para localizar um registro com base no valor de uma chave fornecida. O código a seguir mostra como usar esses
métodos. Métodos de localização e posição para arquivos codificados, pesquise o índice do arquivo, não o arquivo em si. Se a chave não for
encontrada no índice, a posição do cursor não muda.

IKeyedAccessRecordFile myKeyedFile =
new KeyedAccessRecordFile ("// MYHLQ.KEYED.KSDS",
JRIO_READ_MODE);
//
IRecordFile myIndex = myKeyedFile.getPrimaryIndex (); // Encontre a primeira referência à
// Assume que myKey é um objeto da classe Key com um valor myKeyedFile.positionForward
chave no Índice
(myIndex, myKey);
// Agora você pode ler o registro ...

Team-Fly ®
Página 306

Posição em um registro em um arquivo de registro de acesso aleatório

O código a seguir mostra mais métodos de localização de registro JRIO. As pesquisas de registro por chave podem falhar porque a chave
não está contida no índice; as pesquisas de registros por posição (além de gerar uma exceção IO) não falharão.

O JRIO possui métodos que posicionam o cursor lido em relação à posição atual do cursor ou absoluto no início do arquivo. Os métodos de
posicionamento relativo incluem positionNext () e positionPrev (). Métodos de posicionamento absoluto usam um índice de registro como um
argumento; o primeiro registro é referenciado por um valor de índice de zero. Portanto, para fazer referência ao Iº registro em um arquivo de
registro de acesso aleatório, você deve procurar o I— 1 º registro.

O trecho de código mostra a invocação dos métodos read (). Não precisamos nos preocupar com o EOF porque estamos lendo
um registro após posicionar o arquivo no registro desejado.

IRandomAccessRecordFile myRandomFile =
new RandomAccessRecordFile ("// MYHLQ.VSAM.KSDS",
JRIO_READ_MODE;
// recBuffer contém os dados lidos do arquivo. byte [] recBuffer = byte [200]; //
LRECL = 200
// O cursor começa a vida no primeiro registro. Executar um read () // sem posicionar o cursor obtém o primeiro registro no
arquivo. myRandomFile.read (recBuffer);

// O método positionLast () avança o cursor de leitura depois de o último // registro. Portanto, para ler o último registro no
arquivo, você precisa // 'fazer backup' de um registro após avançar para o final do arquivo. myRandomFile.positionLast ();

// 'Fazer backup' de um registro


myRandomFile.positionPrev ();
// Leia o último registro
myRandomFile.read (recBuffer);
// Você pode posicionar o cursor de leitura para frente e para trás. No entanto, // você não pode voltar dois ou mais
registros com um método // Invocation.

// Posicione o cursor de leitura no início do arquivo. myRandomFile.positionFirst ();

// Pula para o próximo registro


myRandomFile.positionNext ();
// Leia o segundo registro. myRandomFile.read
(recBuffer);
// Mova o cursor de leitura para o 6º registro (lembre-se de que as referências // de registro são baseadas em
zero!)
myRandomFile.seek (5L); // Observe o uso do tipo primitivo long
// Leia o sexto registro
myRandomFile.read (recBuffer);
myRandomFile.close ();
Página 307

Ler todos os registros de um fluxo de registro de entrada de arquivo

A história curta é que você codifica um loop lendo um registro por vez até chegar ao final do arquivo. Em outras palavras, nada de
novo a não ser o uso de métodos JRIO para realizar a leitura.

Você pode ler um registro em uma matriz de bytes ou um registro, dependendo da representação do registro codificado. O código a seguir
mostra a leitura em ambas as representações.

Quando você lê em uma matriz de bytes, pode codificar o tamanho da matriz de bytes igual ao LRECL do arquivo ou igual à constante
JRIO_MAX_RECORD_LENGTH. Você não quer que o tamanho da matriz de bytes seja menor que o LRECL do arquivo; você lerá
apenas o número de bytes igual ao tamanho da matriz de bytes. Além disso, você não receberá nenhum aviso de que sua operação de
leitura não recuperou o registro inteiro. No caso de codificação, o tamanho do array de bytes igual a JRIO_MAX_RECORD_LENGTH, o
método read () lerá um número de bytes igual ao LRECL do arquivo. Codificar um tamanho de array de JRIO_MAX_RECORD_LENGTH
remove a necessidade de codificar LRECLs em seus aplicativos.

// Você lê do myFileInputStream independentemente do registro // representação (matriz de bytes ou registro


personalizado)
IFileInputRecordStream myFileInputStream =
new FileInputRecordStream ("// MYHLQ.FLATFILE.DATA");
// Esta é a representação do registro da matriz de bytes
byte [] recBuffer = novo byte [JRIO_MAX_RECORD_LENGTH]; //

// Esta estrutura de loop contínuo é familiar aos programadores de mainframe.

// Programas COBOL têm estruturas de registro com 77 níveis codificados para // detectar EOF. Os programas PL / I usam
condições ON ENDFILE para capturar EOF. // Loop até você quebrar

para(;;)
{
int bytesRead = myFileInputStream.read (recBuffer); if (bytesRead! = JRIO_READ_EOF)

{
// Processa o registro que acabou de ler}

outro
quebrar;
}
myFileInputStream.close ();

O código a seguir mostra uma estrutura de loop diferente.

int bytesRead = myFileInputStream.read (recBuffer); while (bytesRead! =


JRIO_READ_EOF) {
// Processa o registro que acabou de ler
bytesRead = myFileInputStream.read (recBuffer);
}
Página 308

Aqui está um exemplo usando uma representação de registro da estrutura de registro JRIO. O método read () retorna o número
de registros lidos 1 ou 1 (JRIO_READ_EOF).

// O método getCustomRecordType () é codificado em minha classe personalizada que // Implementa as particularidades


do registro e os métodos // get e set do campo de registro.

IRecordType myCustomRecType = myRecRepresentation.getCustomRecordType (); CustomRecord myCustomRecord =


(CustomRecord)
myCustomRecType.newRecord ();
int recsRead = myFileInputStream.read (myCustomRecord); enquanto (recsRead! =
JRIO_READ_EOF) {
// Processa o registro que acabou de ler
recsRead = myFileInputStream.read (myCustomRecord);
}

Ler um registro de um arquivo de registro de acesso com chave

Os detalhes da leitura de um arquivo codificado versus um arquivo sequencial lidam com o estabelecimento das estruturas de conjunto de dados
codificado apropriadas. A metodologia para realizar o I / O é a mesma, independentemente da organização do conjunto de dados subjacente. Isso é
parte do poder de uma linguagem de programação orientada a objetos como Java.

O código a seguir mostra o loop contínuo mostrado anteriormente. No entanto, como o código procura registros com uma chave correspondente,
o código não examina todos os registros no arquivo. Para arquivos com chave, você pode pesquisar registros usando o índice primário ou um
índice alternativo (assumindo que o conjunto de dados tenha um índice alternativo). Na prática, a busca por um registro por índice primário não
estaria contida em um loop, porque os índices primários contêm chaves únicas. A estrutura de loop para pesquisas de registro em arquivos de
acesso por chave é mais apropriada para conjuntos de dados contendo um índice alternativo.

Você pode usar o código na seção que lê todos os registros de um fluxo de entrada de arquivo para ler todos os registros de um arquivo codificado
sequencialmente. Em outras palavras, se você deseja processar todos os registros em um arquivo com chave, não precisa acessá-los por valores de
chave. Você pode acessar os registros sequencialmente usando o código, com as alterações apropriadas nos objetos de arquivo, mostrados na seção
anterior.

IKeyedAccessRecordFile myKeyedFile =
new KeyedAccessRecordFile ("// MYHLQ.VSAMDATA.KSDS");
IRecordFile myPrimaryIDX = myKeyedFile.getPrimaryIndex (); // A representação da matriz de bytes do
registro segue o byte [] recBuffer = novo byte [JRIO_MAX_RECORD_LENGTH]; // Suponha que a chave
tenha um valor.

myKeyedFile.positionForward (chave);
// Faça a leitura no buffer
bytesRead = myKeyedFile.read (myPrimaryIDX, recBuffer); if (bytesRead! = JRIO_READ_EOF)

// Registro encontrado ... processo

O código a seguir mostra o acesso ao registro e a entrada usando um índice alternativo. Observe que o método getAlternateIndex
() requer o nome (o caminho, na verdade) do
Página 309

arquivo de índice alternativo, enquanto o método getPrimaryIndex () não requer nenhum argumento.

myKeyedFile.positionForward (chave);
IRecordFile myAlternateIDX =
myKeyedFile.getAlternateIndex ("// MYHLQ.ALTERNATE.AIX",
JRIO_READ_MODE);
byte [] recBuffer = novo byte [JRIO_MAX_RECORD_LENGTH];
int bytesRead = myKeyedFile.read (myAlternateIDX, recBuffer); while (bytesRead! = JRIO_READ_EOF) {

// Registro encontrado ... processo


// Leia o próximo registro com a chave correspondente
bytesRead = myKeyedFile.read (myAlternateIDX, recBuffer);
}
myKeyedFile.close ();

Ler um registro de um arquivo de registro de acesso aleatório

O código busca um registro específico e então lê esse registro. O código é conceitualmente o mesmo que pesquisar um registro pela chave
primária seguida por uma leitura. Como no caso de leituras sequenciais de um arquivo codificado, você pode usar o código na seção "ler todos os
registros" para acessar e ler todos os registros de um arquivo aleatório.

IRandomAccessRecordFile myRandomFile =
new RandomAccessRecordFile ("// MYHLQ.RANDOM.DATA");
// Você já viu isso antes ...
byte [] recBuffer = novo byte [JRIO_MAX_RECORD_LENGTH]; // recNumWeWant é o número do
registro que estamos procurando. myRandomFile.seek (recNumWeWant);

int bytesRead = myRandomFile.read (recBuffer); if (bytesRead! = JRIO_READ_EOF) {

// Registro encontrado e lido - processá-lo}

myRandomFile.close ();

Excluindo diretórios, arquivos e registros com JRIO

O próximo grupo de trechos de código mostra ações de exclusão.

Excluir um diretório

Antes de excluir um diretório, você verifica se o diretório não é nulo, como mostra o código a seguir. Você deve remover todos os arquivos
do diretório ou a chamada para delete () falhará.

IDirectory myDir;
// Faça coisas com este diretório ... if (myDir! = Null) {

System.out.print ("Diretório localizado em" + myDir.getAbsolutePath ());


Página 310

// Remover arquivos do diretório antes da exclusão do diretório if (myDir.delete ())

System.out.println ("Foi excluído"); outro

System.out.println ("Não foi excluído");


}

Excluir um arquivo de registro

A exclusão de um arquivo é uma única chamada de método JRIO. No entanto, você deve ter permissão para excluir um arquivo. O gerenciador Java
Security, chamado de seu sistema OS / 390, verificará as permissões invocando o método checkDelete () do arquivo (não é um método JRIO!).

IRecordFile myRecordFile;
// Faça coisas com o arquivo de registro. tentar {

if (myRecordFile.delete ())
System.out.println ("Arquivo localizado em" +
myRecordFile.getAbsolutePath () +
"excluído");
outro
System.out.println ("Arquivo localizado em" +
myRecordFile.getAbsolutePath () +
"não excluído");
}
catch (SecurityException sex) {
System.out.println ("Você não tem permissão para excluir" +
myRecordFile.getAbsolutePath ());
}

Excluir um registro de um arquivo de registro de acesso com chave

Você deve ler um registro de um arquivo codificado antes de executar operações no registro, como atualizar ou excluir. O código a seguir
não mostra nenhuma operação de leitura de registro (). Como no exemplo anterior de exclusão de registro, o gerenciador de segurança
verifica as permissões do usuário antes de habilitar qualquer registro ou operação de arquivo. O código a seguir não mostra a captura de
exceções de segurança que podem ser lançadas.

IKeyedAccessRecordFile myKeyedFile =
new KeyedAccessRecordFile ("// MYHLQ.VSAM.KSDS",
JRIO_READ_WRITE_MODE);
// Precisa de um índice para acessar o arquivo, certo? IRecordFile index =
myKeyedFile.getPrimaryIndex ();
// Fazer coisas com o arquivo codificado (ou seja, ler, atualizar, excluir registros) // A chamada deleteRecord remove o
último registro lido
myKeyedFile.deleteRecord (índice);
// Feche o arquivo para fazer
myKeyedFile.close ();
Página 311

Buscando dados codificados e configurando dados para campos

Às vezes, seus registros contêm dados brutos codificados em ASCII, EBCDIC ou alguma outra codificação. A técnica para extrair esses
dados é emitir uma chamada para o construtor String com quatro argumentos:

AString = nova String (byteArray, fieldOffset, fieldLength, Encoding);

O byteArray é o seu registro, o deslocamento do campo é baseado em zero, o comprimento do campo é, obviamente, o comprimento do
campo e a codificação é uma string que descreve a codificação dos caracteres. Se você estiver usando uma representação de estrutura de
registro, obterá métodos para seus campos de registro; os métodos get conteriam código que implementa a técnica anterior.

A Tabela 17.3 mostra algumas das codificações de caracteres mais comuns e os valores das strings de argumento.

O código a seguir mostra a extração de um campo EBCDIC de um registro representado como uma matriz de bytes.

// Suponha que o registro esteja disposto da seguinte maneira: //


Campo 1 = "2A"
// Campo 2 = "4B"
// Campo 3 = "6C"
// Esta é a aparência deste registro // byte [] myEBCDICRec = {

// (byte) 0xF2, (byte) 0XC1,


// (byte) 0XF4, (byte) 0XC2,
// (byte) 0XF6, (byte) 0XC3
// };
// Queremos acessar o Campo 2.
String field2 = new String (myEBCDICRec, // Record Byte array
2 , // Primeiro elemento da matriz do // campo

2 , // Comprimento do campo 2
"Cp1047"); // Valor que representa
// EBCDIC

Tabela 17.3 Valores de codificação de caracteres para o construtor de string

CODIFICAÇÃO VALOR PARA CONSTRUTOR DE CADEIA

8859_1 ASCII (ISO Latin-1)

Cp1047 EBCDIC

UnicodeBig Unicode

UTF8 UTF-8
Página 312

Para outras codificações, simplesmente altere o valor do quarto argumento no construtor String e está tudo pronto.

Quanto a definir o valor de um campo de registro para um valor codificado, a história curta é usar uma chamada ao método getBytes (). O
método getBytes () da classe String converte uma string em uma matriz de bytes de acordo com um esquema de codificação de caracteres
passado como um argumento. Os valores da codificação de caracteres estão listados na Tabela 17.3. Com certeza parece o que precisamos
aqui, não é? Aqui está um exemplo de código:

// Mais uma vez, suponha que o registro esteja disposto da seguinte maneira: //
Campo 1 = "2A"
// Campo 2 = "4B"
// Campo 3 = "6C"
// O código abaixo altera o Campo 2 para "AB"
// myASCIIRec É a representação da matriz de bytes de um registro. final int FIELDOFFSET = 2;

String newASCIIValue = "AB";


// Cria uma matriz de bytes contendo o novo byte de valor do campo 2 [] newField2Bytes =

newASCIIValue.getBytes ("8859_1");
// Precisamos dos deslocamentos de campo para colocar os dados no registro. // Então, coloque os dados de
volta!
para (int i = 0; i <newField2Bytes.length; ++ i)
myASCIIRec [FIELDOFFSET + i] = newField2Bytes [i];

Atualizar um registro em um arquivo de registro de acesso com chave

Atualizar um registro envolve uma invocação de método (você adivinhou - update ()). No entanto, você pode atualizar apenas o último
registro lido. Portanto, seu aplicativo deve ler um registro antes de emitir a chamada para update (). O código a seguir mostra o caminho.
Obviamente, o arquivo devo ser aberto para o modo de leitura e gravação. Não tão óbvio é que você deve acessar o registro a ser atualizado
usando um índice.

O método update () lança exceções específicas do JRIO que surgem quando o código altera os campos-chave indevidamente.

IKeyedAccessRecordFile myKeyedFile =
new KeyedAccessRecordFile ("// MYHLQ.MYKEYED.FILE",
JRIO_READ_WRITE_MODE);
// Precisa de um índice para a chamada de atualização. Aqui, estamos usando o // Índice primário

IRecordFile primIDX = myKeyedFile.getPrimaryIndex ();


// Você pode usar uma representação de registro ou uma representação de byte myKeyedFile.read (primIDX,
recBuffer);
// Segue o código para alterar os campos não-chave //

// Atualize o registro.
myKeyedFile.update (primIDX, recBuffer);
myKeyedFile.close ();
Página 313

Compare Duas Chaves

Comparar chaves é uma operação comum. O JRIO Chave classe tem sua própria é igual a() e comparado a()
métodos. Em Java, os objetos são comparados por referência, mas os métodos na classe Key são comparados por valor.

Dadas as declarações principais no seguinte:

Chave k1 = nova chave ("1A" .getBytes ()); Chave k2 = nova


chave ("2A" .getBytes ()); Chave k3 = nova chave ("2A"
.getBytes ());

A chave k1 não é igual às chaves k2 e k3. As teclas k2 e k3, embora objetos diferentes, são iguais.

Em suma

A única diferença marcante entre os aplicativos do processador de dados de mainframe e os de suas contrapartes de máquinas pequenas é a
quantidade esmagadora de E / S de registro realizada pelo processador de dados de mainframe. Um ambiente de programação sem suporte para
E / S de registro é de pouca utilidade para o programador de mainframe. Com o JRIO, o programador de mainframe pode usar Java para acessar
estruturas de conjuntos de dados IBM familiares e úteis.

Aqui, você viu trechos de código usando JRIO para realizar tarefas úteis. O próximo capítulo mostra código JRIO
adicional e com mais recursos.
Página 314

Esta página foi intencionalmente deixada em branco.


Página 315

CAPÍTULO 18
Java, CICS e IMS
CICS e IMS são dois dos produtos mais amplamente usados na loja de mainframe IBM OS / 390. Para que a tecnologia Java seja uma
força no processamento de mainframe, você, o programador de mainframe, deve ter a capacidade de escrever aplicativos Java usando
transações CICS e aplicativos IMS usando Java.

Este capítulo aborda como acessar transações CICS com Java usando o CICS Transaction Gateway e um conjunto de classes
Java chamado JCICS. Também examinaremos os produtos IBM IMS Connector para Java, que suporta comunicações com IMS
entre um ou mais clientes TCP / IP e um ou mais sistemas IMS.

Java e CICS

Conforme mencionado anteriormente, o programador Java CICS tem duas opções: o Gateway de transação CICS ( CTG) e as classes
JCICS. Estas não são escolhas de um ou outro; você verá que pode usar classes JCICS para escrever código Java que invoca
transações CICS por meio do Transaction Gateway. Primeiro, vamos examinar o Transaction Gateway.
Y

O CICS Transaction Gateway


FL

A IBM Gateway de transação CICS ( CTG) para OS / 390 fornece acesso a partir de navegadores da Web e computadores da rede para
M

aplicativos em execução em um CICS Transaction Server em uma configuração de duas camadas. Ele substitui o CICS Gateway para Java
A

(MVS), que foi enviado como um componente do CICS Transaction Server para OS / 390 1.2.
TE

Team-Fly ®
Página 316

O CTG para OS / 390 é executado no mesmo ambiente operacional que o CICS Transaction Server para OS / 390, utilizando o
UNIX System Services do OS / 390 Operating System. É compatível com o CICS Transaction Server para OS / 390 1.2 e 1.3. Ele
fornece uma API que permite que applets e servlets Java se comuniquem usando o CICS Interface de chamada externa ( ECI). O
CTG para OS / 390 converte esta comunicação ECI para acessar o CICS Transaction Server para OS / 390 usando o

Interface externa do CICS ( EXCI). Ao contrário do CTG suportado em outras plataformas, o CTG para OS / 390 pode apenas encaminhar
pedidos ECI e não o Interface de apresentação externa ( EPI) pedidos.

CTGs em outras plataformas podem acessar vários servidores CICS; entretanto, o CTG para OS / 390 pode acessar apenas o CICS
Transaction Server para OS / 390.

O ambiente de execução JVM e CICS

A JVM usada no CICS usa o TCB aberto fornecido pelo CICS Ambiente de transação aberto
(OTE). O OTE aprimora a arquitetura interna do CICS para permitir que tarefas de usuário especificadas sejam executadas por conta própria bloco
de controle de tarefa ( TCB), o TCB aberto. O TCB aberto que o CICS usa para executar a JVM é denominado J8 TCB. O J8 TCB usa o MVS Ambiente
de linguagem ( LE), em vez de serviços CICS LE, porque o CICS LE não suporta encadeamento. Além disso, cada TCB J8 é configurado para
ser executado como um processo MVS UNIX System Services, que é requerido pela JVM no OS / 390.

Cada programa Java que requer serviços é executado em seu próprio J8 TCB com seu próprio JVM. A JVM não pode ser reutilizada. Ele
é criado, usado e destruído apenas para o programa Java que requer a JVM.

Um TCB aberto é designado a uma tarefa CICS durante a vida da tarefa CICS. Nenhum subdispatch de outras tarefas do CICS ocorre no TCB
aberto. A execução da JVM não deve impactar a carga de trabalho principal do CICS, portanto, é executada em uma prioridade mais baixa do
que o TCB principal do CICS.

Um MVS JVM requer armazenamento significativo acima e abaixo da linha fora dos DSAs do CICS. Na prática, atualmente, não
mais que 30 JVMs podem estar ativos ao mesmo tempo.

A boa notícia é que você, o programador Java CICS, não precisa se preocupar muito com esses detalhes. Quando você escreve um
aplicativo Java CICS, normalmente requer apenas uma JVM. Os programadores do sistema devem configurar o gateway CICS para
disponibilizar a memória necessária para a JVM acima e abaixo da linha para seu programa de aplicativo.

Alterações em sua JCL para execução do Java CICS

Você, ou alguma outra parte responsável pela manutenção do JCL, deve fazer algumas alterações para executar o Java
com o CICS. Para executar apenas com suporte JVM (bytecode), coloque o executável SCEERUN do MVS LE no CICS STEPLIB ou
MVS LINKLIST. Para executar com suporte JVM e suporte CICS LE, você (ou essa outra pessoa) deve colocar SCEERUN no CICS
STEPLIB ou MVS LINKLIST e colocar SCEECICS seguido por SCEERUN em DFHRPL.

Além disso, dois cartões DD são necessários na JCL de inicialização do CICS:

DFHCJVM DD DUMMY
Página 317

DFHJVM DD DSN = CICSTS13.CICS.SDFHENV

As variáveis de ambiente que controlam a inicialização da JVM são fornecidas no SDFHENV PDS. O membro
DFHJVMEV contém valores padrão e deve ser copiado e adaptado aos valores desejados. O nome padrão do membro
copiado é DFHJVM.

DFHCJVM é um programa de lote C padrão que usa o MVS Language Environment. A tarefa do DFHCJVM é criar uma Java
Virtual Machine sempre que um programa Java consistindo em bytecode (em oposição ao código executável criado pelo High
Performance Java Compiler) é carregado. Ele é carregado durante a inicialização do CICS como parte do núcleo do CICS. O
programa usa o DDname DFHCJVM para STDIN em vez do DDname SYSIN.

Modos de operação CTG.

As classes de gateway têm dois modos de operação: Modo de rede e Modo local. No modo de rede, o gateway deve ser personalizado para
escutar em uma porta TCP / IP. O CTG pode ser ativado como uma tarefa iniciada ou a partir de uma linha de comando do UNIX Systems
Services. As solicitações que chegam pela rede podem então ser atendidas pelo gateway e as conexões podem ser feitas aos servidores CICS.

Os programadores de aplicativos no ambiente de mainframe raramente são responsáveis pela configuração dos dispositivos de rede. Vamos
enfrentá-lo - o trabalho do programador de aplicativos é difícil o suficiente sem ter que configurar recursos de rede. Normalmente, o trabalho de
configuração do gateway é delegado a uma equipe separada dentro da organização.

No modo local, nenhuma personalização é necessária. As classes de gateway podem ser utilizadas diretamente no código Java e as conexões podem
ser feitas localmente para servidores CICS em execução no mesmo sistema OS / 390. O suporte CICS para transações gravadas em Java está
incluído no produto base e nenhuma instalação específica é necessária.

Mais boas notícias - você, o programador Java CICS, não precisa se preocupar muito com a configuração (modo
de rede) ou a falta de configuração (modo local).

Fluxo de processamento Java CICS

Aqui estão as etapas típicas necessárias para usar o CICS Transaction Gateway em um aplicativo baseado na Web:

1 Um URL é invocado a partir do navegador com parâmetros de entrada.

2 O servidor Web carrega o servlet se ainda não estiver carregado e invoca o servlet serviço()
método.

3 - No processo de inicialização ( iniciar ), o servlet cria uma conexão lógica local com o CICS Transaction
Gateway. No processo de rescisão ( destruir() ), o servlet fecha a conexão lógica com o CICS Transaction
Gateway.
Página 318

4 - No serviço() método, o servlet envia uma solicitação ao CICS Transaction Gateway usando a API ECI.

5 O CTG chama os serviços de transação do CICS localmente usando EXCI.

6 Um programa JCICS Java recupera dados por meio do CICS COMMAREA.

Na verdade, as etapas anteriores não são peculiares aos programas Java CICS. Eles podem descrever uma transação CICS escrita em
qualquer linguagem de programação suportada pelo CICS, como COBOL, PL / I ou C.

A Figura 18.1 mostra a conexão entre as partes relevantes nos ambientes CICS e de servidor da Web que normalmente
constituem um aplicativo Java CICS de três camadas.

EXCI é o Interface externa do CICS e ECI é o Interface de chamada externa do CICS.

Escrevendo um aplicativo CTG

Execute as seguintes etapas para implementar um aplicativo CICS Transaction Gateway:

1 Importe o pacote Java CICS necessário com.ibm.ctg.client. * ;.

2 Crie a conexão com o CTG para OS / 390. A classe que representa a conexão lógica entre um programa Java e
o CICS Transaction Gateway para OS / 390 é chamada JavaGateway:

JavaGateway jgConnection = new JavaGateway ();

Este é o construtor padrão que cria um objeto JavaGateway em branco. Você deve definir os atributos relevantes para definir o tipo de
conexão que está usando. Por enquanto, suponha que você configurará uma conexão local. Aqui está o código para configurar o objeto
JavaGateway jgConnection para usar uma conexão local:

tentar{
jgConnection.setURL ("local:");

Figura 18.1 Ambiente de aplicativo Java CICS.


Página 319

} catch (java.io.IOExceptoin e) {
// Lide com a exceção
e.printStackTrace ();
}

o setURL () método na classe JavaGateway determina se a conexão é local ou baseada em rede.

3 - Abra a conexão codificando o seguinte:

tentar{
jgConnection.open ();
} catch (java.io.IOException e) {
// Lide com a exceção
e.printStackTrace ();
}

4 - Inicialize o CICS COMMAREA.

A interface ECI envia dados para o programa CICS Transaction Server e recebe dados dele por meio do CICS
COMMAREA. Uma COMMAREA é uma matriz de bytes que pode conter diferentes tipos de dados, como caracteres,
números binários ou números decimais compactados. Para trabalhar com a COMMAREA, você pode manipulá-la um byte
por vez ou pode acessar o conteúdo da COMMAREA usando o IBM Java Record Framework para acessar campos como
objetos (consulte o Capítulo 17, "Java Record I / O usando o JRIO Pacote"). O código a seguir mostra um método,
initCommArea (), que inicializa uma matriz de bytes com os dados a serem enviados. Este é o código do método
initCommArea ():

public byte [] initCommArea (int size, String data) {

// constrói um array temporal de bytes


byte [] dataBytes = data.getBytes ();
// Crie uma nova commArea longa o suficiente para armazenar a solicitação e a resposta

byte [] theCommArea = novo byte [tamanho];


// Copie os dados de entrada na commArea
System.arraycopy (dataBytes, 0, theCommArea, 0, dataBytes.length); // Preencha o restante dos bytes
commArea com 0
int c = dataBytes.length;
enquanto (c <tamanho) theCommArea [c ++] = 0; return
theCommArea;
}

Este método retorna uma matriz de bytes com o conteúdo dos dados da solicitação. Lembre-se de que a matriz de bytes commArea é um
campo de entrada / saída. Seu comprimento deve ser grande o suficiente para armazenar a solicitação e a resposta. No entanto, o
tamanho da COMMAREA não pode exceder 64 KB.

5 Inicialize a COMMAREA invocando o método anterior:

byte [] commArea = initCommArea (2048, dados);


Página 320

6 Crie um pedido. Você pode criar uma solicitação síncrona ou assíncrona. Aqui você verá como criar uma solicitação síncrona. Uma
solicitação síncrona faz com que o tempo de execução espere até que a solicitação seja concluída antes de prosseguir. A classe que
representa uma solicitação ECI é ECIRequest. Aqui está um exemplo de código:

ECIRequest eciRequest = new ECIRequest (CICS_APPLID, // CICS


Region Name
nulo, //ID do usuário

nulo, //Senha
programName, // CICS Target Program Name commArea,
// COMMAREA Byte Array para manter os resultados da solicitação
ECIRequest.ECI_NO_EXTEND, // ECI_NO_EXTEND para informar que unidades estendidas de trabalho do CICS
não são suportadas
ECIRequest.ECI_LUW_NEW); // Este valor é ignorado para não estendido
unidades de trabalho

Faça o fluxo da solicitação para o servidor de transações CICS. Aqui está um exemplo de invocação:

tentar{
jgConnection.flow (eciRequest);
} catch (java.io.IOException e) {
// Lide com a exceção
e.printStackTrace ();
}

7 Processe a resposta do servidor CICS. Os atributos de resposta estão contidos no objeto ECIRequest (propriedade
COMMAREA) utilizado para fazer a solicitação síncrona. Se a transação foi originada de um servlet Java, o servlet poderia
formatar uma página HTML ou XML contendo os dados para a resposta.

8 Feche a conexão com o CICS Transaction Gateway. Aqui está um exemplo de código:

tentar{
jgConnection.close ();
} catch (java.io.IOException e) {
// Lide com a exceção
e.printStackTrace ();
}

Os métodos da classe JavaGateway são diretos: setURL (), open (), flow () e close (). Cada método deve estar
contido em um bloco try / catch.

Escrevendo um programa JCICS usando COMMAREA

Esta seção mostra um programa de amostra que recebe dados e envia de volta a resposta por meio da COMMAREA. Uma
COMMAREA é uma matriz de bytes que pode conter diferentes tipos de dados, como caractere, número binário ou números
decimais compactados. Para trabalhar com a COMMAREA, você pode manipulá-la um byte por vez ou pode
Página 321

acesse o conteúdo de COMMAREA usando o IBM Java Record Framework para acessar campos como objetos.

A classe que representa a COMMAREA é CommAreaHolder. A COMMAREA real é uma variável de instância do tipo byte [] com o valor
do nome. A razão para essa classe de suporte extra é que uma COMMAREA é usada tanto para entrada quanto para saída, portanto, o
nível extra de indireção é necessário, pois o Java só passa argumentos por valor. Na entrada, o valor contém a COMMAREA que está
sendo passada para o programa. O programa retorna uma COMMAREA definindo o valor para um novo byte [] que ele construiu.

A Listagem 18.1 mostra um programa CICS usando as classes JCICS para receber dados da COMMAREA.

O parâmetro ca de aula CommAreaHolder irá manter a entrada e saída do método main (). Observe a propriedade valor do
argumento do método main (). Esta propriedade representa a matriz de bytes que conterá os dados que retornam da
COMMAREA.

Java e IMS

Conforme mencionado anteriormente, a IBM oferece suporte ao acesso Java ao IMS por meio de um produto proprietário denominado
IMS Connect for Java. O IMS Connector for Java fornece uma maneira de criar aplicativos Java que podem acessar transações IMS. Com
suporte adicional do IBMWebSphere Studio e IBM WebSphere Application Server, você pode construir e executar servlets Java que
acessam suas transações de sites da Web.

public static void main (CommAreaHolder ca) {String caString = new String
(ca.value); [2] comprimento recebido int = caString.length ();

System.out.println ("Tamanho da string que obtemos de CommArea:" + receivedLength

);
System.out.println ("String que obtemos de CommArea:" + caString
);
// Escreva a vírgula de volta para ctg
String sCommAreaResponse = ("Mensagem recebida:" + caString); System.out.println ("Responder commArea definido
para:" + sCommAreaResponse); // Cria uma resposta que inclui a mensagem recebida e a data String dateString = (new
java.util.Date ()). ToString ();

String sCommAreaResponse = ("Mensagem recebida. Data:" + dateString); ca.value = sCommAreaResponse.getBytes ();

Retorna;
}

Listagem 18.1 Recebendo dados da COMMAREA.


Página 322

Você não configurará o IMS Connector for Java. O IMS Connector for Java é um software de sistema e são eles
que cuidam da instalação e do ajuste.

O que é IMS Connect para Java ?.

O IMS Connector for Java fornece uma interface Java compatível com o Common Connector Framework para o IMS Connect. O IMS Connector for
Java é uma biblioteca de classes que consiste em dois pacotes:
com.ibm.connector.imstoc e com.ibm.imstoc. Todas as classes no pacote com.ibm.imstoc e muitas das classes no pacote
com.ibm.connector.imstoc são classes de suporte que não são usadas por desenvolvedores de aplicativos durante o
desenvolvimento de aplicativos que usam o IMS Connector for Java.

Pré-requisitos para executar o IMS Connect para Java

Um pré-requisito para usar o IMS Connector for Java é o IMS Connect (anteriormente chamado de Conexão IMS TCP / IP OTMA ou
simplesmente ITOC). O IMS Connect permite que os aplicativos clientes enviem mensagens ao IMS TM por meio do IMS Acesso ao Open
Transaction Manager ( Interface OTMA), fornecendo conexões para transações IMS de uma variedade de plataformas, incluindo estações
de trabalho e produtos de mainframe. O IMS Connect fornecerá melhorias em usabilidade, desempenho e capacidade de instalação do
SMP. Antes de tentar executar um programa de aplicativo Java ou servlet que usa o IMS Connector for Java, Versão 3.5, certifique-se de
que os produtos a seguir estejam instalados na máquina host de destino.

Conector IMS para Conceitos Java

Esta seção fornece uma visão geral de alguns dos conceitos e terminologia necessários para entender o IMS Connector
for Java, e inclui

Formatação MFS

Classes Java fornecidas com o Conector IMS para mensagens IMS

Java

Formatos de mensagem IMS

Informações de logon do IMS

Níveis de sincronização

Gerenciamento de conexão

Conversas IMS

Formatação MFS

Mensagens de entrada e saída da transação que são fornecidas ao IMS por meio do IBM Acesso ao Open Transaction Manager ( OTMA) ignora
o processamento de MFS online. MFS é o componente de processamento online no IMS que executa a formatação da mensagem, como
preenchimento de campo, truncamento, justificação e a inserção de dados literais nas mensagens.
Página 323

Classes Java fornecidas com o IMS Connector para Java

O IMS Connector for Java fornece vários Java beans para ajudá-lo na construção de programas e servlets Java. Todos esses
beans estão no pacote IMS Connector for Java com.ibm.connector.imstoc.
Você pode combinar esses beans em um bean composto que acessa uma transação IMS. O Conector IMS para Java
inclui as seguintes classes documentadas:

IMSConnectionSpec: O bean IMSConnectionSpec fornece informações sobre a conexão entre um programa Java e um
componente de host IMS Connect, bem como informações sobre gerenciamento de conexão. O nome do host
IMSConnectionSpec e as propriedades da porta são específicos para IMS Connector for Java, enquanto as outras
propriedades são herdadas da interface Common Connector Framework

com.ibm.connector.ConnectionSpecManagementProperties.

IMSConvContext: O bean IMSConvContext é usado para cumprir o requisito IMS Connect de que a mesma conexão
é usada para todas as iterações de uma conversa IMS. Uma conexão é um link de comunicação, um soquete no
caso de TCP / IP, e é análoga à linha telefônica que conecta dois telefones durante uma conversa telefônica. O IMS
Connector for Java inclui essa classe em seu modelo de programação para uso por aplicativos da Web de
conversação. Um aplicativo ou servlet Java deve criar uma única instância da classe IMSConvContext no início de
uma conversa e associar essa única instância à conexão usada para a conversa. Isso garante que a conexão será
preservada durante o tempo de vida da conversação IMS e que o CCF ConnectionManager sempre retornará a
mesma conexão para cada iteração da conversação IMS.

IMSConvHttpSessionCleanup: O IMS Connector para Java inclui a classe IMSConvHttpSessionCleanup em seu modelo de
programação para uso apenas por aplicativos da Web de conversação. Esta classe implementa a interface
HttpSessionBindingListener e é usada para capturar o evento não acoplado de um objeto HttpSession durante uma
conversação ativa e, em seguida, realizar a limpeza apropriada.

IMSInteractionSpec: O bean IMSInteractionSpec fornece informações sobre a interação entre um programa Java e um
armazenamento de dados. As propriedades de interação incluem

ConvTerminated, definido como TRUE se o programa de aplicativo IMS host terminar a conversa IMS. O
programa de aplicativo Java verifica o valor dessa propriedade para determinar se a conversa foi encerrada ou
não pelo host.

Nome do armazenamento de dados, o nome do armazenamento de dados IMS de destino que é definido no arquivo de configuração
IMS Connect.

Nome LTERM, usado para substituir o nome LTERM no programa de aplicativo I / O PCB. A substituição é
usada se o cliente não deseja substituir o nome LTERM no PCB de E / S pelo canal de transação.

Nome do mapa ( ou o nome do MFS MOD), que é o nome fornecido por um programa de aplicativo IMS ao retornar
a saída de uma transação. Também pode
Página 324

ser fornecido pelo IMS ao retornar uma mensagem de status, como a saída de um / DIS
comando ou uma mensagem de erro.

Modo, ou o tipo de interação a ser realizada entre o programa Java e o datastore IMS. Os modos que
são atualmente suportados incluem MODE_SEND_RECEIVE, MODE_ACK, MODE_NACK e
MODE_END_CONVERSATION.

Nível de sincronização, ou o que especifica o nível de sincronização da transação - a maneira pela qual o cliente (um
aplicativo Java ou servlet) e o programa de transação do servidor (por exemplo, um programa de aplicativo IMS) interagem
com relação às mensagens de saída da transação. De forma simples, o nível de sincronização determina se as mensagens
de saída da transação devem ou não ser reconhecidas, ou seja, aceitas (ACK) ou rejeitadas (NACK), pelo cliente.

DFSMsg: O bean DFSMsg representa o status IMS ou mensagens de erro que são retornadas a um aplicativo Java ou servlet em
resposta a um comando ou transação. Essas mensagens geralmente começam com os caracteres DFS. Freqüentemente, as
mensagens DFS são retornadas a um aplicativo ou servlet Java. Algumas mensagens DFS indicam situações de erro, enquanto
outras são retornadas como saída de comandos IMS. Em todos os casos, como o OTMA é usado para retornar a mensagem, a
formatação MFS não é executada. No entanto, o IMS inclui um nome MFS MOD (nome do mapa) com mensagens DFS. O IMS
Connector for Java verifica o campo de nome MOD das mensagens que recebe do IMS Connect. Se o MOD tiver um dos seguintes
nomes, a mensagem será processada como uma mensagem DFS. Se o nome não estiver listado na lista a seguir, ele será
processado como saída da transação.

Mensagens IMS

As mensagens enviadas para o IMS ou recebidas do IMS por um aplicativo ou servlet Java usando o IMS Connector for Java podem ser
qualquer uma das seguintes:

Mensagens de entrada de transação IMS

Mensagens de saída de transação IMS

Status IMS ou mensagens de erro (também chamadas de mensagens DFS)

Comandos IMS

Mensagens de saída de comando IMS

O IMS Connector for Java foi projetado principalmente para lidar com os três primeiros tipos de mensagens.

Formato de mensagem IMS

Os segmentos de mensagem que são enviados e recebidos de transações IMS sempre começam com um campo de comprimento de segmento de
dois bytes (chamado LL), seguido por um campo de dois bytes que contém informações IMS (chamado ZZ). O campo de comprimento do segmento
de dois bytes representa o comprimento de todo o segmento da mensagem, incluindo os campos LL e ZZ. Os dados do segmento da mensagem
seguem os campos LL e ZZ. No caso do primeiro segmento de
Página 325

a mensagem de entrada da transação, até os primeiros n + 1 bytes da parte de dados do segmento, contém o código de transação de n bytes,
seguido por um espaço em branco.

Informações de logon do IMS

As informações de logon do IMS (ID do usuário, senha e nome do grupo) fornecidas para o contexto de tempo de execução de um aplicativo ou servlet
Java são usadas pelo IMS Connector for Java.

O ID do usuário, a senha e o nome do grupo são colocados na mensagem OTMA enviada pelo IMS Connector for Java para o
componente do host, IMS Connect. O IMS Connect então liga para o host Security Authorization Facility ( SAF) sob controle do IMS
Connect SETRACF comando.

Nível de Sincronização

Atualmente, todas as interações do IMS Connector para Java usam o protocolo OTMA Modo de confirmação 1, também conhecido como
send-then-commit. Sob esse protocolo, se o nível de sincronização for Confirmar, o IMS enviará a mensagem de saída ao cliente e, em
seguida, aguardará uma resposta do cliente. É responsabilidade do cliente responder ao IMS. Se o nível de sincronização for Nenhum, o
IMS confirma todas as alterações feitas pelo aplicativo IMS sem esperar por uma resposta do cliente.

Para Nível de sincronização nenhum, a transação é executada e o IMS envia a mensagem de saída ao cliente. Todas as alterações do banco de
dados são confirmadas. O IMS não requer que o cliente envie uma mensagem em resposta à mensagem de saída da transação para confirmar as
alterações do banco de dados. Nível de sincronização Nenhum é normalmente usado para aplicativos Java e servlets que executam transações IMS
que navegam ou consultam bancos de dados do host.
Y
FL

Para a confirmação do nível de sincronização, a transação é executada e o IMS envia a mensagem de saída para o cliente sem
M

confirmar nenhuma mudança no banco de dados. O IMS não conclui a transação até que o cliente responda à mensagem de saída da
A

transação enviando uma confirmação positiva ou negativa ao IMS. Se o cliente estiver satisfeito com a saída da transação, ele responde
TE

enviando uma confirmação positiva. O IMS então conclui a transação confirmando as alterações do banco de dados, se necessário. Se
o cliente não estiver satisfeito com a saída da transação (ou não quiser continuar com a transação por qualquer motivo), ele responde
enviando uma confirmação negativa. O IMS então reverte quaisquer alterações no banco de dados.

Gerenciamento de Conexão

O pool de conexão é a chave para aprimorar o desempenho de aplicativos Java ou servlets que acessam transações IMS. Este recurso
de gerenciamento de conexão é fornecido pela IBM Estrutura de conector comum ( CCF). CCF é um conjunto de APIs Java que fornece
serviços de infraestrutura como gerenciamento de conexão, serviços de transação, segurança e recursos de rastreamento para
aplicativos e servlets Java.

Conversas IMS

O IMS Connector para Java Conversational Support permite que os clientes criem aplicativos Java e aplicativos da Web para acessar
transações de conversação IMS. Uma transação IMS conversacional é uma transação que é definida para IMS como sendo
conversacional,

Team-Fly ®
Página 326

o que significa que pode processar transações compostas de várias etapas individuais. Uma transação IMS é composta de uma série
conectada de interações de cliente para programa para cliente. O programa de conversação IMS recebe mensagens do cliente,
processa as solicitações e responde ao cliente. Ele também salva os dados intermediários da transação no área do bloco de rascunho
( SPA). Quando o usuário insere mais dados do cliente, o programa tem os dados que salvou da última mensagem no SPA, e assim
pode continuar processando a solicitação sem que o usuário insira os dados novamente. Quando o cliente envia uma mensagem
para iniciar a próxima iteração da conversa, o programa usa os dados da nova mensagem junto com os dados que salvou no SPA ao
final da última iteração da conversa como sua entrada. Em conversas mais complexas, diferentes transações podem ser chamadas
usando uma troca de programa imediata ou adiada.

Em suma

A IBM forneceu ao programador Java de mainframe as ferramentas para acessar dois pacotes de software de sistema importantes e
comumente usados: IMS e CICS. Com essas ferramentas, os programadores Java têm acesso aos dados e aplicativos de mainframe existentes.
Página 327

CAPÍTULO 19
Java e DB2
Como parte da estratégia "Java Everywhere" da IBM, a IBM forneceu ao programador OS / 390 Java dois mecanismos de acesso para acessar
dados do DB2. Este capítulo discute dois mecanismos: usando Java Database Connectivity, ou JDBC, e usando SQLJ. Além disso, o
programador Java pode usar qualquer um dos mecanismos para acessar os dados do DB2 chamando os procedimentos armazenados do DB2.

O capítulo começa fornecendo uma visão geral de Java com DB2 no ambiente OS / 390. A seguir, o capítulo cobre como o
programador OS / 390 Java pode acessar dados do DB2 usando JDBC, SQLJ e procedimentos armazenados. O leitor verá trechos
de código ilustrando cada mecanismo de acesso. A seguir, uma comparação dos pontos fortes e fracos do JDBC e do SQLJ.

Visão geral de Java e DB2 para OS / 390

A Figura 19.1 mostra os principais componentes que constituem um ambiente Java / DB2 para OS / 390. O programador pode acessar dados do DB2
incluindo o DB2 estático em sua origem Java (SQLJ), DB2 dinâmico (JDBC) ou ambos. Além disso, o programador pode acessar os dados do DB2
fazendo com que seus programas Java acessem os procedimentos armazenados do DB2.

Se o programador Java usa SQLJ ou procedimentos armazenados, ele deve chamar um pré-processador do DB2 chamado
conversor SQLJ. Este tradutor fornece a mesma função que o pré-processador do DB2 para traduzir SQL estático integrado em
programas COBOL, PL / I ou C. Depois de uma tradução bem-sucedida do SQLJ, é normal com a produção de
Página 328

Figura 19.1 Ambiente Java / DB2 para OS / 390

um DBRM e um plano ou vinculação de pacote. O programador Java que usa JDBC para SQL dinâmico ignora essas etapas de DBRM / plano
ou vinculação de pacote.

O programador OS / 390 Java DB2 pode usar uma mistura de instruções SQL estáticas (incluindo procedimentos armazenados) e
dinâmicas como o programador COBOL DB2. Qualquer que seja a combinação de tipos de instrução SQL usados, o código chega ao
compilador Java, onde o compilador produz Java Bytecode padrão de mercado. A partir daqui, o Bytecode pode ser passado para o Máquina
Virtual JAVA ( JVM) para execução de programa ou para IBM Java de alto desempenho ( HPJ) compilador.

O compilador HPJ aceita Bytecode como entrada e produz código nativo OS / 390 como saída. A execução de plataforma cruzada de
Java usando formatos de Bytecode padrão vem em
Página 329

um preço. A execução do bytecode é interpretativa e, portanto, lenta quando comparada às linguagens tradicionais compiladas e vinculadas
como COBOL ou C. Para atender às necessidades dos clientes que desejam as vantagens do desenvolvimento de aplicativos Java
(código-fonte de plataforma cruzada ou o uso de técnicas orientadas a objetos, para exemplo) sem a degradação do desempenho de execução
comum com linguagens interpretativas, a IBM oferece o compilador HPJ. Como mostra a Figura 19.1, o mesmo código-fonte que é compilado e
enviado para a Java Virtual Machine pode ser enviado para o HPJ.

Java Database Connectivity (JDBC)

JDBC é uma API SQL padrão da indústria que permite ao programador Java acessar a maioria dos bancos de dados
relacionais, incluindo DB2. O programador deve incluir o pacote java.sql para usar JDBC. Este pacote inclui a seguinte classe e
interfaces abstratas:

DriverManager. Esta classe é usado para carregar o código do driver do DB2 necessário para criar conexões de banco de dados.

Conexão. Esta interface permite que um programador se conecte e desconecte de uma fonte de dados nomeada.

Declaração. Esta interface é usado para executar instruções SQL. Incluídas com a interface de declaração estão duas
interfaces:

Declaração preparada. Executa instruções SQL contendo parâmetros de entrada.

CallableStatement. Invoca um procedimento armazenado do DB2.

ResultSet. Esta interface é usado para recuperar os dados no conjunto de resultados de uma instrução SQL executada anteriormente.

Essas classes e interfaces não são exclusivas do ambiente OS / 390; qualquer programador Java que deseja acessar dados
de banco de dados relacional com Java usaria essas classes e interfaces.

Usando JDBC

Aqui estão as etapas que um programador Java executa para acessar os dados do DB2 com JDBC (com trechos de código):

1 Importe o pacote JDBC java.sql codificando:

import java.sql. *;

2 Carregue o driver do DB2 usando DriverManager:

String ibmDriver = "ibm.sql.DB2Driver";

tente {Class.forName (ibmDriver); }


catch (exceção ClassNotFoundException) {
exception.printStackTrace ();
}
Página 330

O método Class.forName cria automaticamente uma instância de um objeto driver. Um benefício de usar o método de classe da
classe Class (bastante complicado) é que o método forName () automaticamente registra o driver com o Java runtime. Observe que
você não precisa atribuir os resultados da invocação de forName () a nenhum objeto. Tudo isso faz parte da magia do Java.

3 - Declare e conecte-se a uma fonte de dados nomeada (banco de dados):

String db2URL = "jdbc: db2os390: mydb2db";


Connection myCon;

tente {myCon = DriverManager.getConnection (myURL); } catch (exceção SQLException) {

exception.PrintStackTrace ();
}

A fonte de dados nomeada identifica o banco de dados que será consultado ou atualizado. A forma da string é um URL. O DBA
responsável por manter o catálogo do DB2 deve associar a URL a algum objeto de acesso do DB2, como uma tabela, banco de dados ou
exibição. Em particular, para OS / 390, JDBC identifica uma fonte de dados para conexão aceitando uma URL de banco de dados no
seguinte formato:

jdbc: db2os390: <locationname>

onde <locationname> é o DB2 LOCATION encontrado na tabela do catálogo do DB2


SYSIBM.LOCATIONS.

Depois que a fonte de dados é identificada para o programa Java, o programa deve fazer um conexão para o objeto de acesso nomeado.
o getConnection () método na aula DriverManager faz isso. Se a invocação do método for bem-sucedida, o programa tem um objeto de
conexão, que o programa usa para emitir SQL e processar resultados.

4 - Execute uma ou mais instruções SQL criando um objeto Instrução, associando a (s) instrução (ões) a uma
conexão ativa e executando a instrução usando o método apropriado.

Para instruções SQL SELECT, o método apropriado é executeQuery (). Para instruções SQL que modificam ou criam tabelas, o
método apropriado é executeUpdate (). O código a seguir mostra um programa Java emitindo uma instrução SQL DELETE. A etapa 5
mostra um programa Java emitindo uma instrução SQL SELECT.

Instrução deleteStmt = myCon.createStatement (); // myCon é a conexão ativa

String deleteSQL = "DELETE FROM EMP WHERE UNIT = 'A001'"; deleteStmt.executeUpdate


(deleteSQL); // Executar declaração

As instruções SQL podem ser construídas usando o arsenal completo de métodos e operadores String. O trecho de código
anterior mostra uma string estática. No entanto, você pode construir a string concatenando argumentos e objetos String. O
seguinte é válido no mundo de Java e JDBC:

String tabKeyForDelete = "'A001'";


String deleteSQL = "DELETE FROM EMP WHERE UNIT =" + tabKeyForDelete;
Página 331

Observe a inclusão do aspas simples para dados de string. O DB2 reconhece aspas simples e duplas, desde que você
seja consistente. Se você omitiu as aspas simples ao redor do valor de UNIT no exemplo anterior, seu código geraria uma
SQLException.

5 Recupere os dados retornados por uma instrução SELECT criando um objeto ResultSet e acessando as linhas desse
ResultSet, geralmente com uma construção de loop.

Instrução selectStmt = myCon.createStatement (); ResultSet selectResults =

selectStmt.executeQuery ("SELECIONE NOME, SALÁRIO, UNIDADE DE EMP"); // Loop através de selectResults para
acessar cada linha do // objeto ResultSet

while (selectResults.next ()) {String javaName


= selectResults.getString (1);
float javaSalary = selectResults.getBigDecimal (2,2) .floatValue ();
String javaUnit = selectResults.getString ("UNIT");
}

O método next () de uma instância de ResultSet avança o cursor para a próxima linha. O cursor está inicialmente acima da primeira linha;
portanto, a primeira chamada para next () avança para a primeira linha no ResultSet. Next () retorna falso quando nenhuma linha seguinte
existe no ResultSet.

A classe ResultSet contém get ???? métodos, onde ???? é uma classe ou tipo de dados primitivo Java que corresponde a um tipo de dados SQL.
Um programa pode usar um método para acessar os dados do DB2, mas pode obter resultados inesperados. Por exemplo, um programa que
acessa um tipo DECIMAL como FLOAT pode resultar em perda de precisão. A Tabela 19.1 mostra o que obter ???? métodos da classe ResultSet
são recomendados para obter dados do DB2. Observe que o método recomendado para acessar um tipo de dados DB2 FLOAT é getDouble (), não
getFloat (). O método getObject () genérico pode ser usado para acessar dados de qualquer tipo, mas os resultados retornados podem ser
imprevisíveis.

Apenas as instruções SQL criam conjuntos de resultados. As instruções SQL INSERT, DELETE e UPDATE não.

O JDBC permite duas maneiras de identificar uma coluna no conjunto de resultados: por posição e por nome. Observe a primeira chamada para
getString () no snippet de código anterior:

String javaName = selectResults.getString (1);

A segunda chamada usa o nome da coluna da tabela, "UNIT":

String javaUnit = selectResults.getString ("UNIT");

O autor não tem conhecimento das vantagens de um método de identificação sobre o outro.

Como você sabe, a maioria das entidades indexadas em Java têm índices que começam com zero. Por exemplo, para acessar o primeiro elemento de
uma matriz Java, você acessa o elemento 0. No entanto, com entidades indexadas retornadas por chamadas JDBC, o índice começa com 1, não 0. A
chamada para getString () acessa anteriormente o primeira coluna no conjunto de resultados. Você foi codificar

String javaName = selectResults.getString (0);


Página 332

o compilador Java não detectaria o erro porque a assinatura do método afirma que o método requer um argumento
do tipo int ( ou String). Após a execução, seu programa lançaria um
SQLException.

Você pode usar o Declaração preparada interface para emitir instruções SQL. A interface PreparedStatement é usada para emitir pré-compilado
SQL. O ideal é que o DB2 possa realizar algumas otimizações na instrução SQL. Isso não é exatamente a mesma coisa que
pré-processar sua instrução SQL criando um plano.

Aqui está um snippet de código para emitir SQL com a interface PreparedStatement:

String SQLString = "SELECIONE NOME, SALÁRIO, UNIDADE DE EMP WHERE NAME =? E SALÁRIO>?" ;

Tabela 19.1 Métodos de acesso recomendados de acordo com o tipo de dados do DB2

DB2 DATA TYPE MÉTODO DE ACESSO RECOMENDADO

TINYINT getByte ()

SMALLINT getShort ()

INTEIRO getInt ()

BIGINT getLong ()

REAL getFloat ()

FLUTUADOR getDouble ()

EM DOBRO getDouble ()

DECIMAL getBigDecimal ()

NUMÉRICO getBigDecimal ()

MORDEU getBoolean ()

CARACTERES getString ()

VARCHAR getString ()

LONGVARCHAR getAsciiStream (), getUnicodeStream ()

BINÁRIO getBytes ()

VARBINÁRIO getBytes ()

LONGVARBINARY getBinaryStream ()

ENCONTRO getDate ()

TEMPO consiga tempo()

TIMESTAMP getTimeStamp ()
Página 333

PreparedStatement myStmt = myCon.prepareStatement (SQLString); // Lembre-se de que myCon é a conexão ativa

// Definir parâmetros
myStmt.setString (1, "Lou Marco"); myStmt.setDouble (2,
234567,89);
// Nenhum parâmetro para o método executeQuery ()
ResultSet selectResults = selectStmt.executeQuery ();
// Loop através de selectResults para acessar cada linha do // objeto ResultSet

while (selectResults.next ()) {String javaName


= selectResults.getString (1);
float javaSalary = selectResults.getBigDecimal (2,2) .floatValue ();
String javaUnit = selectResults.getString ("UNIT");
}

Os pontos de interrogação na string SQL são marcadores de posição. o setString () e setDouble () métodos são usados para preencher os espaços
reservados. Você deve conhecer os tipos de dados das colunas do DB2 que deseja acessar. Você pode substituir a palavra "get" pela palavra "set"
com os nomes dos métodos mostrados na Tabela
19.1 para definir valores de predicado SQL para SQL emitido com a interface PreparedStatement.

Observe que, ao usar a interface de instrução, a string SQL é passada para o executeQuery ()
método, enquanto com a interface PreparedStatement, a string SQL não é. Se você esquecer e codificar um argumento de string, não se
preocupe. O compilador Java irá detê-lo em suas trilhas.

A técnica usada para acessar o conjunto de resultados é a mesma, quer você use a interface Statement ou a interface
PreparedStatement.

6 Limpe fechando a declaração e fechando a conexão do banco de dados:

selectStmt.close (); // Fechar o extrato


selectResults.close (); // Fechar o conjunto de resultados myCon.close ()
; // Feche a conexão

O aplicativo deve ter um código que feche as instruções, o conjunto de resultados e a conexão ativa quando o aplicativo encontrar um
erro irrecuperável. Muitos programadores Java fazem chamadas para os métodos close () dentro de um finalmente quadra. Quando
colocados em um bloco finally, os métodos close () são garantido para executar mesmo se nenhuma exceção for lançada.

A estrutura do código Java que emite SQL é semelhante

tentar{
// carregar driver, criar conexão, emitir SQL, processar resultados}

catch (SQLException sqlE) {


// relatar quaisquer erros decorrentes da execução de SQL}

finalmente {
// Fechar declaração (ões), conjuntos de resultados (se houver) e conexão (ões)

}
Página 334

O uso de JDBC, com DB2 em OS / 390, não é diferente do uso de JDBC com outros produtos de banco de dados relacional em
sistemas operacionais diferentes. O único código específico do DB2 é o carregamento e registro do driver do DB2 usando o Class.forName
() método. A criação de instruções usando as interfaces Statement ou PreparedStatement é a mesma.

Java e SQLJ

SQLJ é uma forma padrão de embutir instruções SQL estáticas em programas Java. A metodologia geral é semelhante à
incorporação de SQL em programas COBOL ou PL / I. Um programa Java pode usar SQLJ e JDBC para acessar dados do DB2.

O padrão SQLJ inclui três partes: o SQL integrado, o conversor SQLJ e um ambiente de tempo de execução SQLJ. O programador
incorpora SQL em programas SQLJ precedendo a instrução SQL com o token #sql. Os programas SQLJ podem conter qualquer
manipulação de dados SQL, tabela DB2 DDL, COMMIT / ROLLBACK, UPDATE e DELETE pesquisados e posicionados, CALL para
acessar procedimentos armazenados e SET para variáveis de host. Os arquivos SQLJ contendo Java e SQLJ integrado devem ter
uma extensão sqlj.

O tradutor SQLJ produz fonte Java contendo SQL embutido em arquivos fonte Java. Dito de outra forma, o conversor SQLJ é
semelhante ao pré-processador do DB2 usado pelo programador COBOL, PL / I ou C que acessa os dados do DB2. O conversor SQLJ
também produz perfis que fornecem ao tempo de execução vários detalhes sobre o esquema do banco de dados. Esses perfis são
usados para criar DBRMS vinculados a pacotes ou planos.

O ambiente de tempo de execução SQLJ executa SQL usando, em parte, as informações encontradas no (s) perfil (s) gerado (s) anteriormente.
Normalmente, a implementação do SQL em tempo de execução é feita por meio de JDBC.

Usando SQLJ

Aqui estão as etapas que um programador executa para criar um programa Java / DB2 usando SQLJ.

1 Importe os pacotes java.sql e sqlj.runtime.ref codificando:

import java.sql. *;
import sqlj.runtime.ref. *;

2 Declare e estabeleça um contexto de conexão. Um contexto de conexão é o equivalente SQLJ de uma conexão de banco de dados. Em
JavaSpeak, um contexto de conexão é uma instância de uma classe de contexto de conexão. Veja como um programa SQLJ declara um
contexto de conexão:

# sql context myCtx;

Depois que o programa conhece o contexto de conexão, o programador tem uma escolha de dois métodos de conexão do
DB2. A primeira é invocar o construtor da classe criada pela declaração anterior usando a localização da fonte de dados do
DB2 como argumento. Aqui está um exemplo:
Página 335

// Observe a diferença no segundo qualificador // do nome do local


JDBC
Corda db2URL = "jdbc: db2os390sqlj: mydb2db";

myCtx myCon = new myCtx (db2URL);

O segundo método é invocar o construtor para a classe de conexão de contexto usando a conexão JDBC
retornada por DriverManager.getConnection (). Por exemplo,

Conexão myJDBCCon = DriverManager.getConnection (db2URL);


myCtx myCon = novo mtCtx (myJDBCCon);

3 - Carregue um driver do DB2 conforme mostrado na seção " Usando JDBC. "

4 - Declare iteradores do conjunto de resultados. Um iterador de conjunto de resultados é semelhante ao objeto ResultSet discutido
anteriormente. Na linguagem do SQL estático, um iterador do conjunto de resultados é como um cursor. Como um cursor, a declaração do
iterador identifica as colunas de uma tabela ou junção do DB2 a serem acessadas. Um recurso interessante dos iteradores não
compartilhados por seus primos, os cursores, é que os iteradores podem ser passados para métodos como argumentos (como outros
objetos Java), enquanto os cursores não.

Os iteradores SQLJ vêm em dois sabores: iteradores posicionados e iteradores nomeados. Um iterador posicionado associa as
colunas na tabela de resultados às colunas referenciadas no iterador na ordem da esquerda para a direita. Um iterador nomeado
associa essas colunas ao nome da coluna na tabela do DB2.
Y
FL

O programador declara as classes de iterador correspondentes às instruções SQL que produzem tabelas de conjunto de
resultados com mais de uma linha. O programador declara objetos das classes iteradoras. aqui estão alguns exemplos:
A M

// Por posição, o iterador mostra o tipo de dados da // coluna do conjunto de


TE

resultados.
# sql iterador ByposUnitIter (String);

// O iterador por nome mostra o tipo de dados // e o nome da


coluna do conjunto de resultados
# sql iterador BynameNameIter (unidade String);

É importante lembrar que as instruções #sql anteriores declaram classes Java e que o programa DB2 funciona com
objetos criados a partir dessas classes.

5 Execute a (s) instrução (ões) SQL. Se as instruções SQL acessam mais de uma linha, a instrução SQL pode se referir a um
iterador declarado anteriormente. Aqui está um exemplo:

// Declara um objeto da classe ByposUnitIter ByposUnitIter


aniterByPos;
// Constrói o objeto iterador e executa a instrução
# sql (myCon) aniterByPos =
{SELECIONE A UNIDADE DE EMP WHERE EMPID = 23790};

Team-Fly ®
Página 336

// Faça o FETCH, faça um loop no iterador


# sql (myCon) {FETCH: aniterByPos INTO : javaUnit};
while (! aniterByPos.endFetch ()) {
/ * Fazer coisas com os dados recuperados * /
# sql (myCon) {FETCH: aniterByPos INTO : javaUnit};
}

Como embutir SQL em um programa COBOL ou PL / I, o programador Java passa dados entre o programa Java e o DB2 usando expressões
de host. Uma expressão de host Java pode ser um identificador Java, como mostra a instrução FETCH anterior, ou uma expressão Java
que avalia um valor, precedido por dois pontos. Os identificadores Java, portanto, expressões de host, fazem distinção entre maiúsculas
e minúsculas. Aqui está um exemplo de código Java que usa um iterador nomeado:

// Declara um objeto da classe BynameNameIter // Constrói o objeto iterador e executa a


instrução BynameNameIter aniterByName;

# sql (myCon) aniterByName =


{SELECIONE O NOME DA UNIDADE DE EMP ONDE = 'A001'}; // Obtenha
os dados, faça um loop para baixo do iterador enquanto (aniterByName.next ()) {

/ * Faça coisas com os dados recuperados * / javaName =


aniterByName.Unit (); }

Observe o nome do método acessador fornecido ao programa da classe iteradora:


aniterByName.Unit (). SQLJ gera um método acessador para cada coluna nomeada em
o iterador. Para os casos em que o nome da coluna do DB2 não é um identificador Java válido, o programador
pode usar o recurso AS da instrução SELECT. Por exemplo,

// Declara a classe do iterador nomeado


# sql iterador aNameIter ByName (String MyCol);
// Declare o iterador da classe aNameIter myIter;

# sql myIter =
{SELECIONE "BOM NOME JAVA DO DB2 BAD" COMO MYCOL DE ATABLE};

6 Confirme todas as alterações invocando o método commit () do objeto Connection com código semelhante ao
seguinte:

myCon.commit ();

7 Por fim, feche quaisquer iteradores e feche (desconecte) do banco de dados:

myIter.close ();
myCon.close ();
Página 337

Como acontece com uma conexão JDBC, o programa deve se desconectar do banco de dados quando encontrar um erro
irrecuperável.

Comparando JDBC com SQLJ.

Os programas Java que acessam dados do DB2 podem usar JDBC e SQLJ. No entanto, se um programa Java precisar emitir SQL dinâmico, esse
programa deve usar apenas JDBC. A natureza dinâmica do JDBC tem impacto na segurança. DBAs podem conceder aos usuários acesso às
tabelas; um usuário pode alterar uma tabela ou não. Em contraste, um programa que usa SQLJ usa um pacote ou plano previamente vinculado. O
DBA pode conceder aos usuários autoridade de execução para o pacote ou planos.

Claro, o velho argumento da velocidade de execução ainda se aplica. Os programas que usam instruções SQL vinculadas estaticamente são
executados mais rapidamente do que programas que usam instruções SQL vinculadas dinamicamente. Além disso, os caminhos de acesso contidos
nos DBRMs podem ser analisados e ajustados, gerando mais ganhos de desempenho.

O programa JDBC não pode verificar se os tipos de dados SQL correspondem aos tipos ou classes primitivas Java até o tempo de execução. Em
programas que usam SQLJ, a verificação de tipo é feita no estágio do tradutor (pré-compilador).

Por outro lado, o uso de SQLJ limita a portabilidade do aplicativo. Cada fornecedor poderia fornecer seu tradutor SQLJ, que seria
diferente para produtos diferentes. Além disso, o processo de desenvolvimento do SQLJ é mais complexo devido à necessidade de
tradução (pré-compilação) e vinculação.

Se o desenvolvimento Java / DB2 for paralelo ao desenvolvimento DB2 em COBOL, PL / I ou C, é uma aposta segura assumir que a maioria
dos programas Java / DB2 usaria SQL estático mais do que SQL dinâmico.

O programador de mainframe do DB2 é usado para pré-compilar seu SQL e produzir planos. Não há recursos em Java que
auxiliem o programador na criação de planos. Você, o programador Java DB2, continuará a usar os produtos de sistema
operacional da IBM que pré-compilar SQL, vincular planos e (opcionalmente) criar pacotes.

Em suma

O programador Java tem acesso ao principal produto de banco de dados da IBM, DB2. Como o DB2 está disponível em todas as plataformas IBM, a
promessa do Java, "Escreva uma vez, execute em qualquer lugar", está mais próxima da realidade no mundo IBM.
Página 338

Esta página foi intencionalmente deixada em branco.


Página 339

CAPÍTULO 20
O Sistema de Agendador de Aula do Departamento de Treinamento revisitado

Revisitaremos o programador do departamento de treinamento mostrado no Capítulo 13, "O sistema do programador de aulas do departamento de
treinamento". Neste capítulo, você verá uma função do sistema feita com chamadas de banco de dados DB2 em oposição ao uso de E / S de arquivo
Java nativo.

O capítulo começa descrevendo o recurso implementado e mostra uma solução de mainframe. A seguir, apresentamos uma solução
Java que implementa o recurso. Não repetimos as descrições dos dados; deixamos que o leitor consulte o Capítulo 13 para obter
esses detalhes. Concluímos o capítulo com um resumo das questões-chave abordadas no capítulo.

O recurso do aplicativo definido

O recurso que mostraremos aqui é acessar três tabelas com uma junção SQL para criar uma lista de classes oferecidas depois de um parâmetro
de entrada de data. O SQL fará referência a nomes de tabelas para que você possa ver de onde vêm as colunas. Todos os dados representados
nas tabelas são strings.

O SQL usado no exemplo

A Listagem 20.1 mostrada aqui é o SQL que recuperará os dados necessários.

A quantidade ENTEREDDATE é o parâmetro de data utilizado para qualificar a lista de turmas. Claro, o mesmo SQL será
usado na linguagem procedural e na solução de linguagem Java.
Página 340

selecione classinfo.classid, courseinfo.coursetopic,


instructorinfo.instructorfirstname || "" || instructorinfo.instructorlastname,

classinfo.dateoffered, classinfo.roomnumber
from courseinfo, instructorinfo, classinfo
onde courseinfo.courseid = substr (classinfo.classid, 1, 5) e
classinfo.instructorid = instructorinfo.instructorid e
classinfo.dateoffered> ENTEREDDATE

Listagem 20.1 SQL para recuperar dados.

Uma solução de linguagem procedural para a opção "Exibir lista de turmas depois da
data inserida"

A Listagem 20.2 mostrada aqui é um módulo COBOL que executa as seguintes tarefas:

Aceita um parâmetro de data

Cria uma tabela ISPF para exibir as saídas

Emite o SQL mostrado na Listagem 20.1, reformatado para se adequar aos requisitos de codificação de coluna do COBOL

Busca uma linha recuperada da consulta e adiciona os dados da linha a uma tabela ISPF Salva códigos de retorno para

enviar de volta para a rotina de chamada

Comentários sobre a solução COBOL

O módulo COBOL trata da execução SQL e da criação da estrutura (uma tabela ISPF) que exibirá a saída. O módulo pode ter sido
codificado para passar os dados recuperados pela consulta SQL de volta ao módulo de chamada, deixando o módulo de chamada
construir a tabela ISPF. No entanto, a falta de suporte do COBOL para todas as estruturas de dados, exceto as mais elementares,
torna essa abordagem estranha.

Dada a natureza procedimental das linguagens de programação de mainframe, incluindo COBOL, a abordagem é centrada em torno do processo.
O código mostrado na Listagem 20.2 flui naturalmente. No entanto, não há esperança real de reutilização além de copiar e colar o código de
um conjunto de dados para outro.
Página 341

DIVISÃO DE IDENTIFICAÇÃO.

ID DO PROGRAMA. CBLEX.
DIVISÃO DE MEIO AMBIENTE.
SEÇÃO DE CONFIGURAÇÃO.
SOURCE-COMPUTER. IBM-OS390.
OBJETO-COMPUTADOR. IBM-OS390.
SEÇÃO INPUT-OUTPUT.

FILE-CONTROL.
SELECIONE O PRINTFILE ASSIGN TO PRINTER-QPRINT
A ORGANIZAÇÃO É SEQUENCIAL.

DIVISÃO DE DADOS.

SEÇÃO DE ARQUIVOS.

FD PRINTFILE
O BLOCO CONTÉM 1 ETIQUETA DE REGISTROS OS
REGISTROS SÃO OMITIDOS. IMPRIMIR-GRAVAR
01 IMAGEM X (132).

WOR SEÇÃO KING-STORAGE.


*****************************************************************
* Esta string contém os Comandos da Tabela ISPF para adicionar dados, *
* criar e exibir a tabela de saída ISPF. *
*****************************************************************
01 TABLECOMMANDS.
05 TBADD PIC X (15)
VALOR "TBADD DTELATER".
05 TBCREATE PIC X (72)
V ALUE "TBCREATE DTELATER NAMES (CLASSID, CTOPIC, INSTNAME,
- DOFFERED, ROOMNUM) ".
05 TBDISPL PIC X (17) VALOR "TBDISPL DTELATER".
*****************************************************************
* Código de retorno do ISPF *
*****************************************************************
01 ISPFRC PIC S9 (4) COMP.

*****************************************************************
* Estrutura que contém dados resultantes de SQL Join *
*****************************************************************

01 SQLJOINDATA.
05 CLASSID PIC X (8).
05 CTOPIC PIC X (15).
Página 342

05 INSTNAME PIC X (10).


05 DOFFERED PIC 99/99/9999.
05 ROOMNUM PIC X (5).
** **************************************************************
* A Área de Comunicações SQL *
** **************************************************************
EXEC SQL
INCLUIR SQLCA
END-EXEC.
** **************************************************************
* A seção de ligação contém códigos de retorno retornados ao *
* rotina de chamada e a data da consulta. *
** **************************************************************

SEÇÃO DE LIGAÇÃO.

01 ENTDATE PIC 99/99/9999.


01 ISPFRETURN PIC S9 (4) COMP.
01 SQLCODERETURN PIC S9 (4) COMP.

DIVISÃO DE PROCEDIMENTO.

A000-MAIN.

******************************************************************
* Emita o SQL Join. Relate quaisquer erros. *
* *
******************************************************************

EXEC SQL
SEMPRE QUE SQLERROR IR PARA E000-SET-RETURN-CODES. END-EXEC.

** ****************************************************************
* Emita o SQL que gera um conjunto de resultados contendo linhas *
* com dados para aulas oferecidas depois da data passada. *
** ****************************************************************
exec sql
declarar cursor c1 para
SELECT ClassID, CourseTopic, InstFName || "" ||
InstLname, DateOffered, RoomNum
de CourseInfo, ClassInfo, InstructorInfo onde DateOffered>: ENTDATE e

ClassInfo.InstID = InstructorInfo.InstID e CourseID = Substr (ClassID, 1, 5)

end-exec.
Página 343

EXEC SQL
ABRIR C1
END-EXEC.

LIGUE PARA 'ISPEXEC' USANDO TBCREATE, ISPFRC. SE


ISPFRC> 0
PERFORM E000-SET-RETURN-CODES THRU
EOOO-SET-RETURN-CODES-EXIT.

EXECUTE B000-ADICIONE-LINHAS-PARA-SAÍDA-TELA ATRAVÉS DA TELA


B000-ADD-ROWS-TO-OUTPUT-SCREEN-EXIT.
ATÉ SQLCODE NÃO É IGUAL A ZERO.

A100-DONE.
EXEC SQL
FECHAR C1
END-EXEC.

** ****************************************************************
* Tudo feito. Defina os códigos de retorno e divida. *
** ****************************************************************

900-MAIN-EXIT.
UMA

PERFORM E000-SET-RETURN-CODES THRU


E000-SET-RETURN-CODES-EXIT.
SAÍDA.

** ****************************************************************
* Buscar e adicionar linha à tabela ISPF *
** ***************************************************************s*

B 000-ADICIONE LINHAS À TELA DE SAÍDA.


EXEC SQL
SEMPRE QUE NÃO ENCONTRAR, ACESSE A100-DONE
END-EXEC.
EXEC SQL
FETCH C1 INTO: CLASSID,
: CTOPIC,: INSTNAME,
: DOFFERED,: ROOMNUM
END-EXEC.

LIGUE PARA "ISPEXEC" USANDO TBADD, ISPFRC. SE


ISPFRC> 0
PERFORM E000-REPORT-ERROR THRU
EOOO-REPORT-ERROR-EXIT.
Página 344

B000-ADD-ROWS-TO-OUTPUT-SCREEN-EXIT.
SAÍDA.

** ****************************************************************
* Ocorreu um erro ao emitir SQL. Definir retorno de SQL e ISPF *
* códigos para retornar valores e dividir. *
** ****************************************************************

E 000-SET-RETURN-CODES.
MOVE SQLCODE TO SQLCODERETURN. MOVER
ISPFRC PARA ISPFRETURN.
E 000-SET-RETURN-CODES-EXIT.
SAÍDA.

Listagem 20.2 Uma solução COBOL.

Uma solução de linguagem Java para a opção "Exibir lista de turmas depois da data
inserida".

As listagens de código que se seguem mostram classes Java que contêm métodos que emitem SQL para capturar ofertas de classe que começam
após a data inserida. Apresentamos o código para quatro classes Java. Cada classe contém métodos para um conjunto de tarefas relacionadas.

O código para uma única classe recuperado do banco de dados

A Listagem 20.3 mostra o código Java que modela as informações relevantes para uma única classe. Cada instância da classe mostrada será um
elemento de outra classe que contém todas as classes oferecidas depois da data inserida. As variáveis de instância não são declaradas
privadas; você pode declarar as variáveis de instância privadas e os métodos get / set de código para recuperar ou alterar os valores conforme a
necessidade.

A Listagem 20.4 mostra uma classe Java, sqlClass, com responsabilidades limitadas, mas importantes. A classe sqlClass possui métodos que
estabelecem uma conexão com o banco de dados e se desconectam do banco de dados. Com um pouco de polimento, a classe sqlClass poderia ser
uma excelente utilidade. Em particular, precisaríamos de um mecanismo para obter as informações do driver e o nome do banco de dados (URL) dos
parâmetros.

Classe queryClass, mostrado na Listagem 20.5, possui métodos que emitem a consulta e retornam um valor correspondente a uma única coluna no
conjunto de resultados. A classe queryClass contém a consulta sql como uma string. No entanto, a string de consulta pode ser passada como um
parâmetro.
Página 345

public class CourseOfferedLaterThan {


String classID;
String courseTopic;
String instructorName;
String dateOffered;
String roomNumber;
public CourseOfferedLaterThan (String cID, String cTopic,
String iName, string dOff, String roomNum) {

classID = cID;
courseTopic = cTopic; instructorName =
iName; dateOffered = dOff; roomNumber =
roomNum;

)
}

Listagem 20.3 Código Java para uma única classe.

import java.sql. *;
import java.io. *;
public class sqlClass
{
private String myDriver = "ibm.sql.DB2Driver"; private String myURL = "jdbc:
db2os390: classdb"; conexão protegida myConn;
Y

public sqlClass () {}
FL

public void makeConnection () lança exceção


{
Class.forName (myDriver);
M

myConn = DriverManager.getConnection (myURL);


A

}
TE

public void disconnectFromDB () lança exceção


{
myConn.close ();
}
}

Listagem 20.4 Código Java para a classe sqlClass.

Team-Fly ®
Página 346

import java.sql. *;
import java.io. *;
public class queryClass extends sqlClass
{
String mySelectQuery =
"SELECIONE ClassID, CourseTopic, InstFName ||" "||" +
"InstLname, DateOffered, RoomNum" +
"de CourseInfo, ClassInfo, InstructorInfo" + "onde ClassInfo.InstID = InstructorInfo.InstID" +

"e CourseID = Substr (ClassID, 1, 5)" + "e DateOffered>";

ResultSet myResultSet = null; public queryClass ()


{super ();}
public boolean getCourses (String enterDate) lança exceção
{
String myQuery = mySelectQuery + enterDate; Instrução stmt =
myConn.createStatement (); myResultSet = stmt.executeQuery (myQuery);

return (myResultSet! = null); }

public boolean getNextCourse () lança exceção


{
return myResultSet.next ();
}

public String getColumn (String inCol) lança exceção


{
return myResultSet.getString (inCol);
}
}

Listagem 20.5 Código Java para a classe queryClass.

Observe que queryClass estende sqlClass. É por isso que a referência a myConn, um objeto da classe Connection, é compreendido
em queryClass, embora myConn não seja declarado em queryClass.

Classe CoursesLaterThanOutput, mostrado na Listagem 20.6, modela uma lista de classes que são oferecidas depois de uma data
especificada. A classe contém um construtor que invoca todos os métodos necessários das classes sqlClass e queryClass. Já que
queryClass estende sqlClass, o construtor pode invocar o
desconectarFromDB () método definido em sqlClass referenciando um objeto da classe queryCla