Baixe o app para aproveitar ainda mais
Prévia do material em texto
Programação Orientada a Objetos I VINÍCIUS GODOY Código Logístico 58878 Fundação Biblioteca Nacional ISBN 978-85-387-6531-8 9 7 8 8 5 3 8 7 6 5 3 1 8 A orientação a objetos representou uma revolução na forma de construir softwares. Desde que se popularizou, em torno dos anos 1990, se tornou parte das principais linguagens de programação utilizadas no mercado, como Java, C++ e C#. Ela permitiu a divisão de programas em partes menores e reutilizáveis e a construção de sistemas cada vez mais complexos. Não é à toa que hoje é utilizada em programas não só em computadores e laptops, mas também em celulares, tablets e até mesmo em carros e geladeiras. Programação orientada a objetos I IESDE 2019 Vinícius Godoy Todos os direitos reservados. IESDE BRASIL S/A. Al. Dr. Carlos de Carvalho, 1.482. CEP: 80730-200 Batel – Curitiba – PR 0800 708 88 88 – www.iesde.com.br © 2019 – IESDE BRASIL S/A. É proibida a reprodução, mesmo parcial, por qualquer processo, sem autorização por escrito do autor e do detentor dos direitos autorais. Projeto de capa: IESDE BRASIL S/A. Imagem da capa: IESDE BRASIL S/A. CIP-BRASIL. CATALOGAÇÃO NA PUBLICAÇÃO SINDICATO NACIONAL DOS EDITORES DE LIVROS, RJ M497p Godoy, Vinícius Programação orientada a objetos I / Vinícius Godoy. - 1. ed. - Curitiba [PR] : IESDE Brasil, 2019. 146 p. : il. Inclui bibliografia ISBN 978-85-387-6531-8 1. Java (Linguagem de programação de computador). 2. Programação orientada a objetos (Computação). I. Título. 19-60075 CDD: 005.117 CDU: 004.43 Vinícius Godoy Mestre em Visão Computacional pela Pontifícia Universidade Católica do Paraná (PUCPR), especialista em Desenvolvimento de Jogos de Computadores pela Universidade Positivo (UP) e graduado em Tecnologia em Informática pela Universidade Tecnológica Federal do Paraná (UTFPR). Trabalha na área de informática desde 1997, tendo participado de grandes projetos, como a edição de 100 anos do Dicionário Aurélio Eletrônico. Também atua como moderador do GUJ, o maior fórum de tecnologia Java do Brasil. Sumário Apresentação 7 1 Olá, Java! 9 1.1 Breve histórico 9 1.2 Arquitetura da plataforma 10 1.3 Instalando o ambiente 12 1.4 O primeiro programa Java 16 2 Conhecendo a linguagem 25 2.1 Variáveis e tipos de dados 25 2.2 Controle de fluxo: estruturas de decisão 34 2.3 Controle de fluxo: estruturas de repetição 36 2.4 Escopo de variáveis 39 3 Classes e objetos 43 3.1 Classes e objetos no mundo real 43 3.2 Sua primeira classe 45 3.3 A palavra-chave static 51 3.4. O valor especial null 54 4 Compondo objetos 57 4.1 Classificação no mundo real: o todo e suas partes 57 4.2 Associando objetos 59 4.3 Pacotes 61 4.4 Encapsulamento e modificadores de acesso 63 4.5 Referência a valor 66 5 Hierarquias de classes 71 5.1 Classificação no mundo real: biologia 71 5.2 Apresentando o problema 73 5.3 Herança 77 5.4 Polimorfismo 81 5.5 Classes e métodos abstratos 83 5.6 Interfaces 84 6 Generics e lambda 89 6.1 O que são generics 89 6.2 Generics 91 6.3 Lambda 94 7 A biblioteca de coleções 101 7.1 Listas 101 7.2 Conjuntos 105 7.3 Mapas 111 7.4 Streams 112 8 Tratamento de erros 117 8.1 Entendendo o problema 117 8.2 Disparando exceções 120 8.3 Capturando exceções 124 Gabarito 133 Apresentação Prezado aluno, Neste livro, você começará a desvendar o paradigma orientado a objetos. Ainda me lembro quando, no ano de 1997, um dos meus professores da universidade nos mostrou "um novo recurso de programação que talvez cole". Pela primeira vez, fui apresentado ao conceito de classes, descobri uma nova forma de programar. Fiquei deslumbrado, em meio a colegas meio céticos, com como aqueles recursos permitiam resolver problemas de uma maneira muito mais natural. Foi nesse mesmo ano que conheci uma plataforma de programação que prometia se destacar pela forte integração com outra tecnologia ascendente: a internet. Essa plataforma era o Java, que apresentava uma máquina virtual, tornando-a compatível com vários sistemas operacionais ao mesmo tempo. Seus críticos alegavam que isso tornava a linguagem muito lenta, que consumia muita memória e que ela jamais ganharia força no mercado. Hoje, a orientação a objetos se tornou o padrão da indústria e aquela plataforma de programação sobreviveu em meio a vários concorrentes. Isso mostrou o quão acertada estava a visão de seus criadores, que miraram em necessidades do futuro, prevendo a evolução tecnológica do hardware e da rede mundial de computadores. A cada ano, a relevância da plataforma cresceu: conquistou os servidores de internet e os dispositivos móveis Android e passou a ser parte da ementa de praticamente todas os cursos de informática. Sua máquina virtual tornou-se extremamente veloz e moderna, um conjunto de classes e recursos bastante robustos foi apresentado e uma comunidade se organizou para propor melhorias na plataforma e mantê-la elegante e fiel a seus princípios. Ao escrever esse livro, além da orientação a objetos, procurei gerar um material atualizado, com vários detalhes e recursos da última versão disponível do Java. Além disso, complementei o material com dicas de programação, vindas tanto de livros e materiais de referência internacionais quanto diretamente do cotidiano de um programador. Espero que você aprecie o material e, assim como o jovem Vinícius, se deslumbre e transforme sua forma de programar. Pronto?! Hora de embarcar nessa jornada... 1 Olá, Java! Em nosso livro de programação orientada a objetos, iremos utilizar a plataforma Java. O Java é a linguagem presente em servidores de internet e em celulares Android. Neste primeiro capítulo, aprenderemos o que é a plataforma e como ela está organizada, instalaremos o ambiente e você conseguirá compilar e executar seu primeiro programa em Java. Pronto para aprender um pouco mais? 1.1 Breve histórico Em dezembro de 1990, um time de 13 engenheiros da Sun Microsystems, liderado por James Gosling, Mike Sheridan e Patrick Naughton, iniciou o desenvolvimento do Projeto Green, com o objetivo de desenvolver uma plataforma que representaria a próxima onda tecnológica do mundo. Na visão deles, pessoas poderiam controlar diversos dispositivos, como TVs e telefones, de maneira integrada (GOSLING, 1998). O time focou-se no desenvolvimento de um PDA, uma espécie de tablet antigo, sem fio, capaz de executar animações e controlar vários dispositivos. Esse produto foi chamado de *7 (Star 7) e, embora não tenha chegado ao mercado, levou ao desenvolvimento de diversas tecnologias interessantes. Uma delas foi a linguagem Oak, capaz de executar em diversos dispositivos, que posteriormente se tornaria a linguagem Java. Além da linguagem, uma grande biblioteca de software permitia que seus desenvolvedores fizessem animações, comunicassem via rede e realizassem várias operações no dispositivo de maneira mais fácil. Quando o projeto começou a crescer, os projetistas logo perceberam o potencial da plataforma para a internet. O time trabalhou em um clone do navegador Mosaic, chamado WebRunner, que demonstrava o poder da tecnologia Java. Nele, era possível combinar e executar aplicações Java de maneira segura, em meio a páginas HTML e CSS. A tecnologia foi demonstrada na Conferência de Tecnologia, Design e Entretenimento, no início de 1995, em Monterey, capturando a atenção da audiência ao fazer o desenho de uma molécula em 3D se mover controlada pelo mouse (BYOUS, 2004). Tal tipo de interação, comum hoje em dia, não era possível na internet daquela época. Em junho de 1996, a primeira versão da plataforma Java foi oficialmente disponibilizada para o público, com a promessa de "escrever uma vez e rodar em todo lugar" (SUN MICROSYSTEMS, 1996). Em 1997, a Sun tentou formalizar o Java, mas desistiu do processo, entretanto, isso fez com que ela criasse o Java Community Process (JCP), permitindo que modificações fossem propostas de forma pública e transparente. De novembro de 2006 até maio de 2007,a Sun tornou pública boa parte do código da Máquina Virtual Java, por meio da licença GPL. Nos anos de 2009 e 2010, a Oracle comprou a Sun Microsystems e passou a ser dona da tecnologia. Programação orientada a objetos I10 Este material utilizará a versão 12 da linguagem Java, lançada pela Oracle em março de 2019, que, mesmo após tantos anos, ainda respeita os princípios básicos dos designs originais (SUN MICROSYSTEMS, 1997): • Simples, orientada a objetos e familiar; • Robusta e segura; • Neutralidade de arquitetura e portável; • Alta performance; • Interpretada, multithread1 e dinâmica. É importante ter esses objetivos em mente, pois será mais fácil entender por que os projetistas da linguagem criaram determinados recursos. 1.2 Arquitetura da plataforma Quando falamos em Java, é importante entender que não estamos nos referindo única e simplesmente a uma linguagem de programação. O Java é uma plataforma que inclui: • Bibliotecas de classes: que permitem que você trabalhe facilmente com arquivos, interface gráfica, redes, entre outras funcionalidades. • Ferramentas e utilidades: que envolvem ampla documentação, analisadores de performance e um empacotador de arquivos. • Linguagem Java: que cobriremos intensamente nos demais capítulos do livro. • Máquina Virtual Java: que explicaremos a seguir. Todos esses elementos fornecem um ambiente poderoso, que permitirá que façamos aplicações comerciais robustas. 1.2.1 A Máquina Virtual Java (JVM) Para que um programa de computador funcione, é necessário converter comandos em forma de textos como este: print "Olá mundo!" Em um código de máquina, muito específico e pouco legível, formado de milhares de instruções como estas: AE 1F 91 AA 85 FF C4 1B 32 ... Há duas formas de realizar essa tarefa (MENDONÇA, 2018): • Por meio de um compilador, que traduz o programa inteiro, gerando um arquivo executável. Como esse programa já está na linguagem que o computador entende, sua execução é muito rápida. 1 Multithreading refere-se à capacidade de executar, em um único programa, várias tarefas ao mesmo tempo (GOETZ, 2018). Olá, Java! 11 • Por meio de um interpretador, que traduz o software linha a linha, à medida que o executa. Como o processo de tradução ocorre enquanto o software é executado, a execução é mais lenta, o programador analisa o software. Para você entender melhor essas ferramentas, vamos fazer uma analogia. Vamos supor que você seja o computador e que você só entenda a língua portuguesa. O programador escreveu o software em inglês. Um compilador seria análogo a um tradutor. Ele pegaria um livro inteiro (programa) e o traduziria para o português, de modo que você poderia ler o livro, sem a presença do tradutor ou mesmo do livro original. Já o interpretador seria equivalente a um intérprete, ou seja, à medida que ele lesse uma frase do livro, ele traduziria para você. Note que, para que isso seja possível, o intérprete precisa do livro original em mãos e precisa sempre estar presente. Por outro lado, esse processo é mais interativo, já que você pode tirar dúvidas com o intérprete. Como um dos objetivos da plataforma Java sempre foi garantir uma execução segura e capaz de rodar no maior número de dispositivos possível, uma abordagem híbrida foi implementada (SUN MICROSYSTEMS, 1997). Nessa abordagem, o compilador Java (javac) transformará seu código no bytecode Java. Esse bytecode é um arquivo muito mais próximo da linguagem de máquina do que o código escrito em Java, mas ele ainda não pode ser diretamente executado pelo computador. A execução final desse código, já otimizado, será realizada de maneira interpretada pela Máquina Virtual Java (JVM) instalada. A JVM possui habilidades bastante poderosas, como a capacidade de analisar trechos do bytecode mais utilizados e compilá-los de fato (compilação just-in-time). Além disso, por conhecer a plataforma onde o código está realmente sendo executado, a máquina virtual pode habilitar instruções específicas de modo a aproveitar ao máximo seus recursos. A máquina virtual também pode garantir um ambiente de execução seguro, proibindo que programas maliciosos executem instruções que prejudiquem o computador (ORACLE, 1997). Além disso, pode fornecer ao programador diversas ferramentas para análise do código e medições de performance, tal como uma linguagem interpretada faria. Figura 1 – Compilação e execução na plataforma Java Compilador Empacotador Execução do código final Bytecode compilado programa.class cadastro.class Bytecodes agrupados programa.jar Bytecodes agrupados programa.jar Código-fonte programa.java cadastro.java Execução Compilação Máquina Virtual Java (JVM) Fonte: Mendonça, 2018, p. 110. Programação orientada a objetos I12 A desvantagem dessa abordagem é que o usuário final do programa precisa instalar a máquina virtual mesmo sem entender exatamente o que ela é. Outra desvantagem é que geralmente bytecodes são facilmente reversíveis em seu código-fonte original. Finalmente, embora sejam consideravelmente mais rápidas do que as linguagens puramente interpretadas, esse processo tem impacto na performance que pode ser difícil de medir. Isso dificulta a criação de aplicações de tempo real, tais como reprodutores de vídeo ou jogos. 1.2.2 Como a plataforma é distribuída A plataforma Java é distribuída em dois pacotes: • Java Runtime Environment (JRE): é o pacote de execução. Contém apenas a JVM e o código compilado das bibliotecas padrão. Qualquer pessoa interessada em executar um programa Java deverá ter a JRE instalada em sua máquina. • Java Development Kit (JDK): é o pacote de desenvolvimento. Contém o JRE em conjunto com o compilador Java, códigos-fonte, documentação e ferramentas. Deve ser instalado pelo desenvolvedor para criar projetos. Você pode estar se perguntando: o que é o JEE? Não seria também um pacote, só que mais poderoso? JEE é a sigla de Java Enterprise Edition. Embora soe como uma versão "mais completa" do Java, não é disso que se trata. Ele é uma especificação sobre como diversos serviços adequados a aplicações de rede empresariais (como persistência, mensageria etc.) devem funcionar (ORACLE, 2019a). A implementação desses serviços é feita por mais de 20 empresas terceiras (ORACLE, 2019b). Por exemplo, a JEE descreve a JPA (Java Persistence API), que descreve classes para uso de banco de dados. Tanto o Hibernate (Red Hat) quanto o Toplink (Oracle) são projetos que implementam essa especificação. Graças ao JEE, é possível montar um servidor web com diversas tecnologias, de diversos fabricantes, comunicando entre si. Porém, o estudo do JEE está fora do escopo da nossa disciplina. 1.3 Instalando o ambiente Antes de começarmos a desenvolver, precisaremos instalar todo o ambiente de desenvolvimento. Isto é, precisamos instalar o JDK, contendo compilador, Máquina Virtual Java, documentações e um editor de códigos, também chamado de ambiente integrado de desenvolvimento (IDE). Com esse ambiente em mãos, você poderá executar e testar os exemplos deste livro e resolver os exercícios propostos ao final de cada capítulo. Olá, Java! 13 1.3.1 Instalação do JDK Antes de começarmos a desenvolver, precisamos instalar o JDK. Para isso, entre no site Oracle (disponível em: https://www.oracle.com/technetwork/java/index.html). Você também pode usar o antigo site da Sun, que é muito mais fácil de decorar (disponível em: java.sun.com). Nele, clique em Java SE. Figura 2 – Tela inicial do site Oracle Fonte: Oracle. Em seguida, clique no ícone do JDK. Figura 3 – Página Java SE Downloads Fonte: Oracle. Programação orientada a objetos I14 Na parte inferior da tela, clique em Accept License Agreement e então escolha o instalador de acordo com sua plataforma, por exemplo, no caso do Windows: Figura 4 – Aceitação do contrato de licença Fonte: Oracle. Siga o passo a passo do instalador e aguarde a conclusão do processo de instalação. 1.3.2 Instalando um IDE Nós podemos escrevertodo o nosso código em editores de textos e executar a compilação manualmente, porém, esse é um procedimento tedioso, comparável a tentar escrever um livro usando somente o bloco de notas. Ao longo dos anos, programadores criaram ambientes que nos auxiliam na codificação, chamados de Integrated Development Environment (IDEs). Esses ambientes fornecem ferramentas para análise do código, colorização de comandos, recurso de autocompletar inteligente, execução automática do código etc. No caso do Java, existem três IDEs poderosos e gratuitos: • Netbeans IDE, da Oracle; • Eclipse, da Eclipse Foundation; • IntelliJ IDEA, da Jetbrains. Nós instalaremos o último. Para isso, acesse o site da empresa Jetbrains (disponível em: https://www.jetbrains.com/idea/) e clique em download. https://www.jetbrains.com/idea/ Olá, Java! 15 Figura 5 – Página do IntelliJ IDEA Fonte: JetBrains. Dentre as opções, selecione a versão Community, que é gratuita: Figura 6 – Escolha da versão Fonte: JetBrains. Rode o programa e siga o passo a passo da instalação. Cabe reforçar que os IDEs estão para o Java assim como o Word está para a língua portuguesa. Elas são apenas ferramentas para escrita de código, mas a linguagem de programação Java será a mesma nos três ambientes. Por isso, todo código presente neste livro poderá ser executado em qualquer um desses editores ou, até mesmo, diretamente na linha de comando. Programação orientada a objetos I16 1.4 O primeiro programa Java Vamos agora tentar criar e executar nosso primeiro programa em Java. Para isso, precisaremos abrir o IntelliJ e compreender a estrutura de um código bastante simples. Na primeira vez que você abrir o IntelliJ, ele realizará algumas perguntas para a configuração básica. A primeira pergunta refere-se à importação das suas configurações. Como isso é possível somente se você já tiver instalado uma versão antiga do IntelliJ, simplesmente escolha a opção Do not import settings e siga em frente. Figura 7 – Importação de configurações antigas Fonte: IntelliJ IDEA. Em seguida, o IntelliJ confirmará a sua licença de usuário. Basta confirmar que leu e clicar em Continue. Figura 8 – Contrato de licença do IntelliJ Fonte: IntelliJ IDEA. Olá, Java! 17 A próxima tela pergunta se você deseja ou não enviar estatísticas de uso para a Jetbrains. O objetivo disso é tentar melhorar as futuras versões do IDE. Caso deseje, clique em Send Usage Statistics. Caso não deseje, use o botão Don't send. Figura 9 – Compartilhamento de dados Fonte: IntelliJ IDEA. O próximo passo é escolher se você quer seu IDE no modo escuro ou claro. Muitos programadores consideram o modo escuro menos cansativo e mais interessante, por isso, ele já aparece selecionado no IDE. Escolha uma das opções e clique em Skip Remaining and Set Defaults. Isso pulará as demais configurações que são mais adequadas a usuários avançados. Caso você tenha baixado a versão de avaliação do IDE, e não a versão Community, o último passo será fornecer suas credenciais. Faça isso caso você tenha uma conta Jetbrains ou escolha Evaluate for free caso você queira usar o período de avaliação. Figura 10 – Ativação da licença da versão de avaliação Fonte: IntelliJ IDEA. Programação orientada a objetos I18 1.4.1 Criando o projeto É hora de testar se tudo foi instalado corretamente escrevendo nosso primeiro programa Java. Para isso, abra o IntelliJ IDEA e clique em Create New Project. Figura 11 – Criação de novo projeto Fonte: IntelliJ IDEA. No lado esquerdo, deixe selecionada a opção Java. Certifique-se também de que a versão do JDK seja a 12. Caso não seja, clique em New e selecione a pasta em que você instalou seu JDK. Em seguida, clique em Next. Não é necessário marcar nenhuma opção adicional. Figura 12 – Escolha da linguagem Fonte: IntelliJ IDEA. Na tela seguinte, desmarque a opção Create Project From Template, se estiver marcada, e clique em Next. Olá, Java! 19 Figura 13 – Criação de projeto em branco Fonte: IntelliJ IDEA. Nomeie o projeto como Aula1 e clique em Finish. O IntelliJ provavelmente perguntará se pode criar a pasta do projeto, clique em OK. Figura 14 – Nome e caminho do projeto Fonte: IntelliJ IDEA. Uma tela como esta deverá se abrir: Figura 15 – Seu primeiro projeto Fonte: IntelliJ IDEA. Programação orientada a objetos I20 Caso o menu esquerdo não se abra automaticamente, clique em 1: Project na barra lateral. O código-fonte do seu programa será colocado na pasta src. Ela aparece selecionada na captura de tela da Figura 15. 1.4.2 Escrevendo o código Clique com o botão direito sobre a pasta src, clique em New e depois em Java Class. Dê o nome de Aula1 e clique em OK. Figura 16 – New Java Class Fonte: IntelliJ IDEA. Observe que agora a pasta src contém um arquivo chamado Aula1. No disco, esse arquivo terá a extensão .java. E é nele que o código-fonte da nossa aplicação iniciará. Uma das regras da linguagem é que o nome do arquivo e da estrutura class, dentro dele, devem obrigatoriamente ser iguais. Dentro desse arquivo, o seguinte código deve ter aparecido: public class Aula1 { } Altere-o para: Figura 17 – O primeiro programa Fonte: elaborada pelo autor. Você deve ter notado que, ao fazer isso, aparecem alguns botões verdes na barra lateral esquerda, ao lado da primeira e segunda linhas. Clique em um desses botões e selecione Run 'Aula1.main()'. Olá, Java! 21 Figura 18 – Execução do primeiro programa Fonte: IntelliJ IDEA. Pronto! Você acaba de escrever e executar seu primeiro programa! Perceba que o resultado da execução já apareceu na parte inferior do IDE. É o texto "Olá mundo!". Vamos agora analisar esse programa linha a linha: 1 2 3 4 5 public class Aula1 { public static void main(String[] args) { System.out.println("Olá mundo!"); //Imprime Olá mundo } } O Java é uma linguagem orientada a objetos. Isso quer dizer que você sempre trabalhará com o conceito de classes e objetos. Exploraremos em detalhes esses conceitos a partir do Capítulo 3, mas note que, desde a linha 1, já fomos obrigados a criar uma classe em que iremos trabalhar. Dentro dessa classe, definimos uma função importante, conhecida como função principal (main). Trata-se do ponto de entrada do nosso programa, declarado na linha 2. Tanto a classe quanto a função principal definem dois blocos de código delimitados pelas chaves {}. Na linha 3, dentro da função main, imprimimos o texto "Olá mundo!", utilizando o comando System.out.println. Como o programa só tem essa linha, ele imprime esse texto e encerra. Observe que, logo após o comando, encontramos um comentário, criado por meio das duas barras //. Comentários são completamente ignorados na linguagem e nos permitem escrever anotações para nos acharmos no código. Poderíamos também criar comentários de várias linhas, bastando delimitá-los por /* e */. Programação orientada a objetos I22 Observe que os comandos em Java são terminados pelo ponto e vírgula. Além dos comentários, as quebras de linha, a tabulação e os espaçamentos são ignorados pelo compilador, mas repare que os utilizamos para enfatizar os blocos de código. Essa prática é conhecida como endentação e recomendamos que você a siga durante o código. Alguns programadores, de outras linguagens inserem uma quebra de linha antes de abrir as chaves em cada bloco, o que deixaria nosso programa assim: 1 2 3 4 5 6 7 public class Aula1 { public static void main(String[] args) { System.out.println("Olá mundo!"); //Imprime Olá mundo } } Isso é perfeitamente permitido em Java, porém, embora sintaticamente correto, fere a convenção de código oficial (SUN MICROSYSTEMS, 1997), por isso, daremos preferência para a primeira forma. Considerações finais Neste capítulo, você aprendeu sobre a plataforma Java, sua importância e configurações. Juntamente com o primeiro programa, esse foi o primeiro passo para um aprendizado mais profundo. No próximo capítulo, iniciaremos o estudo da linguagemJava. Inicialmente, conheceremos a estrutura básica, que será similar a qualquer outra linguagem que você já tenha estudado. Aproveite essa oportunidade para exercitar a linguagem de forma prática. Em seguida, estudaremos a orientação a objetos, uma forma de pensar em problemas na hora de escrever software. A plataforma é um ótimo ambiente para esse aprendizado. Com um pouco de esforço e dedicação, tenha certeza de que cada hora de estudo será muito recompensadora. Ampliando seus conhecimentos • INTELLIJ IDEA. Default keymap. 2019. Disponível em: https://resources.jetbrains. com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf. Acesso em: 4 ago. 2019. Você pode conhecer um pouco mais sobre o IDE IntelliJ, com o qual iremos trabalhar. É bastante útil conhecer configurações e teclas de atalho. Por isso, consulte o cheat sheet, que contém um resumo das principais funções. Tenha esse documento em mãos, pois, com o tempo, esse uso será cada vez mais natural. https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf https://resources.jetbrains.com/storage/products/intellij-idea/docs/IntelliJIDEA_ReferenceCard.pdf Olá, Java! 23 • MEYER, Maximiliano. Os melhores salários por linguagem de programação, 2018. Oficina da Net, 1 mar. 2018. Disponível em: https://www.oficinadanet.com.br/post/14518-qual-a- linguagem-de-programacao-e-mais-bem-remunerada. Acesso em: 17 set. 2019. Esse artigo dá um panorama do mercado de trabalho em 2018, envolvendo a linguagem Java e outras linguagens de programação. Observe que, embora o Java não tenha a melhor média salarial, apresenta o maior número de vagas disponibilizadas. Além disso, cabe lembrar que ele também é a linguagem do Android. Por fim, outras linguagens citadas no artigo, como o C# da plataforma .Net, também são orientadas a objetos. Por isso, ao aprender os conceitos deste livro, será fácil migrar para essas linguagens, caso você precise. Atividades 1. O Java é uma linguagem híbrida. Por que isso é interessante? Quais são as desvantagens? 2. Na Seção 1.2, foi dito que o impacto de performance do Java é "difícil de medir". Discorra sobre o porquê dessa afirmação. 3. Quais são os cinco pilares de projeto da linguagem Java? Referências BYOUS, J. Java technology: the early years. The Internet Archive, 20 abr. 2004. Disponível em: https://web. archive.org/web/20050420081440/http://java.sun.com/features/1998/05/birthday.html. Acesso em: 4 jul. 2019. GOETZ, B. Java concorrente na prática. Rio de Janeiro: Alta Books, 2018. GOSLING, J. A brief history of the green project. The Internet Archive, maio 1998. Disponível em: https://web. archive.org/web/20050609085739/http://today.java.net/jag/old/green/. Acesso em: 4 jul. 2019. MENDONÇA, V. G. Introdução à computação. Curitiba: IESDE Brasil, 2018. ORACLE. The Java Language Environment. 1997. Disponível em: https://www.oracle.com/technetwork/java/ intro-141325.html. Acesso em: 4 ago. 2019. ORACLE. Java EE Compatibility. 2019a. Disponível em: https://www.oracle.com/technetwork/java/javaee/ overview/compatibility-jsp-136984.html. Acesso em: 4 jul. 2019. ORACLE. Java EE at a Glance. 2019b. Disponível em: https://www.oracle.com/technetwork/java/javaee/ overview/index.html. Acesso em: 4 jul. 2019. SUN MICROSYSTEMS. Javasoft Ships Java 1.0. The Internet Archive. 23 jan. 1996. Disponível em: https://web.archive.org/web/20070310235103/http://www.sun.com/smi/Press/sunflash/1996-01/ sunflash.960123.10561.xml. Acesso em: 4 ago. 2019. SUN MICROSYSTEMS. Java Code Conventions. Oracle Technetwork, 12 set. 1997. Disponível em: https:// www.oracle.com/technetwork/java/codeconventions-150003.pdf. Acesso em: 4 ago. 2019. 2 Conhecendo a linguagem Neste capítulo, iremos explorar rapidamente a sintaxe básica da linguagem Java. Entenderemos como as declarações de variável funcionam e veremos as principais estruturas de controle para tomada de decisão e repetição. Ainda veremos a linguagem do ponto de vista estruturado, sem focar na parte orientada a objetos – essa segunda parte será tema do próximo capítulo. Apesar de ser um capítulo longo, você não terá dificuldades para acompanhar o assunto se já conhecer outras linguagens de programação. Considere que todos os exemplos que veremos adiante estão contidos no interior do bloco de código da função principal. Por exemplo, se mostrarmos o código: 1 2 int x = 10; System.out.printf("O valor de x é: %d%n", x); Você só conseguirá executar esse exemplo caso inclua todo o código extra exibido no capítulo anterior, ou seja, você precisaria criar o arquivo Aula2.java e, então, digitar dentro dele o seguinte: public class Aula2 { public static void main(String[] args) { var x = 10; System.out.printf("O valor de x é: %d%n", x); } } Embora esse código extra possa parecer excessivo, você logo verá que ele ocorre de maneira natural em programas maiores. 2.1 Variáveis e tipos de dados Java é uma linguagem fortemente tipada. Isso quer dizer que as variáveis estarão associadas a um único tipo de dado durante toda a sua existência. Esse tipo de dado indica a informação que a variável armazenará e quais operações poderemos realizar sobre ela. Os principais tipos de dados, de acordo com Oracle (2017a), são: • Tipos primitivos: • Booleano (verdadeiro/falso): boolean. • Inteiros: byte, short, int e long. • Numéricos decimais: float e double. • Texto: char. Programação orientada a objetos I26 • Tipos não primitivos: • String. • Enumerações. • Arrays. • Referências para objetos: declaradas sempre que usamos uma classe. Há duas formas de declarar variáveis, a primeira é com a tipagem explícita, em que indicamos o tipo diretamente nas formas: tipo nomeDaVariavel; tipo nomeDaVariavel = valor; Para definirmos uma variável textual que guardará um nome, por exemplo, poderíamos fazer: String nome; Ou poderíamos definir uma variável inteira para indicar o número de páginas de um livro já inicializada com o valor 100, na seguinte forma: int paginas = 100; Também é possível declarar mais de uma variável de mesmo tipo em uma única linha, separando-as por vírgula. Por exemplo: int x = 10, y = 20; Embora isso seja possível, não é muito usual, pois a sintaxe pode ser confusa (SIERRA; BATES, 2010). A linha a seguir, por exemplo, declara a variável x sem valor e a variável y com o valor 50, não as duas variáveis com valor 50, como pode parecer: int x, y = 50; Por isso, muitos programadores consideram uma boa prática declarar cada variável em sua própria linha. Além da maneira explícita, podemos declarar variáveis utilizando tipagem implícita, por meio da instrução var. Nesse caso, seremos obrigados a indicar o valor, pois é por meio dele que o Java determinará o tipo da variável. Note que a variável ainda tem um tipo, o qual não poderá mudar. Por exemplo, a variável páginas poderia ser declarada de maneira implícita: var paginas = 100; Conhecendo a linguagem 27 De maneira geral, a forma implícita é preferível. Nela, não é possível declarar múltiplas variáveis na mesma linha. Além de variáveis, podemos declarar constantes com a palavra-chave final. Constantes não podem mudar de valor. final var paginas = 100; Uma vez declaradas, podemos utilizar variáveis para realizar operações básicas, como soma, subtração, comparações e concatenação. As operações básicas variam em cada tipo de dado. Sem perceber, já estudamos a primeira operação básica, chamada de atribuição e realizada pelo operador de =. Por exemplo: 1 2 3 4 5 var x = true; var y = false; System.out.println(x == y); //Imprime false System.out.println(x != y); //Imprime true É com esse operador que substituiremos o valor contido no interior das variáveis. 2.1.1 O tipo de dado booleano O tipo de dado booleano, ou lógico, admite apenas dois valores: verdadeiro (true) ou falso (false). O valor padrão para variáveis desse tipo é false. Podemos comparar se duas variáveis booleanas são iguais, pormeio dos operadores relacionais ==, e se são diferentes, utilizando o operador ! =. Por exemplo: 1 2 3 4 5 var x = true; var y = false; System.out.println(x == y); //Imprime false System.out.println(x != y); //Imprime true Importante: não confunda a atribuição (=) com a igualdade (==). Além desses dois operadores relacionais, o tipo boolean admite três operadores condicionais (também chamados de operadores lógicos). São eles: operador E (&&), OU (||) e NÃO (!). O operador E retorna true somente se os dois valores comparados forem verdadeiros. Já o operador OU retorna false apenas se os dois valores comparados forem falsos. Por fim, o operador NÃO inverte o valor sendo comparado. Por exemplo: Programação orientada a objetos I28 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 boolean v1 = true, v2 = true; boolean f1 = false, f2 = false; System.out.println("Comparação com E (&&)"); System.out.println(v1 && v2); //Imprime true, ambos verdadeiros System.out.println(v1 && f2); //Imprime false, f2 é falso System.out.println(f1 && v2); //Imprime false, f1 é falso System.out.println(f1 && f2); //Imprime false, ambos falsos System.out.println("Comparação com OU (&&)"); System.out.println(v1 || v2); //Imprime true, ambos verdadeiros System.out.println(v1 || f2); //Imprime true, v1 é verdadeiro System.out.println(f1 || v2); //Imprime true, v2 é verdadeiro System.out.println(f1 || f2); //Imprime false, ambos falsos System.out.println("Negação com !"); System.out.println(!v1); //Imprime false, pois v1 é true System.out.println(!f1); //Imprime true, pois f1 é false Obviamente, você poderia combinar operadores para realizar operações complexas, como: var complexo = v1 && !(v2 || f2); Qual seria o valor da variável complexa? Isso te obrigaria a avaliar a expressão parte a parte. v1 && !(v2 || f2) → v2 || f2 é true v1 && !true → !true é false v1 && false → v1 && false é false Portanto, o valor da variável complexa será false. 2.1.2 Tipos numéricos Os tipos numéricos são divididos em números inteiros, sem casa decimal, e números de ponto flutuante. A Tabela 1 descreve os diferentes tipos inteiros, os valores máximos e mínimos que podem ser representados e quantos bytes ocupam na memória. Tabela 1 – Tipos inteiros Tipo Máximo / Mínimo Bits byte –128 até 127 8 short –32.768 até 32.767 16 int –2.147.483.648 até 2.147.483.647 32 long –9.223.372.036.854.775.808 até 9.223.372.036.854.775.807 64 Fonte: ORACLE, 2017b. Para os tipos de dados de ponto flutuante, temos apenas os tipos float (32 bits) e double (64 bits). Apesar do grande número de tipos, utilizaremos int e double na maior parte dos casos. Conhecendo a linguagem 29 Literais numéricos Quando os números aparecem no código sem especificarmos seus tipos, nós os chamamos de literais numéricos (DEITEL; DEITEL, 2010). Podemos utilizar a letra L, para especificar que um número deve ser encarado como long, ou a letra f, para que seja declarado como float. Em literais, também podemos utilizar o _, para separar dígitos. Finalmente, podemos utilizar alguns prefixos para alterar a base numérica em que o literal é fornecido, sendo 0x para hexadecimal, 0b para binário e somente 0 para a base octal. Veja alguns exemplos: var longNum = 12L; //Essa variável é do tipo long var intNum = 1_250; //intNum vale 1250 var doze = 0xB; //B em hexadecimal equivale a 12 em decimal var nove = 011; //11 em octal equivale a 9 em decimal var floatNum = .5f; //O tipo é float e o valor 0.5 var doubleNum = 1_2___5.; //O tipo é double e o valor é 125.0 Você também pode usar a letra L minúscula para o long, mas isso não é recomendado já que ela é muito similar ao número 1. Type casting Quando atribuímos o valor de um tipo de dado a outro, o Java é obrigado a fazer a conversão. Essa operação de conversão é chamada de type casting. Se o tipo de dado não acrescenta imprecisão, a conversão é automática; porém, às vezes, a operação pode resultar em perda de informação e, nesse caso, o Java exigirá que você indique explicitamente que a conversão deve ser feita (SIERRA; BATES, 2010). Como exemplo, considere o código a seguir. 1 2 3 int y = 10; short x = y; System.out.println(x); Esse código apresentará erro na linha 2. Como x é uma variável inteira, ela poderia conter um valor muito maior ou menor do que o máximo permitido para um short. A correção seria fazermos a operação de type casting, indicando o tipo de dado entre parênteses. 1 2 3 int y = 10; short x = (short) y; System.out.println(x); O que aconteceria se o valor em y fosse maior do que o permitido para um short (32.767)? Se fosse 32.768, por exemplo? Se testarmos esse programa com o valor 32.768, obteremos como resultado o valor -32.768. Isso ocorre porque, ao ultrapassarmos o último valor do short, o Java retorna ao primeiro valor possível nesse tipo de dado, no caso -32.768, e continua somando valores, a partir daí, sem apresentar qualquer tipo de erro. Tenha cuidado, portanto, antes de fazer esse tipo de conversão. Programação orientada a objetos I30 Operadores Para os tipos numéricos, o Java fornece os seguintes operadores de comparação: ==, !=, >, <, <= e >=. Observe que o resultado de uma operação de comparação será um booleano, ou seja, um valor true ou false. Além disso, o Java fornece os operadores matemáticos tradicionais para soma, subtração, multiplicação, divisão e resto: +, -, *, / e %. Esses operadores respeitam a precedência matemática, ou seja, multiplicações e divisões irão ocorrer primeiro, caso sejam misturadas a somas e subtrações. Por exemplo: var x = 5 + 10 * 3; //Atribui 35 a x O sinal de menos também pode ser utilizado na frente da variável. Isso é chamado de negação unária, que inverte o sinal da variável. Por exemplo: no código a seguir, a variável outroNumero receberá o valor 12.5. 1 2 var numero = -12.5; var outroNumero = -numero; É possível atribuir a uma variável um valor com base em seu valor antigo, da seguinte forma: 1 2 3 var x = 10; x = x + 5; System.out.println(x); //Imprime 15 A operação de = será avaliada por último. Assim, na linha 2, o Java primeiro avaliará a expressão x + 5 com base no atual valor de x, que é 10 (linha 1). Desse modo, 10 + 5 resulta em 15. Somente após isso o valor de x será substituído. Essa operação é tão comum que os operadores aritméticos podem ser combinados ao sinal de = para realizá-la. Por exemplo: 1 2 3 var x = 10; x += 5; //Equivalente a x = x + 5 System.out.println(x); //Imprime 15 Além desses, para os tipos inteiros, o Java também fornece os operadores ++ e -- para somar ou subtrair 1 ao valor da variável. Ele pode ser usado antes ou depois da variável. Em ambos os casos, o valor desta será modificado, porém, se usado antes dela, o operador retornará o valor antes da modificação. Por exemplo: 1 2 3 4 var x = 10; System.out.println(++x); //Muda o valor para 11 e imprime o resultado System.out.println(x++); //Muda o valor para 12, mas imprime 11 System.out.println(x); //Imprime 12 Por fim, os tipos inteiros também fornecem operadores para manipulação de bits. A Tabela 2, a seguir, descreve seu funcionamento considerando x os últimos 8 bits de uma variável de valor 0010_1011. Conhecendo a linguagem 31 Tabela 2 – Operadores para manipulação de bits Operação Símbolo Exemplo Resultado Right-Shift << x << 1 0101_0110 Left-Shift >> x >> 2 0000_1010 E & x & 1111_0000 0011_0000 OU | x | 1111_0000 1111_1011 OU Exclusivo ^ x ^ 1111_0000 0001_1011 Fonte: Elaborada pelo autor. Se você achou esses operadores muito complexos, não se preocupe, eles geralmente são utilizados apenas em aplicações de baixo nível, como manipulação de protocolos de rede ou programação de firmwares. 2.1.3 O tipo char O tipo char representa um caractere de texto. No fundo, cada caractere é representado por um valor numérico que varia de 0 até 65.535; portanto, ocupa 2 bytes na memória. Por causa dessa conversão, é possível utilizar qualqueroperador numérico em variáveis do tipo char. Definimos um char, de maneira literal, por meio de aspas simples. var letra = 'x'; Por utilizar a codificação Unicode para representar caracteres, o Java não tem problemas com acentuação, como ocorre em outras linguagens. 2.1.4 Texto Além dos oito tipos primitivos vistos até agora, o Java também dá suporte ao texto por meio de variáveis do tipo String. Textos literais são definidos utilizando aspas: var nome = "Programação" Variáveis de texto suportam a operação de concatenação por meio dos operadores + e +=. A concatenação une duas Strings. Por exemplo, a linha 3 deste código imprime o texto "Programação OO em Java": 1 2 3 var titulo = "Programação OO"; var subtitulo = "em java"; System.out.println(titulo + subtitulo); Podemos descobrir a quantidade de caracteres dentro de uma String por meio do comando length(). No caso do código anterior, subtitulo.length() resultaria no valor 7. Por fim, podemos ler o valor de um caractere dentro do texto por meio da função charAt. Devemos informar a posição desse caractere, iniciando em 0. Por exemplo: titulo.charAt(3) nos retornaria a quarta letra, ou seja, char "g". Programação orientada a objetos I32 2.1.5 Enumerações Enumerações representam um conjunto fixo de valores. Imagine a seguinte situação: digamos que você vai trabalhar com um sistema que utilize baralho e gostaria de um tipo de dado para representar os naipes. Como sabemos, só existem quatro naipes possíveis: paus, ouros, espadas e copas. Você poderia criar para isso uma enumeração chamada Naipes. A criação de enumerações envolve dois passos. São eles: 1. Definir a enumeração: dar um nome para esse nosso tipo de dado e, em seguida, especificar quais são os seus valores possíveis. 2. Utilizar a enumeração: declarar uma variável do tipo da enumeração e atribuir a ela valores. Para o caso do exemplo, iniciaríamos criando o arquivo Naipes.java e colocando nele o seguinte conteúdo: 1 2 3 public enum Naipes { Paus, Ouros, Copas, Espadas } Observe que até agora não criamos nenhuma variável. Estamos apenas explicando para o Java o que é um Naipe. Agora, podemos, em nosso main, utilizar esse novo tipo de dado: 1 2 var valor = Naipes.Paus; System.out.println(valor); //Imprime Paus As enumerações contêm várias operações interessantes. É possível chamar Naipes.values() para obtermos um vetor com todos os valores de Naipes possíveis dentro, na ordem em que foram declarados. Além disso, podemos obter um naipe por meio do texto, com o comando valueOf(). Mas atenção: o Java disparará um erro caso o valor não exista. Se quisermos obter o valor do enum em forma de texto, podemos usar a função name(). Por fim, podemos saber qual a ordem do naipe, iniciada em 0, utilizando a função ordinal(). Veja no exemplo a seguir. 1 2 3 4 var valor = "Ouros"; Naipes naipe = Naipes.valueOf(valor); System.out.println(valor.ordinal()); //Imprime 1 System.out.println(valor.name()); //Imprime Ouros As enumerações são muito mais poderosas do que isso, exploraremos vários outros recursos em capítulos futuros. 2.1.6 Vetores e matrizes Muitas vezes, precisamos trabalhar com listas de valores. Para isso, o Java permite definir estruturas conhecidas como vetores (arrays). Elas associam um conjunto de valores a um índice iniciado em 0. Podemos declarar um array utilizando o operador de []. Veja alguns exemplos: Conhecendo a linguagem 33 1 2 3 4 5 6 int[] x = new int[] {10,2,30,-5}; var y = new double[10]; char[][] z; System.out.println(x[0]); //Imprime 10 System.out.println(x[2]); //Imprime 30 Observe, nas linhas 5 e 6, como os índices são usados. O que acontece com o array declarado na linha 2? Nessa linha, foi criado um array com 10 variáveis do tipo double. Como não especificamos seus valores, o Java utilizou o valor padrão. Para qualquer variável numérica, o valor padrão será 0. Então, nesse caso, há um array com 0 em todas as suas posições. A linha 3 é um caso diferente. Criamos uma variável chamada z, que conterá um array bidimensional de caracteres; porém, essa variável não foi inicializada com nenhuma lista. Ela ganha, então, o valor especial null. Se tentarmos imprimir um índice qualquer de z, obteremos uma mensagem de erro. Null é também o valor padrão de Strings, enumerações e, como veremos no próximo capítulo, objetos. Similar às Strings, também é possível utilizar o comando length para descobrir o tamanho de um array. No exemplo anterior, x.length resultaria no valor 4. Diferentemente de outras linguagens, o Java não exige que arrays com mais de uma dimensão sejam quadrados, ou seja, devemos imaginar uma lista bidimensional, como uma lista de listas, e não como uma matriz. Como matrizes retangulares são extremamente comuns, o Java também fornece uma forma direta de declaração desse tipo de estrutura. Vejamos alguns exemplos de declaração de matrizes. 1 2 3 4 5 6 7 8 9 10 11 12 13 var xadrez = new char[8][8]; var irregular = new int[][] { {1}, {1}, {2}, {}, {1}, {2}, {3}, {4}, {5} }; var irregular2 = new int[4][]; irregular2[0] = new int[1]; irregular2[1] = new int[2]; irregular2[2] = new int[0]; irregular2[3] = new int[5]; A declaração da linha 1 cria uma matriz quadrada de oito linhas e oito colunas, todas elas contendo caracteres. Como não informamos exatamente quais caracteres estão lá dentro, o Java os inicializou com o valor padrão 0. Programação orientada a objetos I34 Na linha 2, declaramos a matriz irregular com uma lista de um único inteiro na sua primeira linha, dois inteiros na segunda linha, nenhum inteiro na terceira linha e cinco inteiros na quarta linha. Um detalhe importante sobre o código da linha 5: ele cria um array vazio, o que é diferente do null que comentamos há pouco. Nesse caso, existe um vetor naquela posição, mas ela não possui nenhum número dentro. O null indicaria que o vetor não existe, ou seja, não poderíamos nem mesmo usar a função length sobre ele. Na linha 9, declaramos uma matriz de quatro linhas, mas somente indicamos que as colunas seriam formadas de arrays de ints. As quatro linhas foram criadas, mas contendo null em seus valores. Nas linhas seguintes, definimos quais arrays são esses, linha a linha. Sendo assim, estabelecemos uma matriz exatamente com as mesmas dimensões da matriz irregular, porém com todos os inteiros inicializados em seu valor padrão 0. 2.2 Controle de fluxo: estruturas de decisão Muitas vezes, precisamos executar ou não um trecho de código de acordo com uma condição. O Java apresenta duas estruturas de decisão importantes, o if e o switch. Além disso, ele também fornece um operador com base em decisão: o operador ternário. Vamos estudar essas estruturas. 2.2.1 If e else O if executa uma instrução ou bloco de código caso a condição seja verdadeira. Opcional- mente, podemos usar a instrução else, que será executada caso o if seja falso. Por exemplo: 1 2 3 4 5 6 7 var idade = 10; if (idade < 18) { System.out.println("Menor de idade"); } else { System.out.println("Maior de idade"); } Esse programa imprimirá o texto "Menor de idade". Isso porque a condição idade < 18, presente na linha 3, é verdadeira. Se você alterar o valor da variável idade para 18, ele passará a imprimir "Maior de idade", afinal, a expressão idade < 18 agora é falsa. Como temos apenas um comando dentro do if, as chaves são opcionais, porém constitui boa prática utilizá-las, conforme recomendado nas convenções de código (SUN MICROSYSTEMS, 1997). A exceção disso é quando queremos colocar outro if logo após o else para testar múltiplas condições, como no exemplo a seguir: Conhecendo a linguagem 35 1 2 3 4 5 6 7 if (idade < 14) { System.out.println("Criança"); } else if (idade < 18){ System.out.println("Adolescente"); } else { System.out.println("Adulto"); } Operador ternário Muitas vezes, queremos atribuir o valor de uma variável com base em uma condição. Para isso,podemos utilizar o operador ternário na seguinte forma: valor = condição ? valor caso verdadeiro : valor caso falso; Vejamos um exemplo: var hcg = 4.0; var resultado = hcg >= 5.0 ? "Grávida" : "Não grávida"; Então, a variável resultado receberá o valor "Não grávida". Como se trata de um operador, os valores dos dois lados da expressão precisam ser, obrigatoriamente, do mesmo tipo. 2.2.2 Switch Muitas vezes, precisamos desviar o fluxo com base em um conjunto de valores. Para isso, utilizamos o comando switch. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var exemplo = 1; switch (exemplo) { case 1: System.out.println("Primeira condição"); break; case 2: System.out.println("Segunda condição"); case 3: System.out.println("Terceira condição"); break; default: System.out.println("Outra condição"); break; } Programação orientada a objetos I36 Observe, na linha 2, que o comando switch utilizou como base a variável exemplo, que, nesse caso, é numérica. Ela também poderia ser uma String ou uma enumeração. Em cada case, colocamos o valor esperado seguido de dois pontos (:) e o código que queremos executar, caso o valor da variável corresponda ao case. No exemplo, como o valor da variável é 1, o código imprimirá "Primeira condição". O código dentro de cada case executa até que o break seja executado. Caso não haja um comando break, o código prosseguirá para dentro do próximo case. Desse modo, se alterássemos o valor da variável exemplo para 2, o sistema imprimiria: Segunda condição Terceira condição Isso ocorre porque não há break dentro do case 2 e é chamado de fallthrough. Como você mesmo deve ter notado pelo exemplo, pode gerar um código confuso. Por fim, existe o bloco opcional default, que é executado se nenhuma das condições anteriores for atendida. 2.3 Controle de fluxo: estruturas de repetição É frequente também a necessidade de repetir um trecho de código várias vezes. Essa operação é conhecida como iteração, loop ou laço. Para isso, o Java fornece quatro comandos: while, do while, for e for each. Que tal aprendermos sobre cada um deles? 2.3.1 Repetição com while e do while Os comandos while e do while repetem um comando ou bloco de comandos enquanto uma condição for verdadeira. Observe o código a seguir. 1 2 3 4 5 6 var x = 1; while (x <= 3) { System.out.print(x + ", "); x++; } System.out.println("indiozinhos"); Ele tem como resultado o seguinte texto: 1, 2, 3, indiozinhos O do while é similar, porém a condição será testada ao final da repetição, e o código executará pelo menos uma vez. O código a seguir tem o mesmo resultado: Conhecendo a linguagem 37 1 2 3 4 5 6 var x = 1; do { System.out.print(x + ", "); x++; } while (x <= 3); System.out.println("indiozinhos"); Porém, com x = 4, o primeiro exemplo imprimiria "indiozinhos", enquanto o segundo exemplo imprimiria "4, indiozinhos". 2.3.2 A instrução for A instrução for é formada por três partes: a. Inicialização: permite a criação e inicialização de variáveis. b. Condição: mantém o for executando enquanto for verdadeira. c. Operação: é executada a cada iteração do for. Para deixar mais claro, vamos reescrever o exemplo dos indiozinhos utilizando o for: 1 2 3 4 for (var x = 1; x <= 3; x++) { System.out.print(x + ","); } System.out.println("indiozinhos"); O resultado é o mesmo: 1, 2, 3, indiozinhos O comando for é muito utilizado para imprimir os elementos de um array. Por exemplo: 1 2 3 4 5 var frutas = new String[] {"Laranja, Banana, Maçã"}; for (var i = 0; i < frutas.length; i++) { var fruta = frutas[i]; System.out.println(fruta); } Esse programa imprime, em ordem: Laranja Banana Maçã Há outras formas de fazê-lo. Uma delas é por meio do comando for each, introduzido na versão 5.0 do Java. Veremos seu funcionamento a seguir. Programação orientada a objetos I38 For each Na verdade, iterar sobre os elementos de uma lista é uma tarefa tão comum que uma estrutura for foi criada inteiramente para isso. É chamado de for each, que significa "para cada" em tradução literal. O mesmo código anterior poderia ser reescrito assim: 1 2 3 4 var frutas = new String[] {"Laranja, Banana, Maçã"}; for (var fruta : frutas) { System.out.println(fruta); } Lemos esse for em português da seguinte forma: "para cada fruta no array de frutas". Essa é a melhor forma de iterar: não só melhora a leitura, como é mais eficiente em coleções mais avançadas (BLOCH, 2019). O Capítulo 7, que trata da biblioteca de coleções do Java, mostrará outras formas de percorrer listas de objetos. 2.3.3 Interrompendo laços com break e continue Muitas vezes, precisamos interromper um laço de um loop ou mesmo a iteração inteira, dependendo de alguma condição. Para isso, o Java disponibiliza dois comandos: • Continue: que interrompe imediatamente aquela iteração, fazendo com que a condição do laço seja novamente testada. • Break: que interrompe completamente o laço. Digamos que nós temos uma lista com números e queremos imprimir somente os números pares, na ordem em que aparecem. Poderíamos escrever esse laço assim: 1 2 3 4 5 6 7 8 var numeros = new int[] {10,2,23,11,14,17,6,13}; for (var numero : numeros) { //Se o número é impar if (numero % 2 == 1) { continue; //Pule para o próximo número } System.out.println(numero); } Esse programa imprime: 10 2 14 6 iterar: acessar cada um dos elementos de um vetor em ordem. Conhecendo a linguagem 39 Caso haja um laço dentro do outro, esses comandos atuarão no mais interno, mas é possível também incluir marcadores (labels), caso você precise abandonar os mais distantes. O código a seguir imprime os números linha a linha, mas interrompe o laço imediatamente caso o número 0 seja encontrado. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var matrizQuadrada = new int[][] { {1,2,3,4,5}, {10,20,30,0,40}, {100, 200, 300, 400, 500} }; externo: for (int i = 0; i < matrizQuadrada.length; i++) { System.out.println(); for (var numero : matrizQuadrada[i]) { System.out.print(numero + " "); if (numero == 0) { break externo; } } } O resultado será: 1 2 3 4 5 10 20 30 0 Observe o marcador externo na linha 7, que atua sobre o for da linha seguinte. É ele que faz com que o break colocado na linha 13 interrompa completamente os dois laços, em vez de somente o laço interno. Caso tenha três ou mais comandos for, você poderá usar mais de um marcador, entretanto, uma boa estruturação de código fará com que o uso de labels seja muito raro. 2.4 Escopo de variáveis Observe que todos os programas que vimos até aqui utilizaram as chaves para criar blocos de código. É importante saber que as variáveis só existem a partir da linha em que forem declaradas e no interior de blocos em que foram criadas (SIERRA; BATES, 2010). No caso de um bloco estar dentro do outro, ele conseguirá utilizar variáveis do bloco em que está contido. Por exemplo, no código a seguir: Programação orientada a objetos I40 1 2 3 4 5 6 7 var x = 10; for (var i = 0; i < 10; i++) { var a = i*2; System.out.println(a); System.out.println(x); } System.out.println(a); //ERRO A variável x pode ser utilizada na linha 5, pois está dentro do bloco da função main, assim como o for. Observe que a variável a foi declarada na linha 3, no interior do bloco do for, portanto não pode mais ser usada na linha 7. Como seu bloco deixou de existir, a variável também deixou de existir. O bloco definido nas chaves das linhas 2 e 7 definem o escopo da variável. E a variável i? No caso do comando for, o escopo dela é o mesmo da variável a. É uma boa prática manter as variáveis no menor escopo possível (DEITEL; DEITEL, 2010). Tenha sempre em mente que boas práticas, como restringir o escopo da variável, evitam que você mesmo cometa erros, simplificando a programação. Ampliando seus conhecimentos • ORACLE. Lesson: language basics. The Java Tutorials, 2019. Disponívelem: https://docs. oracle.com/javase/tutorial/java/nutsandbolts/index.html. Acesso em: 24 set. 2019. Caso você domine o inglês, é sempre importante consultar a documentação oficial da Oracle. Nela, você encontrará tutoriais e a descrição detalhada de todos os elementos da linguagem. • SIERRA, K.; BATES, B. Use a cabeça! Java. Rio de Janeiro: Alta Books, 2010. O livro de Kathy Sierra e Bert Bates apresenta a linguagem de maneira bastante extrovertida e, por isso, é uma boa referência. • CURSO de Java 63: printf, 2016. 1 vídeo (21 min). Publicado pelo canal Loiane Groner. Disponível em: https://www.youtube.com/watch?v=3Ie7VMJWoYo. Acesso em: 24 set. 2019. O comando printf é uma poderosa forma de imprimir dados. Para saber mais sobre isso, recomendamos a videoaula 63 do curso de Java, de Loiane Groner. • CURSO de Java 12: lendo dados do teclado usando a classe Scanner, 2015. 1 vídeo (22 min). Publicado pelo canal Loiane Groner. Disponível em: https://www.youtube.com/ watch?v=Z6Y8zupCKfk. Acesso em: 24 set. 2019. Ler dados do teclado também é muito importante. Para saber mais sobre o assunto, assista à videoaula 12, de Loiane Groner. https://docs.oracle.com/javase/tutorial/java/nutsandbolts/index.html https://docs.oracle.com/javase/tutorial/java/nutsandbolts/index.html https://www.youtube.com/watch?v=3Ie7VMJWoYo https://www.youtube.com/watch?v=Z6Y8zupCKfk https://www.youtube.com/watch?v=Z6Y8zupCKfk Conhecendo a linguagem 41 Atividades 1. Escreva dois programas para imprimir todos os números pares de 2 até 20. O primeiro deve usar o comando while e o segundo, o comando for. Não utilize o comando continue. 2. O que o programa a seguir imprime? Você pode executar o código mentalmente ou com a ajuda de um papel. 1 2 3 4 5 var letras = new char[]{'a','b','e','j','m','o','u','v','z',' '}; var idx = new int[]{2, 6, 9, 0, 4, 5, 9, 5, 9, 3, 0, 7, 0}; for (var i : idx) { System.out.print(letras[i]); } 3. Identifique os tipos declarados a seguir. var a = "a"; var b = 10L; var c = -.2; var d = 0f; Referências BLOCH, J. Java Efetivo: as melhores práticas para a plataforma Java. Trad. de C. R. M. Ravaglia. 3. ed. Rio de Janeiro: Alta Books, 2019. DEITEL, H. M.; DEITEL, P. Java: como programar. 8. ed. Trad. de E. Furmankiewicz. São Paulo: Pearson, 2010. ORACLE. Primitive Data Types. The Java Tutorials, 2017a. Disponível em: https://docs.oracle.com/javase/ tutorial/java/nutsandbolts/datatypes.html. Acesso em: 24 set. 2019. ORACLE. Learning the Java Language. The Java Tutorials, 2017b. Disponível em: https://docs.oracle.com/ javase/tutorial/java/TOC.html. Acesso em: 24 set. 2019. SIERRA, K.; BATES, B. Use a cabeça, Java! Trad. de A. J. Coelho. Rio de Janeiro: Alta Books, 2010. SUN MICROSYSTEMS. Java Code Conventions. Oracle Technetwork, 12 set. 1997. Disponível em: https:// www.oracle.com/technetwork/java/codeconventions-150003.pdf. Acesso em: 24 set. 2019. 3 Classes e objetos Problemas são complexos. Pare para pensar alguns segundos em quantos detalhes estão envolvidos na resolução do problema de transportar pessoas: segurança, cobrança, o melhor caminho de um ponto ao outro (mesmo em meio ao trânsito) etc. A tecnologia vem como aliada das empresas nesse ponto, e, dando suporte a tudo, está o software, portanto, não é surpresa que ele seja igualmente complexo. Felizmente, as linguagens de programação fornecem estruturas cada vez mais refinadas para decompor o problema em partes menores. Neste capítulo, iniciaremos o estudo de um dos principais paradigmas modernos que nos auxiliam nessa tarefa: a orientação a objetos. Aqui você começará a desvendar esse mundo, entendendo o que são classes e objetos e como podemos utilizar a linguagem para modelá-los. 3.1 Classes e objetos no mundo real Desde criança, procuramos entender os objetos do mundo real. Quando os analisamos, prestamos atenção em cada um dos seus atributos, como sua cor, forma, altura e peso. Figura 1 – Cor, tamanho, forma são atributos dos objetos al ta na ka /S hu tt er st oc k Além dos atributos, também procuramos conhecer o conjunto de operações que os objetos podem realizar. Por exemplo, pássaros e insetos são capazes de voar, um carro pode transportar pessoas, um cachorro late etc. Programação orientada a objetos I44 Figura 2 – A operação de um instrumento é fazer som m ax im ib ra gi m ov /S hu tt er st oc k Ao entendermos os objetos a nossa volta, os classificamos em diferentes tipos. Por exemplo, na Figura 2, sabemos que a criança está com uma guitarra em mãos. O conceito da guitarra foi percebido pela forma do instrumento, por sabermos que possui cordas, e pela maneira como ela está tocando. Repare que, embora estejamos olhando para um único objeto concreto – isto é, aquela guitarra, daquela menina – para nós, o conceito de guitarra engloba uma série de objetos similares que podem variar em sua cor, tamanho e formato, mas que terão um conjunto de atributos e operações em comum, como representado pela Figura 3. Figura 3 – Guitarra: objetos diferentes, um só conceito Ch ris to ph er H al l/ Sh ut te rs to ck Classes e objetos 45 A este conceito damos o nome de abstração, que é definido por Booch et al. (2006, p. 44) como "as características essenciais de um objeto que os distinguem de todos os outros tipos de objetos e, por consequência, fornecem fronteiras rígidas e bem definidas, relativas à perspectiva do observador". Vemos aqui dois conceitos distintos, mas correlacionados. O primeiro é o conceito de objeto, também chamado de instância, que se refere a um objeto específico. O outro é o conceito de classe, que se refere ao tipo desse objeto e, com base nele, sabemos que atributos e operações deveriam estar presentes em cada uma de suas instâncias. Como já mencionamos em outras ocasiões, o Java é uma linguagem orientada a objetos. O que isso significa? Significa que a linguagem nos dará mecanismos para definir e criar nossas próprias classes e, com base nelas, gerar os objetos que compõem os dados de nossos programas. Programar em uma linguagem orientada a objetos envolve focar os esforços não tanto nos algoritmos, mas em criar em software boas abstrações dos objetos do mundo real e, então, modelar suas interações. Esta estratégia nos permite lidar com a complexidade do software por meio da decomposição dos vários problemas complexos em problemas menores. Na prática, cada classe se transformará em um pequeno programa, com funções e dados bem isolados e definidos. O software como um todo terá centenas ou milhares de classes cooperando entre si. 3.2 Sua primeira classe Em Java, definimos uma nova classe por meio da palavra-chave class. Uma classe definirá um novo tipo de dado. Além disso, cada classe em Java será geralmente definida em seu próprio arquivo. Por exemplo, vamos supor que estejamos programando um sistema astronômico e gostaríamos de descrever os planetas. Sabemos que os planetas, no mundo real, têm milhares de atributos: nome, massa, diâmetro, vegetação, composição mineral, densidade, distância até o Sol, entre outros, mas iremos incluir no sistema somente aqueles que nos interessam. Em nosso caso, poderia ser um texto com o nome, a massa (medida em "Terras") e o diâmetro (medido em quilômetros). Para isso, crie um novo projeto, vá até a pasta src, clique no botão direito e selecione New e Java Class. Dê o nome para essa classe de Planeta e clique em OK. Observe que o IntelliJ criou um arquivo chamado Planeta.java e, dentro dele, colocou o seguinte código: 1 2 public class Planeta { } Nele, as palavras-chave public class indicam ao Java que uma nova classe será criada. Em seguida, escrevemos o seu nome: Planeta. Já definimos que os atributos da nossa classe são três: nome, massa e diâmetro. Representaremos isso em Java, indicando que a classe Planeta possui internamente três variáveis, que devem ser declaradas da maneira explícita: Programação orientada a objetos I46 1 2 3 4 5 publicclass Planeta { String nome = ""; int diametro; double massa; } Observe que, até agora, nós apenas descrevemos para Java o que é um planeta, ou seja, nós modelamos um conceito, uma abstração, na forma de um tipo de dado que chamamos de Planeta. Agora, gostaríamos de criar, em nosso programa, alguns planetas específicos. Por exemplo, podemos descrever os planetas Mercúrio, Terra e Saturno. Estes são nossos objetos, ou seja, instâncias (exemplos) de nossa classe. Vamos fazer isso em um novo arquivo. Clique novamente na pasta src e crie uma nova classe chamada Main. Nele, inclua a função main. Ao final, seu projeto deve se parecer com isso: Figura 4 – Projeto com duas classes no IntelliJ Fonte: Elaborada pelo autor. Criaremos objetos utilizando a palavra-chave new, da seguinte forma: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var planeta1 = new Planeta(); planeta1.nome = "Mercurio"; planeta1.diametro = 4_878; planeta1.massa = 0.055; var planeta2 = new Planeta(); planeta2.nome = "Terra"; planeta2.diametro = 12_742; planeta2.massa = 1.0; var planeta3 = new Planeta(); planeta3.nome = "Saturno"; planeta3.diametro = 120_536; planeta3.massa = 95.2; Classes e objetos 47 Observe que criamos três variáveis chamadas planeta1, planeta2 e planeta3. Essas variáveis têm como tipo de dado a nossa classe Planeta. Além disso, perceba que elas se referem a três objetos diferentes, os planetas Mercúrio, Terra e Saturno. Como o Java é uma linguagem fortemente tipada, ele garantirá que variáveis de classes diferentes não se misturem. Por exemplo, se colocássemos no método main o seguinte código e tentássemos compilar o programa: 1 2 3 4 5 6 7 public class Main { public static void main(String[] args) { var planeta = new Planeta(); var cachorro = new Cachorro(); planeta = cachorro; } } Receberíamos do compilador a seguinte mensagem de erro: Error:(5, 19) java: incompatible types: Cachorro cannot be converted to Planeta Isso significa que Cachorro e Planeta são coisas diferentes e não podemos misturá-los. 3.2.1 Métodos Além de atributos, também podemos definir operações que os planetas podem realizar no contexto do nosso sistema. A primeira operação que podemos inserir para nosso planeta é a obtenção do raio. Como sabemos, o raio equivale à metade do diâmetro. Fazemos isso definindo uma função no interior do objeto, chamada de método. A declaração de função segue a seguinte sintaxe (DEITEL; DEITEL, 2010): tipo nomeDaFunção(tipo parametro1, tipo parametro2) { código return valor; } O tipo de retorno, nome do método e parâmetros de retorno definem o que chamamos de assinatura do método. Observe que o método também define um bloco de código, dentro da classe, portanto, elas podem utilizar os valores dos atributos da classe e, ao mesmo tempo, podem conter novas variáveis restritas ao seu interior, chamadas de variáveis locais. Programação orientada a objetos I48 Voltemos ao exemplo do método do raio. Ele seria implementado da seguinte forma: 1 2 3 4 5 6 7 8 9 public class Planeta { String nome = ""; int diametro; double massa; double raio() { return diametro / 2.0; } } Note que o tipo de retorno dele é um double, já que é o tipo de dado do raio calculado. A palavra-chave return indica qual valor será o resultado da execução desse método. É importante saber que o método será imediatamente interrompido quando ela for atingida. Podemos usar esse método em nosso main, da seguinte forma: 1 2 3 4 5 6 7 var planeta1 = new Planeta(); planeta1.nome = "Mercurio"; planeta1.diametro = 4_878; planeta1.massa = 0.055; System.out.println(planeta1.nome); System.out.println(planeta1.raio()); Esse código imprimirá: Mercurio 2439.0 Como declararíamos o tipo de retorno de um método que, por exemplo, fizesse só impressão de dados sem retornar qualquer tipo de valor? Bastaria declarar o método com o tipo de dado void (vazio). Nesse caso, a palavra return não é mais obrigatória, mas pode ser usada para interromper o método. Por exemplo, poderíamos programar o método imprimir na classe Planeta assim: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Planeta { String nome = ""; int diametro; double massa; double raio() { return diametro / 2.0; } void imprimir() { System.out.println("Nome: " + nome); System.out.println("Diâmetro: " + diametro); System.out.println("Massa: " + massa); } } Classes e objetos 49 Obviamente, um método pode executar outro. Por exemplo, vamos supor que também quiséssemos calcular a área da superfície do planeta. Matematicamente, a área da superfície de uma esfera é definida por 4πr2. Poderíamos implementar o método areaSuperficie() assim: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Planeta { String nome = ""; int diametro; double massa; double raio() { return diametro / 2.0; } double areaSuperficie() { var raioAoQuadrado = raio() * raio(); return 4 * 3.1415 * raioAoQuadrado; } void imprimir() { System.out.println("Nome: " + nome); System.out.println("Diâmetro: " + diametro); System.out.println("Massa: " + massa); System.out.println("Raio: " + raio()); System.out.println("Area da superfície:" + areaSuperficie()); } } Observe, na linha 11, que o método areaSuperficie() define a variável local raioAoQuadrado e utiliza a função raio() duas vezes. Também atualizamos nossa função imprimir() para incluir os dados calculados. Quando planejamos métodos, é importante pensar com cuidado em sua assinatura (BLOCH, 2019). Bons nomes podem melhorar muito a legibilidade do código, simplificando sua manutenção. 3.2.2 Construtores Quando utilizamos a palavra-chave new para criar um novo objeto, estamos utilizando uma função especial chamada construtor (SIERRA; BATES, 2010). Seu objetivo é inicializar o valor dos atributos da classe quando uma nova instância for gerada. Caso não criemos um construtor, o Java o criará automaticamente. Nesse caso, os atributos serão inicializados com o valor padrão (0 para números, false para booleanos e null para objetos), ou com os valores já indicados na declaração (como o valor de um texto vazio que utilizamos no nome, indicado pelas aspas). Entretanto, podemos criar nossos próprios construtores e, inclusive, passar para eles parâmetros. Vamos adicionar um construtor à classe Planeta para que possamos informar o valor dos seus atributos diretamente. O construtor é declarado como na forma de uma função com o próprio nome da classe e sem tipo de retorno, por exemplo: Programação orientada a objetos I50 1 2 3 4 5 6 7 8 9 10 public class Planeta { String nome; int diametro; double massa; public Planeta(String n, int d, double m) { nome = n; diametro = d; massa = m; } Observe que nosso construtor, definido na linha 6, contém três parâmetros que chamamos de n, d e m. Usamos o valor desses parâmetros para inicializar o nome, diâmetro e massa do Planeta. Se tentarmos executar nosso código agora, obteremos o seguinte erro: Main.java Error:(3, 24) java: constructor Planeta in class Planeta cannot be applied to given types; required: java.lang.String,int,double found: no arguments reason: actual and formal argument lists differ in length O que esse erro quer dizer? Significa que na linha 3 do arquivo Main.java estamos usando um construtor sem parâmetros na classe Planeta, mas ele não existe. Isso ocorre porque, ao declararmos nosso construtor, o Java não nos fornecerá mais o construtor padrão automaticamente. Podemos corrigir a função no main utilizando nosso construtor: 1 2 3 4 5 6 var planeta1 = new Planeta("Mercurio", 4_878, 0.055); planeta1.imprimir(); var planeta2 = new Planeta("Terra", 12_742, 1.0); var planeta3 = new Planeta("Saturno", 120_536, 95.2); Uma prática comum em construtores é declarar o nome dos parâmetros exatamente iguais ao nome dos atributos, porém, com o conhecimentoque temos até agora, isso geraria um código ambíguo, observe: 1 2 3 4 5 Planeta(String nome, int diametro, double massa) { nome = nome; diametro = diametro; massa = massa; } Na linha 2, gostaríamos que a palavra nome, do lado esquerdo do sinal de igual, se referisse ao atributo nome do objeto; e a palavra nome, do lado direito, se referisse ao parâmetro nome, declarado Classes e objetos 51 na assinatura do método (antigamente chamado de n). Como resolver esse impasse? Para isso, o Java define a palavra-chave this. Ela significa "o próprio objeto" e, por meio dela, referenciamos o atributo, não o parâmetro do método. Assim, o código final do nosso construtor será: 1 2 3 4 5 Planeta(String nome, int diametro, double massa) { this.nome = nome; this.diametro = diametro; this.massa = massa; } E se quiséssemos que o construtor sem parâmetros ainda existisse? Nesse caso, bastaria declararmos um segundo construtor sem parâmetros e inicializar nele as variáveis: 1 2 3 4 5 Planeta() { this.nome = ""; this.diametro = 0; this.massa = 0; } Há uma forma ainda mais fácil de fazer isso. A palavra-chave this pode ser utilizada na primeira linha de um construtor como uma referência a outro construtor. Desse modo, poderíamos reescrever esse construtor chamando nosso construtor de três parâmetros: 1 2 3 Planeta() { this("", 0, 0.0); } Não há limites para o número de construtores que podemos criar. Observe que, na ausência de um construtor padrão, podemos utilizar construtores para obrigar o usuário a informar determinados atributos. 3.3 A palavra-chave static Às vezes, precisamos criar um método ou atributo que se refere à classe como um todo, e não a determinada instância. Damos a isso o nome de atributo ou método estático (DEITEL; DEITEL, 2010). A diferença deles é que não precisarão da palavra-chave new para funcionar. Além disso, o valor de um atributo estático é compartilhado por todos os objetos da classe (SIERRA; BATES, 2010). Definimos um atributo ou método como estático com a palavra-chave static no momento de sua declaração. Por exemplo, vamos definir o método estático para a descrição para a classe Planeta, que descreve o que um planeta é: 1 2 3 static String descricao() { return "Um corpo celeste esférico que orbita uma estrela"; } Podemos utilizar esse método na função main por meio do comando: System.out.println(Planeta.descricao()); Programação orientada a objetos I52 Essa chamada também seria possível por meio de uma variável do tipo Planeta, por exemplo: 1 2 3 4 var planeta1 = new Planeta("Mercurio", 4_878, 0.055); //Valido, porém, confuso System.out.println(planeta1.descricao()); Observe que, nesse código, a função descrição aparenta não ser estática e, portanto, é uma má prática por levar a esse entendimento errôneo. Um dos grandes usos de atributos estáticos é a definição de constantes. Por exemplo, poderíamos definir a constante PI com: final static double PI = 3.1415; Dessa forma, a versão final da nossa classe Planeta seria: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Planeta { final static double PI = 3.1415; String nome; int diametro; double massa; static String descricao() { return "Um corpo celeste esférico que orbita uma estrela"; } Planeta(String nome, int diametro, double massa) { this.nome = nome; this.diametro = diametro; this.massa = massa; } Planeta() { this("", 0, 0.0); } double raio() { return diametro / 2.0; } double areaSuperficie() { var raioAoQuadrado = raio() * raio(); return 4 * PI * raioAoQuadrado; } void imprimir() { System.out.println("Nome: " + nome); System.out.println("Diâmetro: " + diametro); System.out.println("Massa: " + massa); System.out.println("Raio: " + raio()); System.out.println("Area da superfície:" + areaSuperficie()); } } Classes e objetos 53 Como atributos e métodos estáticos pertencem à classe, não a uma instância específica, será impossível acessar métodos e atributos não estáticos da mesma classe com base neles sem que criemos pelo menos um objeto. Muitos iniciantes em programação acabam criando somente atributos e métodos estáticos ao perceber que não conseguem acessar atributos e métodos sem esse marcador com base no método main. Esta não é uma prática correta e, para piorar, provavelmente será sugerida como correção por sua IDE. Caso você precise acessar métodos da classe em que o main está, prefira criar um objeto, como no exemplo: 1 2 3 4 5 6 7 8 9 10 public class Main { void exemplo() { System.out.println("Chamando método não estático"); } public static void main(String[] args) { var main = new Main(); //Crie um objeto main.exemplo(); //Chame o método não estático } } Se a linha 8 contivesse somente a chamada exemplo() em vez de main.exemplo(), você receberia o erro Non-static method 'exemplo()' cannot be referenced from a static context, que pode ser traduzido como "o método não estático exemplo não pode ser acessado de um contexto estático". Por fim, uma prática comum é criar um método não estático cujo único papel é substituir o main e chamá-lo por meio de um objeto anônimo: 1 2 3 4 5 6 7 8 9 public class Main { void run() { //Esse método substituirá o main } public static void main(String[] args) { new Main().run(); //Cria o objeto e já chama run() } } Lembre-se sempre de que métodos e atributos estáticos são exceção, não regra. Desconfie de sua IDE caso ela esteja sugerindo para marcar todos os lugares dessa forma. Programação orientada a objetos I54 3.4. O valor especial null Podemos utilizar o valor especial null para indicar que uma variável não contém nenhum objeto associado. Se tentarmos utilizar qualquer atributo ou método de uma variável nula, obteremos um erro conhecido como NullPointerException. O código a seguir simula essa condição: 1 2 Planeta terra = null; System.out.println(terra.raio()); Se tentarmos executá-lo, receberemos um erro como este: Exception in thread "main" java.lang.NullPointerException at Main.main(Main.java:5) Observe que ele indica que o tipo do erro é NullPointerException, ou seja, tentamos fazer acesso a uma variável nula. Após o at, o Java indicará a classe e o método em que o problema ocorreu, seguido do nome do arquivo: linha entre parênteses. Nesse caso, o erro ocorreu na classe Main, no método main, que está no arquivo Main.java linha 5. O valor null é o valor padrão para variáveis que guardam objetos. Não confunda o null com objetos que admitem valores vazios. Por exemplo, é possível existir um objeto de texto sem nenhum caractere "" ou mesmo um array com 0 elementos. Quando estudamos as variáveis no capítulo 2, vimos três tipos de dados que também podiam possuir valor null: Strings, arrays e enums. Isso porque o Java também considera esses três tipos como casos especiais de classes. Isso significa que as variáveis que representam esses tipos também são referências, o que não é um problema para Strings ou enums, já que os valores dos objetos dessas classes, uma vez construídos, não podem ser modificados, mas tome cuidado no caso dos arrays. Passar um array por parâmetro não criará uma cópia de seus dados. Quando você estiver começando a programar, provavelmente receberá muitas vezes o erro NullPointerException, descrito acima. Quando isso ocorrer, não se assuste. Basta localizar a linha do erro e verificar qual variável nunca foi inicializada com new. e, assim, você encontrará a falha em seu código. Classes e objetos 55 Considerações finais A capacidade humana de classificar objetos e criar abstrações é realmente impressionante. Quando lemos uma fase simples como "Fui de carro para a escola", não percebemos que as palavras carro e escola representam abstrações para qualquer carro e qualquer escola. Uma linguagem orientada a objetos, como o Java, permite-nos criar abstrações
Compartilhar